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);