Revert "Reimplement TwoPointConicalGradient_gpu"

This reverts commit 84f18c422ae98085c452d3f721ec32228e92f531.

Reason for revert: GMs are incorrect on some mobile devices

Original change's description:
> Reimplement TwoPointConicalGradient_gpu
> 
>     4.24% faster in gradient_conical_clamp_shallow_dither
>     6.43% faster in gradient_conicalOutZero_clamp_hicolor
>    10.41% faster in gradient_conicalOut_clamp_hicolor
>    14.85% faster in gradient_conicalOutZero_clamp_3color
>    16.72% faster in gradient_conicalOut_clamp
>    24.30% faster in gradient_conicalOut_clamp_3color
> 
> CQ_INCLUDE_TRYBOTS=skia.primary:Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-All-Android,Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Debug-All-Android
> 
> Bug: skia:
> Change-Id: I6bc1f24c9463fc9c2acbcba7bd4d55b37ade1613
> Reviewed-on: https://skia-review.googlesource.com/82161
> Reviewed-by: Greg Daniel <egdaniel@google.com>
> Commit-Queue: Yuqian Li <liyuqian@google.com>

TBR=egdaniel@google.com,fmalita@google.com,liyuqian@google.com

Change-Id: Ieb20cce12a958abb8293218b89baf3d5a2aad0a8
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: skia:
Cq-Include-Trybots: skia.primary:Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-All-Android, Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Debug-All-Android
Reviewed-on: https://skia-review.googlesource.com/84801
Reviewed-by: Yuqian Li <liyuqian@google.com>
Commit-Queue: Yuqian Li <liyuqian@google.com>
diff --git a/gm/gradients_2pt_conical.cpp b/gm/gradients_2pt_conical.cpp
index 3eb5ab6..3cda2c6 100644
--- a/gm/gradients_2pt_conical.cpp
+++ b/gm/gradients_2pt_conical.cpp
@@ -49,17 +49,6 @@
                                                  data.fPos, data.fCount, tm, 0, &localMatrix);
 }
 
