TextureView.java revision d6b2a00dd43257d1498b09175bff63663f6cb861
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 final Runnable mUpdateLayerAction = new Runnable() { 100 @Override 101 public void run() { 102 updateLayer(); 103 } 104 }; 105 private SurfaceTexture.OnFrameAvailableListener mUpdateListener; 106 107 /** 108 * Creates a new TextureView. 109 * 110 * @param context The context to associate this view with. 111 */ 112 public TextureView(Context context) { 113 super(context); 114 init(); 115 } 116 117 /** 118 * Creates a new TextureView. 119 * 120 * @param context The context to associate this view with. 121 * @param attrs The attributes of the XML tag that is inflating the view. 122 */ 123 @SuppressWarnings({"UnusedDeclaration"}) 124 public TextureView(Context context, AttributeSet attrs) { 125 super(context, attrs); 126 init(); 127 } 128 129 /** 130 * Creates a new TextureView. 131 * 132 * @param context The context to associate this view with. 133 * @param attrs The attributes of the XML tag that is inflating the view. 134 * @param defStyle The default style to apply to this view. If 0, no style 135 * will be applied (beyond what is included in the theme). This may 136 * either be an attribute resource, whose value will be retrieved 137 * from the current theme, or an explicit style resource. 138 */ 139 @SuppressWarnings({"UnusedDeclaration"}) 140 public TextureView(Context context, AttributeSet attrs, int defStyle) { 141 super(context, attrs, defStyle); 142 init(); 143 } 144 145 private void init() { 146 mLayerPaint = new Paint(); 147 } 148 149 @Override 150 protected void onAttachedToWindow() { 151 super.onAttachedToWindow(); 152 153 if (!isHardwareAccelerated()) { 154 Log.w(LOG_TAG, "A TextureView or a subclass can only be " 155 + "used with hardware acceleration enabled."); 156 } 157 } 158 159 @Override 160 protected void onDetachedFromWindow() { 161 super.onDetachedFromWindow(); 162 163 if (isHardwareAccelerated() && mLayer != null) { 164 if (mListener != null) { 165 mListener.onSurfaceTextureDestroyed(mSurface); 166 } 167 168 mLayer.destroy(); 169 mSurface = null; 170 mLayer = null; 171 } 172 } 173 174 /** 175 * The layer type of a TextureView is ignored since a TextureView is always 176 * considered to act as a hardware layer. The optional paint supplied to this 177 * method will however be taken into account when rendering the content of 178 * this TextureView. 179 * 180 * @param layerType The ype of layer to use with this view, must be one of 181 * {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or 182 * {@link #LAYER_TYPE_HARDWARE} 183 * @param paint The paint used to compose the layer. This argument is optional 184 * and can be null. It is ignored when the layer type is 185 * {@link #LAYER_TYPE_NONE} 186 */ 187 @Override 188 public void setLayerType(int layerType, Paint paint) { 189 if (paint != mLayerPaint) { 190 mLayerPaint = paint; 191 invalidate(); 192 } 193 } 194 195 /** 196 * Always returns {@link #LAYER_TYPE_HARDWARE}. 197 */ 198 @Override 199 public int getLayerType() { 200 return LAYER_TYPE_HARDWARE; 201 } 202 203 /** 204 * Calling this method has no effect. 205 */ 206 @Override 207 public void buildLayer() { 208 } 209 210 /** 211 * Subclasses of TextureView cannot do their own rendering 212 * with the {@link Canvas} object. 213 * 214 * @param canvas The Canvas to which the View is rendered. 215 */ 216 @Override 217 public final void draw(Canvas canvas) { 218 super.draw(canvas); 219 } 220 221 /** 222 * Subclasses of TextureView cannot do their own rendering 223 * with the {@link Canvas} object. 224 * 225 * @param canvas The Canvas to which the View is rendered. 226 */ 227 @Override 228 protected final void onDraw(Canvas canvas) { 229 } 230 231 @Override 232 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 233 super.onSizeChanged(w, h, oldw, oldh); 234 if (mSurface != null) { 235 nSetDefaultBufferSize(mSurface, getWidth(), getHeight()); 236 if (mListener != null) { 237 mListener.onSurfaceTextureSizeChanged(mSurface, getWidth(), getHeight()); 238 } 239 } 240 } 241 242 @Override 243 HardwareLayer getHardwareLayer() { 244 if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) { 245 return null; 246 } 247 248 if (mLayer == null) { 249 mLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer(); 250 mSurface = mAttachInfo.mHardwareRenderer.createSurfaceTexture(mLayer); 251 nSetDefaultBufferSize(mSurface, getWidth(), getHeight()); 252 253 mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() { 254 @Override 255 public void onFrameAvailable(SurfaceTexture surfaceTexture) { 256 // Per SurfaceTexture's documentation, the callback may be invoked 257 // from an arbitrary thread 258 post(mUpdateLayerAction); 259 } 260 }; 261 mSurface.setOnFrameAvailableListener(mUpdateListener); 262 263 if (mListener != null) { 264 mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight()); 265 } 266 } 267 268 return mLayer; 269 } 270 271 @Override 272 protected void onVisibilityChanged(View changedView, int visibility) { 273 super.onVisibilityChanged(changedView, visibility); 274 275 if (mSurface != null) { 276 // When the view becomes invisible, stop updating it, it's a waste of CPU 277 // To cancel updates, the easiest thing to do is simply to remove the 278 // updates listener 279 if (visibility == VISIBLE) { 280 mSurface.setOnFrameAvailableListener(mUpdateListener); 281 updateLayer(); 282 } else { 283 mSurface.setOnFrameAvailableListener(null); 284 } 285 } 286 } 287 288 private void updateLayer() { 289 if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) { 290 return; 291 } 292 293 mAttachInfo.mHardwareRenderer.updateTextureLayer(mLayer, getWidth(), getHeight()); 294 295 invalidate(); 296 } 297 298 /** 299 * <p>Returns a {@link android.graphics.Bitmap} representation of the content 300 * of the associated surface texture. If the surface texture is not available, 301 * this method returns null.</p> 302 * 303 * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888} 304 * pixel format and its dimensions are the same as this view's.</p> 305 * 306 * <p><strong>Do not</strong> invoke this method from a drawing method 307 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p> 308 * 309 * <p>If an error occurs during the copy, an empty bitmap will be returned.</p> 310 * 311 * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface 312 * texture is not available or the width <= 0 or the height <= 0 313 * 314 * @see #isAvailable() 315 * @see #getBitmap(android.graphics.Bitmap) 316 * @see #getBitmap(int, int) 317 */ 318 public Bitmap getBitmap() { 319 return getBitmap(getWidth(), getHeight()); 320 } 321 322 /** 323 * <p>Returns a {@link android.graphics.Bitmap} representation of the content 324 * of the associated surface texture. If the surface texture is not available, 325 * this method returns null.</p> 326 * 327 * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888} 328 * pixel format.</p> 329 * 330 * <p><strong>Do not</strong> invoke this method from a drawing method 331 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p> 332 * 333 * <p>If an error occurs during the copy, an empty bitmap will be returned.</p> 334 * 335 * @param width The width of the bitmap to create 336 * @param height The height of the bitmap to create 337 * 338 * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface 339 * texture is not available or width is <= 0 or height is <= 0 340 * 341 * @see #isAvailable() 342 * @see #getBitmap(android.graphics.Bitmap) 343 * @see #getBitmap() 344 */ 345 public Bitmap getBitmap(int width, int height) { 346 if (isAvailable() && width > 0 && height > 0) { 347 return getBitmap(Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)); 348 } 349 return null; 350 } 351 352 /** 353 * <p>Copies the content of this view's surface texture into the specified 354 * bitmap. If the surface texture is not available, the copy is not executed. 355 * The content of the surface texture will be scaled to fit exactly inside 356 * the specified bitmap.</p> 357 * 358 * <p><strong>Do not</strong> invoke this method from a drawing method 359 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p> 360 * 361 * <p>If an error occurs, the bitmap is left unchanged.</p> 362 * 363 * @param bitmap The bitmap to copy the content of the surface texture into, 364 * cannot be null, all configurations are supported 365 * 366 * @return The bitmap specified as a parameter 367 * 368 * @see #isAvailable() 369 * @see #getBitmap(int, int) 370 * @see #getBitmap() 371 */ 372 public Bitmap getBitmap(Bitmap bitmap) { 373 if (bitmap != null && isAvailable()) { 374 mAttachInfo.mHardwareRenderer.copyLayer(mLayer, bitmap); 375 } 376 return bitmap; 377 } 378 379 /** 380 * Returns true if the {@link SurfaceTexture} associated with this 381 * TextureView is available for rendering. When this method returns 382 * true, {@link #getSurfaceTexture()} returns a valid surface texture. 383 */ 384 public boolean isAvailable() { 385 return mSurface != null; 386 } 387 388 /** 389 * Returns the {@link SurfaceTexture} used by this view. This method 390 * may return null if the view is not attached to a window or if the surface 391 * texture has not been initialized yet. 392 * 393 * @see #isAvailable() 394 */ 395 public SurfaceTexture getSurfaceTexture() { 396 return mSurface; 397 } 398 399 /** 400 * Returns the {@link SurfaceTextureListener} currently associated with this 401 * texture view. 402 * 403 * @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener) 404 * @see SurfaceTextureListener 405 */ 406 public SurfaceTextureListener getSurfaceTextureListener() { 407 return mListener; 408 } 409 410 /** 411 * Sets the {@link SurfaceTextureListener} used to listen to surface 412 * texture events. 413 * 414 * @see #getSurfaceTextureListener() 415 * @see SurfaceTextureListener 416 */ 417 public void setSurfaceTextureListener(SurfaceTextureListener listener) { 418 mListener = listener; 419 } 420 421 /** 422 * This listener can be used to be notified when the surface texture 423 * associated with this texture view is available. 424 */ 425 public static interface SurfaceTextureListener { 426 /** 427 * Invoked when a {@link TextureView}'s SurfaceTexture is ready for use. 428 * 429 * @param surface The surface returned by 430 * {@link android.view.TextureView#getSurfaceTexture()} 431 * @param width The width of the surface 432 * @param height The height of the surface 433 */ 434 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height); 435 436 /** 437 * Invoked when the {@link SurfaceTexture}'s buffers size changed. 438 * 439 * @param surface The surface returned by 440 * {@link android.view.TextureView#getSurfaceTexture()} 441 * @param width The new width of the surface 442 * @param height The new height of the surface 443 */ 444 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height); 445 446 /** 447 * Invoked when the specified {@link SurfaceTexture} is about to be destroyed. 448 * After this method is invoked, no rendering should happen inside the surface 449 * texture. 450 * 451 * @param surface The surface about to be destroyed 452 */ 453 public void onSurfaceTextureDestroyed(SurfaceTexture surface); 454 } 455 456 private static native void nSetDefaultBufferSize(SurfaceTexture surfaceTexture, 457 int width, int height); 458} 459