Remove GrColorSpaceXform from GrGradientEffect

Use a local xform to convert color stops for analytic gradients.
For texture-based gradients, wrap the FP with an xform effect.
To simplify this code, add a new AdjustFP helper to do the color
xform, and also add the MulOutputByInputAlpha stage.

Bug: skia:
Change-Id: Icde19b5ec1c66aae76f894e9978c90a5f00c852e
Reviewed-on: https://skia-review.googlesource.com/62500
Reviewed-by: Florin Malita <fmalita@chromium.org>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/src/shaders/gradients/SkGradientShader.cpp b/src/shaders/gradients/SkGradientShader.cpp
index 35e8342..2e784bb 100644
--- a/src/shaders/gradients/SkGradientShader.cpp
+++ b/src/shaders/gradients/SkGradientShader.cpp
@@ -1147,11 +1147,11 @@
 
 #if SK_SUPPORT_GPU
 
+#include "GrColorSpaceXform.h"
 #include "GrContext.h"
 #include "GrShaderCaps.h"
 #include "GrTextureStripAtlas.h"
 #include "gl/GrGLContext.h"
-#include "glsl/GrGLSLColorSpaceXformHelper.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLProgramDataManager.h"
 #include "glsl/GrGLSLUniformHandler.h"
@@ -1254,9 +1254,6 @@
                 pdman.set1f(fFSYUni, yCoord);
                 fCachedYCoord = yCoord;
             }
-            if (SkToBool(e.fColorSpaceXform)) {
-                fColorSpaceHelper.setData(pdman, e.fColorSpaceXform.get());
-            }
             break;
         }
     }
@@ -1295,8 +1292,6 @@
             break;
     }
 
-    key |= GrColorSpaceXform::XformKey(e.fColorSpaceXform.get()) << kReservedBits;
-
     return key;
 }
 
@@ -1409,9 +1404,10 @@
     if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
         fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
     }
-    if (ge.fColorSpaceXform) {
-        fragBuilder->codeAppend("colorTemp.rgb = clamp(colorTemp.rgb, 0, colorTemp.a);");
-    }
+
+    // If the input colors were floats, or there was a color space xform, we may end up out of
+    // range. The simplest solution is to always clamp our (premul) value here.
+    fragBuilder->codeAppend("colorTemp.rgb = clamp(colorTemp.rgb, 0, colorTemp.a);");
 
     fragBuilder->codeAppendf("%s = %s * colorTemp;", outputColor, inputColor);
 }
@@ -1430,14 +1426,12 @@
         return;
     }
 
-    fColorSpaceHelper.emitCode(uniformHandler, ge.fColorSpaceXform.get());
-
     const char* fsyuni = uniformHandler->getUniformCStr(fFSYUni);
 
     fragBuilder->codeAppendf("half2 coord = half2(%s, %s);", gradientTValue, fsyuni);
     fragBuilder->codeAppendf("%s = ", outputColor);
     fragBuilder->appendTextureLookupAndModulate(inputColor, texSamplers[0], "coord",
-                                                kFloat2_GrSLType, &fColorSpaceHelper);
+                                                kFloat2_GrSLType);
     fragBuilder->codeAppend(";");
 }
 
@@ -1457,7 +1451,6 @@
     fIsOpaque = shader.isOpaque();
 
     fColorType = this->determineColorType(shader);
-    fColorSpaceXform = std::move(args.fColorSpaceXform);
     fWrapMode = args.fWrapMode;
 
     if (kTexture_ColorType == fColorType) {
@@ -1471,10 +1464,12 @@
         }
 
         // Convert input colors to GrColor4f, possibly premul, and apply color space xform