-static sk_sp<SkShader> Make2ConicalOutsideStrip(const SkPoint pts[2], const GradData& data,
-                                                SkShader::TileMode tm, const SkMatrix& localMatrix) {
-    SkPoint center0, center1;
-    SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
-    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
-    center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
-    center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
-    return SkGradientShader::MakeTwoPointConical(center0, radius1, center1, radius1, data.fColors,
-                                                 data.fPos, data.fCount, tm, 0, &localMatrix);
-}
-
 static sk_sp<SkShader> Make2ConicalOutsideFlip(const SkPoint pts[2], const GradData& data,
                              SkShader::TileMode tm, const SkMatrix& localMatrix) {
     SkPoint center0, center1;
@@ -264,8 +253,7 @@
 
 constexpr GradMaker gGradMakersOutside[] = {
     Make2ConicalOutside, Make2ConicalOutsideFlip,
-    Make2ConicalZeroRadOutside, Make2ConicalZeroRadFlipOutside,
-    Make2ConicalOutsideStrip
+    Make2ConicalZeroRadOutside, Make2ConicalZeroRadFlipOutside
 };
 
 constexpr GradMaker gGradMakersInside[] = {
diff --git a/src/gpu/GrProcessor.cpp b/src/gpu/GrProcessor.cpp
index 8f8e797..4294c9f 100644
--- a/src/gpu/GrProcessor.cpp
+++ b/src/gpu/GrProcessor.cpp
@@ -54,7 +54,7 @@
  * we verify the count is as expected.  If a new factory is added, then these numbers must be
  * manually adjusted.
  */
-static const int kFPFactoryCount = 38;
+static const int kFPFactoryCount = 42;
 static const int kGPFactoryCount = 14;
 static const int kXPFactoryCount = 4;
 
diff --git a/src/gpu/GrProcessor.h b/src/gpu/GrProcessor.h
index 0736d03..5947f9f 100644
--- a/src/gpu/GrProcessor.h
+++ b/src/gpu/GrProcessor.h
@@ -69,6 +69,8 @@
         kBlockInputFragmentProcessor_ClassID,
         kCCPRClipProcessor_ClassID,
         kCircleGeometryProcessor_ClassID,
+        kCircleInside2PtConicalEffect_ClassID,
+        kCircleOutside2PtConicalEffect_ClassID,
         kCircularRRectEffect_ClassID,
         kColorMatrixEffect_ClassID,
         kColorTableEffect_ClassID,
@@ -81,9 +83,11 @@
         kDefaultGeoProc_ClassID,
         kDIEllipseGeometryProcessor_ClassID,
         kDisableColorXP_ClassID,
-        kTwoPointConicalEffect_ClassID,
+        kEdge2PtConicalEffect_ClassID,
         kEllipseGeometryProcessor_ClassID,
         kEllipticalRRectEffect_ClassID,
+        kFocalInside2PtConicalEffect_ClassID,
+        kFocalOutside2PtConicalEffect_ClassID,
         kGP_ClassID,
         kGrAARectEffect_ClassID,
         kGrAlphaThresholdFragmentProcessor_ClassID,
diff --git a/src/shaders/gradients/SkGradientShaderPriv.h b/src/shaders/gradients/SkGradientShaderPriv.h
index 28c4666..9d82e82 100644
--- a/src/shaders/gradients/SkGradientShaderPriv.h
+++ b/src/shaders/gradients/SkGradientShaderPriv.h
@@ -255,7 +255,7 @@
     GrGradientEffect(ClassID classID, const CreateArgs&, bool isOpaque);
     explicit GrGradientEffect(const GrGradientEffect&);  // facilitates clone() implementations
 
-    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
+    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final;
 
     // Helper function used by derived class factories to handle color space transformation and
     // modulation by input alpha.
diff --git a/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp b/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp
index ae8ab9d..a11a466 100644
--- a/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp
+++ b/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp
@@ -9,152 +9,200 @@
 #include "SkTwoPointConicalGradient.h"
 
 #if SK_SUPPORT_GPU
+#include "GrCoordTransform.h"
+#include "GrPaint.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLProgramDataManager.h"
 #include "glsl/GrGLSLUniformHandler.h"
 #include "SkTwoPointConicalGradient_gpu.h"
 
+#include <cmath>
+
 // For brevity
 typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
 
-class TwoPointConicalEffect : public GrGradientEffect {
+static const SkScalar kErrorTol = 0.00001f;
+static const SkScalar kEdgeErrorTol = 5.f * kErrorTol;
+
+/**
+ * We have three general cases for 2pt conical gradients. First we always assume that
+ * the start radius <= end radius. Our first case (kInside_) is when the start circle
+ * is completely enclosed by the end circle. The second case (kOutside_) is the case
+ * when the start circle is either completely outside the end circle or the circles
+ * overlap. The final case (kEdge_) is when the start circle is inside the end one,
+ * but the two are just barely touching at 1 point along their edges.
+ */
+enum ConicalType {
+    kInside_ConicalType,
+    kOutside_ConicalType,
+    kEdge_ConicalType,
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void set_matrix_edge_conical(const SkTwoPointConicalGradient& shader,
+                                    SkMatrix* invLMatrix) {
+    // Inverse of the current local matrix is passed in then,
+    // translate to center1, rotate so center2 is on x axis.
+    const SkPoint& center1 = shader.getStartCenter();
+    const SkPoint& center2 = shader.getEndCenter();
+
+    invLMatrix->postTranslate(-center1.fX, -center1.fY);
+
+    SkPoint diff = center2 - center1;
+    SkScalar diffLen = diff.length();
+    if (0 != diffLen) {
+        SkScalar invDiffLen = SkScalarInvert(diffLen);
+        SkMatrix rot;
+        rot.setSinCos(-invDiffLen * diff.fY, invDiffLen * diff.fX);
+        invLMatrix->postConcat(rot);
+    }
+}
+
+class Edge2PtConicalEffect : public GrGradientEffect {
 public:
-    class DegeneratedGLSLProcessor; // radial (center0 == center1) or strip (r0 == r1) case
-    class FocalGLSLProcessor; // all other cases where we can derive a focal point
+    class GLSLEdge2PtConicalProcessor;
 
-    enum Type {
-        kRadial_Type,
-        kStrip_Type,
-        kFocal_Type
-    };
-
-    struct Data {
-        SkScalar    fRadius0;
-        SkScalar    fDiffRadius;
-        Type        fType;
-        bool        fIsSwapped;
-
-        // Construct from the shader, and set the matrix accordingly
-        Data(const SkTwoPointConicalGradient& shader, SkMatrix& matrix);
-
-        bool operator== (const Data& d) const {
-            return fRadius0 == d.fRadius0 && fDiffRadius == d.fDiffRadius && fType == d.fType &&
-                   fIsSwapped == d.fIsSwapped;
-        }
-    };
-
-    static std::unique_ptr<GrFragmentProcessor> Make(const CreateArgs& args, const Data& data);
-
-    SkScalar diffRadius() const { return fData.fDiffRadius; }
-    SkScalar r0() const { return fData.fRadius0; }
-    SkScalar r1() const { return fData.fRadius0 + fData.fDiffRadius; }
-
-    const char* name() const override { return "Two-Point Conical Gradient"; }
-
-    // Whether the focal point (0, 0) is on the end circle with center (1, 0) and radius r1. If this
-    // is true, it's as if an aircraft is flying at Mach 1 and all circles (soundwaves) will go
-    // through the focal point (aircraft). In our previous implementations, this was known as the
-    // edge case where the inside circle touches the outside circle (on the focal point). If we were
-    // to solve for t bruteforcely using a quadratic equation, this case implies that the quadratic
-    // equation degenerates to a linear equation.
-    bool isFocalOnCircle() const { return SkScalarNearlyZero(1 - this->r1()); }
-    bool isSwapped() const { return fData.fIsSwapped; }
-
-    Type getType() const { return fData.fType; }
-
-    // Whether the t we solved is always valid (so we don't need to check r(t) > 0).
-    bool isWellBehaved() const { return !this->isFocalOnCircle() && this->r1() > 1; }
-
-    // Whether r0 == 0 so it's focal without any transformation
-    bool isNativelyFocal() const { return SkScalarNearlyZero(fData.fRadius0); }
-
-    bool isRadiusIncreasing() const { return fData.fDiffRadius > 0; }
-
-protected:
-    void onGetGLSLProcessorKey(const GrShaderCaps& c, GrProcessorKeyBuilder* b) const override {
-        INHERITED::onGetGLSLProcessorKey(c, b);
-        uint32_t key = 0;
-        key |= fData.fType;
-        SkASSERT(key < (1 << 2));
-        key |= (this->isFocalOnCircle() << 2);
-        key |= (this->isWellBehaved() << 3);
-        key |= (this->isRadiusIncreasing() << 4);
-        key |= (this->isNativelyFocal() << 5);
-        key |= (this->isSwapped() << 6);
-        SkASSERT(key < (1 << 7));
-        b->add32(key);
+    static std::unique_ptr<GrFragmentProcessor> Make(const CreateArgs& args) {
+        return GrGradientEffect::AdjustFP(std::unique_ptr<Edge2PtConicalEffect>(
+                new Edge2PtConicalEffect(args)),
+                args);
     }
 
+    const char* name() const override {
+        return "Two-Point Conical Gradient Edge Touching";
+    }
 
-    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+    // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
+    SkScalar center() const { return fCenterX1; }
+    SkScalar diffRadius() const { return fDiffRadius; }
+    SkScalar radius() const { return fRadius0; }
 
     std::unique_ptr<GrFragmentProcessor> clone() const override {
-        return std::unique_ptr<GrFragmentProcessor>(new TwoPointConicalEffect(*this));
+        return std::unique_ptr<GrFragmentProcessor>(new Edge2PtConicalEffect(*this));
     }
 
+private:
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+
     bool onIsEqual(const GrFragmentProcessor& sBase) const override {
-        const TwoPointConicalEffect& s = sBase.cast<TwoPointConicalEffect>();
-        return (INHERITED::onIsEqual(sBase) && fData == s.fData);
+        const Edge2PtConicalEffect& s = sBase.cast<Edge2PtConicalEffect>();
+        return (INHERITED::onIsEqual(sBase) &&
+                this->fCenterX1 == s.fCenterX1 &&
+                this->fRadius0 == s.fRadius0 &&
+                this->fDiffRadius == s.fDiffRadius);
     }
 
-    explicit TwoPointConicalEffect(const CreateArgs& args, const Data data)
-        : INHERITED(kTwoPointConicalEffect_ClassID, args,
-            false /* opaque: draws transparent black outside of the cone. */)
-        , fData(data) {}
+    explicit Edge2PtConicalEffect(const CreateArgs& args)
+            : INHERITED(kEdge2PtConicalEffect_ClassID, args,
+                        false /* opaque: draws transparent black outside of the cone. */) {
+        const SkTwoPointConicalGradient& shader =
+            *static_cast<const SkTwoPointConicalGradient*>(args.fShader);
+        fCenterX1 = shader.getCenterX1();
+        fRadius0 = shader.getStartRadius();
+        fDiffRadius = shader.getDiffRadius();
+        // We should only be calling this shader if we are degenerate case with touching circles
+        // When deciding if we are in edge case, we scaled by the end radius for cases when the
+        // start radius was close to zero, otherwise we scaled by the start radius.  In addition
+        // Our test for the edge case in set_matrix_circle_conical has a higher tolerance so we
+        // need the sqrt value below
+        SkASSERT(SkScalarAbs(SkScalarAbs(fDiffRadius) - fCenterX1) <
+                 (fRadius0 < kErrorTol ? shader.getEndRadius() * kEdgeErrorTol :
+                                         fRadius0 * sqrt(kEdgeErrorTol)));
 
-    explicit TwoPointConicalEffect(const TwoPointConicalEffect& that)
+        // We pass the linear part of the quadratic as a varying.
+        //    float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z)
+        fBTransform = this->getCoordTransform();
+        SkMatrix& bMatrix = *fBTransform.accessMatrix();
+        SkScalar r0dr = fRadius0 * fDiffRadius;
+        bMatrix[SkMatrix::kMScaleX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMScaleX] +
+                                            r0dr * bMatrix[SkMatrix::kMPersp0]);
+        bMatrix[SkMatrix::kMSkewX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMSkewX] +
+                                           r0dr * bMatrix[SkMatrix::kMPersp1]);
+        bMatrix[SkMatrix::kMTransX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMTransX] +
+                                            r0dr * bMatrix[SkMatrix::kMPersp2]);
+        this->addCoordTransform(&fBTransform);
+    }
+
+    explicit Edge2PtConicalEffect(const Edge2PtConicalEffect& that)
             : INHERITED(that)
-            , fData(that.fData) {}
+            , fBTransform(that.fBTransform)
+            , fCenterX1(that.fCenterX1)
+            , fRadius0(that.fRadius0)
+            , fDiffRadius(that.fDiffRadius) {
+        this->addCoordTransform(&fBTransform);
+    }
 
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST
 
-    Data fData;
+    // @{
+    // Cache of values - these can change arbitrarily, EXCEPT
+    // we shouldn't change between degenerate and non-degenerate?!
+
+    GrCoordTransform fBTransform;
+    SkScalar         fCenterX1;
+    SkScalar         fRadius0;
+    SkScalar         fDiffRadius;
+
+    // @}
 
     typedef GrGradientEffect INHERITED;
 };
 
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(TwoPointConicalEffect);
+class Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor : public GrGradientEffect::GLSLProcessor {
+public:
+    GLSLEdge2PtConicalProcessor(const GrProcessor&);
 
+    virtual void emitCode(EmitArgs&) override;
+
+protected:
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
+
+    UniformHandle fParamUni;
+
+    const char* fVSVaryingName;
+    const char* fFSVaryingName;
+
+    // @{
+    /// Values last uploaded as uniforms
+
+    SkScalar fCachedRadius;
+    SkScalar fCachedDiffRadius;
+
+    // @}
+
+private:
+    typedef GrGradientEffect::GLSLProcessor INHERITED;
+
+};
+
+GrGLSLFragmentProcessor* Edge2PtConicalEffect::onCreateGLSLInstance() const {
+    return new Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor(*this);
+}
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(Edge2PtConicalEffect);
+
+/*
+ * All Two point conical gradient test create functions may occasionally create edge case shaders
+ */
 #if GR_TEST_UTILS
-std::unique_ptr<GrFragmentProcessor> TwoPointConicalEffect::TestCreate(
-        GrProcessorTestData* d) {
+std::unique_ptr<GrFragmentProcessor> Edge2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
     SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
-    SkPoint center2 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
     SkScalar radius1 = d->fRandom->nextUScalar1();
-    SkScalar radius2 = d->fRandom->nextUScalar1();
+    SkPoint center2;
+    SkScalar radius2;
+    do {
+        center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
+        // If the circles are identical the factory will give us an empty shader.
+        // This will happen if we pick identical centers
+    } while (center1 == center2);
 
-    constexpr int   kTestTypeMask           = (1 << 2) - 1,
-                    kTestNativelyFocalBit   = (1 << 2),
-                    kTestFocalOnCircleBit   = (1 << 3),
-                    kTestSwappedBit         = (1 << 4);
-                    // We won't treat isWellDefined and isRadiusIncreasing specially beacuse they
-                    // should have high probability to be turned on and off as we're getting random
-                    // radii and centers.
+    // Below makes sure that circle one is contained within circle two
+    // and both circles are touching on an edge
+    SkPoint diff = center2 - center1;
+    SkScalar diffLen = diff.length();
+    radius2 = radius1 + diffLen;
 
-    int mask = d->fRandom->nextU();
-    int type = mask & kTestTypeMask;
-    if (type == TwoPointConicalEffect::kRadial_Type) {
-        center2 = center1;
-    } else if (type == TwoPointConicalEffect::kStrip_Type) {
-        radius1 = SkTMax(radius1, .1f); // Make sure that the radius is non-zero
-        radius2 = radius1;
-    } else { // kFocal_Type
-        if (kTestNativelyFocalBit & mask) {
-            radius1 = 0;
-        }
-        if (kTestFocalOnCircleBit & mask) {
-            radius2 = radius1 + SkPoint::Distance(center1, center2);
-        }
-        if (kTestSwappedBit & mask) {
-            std::swap(radius1, radius2);
-            radius2 = 0;
-        }
-    }
-
-    if (SkScalarNearlyZero(radius1 - radius2) &&
-            SkScalarNearlyZero(SkPoint::Distance(center1, center2))) {
-        radius2 += .1f; // make sure that we're not degenerated
-    }
     RandomGradientParams params(d->fRandom);
     auto shader = params.fUseColors4f ?
         SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
@@ -170,162 +218,1032 @@
 }
 #endif
 
+Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::GLSLEdge2PtConicalProcessor(const GrProcessor&)
+    : fVSVaryingName(nullptr)
+    , fFSVaryingName(nullptr)
+    , fCachedRadius(-SK_ScalarMax)
+    , fCachedDiffRadius(-SK_ScalarMax) {}
+
+void Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::emitCode(EmitArgs& args) {
+    const Edge2PtConicalEffect& ge = args.fFp.cast<Edge2PtConicalEffect>();
+    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
+    this->emitUniforms(uniformHandler, ge);
+    fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf3_GrSLType,
+                                           "Conical2FSParams");
+
+    SkString cName("c");
+    SkString tName("t");
+    SkString p0; // start radius
+    SkString p1; // start radius squared
+    SkString p2; // difference in radii (r1 - r0)
+
+
+    p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
+    p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
+    p2.appendf("%s.z", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
+
+    // We interpolate the linear component in coords[1].
+    SkASSERT(args.fTransformedCoords[0].getType() == args.fTransformedCoords[1].getType());
+    const char* coords2D;
+    SkString bVar;
+    GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+    if (kHalf3_GrSLType == args.fTransformedCoords[0].getType()) {
+        fragBuilder->codeAppendf("\thalf3 interpolants = half3(%s.xy / %s.z, %s.x / %s.z);\n",
+                                 args.fTransformedCoords[0].c_str(),
+                                 args.fTransformedCoords[0].c_str(),
+                                 args.fTransformedCoords[1].c_str(),
+                                 args.fTransformedCoords[1].c_str());
+        coords2D = "interpolants.xy";
+        bVar = "interpolants.z";
+    } else {
+        coords2D = args.fTransformedCoords[0].c_str();
+        bVar.printf("%s.x", args.fTransformedCoords[1].c_str());
+    }
+
+    // output will default to transparent black (we simply won't write anything
+    // else to it if invalid, instead of discarding or returning prematurely)
+    fragBuilder->codeAppendf("\t%s = half4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
+
+    // c = (x^2)+(y^2) - params[1]
+    fragBuilder->codeAppendf("\thalf %s = dot(%s, %s) - %s;\n",
+                           cName.c_str(), coords2D, coords2D, p1.c_str());
+
+    // linear case: t = -c/b
+    fragBuilder->codeAppendf("\thalf %s = -(%s / %s);\n", tName.c_str(),
+                           cName.c_str(), bVar.c_str());
+
+    // if r(t) > 0, then t will be the x coordinate
+    fragBuilder->codeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
+                             p2.c_str(), p0.c_str());
+    fragBuilder->codeAppend("\t");
+    this->emitColor(fragBuilder,
+                    uniformHandler,
+                    args.fShaderCaps,
+                    ge,
+                    tName.c_str(),
+                    args.fOutputColor,
+                    args.fInputColor,
+                    args.fTexSamplers);
+    fragBuilder->codeAppend("\t}\n");
+}
+
+void Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::onSetData(
+        const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) {
+    INHERITED::onSetData(pdman, processor);
+    const Edge2PtConicalEffect& data = processor.cast<Edge2PtConicalEffect>();
+    SkScalar radius0 = data.radius();
+    SkScalar diffRadius = data.diffRadius();
+
+    if (fCachedRadius != radius0 ||
+        fCachedDiffRadius != diffRadius) {
+
+        pdman.set3f(fParamUni, radius0, radius0 * radius0, diffRadius);
+        fCachedRadius = radius0;
+        fCachedDiffRadius = diffRadius;
+    }
+}
+
 //////////////////////////////////////////////////////////////////////////////
-// DegeneratedGLSLProcessor
+// Focal Conical Gradients
 //////////////////////////////////////////////////////////////////////////////
 
-class TwoPointConicalEffect::DegeneratedGLSLProcessor : public GrGradientEffect::GLSLProcessor {
+static ConicalType set_matrix_focal_conical(const SkTwoPointConicalGradient& shader,
+                                            SkMatrix* invLMatrix, SkScalar* focalX) {
+    // Inverse of the current local matrix is passed in then,
+    // translate, scale, and rotate such that endCircle is unit circle on x-axis,
+    // and focal point is at the origin.
+    ConicalType conicalType;
+    const SkPoint& focal = shader.getStartCenter();
+    const SkPoint& centerEnd = shader.getEndCenter();
+    SkScalar radius = shader.getEndRadius();
+    SkScalar invRadius = 1.f / radius;
+
+    SkMatrix matrix;
+
+    matrix.setTranslate(-centerEnd.fX, -centerEnd.fY);
+    matrix.postScale(invRadius, invRadius);
+
+    SkPoint focalTrans;
+    matrix.mapPoints(&focalTrans, &focal, 1);
+    *focalX = focalTrans.length();
+
+    if (0.f != *focalX) {
+        SkScalar invFocalX = SkScalarInvert(*focalX);
+        SkMatrix rot;
+        rot.setSinCos(-invFocalX * focalTrans.fY, invFocalX * focalTrans.fX);
+        matrix.postConcat(rot);
+    }
+
+    matrix.postTranslate(-(*focalX), 0.f);
+
+    // If the focal point is touching the edge of the circle it will
+    // cause a degenerate case that must be handled separately
+    // kEdgeErrorTol = 5 * kErrorTol was picked after manual testing the
+    // stability trade off versus the linear approx used in the Edge Shader
+    if (SkScalarAbs(1.f - (*focalX)) < kEdgeErrorTol) {
+        return kEdge_ConicalType;
+    }
+
+    // Scale factor 1 / (1 - focalX * focalX)
+    SkScalar oneMinusF2 = 1.f - *focalX * *focalX;
+    SkScalar s = SkScalarInvert(oneMinusF2);
+
+
+    if (s >= 0.f) {
+        conicalType = kInside_ConicalType;
+        matrix.postScale(s, s * SkScalarSqrt(oneMinusF2));
+    } else {
+        conicalType = kOutside_ConicalType;
+        matrix.postScale(s, s);
+    }
+
+    invLMatrix->postConcat(matrix);
+
+    return conicalType;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+class FocalOutside2PtConicalEffect : public GrGradientEffect {
+public:
+    class GLSLFocalOutside2PtConicalProcessor;
+
+    static std::unique_ptr<GrFragmentProcessor> Make(const CreateArgs& args, SkScalar focalX,
+                                                     bool isFlipped) {
+        return GrGradientEffect::AdjustFP(std::unique_ptr<FocalOutside2PtConicalEffect>(
+                new FocalOutside2PtConicalEffect(args, focalX, isFlipped)),
+                args);
+    }
+
+    const char* name() const override {
+        return "Two-Point Conical Gradient Focal Outside";
+    }
+
+    std::unique_ptr<GrFragmentProcessor> clone() const override {
+        return std::unique_ptr<GrFragmentProcessor>(new FocalOutside2PtConicalEffect(*this));
+    }
+
+    bool isFlipped() const { return fIsFlipped; }
+    SkScalar focal() const { return fFocalX; }
+
+private:
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+
+    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
+        const FocalOutside2PtConicalEffect& s = sBase.cast<FocalOutside2PtConicalEffect>();
+        return (INHERITED::onIsEqual(sBase) &&
+                this->fFocalX == s.fFocalX &&
+                this->fIsFlipped == s.fIsFlipped);
+    }
+
+    FocalOutside2PtConicalEffect(const CreateArgs& args, SkScalar focalX, bool isFlipped)
+            : INHERITED(kFocalOutside2PtConicalEffect_ClassID, args,
+                        false /* opaque: draws transparent black outside of the cone. */)
+            , fFocalX(focalX)
+            , fIsFlipped(isFlipped) {}
+
+    explicit FocalOutside2PtConicalEffect(const FocalOutside2PtConicalEffect& that)
+            : INHERITED(that), fFocalX(that.fFocalX), fIsFlipped(that.fIsFlipped) {
+    }
+
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST
+
+    SkScalar         fFocalX;
+    bool             fIsFlipped;
+
+    typedef GrGradientEffect INHERITED;
+};
+
+class FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor
+    : public GrGradientEffect::GLSLProcessor {
+public:
+    GLSLFocalOutside2PtConicalProcessor(const GrProcessor&);
+
+    virtual void emitCode(EmitArgs&) override;
+
 protected:
-    void emitCode(EmitArgs& args) override {
-        const TwoPointConicalEffect& ge = args.fFp.cast<TwoPointConicalEffect>();
-        GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
-        this->emitUniforms(uniformHandler, ge);
-        fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType,
-                                               "Conical2FSParams");
-
-        SkString p0; // r0 for radial case, r0^2 for strip case
-        p0.appendf("%s", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
-        const char* tName = "t"; // the gradient
-
-        GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-        SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
-        const char* p = coords2D.c_str();
-
-        if (ge.getType() == kRadial_Type) {
-            fragBuilder->codeAppendf("\thalf %s = length(%s) - %s;", tName, p, p0.c_str());
-        } else {
-            // output will default to transparent black (we simply won't write anything
-            // else to it if invalid, instead of discarding or returning prematurely)
-            fragBuilder->codeAppendf("\t%s = half4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
-            fragBuilder->codeAppendf("\thalf temp = %s - %s.y * %s.y;", p0.c_str(), p, p);
-            fragBuilder->codeAppendf("\tif (temp >= 0) {");
-            fragBuilder->codeAppendf("\t\thalf %s = %s.x + sqrt(temp);", tName, p);
-            fragBuilder->codeAppend("\t\t");
-        }
-
-        this->emitColor(fragBuilder,
-                        uniformHandler,
-                        args.fShaderCaps,
-                        ge,
-                        tName,
-                        args.fOutputColor,
-                        args.fInputColor,
-                        args.fTexSamplers);
-
-        if (ge.getType() != kRadial_Type) {
-            fragBuilder->codeAppendf("\t}");
-        }
-    }
-
-    void onSetData(const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& p) override {
-        INHERITED::onSetData(pdman, p);
-        const TwoPointConicalEffect& data = p.cast<TwoPointConicalEffect>();
-        // kRadialType should imply r1 - r0 = 1 (after our transformation) so r0 = r0 / (r1 - r0)
-        SkASSERT(data.getType() == kStrip_Type || SkScalarNearlyZero(data.r1() - data.r0() - 1));
-        pdman.set1f(fParamUni, data.getType() == kRadial_Type ? data.r0() : data.r0() * data.r0());
-    }
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
 
     UniformHandle fParamUni;
 
+    const char* fVSVaryingName;
+    const char* fFSVaryingName;
+
+    // @{
+    /// Values last uploaded as uniforms
+
+    SkScalar fCachedFocal;
+    SkScalar fCachedFlipSign;
+
+    // @}
+
 private:
     typedef GrGradientEffect::GLSLProcessor INHERITED;
+
 };
 
-//////////////////////////////////////////////////////////////////////////////
-// FocalGLSLProcessor
+GrGLSLFragmentProcessor* FocalOutside2PtConicalEffect::onCreateGLSLInstance() const {
+    return new FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor(*this);
+}
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalOutside2PtConicalEffect);
+
+/*
+ * All Two point conical gradient test create functions may occasionally create edge case shaders
+ */
+#if GR_TEST_UTILS
+std::unique_ptr<GrFragmentProcessor> FocalOutside2PtConicalEffect::TestCreate(
+        GrProcessorTestData* d) {
+    SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
+    SkScalar radius1 = 0.f;
+    SkPoint center2;
+    SkScalar radius2;
+    do {
+        center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
+        // Need to make sure the centers are not the same or else focal point will be inside
+    } while (center1 == center2);
+
+    SkPoint diff = center2 - center1;
+    SkScalar diffLen = diff.length();
+    // Below makes sure that the focal point is not contained within circle two
+    radius2 = d->fRandom->nextRangeF(0.f, diffLen);
+
+    RandomGradientParams params(d->fRandom);
+    auto shader = params.fUseColors4f ?
+        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
+                                              params.fColors4f, params.fColorSpace, params.fStops,
+                                              params.fColorCount, params.fTileMode) :
+        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
+                                              params.fColors, params.fStops,
+                                              params.fColorCount, params.fTileMode);
+    GrTest::TestAsFPArgs asFPArgs(d);
+    std::unique_ptr<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args());
+    GrAlwaysAssert(fp);
+    return fp;
+}
+#endif
+
+FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor
+                            ::GLSLFocalOutside2PtConicalProcessor(const GrProcessor& processor)
+    : fVSVaryingName(nullptr)
+    , fFSVaryingName(nullptr)
+    , fCachedFocal(SK_ScalarMax)
+    , fCachedFlipSign(SK_ScalarMax) {}
+
+void FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::emitCode(EmitArgs& args) {
+    const FocalOutside2PtConicalEffect& ge = args.fFp.cast<FocalOutside2PtConicalEffect>();
+    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
+    this->emitUniforms(uniformHandler, ge);
+    fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf3_GrSLType,
+                                           "Conical2FSParams");
+    SkString tName("t");
+    // TODO: get rid of these locals?
+    SkString p0; // focalX
+    SkString p1; // 1 - focalX * focalX
+    p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
+    p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
+
+    // params.x = focalX
+    // params.y = 1 - focalX * focalX
+    // params.z = flipSign
+    GrShaderVar params = uniformHandler->getUniformVariable(fParamUni);
+
+    // if we have a float3 from being in perspective, convert it to a float2 first
+    GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+    SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
+    const char* coords2D = coords2DString.c_str();
+
+    // t = p.x * focal.x +/- sqrt(p.x^2 + (1 - focal.x^2) * p.y^2)
+
+    // output will default to transparent black (we simply won't write anything
+    // else to it if invalid, instead of discarding or returning prematurely)
+    fragBuilder->codeAppendf("\t%s = half4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
+
+    fragBuilder->codeAppendf("\thalf xs = %s.x * %s.x;\n", coords2D, coords2D);
+    fragBuilder->codeAppendf("\thalf ys = %s.y * %s.y;\n", coords2D, coords2D);
+    fragBuilder->codeAppendf("\thalf d = xs + %s * ys;\n", p1.c_str());
+    fragBuilder->codeAppendf("\thalf %s = %s.x * %s  + sqrt(d);\n",
+                             tName.c_str(), coords2D, p0.c_str());
+
+    fragBuilder->codeAppendf("\tif (%s.z * %s >= 0.0 && d >= 0.0) {\n",
+                             params.c_str(), tName.c_str());
+    fragBuilder->codeAppend("\t\t");
+    this->emitColor(fragBuilder,
+                    uniformHandler,
+                    args.fShaderCaps,
+                    ge,
+                    tName.c_str(),
+                    args.fOutputColor,
+                    args.fInputColor,
+                    args.fTexSamplers);
+    fragBuilder->codeAppend("\t}\n");
+}
+
+void FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::onSetData(
+        const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) {
+    INHERITED::onSetData(pdman, processor);
+    const FocalOutside2PtConicalEffect& data = processor.cast<FocalOutside2PtConicalEffect>();
+    SkScalar focal = data.focal();
+    SkScalar flipSign = data.isFlipped() ? -1 : 1;
+
+    if (fCachedFocal != focal || fCachedFlipSign != flipSign) {
+        SkScalar oneMinus2F = 1.f - focal * focal;
+
+        pdman.set3f(fParamUni, focal, oneMinus2F, flipSign);
+        fCachedFocal = focal;
+        fCachedFlipSign = flipSign;
+    }
+}
+
 //////////////////////////////////////////////////////////////////////////////
 
