From e9e48c37f40c58cbdfc68ccf76636b04f1599ced Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Fri, 28 Aug 2020 23:40:09 +0900 Subject: [PATCH] fixed glide bitmap rotation for HEIF / DNG --- .../aves/channel/calls/ImageDecodeTask.java | 25 +++++++---- .../streams/ImageByteStreamHandler.java | 45 ++++++++++++------- .../aves/model/provider/ImageProvider.java | 12 ++--- 3 files changed, 48 insertions(+), 34 deletions(-) diff --git a/android/app/src/main/java/deckers/thibault/aves/channel/calls/ImageDecodeTask.java b/android/app/src/main/java/deckers/thibault/aves/channel/calls/ImageDecodeTask.java index 6e9924c7e..c6e9df3b5 100644 --- a/android/app/src/main/java/deckers/thibault/aves/channel/calls/ImageDecodeTask.java +++ b/android/app/src/main/java/deckers/thibault/aves/channel/calls/ImageDecodeTask.java @@ -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 argMap = (Map) 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 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)) { diff --git a/android/app/src/main/java/deckers/thibault/aves/model/provider/ImageProvider.java b/android/app/src/main/java/deckers/thibault/aves/model/provider/ImageProvider.java index 547f0367d..6a2df9874 100644 --- a/android/app/src/main/java/deckers/thibault/aves/model/provider/ImageProvider.java +++ b/android/app/src/main/java/deckers/thibault/aves/model/provider/ImageProvider.java @@ -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 newFields = new HashMap<>(); newFields.put("width", rotatedWidth); newFields.put("height", rotatedHeight);