Begin implementation of SkImage_Base::makeColorSpace

Originally:
https://skia-review.googlesource.com/9622

CQ_INCLUDE_TRYBOTS=skia.primary:Test-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release,Test-Android-Clang-PixelC-CPU-TegraX1-arm64-Debug-Android

BUG=skia:

Change-Id: I7ef1daaab32892399e3333e4b2fc75d70a1900e4
Reviewed-on: https://skia-review.googlesource.com/9651
Commit-Queue: Matt Sarett <msarett@google.com>
Reviewed-by: Matt Sarett <msarett@google.com>
diff --git a/src/core/SkColorSpaceXformCanvas.cpp b/src/core/SkColorSpaceXformCanvas.cpp
index 89c81e5..ac93e3a 100644
--- a/src/core/SkColorSpaceXformCanvas.cpp
+++ b/src/core/SkColorSpaceXformCanvas.cpp
@@ -8,6 +8,7 @@
 #include "SkColorFilter.h"
 #include "SkColorSpaceXform.h"
 #include "SkColorSpaceXformCanvas.h"
+#include "SkImage_Base.h"
 #include "SkMakeUnique.h"
 #include "SkNoDrawCanvas.h"
 #include "SkSurface.h"
@@ -72,8 +73,7 @@
     }
 
     sk_sp<const SkImage> xform(const SkImage* img) const {
-        // TODO: for real
-        return sk_ref_sp(img);
+        return as_IB(img)->makeColorSpace(fTargetCS);
     }
 
     void onDrawPaint(const SkPaint& paint) override {
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp
index d76276b..2513e84 100644
--- a/src/image/SkImage.cpp
+++ b/src/image/SkImage.cpp
@@ -312,6 +312,19 @@
     return as_IB(this)->onImageInfo().colorType() == kAlpha_8_SkColorType;
 }
 
+sk_sp<SkImage> SkImage_Base::makeColorSpace(sk_sp<SkColorSpace> target) const {
+    SkColorSpaceTransferFn fn;
+    if (!target || !target->isNumericalTransferFn(&fn)) {
+        return nullptr;
+    }
+
+    if (SkColorSpace::Equals(this->colorSpace(), target.get())) {
+        return sk_ref_sp(const_cast<SkImage_Base*>(this));
+    }
+
+    return this->onMakeColorSpace(std::move(target));
+}
+
 //////////////////////////////////////////////////////////////////////////////////////
 
 #if !SK_SUPPORT_GPU
diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h
index e08d761..284af0d 100644
--- a/src/image/SkImage_Base.h
+++ b/src/image/SkImage_Base.h
@@ -81,14 +81,18 @@
         fAddedToCache.store(true);
     }
 
-    virtual sk_sp<SkImage> onMakeColorSpace(sk_sp<SkColorSpace>) {
-        // TODO: Implement this.
-        return sk_ref_sp(this);
-    }
+    // Transforms image into the input color space.
+    sk_sp<SkImage> makeColorSpace(sk_sp<SkColorSpace> target) const;
 
     virtual bool onPinAsTexture(GrContext*) const { return false; }
     virtual void onUnpinAsTexture(GrContext*) const {}
 
+protected:
+    virtual sk_sp<SkImage> onMakeColorSpace(sk_sp<SkColorSpace>) const {
+        // TODO: Make this pure virtual.
+        return sk_ref_sp(const_cast<SkImage_Base*>(this));
+    }
+
 private:
     // Set true by caches when they cache content that's derived from the current pixels.
     mutable SkAtomic<bool> fAddedToCache;
diff --git a/src/image/SkImage_Generator.cpp b/src/image/SkImage_Generator.cpp
index ae3f9f8..fb80767 100644
--- a/src/image/SkImage_Generator.cpp
+++ b/src/image/SkImage_Generator.cpp
@@ -40,6 +40,7 @@
     GrTexture* asTextureRef(GrContext*, const GrSamplerParams&, SkColorSpace*,
                             sk_sp<SkColorSpace>*, SkScalar scaleAdjust[2]) const override;
     bool onIsLazyGenerated() const override { return true; }
+    sk_sp<SkImage> onMakeColorSpace(sk_sp<SkColorSpace>) const override;
 
 private:
     mutable SkImageCacherator fCache;
@@ -115,6 +116,23 @@
     return validator ? sk_sp<SkImage>(new SkImage_Generator(&validator)) : nullptr;
 }
 
