From e8d2ebb5c29efb996179f9a4b62c78e4d3037e14 Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Thu, 9 Feb 2017 18:38:47 -0800 Subject: [PATCH] Report bitmap config when decoding only metadata With the introduction of support for 16 bit images, bitmaps returned by BitmapFactory may be using the RGBA_F16 configuration. Some apps and libraries assume the returned configuration is always ARGB_8888 and make assumptions based on that to compute cache sizes, etc. This changes extract the output config when BitmapFactory is invoked with the inJustDecodeBounds option. Despite its name, this option is already used (and documented!) to decode more than bounds as it also decodes the MIME type. Bug: 35108998 Test: CtsGraphicsTestCases Change-Id: If71959751458816678e42b21ab26c889aba5dea0 --- api/current.txt | 1 + api/system-current.txt | 1 + api/test-current.txt | 1 + core/jni/android/graphics/BitmapFactory.cpp | 33 ++++++++++++++++--- core/jni/android/graphics/Graphics.cpp | 16 +-------- core/jni/android/graphics/GraphicsJNI.h | 15 +++++++++ .../java/android/graphics/BitmapFactory.java | 8 ++++- 7 files changed, 55 insertions(+), 20 deletions(-) diff --git a/api/current.txt b/api/current.txt index 6d9027a31ebab..c4889bc896122 100644 --- a/api/current.txt +++ b/api/current.txt @@ -12242,6 +12242,7 @@ package android.graphics { field public int inTargetDensity; field public byte[] inTempStorage; field public deprecated boolean mCancel; + field public android.graphics.Bitmap.Config outConfig; field public int outHeight; field public java.lang.String outMimeType; field public int outWidth; diff --git a/api/system-current.txt b/api/system-current.txt index 7341424628f86..02470f934f9c8 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -12810,6 +12810,7 @@ package android.graphics { field public int inTargetDensity; field public byte[] inTempStorage; field public deprecated boolean mCancel; + field public android.graphics.Bitmap.Config outConfig; field public int outHeight; field public java.lang.String outMimeType; field public int outWidth; diff --git a/api/test-current.txt b/api/test-current.txt index 4988ab3747a5d..bf801f8b0c8de 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -12276,6 +12276,7 @@ package android.graphics { field public int inTargetDensity; field public byte[] inTempStorage; field public deprecated boolean mCancel; + field public android.graphics.Bitmap.Config outConfig; field public int outHeight; field public java.lang.String outMimeType; field public int outWidth; diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 19d4848e16560..2aa16b281884e 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -39,6 +39,7 @@ jfieldID gOptions_targetDensityFieldID; jfieldID gOptions_widthFieldID; jfieldID gOptions_heightFieldID; jfieldID gOptions_mimeFieldID; +jfieldID gOptions_outConfigFieldID; jfieldID gOptions_mCancelID; jfieldID gOptions_bitmapFieldID; @@ -47,6 +48,9 @@ jfieldID gBitmap_ninePatchInsetsFieldID; jclass gInsetStruct_class; jmethodID gInsetStruct_constructorMethodID; +jclass gBitmapConfig_class; +jmethodID gBitmapConfig_nativeToConfigMethodID; + using namespace android; jstring encodedFormatToString(JNIEnv* env, SkEncodedImageFormat format) { @@ -298,6 +302,7 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding env->SetIntField(options, gOptions_widthFieldID, -1); env->SetIntField(options, gOptions_heightFieldID, -1); env->SetObjectField(options, gOptions_mimeFieldID, 0); + env->SetObjectField(options, gOptions_outConfigFieldID, 0); jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig); @@ -352,6 +357,9 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding scaledHeight = codec->getInfo().height() / sampleSize; } + // Set the decode colorType + SkColorType decodeColorType = codec->computeOutputColorType(prefColorType); + // Set the options and return if the client only wants the size. if (options != NULL) { jstring mimeType = encodedFormatToString( @@ -363,6 +371,20 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding env->SetIntField(options, gOptions_heightFieldID, scaledHeight); env->SetObjectField(options, gOptions_mimeFieldID, mimeType); + SkColorType outColorType = decodeColorType; + // Scaling can affect the output color type + if (willScale || scale != 1.0f) { + outColorType = colorTypeForScaledOutput(outColorType); + } + + jint configID = GraphicsJNI::colorTypeToLegacyBitmapConfig(outColorType); + if (isHardware) { + configID = GraphicsJNI::kHardware_LegacyBitmapConfig; + } + jobject config = env->CallStaticObjectMethod(gBitmapConfig_class, + gBitmapConfig_nativeToConfigMethodID, configID); + env->SetObjectField(options, gOptions_outConfigFieldID, config); + if (onlyDecodeSize) { return nullptr; } @@ -409,10 +431,6 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding decodeAllocator = &defaultAllocator; } - // Set the decode colorType. This is necessary because we can't always support - // the requested colorType. - SkColorType decodeColorType = codec->computeOutputColorType(prefColorType); - // Construct a color table for the decode if necessary sk_sp colorTable(nullptr); SkPMColor* colorPtr = nullptr; @@ -747,6 +765,8 @@ int register_android_graphics_BitmapFactory(JNIEnv* env) { gOptions_widthFieldID = GetFieldIDOrDie(env, options_class, "outWidth", "I"); gOptions_heightFieldID = GetFieldIDOrDie(env, options_class, "outHeight", "I"); gOptions_mimeFieldID = GetFieldIDOrDie(env, options_class, "outMimeType", "Ljava/lang/String;"); + gOptions_outConfigFieldID = GetFieldIDOrDie(env, options_class, "outConfig", + "Landroid/graphics/Bitmap$Config;"); gOptions_mCancelID = GetFieldIDOrDie(env, options_class, "mCancel", "Z"); jclass bitmap_class = FindClassOrDie(env, "android/graphics/Bitmap"); @@ -758,6 +778,11 @@ int register_android_graphics_BitmapFactory(JNIEnv* env) { gInsetStruct_constructorMethodID = GetMethodIDOrDie(env, gInsetStruct_class, "", "(IIIIIIIIFIF)V"); + gBitmapConfig_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, + "android/graphics/Bitmap$Config")); + gBitmapConfig_nativeToConfigMethodID = GetStaticMethodIDOrDie(env, gBitmapConfig_class, + "nativeToConfig", "(I)Landroid/graphics/Bitmap$Config;"); + return android::RegisterMethodsOrDie(env, "android/graphics/BitmapFactory", gMethods, NELEM(gMethods)); } diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index d5f33cf813057..d78e00a1f1dcb 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -287,21 +287,7 @@ void GraphicsJNI::point_to_jpointf(const SkPoint& r, JNIEnv* env, jobject obj) env->SetFloatField(obj, gPointF_yFieldID, SkScalarToFloat(r.fY)); } -// This enum must keep these int values, to match the int values -// in the java Bitmap.Config enum. -enum LegacyBitmapConfig { - kNo_LegacyBitmapConfig = 0, - kA8_LegacyBitmapConfig = 1, - kIndex8_LegacyBitmapConfig = 2, - kRGB_565_LegacyBitmapConfig = 3, - kARGB_4444_LegacyBitmapConfig = 4, - kARGB_8888_LegacyBitmapConfig = 5, - kRGBA_16F_LegacyBitmapConfig = 6, - kHardware_LegacyBitmapConfig = 7, - - kLastEnum_LegacyBitmapConfig = kHardware_LegacyBitmapConfig -}; - +// See enum values in GraphicsJNI.h jint GraphicsJNI::colorTypeToLegacyBitmapConfig(SkColorType colorType) { switch (colorType) { case kRGBA_F16_SkColorType: diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index e899db5592bcf..8a1ef6ee5d340 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -24,6 +24,21 @@ struct Typeface; class GraphicsJNI { public: + // This enum must keep these int values, to match the int values + // in the java Bitmap.Config enum. + enum LegacyBitmapConfig { + kNo_LegacyBitmapConfig = 0, + kA8_LegacyBitmapConfig = 1, + kIndex8_LegacyBitmapConfig = 2, + kRGB_565_LegacyBitmapConfig = 3, + kARGB_4444_LegacyBitmapConfig = 4, + kARGB_8888_LegacyBitmapConfig = 5, + kRGBA_16F_LegacyBitmapConfig = 6, + kHardware_LegacyBitmapConfig = 7, + + kLastEnum_LegacyBitmapConfig = kHardware_LegacyBitmapConfig + }; + // returns true if an exception is set (and dumps it out to the Log) static bool hasException(JNIEnv*); diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java index 64a726babce05..a3c6c6edb3ada 100644 --- a/graphics/java/android/graphics/BitmapFactory.java +++ b/graphics/java/android/graphics/BitmapFactory.java @@ -350,10 +350,16 @@ public class BitmapFactory { /** * If known, this string is set to the mimetype of the decoded image. - * If not know, or there is an error, it is set to null. + * If not known, or there is an error, it is set to null. */ public String outMimeType; + /** + * If known, the config the decoded bitmap will have. + * If not known, or there is an error, it is set to null. + */ + public Bitmap.Config outConfig; + /** * Temp storage to use for decoding. Suggest 16K or so. */