Add SkBitmap::setColorSpace.

This will be useful for reimplementing PNGCodec in Chrome.

Change-Id: I63ed223a3ff39459310d2330def572b66ec02ace
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/835185
Auto-Submit: John Stiles <johnstiles@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/include/core/SkBitmap.h b/include/core/SkBitmap.h
index 2f1708e..1875c5f 100644
--- a/include/core/SkBitmap.h
+++ b/include/core/SkBitmap.h
@@ -266,6 +266,16 @@
     */
     bool setAlphaType(SkAlphaType alphaType);
 
+    /** Sets the SkColorSpace associated with this SkBitmap.
+
+        The raw pixel data is not altered by this call; no conversion is
+        performed.
+
+        This changes SkColorSpace in SkPixelRef; all bitmaps sharing SkPixelRef
+        are affected.
+    */
+    void setColorSpace(sk_sp<SkColorSpace> colorSpace);
+
     /** Returns pixel address, the base address corresponding to the pixel origin.
 
         @return  pixel address
diff --git a/relnotes/SkBitmap_setColorSpace.md b/relnotes/SkBitmap_setColorSpace.md
new file mode 100644
index 0000000..57686e8
--- /dev/null
+++ b/relnotes/SkBitmap_setColorSpace.md
@@ -0,0 +1,4 @@
+Added `SkBitmap::setColorSpace`. This API allows the colorspace of an existing
+`SkBitmap` to be reinterpreted. The pixel data backing the bitmap will be left
+as-is. The colorspace will be honored when the bitmap is accessed via APIs which
+support colorspace conversion, like `readPixels`.
diff --git a/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp
index 4080b4f..37a05ed 100644
--- a/src/core/SkBitmap.cpp
+++ b/src/core/SkBitmap.cpp
@@ -145,8 +145,6 @@
     return true;
 }
 
-
-
 bool SkBitmap::setAlphaType(SkAlphaType newAlphaType) {
     if (!SkColorTypeValidateAlphaType(this->colorType(), newAlphaType, &newAlphaType)) {
         return false;
@@ -159,6 +157,14 @@
     return true;
 }
 
+void SkBitmap::setColorSpace(sk_sp<SkColorSpace> newColorSpace) {
+    if (this->colorSpace() != newColorSpace.get()) {
+        SkImageInfo newInfo = fPixmap.info().makeColorSpace(std::move(newColorSpace));
+        fPixmap.reset(std::move(newInfo), fPixmap.addr(), fPixmap.rowBytes());
+    }
+    SkDEBUGCODE(this->validate();)
+}
+
 SkIPoint SkBitmap::pixelRefOrigin() const {
     const char* addr = (const char*)fPixmap.addr();
     const char* pix = (const char*)(fPixelRef ? fPixelRef->pixels() : nullptr);
diff --git a/tests/BitmapTest.cpp b/tests/BitmapTest.cpp
index 359e697..520402b 100644
--- a/tests/BitmapTest.cpp
+++ b/tests/BitmapTest.cpp
@@ -174,6 +174,51 @@
     test_peekpixels(reporter);
 }
 
+DEF_TEST(Bitmap_setColorSpace, r) {
+    // Make a 1x1 bitmap holding 50% gray in default colorspace.
+    SkBitmap source;
+    source.allocN32Pixels(1,1);
+    source.eraseColor(SkColorSetARGB(0xFF, 0x80, 0x80, 0x80));
+
+    // Readback should use the normal sRGB colorspace.
+    const SkImageInfo kReadbackInfo = SkImageInfo::Make(/*width=*/1,
+                                                        /*height=*/1,
+                                                        kN32_SkColorType,
+                                                        kOpaque_SkAlphaType,
+                                                        SkColorSpace::MakeSRGB());
+    // Do readback and verify that the color is gray.
+    uint8_t pixelData[4];
+    REPORTER_ASSERT(r, source.readPixels(kReadbackInfo,
+                                         pixelData,
+                                         /*dstRowBytes=*/4,
+                                         /*srcX=*/0,
+                                         /*srcY=*/0));
+    REPORTER_ASSERT(r, pixelData[0] == 0x80);
+    REPORTER_ASSERT(r, pixelData[1] == 0x80);
+    REPORTER_ASSERT(r, pixelData[2] == 0x80);
+
+    // Also check the color with getColor4f, which does not honor colorspaces.
+    uint32_t colorRGBA = source.getColor4f(0, 0).toBytes_RGBA();
+    REPORTER_ASSERT(r, colorRGBA == 0xFF808080, "RGBA=%08X", colorRGBA);
+
+    // Convert the SkBitmap's colorspace to linear.
+    source.setColorSpace(SkColorSpace::MakeSRGBLinear());
+
+    // Readback again and verify that the color is interpreted differently.
+    REPORTER_ASSERT(r, source.readPixels(kReadbackInfo,
+                                         pixelData,
+                                         /*dstRowBytes=*/4,
+                                         /*srcX=*/0,
+                                         /*srcY=*/0));
+    REPORTER_ASSERT(r, pixelData[0] == 0xBC, "R:%02X", pixelData[0]);
+    REPORTER_ASSERT(r, pixelData[1] == 0xBC, "G:%02X", pixelData[1]);
+    REPORTER_ASSERT(r, pixelData[2] == 0xBC, "B:%02X", pixelData[2]);
+
+    // Since getColor4f does not honor colorspaces, this should still contain 50% gray.
+    colorRGBA = source.getColor4f(0, 0).toBytes_RGBA();
+    REPORTER_ASSERT(r, colorRGBA == 0xFF808080, "RGBA=%08X", colorRGBA);
+}
+
 /**
  *  This test checks that getColor works for both swizzles.
  */