blob: 876b7d847919995ac52630eaab65dcaef6984742 [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) {
192 updateLayer();
193 }
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 Guy451ce442011-06-10 15:40:36 -0700313 if (mListener != null) {
314 mListener.onSurfaceTextureSizeChanged(mSurface, getWidth(), getHeight());
315 }
Romain Guy8f0095c2011-05-02 17:24:22 -0700316 }
317 }
318
319 @Override
Romain Guya998dff2012-03-23 18:58:36 -0700320 boolean destroyLayer(boolean valid) {
Romain Guy16260e72011-09-01 14:26:11 -0700321 return false;
322 }
323
Romain Guy1766b0e2011-11-21 18:28:49 -0800324 /**
325 * @hide
326 */
Romain Guy16260e72011-09-01 14:26:11 -0700327 @Override
Romain Guy31f2c2e2011-11-21 10:55:41 -0800328 protected void destroyHardwareResources() {
329 super.destroyHardwareResources();
330 destroySurface();
331 invalidateParentCaches();
332 invalidate(true);
333 }
334
335 @Override
Michael Jurka7e52caf2012-03-06 15:57:06 -0800336 HardwareLayer getHardwareLayer() {
Romain Guyaa6c24c2011-04-28 18:40:04 -0700337 if (mLayer == null) {
Romain Guy58f4edb2011-06-24 14:51:38 -0700338 if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
339 return null;
340 }
341
Romain Guya9489272011-06-22 20:58:11 -0700342 mLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer(mOpaque);
Jamie Gennis2af35242012-04-05 11:44:30 -0700343 if (!mUpdateSurface) {
Jamie Gennis8a34d682012-04-17 16:01:34 -0700344 // Create a new SurfaceTexture for the layer.
Jamie Gennis2af35242012-04-05 11:44:30 -0700345 mSurface = mAttachInfo.mHardwareRenderer.createSurfaceTexture(mLayer);
346 }
Romain Guye5e0c502011-06-15 15:18:31 -0700347 nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
Jamie Gennis2af35242012-04-05 11:44:30 -0700348 nCreateNativeWindow(mSurface);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700349
Romain Guy8f0095c2011-05-02 17:24:22 -0700350 mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() {
Romain Guyaa6c24c2011-04-28 18:40:04 -0700351 @Override
352 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
353 // Per SurfaceTexture's documentation, the callback may be invoked
354 // from an arbitrary thread
Romain Guy58f4edb2011-06-24 14:51:38 -0700355 synchronized (mLock) {
356 mUpdateLayer = true;
357 }
Romain Guy52c145f2012-05-17 18:06:23 -0700358
359 if (Looper.myLooper() == Looper.getMainLooper()) {
360 invalidate();
361 } else {
362 postInvalidate();
363 }
Romain Guyaa6c24c2011-04-28 18:40:04 -0700364 }
Romain Guy8f0095c2011-05-02 17:24:22 -0700365 };
366 mSurface.setOnFrameAvailableListener(mUpdateListener);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700367
Jamie Gennis8a34d682012-04-17 16:01:34 -0700368 if (mListener != null && !mUpdateSurface) {
Romain Guy451ce442011-06-10 15:40:36 -0700369 mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight());
Romain Guyaa6c24c2011-04-28 18:40:04 -0700370 }
Chet Haased15ebf22012-09-05 11:40:29 -0700371 mLayer.setLayerPaint(mLayerPaint);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700372 }
373
Jamie Gennis2af35242012-04-05 11:44:30 -0700374 if (mUpdateSurface) {
375 // Someone has requested that we use a specific SurfaceTexture, so
376 // tell mLayer about it and set the SurfaceTexture to use the
377 // current view size.
378 mUpdateSurface = false;
Romain Guy51f7c6b2012-05-21 16:32:59 -0700379
380 // Since we are updating the layer, force an update to ensure its
381 // parameters are correct (width, height, transform, etc.)
382 synchronized (mLock) {
383 mUpdateLayer = true;
384 }
385 mMatrixChanged = true;
386
Jamie Gennis2af35242012-04-05 11:44:30 -0700387 mAttachInfo.mHardwareRenderer.setSurfaceTexture(mLayer, mSurface);
388 nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
389 }
390
Romain Guy58f4edb2011-06-24 14:51:38 -0700391 applyUpdate();
Alexandre Eliasc01391f2011-08-19 16:03:16 -0700392 applyTransformMatrix();
Romain Guy302a9df2011-08-16 13:55:02 -0700393
Romain Guyaa6c24c2011-04-28 18:40:04 -0700394 return mLayer;
395 }
396
Romain Guy8f0095c2011-05-02 17:24:22 -0700397 @Override
398 protected void onVisibilityChanged(View changedView, int visibility) {
399 super.onVisibilityChanged(changedView, visibility);
400
401 if (mSurface != null) {
402 // When the view becomes invisible, stop updating it, it's a waste of CPU
403 // To cancel updates, the easiest thing to do is simply to remove the
404 // updates listener
405 if (visibility == VISIBLE) {
406 mSurface.setOnFrameAvailableListener(mUpdateListener);
Romain Guya9489272011-06-22 20:58:11 -0700407 updateLayer();
Romain Guy8f0095c2011-05-02 17:24:22 -0700408 } else {
409 mSurface.setOnFrameAvailableListener(null);
410 }
411 }
412 }
413
Romain Guya9489272011-06-22 20:58:11 -0700414 private void updateLayer() {
Romain Guy58f4edb2011-06-24 14:51:38 -0700415 mUpdateLayer = true;
416 invalidate();
417 }
Jamie Gennis2af35242012-04-05 11:44:30 -0700418
Romain Guy58f4edb2011-06-24 14:51:38 -0700419 private void applyUpdate() {
420 if (mLayer == null) {
Romain Guya9489272011-06-22 20:58:11 -0700421 return;
422 }
423
Romain Guy58f4edb2011-06-24 14:51:38 -0700424 synchronized (mLock) {
425 if (mUpdateLayer) {
426 mUpdateLayer = false;
427 } else {
428 return;
429 }
430 }
431
Romain Guy02ccac62011-06-24 13:20:23 -0700432 mLayer.update(getWidth(), getHeight(), mOpaque);
Romain Guya9489272011-06-22 20:58:11 -0700433
Grace Klobacf559372011-06-22 23:05:40 -0700434 if (mListener != null) {
435 mListener.onSurfaceTextureUpdated(mSurface);
436 }
Romain Guya9489272011-06-22 20:58:11 -0700437 }
438
Romain Guyaa6c24c2011-04-28 18:40:04 -0700439 /**
Romain Guy302a9df2011-08-16 13:55:02 -0700440 * <p>Sets the transform to associate with this texture view.
441 * The specified transform applies to the underlying surface
442 * texture and does not affect the size or position of the view
443 * itself, only of its content.</p>
444 *
445 * <p>Some transforms might prevent the content from drawing
446 * all the pixels contained within this view's bounds. In such
447 * situations, make sure this texture view is not marked opaque.</p>
448 *
449 * @param transform The transform to apply to the content of
450 * this view.
451 *
452 * @see #getTransform(android.graphics.Matrix)
453 * @see #isOpaque()
454 * @see #setOpaque(boolean)
455 */
456 public void setTransform(Matrix transform) {
457 mMatrix.set(transform);
458 mMatrixChanged = true;
Alexandre Eliasc01391f2011-08-19 16:03:16 -0700459 invalidateParentIfNeeded();
Romain Guy302a9df2011-08-16 13:55:02 -0700460 }
461
462 /**
463 * Returns the transform associated with this texture view.
464 *
465 * @param transform The {@link Matrix} in which to copy the current
466 * transform. Can be null.
467 *
468 * @return The specified matrix if not null or a new {@link Matrix}
469 * instance otherwise.
470 *
471 * @see #setTransform(android.graphics.Matrix)
472 */
473 public Matrix getTransform(Matrix transform) {
474 if (transform == null) {
475 transform = new Matrix();
476 }
477
478 transform.set(mMatrix);
479
480 return transform;
481 }
482
Alexandre Eliasc01391f2011-08-19 16:03:16 -0700483 private void applyTransformMatrix() {
Romain Guy51f7c6b2012-05-21 16:32:59 -0700484 if (mMatrixChanged && mLayer != null) {
Alexandre Eliasc01391f2011-08-19 16:03:16 -0700485 mLayer.setTransform(mMatrix);
486 mMatrixChanged = false;
487 }
488 }
489
Romain Guy302a9df2011-08-16 13:55:02 -0700490 /**
Romain Guy77a81162011-06-14 16:45:55 -0700491 * <p>Returns a {@link android.graphics.Bitmap} representation of the content
492 * of the associated surface texture. If the surface texture is not available,
493 * this method returns null.</p>
494 *
495 * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888}
496 * pixel format and its dimensions are the same as this view's.</p>
497 *
498 * <p><strong>Do not</strong> invoke this method from a drawing method
499 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
500 *
Romain Guyd6b2a002011-06-17 17:45:59 -0700501 * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
502 *
Romain Guy77a81162011-06-14 16:45:55 -0700503 * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
504 * texture is not available or the width &lt;= 0 or the height &lt;= 0
505 *
506 * @see #isAvailable()
507 * @see #getBitmap(android.graphics.Bitmap)
508 * @see #getBitmap(int, int)
509 */
510 public Bitmap getBitmap() {
511 return getBitmap(getWidth(), getHeight());
512 }
513
514 /**
515 * <p>Returns a {@link android.graphics.Bitmap} representation of the content
516 * of the associated surface texture. If the surface texture is not available,
517 * this method returns null.</p>
518 *
519 * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888}
520 * pixel format.</p>
521 *
522 * <p><strong>Do not</strong> invoke this method from a drawing method
523 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
524 *
Romain Guyd6b2a002011-06-17 17:45:59 -0700525 * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
526 *
Romain Guy77a81162011-06-14 16:45:55 -0700527 * @param width The width of the bitmap to create
528 * @param height The height of the bitmap to create
529 *
530 * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
531 * texture is not available or width is &lt;= 0 or height is &lt;= 0
532 *
533 * @see #isAvailable()
534 * @see #getBitmap(android.graphics.Bitmap)
535 * @see #getBitmap()
536 */
537 public Bitmap getBitmap(int width, int height) {
538 if (isAvailable() && width > 0 && height > 0) {
Dianne Hackborndde331c2012-08-03 14:01:57 -0700539 return getBitmap(Bitmap.createBitmap(getResources().getDisplayMetrics(),
540 width, height, Bitmap.Config.ARGB_8888));
Romain Guy77a81162011-06-14 16:45:55 -0700541 }
542 return null;
543 }
544
545 /**
546 * <p>Copies the content of this view's surface texture into the specified
547 * bitmap. If the surface texture is not available, the copy is not executed.
548 * The content of the surface texture will be scaled to fit exactly inside
549 * the specified bitmap.</p>
550 *
551 * <p><strong>Do not</strong> invoke this method from a drawing method
552 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
553 *
Romain Guyd6b2a002011-06-17 17:45:59 -0700554 * <p>If an error occurs, the bitmap is left unchanged.</p>
555 *
Romain Guy77a81162011-06-14 16:45:55 -0700556 * @param bitmap The bitmap to copy the content of the surface texture into,
557 * cannot be null, all configurations are supported
558 *
559 * @return The bitmap specified as a parameter
560 *
561 * @see #isAvailable()
562 * @see #getBitmap(int, int)
563 * @see #getBitmap()
Romain Guy589b0bb2011-10-10 13:57:47 -0700564 *
565 * @throws IllegalStateException if the hardware rendering context cannot be
566 * acquired to capture the bitmap
Romain Guy77a81162011-06-14 16:45:55 -0700567 */
568 public Bitmap getBitmap(Bitmap bitmap) {
569 if (bitmap != null && isAvailable()) {
Romain Guy589b0bb2011-10-10 13:57:47 -0700570 AttachInfo info = mAttachInfo;
571 if (info != null && info.mHardwareRenderer != null &&
572 info.mHardwareRenderer.isEnabled()) {
573 if (!info.mHardwareRenderer.validate()) {
574 throw new IllegalStateException("Could not acquire hardware rendering context");
575 }
576 }
577
578 applyUpdate();
579 applyTransformMatrix();
580
Romain Guy78245f72012-05-11 10:01:42 -0700581 // This case can happen if the app invokes setSurfaceTexture() before
582 // we are able to create the hardware layer. We can safely initialize
583 // the layer here thanks to the validate() call at the beginning of
584 // this method
585 if (mLayer == null && mUpdateSurface) {
586 getHardwareLayer();
587 }
588
589 if (mLayer != null) {
590 mLayer.copyInto(bitmap);
591 }
Romain Guy77a81162011-06-14 16:45:55 -0700592 }
593 return bitmap;
594 }
595
596 /**
597 * Returns true if the {@link SurfaceTexture} associated with this
598 * TextureView is available for rendering. When this method returns
599 * true, {@link #getSurfaceTexture()} returns a valid surface texture.
600 */
601 public boolean isAvailable() {
602 return mSurface != null;
603 }
604
605 /**
Romain Guy6be3d552011-07-14 18:08:37 -0700606 * <p>Start editing the pixels in the surface. The returned Canvas can be used
607 * to draw into the surface's bitmap. A null is returned if the surface has
608 * not been created or otherwise cannot be edited. You will usually need
609 * to implement
610 * {@link SurfaceTextureListener#onSurfaceTextureAvailable(android.graphics.SurfaceTexture, int, int)}
611 * to find out when the Surface is available for use.</p>
612 *
613 * <p>The content of the Surface is never preserved between unlockCanvas()
614 * and lockCanvas(), for this reason, every pixel within the Surface area
615 * must be written. The only exception to this rule is when a dirty
616 * rectangle is specified, in which case, non-dirty pixels will be
617 * preserved.</p>
618 *
Romain Guy462785f2011-09-27 17:42:10 -0700619 * <p>This method can only be used if the underlying surface is not already
620 * owned by another producer. For instance, if the TextureView is being used
621 * to render the camera's preview you cannot invoke this method.</p>
622 *
Romain Guy6be3d552011-07-14 18:08:37 -0700623 * @return A Canvas used to draw into the surface.
624 *
625 * @see #lockCanvas(android.graphics.Rect)
626 * @see #unlockCanvasAndPost(android.graphics.Canvas)
627 */
628 public Canvas lockCanvas() {
629 return lockCanvas(null);
630 }
631
632 /**
633 * Just like {@link #lockCanvas()} but allows specification of a dirty
634 * rectangle. Every pixel within that rectangle must be written; however
635 * pixels outside the dirty rectangle will be preserved by the next call
636 * to lockCanvas().
637 *
638 * @param dirty Area of the surface that will be modified.
639
640 * @return A Canvas used to draw into the surface.
641 *
642 * @see #lockCanvas()
643 * @see #unlockCanvasAndPost(android.graphics.Canvas)
644 */
645 public Canvas lockCanvas(Rect dirty) {
646 if (!isAvailable()) return null;
647
648 if (mCanvas == null) {
649 mCanvas = new Canvas();
650 }
651
652 synchronized (mNativeWindowLock) {
653 nLockCanvas(mNativeWindow, mCanvas, dirty);
654 }
655 mSaveCount = mCanvas.save();
656
657 return mCanvas;
658 }
659
660 /**
661 * Finish editing pixels in the surface. After this call, the surface's
662 * current pixels will be shown on the screen, but its content is lost,
663 * in particular there is no guarantee that the content of the Surface
664 * will remain unchanged when lockCanvas() is called again.
665 *
666 * @param canvas The Canvas previously returned by lockCanvas()
667 *
668 * @see #lockCanvas()
669 * @see #lockCanvas(android.graphics.Rect)
670 */
671 public void unlockCanvasAndPost(Canvas canvas) {
672 if (mCanvas != null && canvas == mCanvas) {
673 canvas.restoreToCount(mSaveCount);
674 mSaveCount = 0;
675
676 synchronized (mNativeWindowLock) {
677 nUnlockCanvasAndPost(mNativeWindow, mCanvas);
678 }
679 }
680 }
681
682 /**
Romain Guyaa6c24c2011-04-28 18:40:04 -0700683 * Returns the {@link SurfaceTexture} used by this view. This method
Romain Guy77a81162011-06-14 16:45:55 -0700684 * may return null if the view is not attached to a window or if the surface
685 * texture has not been initialized yet.
686 *
687 * @see #isAvailable()
Romain Guyaa6c24c2011-04-28 18:40:04 -0700688 */
689 public SurfaceTexture getSurfaceTexture() {
690 return mSurface;
691 }
692
693 /**
Jamie Gennis2af35242012-04-05 11:44:30 -0700694 * Set the {@link SurfaceTexture} for this view to use. If a {@link
695 * SurfaceTexture} is already being used by this view, it is immediately
696 * released and not be usable any more. The {@link
697 * SurfaceTextureListener#onSurfaceTextureDestroyed} callback is <b>not</b>
Jamie Gennis8a34d682012-04-17 16:01:34 -0700698 * called for the previous {@link SurfaceTexture}. Similarly, the {@link
699 * SurfaceTextureListener#onSurfaceTextureAvailable} callback is <b>not</b>
700 * called for the {@link SurfaceTexture} passed to setSurfaceTexture.
Jamie Gennis2af35242012-04-05 11:44:30 -0700701 *
702 * The {@link SurfaceTexture} object must be detached from all OpenGL ES
703 * contexts prior to calling this method.
704 *
705 * @param surfaceTexture The {@link SurfaceTexture} that the view should use.
706 * @see SurfaceTexture#detachFromGLContext()
Jamie Gennis2af35242012-04-05 11:44:30 -0700707 */
708 public void setSurfaceTexture(SurfaceTexture surfaceTexture) {
709 if (surfaceTexture == null) {
710 throw new NullPointerException("surfaceTexture must not be null");
711 }
712 if (mSurface != null) {
713 mSurface.release();
714 }
715 mSurface = surfaceTexture;
716 mUpdateSurface = true;
717 invalidateParentIfNeeded();
718 }
719
720 /**
Romain Guyaa6c24c2011-04-28 18:40:04 -0700721 * Returns the {@link SurfaceTextureListener} currently associated with this
722 * texture view.
723 *
724 * @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener)
725 * @see SurfaceTextureListener
726 */
727 public SurfaceTextureListener getSurfaceTextureListener() {
728 return mListener;
729 }
730
731 /**
732 * Sets the {@link SurfaceTextureListener} used to listen to surface
733 * texture events.
734 *
735 * @see #getSurfaceTextureListener()
736 * @see SurfaceTextureListener
737 */
738 public void setSurfaceTextureListener(SurfaceTextureListener listener) {
739 mListener = listener;
740 }
741
742 /**
743 * This listener can be used to be notified when the surface texture
744 * associated with this texture view is available.
745 */
746 public static interface SurfaceTextureListener {
747 /**
748 * Invoked when a {@link TextureView}'s SurfaceTexture is ready for use.
749 *
750 * @param surface The surface returned by
751 * {@link android.view.TextureView#getSurfaceTexture()}
Romain Guy451ce442011-06-10 15:40:36 -0700752 * @param width The width of the surface
753 * @param height The height of the surface
Romain Guyaa6c24c2011-04-28 18:40:04 -0700754 */
Romain Guy451ce442011-06-10 15:40:36 -0700755 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height);
Romain Guy8f0095c2011-05-02 17:24:22 -0700756
757 /**
758 * Invoked when the {@link SurfaceTexture}'s buffers size changed.
759 *
760 * @param surface The surface returned by
761 * {@link android.view.TextureView#getSurfaceTexture()}
762 * @param width The new width of the surface
763 * @param height The new height of the surface
764 */
765 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height);
Romain Guy451ce442011-06-10 15:40:36 -0700766
767 /**
768 * Invoked when the specified {@link SurfaceTexture} is about to be destroyed.
Grace Kloba402f0552011-08-09 18:47:17 -0700769 * If returns true, no rendering should happen inside the surface texture after this method
770 * is invoked. If returns false, the client needs to call {@link SurfaceTexture#release()}.
Romain Guy451ce442011-06-10 15:40:36 -0700771 *
772 * @param surface The surface about to be destroyed
773 */
Grace Kloba402f0552011-08-09 18:47:17 -0700774 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface);
Grace Klobacf559372011-06-22 23:05:40 -0700775
776 /**
777 * Invoked when the specified {@link SurfaceTexture} is updated through
778 * {@link SurfaceTexture#updateTexImage()}.
779 *
780 * @param surface The surface just updated
781 */
782 public void onSurfaceTextureUpdated(SurfaceTexture surface);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700783 }
Romain Guy8f0095c2011-05-02 17:24:22 -0700784
Romain Guy6be3d552011-07-14 18:08:37 -0700785 private native void nCreateNativeWindow(SurfaceTexture surface);
786 private native void nDestroyNativeWindow();
787
Romain Guyd6b2a002011-06-17 17:45:59 -0700788 private static native void nSetDefaultBufferSize(SurfaceTexture surfaceTexture,
789 int width, int height);
Romain Guy6be3d552011-07-14 18:08:37 -0700790
791 private static native void nLockCanvas(int nativeWindow, Canvas canvas, Rect dirty);
792 private static native void nUnlockCanvasAndPost(int nativeWindow, Canvas canvas);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700793}