blob: 230f426626a0b4fcc3967e6bbb17d4dc2af7b2ea [file] [log] [blame]
Romain Guyaa6c24c2011-04-28 18:40:04 -07001/*
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;
Romain Guy77a81162011-06-14 16:45:55 -070020import android.graphics.Bitmap;
Romain Guyaa6c24c2011-04-28 18:40:04 -070021import android.graphics.Canvas;
Romain Guy302a9df2011-08-16 13:55:02 -070022import android.graphics.Matrix;
Romain Guyaa6c24c2011-04-28 18:40:04 -070023import android.graphics.Paint;
Romain Guy6be3d552011-07-14 18:08:37 -070024import android.graphics.Rect;
Romain Guyaa6c24c2011-04-28 18:40:04 -070025import android.graphics.SurfaceTexture;
Romain Guy52c145f2012-05-17 18:06:23 -070026import android.os.Looper;
Romain Guyaa6c24c2011-04-28 18:40:04 -070027import android.util.AttributeSet;
28import android.util.Log;
29
30/**
31 * <p>A TextureView can be used to display a content stream. Such a content
32 * stream can for instance be a video or an OpenGL scene. The content stream
33 * can come from the application's process as well as a remote process.</p>
34 *
35 * <p>TextureView can only be used in a hardware accelerated window. When
36 * rendered in software, TextureView will draw nothing.</p>
37 *
38 * <p>Unlike {@link SurfaceView}, TextureView does not create a separate
39 * window but behaves as a regular View. This key difference allows a
40 * TextureView to be moved, transformed, animated, etc. For instance, you
41 * can make a TextureView semi-translucent by calling
42 * <code>myView.setAlpha(0.5f)</code>.</p>
43 *
44 * <p>Using a TextureView is simple: all you need to do is get its
45 * {@link SurfaceTexture}. The {@link SurfaceTexture} can then be used to
46 * render content. The following example demonstrates how to render the
47 * camera preview into a TextureView:</p>
48 *
49 * <pre>
50 * public class LiveCameraActivity extends Activity implements TextureView.SurfaceTextureListener {
51 * private Camera mCamera;
52 * private TextureView mTextureView;
53 *
Romain Guyaa6c24c2011-04-28 18:40:04 -070054 * protected void onCreate(Bundle savedInstanceState) {
55 * super.onCreate(savedInstanceState);
56 *
57 * mTextureView = new TextureView(this);
58 * mTextureView.setSurfaceTextureListener(this);
59 *
60 * setContentView(mTextureView);
61 * }
62 *
Romain Guy451ce442011-06-10 15:40:36 -070063 * public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Romain Guyaa6c24c2011-04-28 18:40:04 -070064 * mCamera = Camera.open();
65 *
66 * try {
67 * mCamera.setPreviewTexture(surface);
68 * mCamera.startPreview();
69 * } catch (IOException ioe) {
70 * // Something bad happened
71 * }
72 * }
Grace Klobacf559372011-06-22 23:05:40 -070073 *
Romain Guy8f0095c2011-05-02 17:24:22 -070074 * public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
75 * // Ignored, Camera does all the work for us
76 * }
Grace Klobacf559372011-06-22 23:05:40 -070077 *
Grace Kloba402f0552011-08-09 18:47:17 -070078 * public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
Romain Guy451ce442011-06-10 15:40:36 -070079 * mCamera.stopPreview();
80 * mCamera.release();
Grace Kloba402f0552011-08-09 18:47:17 -070081 * return true;
Romain Guy451ce442011-06-10 15:40:36 -070082 * }
Grace Klobacf559372011-06-22 23:05:40 -070083 *
84 * public void onSurfaceTextureUpdated(SurfaceTexture surface) {
Romain Guy58f4edb2011-06-24 14:51:38 -070085 * // Invoked every time there's a new Camera preview frame
Grace Klobacf559372011-06-22 23:05:40 -070086 * }
Romain Guyaa6c24c2011-04-28 18:40:04 -070087 * }
88 * </pre>
89 *
90 * <p>A TextureView's SurfaceTexture can be obtained either by invoking
91 * {@link #getSurfaceTexture()} or by using a {@link SurfaceTextureListener}.
92 * It is important to know that a SurfaceTexture is available only after the
93 * TextureView is attached to a window (and {@link #onAttachedToWindow()} has
94 * been invoked.) It is therefore highly recommended you use a listener to
95 * be notified when the SurfaceTexture becomes available.</p>
96 *
Romain Guy462785f2011-09-27 17:42:10 -070097 * <p>It is important to note that only one producer can use the TextureView.
98 * For instance, if you use a TextureView to display the camera preview, you
99 * cannot use {@link #lockCanvas()} to draw onto the TextureView at the same
100 * time.</p>
101 *
Romain Guyaa6c24c2011-04-28 18:40:04 -0700102 * @see SurfaceView
103 * @see SurfaceTexture
104 */
105public class TextureView extends View {
Romain Guy77a81162011-06-14 16:45:55 -0700106 private static final String LOG_TAG = "TextureView";
107
Romain Guyaa6c24c2011-04-28 18:40:04 -0700108 private HardwareLayer mLayer;
109 private SurfaceTexture mSurface;
110 private SurfaceTextureListener mListener;
Romain Guyaa6c24c2011-04-28 18:40:04 -0700111
Romain Guya9489272011-06-22 20:58:11 -0700112 private boolean mOpaque = true;
Romain Guyc989d862011-06-22 14:53:39 -0700113
Romain Guy302a9df2011-08-16 13:55:02 -0700114 private final Matrix mMatrix = new Matrix();
115 private boolean mMatrixChanged;
116
Romain Guy58f4edb2011-06-24 14:51:38 -0700117 private final Object[] mLock = new Object[0];
118 private boolean mUpdateLayer;
Jamie Gennis2af35242012-04-05 11:44:30 -0700119 private boolean mUpdateSurface;
Romain Guy58f4edb2011-06-24 14:51:38 -0700120
Romain Guy8f0095c2011-05-02 17:24:22 -0700121 private SurfaceTexture.OnFrameAvailableListener mUpdateListener;
Romain Guyaa6c24c2011-04-28 18:40:04 -0700122
Romain Guy6be3d552011-07-14 18:08:37 -0700123 private Canvas mCanvas;
124 private int mSaveCount;
125
126 private final Object[] mNativeWindowLock = new Object[0];
127 // Used from native code, do not write!
128 @SuppressWarnings({"UnusedDeclaration"})
129 private int mNativeWindow;
130
Romain Guyaa6c24c2011-04-28 18:40:04 -0700131 /**
132 * Creates a new TextureView.
133 *
134 * @param context The context to associate this view with.
135 */
136 public TextureView(Context context) {
137 super(context);
138 init();
139 }
140
141 /**
142 * Creates a new TextureView.
143 *
144 * @param context The context to associate this view with.
145 * @param attrs The attributes of the XML tag that is inflating the view.
146 */
147 @SuppressWarnings({"UnusedDeclaration"})
148 public TextureView(Context context, AttributeSet attrs) {
149 super(context, attrs);
150 init();
151 }
152
153 /**
154 * Creates a new TextureView.
155 *
156 * @param context The context to associate this view with.
157 * @param attrs The attributes of the XML tag that is inflating the view.
158 * @param defStyle The default style to apply to this view. If 0, no style
159 * will be applied (beyond what is included in the theme). This may
160 * either be an attribute resource, whose value will be retrieved
161 * from the current theme, or an explicit style resource.
162 */
163 @SuppressWarnings({"UnusedDeclaration"})
164 public TextureView(Context context, AttributeSet attrs, int defStyle) {
165 super(context, attrs, defStyle);
166 init();
167 }
168
169 private void init() {
170 mLayerPaint = new Paint();
171 }
172
Romain Guya9489272011-06-22 20:58:11 -0700173 /**
174 * {@inheritDoc}
175 */
176 @Override
177 public boolean isOpaque() {
178 return mOpaque;
179 }
180
181 /**
182 * Indicates whether the content of this TextureView is opaque. The
183 * content is assumed to be opaque by default.
184 *
185 * @param opaque True if the content of this TextureView is opaque,
186 * false otherwise
187 */
188 public void setOpaque(boolean opaque) {
189 if (opaque != mOpaque) {
190 mOpaque = opaque;
Romain Guya8a2f972012-04-12 16:33:44 -0700191 if (mLayer != null) {
Romain Guy88801b22012-10-05 14:58:33 -0700192 updateLayerAndInvalidate();
Romain Guya8a2f972012-04-12 16:33:44 -0700193 }
Romain Guya9489272011-06-22 20:58:11 -0700194 }
195 }
196
Romain Guyaa6c24c2011-04-28 18:40:04 -0700197 @Override
198 protected void onAttachedToWindow() {
199 super.onAttachedToWindow();
200
201 if (!isHardwareAccelerated()) {
Romain Guy77a81162011-06-14 16:45:55 -0700202 Log.w(LOG_TAG, "A TextureView or a subclass can only be "
Romain Guyaa6c24c2011-04-28 18:40:04 -0700203 + "used with hardware acceleration enabled.");
204 }
205 }
206
Romain Guy451ce442011-06-10 15:40:36 -0700207 @Override
208 protected void onDetachedFromWindow() {
209 super.onDetachedFromWindow();
Romain Guy1ac47652012-04-11 18:15:20 -0700210 if (mLayer != null && mAttachInfo != null && mAttachInfo.mHardwareRenderer != null) {
211 boolean success = mAttachInfo.mHardwareRenderer.safelyRun(new Runnable() {
212 @Override
213 public void run() {
214 destroySurface();
215 }
216 });
217
218 if (!success) {
219 Log.w(LOG_TAG, "TextureView was not able to destroy its surface: " + this);
220 }
221 }
Romain Guy31f2c2e2011-11-21 10:55:41 -0800222 }
Romain Guy451ce442011-06-10 15:40:36 -0700223
Romain Guy31f2c2e2011-11-21 10:55:41 -0800224 private void destroySurface() {
Romain Guy80429c42011-06-24 17:20:32 -0700225 if (mLayer != null) {
Jamie Gennis2af35242012-04-05 11:44:30 -0700226 mSurface.detachFromGLContext();
Romain Guy52b307e2012-10-07 17:55:17 -0700227 // SurfaceTexture owns the texture name and detachFromGLContext
228 // should have deleted it
Romain Guyef09a212012-09-25 12:17:14 -0700229 mLayer.clearStorage();
Jamie Gennis2af35242012-04-05 11:44:30 -0700230
Grace Kloba402f0552011-08-09 18:47:17 -0700231 boolean shouldRelease = true;
Romain Guy451ce442011-06-10 15:40:36 -0700232 if (mListener != null) {
Grace Kloba402f0552011-08-09 18:47:17 -0700233 shouldRelease = mListener.onSurfaceTextureDestroyed(mSurface);
Romain Guy451ce442011-06-10 15:40:36 -0700234 }
235
Romain Guy6be3d552011-07-14 18:08:37 -0700236 synchronized (mNativeWindowLock) {
237 nDestroyNativeWindow();
238 }
239
240 mLayer.destroy();
Grace Kloba402f0552011-08-09 18:47:17 -0700241 if (shouldRelease) mSurface.release();
Romain Guy451ce442011-06-10 15:40:36 -0700242 mSurface = null;
243 mLayer = null;
244 }
245 }
246
Romain Guyaa6c24c2011-04-28 18:40:04 -0700247 /**
248 * The layer type of a TextureView is ignored since a TextureView is always
249 * considered to act as a hardware layer. The optional paint supplied to this
250 * method will however be taken into account when rendering the content of
251 * this TextureView.
252 *
253 * @param layerType The ype of layer to use with this view, must be one of
254 * {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
255 * {@link #LAYER_TYPE_HARDWARE}
256 * @param paint The paint used to compose the layer. This argument is optional
257 * and can be null. It is ignored when the layer type is
258 * {@link #LAYER_TYPE_NONE}
259 */
260 @Override
261 public void setLayerType(int layerType, Paint paint) {
262 if (paint != mLayerPaint) {
263 mLayerPaint = paint;
264 invalidate();
265 }
266 }
267
268 /**
269 * Always returns {@link #LAYER_TYPE_HARDWARE}.
270 */
271 @Override
272 public int getLayerType() {
273 return LAYER_TYPE_HARDWARE;
274 }
275
Romain Guy59c7f802011-09-29 17:21:45 -0700276 @Override
277 boolean hasStaticLayer() {
278 return true;
279 }
280
Romain Guyaa6c24c2011-04-28 18:40:04 -0700281 /**
282 * Calling this method has no effect.
283 */
284 @Override
285 public void buildLayer() {
286 }
287
288 /**
289 * Subclasses of TextureView cannot do their own rendering
290 * with the {@link Canvas} object.
291 *
292 * @param canvas The Canvas to which the View is rendered.
293 */
294 @Override
295 public final void draw(Canvas canvas) {
Romain Guy52b307e2012-10-07 17:55:17 -0700296 // NOTE: Maintain this carefully (see View.java)
297 mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
298
Romain Guy58f4edb2011-06-24 14:51:38 -0700299 applyUpdate();
Alexandre Eliasc01391f2011-08-19 16:03:16 -0700300 applyTransformMatrix();
Romain Guyaa6c24c2011-04-28 18:40:04 -0700301 }
302
303 /**
304 * Subclasses of TextureView cannot do their own rendering
305 * with the {@link Canvas} object.
306 *
307 * @param canvas The Canvas to which the View is rendered.
308 */
309 @Override
310 protected final void onDraw(Canvas canvas) {
311 }
312
313 @Override
Romain Guy8f0095c2011-05-02 17:24:22 -0700314 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
315 super.onSizeChanged(w, h, oldw, oldh);
316 if (mSurface != null) {
Romain Guye5e0c502011-06-15 15:18:31 -0700317 nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
Romain Guy88801b22012-10-05 14:58:33 -0700318 updateLayer();
Romain Guy451ce442011-06-10 15:40:36 -0700319 if (mListener != null) {
320 mListener.onSurfaceTextureSizeChanged(mSurface, getWidth(), getHeight());
321 }
Romain Guy8f0095c2011-05-02 17:24:22 -0700322 }
323 }
324
325 @Override
Romain Guya998dff2012-03-23 18:58:36 -0700326 boolean destroyLayer(boolean valid) {
Romain Guy16260e72011-09-01 14:26:11 -0700327 return false;
328 }
329
Romain Guy1766b0e2011-11-21 18:28:49 -0800330 /**
331 * @hide
332 */
Romain Guy16260e72011-09-01 14:26:11 -0700333 @Override
Romain Guy31f2c2e2011-11-21 10:55:41 -0800334 protected void destroyHardwareResources() {
335 super.destroyHardwareResources();
336 destroySurface();
337 invalidateParentCaches();
338 invalidate(true);
339 }
340
341 @Override
Michael Jurka7e52caf2012-03-06 15:57:06 -0800342 HardwareLayer getHardwareLayer() {
Romain Guy52b307e2012-10-07 17:55:17 -0700343 // NOTE: Maintain these two lines very carefully (see View.java)
344 mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
345 mPrivateFlags &= ~PFLAG_DIRTY_MASK;
346
Romain Guyaa6c24c2011-04-28 18:40:04 -0700347 if (mLayer == null) {
Romain Guy58f4edb2011-06-24 14:51:38 -0700348 if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
349 return null;
350 }
351
Romain Guya9489272011-06-22 20:58:11 -0700352 mLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer(mOpaque);
Jamie Gennis2af35242012-04-05 11:44:30 -0700353 if (!mUpdateSurface) {
Jamie Gennis8a34d682012-04-17 16:01:34 -0700354 // Create a new SurfaceTexture for the layer.
Jamie Gennis2af35242012-04-05 11:44:30 -0700355 mSurface = mAttachInfo.mHardwareRenderer.createSurfaceTexture(mLayer);
356 }
Romain Guye5e0c502011-06-15 15:18:31 -0700357 nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
Jamie Gennis2af35242012-04-05 11:44:30 -0700358 nCreateNativeWindow(mSurface);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700359
Romain Guy8f0095c2011-05-02 17:24:22 -0700360 mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() {
Romain Guyaa6c24c2011-04-28 18:40:04 -0700361 @Override
362 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
363 // Per SurfaceTexture's documentation, the callback may be invoked
364 // from an arbitrary thread
Romain Guy88801b22012-10-05 14:58:33 -0700365 updateLayer();
Romain Guy52c145f2012-05-17 18:06:23 -0700366
367 if (Looper.myLooper() == Looper.getMainLooper()) {
368 invalidate();
369 } else {
370 postInvalidate();
371 }
Romain Guyaa6c24c2011-04-28 18:40:04 -0700372 }
Romain Guy8f0095c2011-05-02 17:24:22 -0700373 };
374 mSurface.setOnFrameAvailableListener(mUpdateListener);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700375
Jamie Gennis8a34d682012-04-17 16:01:34 -0700376 if (mListener != null && !mUpdateSurface) {
Romain Guy451ce442011-06-10 15:40:36 -0700377 mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight());
Romain Guyaa6c24c2011-04-28 18:40:04 -0700378 }
Chet Haased15ebf22012-09-05 11:40:29 -0700379 mLayer.setLayerPaint(mLayerPaint);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700380 }
381
Jamie Gennis2af35242012-04-05 11:44:30 -0700382 if (mUpdateSurface) {
383 // Someone has requested that we use a specific SurfaceTexture, so
384 // tell mLayer about it and set the SurfaceTexture to use the
385 // current view size.
386 mUpdateSurface = false;
Romain Guy51f7c6b2012-05-21 16:32:59 -0700387
388 // Since we are updating the layer, force an update to ensure its
389 // parameters are correct (width, height, transform, etc.)
Romain Guy88801b22012-10-05 14:58:33 -0700390 updateLayer();
Romain Guy51f7c6b2012-05-21 16:32:59 -0700391 mMatrixChanged = true;
392
Jamie Gennis2af35242012-04-05 11:44:30 -0700393 mAttachInfo.mHardwareRenderer.setSurfaceTexture(mLayer, mSurface);
394 nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
395 }
396
Romain Guy58f4edb2011-06-24 14:51:38 -0700397 applyUpdate();
Alexandre Eliasc01391f2011-08-19 16:03:16 -0700398 applyTransformMatrix();
Romain Guy302a9df2011-08-16 13:55:02 -0700399
Romain Guyaa6c24c2011-04-28 18:40:04 -0700400 return mLayer;
401 }
402
Romain Guy8f0095c2011-05-02 17:24:22 -0700403 @Override
404 protected void onVisibilityChanged(View changedView, int visibility) {
405 super.onVisibilityChanged(changedView, visibility);
406
407 if (mSurface != null) {
408 // When the view becomes invisible, stop updating it, it's a waste of CPU
409 // To cancel updates, the easiest thing to do is simply to remove the
410 // updates listener
411 if (visibility == VISIBLE) {
412 mSurface.setOnFrameAvailableListener(mUpdateListener);
Romain Guy88801b22012-10-05 14:58:33 -0700413 updateLayerAndInvalidate();
Romain Guy8f0095c2011-05-02 17:24:22 -0700414 } else {
415 mSurface.setOnFrameAvailableListener(null);
416 }
417 }
418 }
419
Romain Guya9489272011-06-22 20:58:11 -0700420 private void updateLayer() {
Romain Guy88801b22012-10-05 14:58:33 -0700421 synchronized (mLock) {
422 mUpdateLayer = true;
423 }
424 }
425
426 private void updateLayerAndInvalidate() {
427 synchronized (mLock) {
428 mUpdateLayer = true;
429 }
Romain Guy58f4edb2011-06-24 14:51:38 -0700430 invalidate();
431 }
Jamie Gennis2af35242012-04-05 11:44:30 -0700432
Romain Guy58f4edb2011-06-24 14:51:38 -0700433 private void applyUpdate() {
434 if (mLayer == null) {
Romain Guya9489272011-06-22 20:58:11 -0700435 return;
436 }
437
Romain Guy58f4edb2011-06-24 14:51:38 -0700438 synchronized (mLock) {
439 if (mUpdateLayer) {
440 mUpdateLayer = false;
441 } else {
442 return;
443 }
444 }
445
Romain Guy02ccac62011-06-24 13:20:23 -0700446 mLayer.update(getWidth(), getHeight(), mOpaque);
Romain Guya9489272011-06-22 20:58:11 -0700447
Grace Klobacf559372011-06-22 23:05:40 -0700448 if (mListener != null) {
449 mListener.onSurfaceTextureUpdated(mSurface);
450 }
Romain Guya9489272011-06-22 20:58:11 -0700451 }
452
Romain Guyaa6c24c2011-04-28 18:40:04 -0700453 /**
Romain Guy302a9df2011-08-16 13:55:02 -0700454 * <p>Sets the transform to associate with this texture view.
455 * The specified transform applies to the underlying surface
456 * texture and does not affect the size or position of the view
457 * itself, only of its content.</p>
458 *
459 * <p>Some transforms might prevent the content from drawing
460 * all the pixels contained within this view's bounds. In such
461 * situations, make sure this texture view is not marked opaque.</p>
462 *
463 * @param transform The transform to apply to the content of
464 * this view.
465 *
466 * @see #getTransform(android.graphics.Matrix)
467 * @see #isOpaque()
468 * @see #setOpaque(boolean)
469 */
470 public void setTransform(Matrix transform) {
471 mMatrix.set(transform);
472 mMatrixChanged = true;
Alexandre Eliasc01391f2011-08-19 16:03:16 -0700473 invalidateParentIfNeeded();
Romain Guy302a9df2011-08-16 13:55:02 -0700474 }
475
476 /**
477 * Returns the transform associated with this texture view.
478 *
479 * @param transform The {@link Matrix} in which to copy the current
480 * transform. Can be null.
481 *
482 * @return The specified matrix if not null or a new {@link Matrix}
483 * instance otherwise.
484 *
485 * @see #setTransform(android.graphics.Matrix)
486 */
487 public Matrix getTransform(Matrix transform) {
488 if (transform == null) {
489 transform = new Matrix();
490 }
491
492 transform.set(mMatrix);
493
494 return transform;
495 }
496
Alexandre Eliasc01391f2011-08-19 16:03:16 -0700497 private void applyTransformMatrix() {
Romain Guy51f7c6b2012-05-21 16:32:59 -0700498 if (mMatrixChanged && mLayer != null) {
Alexandre Eliasc01391f2011-08-19 16:03:16 -0700499 mLayer.setTransform(mMatrix);
500 mMatrixChanged = false;
501 }
502 }
503
Romain Guy302a9df2011-08-16 13:55:02 -0700504 /**
Romain Guy77a81162011-06-14 16:45:55 -0700505 * <p>Returns a {@link android.graphics.Bitmap} representation of the content
506 * of the associated surface texture. If the surface texture is not available,
507 * this method returns null.</p>
508 *
509 * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888}
510 * pixel format and its dimensions are the same as this view's.</p>
511 *
512 * <p><strong>Do not</strong> invoke this method from a drawing method
513 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
514 *
Romain Guyd6b2a002011-06-17 17:45:59 -0700515 * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
516 *
Romain Guy77a81162011-06-14 16:45:55 -0700517 * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
518 * texture is not available or the width &lt;= 0 or the height &lt;= 0
519 *
520 * @see #isAvailable()
521 * @see #getBitmap(android.graphics.Bitmap)
522 * @see #getBitmap(int, int)
523 */
524 public Bitmap getBitmap() {
525 return getBitmap(getWidth(), getHeight());
526 }
527
528 /**
529 * <p>Returns a {@link android.graphics.Bitmap} representation of the content
530 * of the associated surface texture. If the surface texture is not available,
531 * this method returns null.</p>
532 *
533 * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888}
534 * pixel format.</p>
535 *
536 * <p><strong>Do not</strong> invoke this method from a drawing method
537 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
538 *
Romain Guyd6b2a002011-06-17 17:45:59 -0700539 * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
540 *
Romain Guy77a81162011-06-14 16:45:55 -0700541 * @param width The width of the bitmap to create
542 * @param height The height of the bitmap to create
543 *
544 * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
545 * texture is not available or width is &lt;= 0 or height is &lt;= 0
546 *
547 * @see #isAvailable()
548 * @see #getBitmap(android.graphics.Bitmap)
549 * @see #getBitmap()
550 */
551 public Bitmap getBitmap(int width, int height) {
552 if (isAvailable() && width > 0 && height > 0) {
Dianne Hackborndde331c2012-08-03 14:01:57 -0700553 return getBitmap(Bitmap.createBitmap(getResources().getDisplayMetrics(),
554 width, height, Bitmap.Config.ARGB_8888));
Romain Guy77a81162011-06-14 16:45:55 -0700555 }
556 return null;
557 }
558
559 /**
560 * <p>Copies the content of this view's surface texture into the specified
561 * bitmap. If the surface texture is not available, the copy is not executed.
562 * The content of the surface texture will be scaled to fit exactly inside
563 * the specified bitmap.</p>
564 *
565 * <p><strong>Do not</strong> invoke this method from a drawing method
566 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
567 *
Romain Guyd6b2a002011-06-17 17:45:59 -0700568 * <p>If an error occurs, the bitmap is left unchanged.</p>
569 *
Romain Guy77a81162011-06-14 16:45:55 -0700570 * @param bitmap The bitmap to copy the content of the surface texture into,
571 * cannot be null, all configurations are supported
572 *
573 * @return The bitmap specified as a parameter
574 *
575 * @see #isAvailable()
576 * @see #getBitmap(int, int)
577 * @see #getBitmap()
Romain Guy589b0bb2011-10-10 13:57:47 -0700578 *
579 * @throws IllegalStateException if the hardware rendering context cannot be
580 * acquired to capture the bitmap
Romain Guy77a81162011-06-14 16:45:55 -0700581 */
582 public Bitmap getBitmap(Bitmap bitmap) {
583 if (bitmap != null && isAvailable()) {
Romain Guy589b0bb2011-10-10 13:57:47 -0700584 AttachInfo info = mAttachInfo;
585 if (info != null && info.mHardwareRenderer != null &&
586 info.mHardwareRenderer.isEnabled()) {
587 if (!info.mHardwareRenderer.validate()) {
588 throw new IllegalStateException("Could not acquire hardware rendering context");
589 }
590 }
591
592 applyUpdate();
593 applyTransformMatrix();
594
Romain Guy78245f72012-05-11 10:01:42 -0700595 // This case can happen if the app invokes setSurfaceTexture() before
596 // we are able to create the hardware layer. We can safely initialize
597 // the layer here thanks to the validate() call at the beginning of
598 // this method
599 if (mLayer == null && mUpdateSurface) {
600 getHardwareLayer();
601 }
602
603 if (mLayer != null) {
604 mLayer.copyInto(bitmap);
605 }
Romain Guy77a81162011-06-14 16:45:55 -0700606 }
607 return bitmap;
608 }
609
610 /**
611 * Returns true if the {@link SurfaceTexture} associated with this
612 * TextureView is available for rendering. When this method returns
613 * true, {@link #getSurfaceTexture()} returns a valid surface texture.
614 */
615 public boolean isAvailable() {
616 return mSurface != null;
617 }
618
619 /**
Romain Guy6be3d552011-07-14 18:08:37 -0700620 * <p>Start editing the pixels in the surface. The returned Canvas can be used
621 * to draw into the surface's bitmap. A null is returned if the surface has
622 * not been created or otherwise cannot be edited. You will usually need
623 * to implement
624 * {@link SurfaceTextureListener#onSurfaceTextureAvailable(android.graphics.SurfaceTexture, int, int)}
625 * to find out when the Surface is available for use.</p>
626 *
627 * <p>The content of the Surface is never preserved between unlockCanvas()
628 * and lockCanvas(), for this reason, every pixel within the Surface area
629 * must be written. The only exception to this rule is when a dirty
630 * rectangle is specified, in which case, non-dirty pixels will be
631 * preserved.</p>
632 *
Romain Guy462785f2011-09-27 17:42:10 -0700633 * <p>This method can only be used if the underlying surface is not already
634 * owned by another producer. For instance, if the TextureView is being used
635 * to render the camera's preview you cannot invoke this method.</p>
636 *
Romain Guy6be3d552011-07-14 18:08:37 -0700637 * @return A Canvas used to draw into the surface.
638 *
639 * @see #lockCanvas(android.graphics.Rect)
640 * @see #unlockCanvasAndPost(android.graphics.Canvas)
641 */
642 public Canvas lockCanvas() {
643 return lockCanvas(null);
644 }
645
646 /**
647 * Just like {@link #lockCanvas()} but allows specification of a dirty
648 * rectangle. Every pixel within that rectangle must be written; however
649 * pixels outside the dirty rectangle will be preserved by the next call
650 * to lockCanvas().
651 *
652 * @param dirty Area of the surface that will be modified.
653
654 * @return A Canvas used to draw into the surface.
655 *
656 * @see #lockCanvas()
657 * @see #unlockCanvasAndPost(android.graphics.Canvas)
658 */
659 public Canvas lockCanvas(Rect dirty) {
660 if (!isAvailable()) return null;
661
662 if (mCanvas == null) {
663 mCanvas = new Canvas();
664 }
665
666 synchronized (mNativeWindowLock) {
667 nLockCanvas(mNativeWindow, mCanvas, dirty);
668 }
669 mSaveCount = mCanvas.save();
670
671 return mCanvas;
672 }
673
674 /**
675 * Finish editing pixels in the surface. After this call, the surface's
676 * current pixels will be shown on the screen, but its content is lost,
677 * in particular there is no guarantee that the content of the Surface
678 * will remain unchanged when lockCanvas() is called again.
679 *
680 * @param canvas The Canvas previously returned by lockCanvas()
681 *
682 * @see #lockCanvas()
683 * @see #lockCanvas(android.graphics.Rect)
684 */
685 public void unlockCanvasAndPost(Canvas canvas) {
686 if (mCanvas != null && canvas == mCanvas) {
687 canvas.restoreToCount(mSaveCount);
688 mSaveCount = 0;
689
690 synchronized (mNativeWindowLock) {
691 nUnlockCanvasAndPost(mNativeWindow, mCanvas);
692 }
693 }
694 }
695
696 /**
Romain Guyaa6c24c2011-04-28 18:40:04 -0700697 * Returns the {@link SurfaceTexture} used by this view. This method
Romain Guy77a81162011-06-14 16:45:55 -0700698 * may return null if the view is not attached to a window or if the surface
699 * texture has not been initialized yet.
700 *
701 * @see #isAvailable()
Romain Guyaa6c24c2011-04-28 18:40:04 -0700702 */
703 public SurfaceTexture getSurfaceTexture() {
704 return mSurface;
705 }
706
707 /**
Jamie Gennis2af35242012-04-05 11:44:30 -0700708 * Set the {@link SurfaceTexture} for this view to use. If a {@link
709 * SurfaceTexture} is already being used by this view, it is immediately
710 * released and not be usable any more. The {@link
711 * SurfaceTextureListener#onSurfaceTextureDestroyed} callback is <b>not</b>
Jamie Gennis8a34d682012-04-17 16:01:34 -0700712 * called for the previous {@link SurfaceTexture}. Similarly, the {@link
713 * SurfaceTextureListener#onSurfaceTextureAvailable} callback is <b>not</b>
714 * called for the {@link SurfaceTexture} passed to setSurfaceTexture.
Jamie Gennis2af35242012-04-05 11:44:30 -0700715 *
716 * The {@link SurfaceTexture} object must be detached from all OpenGL ES
717 * contexts prior to calling this method.
718 *
719 * @param surfaceTexture The {@link SurfaceTexture} that the view should use.
720 * @see SurfaceTexture#detachFromGLContext()
Jamie Gennis2af35242012-04-05 11:44:30 -0700721 */
722 public void setSurfaceTexture(SurfaceTexture surfaceTexture) {
723 if (surfaceTexture == null) {
724 throw new NullPointerException("surfaceTexture must not be null");
725 }
726 if (mSurface != null) {
727 mSurface.release();
728 }
729 mSurface = surfaceTexture;
730 mUpdateSurface = true;
731 invalidateParentIfNeeded();
732 }
733
734 /**
Romain Guyaa6c24c2011-04-28 18:40:04 -0700735 * Returns the {@link SurfaceTextureListener} currently associated with this
736 * texture view.
737 *
738 * @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener)
739 * @see SurfaceTextureListener
740 */
741 public SurfaceTextureListener getSurfaceTextureListener() {
742 return mListener;
743 }
744
745 /**
746 * Sets the {@link SurfaceTextureListener} used to listen to surface
747 * texture events.
748 *
749 * @see #getSurfaceTextureListener()
750 * @see SurfaceTextureListener
751 */
752 public void setSurfaceTextureListener(SurfaceTextureListener listener) {
753 mListener = listener;
754 }
755
756 /**
757 * This listener can be used to be notified when the surface texture
758 * associated with this texture view is available.
759 */
760 public static interface SurfaceTextureListener {
761 /**
762 * Invoked when a {@link TextureView}'s SurfaceTexture is ready for use.
763 *
764 * @param surface The surface returned by
765 * {@link android.view.TextureView#getSurfaceTexture()}
Romain Guy451ce442011-06-10 15:40:36 -0700766 * @param width The width of the surface
767 * @param height The height of the surface
Romain Guyaa6c24c2011-04-28 18:40:04 -0700768 */
Romain Guy451ce442011-06-10 15:40:36 -0700769 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height);
Romain Guy8f0095c2011-05-02 17:24:22 -0700770
771 /**
772 * Invoked when the {@link SurfaceTexture}'s buffers size changed.
773 *
774 * @param surface The surface returned by
775 * {@link android.view.TextureView#getSurfaceTexture()}
776 * @param width The new width of the surface
777 * @param height The new height of the surface
778 */
779 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height);
Romain Guy451ce442011-06-10 15:40:36 -0700780
781 /**
782 * Invoked when the specified {@link SurfaceTexture} is about to be destroyed.
Grace Kloba402f0552011-08-09 18:47:17 -0700783 * If returns true, no rendering should happen inside the surface texture after this method
784 * is invoked. If returns false, the client needs to call {@link SurfaceTexture#release()}.
Romain Guy52b307e2012-10-07 17:55:17 -0700785 * Most applications should return true.
Romain Guy451ce442011-06-10 15:40:36 -0700786 *
787 * @param surface The surface about to be destroyed
788 */
Grace Kloba402f0552011-08-09 18:47:17 -0700789 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface);
Grace Klobacf559372011-06-22 23:05:40 -0700790
791 /**
792 * Invoked when the specified {@link SurfaceTexture} is updated through
793 * {@link SurfaceTexture#updateTexImage()}.
794 *
795 * @param surface The surface just updated
796 */
797 public void onSurfaceTextureUpdated(SurfaceTexture surface);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700798 }
Romain Guy8f0095c2011-05-02 17:24:22 -0700799
Romain Guy6be3d552011-07-14 18:08:37 -0700800 private native void nCreateNativeWindow(SurfaceTexture surface);
801 private native void nDestroyNativeWindow();
802
Romain Guyd6b2a002011-06-17 17:45:59 -0700803 private static native void nSetDefaultBufferSize(SurfaceTexture surfaceTexture,
804 int width, int height);
Romain Guy6be3d552011-07-14 18:08:37 -0700805
806 private static native void nLockCanvas(int nativeWindow, Canvas canvas, Rect dirty);
807 private static native void nUnlockCanvasAndPost(int nativeWindow, Canvas canvas);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700808}