Implement sweep gradient layout

Add an FP that implements the sweep gradient effect and updates
SkSweepGradient to use the new system if possible.

Bug: skia:
Change-Id: I3f65da01afafae54c45848a6a78fd758f65eb4a6
Reviewed-on: https://skia-review.googlesource.com/148806
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 463b5ed..f2005e5 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -407,6 +407,8 @@
   "$_src/gpu/gradients/GrLinearGradientLayout.h",
   "$_src/gpu/gradients/GrRadialGradientLayout.cpp",
   "$_src/gpu/gradients/GrRadialGradientLayout.h",
+  "$_src/gpu/gradients/GrSweepGradientLayout.cpp",
+  "$_src/gpu/gradients/GrSweepGradientLayout.h",
   "$_src/gpu/gradients/GrClampedGradientEffect.cpp",
   "$_src/gpu/gradients/GrClampedGradientEffect.h",
   "$_src/gpu/gradients/GrTiledGradientEffect.cpp",
diff --git a/gn/sksl.gni b/gn/sksl.gni
index 4ec691e..e5b2fe1 100644
--- a/gn/sksl.gni
+++ b/gn/sksl.gni
@@ -49,6 +49,7 @@
   "$_src/gpu/gradients/GrSingleIntervalGradientColorizer.fp",
   "$_src/gpu/gradients/GrLinearGradientLayout.fp",
   "$_src/gpu/gradients/GrRadialGradientLayout.fp",
+  "$_src/gpu/gradients/GrSweepGradientLayout.fp",
   "$_src/gpu/gradients/GrClampedGradientEffect.fp",
   "$_src/gpu/gradients/GrTiledGradientEffect.fp",
 ]
diff --git a/src/gpu/GrProcessor.h b/src/gpu/GrProcessor.h
index 0f5b11d..6f5225e 100644
--- a/src/gpu/GrProcessor.h
+++ b/src/gpu/GrProcessor.h
@@ -137,6 +137,7 @@
         kGrSpecularLightingEffect_ClassID,
         kGrSRGBEffect_ClassID,
         kGrSweepGradient_ClassID,
+        kGrSweepGradientLayout_ClassID,
         kGrTextureDomainEffect_ClassID,
         kGrTiledGradientEffect_ClassID,
         kGrUnpremulInputFragmentProcessor_ClassID,
diff --git a/src/gpu/gradients/GrGradientShader.cpp b/src/gpu/gradients/GrGradientShader.cpp
index c277997..50a4c8a 100644
--- a/src/gpu/gradients/GrGradientShader.cpp
+++ b/src/gpu/gradients/GrGradientShader.cpp
@@ -12,6 +12,7 @@
 
 #include "GrLinearGradientLayout.h"
 #include "GrRadialGradientLayout.h"
+#include "GrSweepGradientLayout.h"
 
 #include "GrSingleIntervalGradientColorizer.h"
 
@@ -137,4 +138,9 @@
     return make_gradient(shader,args, GrRadialGradientLayout::Make(shader, args));
 }
 
+std::unique_ptr<GrFragmentProcessor> MakeSweep(const SkSweepGradient& shader,
+                                               const GrFPArgs& args) {
+    return make_gradient(shader,args, GrSweepGradientLayout::Make(shader, args));
+}
+
 }
diff --git a/src/gpu/gradients/GrGradientShader.h b/src/gpu/gradients/GrGradientShader.h
index 22233fb..0a09ff7 100644
--- a/src/gpu/gradients/GrGradientShader.h
+++ b/src/gpu/gradients/GrGradientShader.h
@@ -12,6 +12,7 @@
 #include "SkGradientShaderPriv.h"
 #include "SkLinearGradient.h"
 #include "SkRadialGradient.h"
