blob: 0c0cd7629eea2e8f4ea24ed9d2073a191d8421b4 [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;
22import android.graphics.Paint;
23import android.graphics.SurfaceTexture;
24import android.util.AttributeSet;
25import android.util.Log;
26
27/**
28 * <p>A TextureView can be used to display a content stream. Such a content
29 * stream can for instance be a video or an OpenGL scene. The content stream
30 * can come from the application's process as well as a remote process.</p>
31 *
32 * <p>TextureView can only be used in a hardware accelerated window. When
33 * rendered in software, TextureView will draw nothing.</p>
34 *
35 * <p>Unlike {@link SurfaceView}, TextureView does not create a separate
36 * window but behaves as a regular View. This key difference allows a
37 * TextureView to be moved, transformed, animated, etc. For instance, you
38 * can make a TextureView semi-translucent by calling
39 * <code>myView.setAlpha(0.5f)</code>.</p>
40 *
41 * <p>Using a TextureView is simple: all you need to do is get its
42 * {@link SurfaceTexture}. The {@link SurfaceTexture} can then be used to
43 * render content. The following example demonstrates how to render the
44 * camera preview into a TextureView:</p>
45 *
46 * <pre>
47 * public class LiveCameraActivity extends Activity implements TextureView.SurfaceTextureListener {
48 * private Camera mCamera;
49 * private TextureView mTextureView;
50 *
Romain Guyaa6c24c2011-04-28 18:40:04 -070051 * protected void onCreate(Bundle savedInstanceState) {
52 * super.onCreate(savedInstanceState);
53 *
54 * mTextureView = new TextureView(this);
55 * mTextureView.setSurfaceTextureListener(this);
56 *
57 * setContentView(mTextureView);
58 * }
59 *
Romain Guy451ce442011-06-10 15:40:36 -070060 * public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Romain Guyaa6c24c2011-04-28 18:40:04 -070061 * mCamera = Camera.open();
62 *
63 * try {
64 * mCamera.setPreviewTexture(surface);
65 * mCamera.startPreview();
66 * } catch (IOException ioe) {
67 * // Something bad happened
68 * }
69 * }
Grace Klobacf559372011-06-22 23:05:40 -070070 *
Romain Guy8f0095c2011-05-02 17:24:22 -070071 * public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
72 * // Ignored, Camera does all the work for us
73 * }
Grace Klobacf559372011-06-22 23:05:40 -070074 *
Romain Guy451ce442011-06-10 15:40:36 -070075 * public void onSurfaceTextureDestroyed(SurfaceTexture surface) {
76 * mCamera.stopPreview();
77 * mCamera.release();
78 * }
Grace Klobacf559372011-06-22 23:05:40 -070079 *
80 * public void onSurfaceTextureUpdated(SurfaceTexture surface) {
Romain Guy58f4edb2011-06-24 14:51:38 -070081 * // Invoked every time there's a new Camera preview frame
Grace Klobacf559372011-06-22 23:05:40 -070082 * }
Romain Guyaa6c24c2011-04-28 18:40:04 -070083 * }
84 * </pre>
85 *
86 * <p>A TextureView's SurfaceTexture can be obtained either by invoking
87 * {@link #getSurfaceTexture()} or by using a {@link SurfaceTextureListener}.
88 * It is important to know that a SurfaceTexture is available only after the
89 * TextureView is attached to a window (and {@link #onAttachedToWindow()} has
90 * been invoked.) It is therefore highly recommended you use a listener to
91 * be notified when the SurfaceTexture becomes available.</p>
92 *
93 * @see SurfaceView
94 * @see SurfaceTexture
95 */
96public class TextureView extends View {
Romain Guy77a81162011-06-14 16:45:55 -070097 private static final String LOG_TAG = "TextureView";
98
Romain Guyaa6c24c2011-04-28 18:40:04 -070099 private HardwareLayer mLayer;
100 private SurfaceTexture mSurface;
101 private SurfaceTextureListener mListener;
Romain Guyaa6c24c2011-04-28 18:40:04 -0700102
Romain Guya9489272011-06-22 20:58:11 -0700103 private boolean mOpaque = true;
Romain Guyc989d862011-06-22 14:53:39 -0700104
Romain Guy58f4edb2011-06-24 14:51:38 -0700105 private final Object[] mLock = new Object[0];
106 private boolean mUpdateLayer;
107
Romain Guy8f0095c2011-05-02 17:24:22 -0700108 private SurfaceTexture.OnFrameAvailableListener mUpdateListener;
Romain Guyaa6c24c2011-04-28 18:40:04 -0700109
110 /**
111 * Creates a new TextureView.
112 *
113 * @param context The context to associate this view with.
114 */
115 public TextureView(Context context) {
116 super(context);
117 init();
118 }
119
120 /**
121 * Creates a new TextureView.
122 *
123 * @param context The context to associate this view with.
124 * @param attrs The attributes of the XML tag that is inflating the view.
125 */
126 @SuppressWarnings({"UnusedDeclaration"})
127 public TextureView(Context context, AttributeSet attrs) {
128 super(context, attrs);
129 init();
130 }
131
132 /**
133 * Creates a new TextureView.
134 *
135 * @param context The context to associate this view with.
136 * @param attrs The attributes of the XML tag that is inflating the view.
137 * @param defStyle The default style to apply to this view. If 0, no style
138 * will be applied (beyond what is included in the theme). This may
139 * either be an attribute resource, whose value will be retrieved
140 * from the current theme, or an explicit style resource.
141 */
142 @SuppressWarnings({"UnusedDeclaration"})
143 public TextureView(Context context, AttributeSet attrs, int defStyle) {
144 super(context, attrs, defStyle);
145 init();
146 }
147
148 private void init() {
149 mLayerPaint = new Paint();
150 }
151
Romain Guya9489272011-06-22 20:58:11 -0700152 /**
153 * {@inheritDoc}
154 */
155 @Override
156 public boolean isOpaque() {
157 return mOpaque;
158 }
159
160 /**
161 * Indicates whether the content of this TextureView is opaque. The
162 * content is assumed to be opaque by default.
163 *
164 * @param opaque True if the content of this TextureView is opaque,
165 * false otherwise
166 */
167 public void setOpaque(boolean opaque) {
168 if (opaque != mOpaque) {
169 mOpaque = opaque;
Romain Guy58f4edb2011-06-24 14:51:38 -0700170 updateLayer();
Romain Guya9489272011-06-22 20:58:11 -0700171 }
172 }
173
Romain Guyaa6c24c2011-04-28 18:40:04 -0700174 @Override
175 protected void onAttachedToWindow() {
176 super.onAttachedToWindow();
177
178 if (!isHardwareAccelerated()) {
Romain Guy77a81162011-06-14 16:45:55 -0700179 Log.w(LOG_TAG, "A TextureView or a subclass can only be "
Romain Guyaa6c24c2011-04-28 18:40:04 -0700180 + "used with hardware acceleration enabled.");
181 }
182 }
183
Romain Guy451ce442011-06-10 15:40:36 -0700184 @Override
185 protected void onDetachedFromWindow() {
186 super.onDetachedFromWindow();
187
188 if (isHardwareAccelerated() && mLayer != null) {
189 if (mListener != null) {
190 mListener.onSurfaceTextureDestroyed(mSurface);
191 }
192
193 mLayer.destroy();
194 mSurface = null;
195 mLayer = null;
196 }
197 }
198
Romain Guyaa6c24c2011-04-28 18:40:04 -0700199 /**
200 * The layer type of a TextureView is ignored since a TextureView is always
201 * considered to act as a hardware layer. The optional paint supplied to this
202 * method will however be taken into account when rendering the content of
203 * this TextureView.
204 *
205 * @param layerType The ype of layer to use with this view, must be one of
206 * {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
207 * {@link #LAYER_TYPE_HARDWARE}
208 * @param paint The paint used to compose the layer. This argument is optional
209 * and can be null. It is ignored when the layer type is
210 * {@link #LAYER_TYPE_NONE}
211 */
212 @Override
213 public void setLayerType(int layerType, Paint paint) {
214 if (paint != mLayerPaint) {
215 mLayerPaint = paint;
216 invalidate();
217 }
218 }
219
220 /**
221 * Always returns {@link #LAYER_TYPE_HARDWARE}.
222 */
223 @Override
224 public int getLayerType() {
225 return LAYER_TYPE_HARDWARE;
226 }
227
228 /**
229 * Calling this method has no effect.
230 */
231 @Override
232 public void buildLayer() {
233 }
234
235 /**
236 * Subclasses of TextureView cannot do their own rendering
237 * with the {@link Canvas} object.
238 *
239 * @param canvas The Canvas to which the View is rendered.
240 */
241 @Override
242 public final void draw(Canvas canvas) {
Romain Guy58f4edb2011-06-24 14:51:38 -0700243 applyUpdate();
Romain Guyaa6c24c2011-04-28 18:40:04 -0700244 }
245
246 /**
247 * Subclasses of TextureView cannot do their own rendering
248 * with the {@link Canvas} object.
249 *
250 * @param canvas The Canvas to which the View is rendered.
251 */
252 @Override
253 protected final void onDraw(Canvas canvas) {
254 }
255
256 @Override
Romain Guy8f0095c2011-05-02 17:24:22 -0700257 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
258 super.onSizeChanged(w, h, oldw, oldh);
259 if (mSurface != null) {
Romain Guye5e0c502011-06-15 15:18:31 -0700260 nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
Romain Guy451ce442011-06-10 15:40:36 -0700261 if (mListener != null) {
262 mListener.onSurfaceTextureSizeChanged(mSurface, getWidth(), getHeight());
263 }
Romain Guy8f0095c2011-05-02 17:24:22 -0700264 }
265 }
266
267 @Override
Romain Guyaa6c24c2011-04-28 18:40:04 -0700268 HardwareLayer getHardwareLayer() {
Romain Guyaa6c24c2011-04-28 18:40:04 -0700269 if (mLayer == null) {
Romain Guy58f4edb2011-06-24 14:51:38 -0700270 if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
271 return null;
272 }
273
Romain Guya9489272011-06-22 20:58:11 -0700274 mLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer(mOpaque);
Romain Guye5e0c502011-06-15 15:18:31 -0700275 mSurface = mAttachInfo.mHardwareRenderer.createSurfaceTexture(mLayer);
276 nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
Romain Guyaa6c24c2011-04-28 18:40:04 -0700277
Romain Guy8f0095c2011-05-02 17:24:22 -0700278 mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() {
Romain Guyaa6c24c2011-04-28 18:40:04 -0700279 @Override
280 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
281 // Per SurfaceTexture's documentation, the callback may be invoked
282 // from an arbitrary thread
Romain Guy58f4edb2011-06-24 14:51:38 -0700283 synchronized (mLock) {
284 mUpdateLayer = true;
285 }
286 postInvalidateDelayed(0);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700287 }
Romain Guy8f0095c2011-05-02 17:24:22 -0700288 };
289 mSurface.setOnFrameAvailableListener(mUpdateListener);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700290
291 if (mListener != null) {
Romain Guy451ce442011-06-10 15:40:36 -0700292 mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight());
Romain Guyaa6c24c2011-04-28 18:40:04 -0700293 }
294 }
295
Romain Guy58f4edb2011-06-24 14:51:38 -0700296 applyUpdate();
297
Romain Guyaa6c24c2011-04-28 18:40:04 -0700298 return mLayer;
299 }
300
Romain Guy8f0095c2011-05-02 17:24:22 -0700301 @Override
302 protected void onVisibilityChanged(View changedView, int visibility) {
303 super.onVisibilityChanged(changedView, visibility);
304
305 if (mSurface != null) {
306 // When the view becomes invisible, stop updating it, it's a waste of CPU
307 // To cancel updates, the easiest thing to do is simply to remove the
308 // updates listener
309 if (visibility == VISIBLE) {
310 mSurface.setOnFrameAvailableListener(mUpdateListener);
Romain Guya9489272011-06-22 20:58:11 -0700311 updateLayer();
Romain Guy8f0095c2011-05-02 17:24:22 -0700312 } else {
313 mSurface.setOnFrameAvailableListener(null);
314 }
315 }
316 }
317
Romain Guya9489272011-06-22 20:58:11 -0700318 private void updateLayer() {
Romain Guy58f4edb2011-06-24 14:51:38 -0700319 mUpdateLayer = true;
320 invalidate();
321 }
322
323 private void applyUpdate() {
324 if (mLayer == null) {
Romain Guya9489272011-06-22 20:58:11 -0700325 return;
326 }
327
Romain Guy58f4edb2011-06-24 14:51:38 -0700328 synchronized (mLock) {
329 if (mUpdateLayer) {
330 mUpdateLayer = false;
331 } else {
332 return;
333 }
334 }
335
Romain Guy02ccac62011-06-24 13:20:23 -0700336 mLayer.update(getWidth(), getHeight(), mOpaque);
Romain Guya9489272011-06-22 20:58:11 -0700337
Grace Klobacf559372011-06-22 23:05:40 -0700338 if (mListener != null) {
339 mListener.onSurfaceTextureUpdated(mSurface);
340 }
Romain Guya9489272011-06-22 20:58:11 -0700341 }
342
Romain Guyaa6c24c2011-04-28 18:40:04 -0700343 /**
Romain Guy77a81162011-06-14 16:45:55 -0700344 * <p>Returns a {@link android.graphics.Bitmap} representation of the content
345 * of the associated surface texture. If the surface texture is not available,
346 * this method returns null.</p>
347 *
348 * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888}
349 * pixel format and its dimensions are the same as this view's.</p>
350 *
351 * <p><strong>Do not</strong> invoke this method from a drawing method
352 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
353 *
Romain Guyd6b2a002011-06-17 17:45:59 -0700354 * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
355 *
Romain Guy77a81162011-06-14 16:45:55 -0700356 * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
357 * texture is not available or the width &lt;= 0 or the height &lt;= 0
358 *
359 * @see #isAvailable()
360 * @see #getBitmap(android.graphics.Bitmap)
361 * @see #getBitmap(int, int)
362 */
363 public Bitmap getBitmap() {
364 return getBitmap(getWidth(), getHeight());
365 }
366
367 /**
368 * <p>Returns a {@link android.graphics.Bitmap} representation of the content
369 * of the associated surface texture. If the surface texture is not available,
370 * this method returns null.</p>
371 *
372 * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888}
373 * pixel format.</p>
374 *
375 * <p><strong>Do not</strong> invoke this method from a drawing method
376 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
377 *
Romain Guyd6b2a002011-06-17 17:45:59 -0700378 * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
379 *
Romain Guy77a81162011-06-14 16:45:55 -0700380 * @param width The width of the bitmap to create
381 * @param height The height of the bitmap to create
382 *
383 * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
384 * texture is not available or width is &lt;= 0 or height is &lt;= 0
385 *
386 * @see #isAvailable()
387 * @see #getBitmap(android.graphics.Bitmap)
388 * @see #getBitmap()
389 */
390 public Bitmap getBitmap(int width, int height) {
391 if (isAvailable() && width > 0 && height > 0) {
392 return getBitmap(Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888));
393 }
394 return null;
395 }
396
397 /**
398 * <p>Copies the content of this view's surface texture into the specified
399 * bitmap. If the surface texture is not available, the copy is not executed.
400 * The content of the surface texture will be scaled to fit exactly inside
401 * the specified bitmap.</p>
402 *
403 * <p><strong>Do not</strong> invoke this method from a drawing method
404 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
405 *
Romain Guyd6b2a002011-06-17 17:45:59 -0700406 * <p>If an error occurs, the bitmap is left unchanged.</p>
407 *
Romain Guy77a81162011-06-14 16:45:55 -0700408 * @param bitmap The bitmap to copy the content of the surface texture into,
409 * cannot be null, all configurations are supported
410 *
411 * @return The bitmap specified as a parameter
412 *
413 * @see #isAvailable()
414 * @see #getBitmap(int, int)
415 * @see #getBitmap()
416 */
417 public Bitmap getBitmap(Bitmap bitmap) {
418 if (bitmap != null && isAvailable()) {
Romain Guy02ccac62011-06-24 13:20:23 -0700419 mLayer.copyInto(bitmap);
Romain Guy77a81162011-06-14 16:45:55 -0700420 }
421 return bitmap;
422 }
423
424 /**
425 * Returns true if the {@link SurfaceTexture} associated with this
426 * TextureView is available for rendering. When this method returns
427 * true, {@link #getSurfaceTexture()} returns a valid surface texture.
428 */
429 public boolean isAvailable() {
430 return mSurface != null;
431 }
432
433 /**
Romain Guyaa6c24c2011-04-28 18:40:04 -0700434 * Returns the {@link SurfaceTexture} used by this view. This method
Romain Guy77a81162011-06-14 16:45:55 -0700435 * may return null if the view is not attached to a window or if the surface
436 * texture has not been initialized yet.
437 *
438 * @see #isAvailable()
Romain Guyaa6c24c2011-04-28 18:40:04 -0700439 */
440 public SurfaceTexture getSurfaceTexture() {
441 return mSurface;
442 }
443
444 /**
445 * Returns the {@link SurfaceTextureListener} currently associated with this
446 * texture view.
447 *
448 * @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener)
449 * @see SurfaceTextureListener
450 */
451 public SurfaceTextureListener getSurfaceTextureListener() {
452 return mListener;
453 }
454
455 /**
456 * Sets the {@link SurfaceTextureListener} used to listen to surface
457 * texture events.
458 *
459 * @see #getSurfaceTextureListener()
460 * @see SurfaceTextureListener
461 */
462 public void setSurfaceTextureListener(SurfaceTextureListener listener) {
463 mListener = listener;
464 }
465
466 /**
467 * This listener can be used to be notified when the surface texture
468 * associated with this texture view is available.
469 */
470 public static interface SurfaceTextureListener {
471 /**
472 * Invoked when a {@link TextureView}'s SurfaceTexture is ready for use.
473 *
474 * @param surface The surface returned by
475 * {@link android.view.TextureView#getSurfaceTexture()}
Romain Guy451ce442011-06-10 15:40:36 -0700476 * @param width The width of the surface
477 * @param height The height of the surface
Romain Guyaa6c24c2011-04-28 18:40:04 -0700478 */
Romain Guy451ce442011-06-10 15:40:36 -0700479 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height);
Romain Guy8f0095c2011-05-02 17:24:22 -0700480
481 /**
482 * Invoked when the {@link SurfaceTexture}'s buffers size changed.
483 *
484 * @param surface The surface returned by
485 * {@link android.view.TextureView#getSurfaceTexture()}
486 * @param width The new width of the surface
487 * @param height The new height of the surface
488 */
489 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height);
Romain Guy451ce442011-06-10 15:40:36 -0700490
491 /**
492 * Invoked when the specified {@link SurfaceTexture} is about to be destroyed.
493 * After this method is invoked, no rendering should happen inside the surface
494 * texture.
495 *
496 * @param surface The surface about to be destroyed
497 */
498 public void onSurfaceTextureDestroyed(SurfaceTexture surface);
Grace Klobacf559372011-06-22 23:05:40 -0700499
500 /**
501 * Invoked when the specified {@link SurfaceTexture} is updated through
502 * {@link SurfaceTexture#updateTexImage()}.
503 *
504 * @param surface The surface just updated
505 */
506 public void onSurfaceTextureUpdated(SurfaceTexture surface);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700507 }
Romain Guy8f0095c2011-05-02 17:24:22 -0700508
Romain Guyd6b2a002011-06-17 17:45:59 -0700509 private static native void nSetDefaultBufferSize(SurfaceTexture surfaceTexture,
510 int width, int height);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700511}