TextureView.java revision a9489274d67b540804aafb587a226f7c2ae4464d
1/*
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;
20import android.graphics.Bitmap;
21import 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 *
51 *      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 *
60 *      public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
61 *          mCamera = Camera.open();
62 *
63 *          try {
64 *              mCamera.setPreviewTexture(surface);
65 *              mCamera.startPreview();
66 *          } catch (IOException ioe) {
67 *              // Something bad happened
68 *          }
69 *      }
70 *
71 *      public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
72 *          // Ignored, Camera does all the work for us
73 *      }
74 *
75 *      public void onSurfaceTextureDestroyed(SurfaceTexture surface) {
76 *          mCamera.stopPreview();
77 *          mCamera.release();
78 *      }
79 *  }
80 * </pre>
81 *
82 * <p>A TextureView's SurfaceTexture can be obtained either by invoking
83 * {@link #getSurfaceTexture()} or by using a {@link SurfaceTextureListener}.
84 * It is important to know that a SurfaceTexture is available only after the
85 * TextureView is attached to a window (and {@link #onAttachedToWindow()} has
86 * been invoked.) It is therefore highly recommended you use a listener to
87 * be notified when the SurfaceTexture becomes available.</p>
88 *
89 * @see SurfaceView
90 * @see SurfaceTexture
91 */
92public class TextureView extends View {
93    private static final String LOG_TAG = "TextureView";
94
95    private HardwareLayer mLayer;
96    private SurfaceTexture mSurface;
97    private SurfaceTextureListener mListener;
98
99    private boolean mOpaque = true;
100
101    private final Runnable mUpdateLayerAction = new Runnable() {
102        @Override
103        public void run() {
104            updateLayer();
105        }
106    };
107    private SurfaceTexture.OnFrameAvailableListener mUpdateListener;
108
109    /**
110     * Creates a new TextureView.
111     *
112     * @param context The context to associate this view with.
113     */
114    public TextureView(Context context) {
115        super(context);
116        init();
117    }
118
119    /**
120     * Creates a new TextureView.
121     *
122     * @param context The context to associate this view with.
123     * @param attrs The attributes of the XML tag that is inflating the view.
124     */
125    @SuppressWarnings({"UnusedDeclaration"})
126    public TextureView(Context context, AttributeSet attrs) {
127        super(context, attrs);
128        init();
129    }
130
131    /**
132     * Creates a new TextureView.
133     *
134     * @param context The context to associate this view with.
135     * @param attrs The attributes of the XML tag that is inflating the view.
136     * @param defStyle The default style to apply to this view. If 0, no style
137     *        will be applied (beyond what is included in the theme). This may
138     *        either be an attribute resource, whose value will be retrieved
139     *        from the current theme, or an explicit style resource.
140     */
141    @SuppressWarnings({"UnusedDeclaration"})
142    public TextureView(Context context, AttributeSet attrs, int defStyle) {
143        super(context, attrs, defStyle);
144        init();
145    }
146
147    private void init() {
148        mLayerPaint = new Paint();
149    }
150
151    /**
152     * {@inheritDoc}
153     */
154    @Override
155    public boolean isOpaque() {
156        return mOpaque;
157    }
158
159    /**
160     * Indicates whether the content of this TextureView is opaque. The
161     * content is assumed to be opaque by default.
162     *
163     * @param opaque True if the content of this TextureView is opaque,
164     *               false otherwise
165     */
166    public void setOpaque(boolean opaque) {
167        if (opaque != mOpaque) {
168            mOpaque = opaque;
169            updateLayer();
170        }
171    }
172
173    @Override
174    protected void onAttachedToWindow() {
175        super.onAttachedToWindow();
176
177        if (!isHardwareAccelerated()) {
178            Log.w(LOG_TAG, "A TextureView or a subclass can only be "
179                    + "used with hardware acceleration enabled.");
180        }
181    }
182
183    @Override
184    protected void onDetachedFromWindow() {
185        super.onDetachedFromWindow();
186
187        if (isHardwareAccelerated() && mLayer != null) {
188            if (mListener != null) {
189                mListener.onSurfaceTextureDestroyed(mSurface);
190            }
191
192            mLayer.destroy();
193            mSurface = null;
194            mLayer = null;
195        }
196    }
197
198    /**
199     * The layer type of a TextureView is ignored since a TextureView is always
200     * considered to act as a hardware layer. The optional paint supplied to this
201     * method will however be taken into account when rendering the content of
202     * this TextureView.
203     *
204     * @param layerType The ype of layer to use with this view, must be one of
205     *        {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
206     *        {@link #LAYER_TYPE_HARDWARE}
207     * @param paint The paint used to compose the layer. This argument is optional
208     *        and can be null. It is ignored when the layer type is
209     *        {@link #LAYER_TYPE_NONE}
210     */
211    @Override
212    public void setLayerType(int layerType, Paint paint) {
213        if (paint != mLayerPaint) {
214            mLayerPaint = paint;
215            invalidate();
216        }
217    }
218
219    /**
220     * Always returns {@link #LAYER_TYPE_HARDWARE}.
221     */
222    @Override
223    public int getLayerType() {
224        return LAYER_TYPE_HARDWARE;
225    }
226
227    /**
228     * Calling this method has no effect.
229     */
230    @Override
231    public void buildLayer() {
232    }
233
234    /**
235     * Subclasses of TextureView cannot do their own rendering
236     * with the {@link Canvas} object.
237     *
238     * @param canvas The Canvas to which the View is rendered.
239     */
240    @Override
241    public final void draw(Canvas canvas) {
242    }
243
244    /**
245     * Subclasses of TextureView cannot do their own rendering
246     * with the {@link Canvas} object.
247     *
248     * @param canvas The Canvas to which the View is rendered.
249     */
250    @Override
251    protected final void onDraw(Canvas canvas) {
252    }
253
254    @Override
255    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
256        super.onSizeChanged(w, h, oldw, oldh);
257        if (mSurface != null) {
258            nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
259            if (mListener != null) {
260                mListener.onSurfaceTextureSizeChanged(mSurface, getWidth(), getHeight());
261            }
262        }
263    }
264
265    @Override
266    HardwareLayer getHardwareLayer() {
267        if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
268            return null;
269        }
270
271        if (mLayer == null) {
272            mLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer(mOpaque);
273            mSurface = mAttachInfo.mHardwareRenderer.createSurfaceTexture(mLayer);
274            nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
275
276            mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() {
277                @Override
278                public void onFrameAvailable(SurfaceTexture surfaceTexture) {
279                    // Per SurfaceTexture's documentation, the callback may be invoked
280                    // from an arbitrary thread
281                    post(mUpdateLayerAction);
282                }
283            };
284            mSurface.setOnFrameAvailableListener(mUpdateListener);
285
286            if (mListener != null) {
287                mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight());
288            }
289        }
290
291        return mLayer;
292    }
293
294    @Override
295    protected void onVisibilityChanged(View changedView, int visibility) {
296        super.onVisibilityChanged(changedView, visibility);
297
298        if (mSurface != null) {
299            // When the view becomes invisible, stop updating it, it's a waste of CPU
300            // To cancel updates, the easiest thing to do is simply to remove the
301            // updates listener
302            if (visibility == VISIBLE) {
303                mSurface.setOnFrameAvailableListener(mUpdateListener);
304                updateLayer();
305            } else {
306                mSurface.setOnFrameAvailableListener(null);
307            }
308        }
309    }
310
311    private void updateLayer() {
312        if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
313            return;
314        }
315
316        mAttachInfo.mHardwareRenderer.updateTextureLayer(mLayer, getWidth(), getHeight(), mOpaque);
317
318        invalidate();
319    }
320
321    /**
322     * <p>Returns a {@link android.graphics.Bitmap} representation of the content
323     * of the associated surface texture. If the surface texture is not available,
324     * this method returns null.</p>
325     *
326     * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888}
327     * pixel format and its dimensions are the same as this view's.</p>
328     *
329     * <p><strong>Do not</strong> invoke this method from a drawing method
330     * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
331     *
332     * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
333     *
334     * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
335     *         texture is not available or the width &lt;= 0 or the height &lt;= 0
336     *
337     * @see #isAvailable()
338     * @see #getBitmap(android.graphics.Bitmap)
339     * @see #getBitmap(int, int)
340     */
341    public Bitmap getBitmap() {
342        return getBitmap(getWidth(), getHeight());
343    }
344
345    /**
346     * <p>Returns a {@link android.graphics.Bitmap} representation of the content
347     * of the associated surface texture. If the surface texture is not available,
348     * this method returns null.</p>
349     *
350     * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888}
351     * pixel format.</p>
352     *
353     * <p><strong>Do not</strong> invoke this method from a drawing method
354     * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
355     *
356     * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
357     *
358     * @param width The width of the bitmap to create
359     * @param height The height of the bitmap to create
360     *
361     * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
362     *         texture is not available or width is &lt;= 0 or height is &lt;= 0
363     *
364     * @see #isAvailable()
365     * @see #getBitmap(android.graphics.Bitmap)
366     * @see #getBitmap()
367     */
368    public Bitmap getBitmap(int width, int height) {
369        if (isAvailable() && width > 0 && height > 0) {
370            return getBitmap(Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888));
371        }
372        return null;
373    }
374
375    /**
376     * <p>Copies the content of this view's surface texture into the specified
377     * bitmap. If the surface texture is not available, the copy is not executed.
378     * The content of the surface texture will be scaled to fit exactly inside
379     * the specified bitmap.</p>
380     *
381     * <p><strong>Do not</strong> invoke this method from a drawing method
382     * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
383     *
384     * <p>If an error occurs, the bitmap is left unchanged.</p>
385     *
386     * @param bitmap The bitmap to copy the content of the surface texture into,
387     *               cannot be null, all configurations are supported
388     *
389     * @return The bitmap specified as a parameter
390     *
391     * @see #isAvailable()
392     * @see #getBitmap(int, int)
393     * @see #getBitmap()
394     */
395    public Bitmap getBitmap(Bitmap bitmap) {
396        if (bitmap != null && isAvailable()) {
397            mAttachInfo.mHardwareRenderer.copyLayer(mLayer, bitmap);
398        }
399        return bitmap;
400    }
401
402    /**
403     * Returns true if the {@link SurfaceTexture} associated with this
404     * TextureView is available for rendering. When this method returns
405     * true, {@link #getSurfaceTexture()} returns a valid surface texture.
406     */
407    public boolean isAvailable() {
408        return mSurface != null;
409    }
410
411    /**
412     * Returns the {@link SurfaceTexture} used by this view. This method
413     * may return null if the view is not attached to a window or if the surface
414     * texture has not been initialized yet.
415     *
416     * @see #isAvailable()
417     */
418    public SurfaceTexture getSurfaceTexture() {
419        return mSurface;
420    }
421
422    /**
423     * Returns the {@link SurfaceTextureListener} currently associated with this
424     * texture view.
425     *
426     * @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener)
427     * @see SurfaceTextureListener
428     */
429    public SurfaceTextureListener getSurfaceTextureListener() {
430        return mListener;
431    }
432
433    /**
434     * Sets the {@link SurfaceTextureListener} used to listen to surface
435     * texture events.
436     *
437     * @see #getSurfaceTextureListener()
438     * @see SurfaceTextureListener
439     */
440    public void setSurfaceTextureListener(SurfaceTextureListener listener) {
441        mListener = listener;
442    }
443
444    /**
445     * This listener can be used to be notified when the surface texture
446     * associated with this texture view is available.
447     */
448    public static interface SurfaceTextureListener {
449        /**
450         * Invoked when a {@link TextureView}'s SurfaceTexture is ready for use.
451         *
452         * @param surface The surface returned by
453         *                {@link android.view.TextureView#getSurfaceTexture()}
454         * @param width The width of the surface
455         * @param height The height of the surface
456         */
457        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height);
458
459        /**
460         * Invoked when the {@link SurfaceTexture}'s buffers size changed.
461         *
462         * @param surface The surface returned by
463         *                {@link android.view.TextureView#getSurfaceTexture()}
464         * @param width The new width of the surface
465         * @param height The new height of the surface
466         */
467        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height);
468
469        /**
470         * Invoked when the specified {@link SurfaceTexture} is about to be destroyed.
471         * After this method is invoked, no rendering should happen inside the surface
472         * texture.
473         *
474         * @param surface The surface about to be destroyed
475         */
476        public void onSurfaceTextureDestroyed(SurfaceTexture surface);
477    }
478
479    private static native void nSetDefaultBufferSize(SurfaceTexture surfaceTexture,
480            int width, int height);
481}
482