TextureView.java revision 59c7f80dd20258cefa1fc4bdd3c9a709a8dd53b8
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 * <p>It is important to note that only one producer can use the TextureView. 97 * For instance, if you use a TextureView to display the camera preview, you 98 * cannot use {@link #lockCanvas()} to draw onto the TextureView at the same 99 * time.</p> 100 * 101 * @see SurfaceView 102 * @see SurfaceTexture 103 */ 104public class TextureView extends View { 105 private static final String LOG_TAG = "TextureView"; 106 107 private HardwareLayer mLayer; 108 private SurfaceTexture mSurface; 109 private SurfaceTextureListener mListener; 110 111 private boolean mOpaque = true; 112 113 private final Matrix mMatrix = new Matrix(); 114 private boolean mMatrixChanged; 115 116 private final Object[] mLock = new Object[0]; 117 private boolean mUpdateLayer; 118 119 private SurfaceTexture.OnFrameAvailableListener mUpdateListener; 120 121 private Canvas mCanvas; 122 private int mSaveCount; 123 124 private final Object[] mNativeWindowLock = new Object[0]; 125 // Used from native code, do not write! 126 @SuppressWarnings({"UnusedDeclaration"}) 127 private int mNativeWindow; 128 129 /** 130 * Creates a new TextureView. 131 * 132 * @param context The context to associate this view with. 133 */ 134 public TextureView(Context context) { 135 super(context); 136 init(); 137 } 138 139 /** 140 * Creates a new TextureView. 141 * 142 * @param context The context to associate this view with. 143 * @param attrs The attributes of the XML tag that is inflating the view. 144 */ 145 @SuppressWarnings({"UnusedDeclaration"}) 146 public TextureView(Context context, AttributeSet attrs) { 147 super(context, attrs); 148 init(); 149 } 150 151 /** 152 * Creates a new TextureView. 153 * 154 * @param context The context to associate this view with. 155 * @param attrs The attributes of the XML tag that is inflating the view. 156 * @param defStyle The default style to apply to this view. If 0, no style 157 * will be applied (beyond what is included in the theme). This may 158 * either be an attribute resource, whose value will be retrieved 159 * from the current theme, or an explicit style resource. 160 */ 161 @SuppressWarnings({"UnusedDeclaration"}) 162 public TextureView(Context context, AttributeSet attrs, int defStyle) { 163 super(context, attrs, defStyle); 164 init(); 165 } 166 167 private void init() { 168 mLayerPaint = new Paint(); 169 } 170 171 /** 172 * {@inheritDoc} 173 */ 174 @Override 175 public boolean isOpaque() { 176 return mOpaque; 177 } 178 179 /** 180 * Indicates whether the content of this TextureView is opaque. The 181 * content is assumed to be opaque by default. 182 * 183 * @param opaque True if the content of this TextureView is opaque, 184 * false otherwise 185 */ 186 public void setOpaque(boolean opaque) { 187 if (opaque != mOpaque) { 188 mOpaque = opaque; 189 updateLayer(); 190 } 191 } 192 193 @Override 194 protected void onAttachedToWindow() { 195 super.onAttachedToWindow(); 196 197 if (!isHardwareAccelerated()) { 198 Log.w(LOG_TAG, "A TextureView or a subclass can only be " 199 + "used with hardware acceleration enabled."); 200 } 201 } 202 203 @Override 204 protected void onDetachedFromWindow() { 205 super.onDetachedFromWindow(); 206 207 if (mLayer != null) { 208 boolean shouldRelease = true; 209 if (mListener != null) { 210 shouldRelease = mListener.onSurfaceTextureDestroyed(mSurface); 211 } 212 213 synchronized (mNativeWindowLock) { 214 nDestroyNativeWindow(); 215 } 216 217 mLayer.destroy(); 218 if (shouldRelease) mSurface.release(); 219 mSurface = null; 220 mLayer = null; 221 } 222 } 223 224 /** 225 * The layer type of a TextureView is ignored since a TextureView is always 226 * considered to act as a hardware layer. The optional paint supplied to this 227 * method will however be taken into account when rendering the content of 228 * this TextureView. 229 * 230 * @param layerType The ype of layer to use with this view, must be one of 231 * {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or 232 * {@link #LAYER_TYPE_HARDWARE} 233 * @param paint The paint used to compose the layer. This argument is optional 234 * and can be null. It is ignored when the layer type is 235 * {@link #LAYER_TYPE_NONE} 236 */ 237 @Override 238 public void setLayerType(int layerType, Paint paint) { 239 if (paint != mLayerPaint) { 240 mLayerPaint = paint; 241 invalidate(); 242 } 243 } 244 245 /** 246 * Always returns {@link #LAYER_TYPE_HARDWARE}. 247 */ 248 @Override 249 public int getLayerType() { 250 return LAYER_TYPE_HARDWARE; 251 } 252 253 @Override 254 boolean hasStaticLayer() { 255 return true; 256 } 257 258 /** 259 * Calling this method has no effect. 260 */ 261 @Override 262 public void buildLayer() { 263 } 264 265 /** 266 * Subclasses of TextureView cannot do their own rendering 267 * with the {@link Canvas} object. 268 * 269 * @param canvas The Canvas to which the View is rendered. 270 */ 271 @Override 272 public final void draw(Canvas canvas) { 273 applyUpdate(); 274 applyTransformMatrix(); 275 } 276 277 /** 278 * Subclasses of TextureView cannot do their own rendering 279 * with the {@link Canvas} object. 280 * 281 * @param canvas The Canvas to which the View is rendered. 282 */ 283 @Override 284 protected final void onDraw(Canvas canvas) { 285 } 286 287 @Override 288 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 289 super.onSizeChanged(w, h, oldw, oldh); 290 if (mSurface != null) { 291 nSetDefaultBufferSize(mSurface, getWidth(), getHeight()); 292 if (mListener != null) { 293 mListener.onSurfaceTextureSizeChanged(mSurface, getWidth(), getHeight()); 294 } 295 } 296 } 297 298 @Override 299 boolean destroyLayer() { 300 return false; 301 } 302 303 @Override 304 HardwareLayer getHardwareLayer() { 305 if (mLayer == null) { 306 if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) { 307 return null; 308 } 309 310 mLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer(mOpaque); 311 mSurface = mAttachInfo.mHardwareRenderer.createSurfaceTexture(mLayer); 312 nSetDefaultBufferSize(mSurface, getWidth(), getHeight()); 313 nCreateNativeWindow(mSurface); 314 315 mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() { 316 @Override 317 public void onFrameAvailable(SurfaceTexture surfaceTexture) { 318 // Per SurfaceTexture's documentation, the callback may be invoked 319 // from an arbitrary thread 320 synchronized (mLock) { 321 mUpdateLayer = true; 322 } 323 postInvalidateDelayed(0); 324 } 325 }; 326 mSurface.setOnFrameAvailableListener(mUpdateListener); 327 328 if (mListener != null) { 329 mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight()); 330 } 331 } 332 333 applyUpdate(); 334 applyTransformMatrix(); 335 336 return mLayer; 337 } 338 339 @Override 340 protected void onVisibilityChanged(View changedView, int visibility) { 341 super.onVisibilityChanged(changedView, visibility); 342 343 if (mSurface != null) { 344 // When the view becomes invisible, stop updating it, it's a waste of CPU 345 // To cancel updates, the easiest thing to do is simply to remove the 346 // updates listener 347 if (visibility == VISIBLE) { 348 mSurface.setOnFrameAvailableListener(mUpdateListener); 349 updateLayer(); 350 } else { 351 mSurface.setOnFrameAvailableListener(null); 352 } 353 } 354 } 355 356 private void updateLayer() { 357 mUpdateLayer = true; 358 invalidate(); 359 } 360 361 private void applyUpdate() { 362 if (mLayer == null) { 363 return; 364 } 365 366 synchronized (mLock) { 367 if (mUpdateLayer) { 368 mUpdateLayer = false; 369 } else { 370 return; 371 } 372 } 373 374 mLayer.update(getWidth(), getHeight(), mOpaque); 375 376 if (mListener != null) { 377 mListener.onSurfaceTextureUpdated(mSurface); 378 } 379 } 380 381 /** 382 * <p>Sets the transform to associate with this texture view. 383 * The specified transform applies to the underlying surface 384 * texture and does not affect the size or position of the view 385 * itself, only of its content.</p> 386 * 387 * <p>Some transforms might prevent the content from drawing 388 * all the pixels contained within this view's bounds. In such 389 * situations, make sure this texture view is not marked opaque.</p> 390 * 391 * @param transform The transform to apply to the content of 392 * this view. 393 * 394 * @see #getTransform(android.graphics.Matrix) 395 * @see #isOpaque() 396 * @see #setOpaque(boolean) 397 */ 398 public void setTransform(Matrix transform) { 399 mMatrix.set(transform); 400 mMatrixChanged = true; 401 invalidateParentIfNeeded(); 402 } 403 404 /** 405 * Returns the transform associated with this texture view. 406 * 407 * @param transform The {@link Matrix} in which to copy the current 408 * transform. Can be null. 409 * 410 * @return The specified matrix if not null or a new {@link Matrix} 411 * instance otherwise. 412 * 413 * @see #setTransform(android.graphics.Matrix) 414 */ 415 public Matrix getTransform(Matrix transform) { 416 if (transform == null) { 417 transform = new Matrix(); 418 } 419 420 transform.set(mMatrix); 421 422 return transform; 423 } 424 425 private void applyTransformMatrix() { 426 if (mMatrixChanged) { 427 mLayer.setTransform(mMatrix); 428 mMatrixChanged = false; 429 } 430 } 431 432 /** 433 * <p>Returns a {@link android.graphics.Bitmap} representation of the content 434 * of the associated surface texture. If the surface texture is not available, 435 * this method returns null.</p> 436 * 437 * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888} 438 * pixel format and its dimensions are the same as this view's.</p> 439 * 440 * <p><strong>Do not</strong> invoke this method from a drawing method 441 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p> 442 * 443 * <p>If an error occurs during the copy, an empty bitmap will be returned.</p> 444 * 445 * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface 446 * texture is not available or the width <= 0 or the height <= 0 447 * 448 * @see #isAvailable() 449 * @see #getBitmap(android.graphics.Bitmap) 450 * @see #getBitmap(int, int) 451 */ 452 public Bitmap getBitmap() { 453 return getBitmap(getWidth(), getHeight()); 454 } 455 456 /** 457 * <p>Returns a {@link android.graphics.Bitmap} representation of the content 458 * of the associated surface texture. If the surface texture is not available, 459 * this method returns null.</p> 460 * 461 * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888} 462 * pixel format.</p> 463 * 464 * <p><strong>Do not</strong> invoke this method from a drawing method 465 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p> 466 * 467 * <p>If an error occurs during the copy, an empty bitmap will be returned.</p> 468 * 469 * @param width The width of the bitmap to create 470 * @param height The height of the bitmap to create 471 * 472 * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface 473 * texture is not available or width is <= 0 or height is <= 0 474 * 475 * @see #isAvailable() 476 * @see #getBitmap(android.graphics.Bitmap) 477 * @see #getBitmap() 478 */ 479 public Bitmap getBitmap(int width, int height) { 480 if (isAvailable() && width > 0 && height > 0) { 481 return getBitmap(Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)); 482 } 483 return null; 484 } 485 486 /** 487 * <p>Copies the content of this view's surface texture into the specified 488 * bitmap. If the surface texture is not available, the copy is not executed. 489 * The content of the surface texture will be scaled to fit exactly inside 490 * the specified bitmap.</p> 491 * 492 * <p><strong>Do not</strong> invoke this method from a drawing method 493 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p> 494 * 495 * <p>If an error occurs, the bitmap is left unchanged.</p> 496 * 497 * @param bitmap The bitmap to copy the content of the surface texture into, 498 * cannot be null, all configurations are supported 499 * 500 * @return The bitmap specified as a parameter 501 * 502 * @see #isAvailable() 503 * @see #getBitmap(int, int) 504 * @see #getBitmap() 505 */ 506 public Bitmap getBitmap(Bitmap bitmap) { 507 if (bitmap != null && isAvailable()) { 508 mLayer.copyInto(bitmap); 509 } 510 return bitmap; 511 } 512 513 /** 514 * Returns true if the {@link SurfaceTexture} associated with this 515 * TextureView is available for rendering. When this method returns 516 * true, {@link #getSurfaceTexture()} returns a valid surface texture. 517 */ 518 public boolean isAvailable() { 519 return mSurface != null; 520 } 521 522 /** 523 * <p>Start editing the pixels in the surface. The returned Canvas can be used 524 * to draw into the surface's bitmap. A null is returned if the surface has 525 * not been created or otherwise cannot be edited. You will usually need 526 * to implement 527 * {@link SurfaceTextureListener#onSurfaceTextureAvailable(android.graphics.SurfaceTexture, int, int)} 528 * to find out when the Surface is available for use.</p> 529 * 530 * <p>The content of the Surface is never preserved between unlockCanvas() 531 * and lockCanvas(), for this reason, every pixel within the Surface area 532 * must be written. The only exception to this rule is when a dirty 533 * rectangle is specified, in which case, non-dirty pixels will be 534 * preserved.</p> 535 * 536 * <p>This method can only be used if the underlying surface is not already 537 * owned by another producer. For instance, if the TextureView is being used 538 * to render the camera's preview you cannot invoke this method.</p> 539 * 540 * @return A Canvas used to draw into the surface. 541 * 542 * @see #lockCanvas(android.graphics.Rect) 543 * @see #unlockCanvasAndPost(android.graphics.Canvas) 544 */ 545 public Canvas lockCanvas() { 546 return lockCanvas(null); 547 } 548 549 /** 550 * Just like {@link #lockCanvas()} but allows specification of a dirty 551 * rectangle. Every pixel within that rectangle must be written; however 552 * pixels outside the dirty rectangle will be preserved by the next call 553 * to lockCanvas(). 554 * 555 * @param dirty Area of the surface that will be modified. 556 557 * @return A Canvas used to draw into the surface. 558 * 559 * @see #lockCanvas() 560 * @see #unlockCanvasAndPost(android.graphics.Canvas) 561 */ 562 public Canvas lockCanvas(Rect dirty) { 563 if (!isAvailable()) return null; 564 565 if (mCanvas == null) { 566 mCanvas = new Canvas(); 567 } 568 569 synchronized (mNativeWindowLock) { 570 nLockCanvas(mNativeWindow, mCanvas, dirty); 571 } 572 mSaveCount = mCanvas.save(); 573 574 return mCanvas; 575 } 576 577 /** 578 * Finish editing pixels in the surface. After this call, the surface's 579 * current pixels will be shown on the screen, but its content is lost, 580 * in particular there is no guarantee that the content of the Surface 581 * will remain unchanged when lockCanvas() is called again. 582 * 583 * @param canvas The Canvas previously returned by lockCanvas() 584 * 585 * @see #lockCanvas() 586 * @see #lockCanvas(android.graphics.Rect) 587 */ 588 public void unlockCanvasAndPost(Canvas canvas) { 589 if (mCanvas != null && canvas == mCanvas) { 590 canvas.restoreToCount(mSaveCount); 591 mSaveCount = 0; 592 593 synchronized (mNativeWindowLock) { 594 nUnlockCanvasAndPost(mNativeWindow, mCanvas); 595 } 596 } 597 } 598 599 /** 600 * Returns the {@link SurfaceTexture} used by this view. This method 601 * may return null if the view is not attached to a window or if the surface 602 * texture has not been initialized yet. 603 * 604 * @see #isAvailable() 605 */ 606 public SurfaceTexture getSurfaceTexture() { 607 return mSurface; 608 } 609 610 /** 611 * Returns the {@link SurfaceTextureListener} currently associated with this 612 * texture view. 613 * 614 * @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener) 615 * @see SurfaceTextureListener 616 */ 617 public SurfaceTextureListener getSurfaceTextureListener() { 618 return mListener; 619 } 620 621 /** 622 * Sets the {@link SurfaceTextureListener} used to listen to surface 623 * texture events. 624 * 625 * @see #getSurfaceTextureListener() 626 * @see SurfaceTextureListener 627 */ 628 public void setSurfaceTextureListener(SurfaceTextureListener listener) { 629 mListener = listener; 630 } 631 632 /** 633 * This listener can be used to be notified when the surface texture 634 * associated with this texture view is available. 635 */ 636 public static interface SurfaceTextureListener { 637 /** 638 * Invoked when a {@link TextureView}'s SurfaceTexture is ready for use. 639 * 640 * @param surface The surface returned by 641 * {@link android.view.TextureView#getSurfaceTexture()} 642 * @param width The width of the surface 643 * @param height The height of the surface 644 */ 645 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height); 646 647 /** 648 * Invoked when the {@link SurfaceTexture}'s buffers size changed. 649 * 650 * @param surface The surface returned by 651 * {@link android.view.TextureView#getSurfaceTexture()} 652 * @param width The new width of the surface 653 * @param height The new height of the surface 654 */ 655 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height); 656 657 /** 658 * Invoked when the specified {@link SurfaceTexture} is about to be destroyed. 659 * If returns true, no rendering should happen inside the surface texture after this method 660 * is invoked. If returns false, the client needs to call {@link SurfaceTexture#release()}. 661 * 662 * @param surface The surface about to be destroyed 663 */ 664 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface); 665 666 /** 667 * Invoked when the specified {@link SurfaceTexture} is updated through 668 * {@link SurfaceTexture#updateTexImage()}. 669 * 670 * @param surface The surface just updated 671 */ 672 public void onSurfaceTextureUpdated(SurfaceTexture surface); 673 } 674 675 private native void nCreateNativeWindow(SurfaceTexture surface); 676 private native void nDestroyNativeWindow(); 677 678 private static native void nSetDefaultBufferSize(SurfaceTexture surfaceTexture, 679 int width, int height); 680 681 private static native void nLockCanvas(int nativeWindow, Canvas canvas, Rect dirty); 682 private static native void nUnlockCanvasAndPost(int nativeWindow, Canvas canvas); 683} 684