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