Add Bitmap.setColorSpace()

Bug: 120904891
Test: CtsGraphicsTestCases
Change-Id: I30be2cd77d810210ad87b281e6bb5587863982c3
diff --git a/api/test-current.txt b/api/test-current.txt
index 049e002..a883f0d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -516,6 +516,7 @@
 
   public final class Bitmap implements android.os.Parcelable {
     method public void eraseColor(@ColorLong long);
+    method public void setColorSpace(@NonNull android.graphics.ColorSpace);
   }
 
   public final class ImageDecoder implements java.lang.AutoCloseable {
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 5de0883..c74797b 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -93,6 +93,11 @@
         mBitmap->setAlphaType(alphaType);
     }
 
+    void setColorSpace(sk_sp<SkColorSpace> colorSpace) {
+        assertValid();
+        mBitmap->setColorSpace(colorSpace);
+    }
+
     const SkImageInfo& info() {
         if (mBitmap) {
             return mBitmap->info();
@@ -959,6 +964,12 @@
     return JNI_TRUE;
 }
 
+static void Bitmap_setColorSpace(JNIEnv* env, jobject, jlong bitmapHandle, jlong colorSpacePtr) {
+    LocalScopedBitmap bitmapHolder(bitmapHandle);
+    sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpacePtr);
+    bitmapHolder->setColorSpace(cs);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle,
@@ -1239,6 +1250,7 @@
     {   "nativeCreateGraphicBufferHandle", "(J)Landroid/graphics/GraphicBuffer;",
         (void*) Bitmap_createGraphicBufferHandle },
     {   "nativeGetColorSpace",      "(J[F[F)Z", (void*)Bitmap_getColorSpace },
+    {   "nativeSetColorSpace",      "(JJ)V", (void*)Bitmap_setColorSpace },
     {   "nativeIsSRGB",             "(J)Z", (void*)Bitmap_isSRGB },
     {   "nativeIsSRGBLinear",       "(J)Z", (void*)Bitmap_isSRGBLinear},
     {   "nativeCopyColorSpace",     "(JJ)V",
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 8636949..f0e2361 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -1751,6 +1751,50 @@
     }
 
     /**
+     * <p>Modifies the bitmap to have the specified {@link ColorSpace}, without
+     * affecting the underlying allocation backing the bitmap.</p>
+     *
+     * @throws IllegalArgumentException If the specified color space is {@code null}, not
+     *         {@link ColorSpace.Model#RGB RGB}, has a transfer function that is not an
+     *         {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}, or whose
+     *         components min/max values reduce the numerical range compared to the
+     *         previously assigned color space.
+     *
+     * @param colorSpace to assign to the bitmap
+     * @hide
+     */
+    @TestApi
+    public void setColorSpace(@NonNull ColorSpace colorSpace) {
+        checkRecycled("setColorSpace called on a recycled bitmap");
+        if (colorSpace == null) {
+            throw new IllegalArgumentException("The colorSpace cannot be set to null");
+        }
+        if (getColorSpace() != null) {
+            if (mColorSpace.getComponentCount() != colorSpace.getComponentCount()) {
+                throw new IllegalArgumentException("The new ColorSpace must have the same "
+                        + "component count as the current ColorSpace");
+            }
+            for (int i = 0; i < mColorSpace.getComponentCount(); i++) {
+                if (mColorSpace.getMinValue(i) < colorSpace.getMinValue(i)) {
+                    throw new IllegalArgumentException("The new ColorSpace cannot increase the "
+                            + "minimum value for any of the components compared to the current "
+                            + "ColorSpace. To perform this type of conversion create a new Bitmap "
+                            + "in the desired ColorSpace and draw this Bitmap into it.");
+                }
+                if (mColorSpace.getMaxValue(i) > colorSpace.getMaxValue(i)) {
+                    throw new IllegalArgumentException("The new ColorSpace cannot decrease the "
+                            + "maximum value for any of the components compared to the current "
+                            + "ColorSpace/ To perform this type of conversion create a new Bitmap"
+                            + "in the desired ColorSpace and draw this Bitmap into it.");
+                }
+            }
+        }
+
+        nativeSetColorSpace(mNativePtr, colorSpace.getNativeInstance());
+        mColorSpace = colorSpace;
+    }
+
+    /**
      * Fills the bitmap's pixels with the specified {@link Color}.
      *
      * @throws IllegalStateException if the bitmap is not mutable.
@@ -2174,6 +2218,7 @@
                                                                 long nativeColorSpace);
     private static native GraphicBuffer nativeCreateGraphicBufferHandle(long nativeBitmap);
     private static native boolean nativeGetColorSpace(long nativePtr, float[] xyz, float[] params);
+    private static native void nativeSetColorSpace(long nativePtr, long nativeColorSpace);
     private static native boolean nativeIsSRGB(long nativePtr);
     private static native boolean nativeIsSRGBLinear(long nativePtr);
     private static native void nativeCopyColorSpace(long srcBitmap, long dstBitmap);