TextureView.java revision 302a9df1d50373c82923bb84ff665dfce584fb22
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.view; 18 19import android.content.Context; 20import android.graphics.Bitmap; 21import android.graphics.Canvas; 22import android.graphics.Matrix; 23import android.graphics.Paint; 24import android.graphics.Rect; 25import android.graphics.SurfaceTexture; 26import android.util.AttributeSet; 27import android.util.Log; 28 29/** 30 * <p>A TextureView can be used to display a content stream. Such a content 31 * stream can for instance be a video or an OpenGL scene. The content stream 32 * can come from the application's process as well as a remote process.</p> 33 * 34 * <p>TextureView can only be used in a hardware accelerated window. When 35 * rendered in software, TextureView will draw nothing.</p> 36 * 37 * <p>Unlike {@link SurfaceView}, TextureView does not create a separate 38 * window but behaves as a regular View. This key difference allows a 39 * TextureView to be moved, transformed, animated, etc. For instance, you 40 * can make a TextureView semi-translucent by calling 41 * <code>myView.setAlpha(0.5f)</code>.</p> 42 * 43 * <p>Using a TextureView is simple: all you need to do is get its 44 * {@link SurfaceTexture}. The {@link SurfaceTexture} can then be used to 45 * render content. The following example demonstrates how to render the 46 * camera preview into a TextureView:</p> 47 * 48 * <pre> 49 * public class LiveCameraActivity extends Activity implements TextureView.SurfaceTextureListener { 50 * private Camera mCamera; 51 * private TextureView mTextureView; 52 * 53 * protected void onCreate(Bundle savedInstanceState) { 54 * super.onCreate(savedInstanceState); 55 * 56 * mTextureView = new TextureView(this); 57 * mTextureView.setSurfaceTextureListener(this); 58 * 59 * setContentView(mTextureView); 60 * } 61 * 62 * public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 63 * mCamera = Camera.open(); 64 * 65 * try { 66 * mCamera.setPreviewTexture(surface); 67 * mCamera.startPreview(); 68 * } catch (IOException ioe) { 69 * // Something bad happened 70 * } 71 * } 72 * 73 * public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 74 * // Ignored, Camera does all the work for us 75 * } 76 * 77 * public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 78 * mCamera.stopPreview(); 79 * mCamera.release(); 80 * return true; 81 * } 82 * 83 * public void onSurfaceTextureUpdated(SurfaceTexture surface) { 84 * // Invoked every time there's a new Camera preview frame 85 * } 86 * } 87 * </pre> 88 * 89 * <p>A TextureView's SurfaceTexture can be obtained either by invoking 90 * {@link #getSurfaceTexture()} or by using a {@link SurfaceTextureListener}. 91 * It is important to know that a SurfaceTexture is available only after the 92 * TextureView is attached to a window (and {@link #onAttachedToWindow()} has 93 * been invoked.) It is therefore highly recommended you use a listener to 94 * be notified when the SurfaceTexture becomes available.</p> 95 * 96 * @see SurfaceView 97 * @see SurfaceTexture 98 */ 99public class TextureView extends View { 100 private static final String LOG_TAG = "TextureView"; 101 102 private HardwareLayer mLayer; 103 private SurfaceTexture mSurface; 104 private SurfaceTextureListener mListener; 105 106 private boolean mOpaque = true; 107 108 private final Matrix mMatrix = new Matrix(); 109 private boolean mMatrixChanged; 110 111 private final Object[] mLock = new Object[0]; 112 private boolean mUpdateLayer; 113 114 private SurfaceTexture.OnFrameAvailableListener mUpdateListener; 115 116 private Canvas mCanvas; 117 private int mSaveCount; 118 119 private final Object[] mNativeWindowLock = new Object[0]; 120 // Used from native code, do not write! 121 @SuppressWarnings({"UnusedDeclaration"}) 122 private int mNativeWindow; 123 124 /** 125 * Creates a new TextureView. 126 * 127 * @param context The context to associate this view with. 128 */ 129 public TextureView(Context context) { 130 super(context); 131 init(); 132 } 133 134 /** 135 * Creates a new TextureView. 136 * 137 * @param context The context to associate this view with. 138 * @param attrs The attributes of the XML tag that is inflating the view. 139 */ 140 @SuppressWarnings({"UnusedDeclaration"}) 141 public TextureView(Context context, AttributeSet attrs) { 142 super(context, attrs); 143 init(); 144 } 145 146 /** 147 * Creates a new TextureView. 148 * 149 * @param context The context to associate this view with. 150 * @param attrs The attributes of the XML tag that is inflating the view. 151 * @param defStyle The default style to apply to this view. If 0, no style 152 * will be applied (beyond what is included in the theme). This may 153 * either be an attribute resource, whose value will be retrieved 154 * from the current theme, or an explicit style resource. 155 */ 156 @SuppressWarnings({"UnusedDeclaration"}) 157 public TextureView(Context context, AttributeSet attrs, int defStyle) { 158 super(context, attrs, defStyle); 159 init(); 160 } 161 162 private void init() { 163 mLayerPaint = new Paint(); 164 } 165 166 /** 167 * {@inheritDoc} 168 */ 169 @Override 170 public boolean isOpaque() { 171 return mOpaque; 172 } 173 174 /** 175 * Indicates whether the content of this TextureView is opaque. The 176 * content is assumed to be opaque by default. 177 * 178 * @param opaque True if the content of this TextureView is opaque, 179 * false otherwise 180 */ 181 public void setOpaque(boolean opaque) { 182 if (opaque != mOpaque) { 183 mOpaque = opaque; 184 updateLayer(); 185 } 186 } 187 188 @Override 189 protected void onAttachedToWindow() { 190 super.onAttachedToWindow(); 191 192 if (!isHardwareAccelerated()) { 193 Log.w(LOG_TAG, "A TextureView or a subclass can only be " 194 + "used with hardware acceleration enabled."); 195 } 196 } 197 198 @Override 199 protected void onDetachedFromWindow() { 200 super.onDetachedFromWindow(); 201 202 if (mLayer != null) { 203 boolean shouldRelease = true; 204 if (mListener != null) { 205 shouldRelease = mListener.onSurfaceTextureDestroyed(mSurface); 206 } 207 208 synchronized (mNativeWindowLock) { 209 nDestroyNativeWindow(); 210 } 211 212 mLayer.destroy(); 213 if (shouldRelease) mSurface.release(); 214 mSurface = null; 215 mLayer = null; 216 } 217 } 218 219 /** 220 * The layer type of a TextureView is ignored since a TextureView is always 221 * considered to act as a hardware layer. The optional paint supplied to this 222 * method will however be taken into account when rendering the content of 223 * this TextureView. 224 * 225 * @param layerType The ype of layer to use with this view, must be one of 226 * {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or 227 * {@link #LAYER_TYPE_HARDWARE} 228 * @param paint The paint used to compose the layer. This argument is optional 229 * and can be null. It is ignored when the layer type is 230 * {@link #LAYER_TYPE_NONE} 231 */ 232 @Override 233 public void setLayerType(int layerType, Paint paint) { 234 if (paint != mLayerPaint) { 235 mLayerPaint = paint; 236 invalidate(); 237 } 238 } 239 240 /** 241 * Always returns {@link #LAYER_TYPE_HARDWARE}. 242 */ 243 @Override 244 public int getLayerType() { 245 return LAYER_TYPE_HARDWARE; 246 } 247 248 /** 249 * Calling this method has no effect. 250 */ 251 @Override 252 public void buildLayer() { 253 } 254 255 /** 256 * Subclasses of TextureView cannot do their own rendering 257 * with the {@link Canvas} object. 258 * 259 * @param canvas The Canvas to which the View is rendered. 260 */ 261 @Override 262 public final void draw(Canvas canvas) { 263 applyUpdate(); 264 } 265 266 /** 267 * Subclasses of TextureView cannot do their own rendering 268 * with the {@link Canvas} object. 269 * 270 * @param canvas The Canvas to which the View is rendered. 271 */ 272 @Override 273 protected final void onDraw(Canvas canvas) { 274 } 275 276 @Override 277 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 278 super.onSizeChanged(w, h, oldw, oldh); 279 if (mSurface != null) { 280 nSetDefaultBufferSize(mSurface, getWidth(), getHeight()); 281 if (mListener != null) { 282 mListener.onSurfaceTextureSizeChanged(mSurface, getWidth(), getHeight()); 283 } 284 } 285 } 286 287 @Override 288 HardwareLayer getHardwareLayer() { 289 if (mLayer == null) { 290 if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) { 291 return null; 292 } 293 294 mLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer(mOpaque); 295 mSurface = mAttachInfo.mHardwareRenderer.createSurfaceTexture(mLayer); 296 nSetDefaultBufferSize(mSurface, getWidth(), getHeight()); 297 nCreateNativeWindow(mSurface); 298 299 mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() { 300 @Override 301 public void onFrameAvailable(SurfaceTexture surfaceTexture) { 302 // Per SurfaceTexture's documentation, the callback may be invoked 303 // from an arbitrary thread 304 synchronized (mLock) { 305 mUpdateLayer = true; 306 } 307 postInvalidateDelayed(0); 308 } 309 }; 310 mSurface.setOnFrameAvailableListener(mUpdateListener); 311 312 if (mListener != null) { 313 mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight()); 314 } 315 } 316 317 applyUpdate(); 318 319 if (mMatrixChanged) { 320 mLayer.setTransform(mMatrix); 321 mMatrixChanged = false; 322 } 323 324 return mLayer; 325 } 326 327 @Override 328 protected void onVisibilityChanged(View changedView, int visibility) { 329 super.onVisibilityChanged(changedView, visibility); 330 331 if (mSurface != null) { 332 // When the view becomes invisible, stop updating it, it's a waste of CPU 333 // To cancel updates, the easiest thing to do is simply to remove the 334 // updates listener 335 if (visibility == VISIBLE) { 336 mSurface.setOnFrameAvailableListener(mUpdateListener); 337 updateLayer(); 338 } else { 339 mSurface.setOnFrameAvailableListener(null); 340 } 341 } 342 } 343 344 private void updateLayer() { 345 mUpdateLayer = true; 346 invalidate(); 347 } 348 349 private void applyUpdate() { 350 if (mLayer == null) { 351 return; 352 } 353 354 synchronized (mLock) { 355 if (mUpdateLayer) { 356 mUpdateLayer = false; 357 } else { 358 return; 359 } 360 } 361 362 mLayer.update(getWidth(), getHeight(), mOpaque); 363 364 if (mListener != null) { 365 mListener.onSurfaceTextureUpdated(mSurface); 366 } 367 } 368 369 /** 370 * <p>Sets the transform to associate with this texture view. 371 * The specified transform applies to the underlying surface 372 * texture and does not affect the size or position of the view 373 * itself, only of its content.</p> 374 * 375 * <p>Some transforms might prevent the content from drawing 376 * all the pixels contained within this view's bounds. In such 377 * situations, make sure this texture view is not marked opaque.</p> 378 * 379 * @param transform The transform to apply to the content of 380 * this view. 381 * 382 * @see #getTransform(android.graphics.Matrix) 383 * @see #isOpaque() 384 * @see #setOpaque(boolean) 385 */ 386 public void setTransform(Matrix transform) { 387 mMatrix.set(transform); 388 mMatrixChanged = true; 389 invalidate(); 390 } 391 392 /** 393 * Returns the transform associated with this texture view. 394 * 395 * @param transform The {@link Matrix} in which to copy the current 396 * transform. Can be null. 397 * 398 * @return The specified matrix if not null or a new {@link Matrix} 399 * instance otherwise. 400 * 401 * @see #setTransform(android.graphics.Matrix) 402 */ 403 public Matrix getTransform(Matrix transform) { 404 if (transform == null) { 405 transform = new Matrix(); 406 } 407 408 transform.set(mMatrix); 409 410 return transform; 411 } 412 413 /** 414 * <p>Returns a {@link android.graphics.Bitmap} representation of the content 415 * of the associated surface texture. If the surface texture is not available, 416 * this method returns null.</p> 417 * 418 * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888} 419 * pixel format and its dimensions are the same as this view's.</p> 420 * 421 * <p><strong>Do not</strong> invoke this method from a drawing method 422 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p> 423 * 424 * <p>If an error occurs during the copy, an empty bitmap will be returned.</p> 425 * 426 * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface 427 * texture is not available or the width <= 0 or the height <= 0 428 * 429 * @see #isAvailable() 430 * @see #getBitmap(android.graphics.Bitmap) 431 * @see #getBitmap(int, int) 432 */ 433 public Bitmap getBitmap() { 434 return getBitmap(getWidth(), getHeight()); 435 } 436 437 /** 438 * <p>Returns a {@link android.graphics.Bitmap} representation of the content 439 * of the associated surface texture. If the surface texture is not available, 440 * this method returns null.</p> 441 * 442 * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888} 443 * pixel format.</p> 444 * 445 * <p><strong>Do not</strong> invoke this method from a drawing method 446 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p> 447 * 448 * <p>If an error occurs during the copy, an empty bitmap will be returned.</p> 449 * 450 * @param width The width of the bitmap to create 451 * @param height The height of the bitmap to create 452 * 453 * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface 454 * texture is not available or width is <= 0 or height is <= 0 455 * 456 * @see #isAvailable() 457 * @see #getBitmap(android.graphics.Bitmap) 458 * @see #getBitmap() 459 */ 460 public Bitmap getBitmap(int width, int height) { 461 if (isAvailable() && width > 0 && height > 0) { 462 return getBitmap(Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)); 463 } 464 return null; 465 } 466 467 /** 468 * <p>Copies the content of this view's surface texture into the specified 469 * bitmap. If the surface texture is not available, the copy is not executed. 470 * The content of the surface texture will be scaled to fit exactly inside 471 * the specified bitmap.</p> 472 * 473 * <p><strong>Do not</strong> invoke this method from a drawing method 474 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p> 475 * 476 * <p>If an error occurs, the bitmap is left unchanged.</p> 477 * 478 * @param bitmap The bitmap to copy the content of the surface texture into, 479 * cannot be null, all configurations are supported 480 * 481 * @return The bitmap specified as a parameter 482 * 483 * @see #isAvailable() 484 * @see #getBitmap(int, int) 485 * @see #getBitmap() 486 */ 487 public Bitmap getBitmap(Bitmap bitmap) { 488 if (bitmap != null && isAvailable()) { 489 mLayer.copyInto(bitmap); 490 } 491 return bitmap; 492 } 493 494 /** 495 * Returns true if the {@link SurfaceTexture} associated with this 496 * TextureView is available for rendering. When this method returns 497 * true, {@link #getSurfaceTexture()} returns a valid surface texture. 498 */ 499 public boolean isAvailable() { 500 return mSurface != null; 501 } 502 503 /** 504 * <p>Start editing the pixels in the surface. The returned Canvas can be used 505 * to draw into the surface's bitmap. A null is returned if the surface has 506 * not been created or otherwise cannot be edited. You will usually need 507 * to implement 508 * {@link SurfaceTextureListener#onSurfaceTextureAvailable(android.graphics.SurfaceTexture, int, int)} 509 * to find out when the Surface is available for use.</p> 510 * 511 * <p>The content of the Surface is never preserved between unlockCanvas() 512 * and lockCanvas(), for this reason, every pixel within the Surface area 513 * must be written. The only exception to this rule is when a dirty 514 * rectangle is specified, in which case, non-dirty pixels will be 515 * preserved.</p> 516 * 517 * @return A Canvas used to draw into the surface. 518 * 519 * @see #lockCanvas(android.graphics.Rect) 520 * @see #unlockCanvasAndPost(android.graphics.Canvas) 521 */ 522 public Canvas lockCanvas() { 523 return lockCanvas(null); 524 } 525 526 /** 527 * Just like {@link #lockCanvas()} but allows specification of a dirty 528 * rectangle. Every pixel within that rectangle must be written; however 529 * pixels outside the dirty rectangle will be preserved by the next call 530 * to lockCanvas(). 531 * 532 * @param dirty Area of the surface that will be modified. 533 534 * @return A Canvas used to draw into the surface. 535 * 536 * @see #lockCanvas() 537 * @see #unlockCanvasAndPost(android.graphics.Canvas) 538 */ 539 public Canvas lockCanvas(Rect dirty) { 540 if (!isAvailable()) return null; 541 542 if (mCanvas == null) { 543 mCanvas = new Canvas(); 544 } 545 546 synchronized (mNativeWindowLock) { 547 nLockCanvas(mNativeWindow, mCanvas, dirty); 548 } 549 mSaveCount = mCanvas.save(); 550 551 return mCanvas; 552 } 553 554 /** 555 * Finish editing pixels in the surface. After this call, the surface's 556 * current pixels will be shown on the screen, but its content is lost, 557 * in particular there is no guarantee that the content of the Surface 558 * will remain unchanged when lockCanvas() is called again. 559 * 560 * @param canvas The Canvas previously returned by lockCanvas() 561 * 562 * @see #lockCanvas() 563 * @see #lockCanvas(android.graphics.Rect) 564 */ 565 public void unlockCanvasAndPost(Canvas canvas) { 566 if (mCanvas != null && canvas == mCanvas) { 567 canvas.restoreToCount(mSaveCount); 568 mSaveCount = 0; 569 570 synchronized (mNativeWindowLock) { 571 nUnlockCanvasAndPost(mNativeWindow, mCanvas); 572 } 573 } 574 } 575 576 /** 577 * Returns the {@link SurfaceTexture} used by this view. This method 578 * may return null if the view is not attached to a window or if the surface 579 * texture has not been initialized yet. 580 * 581 * @see #isAvailable() 582 */ 583 public SurfaceTexture getSurfaceTexture() { 584 return mSurface; 585 } 586 587 /** 588 * Returns the {@link SurfaceTextureListener} currently associated with this 589 * texture view. 590 * 591 * @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener) 592 * @see SurfaceTextureListener 593 */ 594 public SurfaceTextureListener getSurfaceTextureListener() { 595 return mListener; 596 } 597 598 /** 599 * Sets the {@link SurfaceTextureListener} used to listen to surface 600 * texture events. 601 * 602 * @see #getSurfaceTextureListener() 603 * @see SurfaceTextureListener 604 */ 605 public void setSurfaceTextureListener(SurfaceTextureListener listener) { 606 mListener = listener; 607 } 608 609 /** 610 * This listener can be used to be notified when the surface texture 611 * associated with this texture view is available. 612 */ 613 public static interface SurfaceTextureListener { 614 /** 615 * Invoked when a {@link TextureView}'s SurfaceTexture is ready for use. 616 * 617 * @param surface The surface returned by 618 * {@link android.view.TextureView#getSurfaceTexture()} 619 * @param width The width of the surface 620 * @param height The height of the surface 621 */ 622 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height); 623 624 /** 625 * Invoked when the {@link SurfaceTexture}'s buffers size changed. 626 * 627 * @param surface The surface returned by 628 * {@link android.view.TextureView#getSurfaceTexture()} 629 * @param width The new width of the surface 630 * @param height The new height of the surface 631 */ 632 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height); 633 634 /** 635 * Invoked when the specified {@link SurfaceTexture} is about to be destroyed. 636 * If returns true, no rendering should happen inside the surface texture after this method 637 * is invoked. If returns false, the client needs to call {@link SurfaceTexture#release()}. 638 * 639 * @param surface The surface about to be destroyed 640 */ 641 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface); 642 643 /** 644 * Invoked when the specified {@link SurfaceTexture} is updated through 645 * {@link SurfaceTexture#updateTexImage()}. 646 * 647 * @param surface The surface just updated 648 */ 649 public void onSurfaceTextureUpdated(SurfaceTexture surface); 650 } 651 652 private native void nCreateNativeWindow(SurfaceTexture surface); 653 private native void nDestroyNativeWindow(); 654 655 private static native void nSetDefaultBufferSize(SurfaceTexture surfaceTexture, 656 int width, int height); 657 658 private static native void nLockCanvas(int nativeWindow, Canvas canvas, Rect dirty); 659 private static native void nUnlockCanvasAndPost(int nativeWindow, Canvas canvas); 660} 661