+sk_sp<SkImage> SkImage_Generator::onMakeColorSpace(sk_sp<SkColorSpace> target) const {
+    SkBitmap dst;
+    SkImageInfo dstInfo = fCache.info().makeColorSpace(target);
+    if (kIndex_8_SkColorType == dstInfo.colorType() ||
+        kGray_8_SkColorType == dstInfo.colorType()) {
+        dstInfo = dstInfo.makeColorType(kN32_SkColorType);
+    }
+    dst.allocPixels(dstInfo);
+
+    if (!fCache.directGeneratePixels(dstInfo, dst.getPixels(), dst.rowBytes(), 0, 0)) {
+        return nullptr;
+    }
+
+    dst.setImmutable();
+    return SkImage::MakeFromBitmap(dst);
+}
+
 sk_sp<SkImage> SkImage::MakeFromGenerator(std::unique_ptr<SkImageGenerator> generator,
                                           const SkIRect* subset) {
     SkImageCacherator::Validator validator(
diff --git a/src/image/SkImage_Raster.cpp b/src/image/SkImage_Raster.cpp
index 220cc01..782093e 100644
--- a/src/image/SkImage_Raster.cpp
+++ b/src/image/SkImage_Raster.cpp
@@ -120,6 +120,8 @@
         return fBitmap.pixelRef() && fBitmap.pixelRef()->isLazyGenerated();
     }
 
+    sk_sp<SkImage> onMakeColorSpace(sk_sp<SkColorSpace>) const override;
+
 #if SK_SUPPORT_GPU
     sk_sp<GrTexture> refPinnedTexture(uint32_t* uniqueID) const override;
     bool onPinAsTexture(GrContext*) const override;
@@ -365,3 +367,21 @@
     }
     return this->INHERITED::onAsLegacyBitmap(bitmap, mode);
 }
+
+sk_sp<SkImage> SkImage_Raster::onMakeColorSpace(sk_sp<SkColorSpace> target) const {
+    SkBitmap dst;
+    SkImageInfo dstInfo = fBitmap.info().makeColorSpace(target);
+    if (kIndex_8_SkColorType == dstInfo.colorType() ||
+        kGray_8_SkColorType == dstInfo.colorType())
+    {
+        dstInfo = dstInfo.makeColorType(kN32_SkColorType);
+    }
+    dst.allocPixels(dstInfo);
+
+    SkPixmap src;
+    SkAssertResult(this->onPeekPixels(&src));
+
+    SkAssertResult(dst.writePixels(src));
+    dst.setImmutable();
+    return SkImage::MakeFromBitmap(dst);
+}
diff --git a/tests/ImageTest.cpp b/tests/ImageTest.cpp
index 720f016..e0e64ea 100644
--- a/tests/ImageTest.cpp
+++ b/tests/ImageTest.cpp
@@ -1050,6 +1050,10 @@
                                     nullptr, nullptr, SkImage::BitDepth::kU8, std::move(space));
 };
 
+static inline bool almost_equal(int a, int b) {
+    return SkTAbs(a - b) <= 1;
+}
+
 DEF_TEST(Image_ColorSpace, r) {
     sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB();
     sk_sp<SkImage> image = GetResourceAsImage("mandrill_512_q075.jpg");
@@ -1082,6 +1086,34 @@
     REPORTER_ASSERT(r, SkColorSpace::Equals(rec2020.get(), image->colorSpace()));
 }
 
+DEF_TEST(Image_makeColorSpace, r) {
+    sk_sp<SkColorSpace> p3 = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+                                                   SkColorSpace::kDCIP3_D65_Gamut);
+
+    SkBitmap srgbBitmap;
+    srgbBitmap.allocPixels(SkImageInfo::MakeS32(1, 1, kOpaque_SkAlphaType));
+    *srgbBitmap.getAddr32(0, 0) = SkSwizzle_RGBA_to_PMColor(0xFF604020);
+    srgbBitmap.setImmutable();
+    sk_sp<SkImage> srgbImage = SkImage::MakeFromBitmap(srgbBitmap);
+    sk_sp<SkImage> p3Image = as_IB(srgbImage)->makeColorSpace(p3);
+    SkBitmap p3Bitmap;
+    bool success = p3Image->asLegacyBitmap(&p3Bitmap, SkImage::kRO_LegacyBitmapMode);
+    REPORTER_ASSERT(r, success);
+    p3Bitmap.lockPixels();
+    REPORTER_ASSERT(r, almost_equal(0x28, SkGetPackedR32(*p3Bitmap.getAddr32(0, 0))));
+    REPORTER_ASSERT(r, almost_equal(0x40, SkGetPackedG32(*p3Bitmap.getAddr32(0, 0))));
+    REPORTER_ASSERT(r, almost_equal(0x5E, SkGetPackedB32(*p3Bitmap.getAddr32(0, 0))));
+
+    srgbImage = GetResourceAsImage("1x1.png");
+    p3Image = as_IB(srgbImage)->makeColorSpace(p3);
+    success = p3Image->asLegacyBitmap(&p3Bitmap, SkImage::kRO_LegacyBitmapMode);
+    REPORTER_ASSERT(r, success);
+    p3Bitmap.lockPixels();
+    REPORTER_ASSERT(r, almost_equal(0x8B, SkGetPackedR32(*p3Bitmap.getAddr32(0, 0))));
+    REPORTER_ASSERT(r, almost_equal(0x82, SkGetPackedG32(*p3Bitmap.getAddr32(0, 0))));
+    REPORTER_ASSERT(r, almost_equal(0x77, SkGetPackedB32(*p3Bitmap.getAddr32(0, 0))));
+}
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 static void make_all_premul(SkBitmap* bm) {