summaryrefslogtreecommitdiffstats
path: root/cropper
diff options
context:
space:
mode:
authorThomas <tschneider.ac@gmail.com>2022-06-05 15:48:58 +0200
committerThomas <tschneider.ac@gmail.com>2022-06-05 15:48:58 +0200
commit07ba1d2219707ca9acdc8022777ad53e5462de62 (patch)
treec77f34303a571cf99882453a743cc040f2b08fc9 /cropper
parented76c97e8bd9273f96c201aff7a71aa9001af7a5 (diff)
Start adding media edition
Diffstat (limited to 'cropper')
-rw-r--r--cropper/build.gradle23
-rw-r--r--cropper/src/main/AndroidManifest.xml3
-rw-r--r--cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapCroppingWorkerTask.java354
-rw-r--r--cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapLoadingWorkerTask.java176
-rw-r--r--cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapUtils.java923
-rw-r--r--cropper/src/main/java/com/theartofdev/edmodo/cropper/CropImage.java1048
-rw-r--r--cropper/src/main/java/com/theartofdev/edmodo/cropper/CropImageActivity.java367
-rw-r--r--cropper/src/main/java/com/theartofdev/edmodo/cropper/CropImageAnimation.java123
-rw-r--r--cropper/src/main/java/com/theartofdev/edmodo/cropper/CropImageOptions.java541
-rw-r--r--cropper/src/main/java/com/theartofdev/edmodo/cropper/CropImageView.java2296
-rw-r--r--cropper/src/main/java/com/theartofdev/edmodo/cropper/CropOverlayView.java1118
-rw-r--r--cropper/src/main/java/com/theartofdev/edmodo/cropper/CropWindowHandler.java405
-rw-r--r--cropper/src/main/java/com/theartofdev/edmodo/cropper/CropWindowMoveHandler.java786
-rw-r--r--cropper/src/main/res/drawable-hdpi/crop_image_menu_flip.pngbin0 -> 262 bytes
-rw-r--r--cropper/src/main/res/drawable-hdpi/crop_image_menu_rotate_left.pngbin0 -> 634 bytes
-rw-r--r--cropper/src/main/res/drawable-hdpi/crop_image_menu_rotate_right.pngbin0 -> 617 bytes
-rw-r--r--cropper/src/main/res/drawable-xhdpi/crop_image_menu_flip.pngbin0 -> 259 bytes
-rw-r--r--cropper/src/main/res/drawable-xhdpi/crop_image_menu_rotate_left.pngbin0 -> 798 bytes
-rw-r--r--cropper/src/main/res/drawable-xhdpi/crop_image_menu_rotate_right.pngbin0 -> 787 bytes
-rw-r--r--cropper/src/main/res/drawable-xxhdpi/crop_image_menu_flip.pngbin0 -> 417 bytes
-rw-r--r--cropper/src/main/res/drawable-xxhdpi/crop_image_menu_rotate_left.pngbin0 -> 1181 bytes
-rw-r--r--cropper/src/main/res/drawable-xxhdpi/crop_image_menu_rotate_right.pngbin0 -> 1165 bytes
-rw-r--r--cropper/src/main/res/drawable-xxxhdpi/crop_image_menu_flip.pngbin0 -> 508 bytes
-rw-r--r--cropper/src/main/res/drawable-xxxhdpi/crop_image_menu_rotate_left.pngbin0 -> 1577 bytes
-rw-r--r--cropper/src/main/res/drawable-xxxhdpi/crop_image_menu_rotate_right.pngbin0 -> 1570 bytes
-rw-r--r--cropper/src/main/res/layout/crop_image_activity.xml5
-rw-r--r--cropper/src/main/res/layout/crop_image_view.xml25
-rw-r--r--cropper/src/main/res/menu/crop_image_menu.xml35
-rw-r--r--cropper/src/main/res/values-ar/strings.xml16
-rw-r--r--cropper/src/main/res/values-cs/strings.xml16
-rw-r--r--cropper/src/main/res/values-de/strings.xml16
-rw-r--r--cropper/src/main/res/values-es-rGT/strings.xml12
-rw-r--r--cropper/src/main/res/values-es/strings.xml12
-rw-r--r--cropper/src/main/res/values-fa/strings.xml11
-rw-r--r--cropper/src/main/res/values-fr/strings.xml12
-rw-r--r--cropper/src/main/res/values-he/strings.xml16
-rw-r--r--cropper/src/main/res/values-hi/strings.xml11
-rw-r--r--cropper/src/main/res/values-id/strings.xml12
-rw-r--r--cropper/src/main/res/values-in/strings.xml12
-rw-r--r--cropper/src/main/res/values-it/strings.xml16
-rw-r--r--cropper/src/main/res/values-ja/strings.xml11
-rw-r--r--cropper/src/main/res/values-ko/strings.xml16
-rw-r--r--cropper/src/main/res/values-ms/strings.xml12
-rw-r--r--cropper/src/main/res/values-nb/strings.xml16
-rw-r--r--cropper/src/main/res/values-nl/strings.xml16
-rw-r--r--cropper/src/main/res/values-pl/strings.xml16
-rw-r--r--cropper/src/main/res/values-pt-rBR/strings.xml14
-rw-r--r--cropper/src/main/res/values-ru-rRU/strings.xml10
-rw-r--r--cropper/src/main/res/values-sv/strings.xml16
-rw-r--r--cropper/src/main/res/values-tr/strings.xml12
-rw-r--r--cropper/src/main/res/values-ur/strings.xml12
-rw-r--r--cropper/src/main/res/values-vi/strings.xml16
-rw-r--r--cropper/src/main/res/values-zh-rCN/strings.xml16
-rw-r--r--cropper/src/main/res/values-zh-rTW/strings.xml16
-rw-r--r--cropper/src/main/res/values-zh/strings.xml16
-rw-r--r--cropper/src/main/res/values/attrs.xml50
-rw-r--r--cropper/src/main/res/values/strings.xml16
57 files changed, 8671 insertions, 0 deletions
diff --git a/cropper/build.gradle b/cropper/build.gradle
new file mode 100644
index 000000000..f603264a2
--- /dev/null
+++ b/cropper/build.gradle
@@ -0,0 +1,23 @@
+apply plugin: 'com.android.library'
+
+android {
+
+ compileSdk 31
+ defaultConfig {
+ minSdkVersion 14
+ versionCode 1
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ lintOptions {
+ abortOnError false
+ }
+}
+
+dependencies {
+ api 'androidx.appcompat:appcompat:1.4.2'
+ implementation "androidx.exifinterface:exifinterface:1.3.3"
+}
+
diff --git a/cropper/src/main/AndroidManifest.xml b/cropper/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..fe8baa84d
--- /dev/null
+++ b/cropper/src/main/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest package="com.theartofdev.edmodo.cropper">
+
+</manifest>
diff --git a/cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapCroppingWorkerTask.java b/cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapCroppingWorkerTask.java
new file mode 100644
index 000000000..54d33df4e
--- /dev/null
+++ b/cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapCroppingWorkerTask.java
@@ -0,0 +1,354 @@
+// "Therefore those skilled at the unorthodox
+// are infinite as heaven and earth,
+// inexhaustible as the great rivers.
+// When they come to an end,
+// they begin again,
+// like the days and months;
+// they die and are reborn,
+// like the four seasons."
+//
+// - Sun Tsu,
+// "The Art of War"
+
+package com.theartofdev.edmodo.cropper;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.AsyncTask;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Task to crop bitmap asynchronously from the UI thread.
+ */
+final class BitmapCroppingWorkerTask
+ extends AsyncTask<Void, Void, BitmapCroppingWorkerTask.Result> {
+
+ // region: Fields and Consts
+
+ /**
+ * Use a WeakReference to ensure the ImageView can be garbage collected
+ */
+ private final WeakReference<CropImageView> mCropImageViewReference;
+
+ /**
+ * the bitmap to crop
+ */
+ private final Bitmap mBitmap;
+
+ /**
+ * The Android URI of the image to load
+ */
+ private final Uri mUri;
+
+ /**
+ * The context of the crop image view widget used for loading of bitmap by Android URI
+ */
+ private final Context mContext;
+
+ /**
+ * Required cropping 4 points (x0,y0,x1,y1,x2,y2,x3,y3)
+ */
+ private final float[] mCropPoints;
+
+ /**
+ * Degrees the image was rotated after loading
+ */
+ private final int mDegreesRotated;
+
+ /**
+ * the original width of the image to be cropped (for image loaded from URI)
+ */
+ private final int mOrgWidth;
+
+ /**
+ * the original height of the image to be cropped (for image loaded from URI)
+ */
+ private final int mOrgHeight;
+
+ /**
+ * is there is fixed aspect ratio for the crop rectangle
+ */
+ private final boolean mFixAspectRatio;
+
+ /**
+ * the X aspect ration of the crop rectangle
+ */
+ private final int mAspectRatioX;
+
+ /**
+ * the Y aspect ration of the crop rectangle
+ */
+ private final int mAspectRatioY;
+
+ /**
+ * required width of the cropping image
+ */
+ private final int mReqWidth;
+
+ /**
+ * required height of the cropping image
+ */
+ private final int mReqHeight;
+
+ /**
+ * is the image flipped horizontally
+ */
+ private final boolean mFlipHorizontally;
+
+ /**
+ * is the image flipped vertically
+ */
+ private final boolean mFlipVertically;
+
+ /**
+ * The option to handle requested width/height
+ */
+ private final CropImageView.RequestSizeOptions mReqSizeOptions;
+
+ /**
+ * the Android Uri to save the cropped image to
+ */
+ private final Uri mSaveUri;
+
+ /**
+ * the compression format to use when writing the image
+ */
+ private final Bitmap.CompressFormat mSaveCompressFormat;
+
+ /**
+ * the quality (if applicable) to use when writing the image (0 - 100)
+ */
+ private final int mSaveCompressQuality;
+ // endregion
+
+ BitmapCroppingWorkerTask(
+ CropImageView cropImageView,
+ Bitmap bitmap,
+ float[] cropPoints,
+ int degreesRotated,
+ boolean fixAspectRatio,
+ int aspectRatioX,
+ int aspectRatioY,
+ int reqWidth,
+ int reqHeight,
+ boolean flipHorizontally,
+ boolean flipVertically,
+ CropImageView.RequestSizeOptions options,
+ Uri saveUri,
+ Bitmap.CompressFormat saveCompressFormat,
+ int saveCompressQuality) {
+
+ mCropImageViewReference = new WeakReference<>(cropImageView);
+ mContext = cropImageView.getContext();
+ mBitmap = bitmap;
+ mCropPoints = cropPoints;
+ mUri = null;
+ mDegreesRotated = degreesRotated;
+ mFixAspectRatio = fixAspectRatio;
+ mAspectRatioX = aspectRatioX;
+ mAspectRatioY = aspectRatioY;
+ mReqWidth = reqWidth;
+ mReqHeight = reqHeight;
+ mFlipHorizontally = flipHorizontally;
+ mFlipVertically = flipVertically;
+ mReqSizeOptions = options;
+ mSaveUri = saveUri;
+ mSaveCompressFormat = saveCompressFormat;
+ mSaveCompressQuality = saveCompressQuality;
+ mOrgWidth = 0;
+ mOrgHeight = 0;
+ }
+
+ BitmapCroppingWorkerTask(
+ CropImageView cropImageView,
+ Uri uri,
+ float[] cropPoints,
+ int degreesRotated,
+ int orgWidth,
+ int orgHeight,
+ boolean fixAspectRatio,
+ int aspectRatioX,
+ int aspectRatioY,
+ int reqWidth,
+ int reqHeight,
+ boolean flipHorizontally,
+ boolean flipVertically,
+ CropImageView.RequestSizeOptions options,
+ Uri saveUri,
+ Bitmap.CompressFormat saveCompressFormat,
+ int saveCompressQuality) {
+
+ mCropImageViewReference = new WeakReference<>(cropImageView);
+ mContext = cropImageView.getContext();
+ mUri = uri;
+ mCropPoints = cropPoints;
+ mDegreesRotated = degreesRotated;
+ mFixAspectRatio = fixAspectRatio;
+ mAspectRatioX = aspectRatioX;
+ mAspectRatioY = aspectRatioY;
+ mOrgWidth = orgWidth;
+ mOrgHeight = orgHeight;
+ mReqWidth = reqWidth;
+ mReqHeight = reqHeight;
+ mFlipHorizontally = flipHorizontally;
+ mFlipVertically = flipVertically;
+ mReqSizeOptions = options;
+ mSaveUri = saveUri;
+ mSaveCompressFormat = saveCompressFormat;
+ mSaveCompressQuality = saveCompressQuality;
+ mBitmap = null;
+ }
+
+ /**
+ * The Android URI that this task is currently loading.
+ */
+ public Uri getUri() {
+ return mUri;
+ }
+
+ /**
+ * Crop image in background.
+ *
+ * @param params ignored
+ * @return the decoded bitmap data
+ */
+ @Override
+ protected BitmapCroppingWorkerTask.Result doInBackground(Void... params) {
+ try {
+ if (!isCancelled()) {
+
+ BitmapUtils.BitmapSampled bitmapSampled;
+ if (mUri != null) {
+ bitmapSampled =
+ BitmapUtils.cropBitmap(
+ mContext,
+ mUri,
+ mCropPoints,
+ mDegreesRotated,
+ mOrgWidth,
+ mOrgHeight,
+ mFixAspectRatio,
+ mAspectRatioX,
+ mAspectRatioY,
+ mReqWidth,
+ mReqHeight,
+ mFlipHorizontally,
+ mFlipVertically);
+ } else if (mBitmap != null) {
+ bitmapSampled =
+ BitmapUtils.cropBitmapObjectHandleOOM(
+ mBitmap,
+ mCropPoints,
+ mDegreesRotated,
+ mFixAspectRatio,
+ mAspectRatioX,
+ mAspectRatioY,
+ mFlipHorizontally,
+ mFlipVertically);
+ } else {
+ return new Result((Bitmap) null, 1);
+ }
+
+ Bitmap bitmap =
+ BitmapUtils.resizeBitmap(bitmapSampled.bitmap, mReqWidth, mReqHeight, mReqSizeOptions);
+
+ if (mSaveUri == null) {
+ return new Result(bitmap, bitmapSampled.sampleSize);
+ } else {
+ BitmapUtils.writeBitmapToUri(
+ mContext, bitmap, mSaveUri, mSaveCompressFormat, mSaveCompressQuality);
+ if (bitmap != null) {
+ bitmap.recycle();
+ }
+ return new Result(mSaveUri, bitmapSampled.sampleSize);
+ }
+ }
+ return null;
+ } catch (Exception e) {
+ return new Result(e, mSaveUri != null);
+ }
+ }
+
+ /**
+ * Once complete, see if ImageView is still around and set bitmap.
+ *
+ * @param result the result of bitmap cropping
+ */
+ @Override
+ protected void onPostExecute(Result result) {
+ if (result != null) {
+ boolean completeCalled = false;
+ if (!isCancelled()) {
+ CropImageView cropImageView = mCropImageViewReference.get();
+ if (cropImageView != null) {
+ completeCalled = true;
+ cropImageView.onImageCroppingAsyncComplete(result);
+ }
+ }
+ if (!completeCalled && result.bitmap != null) {
+ // fast release of unused bitmap
+ result.bitmap.recycle();
+ }
+ }
+ }
+
+ // region: Inner class: Result
+
+ /**
+ * The result of BitmapCroppingWorkerTask async loading.
+ */
+ static final class Result {
+
+ /**
+ * The cropped bitmap
+ */
+ public final Bitmap bitmap;
+
+ /**
+ * The saved cropped bitmap uri
+ */
+ public final Uri uri;
+
+ /**
+ * The error that occurred during async bitmap cropping.
+ */
+ final Exception error;
+
+ /**
+ * is the cropping request was to get a bitmap or to save it to uri
+ */
+ final boolean isSave;
+
+ /**
+ * sample size used creating the crop bitmap to lower its size
+ */
+ final int sampleSize;
+
+ Result(Bitmap bitmap, int sampleSize) {
+ this.bitmap = bitmap;
+ this.uri = null;
+ this.error = null;
+ this.isSave = false;
+ this.sampleSize = sampleSize;
+ }
+
+ Result(Uri uri, int sampleSize) {
+ this.bitmap = null;
+ this.uri = uri;
+ this.error = null;
+ this.isSave = true;
+ this.sampleSize = sampleSize;
+ }
+
+ Result(Exception error, boolean isSave) {
+ this.bitmap = null;
+ this.uri = null;
+ this.error = error;
+ this.isSave = isSave;
+ this.sampleSize = 1;
+ }
+ }
+ // endregion
+}
diff --git a/cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapLoadingWorkerTask.java b/cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapLoadingWorkerTask.java
new file mode 100644
index 000000000..a6ecf9396
--- /dev/null
+++ b/cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapLoadingWorkerTask.java
@@ -0,0 +1,176 @@
+// "Therefore those skilled at the unorthodox
+// are infinite as heaven and earth,
+// inexhaustible as the great rivers.
+// When they come to an end,
+// they begin again,
+// like the days and months;
+// they die and are reborn,
+// like the four seasons."
+//
+// - Sun Tsu,
+// "The Art of War"
+
+package com.theartofdev.edmodo.cropper;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.util.DisplayMetrics;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Task to load bitmap asynchronously from the UI thread.
+ */
+final class BitmapLoadingWorkerTask extends AsyncTask<Void, Void, BitmapLoadingWorkerTask.Result> {
+
+ // region: Fields and Consts
+
+ /**
+ * Use a WeakReference to ensure the ImageView can be garbage collected
+ */
+ private final WeakReference<CropImageView> mCropImageViewReference;
+
+ /**
+ * The Android URI of the image to load
+ */
+ private final Uri mUri;
+
+ /**
+ * The context of the crop image view widget used for loading of bitmap by Android URI
+ */
+ private final Context mContext;
+
+ /**
+ * required width of the cropping image after density adjustment
+ */
+ private final int mWidth;
+
+ /**
+ * required height of the cropping image after density adjustment
+ */
+ private final int mHeight;
+ // endregion
+
+ public BitmapLoadingWorkerTask(CropImageView cropImageView, Uri uri) {
+ mUri = uri;
+ mCropImageViewReference = new WeakReference<>(cropImageView);
+
+ mContext = cropImageView.getContext();
+
+ DisplayMetrics metrics = cropImageView.getResources().getDisplayMetrics();
+ double densityAdj = metrics.density > 1 ? 1 / metrics.density : 1;
+ mWidth = (int) (metrics.widthPixels * densityAdj);
+ mHeight = (int) (metrics.heightPixels * densityAdj);
+ }
+
+ /**
+ * The Android URI that this task is currently loading.
+ */
+ public Uri getUri() {
+ return mUri;
+ }
+
+ /**
+ * Decode image in background.
+ *
+ * @param params ignored
+ * @return the decoded bitmap data
+ */
+ @Override
+ protected Result doInBackground(Void... params) {
+ try {
+ if (!isCancelled()) {
+
+ BitmapUtils.BitmapSampled decodeResult =
+ BitmapUtils.decodeSampledBitmap(mContext, mUri, mWidth, mHeight);
+
+ if (!isCancelled()) {
+
+ BitmapUtils.RotateBitmapResult rotateResult =
+ BitmapUtils.rotateBitmapByExif(decodeResult.bitmap, mContext, mUri);
+
+ return new Result(
+ mUri, rotateResult.bitmap, decodeResult.sampleSize, rotateResult.degrees);
+ }
+ }
+ return null;
+ } catch (Exception e) {
+ return new Result(mUri, e);
+ }
+ }
+
+ /**
+ * Once complete, see if ImageView is still around and set bitmap.
+ *
+ * @param result the result of bitmap loading
+ */
+ @Override
+ protected void onPostExecute(Result result) {
+ if (result != null) {
+ boolean completeCalled = false;
+ if (!isCancelled()) {
+ CropImageView cropImageView = mCropImageViewReference.get();
+ if (cropImageView != null) {
+ completeCalled = true;
+ cropImageView.onSetImageUriAsyncComplete(result);
+ }
+ }
+ if (!completeCalled && result.bitmap != null) {
+ // fast release of unused bitmap
+ result.bitmap.recycle();
+ }
+ }
+ }
+
+ // region: Inner class: Result
+
+ /**
+ * The result of BitmapLoadingWorkerTask async loading.
+ */
+ public static final class Result {
+
+ /**
+ * The Android URI of the image to load
+ */
+ public final Uri uri;
+
+ /**
+ * The loaded bitmap
+ */
+ public final Bitmap bitmap;
+
+ /**
+ * The sample size used to load the given bitmap
+ */
+ public final int loadSampleSize;
+
+ /**
+ * The degrees the image was rotated
+ */
+ public final int degreesRotated;
+
+ /**
+ * The error that occurred during async bitmap loading.
+ */
+ public final Exception error;
+
+ Result(Uri uri, Bitmap bitmap, int loadSampleSize, int degreesRotated) {
+ this.uri = uri;
+ this.bitmap = bitmap;
+ this.loadSampleSize = loadSampleSize;
+ this.degreesRotated = degreesRotated;
+ this.error = null;
+ }
+
+ Result(Uri uri, Exception error) {
+ this.uri = uri;
+ this.bitmap = null;
+ this.loadSampleSize = 0;
+ this.degreesRotated = 0;
+ this.error = error;
+ }
+ }
+ // endregion
+}
diff --git a/cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapUtils.java b/cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapUtils.java
new file mode 100644
index 000000000..7190bd429
--- /dev/null
+++ b/cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapUtils.java
@@ -0,0 +1,923 @@
+// "Therefore those skilled at the unorthodox
+// are infinite as heaven and earth,
+// inexhaustible as the great rivers.
+// When they come to an end,
+// they begin again,
+// like the days and months;
+// they die and are reborn,
+// like the four seasons."
+//
+// - Sun Tsu,
+// "The Art of War"
+
+package com.theartofdev.edmodo.cropper;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapRegionDecoder;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.net.Uri;
+import android.util.Log;
+import android.util.Pair;
+
+import androidx.exifinterface.media.ExifInterface;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.ref.WeakReference;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+
+/**
+ * Utility class that deals with operations with an ImageView.
+ */
+final class BitmapUtils {
+
+ static final Rect EMPTY_RECT = new Rect();
+
+ static final RectF EMPTY_RECT_F = new RectF();
+
+ /**
+ * Reusable rectangle for general internal usage
+ */
+ static final RectF RECT = new RectF();
+
+ /**
+ * Reusable point for general internal usage
+ */
+ static final float[] POINTS = new flo