+#include "SkSweepGradient.h"
 
 namespace GrGradientShader {
     std::unique_ptr<GrFragmentProcessor> MakeLinear(const SkLinearGradient& shader,
@@ -19,6 +20,9 @@
 
     std::unique_ptr<GrFragmentProcessor> MakeRadial(const SkRadialGradient& shader,
                                                     const GrFPArgs& args);
+
+    std::unique_ptr<GrFragmentProcessor> MakeSweep(const SkSweepGradient& shader,
+                                                   const GrFPArgs& args);
 }
 
 #endif // GrGradientShader_DEFINE
diff --git a/src/gpu/gradients/GrSweepGradientLayout.cpp b/src/gpu/gradients/GrSweepGradientLayout.cpp
new file mode 100644
index 0000000..227ff5f
--- /dev/null
+++ b/src/gpu/gradients/GrSweepGradientLayout.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**************************************************************************************************
+ *** This file was autogenerated from GrSweepGradientLayout.fp; do not modify.
+ **************************************************************************************************/
+#include "GrSweepGradientLayout.h"
+#include "glsl/GrGLSLFragmentProcessor.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "glsl/GrGLSLProgramBuilder.h"
+#include "GrTexture.h"
+#include "SkSLCPP.h"
+#include "SkSLUtil.h"
+class GrGLSLSweepGradientLayout : public GrGLSLFragmentProcessor {
+public:
+    GrGLSLSweepGradientLayout() {}
+    void emitCode(EmitArgs& args) override {
+        GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+        const GrSweepGradientLayout& _outer = args.fFp.cast<GrSweepGradientLayout>();
+        (void)_outer;
+        auto gradientMatrix = _outer.gradientMatrix();
+        (void)gradientMatrix;
+        auto bias = _outer.bias();
+        (void)bias;
+        auto scale = _outer.scale();
+        (void)scale;
+        fBiasVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType,
+                                                    kDefault_GrSLPrecision, "bias");
+        fScaleVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType,
+                                                     kDefault_GrSLPrecision, "scale");
+        SkString sk_TransformedCoords2D_0 = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
+        fragBuilder->codeAppendf(
+                "half angle;\nif (sk_Caps.atan2ImplementedAsAtanYOverX) {\n    angle = half(2.0 * "
+                "atan(-%s.y, length(%s) - %s.x));\n} else {\n    angle = half(atan(-%s.y, "
+                "-%s.x));\n}\n%s = half4(((float(float(angle) * 0.15915494309180001) + 0.5) + %s) "
+                "* %s);\n",
+                sk_TransformedCoords2D_0.c_str(), sk_TransformedCoords2D_0.c_str(),
+                sk_TransformedCoords2D_0.c_str(), sk_TransformedCoords2D_0.c_str(),
+                sk_TransformedCoords2D_0.c_str(), args.fOutputColor,
+                args.fUniformHandler->getUniformCStr(fBiasVar),
+                args.fUniformHandler->getUniformCStr(fScaleVar));
+    }
+
+private:
+    void onSetData(const GrGLSLProgramDataManager& pdman,
+                   const GrFragmentProcessor& _proc) override {
+        const GrSweepGradientLayout& _outer = _proc.cast<GrSweepGradientLayout>();
+        {
+            float biasValue = _outer.bias();
+            if (fBiasPrev != biasValue) {
+                fBiasPrev = biasValue;
+                pdman.set1f(fBiasVar, biasValue);
+            }
+            float scaleValue = _outer.scale();
+            if (fScalePrev != scaleValue) {
+                fScalePrev = scaleValue;
+                pdman.set1f(fScaleVar, scaleValue);
+            }
+        }
+    }
+    float fBiasPrev = SK_FloatNaN;
+    float fScalePrev = SK_FloatNaN;
+    UniformHandle fBiasVar;
+    UniformHandle fScaleVar;
+};
+GrGLSLFragmentProcessor* GrSweepGradientLayout::onCreateGLSLInstance() const {
+    return new GrGLSLSweepGradientLayout();
+}
+void GrSweepGradientLayout::onGetGLSLProcessorKey(const GrShaderCaps& caps,
+                                                  GrProcessorKeyBuilder* b) const {}
+bool GrSweepGradientLayout::onIsEqual(const GrFragmentProcessor& other) const {
+    const GrSweepGradientLayout& that = other.cast<GrSweepGradientLayout>();
+    (void)that;
+    if (fGradientMatrix != that.fGradientMatrix) return false;
+    if (fBias != that.fBias) return false;
+    if (fScale != that.fScale) return false;
+    return true;
+}
+GrSweepGradientLayout::GrSweepGradientLayout(const GrSweepGradientLayout& src)
+        : INHERITED(kGrSweepGradientLayout_ClassID, src.optimizationFlags())
+        , fGradientMatrix(src.fGradientMatrix)
+        , fBias(src.fBias)
+        , fScale(src.fScale)
+        , fCoordTransform0(src.fCoordTransform0) {
+    this->addCoordTransform(&fCoordTransform0);
+}
+std::unique_ptr<GrFragmentProcessor> GrSweepGradientLayout::clone() const {
+    return std::unique_ptr<GrFragmentProcessor>(new GrSweepGradientLayout(*this));
+}
+
+std::unique_ptr<GrFragmentProcessor> GrSweepGradientLayout::Make(const SkSweepGradient& grad,
+                                                                 const GrFPArgs& args) {
+    SkMatrix matrix;
+    if (!grad.totalLocalMatrix(args.fPreLocalMatrix, args.fPostLocalMatrix)->invert(&matrix)) {
+        return nullptr;
+    }
+    matrix.postConcat(grad.getGradientMatrix());
+    return std::unique_ptr<GrFragmentProcessor>(
+            new GrSweepGradientLayout(matrix, grad.getTBias(), grad.getTScale()));
+}
diff --git a/src/gpu/gradients/GrSweepGradientLayout.fp b/src/gpu/gradients/GrSweepGradientLayout.fp
new file mode 100644
index 0000000..7b98cb7
--- /dev/null
+++ b/src/gpu/gradients/GrSweepGradientLayout.fp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+in half4x4 gradientMatrix;
+
+layout(tracked) in uniform half bias;
+layout(tracked) in uniform half scale;
+
+@coordTransform {
+    gradientMatrix
+}
+
+void main() {
+    // On some devices they incorrectly implement atan2(y,x) as atan(y/x). In actuality it is
+    // atan2(y,x) = 2 * atan(y / (sqrt(x^2 + y^2) + x)). So to work around this we pass in (sqrt(x^2
+    // + y^2) + x) as the second parameter to atan2 in these cases. We let the device handle the
+    // undefined behavior of the second paramenter being 0 instead of doing the divide ourselves and
+    // using atan instead.
+    half angle;
+    if (sk_Caps.atan2ImplementedAsAtanYOverX) {
+        angle = 2 * atan(-sk_TransformedCoords2D[0].y,
+                         length(sk_TransformedCoords2D[0]) - sk_TransformedCoords2D[0].x);
+    } else {
+        angle = atan(-sk_TransformedCoords2D[0].y, -sk_TransformedCoords2D[0].x);
+    }
+
+    // 0.1591549430918 is 1/(2*pi), used since atan returns values [-pi, pi]
+    sk_OutColor = half4((angle * 0.1591549430918 + 0.5 + bias) * scale);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+@header {
+    #include "SkSweepGradient.h"
+}
+
+@make {
+    static std::unique_ptr<GrFragmentProcessor> Make(const SkSweepGradient& gradient,
+                                                     const GrFPArgs& args);
+}
+
+@cppEnd {
+    std::unique_ptr<GrFragmentProcessor> GrSweepGradientLayout::Make(
+            const SkSweepGradient& grad, const GrFPArgs& args) {
+        SkMatrix matrix;
+        if (!grad.totalLocalMatrix(args.fPreLocalMatrix, args.fPostLocalMatrix)->invert(&matrix)) {
+            return nullptr;
+        }
+        matrix.postConcat(grad.getGradientMatrix());
+        return std::unique_ptr<GrFragmentProcessor>(new GrSweepGradientLayout(
+                matrix, grad.getTBias(), grad.getTScale()));
+    }
+}
diff --git a/src/gpu/gradients/GrSweepGradientLayout.h b/src/gpu/gradients/GrSweepGradientLayout.h
new file mode 100644
index 0000000..ff4e143
--- /dev/null
+++ b/src/gpu/gradients/GrSweepGradientLayout.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**************************************************************************************************
+ *** This file was autogenerated from GrSweepGradientLayout.fp; do not modify.
+ **************************************************************************************************/
+#ifndef GrSweepGradientLayout_DEFINED
+#define GrSweepGradientLayout_DEFINED
+#include "SkTypes.h"
+
+#include "SkSweepGradient.h"
+#include "GrFragmentProcessor.h"
+#include "GrCoordTransform.h"
+class GrSweepGradientLayout : public GrFragmentProcessor {
+public:
+    const SkMatrix44& gradientMatrix() const { return fGradientMatrix; }
+    float bias() const { return fBias; }
+    float scale() const { return fScale; }
+
+    static std::unique_ptr<GrFragmentProcessor> Make(const SkSweepGradient& gradient,
+                                                     const GrFPArgs& args);
+    GrSweepGradientLayout(const GrSweepGradientLayout& src);
+    std::unique_ptr<GrFragmentProcessor> clone() const override;
+    const char* name() const override { return "SweepGradientLayout"; }
+
+private:
+    GrSweepGradientLayout(SkMatrix44 gradientMatrix, float bias, float scale)
+            : INHERITED(kGrSweepGradientLayout_ClassID, kNone_OptimizationFlags)
+            , fGradientMatrix(gradientMatrix)
+            , fBias(bias)
+            , fScale(scale)
+            , fCoordTransform0(gradientMatrix) {
+        this->addCoordTransform(&fCoordTransform0);
+    }
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST
+    SkMatrix44 fGradientMatrix;
+    float fBias;
+    float fScale;
+    GrCoordTransform fCoordTransform0;
+    typedef GrFragmentProcessor INHERITED;
+};
+#endif
diff --git a/src/shaders/gradients/SkSweepGradient.cpp b/src/shaders/gradients/SkSweepGradient.cpp
index 89c5cb1..a37dc8f 100644
--- a/src/shaders/gradients/SkSweepGradient.cpp
+++ b/src/shaders/gradients/SkSweepGradient.cpp
@@ -72,6 +72,8 @@
 #include "gl/GrGLContext.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 
+#include "gradients/GrGradientShader.h"
+
 class GrSweepGradient : public GrGradientEffect {
 public:
     class GLSLSweepProcessor;
@@ -216,6 +218,13 @@
 
 std::unique_ptr<GrFragmentProcessor> SkSweepGradient::asFragmentProcessor(
         const GrFPArgs& args) const {
+
+    // Try to use new gradient system first
+    std::unique_ptr<GrFragmentProcessor> gradient = GrGradientShader::MakeSweep(*this, args);
+    if (gradient) {
+        return gradient;
+    }
+
     SkMatrix matrix;
     if (!this->totalLocalMatrix(args.fPreLocalMatrix, args.fPostLocalMatrix)->invert(&matrix)) {
         return nullptr;
diff --git a/src/shaders/gradients/SkSweepGradient.h b/src/shaders/gradients/SkSweepGradient.h
index a58c957..8957b47 100644
--- a/src/shaders/gradients/SkSweepGradient.h
+++ b/src/shaders/gradients/SkSweepGradient.h
@@ -20,6 +20,10 @@
     std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&) const override;
 #endif
 
+    SkScalar getTBias() const { return fTBias; }
+
+    SkScalar getTScale() const { return fTScale; }
+
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSweepGradient)
 
 protected:
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
index f1c9ed6..bf9e5e1 100644
--- a/src/sksl/SkSLIRGenerator.cpp
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -136,6 +136,7 @@
     CAP(mustEnableSpecificAdvBlendEqs);
     CAP(mustDeclareFragmentShaderOutput);
     CAP(mustDoOpBetweenFloorAndAbs);
+    CAP(atan2ImplementedAsAtanYOverX);
     CAP(canUseAnyFunctionInShader);
     CAP(floatIs32Bits);
     CAP(integerSupport);
diff --git a/src/sksl/SkSLUtil.h b/src/sksl/SkSLUtil.h
index 1654562..b624b58 100644
--- a/src/sksl/SkSLUtil.h
+++ b/src/sksl/SkSLUtil.h
@@ -57,6 +57,10 @@
         return k400_GrGLSLGeneration;
     }
 
+    bool atan2ImplementedAsAtanYOverX() const {
+        return false;
+    }
+
     bool canUseMinAndAbsTogether() const {
         return true;
     }