TextureView.java revision a9489274d67b540804aafb587a226f7c2ae4464d
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.Paint; 23import android.graphics.SurfaceTexture; 24import android.util.AttributeSet; 25import android.util.Log; 26 27/** 28 * <p>A TextureView can be used to display a content stream. Such a content 29 * stream can for instance be a video or an OpenGL scene. The content stream 30 * can come from the application's process as well as a remote process.</p> 31 * 32 * <p>TextureView can only be used in a hardware accelerated window. When 33 * rendered in software, TextureView will draw nothing.</p> 34 * 35 * <p>Unlike {@link SurfaceView}, TextureView does not create a separate 36 * window but behaves as a regular View. This key difference allows a 37 * TextureView to be moved, transformed, animated, etc. For instance, you 38 * can make a TextureView semi-translucent by calling 39 * <code>myView.setAlpha(0.5f)</code>.</p> 40 * 41 * <p>Using a TextureView is simple: all you need to do is get its 42 * {@link SurfaceTexture}. The {@link SurfaceTexture} can then be used to 43 * render content. The following example demonstrates how to render the 44 * camera preview into a TextureView:</p> 45 * 46 * <pre> 47 * public class LiveCameraActivity extends Activity implements TextureView.SurfaceTextureListener { 48 * private Camera mCamera; 49 * private TextureView mTextureView; 50 * 51 * protected void onCreate(Bundle savedInstanceState) { 52 * super.onCreate(savedInstanceState); 53 * 54 * mTextureView = new TextureView(this); 55 * mTextureView.setSurfaceTextureListener(this); 56 * 57 * setContentView(mTextureView); 58 * } 59 * 60 * public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 61 * mCamera = Camera.open(); 62 * 63 * try { 64 * mCamera.setPreviewTexture(surface); 65 * mCamera.startPreview(); 66 * } catch (IOException ioe) { 67 * // Something bad happened 68 * } 69 * } 70 * 71 * public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 72 * // Ignored, Camera does all the work for us 73 * } 74 * 75 * public void onSurfaceTextureDestroyed(SurfaceTexture surface) { 76 * mCamera.stopPreview(); 77 * mCamera.release(); 78 * } 79 * } 80 * </pre> 81 * 82 * <p>A TextureView's SurfaceTexture can be obtained either by invoking 83 * {@link #getSurfaceTexture()} or by using a {@link SurfaceTextureListener}. 84 * It is important to know that a SurfaceTexture is available only after the 85 * TextureView is attached to a window (and {@link #onAttachedToWindow()} has 86 * been invoked.) It is therefore highly recommended you use a listener to 87 * be notified when the SurfaceTexture becomes available.</p> 88 * 89 * @see SurfaceView 90 * @see SurfaceTexture 91 */ 92public class TextureView extends View { 93 private static final String LOG_TAG = "TextureView"; 94 95 private HardwareLayer mLayer; 96 private SurfaceTexture mSurface; 97 private SurfaceTextureListener mListener; 98 99 private boolean mOpaque = true; 100 101 private final Runnable mUpdateLayerAction = new Runnable() { 102 @Override 103 public void run() { 104 updateLayer(); 105 } 106 }; 107 private SurfaceTexture.OnFrameAvailableListener mUpdateListener; 108 109 /** 110 * Creates a new TextureView. 111 * 112 * @param context The context to associate this view with. 113 */ 114 public TextureView(Context context) { 115 super(context); 116 init(); 117 } 118 119 /** 120 * Creates a new TextureView. 121 * 122 * @param context The context to associate this view with. 123 * @param attrs The attributes of the XML tag that is inflating the view. 124 */ 125 @SuppressWarnings({"UnusedDeclaration"}) 126 public TextureView(Context context, AttributeSet attrs) { 127 super(context, attrs); 128 init(); 129 } 130 131 /** 132 * Creates a new TextureView. 133 * 134 * @param context The context to associate this view with. 135 * @param attrs The attributes of the XML tag that is inflating the view. 136 * @param defStyle The default style to apply to this view. If 0, no style 137 * will be applied (beyond what is included in the theme). This may 138 * either be an attribute resource, whose value will be retrieved 139 * from the current theme, or an explicit style resource. 140 */ 141 @SuppressWarnings({"UnusedDeclaration"}) 142 public TextureView(Context context, AttributeSet attrs, int defStyle) { 143 super(context, attrs, defStyle); 144 init(); 145 } 146 147 private void init() { 148 mLayerPaint = new Paint(); 149 } 150 151 /** 152 * {@inheritDoc} 153 */ 154 @Override 155 public boolean isOpaque() { 156 return mOpaque; 157 } 158 159 /** 160 * Indicates whether the content of this TextureView is opaque. The 161 * content is assumed to be opaque by default. 162 * 163 * @param opaque True if the content of this TextureView is opaque, 164 * false otherwise 165 */ 166 public void setOpaque(boolean opaque) { 167 if (opaque != mOpaque) { 168 mOpaque = opaque; 169 updateLayer(); 170 } 171 } 172 173 @Override 174 protected void onAttachedToWindow() { 175 super.onAttachedToWindow(); 176 177 if (!isHardwareAccelerated()) { 178 Log.w(LOG_TAG, "A TextureView or a subclass can only be " 179 + "used with hardware acceleration enabled."); 180 } 181 } 182 183 @Override 184 protected void onDetachedFromWindow() { 185 super.onDetachedFromWindow(); 186 187 if (isHardwareAccelerated() && mLayer != null) { 188 if (mListener != null) { 189 mListener.onSurfaceTextureDestroyed(mSurface); 190 } 191 192 mLayer.destroy(); 193 mSurface = null; 194 mLayer = null; 195 } 196 } 197 198 /** 199 * The layer type of a TextureView is ignored since a TextureView is always 200 * considered to act as a hardware layer. The optional paint supplied to this 201 * method will however be taken into account when rendering the content of 202 * this TextureView. 203 * 204 * @param layerType The ype of layer to use with this view, must be one of 205 * {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or 206 * {@link #LAYER_TYPE_HARDWARE} 207 * @param paint The paint used to compose the layer. This argument is optional 208 * and can be null. It is ignored when the layer type is 209 * {@link #LAYER_TYPE_NONE} 210 */ 211 @Override 212 public void setLayerType(int layerType, Paint paint) { 213 if (paint != mLayerPaint) { 214 mLayerPaint = paint; 215 invalidate(); 216 } 217 } 218 219 /** 220 * Always returns {@link #LAYER_TYPE_HARDWARE}. 221 */ 222 @Override 223 public int getLayerType() { 224 return LAYER_TYPE_HARDWARE; 225 } 226 227 /** 228 * Calling this method has no effect. 229 */ 230 @Override 231 public void buildLayer() { 232 } 233 234 /** 235 * Subclasses of TextureView cannot do their own rendering 236 * with the {@link Canvas} object. 237 * 238 * @param canvas The Canvas to which the View is rendered. 239 */ 240 @Override 241 public final void draw(Canvas canvas) { 242 } 243 244 /** 245 * Subclasses of TextureView cannot do their own rendering 246 * with the {@link Canvas} object. 247 * 248 * @param canvas The Canvas to which the View is rendered. 249 */ 250 @Override 251 protected final void onDraw(Canvas canvas) { 252 } 253 254 @Override 255 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 256 super.onSizeChanged(w, h, oldw, oldh); 257 if (mSurface != null) { 258 nSetDefaultBufferSize(mSurface, getWidth(), getHeight()); 259 if (mListener != null) { 260 mListener.onSurfaceTextureSizeChanged(mSurface, getWidth(), getHeight()); 261 } 262 } 263 } 264 265 @Override 266 HardwareLayer getHardwareLayer() { 267 if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) { 268 return null; 269 } 270 271 if (mLayer == null) { 272 mLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer(mOpaque); 273 mSurface = mAttachInfo.mHardwareRenderer.createSurfaceTexture(mLayer); 274 nSetDefaultBufferSize(mSurface, getWidth(), getHeight()); 275 276 mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() { 277 @Override 278 public void onFrameAvailable(SurfaceTexture surfaceTexture) { 279 // Per SurfaceTexture's documentation, the callback may be invoked 280 // from an arbitrary thread 281 post(mUpdateLayerAction); 282 } 283 }; 284 mSurface.setOnFrameAvailableListener(mUpdateListener); 285 286 if (mListener != null) { 287 mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight()); 288 } 289 } 290 291 return mLayer; 292 } 293 294 @Override 295 protected void onVisibilityChanged(View changedView, int visibility) { 296 super.onVisibilityChanged(changedView, visibility); 297 298 if (mSurface != null) { 299 // When the view becomes invisible, stop updating it, it's a waste of CPU 300 // To cancel updates, the easiest thing to do is simply to remove the 301 // updates listener 302 if (visibility == VISIBLE) { 303 mSurface.setOnFrameAvailableListener(mUpdateListener); 304 updateLayer(); 305 } else { 306 mSurface.setOnFrameAvailableListener(null); 307 } 308 } 309 } 310 311 private void updateLayer() { 312 if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) { 313 return; 314 } 315 316 mAttachInfo.mHardwareRenderer.updateTextureLayer(mLayer, getWidth(), getHeight(), mOpaque); 317 318 invalidate(); 319 } 320 321 /** 322 * <p>Returns a {@link android.graphics.Bitmap} representation of the content 323 * of the associated surface texture. If the surface texture is not available, 324 * this method returns null.</p> 325 * 326 * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888} 327 * pixel format and its dimensions are the same as this view's.</p> 328 * 329 * <p><strong>Do not</strong> invoke this method from a drawing method 330 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p> 331 * 332 * <p>If an error occurs during the copy, an empty bitmap will be returned.</p> 333 * 334 * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface 335 * texture is not available or the width <= 0 or the height <= 0 336 * 337 * @see #isAvailable() 338 * @see #getBitmap(android.graphics.Bitmap) 339 * @see #getBitmap(int, int) 340 */ 341 public Bitmap getBitmap() { 342 return getBitmap(getWidth(), getHeight()); 343 } 344 345 /** 346 * <p>Returns a {@link android.graphics.Bitmap} representation of the content 347 * of the associated surface texture. If the surface texture is not available, 348 * this method returns null.</p> 349 * 350 * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888} 351 * pixel format.</p> 352 * 353 * <p><strong>Do not</strong> invoke this method from a drawing method 354 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p> 355 * 356 * <p>If an error occurs during the copy, an empty bitmap will be returned.</p> 357 * 358 * @param width The width of the bitmap to create 359 * @param height The height of the bitmap to create 360 * 361 * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface 362 * texture is not available or width is <= 0 or height is <= 0 363 * 364 * @see #isAvailable() 365 * @see #getBitmap(android.graphics.Bitmap) 366 * @see #getBitmap() 367 */ 368 public Bitmap getBitmap(int width, int height) { 369 if (isAvailable() && width > 0 && height > 0) { 370 return getBitmap(Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)); 371 } 372 return null; 373 } 374 375 /** 376 * <p>Copies the content of this view's surface texture into the specified 377 * bitmap. If the surface texture is not available, the copy is not executed. 378 * The content of the surface texture will be scaled to fit exactly inside 379 * the specified bitmap.</p> 380 * 381 * <p><strong>Do not</strong> invoke this method from a drawing method 382 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p> 383 * 384 * <p>If an error occurs, the bitmap is left unchanged.</p> 385 * 386 * @param bitmap The bitmap to copy the content of the surface texture into, 387 * cannot be null, all configurations are supported 388 * 389 * @return The bitmap specified as a parameter 390 * 391 * @see #isAvailable() 392 * @see #getBitmap(int, int) 393 * @see #getBitmap() 394 */ 395 public Bitmap getBitmap(Bitmap bitmap) { 396 if (bitmap != null && isAvailable()) { 397 mAttachInfo.mHardwareRenderer.copyLayer(mLayer, bitmap); 398 } 399 return bitmap; 400 } 401 402 /** 403 * Returns true if the {@link SurfaceTexture} associated with this 404 * TextureView is available for rendering. When this method returns 405 * true, {@link #getSurfaceTexture()} returns a valid surface texture. 406 */ 407 public boolean isAvailable() { 408 return mSurface != null; 409 } 410 411 /** 412 * Returns the {@link SurfaceTexture} used by this view. This method 413 * may return null if the view is not attached to a window or if the surface 414 * texture has not been initialized yet. 415 * 416 * @see #isAvailable() 417 */ 418 public SurfaceTexture getSurfaceTexture() { 419 return mSurface; 420 } 421 422 /** 423 * Returns the {@link SurfaceTextureListener} currently associated with this 424 * texture view. 425 * 426 * @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener) 427 * @see SurfaceTextureListener 428 */ 429 public SurfaceTextureListener getSurfaceTextureListener() { 430 return mListener; 431 } 432 433 /** 434 * Sets the {@link SurfaceTextureListener} used to listen to surface 435 * texture events. 436 * 437 * @see #getSurfaceTextureListener() 438 * @see SurfaceTextureListener 439 */ 440 public void setSurfaceTextureListener(SurfaceTextureListener listener) { 441 mListener = listener; 442 } 443 444 /** 445 * This listener can be used to be notified when the surface texture 446 * associated with this texture view is available. 447 */ 448 public static interface SurfaceTextureListener { 449 /** 450 * Invoked when a {@link TextureView}'s SurfaceTexture is ready for use. 451 * 452 * @param surface The surface returned by 453 * {@link android.view.TextureView#getSurfaceTexture()} 454 * @param width The width of the surface 455 * @param height The height of the surface 456 */ 457 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height); 458 459 /** 460 * Invoked when the {@link SurfaceTexture}'s buffers size changed. 461 * 462 * @param surface The surface returned by 463 * {@link android.view.TextureView#getSurfaceTexture()} 464 * @param width The new width of the surface 465 * @param height The new height of the surface 466 */ 467 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height); 468 469 /** 470 * Invoked when the specified {@link SurfaceTexture} is about to be destroyed. 471 * After this method is invoked, no rendering should happen inside the surface 472 * texture. 473 * 474 * @param surface The surface about to be destroyed 475 */ 476 public void onSurfaceTextureDestroyed(SurfaceTexture surface); 477 } 478 479 private static native void nSetDefaultBufferSize(SurfaceTexture surfaceTexture, 480 int width, int height); 481} 482