fixed glide bitmap rotation for HEIF / DNG
This commit is contained in:
parent
4fdcae3909
commit
e9e48c37f4
3 changed files with 48 additions and 34 deletions
|
@ -5,7 +5,6 @@ import android.app.Activity;
|
|||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Matrix;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.provider.MediaStore;
|
||||
|
@ -18,6 +17,7 @@ import androidx.annotation.RequiresApi;
|
|||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.Key;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.load.resource.bitmap.TransformationUtils;
|
||||
import com.bumptech.glide.request.FutureTarget;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import com.bumptech.glide.signature.ObjectKey;
|
||||
|
@ -28,6 +28,7 @@ import java.util.concurrent.ExecutionException;
|
|||
|
||||
import deckers.thibault.aves.decoder.VideoThumbnail;
|
||||
import deckers.thibault.aves.model.AvesImageEntry;
|
||||
import deckers.thibault.aves.utils.MimeTypes;
|
||||
import deckers.thibault.aves.utils.Utils;
|
||||
import io.flutter.plugin.common.MethodChannel;
|
||||
|
||||
|
@ -136,12 +137,7 @@ public class ImageDecodeTask extends AsyncTask<ImageDecodeTask.Params, Void, Ima
|
|||
Bitmap bitmap = MediaStore.Images.Thumbnails.getThumbnail(resolver, contentId, MediaStore.Images.Thumbnails.MINI_KIND, null);
|
||||
// from Android Q, returned thumbnail is already rotated according to EXIF orientation
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q && bitmap != null) {
|
||||
Integer orientationDegrees = entry.orientationDegrees;
|
||||
if (orientationDegrees != null && orientationDegrees != 0) {
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.postRotate(orientationDegrees);
|
||||
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
||||
}
|
||||
bitmap = rotateBitmap(bitmap, entry.orientationDegrees);
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
@ -151,7 +147,6 @@ public class ImageDecodeTask extends AsyncTask<ImageDecodeTask.Params, Void, Ima
|
|||
AvesImageEntry entry = params.entry;
|
||||
int width = params.width;
|
||||
int height = params.height;
|
||||
// Log.d(LOG_TAG, "getThumbnailByGlide width=" + width + ", path=" + entry.path);
|
||||
|
||||
// add signature to ignore cache for images which got modified but kept the same URI
|
||||
Key signature = new ObjectKey("" + entry.dateModifiedSecs + entry.width + entry.orientationDegrees);
|
||||
|
@ -178,12 +173,24 @@ public class ImageDecodeTask extends AsyncTask<ImageDecodeTask.Params, Void, Ima
|
|||
}
|
||||
|
||||
try {
|
||||
return target.get();
|
||||
Bitmap bitmap = target.get();
|
||||
String mimeType = entry.mimeType;
|
||||
if (MimeTypes.DNG.equals(mimeType) || MimeTypes.HEIC.equals(mimeType) || MimeTypes.HEIF.equals(mimeType)) {
|
||||
bitmap = rotateBitmap(bitmap, entry.orientationDegrees);
|
||||
}
|
||||
return bitmap;
|
||||
} finally {
|
||||
Glide.with(activity).clear(target);
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap rotateBitmap(Bitmap bitmap, Integer orientationDegrees) {
|
||||
if (bitmap != null && orientationDegrees != null) {
|
||||
bitmap = TransformationUtils.rotateImage(bitmap, orientationDegrees);
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Result result) {
|
||||
MethodChannel.Result r = result.params.result;
|
||||
|
|
|
@ -3,17 +3,15 @@ package deckers.thibault.aves.channel.streams;
|
|||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.ImageDecoder;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.load.resource.bitmap.TransformationUtils;
|
||||
import com.bumptech.glide.request.FutureTarget;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import com.bumptech.glide.request.target.Target;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
@ -30,6 +28,7 @@ public class ImageByteStreamHandler implements EventChannel.StreamHandler {
|
|||
private Activity activity;
|
||||
private Uri uri;
|
||||
private String mimeType;
|
||||
private int orientationDegrees;
|
||||
private EventChannel.EventSink eventSink;
|
||||
private Handler handler;
|
||||
|
||||
|
@ -40,6 +39,7 @@ public class ImageByteStreamHandler implements EventChannel.StreamHandler {
|
|||
Map<String, Object> argMap = (Map<String, Object>) arguments;
|
||||
this.mimeType = (String) argMap.get("mimeType");
|
||||
this.uri = Uri.parse((String) argMap.get("uri"));
|
||||
this.orientationDegrees = (int) argMap.get("orientationDegrees");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ public class ImageByteStreamHandler implements EventChannel.StreamHandler {
|
|||
.asBitmap()
|
||||
.apply(options)
|
||||
.load(new VideoThumbnail(activity, uri))
|
||||
.submit(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL);
|
||||
.submit();
|
||||
try {
|
||||
Bitmap bitmap = target.get();
|
||||
if (bitmap != null) {
|
||||
|
@ -88,23 +88,34 @@ public class ImageByteStreamHandler implements EventChannel.StreamHandler {
|
|||
}
|
||||
} catch (Exception e) {
|
||||
error("getImage-video-exception", "failed to get image from uri=" + uri, e.getMessage());
|
||||
} finally {
|
||||
Glide.with(activity).clear(target);
|
||||
}
|
||||
Glide.with(activity).clear(target);
|
||||
} else {
|
||||
ContentResolver cr = activity.getContentResolver();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && (MimeTypes.HEIC.equals(mimeType) || MimeTypes.HEIF.equals(mimeType))) {
|
||||
// as of Flutter v1.15.17, Dart Image.memory cannot decode HEIF/HEIC images
|
||||
// so we convert the image using Android native decoder
|
||||
if (MimeTypes.DNG.equals(mimeType) || MimeTypes.HEIC.equals(mimeType) || MimeTypes.HEIF.equals(mimeType)) {
|
||||
// as of Flutter v1.20, Dart Image.memory cannot decode DNG/HEIC/HEIF images
|
||||
// so we convert the image on platform side first
|
||||
FutureTarget<Bitmap> target = Glide.with(activity)
|
||||
.asBitmap()
|
||||
.load(uri)
|
||||
.submit();
|
||||
try {
|
||||
ImageDecoder.Source source = ImageDecoder.createSource(cr, uri);
|
||||
Bitmap bitmap = ImageDecoder.decodeBitmap(source);
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
// we compress the bitmap because Dart Image.memory cannot decode the raw bytes
|
||||
// Bitmap.CompressFormat.PNG is slower than JPEG
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, stream);
|
||||
success(stream.toByteArray());
|
||||
} catch (IOException e) {
|
||||
error("getImage-image-decode-exception", "failed to decode image from uri=" + uri, e.getMessage());
|
||||
Bitmap bitmap = target.get();
|
||||
if (bitmap != null) {
|
||||
bitmap = TransformationUtils.rotateImage(bitmap, orientationDegrees);
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
// we compress the bitmap because Dart Image.memory cannot decode the raw bytes
|
||||
// Bitmap.CompressFormat.PNG is slower than JPEG
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, stream);
|
||||
success(stream.toByteArray());
|
||||
} else {
|
||||
error("getImage-image-decode-null", "failed to get image from uri=" + uri, null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
error("getImage-image-decode-exception", "failed to get image from uri=" + uri, e.getMessage());
|
||||
} finally {
|
||||
Glide.with(activity).clear(target);
|
||||
}
|
||||
} else {
|
||||
try (InputStream is = cr.openInputStream(uri)) {
|
||||
|
|
|
@ -5,7 +5,6 @@ import android.content.Context;
|
|||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Matrix;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
|
@ -14,6 +13,7 @@ import android.util.Log;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.exifinterface.media.ExifInterface;
|
||||
|
||||
import com.bumptech.glide.load.resource.bitmap.TransformationUtils;
|
||||
import com.commonsware.cwac.document.DocumentFileCompat;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
@ -196,11 +196,7 @@ public abstract class ImageProvider {
|
|||
callback.onFailure(new Exception("failed to decode image at path=" + path));
|
||||
return;
|
||||
}
|
||||
Matrix matrix = new Matrix();
|
||||
int originalWidth = originalImage.getWidth();
|
||||
int originalHeight = originalImage.getHeight();
|
||||
matrix.setRotate(clockwise ? 90 : -90, originalWidth >> 1, originalHeight >> 1);
|
||||
Bitmap rotatedImage = Bitmap.createBitmap(originalImage, 0, 0, originalWidth, originalHeight, matrix, true);
|
||||
Bitmap rotatedImage = TransformationUtils.rotateImage(originalImage, clockwise ? 90 : -90);
|
||||
|
||||
try (FileOutputStream fos = new FileOutputStream(editablePath)) {
|
||||
rotatedImage.compress(Bitmap.CompressFormat.PNG, 100, fos);
|
||||
|
@ -213,8 +209,8 @@ public abstract class ImageProvider {
|
|||
}
|
||||
|
||||
// update fields in media store
|
||||
@SuppressWarnings("SuspiciousNameCombination") int rotatedWidth = originalHeight;
|
||||
@SuppressWarnings("SuspiciousNameCombination") int rotatedHeight = originalWidth;
|
||||
@SuppressWarnings("SuspiciousNameCombination") int rotatedWidth = originalImage.getHeight();
|
||||
@SuppressWarnings("SuspiciousNameCombination") int rotatedHeight = originalImage.getWidth();
|
||||
Map<String, Object> newFields = new HashMap<>();
|
||||
newFields.put("width", rotatedWidth);
|
||||
newFields.put("height", rotatedHeight);
|
||||
|
|
Loading…
Reference in a new issue