diff --git a/cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapCroppingWorkerTask.java b/cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapCroppingWorkerTask.java index e5c7a7e2..702da115 100644 --- a/cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapCroppingWorkerTask.java +++ b/cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapCroppingWorkerTask.java @@ -91,6 +91,16 @@ final class BitmapCroppingWorkerTask extends AsyncTask(cropImageView); @@ -128,6 +138,8 @@ final class BitmapCroppingWorkerTask extends AsyncTask(cropImageView); @@ -154,6 +166,8 @@ final class BitmapCroppingWorkerTask extends AsyncTask(new BitmapCroppingWorkerTask(this, mLoadedImageUri, getCropPoints(), mDegreesRotated, orgWidth, orgHeight, mCropOverlayView.isFixAspectRatio(), mCropOverlayView.getAspectRatioX(), mCropOverlayView.getAspectRatioY(), - reqWidth, reqHeight, options, + reqWidth, reqHeight, mCropOverlayView.getIsFlippedHorizontally(), mCropOverlayView.getIsFlippedVertically(), options, saveUri, saveCompressFormat, saveCompressQuality)); } else { mBitmapCroppingWorkerTask = new WeakReference<>(new BitmapCroppingWorkerTask(this, mBitmap, getCropPoints(), mDegreesRotated, mCropOverlayView.isFixAspectRatio(), mCropOverlayView.getAspectRatioX(), mCropOverlayView.getAspectRatioY(), - reqWidth, reqHeight, options, + reqWidth, reqHeight, mCropOverlayView.getIsFlippedHorizontally(), mCropOverlayView.getIsFlippedVertically(), options, saveUri, saveCompressFormat, saveCompressQuality)); } mBitmapCroppingWorkerTask.get().execute(); @@ -1143,6 +1190,8 @@ public Parcelable onSaveInstanceState() { bundle.putString("CROP_SHAPE", mCropOverlayView.getCropShape().name()); bundle.putBoolean("CROP_AUTO_ZOOM_ENABLED", mAutoZoomEnabled); bundle.putInt("CROP_MAX_ZOOM", mMaxZoom); + bundle.putBoolean("CROP_FLIP_HORIZONTALLY", mCropOverlayView.getIsFlippedHorizontally()); + bundle.putBoolean("CROP_FLIP_VERTICALLY", mCropOverlayView.getIsFlippedVertically()); return bundle; } @@ -1197,6 +1246,9 @@ public void onRestoreInstanceState(Parcelable state) { mAutoZoomEnabled = bundle.getBoolean("CROP_AUTO_ZOOM_ENABLED"); mMaxZoom = bundle.getInt("CROP_MAX_ZOOM"); + + mCropOverlayView.setFlipHorizontally(bundle.getBoolean("CROP_FLIP_HORIZONTALLY")); + mCropOverlayView.setFlipVertically(bundle.getBoolean("CROP_FLIP_VERTICALLY")); } super.onRestoreInstanceState(bundle.getParcelable("instanceState")); @@ -1390,7 +1442,9 @@ private void applyImageMatrix(float width, float height, boolean center, boolean } // scale by the current zoom level - mImageMatrix.postScale(mZoom, mZoom, BitmapUtils.getRectCenterX(mImagePoints), BitmapUtils.getRectCenterY(mImagePoints)); + float scaleX = mCropOverlayView.getIsFlippedHorizontally() ? -mZoom : mZoom; + float scaleY = mCropOverlayView.getIsFlippedVertically() ? -mZoom : mZoom; + mImageMatrix.postScale(scaleX, scaleY, BitmapUtils.getRectCenterX(mImagePoints), BitmapUtils.getRectCenterY(mImagePoints)); mapImagePointsByImageMatrix(); mImageMatrix.mapRect(cropRect); @@ -1398,20 +1452,21 @@ private void applyImageMatrix(float width, float height, boolean center, boolean if (center) { // set the zoomed area to be as to the center of cropping window as possible mZoomOffsetX = width > BitmapUtils.getRectWidth(mImagePoints) ? 0 - : Math.max(Math.min(width / 2 - cropRect.centerX(), -BitmapUtils.getRectLeft(mImagePoints)), getWidth() - BitmapUtils.getRectRight(mImagePoints)) / mZoom; + : Math.max(Math.min(width / 2 - cropRect.centerX(), -BitmapUtils.getRectLeft(mImagePoints)), getWidth() - BitmapUtils.getRectRight(mImagePoints)) / scaleX; mZoomOffsetY = height > BitmapUtils.getRectHeight(mImagePoints) ? 0 - : Math.max(Math.min(height / 2 - cropRect.centerY(), -BitmapUtils.getRectTop(mImagePoints)), getHeight() - BitmapUtils.getRectBottom(mImagePoints)) / mZoom; + : Math.max(Math.min(height / 2 - cropRect.centerY(), -BitmapUtils.getRectTop(mImagePoints)), getHeight() - BitmapUtils.getRectBottom(mImagePoints)) / scaleY; } else { // adjust the zoomed area so the crop window rectangle will be inside the area in case it was moved outside - mZoomOffsetX = Math.min(Math.max(mZoomOffsetX * mZoom, -cropRect.left), -cropRect.right + width) / mZoom; - mZoomOffsetY = Math.min(Math.max(mZoomOffsetY * mZoom, -cropRect.top), -cropRect.bottom + height) / mZoom; + mZoomOffsetX = Math.min(Math.max(mZoomOffsetX * scaleX, -cropRect.left), -cropRect.right + width) / scaleX; + mZoomOffsetY = Math.min(Math.max(mZoomOffsetY * scaleY, -cropRect.top), -cropRect.bottom + height) / scaleY; } // apply to zoom offset translate and update the crop rectangle to offset correctly - mImageMatrix.postTranslate(mZoomOffsetX * mZoom, mZoomOffsetY * mZoom); - cropRect.offset(mZoomOffsetX * mZoom, mZoomOffsetY * mZoom); + mImageMatrix.postTranslate(mZoomOffsetX * scaleX, mZoomOffsetY * scaleY); + cropRect.offset(mZoomOffsetX * scaleX, mZoomOffsetY * scaleY); mCropOverlayView.setCropWindowRect(cropRect); mapImagePointsByImageMatrix(); + mCropOverlayView.invalidate(); // set matrix to apply if (animate) { diff --git a/cropper/src/main/java/com/theartofdev/edmodo/cropper/CropOverlayView.java b/cropper/src/main/java/com/theartofdev/edmodo/cropper/CropOverlayView.java index 0d2ac520..eb3c9d8b 100644 --- a/cropper/src/main/java/com/theartofdev/edmodo/cropper/CropOverlayView.java +++ b/cropper/src/main/java/com/theartofdev/edmodo/cropper/CropOverlayView.java @@ -158,6 +158,16 @@ public class CropOverlayView extends View { */ private float mTargetAspectRatio = ((float) mAspectRatioX) / mAspectRatioY; + /** + * Flag indicating if the image should be flipped horizontally + */ + private boolean mFlipHorizontally; + + /** + * Flag indicating if the image should be flipped vertically + */ + private boolean mFlipVertically; + /** * Instance variables for customizable attributes */ @@ -379,6 +389,34 @@ public void setAspectRatioY(int aspectRatioY) { } } + /** + * whether the image is flipped horizontally + */ + public boolean getIsFlippedHorizontally() { + return mFlipHorizontally; + } + + /** + * Sets whether the image should be flipped horizontally + */ + public void setFlipHorizontally(boolean flipHorizontally) { + mFlipHorizontally = flipHorizontally; + } + + /** + * whether the image is flipped vertically + */ + public boolean getIsFlippedVertically() { + return mFlipVertically; + } + + /** + * Sets whether the image should be flipped vertically + */ + public void setFlipVertically(boolean flipVertically) { + mFlipVertically = flipVertically; + } + /** * An edge of the crop window will snap to the corresponding edge of a * specified bounding box when the crop window edge is less than or equal to @@ -476,6 +514,8 @@ public void setInitialAttributeValues(CropImageOptions options) { setAspectRatioY(options.aspectRatioY); + setFlipHorizontally(options.flipHorizontally); + setMultiTouchEnabled(options.multiTouchEnabled); mTouchRadius = options.touchRadius; diff --git a/cropper/src/main/res/drawable-hdpi/crop_image_menu_flip.png b/cropper/src/main/res/drawable-hdpi/crop_image_menu_flip.png new file mode 100644 index 00000000..133395df Binary files /dev/null and b/cropper/src/main/res/drawable-hdpi/crop_image_menu_flip.png differ diff --git a/cropper/src/main/res/drawable-xhdpi/crop_image_menu_flip.png b/cropper/src/main/res/drawable-xhdpi/crop_image_menu_flip.png new file mode 100644 index 00000000..79910ffe Binary files /dev/null and b/cropper/src/main/res/drawable-xhdpi/crop_image_menu_flip.png differ diff --git a/cropper/src/main/res/drawable-xxhdpi/crop_image_menu_flip.png b/cropper/src/main/res/drawable-xxhdpi/crop_image_menu_flip.png new file mode 100644 index 00000000..3629e38d Binary files /dev/null and b/cropper/src/main/res/drawable-xxhdpi/crop_image_menu_flip.png differ diff --git a/cropper/src/main/res/drawable-xxxhdpi/crop_image_menu_flip.png b/cropper/src/main/res/drawable-xxxhdpi/crop_image_menu_flip.png new file mode 100644 index 00000000..4200cb86 Binary files /dev/null and b/cropper/src/main/res/drawable-xxxhdpi/crop_image_menu_flip.png differ diff --git a/cropper/src/main/res/values/attrs.xml b/cropper/src/main/res/values/attrs.xml index d1622b03..5719ac98 100644 --- a/cropper/src/main/res/values/attrs.xml +++ b/cropper/src/main/res/values/attrs.xml @@ -42,6 +42,8 @@ + + \ No newline at end of file diff --git a/sample/src/main/java/com/theartofdev/edmodo/cropper/sample/CropImageViewOptions.java b/sample/src/main/java/com/theartofdev/edmodo/cropper/sample/CropImageViewOptions.java index ae27807a..9f0400af 100644 --- a/sample/src/main/java/com/theartofdev/edmodo/cropper/sample/CropImageViewOptions.java +++ b/sample/src/main/java/com/theartofdev/edmodo/cropper/sample/CropImageViewOptions.java @@ -40,4 +40,8 @@ final class CropImageViewOptions { public boolean showCropOverlay; public boolean showProgressBar; + + public boolean flipHorizontally; + + public boolean flipVertically; } diff --git a/sample/src/main/java/com/theartofdev/edmodo/cropper/sample/MainFragment.java b/sample/src/main/java/com/theartofdev/edmodo/cropper/sample/MainFragment.java index 731fe2b1..41434291 100644 --- a/sample/src/main/java/com/theartofdev/edmodo/cropper/sample/MainFragment.java +++ b/sample/src/main/java/com/theartofdev/edmodo/cropper/sample/MainFragment.java @@ -76,6 +76,8 @@ public void setCropImageViewOptions(CropImageViewOptions options) { mCropImageView.setShowProgressBar(options.showProgressBar); mCropImageView.setAutoZoomEnabled(options.autoZoomEnabled); mCropImageView.setMaxZoom(options.maxZoomLevel); + mCropImageView.setFlippedHorizontally(options.flipHorizontally); + mCropImageView.setFlippedVertically(options.flipVertically); } /** @@ -103,6 +105,8 @@ public void updateCurrentCropViewOptions() { options.showProgressBar = mCropImageView.isShowProgressBar(); options.autoZoomEnabled = mCropImageView.isAutoZoomEnabled(); options.maxZoomLevel = mCropImageView.getMaxZoom(); + options.flipHorizontally = mCropImageView.isFlippedHorizontally(); + options.flipVertically = mCropImageView.isFlippedVertically(); ((MainActivity) getActivity()).setCurrentOptions(options); } @@ -161,6 +165,12 @@ public boolean onOptionsItemSelected(MenuItem item) { } else if (item.getItemId() == R.id.main_action_rotate) { mCropImageView.rotateImage(90); return true; + } else if (item.getItemId() == R.id.main_action_flip_horizontally) { + mCropImageView.flipImageHorizontally(); + return true; + } else if (item.getItemId() == R.id.main_action_flip_vertically) { + mCropImageView.flipImageVertically(); + return true; } return super.onOptionsItemSelected(item); } diff --git a/sample/src/main/res/menu/main.xml b/sample/src/main/res/menu/main.xml index 7698c09d..35e28a81 100644 --- a/sample/src/main/res/menu/main.xml +++ b/sample/src/main/res/menu/main.xml @@ -1,11 +1,25 @@ + xmlns:app="http://schemas.android.com/apk/res-auto"> + + + + + + + CROP Rotate 90 degrees clockwise + Flip + Flip horizontally + Flip vertically Crop View Presets Circular Rectangular