+        auto colorSpaceXform = GrColorSpaceXform::Make(shader.fColorSpace.get(),
+                                                       args.fDstColorSpace);
         SkASSERT(shader.fOrigColors && shader.fOrigColors4f);
         fColors4f.setCount(shader.fColorCount);
         for (int i = 0; i < shader.fColorCount; ++i) {
-            if (args.fGammaCorrect) {
+            if (args.fDstColorSpace) {
                 fColors4f[i] = GrColor4f::FromSkColor4f(shader.fOrigColors4f[i]);
             } else {
                 GrColor grColor = SkColorToUnpremulGrColor(shader.fOrigColors[i]);
@@ -1485,9 +1480,9 @@
                 fColors4f[i] = fColors4f[i].premul();
             }
 
-            if (fColorSpaceXform) {
+            if (colorSpaceXform) {
                 // We defer clamping to after interpolation (see emitAnalyticalColor)
-                fColors4f[i] = fColorSpaceXform->unclampedXform(fColors4f[i]);
+                fColors4f[i] = colorSpaceXform->unclampedXform(fColors4f[i]);
             }
         }
 
@@ -1512,7 +1507,7 @@
         case kTexture_ColorType:
             SkGradientShaderBase::GradientBitmapType bitmapType =
                 SkGradientShaderBase::GradientBitmapType::kLegacy;
-            if (args.fGammaCorrect) {
+            if (args.fDstColorSpace) {
                 // Try to use F16 if we can
                 if (args.fContext->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig)) {
                     bitmapType = SkGradientShaderBase::GradientBitmapType::kHalfFloat;
@@ -1580,7 +1575,6 @@
 GrGradientEffect::GrGradientEffect(const GrGradientEffect& that)
         : INHERITED(that.classID(), OptFlags(that.fIsOpaque))
         , fColors4f(that.fColors4f)
-        , fColorSpaceXform(that.fColorSpaceXform)
         , fPositions(that.fPositions)
         , fWrapMode(that.fWrapMode)
         , fCoordTransform(that.fCoordTransform)
@@ -1634,7 +1628,7 @@
             }
         }
     }
-    return GrColorSpaceXform::Equals(this->fColorSpaceXform.get(), ge.fColorSpaceXform.get());
+    return true;
 }
 
 #if GR_TEST_UTILS
diff --git a/src/shaders/gradients/SkGradientShaderPriv.h b/src/shaders/gradients/SkGradientShaderPriv.h
index 1310589..775cb39 100644
--- a/src/shaders/gradients/SkGradientShaderPriv.h
+++ b/src/shaders/gradients/SkGradientShaderPriv.h
@@ -280,7 +280,6 @@
 #include "GrColorSpaceXform.h"
 #include "GrCoordTransform.h"
 #include "GrFragmentProcessor.h"
-#include "glsl/GrGLSLColorSpaceXformHelper.h"
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLProgramDataManager.h"
 
@@ -319,13 +318,11 @@
                    const SkGradientShaderBase* shader,
                    const SkMatrix* matrix,
                    SkShader::TileMode tileMode,
-                   sk_sp<GrColorSpaceXform> colorSpaceXform,
-                   bool gammaCorrect)
+                   const SkColorSpace* dstColorSpace)
                 : fContext(context)
                 , fShader(shader)
                 , fMatrix(matrix)
-                , fColorSpaceXform(std::move(colorSpaceXform))
-                , fGammaCorrect(gammaCorrect) {
+                , fDstColorSpace(dstColorSpace) {
             switch (tileMode) {
                 case SkShader::kClamp_TileMode:
                     fWrapMode = GrSamplerState::WrapMode::kClamp;
@@ -343,21 +340,18 @@
                    const SkGradientShaderBase* shader,
                    const SkMatrix* matrix,
                    GrSamplerState::WrapMode wrapMode,
-                   sk_sp<GrColorSpaceXform> colorSpaceXform,
-                   bool gammaCorrect)
+                   const SkColorSpace* dstColorSpace)
                 : fContext(context)
                 , fShader(shader)
                 , fMatrix(matrix)
                 , fWrapMode(wrapMode)
-                , fColorSpaceXform(std::move(colorSpaceXform))
-                , fGammaCorrect(gammaCorrect) {}
+                , fDstColorSpace(dstColorSpace) {}
 
         GrContext*                  fContext;
         const SkGradientShaderBase* fShader;
         const SkMatrix*             fMatrix;
         GrSamplerState::WrapMode    fWrapMode;
-        sk_sp<GrColorSpaceXform>    fColorSpaceXform;
-        bool                        fGammaCorrect;
+        const SkColorSpace*         fDstColorSpace;
     };
 
     class GLSLProcessor;
