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