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
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 088e13f..be12700 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -61,8 +61,11 @@
         "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 @@
         "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 1092222..18d9b5a 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -131,6 +131,7 @@
 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 @@
     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 876bd4f..876b70f 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -334,7 +334,7 @@
 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 @@
     }
 
     SkBitmap bitmap;
-    sk_sp<SkColorSpace> 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<Bitmap> nativeBitmap = Bitmap::allocateHeapBitmap(&bitmap);
     if (!nativeBitmap) {
@@ -582,16 +573,13 @@
     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<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(env, jColorSpace);
-    if (GraphicsJNI::hasException(env)) {
-        return;
-    }
-
     LocalScopedBitmap bitmap(bitmapHandle);
     SkBitmap skBitmap;
     bitmap->getSkBitmap(&skBitmap);
+
+    sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
     SkColor4f color = SkColor4f{r, g, b, a};
     bitmapErase(skBitmap, color, cs);
 }
@@ -1141,14 +1129,12 @@
 }
 
 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<SkColorSpace> colorSpace = SkColorSpace::MakeRGB(p, xyzMatrix);
+                                               jlong colorSpacePtr) {
     AHardwareBuffer* hwBuf = android_hardware_HardwareBuffer_getNativeHardwareBuffer(env,
         hardwareBuffer);
     sp<GraphicBuffer> buffer(AHardwareBuffer_to_GraphicBuffer(hwBuf));
-    sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, colorSpace);
+    sk_sp<Bitmap> 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 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 @@
     {   "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 @@
         (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 4b0ab5b..7d0d3d8 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -179,7 +179,7 @@
 }
 
 static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> 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 @@
     float scale = 1.0f;
     bool requireUnpremultiplied = false;
     jobject javaBitmap = NULL;
-    sk_sp<SkColorSpace> prefColorSpace = nullptr;
+    sk_sp<SkColorSpace> prefColorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
 
     // Update with options supplied by the client.
     if (options != NULL) {
@@ -213,8 +213,6 @@
 
         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 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<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage));
@@ -524,13 +522,13 @@
         std::unique_ptr<SkStreamRewindable> 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 @@
     // 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 @@
     std::unique_ptr<SkStreamRewindable> 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<Asset*>(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<AssetStreamAdaptor>(asset), padding, options);
+    return doDecode(env, skstd::make_unique<AssetStreamAdaptor>(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<SkMemoryStream>(ar.ptr() + offset, length, false),
-                    nullptr, options);
+                    nullptr, options, colorSpaceHandle);
 }
 
 static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) {
@@ -600,31 +600,26 @@
     return isSeekable(descriptor) ? JNI_TRUE : JNI_FALSE;
 }
 