-class TwoPointConicalEffect::FocalGLSLProcessor : public GrGradientEffect::GLSLProcessor {
+class FocalInside2PtConicalEffect : public GrGradientEffect {
+public:
+    class GLSLFocalInside2PtConicalProcessor;
+
+    static std::unique_ptr<GrFragmentProcessor> Make(const CreateArgs& args, SkScalar focalX) {
+        return GrGradientEffect::AdjustFP(std::unique_ptr<FocalInside2PtConicalEffect>(
+                new FocalInside2PtConicalEffect(args, focalX)),
+                args);
+    }
+
+    const char* name() const override {
+        return "Two-Point Conical Gradient Focal Inside";
+    }
+
+    std::unique_ptr<GrFragmentProcessor> clone() const override {
+        return std::unique_ptr<GrFragmentProcessor>(new FocalInside2PtConicalEffect(*this));
+    }
+
+    SkScalar focal() const { return fFocalX; }
+
+    typedef FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor GLSLProcessor;
+
+private:
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+
+    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
+        const FocalInside2PtConicalEffect& s = sBase.cast<FocalInside2PtConicalEffect>();
+        return (INHERITED::onIsEqual(sBase) &&
+                this->fFocalX == s.fFocalX);
+    }
+
+    FocalInside2PtConicalEffect(const CreateArgs& args, SkScalar focalX)
+            : INHERITED(kFocalInside2PtConicalEffect_ClassID, args,
+                        args.fShader->colorsAreOpaque()), fFocalX(focalX) {}
+
+    explicit FocalInside2PtConicalEffect(const FocalInside2PtConicalEffect& that)
+            : INHERITED(that), fFocalX(that.fFocalX) {}
+
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST
+
+    SkScalar         fFocalX;
+
+    typedef GrGradientEffect INHERITED;
+};
+
+class FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor
+    : public GrGradientEffect::GLSLProcessor {
+public:
+    GLSLFocalInside2PtConicalProcessor(const GrProcessor&);
+
+    virtual void emitCode(EmitArgs&) override;
+
 protected:
-    void emitCode(EmitArgs& args) override {
-        const TwoPointConicalEffect& ge = args.fFp.cast<TwoPointConicalEffect>();
-        GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
-        this->emitUniforms(uniformHandler, ge);
-        fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf2_GrSLType,
-                                               "Conical2FSParams");
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
 
-        SkString p0; // 1 / r1
-        SkString p1; // r0 / (r1 - r0)
-        p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
-        p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
-        const char* tName = "t"; // the gradient
+    UniformHandle fFocalUni;
 
-        GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-        SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
-        const char* p = coords2D.c_str();
+    const char* fVSVaryingName;
+    const char* fFSVaryingName;
 
-        if (ge.isFocalOnCircle()) {
-            fragBuilder->codeAppendf("\thalf %s_prime = dot(%s, %s) / %s.x;", tName, p, p, p);
-        } else if (ge.isWellBehaved()) {
-            // empty sign is positive
-            char sign = ge.isRadiusIncreasing() ? ' ' : '-';
-            fragBuilder->codeAppendf("\thalf %s_prime = %clength(%s) - %s.x * %s;",
-                    tName, sign, p, p, p0.c_str());
-        } else {
-            char sign = ge.isSwapped() ? '-' : ' ';
-            fragBuilder->codeAppendf("\thalf temp = %s.x * %s.x - %s.y * %s.y;", p, p, p, p);
-            fragBuilder->codeAppendf("\thalf %s_prime = (%csqrt(temp) - %s.x * %s);",
-                    tName, sign, p, p0.c_str());
-        }
-        // "- 0" is much faster than "- p1" so we specialize the navtively focal case where p1 = 0.
-        fragBuilder->codeAppendf("\thalf %s = %s_prime - %s;", tName, tName,
-                ge.isNativelyFocal() ? "0" : p1.c_str());
+    // @{
+    /// Values last uploaded as uniforms
 
-        if (ge.isSwapped()) {
-            fragBuilder->codeAppendf("\t%s = 1 - %s;", tName, tName);
-        }
+    SkScalar fCachedFocal;
 
-        if (!ge.isWellBehaved()) {
-            // output will default to transparent black (we simply won't write anything
-            // else to it if invalid, instead of discarding or returning prematurely)
-            fragBuilder->codeAppendf("\t%s = half4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
-            // r(t) must be nonnegative
-            char direction = ge.isRadiusIncreasing() ? '>' : '<';
-            fragBuilder->codeAppendf("\tif (%s_prime %c= 0.0) {\n", tName, direction);
-            fragBuilder->codeAppend("\t\t");
-        }
-        this->emitColor(fragBuilder,
-                        uniformHandler,
-                        args.fShaderCaps,
-                        ge,
-                        tName,
-                        args.fOutputColor,
-                        args.fInputColor,
-                        args.fTexSamplers);
-        if (!ge.isWellBehaved()) {
-            fragBuilder->codeAppend("\t};");
-        }
+    // @}
+
+private:
+    typedef GrGradientEffect::GLSLProcessor INHERITED;
+
+};
+
+GrGLSLFragmentProcessor* FocalInside2PtConicalEffect::onCreateGLSLInstance() const {
+    return new FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor(*this);
+}
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalInside2PtConicalEffect);
+
+/*
+ * All Two point conical gradient test create functions may occasionally create edge case shaders
+ */
+#if GR_TEST_UTILS
+std::unique_ptr<GrFragmentProcessor> FocalInside2PtConicalEffect::TestCreate(
+        GrProcessorTestData* d) {
+    SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
+    SkScalar radius1 = 0.f;
+    SkPoint center2;
+    SkScalar radius2;
+    do {
+        center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
+        // Below makes sure radius2 is larger enouch such that the focal point
+        // is inside the end circle
+        SkScalar increase = d->fRandom->nextUScalar1();
+        SkPoint diff = center2 - center1;
+        SkScalar diffLen = diff.length();
+        radius2 = diffLen + increase;
+        // If the circles are identical the factory will give us an empty shader.
+    } while (radius1 == radius2 && center1 == center2);
+
+    RandomGradientParams params(d->fRandom);
+    auto shader = params.fUseColors4f ?
+        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
+                                              params.fColors4f, params.fColorSpace, params.fStops,
+                                              params.fColorCount, params.fTileMode) :
+        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
+                                              params.fColors, params.fStops,
+                                              params.fColorCount, params.fTileMode);
+    GrTest::TestAsFPArgs asFPArgs(d);
+    std::unique_ptr<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args());
+    GrAlwaysAssert(fp);
+    return fp;
+}
+#endif
+
+FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor
+                           ::GLSLFocalInside2PtConicalProcessor(const GrProcessor&)
+    : fVSVaryingName(nullptr)
+    , fFSVaryingName(nullptr)
+    , fCachedFocal(SK_ScalarMax) {}
+
+void FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::emitCode(EmitArgs& args) {
+    const FocalInside2PtConicalEffect& ge = args.fFp.cast<FocalInside2PtConicalEffect>();
+    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
+    this->emitUniforms(uniformHandler, ge);
+    fFocalUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType,
+                                           "Conical2FSParams");
+    SkString tName("t");
+
+    // this is the distance along x-axis from the end center to focal point in
+    // transformed coordinates
+    GrShaderVar focal = uniformHandler->getUniformVariable(fFocalUni);
+
+    // if we have a float3 from being in perspective, convert it to a float2 first
+    GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+    SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
+    const char* coords2D = coords2DString.c_str();
+
+    // t = p.x * focalX + length(p)
+    fragBuilder->codeAppendf("\thalf %s = %s.x * %s  + length(%s);\n", tName.c_str(),
+                             coords2D, focal.c_str(), coords2D);
+
+    this->emitColor(fragBuilder,
+                    uniformHandler,
+                    args.fShaderCaps,
+                    ge,
+                    tName.c_str(),
+                    args.fOutputColor,
+                    args.fInputColor,
+                    args.fTexSamplers);
+}
+
+void FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::onSetData(
+        const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) {
+    INHERITED::onSetData(pdman, processor);
+    const FocalInside2PtConicalEffect& data = processor.cast<FocalInside2PtConicalEffect>();
+    SkScalar focal = data.focal();
+
+    if (fCachedFocal != focal) {
+        pdman.set1f(fFocalUni, SkScalarToFloat(focal));
+        fCachedFocal = focal;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Circle Conical Gradients
+//////////////////////////////////////////////////////////////////////////////
+
+struct CircleConicalInfo {
+    SkPoint fCenterEnd;
+    SkScalar fA;
+    SkScalar fB;
+    SkScalar fC;
+
+    bool operator==(const CircleConicalInfo& other) const {
+        return fCenterEnd == other.fCenterEnd
+            && fA         == other.fA
+            && fB         == other.fB
+            && fC         == other.fC;
     }
 
-    void onSetData(const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& p) override {
-        INHERITED::onSetData(pdman, p);
-        const TwoPointConicalEffect& data = p.cast<TwoPointConicalEffect>();
-        SkScalar r0 = data.r0();
-        SkScalar r1 = data.r1();
-        pdman.set2f(fParamUni, 1 / r1, r0 / (r1 - r0));
+    bool operator!=(const CircleConicalInfo& other) const { return !(*this == other); }
+
+    // true when endRadius < startRadius
+    bool isFlipped() const {
+        // B = (endRadius/startRadius - 1) * C
+        return std::signbit(fB) != std::signbit(fC);
+    }
+};
+
+// Returns focal distance along x-axis in transformed coords
+static ConicalType set_matrix_circle_conical(const SkTwoPointConicalGradient& shader,
+                                             SkMatrix* invLMatrix, CircleConicalInfo* info) {
+    // Inverse of the current local matrix is passed in then,
+    // translate and scale such that start circle is on the origin and has radius 1
+    const SkPoint& centerStart = shader.getStartCenter();
+    const SkPoint& centerEnd = shader.getEndCenter();
+    SkScalar radiusStart = shader.getStartRadius();
+    SkScalar radiusEnd = shader.getEndRadius();
+
+    SkMatrix matrix;
+
+    matrix.setTranslate(-centerStart.fX, -centerStart.fY);
+
+    SkScalar invStartRad = 1.f / radiusStart;
+    matrix.postScale(invStartRad, invStartRad);
+
+    radiusEnd /= radiusStart;
+
+    SkPoint centerEndTrans;
+    matrix.mapPoints(&centerEndTrans, &centerEnd, 1);
+
+    SkScalar A = centerEndTrans.fX * centerEndTrans.fX + centerEndTrans.fY * centerEndTrans.fY
+                 - radiusEnd * radiusEnd + 2 * radiusEnd - 1;
+
+    // Check to see if start circle is inside end circle with edges touching.
+    // If touching we return that it is of kEdge_ConicalType, and leave the matrix setting
+    // to the edge shader. kEdgeErrorTol = 5 * kErrorTol was picked after manual testing
+    // so that C = 1 / A is stable, and the linear approximation used in the Edge shader is
+    // still accurate.
+    if (SkScalarAbs(A) < kEdgeErrorTol) {
+        return kEdge_ConicalType;
     }
 
+    SkScalar C = 1.f / A;
+    SkScalar B = (radiusEnd - 1.f) * C;
+
+    matrix.postScale(C, C);
+
+    invLMatrix->postConcat(matrix);
+
+    info->fCenterEnd = centerEndTrans;
+    info->fA = A;
+    info->fB = B;
+    info->fC = C;
+
+    // if A ends up being negative, the start circle is contained completely inside the end cirlce
+    if (A < 0.f) {
+        return kInside_ConicalType;
+    }
+    return kOutside_ConicalType;
+}
+
+class CircleInside2PtConicalEffect : public GrGradientEffect {
+public:
+    class GLSLCircleInside2PtConicalProcessor;
+
+    static std::unique_ptr<GrFragmentProcessor> Make(const CreateArgs& args,
+                                                     const CircleConicalInfo& info) {
+        return GrGradientEffect::AdjustFP(std::unique_ptr<CircleInside2PtConicalEffect>(
+                new CircleInside2PtConicalEffect(args, info)),
+                args);
+    }
+
+    const char* name() const override { return "Two-Point Conical Gradient Inside"; }
+
+    std::unique_ptr<GrFragmentProcessor> clone() const override {
+        return std::unique_ptr<GrFragmentProcessor>(new CircleInside2PtConicalEffect(*this));
+    }
+
+    SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
+    SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
+    SkScalar A() const { return fInfo.fA; }
+    SkScalar B() const { return fInfo.fB; }
+    SkScalar C() const { return fInfo.fC; }
+    bool isFlipped() const { return fInfo.isFlipped(); }
+
+private:
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+
+    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
+        const CircleInside2PtConicalEffect& s = sBase.cast<CircleInside2PtConicalEffect>();
+        return INHERITED::onIsEqual(sBase) && fInfo == s.fInfo;
+    }
+
+    CircleInside2PtConicalEffect(const CreateArgs& args, const CircleConicalInfo& info)
+            : INHERITED(kCircleInside2PtConicalEffect_ClassID, args,
+                        args.fShader->colorsAreOpaque()), fInfo(info) {}
+
+    explicit CircleInside2PtConicalEffect(const CircleInside2PtConicalEffect& that)
+            : INHERITED(that), fInfo(that.fInfo) {}
+
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST
+
+    const CircleConicalInfo fInfo;
+
+    typedef GrGradientEffect INHERITED;
+};
+
+class CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor
+    : public GrGradientEffect::GLSLProcessor {
+public:
+    GLSLCircleInside2PtConicalProcessor(const GrProcessor&);
+
+    virtual void emitCode(EmitArgs&) override;
+
+protected:
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
+
+    UniformHandle fCenterUni;
     UniformHandle fParamUni;
 
+    const char* fVSVaryingName;
+    const char* fFSVaryingName;
+
+    // @{
+    /// Values last uploaded as uniforms
+
+    SkScalar fCachedCenterX;
+    SkScalar fCachedCenterY;
+    SkScalar fCachedA;
+    SkScalar fCachedB;
+    SkScalar fCachedC;
+    SkScalar fCachedFlipSign;
+
+    // @}
+
 private:
     typedef GrGradientEffect::GLSLProcessor INHERITED;
+
 };
 
+GrGLSLFragmentProcessor* CircleInside2PtConicalEffect::onCreateGLSLInstance() const {
+    return new CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor(*this);
+}
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleInside2PtConicalEffect);
+
+/*
+ * All Two point conical gradient test create functions may occasionally create edge case shaders
+ */
+#if GR_TEST_UTILS
+std::unique_ptr<GrFragmentProcessor> CircleInside2PtConicalEffect::TestCreate(
+        GrProcessorTestData* d) {
+    SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
+    SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0
+    SkPoint center2;
+    SkScalar radius2;
+    do {
+        center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
+        // Below makes sure that circle one is contained within circle two
+        SkScalar increase = d->fRandom->nextUScalar1();
+        SkPoint diff = center2 - center1;
+        SkScalar diffLen = diff.length();
+        radius2 = radius1 + diffLen + increase;
+        // If the circles are identical the factory will give us an empty shader.
+    } while (radius1 == radius2 && center1 == center2);
+
+    RandomGradientParams params(d->fRandom);
+    auto shader = params.fUseColors4f ?
+        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
+                                              params.fColors4f, params.fColorSpace, params.fStops,
+                                              params.fColorCount, params.fTileMode) :
+        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
+                                              params.fColors, params.fStops,
+                                              params.fColorCount, params.fTileMode);
+    GrTest::TestAsFPArgs asFPArgs(d);
+    std::unique_ptr<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args());
+    GrAlwaysAssert(fp);
+    return fp;
+}
+#endif
+
+CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor
+                            ::GLSLCircleInside2PtConicalProcessor(const GrProcessor& processor)
+    : fVSVaryingName(nullptr)
+    , fFSVaryingName(nullptr)
+    , fCachedCenterX(SK_ScalarMax)
+    , fCachedCenterY(SK_ScalarMax)
+    , fCachedA(SK_ScalarMax)
+    , fCachedB(SK_ScalarMax)
+    , fCachedC(SK_ScalarMax)
+    , fCachedFlipSign(SK_ScalarMax) {}
+
+void CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::emitCode(EmitArgs& args) {
+    const CircleInside2PtConicalEffect& ge = args.fFp.cast<CircleInside2PtConicalEffect>();
+    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
+    this->emitUniforms(uniformHandler, ge);
+    fCenterUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf2_GrSLType,
+                                            "Conical2FSCenter");
+    fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
+                                           "Conical2FSParams");
+    SkString tName("t");
+
+    GrShaderVar center = uniformHandler->getUniformVariable(fCenterUni);
+    // params.x = A
+    // params.y = B
+    // params.z = C
+    // params.w = flipSign
+    GrShaderVar params = uniformHandler->getUniformVariable(fParamUni);
+
+    // if we have a float3 from being in perspective, convert it to a float2 first
+    GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+    SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
+    const char* coords2D = coords2DString.c_str();
+
+    // p = coords2D
+    // e = center end
+    // r = radius end
+    // A = dot(e, e) - r^2 + 2 * r - 1
+    // B = (r -1) / A
+    // C = 1 / A
+    // d = dot(e, p) + B
+    // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
+    fragBuilder->codeAppendf("\thalf pDotp = dot(%s,  %s);\n", coords2D, coords2D);
+    fragBuilder->codeAppendf("\thalf d = dot(%s,  %s) + %s.y;\n", coords2D, center.c_str(),
+                             params.c_str());
+    fragBuilder->codeAppendf("\thalf %s = d + %s.w * sqrt(d * d - %s.x * pDotp + %s.z);\n",
+                             tName.c_str(), params.c_str(), params.c_str(), params.c_str());
+
+    this->emitColor(fragBuilder,
+                    uniformHandler,
+                    args.fShaderCaps,
+                    ge,
+                    tName.c_str(),
+                    args.fOutputColor,
+                    args.fInputColor,
+                    args.fTexSamplers);
+}
+
+void CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::onSetData(
+        const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) {
+    INHERITED::onSetData(pdman, processor);
+    const CircleInside2PtConicalEffect& data = processor.cast<CircleInside2PtConicalEffect>();
+    SkScalar centerX = data.centerX();
+    SkScalar centerY = data.centerY();
+    SkScalar A = data.A();
+    SkScalar B = data.B();
+    SkScalar C = data.C();
+    SkScalar flipSign = data.isFlipped() ? -1 : 1;
+
+    if (fCachedCenterX != centerX || fCachedCenterY != centerY) {
+        pdman.set2f(fCenterUni, centerX, centerY);
+        fCachedCenterX = centerX;
+        fCachedCenterY = centerY;
+    }
+
+    if (fCachedA != A || fCachedB != B || fCachedC != C || fCachedFlipSign != flipSign) {
+        pdman.set4f(fParamUni, A, B, C, flipSign);
+        fCachedA = A;
+        fCachedB = B;
+        fCachedC = C;
+        fCachedFlipSign = flipSign;
+    }
+}
+
 //////////////////////////////////////////////////////////////////////////////
 
