diff options
-rw-r--r-- | api/current.txt | 1 | ||||
-rw-r--r-- | api/system-current.txt | 1 | ||||
-rw-r--r-- | api/test-current.txt | 1 | ||||
-rw-r--r-- | core/jni/android/graphics/BitmapFactory.cpp | 111 | ||||
-rw-r--r-- | graphics/java/android/graphics/BitmapFactory.java | 9 |
5 files changed, 122 insertions, 1 deletions
diff --git a/api/current.txt b/api/current.txt index ec2a0e6479f0..08e1b2db144f 100644 --- a/api/current.txt +++ b/api/current.txt @@ -12525,6 +12525,7 @@ package android.graphics { field public int inTargetDensity; field public byte[] inTempStorage; field public deprecated boolean mCancel; + field public android.graphics.ColorSpace outColorSpace; field public android.graphics.Bitmap.Config outConfig; field public int outHeight; field public java.lang.String outMimeType; diff --git a/api/system-current.txt b/api/system-current.txt index 2890041f13a1..02dca8b327f4 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -13254,6 +13254,7 @@ package android.graphics { field public int inTargetDensity; field public byte[] inTempStorage; field public deprecated boolean mCancel; + field public android.graphics.ColorSpace outColorSpace; field public android.graphics.Bitmap.Config outConfig; field public int outHeight; field public java.lang.String outMimeType; diff --git a/api/test-current.txt b/api/test-current.txt index 69ea62018e99..5d01e37a6e71 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -12575,6 +12575,7 @@ package android.graphics { field public int inTargetDensity; field public byte[] inTempStorage; field public deprecated boolean mCancel; + field public android.graphics.ColorSpace outColorSpace; field public android.graphics.Bitmap.Config outConfig; field public int outHeight; field public java.lang.String outMimeType; diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index e64a57447e6b..3dc1be6a26e3 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -39,6 +39,7 @@ jfieldID gOptions_widthFieldID; jfieldID gOptions_heightFieldID; jfieldID gOptions_mimeFieldID; jfieldID gOptions_outConfigFieldID; +jfieldID gOptions_outColorSpaceFieldID; jfieldID gOptions_mCancelID; jfieldID gOptions_bitmapFieldID; @@ -50,6 +51,20 @@ jmethodID gInsetStruct_constructorMethodID; jclass gBitmapConfig_class; jmethodID gBitmapConfig_nativeToConfigMethodID; +jclass gColorSpace_class; +jmethodID gColorSpace_getMethodID; +jmethodID gColorSpace_matchMethodID; + +jclass gColorSpaceRGB_class; +jmethodID gColorSpaceRGB_constructorMethodID; + +jclass gColorSpace_Named_class; +jfieldID gColorSpace_Named_sRGBFieldID; +jfieldID gColorSpace_Named_LinearExtendedSRGBFieldID; + +jclass gTransferParameters_class; +jmethodID gTransferParameters_constructorMethodID; + using namespace android; jstring encodedFormatToString(JNIEnv* env, SkEncodedImageFormat format) { @@ -228,6 +243,70 @@ static bool needsFineScale(const SkISize fullSize, const SkISize decodedSize, needsFineScale(fullSize.height(), decodedSize.height(), sampleSize); } +static jobject getColorSpace(JNIEnv* env, + sk_sp<SkColorSpace>& decodeColorSpace, SkColorType decodeColorType) { + jobject colorSpace = nullptr; + + // No need to match, we know what the output color space will be + if (decodeColorType == kRGBA_F16_SkColorType) { + jobject linearExtendedSRGB = env->GetStaticObjectField( + gColorSpace_Named_class, gColorSpace_Named_LinearExtendedSRGBFieldID); + colorSpace = env->CallStaticObjectMethod(gColorSpace_class, + gColorSpace_getMethodID, linearExtendedSRGB); + } else { + // Same here, no need to match + if (decodeColorSpace->isSRGB()) { + jobject sRGB = env->GetStaticObjectField( + gColorSpace_Named_class, gColorSpace_Named_sRGBFieldID); + colorSpace = env->CallStaticObjectMethod(gColorSpace_class, + gColorSpace_getMethodID, sRGB); + } else if (decodeColorSpace.get() != nullptr) { + // Try to match against known RGB color spaces using the CIE XYZ D50 + // conversion matrix and numerical transfer function parameters + SkMatrix44 xyzMatrix(SkMatrix44::kUninitialized_Constructor); + LOG_ALWAYS_FATAL_IF(!decodeColorSpace->toXYZD50(&xyzMatrix)); + + SkColorSpaceTransferFn transferParams; + // We can only handle numerical transfer functions at the moment + LOG_ALWAYS_FATAL_IF(!decodeColorSpace->isNumericalTransferFn(&transferParams)); + + jobject params = env->NewObject(gTransferParameters_class, + gTransferParameters_constructorMethodID, + transferParams.fA, transferParams.fB, transferParams.fC, + transferParams.fD, transferParams.fE, transferParams.fF, + transferParams.fG); + + jfloatArray xyzArray = env->NewFloatArray(9); + jfloat xyz[9] = { + xyzMatrix.getFloat(0, 0), + xyzMatrix.getFloat(1, 0), + xyzMatrix.getFloat(2, 0), + xyzMatrix.getFloat(0, 1), + xyzMatrix.getFloat(1, 1), + xyzMatrix.getFloat(2, 1), + xyzMatrix.getFloat(0, 2), + xyzMatrix.getFloat(1, 2), + xyzMatrix.getFloat(2, 2) + }; + env->SetFloatArrayRegion(xyzArray, 0, 9, xyz); + + colorSpace = env->CallStaticObjectMethod(gColorSpace_class, + gColorSpace_matchMethodID, xyzArray, params); + + if (colorSpace == nullptr) { + // We couldn't find an exact match, let's create a new color space + // instance with the 3x3 conversion matrix and transfer function + colorSpace = env->NewObject(gColorSpaceRGB_class, + gColorSpaceRGB_constructorMethodID, + env->NewStringUTF("Unknown"), xyzArray, params); + } + + env->DeleteLocalRef(xyzArray); + } + } + return colorSpace; +} + static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) { // This function takes ownership of the input stream. Since the SkAndroidCodec // will take ownership of the stream, we don't necessarily need to take ownership @@ -263,6 +342,7 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding env->SetIntField(options, gOptions_heightFieldID, -1); env->SetObjectField(options, gOptions_mimeFieldID, 0); env->SetObjectField(options, gOptions_outConfigFieldID, 0); + env->SetObjectField(options, gOptions_outColorSpaceFieldID, 0); jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig); @@ -319,6 +399,7 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding // Set the decode colorType SkColorType decodeColorType = codec->computeOutputColorType(prefColorType); + sk_sp<SkColorSpace> decodeColorSpace = codec->computeOutputColorSpace(decodeColorType); // Set the options and return if the client only wants the size. if (options != NULL) { @@ -345,6 +426,9 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding gBitmapConfig_nativeToConfigMethodID, configID); env->SetObjectField(options, gOptions_outConfigFieldID, config); + env->SetObjectField(options, gOptions_outColorSpaceFieldID, + getColorSpace(env, decodeColorSpace, decodeColorType)); + if (onlyDecodeSize) { return nullptr; } @@ -412,7 +496,7 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding SkAlphaType alphaType = codec->computeOutputAlphaType(requireUnpremultiplied); const SkImageInfo decodeInfo = SkImageInfo::Make(size.width(), size.height(), - decodeColorType, alphaType, codec->computeOutputColorSpace(decodeColorType)); + decodeColorType, alphaType, decodeColorSpace); // For wide gamut images, we will leave the color space on the SkBitmap. Otherwise, // use the default. @@ -725,6 +809,8 @@ int register_android_graphics_BitmapFactory(JNIEnv* env) { gOptions_mimeFieldID = GetFieldIDOrDie(env, options_class, "outMimeType", "Ljava/lang/String;"); gOptions_outConfigFieldID = GetFieldIDOrDie(env, options_class, "outConfig", "Landroid/graphics/Bitmap$Config;"); + gOptions_outColorSpaceFieldID = GetFieldIDOrDie(env, options_class, "outColorSpace", + "Landroid/graphics/ColorSpace;"); gOptions_mCancelID = GetFieldIDOrDie(env, options_class, "mCancel", "Z"); jclass bitmap_class = FindClassOrDie(env, "android/graphics/Bitmap"); @@ -741,6 +827,29 @@ int register_android_graphics_BitmapFactory(JNIEnv* env) { gBitmapConfig_nativeToConfigMethodID = GetStaticMethodIDOrDie(env, gBitmapConfig_class, "nativeToConfig", "(I)Landroid/graphics/Bitmap$Config;"); + gColorSpace_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ColorSpace")); + gColorSpace_getMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class, + "get", "(Landroid/graphics/ColorSpace$Named;)Landroid/graphics/ColorSpace;"); + gColorSpace_matchMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class, "match", + "([FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)Landroid/graphics/ColorSpace;"); + + gColorSpaceRGB_class = MakeGlobalRefOrDie(env, + FindClassOrDie(env, "android/graphics/ColorSpace$Rgb")); + gColorSpaceRGB_constructorMethodID = GetMethodIDOrDie(env, gColorSpaceRGB_class, + "<init>", "(Ljava/lang/String;[FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)V"); + + gColorSpace_Named_class = MakeGlobalRefOrDie(env, + FindClassOrDie(env, "android/graphics/ColorSpace$Named")); + gColorSpace_Named_sRGBFieldID = GetStaticFieldIDOrDie(env, + gColorSpace_Named_class, "SRGB", "Landroid/graphics/ColorSpace$Named;"); + gColorSpace_Named_LinearExtendedSRGBFieldID = GetStaticFieldIDOrDie(env, + gColorSpace_Named_class, "LINEAR_EXTENDED_SRGB", "Landroid/graphics/ColorSpace$Named;"); + + gTransferParameters_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, + "android/graphics/ColorSpace$Rgb$TransferParameters")); + gTransferParameters_constructorMethodID = GetMethodIDOrDie(env, gTransferParameters_class, + "<init>", "(DDDDDDD)V"); + return android::RegisterMethodsOrDie(env, "android/graphics/BitmapFactory", gMethods, NELEM(gMethods)); } diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java index a3c6c6edb3ad..ceedc1fdb360 100644 --- a/graphics/java/android/graphics/BitmapFactory.java +++ b/graphics/java/android/graphics/BitmapFactory.java @@ -361,6 +361,15 @@ public class BitmapFactory { public Bitmap.Config outConfig; /** + * If known, the color space the decoded bitmap will have. Note that the + * output color space is not guaranteed to be the color space the bitmap + * is encoded with. If not known (when the config is + * {@link Bitmap.Config#ALPHA_8} for instance), or there is an error, + * it is set to null. + */ + public ColorSpace outColorSpace; + + /** * Temp storage to use for decoding. Suggest 16K or so. */ public byte[] inTempStorage; |