From 0225fcd8513803711a3d5647e4927e2d8a9cfe34 Mon Sep 17 00:00:00 2001 From: Ruben Brunk Date: Mon, 15 Oct 2012 12:02:54 -0700 Subject: Added fixed aspect ratio support for cropping. Bug: 7350377 Change-Id: I8110ea999c764de675fe11f586ab9bc7af205f46 --- .../gallery3d/filtershow/FilterShowActivity.java | 47 ++-- .../gallery3d/filtershow/PanelController.java | 97 +++++++- .../filtershow/imageshow/GeometryMath.java | 54 +++++ .../gallery3d/filtershow/imageshow/ImageCrop.java | 245 ++++++++++++++++++--- .../filtershow/imageshow/ImageGeometry.java | 38 +++- .../filtershow/imageshow/ImageRotate.java | 28 +-- .../filtershow/imageshow/ImageStraighten.java | 20 +- .../gallery3d/filtershow/ui/ImageButtonTitle.java | 4 + 8 files changed, 420 insertions(+), 113 deletions(-) create mode 100644 src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java diff --git a/src/com/android/gallery3d/filtershow/FilterShowActivity.java b/src/com/android/gallery3d/filtershow/FilterShowActivity.java index f76179d8b..35c74ebe1 100644 --- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java +++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java @@ -222,7 +222,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, mPanelController.addPanel(mColorsButton, mListColors, 3); - int []recastIDs = { + int[] recastIDs = { R.id.vignetteButton, R.id.vibranceButton, R.id.contrastButton, @@ -232,7 +232,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, R.id.exposureButton, R.id.shadowRecoveryButton }; - ImageFilter []filters = { + ImageFilter[] filters = { new ImageFilterVignette(), new ImageFilterVibrance(), new ImageFilterContrast(), @@ -243,7 +243,6 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, new ImageFilterShadows() }; - for (int i = 0; i < filters.length; i++) { ImageSmallFilter fView = new ImageSmallFilter(this); @@ -258,36 +257,36 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, fView.setId(recastIDs[i]); mPanelController.addComponent(mColorsButton, fView); - listColors.addView(fView,pos); + listColors.addView(fView, pos); } - int []overlayIDs = { + int[] overlayIDs = { R.id.sharpenButton, R.id.curvesButtonRGB }; - int []overlayBitmaps = { + int[] overlayBitmaps = { R.drawable.filtershow_button_colors_sharpen, R.drawable.filtershow_button_colors_curve }; - int []overlayNames = { + int[] overlayNames = { R.string.sharpen, R.string.curvesRGB }; - for (int i = 0; i < overlayIDs.length; i++) { + for (int i = 0; i < overlayIDs.length; i++) { ImageWithIcon fView = new ImageWithIcon(this); View v = listColors.findViewById(overlayIDs[i]); int pos = listColors.indexOfChild(v); listColors.removeView(v); - final int sid =overlayNames[i]; - ImageFilterExposure efilter = new ImageFilterExposure(){ + final int sid = overlayNames[i]; + ImageFilterExposure efilter = new ImageFilterExposure() { { mName = getString(sid); } }; efilter.setParameter(-300); Bitmap bitmap = BitmapFactory.decodeResource(getResources(), - overlayBitmaps[i] ); + overlayBitmaps[i]); fView.setIcon(bitmap); fView.setImageFilter(efilter); @@ -296,7 +295,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, fView.setId(overlayIDs[i]); mPanelController.addComponent(mColorsButton, fView); - listColors.addView(fView,pos); + listColors.addView(fView, pos); } mPanelController.addComponent(mColorsButton, findViewById(R.id.curvesButtonRGB)); @@ -311,6 +310,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, mPanelController.addView(findViewById(R.id.applyEffect)); mPanelController.addView(findViewById(R.id.pickCurvesChannel)); + mPanelController.addView(findViewById(R.id.aspect)); findViewById(R.id.resetOperationsButton).setOnClickListener( createOnClickResetOperationsButton()); @@ -330,27 +330,27 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, mImageZoom.setSeekBar(seekBar); mPanelController.setRowPanel(findViewById(R.id.secondRowPanel)); mPanelController.setUtilityPanel(this, findViewById(R.id.filterButtonsList), - findViewById(R.id.applyEffect)); + findViewById(R.id.applyEffect), findViewById(R.id.aspect)); mPanelController.setMasterImage(mImageShow); mPanelController.setCurrentPanel(mFxButton); Intent intent = getIntent(); String data = intent.getDataString(); if (data != null) { Uri uri = Uri.parse(data); - mImageLoader.loadBitmap(uri,getScreenImageSize()); + mImageLoader.loadBitmap(uri, getScreenImageSize()); } else { pickImage(); } } - private int getScreenImageSize(){ - DisplayMetrics metrics = new DisplayMetrics(); + private int getScreenImageSize() { + DisplayMetrics metrics = new DisplayMetrics(); Display display = getWindowManager().getDefaultDisplay(); - Point size = new Point(); + Point size = new Point(); display.getSize(size); display.getMetrics(metrics); int msize = Math.min(size.x, size.y); - return (133*msize)/metrics.densityDpi; + return (133 * msize) / metrics.densityDpi; } private void showSavingProgress() { @@ -540,7 +540,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, filter.setController(this); filter.setImageLoader(mImageLoader); listFilters.addView(filter); - ImageSmallFilter previousFilter = filter; + ImageSmallFilter previousFilter = filter; BitmapFactory.Options o = new BitmapFactory.Options(); o.inScaled = false; @@ -577,9 +577,11 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, Drawable npd2 = getResources().getDrawable(R.drawable.filtershow_border_brush); borders[p++] = new ImageFilterBorder(npd2); borders[p++] = new ImageFilterParametricBorder(Color.BLACK, mImageBorderSize, 0); - borders[p++] = new ImageFilterParametricBorder(Color.BLACK, mImageBorderSize, mImageBorderSize); + borders[p++] = new ImageFilterParametricBorder(Color.BLACK, mImageBorderSize, + mImageBorderSize); borders[p++] = new ImageFilterParametricBorder(Color.WHITE, mImageBorderSize, 0); - borders[p++] = new ImageFilterParametricBorder(Color.WHITE, mImageBorderSize, mImageBorderSize); + borders[p++] = new ImageFilterParametricBorder(Color.WHITE, mImageBorderSize, + mImageBorderSize); ImageSmallFilter previousFilter = null; for (int i = 0; i < p; i++) { @@ -637,7 +639,6 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, } } - // ////////////////////////////////////////////////////////////////////////////// // imageState panel... @@ -809,7 +810,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, if (resultCode == RESULT_OK) { if (requestCode == SELECT_PICTURE) { Uri selectedImageUri = data.getData(); - mImageLoader.loadBitmap(selectedImageUri,getScreenImageSize()); + mImageLoader.loadBitmap(selectedImageUri, getScreenImageSize()); } } } diff --git a/src/com/android/gallery3d/filtershow/PanelController.java b/src/com/android/gallery3d/filtershow/PanelController.java index a21bb4fe1..6c69cf567 100644 --- a/src/com/android/gallery3d/filtershow/PanelController.java +++ b/src/com/android/gallery3d/filtershow/PanelController.java @@ -20,9 +20,11 @@ import com.android.gallery3d.filtershow.filters.ImageFilterSharpen; import com.android.gallery3d.filtershow.filters.ImageFilterVibrance; import com.android.gallery3d.filtershow.filters.ImageFilterVignette; import com.android.gallery3d.filtershow.filters.ImageFilterWBalance; +import com.android.gallery3d.filtershow.imageshow.ImageCrop; import com.android.gallery3d.filtershow.imageshow.ImageShow; import com.android.gallery3d.filtershow.presets.ImagePreset; import com.android.gallery3d.filtershow.ui.ImageCurves; +import com.android.gallery3d.filtershow.ui.ImageButtonTitle; import java.util.HashMap; import java.util.Vector; @@ -113,17 +115,96 @@ public class PanelController implements OnClickListener { private String mEffectName = null; private int mParameterValue = 0; private boolean mShowParameterValue = false; - - public UtilityPanel(Context context, View view, View textView) { + private View mAspectButton = null; + private int mCurrentAspectButton = 0; + private static final int NUMBER_OF_ASPECT_BUTTONS = 6; + private static final int ASPECT_NONE = 0; + private static final int ASPECT_1TO1 = 1; + private static final int ASPECT_5TO7 = 2; + private static final int ASPECT_4TO6 = 3; + private static final int ASPECT_16TO9 = 4; + private static final int ASPECT_ORIG = 5; + + public UtilityPanel(Context context, View view, View textView, View button) { mContext = context; mView = view; mTextView = (TextView) textView; + mAspectButton = button; } public boolean selected() { return mSelected; } + public void nextAspectButton() { + if (mAspectButton instanceof ImageButtonTitle + && mCurrentImage instanceof ImageCrop) { + switch (mCurrentAspectButton) { + case ASPECT_NONE: + ((ImageButtonTitle) mAspectButton).setText(mContext + .getString(R.string.aspect) + + " " + + mContext.getString(R.string.aspect1to1_effect)); + ((ImageCrop) mCurrentImage).apply(1, 1); + break; + case ASPECT_1TO1: + ((ImageButtonTitle) mAspectButton).setText(mContext + .getString(R.string.aspect) + + " " + + mContext.getString(R.string.aspect5to7_effect)); + ((ImageCrop) mCurrentImage).apply(7, 5); + break; + case ASPECT_5TO7: + ((ImageButtonTitle) mAspectButton).setText(mContext + .getString(R.string.aspect) + + " " + + mContext.getString(R.string.aspect4to6_effect)); + ((ImageCrop) mCurrentImage).apply(6, 4); + break; + case ASPECT_4TO6: + ((ImageButtonTitle) mAspectButton).setText(mContext + .getString(R.string.aspect) + + " " + + mContext.getString(R.string.aspect9to16_effect)); + ((ImageCrop) mCurrentImage).apply(16, 9); + break; + case ASPECT_16TO9: + ((ImageButtonTitle) mAspectButton).setText(mContext + .getString(R.string.aspect) + + " " + + mContext.getString(R.string.aspectOriginal_effect)); + ((ImageCrop) mCurrentImage).applyOriginal(); + break; + case ASPECT_ORIG: + ((ImageButtonTitle) mAspectButton).setText(mContext + .getString(R.string.aspect) + + " " + + mContext.getString(R.string.aspectNone_effect)); + ((ImageCrop) mCurrentImage).applyClear(); + break; + default: + ((ImageButtonTitle) mAspectButton).setText(mContext + .getString(R.string.aspect) + + " " + + mContext.getString(R.string.aspect1to1_effect)); + ((ImageCrop) mCurrentImage).applyClear(); + break; + } + mCurrentAspectButton = (mCurrentAspectButton + 1) % NUMBER_OF_ASPECT_BUTTONS; + } + } + + public void showAspectButtons() { + if (mAspectButton != null) + mAspectButton.setVisibility(View.VISIBLE); + mCurrentAspectButton = ASPECT_NONE; + } + + public void hideAspectButtons() { + if (mAspectButton != null) + mAspectButton.setVisibility(View.GONE); + } + public void onNewValue(int value) { mParameterValue = value; updateText(); @@ -259,8 +340,9 @@ public class PanelController implements OnClickListener { mRowPanel = rowPanel; } - public void setUtilityPanel(Context context, View utilityPanel, View textView) { - mUtilityPanel = new UtilityPanel(context, utilityPanel, textView); + public void setUtilityPanel(Context context, View utilityPanel, View textView, + View button) { + mUtilityPanel = new UtilityPanel(context, utilityPanel, textView, button); } public void setMasterImage(ImageShow imageShow) { @@ -402,7 +484,7 @@ public class PanelController implements OnClickListener { if (mCurrentImage != null) { mCurrentImage.unselect(); } - + mUtilityPanel.hideAspectButtons(); switch (view.getId()) { case R.id.straightenButton: { mCurrentImage = showImageView(R.id.imageStraighten); @@ -415,6 +497,7 @@ public class PanelController implements OnClickListener { String ename = mCurrentImage.getContext().getString(R.string.crop); mUtilityPanel.setEffectName(ename); mUtilityPanel.setShowParameter(false); + mUtilityPanel.showAspectButtons(); break; } case R.id.rotateButton: { @@ -510,6 +593,10 @@ public class PanelController implements OnClickListener { ensureFilter("Redeye"); break; } + case R.id.aspect: { + mUtilityPanel.nextAspectButton(); + break; + } case R.id.applyEffect: { showPanel(mCurrentPanel); break; diff --git a/src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java b/src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java new file mode 100644 index 000000000..95d174f42 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java @@ -0,0 +1,54 @@ +package com.android.gallery3d.filtershow.imageshow; + +public class GeometryMath { + protected static float clamp(float i, float low, float high) { + return Math.max(Math.min(i, high), low); + } + + protected static float[] shortestVectorFromPointToLine(float[] point, float[] l1, float[] l2) { + float x1 = l1[0]; + float x2 = l2[0]; + float y1 = l1[1]; + float y2 = l2[1]; + float xdelt = x2 - x1; + float ydelt = y2 - y1; + if (xdelt == 0 && ydelt == 0) + return null; + float u = ((point[0] - x1) * xdelt + (point[1] - y1) * ydelt) + / (xdelt * xdelt + ydelt * ydelt); + float[] ret = { + (x1 + u * (x2 - x1)), (y1 + u * (y2 - y1)) + }; + return ret; + } + + //A . B + protected static float dotProduct(float[] a, float[] b){ + return a[0] * b[0] + a[1] * b[1]; + } + + protected static float[] normalize(float[] a){ + float length = (float) Math.sqrt(a[0] * a[0] + a[1] * a[1]); + float[] b = { a[0] / length, a[1] / length }; + return b; + } + + //A onto B + protected static float scalarProjection(float[] a, float[] b){ + float length = (float) Math.sqrt(b[0] * b[0] + b[1] * b[1]); + return dotProduct(a, b) / length; + } + + protected static float[] getVectorFromPoints(float [] point1, float [] point2){ + float [] p = { point2[0] - point1[0], point2[1] - point1[1] }; + return p; + } + + protected static float[] getUnitVectorFromPoints(float [] point1, float [] point2){ + float [] p = { point2[0] - point1[0], point2[1] - point1[1] }; + float length = (float) Math.sqrt(p[0] * p[0] + p[1] * p[1]); + p[0] = p[0] / length; + p[1] = p[1] / length; + return p; + } +} diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java b/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java index 4d171bf4c..e34e3a249 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java @@ -36,6 +36,12 @@ public class ImageCrop extends ImageGeometry { private static final int MOVE_BOTTOM = 8; private static final int MOVE_BLOCK = 16; + //Corners + private static final int TOP_LEFT = MOVE_TOP | MOVE_LEFT; + private static final int TOP_RIGHT = MOVE_TOP | MOVE_RIGHT; + private static final int BOTTOM_RIGHT = MOVE_BOTTOM | MOVE_RIGHT; + private static final int BOTTOM_LEFT = MOVE_BOTTOM | MOVE_LEFT; + private static final float MIN_CROP_WIDTH_HEIGHT = 0.1f; private static final int TOUCH_TOLERANCE = 30; @@ -78,6 +84,104 @@ public class ImageCrop extends ImageGeometry { borderPaint.setStrokeWidth(2f); } + private boolean switchCropBounds(int moving_corner, RectF dst) { + RectF crop = getCropBoundsDisplayed(); + float dx1 = 0; + float dy1 = 0; + float dx2 = 0; + float dy2 = 0; + if ((moving_corner & MOVE_RIGHT) != 0) { + dx1 = mCurrentX - crop.right; + } else if ((moving_corner & MOVE_LEFT) != 0) { + dx1 = mCurrentX - crop.left; + } + if ((moving_corner & MOVE_BOTTOM) != 0) { + dy1 = mCurrentY - crop.bottom; + } else if ((moving_corner & MOVE_TOP) != 0) { + dy1 = mCurrentY - crop.top; + } + RectF newCrop = null; + //Fix opposite corner in place and move sides + if (moving_corner == BOTTOM_RIGHT) { + newCrop = new RectF(crop.left, crop.top, crop.left + crop.height(), crop.top + + crop.width()); + } else if (moving_corner == BOTTOM_LEFT) { + newCrop = new RectF(crop.right - crop.height(), crop.top, crop.right, crop.top + + crop.width()); + } else if (moving_corner == TOP_LEFT) { + newCrop = new RectF(crop.right - crop.height(), crop.bottom - crop.width(), + crop.right, crop.bottom); + } else if (moving_corner == TOP_RIGHT) { + newCrop = new RectF(crop.left, crop.bottom - crop.width(), crop.left + + crop.height(), crop.bottom); + } + if ((moving_corner & MOVE_RIGHT) != 0) { + dx2 = mCurrentX - newCrop.right; + } else if ((moving_corner & MOVE_LEFT) != 0) { + dx2 = mCurrentX - newCrop.left; + } + if ((moving_corner & MOVE_BOTTOM) != 0) { + dy2 = mCurrentY - newCrop.bottom; + } else if ((moving_corner & MOVE_TOP) != 0) { + dy2 = mCurrentY - newCrop.top; + } + if (Math.sqrt(dx1*dx1 + dy1*dy1) > Math.sqrt(dx2*dx2 + dy2*dy2)){ + Matrix m = getCropBoundDisplayMatrix(); + Matrix m0 = new Matrix(); + if (!m.invert(m0)){ + if (LOGV) + Log.v(LOGTAG, "FAILED TO INVERT CROP MATRIX"); + return false; + } + if (!m0.mapRect(newCrop)){ + if (LOGV) + Log.v(LOGTAG, "FAILED TO MAP RECTANGLE TO RECTANGLE"); + return false; + } + float temp = mAspectWidth; + mAspectWidth = mAspectHeight; + mAspectHeight = temp; + dst.set(newCrop); + return true; + } + return false; + } + + public void apply(float w, float h){ + mFixAspectRatio = true; + mAspectWidth = w; + mAspectHeight = h; + setLocalCropBounds(getUntranslatedStraightenCropBounds(getLocalPhotoBounds(), + getLocalStraighten())); + cropSetup(); + saveAndSetPreset(); + invalidate(); + } + + public void applyOriginal() { + mFixAspectRatio = true; + RectF photobounds = getLocalPhotoBounds(); + float w = photobounds.width(); + float h = photobounds.height(); + float scale = Math.min(w, h); + mAspectWidth = w / scale; + mAspectHeight = h / scale; + setLocalCropBounds(getUntranslatedStraightenCropBounds(photobounds, + getLocalStraighten())); + cropSetup(); + saveAndSetPreset(); + invalidate(); + } + + public void applyClear() { + mFixAspectRatio = false; + setLocalCropBounds(getUntranslatedStraightenCropBounds(getLocalPhotoBounds(), + getLocalStraighten())); + cropSetup(); + saveAndSetPreset(); + invalidate(); + } + private float getScaledMinWidthHeight() { RectF disp = new RectF(0, 0, getWidth(), getHeight()); float scaled = Math.min(disp.width(), disp.height()) * MIN_CROP_WIDTH_HEIGHT @@ -94,6 +198,19 @@ public class ImageCrop extends ImageGeometry { return m; } + protected Matrix getCropBoundDisplayMatrix(){ + Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds()); + if (m == null) { + if (LOGV) + Log.v(LOGTAG, "FAILED TO MAP CROP BOUNDS TO RECTANGLE"); + m = new Matrix(); + } + float zoom = computeScale(getWidth(), getHeight()); + m.postTranslate(mXOffset, mYOffset); + m.postScale(zoom, zoom, mCenterX, mCenterY); + return m; + } + protected RectF getCropBoundsDisplayed() { RectF bounds = getLocalCropBounds(); RectF crop = new RectF(bounds); @@ -249,9 +366,47 @@ public class ImageCrop extends ImageGeometry { if (cropped.contains(x, y) && (movingEdges == 0)) { movingEdges = MOVE_BLOCK; } + if (mFixAspectRatio && (movingEdges != MOVE_BLOCK)) { + movingEdges = fixEdgeToCorner(movingEdges); + } invalidate(); } + private int fixEdgeToCorner(int moving_edges){ + if (moving_edges == MOVE_LEFT) { + moving_edges |= MOVE_TOP; + } + if (moving_edges == MOVE_TOP) { + moving_edges |= MOVE_LEFT; + } + if (moving_edges == MOVE_RIGHT) { + moving_edges |= MOVE_BOTTOM; + } + if (moving_edges == MOVE_BOTTOM) { + moving_edges |= MOVE_RIGHT; + } + return moving_edges; + } + + private RectF fixedCornerResize(RectF r, int moving_corner, float dx, float dy){ + RectF newCrop = null; + //Fix opposite corner in place and move sides + if (moving_corner == BOTTOM_RIGHT) { + newCrop = new RectF(r.left, r.top, r.left + r.width() + dx, r.top + r.height() + + dy); + } else if (moving_corner == BOTTOM_LEFT) { + newCrop = new RectF(r.right - r.width() + dx, r.top, r.right, r.top + r.height() + + dy); + } else if (moving_corner == TOP_LEFT) { + newCrop = new RectF(r.right - r.width() + dx, r.bottom - r.height() + dy, + r.right, r.bottom); + } else if (moving_corner == TOP_RIGHT) { + newCrop = new RectF(r.left, r.bottom - r.height() + dy, r.left + + r.width() + dx, r.bottom); + } + return newCrop; + } + private void moveEdges(float dX, float dY) { RectF cropped = getRotatedCropBounds(); float minWidthHeight = getScaledMinWidthHeight(); @@ -260,25 +415,22 @@ public class ImageCrop extends ImageGeometry { float deltaY = dY / scale; int select = movingEdges; if (mFixAspectRatio && (select != MOVE_BLOCK)) { - if ((select & MOVE_LEFT) != 0) { - select &= ~MOVE_BOTTOM; + if (select == MOVE_LEFT) { select |= MOVE_TOP; - deltaY = getNewHeightForWidthAspect(deltaX, mAspectWidth, mAspectHeight); } - if ((select & MOVE_TOP) != 0) { - select &= ~MOVE_RIGHT; + if (select == MOVE_TOP) { select |= MOVE_LEFT; - deltaX = getNewWidthForHeightAspect(deltaY, mAspectWidth, mAspectHeight); } - if ((select & MOVE_RIGHT) != 0) { - select &= ~MOVE_TOP; + if (select == MOVE_RIGHT) { select |= MOVE_BOTTOM; - deltaY = getNewHeightForWidthAspect(deltaX, mAspectWidth, mAspectHeight); } - if ((select & MOVE_BOTTOM) != 0) { - select &= ~MOVE_LEFT; + if (select == MOVE_BOTTOM) { select |= MOVE_RIGHT; - deltaX = getNewWidthForHeightAspect(deltaY, mAspectWidth, mAspectHeight); + } + RectF blank = new RectF(); + if(switchCropBounds(select, blank)){ + setCropBounds(blank); + return; } } @@ -293,6 +445,7 @@ public class ImageCrop extends ImageGeometry { } else { float dx = 0; float dy = 0; + if ((select & MOVE_LEFT) != 0) { dx = Math.min(cropped.left + deltaX, cropped.right - minWidthHeight) - cropped.left; } @@ -309,26 +462,50 @@ public class ImageCrop extends ImageGeometry { } if (mFixAspectRatio) { - if (dx < dy) { - dy = getNewHeightForWidthAspect(dx, mAspectWidth, mAspectHeight); - } else { - dx = getNewWidthForHeightAspect(dy, mAspectWidth, mAspectHeight); + RectF crop = getCropBoundsDisplayed(); + float [] l1 = {crop.left, crop.bottom}; + float [] l2 = {crop.right, crop.top}; + if(movingEdges == TOP_LEFT || movingEdges == BOTTOM_RIGHT){ + l1[1] = crop.top; + l2[1] = crop.bottom; + } + float[] b = { l1[0] - l2[0], l1[1] - l2[1] }; + float[] disp = {dx, dy}; + float[] bUnit = GeometryMath.normalize(b); + float sp = GeometryMath.scalarProjection(disp, bUnit); + dx = sp * bUnit[0]; + dy = sp * bUnit[1]; + RectF newCrop = fixedCornerResize(crop, select, dx * scale, dy * scale); + Matrix m = getCropBoundDisplayMatrix(); + Matrix m0 = new Matrix(); + if (!m.invert(m0)){ + if (LOGV) + Log.v(LOGTAG, "FAILED TO INVERT CROP MATRIX"); + return; + } + if (!m0.mapRect(newCrop)){ + if (LOGV) + Log.v(LOGTAG, "FAILED TO MAP RECTANGLE TO RECTANGLE"); + return; + } + setCropBounds(newCrop); + return; + } else { + if ((select & MOVE_LEFT) != 0) { + cropped.left += dx; + } + if ((select & MOVE_TOP) != 0) { + cropped.top += dy; + } + if ((select & MOVE_RIGHT) != 0) { + cropped.right += dx; + } + if ((select & MOVE_BOTTOM) != 0) { + cropped.bottom += dy; } - } - - if ((select & MOVE_LEFT) != 0) { - cropped.left += dx; - } - if ((select & MOVE_TOP) != 0) { - cropped.top += dy; - } - if ((select & MOVE_RIGHT) != 0) { - cropped.right += dx; - } - if ((select & MOVE_BOTTOM) != 0) { - cropped.bottom += dy; } } + movingEdges = select; Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds()); Matrix m0 = new Matrix(); if (!m.invert(m0)) { @@ -363,9 +540,9 @@ public class ImageCrop extends ImageGeometry { @Override protected void setActionMove(float x, float y) { - if (movingEdges != 0) + if (movingEdges != 0){ moveEdges(x - mCurrentX, y - mCurrentY); - + } super.setActionMove(x, y); } @@ -435,7 +612,7 @@ public class ImageCrop extends ImageGeometry { canvas.restore(); } - private int bitCycleLeft(int x, int times, int d){ + private int bitCycleLeft(int x, int times, int d) { int mask = (1 << d) - 1; int mout = x & mask; times %= d; @@ -449,7 +626,7 @@ public class ImageCrop extends ImageGeometry { protected int decoder(int movingEdges, float rotation) { int rot = constrainedRotation(rotation); - switch(rot){ + switch (rot) { case 90: return bitCycleLeft(movingEdges, 3, 4); case 180: @@ -460,4 +637,4 @@ public class ImageCrop extends ImageGeometry { return movingEdges; } } -} \ No newline at end of file +} diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java b/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java index 68df702ea..d5a7ada69 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java @@ -31,6 +31,7 @@ import android.view.View; import com.android.gallery3d.filtershow.imageshow.GeometryMetadata.FLIP; import com.android.gallery3d.filtershow.presets.ImagePreset; +import com.android.gallery3d.filtershow.imageshow.GeometryMath; public abstract class ImageGeometry extends ImageSlave { private boolean mVisibilityGained = false; @@ -74,6 +75,35 @@ public abstract class ImageGeometry extends ImageSlave { calculateLocalScalingFactorAndOffset(); } + protected static float angleFor(float dx, float dy) { + return (float) (Math.atan2(dx, dy) * 180 / Math.PI); + } + + protected static int snappedAngle(float angle) { + float remainder = angle % 90; + int current = (int) (angle / 90); // truncates + if (remainder < -45) { + --current; + } else if (remainder > 45) { + ++current; + } + return current * 90; + } + + protected float getCurrentTouchAngle(){ + if (mCurrentX == mTouchCenterX && mCurrentY == mTouchCenterY) { + return 0; + } + float dX1 = mTouchCenterX - mCenterX; + float dY1 = mTouchCenterY - mCenterY; + float dX2 = mCurrentX - mCenterX; + float dY2 = mCurrentY - mCenterY; + + float angleA = angleFor(dX1, dY1); + float angleB = angleFor(dX2, dY2); + return (angleB - angleA) % 360; + } + protected float computeScale(float width, float height) { float imageWidth = mLocalGeometry.getPhotoBounds().width(); float imageHeight = mLocalGeometry.getPhotoBounds().height(); @@ -219,8 +249,8 @@ public abstract class ImageGeometry extends ImageSlave { if (array.length < 2) return; for (int x = 0; x < array.length; x += 2) { - array[x] = clamp(array[x], imageBound.left, imageBound.right); - array[x + 1] = clamp(array[x + 1], imageBound.top, imageBound.bottom); + array[x] = GeometryMath.clamp(array[x], imageBound.left, imageBound.right); + array[x + 1] = GeometryMath.clamp(array[x + 1], imageBound.top, imageBound.bottom); } } @@ -346,10 +376,6 @@ public abstract class ImageGeometry extends ImageSlave { setImagePreset(copy); } - protected static float clamp(float i, float low, float high) { - return Math.max(Math.min(i, high), low); - } - public static RectF getUntranslatedStraightenCropBounds(RectF imageRect, float straightenAngle) { float deg = straightenAngle; if (deg < 0) { diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageRotate.java b/src/com/android/gallery3d/filtershow/imageshow/ImageRotate.java index 089e11732..775aa716b 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageRotate.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageRotate.java @@ -23,7 +23,6 @@ import android.graphics.Paint; import android.util.AttributeSet; public class ImageRotate extends ImageGeometry { - private static final float MATH_PI = (float) Math.PI; private float mBaseAngle = 0; private float mAngle = 0; @@ -39,35 +38,10 @@ public class ImageRotate extends ImageGeometry { super(context); } - private float angleFor(float dx, float dy) { - return (float) (Math.atan2(dx, dy) * 180 / MATH_PI); - } - - private int snappedAngle(float angle) { - float remainder = angle % 90; - int current = (int) (angle / 90); // truncates - if (remainder < -45) { - --current; - } else if (remainder > 45) { - ++current; - } - return current * 90; - } - private static final Paint gPaint = new Paint(); private void computeValue() { - if (mCurrentX == mTouchCenterX && mCurrentY == mTouchCenterY) { - return; - } - float dX1 = mTouchCenterX - mCenterX; - float dY1 = mTouchCenterY - mCenterY; - float dX2 = mCurrentX - mCenterX; - float dY2 = mCurrentY - mCenterY; - - float angleA = angleFor(dX1, dY1); - float angleB = angleFor(dX2, dY2); - float angle = (angleB - angleA) % 360; + float angle = getCurrentTouchAngle(); mAngle = (mBaseAngle - angle) % 360; } diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java index 2fd6b9b35..26c9671bb 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java @@ -24,8 +24,6 @@ import android.graphics.Path; import android.graphics.RectF; import android.util.AttributeSet; -import com.android.gallery3d.filtershow.imageshow.ImageGeometry.MODES; - public class ImageStraighten extends ImageGeometry { private float mBaseAngle = 0; @@ -61,22 +59,8 @@ public class ImageStraighten extends ImageGeometry { setCropToStraighten(); } - private float angleFor(float dx, float dy) { - return (float) (Math.atan2(dx, dy) * 180 / Math.PI); - } - private void computeValue() { - if (mCurrentX == mTouchCenterX && mCurrentY == mTouchCenterY) { - return; - } - float dX1 = mTouchCenterX - mCenterX; - float dY1 = mTouchCenterY - mCenterY; - float dX2 = mCurrentX - mCenterX; - float dY2 = mCurrentY - mCenterY; - - float angleA = angleFor(dX1, dY1); - float angleB = angleFor(dX2, dY2); - float angle = (angleB - angleA) % 360; + float angle = getCurrentTouchAngle(); mAngle = (mBaseAngle - angle) % 360; mAngle = Math.max(MIN_STRAIGHTEN_ANGLE, mAngle); mAngle = Math.min(MAX_STRAIGHTEN_ANGLE, mAngle); @@ -100,7 +84,7 @@ public class ImageStraighten extends ImageGeometry { @Override public void onNewValue(int value) { - setLocalStraighten(clamp(value, MIN_STRAIGHTEN_ANGLE, MAX_STRAIGHTEN_ANGLE)); + setLocalStraighten(GeometryMath.clamp(value, MIN_STRAIGHTEN_ANGLE, MAX_STRAIGHTEN_ANGLE)); if (getPanelController() != null) { getPanelController().onNewValue((int) getLocalStraighten()); } diff --git a/src/com/android/gallery3d/filtershow/ui/ImageButtonTitle.java b/src/com/android/gallery3d/filtershow/ui/ImageButtonTitle.java index 51ed7fb20..7e12b98f9 100644 --- a/src/com/android/gallery3d/filtershow/ui/ImageButtonTitle.java +++ b/src/com/android/gallery3d/filtershow/ui/ImageButtonTitle.java @@ -25,6 +25,10 @@ public class ImageButtonTitle extends ImageButton { mTextPadding = value; } + public void setText(String text) { + mText = text; + } + public ImageButtonTitle(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = getContext().obtainStyledAttributes( -- cgit v1.2.3