blob: a9b6e097daf461abccae6198516f267cf7577065 [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 Guyef09a212012-09-25 12:17:14 -0700227 mLayer.clearStorage();
Jamie Gennis2af35242012-04-05 11:44:30 -0700228
Grace Kloba402f0552011-08-09 18:47:17 -0700229 boolean shouldRelease = true;
Romain Guy451ce442011-06-10 15:40:36 -0700230 if (mListener != null) {
Grace Kloba402f0552011-08-09 18:47:17 -0700231 shouldRelease = mListener.onSurfaceTextureDestroyed(mSurface);
Romain Guy451ce442011-06-10 15:40:36 -0700232 }
233
Romain Guy6be3d552011-07-14 18:08:37 -0700234 synchronized (mNativeWindowLock) {
235 nDestroyNativeWindow();
236 }
237
238 mLayer.destroy();
Grace Kloba402f0552011-08-09 18:47:17 -0700239 if (shouldRelease) mSurface.release();
Romain Guy451ce442011-06-10 15:40:36 -0700240 mSurface = null;
241 mLayer = null;
242 }
243 }
244
Romain Guyaa6c24c2011-04-28 18:40:04 -0700245 /**
246 * The layer type of a TextureView is ignored since a TextureView is always
247 * considered to act as a hardware layer. The optional paint supplied to this
248 * method will however be taken into account when rendering the content of
249 * this TextureView.
250 *
251 * @param layerType The ype of layer to use with this view, must be one of
252 * {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
253 * {@link #LAYER_TYPE_HARDWARE}
254 * @param paint The paint used to compose the layer. This argument is optional
255 * and can be null. It is ignored when the layer type is
256 * {@link #LAYER_TYPE_NONE}
257 */
258 @Override
259 public void setLayerType(int layerType, Paint paint) {
260 if (paint != mLayerPaint) {
261 mLayerPaint = paint;
262 invalidate();
263 }
264 }
265
266 /**
267 * Always returns {@link #LAYER_TYPE_HARDWARE}.
268 */
269 @Override
270 public int getLayerType() {
271 return LAYER_TYPE_HARDWARE;
272 }
273
Romain Guy59c7f802011-09-29 17:21:45 -0700274 @Override
275 boolean hasStaticLayer() {
276 return true;
277 }
278
Romain Guyaa6c24c2011-04-28 18:40:04 -0700279 /**
280 * Calling this method has no effect.
281 */
282 @Override
283 public void buildLayer() {
284 }
285
286 /**
287 * Subclasses of TextureView cannot do their own rendering
288 * with the {@link Canvas} object.
289 *
290 * @param canvas The Canvas to which the View is rendered.
291 */
292 @Override
293 public final void draw(Canvas canvas) {
Romain Guy58f4edb2011-06-24 14:51:38 -0700294 applyUpdate();
Alexandre Eliasc01391f2011-08-19 16:03:16 -0700295 applyTransformMatrix();
Romain Guyaa6c24c2011-04-28 18:40:04 -0700296 }
297
298 /**
299 * Subclasses of TextureView cannot do their own rendering
300 * with the {@link Canvas} object.
301 *
302 * @param canvas The Canvas to which the View is rendered.
303 */
304 @Override
305 protected final void onDraw(Canvas canvas) {
306 }
307
308 @Override
Romain Guy8f0095c2011-05-02 17:24:22 -0700309 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
310 super.onSizeChanged(w, h, oldw, oldh);
311 if (mSurface != null) {
Romain Guye5e0c502011-06-15 15:18:31 -0700312 nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
Romain Guy88801b22012-10-05 14:58:33 -0700313 updateLayer();
Romain Guy451ce442011-06-10 15:40:36 -0700314 if (mListener != null) {
315 mListener.onSurfaceTextureSizeChanged(mSurface, getWidth(), getHeight());
316 }
Romain Guy8f0095c2011-05-02 17:24:22 -0700317 }
318 }
319
320 @Override
Romain Guya998dff2012-03-23 18:58:36 -0700321 boolean destroyLayer(boolean valid) {
Romain Guy16260e72011-09-01 14:26:11 -0700322 return false;
323 }
324
Romain Guy1766b0e2011-11-21 18:28:49 -0800325 /**
326 * @hide
327 */
Romain Guy16260e72011-09-01 14:26:11 -0700328 @Override
Romain Guy31f2c2e2011-11-21 10:55:41 -0800329 protected void destroyHardwareResources() {
330 super.destroyHardwareResources();
331 destroySurface();
332 invalidateParentCaches();
333 invalidate(true);
334 }
335
336 @Override
Michael Jurka7e52caf2012-03-06 15:57:06 -0800337 HardwareLayer getHardwareLayer() {
Romain Guyaa6c24c2011-04-28 18:40:04 -0700338 if (mLayer == null) {
Romain Guy58f4edb2011-06-24 14:51:38 -0700339 if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
340 return null;
341 }
342
Romain Guya9489272011-06-22 20:58:11 -0700343 mLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer(mOpaque);
Jamie Gennis2af35242012-04-05 11:44:30 -0700344 if (!mUpdateSurface) {
Jamie Gennis8a34d682012-04-17 16:01:34 -0700345 // Create a new SurfaceTexture for the layer.
Jamie Gennis2af35242012-04-05 11:44:30 -0700346 mSurface = mAttachInfo.mHardwareRenderer.createSurfaceTexture(mLayer);
347 }
Romain Guye5e0c502011-06-15 15:18:31 -0700348 nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
Jamie Gennis2af35242012-04-05 11:44:30 -0700349 nCreateNativeWindow(mSurface);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700350
Romain Guy8f0095c2011-05-02 17:24:22 -0700351 mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() {
Romain Guyaa6c24c2011-04-28 18:40:04 -0700352 @Override
353 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
354 // Per SurfaceTexture's documentation, the callback may be invoked
355 // from an arbitrary thread
Romain Guy88801b22012-10-05 14:58:33 -0700356 updateLayer();
Romain Guy52c145f2012-05-17 18:06:23 -0700357
358 if (Looper.myLooper() == Looper.getMainLooper()) {
359 invalidate();
360 } else {
361 postInvalidate();
362 }
Romain Guyaa6c24c2011-04-28 18:40:04 -0700363 }
Romain Guy8f0095c2011-05-02 17:24:22 -0700364 };
365 mSurface.setOnFrameAvailableListener(mUpdateListener);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700366
Jamie Gennis8a34d682012-04-17 16:01:34 -0700367 if (mListener != null && !mUpdateSurface) {
Romain Guy451ce442011-06-10 15:40:36 -0700368 mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight());
Romain Guyaa6c24c2011-04-28 18:40:04 -0700369 }
Chet Haased15ebf22012-09-05 11:40:29 -0700370 mLayer.setLayerPaint(mLayerPaint);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700371 }
372
Jamie Gennis2af35242012-04-05 11:44:30 -0700373 if (mUpdateSurface) {
374 // Someone has requested that we use a specific SurfaceTexture, so
375 // tell mLayer about it and set the SurfaceTexture to use the
376 // current view size.
377 mUpdateSurface = false;
Romain Guy51f7c6b2012-05-21 16:32:59 -0700378
379 // Since we are updating the layer, force an update to ensure its
380 // parameters are correct (width, height, transform, etc.)
Romain Guy88801b22012-10-05 14:58:33 -0700381 updateLayer();
Romain Guy51f7c6b2012-05-21 16:32:59 -0700382 mMatrixChanged = true;
383
Jamie Gennis2af35242012-04-05 11:44:30 -0700384 mAttachInfo.mHardwareRenderer.setSurfaceTexture(mLayer, mSurface);
385 nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
386 }
387
Romain Guy58f4edb2011-06-24 14:51:38 -0700388 applyUpdate();
Alexandre Eliasc01391f2011-08-19 16:03:16 -0700389 applyTransformMatrix();
Romain Guy302a9df2011-08-16 13:55:02 -0700390
Romain Guyaa6c24c2011-04-28 18:40:04 -0700391 return mLayer;
392 }
393
Romain Guy8f0095c2011-05-02 17:24:22 -0700394 @Override
395 protected void onVisibilityChanged(View changedView, int visibility) {
396 super.onVisibilityChanged(changedView, visibility);
397
398 if (mSurface != null) {
399 // When the view becomes invisible, stop updating it, it's a waste of CPU
400 // To cancel updates, the easiest thing to do is simply to remove the
401 // updates listener
402 if (visibility == VISIBLE) {
403 mSurface.setOnFrameAvailableListener(mUpdateListener);
Romain Guy88801b22012-10-05 14:58:33 -0700404 updateLayerAndInvalidate();
Romain Guy8f0095c2011-05-02 17:24:22 -0700405 } else {
406 mSurface.setOnFrameAvailableListener(null);
407 }
408 }
409 }
410
Romain Guya9489272011-06-22 20:58:11 -0700411 private void updateLayer() {
Romain Guy88801b22012-10-05 14:58:33 -0700412 synchronized (mLock) {
413 mUpdateLayer = true;
414 }
415 }
416
417 private void updateLayerAndInvalidate() {
418 synchronized (mLock) {
419 mUpdateLayer = true;
420 }
Romain Guy58f4edb2011-06-24 14:51:38 -0700421 invalidate();
422 }
Jamie Gennis2af35242012-04-05 11:44:30 -0700423
Romain Guy58f4edb2011-06-24 14:51:38 -0700424 private void applyUpdate() {
425 if (mLayer == null) {
Romain Guya9489272011-06-22 20:58:11 -0700426 return;
427 }
428
Romain Guy58f4edb2011-06-24 14:51:38 -0700429 synchronized (mLock) {
430 if (mUpdateLayer) {
431 mUpdateLayer = false;
432 } else {
433 return;
434 }
435 }
436
Romain Guy02ccac62011-06-24 13:20:23 -0700437 mLayer.update(getWidth(), getHeight(), mOpaque);
Romain Guya9489272011-06-22 20:58:11 -0700438
Grace Klobacf559372011-06-22 23:05:40 -0700439 if (mListener != null) {
440 mListener.onSurfaceTextureUpdated(mSurface);
441 }
Romain Guya9489272011-06-22 20:58:11 -0700442 }
443
Romain Guyaa6c24c2011-04-28 18:40:04 -0700444 /**
Romain Guy302a9df2011-08-16 13:55:02 -0700445 * <p>Sets the transform to associate with this texture view.
446 * The specified transform applies to the underlying surface
447 * texture and does not affect the size or position of the view
448 * itself, only of its content.</p>
449 *
450 * <p>Some transforms might prevent the content from drawing
451 * all the pixels contained within this view's bounds. In such
452 * situations, make sure this texture view is not marked opaque.</p>
453 *
454 * @param transform The transform to apply to the content of
455 * this view.
456 *
457 * @see #getTransform(android.graphics.Matrix)
458 * @see #isOpaque()
459 * @see #setOpaque(boolean)
460 */
461 public void setTransform(Matrix transform) {
462 mMatrix.set(transform);
463 mMatrixChanged = true;
Alexandre Eliasc01391f2011-08-19 16:03:16 -0700464 invalidateParentIfNeeded();
Romain Guy302a9df2011-08-16 13:55:02 -0700465 }
466
467 /**
468 * Returns the transform associated with this texture view.
469 *
470 * @param transform The {@link Matrix} in which to copy the current
471 * transform. Can be null.
472 *
473 * @return The specified matrix if not null or a new {@link Matrix}
474 * instance otherwise.
475 *
476 * @see #setTransform(android.graphics.Matrix)
477 */
478 public Matrix getTransform(Matrix transform) {
479 if (transform == null) {
480 transform = new Matrix();
481 }
482
483 transform.set(mMatrix);
484
485 return transform;
486 }
487
Alexandre Eliasc01391f2011-08-19 16:03:16 -0700488 private void applyTransformMatrix() {
Romain Guy51f7c6b2012-05-21 16:32:59 -0700489 if (mMatrixChanged && mLayer != null) {
Alexandre Eliasc01391f2011-08-19 16:03:16 -0700490 mLayer.setTransform(mMatrix);
491 mMatrixChanged = false;
492 }
493 }
494
Romain Guy302a9df2011-08-16 13:55:02 -0700495 /**
Romain Guy77a81162011-06-14 16:45:55 -0700496 * <p>Returns a {@link android.graphics.Bitmap} representation of the content
497 * of the associated surface texture. If the surface texture is not available,
498 * this method returns null.</p>
499 *
500 * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888}
501 * pixel format and its dimensions are the same as this view's.</p>
502 *
503 * <p><strong>Do not</strong> invoke this method from a drawing method
504 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
505 *
Romain Guyd6b2a002011-06-17 17:45:59 -0700506 * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
507 *
Romain Guy77a81162011-06-14 16:45:55 -0700508 * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
509 * texture is not available or the width &lt;= 0 or the height &lt;= 0
510 *
511 * @see #isAvailable()
512 * @see #getBitmap(android.graphics.Bitmap)
513 * @see #getBitmap(int, int)
514 */
515 public Bitmap getBitmap() {
516 return getBitmap(getWidth(), getHeight());
517 }
518
519 /**
520 * <p>Returns a {@link android.graphics.Bitmap} representation of the content
521 * of the associated surface texture. If the surface texture is not available,
522 * this method returns null.</p>
523 *
524 * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888}
525 * pixel format.</p>
526 *
527 * <p><strong>Do not</strong> invoke this method from a drawing method
528 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
529 *
Romain Guyd6b2a002011-06-17 17:45:59 -0700530 * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
531 *
Romain Guy77a81162011-06-14 16:45:55 -0700532 * @param width The width of the bitmap to create
533 * @param height The height of the bitmap to create
534 *
535 * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
536 * texture is not available or width is &lt;= 0 or height is &lt;= 0
537 *
538 * @see #isAvailable()
539 * @see #getBitmap(android.graphics.Bitmap)
540 * @see #getBitmap()
541 */
542 public Bitmap getBitmap(int width, int height) {
543 if (isAvailable() && width > 0 && height > 0) {
Dianne Hackborndde331c2012-08-03 14:01:57 -0700544 return getBitmap(Bitmap.createBitmap(getResources().getDisplayMetrics(),
545 width, height, Bitmap.Config.ARGB_8888));
Romain Guy77a81162011-06-14 16:45:55 -0700546 }
547 return null;
548 }
549
550 /**
551 * <p>Copies the content of this view's surface texture into the specified
552 * bitmap. If the surface texture is not available, the copy is not executed.
553 * The content of the surface texture will be scaled to fit exactly inside
554 * the specified bitmap.</p>
555 *
556 * <p><strong>Do not</strong> invoke this method from a drawing method
557 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
558 *
Romain Guyd6b2a002011-06-17 17:45:59 -0700559 * <p>If an error occurs, the bitmap is left unchanged.</p>
560 *
Romain Guy77a81162011-06-14 16:45:55 -0700561 * @param bitmap The bitmap to copy the content of the surface texture into,
562 * cannot be null, all configurations are supported
563 *
564 * @return The bitmap specified as a parameter
565 *
566 * @see #isAvailable()
567 * @see #getBitmap(int, int)
568 * @see #getBitmap()
Romain Guy589b0bb2011-10-10 13:57:47 -0700569 *
570 * @throws IllegalStateException if the hardware rendering context cannot be
571 * acquired to capture the bitmap
Romain Guy77a81162011-06-14 16:45:55 -0700572 */
573 public Bitmap getBitmap(Bitmap bitmap) {
574 if (bitmap != null && isAvailable()) {
Romain Guy589b0bb2011-10-10 13:57:47 -0700575 AttachInfo info = mAttachInfo;
576 if (info != null && info.mHardwareRenderer != null &&
577 info.mHardwareRenderer.isEnabled()) {
578 if (!info.mHardwareRenderer.validate()) {
579 throw new IllegalStateException("Could not acquire hardware rendering context");
580 }
581 }
582
583 applyUpdate();
584 applyTransformMatrix();
585
Romain Guy78245f72012-05-11 10:01:42 -0700586 // This case can happen if the app invokes setSurfaceTexture() before
587 // we are able to create the hardware layer. We can safely initialize
588 // the layer here thanks to the validate() call at the beginning of
589 // this method
590 if (mLayer == null && mUpdateSurface) {
591 getHardwareLayer();
592 }
593
594 if (mLayer != null) {
595 mLayer.copyInto(bitmap);
596 }
Romain Guy77a81162011-06-14 16:45:55 -0700597 }
598 return bitmap;
599 }
600
601 /**
602 * Returns true if the {@link SurfaceTexture} associated with this
603 * TextureView is available for rendering. When this method returns
604 * true, {@link #getSurfaceTexture()} returns a valid surface texture.
605 */
606 public boolean isAvailable() {
607 return mSurface != null;
608 }
609
610 /**
Romain Guy6be3d552011-07-14 18:08:37 -0700611 * <p>Start editing the pixels in the surface. The returned Canvas can be used
612 * to draw into the surface's bitmap. A null is returned if the surface has
613 * not been created or otherwise cannot be edited. You will usually need
614 * to implement
615 * {@link SurfaceTextureListener#onSurfaceTextureAvailable(android.graphics.SurfaceTexture, int, int)}
616 * to find out when the Surface is available for use.</p>
617 *
618 * <p>The content of the Surface is never preserved between unlockCanvas()
619 * and lockCanvas(), for this reason, every pixel within the Surface area
620 * must be written. The only exception to this rule is when a dirty
621 * rectangle is specified, in which case, non-dirty pixels will be
622 * preserved.</p>
623 *
Romain Guy462785f2011-09-27 17:42:10 -0700624 * <p>This method can only be used if the underlying surface is not already
625 * owned by another producer. For instance, if the TextureView is being used
626 * to render the camera's preview you cannot invoke this method.</p>
627 *
Romain Guy6be3d552011-07-14 18:08:37 -0700628 * @return A Canvas used to draw into the surface.
629 *
630 * @see #lockCanvas(android.graphics.Rect)
631 * @see #unlockCanvasAndPost(android.graphics.Canvas)
632 */
633 public Canvas lockCanvas() {
634 return lockCanvas(null);
635 }
636
637 /**
638 * Just like {@link #lockCanvas()} but allows specification of a dirty
639 * rectangle. Every pixel within that rectangle must be written; however
640 * pixels outside the dirty rectangle will be preserved by the next call
641 * to lockCanvas().
642 *
643 * @param dirty Area of the surface that will be modified.
644
645 * @return A Canvas used to draw into the surface.
646 *
647 * @see #lockCanvas()
648 * @see #unlockCanvasAndPost(android.graphics.Canvas)
649 */
650 public Canvas lockCanvas(Rect dirty) {
651 if (!isAvailable()) return null;
652
653 if (mCanvas == null) {
654 mCanvas = new Canvas();
655 }
656
657 synchronized (mNativeWindowLock) {
658 nLockCanvas(mNativeWindow, mCanvas, dirty);
659 }
660 mSaveCount = mCanvas.save();
661
662 return mCanvas;
663 }
664
665 /**
666 * Finish editing pixels in the surface. After this call, the surface's
667 * current pixels will be shown on the screen, but its content is lost,
668 * in particular there is no guarantee that the content of the Surface
669 * will remain unchanged when lockCanvas() is called again.
670 *
671 * @param canvas The Canvas previously returned by lockCanvas()
672 *
673 * @see #lockCanvas()
674 * @see #lockCanvas(android.graphics.Rect)
675 */
676 public void unlockCanvasAndPost(Canvas canvas) {
677 if (mCanvas != null && canvas == mCanvas) {
678 canvas.restoreToCount(mSaveCount);
679 mSaveCount = 0;
680
681 synchronized (mNativeWindowLock) {
682 nUnlockCanvasAndPost(mNativeWindow, mCanvas);
683 }
684 }
685 }
686
687 /**
Romain Guyaa6c24c2011-04-28 18:40:04 -0700688 * Returns the {@link SurfaceTexture} used by this view. This method
Romain Guy77a81162011-06-14 16:45:55 -0700689 * may return null if the view is not attached to a window or if the surface
690 * texture has not been initialized yet.
691 *
692 * @see #isAvailable()
Romain Guyaa6c24c2011-04-28 18:40:04 -0700693 */
694 public SurfaceTexture getSurfaceTexture() {
695 return mSurface;
696 }
697
698 /**
Jamie Gennis2af35242012-04-05 11:44:30 -0700699 * Set the {@link SurfaceTexture} for this view to use. If a {@link
700 * SurfaceTexture} is already being used by this view, it is immediately
701 * released and not be usable any more. The {@link
702 * SurfaceTextureListener#onSurfaceTextureDestroyed} callback is <b>not</b>
Jamie Gennis8a34d682012-04-17 16:01:34 -0700703 * called for the previous {@link SurfaceTexture}. Similarly, the {@link
704 * SurfaceTextureListener#onSurfaceTextureAvailable} callback is <b>not</b>
705 * called for the {@link SurfaceTexture} passed to setSurfaceTexture.
Jamie Gennis2af35242012-04-05 11:44:30 -0700706 *
707 * The {@link SurfaceTexture} object must be detached from all OpenGL ES
708 * contexts prior to calling this method.
709 *
710 * @param surfaceTexture The {@link SurfaceTexture} that the view should use.
711 * @see SurfaceTexture#detachFromGLContext()
Jamie Gennis2af35242012-04-05 11:44:30 -0700712 */
713 public void setSurfaceTexture(SurfaceTexture surfaceTexture) {
714 if (surfaceTexture == null) {
715 throw new NullPointerException("surfaceTexture must not be null");
716 }
717 if (mSurface != null) {
718 mSurface.release();
719 }
720 mSurface = surfaceTexture;
721 mUpdateSurface = true;
722 invalidateParentIfNeeded();
723 }
724
725 /**
Romain Guyaa6c24c2011-04-28 18:40:04 -0700726 * Returns the {@link SurfaceTextureListener} currently associated with this
727 * texture view.
728 *
729 * @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener)
730 * @see SurfaceTextureListener
731 */
732 public SurfaceTextureListener getSurfaceTextureListener() {
733 return mListener;
734 }
735
736 /**
737 * Sets the {@link SurfaceTextureListener} used to listen to surface
738 * texture events.
739 *
740 * @see #getSurfaceTextureListener()
741 * @see SurfaceTextureListener
742 */
743 public void setSurfaceTextureListener(SurfaceTextureListener listener) {
744 mListener = listener;
745 }
746
747 /**
748 * This listener can be used to be notified when the surface texture
749 * associated with this texture view is available.
750 */
751 public static interface SurfaceTextureListener {
752 /**
753 * Invoked when a {@link TextureView}'s SurfaceTexture is ready for use.
754 *
755 * @param surface The surface returned by
756 * {@link android.view.TextureView#getSurfaceTexture()}
Romain Guy451ce442011-06-10 15:40:36 -0700757 * @param width The width of the surface
758 * @param height The height of the surface
Romain Guyaa6c24c2011-04-28 18:40:04 -0700759 */
Romain Guy451ce442011-06-10 15:40:36 -0700760 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height);
Romain Guy8f0095c2011-05-02 17:24:22 -0700761
762 /**
763 * Invoked when the {@link SurfaceTexture}'s buffers size changed.
764 *
765 * @param surface The surface returned by
766 * {@link android.view.TextureView#getSurfaceTexture()}
767 * @param width The new width of the surface
768 * @param height The new height of the surface
769 */
770 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height);
Romain Guy451ce442011-06-10 15:40:36 -0700771
772 /**
773 * Invoked when the specified {@link SurfaceTexture} is about to be destroyed.
Grace Kloba402f0552011-08-09 18:47:17 -0700774 * If returns true, no rendering should happen inside the surface texture after this method
775 * is invoked. If returns false, the client needs to call {@link SurfaceTexture#release()}.
Romain Guy451ce442011-06-10 15:40:36 -0700776 *
777 * @param surface The surface about to be destroyed
778 */
Grace Kloba402f0552011-08-09 18:47:17 -0700779 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface);
Grace Klobacf559372011-06-22 23:05:40 -0700780
781 /**
782 * Invoked when the specified {@link SurfaceTexture} is updated through
783 * {@link SurfaceTexture#updateTexImage()}.
784 *
785 * @param surface The surface just updated
786 */
787 public void onSurfaceTextureUpdated(SurfaceTexture surface);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700788 }
Romain Guy8f0095c2011-05-02 17:24:22 -0700789
Romain Guy6be3d552011-07-14 18:08:37 -0700790 private native void nCreateNativeWindow(SurfaceTexture surface);
791 private native void nDestroyNativeWindow();
792
Romain Guyd6b2a002011-06-17 17:45:59 -0700793 private static native void nSetDefaultBufferSize(SurfaceTexture surfaceTexture,
794 int width, int height);
Romain Guy6be3d552011-07-14 18:08:37 -0700795
796 private static native void nLockCanvas(int nativeWindow, Canvas canvas, Rect dirty);
797 private static native void nUnlockCanvasAndPost(int nativeWindow, Canvas canvas);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700798}