-GrGLSLFragmentProcessor* TwoPointConicalEffect::onCreateGLSLInstance() const {
-    if (fData.fType == kRadial_Type || fData.fType == kStrip_Type) {
-        return new DegeneratedGLSLProcessor;
+class CircleOutside2PtConicalEffect : public GrGradientEffect {
+public:
+    class GLSLCircleOutside2PtConicalProcessor;
+
+    static std::unique_ptr<GrFragmentProcessor> Make(const CreateArgs& args,
+                                                     const CircleConicalInfo& info) {
+        return GrGradientEffect::AdjustFP(std::unique_ptr<CircleOutside2PtConicalEffect>(
+                new CircleOutside2PtConicalEffect(args, info)),
+                args);
     }
-    return new FocalGLSLProcessor;
+
+    const char* name() const override { return "Two-Point Conical Gradient Outside"; }
+
+    std::unique_ptr<GrFragmentProcessor> clone() const override {
+        return std::unique_ptr<GrFragmentProcessor>(new CircleOutside2PtConicalEffect(*this));
+    }
+
+    SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
+    SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
+    SkScalar A() const { return fInfo.fA; }
+    SkScalar B() const { return fInfo.fB; }
+    SkScalar C() const { return fInfo.fC; }
+    SkScalar tLimit() const { return fTLimit; }
+    bool isFlipped() const { return fInfo.isFlipped(); }
+
+private:
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+
+    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
+        const CircleOutside2PtConicalEffect& s = sBase.cast<CircleOutside2PtConicalEffect>();
+        return INHERITED::onIsEqual(sBase) && fInfo == s.fInfo && fTLimit == s.fTLimit;
+    }
+
+    CircleOutside2PtConicalEffect(const CreateArgs& args, const CircleConicalInfo& info)
+            : INHERITED(kCircleOutside2PtConicalEffect_ClassID, args,
+                        false /* opaque: draws transparent black outside of the cone. */)
+            , fInfo(info) {
+        const SkTwoPointConicalGradient& shader =
+            *static_cast<const SkTwoPointConicalGradient*>(args.fShader);
+        if (shader.getStartRadius() != shader.getEndRadius()) {
+            fTLimit = shader.getStartRadius() / (shader.getStartRadius() - shader.getEndRadius());
+        } else {
+            fTLimit = SK_ScalarMin;
+        }
+    }
+
+    explicit CircleOutside2PtConicalEffect(const CircleOutside2PtConicalEffect& that)
+            : INHERITED(that)
+            , fInfo(that.fInfo)
+            , fTLimit(that.fTLimit) {}
+
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST
+
+    const CircleConicalInfo fInfo;
+    SkScalar fTLimit;
+
+    typedef GrGradientEffect INHERITED;
+};
+
+class CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor
+    : public GrGradientEffect::GLSLProcessor {
+public:
+    GLSLCircleOutside2PtConicalProcessor(const GrProcessor&);
+
+    virtual void emitCode(EmitArgs&) override;
+
+protected:
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
+
+    UniformHandle fCenterUni;
+    UniformHandle fParamUni;
+    UniformHandle fFlipSignUni;
+
+    const char* fVSVaryingName;
+    const char* fFSVaryingName;
+
+    // @{
+    /// Values last uploaded as uniforms
+
+    SkScalar fCachedCenterX;
+    SkScalar fCachedCenterY;
+    SkScalar fCachedA;
+    SkScalar fCachedB;
+    SkScalar fCachedC;
+    SkScalar fCachedTLimit;
+    SkScalar fCachedFlipSign;
+
+    // @}
+
+private:
+    typedef GrGradientEffect::GLSLProcessor INHERITED;
+
+};
+
+GrGLSLFragmentProcessor* CircleOutside2PtConicalEffect::onCreateGLSLInstance() const {
+    return new CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor(*this);
 }
 
-std::unique_ptr<GrFragmentProcessor> TwoPointConicalEffect::Make(
-        const GrGradientEffect::CreateArgs& args, const Data& data) {
-    return GrGradientEffect::AdjustFP(
-            std::unique_ptr<TwoPointConicalEffect>(new TwoPointConicalEffect(args, data)),
-            args);
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleOutside2PtConicalEffect);
+
+/*
+ * All Two point conical gradient test create functions may occasionally create edge case shaders
+ */
+#if GR_TEST_UTILS
+std::unique_ptr<GrFragmentProcessor> CircleOutside2PtConicalEffect::TestCreate(
+        GrProcessorTestData* d) {
+    SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
+    SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0
+    SkPoint center2;
+    SkScalar radius2;
+    SkScalar diffLen;
+    do {
+        center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
+        // If the circles share a center than we can't be in the outside case
+    } while (center1 == center2);
+    SkPoint diff = center2 - center1;
+    diffLen = diff.length();
+    // Below makes sure that circle one is not contained within circle two
+    // and have radius2 >= radius to match sorting on cpu side
+    radius2 = radius1 + d->fRandom->nextRangeF(0.f, diffLen);
+
+    RandomGradientParams params(d->fRandom);
+    auto shader = params.fUseColors4f ?
+        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
+                                              params.fColors4f, params.fColorSpace, params.fStops,
+                                              params.fColorCount, params.fTileMode) :
+        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
+                                              params.fColors, params.fStops,
+                                              params.fColorCount, params.fTileMode);
+    GrTest::TestAsFPArgs asFPArgs(d);
+    std::unique_ptr<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args());
+    GrAlwaysAssert(fp);
+    return fp;
 }
