Switch gradients to as-encoded blending

Stops are now always transformed all the way to destination color space,
using a new helper struct. Even textured gradients on GPU pre-transform
the stops, for simplicity and consistency. We plumb the destination
config in to drive the choice of texture format - this is simpler and
more correct.

Bug: skia:
Change-Id: Ie3aea9d29a8a5973a72551d9efeaf247ce29606b
Reviewed-on: https://skia-review.googlesource.com/139173
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/src/shaders/gradients/Sk4fGradientBase.cpp b/src/shaders/gradients/Sk4fGradientBase.cpp
index 19eabcf..0f6da52 100644
--- a/src/shaders/gradients/Sk4fGradientBase.cpp
+++ b/src/shaders/gradients/Sk4fGradientBase.cpp
@@ -21,19 +21,19 @@
 
 class IntervalIterator {
 public:
-    IntervalIterator(const SkGradientShaderBase& shader, SkColorSpace* dstCS, bool reverse)
+    IntervalIterator(const SkGradientShaderBase& shader, bool reverse)
         : fShader(shader)
-        , fDstCS(dstCS)
         , fFirstPos(reverse ? SK_Scalar1 : 0)
         , fBegin(reverse ? shader.fColorCount - 1 : 0)
         , fAdvance(reverse ? -1 : 1) {
         SkASSERT(shader.fColorCount > 0);
     }
 
-    void iterate(std::function<void(const SkColor4f&, const SkColor4f&,
+    void iterate(const SkColor4f* colors,
+                 std::function<void(const SkColor4f&, const SkColor4f&,
                                     SkScalar, SkScalar)> func) const {
         if (!fShader.fOrigPos) {
-            this->iterateImplicitPos(func);
+            this->iterateImplicitPos(colors, func);
             return;
         }
 
@@ -48,8 +48,7 @@
             const SkScalar currPos = fShader.fOrigPos[curr];
             if (currPos != prevPos) {
                 SkASSERT((currPos - prevPos > 0) == (fAdvance > 0));
-                func(fShader.getXformedColor(prev, fDstCS), fShader.getXformedColor(curr, fDstCS),
-                     prevPos, currPos);
+                func(colors[prev], colors[curr], prevPos, currPos);
             }
 
             prev = curr;
@@ -58,7 +57,8 @@
     }
 
 private:
-    void iterateImplicitPos(std::function<void(const SkColor4f&, const SkColor4f&,
+    void iterateImplicitPos(const SkColor4f* colors,
+                            std::function<void(const SkColor4f&, const SkColor4f&,
                                                SkScalar, SkScalar)> func) const {
         // When clients don't provide explicit color stop positions (fPos == nullptr),
         // the color stops are distributed evenly across the unit interval
@@ -73,33 +73,28 @@
             SkASSERT(curr >= 0 && curr < fShader.fColorCount);
 
             const SkScalar currPos = prevPos + dt;
-            func(fShader.getXformedColor(prev, fDstCS),
-                 fShader.getXformedColor(curr, fDstCS),
-                 prevPos, currPos);
+            func(colors[prev], colors[curr], prevPos, currPos);
             prev = curr;
             prevPos = currPos;
         }
 
         // emit the last interval with a pinned end position, to avoid precision issues
-        func(fShader.getXformedColor(prev, fDstCS),
-             fShader.getXformedColor(prev + fAdvance, fDstCS),
-             prevPos, 1 - fFirstPos);
+        func(colors[prev], colors[prev + fAdvance], prevPos, 1 - fFirstPos);
     }
 
     const SkGradientShaderBase& fShader;
-    SkColorSpace*               fDstCS;
     const SkScalar              fFirstPos;
     const int                   fBegin;
     const int                   fAdvance;
 };
 
 void addMirrorIntervals(const SkGradientShaderBase& shader,
-                        SkColorSpace* dstCS,
+                        const SkColor4f* colors,
                         const Sk4f& componentScale,
                         bool premulColors, bool reverse,
                         Sk4fGradientIntervalBuffer::BufferType* buffer) {
-    const IntervalIterator iter(shader, dstCS, reverse);
-    iter.iterate([&] (const SkColor4f& c0, const SkColor4f& c1, SkScalar t0, SkScalar t1) {
+    const IntervalIterator iter(shader, reverse);
+    iter.iterate(colors, [&] (const SkColor4f& c0, const SkColor4f& c1, SkScalar t0, SkScalar t1) {
         SkASSERT(buffer->empty() || buffer->back().fT1 == 2 - t0);
 
         const auto mirror_t0 = 2 - t0;
@@ -193,20 +188,25 @@
     const SkScalar first_pos = reverse ? SK_Scalar1 : 0;
     const SkScalar last_pos = SK_Scalar1 - first_pos;
 
+    // Transform all of the colors to destination color space
+    SkColor4fXformer xformedColors(shader.fOrigColors4f, count, shader.fColorSpace.get(), dstCS);
+
     if (tileMode == SkShader::kClamp_TileMode) {
         // synthetic edge interval: -/+inf .. P0
-        const Sk4f clamp_color = pack_color(shader.getXformedColor(first_index, dstCS),
+        const Sk4f clamp_color = pack_color(xformedColors.fColors[first_index],
                                             premulColors, componentScale);
         const SkScalar clamp_pos = reverse ? SK_ScalarInfinity : SK_ScalarNegativeInfinity;
         fIntervals.emplace_back(clamp_color, clamp_pos,
                                 clamp_color, first_pos);
     } else if (tileMode == SkShader::kMirror_TileMode && reverse) {
         // synthetic mirror intervals injected before main intervals: (2 .. 1]
-        addMirrorIntervals(shader, dstCS, componentScale, premulColors, false, &fIntervals);
+        addMirrorIntervals(shader, xformedColors.fColors, componentScale, premulColors, false,
+                           &fIntervals);
     }
 
-    const IntervalIterator iter(shader, dstCS, reverse);
-    iter.iterate([&] (const SkColor4f& c0, const SkColor4f& c1, SkScalar t0, SkScalar t1) {
+    const IntervalIterator iter(shader, reverse);
+    iter.iterate(xformedColors.fColors,
+                 [&] (const SkColor4f& c0, const SkColor4f& c1, SkScalar t0, SkScalar t1) {
         SkASSERT(fIntervals.empty() || fIntervals.back().fT1 == t0);
 
         fIntervals.emplace_back(pack_color(c0, premulColors, componentScale), t0,
@@ -215,14 +215,15 @@
 
     if (tileMode == SkShader::kClamp_TileMode) {
         // synthetic edge interval: Pn .. +/-inf
-        const Sk4f clamp_color = pack_color(shader.getXformedColor(last_index, dstCS),
+        const Sk4f clamp_color = pack_color(xformedColors.fColors[last_index],
                                             premulColors, componentScale);
         const SkScalar clamp_pos = reverse ? SK_ScalarNegativeInfinity : SK_ScalarInfinity;
         fIntervals.emplace_back(clamp_color, last_pos,
                                 clamp_color, clamp_pos);
     } else if (tileMode == SkShader::kMirror_TileMode && !reverse) {
         // synthetic mirror intervals injected after main intervals: [1 .. 2)
-        addMirrorIntervals(shader, dstCS, componentScale, premulColors, true, &fIntervals);
+        addMirrorIntervals(shader, xformedColors.fColors, componentScale, premulColors, true,
+                           &fIntervals);
     }
 }