CameraScreenNail.java revision c82461e017eed0c8bf73a646d782be3bb4f8d817
1/*
2 * Copyright (C) 2012 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 com.android.camera;
18
19import android.annotation.TargetApi;
20import android.graphics.SurfaceTexture;
21import android.util.Log;
22
23import com.android.gallery3d.common.ApiHelper;
24import com.android.gallery3d.ui.GLCanvas;
25import com.android.gallery3d.ui.RawTexture;
26import com.android.gallery3d.ui.SurfaceTextureScreenNail;
27
28/*
29 * This is a ScreenNail which can displays camera preview.
30 */
31@TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB)
32public class CameraScreenNail extends SurfaceTextureScreenNail {
33    private static final String TAG = "CameraScreenNail";
34    private static final int ANIM_NONE = 0;
35    // Capture animation is about to start.
36    private static final int ANIM_CAPTURE_START = 1;
37    // Capture animation is running.
38    private static final int ANIM_CAPTURE_RUNNING = 2;
39    // Switch camera animation needs to copy texture.
40    private static final int ANIM_SWITCH_COPY_TEXTURE = 3;
41    // Switch camera animation shows the initial feedback by darkening the
42    // preview.
43    private static final int ANIM_SWITCH_DARK_PREVIEW = 4;
44    // Switch camera animation is waiting for the first frame.
45    private static final int ANIM_SWITCH_WAITING_FIRST_FRAME = 5;
46    // Switch camera animation is about to start.
47    private static final int ANIM_SWITCH_START = 6;
48    // Switch camera animation is running.
49    private static final int ANIM_SWITCH_RUNNING = 7;
50
51    private boolean mVisible;
52    // True if first onFrameAvailable has been called. If screen nail is drawn
53    // too early, it will be all white.
54    private boolean mFirstFrameArrived;
55    private Listener mListener;
56    private final float[] mTextureTransformMatrix = new float[16];
57
58    // Animation.
59    private CaptureAnimManager mCaptureAnimManager = new CaptureAnimManager();
60    private SwitchAnimManager mSwitchAnimManager = new SwitchAnimManager();
61    private int mAnimState = ANIM_NONE;
62    private RawTexture mAnimTexture;
63    // Some methods are called by GL thread and some are called by main thread.
64    // This protects mAnimState, mVisible, and surface texture. This also makes
65    // sure some code are atomic. For example, requestRender and setting
66    // mAnimState.
67    private Object mLock = new Object();
68
69    public interface Listener {
70        void requestRender();
71        // Preview has been copied to a texture.
72        void onPreviewTextureCopied();
73    }
74
75    public CameraScreenNail(Listener listener) {
76        mListener = listener;
77    }
78
79    @Override
80    public void acquireSurfaceTexture() {
81        synchronized (mLock) {
82            mFirstFrameArrived = false;
83            super.acquireSurfaceTexture();
84            mAnimTexture = new RawTexture(getWidth(), getHeight(), true);
85        }
86    }
87
88    @Override
89    public void releaseSurfaceTexture() {
90        synchronized (mLock) {
91            super.releaseSurfaceTexture();
92            mAnimState = ANIM_NONE; // stop the animation
93        }
94    }
95
96    public void copyTexture() {
97        synchronized (mLock) {
98            mListener.requestRender();
99            mAnimState = ANIM_SWITCH_COPY_TEXTURE;
100        }
101    }
102
103    public void animateSwitchCamera() {
104        Log.v(TAG, "animateSwitchCamera");
105        synchronized (mLock) {
106            if (mAnimState == ANIM_SWITCH_DARK_PREVIEW) {
107                // Do not request render here because camera has been just
108                // started. We do not want to draw black frames.
109                mAnimState = ANIM_SWITCH_WAITING_FIRST_FRAME;
110            }
111        }
112    }
113
114    public void animateCapture(int animOrientation) {
115        synchronized (mLock) {
116            mCaptureAnimManager.setOrientation(animOrientation);
117            mListener.requestRender();
118            mAnimState = ANIM_CAPTURE_START;
119        }
120    }
121
122    public void directDraw(GLCanvas canvas, int x, int y, int width, int height) {
123        super.draw(canvas, x, y, width, height);
124    }
125
126    @Override
127    public void draw(GLCanvas canvas, int x, int y, int width, int height) {
128        synchronized (mLock) {
129            if (!mVisible) mVisible = true;
130            SurfaceTexture surfaceTexture = getSurfaceTexture();
131            if (surfaceTexture == null || !mFirstFrameArrived) return;
132
133            if (mAnimState == ANIM_NONE) {
134                super.draw(canvas, x, y, width, height);
135                return;
136            }
137
138            switch (mAnimState) {
139                case ANIM_SWITCH_COPY_TEXTURE:
140                    copyPreviewTexture(canvas);
141                    mSwitchAnimManager.setReviewDrawingSize(width, height);
142                    mListener.onPreviewTextureCopied();
143                    mAnimState = ANIM_SWITCH_DARK_PREVIEW;
144                    // The texture is ready. Fall through to draw darkened
145                    // preview.
146                case ANIM_SWITCH_DARK_PREVIEW:
147                case ANIM_SWITCH_WAITING_FIRST_FRAME:
148                    // Consume the frame. If the buffers are full,
149                    // onFrameAvailable will not be called. Animation state
150                    // relies on onFrameAvailable.
151                    surfaceTexture.updateTexImage();
152                    mSwitchAnimManager.drawDarkPreview(canvas, x, y, width,
153                            height, mAnimTexture);
154                    return;
155                case ANIM_SWITCH_START:
156                    mSwitchAnimManager.startAnimation();
157                    mAnimState = ANIM_SWITCH_RUNNING;
158                    break;
159                case ANIM_CAPTURE_START:
160                    copyPreviewTexture(canvas);
161                    mCaptureAnimManager.startAnimation(x, y, width, height);
162                    mAnimState = ANIM_CAPTURE_RUNNING;
163                    break;
164            }
165
166            if (mAnimState == ANIM_CAPTURE_RUNNING || mAnimState == ANIM_SWITCH_RUNNING) {
167                boolean drawn;
168                if (mAnimState == ANIM_CAPTURE_RUNNING) {
169                    drawn = mCaptureAnimManager.drawAnimation(canvas, this, mAnimTexture);
170                } else {
171                    drawn = mSwitchAnimManager.drawAnimation(canvas, x, y,
172                            width, height, this, mAnimTexture);
173                }
174                if (drawn) {
175                    mListener.requestRender();
176                } else {
177                    // Continue to the normal draw procedure if the animation is
178                    // not drawn.
179                    mAnimState = ANIM_NONE;
180                    super.draw(canvas, x, y, width, height);
181                }
182            }
183        } // mLock
184    }
185
186    private void copyPreviewTexture(GLCanvas canvas) {
187        int width = mAnimTexture.getWidth();
188        int height = mAnimTexture.getHeight();
189        canvas.beginRenderTarget(mAnimTexture);
190        // Flip preview texture vertically. OpenGL uses bottom left point
191        // as the origin (0, 0).
192        canvas.translate(0, height);
193        canvas.scale(1, -1, 1);
194        getSurfaceTexture().getTransformMatrix(mTextureTransformMatrix);
195        canvas.drawTexture(mExtTexture,
196                mTextureTransformMatrix, 0, 0, width, height);
197        canvas.endRenderTarget();
198    }
199
200    @Override
201    public void noDraw() {
202        synchronized (mLock) {
203            mVisible = false;
204        }
205    }
206
207    @Override
208    public void recycle() {
209        synchronized (mLock) {
210            mVisible = false;
211        }
212    }
213
214    @Override
215    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
216        synchronized (mLock) {
217            mFirstFrameArrived = true;
218            if (mVisible) {
219                if (mAnimState == ANIM_SWITCH_WAITING_FIRST_FRAME) {
220                    mAnimState = ANIM_SWITCH_START;
221                }
222                // We need to ask for re-render if the SurfaceTexture receives a new
223                // frame.
224                mListener.requestRender();
225            }
226        }
227    }
228
229    // We need to keep track of the size of preview frame on the screen because
230    // it's needed when we do switch-camera animation. See comments in
231    // SwitchAnimManager.java. This is based on the natural orientation, not the
232    // view system orientation.
233    public void setPreviewFrameLayoutSize(int width, int height) {
234        synchronized (mLock) {
235            mSwitchAnimManager.setPreviewFrameLayoutSize(width, height);
236        }
237    }
238}
239