From 0e443d1633b97766182f6611c63fce2c65d6c822 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Wed, 19 Dec 2018 11:38:35 -0500 Subject: [PATCH] ColorSpaces have native SkColorSpaces Bug: 120904891 Bug: 122844033 Bug: 122514935 Test: CtsGraphicsTestCases Create an SkColorSpace when creating an android.graphics.ColorSpace.Rgb. This allows simplifying getNativeColorSpace, so that each time we need it, we do not need to call back up into Java or reconstruct the SkColorSpace. Funnel all ColorSpace.Rgb constructors into one, so we can set a final mNativePtr in the base constructor. Update TransferParameters to also be final. Use the same TransferParameters as SRGB in EXTENDED_SRGB and DISPLAY_P3. When passing a BitmapFactory.Options to native, call getNativeInstance() (which may throw an Exception) and pass the result to native. Move native ColorSpace code into its own file. Make Paint methods @CriticalNative again, now that they do not need the JNIEnv. Remove unused decodeBitmap. Change-Id: I60adbb060b87ab6f92559f1217bfefc0b9ea72e1 --- core/jni/Android.bp | 5 +- core/jni/AndroidRuntime.cpp | 4 + core/jni/android/graphics/Bitmap.cpp | 38 ++--- core/jni/android/graphics/BitmapFactory.cpp | 39 +++-- core/jni/android/graphics/BitmapFactory.h | 2 - .../android/graphics/BitmapRegionDecoder.cpp | 8 +- core/jni/android/graphics/Graphics.cpp | 86 ----------- core/jni/android/graphics/GraphicsJNI.h | 11 +- core/jni/android/graphics/ImageDecoder.cpp | 6 +- core/jni/android/graphics/Paint.cpp | 63 ++++---- core/jni/android_graphics_ColorSpace.cpp | 83 +++++++++++ graphics/java/android/graphics/Bitmap.java | 60 +++----- .../java/android/graphics/BitmapFactory.java | 35 ++++- .../android/graphics/BitmapRegionDecoder.java | 5 +- .../java/android/graphics/ColorSpace.java | 141 +++++++++++++----- .../java/android/graphics/ImageDecoder.java | 17 +-- graphics/java/android/graphics/Paint.java | 15 +- 17 files changed, 325 insertions(+), 293 deletions(-) create mode 100644 core/jni/android_graphics_ColorSpace.cpp diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 088e13ff92bc1..be127009a21d7 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -61,8 +61,11 @@ cc_library_shared { "android_database_SQLiteConnection.cpp", "android_database_SQLiteGlobal.cpp", "android_database_SQLiteDebug.cpp", + "android_graphics_Canvas.cpp", + "android_graphics_ColorSpace.cpp", "android_graphics_drawable_AnimatedVectorDrawable.cpp", "android_graphics_drawable_VectorDrawable.cpp", + "android_graphics_Picture.cpp", "android_view_DisplayEventReceiver.cpp", "android_view_DisplayListCanvas.cpp", "android_view_TextureLayer.cpp", @@ -117,8 +120,6 @@ cc_library_shared { "android_util_StringBlock.cpp", "android_util_XmlBlock.cpp", "android_util_jar_StrictJarFile.cpp", - "android_graphics_Canvas.cpp", - "android_graphics_Picture.cpp", "android/graphics/AnimatedImageDrawable.cpp", "android/graphics/Bitmap.cpp", "android/graphics/BitmapFactory.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 109222298fe5c..18d9b5aeb1c83 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -131,6 +131,7 @@ extern int register_android_content_res_ApkAssets(JNIEnv* env); extern int register_android_graphics_Canvas(JNIEnv* env); extern int register_android_graphics_CanvasProperty(JNIEnv* env); extern int register_android_graphics_ColorFilter(JNIEnv* env); +extern int register_android_graphics_ColorSpace(JNIEnv* env); extern int register_android_graphics_DrawFilter(JNIEnv* env); extern int register_android_graphics_FontFamily(JNIEnv* env); extern int register_android_graphics_Matrix(JNIEnv* env); @@ -1361,6 +1362,9 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_os_VintfRuntimeInfo), REG_JNI(register_android_nio_utils), REG_JNI(register_android_graphics_Canvas), + // This needs to be before register_android_graphics_Graphics, or the latter + // will not be able to find the jmethodID for ColorSpace.get(). + REG_JNI(register_android_graphics_ColorSpace), REG_JNI(register_android_graphics_Graphics), REG_JNI(register_android_view_DisplayEventReceiver), REG_JNI(register_android_view_RenderNode), diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 876bd4fbfce4a..876b70f3d6ae0 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -334,7 +334,7 @@ static int getPremulBitmapCreateFlags(bool isMutable) { static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, jint offset, jint stride, jint width, jint height, jint configHandle, jboolean isMutable, - jfloatArray xyzD50, jobject transferParameters) { + jlong colorSpacePtr) { SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle); if (NULL != jColors) { size_t n = env->GetArrayLength(jColors); @@ -350,17 +350,8 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, } SkBitmap bitmap; - sk_sp colorSpace; - - if (xyzD50 == nullptr || transferParameters == nullptr) { - colorSpace = SkColorSpace::MakeSRGB(); - } else { - skcms_TransferFunction p = GraphicsJNI::getNativeTransferParameters(env, transferParameters); - skcms_Matrix3x3 xyzMatrix = GraphicsJNI::getNativeXYZMatrix(env, xyzD50); - colorSpace = SkColorSpace::MakeRGB(p, xyzMatrix); - } - - bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType, colorSpace)); + bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType, + GraphicsJNI::getNativeColorSpace(colorSpacePtr))); sk_sp nativeBitmap = Bitmap::allocateHeapBitmap(&bitmap); if (!nativeBitmap) { @@ -582,16 +573,13 @@ static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) { bitmapErase(skBitmap, SkColor4f::FromColor(color), SkColorSpace::MakeSRGB()); } -static void Bitmap_eraseLong(JNIEnv* env, jobject, jlong bitmapHandle, jobject jColorSpace, +static void Bitmap_eraseLong(JNIEnv* env, jobject, jlong bitmapHandle, jlong colorSpaceHandle, jfloat r, jfloat g, jfloat b, jfloat a) { - sk_sp cs = GraphicsJNI::getNativeColorSpace(env, jColorSpace); - if (GraphicsJNI::hasException(env)) { - return; - } - LocalScopedBitmap bitmap(bitmapHandle); SkBitmap skBitmap; bitmap->getSkBitmap(&skBitmap); + + sk_sp cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); SkColor4f color = SkColor4f{r, g, b, a}; bitmapErase(skBitmap, color, cs); } @@ -1141,14 +1129,12 @@ static jobject Bitmap_createHardwareBitmap(JNIEnv* env, jobject, jobject graphic } static jobject Bitmap_wrapHardwareBufferBitmap(JNIEnv* env, jobject, jobject hardwareBuffer, - jfloatArray xyzD50, jobject transferParameters) { - skcms_TransferFunction p = GraphicsJNI::getNativeTransferParameters(env, transferParameters); - skcms_Matrix3x3 xyzMatrix = GraphicsJNI::getNativeXYZMatrix(env, xyzD50); - sk_sp colorSpace = SkColorSpace::MakeRGB(p, xyzMatrix); + jlong colorSpacePtr) { AHardwareBuffer* hwBuf = android_hardware_HardwareBuffer_getNativeHardwareBuffer(env, hardwareBuffer); sp buffer(AHardwareBuffer_to_GraphicBuffer(hwBuf)); - sk_sp bitmap = Bitmap::createFrom(buffer, colorSpace); + sk_sp bitmap = Bitmap::createFrom(buffer, + GraphicsJNI::getNativeColorSpace(colorSpacePtr)); if (!bitmap.get()) { ALOGW("failed to create hardware bitmap from hardware buffer"); return NULL; @@ -1190,7 +1176,7 @@ static void Bitmap_setImmutable(JNIEnv* env, jobject, jlong bitmapHandle) { /////////////////////////////////////////////////////////////////////////////// static const JNINativeMethod gBitmapMethods[] = { - { "nativeCreate", "([IIIIIIZ[FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)Landroid/graphics/Bitmap;", + { "nativeCreate", "([IIIIIIZJ)Landroid/graphics/Bitmap;", (void*)Bitmap_creator }, { "nativeCopy", "(JIZ)Landroid/graphics/Bitmap;", (void*)Bitmap_copy }, @@ -1204,7 +1190,7 @@ static const JNINativeMethod gBitmapMethods[] = { { "nativeCompress", "(JIILjava/io/OutputStream;[B)Z", (void*)Bitmap_compress }, { "nativeErase", "(JI)V", (void*)Bitmap_erase }, - { "nativeErase", "(JLandroid/graphics/ColorSpace;FFFF)V", (void*)Bitmap_eraseLong }, + { "nativeErase", "(JJFFFF)V", (void*)Bitmap_eraseLong }, { "nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes }, { "nativeConfig", "(J)I", (void*)Bitmap_config }, { "nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha }, @@ -1236,7 +1222,7 @@ static const JNINativeMethod gBitmapMethods[] = { (void*)Bitmap_copyPreserveInternalConfig }, { "nativeCreateHardwareBitmap", "(Landroid/graphics/GraphicBuffer;)Landroid/graphics/Bitmap;", (void*) Bitmap_createHardwareBitmap }, - { "nativeWrapHardwareBufferBitmap", "(Landroid/hardware/HardwareBuffer;[FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)Landroid/graphics/Bitmap;", + { "nativeWrapHardwareBufferBitmap", "(Landroid/hardware/HardwareBuffer;J)Landroid/graphics/Bitmap;", (void*) Bitmap_wrapHardwareBufferBitmap }, { "nativeCreateGraphicBufferHandle", "(J)Landroid/graphics/GraphicBuffer;", (void*) Bitmap_createGraphicBufferHandle }, diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 4b0ab5b2d6ce2..7d0d3d8442a1f 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -179,7 +179,7 @@ static bool needsFineScale(const SkISize fullSize, const SkISize decodedSize, } static jobject doDecode(JNIEnv* env, std::unique_ptr stream, - jobject padding, jobject options) { + jobject padding, jobject options, jlong colorSpaceHandle) { // Set default values for the options parameters. int sampleSize = 1; bool onlyDecodeSize = false; @@ -189,7 +189,7 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr stream, float scale = 1.0f; bool requireUnpremultiplied = false; jobject javaBitmap = NULL; - sk_sp prefColorSpace = nullptr; + sk_sp prefColorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); // Update with options supplied by the client. if (options != NULL) { @@ -213,8 +213,6 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr stream, jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig); - jobject jcolorSpace = env->GetObjectField(options, gOptions_colorSpaceFieldID); - prefColorSpace = GraphicsJNI::getNativeColorSpace(env, jcolorSpace); isHardware = GraphicsJNI::isHardwareConfig(env, jconfig); isMutable = env->GetBooleanField(options, gOptions_mutableFieldID); requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID); @@ -515,7 +513,7 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr stream, } static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage, - jobject padding, jobject options) { + jobject padding, jobject options, jlong colorSpaceHandle) { jobject bitmap = NULL; std::unique_ptr stream(CreateJavaInputStreamAdaptor(env, is, storage)); @@ -524,13 +522,13 @@ static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteA std::unique_ptr bufferedStream( SkFrontBufferedStream::Make(std::move(stream), SkCodec::MinBufferedBytesNeeded())); SkASSERT(bufferedStream.get() != NULL); - bitmap = doDecode(env, std::move(bufferedStream), padding, options); + bitmap = doDecode(env, std::move(bufferedStream), padding, options, colorSpaceHandle); } return bitmap; } static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor, - jobject padding, jobject bitmapFactoryOptions) { + jobject padding, jobject bitmapFactoryOptions, jlong colorSpaceHandle) { NPE_CHECK_RETURN_ZERO(env, fileDescriptor); @@ -566,7 +564,8 @@ static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fi // If there is no offset for the file descriptor, we use SkFILEStream directly. if (::lseek(descriptor, 0, SEEK_CUR) == 0) { assert(isSeekable(dupDescriptor)); - return doDecode(env, std::move(fileStream), padding, bitmapFactoryOptions); + return doDecode(env, std::move(fileStream), padding, bitmapFactoryOptions, + colorSpaceHandle); } // Use a buffered stream. Although an SkFILEStream can be rewound, this @@ -575,24 +574,25 @@ static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fi std::unique_ptr stream(SkFrontBufferedStream::Make(std::move(fileStream), SkCodec::MinBufferedBytesNeeded())); - return doDecode(env, std::move(stream), padding, bitmapFactoryOptions); + return doDecode(env, std::move(stream), padding, bitmapFactoryOptions, colorSpaceHandle); } static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset, - jobject padding, jobject options) { + jobject padding, jobject options, jlong colorSpaceHandle) { Asset* asset = reinterpret_cast(native_asset); // since we know we'll be done with the asset when we return, we can // just use a simple wrapper - return doDecode(env, skstd::make_unique(asset), padding, options); + return doDecode(env, skstd::make_unique(asset), padding, options, + colorSpaceHandle); } static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray, - jint offset, jint length, jobject options) { + jint offset, jint length, jobject options, jlong colorSpaceHandle) { AutoJavaByteArray ar(env, byteArray); return doDecode(env, skstd::make_unique(ar.ptr() + offset, length, false), - nullptr, options); + nullptr, options, colorSpaceHandle); } static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) { @@ -600,31 +600,26 @@ static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) { return isSeekable(descriptor) ? JNI_TRUE : JNI_FALSE; } -jobject decodeBitmap(JNIEnv* env, void* data, size_t size) { - return doDecode(env, skstd::make_unique(data, size), - nullptr, nullptr); -} - /////////////////////////////////////////////////////////////////////////////// static const JNINativeMethod gMethods[] = { { "nativeDecodeStream", - "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", + "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;J)Landroid/graphics/Bitmap;", (void*)nativeDecodeStream }, { "nativeDecodeFileDescriptor", - "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", + "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;J)Landroid/graphics/Bitmap;", (void*)nativeDecodeFileDescriptor }, { "nativeDecodeAsset", - "(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", + "(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;J)Landroid/graphics/Bitmap;", (void*)nativeDecodeAsset }, { "nativeDecodeByteArray", - "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", + "([BIILandroid/graphics/BitmapFactory$Options;J)Landroid/graphics/Bitmap;", (void*)nativeDecodeByteArray }, diff --git a/core/jni/android/graphics/BitmapFactory.h b/core/jni/android/graphics/BitmapFactory.h index 1ee49fa0af77a..e37c98dc66ffd 100644 --- a/core/jni/android/graphics/BitmapFactory.h +++ b/core/jni/android/graphics/BitmapFactory.h @@ -28,6 +28,4 @@ extern jmethodID gBitmapConfig_nativeToConfigMethodID; jstring encodedFormatToString(JNIEnv* env, SkEncodedImageFormat format); -jobject decodeBitmap(JNIEnv* env, void* data, size_t size); - #endif // _ANDROID_GRAPHICS_BITMAP_FACTORY_H_ diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp index f831c051182ec..b4ba749b75bfb 100644 --- a/core/jni/android/graphics/BitmapRegionDecoder.cpp +++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp @@ -124,7 +124,7 @@ static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz, * reportSizeToVM not supported */ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint inputX, - jint inputY, jint inputWidth, jint inputHeight, jobject options) { + jint inputY, jint inputWidth, jint inputHeight, jobject options, jlong colorSpaceHandle) { // Set default options. int sampleSize = 1; @@ -132,14 +132,12 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in bool requireUnpremul = false; jobject javaBitmap = NULL; bool isHardware = false; - sk_sp colorSpace = nullptr; + sk_sp colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); // Update the default options with any options supplied by the client. if (NULL != options) { sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); colorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig); - jobject jcolorSpace = env->GetObjectField(options, gOptions_colorSpaceFieldID); - colorSpace = GraphicsJNI::getNativeColorSpace(env, jcolorSpace); isHardware = GraphicsJNI::isHardwareConfig(env, jconfig); requireUnpremul = !env->GetBooleanField(options, gOptions_premultipliedFieldID); javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); @@ -255,7 +253,7 @@ static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) { static const JNINativeMethod gBitmapRegionDecoderMethods[] = { { "nativeDecodeRegion", - "(JIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", + "(JIIIILandroid/graphics/BitmapFactory$Options;J)Landroid/graphics/Bitmap;", (void*)nativeDecodeRegion}, { "nativeGetHeight", "(J)I", (void*)nativeGetHeight}, diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index 9e74b883a298f..6570992b4b23c 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -178,23 +178,11 @@ static jclass gVMRuntime_class; static jmethodID gVMRuntime_newNonMovableArray; static jmethodID gVMRuntime_addressOf; -static jfieldID gTransferParams_aFieldID; -static jfieldID gTransferParams_bFieldID; -static jfieldID gTransferParams_cFieldID; -static jfieldID gTransferParams_dFieldID; -static jfieldID gTransferParams_eFieldID; -static jfieldID gTransferParams_fFieldID; -static jfieldID gTransferParams_gFieldID; - static jclass gColorSpace_class; -static jfieldID gColorSpace_IlluminantD50FieldID; -static jmethodID gColorSpace_adaptMethodID; static jmethodID gColorSpace_getMethodID; static jmethodID gColorSpace_matchMethodID; static jclass gColorSpaceRGB_class; -static jmethodID gColorSpaceRGB_getTransferParametersMethodID; -static jmethodID gColorSpaceRGB_getTransformMethodID; static jmethodID gColorSpaceRGB_constructorMethodID; static jclass gColorSpace_Named_class; @@ -424,63 +412,6 @@ jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region) /////////////////////////////////////////////////////////////////////////////// -skcms_TransferFunction GraphicsJNI::getNativeTransferParameters(JNIEnv* env, jobject transferParams) { - skcms_TransferFunction p; - p.a = (float) env->GetDoubleField(transferParams, gTransferParams_aFieldID); - p.b = (float) env->GetDoubleField(transferParams, gTransferParams_bFieldID); - p.c = (float) env->GetDoubleField(transferParams, gTransferParams_cFieldID); - p.d = (float) env->GetDoubleField(transferParams, gTransferParams_dFieldID); - p.e = (float) env->GetDoubleField(transferParams, gTransferParams_eFieldID); - p.f = (float) env->GetDoubleField(transferParams, gTransferParams_fFieldID); - p.g = (float) env->GetDoubleField(transferParams, gTransferParams_gFieldID); - return p; -} - -skcms_Matrix3x3 GraphicsJNI::getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50) { - skcms_Matrix3x3 xyzMatrix; - jfloat* array = env->GetFloatArrayElements(xyzD50, NULL); - xyzMatrix.vals[0][0] = array[0]; - xyzMatrix.vals[1][0] = array[1]; - xyzMatrix.vals[2][0] = array[2]; - xyzMatrix.vals[0][1] = array[3]; - xyzMatrix.vals[1][1] = array[4]; - xyzMatrix.vals[2][1] = array[5]; - xyzMatrix.vals[0][2] = array[6]; - xyzMatrix.vals[1][2] = array[7]; - xyzMatrix.vals[2][2] = array[8]; - env->ReleaseFloatArrayElements(xyzD50, array, 0); - return xyzMatrix; -} - -sk_sp GraphicsJNI::getNativeColorSpace(JNIEnv* env, jobject colorSpace) { - if (colorSpace == nullptr) return nullptr; - if (!env->IsInstanceOf(colorSpace, gColorSpaceRGB_class)) { - doThrowIAE(env, "The color space must be an RGB color space"); - return nullptr; - } - - jobject transferParams = env->CallObjectMethod(colorSpace, - gColorSpaceRGB_getTransferParametersMethodID); - if (transferParams == nullptr) { - doThrowIAE(env, "The color space must use an ICC parametric transfer function"); - return nullptr; - } - - jfloatArray illuminantD50 = (jfloatArray) env->GetStaticObjectField(gColorSpace_class, - gColorSpace_IlluminantD50FieldID); - jobject colorSpaceD50 = env->CallStaticObjectMethod(gColorSpace_class, - gColorSpace_adaptMethodID, colorSpace, illuminantD50); - - jfloatArray xyzD50 = (jfloatArray) env->CallObjectMethod(colorSpaceD50, - gColorSpaceRGB_getTransformMethodID); - - skcms_Matrix3x3 xyzMatrix = getNativeXYZMatrix(env, xyzD50); - skcms_TransferFunction transferFunction = getNativeTransferParameters(env, transferParams); - - return SkColorSpace::MakeRGB(transferFunction, xyzMatrix); -} - - jobject GraphicsJNI::getColorSpace(JNIEnv* env, sk_sp& decodeColorSpace, SkColorType decodeColorType) { jobject colorSpace = nullptr; @@ -712,20 +643,7 @@ int register_android_graphics_Graphics(JNIEnv* env) "(Ljava/lang/Class;I)Ljava/lang/Object;"); gVMRuntime_addressOf = GetMethodIDOrDie(env, gVMRuntime_class, "addressOf", "(Ljava/lang/Object;)J"); - jclass transfer_params_class = FindClassOrDie(env, "android/graphics/ColorSpace$Rgb$TransferParameters"); - gTransferParams_aFieldID = GetFieldIDOrDie(env, transfer_params_class, "a", "D"); - gTransferParams_bFieldID = GetFieldIDOrDie(env, transfer_params_class, "b", "D"); - gTransferParams_cFieldID = GetFieldIDOrDie(env, transfer_params_class, "c", "D"); - gTransferParams_dFieldID = GetFieldIDOrDie(env, transfer_params_class, "d", "D"); - gTransferParams_eFieldID = GetFieldIDOrDie(env, transfer_params_class, "e", "D"); - gTransferParams_fFieldID = GetFieldIDOrDie(env, transfer_params_class, "f", "D"); - gTransferParams_gFieldID = GetFieldIDOrDie(env, transfer_params_class, "g", "D"); - gColorSpace_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ColorSpace")); - gColorSpace_IlluminantD50FieldID = GetStaticFieldIDOrDie(env, - gColorSpace_class, "ILLUMINANT_D50", "[F"); - gColorSpace_adaptMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class, "adapt", - "(Landroid/graphics/ColorSpace;[F)Landroid/graphics/ColorSpace;"); gColorSpace_getMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class, "get", "(Landroid/graphics/ColorSpace$Named;)Landroid/graphics/ColorSpace;"); gColorSpace_matchMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class, "match", @@ -735,10 +653,6 @@ int register_android_graphics_Graphics(JNIEnv* env) FindClassOrDie(env, "android/graphics/ColorSpace$Rgb")); gColorSpaceRGB_constructorMethodID = GetMethodIDOrDie(env, gColorSpaceRGB_class, "", "(Ljava/lang/String;[FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)V"); - gColorSpaceRGB_getTransferParametersMethodID = GetMethodIDOrDie(env, gColorSpaceRGB_class, - "getTransferParameters", "()Landroid/graphics/ColorSpace$Rgb$TransferParameters;"); - gColorSpaceRGB_getTransformMethodID = GetMethodIDOrDie(env, gColorSpaceRGB_class, - "getTransform", "()[F"); gColorSpace_Named_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ColorSpace$Named")); diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index 699d153874a27..e0ff9b2ff8526 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -100,9 +100,14 @@ public: int srcStride, int x, int y, int width, int height, SkBitmap* dstBitmap); - static skcms_TransferFunction getNativeTransferParameters(JNIEnv* env, jobject transferParams); - static skcms_Matrix3x3 getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50); - static sk_sp getNativeColorSpace(JNIEnv* env, jobject colorSpace); + /** + * Convert the native SkColorSpace retrieved from ColorSpace.Rgb.getNativeInstance(). + * + * This will never throw an Exception. If the ColorSpace is one that Skia cannot + * use, ColorSpace.Rgb.getNativeInstance() would have thrown an Exception. It may, + * however, be nullptr, which may be acceptable. + */ + static sk_sp getNativeColorSpace(jlong colorSpaceHandle); static jobject getColorSpace(JNIEnv* env, sk_sp& decodeColorSpace, SkColorType decodeColorType); diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp index df735ae12feb9..5f126653600e0 100644 --- a/core/jni/android/graphics/ImageDecoder.cpp +++ b/core/jni/android/graphics/ImageDecoder.cpp @@ -202,7 +202,7 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong jint desiredWidth, jint desiredHeight, jobject jsubset, jboolean requireMutable, jint allocator, jboolean requireUnpremul, jboolean preferRamOverQuality, - jboolean asAlphaMask, jobject jcolorSpace) { + jboolean asAlphaMask, jlong colorSpaceHandle) { auto* decoder = reinterpret_cast(nativePtr); SkAndroidCodec* codec = decoder->mCodec.get(); const SkISize desiredSize = SkISize::Make(desiredWidth, desiredHeight); @@ -256,7 +256,7 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong // This is currently the only way to know that we should decode to F16. colorType = codec->computeOutputColorType(colorType); } - sk_sp colorSpace = GraphicsJNI::getNativeColorSpace(env, jcolorSpace); + sk_sp colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); colorSpace = codec->computeOutputColorSpace(colorType, colorSpace); decodeInfo = decodeInfo.makeColorType(colorType).makeColorSpace(colorSpace); @@ -508,7 +508,7 @@ static const JNINativeMethod gImageDecoderMethods[] = { { "nCreate", "([BIILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray }, { "nCreate", "(Ljava/io/InputStream;[BLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream }, { "nCreate", "(Ljava/io/FileDescriptor;Landroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd }, - { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZLandroid/graphics/ColorSpace;)Landroid/graphics/Bitmap;", + { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZJ)Landroid/graphics/Bitmap;", (void*) ImageDecoder_nDecodeBitmap }, { "nGetSampledSize","(JI)Landroid/util/Size;", (void*) ImageDecoder_nGetSampledSize }, { "nGetPadding", "(JLandroid/graphics/Rect;)V", (void*) ImageDecoder_nGetPadding }, diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index e2e3042ee5b9d..7679c5b63274a 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -557,41 +557,6 @@ namespace PaintGlue { return result; } - // FIXME: Make this CriticalNative when we no longer need to use JNIEnv. b/122514935 will allow - // passing the SkColorSpace directly from JNI. - static void setColor(JNIEnv* env, jobject clazz, jlong paintHandle, jobject jColorSpace, - jfloat r, jfloat g, jfloat b, jfloat a) { - sk_sp cs = GraphicsJNI::getNativeColorSpace(env, jColorSpace); - if (GraphicsJNI::hasException(env)) { - return; - } - - SkColor4f color = SkColor4f{r, g, b, a}; - reinterpret_cast(paintHandle)->setColor4f(color, cs.get()); - } - - // FIXME: Make this CriticalNative when we no longer need to use JNIEnv. b/122514935 will allow - // passing the SkColorSpace directly from JNI. - static void setShadowLayer(JNIEnv* env, jobject clazz, jlong paintHandle, jfloat radius, - jfloat dx, jfloat dy, jobject jColorSpace, - jfloat r, jfloat g, jfloat b, jfloat a) { - sk_sp cs = GraphicsJNI::getNativeColorSpace(env, jColorSpace); - if (GraphicsJNI::hasException(env)) { - return; - } - - SkColor4f color = SkColor4f{r, g, b, a}; - - Paint* paint = reinterpret_cast(paintHandle); - if (radius <= 0) { - paint->setLooper(nullptr); - } - else { - SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius); - paint->setLooper(SkBlurDrawLooper::Make(color, cs.get(), sigma, dx, dy)); - } - } - // ------------------ @FastNative --------------------------- static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) { @@ -787,6 +752,13 @@ namespace PaintGlue { obj->setStyle(style); } + static void setColor(jlong paintHandle, jlong colorSpaceHandle, + jfloat r, jfloat g, jfloat b, jfloat a) { + sk_sp cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); + SkColor4f color = SkColor4f{r, g, b, a}; + reinterpret_cast(paintHandle)->setColor4f(color, cs.get()); + } + static void setAlpha(jlong paintHandle, jint a) { reinterpret_cast(paintHandle)->setAlpha(a); } @@ -1034,6 +1006,22 @@ namespace PaintGlue { return SkScalarToFloat(Paint::kStdStrikeThru_Thickness * textSize); } + static void setShadowLayer(jlong paintHandle, jfloat radius, + jfloat dx, jfloat dy, jlong colorSpaceHandle, + jfloat r, jfloat g, jfloat b, jfloat a) { + sk_sp cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); + SkColor4f color = SkColor4f{r, g, b, a}; + + Paint* paint = reinterpret_cast(paintHandle); + if (radius <= 0) { + paint->setLooper(nullptr); + } + else { + SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius); + paint->setLooper(SkBlurDrawLooper::Make(color, cs.get(), sigma, dx, dy)); + } + } + static jboolean hasShadowLayer(jlong paintHandle) { Paint* paint = reinterpret_cast(paintHandle); return paint->getLooper() && paint->getLooper()->asABlurShadow(nullptr); @@ -1082,9 +1070,6 @@ static const JNINativeMethod methods[] = { {"nGetRunAdvance", "(J[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F}, {"nGetOffsetForAdvance", "(J[CIIIIZF)I", (void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I}, - {"nSetColor","(JLandroid/graphics/ColorSpace;FFFF)V", (void*) PaintGlue::setColor}, - {"nSetShadowLayer", "(JFFFLandroid/graphics/ColorSpace;FFFF)V", - (void*)PaintGlue::setShadowLayer}, // --------------- @FastNative ---------------------- @@ -1114,6 +1099,7 @@ static const JNINativeMethod methods[] = { {"nSetDither","(JZ)V", (void*) PaintGlue::setDither}, {"nGetStyle","(J)I", (void*) PaintGlue::getStyle}, {"nSetStyle","(JI)V", (void*) PaintGlue::setStyle}, + {"nSetColor","(JJFFFF)V", (void*) PaintGlue::setColor}, {"nSetAlpha","(JI)V", (void*) PaintGlue::setAlpha}, {"nGetStrokeWidth","(J)F", (void*) PaintGlue::getStrokeWidth}, {"nSetStrokeWidth","(JF)V", (void*) PaintGlue::setStrokeWidth}, @@ -1154,6 +1140,7 @@ static const JNINativeMethod methods[] = { {"nGetUnderlineThickness","(J)F", (void*) PaintGlue::getUnderlineThickness}, {"nGetStrikeThruPosition","(J)F", (void*) PaintGlue::getStrikeThruPosition}, {"nGetStrikeThruThickness","(J)F", (void*) PaintGlue::getStrikeThruThickness}, + {"nSetShadowLayer", "(JFFFJFFFF)V", (void*)PaintGlue::setShadowLayer}, {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer}, {"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement}, }; diff --git a/core/jni/android_graphics_ColorSpace.cpp b/core/jni/android_graphics_ColorSpace.cpp new file mode 100644 index 0000000000000..7516c24476b14 --- /dev/null +++ b/core/jni/android_graphics_ColorSpace.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "jni.h" +#include "GraphicsJNI.h" +#include "core_jni_helpers.h" + +#include "SkColorSpace.h" + +using namespace android; + +static skcms_Matrix3x3 getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50) { + skcms_Matrix3x3 xyzMatrix; + jfloat* array = env->GetFloatArrayElements(xyzD50, NULL); + xyzMatrix.vals[0][0] = array[0]; + xyzMatrix.vals[1][0] = array[1]; + xyzMatrix.vals[2][0] = array[2]; + xyzMatrix.vals[0][1] = array[3]; + xyzMatrix.vals[1][1] = array[4]; + xyzMatrix.vals[2][1] = array[5]; + xyzMatrix.vals[0][2] = array[6]; + xyzMatrix.vals[1][2] = array[7]; + xyzMatrix.vals[2][2] = array[8]; + env->ReleaseFloatArrayElements(xyzD50, array, 0); + return xyzMatrix; +} + +/////////////////////////////////////////////////////////////////////////////// + +sk_sp GraphicsJNI::getNativeColorSpace(jlong colorSpaceHandle) { + if (colorSpaceHandle == 0) return nullptr; + return sk_ref_sp(reinterpret_cast(colorSpaceHandle)); +} + +static void unref_colorSpace(SkColorSpace* cs) { + SkSafeUnref(cs); +} + +static jlong ColorSpace_getNativeFinalizer(JNIEnv*, jobject) { + return static_cast(reinterpret_cast(&unref_colorSpace)); +} + +static jlong ColorSpace_creator(JNIEnv* env, jobject, jfloat a, jfloat b, jfloat c, + jfloat d, jfloat e, jfloat f, jfloat g, jfloatArray xyzD50) { + skcms_TransferFunction p; + p.a = a; + p.b = b; + p.c = c; + p.d = d; + p.e = e; + p.f = f; + p.g = g; + skcms_Matrix3x3 xyzMatrix = getNativeXYZMatrix(env, xyzD50); + + return reinterpret_cast(SkColorSpace::MakeRGB(p, xyzMatrix).release()); +} + +static const JNINativeMethod gColorSpaceRgbMethods[] = { + { "nativeGetNativeFinalizer", "()J", (void*)ColorSpace_getNativeFinalizer }, + { "nativeCreate", "(FFFFFFF[F)J", (void*)ColorSpace_creator } +}; + +namespace android { + +int register_android_graphics_ColorSpace(JNIEnv* env) { + return android::RegisterMethodsOrDie(env, "android/graphics/ColorSpace$Rgb", + gColorSpaceRgbMethods, NELEM(gColorSpaceRgbMethods)); +} + +}; // namespace android diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 30f0bfa68b4a1..e4612b7f08ea7 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -21,7 +21,6 @@ import android.annotation.ColorInt; import android.annotation.ColorLong; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.Size; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.annotation.WorkerThread; @@ -748,22 +747,10 @@ public final class Bitmap implements Parcelable { throw new IllegalArgumentException("usage flags must contain USAGE_GPU_SAMPLED_IMAGE."); } int format = hardwareBuffer.getFormat(); - ColorSpace.Rgb rgb = null; - if (colorSpace != null) { - if (!(colorSpace instanceof ColorSpace.Rgb)) { - throw new IllegalArgumentException("colorSpace must be an RGB color space"); - } - rgb = (ColorSpace.Rgb) colorSpace; - } else { - rgb = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB); + if (colorSpace == null) { + colorSpace = ColorSpace.get(ColorSpace.Named.SRGB); } - ColorSpace.Rgb.TransferParameters parameters = rgb.getTransferParameters(); - if (parameters == null) { - throw new IllegalArgumentException("colorSpace must use an ICC " - + "parametric transfer function"); - } - ColorSpace.Rgb d50 = (ColorSpace.Rgb) ColorSpace.adapt(rgb, ColorSpace.ILLUMINANT_D50); - return nativeWrapHardwareBufferBitmap(hardwareBuffer, d50.getTransform(), parameters); + return nativeWrapHardwareBufferBitmap(hardwareBuffer, colorSpace.getNativeInstance()); } /** @@ -1103,26 +1090,18 @@ public final class Bitmap implements Parcelable { throw new IllegalArgumentException("can't create bitmap without a color space"); } - Bitmap bm; - // nullptr color spaces have a particular meaning in native and are interpreted as sRGB - // (we also avoid the unnecessary extra work of the else branch) - if (config != Config.ARGB_8888 || colorSpace == ColorSpace.get(ColorSpace.Named.SRGB)) { - bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true, null, null); - } else { - if (!(colorSpace instanceof ColorSpace.Rgb)) { - throw new IllegalArgumentException("colorSpace must be an RGB color space"); + if (config != Config.ARGB_8888) { + if (config == Config.RGBA_F16) { + // FIXME: This should be LINEAR_EXTENDED_SRGB, but that would fail a CTS test. See + // b/120960866. SRGB matches the old (incorrect) behavior. + //colorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB); + colorSpace = ColorSpace.get(ColorSpace.Named.SRGB); + } else { + colorSpace = ColorSpace.get(ColorSpace.Named.SRGB); } - ColorSpace.Rgb rgb = (ColorSpace.Rgb) colorSpace; - ColorSpace.Rgb.TransferParameters parameters = rgb.getTransferParameters(); - if (parameters == null) { - throw new IllegalArgumentException("colorSpace must use an ICC " - + "parametric transfer function"); - } - - ColorSpace.Rgb d50 = (ColorSpace.Rgb) ColorSpace.adapt(rgb, ColorSpace.ILLUMINANT_D50); - bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true, - d50.getTransform(), parameters); } + Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true, + colorSpace.getNativeInstance()); if (display != null) { bm.mDensity = display.densityDpi; @@ -1200,8 +1179,9 @@ public final class Bitmap implements Parcelable { if (width <= 0 || height <= 0) { throw new IllegalArgumentException("width and height must be > 0"); } + ColorSpace sRGB = ColorSpace.get(ColorSpace.Named.SRGB); Bitmap bm = nativeCreate(colors, offset, stride, width, height, - config.nativeInt, false, null, null); + config.nativeInt, false, sRGB.getNativeInstance()); if (display != null) { bm.mDensity = display.densityDpi; } @@ -1802,7 +1782,7 @@ public final class Bitmap implements Parcelable { float g = Color.green(c); float b = Color.blue(c); float a = Color.alpha(c); - nativeErase(mNativePtr, cs, r, g, b, a); + nativeErase(mNativePtr, cs.getNativeInstance(), r, g, b, a); } /** @@ -2133,8 +2113,7 @@ public final class Bitmap implements Parcelable { private static native Bitmap nativeCreate(int[] colors, int offset, int stride, int width, int height, int nativeConfig, boolean mutable, - @Nullable @Size(9) float[] xyzD50, - @Nullable ColorSpace.Rgb.TransferParameters p); + long nativeColorSpace); private static native Bitmap nativeCopy(long nativeSrcBitmap, int nativeConfig, boolean isMutable); private static native Bitmap nativeCopyAshmem(long nativeSrcBitmap); @@ -2149,7 +2128,7 @@ public final class Bitmap implements Parcelable { int quality, OutputStream stream, byte[] tempStorage); private static native void nativeErase(long nativeBitmap, int color); - private static native void nativeErase(long nativeBitmap, ColorSpace cs, + private static native void nativeErase(long nativeBitmap, long colorSpacePtr, float r, float g, float b, float a); private static native int nativeRowBytes(long nativeBitmap); private static native int nativeConfig(long nativeBitmap); @@ -2194,8 +2173,7 @@ public final class Bitmap implements Parcelable { private static native Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap); private static native Bitmap nativeCreateHardwareBitmap(GraphicBuffer buffer); private static native Bitmap nativeWrapHardwareBufferBitmap(HardwareBuffer buffer, - @Size(9) float[] xyzD50, - ColorSpace.Rgb.TransferParameters p); + long nativeColorSpace); private static native GraphicBuffer nativeCreateGraphicBufferHandle(long nativeBitmap); private static native boolean nativeGetColorSpace(long nativePtr, float[] xyz, float[] params); private static native boolean nativeIsSRGB(long nativePtr); diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java index 022fbdc9df749..7aff0414106a7 100644 --- a/graphics/java/android/graphics/BitmapFactory.java +++ b/graphics/java/android/graphics/BitmapFactory.java @@ -460,6 +460,21 @@ public class BitmapFactory { } } } + + /** + * Helper for passing SkColorSpace pointer to native. + * + * @throws IllegalArgumentException if the ColorSpace is not Rgb or does + * not have TransferParameters. + */ + static long nativeColorSpace(Options opts) { + if (opts == null || opts.inPreferredColorSpace == null) { + return 0; + } + + return opts.inPreferredColorSpace.getNativeInstance(); + } + } /** @@ -633,7 +648,8 @@ public class BitmapFactory { Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap"); try { - bm = nativeDecodeByteArray(data, offset, length, opts); + bm = nativeDecodeByteArray(data, offset, length, opts, + Options.nativeColorSpace(opts)); if (bm == null && opts != null && opts.inBitmap != null) { throw new IllegalArgumentException("Problem decoding into existing bitmap"); @@ -728,7 +744,7 @@ public class BitmapFactory { try { if (is instanceof AssetManager.AssetInputStream) { final long asset = ((AssetManager.AssetInputStream) is).getNativeAsset(); - bm = nativeDecodeAsset(asset, outPadding, opts); + bm = nativeDecodeAsset(asset, outPadding, opts, Options.nativeColorSpace(opts)); } else { bm = decodeStreamInternal(is, outPadding, opts); } @@ -755,7 +771,8 @@ public class BitmapFactory { byte [] tempStorage = null; if (opts != null) tempStorage = opts.inTempStorage; if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE]; - return nativeDecodeStream(is, tempStorage, outPadding, opts); + return nativeDecodeStream(is, tempStorage, outPadding, opts, + Options.nativeColorSpace(opts)); } /** @@ -798,7 +815,8 @@ public class BitmapFactory { Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeFileDescriptor"); try { if (nativeIsSeekable(fd)) { - bm = nativeDecodeFileDescriptor(fd, outPadding, opts); + bm = nativeDecodeFileDescriptor(fd, outPadding, opts, + Options.nativeColorSpace(opts)); } else { FileInputStream fis = new FileInputStream(fd); try { @@ -835,14 +853,15 @@ public class BitmapFactory { @UnsupportedAppUsage private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage, - Rect padding, Options opts); + Rect padding, Options opts, long colorSpaceHandle); @UnsupportedAppUsage private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd, - Rect padding, Options opts); + Rect padding, Options opts, long colorSpaceHandle); @UnsupportedAppUsage - private static native Bitmap nativeDecodeAsset(long nativeAsset, Rect padding, Options opts); + private static native Bitmap nativeDecodeAsset(long nativeAsset, Rect padding, Options opts, + long colorSpaceHandle); @UnsupportedAppUsage private static native Bitmap nativeDecodeByteArray(byte[] data, int offset, - int length, Options opts); + int length, Options opts, long colorSpaceHandle); private static native boolean nativeIsSeekable(FileDescriptor fd); } diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java index 43282d39ed477..1410423eafac7 100644 --- a/graphics/java/android/graphics/BitmapRegionDecoder.java +++ b/graphics/java/android/graphics/BitmapRegionDecoder.java @@ -195,7 +195,8 @@ public final class BitmapRegionDecoder { || rect.top >= getHeight()) throw new IllegalArgumentException("rectangle is outside the image"); return nativeDecodeRegion(mNativeBitmapRegionDecoder, rect.left, rect.top, - rect.right - rect.left, rect.bottom - rect.top, options); + rect.right - rect.left, rect.bottom - rect.top, options, + BitmapFactory.Options.nativeColorSpace(options)); } } @@ -265,7 +266,7 @@ public final class BitmapRegionDecoder { private static native Bitmap nativeDecodeRegion(long lbm, int start_x, int start_y, int width, int height, - BitmapFactory.Options options); + BitmapFactory.Options options, long colorSpaceHandle); private static native int nativeGetWidth(long lbm); private static native int nativeGetHeight(long lbm); private static native void nativeClean(long lbm); diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java index 9fa70a5ab19c5..4755d45cd434e 100644 --- a/graphics/java/android/graphics/ColorSpace.java +++ b/graphics/java/android/graphics/ColorSpace.java @@ -25,6 +25,8 @@ import android.annotation.Size; import android.annotation.SuppressAutoDoc; import android.util.Pair; +import libcore.util.NativeAllocationRegistry; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -199,6 +201,9 @@ public abstract class ColorSpace { private static final float[] NTSC_1953_PRIMARIES = { 0.67f, 0.33f, 0.21f, 0.71f, 0.14f, 0.08f }; private static final float[] ILLUMINANT_D50_XYZ = { 0.964212f, 1.0f, 0.825188f }; + private static final Rgb.TransferParameters SRGB_TRANSFER_PARAMETERS = + new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4); + // See static initialization block next to #get(Named) private static final ColorSpace[] sNamedColorSpaces = new ColorSpace[Named.values().length]; @@ -1340,6 +1345,26 @@ public abstract class ColorSpace { return colorSpace; } + /** + * Helper method for creating native SkColorSpace. + * + * This essentially calls adapt on a ColorSpace that has not been fully + * created. It also does not fully create the adapted ColorSpace, but + * just returns the transform. + */ + @NonNull @Size(9) + private static float[] adaptToIlluminantD50( + @NonNull @Size(2) float[] origWhitePoint, + @NonNull @Size(9) float[] origTransform) { + float[] desired = ILLUMINANT_D50; + if (compare(origWhitePoint, desired)) return origTransform; + + float[] xyz = xyYToXyz(desired); + float[] adaptationTransform = chromaticAdaptation(Adaptation.BRADFORD.mTransform, + xyYToXyz(origWhitePoint), xyz); + return mul3x3(adaptationTransform, origTransform); + } + /** *

Returns an instance of {@link ColorSpace} whose ID matches the * specified ID.

@@ -1431,7 +1456,7 @@ public abstract class ColorSpace { "sRGB IEC61966-2.1", SRGB_PRIMARIES, ILLUMINANT_D65, - new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4), + SRGB_TRANSFER_PARAMETERS, Named.SRGB.ordinal() ); sNamedColorSpaces[Named.LINEAR_SRGB.ordinal()] = new ColorSpace.Rgb( @@ -1446,9 +1471,11 @@ public abstract class ColorSpace { "scRGB-nl IEC 61966-2-2:2003", SRGB_PRIMARIES, ILLUMINANT_D65, + null, x -> absRcpResponse(x, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4), x -> absResponse(x, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4), -0.799f, 2.399f, + null, // FIXME: Use SRGB_TRANSFER_PARAMETERS Named.EXTENDED_SRGB.ordinal() ); sNamedColorSpaces[Named.LINEAR_EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb( @@ -1485,7 +1512,7 @@ public abstract class ColorSpace { "Display P3", new float[] { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f }, ILLUMINANT_D65, - new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4), + SRGB_TRANSFER_PARAMETERS, Named.DISPLAY_P3.ordinal() ); sNamedColorSpaces[Named.NTSC_1953.ordinal()] = new ColorSpace.Rgb( @@ -1966,6 +1993,15 @@ public abstract class ColorSpace { } } + /** + * Retrieve the native SkColorSpace object for passing to native. + * + * Only valid on ColorSpace.Rgb. + */ + long getNativeInstance() { + throw new IllegalArgumentException("colorSpace must be an RGB color space"); + } + /** * {@usesMathJax} * @@ -2269,7 +2305,22 @@ public abstract class ColorSpace { private final boolean mIsWideGamut; private final boolean mIsSrgb; - @Nullable private TransferParameters mTransferParameters; + @Nullable private final TransferParameters mTransferParameters; + private final long mNativePtr; + + @Override + long getNativeInstance() { + if (mNativePtr == 0) { + // If this object has TransferParameters, it must have a native object. + throw new IllegalArgumentException("ColorSpace must use an ICC " + + "parametric transfer function! used " + this); + } + return mNativePtr; + } + + private static native long nativeGetNativeFinalizer(); + private static native long nativeCreate(float a, float b, float c, float d, + float e, float f, float g, float[] xyz); /** *

Creates a new RGB color space using a 3x3 column-major transform matrix. @@ -2298,8 +2349,8 @@ public abstract class ColorSpace { @NonNull @Size(9) float[] toXYZ, @NonNull DoubleUnaryOperator oetf, @NonNull DoubleUnaryOperator eotf) { - this(name, computePrimaries(toXYZ), computeWhitePoint(toXYZ), - oetf, eotf, 0.0f, 1.0f, MIN_ID); + this(name, computePrimaries(toXYZ), computeWhitePoint(toXYZ), null, + oetf, eotf, 0.0f, 1.0f, null, MIN_ID); } /** @@ -2349,7 +2400,7 @@ public abstract class ColorSpace { @NonNull DoubleUnaryOperator eotf, float min, float max) { - this(name, primaries, whitePoint, oetf, eotf, min, max, MIN_ID); + this(name, primaries, whitePoint, null, oetf, eotf, min, max, null, MIN_ID); } /** @@ -2459,7 +2510,7 @@ public abstract class ColorSpace { @NonNull @Size(min = 2, max = 3) float[] whitePoint, @NonNull TransferParameters function, @IntRange(from = MIN_ID, to = MAX_ID) int id) { - this(name, primaries, whitePoint, + this(name, primaries, whitePoint, null, function.e == 0.0 && function.f == 0.0 ? x -> rcpResponse(x, function.a, function.b, function.c, function.d, function.g) : @@ -2470,8 +2521,7 @@ public abstract class ColorSpace { function.c, function.d, function.g) : x -> response(x, function.a, function.b, function.c, function.d, function.e, function.f, function.g), - 0.0f, 1.0f, id); - mTransferParameters = function; + 0.0f, 1.0f, function, id); } /** @@ -2586,13 +2636,12 @@ public abstract class ColorSpace { float min, float max, @IntRange(from = MIN_ID, to = MAX_ID) int id) { - this(name, primaries, whitePoint, + this(name, primaries, whitePoint, null, gamma == 1.0 ? DoubleUnaryOperator.identity() : x -> Math.pow(x < 0.0 ? 0.0 : x, 1 / gamma), gamma == 1.0 ? DoubleUnaryOperator.identity() : x -> Math.pow(x < 0.0 ? 0.0 : x, gamma), - min, max, id); - mTransferParameters = new TransferParameters(1.0, 0.0, 0.0, 0.0, gamma); + min, max, new TransferParameters(1.0, 0.0, 0.0, 0.0, gamma), id); } /** @@ -2615,10 +2664,13 @@ public abstract class ColorSpace { * @param name Name of the color space, cannot be null, its length must be >= 1 * @param primaries RGB primaries as an array of 6 (xy) or 9 (XYZ) floats * @param whitePoint Reference white as an array of 2 (xy) or 3 (XYZ) floats + * @param transform Computed transform matrix that converts from RGB to XYZ, or + * {@code null} to compute it from {@code primaries} and {@code whitePoint}. * @param oetf Opto-electronic transfer function, cannot be null * @param eotf Electro-optical transfer function, cannot be null * @param min The minimum valid value in this color space's RGB range * @param max The maximum valid value in this color space's RGB range + * @param transferParameters Parameters for the transfer functions * @param id ID of this color space as an integer between {@link #MIN_ID} and {@link #MAX_ID} * * @throws IllegalArgumentException If any of the following conditions is met: @@ -2637,10 +2689,12 @@ public abstract class ColorSpace { @NonNull @Size(min = 1) String name, @NonNull @Size(min = 6, max = 9) float[] primaries, @NonNull @Size(min = 2, max = 3) float[] whitePoint, + @Nullable @Size(9) float[] transform, @NonNull DoubleUnaryOperator oetf, @NonNull DoubleUnaryOperator eotf, float min, float max, + @Nullable TransferParameters transferParameters, @IntRange(from = MIN_ID, to = MAX_ID) int id) { super(name, Model.RGB, id); @@ -2668,7 +2722,15 @@ public abstract class ColorSpace { mWhitePoint = xyWhitePoint(whitePoint); mPrimaries = xyPrimaries(primaries); - mTransform = computeXYZMatrix(mPrimaries, mWhitePoint); + if (transform == null) { + mTransform = computeXYZMatrix(mPrimaries, mWhitePoint); + } else { + if (transform.length != 9) { + throw new IllegalArgumentException("Transform must have 9 entries! Has " + + transform.length); + } + mTransform = transform; + } mInverseTransform = inverse3x3(mTransform); mOetf = oetf; @@ -2681,10 +2743,39 @@ public abstract class ColorSpace { mClampedOetf = oetf.andThen(clamp); mClampedEotf = clamp.andThen(eotf); + mTransferParameters = transferParameters; + // A color space is wide-gamut if its area is >90% of NTSC 1953 and // if it entirely contains the Color space definition in xyY mIsWideGamut = isWideGamut(mPrimaries, min, max); mIsSrgb = isSrgb(mPrimaries, mWhitePoint, oetf, eotf, min, max, id); + + if (mTransferParameters != null) { + if (mWhitePoint == null || mTransform == null) { + throw new IllegalStateException( + "ColorSpace (" + this + ") cannot create native object! mWhitePoint: " + + mWhitePoint + " mTransform: " + mTransform); + } + + // This mimics the old code that was in native. + float[] nativeTransform = adaptToIlluminantD50(mWhitePoint, mTransform); + mNativePtr = nativeCreate((float) mTransferParameters.a, + (float) mTransferParameters.b, + (float) mTransferParameters.c, + (float) mTransferParameters.d, + (float) mTransferParameters.e, + (float) mTransferParameters.f, + (float) mTransferParameters.g, + nativeTransform); + NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativePtr); + } else { + mNativePtr = 0; + } + } + + private static class NoImagePreloadHolder { + public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry( + ColorSpace.Rgb.class.getClassLoader(), nativeGetNativeFinalizer(), 0); } /** @@ -2695,27 +2786,9 @@ public abstract class ColorSpace { private Rgb(Rgb colorSpace, @NonNull @Size(9) float[] transform, @NonNull @Size(min = 2, max = 3) float[] whitePoint) { - super(colorSpace.getName(), Model.RGB, -1); - - mWhitePoint = xyWhitePoint(whitePoint); - mPrimaries = colorSpace.mPrimaries; - - mTransform = transform; - mInverseTransform = inverse3x3(transform); - - mMin = colorSpace.mMin; - mMax = colorSpace.mMax; - - mOetf = colorSpace.mOetf; - mEotf = colorSpace.mEotf; - - mClampedOetf = colorSpace.mClampedOetf; - mClampedEotf = colorSpace.mClampedEotf; - - mIsWideGamut = colorSpace.mIsWideGamut; - mIsSrgb = colorSpace.mIsSrgb; - - mTransferParameters = colorSpace.mTransferParameters; + this(colorSpace.getName(), colorSpace.mPrimaries, whitePoint, transform, + colorSpace.mOetf, colorSpace.mEotf, colorSpace.mMin, colorSpace.mMax, + colorSpace.mTransferParameters, MIN_ID); } /** diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index e3b165ccaf34a..466a5fc2a7708 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -1628,17 +1628,6 @@ public final class ImageDecoder implements AutoCloseable { if (mPostProcessor != null && mUnpremultipliedRequired) { throw new IllegalStateException("Cannot draw to unpremultiplied pixels!"); } - - if (mDesiredColorSpace != null) { - if (!(mDesiredColorSpace instanceof ColorSpace.Rgb)) { - throw new IllegalArgumentException("The target color space must use the " - + "RGB color model - provided: " + mDesiredColorSpace); - } - if (((ColorSpace.Rgb) mDesiredColorSpace).getTransferParameters() == null) { - throw new IllegalArgumentException("The target color space must use an " - + "ICC parametric transfer function - provided: " + mDesiredColorSpace); - } - } } private static void checkSubset(int width, int height, Rect r) { @@ -1655,10 +1644,12 @@ public final class ImageDecoder implements AutoCloseable { @NonNull private Bitmap decodeBitmapInternal() throws IOException { checkState(); + long colorSpacePtr = mDesiredColorSpace == null ? 0 : + mDesiredColorSpace.getNativeInstance(); return nDecodeBitmap(mNativePtr, this, mPostProcessor != null, mDesiredWidth, mDesiredHeight, mCropRect, mMutable, mAllocator, mUnpremultipliedRequired, - mConserveMemory, mDecodeAsAlphaMask, mDesiredColorSpace); + mConserveMemory, mDecodeAsAlphaMask, colorSpacePtr); } private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener, @@ -1946,7 +1937,7 @@ public final class ImageDecoder implements AutoCloseable { @Nullable Rect cropRect, boolean mutable, int allocator, boolean unpremulRequired, boolean conserveMemory, boolean decodeAsAlphaMask, - @Nullable ColorSpace desiredColorSpace) + long desiredColorSpace) throws IOException; private static native Size nGetSampledSize(long nativePtr, int sampleSize); diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 54e1abcaf1b7e..7eee6f4bf37df 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -1017,7 +1017,7 @@ public class Paint { float b = Color.blue(color); float a = Color.alpha(color); - nSetColor(mNativePaint, cs, r, g, b, a); + nSetColor(mNativePaint, cs.getNativeInstance(), r, g, b, a); mColor = color; } @@ -1455,7 +1455,7 @@ public class Paint { float g = Color.green(shadowColor); float b = Color.blue(shadowColor); float a = Color.alpha(shadowColor); - nSetShadowLayer(mNativePaint, radius, dx, dy, cs, r, g, b, a); + nSetShadowLayer(mNativePaint, radius, dx, dy, cs.getNativeInstance(), r, g, b, a); mShadowLayerRadius = radius; mShadowLayerDx = dx; @@ -3003,11 +3003,6 @@ public class Paint { int contextStart, int contextEnd, boolean isRtl, int offset); private static native int nGetOffsetForAdvance(long paintPtr, char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float advance); - private static native void nSetColor(long paintPtr, ColorSpace cs, - float r, float g, float b, float a); - private static native void nSetShadowLayer(long paintPtr, - float radius, float dx, float dy, ColorSpace cs, - float r, float g, float b, float a); // ---------------- @FastNative ------------------------ @@ -3063,7 +3058,8 @@ public class Paint { int mMinikinLocaleListId); @CriticalNative private static native void nSetShadowLayer(long paintPtr, - float radius, float dx, float dy, @ColorInt int color); + float radius, float dx, float dy, long colorSpaceHandle, + float r, float g, float b, float a); @CriticalNative private static native boolean nHasShadowLayer(long paintPtr); @CriticalNative @@ -3111,6 +3107,9 @@ public class Paint { @CriticalNative private static native void nSetFilterBitmap(long paintPtr, boolean filter); @CriticalNative + private static native void nSetColor(long paintPtr, long colorSpaceHandle, + float r, float g, float b, float a); + @CriticalNative private static native void nSetStrikeThruText(long paintPtr, boolean strikeThruText); @CriticalNative private static native boolean nIsElegantTextHeight(long paintPtr);