-jobject decodeBitmap(JNIEnv* env, void* data, size_t size) {
-    return doDecode(env, skstd::make_unique<SkMemoryStream>(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 1ee49fa..e37c98d 100644
--- a/core/jni/android/graphics/BitmapFactory.h
+++ b/core/jni/android/graphics/BitmapFactory.h
@@ -28,6 +28,4 @@
 
 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 f831c05..b4ba749 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -124,7 +124,7 @@
  * 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 @@
     bool requireUnpremul = false;
     jobject javaBitmap = NULL;
     bool isHardware = false;
-    sk_sp<SkColorSpace> colorSpace = nullptr;
+    sk_sp<SkColorSpace> 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 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 9e74b88..6570992 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -178,23 +178,11 @@
 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 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-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<SkColorSpace> 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<SkColorSpace>& decodeColorSpace,
         SkColorType decodeColorType) {
     jobject colorSpace = nullptr;
@@ -712,20 +643,7 @@
                                                      "(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 @@
             FindClassOrDie(env, "android/graphics/ColorSpace$Rgb"));
     gColorSpaceRGB_constructorMethodID = GetMethodIDOrDie(env, gColorSpaceRGB_class,
             "<init>", "(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 699d153..e0ff9b2 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -100,9 +100,14 @@
             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<SkColorSpace> 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<SkColorSpace> getNativeColorSpace(jlong colorSpaceHandle);
 
     static jobject getColorSpace(JNIEnv* env, sk_sp<SkColorSpace>& decodeColorSpace,
             SkColorType decodeColorType);
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index df735ae..5f12665 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -202,7 +202,7 @@
                                           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<ImageDecoder*>(nativePtr);
     SkAndroidCodec* codec = decoder->mCodec.get();
     const SkISize desiredSize = SkISize::Make(desiredWidth, desiredHeight);
@@ -256,7 +256,7 @@
         // This is currently the only way to know that we should decode to F16.
         colorType = codec->computeOutputColorType(colorType);
     }
-    sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(env, jcolorSpace);
+    sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
     colorSpace = codec->computeOutputColorSpace(colorType, colorSpace);
     decodeInfo = decodeInfo.makeColorType(colorType).makeColorSpace(colorSpace);
 
@@ -508,7 +508,7 @@
     { "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 e2e3042..7679c5b 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -557,41 +557,6 @@
         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<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(env, jColorSpace);
-        if (GraphicsJNI::hasException(env)) {
-            return;
-        }
-
-        SkColor4f color = SkColor4f{r, g, b, a};
-        reinterpret_cast<Paint*>(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<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(env, jColorSpace);
-        if (GraphicsJNI::hasException(env)) {
-            return;
-        }
-
-        SkColor4f color = SkColor4f{r, g, b, a};
-
-        Paint* paint = reinterpret_cast<Paint*>(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 @@
         obj->setStyle(style);
     }
 
+    static void setColor(jlong paintHandle, jlong colorSpaceHandle,
+            jfloat r, jfloat g, jfloat b, jfloat a) {
+        sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
+        SkColor4f color = SkColor4f{r, g, b, a};
+        reinterpret_cast<Paint*>(paintHandle)->setColor4f(color, cs.get());
+    }
+
     static void setAlpha(jlong paintHandle, jint a) {
         reinterpret_cast<Paint*>(paintHandle)->setAlpha(a);
     }
@@ -1034,6 +1006,22 @@
         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<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
+        SkColor4f color = SkColor4f{r, g, b, a};
+
+        Paint* paint = reinterpret_cast<Paint*>(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<Paint*>(paintHandle);
         return paint->getLooper() && paint->getLooper()->asABlurShadow(nullptr);
@@ -1082,9 +1070,6 @@
     {"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 @@
     {"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 @@
     {"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 0000000..7516c24
--- /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<SkColorSpace> GraphicsJNI::getNativeColorSpace(jlong colorSpaceHandle) {
+    if (colorSpaceHandle == 0) return nullptr;
+    return sk_ref_sp(reinterpret_cast<SkColorSpace*>(colorSpaceHandle));
+}
+
+static void unref_colorSpace(SkColorSpace* cs) {
+    SkSafeUnref(cs);
+}
+
+static jlong ColorSpace_getNativeFinalizer(JNIEnv*, jobject) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&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<jlong>(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 30f0bfa..e4612b7 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -21,7 +21,6 @@
 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 @@
             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 @@
             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 @@
         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 @@
         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 @@
     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 @@
                                             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 @@
     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 022fbdc..7aff041 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -460,6 +460,21 @@
                 }
             }
         }
+
+        /**
+         *  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 @@
 
         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 @@
         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 @@
         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 @@
         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 @@
 
     @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 43282d3..1410423 100644
--- a/graphics/java/android/graphics/BitmapRegionDecoder.java
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -195,7 +195,8 @@
                     || 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 @@
 
     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 9fa70a5..4755d45 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -25,6 +25,8 @@
 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 @@
     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];
 
@@ -1341,6 +1346,26 @@
     }
 
     /**
+     * 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);
+    }
+
+    /**
      * <p>Returns an instance of {@link ColorSpace} whose ID matches the
      * specified ID.</p>
      *
@@ -1431,7 +1456,7 @@
                 "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 @@
                 "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 @@
                 "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(
@@ -1967,6 +1994,15 @@
     }
 
     /**
+     * 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}
      *
      * <p>An RGB color space is an additive color space using the
@@ -2269,7 +2305,22 @@
         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);
 
         /**
          * <p>Creates a new RGB color space using a 3x3 column-major transform matrix.
@@ -2298,8 +2349,8 @@
                 @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 @@
                 @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 @@
                 @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 @@
                                     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 @@
                 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 @@
          * @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 @@
                 @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 @@
             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 @@
             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 @@
         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 e3b165c..466a5fc 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -1628,17 +1628,6 @@
         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 @@
     @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 @@
             @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 54e1abc..7eee6f4 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1017,7 +1017,7 @@
         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 @@
         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 @@
             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 @@
             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 @@
     @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);