@@ -404,7 +398,28 @@
     GrGradientEffect(ClassID classID, const CreateArgs&, bool isOpaque);
     explicit GrGradientEffect(const GrGradientEffect&);  // facilitates clone() implementations
 
-    #if GR_TEST_UTILS
+    // Helper function used by derived class factories to handle color space transformation and
+    // 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 (kTexture_ColorType == gradientFP->getColorType()) {
+            fp = GrColorSpaceXformEffect::Make(std::move(gradientFP),
+                                               args.fShader->fColorSpace.get(),
+                                               args.fDstColorSpace);
+        } else {
+            fp = std::move(gradientFP);
+        }
+        return GrFragmentProcessor::MulOutputByInputAlpha(std::move(fp));
+    }
+
+#if GR_TEST_UTILS
     /** Helper struct that stores (and populates) parameters to construct a random gradient.
         If fUseColors4f is true, then the SkColor4f factory should be called, with fColors4f and
         fColorSpace. Otherwise, the SkColor factory should be called, with fColors. fColorCount
@@ -441,9 +456,6 @@
 
     SkTDArray<GrColor4f> fColors4f;
 
-    // Only present if a color space transformation is needed
-    sk_sp<GrColorSpaceXform> fColorSpaceXform;
-
     SkTDArray<SkScalar> fPositions;
     GrSamplerState::WrapMode fWrapMode;
 
@@ -532,7 +544,6 @@
     GrGLSLProgramDataManager::UniformHandle fColorsUni;
     GrGLSLProgramDataManager::UniformHandle fExtraStopT;
     GrGLSLProgramDataManager::UniformHandle fFSYUni;
-    GrGLSLColorSpaceXformHelper             fColorSpaceHelper;
 
     typedef GrGLSLFragmentProcessor INHERITED;
 };
diff --git a/src/shaders/gradients/SkLinearGradient.cpp b/src/shaders/gradients/SkLinearGradient.cpp
index 8b439ce..c0cade3 100644
--- a/src/shaders/gradients/SkLinearGradient.cpp
+++ b/src/shaders/gradients/SkLinearGradient.cpp
@@ -100,8 +100,9 @@
     class GLSLLinearProcessor;
 
     static std::unique_ptr<GrFragmentProcessor> Make(const CreateArgs& args) {
-        auto processor = std::unique_ptr<GrLinearGradient>(new GrLinearGradient(args));
-        return processor->isValid() ? std::move(processor) : nullptr;
+        return GrGradientEffect::AdjustFP(std::unique_ptr<GrLinearGradient>(
+                new GrLinearGradient(args)),
+                args);
     }
 
     const char* name() const override { return "Linear Gradient"; }
@@ -212,15 +213,8 @@
     }
     matrix.postConcat(fPtsToUnit);
 
-    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
-                                                                       args.fDstColorSpace);
-    auto inner = GrLinearGradient::Make(GrGradientEffect::CreateArgs(
-            args.fContext, this, &matrix, fTileMode, std::move(colorSpaceXform),
-            SkToBool(args.fDstColorSpace)));
-    if (!inner) {
-        return nullptr;
-    }
-    return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
+    return GrLinearGradient::Make(GrGradientEffect::CreateArgs(
+            args.fContext, this, &matrix, fTileMode, args.fDstColorSpace));
 }
 
 
diff --git a/src/shaders/gradients/SkRadialGradient.cpp b/src/shaders/gradients/SkRadialGradient.cpp
index 5c31688..e704554 100644
--- a/src/shaders/gradients/SkRadialGradient.cpp
+++ b/src/shaders/gradients/SkRadialGradient.cpp
@@ -69,8 +69,9 @@
     class GLSLRadialProcessor;
 
     static std::unique_ptr<GrFragmentProcessor> Make(const CreateArgs& args) {
-        auto processor = std::unique_ptr<GrRadialGradient>(new GrRadialGradient(args));
-        return processor->isValid() ? std::move(processor) : nullptr;
+        return GrGradientEffect::AdjustFP(std::unique_ptr<GrRadialGradient>(
+                new GrRadialGradient(args)),
+                args);
     }
 
     const char* name() const override { return "Radial Gradient"; }
@@ -184,15 +185,9 @@
         matrix.postConcat(inv);
     }
     matrix.postConcat(fPtsToUnit);
-    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
-                                                                       args.fDstColorSpace);
-    auto inner = GrRadialGradient::Make(GrGradientEffect::CreateArgs(
-            args.fContext, this, &matrix, fTileMode, std::move(colorSpaceXform),
-            SkToBool(args.fDstColorSpace)));
-    if (!inner) {
-        return nullptr;
-    }
-    return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
+
+    return GrRadialGradient::Make(GrGradientEffect::CreateArgs(
+            args.fContext, this, &matrix, fTileMode, args.fDstColorSpace));
 }
 
 #endif
diff --git a/src/shaders/gradients/SkSweepGradient.cpp b/src/shaders/gradients/SkSweepGradient.cpp
index 792024f..551a6fb 100644
--- a/src/shaders/gradients/SkSweepGradient.cpp
+++ b/src/shaders/gradients/SkSweepGradient.cpp
@@ -76,8 +76,9 @@
 
     static std::unique_ptr<GrFragmentProcessor> Make(const CreateArgs& args, SkScalar tBias,
                                                      SkScalar tScale) {
-        auto processor = std::unique_ptr<GrSweepGradient>(new GrSweepGradient(args, tBias, tScale));
-        return processor->isValid() ? std::move(processor) : nullptr;
+        return GrGradientEffect::AdjustFP(std::unique_ptr<GrSweepGradient>(
+                new GrSweepGradient(args, tBias, tScale)),
+                args);
     }
 
     const char* name() const override { return "Sweep Gradient"; }
@@ -240,16 +241,10 @@
     }
     matrix.postConcat(fPtsToUnit);
 
-    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
-                                                                       args.fDstColorSpace);
-    auto inner = GrSweepGradient::Make(
+    return GrSweepGradient::Make(
             GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode,
-                                         std::move(colorSpaceXform), SkToBool(args.fDstColorSpace)),
+                                         args.fDstColorSpace),
             fTBias, fTScale);
-    if (!inner) {
-        return nullptr;
-    }
-    return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
 }
 
 #endif
diff --git a/src/shaders/gradients/SkTwoPointConicalGradient.cpp b/src/shaders/gradients/SkTwoPointConicalGradient.cpp
index 04448b4..d84108b 100644
--- a/src/shaders/gradients/SkTwoPointConicalGradient.cpp
+++ b/src/shaders/gradients/SkTwoPointConicalGradient.cpp
@@ -130,15 +130,8 @@
 std::unique_ptr<GrFragmentProcessor> SkTwoPointConicalGradient::asFragmentProcessor(
         const AsFPArgs& args) const {
     SkASSERT(args.fContext);
-    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
-                                                                       args.fDstColorSpace);
-    auto inner = Gr2PtConicalGradientEffect::Make(GrGradientEffect::CreateArgs(
-            args.fContext, this, args.fLocalMatrix, fTileMode, std::move(colorSpaceXform),
-            SkToBool(args.fDstColorSpace)));
-    if (!inner) {
-        return nullptr;
-    }
-    return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
+    return Gr2PtConicalGradientEffect::Make(GrGradientEffect::CreateArgs(
+            args.fContext, this, args.fLocalMatrix, fTileMode, args.fDstColorSpace));
 }
 
 #endif
diff --git a/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp b/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp
index a48c8d9..f370caf 100644
--- a/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp
+++ b/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp
@@ -64,8 +64,9 @@
     class GLSLEdge2PtConicalProcessor;
 
     static std::unique_ptr<GrFragmentProcessor> Make(const CreateArgs& args) {
-        auto processor = std::unique_ptr<Edge2PtConicalEffect>(new Edge2PtConicalEffect(args));
-        return processor->isValid() ? std::move(processor) : nullptr;
+        return GrGradientEffect::AdjustFP(std::unique_ptr<Edge2PtConicalEffect>(
+                new Edge2PtConicalEffect(args)),
+                args);
     }
 
     const char* name() const override {
@@ -383,9 +384,9 @@
 
     static std::unique_ptr<GrFragmentProcessor> Make(const CreateArgs& args, SkScalar focalX,
                                                      bool isFlipped) {
-        auto processor = std::unique_ptr<FocalOutside2PtConicalEffect>(
-                new FocalOutside2PtConicalEffect(args, focalX, isFlipped));
-        return processor->isValid() ? std::move(processor) : nullptr;
+        return GrGradientEffect::AdjustFP(std::unique_ptr<FocalOutside2PtConicalEffect>(
+                new FocalOutside2PtConicalEffect(args, focalX, isFlipped)),
+                args);
     }
 
     const char* name() const override {
@@ -590,9 +591,9 @@
     class GLSLFocalInside2PtConicalProcessor;
 
     static std::unique_ptr<GrFragmentProcessor> Make(const CreateArgs& args, SkScalar focalX) {
-        auto processor = std::unique_ptr<FocalInside2PtConicalEffect>(
-                new FocalInside2PtConicalEffect(args, focalX));
-        return processor->isValid() ? std::move(processor) : nullptr;
+        return GrGradientEffect::AdjustFP(std::unique_ptr<FocalInside2PtConicalEffect>(
+                new FocalInside2PtConicalEffect(args, focalX)),
+                args);
     }
 
     const char* name() const override {
@@ -848,9 +849,9 @@
 
     static std::unique_ptr<GrFragmentProcessor> Make(const CreateArgs& args,
                                                      const CircleConicalInfo& info) {
-        auto processor = std::unique_ptr<CircleInside2PtConicalEffect>(
-                new CircleInside2PtConicalEffect(args, info));
-        return processor->isValid() ? std::move(processor) : nullptr;
+        return GrGradientEffect::AdjustFP(std::unique_ptr<CircleInside2PtConicalEffect>(
+                new CircleInside2PtConicalEffect(args, info)),
+                args);
     }
 
     const char* name() const override { return "Two-Point Conical Gradient Inside"; }
@@ -1069,7 +1070,9 @@
 
     static std::unique_ptr<GrFragmentProcessor> Make(const CreateArgs& args,
                                                      const CircleConicalInfo& info) {
-        return std::unique_ptr<GrFragmentProcessor>(new CircleOutside2PtConicalEffect(args, info));
+        return GrGradientEffect::AdjustFP(std::unique_ptr<CircleOutside2PtConicalEffect>(
+                new CircleOutside2PtConicalEffect(args, info)),
+                args);
     }
 
     const char* name() const override { return "Two-Point Conical Gradient Outside"; }
@@ -1335,7 +1338,7 @@
     }
 
     GrGradientEffect::CreateArgs newArgs(args.fContext, args.fShader, &matrix, args.fWrapMode,
-                                         std::move(args.fColorSpaceXform), args.fGammaCorrect);
+                                         args.fDstColorSpace);
 
     if (shader.getStartRadius() < kErrorTol) {
         SkScalar focalX;