+#endif
+
+CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor
+                             ::GLSLCircleOutside2PtConicalProcessor(const GrProcessor& processor)
+    : fVSVaryingName(nullptr)
+    , fFSVaryingName(nullptr)
+    , fCachedCenterX(SK_ScalarMax)
+    , fCachedCenterY(SK_ScalarMax)
+    , fCachedA(SK_ScalarMax)
+    , fCachedB(SK_ScalarMax)
+    , fCachedC(SK_ScalarMax)
+    , fCachedTLimit(SK_ScalarMax)
+    , fCachedFlipSign(SK_ScalarMax) {}
+
+void CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::emitCode(EmitArgs& args) {
+    const CircleOutside2PtConicalEffect& ge = args.fFp.cast<CircleOutside2PtConicalEffect>();
+    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
+    this->emitUniforms(uniformHandler, ge);
+    fCenterUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf2_GrSLType,
+                                            "Conical2FSCenter");
+    fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
+                                           "Conical2FSParams");
+    fFlipSignUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType,
+                                           "Conical2FSFlipSign");
+    SkString tName("t");
+
+    GrShaderVar center = uniformHandler->getUniformVariable(fCenterUni);
+    // params.x = A
+    // params.y = B
+    // params.z = C
+    GrShaderVar params = uniformHandler->getUniformVariable(fParamUni);
+    GrShaderVar flipsign = uniformHandler->getUniformVariable(fFlipSignUni);
+
+    // if we have a float3 from being in perspective, convert it to a float2 first
+    GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+    SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
+    const char* coords2D = coords2DString.c_str();
+
+    // output will default to transparent black (we simply won't write anything
+    // else to it if invalid, instead of discarding or returning prematurely)
+    fragBuilder->codeAppendf("\t%s = half4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
+
+    // p = coords2D
+    // e = center end
+    // r = radius end
+    // A = dot(e, e) - r^2 + 2 * r - 1
+    // B = (r -1) / A
+    // C = 1 / A
+    // d = dot(e, p) + B
+    // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
+
+    fragBuilder->codeAppendf("\thalf pDotp = dot(%s,  %s);\n", coords2D, coords2D);
+    fragBuilder->codeAppendf("\thalf d = dot(%s,  %s) + %s.y;\n", coords2D, center.c_str(),
+                             params.c_str());
+    fragBuilder->codeAppendf("\thalf deter = d * d - %s.x * pDotp + %s.z;\n", params.c_str(),
+                             params.c_str());
+    fragBuilder->codeAppendf("\thalf %s = d + sqrt(deter);\n",
+                             tName.c_str());
+
+    fragBuilder->codeAppendf("\tif (%s * (%s - %s.w) >= 0 && deter >= 0.0) {\n",
+                             flipsign.c_str(), tName.c_str(), params.c_str());
+    fragBuilder->codeAppend("\t\t");
+    this->emitColor(fragBuilder,
+                    uniformHandler,
+                    args.fShaderCaps,
+                    ge,
+                    tName.c_str(),
+                    args.fOutputColor,
+                    args.fInputColor,
+                    args.fTexSamplers);
+    fragBuilder->codeAppend("\t}\n");
+}
+
+void CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::onSetData(
+        const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) {
+    INHERITED::onSetData(pdman, processor);
+    const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>();
+    SkScalar centerX = data.centerX();
+    SkScalar centerY = data.centerY();
+    SkScalar A = data.A();
+    SkScalar B = data.B();
+    SkScalar C = data.C();
+    SkScalar tLimit = data.tLimit();
+    SkScalar flipSign = data.isFlipped() ? -1 : 1;
+
+    if (fCachedCenterX != centerX || fCachedCenterY != centerY) {
+        pdman.set2f(fCenterUni, centerX, centerY);
+        fCachedCenterX = centerX;
+        fCachedCenterY = centerY;
+    }
+
+    if (fCachedA != A || fCachedB != B || fCachedC != C || fCachedTLimit != tLimit) {
+        pdman.set4f(fParamUni, A, B, C, tLimit);
+        fCachedA = A;
+        fCachedB = B;
+        fCachedC = C;
+        fCachedTLimit = tLimit;
+    }
+
+    if (fCachedFlipSign != flipSign) {
+        pdman.set1f(fFlipSignUni, flipSign);
+        fCachedFlipSign = flipSign;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
 
 std::unique_ptr<GrFragmentProcessor> Gr2PtConicalGradientEffect::Make(
         const GrGradientEffect::CreateArgs& args) {
@@ -345,70 +1263,33 @@
     }
 
     GrGradientEffect::CreateArgs newArgs(args.fContext, args.fShader, &matrix, args.fWrapMode,
-        args.fDstColorSpace);
-    // Data and matrix has to be prepared before constructing TwoPointConicalEffect so its parent
-    // class can have the right matrix to work with during construction.
-    TwoPointConicalEffect::Data data(shader, matrix);
-    return TwoPointConicalEffect::Make(newArgs, data);
-}
+                                         args.fDstColorSpace);
 
