diff options
author | Ruben Brunk <rubenbrunk@google.com> | 2012-10-12 05:52:06 -0700 |
---|---|---|
committer | Ruben Brunk <rubenbrunk@google.com> | 2012-10-12 13:02:45 -0700 |
commit | 99abd47222e829493b0757fe332bb83926c5d85d (patch) | |
tree | 2a629cef3abe01e322385377170bc24f3479ac1b | |
parent | 98de2e2ff9ab7959ee7d6035a9f87ed39ebc7537 (diff) | |
download | android_packages_apps_Snap-99abd47222e829493b0757fe332bb83926c5d85d.tar.gz android_packages_apps_Snap-99abd47222e829493b0757fe332bb83926c5d85d.tar.bz2 android_packages_apps_Snap-99abd47222e829493b0757fe332bb83926c5d85d.zip |
Adding crop UI. Fixes a number of other UI bugs.
Bug:7337191
Change-Id: If63dbdac6722ad4fc6c30c165d6c2eeb7011c240
7 files changed, 417 insertions, 307 deletions
diff --git a/src/com/android/gallery3d/filtershow/FilterShowActivity.java b/src/com/android/gallery3d/filtershow/FilterShowActivity.java index e8770548d..d3080593f 100644 --- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java +++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java @@ -77,7 +77,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, private ImageBorder mImageBorders = null; private ImageStraighten mImageStraighten = null; private ImageZoom mImageZoom = null; - private final ImageCrop mImageCrop = null; + private ImageCrop mImageCrop = null; private ImageRotate mImageRotate = null; private ImageFlip mImageFlip = null; @@ -140,8 +140,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, mImageBorders = (ImageBorder) findViewById(R.id.imageBorder); mImageStraighten = (ImageStraighten) findViewById(R.id.imageStraighten); mImageZoom = (ImageZoom) findViewById(R.id.imageZoom); - // TODO: implement crop - // mImageCrop = (ImageCrop) findViewById(R.id.imageCrop); + mImageCrop = (ImageCrop) findViewById(R.id.imageCrop); mImageRotate = (ImageRotate) findViewById(R.id.imageRotate); mImageFlip = (ImageFlip) findViewById(R.id.imageFlip); @@ -150,8 +149,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, mImageViews.add(mImageBorders); mImageViews.add(mImageStraighten); mImageViews.add(mImageZoom); - // TODO: implement crop - // mImageViews.add(mImageCrop); + mImageViews.add(mImageCrop); mImageViews.add(mImageRotate); mImageViews.add(mImageFlip); @@ -180,9 +178,8 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, mImageStraighten.setMaster(mImageShow); mImageZoom.setImageLoader(mImageLoader); mImageZoom.setMaster(mImageShow); - // TODO: implement crop - // mImageCrop.setImageLoader(mImageLoader); - // mImageCrop.setMaster(mImageShow); + mImageCrop.setImageLoader(mImageLoader); + mImageCrop.setMaster(mImageShow); mImageRotate.setImageLoader(mImageLoader); mImageRotate.setMaster(mImageShow); mImageFlip.setImageLoader(mImageLoader); @@ -192,8 +189,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, mPanelController.addImageView(findViewById(R.id.imageCurves)); mPanelController.addImageView(findViewById(R.id.imageBorder)); mPanelController.addImageView(findViewById(R.id.imageStraighten)); - // TODO: implement crop - // mPanelController.addImageView(findViewById(R.id.imageCrop)); + mPanelController.addImageView(findViewById(R.id.imageCrop)); mPanelController.addImageView(findViewById(R.id.imageRotate)); mPanelController.addImageView(findViewById(R.id.imageFlip)); mPanelController.addImageView(findViewById(R.id.imageZoom)); @@ -203,8 +199,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, mPanelController.addPanel(mGeometryButton, mListGeometry, 2); mPanelController.addComponent(mGeometryButton, findViewById(R.id.straightenButton)); - // TODO: implement crop -// mPanelController.addComponent(mGeometryButton, findViewById(R.id.cropButton)); + mPanelController.addComponent(mGeometryButton, findViewById(R.id.cropButton)); mPanelController.addComponent(mGeometryButton, findViewById(R.id.rotateButton)); mPanelController.addComponent(mGeometryButton, findViewById(R.id.flipButton)); diff --git a/src/com/android/gallery3d/filtershow/PanelController.java b/src/com/android/gallery3d/filtershow/PanelController.java index a937452b3..963d17a6f 100644 --- a/src/com/android/gallery3d/filtershow/PanelController.java +++ b/src/com/android/gallery3d/filtershow/PanelController.java @@ -400,16 +400,13 @@ public class PanelController implements OnClickListener { mUtilityPanel.setEffectName(ename); break; } - /* - // TODO: implement crop case R.id.cropButton: { mCurrentImage = showImageView(R.id.imageCrop); String ename = mCurrentImage.getContext().getString(R.string.crop); mUtilityPanel.setEffectName(ename); - mUtilityPanel.showParameter(false); + mUtilityPanel.setShowParameter(false); break; } - */ case R.id.rotateButton: { mCurrentImage = showImageView(R.id.imageRotate); String ename = mCurrentImage.getContext().getString(R.string.rotate); diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java index 527800e13..9bdffee2e 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java @@ -20,6 +20,8 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; import com.android.gallery3d.filtershow.imageshow.GeometryMetadata; @@ -61,15 +63,18 @@ public class ImageFilterGeometry extends ImageFilter { native protected void nativeApplyFilterStraighten(Bitmap src, int srcWidth, int srcHeight, Bitmap dst, int dstWidth, int dstHeight, float straightenAngle); - public Matrix buildMatrix(Bitmap bitmap, boolean rotated) { - float dx = bitmap.getWidth()/2; - float dy = bitmap.getHeight()/2; + public Matrix buildMatrix(RectF r) { + float dx = r.width()/2; + float dy = r.height()/2; if(mGeometry.hasSwitchedWidthHeight()){ float temp = dx; dx = dy; dy = temp; } - Matrix m = mGeometry.buildGeometryMatrix(bitmap.getWidth(), bitmap.getHeight(), 1f/mGeometry.getScaleFactor(), dx, dy); + float w = r.left * 2 + r.width(); + float h = r.top * 2 + r.height(); + Matrix m = mGeometry.buildGeometryMatrix(w, h, 1f, dx, dy, false); + return m; } @@ -78,18 +83,18 @@ public class ImageFilterGeometry extends ImageFilter { // TODO: implement bilinear or bicubic here... for now, just use // canvas to do a simple implementation... // TODO: and be more memory efficient! (do it in native?) - + Rect cropBounds = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); + RectF c = mGeometry.getCropBounds(); + if(c != null && c.width() > 0 && c.height() > 0) + c.roundOut(cropBounds); Bitmap temp = null; - float rotation = mGeometry.getRotation(); - boolean rotated = false; - if (rotation == 0 || rotation % 180 == 0) { - temp = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), mConfig); + if (mGeometry.hasSwitchedWidthHeight()) { + temp = Bitmap.createBitmap(cropBounds.height(), cropBounds.width(), mConfig); } else { - temp = Bitmap.createBitmap(bitmap.getHeight(), bitmap.getWidth(), mConfig); - rotated = true; + temp = Bitmap.createBitmap(cropBounds.width(), cropBounds.height(), mConfig); } - Matrix drawMatrix = buildMatrix(bitmap, rotated); + Matrix drawMatrix = buildMatrix(c); Canvas canvas = new Canvas(temp); canvas.drawBitmap(bitmap, drawMatrix, new Paint()); return temp; diff --git a/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java b/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java index a14e065b2..d412f5890 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java +++ b/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java @@ -33,7 +33,6 @@ public class GeometryMetadata { private final RectF mPhotoBounds = new RectF(); private FLIP mFlip = FLIP.NONE; - private RectF mBounds = new RectF(); public enum FLIP { @@ -47,7 +46,7 @@ public class GeometryMetadata { set(g); } - public Bitmap apply(Bitmap original, float scaleFactor, boolean highQuality){ + public Bitmap apply(Bitmap original, float scaleFactor, boolean highQuality) { mImageFilter.setGeometryMetadata(this); Bitmap m = mImageFilter.apply(original, scaleFactor, highQuality); return m; @@ -87,7 +86,6 @@ public class GeometryMetadata { return new RectF(mPhotoBounds); } - public void setScaleFactor(float scale) { mScaleFactor = scale; } @@ -182,27 +180,34 @@ public class GeometryMetadata { } } - public boolean hasSwitchedWidthHeight(){ + public boolean hasSwitchedWidthHeight() { return (((int) (mRotation / 90)) % 2) != 0; } - public Matrix buildGeometryMatrix(float width, float height, float scaling, float dx, float dy){ - float dx0 = width/2; - float dy0 = height/2; + public Matrix buildGeometryMatrix(float width, float height, float scaling, float dx, float dy, + float rotation) { + float dx0 = width / 2; + float dy0 = height / 2; Matrix m = getFlipMatrix(width, height); m.postTranslate(-dx0, -dy0); - float rot = mRotation % 360; - if (rot < 0) - rot += 360; - m.postRotate(rot + mStraightenRotation); + m.postRotate(rotation); m.postScale(scaling, scaling); m.postTranslate(dx, dy); return m; } - public Matrix buildGeometryUIMatrix(float scaling, float dx, float dy){ + public Matrix buildGeometryMatrix(float width, float height, float scaling, float dx, float dy, + boolean onlyRotate) { + float rot = mRotation; + if (!onlyRotate) { + rot += mStraightenRotation; + } + return buildGeometryMatrix(width, height, scaling, dx, dy, rot); + } + + public Matrix buildGeometryUIMatrix(float scaling, float dx, float dy) { float w = mPhotoBounds.width(); float h = mPhotoBounds.height(); - return buildGeometryMatrix(w, h, scaling, dx, dy); + return buildGeometryMatrix(w, h, scaling, dx, dy, false); } } diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java b/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java index 90d36e942..4d171bf4c 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java @@ -22,12 +22,10 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; -import android.graphics.Path; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Log; - import com.android.gallery3d.R; public class ImageCrop extends ImageGeometry { @@ -40,19 +38,14 @@ public class ImageCrop extends ImageGeometry { private static final float MIN_CROP_WIDTH_HEIGHT = 0.1f; private static final int TOUCH_TOLERANCE = 30; - private static final int SHADOW_ALPHA = 160; - private final float mAspectWidth = 4; - private final float mAspectHeight = 3; - private final boolean mFixAspectRatio = false; // not working yet + private boolean mFirstDraw = true; + private float mAspectWidth = 1; + private float mAspectHeight = 1; + private boolean mFixAspectRatio = false; private final Paint borderPaint; - private float mCropOffsetX = 0; - private float mCropOffsetY = 0; - private float mPrevOffsetX = 0; - private float mPrevOffsetY = 0; - private int movingEdges; private final Drawable cropIndicator; private final int indicatorSize; @@ -86,22 +79,21 @@ public class ImageCrop extends ImageGeometry { } private float getScaledMinWidthHeight() { - RectF disp = getLocalDisplayBounds(); + RectF disp = new RectF(0, 0, getWidth(), getHeight()); float scaled = Math.min(disp.width(), disp.height()) * MIN_CROP_WIDTH_HEIGHT - / getLocalScale(); + / computeScale(getWidth(), getHeight()); return scaled; } - protected static Matrix getCropRotationMatrix(float rotation, RectF localImage) { - Matrix m = new Matrix(); - m.setRotate(rotation, localImage.centerX(), localImage.centerY()); + protected Matrix getCropRotationMatrix(float rotation, RectF localImage) { + Matrix m = getLocalGeoFlipMatrix(localImage.width(), localImage.height()); + m.postRotate(rotation, localImage.centerX(), localImage.centerY()); if (!m.rectStaysRect()) { return null; } return m; } - @Override protected RectF getCropBoundsDisplayed() { RectF bounds = getLocalCropBounds(); RectF crop = new RectF(bounds); @@ -115,7 +107,7 @@ public class ImageCrop extends ImageGeometry { m.mapRect(crop); } m = new Matrix(); - float zoom = getLocalScale(); + float zoom = computeScale(getWidth(), getHeight()); m.setScale(zoom, zoom, mCenterX, mCenterY); m.preTranslate(mXOffset, mYOffset); m.mapRect(crop); @@ -137,6 +129,44 @@ public class ImageCrop extends ImageGeometry { return crop; } + private RectF getUnrotatedCropBounds(RectF cropBounds) { + Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds()); + + if (m == null) { + if (LOGV) + Log.v(LOGTAG, "FAILED TO GET ROTATION MATRIX"); + return null; + } + Matrix m0 = new Matrix(); + if (!m.invert(m0)) { + if (LOGV) + Log.v(LOGTAG, "FAILED TO INVERT ROTATION MATRIX"); + return null; + } + RectF crop = new RectF(cropBounds); + if (!m0.mapRect(crop)) { + if (LOGV) + Log.v(LOGTAG, "FAILED TO UNROTATE CROPPING BOUNDS"); + return null; + } + return crop; + } + + private RectF getRotatedStraightenBounds() { + RectF straightenBounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(), + getLocalStraighten()); + Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds()); + + if (m == null) { + if (LOGV) + Log.v(LOGTAG, "FAILED TO MAP STRAIGHTEN BOUNDS TO RECTANGLE"); + return null; + } else { + m.mapRect(straightenBounds); + } + return straightenBounds; + } + /** * Sets cropped bounds; modifies the bounds if it's smaller than the allowed * dimensions. @@ -145,16 +175,33 @@ public class ImageCrop extends ImageGeometry { // Avoid cropping smaller than minimum width or height. RectF cbounds = new RectF(bounds); float minWidthHeight = getScaledMinWidthHeight(); + float aw = mAspectWidth; + float ah = mAspectHeight; + if (mFixAspectRatio) { + minWidthHeight /= aw * ah; + int r = (int) (getLocalRotation() / 90); + if (r % 2 != 0) { + float temp = aw; + aw = ah; + ah = temp; + } + } float newWidth = cbounds.width(); float newHeight = cbounds.height(); - if (newWidth < minWidthHeight) { - newWidth = minWidthHeight; - } - if (newHeight < minWidthHeight) { - newHeight = minWidthHeight; + if (mFixAspectRatio) { + if (newWidth < (minWidthHeight * aw) || newHeight < (minWidthHeight * ah)) { + newWidth = minWidthHeight * aw; + newHeight = minWidthHeight * ah; + } + } else { + if (newWidth < minWidthHeight) { + newWidth = minWidthHeight; + } + if (newHeight < minWidthHeight) { + newHeight = minWidthHeight; + } } - RectF pbounds = getLocalPhotoBounds(); if (pbounds.width() < minWidthHeight) { newWidth = pbounds.width(); @@ -164,18 +211,14 @@ public class ImageCrop extends ImageGeometry { } cbounds.set(cbounds.left, cbounds.top, cbounds.left + newWidth, cbounds.top + newHeight); - RectF snappedCrop = findCropBoundForRotatedImg(cbounds, pbounds, getLocalStraighten(), - mCenterX - mXOffset, mCenterY - mYOffset); - - RectF straightenBounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(), getLocalStraighten()); - snappedCrop.intersect(straightenBounds); - + RectF straightenBounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(), + getLocalStraighten()); + cbounds.intersect(straightenBounds); if (mFixAspectRatio) { - // TODO: add aspect ratio stuff - fixAspectRatio(snappedCrop, mAspectWidth, mAspectHeight); + fixAspectRatio(cbounds, aw, ah); } - setLocalCropBounds(snappedCrop); + setLocalCropBounds(cbounds); invalidate(); } @@ -202,33 +245,88 @@ public class ImageCrop extends ImageGeometry { else if (bottom <= TOUCH_TOLERANCE) { movingEdges |= MOVE_BOTTOM; } + // Check inside block. + if (cropped.contains(x, y) && (movingEdges == 0)) { + movingEdges = MOVE_BLOCK; + } invalidate(); } private void moveEdges(float dX, float dY) { RectF cropped = getRotatedCropBounds(); float minWidthHeight = getScaledMinWidthHeight(); - float scale = getLocalScale(); + float scale = computeScale(getWidth(), getHeight()); float deltaX = dX / scale; float deltaY = dY / scale; - if (movingEdges == MOVE_BLOCK) { - // TODO + int select = movingEdges; + if (mFixAspectRatio && (select != MOVE_BLOCK)) { + if ((select & MOVE_LEFT) != 0) { + select &= ~MOVE_BOTTOM; + select |= MOVE_TOP; + deltaY = getNewHeightForWidthAspect(deltaX, mAspectWidth, mAspectHeight); + } + if ((select & MOVE_TOP) != 0) { + select &= ~MOVE_RIGHT; + select |= MOVE_LEFT; + deltaX = getNewWidthForHeightAspect(deltaY, mAspectWidth, mAspectHeight); + } + if ((select & MOVE_RIGHT) != 0) { + select &= ~MOVE_TOP; + select |= MOVE_BOTTOM; + deltaY = getNewHeightForWidthAspect(deltaX, mAspectWidth, mAspectHeight); + } + if ((select & MOVE_BOTTOM) != 0) { + select &= ~MOVE_LEFT; + select |= MOVE_RIGHT; + deltaX = getNewWidthForHeightAspect(deltaY, mAspectWidth, mAspectHeight); + } + } + + if (select == MOVE_BLOCK) { + RectF straight = getRotatedStraightenBounds(); + // Move the whole cropped bounds within the photo display bounds. + deltaX = (deltaX > 0) ? Math.min(straight.right - cropped.right, deltaX) + : Math.max(straight.left - cropped.left, deltaX); + deltaY = (deltaY > 0) ? Math.min(straight.bottom - cropped.bottom, deltaY) + : Math.max(straight.top - cropped.top, deltaY); + cropped.offset(deltaX, deltaY); } else { - if ((movingEdges & MOVE_LEFT) != 0) { - cropped.left = Math.min(cropped.left + deltaX, cropped.right - minWidthHeight); - fixRectAspectW(cropped); + float dx = 0; + float dy = 0; + if ((select & MOVE_LEFT) != 0) { + dx = Math.min(cropped.left + deltaX, cropped.right - minWidthHeight) - cropped.left; + } + if ((select & MOVE_TOP) != 0) { + dy = Math.min(cropped.top + deltaY, cropped.bottom - minWidthHeight) - cropped.top; + } + if ((select & MOVE_RIGHT) != 0) { + dx = Math.max(cropped.right + deltaX, cropped.left + minWidthHeight) + - cropped.right; } - if ((movingEdges & MOVE_TOP) != 0) { - cropped.top = Math.min(cropped.top + deltaY, cropped.bottom - minWidthHeight); - fixRectAspectH(cropped); + if ((select & MOVE_BOTTOM) != 0) { + dy = Math.max(cropped.bottom + deltaY, cropped.top + minWidthHeight) + - cropped.bottom; } - if ((movingEdges & MOVE_RIGHT) != 0) { - cropped.right = Math.max(cropped.right + deltaX, cropped.left + minWidthHeight); - fixRectAspectW(cropped); + + if (mFixAspectRatio) { + if (dx < dy) { + dy = getNewHeightForWidthAspect(dx, mAspectWidth, mAspectHeight); + } else { + dx = getNewWidthForHeightAspect(dy, mAspectWidth, mAspectHeight); + } + } + + if ((select & MOVE_LEFT) != 0) { + cropped.left += dx; } - if ((movingEdges & MOVE_BOTTOM) != 0) { - cropped.bottom = Math.max(cropped.bottom + deltaY, cropped.top + minWidthHeight); - fixRectAspectH(cropped); + if ((select & MOVE_TOP) != 0) { + cropped.top += dy; + } + if ((select & MOVE_RIGHT) != 0) { + cropped.right += dx; + } + if ((select & MOVE_BOTTOM) != 0) { + cropped.bottom += dy; } } Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds()); @@ -244,24 +342,6 @@ public class ImageCrop extends ImageGeometry { setCropBounds(cropped); } - private void fixRectAspectH(RectF cropped) { - if (mFixAspectRatio) { - float half = getNewWidthForHeightAspect(cropped.height(), mAspectWidth, mAspectHeight) / 2; - float mid = (cropped.right - cropped.left) / 2; - cropped.left = mid - half; - cropped.right = mid + half; - } - } - - private void fixRectAspectW(RectF cropped) { - if (mFixAspectRatio) { - float half = getNewHeightForWidthAspect(cropped.width(), mAspectWidth, mAspectHeight) / 2; - float mid = (cropped.bottom - cropped.top) / 2; - cropped.top = mid - half; - cropped.bottom = mid + half; - } - } - private void drawIndicator(Canvas canvas, Drawable indicator, float centerX, float centerY) { int left = (int) centerX - indicatorSize / 2; int top = (int) centerY - indicatorSize / 2; @@ -273,81 +353,111 @@ public class ImageCrop extends ImageGeometry { protected void setActionDown(float x, float y) { super.setActionDown(x, y); detectMovingEdges(x, y); - if (movingEdges == 0) { - mPrevOffsetX = mCropOffsetX; - mPrevOffsetY = mCropOffsetY; - } + } + + @Override + protected void setActionUp() { + super.setActionUp(); + movingEdges = 0; } @Override protected void setActionMove(float x, float y) { - if (movingEdges != 0) { + if (movingEdges != 0) moveEdges(x - mCurrentX, y - mCurrentY); + + super.setActionMove(x, y); + } + + private void cropSetup() { + if (mFixAspectRatio) { + RectF cb = getRotatedCropBounds(); + fixAspectRatio(cb, mAspectWidth, mAspectHeight); + RectF cb0 = getUnrotatedCropBounds(cb); + setCropBounds(cb0); } else { - float dx = x - mTouchCenterX; - float dy = y - mTouchCenterY; - mCropOffsetX = dx + mPrevOffsetX; - mCropOffsetY = dy + mPrevOffsetY; + setCropBounds(getLocalCropBounds()); } - super.setActionMove(x, y); } @Override protected void gainedVisibility() { - setCropBounds(getLocalCropBounds()); - super.gainedVisibility(); + cropSetup(); + mFirstDraw = true; } - protected RectF drawCrop(Canvas canvas, Paint p, RectF cropBounds, float scale, - float rotation, float centerX, float centerY, float offsetX, float offsetY) { - RectF crop = new RectF(cropBounds); - Matrix m = new Matrix(); - m.preTranslate(offsetX, offsetY); - m.mapRect(crop); + @Override + public void resetParameter() { + super.resetParameter(); + cropSetup(); + } - m.setRotate(rotation, centerX, centerY); - if (!m.rectStaysRect()) { - float[] corners = getCornersFromRect(crop); - m.mapPoints(corners); - drawClosedPath(canvas, p, corners); - } else { - RectF crop2 = new RectF(crop); - m.mapRect(crop2); - Path path = new Path(); - path.addRect(crop2, Path.Direction.CCW); - canvas.drawPath(path, p); - } - return crop; + @Override + protected void lostVisibility() { } @Override protected void drawShape(Canvas canvas, Bitmap image) { + // TODO: move style to xml gPaint.setAntiAlias(true); gPaint.setFilterBitmap(true); gPaint.setDither(true); gPaint.setARGB(255, 255, 255, 255); - drawTransformedBitmap(canvas, image, gPaint, false); - float scale = getLocalScale(); + if (mFirstDraw) { + cropSetup(); + mFirstDraw = false; + } float rotation = getLocalRotation(); - - RectF scaledCrop = drawCrop(canvas, gPaint, getLocalCropBounds(), scale, - rotation, mCenterX, mCenterY, mXOffset, - mYOffset); - - boolean notMoving = movingEdges == 0; - if (((movingEdges & MOVE_TOP) != 0) || notMoving) { + drawTransformedBitmap(canvas, image, gPaint, true); + + gPaint.setARGB(255, 125, 255, 128); + gPaint.setStrokeWidth(3); + gPaint.setStyle(Paint.Style.STROKE); + drawStraighten(canvas, gPaint); + RectF scaledCrop = unrotatedCropBounds(); + int decoded_moving = decoder(movingEdges, rotation); + canvas.save(); + canvas.rotate(rotation, mCenterX, mCenterY); + boolean notMoving = decoded_moving == 0; + if (((decoded_moving & MOVE_TOP) != 0) || notMoving) { drawIndicator(canvas, cropIndicator, scaledCrop.centerX(), scaledCrop.top); } - if (((movingEdges & MOVE_BOTTOM) != 0) || notMoving) { + if (((decoded_moving & MOVE_BOTTOM) != 0) || notMoving) { drawIndicator(canvas, cropIndicator, scaledCrop.centerX(), scaledCrop.bottom); } - if (((movingEdges & MOVE_LEFT) != 0) || notMoving) { + if (((decoded_moving & MOVE_LEFT) != 0) || notMoving) { drawIndicator(canvas, cropIndicator, scaledCrop.left, scaledCrop.centerY()); } - if (((movingEdges & MOVE_RIGHT) != 0) || notMoving) { + if (((decoded_moving & MOVE_RIGHT) != 0) || notMoving) { drawIndicator(canvas, cropIndicator, scaledCrop.right, scaledCrop.centerY()); } + canvas.restore(); + } + + private int bitCycleLeft(int x, int times, int d){ + int mask = (1 << d) - 1; + int mout = x & mask; + times %= d; + int hi = mout >> (d - times); + int low = (mout << times) & mask; + int ret = x & ~mask; + ret |= low; + ret |= hi; + return ret; } -} + protected int decoder(int movingEdges, float rotation) { + int rot = constrainedRotation(rotation); + switch(rot){ + case 90: + return bitCycleLeft(movingEdges, 3, 4); + case 180: + return bitCycleLeft(movingEdges, 2, 4); + case 270: + return bitCycleLeft(movingEdges, 1, 4); + default: + 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 8da673ba6..d8d03dc22 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java @@ -26,7 +26,6 @@ import android.graphics.Paint.Style; import android.graphics.Path; import android.graphics.RectF; import android.util.AttributeSet; -import android.util.Log; import android.view.MotionEvent; import android.view.View; @@ -49,7 +48,7 @@ public abstract class ImageGeometry extends ImageSlave { protected float mTouchCenterY; // Local geometry data - private GeometryMetadata mLocalGeoMetadata = null; + private GeometryMetadata mLocalGeometry = null; private RectF mLocalDisplayBounds = null; protected float mXOffset = 0; protected float mYOffset = 0; @@ -75,9 +74,9 @@ public abstract class ImageGeometry extends ImageSlave { calculateLocalScalingFactorAndOffset(); } - private float computeScale(float width, float height) { - float imageWidth = mLocalGeoMetadata.getPhotoBounds().width(); - float imageHeight = mLocalGeoMetadata.getPhotoBounds().height(); + protected float computeScale(float width, float height) { + float imageWidth = mLocalGeometry.getPhotoBounds().width(); + float imageHeight = mLocalGeometry.getPhotoBounds().height(); float zoom = width / imageWidth; if (imageHeight > imageWidth) { zoom = height / imageHeight; @@ -86,9 +85,9 @@ public abstract class ImageGeometry extends ImageSlave { } private void calculateLocalScalingFactorAndOffset() { - if (mLocalGeoMetadata == null || mLocalDisplayBounds == null) + if (mLocalGeometry == null || mLocalDisplayBounds == null) return; - RectF imageBounds = mLocalGeoMetadata.getPhotoBounds(); + RectF imageBounds = mLocalGeometry.getPhotoBounds(); float imageWidth = imageBounds.width(); float imageHeight = imageBounds.height(); float displayWidth = mLocalDisplayBounds.width(); @@ -98,6 +97,7 @@ public abstract class ImageGeometry extends ImageSlave { mCenterY = displayHeight / 2; mYOffset = (displayHeight - imageHeight) / 2.0f; mXOffset = (displayWidth - imageWidth) / 2.0f; + updateScale(); } @Override @@ -113,16 +113,16 @@ public abstract class ImageGeometry extends ImageSlave { // Overwrites local with master protected void syncLocalToMasterGeometry() { - mLocalGeoMetadata = getMaster().getGeometry(); + mLocalGeometry = getMaster().getGeometry(); calculateLocalScalingFactorAndOffset(); } protected RectF getLocalPhotoBounds() { - return mLocalGeoMetadata.getPhotoBounds(); + return mLocalGeometry.getPhotoBounds(); } protected RectF getLocalCropBounds() { - return mLocalGeoMetadata.getCropBounds(); + return mLocalGeometry.getCropBounds(); } protected RectF getLocalDisplayBounds() { @@ -130,52 +130,62 @@ public abstract class ImageGeometry extends ImageSlave { } protected float getLocalScale() { - return mLocalGeoMetadata.getScaleFactor(); + return mLocalGeometry.getScaleFactor(); } protected float getLocalRotation() { - return mLocalGeoMetadata.getRotation(); + return mLocalGeometry.getRotation(); } protected float getLocalStraighten() { - return mLocalGeoMetadata.getStraightenRotation(); + return mLocalGeometry.getStraightenRotation(); } protected void setLocalScale(float s) { - mLocalGeoMetadata.setScaleFactor(s); + mLocalGeometry.setScaleFactor(s); } - protected void updateScale(){ - RectF bounds = getUntranslatedStraightenCropBounds(mLocalGeoMetadata.getPhotoBounds(), + protected void updateScale() { + RectF bounds = getUntranslatedStraightenCropBounds(mLocalGeometry.getPhotoBounds(), getLocalStraighten()); float zoom = computeScale(bounds.width(), bounds.height()); setLocalScale(zoom); } protected void setLocalRotation(float r) { - mLocalGeoMetadata.setRotation(r); + mLocalGeometry.setRotation(r); updateScale(); } - private Matrix getLocalGeoMatrix(float scaling, float dx, float dy) { - return mLocalGeoMetadata.buildGeometryUIMatrix(scaling, dx, dy); + /** + * Constrains rotation to be in [0, 90, 180, 270]. + */ + protected int constrainedRotation(float rotation) { + int r = (int) ((rotation % 360) / 90); + r = (r < 0) ? (r + 4) : r; + return r * 90; + } + + protected Matrix getLocalGeoFlipMatrix(float width, float height) { + return mLocalGeometry.getFlipMatrix(width, height); } protected void setLocalStraighten(float r) { - mLocalGeoMetadata.setStraightenRotation(r); + mLocalGeometry.setStraightenRotation(r); updateScale(); } protected void setLocalCropBounds(RectF c) { - mLocalGeoMetadata.setCropBounds(c); + mLocalGeometry.setCropBounds(c); + updateScale(); } protected FLIP getLocalFlip() { - return mLocalGeoMetadata.getFlipType(); + return mLocalGeometry.getFlipType(); } protected void setLocalFlip(FLIP flip) { - mLocalGeoMetadata.setFlipType(flip); + mLocalGeometry.setFlipType(flip); } protected float getTotalLocalRotation() { @@ -191,35 +201,18 @@ public abstract class ImageGeometry extends ImageSlave { protected static float[] getCornersFromRect(RectF r) { // Order is: // 0------->1 - // ^ | - // | v + // ^ | + // | v // 3<-------2 float[] corners = { r.left, r.top, // 0 r.right, r.top, // 1 r.right, r.bottom,// 2 - r.left, r.bottom - // 3 + r.left, r.bottom // 3 }; return corners; } - // Returns maximal rectangular crop bound that still fits within - // the image bound after the image has been rotated. - protected static RectF findCropBoundForRotatedImg(RectF cropBound, RectF imageBound, - float rotation, float centerX, float centerY) { - Matrix m = new Matrix(); - float[] cropEdges = getCornersFromRect(cropBound); - m.setRotate(rotation, centerX, centerY); - Matrix m0 = new Matrix(); - if (!m.invert(m0)) - return null; - m0.mapPoints(cropEdges); - getEdgePoints(imageBound, cropEdges); - m.mapPoints(cropEdges); - return trapToRect(cropEdges); - } - // If edge point [x, y] in array [x0, y0, x1, y1, ...] is outside of the // image bound rectangle, clamps it to the edge of the rectangle. protected static void getEdgePoints(RectF imageBound, float[] array) { @@ -231,24 +224,6 @@ public abstract class ImageGeometry extends ImageSlave { } } - protected static RectF trapToRect(float[] array) { - float dx0 = array[4] - array[0]; - float dy0 = array[5] - array[1]; - float dx1 = array[6] - array[2]; - float dy1 = array[7] - array[3]; - float l0 = dx0 * dx0 + dy0 * dy0; - float l1 = dx1 * dx1 + dy1 * dy1; - if (l0 > l1) { - RectF n = new RectF(array[2], array[3], array[6], array[7]); - n.sort(); - return n; - } else { - RectF n = new RectF(array[0], array[1], array[4], array[5]); - n.sort(); - return n; - } - } - protected static Path drawClosedPath(Canvas canvas, Paint paint, float[] points) { Path crop = new Path(); crop.moveTo(points[0], points[1]); @@ -260,26 +235,14 @@ public abstract class ImageGeometry extends ImageSlave { return crop; } - 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; - } - protected static void fixAspectRatio(RectF r, float w, float h) { float scale = Math.min(r.width() / w, r.height() / h); - r.set(r.left, r.top, scale * w, scale * h); + float centX = r.centerX(); + float centY = r.centerY(); + float hw = scale * w / 2; + float hh = scale * h / 2; + r.set(centX - hw, centY - hh, centX + hw, centY + hh); + } protected static float getNewHeightForWidthAspect(float width, float w, float h) { @@ -296,6 +259,7 @@ public abstract class ImageGeometry extends ImageSlave { if (visibility == View.VISIBLE) { mVisibilityGained = true; syncLocalToMasterGeometry(); + updateScale(); gainedVisibility(); } else { if (mVisibilityGained == true && mHasDrawn == true) { @@ -308,7 +272,6 @@ public abstract class ImageGeometry extends ImageSlave { protected void gainedVisibility() { // TODO: Override this stub. - updateScale(); } protected void lostVisibility() { @@ -330,7 +293,6 @@ public abstract class ImageGeometry extends ImageSlave { case (MotionEvent.ACTION_UP): setActionUp(); saveAndSetPreset(); - Log.v(LOGTAG, "up action"); break; case (MotionEvent.ACTION_MOVE): setActionMove(event.getX(), event.getY()); @@ -378,13 +340,12 @@ public abstract class ImageGeometry extends ImageSlave { protected void saveAndSetPreset() { ImagePreset copy = new ImagePreset(getImagePreset()); - copy.setGeometry(mLocalGeoMetadata); + copy.setGeometry(mLocalGeometry); copy.setHistoryName("Geometry"); copy.setIsFx(false); setImagePreset(copy); } - // protected static float clamp(float i, float low, float high) { return Math.max(Math.min(i, high), low); } @@ -413,105 +374,123 @@ public abstract class ImageGeometry extends ImageSlave { return new RectF(left, top, right, bottom); } - protected static void drawShadows(Canvas canvas, Paint p, RectF innerBounds, RectF outerBounds, - float rotation, float centerX, float centerY) { + protected Matrix getGeoMatrix(RectF r, boolean onlyRotate) { + float scale = computeScale(getWidth(), getHeight()); + float yoff = getHeight() / 2; + float xoff = getWidth() / 2; + float w = r.left * 2 + r.width(); + float h = r.top * 2 + r.height(); + return mLocalGeometry.buildGeometryMatrix(w, h, scale, xoff, yoff, onlyRotate); + } + + protected void drawImageBitmap(Canvas canvas, Bitmap bitmap, Paint paint, Matrix m) { canvas.save(); - canvas.rotate(rotation, centerX, centerY); - float dWidth = outerBounds.width(); - float dHeight = outerBounds.height(); - canvas.drawRect(0, 0, dWidth, innerBounds.top, p); - canvas.drawRect(0, innerBounds.bottom, dWidth, dHeight, p); - canvas.drawRect(0, innerBounds.top, innerBounds.left, innerBounds.bottom, - p); - canvas.drawRect(innerBounds.right, innerBounds.top, dWidth, - innerBounds.bottom, p); - canvas.rotate(-rotation, centerX, centerY); + canvas.drawBitmap(bitmap, m, paint); canvas.restore(); } - public Matrix computeBoundsMatrix(Bitmap bitmap) { - float w = getWidth(); - float h = getHeight(); - Matrix boundsMatrix = new Matrix(); - boundsMatrix.setTranslate((getWidth() - bitmap.getWidth()) / 2.0f, - (getHeight() - bitmap.getHeight()) / 2.0f); - boundsMatrix.postRotate(getLocalRotation(), getWidth() / 2.0f, getHeight() / 2.0f); - float scale = computeScale(w, h); - boundsMatrix.postScale(scale, scale, getWidth()/2, getHeight()/2); - return boundsMatrix; + protected void drawImageBitmap(Canvas canvas, Bitmap bitmap, Paint paint) { + float scale = computeScale(getWidth(), getHeight()); + float yoff = getHeight() / 2; + float xoff = getWidth() / 2; + Matrix m = mLocalGeometry.buildGeometryUIMatrix(scale, xoff, yoff); + drawImageBitmap(canvas, bitmap, paint, m); } - public RectF cropBounds(Bitmap bitmap) { - Matrix boundsMatrix = computeBoundsMatrix(bitmap); + protected RectF straightenBounds() { RectF bounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(), getLocalStraighten()); - RectF transformedBounds = new RectF(bounds); - boundsMatrix.mapRect(transformedBounds); - return transformedBounds; + Matrix m = getGeoMatrix(bounds, true); + m.mapRect(bounds); + return bounds; } - protected void drawImage(Canvas canvas, Bitmap bitmap, Paint paint) { - float scale = computeScale(getWidth(), getHeight()); - float yoff = getHeight()/2; - float xoff = getWidth()/2; - Matrix m = getLocalGeoMatrix(scale, xoff, yoff); + protected void drawStraighten(Canvas canvas, Paint paint) { + RectF bounds = straightenBounds(); canvas.save(); - canvas.drawBitmap(bitmap, m, paint); + canvas.drawRect(bounds, paint); + canvas.restore(); } - protected void drawTransformedBitmap(Canvas canvas, Bitmap bitmap, Paint paint, boolean clip) { - float w = getWidth(); - float h = getHeight(); - Matrix boundsMatrix = computeBoundsMatrix(bitmap); - RectF bounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(), - getLocalStraighten()); - RectF transformedBounds = new RectF(bounds); - boundsMatrix.mapRect(transformedBounds); - canvas.save(); - paint.setARGB(255, 0, 0, 0); - drawImage(canvas, bitmap, paint); - canvas.restore(); + protected RectF unrotatedCropBounds() { + RectF bounds = getLocalCropBounds(); + RectF pbounds = getLocalPhotoBounds(); + float scale = computeScale(getWidth(), getHeight()); + float yoff = getHeight() / 2; + float xoff = getWidth() / 2; + Matrix m = mLocalGeometry.buildGeometryMatrix(pbounds.width(), pbounds.height(), scale, xoff, yoff, 0); + m.mapRect(bounds); + return bounds; + } + + protected RectF cropBounds() { + RectF bounds = getLocalCropBounds(); + Matrix m = getGeoMatrix(getLocalPhotoBounds(), true); + m.mapRect(bounds); + return bounds; + } + + // Fails for non-90 degree + protected void drawCrop(Canvas canvas, Paint paint) { + RectF bounds = cropBounds(); canvas.save(); - canvas.setMatrix(boundsMatrix); - paint.setColor(Color.WHITE); - paint.setStyle(Style.STROKE); - paint.setStrokeWidth(2); canvas.drawRect(bounds, paint); canvas.restore(); + } - if (!clip) { // we display the rest of the bitmap grayed-out - drawShadows(canvas, transformedBounds, new RectF(0, 0, w, h), paint); + protected void drawCropSafe(Canvas canvas, Paint paint) { + Matrix m = getGeoMatrix(getLocalPhotoBounds(), true); + RectF crop = getLocalCropBounds(); + if (!m.rectStaysRect()) { + float[] corners = getCornersFromRect(crop); + m.mapPoints(corners); + drawClosedPath(canvas, paint, corners); + } else { + m.mapRect(crop); + Path path = new Path(); + path.addRect(crop, Path.Direction.CCW); + canvas.drawPath(path, paint); } } - protected RectF getCropBoundsDisplayed() { - return getCropBoundsDisplayed(getLocalCropBounds()); + protected void drawTransformedBitmap(Canvas canvas, Bitmap bitmap, Paint paint, boolean clip) { + paint.setARGB(255, 0, 0, 0); + drawImageBitmap(canvas, bitmap, paint); + paint.setColor(Color.WHITE); + paint.setStyle(Style.STROKE); + paint.setStrokeWidth(2); + drawCropSafe(canvas, paint); + paint.setARGB(128, 0, 0, 0); + paint.setStyle(Paint.Style.FILL); + drawShadows(canvas, paint, unrotatedCropBounds()); } - protected RectF getCropBoundsDisplayed(RectF bounds) { - RectF crop = new RectF(bounds); - Matrix m = new Matrix(); - float zoom = getLocalScale(); - m.setScale(zoom, zoom, mCenterX, mCenterY); - m.preTranslate(mXOffset, mYOffset); - m.mapRect(crop); - return crop; + protected void drawShadows(Canvas canvas, Paint p, RectF innerBounds) { + RectF display = new RectF(0, 0, getWidth(), getHeight()); + drawShadows(canvas, p, innerBounds, display, getLocalRotation(), getWidth() / 2, + getHeight() / 2); } - protected void drawShadows(Canvas canvas, RectF innerBounds, RectF outerBounds, Paint p) { - float dWidth = outerBounds.width(); - float dHeight = outerBounds.height(); - - // TODO: move style to xml - p.setARGB(128, 0, 0, 0); - p.setStyle(Paint.Style.FILL); + protected static void drawShadows(Canvas canvas, Paint p, RectF innerBounds, RectF outerBounds, + float rotation, float centerX, float centerY) { + canvas.save(); + canvas.rotate(rotation, centerX, centerY); - canvas.drawRect(0, 0, dWidth, innerBounds.top, p); - canvas.drawRect(0, innerBounds.bottom, dWidth, dHeight, p); - canvas.drawRect(0, innerBounds.top, innerBounds.left, innerBounds.bottom, + float x = (outerBounds.left - outerBounds.right); + float y = (outerBounds.top - outerBounds.bottom); + float longest = (float) Math.sqrt(x * x + y * y) / 2; + float minX = centerX - longest; + float maxX = centerX + longest; + float minY = centerY - longest; + float maxY = centerY + longest; + canvas.drawRect(minX, minY, innerBounds.right, innerBounds.top, p); + canvas.drawRect(minX, innerBounds.top, innerBounds.left, maxY, p); + canvas.drawRect(innerBounds.left, innerBounds.bottom, maxX, maxY, p); - canvas.drawRect(innerBounds.right, innerBounds.top, dWidth, + canvas.drawRect(innerBounds.right, minY, maxX, innerBounds.bottom, p); + canvas.rotate(-rotation, centerX, centerY); + canvas.restore(); } @Override diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java index a94d6292f..2fd6b9b35 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java @@ -24,6 +24,8 @@ 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; @@ -46,11 +48,17 @@ public class ImageStraighten extends ImageGeometry { mBaseAngle = mAngle = getLocalStraighten(); } + private void setCropToStraighten(){ + setLocalCropBounds(getUntranslatedStraightenCropBounds(getLocalPhotoBounds(), + getLocalStraighten())); + } + @Override protected void setActionMove(float x, float y) { super.setActionMove(x, y); computeValue(); setLocalStraighten(mAngle); + setCropToStraighten(); } private float angleFor(float dx, float dy) { @@ -80,6 +88,17 @@ public class ImageStraighten extends ImageGeometry { } @Override + protected void gainedVisibility(){ + setCropToStraighten(); + } + + @Override + protected void setActionUp() { + super.setActionUp(); + setCropToStraighten(); + } + + @Override public void onNewValue(int value) { setLocalStraighten(clamp(value, MIN_STRAIGHTEN_ANGLE, MAX_STRAIGHTEN_ANGLE)); if (getPanelController() != null) { @@ -98,7 +117,7 @@ public class ImageStraighten extends ImageGeometry { drawTransformedBitmap(canvas, image, gPaint, false); // Draw the grid - RectF bounds = cropBounds(image); + RectF bounds = straightenBounds(); Path path = new Path(); path.addRect(bounds, Path.Direction.CCW); gPaint.setARGB(255, 255, 255, 255); |