diff options
author | Chih-Chung Chang <> | 2009-04-08 22:35:03 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-04-08 22:35:03 -0700 |
commit | 8f7c4a4f45fa83740a612e33ad87bea48150bcba (patch) | |
tree | e68767de84eb7e61ef7d0d4c1469720694753a38 /src | |
parent | 233b3b9a01f9ddabf5dd7100d4fdd267263c8651 (diff) | |
download | LegacyCamera-8f7c4a4f45fa83740a612e33ad87bea48150bcba.zip LegacyCamera-8f7c4a4f45fa83740a612e33ad87bea48150bcba.tar.gz LegacyCamera-8f7c4a4f45fa83740a612e33ad87bea48150bcba.tar.bz2 |
AI 145270: Code clean up.
Automated import of CL 145270
Diffstat (limited to 'src')
-rw-r--r-- | src/com/android/camera/ActionMenuButton.java | 3 | ||||
-rw-r--r-- | src/com/android/camera/CropImage.java | 780 | ||||
-rw-r--r-- | src/com/android/camera/HighlightView.java | 199 |
3 files changed, 426 insertions, 556 deletions
diff --git a/src/com/android/camera/ActionMenuButton.java b/src/com/android/camera/ActionMenuButton.java index 41a407b..3b473be 100644 --- a/src/com/android/camera/ActionMenuButton.java +++ b/src/com/android/camera/ActionMenuButton.java @@ -59,7 +59,8 @@ public class ActionMenuButton extends TextView { private void init() { setFocusable(true); - setPadding(PADDING_H, PADDING_V, PADDING_H, PADDING_V); + // We need extra padding below to prevent the bubble being cut. + setPadding(0, 0, 0, PADDING_V); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(getContext().getResources() diff --git a/src/com/android/camera/CropImage.java b/src/com/android/camera/CropImage.java index 0932471..29d857c 100644 --- a/src/com/android/camera/CropImage.java +++ b/src/com/android/camera/CropImage.java @@ -43,7 +43,6 @@ import android.provider.MediaStore; import android.util.AttributeSet; import android.util.Config; import android.util.Log; -import android.view.Menu; import android.view.MotionEvent; import android.view.View; import android.view.Window; @@ -66,284 +65,21 @@ public class CropImage extends Activity { private int mOutputX, mOutputY; private boolean mDoFaceDetection = true; private boolean mCircleCrop = false; - private boolean mWaitingToPick; + boolean mWaitingToPick; private boolean mScale; - private boolean mSaving; + boolean mSaving; private boolean mScaleUp = true; - CropImageView mImageView; - ContentResolver mContentResolver; + private CropImageView mImageView; + private ContentResolver mContentResolver; - Bitmap mBitmap; - Bitmap mCroppedImage; + private Bitmap mBitmap; + private Bitmap mCroppedImage; HighlightView mCrop; - IImage mImage; + private IImage mImage; - public CropImage() { - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - return super.onCreateOptionsMenu(menu); - } - - public static class CropImageView extends ImageViewTouchBase { - ArrayList<HighlightView> mHighlightViews = - new ArrayList<HighlightView>(); - HighlightView mMotionHighlightView = null; - float mLastX, mLastY; - int mMotionEdge; - - public CropImageView(Context context) { - super(context); - } - - @Override - protected boolean doesScrolling() { - return false; - } - - @Override - protected void onLayout(boolean changed, int left, int top, - int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - if (mBitmapDisplayed != null) { - for (HighlightView hv : mHighlightViews) { - hv.mMatrix.set(getImageMatrix()); - hv.invalidate(); - if (hv.mIsFocused) { - centerBasedOnHighlightView(hv); - } - } - } - } - - public CropImageView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - protected void zoomTo(float scale, float centerX, float centerY) { - super.zoomTo(scale, centerX, centerY); - for (HighlightView hv : mHighlightViews) { - hv.mMatrix.set(getImageMatrix()); - hv.invalidate(); - } - } - - protected void zoomIn() { - super.zoomIn(); - for (HighlightView hv : mHighlightViews) { - hv.mMatrix.set(getImageMatrix()); - hv.invalidate(); - } - } - - protected void zoomOut() { - super.zoomOut(); - for (HighlightView hv : mHighlightViews) { - hv.mMatrix.set(getImageMatrix()); - hv.invalidate(); - } - } - - - @Override - protected boolean usePerfectFitBitmap() { - return false; - } - - @Override - protected void postTranslate(float deltaX, float deltaY) { - super.postTranslate(deltaX, deltaY); - for (int i = 0; i < mHighlightViews.size(); i++) { - HighlightView hv = mHighlightViews.get(i); - hv.mMatrix.postTranslate(deltaX, deltaY); - hv.invalidate(); - } - } - - private void recomputeFocus(MotionEvent event) { - for (int i = 0; i < mHighlightViews.size(); i++) { - HighlightView hv = mHighlightViews.get(i); - hv.setFocus(false); - hv.invalidate(); - } - - for (int i = 0; i < mHighlightViews.size(); i++) { - HighlightView hv = mHighlightViews.get(i); - int edge = hv.getHit(event.getX(), event.getY()); - if (edge != HighlightView.GROW_NONE) { - if (!hv.hasFocus()) { - hv.setFocus(true); - hv.invalidate(); - } - break; - } - } - invalidate(); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - CropImage cropImage = (CropImage) mContext; - if (cropImage.mSaving) { - return false; - } - - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - if (cropImage.mWaitingToPick) { - recomputeFocus(event); - } else { - for (int i = 0; i < mHighlightViews.size(); i++) { - HighlightView hv = mHighlightViews.get(i); - int edge = hv.getHit(event.getX(), event.getY()); - if (edge != HighlightView.GROW_NONE) { - mMotionEdge = edge; - mMotionHighlightView = hv; - mLastX = event.getX(); - mLastY = event.getY(); - mMotionHighlightView.setMode( - edge == HighlightView.MOVE - ? HighlightView.ModifyMode.Move - : HighlightView.ModifyMode.Grow); - break; - } - } - } - break; - case MotionEvent.ACTION_UP: - if (cropImage.mWaitingToPick) { - for (int i = 0; i < mHighlightViews.size(); i++) { - HighlightView hv = mHighlightViews.get(i); - if (hv.hasFocus()) { - cropImage.mCrop = hv; - for (int j = 0; j < mHighlightViews.size(); - j++) { - if (j == i) { - continue; - } - mHighlightViews.get(j).setHidden(true); - } - centerBasedOnHighlightView(hv); - ((CropImage) mContext).mWaitingToPick = false; - return true; - } - } - } else if (mMotionHighlightView != null) { - centerBasedOnHighlightView(mMotionHighlightView); - mMotionHighlightView.setMode( - HighlightView.ModifyMode.None); - } - mMotionHighlightView = null; - break; - case MotionEvent.ACTION_MOVE: - if (cropImage.mWaitingToPick) { - recomputeFocus(event); - } else if (mMotionHighlightView != null) { - mMotionHighlightView.handleMotion(mMotionEdge, - event.getX() - mLastX, - event.getY() - mLastY); - mLastX = event.getX(); - mLastY = event.getY(); - - if (true) { - // This section of code is optional. It has some - // user benefit in that moving the crop rectangle - // against the edge of the screen causes scrolling - // but it means that the crop rectangle is no longer - // fixed under the user's finger. - ensureVisible(mMotionHighlightView); - } - } - break; - } - - switch (event.getAction()) { - case MotionEvent.ACTION_UP: - center(true, true, true); - break; - case MotionEvent.ACTION_MOVE: - // if we're not zoomed then there's no point in even - // allowing the user to move the image around. This call - // to center puts it back to the normalized location (with - // false meaning don't animate). - if (getScale() == 1F) { - center(true, true, false); - } - break; - } - - return true; - } - - private void ensureVisible(HighlightView hv) { - Rect r = hv.mDrawRect; - - int panDeltaX1 = Math.max(0, mLeft - r.left); - int panDeltaX2 = Math.min(0, mRight - r.right); - - int panDeltaY1 = Math.max(0, mTop - r.top); - int panDeltaY2 = Math.min(0, mBottom - r.bottom); - - int panDeltaX = panDeltaX1 != 0 ? panDeltaX1 : panDeltaX2; - int panDeltaY = panDeltaY1 != 0 ? panDeltaY1 : panDeltaY2; - - if (panDeltaX != 0 || panDeltaY != 0) { - panBy(panDeltaX, panDeltaY); - } - } - - private void centerBasedOnHighlightView(HighlightView hv) { - Rect drawRect = hv.mDrawRect; - - float width = drawRect.width(); - float height = drawRect.height(); - - float thisWidth = getWidth(); - float thisHeight = getHeight(); - - float z1 = thisWidth / width * .6F; - float z2 = thisHeight / height * .6F; - - float zoom = Math.min(z1, z2); - zoom = zoom * this.getScale(); - zoom = Math.max(1F, zoom); - - if ((Math.abs(zoom - getScale()) / zoom) > .1) { - float [] coordinates = new float[] {hv.mCropRect.centerX(), - hv.mCropRect.centerY()}; - getImageMatrix().mapPoints(coordinates); - zoomTo(zoom, coordinates[0], coordinates[1], 300F); - } - - ensureVisible(hv); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - for (int i = 0; i < mHighlightViews.size(); i++) { - mHighlightViews.get(i).draw(canvas); - } - } - - public HighlightView get(int i) { - return mHighlightViews.get(i); - } - - public int size() { - return mHighlightViews.size(); - } - - public void add(HighlightView hv) { - mHighlightViews.add(hv); - invalidate(); - } - } - - private void fillCanvas(int width, int height, Canvas c) { + private static void fillCanvas(int width, int height, Canvas c) { Paint paint = new Paint(); paint.setColor(0x00000000); // pure alpha paint.setStyle(android.graphics.Paint.Style.FILL); @@ -365,7 +101,7 @@ public class CropImage extends Activity { MenuHelper.showStorageToast(this); try { - android.content.Intent intent = getIntent(); + Intent intent = getIntent(); Bundle extras = intent.getExtras(); if (Config.LOGV) { Log.v(TAG, "extras are " + extras); @@ -425,46 +161,9 @@ public class CropImage extends Activity { mHandler.postDelayed(new Runnable() { public void run() { - if (isFinishing()) { - return; - } - mFaceDetectionDialog = ProgressDialog.show(CropImage.this, - null, - getResources().getString( - R.string.runningFaceDetection), - true, false); - mImageView.setImageBitmapResetBase(mBitmap, true, true); - if (mImageView.getScale() == 1F) { - mImageView.center(true, true, false); - } - - new Thread(new Runnable() { - public void run() { - final Bitmap b = mImage != null - ? mImage.fullSizeBitmap(500) - : mBitmap; - if (Config.LOGV) { - Log.v(TAG, "back from fullSizeBitmap(500) " - + "with bitmap of size " + b.getWidth() - + " / " + b.getHeight()); - } - mHandler.post(new Runnable() { - public void run() { - if (b != mBitmap && b != null) { - mBitmap = b; - mImageView.setImageBitmapResetBase(b, - true, false); - } - if (mImageView.getScale() == 1F) { - mImageView.center(true, true, false); - } - - new Thread(mRunFaceDetection).start(); - } - }); - } - }).start(); - }}, 100); + startFaceDetection(); + } + }, 100); } catch (Exception e) { Log.e(TAG, "Failed to load bitmap", e); finish(); @@ -486,6 +185,45 @@ public class CropImage extends Activity { }); } + private void startFaceDetection() { + if (isFinishing()) { + return; + } + mFaceDetectionDialog = ProgressDialog.show(CropImage.this, null, + getResources().getString(R.string.runningFaceDetection), + true, false); + mImageView.setImageBitmapResetBase(mBitmap, true, true); + if (mImageView.getScale() == 1F) { + mImageView.center(true, true, false); + } + + new Thread(new Runnable() { + public void run() { + final Bitmap b = (mImage != null) + ? mImage.fullSizeBitmap(500) + : mBitmap; + if (Config.LOGV) { + Log.v(TAG, "back from fullSizeBitmap(500) " + + "with bitmap of size " + b.getWidth() + + " / " + b.getHeight()); + } + mHandler.post(new Runnable() { + public void run() { + if (b != mBitmap && b != null) { + mBitmap = b; + mImageView.setImageBitmapResetBase(b, true, false); + } + if (mImageView.getScale() == 1F) { + mImageView.center(true, true, false); + } + + new Thread(mRunFaceDetection).start(); + } + }); + } + }).start(); + } + private void onSaveClicked() { // TODO this code needs to change to use the decode/crop/encode single // step api so that we don't require that the whole (possibly large) @@ -531,9 +269,7 @@ public class CropImage extends Activity { /* If the output is required to a specific size then scale or fill */ if (mOutputX != 0 && mOutputY != 0) { - if (mScale) { - /* Scale the image to the required dimensions */ mCroppedImage = ImageLoader.transform(new Matrix(), mCroppedImage, mOutputX, mOutputY, mScaleUp); @@ -562,6 +298,7 @@ public class CropImage extends Activity { } } + // Return the cropped image directly or save it to the specified URI. Bundle myExtras = getIntent().getExtras(); if (myExtras != null && (myExtras.getParcelable("data") != null || myExtras.getBoolean("return-data"))) { @@ -595,7 +332,6 @@ public class CropImage extends Activity { mCroppedImage.compress(mSaveFormat, 75, outputStream); } - } catch (IOException ex) { if (Config.LOGV) { Log.v(TAG, "got IOException " + ex); @@ -618,87 +354,66 @@ public class CropImage extends Activity { extras.putString("rect", mCrop.getCropRect().toString()); - // here we decide whether to create a new image or - // modify the existing image - if (false) { - /* - // this is the "modify" case - ImageManager.IGetBoolean_cancelable cancelable = - mImage.saveImageContents(mCroppedImage, null, - null, null, mImage.getDateTaken(), 0, false); - boolean didSave = cancelable.get(); - extras.putString("thumb1uri", - mImage.thumbUri().toString()); + java.io.File oldPath + = new java.io.File(mImage.getDataPath()); + java.io.File directory + = new java.io.File(oldPath.getParent()); + + int x = 0; + String fileName = oldPath.getName(); + fileName = fileName.substring(0, + fileName.lastIndexOf(".")); + + while (true) { + x += 1; + String candidate = directory.toString() + + "/" + fileName + "-" + x + ".jpg"; + if (Config.LOGV) { + Log.v(TAG, "candidate is " + candidate); + } + boolean exists = + (new java.io.File(candidate)).exists(); + if (!exists) { + break; + } + } + + try { + Uri newUri = ImageManager + .instance() + .addImage( + CropImage.this, + getContentResolver(), + mImage.getTitle(), + mImage.getDescription(), + mImage.getDateTaken(), + null, // TODO this null is + // going to cause us to + // lose the location (gps) + 0, // TODO this is going to + // cause the orientation + // to reset + directory.toString(), + fileName + "-" + x + ".jpg"); + + IAddImageCancelable cancelable = + ImageManager.instance() + .storeImage( + newUri, + CropImage.this, + getContentResolver(), + 0, // TODO fix this orientation + mCroppedImage, + null); + + cancelable.get(); setResult(RESULT_OK, (new Intent()) - .setAction(mImage.fullSizeImageUri() - .toString()) + .setAction(newUri.toString()) .putExtras(extras)); - */ - } else { - // this is the "new image" case - java.io.File oldPath - = new java.io.File(mImage.getDataPath()); - java.io.File directory - = new java.io.File(oldPath.getParent()); - - int x = 0; - String fileName = oldPath.getName(); - fileName = fileName.substring(0, - fileName.lastIndexOf(".")); - - while (true) { - x += 1; - String candidate = directory.toString() - + "/" + fileName + "-" + x + ".jpg"; - if (Config.LOGV) { - Log.v(TAG, "candidate is " - + candidate); - } - boolean exists = - (new java.io.File(candidate)).exists(); - if (!exists) { - break; - } - } - - try { - Uri newUri = ImageManager - .instance() - .addImage( - CropImage.this, - getContentResolver(), - mImage.getTitle(), - mImage.getDescription(), - mImage.getDateTaken(), - null, // TODO this null is - // going to cause us to - // lose the location (gps) - 0, // TODO this is going to - // cause the orientation - // to reset - directory.toString(), - fileName + "-" + x + ".jpg"); - - IAddImageCancelable cancelable = - ImageManager.instance() - .storeImage( - newUri, - CropImage.this, - getContentResolver(), - 0, // TODO fix this orientation - mCroppedImage, - null); - - cancelable.get(); - setResult(RESULT_OK, - (new Intent()) - .setAction(newUri.toString()) - .putExtras(extras)); - } catch (Exception ex) { - // basically ignore this or put up - // some ui saying we failed - } + } catch (Exception ex) { + // basically ignore this or put up + // some ui saying we failed } } finish(); @@ -709,20 +424,15 @@ public class CropImage extends Activity { } } - @Override - public void onResume() { - super.onResume(); - } - Handler mHandler = new Handler(); Runnable mRunFaceDetection = new Runnable() { float mScale = 1F; - RectF mUnion = null; Matrix mImageMatrix; FaceDetector.Face[] mFaces = new FaceDetector.Face[3]; int mNumFaces; + // For each face, we create a HightlightView for it. private void handleFace(FaceDetector.Face f) { PointF midPoint = new PointF(); @@ -734,7 +444,7 @@ public class CropImage extends Activity { int midX = (int) midPoint.x; int midY = (int) midPoint.y; - HighlightView hv = makeHighlightView(); + HighlightView hv = new HighlightView(mImageView); int width = mBitmap.getWidth(); int height = mBitmap.getHeight(); @@ -764,21 +474,12 @@ public class CropImage extends Activity { hv.setup(mImageMatrix, imageRect, faceRect, mCircleCrop, mAspectX != 0 && mAspectY != 0); - if (mUnion == null) { - mUnion = new RectF(faceRect); - } else { - mUnion.union(faceRect); - } - mImageView.add(hv); } - private HighlightView makeHighlightView() { - return new HighlightView(mImageView); - } - + // Create a default HightlightView if we found no face in the picture. private void makeDefault() { - HighlightView hv = makeHighlightView(); + HighlightView hv = new HighlightView(mImageView); int width = mBitmap.getWidth(); int height = mBitmap.getHeight(); @@ -792,10 +493,8 @@ public class CropImage extends Activity { if (mAspectX != 0 && mAspectY != 0) { if (mAspectX > mAspectY) { cropHeight = cropWidth * mAspectY / mAspectX; -// Log.v(TAG, "adjusted cropHeight to " + cropHeight); } else { cropWidth = cropHeight * mAspectX / mAspectY; -// Log.v(TAG, "adjusted cropWidth to " + cropWidth); } } @@ -808,12 +507,12 @@ public class CropImage extends Activity { mImageView.add(hv); } + // Scale the image down for faster face detection. private Bitmap prepareBitmap() { if (mBitmap == null) { return null; } - // scale the image down for faster face detection // 256 pixels wide is enough. if (mBitmap.getWidth() > 256) { mScale = 256.0F / (float) mBitmap.getWidth(); @@ -832,11 +531,8 @@ public class CropImage extends Activity { mScale = 1.0F / mScale; if (faceBitmap != null && mDoFaceDetection) { FaceDetector detector = new FaceDetector(faceBitmap.getWidth(), - faceBitmap.getHeight(), mFaces.length); + faceBitmap.getHeight(), mFaces.length); mNumFaces = detector.findFaces(faceBitmap, mFaces); - if (Config.LOGV) { - Log.v(TAG, "numFaces is " + mNumFaces); - } } mHandler.post(new Runnable() { public void run() { @@ -864,7 +560,6 @@ public class CropImage extends Activity { } } }); - } }; @@ -888,3 +583,248 @@ public class CropImage extends Activity { } } } + +class CropImageView extends ImageViewTouchBase { + ArrayList<HighlightView> mHighlightViews = new ArrayList<HighlightView>(); + HighlightView mMotionHighlightView = null; + float mLastX, mLastY; + int mMotionEdge; + + @Override + protected boolean doesScrolling() { + return false; + } + + @Override + protected void onLayout(boolean changed, int left, int top, + int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + if (mBitmapDisplayed != null) { + for (HighlightView hv : mHighlightViews) { + hv.mMatrix.set(getImageMatrix()); + hv.invalidate(); + if (hv.mIsFocused) { + centerBasedOnHighlightView(hv); + } + } + } + } + + public CropImageView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + protected void zoomTo(float scale, float centerX, float centerY) { + super.zoomTo(scale, centerX, centerY); + for (HighlightView hv : mHighlightViews) { + hv.mMatrix.set(getImageMatrix()); + hv.invalidate(); + } + } + + protected void zoomIn() { + super.zoomIn(); + for (HighlightView hv : mHighlightViews) { + hv.mMatrix.set(getImageMatrix()); + hv.invalidate(); + } + } + + protected void zoomOut() { + super.zoomOut(); + for (HighlightView hv : mHighlightViews) { + hv.mMatrix.set(getImageMatrix()); + hv.invalidate(); + } + } + + @Override + protected boolean usePerfectFitBitmap() { + return false; + } + + @Override + protected void postTranslate(float deltaX, float deltaY) { + super.postTranslate(deltaX, deltaY); + for (int i = 0; i < mHighlightViews.size(); i++) { + HighlightView hv = mHighlightViews.get(i); + hv.mMatrix.postTranslate(deltaX, deltaY); + hv.invalidate(); + } + } + + // According to the event's position, change the focus to the first + // hitting cropping rectangle. + private void recomputeFocus(MotionEvent event) { + for (int i = 0; i < mHighlightViews.size(); i++) { + HighlightView hv = mHighlightViews.get(i); + hv.setFocus(false); + hv.invalidate(); + } + + for (int i = 0; i < mHighlightViews.size(); i++) { + HighlightView hv = mHighlightViews.get(i); + int edge = hv.getHit(event.getX(), event.getY()); + if (edge != HighlightView.GROW_NONE) { + if (!hv.hasFocus()) { + hv.setFocus(true); + hv.invalidate(); + } + break; + } + } + invalidate(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + CropImage cropImage = (CropImage) mContext; + if (cropImage.mSaving) { + return false; + } + + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + if (cropImage.mWaitingToPick) { + recomputeFocus(event); + } else { + for (int i = 0; i < mHighlightViews.size(); i++) { + HighlightView hv = mHighlightViews.get(i); + int edge = hv.getHit(event.getX(), event.getY()); + if (edge != HighlightView.GROW_NONE) { + mMotionEdge = edge; + mMotionHighlightView = hv; + mLastX = event.getX(); + mLastY = event.getY(); + mMotionHighlightView.setMode( + (edge == HighlightView.MOVE) + ? HighlightView.ModifyMode.Move + : HighlightView.ModifyMode.Grow); + break; + } + } + } + break; + case MotionEvent.ACTION_UP: + if (cropImage.mWaitingToPick) { + for (int i = 0; i < mHighlightViews.size(); i++) { + HighlightView hv = mHighlightViews.get(i); + if (hv.hasFocus()) { + cropImage.mCrop = hv; + for (int j = 0; j < mHighlightViews.size(); j++) { + if (j == i) { + continue; + } + mHighlightViews.get(j).setHidden(true); + } + centerBasedOnHighlightView(hv); + ((CropImage) mContext).mWaitingToPick = false; + return true; + } + } + } else if (mMotionHighlightView != null) { + centerBasedOnHighlightView(mMotionHighlightView); + mMotionHighlightView.setMode( + HighlightView.ModifyMode.None); + } + mMotionHighlightView = null; + break; + case MotionEvent.ACTION_MOVE: + if (cropImage.mWaitingToPick) { + recomputeFocus(event); + } else if (mMotionHighlightView != null) { + mMotionHighlightView.handleMotion(mMotionEdge, + event.getX() - mLastX, + event.getY() - mLastY); + mLastX = event.getX(); + mLastY = event.getY(); + + if (true) { + // This section of code is optional. It has some user + // benefit in that moving the crop rectangle against + // the edge of the screen causes scrolling but it means + // that the crop rectangle is no longer fixed under + // the user's finger. + ensureVisible(mMotionHighlightView); + } + } + break; + } + + switch (event.getAction()) { + case MotionEvent.ACTION_UP: + center(true, true, true); + break; + case MotionEvent.ACTION_MOVE: + // if we're not zoomed then there's no point in even allowing + // the user to move the image around. This call to center puts + // it back to the normalized location (with false meaning don't + // animate). + if (getScale() == 1F) { + center(true, true, false); + } + break; + } + + return true; + } + + // Pan the displayed image to make sure the cropping rectangle is visible. + private void ensureVisible(HighlightView hv) { + Rect r = hv.mDrawRect; + + int panDeltaX1 = Math.max(0, mLeft - r.left); + int panDeltaX2 = Math.min(0, mRight - r.right); + + int panDeltaY1 = Math.max(0, mTop - r.top); + int panDeltaY2 = Math.min(0, mBottom - r.bottom); + + int panDeltaX = panDeltaX1 != 0 ? panDeltaX1 : panDeltaX2; + int panDeltaY = panDeltaY1 != 0 ? panDeltaY1 : panDeltaY2; + + if (panDeltaX != 0 || panDeltaY != 0) { + panBy(panDeltaX, panDeltaY); + } + } + + // If the cropping rectangle's size changed significantly, change the + // view's center and scale according to the cropping rectangle. + private void centerBasedOnHighlightView(HighlightView hv) { + Rect drawRect = hv.mDrawRect; + + float width = drawRect.width(); + float height = drawRect.height(); + + float thisWidth = getWidth(); + float thisHeight = getHeight(); + + float z1 = thisWidth / width * .6F; + float z2 = thisHeight / height * .6F; + + float zoom = Math.min(z1, z2); + zoom = zoom * this.getScale(); + zoom = Math.max(1F, zoom); + + if ((Math.abs(zoom - getScale()) / zoom) > .1) { + float [] coordinates = new float[] {hv.mCropRect.centerX(), + hv.mCropRect.centerY()}; + getImageMatrix().mapPoints(coordinates); + zoomTo(zoom, coordinates[0], coordinates[1], 300F); + } + + ensureVisible(hv); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + for (int i = 0; i < mHighlightViews.size(); i++) { + mHighlightViews.get(i).draw(canvas); + } + } + + public void add(HighlightView hv) { + mHighlightViews.add(hv); + invalidate(); + } +} diff --git a/src/com/android/camera/HighlightView.java b/src/com/android/camera/HighlightView.java index b5d368a..50bb042 100644 --- a/src/com/android/camera/HighlightView.java +++ b/src/com/android/camera/HighlightView.java @@ -29,15 +29,14 @@ import android.util.Log; import android.view.KeyEvent; import android.view.View; - +// This class is used by CropImage to display a highlighted cropping rectangle +// overlayed with the image. There are two coordinate spaces in use. One is +// image, another is screen. computeLayout() uses mMatrix to map from image +// space to screen space. public class HighlightView { - private static final String TAG = "CropImage"; - View mContext; - Path mPath; - Rect mViewDrawingRect = new Rect(); + private static final String TAG = "HighlightView"; + View mContext; // The View displaying the image. - int mMotionMode; - public static final int GROW_NONE = (1 << 0); public static final int GROW_LEFT_EDGE = (1 << 1); public static final int GROW_RIGHT_EDGE = (1 << 2); @@ -47,10 +46,9 @@ public class HighlightView { public HighlightView(View ctx) { mContext = ctx; - mPath = new Path(); } - private void initHighlightView() { + private void init() { android.content.res.Resources resources = mContext.getResources(); mResizeDrawableWidth = resources.getDrawable(R.drawable.camera_crop_width); @@ -80,33 +78,31 @@ public class HighlightView { return; } canvas.save(); - mPath.reset(); + Path path = new Path(); if (!hasFocus()) { mOutlinePaint.setColor(0xFF000000); canvas.drawRect(mDrawRect, mOutlinePaint); } else { - mContext.getDrawingRect(mViewDrawingRect); + Rect viewDrawingRect = new Rect(); + mContext.getDrawingRect(viewDrawingRect); if (mCircle) { - float width = mDrawRect.width() - - (getPaddingLeft() + getPaddingRight()); - float height = mDrawRect.height() - - (getPaddingTop() + getPaddingBottom()); - mPath.addCircle( - mDrawRect.left + getPaddingLeft() + (width / 2), - mDrawRect.top + getPaddingTop() + (height / 2), - width / 2, - Path.Direction.CW); + float width = mDrawRect.width(); + float height = mDrawRect.height(); + path.addCircle(mDrawRect.left + (width / 2), + mDrawRect.top + (height / 2), + width / 2, + Path.Direction.CW); mOutlinePaint.setColor(0xFFEF04D6); } else { - mPath.addRect(new RectF(mDrawRect), Path.Direction.CW); + path.addRect(new RectF(mDrawRect), Path.Direction.CW); mOutlinePaint.setColor(0xFFFF8A00); } - canvas.clipPath(mPath, Region.Op.DIFFERENCE); - canvas.drawRect(mViewDrawingRect, + canvas.clipPath(path, Region.Op.DIFFERENCE); + canvas.drawRect(viewDrawingRect, hasFocus() ? mFocusPaint : mNoFocusPaint); canvas.restore(); - canvas.drawPath(mPath, mOutlinePaint); + canvas.drawPath(path, mOutlinePaint); if (mMode == ModifyMode.Grow) { if (mCircle) { @@ -169,22 +165,8 @@ public class HighlightView { } } } - } - float getPaddingTop() { - return 0F; - } - float getPaddingBottom() { - return 0F; - } - float getPaddingLeft() { - return 0F; - } - float getPaddingRight() { - return 0F; - } - public ModifyMode getMode() { return mMode; } @@ -196,6 +178,7 @@ public class HighlightView { } } + // Determines which edges are hit by touching at (x, y). public int getHit(float x, float y) { Rect r = computeLayout(); final float hysteresis = 20F; @@ -206,7 +189,7 @@ public class HighlightView { float distY = y - r.centerY(); int distanceFromCenter = (int) Math.sqrt(distX * distX + distY * distY); - int radius = (int) (mDrawRect.width() - getPaddingLeft()) / 2; + int radius = (int) (mDrawRect.width()) / 2; int delta = distanceFromCenter - radius; if (Math.abs(delta) <= hysteresis) { if (Math.abs(distY) > Math.abs(distX)) { @@ -228,11 +211,14 @@ public class HighlightView { retval = GROW_NONE; } } else { + // verticalCheck makes sure the position is between the top and + // the bottom edge (with some tolerance). Similar for horizCheck. boolean verticalCheck = (y >= r.top - hysteresis) && (y < r.bottom + hysteresis); boolean horizCheck = (x >= r.left - hysteresis) && (x < r.right + hysteresis); + // Check whether the position is near some edge(s). if ((Math.abs(r.left - x) < hysteresis) && verticalCheck) { retval |= GROW_LEFT_EDGE; } @@ -246,6 +232,7 @@ public class HighlightView { retval |= GROW_BOTTOM_EDGE; } + // Not near any edge but inside the rectangle: move. if (retval == GROW_NONE && r.contains((int) x, (int) y)) { retval = MOVE; } @@ -253,11 +240,14 @@ public class HighlightView { return retval; } + // Handles motion (dx, dy) in screen space. + // The "edge" parameter specifies which edges the user is dragging. void handleMotion(int edge, float dx, float dy) { Rect r = computeLayout(); if (edge == GROW_NONE) { return; } else if (edge == MOVE) { + // Convert to image space before sending to moveBy(). moveBy(dx * (mCropRect.width() / r.width()), dy * (mCropRect.height() / r.height())); } else { @@ -268,19 +258,22 @@ public class HighlightView { if (((GROW_TOP_EDGE | GROW_BOTTOM_EDGE) & edge) == 0) { dy = 0; } - + + // Convert to image space before sending to growBy(). float xDelta = dx * (mCropRect.width() / r.width()); float yDelta = dy * (mCropRect.height() / r.height()); growBy((((edge & GROW_LEFT_EDGE) != 0) ? -1 : 1) * xDelta, (((edge & GROW_TOP_EDGE) != 0) ? -1 : 1) * yDelta); - } } + // Grows the cropping rectange by (dx, dy) in image space. void moveBy(float dx, float dy) { Rect invalRect = new Rect(mDrawRect); mCropRect.offset(dx, dy); + + // Put the cropping rectangle inside image rectangle. mCropRect.offset( Math.max(0, mImageRect.left - mCropRect.left), Math.max(0, mImageRect.top - mCropRect.top)); @@ -295,13 +288,7 @@ public class HighlightView { mContext.invalidate(invalRect); } - private void shift(RectF r, float dx, float dy) { - r.left += dx; - r.right += dx; - r.top += dy; - r.bottom += dy; - } - + // Grows the cropping rectange by (dx, dy) in image space. void growBy(float dx, float dy) { if (mMaintainAspectRatio) { if (dx != 0) { @@ -311,6 +298,9 @@ public class HighlightView { } } + // Don't let the cropping rectangle grow too fast. + // Grow at most half of the difference between the image rectangle and + // the cropping rectangle. RectF r = new RectF(mCropRect); if (dx > 0F && r.width() + 2 * dx > mImageRect.width()) { float adjustment = (mImageRect.width() - r.width()) / 2F; @@ -329,9 +319,10 @@ public class HighlightView { r.inset(-dx, -dy); - float widthCap = 25F; - if (r.width() < 25) { - r.inset(-(25F - r.width()) / 2F, 0F); + // Don't let the cropping rectangle shrink too fast. + final float widthCap = 25F; + if (r.width() < widthCap) { + r.inset(-(widthCap - r.width()) / 2F, 0F); } float heightCap = mMaintainAspectRatio ? (widthCap / mInitialAspectRatio) @@ -340,48 +331,30 @@ public class HighlightView { r.inset(0F, -(heightCap - r.height()) / 2F); } + // Put the cropping rectangle inside the image rectangle. if (r.left < mImageRect.left) { - shift(r, mImageRect.left - r.left, 0F); + r.offset(mImageRect.left - r.left, 0F); } else if (r.right > mImageRect.right) { - shift(r, -(r.right - mImageRect.right), 0); + r.offset(-(r.right - mImageRect.right), 0); } if (r.top < mImageRect.top) { - shift(r, 0F, mImageRect.top - r.top); + r.offset(0F, mImageRect.top - r.top); } else if (r.bottom > mImageRect.bottom) { - shift(r, 0F, -(r.bottom - mImageRect.bottom)); - } -/* - RectF rCandidate = new RectF(r); - r.intersect(mImageRect); - if (mMaintainAspectRatio) { - if (r.left != rCandidate.left) { - Log.v(TAG, "bail 1"); - return; - } - if (r.right != rCandidate.right) { - Log.v(TAG, "bail 2"); - return; - } - if (r.top != rCandidate.top) { - Log.v(TAG, "bail 3"); - return; - } - if (r.bottom != rCandidate.bottom) { - Log.v(TAG, "bail 4"); - return; - } + r.offset(0F, -(r.bottom - mImageRect.bottom)); } -*/ + mCropRect.set(r); mDrawRect = computeLayout(); mContext.invalidate(); } + // Returns the cropping rectangle in image space. public Rect getCropRect() { return new Rect((int) mCropRect.left, (int) mCropRect.top, (int) mCropRect.right, (int) mCropRect.bottom); } + // Maps the cropping rectangle from image space to screen space. private Rect computeLayout() { RectF r = new RectF(mCropRect.left, mCropRect.top, mCropRect.right, mCropRect.bottom); @@ -396,11 +369,6 @@ public class HighlightView { public void setup(Matrix m, Rect imageRect, RectF cropRect, boolean circle, boolean maintainAspectRatio) { - if (Config.LOGV) { - Log.v(TAG, "setup... " + imageRect + "; " + cropRect - + "; maintain " + maintainAspectRatio - + "; circle " + circle); - } if (circle) { maintainAspectRatio = true; } @@ -421,66 +389,27 @@ public class HighlightView { mOutlinePaint.setAntiAlias(true); mMode = ModifyMode.None; - initHighlightView(); - } - - public void modify(int keyCode, long repeatCount) { - float factor = Math.max(.01F, Math.min(.1F, repeatCount * .01F)); - float widthUnits = factor * (float) mContext.getWidth(); - float heightUnits = widthUnits; - - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_LEFT: - if (mMode == ModifyMode.Move) { - moveBy(-widthUnits, 0); - } else if (mMode == ModifyMode.Grow) { - growBy(-widthUnits, 0); - } - break; - - case KeyEvent.KEYCODE_DPAD_RIGHT: - if (mMode == ModifyMode.Move) { - moveBy(widthUnits, 0); - } else if (mMode == ModifyMode.Grow) { - growBy(widthUnits, 0); - } - break; - - case KeyEvent.KEYCODE_DPAD_UP: - if (mMode == ModifyMode.Move) { - moveBy(0, -heightUnits); - } else if (mMode == ModifyMode.Grow) { - growBy(0, -heightUnits); - } - break; - - case KeyEvent.KEYCODE_DPAD_DOWN: - if (mMode == ModifyMode.Move) { - moveBy(0, heightUnits); - } else if (mMode == ModifyMode.Grow) { - growBy(0, heightUnits); - } - break; - } + init(); } enum ModifyMode { None, Move, Grow }; - ModifyMode mMode = ModifyMode.None; + private ModifyMode mMode = ModifyMode.None; - Rect mDrawRect; - RectF mImageRect; - RectF mCropRect; + Rect mDrawRect; // in screen space + private RectF mImageRect; // in image space + RectF mCropRect; // in image space Matrix mMatrix; - boolean mMaintainAspectRatio = false; - float mInitialAspectRatio; - boolean mCircle = false; + private boolean mMaintainAspectRatio = false; + private float mInitialAspectRatio; + private boolean mCircle = false; - Drawable mResizeDrawableWidth, mResizeDrawableHeight, - mResizeDrawableDiagonal; + private Drawable mResizeDrawableWidth; + private Drawable mResizeDrawableHeight; + private Drawable mResizeDrawableDiagonal; - Paint mFocusPaint = new Paint(); - Paint mNoFocusPaint = new Paint(); - Paint mOutlinePaint = new Paint(); + private Paint mFocusPaint = new Paint(); + private Paint mNoFocusPaint = new Paint(); + private Paint mOutlinePaint = new Paint(); } |