CameraScreenNail.java revision 6342be380a34976c828d21c2a3c172630123846a
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 display camera's preview. 30 */ 31@TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB) 32public class CameraScreenNail extends SurfaceTextureScreenNail { 33 private static final String TAG = "CAM_ScreenNail"; 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 private OnFrameDrawnListener mOneTimeFrameDrawnListener; 70 private float mAspectRatio; 71 private int mRenderWidth; 72 private int mRenderHeight; 73 private int mPreviewWidth; 74 private int mPreviewHeight; 75 private boolean mFullScreen; 76 private int mRotation; 77 78 public interface Listener { 79 void requestRender(); 80 // Preview has been copied to a texture. 81 void onPreviewTextureCopied(); 82 83 void onCaptureTextureCopied(); 84 } 85 86 public interface OnFrameDrawnListener { 87 void onFrameDrawn(CameraScreenNail c); 88 } 89 90 public CameraScreenNail(Listener listener) { 91 mListener = listener; 92 } 93 94 public void setFullScreen(boolean full) { 95 mFullScreen = full; 96 } 97 98 @Override 99 public void setSize(int w, int h) { 100 super.setSize(w, h); 101 if (w > h) { 102 mAspectRatio = (float) w / h; 103 } else { 104 mAspectRatio = (float) h / w; 105 } 106 updateRenderSize(); 107 } 108 109 private void setPreviewLayoutSize(int w, int h) { 110 mPreviewWidth = w; 111 mPreviewHeight = h; 112 updateRenderSize(); 113 } 114 115 private void updateRenderSize() { 116 // these callbacks above come at different times, 117 // so make sure we have all the data 118 if (mPreviewWidth != 0 && mAspectRatio > 0) { 119 if (mPreviewWidth > mPreviewHeight) { 120 mRenderWidth = Math.max(mPreviewWidth, 121 (int) (mPreviewHeight * mAspectRatio)); 122 mRenderHeight = Math.max(mPreviewHeight, 123 (int)(mPreviewWidth / mAspectRatio)); 124 } else { 125 mRenderWidth = Math.max(mPreviewWidth, 126 (int) (mPreviewHeight / mAspectRatio)); 127 mRenderHeight = Math.max(mPreviewHeight, 128 (int) (mPreviewWidth * mAspectRatio)); 129 } 130 } 131 } 132 133 @Override 134 public void acquireSurfaceTexture() { 135 synchronized (mLock) { 136 mFirstFrameArrived = false; 137 super.acquireSurfaceTexture(); 138 mAnimTexture = new RawTexture(getWidth(), getHeight(), true); 139 } 140 } 141 142 @Override 143 public void releaseSurfaceTexture() { 144 synchronized (mLock) { 145 super.releaseSurfaceTexture(); 146 mAnimState = ANIM_NONE; // stop the animation 147 mRotation = 0; 148 } 149 } 150 151 public void copyTexture() { 152 synchronized (mLock) { 153 mListener.requestRender(); 154 mAnimState = ANIM_SWITCH_COPY_TEXTURE; 155 } 156 } 157 158 public void animateSwitchCamera() { 159 Log.v(TAG, "animateSwitchCamera"); 160 synchronized (mLock) { 161 if (mAnimState == ANIM_SWITCH_DARK_PREVIEW) { 162 // Do not request render here because camera has been just 163 // started. We do not want to draw black frames. 164 mAnimState = ANIM_SWITCH_WAITING_FIRST_FRAME; 165 } 166 } 167 } 168 169 public void animateCapture(int animOrientation) { 170 synchronized (mLock) { 171 mCaptureAnimManager.setOrientation(animOrientation); 172 mListener.requestRender(); 173 mAnimState = ANIM_CAPTURE_START; 174 } 175 } 176 177 public void setInitialOrientation(int initialOrientation) { 178 mRotation = initialOrientation; 179 } 180 181 private void callbackIfNeeded() { 182 if (mOneTimeFrameDrawnListener != null) { 183 mOneTimeFrameDrawnListener.onFrameDrawn(this); 184 mOneTimeFrameDrawnListener = null; 185 } 186 } 187 188 public void directDraw(GLCanvas canvas, int x, int y, int width, int height) { 189 if (mRotation != 0) { 190 canvas.save(GLCanvas.SAVE_FLAG_MATRIX); 191 canvas.rotate(mRotation, 0f, 0f, 1.0f); 192 } 193 super.draw(canvas, x, y, width, height); 194 if (mRotation != 0) { 195 canvas.restore(); 196 } 197 } 198 199 @Override 200 public void draw(GLCanvas canvas, int x, int y, int width, int height) { 201 synchronized (mLock) { 202 if (!mVisible) mVisible = true; 203 SurfaceTexture surfaceTexture = getSurfaceTexture(); 204 if (surfaceTexture == null || !mFirstFrameArrived) return; 205 206 switch (mAnimState) { 207 case ANIM_NONE: 208 if (mFullScreen && (mRenderWidth != 0)) { 209 // overscale image to make it fullscreen 210 x = (x + width / 2) - mRenderWidth / 2; 211 y = (y + height / 2) - mRenderHeight / 2; 212 width = mRenderWidth; 213 height = mRenderHeight; 214 } 215 super.draw(canvas, x, y, width, height); 216 break; 217 case ANIM_SWITCH_COPY_TEXTURE: 218 copyPreviewTexture(canvas); 219 mSwitchAnimManager.setReviewDrawingSize(width, height); 220 mListener.onPreviewTextureCopied(); 221 mAnimState = ANIM_SWITCH_DARK_PREVIEW; 222 // The texture is ready. Fall through to draw darkened 223 // preview. 224 case ANIM_SWITCH_DARK_PREVIEW: 225 case ANIM_SWITCH_WAITING_FIRST_FRAME: 226 // Consume the frame. If the buffers are full, 227 // onFrameAvailable will not be called. Animation state 228 // relies on onFrameAvailable. 229 surfaceTexture.updateTexImage(); 230 if (mRenderWidth != 0) { 231 // overscale image to make it fullscreen 232 x = (x + width / 2) - mRenderWidth / 2; 233 y = (y + height / 2) - mRenderHeight / 2; 234 width = mRenderWidth; 235 height = mRenderHeight; 236 } 237 mSwitchAnimManager.drawDarkPreview(canvas, x, y, width, 238 height, mAnimTexture); 239 break; 240 case ANIM_SWITCH_START: 241 mSwitchAnimManager.startAnimation(); 242 mAnimState = ANIM_SWITCH_RUNNING; 243 break; 244 case ANIM_CAPTURE_START: 245 copyPreviewTexture(canvas); 246 mListener.onCaptureTextureCopied(); 247 if (mRenderWidth > 0) { 248 // overscale image to make it fullscreen 249 x = (x + width / 2) - mRenderWidth / 2; 250 y = (y + height / 2) - mRenderHeight / 2; 251 width = mRenderWidth; 252 height = mRenderHeight; 253 } 254 mCaptureAnimManager.startAnimation(x, y, width, height); 255 mAnimState = ANIM_CAPTURE_RUNNING; 256 break; 257 } 258 259 if (mAnimState == ANIM_CAPTURE_RUNNING || mAnimState == ANIM_SWITCH_RUNNING) { 260 boolean drawn; 261 if (mAnimState == ANIM_CAPTURE_RUNNING) { 262 drawn = mCaptureAnimManager.drawAnimation(canvas, this, mAnimTexture); 263 } else { 264 if (mRenderWidth != 0) { 265 // overscale image to make it fullscreen 266 x = (x + width / 2) - mRenderWidth / 2; 267 y = (y + height / 2) - mRenderHeight / 2; 268 width = mRenderWidth; 269 height = mRenderHeight; 270 } 271 drawn = mSwitchAnimManager.drawAnimation(canvas, x, y, 272 width, height, this, mAnimTexture); 273 } 274 if (drawn) { 275 mListener.requestRender(); 276 } else { 277 // Continue to the normal draw procedure if the animation is 278 // not drawn. 279 mAnimState = ANIM_NONE; 280 if (mRenderWidth != 0) { 281 // overscale image to make it fullscreen 282 x = (x + width / 2) - mRenderWidth / 2; 283 y = (y + height / 2) - mRenderHeight / 2; 284 width = mRenderWidth; 285 height = mRenderHeight; 286 } 287 super.draw(canvas, x, y, width, height); 288 } 289 } 290 callbackIfNeeded(); 291 } // mLock 292 } 293 294 private void copyPreviewTexture(GLCanvas canvas) { 295 int width = mAnimTexture.getWidth(); 296 int height = mAnimTexture.getHeight(); 297 canvas.beginRenderTarget(mAnimTexture); 298 // Flip preview texture vertically. OpenGL uses bottom left point 299 // as the origin (0, 0). 300 canvas.translate(0, height); 301 canvas.scale(1, -1, 1); 302 getSurfaceTexture().getTransformMatrix(mTextureTransformMatrix); 303 canvas.drawTexture(mExtTexture, 304 mTextureTransformMatrix, 0, 0, width, height); 305 canvas.endRenderTarget(); 306 } 307 308 @Override 309 public void noDraw() { 310 synchronized (mLock) { 311 mVisible = false; 312 } 313 } 314 315 @Override 316 public void recycle() { 317 synchronized (mLock) { 318 mVisible = false; 319 } 320 } 321 322 @Override 323 public void onFrameAvailable(SurfaceTexture surfaceTexture) { 324 synchronized (mLock) { 325 if (getSurfaceTexture() != surfaceTexture) { 326 return; 327 } 328 mFirstFrameArrived = true; 329 if (mVisible) { 330 if (mAnimState == ANIM_SWITCH_WAITING_FIRST_FRAME) { 331 mAnimState = ANIM_SWITCH_START; 332 } 333 // We need to ask for re-render if the SurfaceTexture receives a new 334 // frame. 335 mListener.requestRender(); 336 } 337 } 338 } 339 340 // We need to keep track of the size of preview frame on the screen because 341 // it's needed when we do switch-camera animation. See comments in 342 // SwitchAnimManager.java. This is based on the natural orientation, not the 343 // view system orientation. 344 public void setPreviewFrameLayoutSize(int width, int height) { 345 synchronized (mLock) { 346 mSwitchAnimManager.setPreviewFrameLayoutSize(width, height); 347 setPreviewLayoutSize(width, height); 348 } 349 } 350 351 public void setOneTimeOnFrameDrawnListener(OnFrameDrawnListener l) { 352 synchronized (mLock) { 353 mFirstFrameArrived = false; 354 mOneTimeFrameDrawnListener = l; 355 } 356 } 357} 358