Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6da7b0ad authored by Ivan Chiang's avatar Ivan Chiang
Browse files

Fix can't create thumbnail and wrong orientation issue

- When MediaMetadataRetriever can't create the thumbnail of some
  HEIF files, attempt decoding it from ExifInterface.

- ImageDecoder can't create the thumbnail with getThumbnailBytes
  from ExifInterface in some cases. It will occur DecodeException:
  Failed to create image decoder with message 'unimplemented'Input
  contained an error. Attempt to decoding the full image in these
  cases.

- Use orientation from ExifInterface to transform the thumbnail to
  right orientation.

Test: manual
Test: atest ThumbnailUtilsTest
Bug: 130775874
Fix: 130446058
Change-Id: Icd0726ec49fe85651150736199c3caa184fa1a3f
parent 8bfe7fd3
Loading
Loading
Loading
Loading
+26 −3
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.content;

import static android.provider.DocumentsContract.EXTRA_ORIENTATION;

import android.accounts.Account;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -40,6 +42,7 @@ import android.graphics.Bitmap;
import android.graphics.ImageDecoder;
import android.graphics.ImageDecoder.ImageInfo;
import android.graphics.ImageDecoder.Source;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -56,6 +59,7 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.system.Int32Ref;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
@@ -3563,9 +3567,14 @@ public abstract class ContentResolver implements ContentInterface {
        // Convert to Point, since that's what the API is defined as
        final Bundle opts = new Bundle();
        opts.putParcelable(EXTRA_SIZE, Point.convert(size));

        return ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> {
            return content.openTypedAssetFile(uri, "image/*", opts, signal);
        final Int32Ref orientation = new Int32Ref(0);

        Bitmap bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> {
            final AssetFileDescriptor afd = content.openTypedAssetFile(uri, "image/*", opts,
                    signal);
            final Bundle extras = afd.getExtras();
            orientation.value = (extras != null) ? extras.getInt(EXTRA_ORIENTATION, 0) : 0;
            return afd;
        }), (ImageDecoder decoder, ImageInfo info, Source source) -> {
            decoder.setAllocator(allocator);

@@ -3581,6 +3590,20 @@ public abstract class ContentResolver implements ContentInterface {
                decoder.setTargetSampleSize(sample);
            }
        });

        // Transform the bitmap if requested. We use a side-channel to
        // communicate the orientation, since EXIF thumbnails don't contain
        // the rotation flags of the original image.
        if (orientation.value != 0) {
            final int width = bitmap.getWidth();
            final int height = bitmap.getHeight();

            final Matrix m = new Matrix();
            m.setRotate(orientation.value, width / 2, height / 2);
            bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, m, false);
        }

        return bitmap;
    }

    /** {@hide} */
+13 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import android.graphics.Bitmap;
import android.graphics.ImageDecoder;
import android.graphics.Point;
import android.media.ExifInterface;
import android.media.MediaFile;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -1698,6 +1699,18 @@ public final class DocumentsContract {
        } catch (IOException e) {
        }

        // Use ImageDecoder to do full image decode of heif format file
        // will have right orientation. So, we don't need to add orientation
        // information into extras.
        final String mimeType = MediaFile.getMimeTypeForFile(file.getName());
        if (mimeType.equals("image/heif")
                || mimeType.equals("image/heif-sequence")
                || mimeType.equals("image/heic")
                || mimeType.equals("image/heic-sequence")) {
            return new AssetFileDescriptor(pfd, 0 /* startOffset */,
                    AssetFileDescriptor.UNKNOWN_LENGTH, null /* extras */);
        }

        return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH, extras);
    }

+52 −5
Original line number Diff line number Diff line
@@ -244,30 +244,77 @@ public class ThumbnailUtils {

        final Resizer resizer = new Resizer(size, signal);
        final String mimeType = MediaFile.getMimeTypeForFile(file.getName());
        Bitmap bitmap = null;
        ExifInterface exif = null;
        int orientation = 0;

        // get orientation
        if (MediaFile.isExifMimeType(mimeType)) {
            exif = new ExifInterface(file);
            switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0)) {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    orientation = 90;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    orientation = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    orientation = 270;
                    break;
            }
        }

        boolean isHeifFile = false;

        if (mimeType.equals("image/heif")
                || mimeType.equals("image/heif-sequence")
                || mimeType.equals("image/heic")
                || mimeType.equals("image/heic-sequence")) {
            isHeifFile = true;
            try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) {
                retriever.setDataSource(file.getAbsolutePath());
                return retriever.getThumbnailImageAtIndex(-1,
                bitmap = retriever.getThumbnailImageAtIndex(-1,
                        new MediaMetadataRetriever.BitmapParams(), size.getWidth(),
                        size.getWidth() * size.getHeight());
            } catch (RuntimeException e) {
                throw new IOException("Failed to create thumbnail", e);
            }
        } else if (MediaFile.isExifMimeType(mimeType)) {
            final ExifInterface exif = new ExifInterface(file);
        }

        if (bitmap == null && exif != null) {
            final byte[] raw = exif.getThumbnailBytes();
            if (raw != null) {
                return ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer);
                try {
                    bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer);
                } catch (ImageDecoder.DecodeException e) {
                    Log.w(TAG, e);
                }
            }
        }

        // Checkpoint before going deeper
        if (signal != null) signal.throwIfCanceled();

        return ImageDecoder.decodeBitmap(ImageDecoder.createSource(file), resizer);
        if (bitmap == null) {
            bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(file), resizer);
            // Use ImageDecoder to do full image decode of heif format file
            // will have right orientation. Don't rotate the bitmap again.
            if (isHeifFile) {
                return bitmap;
            }
        }

        // Transform the bitmap if the orientation of the image is not 0.
        if (orientation != 0 && bitmap != null) {
            final int width = bitmap.getWidth();
            final int height = bitmap.getHeight();

            final Matrix m = new Matrix();
            m.setRotate(orientation, width / 2, height / 2);
            bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, m, false);
        }

        return bitmap;
    }

    /**