fedilab-Android-App/cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapCroppingWorkerTask.java

355 lines
10 KiB
Java

// "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
}