Fill in gamut-transformation TODOs for software pipeline.

This adds support for paint colors, color shaders, and mode color filters (everything that the software pipeline supports today).

BUG=skia:

GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=4781

Change-Id: I6e5da6d0dba03fbc82ecaa233ce8c727e7ce17b3
Reviewed-on: https://skia-review.googlesource.com/4781
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/src/core/SkColorShader.cpp b/src/core/SkColorShader.cpp
index a761d60..69d9e46 100644
--- a/src/core/SkColorShader.cpp
+++ b/src/core/SkColorShader.cpp
@@ -320,7 +320,10 @@
     auto color = scratch->make<SkPM4f>(SkPM4f_from_SkColor(fColor, dst));
     p->append(SkRasterPipeline::move_src_dst);
     p->append(SkRasterPipeline::constant_color, color);
-    // TODO: sRGB -> dst gamut correction if needed
+    if (!append_gamut_transform(p, scratch,
+                                SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named).get(), dst)) {
+        return false;
+    }
     p->append(SkRasterPipeline::srcin);
     return true;
 }
@@ -331,7 +334,9 @@
     auto color = scratch->make<SkPM4f>(fColor4.premul());
     p->append(SkRasterPipeline::move_src_dst);
     p->append(SkRasterPipeline::constant_color, color);
-    // TODO: fColorSpace -> dst gamut correction if needed
+    if (!append_gamut_transform(p, scratch, fColorSpace.get(), dst)) {
+        return false;
+    }
     p->append(SkRasterPipeline::srcin);
     return true;
 }
diff --git a/src/core/SkPM4fPriv.h b/src/core/SkPM4fPriv.h
index f70c5c5..304a49f 100644
--- a/src/core/SkPM4fPriv.h
+++ b/src/core/SkPM4fPriv.h
@@ -10,7 +10,10 @@
 
 #include "SkColorPriv.h"
 #include "SkColorSpace.h"
+#include "SkColorSpace_Base.h"
+#include "SkFixedAlloc.h"
 #include "SkPM4f.h"
+#include "SkRasterPipeline.h"
 #include "SkSRGB.h"
 
 static inline Sk4f set_alpha(const Sk4f& px, float alpha) {
@@ -72,12 +75,58 @@
     return linear;
 }
 
+
+// N.B. scratch_matrix_3x4 must live at least as long as p.
+static inline bool append_gamut_transform(SkRasterPipeline* p, float scratch_matrix_3x4[12],
+                                          SkColorSpace* src, SkColorSpace* dst) {
+    if (src == dst) { return true; }
+    if (!dst)       { return true; }   // Legacy modes intentionally ignore color gamut.
+    if (!src)       { return true; }   // A null src color space means linear gamma, dst gamut.
+
+    auto toXYZ = as_CSB(src)->  toXYZD50(),
+       fromXYZ = as_CSB(dst)->fromXYZD50();
+    if (!toXYZ || !fromXYZ) { return false; }  // Unsupported color space type.
+
+    if (as_CSB(src)->toXYZD50Hash() == as_CSB(dst)->toXYZD50Hash()) { return true; }
+
+    SkMatrix44 m44(*fromXYZ, *toXYZ);
+
+    // Convert from 4x4 to (column-major) 3x4.
+    auto ptr = scratch_matrix_3x4;
+    *ptr++ = m44.get(0,0); *ptr++ = m44.get(1,0); *ptr++ = m44.get(2,0);
+    *ptr++ = m44.get(0,1); *ptr++ = m44.get(1,1); *ptr++ = m44.get(2,1);
+    *ptr++ = m44.get(0,2); *ptr++ = m44.get(1,2); *ptr++ = m44.get(2,2);
+    *ptr++ = m44.get(0,3); *ptr++ = m44.get(1,3); *ptr++ = m44.get(2,3);
+
+    p->append(SkRasterPipeline::matrix_3x4, scratch_matrix_3x4);
+    // TODO: detect whether we can skip the clamps?
+    p->append(SkRasterPipeline::clamp_0);
+    p->append(SkRasterPipeline::clamp_a);
+    return true;
+}
+
+static inline bool append_gamut_transform(SkRasterPipeline* p, SkFallbackAlloc* scratch,
+                                          SkColorSpace* src, SkColorSpace* dst) {
+    struct matrix_3x4 { float arr[12]; };
+    return append_gamut_transform(p, scratch->make<matrix_3x4>()->arr, src, dst);
+}
+
 static inline SkPM4f SkPM4f_from_SkColor(SkColor color, SkColorSpace* dst) {
     SkColor4f color4f;
     if (dst) {
         // sRGB gamma, sRGB gamut.
         color4f = SkColor4f::FromColor(color);
-        // TODO: gamut transform if needed
+        void* color4f_ptr = &color4f;
+
+        float scratch_matrix_3x4[12];
+
+        SkRasterPipeline p;
+        p.append(SkRasterPipeline::constant_color, color4f_ptr);
+        append_gamut_transform(&p, scratch_matrix_3x4,
+                               SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named).get(), dst);
+        p.append(SkRasterPipeline::store_f32, &color4f_ptr);
+
+        p.compile()(0,1);
     } else {
         // Linear gamma, dst gamut.
         swizzle_rb(SkNx_cast<float>(Sk4b::Load(&color)) * (1/255.0f)).store(&color4f);