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