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

Commit fff1a293 authored by Xiao Huang's avatar Xiao Huang
Browse files

Fallback to RGBA_8888 when P010 is not supported

It's possible that a TV does not support P010, which means
the TV is not able to decode 10-bit HEIF to RGBA_1010102.
In this case, falling back to output RGBA_8888 is necessary.

Bug: 276879147
Test: atest BitmapFactory#testDecode10BitHEIF10BitBitmap
      atest ImageDecoderTest#testDecode10BitHeif
Change-Id: I33f7f30cbee0080dc81764c73428dcac3c75ce88
parent 50ab78c2
Loading
Loading
Loading
Loading
+30 −11
Original line number Diff line number Diff line
@@ -42,7 +42,6 @@ import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.net.Uri;
import android.os.Build;
import android.os.SystemProperties;
import android.os.Trace;
import android.system.ErrnoException;
import android.system.Os;
@@ -2069,47 +2068,67 @@ public final class ImageDecoder implements AutoCloseable {
    }

    private static boolean sIsP010SupportedForAV1 = false;
    private static boolean sIsP010SupportedForAV1Initialized = false;
    private static final Object sIsP010SupportedForAV1Lock = new Object();
    private static boolean sIsP010SupportedForHEVC = false;
    private static boolean sIsP010SupportedFlagsInitialized = false;
    private static final Object sIsP010SupportedLock = new Object();

    /**
     * Checks if the device supports decoding 10-bit AV1.
     */
    @SuppressWarnings("AndroidFrameworkCompatChange")  // This is not an app-visible API.
    private static boolean isP010SupportedForAV1() {
        synchronized (sIsP010SupportedForAV1Lock) {
            if (sIsP010SupportedForAV1Initialized) {
        synchronized (sIsP010SupportedLock) {
            if (sIsP010SupportedFlagsInitialized) {
                return sIsP010SupportedForAV1;
            }
            checkP010SupportforAV1HEVC();
            return sIsP010SupportedForAV1;
        }
    }

            sIsP010SupportedForAV1Initialized = true;
            return sIsP010SupportedForAV1 = isP010SupportedforMime("video/av01");
    /**
     * Checks if the device supports decoding 10-bit HEVC.
     * This method is called by JNI.
     */
    @SuppressWarnings("unused")
    private static boolean isP010SupportedForHEVC() {
        synchronized (sIsP010SupportedLock) {
            if (sIsP010SupportedFlagsInitialized) {
                return sIsP010SupportedForHEVC;
            }
            checkP010SupportforAV1HEVC();
            return sIsP010SupportedForHEVC;
        }
    }

    /**
     * Checks if the device supports decoding 10-bit for the given mime type.
     */
    private static boolean isP010SupportedforMime(String mime) {
    private static void checkP010SupportforAV1HEVC() {
        MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
        for (MediaCodecInfo mediaCodecInfo : codecList.getCodecInfos()) {
            if (mediaCodecInfo.isEncoder()) {
                continue;
            }
            for (String mediaType : mediaCodecInfo.getSupportedTypes()) {
                if (mediaType.equalsIgnoreCase(mime)) {
                if (mediaType.equalsIgnoreCase("video/av01")
                        || mediaType.equalsIgnoreCase("video/hevc")) {
                    MediaCodecInfo.CodecCapabilities codecCapabilities =
                        mediaCodecInfo.getCapabilitiesForType(mediaType);
                    for (int i = 0; i < codecCapabilities.colorFormats.length; ++i) {
                        if (codecCapabilities.colorFormats[i]
                            == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010) {
                            return true;
                            if (mediaType.equalsIgnoreCase("video/av01")) {
                                sIsP010SupportedForAV1 = true;
                            } else {
                                sIsP010SupportedForHEVC = true;
                            }
                        }
                    }
                }
            }
        return false;
        }
        sIsP010SupportedFlagsInitialized = true;
    }

    /**
+8 −0
Original line number Diff line number Diff line
@@ -401,6 +401,14 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream,
        decodeColorType = kN32_SkColorType;
    }

    // b/276879147, fallback to RGBA_8888 when decoding HEIF and P010 is not supported.
    if (decodeColorType == kRGBA_1010102_SkColorType &&
        codec->getEncodedFormat() == SkEncodedImageFormat::kHEIF &&
        env->CallStaticBooleanMethod(gImageDecoder_class,
                                     gImageDecoder_isP010SupportedForHEVCMethodID) == JNI_FALSE) {
        decodeColorType = kN32_SkColorType;
    }

    sk_sp<SkColorSpace> decodeColorSpace = codec->computeOutputColorSpace(
            decodeColorType, prefColorSpace);

+5 −1
Original line number Diff line number Diff line
#ifndef _ANDROID_GRAPHICS_BITMAP_FACTORY_H_
#define _ANDROID_GRAPHICS_BITMAP_FACTORY_H_

#include <SkEncodedImageFormat.h>

#include "GraphicsJNI.h"
#include "SkEncodedImageFormat.h"

extern jclass gOptions_class;
extern jfieldID gOptions_justBoundsFieldID;
@@ -26,6 +27,9 @@ extern jfieldID gOptions_bitmapFieldID;
extern jclass gBitmapConfig_class;
extern jmethodID gBitmapConfig_nativeToConfigMethodID;

extern jclass gImageDecoder_class;
extern jmethodID gImageDecoder_isP010SupportedForHEVCMethodID;

jstring getMimeTypeAsJavaString(JNIEnv*, SkEncodedImageFormat);

#endif  // _ANDROID_GRAPHICS_BITMAP_FACTORY_H_
+13 −1
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include <SkCodecAnimation.h>
#include <SkColorSpace.h>
#include <SkColorType.h>
#include <SkEncodedImageFormat.h>
#include <SkImageInfo.h>
#include <SkRect.h>
#include <SkSize.h>
@@ -48,7 +49,8 @@

using namespace android;

static jclass    gImageDecoder_class;
jclass gImageDecoder_class;
jmethodID gImageDecoder_isP010SupportedForHEVCMethodID;
static jclass    gSize_class;
static jclass    gDecodeException_class;
static jclass    gCanvas_class;
@@ -298,6 +300,14 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
        colorType = kN32_SkColorType;
    }

    // b/276879147, fallback to RGBA_8888 when decoding HEIF and P010 is not supported.
    if (colorType == kRGBA_1010102_SkColorType &&
        decoder->mCodec->getEncodedFormat() == SkEncodedImageFormat::kHEIF &&
        env->CallStaticBooleanMethod(gImageDecoder_class,
                                     gImageDecoder_isP010SupportedForHEVCMethodID) == JNI_FALSE) {
        colorType = kN32_SkColorType;
    }

    if (!decoder->setOutColorType(colorType)) {
        doThrowISE(env, "Failed to set out color type!");
        return nullptr;
@@ -540,6 +550,8 @@ int register_android_graphics_ImageDecoder(JNIEnv* env) {
    gImageDecoder_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder"));
    gImageDecoder_constructorMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "<init>", "(JIIZZ)V");
    gImageDecoder_postProcessMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "postProcessAndRelease", "(Landroid/graphics/Canvas;)I");
    gImageDecoder_isP010SupportedForHEVCMethodID =
            GetStaticMethodIDOrDie(env, gImageDecoder_class, "isP010SupportedForHEVC", "()Z");

    gSize_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/util/Size"));
    gSize_constructorMethodID = GetMethodIDOrDie(env, gSize_class, "<init>", "(II)V");