blob: 244dc337d6f98dd6f711cf44fcf2b7666a388ed9 [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 Guy67603c62013-06-27 10:58:10 -0700111 private boolean mHadSurface;
Romain Guyaa6c24c2011-04-28 18:40:04 -0700112
Romain Guya9489272011-06-22 20:58:11 -0700113 private boolean mOpaque = true;
Romain Guyc989d862011-06-22 14:53:39 -0700114
Romain Guy302a9df2011-08-16 13:55:02 -0700115 private final Matrix mMatrix = new Matrix();
116 private boolean mMatrixChanged;
117
Romain Guy58f4edb2011-06-24 14:51:38 -0700118 private final Object[] mLock = new Object[0];
119 private boolean mUpdateLayer;
Jamie Gennis2af35242012-04-05 11:44:30 -0700120 private boolean mUpdateSurface;
Romain Guy58f4edb2011-06-24 14:51:38 -0700121
Romain Guy8f0095c2011-05-02 17:24:22 -0700122 private SurfaceTexture.OnFrameAvailableListener mUpdateListener;
Romain Guyaa6c24c2011-04-28 18:40:04 -0700123
Romain Guy6be3d552011-07-14 18:08:37 -0700124 private Canvas mCanvas;
125 private int mSaveCount;
126
127 private final Object[] mNativeWindowLock = new Object[0];
128 // Used from native code, do not write!
129 @SuppressWarnings({"UnusedDeclaration"})
130 private int mNativeWindow;
131
Romain Guyaa6c24c2011-04-28 18:40:04 -0700132 /**
133 * Creates a new TextureView.
134 *
135 * @param context The context to associate this view with.
136 */
137 public TextureView(Context context) {
138 super(context);
139 init();
140 }
141
142 /**
143 * Creates a new TextureView.
144 *
145 * @param context The context to associate this view with.
146 * @param attrs The attributes of the XML tag that is inflating the view.
147 */
148 @SuppressWarnings({"UnusedDeclaration"})
149 public TextureView(Context context, AttributeSet attrs) {
150 super(context, attrs);
151 init();
152 }
153
154 /**
155 * Creates a new TextureView.
156 *
157 * @param context The context to associate this view with.
158 * @param attrs The attributes of the XML tag that is inflating the view.
159 * @param defStyle The default style to apply to this view. If 0, no style
160 * will be applied (beyond what is included in the theme). This may
161 * either be an attribute resource, whose value will be retrieved
162 * from the current theme, or an explicit style resource.
163 */
164 @SuppressWarnings({"UnusedDeclaration"})
165 public TextureView(Context context, AttributeSet attrs, int defStyle) {
166 super(context, attrs, defStyle);
167 init();
168 }
169
170 private void init() {
171 mLayerPaint = new Paint();
172 }
173
Romain Guya9489272011-06-22 20:58:11 -0700174 /**
175 * {@inheritDoc}
176 */
177 @Override
178 public boolean isOpaque() {
179 return mOpaque;
180 }
181
182 /**
183 * Indicates whether the content of this TextureView is opaque. The
184 * content is assumed to be opaque by default.
185 *
186 * @param opaque True if the content of this TextureView is opaque,
187 * false otherwise
188 */
189 public void setOpaque(boolean opaque) {
190 if (opaque != mOpaque) {
191 mOpaque = opaque;
Romain Guya8a2f972012-04-12 16:33:44 -0700192 if (mLayer != null) {
Romain Guy88801b22012-10-05 14:58:33 -0700193 updateLayerAndInvalidate();
Romain Guya8a2f972012-04-12 16:33:44 -0700194 }
Romain Guya9489272011-06-22 20:58:11 -0700195 }
196 }
197
Romain Guyaa6c24c2011-04-28 18:40:04 -0700198 @Override
199 protected void onAttachedToWindow() {
200 super.onAttachedToWindow();
201
202 if (!isHardwareAccelerated()) {
Romain Guy77a81162011-06-14 16:45:55 -0700203 Log.w(LOG_TAG, "A TextureView or a subclass can only be "
Romain Guyaa6c24c2011-04-28 18:40:04 -0700204 + "used with hardware acceleration enabled.");
205 }
Romain Guy67603c62013-06-27 10:58:10 -0700206
207 if (mHadSurface) {
208 invalidate(true);
209 mHadSurface = false;
210 }
Romain Guyaa6c24c2011-04-28 18:40:04 -0700211 }
212
Romain Guy451ce442011-06-10 15:40:36 -0700213 @Override
214 protected void onDetachedFromWindow() {
215 super.onDetachedFromWindow();
Romain Guy1ac47652012-04-11 18:15:20 -0700216 if (mLayer != null && mAttachInfo != null && mAttachInfo.mHardwareRenderer != null) {
217 boolean success = mAttachInfo.mHardwareRenderer.safelyRun(new Runnable() {
218 @Override
219 public void run() {
220 destroySurface();
221 }
222 });
223
224 if (!success) {
225 Log.w(LOG_TAG, "TextureView was not able to destroy its surface: " + this);
226 }
227 }
Romain Guy31f2c2e2011-11-21 10:55:41 -0800228 }
Romain Guy451ce442011-06-10 15:40:36 -0700229
Romain Guy31f2c2e2011-11-21 10:55:41 -0800230 private void destroySurface() {
Romain Guy80429c42011-06-24 17:20:32 -0700231 if (mLayer != null) {
Jamie Gennis2af35242012-04-05 11:44:30 -0700232 mSurface.detachFromGLContext();
Romain Guy52b307e2012-10-07 17:55:17 -0700233 // SurfaceTexture owns the texture name and detachFromGLContext
234 // should have deleted it
Romain Guyef09a212012-09-25 12:17:14 -0700235 mLayer.clearStorage();
Jamie Gennis2af35242012-04-05 11:44:30 -0700236
Grace Kloba402f0552011-08-09 18:47:17 -0700237 boolean shouldRelease = true;
Romain Guy451ce442011-06-10 15:40:36 -0700238 if (mListener != null) {
Grace Kloba402f0552011-08-09 18:47:17 -0700239 shouldRelease = mListener.onSurfaceTextureDestroyed(mSurface);
Romain Guy451ce442011-06-10 15:40:36 -0700240 }
241
Romain Guy6be3d552011-07-14 18:08:37 -0700242 synchronized (mNativeWindowLock) {
243 nDestroyNativeWindow();
244 }
245
246 mLayer.destroy();
Grace Kloba402f0552011-08-09 18:47:17 -0700247 if (shouldRelease) mSurface.release();
Romain Guy451ce442011-06-10 15:40:36 -0700248 mSurface = null;
249 mLayer = null;
Romain Guy67603c62013-06-27 10:58:10 -0700250
251 mHadSurface = true;
Romain Guy451ce442011-06-10 15:40:36 -0700252 }
253 }
254
Romain Guyaa6c24c2011-04-28 18:40:04 -0700255 /**
256 * The layer type of a TextureView is ignored since a TextureView is always
257 * considered to act as a hardware layer. The optional paint supplied to this
258 * method will however be taken into account when rendering the content of
259 * this TextureView.
260 *
261 * @param layerType The ype of layer to use with this view, must be one of
262 * {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
263 * {@link #LAYER_TYPE_HARDWARE}
264 * @param paint The paint used to compose the layer. This argument is optional
265 * and can be null. It is ignored when the layer type is
266 * {@link #LAYER_TYPE_NONE}
267 */
268 @Override
269 public void setLayerType(int layerType, Paint paint) {
270 if (paint != mLayerPaint) {
Romain Guyefb709a2013-04-30 11:12:26 -0700271 mLayerPaint = paint == null ? new Paint() : paint;
Romain Guyaa6c24c2011-04-28 18:40:04 -0700272 invalidate();
273 }
274 }
275
276 /**
277 * Always returns {@link #LAYER_TYPE_HARDWARE}.
278 */
279 @Override
280 public int getLayerType() {
281 return LAYER_TYPE_HARDWARE;
282 }
283
Romain Guy59c7f802011-09-29 17:21:45 -0700284 @Override
285 boolean hasStaticLayer() {
286 return true;
287 }
288
Romain Guyaa6c24c2011-04-28 18:40:04 -0700289 /**
290 * Calling this method has no effect.
291 */
292 @Override
293 public void buildLayer() {
294 }
295
296 /**
297 * Subclasses of TextureView cannot do their own rendering
298 * with the {@link Canvas} object.
299 *
300 * @param canvas The Canvas to which the View is rendered.
301 */
302 @Override
303 public final void draw(Canvas canvas) {
Romain Guy52b307e2012-10-07 17:55:17 -0700304 // NOTE: Maintain this carefully (see View.java)
305 mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
306
Romain Guy58f4edb2011-06-24 14:51:38 -0700307 applyUpdate();
Alexandre Eliasc01391f2011-08-19 16:03:16 -0700308 applyTransformMatrix();
Romain Guyaa6c24c2011-04-28 18:40:04 -0700309 }
310
311 /**
312 * Subclasses of TextureView cannot do their own rendering
313 * with the {@link Canvas} object.
314 *
315 * @param canvas The Canvas to which the View is rendered.
316 */
317 @Override
318 protected final void onDraw(Canvas canvas) {
319 }
320
321 @Override
Romain Guy8f0095c2011-05-02 17:24:22 -0700322 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
323 super.onSizeChanged(w, h, oldw, oldh);
324 if (mSurface != null) {
Romain Guye5e0c502011-06-15 15:18:31 -0700325 nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
Romain Guy88801b22012-10-05 14:58:33 -0700326 updateLayer();
Romain Guy451ce442011-06-10 15:40:36 -0700327 if (mListener != null) {
328 mListener.onSurfaceTextureSizeChanged(mSurface, getWidth(), getHeight());
329 }
Romain Guy8f0095c2011-05-02 17:24:22 -0700330 }
331 }
332
333 @Override
Romain Guya998dff2012-03-23 18:58:36 -0700334 boolean destroyLayer(boolean valid) {
Romain Guy16260e72011-09-01 14:26:11 -0700335 return false;
336 }
337
Romain Guy1766b0e2011-11-21 18:28:49 -0800338 /**
339 * @hide
340 */
Romain Guy16260e72011-09-01 14:26:11 -0700341 @Override
Romain Guy31f2c2e2011-11-21 10:55:41 -0800342 protected void destroyHardwareResources() {
343 super.destroyHardwareResources();
344 destroySurface();
345 invalidateParentCaches();
346 invalidate(true);
347 }
348
349 @Override
Michael Jurka7e52caf2012-03-06 15:57:06 -0800350 HardwareLayer getHardwareLayer() {
Romain Guy52b307e2012-10-07 17:55:17 -0700351 // NOTE: Maintain these two lines very carefully (see View.java)
352 mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
353 mPrivateFlags &= ~PFLAG_DIRTY_MASK;
354
Romain Guyaa6c24c2011-04-28 18:40:04 -0700355 if (mLayer == null) {
Romain Guy58f4edb2011-06-24 14:51:38 -0700356 if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
357 return null;
358 }
359
Romain Guya9489272011-06-22 20:58:11 -0700360 mLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer(mOpaque);
Jamie Gennis2af35242012-04-05 11:44:30 -0700361 if (!mUpdateSurface) {
Jamie Gennis8a34d682012-04-17 16:01:34 -0700362 // Create a new SurfaceTexture for the layer.
Jamie Gennis2af35242012-04-05 11:44:30 -0700363 mSurface = mAttachInfo.mHardwareRenderer.createSurfaceTexture(mLayer);
364 }
Romain Guye5e0c502011-06-15 15:18:31 -0700365 nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
Jamie Gennis2af35242012-04-05 11:44:30 -0700366 nCreateNativeWindow(mSurface);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700367
Romain Guy8f0095c2011-05-02 17:24:22 -0700368 mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() {
Romain Guyaa6c24c2011-04-28 18:40:04 -0700369 @Override
370 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
371 // Per SurfaceTexture's documentation, the callback may be invoked
372 // from an arbitrary thread
Romain Guy88801b22012-10-05 14:58:33 -0700373 updateLayer();
Romain Guy52c145f2012-05-17 18:06:23 -0700374
375 if (Looper.myLooper() == Looper.getMainLooper()) {
376 invalidate();
377 } else {
378 postInvalidate();
379 }
Romain Guyaa6c24c2011-04-28 18:40:04 -0700380 }
Romain Guy8f0095c2011-05-02 17:24:22 -0700381 };
382 mSurface.setOnFrameAvailableListener(mUpdateListener);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700383
Jamie Gennis8a34d682012-04-17 16:01:34 -0700384 if (mListener != null && !mUpdateSurface) {
Romain Guy451ce442011-06-10 15:40:36 -0700385 mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight());
Romain Guyaa6c24c2011-04-28 18:40:04 -0700386 }
Chet Haased15ebf22012-09-05 11:40:29 -0700387 mLayer.setLayerPaint(mLayerPaint);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700388 }
389
Jamie Gennis2af35242012-04-05 11:44:30 -0700390 if (mUpdateSurface) {
391 // Someone has requested that we use a specific SurfaceTexture, so
392 // tell mLayer about it and set the SurfaceTexture to use the
393 // current view size.
394 mUpdateSurface = false;
Romain Guy51f7c6b2012-05-21 16:32:59 -0700395
396 // Since we are updating the layer, force an update to ensure its
397 // parameters are correct (width, height, transform, etc.)
Romain Guy88801b22012-10-05 14:58:33 -0700398 updateLayer();
Romain Guy51f7c6b2012-05-21 16:32:59 -0700399 mMatrixChanged = true;
400
Jamie Gennis2af35242012-04-05 11:44:30 -0700401 mAttachInfo.mHardwareRenderer.setSurfaceTexture(mLayer, mSurface);
402 nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
403 }
404
Romain Guy58f4edb2011-06-24 14:51:38 -0700405 applyUpdate();
Alexandre Eliasc01391f2011-08-19 16:03:16 -0700406 applyTransformMatrix();
Romain Guy302a9df2011-08-16 13:55:02 -0700407
Romain Guyaa6c24c2011-04-28 18:40:04 -0700408 return mLayer;
409 }
410
Romain Guy8f0095c2011-05-02 17:24:22 -0700411 @Override
412 protected void onVisibilityChanged(View changedView, int visibility) {
413 super.onVisibilityChanged(changedView, visibility);
414
415 if (mSurface != null) {
416 // When the view becomes invisible, stop updating it, it's a waste of CPU
417 // To cancel updates, the easiest thing to do is simply to remove the
418 // updates listener
419 if (visibility == VISIBLE) {
420 mSurface.setOnFrameAvailableListener(mUpdateListener);
Romain Guy88801b22012-10-05 14:58:33 -0700421 updateLayerAndInvalidate();
Romain Guy8f0095c2011-05-02 17:24:22 -0700422 } else {
423 mSurface.setOnFrameAvailableListener(null);
424 }
425 }
426 }
427
Romain Guya9489272011-06-22 20:58:11 -0700428 private void updateLayer() {
Romain Guy88801b22012-10-05 14:58:33 -0700429 synchronized (mLock) {
430 mUpdateLayer = true;
431 }
432 }
433
434 private void updateLayerAndInvalidate() {
435 synchronized (mLock) {
436 mUpdateLayer = true;
437 }
Romain Guy58f4edb2011-06-24 14:51:38 -0700438 invalidate();
439 }
Jamie Gennis2af35242012-04-05 11:44:30 -0700440
Romain Guy58f4edb2011-06-24 14:51:38 -0700441 private void applyUpdate() {
442 if (mLayer == null) {
Romain Guya9489272011-06-22 20:58:11 -0700443 return;
444 }
445
Romain Guy58f4edb2011-06-24 14:51:38 -0700446 synchronized (mLock) {
447 if (mUpdateLayer) {
448 mUpdateLayer = false;
449 } else {
450 return;
451 }
452 }
453
Romain Guy02ccac62011-06-24 13:20:23 -0700454 mLayer.update(getWidth(), getHeight(), mOpaque);
Romain Guya9489272011-06-22 20:58:11 -0700455
Grace Klobacf559372011-06-22 23:05:40 -0700456 if (mListener != null) {
457 mListener.onSurfaceTextureUpdated(mSurface);
458 }
Romain Guya9489272011-06-22 20:58:11 -0700459 }
460
Romain Guyaa6c24c2011-04-28 18:40:04 -0700461 /**
Romain Guy302a9df2011-08-16 13:55:02 -0700462 * <p>Sets the transform to associate with this texture view.
463 * The specified transform applies to the underlying surface
464 * texture and does not affect the size or position of the view
465 * itself, only of its content.</p>
466 *
467 * <p>Some transforms might prevent the content from drawing
468 * all the pixels contained within this view's bounds. In such
469 * situations, make sure this texture view is not marked opaque.</p>
470 *
471 * @param transform The transform to apply to the content of
472 * this view.
473 *
474 * @see #getTransform(android.graphics.Matrix)
475 * @see #isOpaque()
476 * @see #setOpaque(boolean)
477 */
478 public void setTransform(Matrix transform) {
479 mMatrix.set(transform);
480 mMatrixChanged = true;
Alexandre Eliasc01391f2011-08-19 16:03:16 -0700481 invalidateParentIfNeeded();
Romain Guy302a9df2011-08-16 13:55:02 -0700482 }
483
484 /**
485 * Returns the transform associated with this texture view.
486 *
487 * @param transform The {@link Matrix} in which to copy the current
488 * transform. Can be null.
489 *
490 * @return The specified matrix if not null or a new {@link Matrix}
491 * instance otherwise.
492 *
493 * @see #setTransform(android.graphics.Matrix)
494 */
495 public Matrix getTransform(Matrix transform) {
496 if (transform == null) {
497 transform = new Matrix();
498 }
499
500 transform.set(mMatrix);
501
502 return transform;
503 }
504
Alexandre Eliasc01391f2011-08-19 16:03:16 -0700505 private void applyTransformMatrix() {
Romain Guy51f7c6b2012-05-21 16:32:59 -0700506 if (mMatrixChanged && mLayer != null) {
Alexandre Eliasc01391f2011-08-19 16:03:16 -0700507 mLayer.setTransform(mMatrix);
508 mMatrixChanged = false;
509 }
510 }
511
Romain Guy302a9df2011-08-16 13:55:02 -0700512 /**
Romain Guy77a81162011-06-14 16:45:55 -0700513 * <p>Returns a {@link android.graphics.Bitmap} representation of the content
514 * of the associated surface texture. If the surface texture is not available,
515 * this method returns null.</p>
516 *
517 * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888}
518 * pixel format and its dimensions are the same as this view's.</p>
519 *
520 * <p><strong>Do not</strong> invoke this method from a drawing method
521 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
522 *
Romain Guyd6b2a002011-06-17 17:45:59 -0700523 * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
524 *
Romain Guy77a81162011-06-14 16:45:55 -0700525 * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
526 * texture is not available or the width &lt;= 0 or the height &lt;= 0
527 *
528 * @see #isAvailable()
529 * @see #getBitmap(android.graphics.Bitmap)
530 * @see #getBitmap(int, int)
531 */
532 public Bitmap getBitmap() {
533 return getBitmap(getWidth(), getHeight());
534 }
535
536 /**
537 * <p>Returns a {@link android.graphics.Bitmap} representation of the content
538 * of the associated surface texture. If the surface texture is not available,
539 * this method returns null.</p>
540 *
541 * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888}
542 * pixel format.</p>
543 *
544 * <p><strong>Do not</strong> invoke this method from a drawing method
545 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
546 *
Romain Guyd6b2a002011-06-17 17:45:59 -0700547 * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
548 *
Romain Guy77a81162011-06-14 16:45:55 -0700549 * @param width The width of the bitmap to create
550 * @param height The height of the bitmap to create
551 *
552 * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
553 * texture is not available or width is &lt;= 0 or height is &lt;= 0
554 *
555 * @see #isAvailable()
556 * @see #getBitmap(android.graphics.Bitmap)
557 * @see #getBitmap()
558 */
559 public Bitmap getBitmap(int width, int height) {
560 if (isAvailable() && width > 0 && height > 0) {
Dianne Hackborndde331c2012-08-03 14:01:57 -0700561 return getBitmap(Bitmap.createBitmap(getResources().getDisplayMetrics(),
562 width, height, Bitmap.Config.ARGB_8888));
Romain Guy77a81162011-06-14 16:45:55 -0700563 }
564 return null;
565 }
566
567 /**
568 * <p>Copies the content of this view's surface texture into the specified
569 * bitmap. If the surface texture is not available, the copy is not executed.
570 * The content of the surface texture will be scaled to fit exactly inside
571 * the specified bitmap.</p>
572 *
573 * <p><strong>Do not</strong> invoke this method from a drawing method
574 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
575 *
Romain Guyd6b2a002011-06-17 17:45:59 -0700576 * <p>If an error occurs, the bitmap is left unchanged.</p>
577 *
Romain Guy77a81162011-06-14 16:45:55 -0700578 * @param bitmap The bitmap to copy the content of the surface texture into,
579 * cannot be null, all configurations are supported
580 *
581 * @return The bitmap specified as a parameter
582 *
583 * @see #isAvailable()
584 * @see #getBitmap(int, int)
585 * @see #getBitmap()
Romain Guy589b0bb2011-10-10 13:57:47 -0700586 *
587 * @throws IllegalStateException if the hardware rendering context cannot be
588 * acquired to capture the bitmap
Romain Guy77a81162011-06-14 16:45:55 -0700589 */
590 public Bitmap getBitmap(Bitmap bitmap) {
591 if (bitmap != null && isAvailable()) {
Romain Guy589b0bb2011-10-10 13:57:47 -0700592 AttachInfo info = mAttachInfo;
593 if (info != null && info.mHardwareRenderer != null &&
594 info.mHardwareRenderer.isEnabled()) {
595 if (!info.mHardwareRenderer.validate()) {
596 throw new IllegalStateException("Could not acquire hardware rendering context");
597 }
598 }
599
600 applyUpdate();
601 applyTransformMatrix();
602
Romain Guy78245f72012-05-11 10:01:42 -0700603 // This case can happen if the app invokes setSurfaceTexture() before
604 // we are able to create the hardware layer. We can safely initialize
605 // the layer here thanks to the validate() call at the beginning of
606 // this method
607 if (mLayer == null && mUpdateSurface) {
608 getHardwareLayer();
609 }
610
611 if (mLayer != null) {
612 mLayer.copyInto(bitmap);
613 }
Romain Guy77a81162011-06-14 16:45:55 -0700614 }
615 return bitmap;
616 }
617
618 /**
619 * Returns true if the {@link SurfaceTexture} associated with this
620 * TextureView is available for rendering. When this method returns
621 * true, {@link #getSurfaceTexture()} returns a valid surface texture.
622 */
623 public boolean isAvailable() {
624 return mSurface != null;
625 }
626
627 /**
Romain Guy6be3d552011-07-14 18:08:37 -0700628 * <p>Start editing the pixels in the surface. The returned Canvas can be used
629 * to draw into the surface's bitmap. A null is returned if the surface has
630 * not been created or otherwise cannot be edited. You will usually need
631 * to implement
632 * {@link SurfaceTextureListener#onSurfaceTextureAvailable(android.graphics.SurfaceTexture, int, int)}
633 * to find out when the Surface is available for use.</p>
634 *
635 * <p>The content of the Surface is never preserved between unlockCanvas()
636 * and lockCanvas(), for this reason, every pixel within the Surface area
637 * must be written. The only exception to this rule is when a dirty
638 * rectangle is specified, in which case, non-dirty pixels will be
639 * preserved.</p>
640 *
Romain Guy462785f2011-09-27 17:42:10 -0700641 * <p>This method can only be used if the underlying surface is not already
642 * owned by another producer. For instance, if the TextureView is being used
643 * to render the camera's preview you cannot invoke this method.</p>
644 *
Romain Guy6be3d552011-07-14 18:08:37 -0700645 * @return A Canvas used to draw into the surface.
646 *
647 * @see #lockCanvas(android.graphics.Rect)
648 * @see #unlockCanvasAndPost(android.graphics.Canvas)
649 */
650 public Canvas lockCanvas() {
651 return lockCanvas(null);
652 }
653
654 /**
655 * Just like {@link #lockCanvas()} but allows specification of a dirty
656 * rectangle. Every pixel within that rectangle must be written; however
657 * pixels outside the dirty rectangle will be preserved by the next call
658 * to lockCanvas().
659 *
660 * @param dirty Area of the surface that will be modified.
661
662 * @return A Canvas used to draw into the surface.
663 *
664 * @see #lockCanvas()
665 * @see #unlockCanvasAndPost(android.graphics.Canvas)
666 */
667 public Canvas lockCanvas(Rect dirty) {
668 if (!isAvailable()) return null;
669
670 if (mCanvas == null) {
671 mCanvas = new Canvas();
672 }
673
674 synchronized (mNativeWindowLock) {
675 nLockCanvas(mNativeWindow, mCanvas, dirty);
676 }
677 mSaveCount = mCanvas.save();
678
679 return mCanvas;
680 }
681
682 /**
683 * Finish editing pixels in the surface. After this call, the surface's
684 * current pixels will be shown on the screen, but its content is lost,
685 * in particular there is no guarantee that the content of the Surface
686 * will remain unchanged when lockCanvas() is called again.
687 *
688 * @param canvas The Canvas previously returned by lockCanvas()
689 *
690 * @see #lockCanvas()
691 * @see #lockCanvas(android.graphics.Rect)
692 */
693 public void unlockCanvasAndPost(Canvas canvas) {
694 if (mCanvas != null && canvas == mCanvas) {
695 canvas.restoreToCount(mSaveCount);
696 mSaveCount = 0;
697
698 synchronized (mNativeWindowLock) {
699 nUnlockCanvasAndPost(mNativeWindow, mCanvas);
700 }
701 }
702 }
703
704 /**
Romain Guyaa6c24c2011-04-28 18:40:04 -0700705 * Returns the {@link SurfaceTexture} used by this view. This method
Romain Guy77a81162011-06-14 16:45:55 -0700706 * may return null if the view is not attached to a window or if the surface
707 * texture has not been initialized yet.
708 *
709 * @see #isAvailable()
Romain Guyaa6c24c2011-04-28 18:40:04 -0700710 */
711 public SurfaceTexture getSurfaceTexture() {
712 return mSurface;
713 }
714
715 /**
Jamie Gennis2af35242012-04-05 11:44:30 -0700716 * Set the {@link SurfaceTexture} for this view to use. If a {@link
717 * SurfaceTexture} is already being used by this view, it is immediately
718 * released and not be usable any more. The {@link
719 * SurfaceTextureListener#onSurfaceTextureDestroyed} callback is <b>not</b>
Jamie Gennis8a34d682012-04-17 16:01:34 -0700720 * called for the previous {@link SurfaceTexture}. Similarly, the {@link
721 * SurfaceTextureListener#onSurfaceTextureAvailable} callback is <b>not</b>
722 * called for the {@link SurfaceTexture} passed to setSurfaceTexture.
Jamie Gennis2af35242012-04-05 11:44:30 -0700723 *
724 * The {@link SurfaceTexture} object must be detached from all OpenGL ES
725 * contexts prior to calling this method.
726 *
727 * @param surfaceTexture The {@link SurfaceTexture} that the view should use.
728 * @see SurfaceTexture#detachFromGLContext()
Jamie Gennis2af35242012-04-05 11:44:30 -0700729 */
730 public void setSurfaceTexture(SurfaceTexture surfaceTexture) {
731 if (surfaceTexture == null) {
732 throw new NullPointerException("surfaceTexture must not be null");
733 }
734 if (mSurface != null) {
735 mSurface.release();
736 }
737 mSurface = surfaceTexture;
738 mUpdateSurface = true;
739 invalidateParentIfNeeded();
740 }
741
742 /**
Romain Guyaa6c24c2011-04-28 18:40:04 -0700743 * Returns the {@link SurfaceTextureListener} currently associated with this
744 * texture view.
745 *
746 * @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener)
747 * @see SurfaceTextureListener
748 */
749 public SurfaceTextureListener getSurfaceTextureListener() {
750 return mListener;
751 }
752
753 /**
754 * Sets the {@link SurfaceTextureListener} used to listen to surface
755 * texture events.
756 *
757 * @see #getSurfaceTextureListener()
758 * @see SurfaceTextureListener
759 */
760 public void setSurfaceTextureListener(SurfaceTextureListener listener) {
761 mListener = listener;
762 }
763
764 /**
765 * This listener can be used to be notified when the surface texture
766 * associated with this texture view is available.
767 */
768 public static interface SurfaceTextureListener {
769 /**
770 * Invoked when a {@link TextureView}'s SurfaceTexture is ready for use.
771 *
772 * @param surface The surface returned by
773 * {@link android.view.TextureView#getSurfaceTexture()}
Romain Guy451ce442011-06-10 15:40:36 -0700774 * @param width The width of the surface
775 * @param height The height of the surface
Romain Guyaa6c24c2011-04-28 18:40:04 -0700776 */
Romain Guy451ce442011-06-10 15:40:36 -0700777 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height);
Romain Guy8f0095c2011-05-02 17:24:22 -0700778
779 /**
780 * Invoked when the {@link SurfaceTexture}'s buffers size changed.
781 *
782 * @param surface The surface returned by
783 * {@link android.view.TextureView#getSurfaceTexture()}
784 * @param width The new width of the surface
785 * @param height The new height of the surface
786 */
787 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height);
Romain Guy451ce442011-06-10 15:40:36 -0700788
789 /**
790 * Invoked when the specified {@link SurfaceTexture} is about to be destroyed.
Grace Kloba402f0552011-08-09 18:47:17 -0700791 * If returns true, no rendering should happen inside the surface texture after this method
792 * is invoked. If returns false, the client needs to call {@link SurfaceTexture#release()}.
Romain Guy52b307e2012-10-07 17:55:17 -0700793 * Most applications should return true.
Romain Guy451ce442011-06-10 15:40:36 -0700794 *
795 * @param surface The surface about to be destroyed
796 */
Grace Kloba402f0552011-08-09 18:47:17 -0700797 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface);
Grace Klobacf559372011-06-22 23:05:40 -0700798
799 /**
800 * Invoked when the specified {@link SurfaceTexture} is updated through
801 * {@link SurfaceTexture#updateTexImage()}.
802 *
803 * @param surface The surface just updated
804 */
805 public void onSurfaceTextureUpdated(SurfaceTexture surface);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700806 }
Romain Guy8f0095c2011-05-02 17:24:22 -0700807
Romain Guy6be3d552011-07-14 18:08:37 -0700808 private native void nCreateNativeWindow(SurfaceTexture surface);
809 private native void nDestroyNativeWindow();
810
Romain Guyd6b2a002011-06-17 17:45:59 -0700811 private static native void nSetDefaultBufferSize(SurfaceTexture surfaceTexture,
812 int width, int height);
Romain Guy6be3d552011-07-14 18:08:37 -0700813
814 private static native void nLockCanvas(int nativeWindow, Canvas canvas, Rect dirty);
815 private static native void nUnlockCanvasAndPost(int nativeWindow, Canvas canvas);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700816}