-TwoPointConicalEffect::Data::Data(const SkTwoPointConicalGradient& shader, SkMatrix& matrix) {
-    fIsSwapped = false;
-    if (SkScalarNearlyZero(shader.getCenterX1())) {
-        fType = kRadial_Type;
-        SkScalar dr = shader.getDiffRadius();
-        // Map center to (0, 0) and scale dr to 1
-        matrix.postTranslate(-shader.getStartCenter().fX, -shader.getStartCenter().fY);
-        matrix.postScale(1 / dr, 1 / dr);
-        fRadius0 = shader.getStartRadius() / dr;
-        fDiffRadius = 1;
-    } else {
-        // Map centers to (0, 0), (1, 0)
-        const SkPoint centers[2] = { shader.getStartCenter(), shader.getEndCenter() };
-        const SkPoint unitvec[2] = { { 0, 0 },{ 1, 0 } };
-        SkMatrix gradientMatrix;
-        // The radial case is already handled so this must succeed
-        SkAssertResult(gradientMatrix.setPolyToPoly(centers, unitvec, 2));
-        matrix.postConcat(gradientMatrix);
-        fRadius0 = shader.getStartRadius() / shader.getCenterX1();
-        fDiffRadius = shader.getDiffRadius() / shader.getCenterX1();
-
-        if (SkScalarNearlyZero(shader.getDiffRadius())) {
-            fType = kStrip_Type;
-        } else { // focal case
-            fType = kFocal_Type;
-            if (SkScalarNearlyZero(shader.getEndRadius())) {
-                // swap r0, r1
-                matrix.postTranslate(-1, 0);
-                matrix.postScale(-1, 1);
-                fRadius0 = 0;
-                fDiffRadius = -fDiffRadius;
-                fIsSwapped = true;
-            }
-
-            // Map {focal point, (1, 0)} to {(0, 0), (1, 0)}
-            SkScalar focalX = - fRadius0 / fDiffRadius;
-            const SkPoint from[2]   = { {focalX, 0}, {1, 0} };
-            const SkPoint to[2]     = { {0, 0}, {1, 0} };
-            SkMatrix focalMatrix;
-            focalMatrix.setPolyToPoly(from, to, 2);
-            matrix.postConcat(focalMatrix);
-            fRadius0 /= SkScalarAbs(1 - focalX);
-            fDiffRadius /= SkScalarAbs(1 - focalX);
-
-            SkScalar r0 = fRadius0;
-            SkScalar r1 = fRadius0 + fDiffRadius;
-            // The following transformations are not reflected on data; they're just to accelerate
-            // the shader computation by saving some arithmatic operations.
-            bool isFocalOnCircle = SkScalarNearlyZero(1 - r1);
-            if (isFocalOnCircle) {
-                matrix.postScale(0.5, 0.5); // r1 = 1 so r1 + 1 = 2 and 0.5 = 1 / (r1 + 1)
-            } else {
-                matrix.postScale(r1 / (r1 * r1 - 1), 1 / sqrt(SkScalarAbs(r1 * r1 - 1)));
-            }
-            matrix.postScale(r1 / (r1 - r0), r1 / (r1 - r0));
+    if (shader.getStartRadius() < kErrorTol) {
+        SkScalar focalX;
+        ConicalType type = set_matrix_focal_conical(shader, &matrix, &focalX);
+        if (type == kInside_ConicalType) {
+            return FocalInside2PtConicalEffect::Make(newArgs, focalX);
+        } else if(type == kEdge_ConicalType) {
+            set_matrix_edge_conical(shader, &matrix);
+            return Edge2PtConicalEffect::Make(newArgs);
+        } else {
+            const bool isFlipped = shader.getStartRadius() > shader.getEndRadius();
+            return FocalOutside2PtConicalEffect::Make(newArgs, focalX, isFlipped);
         }
     }
+
+    CircleConicalInfo info;
+    ConicalType type = set_matrix_circle_conical(shader, &matrix, &info);
+
+    if (type == kInside_ConicalType) {
+        return CircleInside2PtConicalEffect::Make(newArgs, info);
+    } else if (type == kEdge_ConicalType) {
+        set_matrix_edge_conical(shader, &matrix);
+        return Edge2PtConicalEffect::Make(newArgs);
+    } else {
+        return CircleOutside2PtConicalEffect::Make(newArgs, info);
+    }
 }
 
 #endif