blob: 1890c7630755e4c734d52d5c8a3e83c83b91aa85 [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.gallery3d.ui;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.RectF;
import android.media.FaceDetector;
import android.os.Handler;
import android.os.Message;
import android.util.FloatMath;
import android.view.MotionEvent;
import android.view.animation.DecelerateInterpolator;
import android.widget.Toast;
import com.android.gallery3d.R;
import com.android.gallery3d.anim.Animation;
import com.android.gallery3d.app.AbstractGalleryActivity;
import com.android.gallery3d.common.Utils;
import java.util.ArrayList;
import javax.microedition.khronos.opengles.GL11;
/**
* The activity can crop specific region of interest from an image.
*/
public class CropView extends GLView {
@SuppressWarnings("unused")
private static final String TAG = "CropView";
private static final int FACE_PIXEL_COUNT = 120000; // around 400x300
private static final int COLOR_OUTLINE = 0xFF008AFF;
private static final int COLOR_FACE_OUTLINE = 0xFF000000;
private static final float OUTLINE_WIDTH = 3f;
private static final int SIZE_UNKNOWN = -1;
private static final int TOUCH_TOLERANCE = 30;
private static final float MIN_SELECTION_LENGTH = 16f;
public static final float UNSPECIFIED = -1f;
private static final int MAX_FACE_COUNT = 3;
private static final float FACE_EYE_RATIO = 2f;
private static final int ANIMATION_DURATION = 1250;
private static final int MOVE_LEFT = 1;
private static final int MOVE_TOP = 2;
private static final int MOVE_RIGHT = 4;
private static final int MOVE_BOTTOM = 8;
private static final int MOVE_BLOCK = 16;
private static final float MAX_SELECTION_RATIO = 0.8f;
private static final float MIN_SELECTION_RATIO = 0.4f;
private static final float SELECTION_RATIO = 0.60f;
private static final int ANIMATION_TRIGGER = 64;
private static final int MSG_UPDATE_FACES = 1;
private float mAspectRatio = UNSPECIFIED;
private float mSpotlightRatioX = 0;
private float mSpotlightRatioY = 0;
private Handler mMainHandler;
private FaceHighlightView mFaceDetectionView;
private HighlightRectangle mHighlightRectangle;
private TileImageView mImageView;
private AnimationController mAnimation = new AnimationController();
private int mImageWidth = SIZE_UNKNOWN;
private int mImageHeight = SIZE_UNKNOWN;
private AbstractGalleryActivity mActivity;
private GLPaint mPaint = new GLPaint();
private GLPaint mFacePaint = new GLPaint();
private int mImageRotation;
public CropView(AbstractGalleryActivity activity) {
mActivity = activity;
mImageView = new TileImageView(activity);
mFaceDetectionView = new FaceHighlightView();
mHighlightRectangle = new HighlightRectangle();
addComponent(mImageView);
addComponent(mFaceDetectionView);
addComponent(mHighlightRectangle);
mHighlightRectangle.setVisibility(GLView.INVISIBLE);
mPaint.setColor(COLOR_OUTLINE);
mPaint.setLineWidth(OUTLINE_WIDTH);
mFacePaint.setColor(COLOR_FACE_OUTLINE);
mFacePaint.setLineWidth(OUTLINE_WIDTH);
mMainHandler = new SynchronizedHandler(activity.getGLRoot()) {
@Override
public void handleMessage(Message message) {
Utils.assertTrue(message.what == MSG_UPDATE_FACES);
((DetectFaceTask) message.obj).updateFaces();
}
};
}
public void setAspectRatio(float ratio) {
mAspectRatio = ratio;
}
public void setSpotlightRatio(float ratioX, float ratioY) {
mSpotlightRatioX = ratioX;
mSpotlightRatioY = ratioY;
}
@Override
public void onLayout(boolean changed, int l, int t, int r, int b) {
int width = r - l;
int height = b - t;
mFaceDetectionView.layout(0, 0, width, height);
mHighlightRectangle.layout(0, 0, width, height);
mImageView.layout(0, 0, width, height);
if (mImageHeight != SIZE_UNKNOWN) {
mAnimation.initialize();
if (mHighlightRectangle.getVisibility() == GLView.VISIBLE) {
mAnimation.parkNow(
mHighlightRectangle.mHighlightRect);
}
}
}
private boolean setImageViewPosition(int centerX, int centerY, float scale) {
int inverseX = mImageWidth - centerX;
int inverseY = mImageHeight - centerY;
TileImageView t = mImageView;
int rotation = mImageRotation;
switch (rotation) {
case 0: return t.setPosition(centerX, centerY, scale, 0);
case 90: return t.setPosition(centerY, inverseX, scale, 90);
case 180: return t.setPosition(inverseX, inverseY, scale, 180);
case 270: return t.setPosition(inverseY, centerX, scale, 270);
default: throw new IllegalArgumentException(String.valueOf(rotation));
}
}
@Override
public void render(GLCanvas canvas) {
AnimationController a = mAnimation;
if (a.calculate(AnimationTime.get())) invalidate();
setImageViewPosition(a.getCenterX(), a.getCenterY(), a.getScale());
super.render(canvas);
}
@Override
public void renderBackground(GLCanvas canvas) {
canvas.clearBuffer();
}
public RectF getCropRectangle() {
if (mHighlightRectangle.getVisibility() == GLView.INVISIBLE) return null;
RectF rect = mHighlightRectangle.mHighlightRect;
RectF result = new RectF(rect.left * mImageWidth, rect.top * mImageHeight,
rect.right * mImageWidth, rect.bottom * mImageHeight);
return result;
}
public int getImageWidth() {
return mImageWidth;
}
public int getImageHeight() {
return mImageHeight;
}
private class FaceHighlightView extends GLView {
private static final int INDEX_NONE = -1;
private ArrayList<RectF> mFaces = new ArrayList<RectF>();
private RectF mRect = new RectF();
private int mPressedFaceIndex = INDEX_NONE;
public void addFace(RectF faceRect) {
mFaces.add(faceRect);
invalidate();
}
private void renderFace(GLCanvas canvas, RectF face, boolean pressed) {
GL11 gl = canvas.getGLInstance();
if (pressed) {
gl.glEnable(GL11.GL_STENCIL_TEST);
gl.glClear(GL11.GL_STENCIL_BUFFER_BIT);
gl.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE);
gl.glStencilFunc(GL11.GL_ALWAYS, 1, 1);
}
RectF r = mAnimation.mapRect(face, mRect);
canvas.fillRect(r.left, r.top, r.width(), r.height(), Color.TRANSPARENT);
canvas.drawRect(r.left, r.top, r.width(), r.height(), mFacePaint);
if (pressed) {
gl.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
}
}
@Override
protected void renderBackground(GLCanvas canvas) {
ArrayList<RectF> faces = mFaces;
for (int i = 0, n = faces.size(); i < n; ++i) {
renderFace(canvas, faces.get(i), i == mPressedFaceIndex);
}
GL11 gl = canvas.getGLInstance();
if (mPressedFaceIndex != INDEX_NONE) {
gl.glStencilFunc(GL11.GL_NOTEQUAL, 1, 1);
canvas.fillRect(0, 0, getWidth(), getHeight(), 0x66000000);
gl.glDisable(GL11.GL_STENCIL_TEST);
}
}
private void setPressedFace(int index) {
if (mPressedFaceIndex == index) return;
mPressedFaceIndex = index;
invalidate();
}
private int getFaceIndexByPosition(float x, float y) {
ArrayList<RectF> faces = mFaces;
for (int i = 0, n = faces.size(); i < n; ++i) {
RectF r = mAnimation.mapRect(faces.get(i), mRect);
if (r.contains(x, y)) return i;
}
return INDEX_NONE;
}
@Override
protected boolean onTouch(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE: {
setPressedFace(getFaceIndexByPosition(x, y));
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
int index = mPressedFaceIndex;
setPressedFace(INDEX_NONE);
if (index != INDEX_NONE) {
mHighlightRectangle.setRectangle(mFaces.get(index));
mHighlightRectangle.setVisibility(GLView.VISIBLE);
setVisibility(GLView.INVISIBLE);
}
}
}
return true;
}
}
private class AnimationController extends Animation {
private int mCurrentX;
private int mCurrentY;
private float mCurrentScale;
private int mStartX;
private int mStartY;
private float mStartScale;
private int mTargetX;
private int mTargetY;
private float mTargetScale;
public AnimationController() {
setDuration(ANIMATION_DURATION);
setInterpolator(new DecelerateInterpolator(4));
}
public void initialize() {
mCurrentX = mImageWidth / 2;
mCurrentY = mImageHeight / 2;
mCurrentScale = Math.min(2, Math.min(
(float) getWidth() / mImageWidth,
(float) getHeight() / mImageHeight));
}
public void startParkingAnimation(RectF highlight) {
RectF r = mAnimation.mapRect(highlight, new RectF());
int width = getWidth();
int height = getHeight();
float wr = r.width() / width;
float hr = r.height() / height;
final int d = ANIMATION_TRIGGER;
if (wr >= MIN_SELECTION_RATIO && wr < MAX_SELECTION_RATIO
&& hr >= MIN_SELECTION_RATIO && hr < MAX_SELECTION_RATIO
&& r.left >= d && r.right < width - d
&& r.top >= d && r.bottom < height - d) return;
mStartX = mCurrentX;
mStartY = mCurrentY;
mStartScale = mCurrentScale;
calculateTarget(highlight);
start();
}
public void parkNow(RectF highlight) {
calculateTarget(highlight);
forceStop();
mStartX = mCurrentX = mTargetX;
mStartY = mCurrentY = mTargetY;
mStartScale = mCurrentScale = mTargetScale;
}
public void inverseMapPoint(PointF point) {
float s = mCurrentScale;
point.x = Utils.clamp(((point.x - getWidth() * 0.5f) / s
+ mCurrentX) / mImageWidth, 0, 1);
point.y = Utils.clamp(((point.y - getHeight() * 0.5f) / s
+ mCurrentY) / mImageHeight, 0, 1);
}
public RectF mapRect(RectF input, RectF output) {
float offsetX = getWidth() * 0.5f;
float offsetY = getHeight() * 0.5f;
int x = mCurrentX;
int y = mCurrentY;
float s = mCurrentScale;
output.set(
offsetX + (input.left * mImageWidth - x) * s,
offsetY + (input.top * mImageHeight - y) * s,
offsetX + (input.right * mImageWidth - x) * s,
offsetY + (input.bottom * mImageHeight - y) * s);
return output;
}
@Override
protected void onCalculate(float progress) {
mCurrentX = Math.round(mStartX + (mTargetX - mStartX) * progress);
mCurrentY = Math.round(mStartY + (mTargetY - mStartY) * progress);
mCurrentScale = mStartScale + (mTargetScale - mStartScale) * progress;
if (mCurrentX == mTargetX && mCurrentY == mTargetY
&& mCurrentScale == mTargetScale) forceStop();
}
public int getCenterX() {
return mCurrentX;
}
public int getCenterY() {
return mCurrentY;
}
public float getScale() {
return mCurrentScale;
}
private void calculateTarget(RectF highlight) {
float width = getWidth();
float height = getHeight();
if (mImageWidth != SIZE_UNKNOWN) {
float minScale = Math.min(width / mImageWidth, height / mImageHeight);
float scale = Utils.clamp(SELECTION_RATIO * Math.min(
width / (highlight.width() * mImageWidth),
height / (highlight.height() * mImageHeight)), minScale, 2f);
int centerX = Math.round(
mImageWidth * (highlight.left + highlight.right) * 0.5f);
int centerY = Math.round(
mImageHeight * (highlight.top + highlight.bottom) * 0.5f);
if (Math.round(mImageWidth * scale) > width) {
int limitX = Math.round(width * 0.5f / scale);
centerX = Math.round(
(highlight.left + highlight.right) * mImageWidth / 2);
centerX = Utils.clamp(centerX, limitX, mImageWidth - limitX);
} else {
centerX = mImageWidth / 2;
}
if (Math.round(mImageHeight * scale) > height) {
int limitY = Math.round(height * 0.5f / scale);
centerY = Math.round(
(highlight.top + highlight.bottom) * mImageHeight / 2);
centerY = Utils.clamp(centerY, limitY, mImageHeight - limitY);
} else {
centerY = mImageHeight / 2;
}
mTargetX = centerX;
mTargetY = centerY;
mTargetScale = scale;
}
}
}
private class HighlightRectangle extends GLView {
private RectF mHighlightRect = new RectF(0.25f, 0.25f, 0.75f, 0.75f);
private RectF mTempRect = new RectF();
private PointF mTempPoint = new PointF();
private ResourceTexture mArrow;
private int mMovingEdges = 0;
private float mReferenceX;
private float mReferenceY;
public HighlightRectangle() {
mArrow = new ResourceTexture(mActivity.getAndroidContext(),
R.drawable.camera_crop_holo);
}
public void setInitRectangle() {
float targetRatio = mAspectRatio == UNSPECIFIED
? 1f
: mAspectRatio * mImageHeight / mImageWidth;
float w = SELECTION_RATIO / 2f;
float h = SELECTION_RATIO / 2f;
if (targetRatio > 1) {
h = w / targetRatio;
} else {
w = h * targetRatio;
}
mHighlightRect.set(0.5f - w, 0.5f - h, 0.5f + w, 0.5f + h);
}
public void setRectangle(RectF faceRect) {
mHighlightRect.set(faceRect);
mAnimation.startParkingAnimation(faceRect);
invalidate();
}
private void moveEdges(MotionEvent event) {
float scale = mAnimation.getScale();
float dx = (event.getX() - mReferenceX) / scale / mImageWidth;
float dy = (event.getY() - mReferenceY) / scale / mImageHeight;
mReferenceX = event.getX();
mReferenceY = event.getY();
RectF r = mHighlightRect;
if ((mMovingEdges & MOVE_BLOCK) != 0) {
dx = Utils.clamp(dx, -r.left, 1 - r.right);
dy = Utils.clamp(dy, -r.top , 1 - r.bottom);
r.top += dy;
r.bottom += dy;
r.left += dx;
r.right += dx;
} else {
PointF point = mTempPoint;
point.set(mReferenceX, mReferenceY);
mAnimation.inverseMapPoint(point);
float left = r.left + MIN_SELECTION_LENGTH / mImageWidth;
float right = r.right - MIN_SELECTION_LENGTH / mImageWidth;
float top = r.top + MIN_SELECTION_LENGTH / mImageHeight;
float bottom = r.bottom - MIN_SELECTION_LENGTH / mImageHeight;
if ((mMovingEdges & MOVE_RIGHT) != 0) {
r.right = Utils.clamp(point.x, left, 1f);
}
if ((mMovingEdges & MOVE_LEFT) != 0) {
r.left = Utils.clamp(point.x, 0, right);
}
if ((mMovingEdges & MOVE_TOP) != 0) {
r.top = Utils.clamp(point.y, 0, bottom);
}
if ((mMovingEdges & MOVE_BOTTOM) != 0) {
r.bottom = Utils.clamp(point.y, top, 1f);
}
if (mAspectRatio != UNSPECIFIED) {
float targetRatio = mAspectRatio * mImageHeight / mImageWidth;
if (r.width() / r.height() > targetRatio) {
float height = r.width() / targetRatio;
if ((mMovingEdges & MOVE_BOTTOM) != 0) {
r.bottom = Utils.clamp(r.top + height, top, 1f);
} else {
r.top = Utils.clamp(r.bottom - height, 0, bottom);
}
} else {
float width = r.height() * targetRatio;
if ((mMovingEdges & MOVE_LEFT) != 0) {
r.left = Utils.clamp(r.right - width, 0, right);
} else {
r.right = Utils.clamp(r.left + width, left, 1f);
}
}
if (r.width() / r.height() > targetRatio) {
float width = r.height() * targetRatio;
if ((mMovingEdges & MOVE_LEFT) != 0) {
r.left = Utils.clamp(r.right - width, 0, right);
} else {
r.right = Utils.clamp(r.left + width, left, 1f);
}
} else {
float height = r.width() / targetRatio;
if ((mMovingEdges & MOVE_BOTTOM) != 0) {
r.bottom = Utils.clamp(r.top + height, top, 1f);
} else {
r.top = Utils.clamp(r.bottom - height, 0, bottom);
}
}
}
}
invalidate();
}
private void setMovingEdges(MotionEvent event) {
RectF r = mAnimation.mapRect(mHighlightRect, mTempRect);
float x = event.getX();
float y = event.getY();
if (x > r.left + TOUCH_TOLERANCE && x < r.right - TOUCH_TOLERANCE
&& y > r.top + TOUCH_TOLERANCE && y < r.bottom - TOUCH_TOLERANCE) {
mMovingEdges = MOVE_BLOCK;
return;
}
boolean inVerticalRange = (r.top - TOUCH_TOLERANCE) <= y
&& y <= (r.bottom + TOUCH_TOLERANCE);
boolean inHorizontalRange = (r.left - TOUCH_TOLERANCE) <= x
&& x <= (r.right + TOUCH_TOLERANCE);
if (inVerticalRange) {
boolean left = Math.abs(x - r.left) <= TOUCH_TOLERANCE;
boolean right = Math.abs(x - r.right) <= TOUCH_TOLERANCE;
if (left && right) {
left = Math.abs(x - r.left) < Math.abs(x - r.right);
right = !left;
}
if (left) mMovingEdges |= MOVE_LEFT;
if (right) mMovingEdges |= MOVE_RIGHT;
if (mAspectRatio != UNSPECIFIED && inHorizontalRange) {
mMovingEdges |= (y >
(r.top + r.bottom) / 2) ? MOVE_BOTTOM : MOVE_TOP;
}
}
if (inHorizontalRange) {
boolean top = Math.abs(y - r.top) <= TOUCH_TOLERANCE;
boolean bottom = Math.abs(y - r.bottom) <= TOUCH_TOLERANCE;
if (top && bottom) {
top = Math.abs(y - r.top) < Math.abs(y - r.bottom);
bottom = !top;
}
if (top) mMovingEdges |= MOVE_TOP;
if (bottom) mMovingEdges |= MOVE_BOTTOM;
if (mAspectRatio != UNSPECIFIED && inVerticalRange) {
mMovingEdges |= (x >
(r.left + r.right) / 2) ? MOVE_RIGHT : MOVE_LEFT;
}
}
}
@Override
protected boolean onTouch(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
mReferenceX = event.getX();
mReferenceY = event.getY();
setMovingEdges(event);
invalidate();
return true;
}
case MotionEvent.ACTION_MOVE:
moveEdges(event);
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
mMovingEdges = 0;
mAnimation.startParkingAnimation(mHighlightRect);
invalidate();
return true;
}
}
return true;
}
@Override
protected void renderBackground(GLCanvas canvas) {
RectF r = mAnimation.mapRect(mHighlightRect, mTempRect);
drawHighlightRectangle(canvas, r);
float centerY = (r.top + r.bottom) / 2;
float centerX = (r.left + r.right) / 2;
boolean notMoving = mMovingEdges == 0;
if ((mMovingEdges & MOVE_RIGHT) != 0 || notMoving) {
mArrow.draw(canvas,
Math.round(r.right - mArrow.getWidth() / 2),
Math.round(centerY - mArrow.getHeight() / 2));
}
if ((mMovingEdges & MOVE_LEFT) != 0 || notMoving) {
mArrow.draw(canvas,
Math.round(r.left - mArrow.getWidth() / 2),
Math.round(centerY - mArrow.getHeight() / 2));
}
if ((mMovingEdges & MOVE_TOP) != 0 || notMoving) {
mArrow.draw(canvas,
Math.round(centerX - mArrow.getWidth() / 2),
Math.round(r.top - mArrow.getHeight() / 2));
}
if ((mMovingEdges & MOVE_BOTTOM) != 0 || notMoving) {
mArrow.draw(canvas,
Math.round(centerX - mArrow.getWidth() / 2),
Math.round(r.bottom - mArrow.getHeight() / 2));
}
}
private void drawHighlightRectangle(GLCanvas canvas, RectF r) {
GL11 gl = canvas.getGLInstance();
gl.glLineWidth(3.0f);
gl.glEnable(GL11.GL_LINE_SMOOTH);
gl.glEnable(GL11.GL_STENCIL_TEST);
gl.glClear(GL11.GL_STENCIL_BUFFER_BIT);
gl.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE);
gl.glStencilFunc(GL11.GL_ALWAYS, 1, 1);
if (mSpotlightRatioX == 0 || mSpotlightRatioY == 0) {
canvas.fillRect(r.left, r.top, r.width(), r.height(), Color.TRANSPARENT);
canvas.drawRect(r.left, r.top, r.width(), r.height(), mPaint);
} else {
float sx = r.width() * mSpotlightRatioX;
float sy = r.height() * mSpotlightRatioY;
float cx = r.centerX();
float cy = r.centerY();
canvas.fillRect(cx - sx / 2, cy - sy / 2, sx, sy, Color.TRANSPARENT);
canvas.drawRect(cx - sx / 2, cy - sy / 2, sx, sy, mPaint);
canvas.drawRect(r.left, r.top, r.width(), r.height(), mPaint);
gl.glStencilFunc(GL11.GL_NOTEQUAL, 1, 1);
gl.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE);
canvas.drawRect(cx - sy / 2, cy - sx / 2, sy, sx, mPaint);
canvas.fillRect(cx - sy / 2, cy - sx / 2, sy, sx, Color.TRANSPARENT);
canvas.fillRect(r.left, r.top, r.width(), r.height(), 0x80000000);
}
gl.glStencilFunc(GL11.GL_NOTEQUAL, 1, 1);
gl.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
canvas.fillRect(0, 0, getWidth(), getHeight(), 0xA0000000);
gl.glDisable(GL11.GL_STENCIL_TEST);
}
}
private class DetectFaceTask extends Thread {
private final FaceDetector.Face[] mFaces = new FaceDetector.Face[MAX_FACE_COUNT];
private final Bitmap mFaceBitmap;
private int mFaceCount;
public DetectFaceTask(Bitmap bitmap) {
mFaceBitmap = bitmap;
setName("face-detect");
}
@Override
public void run() {
Bitmap bitmap = mFaceBitmap;
FaceDetector detector = new FaceDetector(
bitmap.getWidth(), bitmap.getHeight(), MAX_FACE_COUNT);
mFaceCount = detector.findFaces(bitmap, mFaces);
mMainHandler.sendMessage(
mMainHandler.obtainMessage(MSG_UPDATE_FACES, this));
}
private RectF getFaceRect(FaceDetector.Face face) {
PointF point = new PointF();
face.getMidPoint(point);
int width = mFaceBitmap.getWidth();
int height = mFaceBitmap.getHeight();
float rx = face.eyesDistance() * FACE_EYE_RATIO;
float ry = rx;
float aspect = mAspectRatio;
if (aspect != UNSPECIFIED) {
if (aspect > 1) {
rx = ry * aspect;
} else {
ry = rx / aspect;
}
}
RectF r = new RectF(
point.x - rx, point.y - ry, point.x + rx, point.y + ry);
r.intersect(0, 0, width, height);
if (aspect != UNSPECIFIED) {
if (r.width() / r.height() > aspect) {
float w = r.height() * aspect;
r.left = (r.left + r.right - w) * 0.5f;
r.right = r.left + w;
} else {
float h = r.width() / aspect;
r.top = (r.top + r.bottom - h) * 0.5f;
r.bottom = r.top + h;
}
}
r.left /= width;
r.right /= width;
r.top /= height;
r.bottom /= height;
return r;
}
public void updateFaces() {
if (mFaceCount > 1) {
for (int i = 0, n = mFaceCount; i < n; ++i) {
mFaceDetectionView.addFace(getFaceRect(mFaces[i]));
}
mFaceDetectionView.setVisibility(GLView.VISIBLE);
Toast.makeText(mActivity.getAndroidContext(),
R.string.multiface_crop_help, Toast.LENGTH_SHORT).show();
} else if (mFaceCount == 1) {
mFaceDetectionView.setVisibility(GLView.INVISIBLE);
mHighlightRectangle.setRectangle(getFaceRect(mFaces[0]));
mHighlightRectangle.setVisibility(GLView.VISIBLE);
} else /*mFaceCount == 0*/ {
mHighlightRectangle.setInitRectangle();
mHighlightRectangle.setVisibility(GLView.VISIBLE);
}
}
}
public void setDataModel(TileImageView.Model dataModel, int rotation) {
if (((rotation / 90) & 0x01) != 0) {
mImageWidth = dataModel.getImageHeight();
mImageHeight = dataModel.getImageWidth();
} else {
mImageWidth = dataModel.getImageWidth();
mImageHeight = dataModel.getImageHeight();
}
mImageRotation = rotation;
mImageView.setModel(dataModel);
mAnimation.initialize();
}
public void detectFaces(Bitmap bitmap) {
int rotation = mImageRotation;
int width = bitmap.getWidth();
int height = bitmap.getHeight();
float scale = FloatMath.sqrt((float) FACE_PIXEL_COUNT / (width * height));
// faceBitmap is a correctly rotated bitmap, as viewed by a user.
Bitmap faceBitmap;
if (((rotation / 90) & 1) == 0) {
int w = (Math.round(width * scale) & ~1); // must be even
int h = Math.round(height * scale);
faceBitmap = Bitmap.createBitmap(w, h, Config.RGB_565);
Canvas canvas = new Canvas(faceBitmap);
canvas.rotate(rotation, w / 2, h / 2);
canvas.scale((float) w / width, (float) h / height);
canvas.drawBitmap(bitmap, 0, 0, new Paint(Paint.FILTER_BITMAP_FLAG));
} else {
int w = (Math.round(height * scale) & ~1); // must be even
int h = Math.round(width * scale);
faceBitmap = Bitmap.createBitmap(w, h, Config.RGB_565);
Canvas canvas = new Canvas(faceBitmap);
canvas.translate(w / 2, h / 2);
canvas.rotate(rotation);
canvas.translate(-h / 2, -w / 2);
canvas.scale((float) w / height, (float) h / width);
canvas.drawBitmap(bitmap, 0, 0, new Paint(Paint.FILTER_BITMAP_FLAG));
}
new DetectFaceTask(faceBitmap).start();
}
public void initializeHighlightRectangle() {
mHighlightRectangle.setInitRectangle();
mHighlightRectangle.setVisibility(GLView.VISIBLE);
}
public void resume() {
mImageView.prepareTextures();
}
public void pause() {
mImageView.freeTextures();
}
}