Add gpu fast path for two point conical gradients.
authorcommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 22 Apr 2014 20:56:37 +0000 (20:56 +0000)
committercommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 22 Apr 2014 20:56:37 +0000 (20:56 +0000)
Also shader changes should help numerical issues on certain android gpus

BUG=179264
R=bsalomon@google.com

Author: egdaniel@google.com

Review URL: https://codereview.chromium.org/238443006

git-svn-id: http://skia.googlecode.com/svn/trunk@14316 2bbb7eff-a529-9590-31e7-b0007b416f81

expectations/gm/ignored-tests.txt
gm/gradients_2pt_conical.cpp [new file with mode: 0644]
gyp/gmslides.gypi
src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp

index dc68fe0..89bc650 100644 (file)
@@ -68,3 +68,5 @@ twopointconical
 lightingcolorfilter
 radial_gradient2
 gradient_dirty_laundry
+shallow_gradient_conical
+
diff --git a/gm/gradients_2pt_conical.cpp b/gm/gradients_2pt_conical.cpp
new file mode 100644 (file)
index 0000000..969d290
--- /dev/null
@@ -0,0 +1,375 @@
+
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "gm.h"
+#include "SkGradientShader.h"
+
+namespace skiagm {
+
+struct GradData {
+    int             fCount;
+    const SkColor*  fColors;
+    const SkScalar* fPos;
+};
+
+static const SkColor gColors[] = {
+    SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
+};
+static const SkScalar gPos0[] = { 0, SK_Scalar1 };
+static const SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
+static const SkScalar gPos2[] = {
+    0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1
+};
+
+static const SkScalar gPosClamp[]   = {0.0f, 0.0f, 1.0f, 1.0f};
+static const SkColor  gColorClamp[] = {
+    SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorBLUE
+};
+
+static const GradData gGradData[] = {
+    { 2, gColors, gPos0 },
+    { 2, gColors, gPos1 },
+    { 5, gColors, gPos2 },
+    { 4, gColorClamp, gPosClamp }
+};
+
+static SkShader* Make2ConicalOutside(const SkPoint pts[2], const GradData& data,
+                             SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center0, center1;
+    SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 10);
+    SkScalar radius1 = SkScalarDiv(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::CreateTwoPointConical(center0, radius0,
+                                                   center1, radius1,
+                                                   data.fColors, data.fPos,
+                                                   data.fCount, tm, mapper);
+}
+
+static SkShader* Make2ConicalOutsideFlip(const SkPoint pts[2], const GradData& data,
+                             SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center0, center1;
+    SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 10);
+    SkScalar radius1 = SkScalarDiv(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::CreateTwoPointConical(center1, radius1,
+                                                   center0, radius0,
+                                                   data.fColors, data.fPos,
+                                                   data.fCount, tm, mapper);
+}
+
+static SkShader* Make2ConicalInside(const SkPoint pts[2], const GradData& data,
+                             SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center0, center1;
+    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
+                SkScalarAve(pts[0].fY, pts[1].fY));
+    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
+                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
+    return SkGradientShader::CreateTwoPointConical(
+                                                  center1, (pts[1].fX - pts[0].fX) / 7,
+                                                  center0, (pts[1].fX - pts[0].fX) / 2,
+                                                  data.fColors, data.fPos, data.fCount, tm, mapper);
+}
+
+static SkShader* Make2ConicalInsideFlip(const SkPoint pts[2], const GradData& data,
+                             SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center0, center1;
+    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
+                SkScalarAve(pts[0].fY, pts[1].fY));
+    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
+                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
+    return SkGradientShader::CreateTwoPointConical(
+                                                  center0, (pts[1].fX - pts[0].fX) / 2,
+                                                  center1, (pts[1].fX - pts[0].fX) / 7,
+                                                  data.fColors, data.fPos, data.fCount, tm, mapper);
+}
+
+static SkShader* Make2ConicalInsideCenter(const SkPoint pts[2], const GradData& data,
+                             SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center0, center1;
+    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
+                SkScalarAve(pts[0].fY, pts[1].fY));
+    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
+                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
+    return SkGradientShader::CreateTwoPointConical(
+                                                  center0, (pts[1].fX - pts[0].fX) / 7,
+                                                  center0, (pts[1].fX - pts[0].fX) / 2,
+                                                  data.fColors, data.fPos, data.fCount, tm, mapper);
+}
+
+static SkShader* Make2ConicalZeroRad(const SkPoint pts[2], const GradData& data,
+                             SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center0, center1;
+    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
+                SkScalarAve(pts[0].fY, pts[1].fY));
+    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
+                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
+    return SkGradientShader::CreateTwoPointConical(
+                                                  center1, 0.0,
+                                                  center0, (pts[1].fX - pts[0].fX) / 2,
+                                                  data.fColors, data.fPos, data.fCount, tm, mapper);
+}
+
+static SkShader* Make2ConicalZeroRadFlip(const SkPoint pts[2], const GradData& data,
+                             SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center0, center1;
+    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
+                SkScalarAve(pts[0].fY, pts[1].fY));
+    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
+                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
+    return SkGradientShader::CreateTwoPointConical(
+                                                  center1, (pts[1].fX - pts[0].fX) / 2,
+                                                  center0, 0.0,
+                                                  data.fColors, data.fPos, data.fCount, tm, mapper);
+}
+
+static SkShader* Make2ConicalZeroRadCenter(const SkPoint pts[2], const GradData& data,
+                             SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center0, center1;
+    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
+                SkScalarAve(pts[0].fY, pts[1].fY));
+    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
+                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
+    return SkGradientShader::CreateTwoPointConical(
+                                                  center0, 0.0,
+                                                  center0, (pts[1].fX - pts[0].fX) / 2,
+                                                  data.fColors, data.fPos, data.fCount, tm, mapper);
+}
+
+static SkShader* Make2ConicalZeroRadOutside(const SkPoint pts[2], const GradData& data,
+                             SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center0, center1;
+    SkScalar radius0 = 0.0;
+    SkScalar radius1 = SkScalarDiv(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::CreateTwoPointConical(center0, radius0,
+                                                   center1, radius1,
+                                                   data.fColors, data.fPos,
+                                                   data.fCount, tm, mapper);
+}
+
+static SkShader* Make2ConicalZeroRadFlipOutside(const SkPoint pts[2], const GradData& data,
+                             SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center0, center1;
+    SkScalar radius0 = 0.0;
+    SkScalar radius1 = SkScalarDiv(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::CreateTwoPointConical(center1, radius1,
+                                                   center0, radius0,
+                                                   data.fColors, data.fPos,
+                                                   data.fCount, tm, mapper);
+}
+
+static SkShader* Make2ConicalEdgeX(const SkPoint pts[2], const GradData& data,
+                             SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center0, center1;
+    SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 7);
+    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
+    center1.set(SkScalarAve(pts[0].fX, pts[1].fX),
+                SkScalarAve(pts[0].fY, pts[1].fY));
+    center0.set(center1.fX + radius1, center1.fY);
+    return SkGradientShader::CreateTwoPointConical(center0, radius0,
+                                                   center1, radius1,
+                                                   data.fColors, data.fPos,
+                                                   data.fCount, tm, mapper);
+}
+
+static SkShader* Make2ConicalEdgeY(const SkPoint pts[2], const GradData& data,
+                             SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center0, center1;
+    SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 7);
+    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
+    center1.set(SkScalarAve(pts[0].fX, pts[1].fX),
+                SkScalarAve(pts[0].fY, pts[1].fY));
+    center0.set(center1.fX, center1.fY + radius1);
+    return SkGradientShader::CreateTwoPointConical(center0, radius0,
+                                                   center1, radius1,
+                                                   data.fColors, data.fPos,
+                                                   data.fCount, tm, mapper);
+}
+static SkShader* Make2ConicalZeroRadEdgeX(const SkPoint pts[2], const GradData& data,
+                             SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center0, center1;
+    SkScalar radius0 = 0.0;
+    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
+    center1.set(SkScalarAve(pts[0].fX, pts[1].fX),
+                SkScalarAve(pts[0].fY, pts[1].fY));
+    center0.set(center1.fX + radius1, center1.fY);
+    return SkGradientShader::CreateTwoPointConical(center0, radius0,
+                                                   center1, radius1,
+                                                   data.fColors, data.fPos,
+                                                   data.fCount, tm, mapper);
+}
+
+static SkShader* Make2ConicalZeroRadEdgeY(const SkPoint pts[2], const GradData& data,
+                             SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center0, center1;
+    SkScalar radius0 = 0.0;
+    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
+    center1.set(SkScalarAve(pts[0].fX, pts[1].fX),
+                SkScalarAve(pts[0].fY, pts[1].fY));
+    center0.set(center1.fX, center1.fY + radius1);
+    return SkGradientShader::CreateTwoPointConical(center0, radius0,
+                                                   center1, radius1,
+                                                   data.fColors, data.fPos,
+                                                   data.fCount, tm, mapper);
+}
+
+static SkShader* Make2ConicalTouchX(const SkPoint pts[2], const GradData& data,
+                             SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center0, center1;
+    SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 7);
+    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
+    center1.set(SkScalarAve(pts[0].fX, pts[1].fX),
+                SkScalarAve(pts[0].fY, pts[1].fY));
+    center0.set(center1.fX - radius1 + radius0, center1.fY);
+    return SkGradientShader::CreateTwoPointConical(center0, radius0,
+                                                   center1, radius1,
+                                                   data.fColors, data.fPos,
+                                                   data.fCount, tm, mapper);
+}
+
+static SkShader* Make2ConicalTouchY(const SkPoint pts[2], const GradData& data,
+                             SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center0, center1;
+    SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 7);
+    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
+    center1.set(SkScalarAve(pts[0].fX, pts[1].fX),
+                SkScalarAve(pts[0].fY, pts[1].fY));
+    center0.set(center1.fX, center1.fY + radius1 - radius0);
+    return SkGradientShader::CreateTwoPointConical(center0, radius0,
+                                                   center1, radius1,
+                                                   data.fColors, data.fPos,
+                                                   data.fCount, tm, mapper);
+}
+
+static SkShader* Make2ConicalInsideSmallRad(const SkPoint pts[2], const GradData& data,
+                             SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center0, center1;
+    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
+                SkScalarAve(pts[0].fY, pts[1].fY));
+    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
+                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
+    return SkGradientShader::CreateTwoPointConical(
+                                                  center0, 0.0000000000000000001,
+                                                  center0, (pts[1].fX - pts[0].fX) / 2,
+                                                  data.fColors, data.fPos, data.fCount, tm, mapper);
+}
+
+typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data,
+                               SkShader::TileMode tm, SkUnitMapper* mapper);
+
+static const GradMaker gGradMakersOutside[] = {
+    Make2ConicalOutside, Make2ConicalOutsideFlip,
+    Make2ConicalZeroRadOutside, Make2ConicalZeroRadFlipOutside
+};
+
+static const GradMaker gGradMakersInside[] = {
+    Make2ConicalInside, Make2ConicalInsideFlip, Make2ConicalInsideCenter,
+    Make2ConicalZeroRad, Make2ConicalZeroRadFlip, Make2ConicalZeroRadCenter,
+};
+
+static const GradMaker gGradMakersEdgeCases[] = {
+    Make2ConicalEdgeX, Make2ConicalEdgeY,
+    Make2ConicalZeroRadEdgeX, Make2ConicalZeroRadEdgeY,
+    Make2ConicalTouchX, Make2ConicalTouchY,
+    Make2ConicalInsideSmallRad
+};
+
+
+static const struct {
+    const GradMaker*   fMaker;
+    const int fCount;
+    const char* fName;
+} gGradCases[] = {
+    { gGradMakersOutside,   SK_ARRAY_COUNT(gGradMakersOutside),     "outside"  },
+    { gGradMakersInside,    SK_ARRAY_COUNT(gGradMakersInside),      "inside"  },
+    { gGradMakersEdgeCases, SK_ARRAY_COUNT(gGradMakersEdgeCases),   "edge"  },
+};
+
+enum GradCaseType { // these must match the order in gGradCases
+    kOutside_GradCaseType,
+    kInside_GradCaseType,
+    kEdge_GradCaseType,
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class ConicalGradientsGM : public GM {
+public:
+    ConicalGradientsGM(GradCaseType gradCaseType) {
+        this->setBGColor(0xFFDDDDDD);
+        fName.printf("gradients_2pt_conical_%s", gGradCases[gradCaseType].fName);
+        fGradCaseType = gradCaseType;
+    }
+
+protected:
+    SkString onShortName() {
+        return fName;
+    }
+
+    virtual SkISize onISize() { return SkISize::Make(840, 815); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+
+        SkPoint pts[2] = {
+            { 0, 0 },
+            { SkIntToScalar(100), SkIntToScalar(100) }
+        };
+        SkShader::TileMode tm = SkShader::kClamp_TileMode;
+        SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
+        SkPaint paint;
+        paint.setAntiAlias(true);
+
+        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
+
+        const GradMaker* gradMaker = gGradCases[fGradCaseType].fMaker;
+        const int count = gGradCases[fGradCaseType].fCount;
+
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
+            canvas->save();
+            for (int j = 0; j < count; j++) {
+                SkShader* shader = gradMaker[j](pts, gGradData[i], tm, NULL);
+
+                if (i == 3) { // if the clamp case
+                    SkMatrix scale;
+                    scale.setScale(0.5f, 0.5f);
+                    scale.postTranslate(25.f, 25.f);
+                    shader->setLocalMatrix(scale);
+                }
+
+                paint.setShader(shader);
+                canvas->drawRect(r, paint);
+                shader->unref();
+                canvas->translate(0, SkIntToScalar(120));
+            }
+            canvas->restore();
+            canvas->translate(SkIntToScalar(120), 0);
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+
+    GradCaseType fGradCaseType;
+    SkString fName;
+};
+///////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory1(void*) { return new ConicalGradientsGM(kInside_GradCaseType); }
+static GMRegistry reg1(MyFactory1);
+
+static GM* MyFactory2(void*) { return new ConicalGradientsGM(kOutside_GradCaseType); }
+static GMRegistry reg2(MyFactory2);
+
+static GM* MyFactory3(void*) { return new ConicalGradientsGM(kEdge_GradCaseType); }
+static GMRegistry reg3(MyFactory3);
+}
+
index e381743..67a9adb 100644 (file)
@@ -80,6 +80,7 @@
     '../gm/getpostextpath.cpp',
     '../gm/giantbitmap.cpp',
     '../gm/gradients.cpp',
+    '../gm/gradients_2pt_conical.cpp',
     '../gm/gradients_no_texture.cpp',
     '../gm/gradientDirtyLaundry.cpp',
     '../gm/gradient_matrix.cpp',
index 7cdb62d..f7b2059 100644 (file)
@@ -1,3 +1,4 @@
+
 /*
  * Copyright 2014 Google Inc.
  *
 // For brevity
 typedef GrGLUniformManager::UniformHandle UniformHandle;
 
+static const SkScalar kErrorTol = 0.00001;
+
+/**
+ * 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_default_conical(const SkTwoPointConicalGradient& shader,
-                                       SkMatrix* invLMatrix) {
+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();
@@ -36,52 +53,51 @@ static void set_matrix_default_conical(const SkTwoPointConicalGradient& shader,
     }
 }
 
-class GLDefault2PtConicalEffect;
+class GLEdge2PtConicalEffect;
 
-class Default2PtConicalEffect : public GrGradientEffect {
+class Edge2PtConicalEffect : public GrGradientEffect {
 public:
 
     static GrEffectRef* Create(GrContext* ctx,
                                const SkTwoPointConicalGradient& shader,
                                const SkMatrix& matrix,
                                SkShader::TileMode tm) {
-        AutoEffectUnref effect(SkNEW_ARGS(Default2PtConicalEffect, (ctx, shader, matrix, tm)));
+        AutoEffectUnref effect(SkNEW_ARGS(Edge2PtConicalEffect, (ctx, shader, matrix, tm)));
         return CreateEffectRef(effect);
     }
 
-    virtual ~Default2PtConicalEffect() { }
+    virtual ~Edge2PtConicalEffect() {}
 
-    static const char* Name() { return "Two-Point Conical Gradient"; }
+    static const char* Name() { return "Two-Point Conical Gradient Edge Touching"; }
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
 
     // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
-    bool isDegenerate() const { return SkScalarAbs(fDiffRadius) == SkScalarAbs(fCenterX1); }
-    bool isFlipped() const { return fIsFlipped; }
     SkScalar center() const { return fCenterX1; }
     SkScalar diffRadius() const { return fDiffRadius; }
     SkScalar radius() const { return fRadius0; }
 
-    typedef GLDefault2PtConicalEffect GLEffect;
+    typedef GLEdge2PtConicalEffect GLEffect;
 
 private:
     virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
-        const Default2PtConicalEffect& s = CastEffect<Default2PtConicalEffect>(sBase);
+        const Edge2PtConicalEffect& s = CastEffect<Edge2PtConicalEffect>(sBase);
         return (INHERITED::onIsEqual(sBase) &&
                 this->fCenterX1 == s.fCenterX1 &&
                 this->fRadius0 == s.fRadius0 &&
-                this->fDiffRadius == s.fDiffRadius &&
-                this->fIsFlipped == s.fIsFlipped);
+                this->fDiffRadius == s.fDiffRadius);
     }
 
-    Default2PtConicalEffect(GrContext* ctx,
-                            const SkTwoPointConicalGradient& shader,
-                            const SkMatrix& matrix,
-                            SkShader::TileMode tm)
+    Edge2PtConicalEffect(GrContext* ctx,
+                         const SkTwoPointConicalGradient& shader,
+                         const SkMatrix& matrix,
+                         SkShader::TileMode tm)
         : INHERITED(ctx, shader, matrix, tm),
         fCenterX1(shader.getCenterX1()),
         fRadius0(shader.getStartRadius()),
-        fDiffRadius(shader.getDiffRadius()),
-        fIsFlipped(shader.isFlippedGrad()) {
+        fDiffRadius(shader.getDiffRadius()){
+        // We should only be calling this shader if we are degenerate case with touching circles
+        SkASSERT(SkScalarAbs(fDiffRadius) - SkScalarAbs(fCenterX1) < kErrorTol) ;
+
         // We pass the linear part of the quadratic as a varying.
         //    float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z)
         fBTransform = this->getCoordTransform();
@@ -106,17 +122,16 @@ private:
     SkScalar         fCenterX1;
     SkScalar         fRadius0;
     SkScalar         fDiffRadius;
-    bool             fIsFlipped;
 
     // @}
 
     typedef GrGradientEffect INHERITED;
 };
 
-class GLDefault2PtConicalEffect : public GrGLGradientEffect {
+class GLEdge2PtConicalEffect : public GrGLGradientEffect {
 public:
-    GLDefault2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&);
-    virtual ~GLDefault2PtConicalEffect() { }
+    GLEdge2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&);
+    virtual ~GLEdge2PtConicalEffect() { }
 
     virtual void emitCode(GrGLShaderBuilder*,
                           const GrDrawEffect&,
@@ -135,13 +150,9 @@ protected:
     const char* fVSVaryingName;
     const char* fFSVaryingName;
 
-    bool fIsDegenerate;
-    bool fIsFlipped;
-
     // @{
     /// Values last uploaded as uniforms
 
-    SkScalar fCachedCenter;
     SkScalar fCachedRadius;
     SkScalar fCachedDiffRadius;
 
@@ -152,25 +163,31 @@ private:
 
 };
 
-const GrBackendEffectFactory& Default2PtConicalEffect::getFactory() const {
-    return GrTBackendEffectFactory<Default2PtConicalEffect>::getInstance();
+const GrBackendEffectFactory& Edge2PtConicalEffect::getFactory() const {
+    return GrTBackendEffectFactory<Edge2PtConicalEffect>::getInstance();
 }
 
-GR_DEFINE_EFFECT_TEST(Default2PtConicalEffect);
+GR_DEFINE_EFFECT_TEST(Edge2PtConicalEffect);
 
-GrEffectRef* Default2PtConicalEffect::TestCreate(SkRandom* random,
-                                            GrContext* context,
-                                            const GrDrawTargetCaps&,
-                                            GrTexture**) {
+GrEffectRef* Edge2PtConicalEffect::TestCreate(SkRandom* random,
+                                              GrContext* context,
+                                              const GrDrawTargetCaps&,
+                                              GrTexture**) {
     SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
     SkScalar radius1 = random->nextUScalar1();
     SkPoint center2;
     SkScalar radius2;
     do {
         center2.set(random->nextUScalar1(), random->nextUScalar1());
-        radius2 = random->nextUScalar1 ();
         // If the circles are identical the factory will give us an empty shader.
-    } while (radius1 == radius2 && center1 == center2);
+        // This will happen if we pick identical centers
+    } while (center1 == center2);
+
+    // 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;
 
     SkColor colors[kMaxRandomGradientColors];
     SkScalar stopsArray[kMaxRandomGradientColors];
@@ -185,62 +202,42 @@ GrEffectRef* Default2PtConicalEffect::TestCreate(SkRandom* random,
     return shader->asNewEffect(context, paint);
 }
 
-
-/////////////////////////////////////////////////////////////////////
-
-GLDefault2PtConicalEffect::GLDefault2PtConicalEffect(const GrBackendEffectFactory& factory,
-                                           const GrDrawEffect& drawEffect)
+GLEdge2PtConicalEffect::GLEdge2PtConicalEffect(const GrBackendEffectFactory& factory,
+                                               const GrDrawEffect& drawEffect)
     : INHERITED(factory)
     , fVSVaryingName(NULL)
     , fFSVaryingName(NULL)
-    , fCachedCenter(SK_ScalarMax)
     , fCachedRadius(-SK_ScalarMax)
-    , fCachedDiffRadius(-SK_ScalarMax) {
-
-    const Default2PtConicalEffect& data = drawEffect.castEffect<Default2PtConicalEffect>();
-    fIsDegenerate = data.isDegenerate();
-    fIsFlipped = data.isFlipped();
-}
-
-void GLDefault2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
-                                    const GrDrawEffect&,
-                                    EffectKey key,
-                                    const char* outputColor,
-                                    const char* inputColor,
-                                    const TransformedCoordsArray& coords,
-                                    const TextureSamplerArray& samplers) {
+    , fCachedDiffRadius(-SK_ScalarMax) {}
+
+void GLEdge2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
+                                      const GrDrawEffect&,
+                                      EffectKey key,
+                                      const char* outputColor,
+                                      const char* inputColor,
+                                      const TransformedCoordsArray& coords,
+                                      const TextureSamplerArray& samplers) {
     this->emitUniforms(builder, key);
     fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
-                                         kFloat_GrSLType, "Conical2FSParams", 6);
+                                         kFloat_GrSLType, "Conical2FSParams", 3);
 
     SkString cName("c");
-    SkString ac4Name("ac4");
-    SkString dName("d");
-    SkString qName("q");
-    SkString r0Name("r0");
-    SkString r1Name("r1");
     SkString tName("t");
-    SkString p0; // 4a
-    SkString p1; // 1/a
-    SkString p2; // distance between centers
-    SkString p3; // start radius
-    SkString p4; // start radius squared
-    SkString p5; // difference in radii (r1 - r0)
+    SkString p0; // start radius
+    SkString p1; // start radius squared
+    SkString p2; // difference in radii (r1 - r0)
 
     builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
     builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
     builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
-    builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3);
-    builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4);
-    builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5);
 
     // We interpolate the linear component in coords[1].
     SkASSERT(coords[0].type() == coords[1].type());
     const char* coords2D;
     SkString bVar;
     if (kVec3f_GrSLType == coords[0].type()) {
-        builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n",
-                               coords[0].c_str(), coords[1].c_str(), coords[0].c_str());
+        builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy / %s.z, %s.x / %s.z);\n",
+                               coords[0].c_str(), coords[0].c_str(), coords[1].c_str(), coords[1].c_str());
         coords2D = "interpolants.xy";
         bVar = "interpolants.z";
     } else {
@@ -252,144 +249,316 @@ void GLDefault2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
     // else to it if invalid, instead of discarding or returning prematurely)
     builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor);
 
-    // c = (x^2)+(y^2) - params[4]
+    // c = (x^2)+(y^2) - params[1]
     builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
-                           cName.c_str(), coords2D, coords2D, p4.c_str());
-
-    // Non-degenerate case (quadratic)
-    if (!fIsDegenerate) {
-
-        // ac4 = params[0] * c
-        builder->fsCodeAppendf("\tfloat %s = %s * %s;\n", ac4Name.c_str(), p0.c_str(),
-                               cName.c_str());
-
-        // d = b^2 - ac4
-        builder->fsCodeAppendf("\tfloat %s = %s * %s - %s;\n", dName.c_str(),
-                               bVar.c_str(), bVar.c_str(), ac4Name.c_str());
-
-        // only proceed if discriminant is >= 0
-        builder->fsCodeAppendf("\tif (%s >= 0.0) {\n", dName.c_str());
-
-        // intermediate value we'll use to compute the roots
-        // q = -0.5 * (b +/- sqrt(d))
-        builder->fsCodeAppendf("\t\tfloat %s = -0.5 * (%s + (%s < 0.0 ? -1.0 : 1.0)"
-                               " * sqrt(%s));\n", qName.c_str(), bVar.c_str(),
-                               bVar.c_str(), dName.c_str());
-
-        // compute both roots
-        // r0 = q * params[1]
-        builder->fsCodeAppendf("\t\tfloat %s = %s * %s;\n", r0Name.c_str(),
-                               qName.c_str(), p1.c_str());
-        // r1 = c / q
-        builder->fsCodeAppendf("\t\tfloat %s = %s / %s;\n", r1Name.c_str(),
-                               cName.c_str(), qName.c_str());
-
-        // Note: If there are two roots that both generate radius(t) > 0, the
-        // Canvas spec says to choose the larger t.
-
-        // so we'll look at the larger one first (or smaller if flipped):
-        if (!fIsFlipped) {
-            builder->fsCodeAppendf("\t\tfloat %s = max(%s, %s);\n", tName.c_str(),
-                                   r0Name.c_str(), r1Name.c_str());
-        } else {
-            builder->fsCodeAppendf("\t\tfloat %s = min(%s, %s);\n", tName.c_str(),
-                                   r0Name.c_str(), r1Name.c_str());
-        }
+                           cName.c_str(), coords2D, coords2D, p1.c_str());
+
+    // linear case: t = -c/b
+    builder->fsCodeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(),
+                           cName.c_str(), bVar.c_str());
+
+    // if r(t) > 0, then t will be the x coordinate
+    builder->fsCodeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
+                           p2.c_str(), p0.c_str());
+    builder->fsCodeAppend("\t");
+    this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
+    builder->fsCodeAppend("\t}\n");
+}
 
-        // if r(t) > 0, then we're done; t will be our x coordinate
-        builder->fsCodeAppendf("\t\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
-                               p5.c_str(), p3.c_str());
+void GLEdge2PtConicalEffect::setData(const GrGLUniformManager& uman,
+                                     const GrDrawEffect& drawEffect) {
+    INHERITED::setData(uman, drawEffect);
+    const Edge2PtConicalEffect& data = drawEffect.castEffect<Edge2PtConicalEffect>();
+    SkScalar radius0 = data.radius();
+    SkScalar diffRadius = data.diffRadius();
 
-        builder->fsCodeAppend("\t\t");
-        this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
+    if (fCachedRadius != radius0 ||
+        fCachedDiffRadius != diffRadius) {
 
-        // otherwise, if r(t) for the larger root was <= 0, try the other root
-        builder->fsCodeAppend("\t\t} else {\n");
-        if (!fIsFlipped) {
-            builder->fsCodeAppendf("\t\t\t%s = min(%s, %s);\n", tName.c_str(),
-                                   r0Name.c_str(), r1Name.c_str());
-        } else {
-            builder->fsCodeAppendf("\t\t\t%s = max(%s, %s);\n", tName.c_str(),
-                                   r0Name.c_str(), r1Name.c_str());
-        }
+        float values[3] = {
+            SkScalarToFloat(radius0),
+            SkScalarToFloat(SkScalarMul(radius0, radius0)),
+            SkScalarToFloat(diffRadius)
+        };
+
+        uman.set1fv(fParamUni, 3, values);
+        fCachedRadius = radius0;
+        fCachedDiffRadius = diffRadius;
+    }
+}
+
+GrGLEffect::EffectKey GLEdge2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect,
+                                                     const GrGLCaps&) {
+    return GenBaseGradientKey(drawEffect);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Focal Conical Gradients
+//////////////////////////////////////////////////////////////////////////////
+
+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.0 / radius;
+
+    SkMatrix matrix;
 
-        // if r(t) > 0 for the smaller root, then t will be our x coordinate
-        builder->fsCodeAppendf("\t\t\tif (%s * %s + %s > 0.0) {\n",
-                               tName.c_str(), p5.c_str(), p3.c_str());
+    matrix.setTranslate(-centerEnd.fX, -centerEnd.fY);
+    matrix.postScale(invRadius, invRadius);
 
-        builder->fsCodeAppend("\t\t\t");
-        this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
+    SkPoint focalTrans;
+    matrix.mapPoints(&focalTrans, &focal, 1);
+    *focalX = focalTrans.length();
 
-        // end if (r(t) > 0) for smaller root
-        builder->fsCodeAppend("\t\t\t}\n");
-        // end if (r(t) > 0), else, for larger root
-        builder->fsCodeAppend("\t\t}\n");
-        // end if (discriminant >= 0)
-        builder->fsCodeAppend("\t}\n");
+    if (0.0 != *focalX) {
+        SkScalar invFocalX = SkScalarInvert(*focalX);
+        SkMatrix rot;
+        rot.setSinCos(-SkScalarMul(invFocalX, focalTrans.fY),
+                      SkScalarMul(invFocalX, focalTrans.fX));
+        matrix.postConcat(rot);
+    }
+
+    matrix.postTranslate(-(*focalX), 0.0);
+
+    // If the focal point is touching the edge of the circle it will
+    // cause a degenerate case that must be handled separately
+    // 5 * kErrorTol was picked after manual testing the stability trade off
+    // versus the linear approx used in the Edge Shader
+    if (SkScalarAbs(1.0 - (*focalX)) < 5 *  kErrorTol) {
+        return kEdge_ConicalType;
+    }
+
+    // Scale factor 1 / (1 - focalX * focalX)
+    SkScalar oneMinusF2 = 1.0 - SkScalarMul(*focalX, *focalX);
+    SkScalar s = SkScalarDiv(1.0, oneMinusF2);
+
+
+    if (s >= 0.0) {
+        conicalType = kInside_ConicalType;
+        matrix.postScale(s, s * SkScalarSqrt(oneMinusF2));
     } else {
+        conicalType = kOutside_ConicalType;
+        matrix.postScale(s, s);
+    }
+
+    invLMatrix->postConcat(matrix);
+
+    return conicalType;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+class GLFocalOutside2PtConicalEffect;
+
+class FocalOutside2PtConicalEffect : public GrGradientEffect {
+public:
+
+    static GrEffectRef* Create(GrContext* ctx,
+                               const SkTwoPointConicalGradient& shader,
+                               const SkMatrix& matrix,
+                               SkShader::TileMode tm,
+                               SkScalar focalX) {
+        AutoEffectUnref effect(SkNEW_ARGS(FocalOutside2PtConicalEffect, (ctx, shader, matrix, tm, focalX)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual ~FocalOutside2PtConicalEffect() { }
+
+    static const char* Name() { return "Two-Point Conical Gradient Focal Outside"; }
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+
+    bool isFlipped() const { return fIsFlipped; }
+    SkScalar focal() const { return fFocalX; }
+
+    typedef GLFocalOutside2PtConicalEffect GLEffect;
+
+private:
+    virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
+        const FocalOutside2PtConicalEffect& s = CastEffect<FocalOutside2PtConicalEffect>(sBase);
+        return (INHERITED::onIsEqual(sBase) &&
+                this->fFocalX == s.fFocalX &&
+                this->fIsFlipped == s.fIsFlipped);
+    }
+
+    FocalOutside2PtConicalEffect(GrContext* ctx,
+                                 const SkTwoPointConicalGradient& shader,
+                                 const SkMatrix& matrix,
+                                 SkShader::TileMode tm,
+                                 SkScalar focalX)
+    : INHERITED(ctx, shader, matrix, tm), fFocalX(focalX), fIsFlipped(shader.isFlippedGrad()) {}
+
+    GR_DECLARE_EFFECT_TEST;
+
+    SkScalar         fFocalX;
+    bool             fIsFlipped;
+
+    typedef GrGradientEffect INHERITED;
+};
+
+class GLFocalOutside2PtConicalEffect : public GrGLGradientEffect {
+public:
+    GLFocalOutside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&);
+    virtual ~GLFocalOutside2PtConicalEffect() { }
+
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrDrawEffect&,
+                          EffectKey,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TransformedCoordsArray&,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+    virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
+
+    static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
+
+protected:
+    UniformHandle fParamUni;
+
+    const char* fVSVaryingName;
+    const char* fFSVaryingName;
+
+    bool fIsFlipped;
+
+    // @{
+    /// Values last uploaded as uniforms
+
+    SkScalar fCachedFocal;
+
+    // @}
+
+private:
+    typedef GrGLGradientEffect INHERITED;
+
+};
+
+const GrBackendEffectFactory& FocalOutside2PtConicalEffect::getFactory() const {
+    return GrTBackendEffectFactory<FocalOutside2PtConicalEffect>::getInstance();
+}
+
+GR_DEFINE_EFFECT_TEST(FocalOutside2PtConicalEffect);
+
+GrEffectRef* FocalOutside2PtConicalEffect::TestCreate(SkRandom* random,
+                                                      GrContext* context,
+                                                      const GrDrawTargetCaps&,
+                                                      GrTexture**) {
+    SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
+    SkScalar radius1 = 0.0;
+    SkPoint center2;
+    SkScalar radius2;
+    do {
+        center2.set(random->nextUScalar1(), random->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 = random->nextRangeF(0.0, diffLen);
+
+    SkColor colors[kMaxRandomGradientColors];
+    SkScalar stopsArray[kMaxRandomGradientColors];
+    SkScalar* stops = stopsArray;
+    SkShader::TileMode tm;
+    int colorCount = RandomGradientParams(random, colors, &stops, &tm);
+    SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
+                                                                          center2, radius2,
+                                                                          colors, stops, colorCount,
+                                                                          tm));
+    SkPaint paint;
+    return shader->asNewEffect(context, paint);
+}
+
+GLFocalOutside2PtConicalEffect::GLFocalOutside2PtConicalEffect(const GrBackendEffectFactory& factory,
+                                                               const GrDrawEffect& drawEffect)
+    : INHERITED(factory)
+    , fVSVaryingName(NULL)
+    , fFSVaryingName(NULL)
+    , fCachedFocal(SK_ScalarMax) {
+    const FocalOutside2PtConicalEffect& data = drawEffect.castEffect<FocalOutside2PtConicalEffect>();
+    fIsFlipped = data.isFlipped();
+}
+
+void GLFocalOutside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
+                                              const GrDrawEffect&,
+                                              EffectKey key,
+                                              const char* outputColor,
+                                              const char* inputColor,
+                                              const TransformedCoordsArray& coords,
+                                              const TextureSamplerArray& samplers) {
+    this->emitUniforms(builder, key);
+    fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
+                                         kFloat_GrSLType, "Conical2FSParams", 2);
+    SkString tName("t");
+    SkString p0; // focalX
+    SkString p1; // 1 - focalX * focalX
+
+    builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
+    builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
+
+    // if we have a vec3 from being in perspective, convert it to a vec2 first
+    SkString coords2DString = builder->ensureFSCoords2D(coords, 0); 
+    const char* coords2D = coords2DString.c_str(); 
+
+    // t = p.x * focal.x +/- sqrt(p.x^2 + (1 - focal.x^2) * p.y^2)
 
-        // linear case: t = -c/b
-        builder->fsCodeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(),
-                               cName.c_str(), bVar.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)
+    builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor);
+
+    builder->fsCodeAppendf("\tfloat xs = %s.x * %s.x;\n", coords2D, coords2D);
+    builder->fsCodeAppendf("\tfloat ys = %s.y * %s.y;\n", coords2D, coords2D);
+    builder->fsCodeAppendf("\tfloat d = xs + %s * ys;\n", p1.c_str());
 
-        // if r(t) > 0, then t will be the x coordinate
-        builder->fsCodeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
-                               p5.c_str(), p3.c_str());
-        builder->fsCodeAppend("\t");
-        this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
-        builder->fsCodeAppend("\t}\n");
+    // Must check to see if we flipped the circle order (to make sure start radius < end radius)
+    // If so we must also flip sign on sqrt
+    if (!fIsFlipped) {
+        builder->fsCodeAppendf("\tfloat %s = %s.x * %s  + sqrt(d);\n", tName.c_str(),
+                               coords2D, p0.c_str());
+    } else {
+        builder->fsCodeAppendf("\tfloat %s = %s.x * %s  - sqrt(d);\n", tName.c_str(),
+                               coords2D, p0.c_str());
     }
+
+    builder->fsCodeAppendf("\tif (%s >= 0.0 && d >= 0.0) {\n", tName.c_str());
+    builder->fsCodeAppend("\t\t");
+    this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
+    builder->fsCodeAppend("\t}\n");
 }
 
-void GLDefault2PtConicalEffect::setData(const GrGLUniformManager& uman,
-                                   const GrDrawEffect& drawEffect) {
+void GLFocalOutside2PtConicalEffect::setData(const GrGLUniformManager& uman,
+                                             const GrDrawEffect& drawEffect) {
     INHERITED::setData(uman, drawEffect);
-    const Default2PtConicalEffect& data = drawEffect.castEffect<Default2PtConicalEffect>();
-    SkASSERT(data.isDegenerate() == fIsDegenerate);
+    const FocalOutside2PtConicalEffect& data = drawEffect.castEffect<FocalOutside2PtConicalEffect>();
     SkASSERT(data.isFlipped() == fIsFlipped);
-    SkScalar centerX1 = data.center();
-    SkScalar radius0 = data.radius();
-    SkScalar diffRadius = data.diffRadius();
+    SkScalar focal = data.focal();
 
-    if (fCachedCenter != centerX1 ||
-        fCachedRadius != radius0 ||
-        fCachedDiffRadius != diffRadius) {
+    if (fCachedFocal != focal) {
+        SkScalar oneMinus2F = 1.0 - SkScalarMul(focal, focal);
 
-        SkScalar a = SkScalarMul(centerX1, centerX1) - diffRadius * diffRadius;
-
-        // When we're in the degenerate (linear) case, the second
-        // value will be INF but the program doesn't read it. (We
-        // use the same 6 uniforms even though we don't need them
-        // all in the linear case just to keep the code complexity
-        // down).
-        float values[6] = {
-            SkScalarToFloat(a * 4),
-            1.f / (SkScalarToFloat(a)),
-            SkScalarToFloat(centerX1),
-            SkScalarToFloat(radius0),
-            SkScalarToFloat(SkScalarMul(radius0, radius0)),
-            SkScalarToFloat(diffRadius)
+        float values[2] = {
+            SkScalarToFloat(focal),
+            SkScalarToFloat(oneMinus2F),
         };
 
-        uman.set1fv(fParamUni, 6, values);
-        fCachedCenter = centerX1;
-        fCachedRadius = radius0;
-        fCachedDiffRadius = diffRadius;
+        uman.set1fv(fParamUni, 2, values);
+        fCachedFocal = focal;
     }
 }
 
-GrGLEffect::EffectKey GLDefault2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect,
-                                                   const GrGLCaps&) {
+GrGLEffect::EffectKey GLFocalOutside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect,
+                                                             const GrGLCaps&) {
     enum {
-        kIsDegenerate = 1 << kBaseKeyBitCnt,
-        kIsFlipped = 1 << (kBaseKeyBitCnt + 1),
+        kIsFlipped = 1 << kBaseKeyBitCnt,
     };
 
     EffectKey key = GenBaseGradientKey(drawEffect);
-    if (drawEffect.castEffect<Default2PtConicalEffect>().isDegenerate()) {
-        key |= kIsDegenerate;
-    }
-    if (drawEffect.castEffect<Default2PtConicalEffect>().isFlipped()) {
+
+    if (drawEffect.castEffect<FocalOutside2PtConicalEffect>().isFlipped()) {
         key |= kIsFlipped;
     }
     return key;
@@ -397,17 +566,737 @@ GrGLEffect::EffectKey GLDefault2PtConicalEffect::GenKey(const GrDrawEffect& draw
 
 //////////////////////////////////////////////////////////////////////////////
 
-GrEffectRef* Gr2PtConicalGradientEffect::Create(GrContext* ctx,
-                                                const SkTwoPointConicalGradient& shader,
-                                                SkShader::TileMode tm) {
+class GLFocalInside2PtConicalEffect;
 
-    SkMatrix matrix;
-    if (!shader.getLocalMatrix().invert(&matrix)) {
-        return NULL;
+class FocalInside2PtConicalEffect : public GrGradientEffect {
+public:
+
+    static GrEffectRef* Create(GrContext* ctx,
+                               const SkTwoPointConicalGradient& shader,
+                               const SkMatrix& matrix,
+                               SkShader::TileMode tm,
+                               SkScalar focalX) {
+        AutoEffectUnref effect(SkNEW_ARGS(FocalInside2PtConicalEffect, (ctx, shader, matrix, tm, focalX)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual ~FocalInside2PtConicalEffect() {}
+
+    static const char* Name() { return "Two-Point Conical Gradient Focal Inside"; }
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+
+    SkScalar focal() const { return fFocalX; }
+
+    typedef GLFocalInside2PtConicalEffect GLEffect;
+
+private:
+    virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
+        const FocalInside2PtConicalEffect& s = CastEffect<FocalInside2PtConicalEffect>(sBase);
+        return (INHERITED::onIsEqual(sBase) &&
+                this->fFocalX == s.fFocalX);
     }
 
-    set_matrix_default_conical(shader, &matrix);
-    return Default2PtConicalEffect::Create(ctx, shader, matrix, tm);
+    FocalInside2PtConicalEffect(GrContext* ctx,
+                                const SkTwoPointConicalGradient& shader,
+                                const SkMatrix& matrix,
+                                SkShader::TileMode tm,
+                                SkScalar focalX)
+        : INHERITED(ctx, shader, matrix, tm), fFocalX(focalX) {}
+
+    GR_DECLARE_EFFECT_TEST;
+
+    SkScalar         fFocalX;
+
+    typedef GrGradientEffect INHERITED;
+};
+
+class GLFocalInside2PtConicalEffect : public GrGLGradientEffect {
+public:
+    GLFocalInside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&);
+    virtual ~GLFocalInside2PtConicalEffect() {}
+
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrDrawEffect&,
+                          EffectKey,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TransformedCoordsArray&,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+    virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
+
+    static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
+
+protected:
+    UniformHandle fFocalUni;
+
+    const char* fVSVaryingName;
+    const char* fFSVaryingName;
+
+    // @{
+    /// Values last uploaded as uniforms
+
+    SkScalar fCachedFocal;
+
+    // @}
+
+private:
+    typedef GrGLGradientEffect INHERITED;
+
+};
+
+const GrBackendEffectFactory& FocalInside2PtConicalEffect::getFactory() const {
+    return GrTBackendEffectFactory<FocalInside2PtConicalEffect>::getInstance();
+}
+
+GR_DEFINE_EFFECT_TEST(FocalInside2PtConicalEffect);
+
+GrEffectRef* FocalInside2PtConicalEffect::TestCreate(SkRandom* random,
+                                                     GrContext* context,
+                                                     const GrDrawTargetCaps&,
+                                                     GrTexture**) {
+    SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
+    SkScalar radius1 = 0.0;
+    SkPoint center2;
+    SkScalar radius2;
+    do {
+        center2.set(random->nextUScalar1(), random->nextUScalar1());
+        // Below makes sure radius2 is larger enouch such that the focal point
+        // is inside the end circle
+        SkScalar increase = random->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);
+
+    SkColor colors[kMaxRandomGradientColors];
+    SkScalar stopsArray[kMaxRandomGradientColors];
+    SkScalar* stops = stopsArray;
+    SkShader::TileMode tm;
+    int colorCount = RandomGradientParams(random, colors, &stops, &tm);
+    SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
+                                                                          center2, radius2,
+                                                                          colors, stops, colorCount,
+                                                                          tm));
+    SkPaint paint;
+    return shader->asNewEffect(context, paint);
+}
+
+GLFocalInside2PtConicalEffect::GLFocalInside2PtConicalEffect(const GrBackendEffectFactory& factory,
+                                                             const GrDrawEffect& drawEffect)
+    : INHERITED(factory)
+    , fVSVaryingName(NULL)
+    , fFSVaryingName(NULL)
+    , fCachedFocal(SK_ScalarMax) {}
+
+void GLFocalInside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
+                                             const GrDrawEffect&,
+                                             EffectKey key,
+                                             const char* outputColor,
+                                             const char* inputColor,
+                                             const TransformedCoordsArray& coords,
+                                             const TextureSamplerArray& samplers) {
+    this->emitUniforms(builder, key);
+    fFocalUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
+                                    kFloat_GrSLType, "Conical2FSParams");
+    SkString tName("t");
+
+    // this is the distance along x-axis from the end center to focal point in
+    // transformed coordinates
+    GrGLShaderVar focal = builder->getUniformVariable(fFocalUni);
+
+    // if we have a vec3 from being in perspective, convert it to a vec2 first
+    SkString coords2DString = builder->ensureFSCoords2D(coords, 0); 
+    const char* coords2D = coords2DString.c_str(); 
+
+    // t = p.x * focalX + length(p)
+    builder->fsCodeAppendf("\tfloat %s = %s.x * %s  + length(%s);\n", tName.c_str(),
+                           coords2D, focal.c_str(), coords2D);
+
+    this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
+}
+
+void GLFocalInside2PtConicalEffect::setData(const GrGLUniformManager& uman,
+                                            const GrDrawEffect& drawEffect) {
+    INHERITED::setData(uman, drawEffect);
+    const FocalInside2PtConicalEffect& data = drawEffect.castEffect<FocalInside2PtConicalEffect>();
+    SkScalar focal = data.focal();
+
+    if (fCachedFocal != focal) {
+        uman.set1f(fFocalUni, SkScalarToFloat(focal));
+        fCachedFocal = focal;
+    }
+}
+
+GrGLEffect::EffectKey GLFocalInside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect,
+                                                            const GrGLCaps&) {
+    return GenBaseGradientKey(drawEffect);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Circle Conical Gradients
+//////////////////////////////////////////////////////////////////////////////
+
+struct CircleConicalInfo {
+    SkPoint fCenterEnd;
+    SkScalar fA;
+    SkScalar fB;
+    SkScalar 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.0 / 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. 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) < 5 * kErrorTol) {
+        return kEdge_ConicalType;
+    }
+
+    SkScalar C = 1.0 / A;
+    SkScalar B = (radiusEnd - 1.0) * 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.0) {
+        return kInside_ConicalType;
+    }
+    return kOutside_ConicalType;
+}
+
+class GLCircleInside2PtConicalEffect;
+
+class CircleInside2PtConicalEffect : public GrGradientEffect {
+public:
+
+    static GrEffectRef* Create(GrContext* ctx,
+                               const SkTwoPointConicalGradient& shader,
+                               const SkMatrix& matrix,
+                               SkShader::TileMode tm,
+                               const CircleConicalInfo& info) {
+        AutoEffectUnref effect(SkNEW_ARGS(CircleInside2PtConicalEffect, (ctx, shader, matrix, tm, info)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual ~CircleInside2PtConicalEffect() {}
+
+    static const char* Name() { return "Two-Point Conical Gradient Inside"; }
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+
+    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; }
+
+    typedef GLCircleInside2PtConicalEffect GLEffect;
+
+private:
+    virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
+        const CircleInside2PtConicalEffect& s = CastEffect<CircleInside2PtConicalEffect>(sBase);
+        return (INHERITED::onIsEqual(sBase) &&
+                this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
+                this->fInfo.fA == s.fInfo.fA &&
+                this->fInfo.fB == s.fInfo.fB &&
+                this->fInfo.fC == s.fInfo.fC);
+    }
+
+    CircleInside2PtConicalEffect(GrContext* ctx,
+                                 const SkTwoPointConicalGradient& shader,
+                                 const SkMatrix& matrix,
+                                 SkShader::TileMode tm,
+                                 const CircleConicalInfo& info)
+        : INHERITED(ctx, shader, matrix, tm), fInfo(info) {}
+
+    GR_DECLARE_EFFECT_TEST;
+
+    const CircleConicalInfo fInfo;
+
+    typedef GrGradientEffect INHERITED;
+};
+
+class GLCircleInside2PtConicalEffect : public GrGLGradientEffect {
+public:
+    GLCircleInside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&);
+    virtual ~GLCircleInside2PtConicalEffect() {}
+
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrDrawEffect&,
+                          EffectKey,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TransformedCoordsArray&,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+    virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
+
+    static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
+
+protected:
+    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;
+
+    // @}
+
+private:
+    typedef GrGLGradientEffect INHERITED;
+
+};
+
+const GrBackendEffectFactory& CircleInside2PtConicalEffect::getFactory() const {
+    return GrTBackendEffectFactory<CircleInside2PtConicalEffect>::getInstance();
+}
+
+GR_DEFINE_EFFECT_TEST(CircleInside2PtConicalEffect);
+
+GrEffectRef* CircleInside2PtConicalEffect::TestCreate(SkRandom* random,
+                                                      GrContext* context,
+                                                      const GrDrawTargetCaps&,
+                                                      GrTexture**) {
+    SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
+    SkScalar radius1 = random->nextUScalar1() + 0.0001; // make sure radius1 != 0
+    SkPoint center2;
+    SkScalar radius2;
+    do {
+        center2.set(random->nextUScalar1(), random->nextUScalar1());
+        // Below makes sure that circle one is contained within circle two
+        SkScalar increase = random->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);
+
+    SkColor colors[kMaxRandomGradientColors];
+    SkScalar stopsArray[kMaxRandomGradientColors];
+    SkScalar* stops = stopsArray;
+    SkShader::TileMode tm;
+    int colorCount = RandomGradientParams(random, colors, &stops, &tm);
+    SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
+                                                                          center2, radius2,
+                                                                          colors, stops, colorCount,
+                                                                          tm));
+    SkPaint paint;
+    return shader->asNewEffect(context, paint);
+}
+
+GLCircleInside2PtConicalEffect::GLCircleInside2PtConicalEffect(const GrBackendEffectFactory& factory,
+                                                               const GrDrawEffect& drawEffect)
+    : INHERITED(factory)
+    , fVSVaryingName(NULL)
+    , fFSVaryingName(NULL)
+    , fCachedCenterX(SK_ScalarMax)
+    , fCachedCenterY(SK_ScalarMax)
+    , fCachedA(SK_ScalarMax)
+    , fCachedB(SK_ScalarMax)
+    , fCachedC(SK_ScalarMax) {}
+
+void GLCircleInside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
+                                              const GrDrawEffect&,
+                                              EffectKey key,
+                                              const char* outputColor,
+                                              const char* inputColor,
+                                              const TransformedCoordsArray& coords,
+                                              const TextureSamplerArray& samplers) {
+    this->emitUniforms(builder, key);
+    fCenterUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
+                                     kVec2f_GrSLType, "Conical2FSCenter");
+    fParamUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
+                                    kVec3f_GrSLType, "Conical2FSParams");
+    SkString tName("t");
+
+    GrGLShaderVar center = builder->getUniformVariable(fCenterUni);
+    // params.x = A
+    // params.y = B
+    // params.z = C
+    GrGLShaderVar params = builder->getUniformVariable(fParamUni);
+
+    // if we have a vec3 from being in perspective, convert it to a vec2 first
+    SkString coords2DString = builder->ensureFSCoords2D(coords, 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)
+    builder->fsCodeAppendf("\tfloat pDotp = dot(%s,  %s);\n", coords2D, coords2D);
+    builder->fsCodeAppendf("\tfloat d = dot(%s,  %s) + %s.y;\n", coords2D, center.c_str(), params.c_str());
+    builder->fsCodeAppendf("\tfloat %s = d + sqrt(d * d - %s.x * pDotp + %s.z);\n",
+                           tName.c_str(), params.c_str(), params.c_str());
+
+    this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
+}
+
+void GLCircleInside2PtConicalEffect::setData(const GrGLUniformManager& uman,
+                                             const GrDrawEffect& drawEffect) {
+    INHERITED::setData(uman, drawEffect);
+    const CircleInside2PtConicalEffect& data = drawEffect.castEffect<CircleInside2PtConicalEffect>();
+    SkScalar centerX = data.centerX();
+    SkScalar centerY = data.centerY();
+    SkScalar A = data.A();
+    SkScalar B = data.B();
+    SkScalar C = data.C();
+
+    if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
+        fCachedA != A || fCachedB != B || fCachedC != C) {
+
+        uman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
+        uman.set3f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C));
+
+        fCachedCenterX = centerX;
+        fCachedCenterY = centerY;
+        fCachedA = A;
+        fCachedB = B;
+        fCachedC = C;
+    }
+}
+
+GrGLEffect::EffectKey GLCircleInside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect,
+                                                             const GrGLCaps&) {
+    EffectKey key = GenBaseGradientKey(drawEffect);
+    return key;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+class GLCircleOutside2PtConicalEffect;
+
+class CircleOutside2PtConicalEffect : public GrGradientEffect {
+public:
+
+    static GrEffectRef* Create(GrContext* ctx,
+                               const SkTwoPointConicalGradient& shader,
+                               const SkMatrix& matrix,
+                               SkShader::TileMode tm,
+                               const CircleConicalInfo& info) {
+        AutoEffectUnref effect(SkNEW_ARGS(CircleOutside2PtConicalEffect, (ctx, shader, matrix, tm, info)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual ~CircleOutside2PtConicalEffect() {}
+
+    static const char* Name() { return "Two-Point Conical Gradient Outside"; }
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+
+    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 fIsFlipped; }
+
+    typedef GLCircleOutside2PtConicalEffect GLEffect;
+
+private:
+    virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
+        const CircleOutside2PtConicalEffect& s = CastEffect<CircleOutside2PtConicalEffect>(sBase);
+        return (INHERITED::onIsEqual(sBase) &&
+                this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
+                this->fInfo.fA == s.fInfo.fA &&
+                this->fInfo.fB == s.fInfo.fB &&
+                this->fInfo.fC == s.fInfo.fC &&
+                this->fTLimit == s.fTLimit &&
+                this->fIsFlipped == s.fIsFlipped);
+    }
+
+    CircleOutside2PtConicalEffect(GrContext* ctx,
+                                  const SkTwoPointConicalGradient& shader,
+                                  const SkMatrix& matrix,
+                                  SkShader::TileMode tm,
+                                  const CircleConicalInfo& info)
+        : INHERITED(ctx, shader, matrix, tm), fInfo(info) {
+        if (shader.getStartRadius() != shader.getEndRadius()) {
+            fTLimit = SkScalarDiv(shader.getStartRadius(), (shader.getStartRadius() - shader.getEndRadius()));
+        } else {
+            fTLimit = SK_ScalarMin;
+        }
+
+        fIsFlipped = shader.isFlippedGrad();
+    }
+
+    GR_DECLARE_EFFECT_TEST;
+
+    const CircleConicalInfo fInfo;
+    SkScalar fTLimit;
+    bool fIsFlipped;
+
+    typedef GrGradientEffect INHERITED;
+};
+
+class GLCircleOutside2PtConicalEffect : public GrGLGradientEffect {
+public:
+    GLCircleOutside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&);
+    virtual ~GLCircleOutside2PtConicalEffect() {}
+
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrDrawEffect&,
+                          EffectKey,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TransformedCoordsArray&,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+    virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
+
+    static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
+
+protected:
+    UniformHandle fCenterUni;
+    UniformHandle fParamUni;
+
+    const char* fVSVaryingName;
+    const char* fFSVaryingName;
+
+    bool fIsFlipped;
+
+    // @{
+    /// Values last uploaded as uniforms
+
+    SkScalar fCachedCenterX;
+    SkScalar fCachedCenterY;
+    SkScalar fCachedA;
+    SkScalar fCachedB;
+    SkScalar fCachedC;
+    SkScalar fCachedTLimit;
+
+    // @}
+
+private:
+    typedef GrGLGradientEffect INHERITED;
+
+};
+
+const GrBackendEffectFactory& CircleOutside2PtConicalEffect::getFactory() const {
+    return GrTBackendEffectFactory<CircleOutside2PtConicalEffect>::getInstance();
+}
+
+GR_DEFINE_EFFECT_TEST(CircleOutside2PtConicalEffect);
+
+GrEffectRef* CircleOutside2PtConicalEffect::TestCreate(SkRandom* random,
+                                                       GrContext* context,
+                                                       const GrDrawTargetCaps&,
+                                                       GrTexture**) {
+    SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
+    SkScalar radius1 = random->nextUScalar1() + 0.0001; // make sure radius1 != 0
+    SkPoint center2;
+    SkScalar radius2;
+    SkScalar diffLen;
+    do {
+        center2.set(random->nextUScalar1(), random->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 + random->nextRangeF(0.0, diffLen);
+
+    SkColor colors[kMaxRandomGradientColors];
+    SkScalar stopsArray[kMaxRandomGradientColors];
+    SkScalar* stops = stopsArray;
+    SkShader::TileMode tm;
+    int colorCount = RandomGradientParams(random, colors, &stops, &tm);
+    SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
+                                                                          center2, radius2,
+                                                                          colors, stops, colorCount,
+                                                                          tm));
+    SkPaint paint;
+    return shader->asNewEffect(context, paint);
+}
+
+GLCircleOutside2PtConicalEffect::GLCircleOutside2PtConicalEffect(const GrBackendEffectFactory& factory,
+                                                                 const GrDrawEffect& drawEffect)
+    : INHERITED(factory)
+    , fVSVaryingName(NULL)
+    , fFSVaryingName(NULL)
+    , fCachedCenterX(SK_ScalarMax)
+    , fCachedCenterY(SK_ScalarMax)
+    , fCachedA(SK_ScalarMax)
+    , fCachedB(SK_ScalarMax)
+    , fCachedC(SK_ScalarMax)
+    , fCachedTLimit(SK_ScalarMax) {
+    const CircleOutside2PtConicalEffect& data = drawEffect.castEffect<CircleOutside2PtConicalEffect>();
+    fIsFlipped = data.isFlipped();
+    }
+
+void GLCircleOutside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
+                                               const GrDrawEffect&,
+                                               EffectKey key,
+                                               const char* outputColor,
+                                               const char* inputColor,
+                                               const TransformedCoordsArray& coords,
+                                               const TextureSamplerArray& samplers) {
+    this->emitUniforms(builder, key);
+    fCenterUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
+                                     kVec2f_GrSLType, "Conical2FSCenter");
+    fParamUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
+                                    kVec4f_GrSLType, "Conical2FSParams");
+    SkString tName("t");
+
+    GrGLShaderVar center = builder->getUniformVariable(fCenterUni);
+    // params.x = A
+    // params.y = B
+    // params.z = C
+    GrGLShaderVar params = builder->getUniformVariable(fParamUni);
+
+    // if we have a vec3 from being in perspective, convert it to a vec2 first
+    SkString coords2DString = builder->ensureFSCoords2D(coords, 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)
+    builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor);
+
+    // 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)
+
+    builder->fsCodeAppendf("\tfloat pDotp = dot(%s,  %s);\n", coords2D, coords2D);
+    builder->fsCodeAppendf("\tfloat d = dot(%s,  %s) + %s.y;\n", coords2D, center.c_str(), params.c_str());
+    builder->fsCodeAppendf("\tfloat deter = d * d - %s.x * pDotp + %s.z;\n", params.c_str(), params.c_str());
+
+    // Must check to see if we flipped the circle order (to make sure start radius < end radius)
+    // If so we must also flip sign on sqrt
+    if (!fIsFlipped) {
+        builder->fsCodeAppendf("\tfloat %s = d + sqrt(deter);\n", tName.c_str());
+    } else {
+        builder->fsCodeAppendf("\tfloat %s = d - sqrt(deter);\n", tName.c_str());
+    }
+
+    builder->fsCodeAppendf("\tif (%s >= %s.w && deter >= 0.0) {\n", tName.c_str(), params.c_str());
+    builder->fsCodeAppend("\t\t");
+    this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
+    builder->fsCodeAppend("\t}\n");
+}
+
+void GLCircleOutside2PtConicalEffect::setData(const GrGLUniformManager& uman,
+                                              const GrDrawEffect& drawEffect) {
+    INHERITED::setData(uman, drawEffect);
+    const CircleOutside2PtConicalEffect& data = drawEffect.castEffect<CircleOutside2PtConicalEffect>();
+    SkASSERT(data.isFlipped() == fIsFlipped);
+    SkScalar centerX = data.centerX();
+    SkScalar centerY = data.centerY();
+    SkScalar A = data.A();
+    SkScalar B = data.B();
+    SkScalar C = data.C();
+    SkScalar tLimit = data.tLimit();
+
+    if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
+        fCachedA != A || fCachedB != B || fCachedC != C || fCachedTLimit != tLimit) {
+
+        uman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
+        uman.set4f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C),
+                   SkScalarToFloat(tLimit));
+
+        fCachedCenterX = centerX;
+        fCachedCenterY = centerY;
+        fCachedA = A;
+        fCachedB = B;
+        fCachedC = C;
+        fCachedTLimit = tLimit;
+    }
+}
+
+GrGLEffect::EffectKey GLCircleOutside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect,
+                                                              const GrGLCaps&) {
+    enum {
+        kIsFlipped = 1 << kBaseKeyBitCnt,
+    };
+
+    EffectKey key = GenBaseGradientKey(drawEffect);
+
+    if (drawEffect.castEffect<CircleOutside2PtConicalEffect>().isFlipped()) {
+        key |= kIsFlipped;
+    }
+    return key;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+GrEffectRef* Gr2PtConicalGradientEffect::Create(GrContext* ctx,
+                                                const SkTwoPointConicalGradient& shader,
+                                                SkShader::TileMode tm) {
+    SkMatrix matrix;
+    if (!shader.getLocalMatrix().invert(&matrix)) {
+        return NULL;
+    }
+
+    if (shader.getStartRadius() < kErrorTol) {
+        SkScalar focalX;
+        ConicalType type = set_matrix_focal_conical(shader, &matrix, &focalX);
+        if (type == kInside_ConicalType) {
+            return FocalInside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX);
+        } else if(type == kEdge_ConicalType) {
+            set_matrix_edge_conical(shader, &matrix);
+            return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm);
+        } else {
+            return FocalOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX);
+        }
+    }
+
+    CircleConicalInfo info;
+    ConicalType type = set_matrix_circle_conical(shader, &matrix, &info);
+
+    if (type == kInside_ConicalType) {
+        return CircleInside2PtConicalEffect::Create(ctx, shader, matrix, tm, info);
+    } else if (type == kEdge_ConicalType) {
+        set_matrix_edge_conical(shader, &matrix);
+        return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm);
+    } else {
+        return CircleOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, info);
+    }
 }
 
 #endif