FaceView.java revision f43dee9f40bb4f21df23ae3876213a5b2f9afcfc
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 com.android.camera.ui; 18 19import android.annotation.TargetApi; 20import android.content.Context; 21import android.content.res.Resources; 22import android.graphics.Canvas; 23import android.graphics.Matrix; 24import android.graphics.Paint; 25import android.graphics.Paint.Style; 26import android.graphics.RectF; 27import android.hardware.Camera.Face; 28import android.os.Handler; 29import android.os.Message; 30import android.util.AttributeSet; 31import android.util.Log; 32import android.view.View; 33 34import com.android.camera.CameraActivity; 35import com.android.camera.CameraScreenNail; 36import com.android.camera.Util; 37import com.android.gallery3d.R; 38import com.android.gallery3d.common.ApiHelper; 39 40@TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) 41public class FaceView extends View 42 implements FocusIndicator, Rotatable { 43 private static final String TAG = "CAM FaceView"; 44 private final boolean LOGV = false; 45 // The value for android.hardware.Camera.setDisplayOrientation. 46 private int mDisplayOrientation; 47 // The orientation compensation for the face indicator to make it look 48 // correctly in all device orientations. Ex: if the value is 90, the 49 // indicator should be rotated 90 degrees counter-clockwise. 50 private int mOrientation; 51 private boolean mMirror; 52 private boolean mPause; 53 private Matrix mMatrix = new Matrix(); 54 private RectF mRect = new RectF(); 55 // As face detection can be flaky, we add a layer of filtering on top of it 56 // to avoid rapid changes in state (eg, flickering between has faces and 57 // not having faces) 58 private Face[] mFaces; 59 private Face[] mPendingFaces; 60 private int mColor; 61 private final int mFocusingColor; 62 private final int mFocusedColor; 63 private final int mFailColor; 64 private Paint mPaint; 65 private volatile boolean mBlocked; 66 67 private int mUncroppedWidth; 68 private int mUncroppedHeight; 69 private static final int MSG_SWITCH_FACES = 1; 70 private static final int SWITCH_DELAY = 70; 71 private boolean mStateSwitchPending = false; 72 private Handler mHandler = new Handler() { 73 @Override 74 public void handleMessage(Message msg) { 75 switch (msg.what) { 76 case MSG_SWITCH_FACES: 77 mStateSwitchPending = false; 78 mFaces = mPendingFaces; 79 invalidate(); 80 break; 81 } 82 } 83 }; 84 85 public FaceView(Context context, AttributeSet attrs) { 86 super(context, attrs); 87 Resources res = getResources(); 88 mFocusingColor = res.getColor(R.color.face_detect_start); 89 mFocusedColor = res.getColor(R.color.face_detect_success); 90 mFailColor = res.getColor(R.color.face_detect_fail); 91 mColor = mFocusingColor; 92 mPaint = new Paint(); 93 mPaint.setAntiAlias(true); 94 mPaint.setStyle(Style.STROKE); 95 mPaint.setStrokeWidth(res.getDimension(R.dimen.face_circle_stroke)); 96 } 97 98 public void setFaces(Face[] faces) { 99 if (LOGV) Log.v(TAG, "Num of faces=" + faces.length); 100 if (mPause) return; 101 if (mFaces != null) { 102 if ((faces.length > 0 && mFaces.length == 0) 103 || (faces.length == 0 && mFaces.length > 0)) { 104 mPendingFaces = faces; 105 if (!mStateSwitchPending) { 106 mStateSwitchPending = true; 107 mHandler.sendEmptyMessageDelayed(MSG_SWITCH_FACES, SWITCH_DELAY); 108 } 109 return; 110 } 111 } 112 if (mStateSwitchPending) { 113 mStateSwitchPending = false; 114 mHandler.removeMessages(MSG_SWITCH_FACES); 115 } 116 mFaces = faces; 117 invalidate(); 118 } 119 120 public void setDisplayOrientation(int orientation) { 121 mDisplayOrientation = orientation; 122 if (LOGV) Log.v(TAG, "mDisplayOrientation=" + orientation); 123 } 124 125 @Override 126 public void setOrientation(int orientation, boolean animation) { 127 mOrientation = orientation; 128 invalidate(); 129 } 130 131 public void setMirror(boolean mirror) { 132 mMirror = mirror; 133 if (LOGV) Log.v(TAG, "mMirror=" + mirror); 134 } 135 136 public boolean faceExists() { 137 return (mFaces != null && mFaces.length > 0); 138 } 139 140 @Override 141 public void showStart() { 142 mColor = mFocusingColor; 143 invalidate(); 144 } 145 146 // Ignore the parameter. No autofocus animation for face detection. 147 @Override 148 public void showSuccess(boolean timeout) { 149 mColor = mFocusedColor; 150 invalidate(); 151 } 152 153 // Ignore the parameter. No autofocus animation for face detection. 154 @Override 155 public void showFail(boolean timeout) { 156 mColor = mFailColor; 157 invalidate(); 158 } 159 160 @Override 161 public void clear() { 162 // Face indicator is displayed during preview. Do not clear the 163 // drawable. 164 mColor = mFocusingColor; 165 mFaces = null; 166 invalidate(); 167 } 168 169 public void pause() { 170 mPause = true; 171 } 172 173 public void resume() { 174 mPause = false; 175 } 176 177 public void setBlockDraw(boolean block) { 178 mBlocked = block; 179 } 180 181 @Override 182 protected void onDraw(Canvas canvas) { 183 if (!mBlocked && (mFaces != null) && (mFaces.length > 0)) { 184 int rw, rh; 185 if (mUncroppedWidth == 0) { 186 // TODO: This check is temporary. It needs to be removed after the 187 // refactoring is fully functioning. 188 final CameraScreenNail sn = ((CameraActivity) getContext()).getCameraScreenNail(); 189 rw = sn.getUncroppedRenderWidth(); 190 rh = sn.getUncroppedRenderHeight(); 191 } else { 192 rw = mUncroppedWidth; 193 rh = mUncroppedHeight; 194 } 195 // Prepare the matrix. 196 if (((rh > rw) && ((mDisplayOrientation == 0) || (mDisplayOrientation == 180))) 197 || ((rw > rh) && ((mDisplayOrientation == 90) || (mDisplayOrientation == 270)))) { 198 int temp = rw; 199 rw = rh; 200 rh = temp; 201 } 202 Util.prepareMatrix(mMatrix, mMirror, mDisplayOrientation, rw, rh); 203 int dx = (getWidth() - rw) / 2; 204 int dy = (getHeight() - rh) / 2; 205 206 // Focus indicator is directional. Rotate the matrix and the canvas 207 // so it looks correctly in all orientations. 208 canvas.save(); 209 mMatrix.postRotate(mOrientation); // postRotate is clockwise 210 canvas.rotate(-mOrientation); // rotate is counter-clockwise (for canvas) 211 for (int i = 0; i < mFaces.length; i++) { 212 // Filter out false positives. 213 if (mFaces[i].score < 50) continue; 214 215 // Transform the coordinates. 216 mRect.set(mFaces[i].rect); 217 if (LOGV) Util.dumpRect(mRect, "Original rect"); 218 mMatrix.mapRect(mRect); 219 if (LOGV) Util.dumpRect(mRect, "Transformed rect"); 220 mPaint.setColor(mColor); 221 mRect.offset(dx, dy); 222 canvas.drawOval(mRect, mPaint); 223 } 224 canvas.restore(); 225 } 226 super.onDraw(canvas); 227 } 228} 229