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/SkGradientShaderPriv.h b/src/shaders/gradients/SkGradientShaderPriv.h
index 9a565a8..0f3e736 100644
--- a/src/shaders/gradients/SkGradientShaderPriv.h
+++ b/src/shaders/gradients/SkGradientShaderPriv.h
@@ -12,6 +12,7 @@
 
 #include "SkArenaAlloc.h"
 #include "SkMatrix.h"
+#include "SkPM4fPriv.h"
 #include "SkShaderBase.h"
 #include "SkTArray.h"
 #include "SkTemplates.h"
@@ -63,18 +64,10 @@
 
     bool isOpaque() const override;
 
-    enum class GradientBitmapType : uint8_t {
-        kLegacy,
-        kSRGB,
-        kHalfFloat,
-    };
-
-    void getGradientTableBitmap(SkBitmap*, GradientBitmapType bitmapType) const;
+    void getGradientTableBitmap(const SkColor4f* colors, SkBitmap*, SkColorType) const;
 
     uint32_t getGradFlags() const { return fGradFlags; }
 
-    SkColor4f getXformedColor(size_t index, SkColorSpace*) const;
-
 protected:
     class GradientShaderBase4fContext;
 
@@ -85,7 +78,7 @@
 
     bool onAsLuminanceColor(SkColor*) const override;
 
-    void initLinearBitmap(SkBitmap* bitmap, GradientBitmapType) const;
+    void initLinearBitmap(const SkColor4f* colors, SkBitmap* bitmap, SkColorType colorType) const;
 
     bool onAppendStages(const StageRec&) const override;
 
@@ -119,7 +112,7 @@
 
     SkColor getLegacyColor(int i) const {
         SkASSERT(i < fColorCount);
-        return fOrigColors4f[i].toSkColor();
+        return Sk4f_toL32(swizzle_rb(Sk4f::Load(fOrigColors4f[i].vec())));
     }
 
     SkColor4f*          fOrigColors4f; // original colors, as linear floats
@@ -145,6 +138,15 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+struct SkColor4fXformer {
+    SkColor4fXformer(const SkColor4f* colors, int colorCount, SkColorSpace* src, SkColorSpace* dst);
+
+    const SkColor4f*              fColors;
+    SkSTArray<4, SkColor4f, true> fStorage;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
 #if SK_SUPPORT_GPU
 
 #include "GrColorSpaceInfo.h"
@@ -188,11 +190,11 @@
                    const SkGradientShaderBase* shader,
                    const SkMatrix* matrix,
                    SkShader::TileMode tileMode,
-                   SkColorSpace* dstColorSpace)
+                   const GrColorSpaceInfo* dstColorSpaceInfo)
                 : fContext(context)
                 , fShader(shader)
                 , fMatrix(matrix)
-                , fDstColorSpace(dstColorSpace) {
+                , fDstColorSpaceInfo(dstColorSpaceInfo) {
             switch (tileMode) {
                 case SkShader::kClamp_TileMode:
                     fWrapMode = GrSamplerState::WrapMode::kClamp;
@@ -214,18 +216,18 @@
                    const SkGradientShaderBase* shader,
                    const SkMatrix* matrix,
                    GrSamplerState::WrapMode wrapMode,
-                   SkColorSpace* dstColorSpace)
+                   const GrColorSpaceInfo* dstColorSpaceInfo)
                 : fContext(context)
                 , fShader(shader)
                 , fMatrix(matrix)
                 , fWrapMode(wrapMode)
-                , fDstColorSpace(dstColorSpace) {}
+                , fDstColorSpaceInfo(dstColorSpaceInfo) {}
 
         GrContext*                  fContext;
         const SkGradientShaderBase* fShader;
         const SkMatrix*             fMatrix;
         GrSamplerState::WrapMode    fWrapMode;
-        SkColorSpace*               fDstColorSpace;
+        const GrColorSpaceInfo*     fDstColorSpaceInfo;
     };
 
     class GLSLProcessor;
@@ -255,29 +257,13 @@
 
     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
 
-    // Helper function used by derived class factories to handle color space transformation and
-    // modulation by input alpha.
+    // Helper function used by derived class factories to handle modulation by input alpha.
     static std::unique_ptr<GrFragmentProcessor> AdjustFP(
             std::unique_ptr<GrGradientEffect> gradientFP, const CreateArgs& args) {
         if (!gradientFP->isValid()) {
             return nullptr;
         }
-        std::unique_ptr<GrFragmentProcessor> fp;
-        // With analytic gradients, we pre-convert the stops to the destination color space, so no
-        // xform is needed. With texture-based gradients, we leave the data in the source color
-        // space (to avoid clamping if we can't use F16)... Add an extra FP to do the xform.
-        if (gradientFP->fStrategy == InterpolationStrategy::kTexture) {
-            // Our texture is always either F16 or sRGB, so the data is "linear" in the shader.
-            // Create our xform assuming float inputs, which will suppress any extra sRGB work.
-            // We do support having a transfer function on the color space of the stops, so
-            // this FP may include that transformation.
-            fp = GrColorSpaceXformEffect::Make(std::move(gradientFP),
-                                               args.fShader->fColorSpace.get(),
-                                               args.fDstColorSpace);
-        } else {
-            fp = std::move(gradientFP);
-        }
-        return GrFragmentProcessor::MulChildByInputAlpha(std::move(fp));
+        return GrFragmentProcessor::MulChildByInputAlpha(std::move(gradientFP));
     }
 
 #if GR_TEST_UTILS
@@ -313,7 +299,8 @@
     }
 
 private:
-    void addInterval(const SkGradientShaderBase&, size_t idx0, size_t idx1, SkColorSpace*);
+    void addInterval(const SkGradientShaderBase&, const SkColor4f* colors,
+                     size_t idx0, size_t idx1);
 
     static OptimizationFlags OptFlags(bool isOpaque);