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 17 package android.view; 18 19 import android.content.Context; 20 import android.graphics.Bitmap; 21 import android.graphics.Canvas; 22 import android.graphics.Matrix; 23 import android.graphics.Paint; 24 import android.graphics.Rect; 25 import android.graphics.SurfaceTexture; 26 import android.util.AttributeSet; 27 import 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 */ 104 public 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 */ TextureView(Context context)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"}) TextureView(Context context, AttributeSet attrs)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"}) TextureView(Context context, AttributeSet attrs, int defStyle)162 public TextureView(Context context, AttributeSet attrs, int defStyle) { 163 super(context, attrs, defStyle); 164 init(); 165 } 166 init()167 private void init() { 168 mLayerPaint = new Paint(); 169 } 170 171 /** 172 * {@inheritDoc} 173 */ 174 @Override isOpaque()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 */ setOpaque(boolean opaque)186 public void setOpaque(boolean opaque) { 187 if (opaque != mOpaque) { 188 mOpaque = opaque; 189 updateLayer(); 190 } 191 } 192 193 @Override onAttachedToWindow()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 onDetachedFromWindow()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 setLayerType(int layerType, Paint paint)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 getLayerType()249 public int getLayerType() { 250 return LAYER_TYPE_HARDWARE; 251 } 252 253 @Override hasStaticLayer()254 boolean hasStaticLayer() { 255 return true; 256 } 257 258 /** 259 * Calling this method has no effect. 260 */ 261 @Override buildLayer()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 draw(Canvas canvas)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 onDraw(Canvas canvas)284 protected final void onDraw(Canvas canvas) { 285 } 286 287 @Override onSizeChanged(int w, int h, int oldw, int oldh)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 destroyLayer()299 boolean destroyLayer() { 300 return false; 301 } 302 303 @Override getHardwareLayer()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 onVisibilityChanged(View changedView, int visibility)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 updateLayer()356 private void updateLayer() { 357 mUpdateLayer = true; 358 invalidate(); 359 } 360 applyUpdate()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 */ setTransform(Matrix transform)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 */ getTransform(Matrix transform)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 applyTransformMatrix()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 */ getBitmap()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 */ getBitmap(int width, int height)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 * @throws IllegalStateException if the hardware rendering context cannot be 507 * acquired to capture the bitmap 508 */ getBitmap(Bitmap bitmap)509 public Bitmap getBitmap(Bitmap bitmap) { 510 if (bitmap != null && isAvailable()) { 511 AttachInfo info = mAttachInfo; 512 if (info != null && info.mHardwareRenderer != null && 513 info.mHardwareRenderer.isEnabled()) { 514 if (!info.mHardwareRenderer.validate()) { 515 throw new IllegalStateException("Could not acquire hardware rendering context"); 516 } 517 } 518 519 applyUpdate(); 520 applyTransformMatrix(); 521 522 mLayer.copyInto(bitmap); 523 } 524 return bitmap; 525 } 526 527 /** 528 * Returns true if the {@link SurfaceTexture} associated with this 529 * TextureView is available for rendering. When this method returns 530 * true, {@link #getSurfaceTexture()} returns a valid surface texture. 531 */ isAvailable()532 public boolean isAvailable() { 533 return mSurface != null; 534 } 535 536 /** 537 * <p>Start editing the pixels in the surface. The returned Canvas can be used 538 * to draw into the surface's bitmap. A null is returned if the surface has 539 * not been created or otherwise cannot be edited. You will usually need 540 * to implement 541 * {@link SurfaceTextureListener#onSurfaceTextureAvailable(android.graphics.SurfaceTexture, int, int)} 542 * to find out when the Surface is available for use.</p> 543 * 544 * <p>The content of the Surface is never preserved between unlockCanvas() 545 * and lockCanvas(), for this reason, every pixel within the Surface area 546 * must be written. The only exception to this rule is when a dirty 547 * rectangle is specified, in which case, non-dirty pixels will be 548 * preserved.</p> 549 * 550 * <p>This method can only be used if the underlying surface is not already 551 * owned by another producer. For instance, if the TextureView is being used 552 * to render the camera's preview you cannot invoke this method.</p> 553 * 554 * @return A Canvas used to draw into the surface. 555 * 556 * @see #lockCanvas(android.graphics.Rect) 557 * @see #unlockCanvasAndPost(android.graphics.Canvas) 558 */ lockCanvas()559 public Canvas lockCanvas() { 560 return lockCanvas(null); 561 } 562 563 /** 564 * Just like {@link #lockCanvas()} but allows specification of a dirty 565 * rectangle. Every pixel within that rectangle must be written; however 566 * pixels outside the dirty rectangle will be preserved by the next call 567 * to lockCanvas(). 568 * 569 * @param dirty Area of the surface that will be modified. 570 571 * @return A Canvas used to draw into the surface. 572 * 573 * @see #lockCanvas() 574 * @see #unlockCanvasAndPost(android.graphics.Canvas) 575 */ lockCanvas(Rect dirty)576 public Canvas lockCanvas(Rect dirty) { 577 if (!isAvailable()) return null; 578 579 if (mCanvas == null) { 580 mCanvas = new Canvas(); 581 } 582 583 synchronized (mNativeWindowLock) { 584 nLockCanvas(mNativeWindow, mCanvas, dirty); 585 } 586 mSaveCount = mCanvas.save(); 587 588 return mCanvas; 589 } 590 591 /** 592 * Finish editing pixels in the surface. After this call, the surface's 593 * current pixels will be shown on the screen, but its content is lost, 594 * in particular there is no guarantee that the content of the Surface 595 * will remain unchanged when lockCanvas() is called again. 596 * 597 * @param canvas The Canvas previously returned by lockCanvas() 598 * 599 * @see #lockCanvas() 600 * @see #lockCanvas(android.graphics.Rect) 601 */ unlockCanvasAndPost(Canvas canvas)602 public void unlockCanvasAndPost(Canvas canvas) { 603 if (mCanvas != null && canvas == mCanvas) { 604 canvas.restoreToCount(mSaveCount); 605 mSaveCount = 0; 606 607 synchronized (mNativeWindowLock) { 608 nUnlockCanvasAndPost(mNativeWindow, mCanvas); 609 } 610 } 611 } 612 613 /** 614 * Returns the {@link SurfaceTexture} used by this view. This method 615 * may return null if the view is not attached to a window or if the surface 616 * texture has not been initialized yet. 617 * 618 * @see #isAvailable() 619 */ getSurfaceTexture()620 public SurfaceTexture getSurfaceTexture() { 621 return mSurface; 622 } 623 624 /** 625 * Returns the {@link SurfaceTextureListener} currently associated with this 626 * texture view. 627 * 628 * @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener) 629 * @see SurfaceTextureListener 630 */ getSurfaceTextureListener()631 public SurfaceTextureListener getSurfaceTextureListener() { 632 return mListener; 633 } 634 635 /** 636 * Sets the {@link SurfaceTextureListener} used to listen to surface 637 * texture events. 638 * 639 * @see #getSurfaceTextureListener() 640 * @see SurfaceTextureListener 641 */ setSurfaceTextureListener(SurfaceTextureListener listener)642 public void setSurfaceTextureListener(SurfaceTextureListener listener) { 643 mListener = listener; 644 } 645 646 /** 647 * This listener can be used to be notified when the surface texture 648 * associated with this texture view is available. 649 */ 650 public static interface SurfaceTextureListener { 651 /** 652 * Invoked when a {@link TextureView}'s SurfaceTexture is ready for use. 653 * 654 * @param surface The surface returned by 655 * {@link android.view.TextureView#getSurfaceTexture()} 656 * @param width The width of the surface 657 * @param height The height of the surface 658 */ onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)659 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height); 660 661 /** 662 * Invoked when the {@link SurfaceTexture}'s buffers size changed. 663 * 664 * @param surface The surface returned by 665 * {@link android.view.TextureView#getSurfaceTexture()} 666 * @param width The new width of the surface 667 * @param height The new height of the surface 668 */ onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)669 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height); 670 671 /** 672 * Invoked when the specified {@link SurfaceTexture} is about to be destroyed. 673 * If returns true, no rendering should happen inside the surface texture after this method 674 * is invoked. If returns false, the client needs to call {@link SurfaceTexture#release()}. 675 * 676 * @param surface The surface about to be destroyed 677 */ onSurfaceTextureDestroyed(SurfaceTexture surface)678 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface); 679 680 /** 681 * Invoked when the specified {@link SurfaceTexture} is updated through 682 * {@link SurfaceTexture#updateTexImage()}. 683 * 684 * @param surface The surface just updated 685 */ onSurfaceTextureUpdated(SurfaceTexture surface)686 public void onSurfaceTextureUpdated(SurfaceTexture surface); 687 } 688 nCreateNativeWindow(SurfaceTexture surface)689 private native void nCreateNativeWindow(SurfaceTexture surface); nDestroyNativeWindow()690 private native void nDestroyNativeWindow(); 691 nSetDefaultBufferSize(SurfaceTexture surfaceTexture, int width, int height)692 private static native void nSetDefaultBufferSize(SurfaceTexture surfaceTexture, 693 int width, int height); 694 nLockCanvas(int nativeWindow, Canvas canvas, Rect dirty)695 private static native void nLockCanvas(int nativeWindow, Canvas canvas, Rect dirty); nUnlockCanvasAndPost(int nativeWindow, Canvas canvas)696 private static native void nUnlockCanvasAndPost(int nativeWindow, Canvas canvas); 697 } 698