• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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 
FaceView(Context context, AttributeSet attrs)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 
setFaces(Face[] faces)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 
setDisplayOrientation(int orientation)110     public void setDisplayOrientation(int orientation) {
111         mDisplayOrientation = orientation;
112         if (LOGV) {
113             Log.v(TAG, "mDisplayOrientation=" + orientation);
114         }
115     }
116 
117     @Override
setOrientation(int orientation, boolean animation)118     public void setOrientation(int orientation, boolean animation) {
119         mOrientation = orientation;
120         invalidate();
121     }
122 
setMirror(boolean mirror)123     public void setMirror(boolean mirror) {
124         mMirror = mirror;
125         if (LOGV) {
126             Log.v(TAG, "mMirror=" + mirror);
127         }
128     }
129 
faceExists()130     public boolean faceExists() {
131         return (mFaces != null && mFaces.length > 0);
132     }
133 
clear()134     public void clear() {
135         // Face indicator is displayed during preview. Do not clear the
136         // drawable.
137         mFaces = null;
138         invalidate();
139     }
140 
pause()141     public void pause() {
142         mPause = true;
143     }
144 
resume()145     public void resume() {
146         mPause = false;
147     }
148 
setBlockDraw(boolean block)149     public void setBlockDraw(boolean block) {
150         mBlocked = block;
151     }
152 
153     @Override
onDraw(Canvas canvas)154     protected void onDraw(Canvas canvas) {
155         if (!mBlocked && (mFaces != null) && (mFaces.length > 0)) {
156             int rw, rh;
157             rw = (int) mPreviewArea.width();
158             rh = (int) mPreviewArea.height();
159             // Prepare the matrix.
160             if (((rh > rw) && ((mDisplayOrientation == 0) || (mDisplayOrientation == 180)))
161                     || ((rw > rh) && ((mDisplayOrientation == 90) || (mDisplayOrientation == 270)))) {
162                 int temp = rw;
163                 rw = rh;
164                 rh = temp;
165             }
166             CameraUtil.prepareMatrix(mMatrix, mMirror, mDisplayOrientation, rw, rh);
167             // Focus indicator is directional. Rotate the matrix and the canvas
168             // so it looks correctly in all orientations.
169             canvas.save();
170             mMatrix.postRotate(mOrientation); // postRotate is clockwise
171             canvas.rotate(-mOrientation); // rotate is counter-clockwise (for canvas)
172             for (int i = 0; i < mFaces.length; i++) {
173                 // Filter out false positives.
174                 if (mFaces[i].score < 50) continue;
175 
176                 // Transform the coordinates.
177                 mRect.set(mFaces[i].rect);
178                 if (LOGV) {
179                     CameraUtil.dumpRect(mRect, "Original rect");
180                 }
181                 mMatrix.mapRect(mRect);
182                 if (LOGV) {
183                     CameraUtil.dumpRect(mRect, "Transformed rect");
184                 }
185                 mPaint.setColor(mColor);
186                 mRect.offset(mPreviewArea.left, mPreviewArea.top);
187                 canvas.drawRect(mRect, mPaint);
188             }
189             canvas.restore();
190         }
191         super.onDraw(canvas);
192     }
193 
194     @Override
onPreviewAreaChanged(RectF previewArea)195     public void onPreviewAreaChanged(RectF previewArea) {
196         mPreviewArea.set(previewArea);
197     }
198 }
199