Home | History | Annotate | Download | only in gradients
      1 
      2 /*
      3  * Copyright 2014 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 #include "SkTwoPointConicalGradient_gpu.h"
     10 
     11 #include "SkTwoPointConicalGradient.h"
     12 
     13 #if SK_SUPPORT_GPU
     14 #include "GrTBackendEffectFactory.h"
     15 // For brevity
     16 typedef GrGLUniformManager::UniformHandle UniformHandle;
     17 
     18 static const SkScalar kErrorTol = 0.00001f;
     19 static const SkScalar kEdgeErrorTol = 5.f * kErrorTol;
     20 
     21 /**
     22  * We have three general cases for 2pt conical gradients. First we always assume that
     23  * the start radius <= end radius. Our first case (kInside_) is when the start circle
     24  * is completely enclosed by the end circle. The second case (kOutside_) is the case
     25  * when the start circle is either completely outside the end circle or the circles
     26  * overlap. The final case (kEdge_) is when the start circle is inside the end one,
     27  * but the two are just barely touching at 1 point along their edges.
     28  */
     29 enum ConicalType {
     30     kInside_ConicalType,
     31     kOutside_ConicalType,
     32     kEdge_ConicalType,
     33 };
     34 
     35 //////////////////////////////////////////////////////////////////////////////
     36 
     37 static void set_matrix_edge_conical(const SkTwoPointConicalGradient& shader,
     38                                     SkMatrix* invLMatrix) {
     39     // Inverse of the current local matrix is passed in then,
     40     // translate to center1, rotate so center2 is on x axis.
     41     const SkPoint& center1 = shader.getStartCenter();
     42     const SkPoint& center2 = shader.getEndCenter();
     43 
     44     invLMatrix->postTranslate(-center1.fX, -center1.fY);
     45 
     46     SkPoint diff = center2 - center1;
     47     SkScalar diffLen = diff.length();
     48     if (0 != diffLen) {
     49         SkScalar invDiffLen = SkScalarInvert(diffLen);
     50         SkMatrix rot;
     51         rot.setSinCos(-SkScalarMul(invDiffLen, diff.fY),
     52                        SkScalarMul(invDiffLen, diff.fX));
     53         invLMatrix->postConcat(rot);
     54     }
     55 }
     56 
     57 class GLEdge2PtConicalEffect;
     58 
     59 class Edge2PtConicalEffect : public GrGradientEffect {
     60 public:
     61 
     62     static GrEffectRef* Create(GrContext* ctx,
     63                                const SkTwoPointConicalGradient& shader,
     64                                const SkMatrix& matrix,
     65                                SkShader::TileMode tm) {
     66         AutoEffectUnref effect(SkNEW_ARGS(Edge2PtConicalEffect, (ctx, shader, matrix, tm)));
     67         return CreateEffectRef(effect);
     68     }
     69 
     70     virtual ~Edge2PtConicalEffect() {}
     71 
     72     static const char* Name() { return "Two-Point Conical Gradient Edge Touching"; }
     73     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
     74 
     75     // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
     76     SkScalar center() const { return fCenterX1; }
     77     SkScalar diffRadius() const { return fDiffRadius; }
     78     SkScalar radius() const { return fRadius0; }
     79 
     80     typedef GLEdge2PtConicalEffect GLEffect;
     81 
     82 private:
     83     virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
     84         const Edge2PtConicalEffect& s = CastEffect<Edge2PtConicalEffect>(sBase);
     85         return (INHERITED::onIsEqual(sBase) &&
     86                 this->fCenterX1 == s.fCenterX1 &&
     87                 this->fRadius0 == s.fRadius0 &&
     88                 this->fDiffRadius == s.fDiffRadius);
     89     }
     90 
     91     Edge2PtConicalEffect(GrContext* ctx,
     92                          const SkTwoPointConicalGradient& shader,
     93                          const SkMatrix& matrix,
     94                          SkShader::TileMode tm)
     95         : INHERITED(ctx, shader, matrix, tm),
     96         fCenterX1(shader.getCenterX1()),
     97         fRadius0(shader.getStartRadius()),
     98         fDiffRadius(shader.getDiffRadius()){
     99         // We should only be calling this shader if we are degenerate case with touching circles
    100         // When deciding if we are in edge case, we scaled by the end radius for cases when the
    101         // start radius was close to zero, otherwise we scaled by the start radius
    102         SkASSERT(SkScalarAbs(SkScalarAbs(fDiffRadius) - SkScalarAbs(fCenterX1)) <
    103                  kEdgeErrorTol * (fRadius0 < kErrorTol ? shader.getEndRadius() : fRadius0));
    104 
    105         // We pass the linear part of the quadratic as a varying.
    106         //    float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z)
    107         fBTransform = this->getCoordTransform();
    108         SkMatrix& bMatrix = *fBTransform.accessMatrix();
    109         SkScalar r0dr = SkScalarMul(fRadius0, fDiffRadius);
    110         bMatrix[SkMatrix::kMScaleX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) +
    111                                             SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp0]));
    112         bMatrix[SkMatrix::kMSkewX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) +
    113                                            SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp1]));
    114         bMatrix[SkMatrix::kMTransX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) +
    115                                             SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp2]));
    116         this->addCoordTransform(&fBTransform);
    117     }
    118 
    119     GR_DECLARE_EFFECT_TEST;
    120 
    121     // @{
    122     // Cache of values - these can change arbitrarily, EXCEPT
    123     // we shouldn't change between degenerate and non-degenerate?!
    124 
    125     GrCoordTransform fBTransform;
    126     SkScalar         fCenterX1;
    127     SkScalar         fRadius0;
    128     SkScalar         fDiffRadius;
    129 
    130     // @}
    131 
    132     typedef GrGradientEffect INHERITED;
    133 };
    134 
    135 class GLEdge2PtConicalEffect : public GrGLGradientEffect {
    136 public:
    137     GLEdge2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&);
    138     virtual ~GLEdge2PtConicalEffect() { }
    139 
    140     virtual void emitCode(GrGLShaderBuilder*,
    141                           const GrDrawEffect&,
    142                           EffectKey,
    143                           const char* outputColor,
    144                           const char* inputColor,
    145                           const TransformedCoordsArray&,
    146                           const TextureSamplerArray&) SK_OVERRIDE;
    147     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
    148 
    149     static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
    150 
    151 protected:
    152     UniformHandle fParamUni;
    153 
    154     const char* fVSVaryingName;
    155     const char* fFSVaryingName;
    156 
    157     // @{
    158     /// Values last uploaded as uniforms
    159 
    160     SkScalar fCachedRadius;
    161     SkScalar fCachedDiffRadius;
    162 
    163     // @}
    164 
    165 private:
    166     typedef GrGLGradientEffect INHERITED;
    167 
    168 };
    169 
    170 const GrBackendEffectFactory& Edge2PtConicalEffect::getFactory() const {
    171     return GrTBackendEffectFactory<Edge2PtConicalEffect>::getInstance();
    172 }
    173 
    174 GR_DEFINE_EFFECT_TEST(Edge2PtConicalEffect);
    175 
    176 GrEffectRef* Edge2PtConicalEffect::TestCreate(SkRandom* random,
    177                                               GrContext* context,
    178                                               const GrDrawTargetCaps&,
    179                                               GrTexture**) {
    180     SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
    181     SkScalar radius1 = random->nextUScalar1();
    182     SkPoint center2;
    183     SkScalar radius2;
    184     do {
    185         center2.set(random->nextUScalar1(), random->nextUScalar1());
    186         // If the circles are identical the factory will give us an empty shader.
    187         // This will happen if we pick identical centers
    188     } while (center1 == center2);
    189 
    190     // Below makes sure that circle one is contained within circle two
    191     // and both circles are touching on an edge
    192     SkPoint diff = center2 - center1;
    193     SkScalar diffLen = diff.length();
    194     radius2 = radius1 + diffLen;
    195 
    196     SkColor colors[kMaxRandomGradientColors];
    197     SkScalar stopsArray[kMaxRandomGradientColors];
    198     SkScalar* stops = stopsArray;
    199     SkShader::TileMode tm;
    200     int colorCount = RandomGradientParams(random, colors, &stops, &tm);
    201     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
    202                                                                           center2, radius2,
    203                                                                           colors, stops, colorCount,
    204                                                                           tm));
    205     SkPaint paint;
    206     GrEffectRef* effect;
    207     GrColor grColor;
    208     shader->asNewEffect(context, paint, NULL, &grColor, &effect);
    209     return effect;
    210 }
    211 
    212 GLEdge2PtConicalEffect::GLEdge2PtConicalEffect(const GrBackendEffectFactory& factory,
    213                                                const GrDrawEffect& drawEffect)
    214     : INHERITED(factory)
    215     , fVSVaryingName(NULL)
    216     , fFSVaryingName(NULL)
    217     , fCachedRadius(-SK_ScalarMax)
    218     , fCachedDiffRadius(-SK_ScalarMax) {}
    219 
    220 void GLEdge2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
    221                                       const GrDrawEffect&,
    222                                       EffectKey key,
    223                                       const char* outputColor,
    224                                       const char* inputColor,
    225                                       const TransformedCoordsArray& coords,
    226                                       const TextureSamplerArray& samplers) {
    227     this->emitUniforms(builder, key);
    228     fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
    229                                          kFloat_GrSLType, "Conical2FSParams", 3);
    230 
    231     SkString cName("c");
    232     SkString tName("t");
    233     SkString p0; // start radius
    234     SkString p1; // start radius squared
    235     SkString p2; // difference in radii (r1 - r0)
    236 
    237     builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
    238     builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
    239     builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
    240 
    241     // We interpolate the linear component in coords[1].
    242     SkASSERT(coords[0].type() == coords[1].type());
    243     const char* coords2D;
    244     SkString bVar;
    245     if (kVec3f_GrSLType == coords[0].type()) {
    246         builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy / %s.z, %s.x / %s.z);\n",
    247                                coords[0].c_str(), coords[0].c_str(), coords[1].c_str(), coords[1].c_str());
    248         coords2D = "interpolants.xy";
    249         bVar = "interpolants.z";
    250     } else {
    251         coords2D = coords[0].c_str();
    252         bVar.printf("%s.x", coords[1].c_str());
    253     }
    254 
    255     // output will default to transparent black (we simply won't write anything
    256     // else to it if invalid, instead of discarding or returning prematurely)
    257     builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor);
    258 
    259     // c = (x^2)+(y^2) - params[1]
    260     builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
    261                            cName.c_str(), coords2D, coords2D, p1.c_str());
    262 
    263     // linear case: t = -c/b
    264     builder->fsCodeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(),
    265                            cName.c_str(), bVar.c_str());
    266 
    267     // if r(t) > 0, then t will be the x coordinate
    268     builder->fsCodeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
    269                            p2.c_str(), p0.c_str());
    270     builder->fsCodeAppend("\t");
    271     this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
    272     builder->fsCodeAppend("\t}\n");
    273 }
    274 
    275 void GLEdge2PtConicalEffect::setData(const GrGLUniformManager& uman,
    276                                      const GrDrawEffect& drawEffect) {
    277     INHERITED::setData(uman, drawEffect);
    278     const Edge2PtConicalEffect& data = drawEffect.castEffect<Edge2PtConicalEffect>();
    279     SkScalar radius0 = data.radius();
    280     SkScalar diffRadius = data.diffRadius();
    281 
    282     if (fCachedRadius != radius0 ||
    283         fCachedDiffRadius != diffRadius) {
    284 
    285         float values[3] = {
    286             SkScalarToFloat(radius0),
    287             SkScalarToFloat(SkScalarMul(radius0, radius0)),
    288             SkScalarToFloat(diffRadius)
    289         };
    290 
    291         uman.set1fv(fParamUni, 3, values);
    292         fCachedRadius = radius0;
    293         fCachedDiffRadius = diffRadius;
    294     }
    295 }
    296 
    297 GrGLEffect::EffectKey GLEdge2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect,
    298                                                      const GrGLCaps&) {
    299     return GenBaseGradientKey(drawEffect);
    300 }
    301 
    302 //////////////////////////////////////////////////////////////////////////////
    303 // Focal Conical Gradients
    304 //////////////////////////////////////////////////////////////////////////////
    305 
    306 static ConicalType set_matrix_focal_conical(const SkTwoPointConicalGradient& shader,
    307                                             SkMatrix* invLMatrix, SkScalar* focalX) {
    308     // Inverse of the current local matrix is passed in then,
    309     // translate, scale, and rotate such that endCircle is unit circle on x-axis,
    310     // and focal point is at the origin.
    311     ConicalType conicalType;
    312     const SkPoint& focal = shader.getStartCenter();
    313     const SkPoint& centerEnd = shader.getEndCenter();
    314     SkScalar radius = shader.getEndRadius();
    315     SkScalar invRadius = 1.f / radius;
    316 
    317     SkMatrix matrix;
    318 
    319     matrix.setTranslate(-centerEnd.fX, -centerEnd.fY);
    320     matrix.postScale(invRadius, invRadius);
    321 
    322     SkPoint focalTrans;
    323     matrix.mapPoints(&focalTrans, &focal, 1);
    324     *focalX = focalTrans.length();
    325 
    326     if (0.f != *focalX) {
    327         SkScalar invFocalX = SkScalarInvert(*focalX);
    328         SkMatrix rot;
    329         rot.setSinCos(-SkScalarMul(invFocalX, focalTrans.fY),
    330                       SkScalarMul(invFocalX, focalTrans.fX));
    331         matrix.postConcat(rot);
    332     }
    333 
    334     matrix.postTranslate(-(*focalX), 0.f);
    335 
    336     // If the focal point is touching the edge of the circle it will
    337     // cause a degenerate case that must be handled separately
    338     // kEdgeErrorTol = 5 * kErrorTol was picked after manual testing the
    339     // stability trade off versus the linear approx used in the Edge Shader
    340     if (SkScalarAbs(1.f - (*focalX)) < kEdgeErrorTol) {
    341         return kEdge_ConicalType;
    342     }
    343 
    344     // Scale factor 1 / (1 - focalX * focalX)
    345     SkScalar oneMinusF2 = 1.f - SkScalarMul(*focalX, *focalX);
    346     SkScalar s = SkScalarDiv(1.f, oneMinusF2);
    347 
    348 
    349     if (s >= 0.f) {
    350         conicalType = kInside_ConicalType;
    351         matrix.postScale(s, s * SkScalarSqrt(oneMinusF2));
    352     } else {
    353         conicalType = kOutside_ConicalType;
    354         matrix.postScale(s, s);
    355     }
    356 
    357     invLMatrix->postConcat(matrix);
    358 
    359     return conicalType;
    360 }
    361 
    362 //////////////////////////////////////////////////////////////////////////////
    363 
    364 class GLFocalOutside2PtConicalEffect;
    365 
    366 class FocalOutside2PtConicalEffect : public GrGradientEffect {
    367 public:
    368 
    369     static GrEffectRef* Create(GrContext* ctx,
    370                                const SkTwoPointConicalGradient& shader,
    371                                const SkMatrix& matrix,
    372                                SkShader::TileMode tm,
    373                                SkScalar focalX) {
    374         AutoEffectUnref effect(SkNEW_ARGS(FocalOutside2PtConicalEffect, (ctx, shader, matrix, tm, focalX)));
    375         return CreateEffectRef(effect);
    376     }
    377 
    378     virtual ~FocalOutside2PtConicalEffect() { }
    379 
    380     static const char* Name() { return "Two-Point Conical Gradient Focal Outside"; }
    381     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
    382 
    383     bool isFlipped() const { return fIsFlipped; }
    384     SkScalar focal() const { return fFocalX; }
    385 
    386     typedef GLFocalOutside2PtConicalEffect GLEffect;
    387 
    388 private:
    389     virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
    390         const FocalOutside2PtConicalEffect& s = CastEffect<FocalOutside2PtConicalEffect>(sBase);
    391         return (INHERITED::onIsEqual(sBase) &&
    392                 this->fFocalX == s.fFocalX &&
    393                 this->fIsFlipped == s.fIsFlipped);
    394     }
    395 
    396     FocalOutside2PtConicalEffect(GrContext* ctx,
    397                                  const SkTwoPointConicalGradient& shader,
    398                                  const SkMatrix& matrix,
    399                                  SkShader::TileMode tm,
    400                                  SkScalar focalX)
    401     : INHERITED(ctx, shader, matrix, tm), fFocalX(focalX), fIsFlipped(shader.isFlippedGrad()) {}
    402 
    403     GR_DECLARE_EFFECT_TEST;
    404 
    405     SkScalar         fFocalX;
    406     bool             fIsFlipped;
    407 
    408     typedef GrGradientEffect INHERITED;
    409 };
    410 
    411 class GLFocalOutside2PtConicalEffect : public GrGLGradientEffect {
    412 public:
    413     GLFocalOutside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&);
    414     virtual ~GLFocalOutside2PtConicalEffect() { }
    415 
    416     virtual void emitCode(GrGLShaderBuilder*,
    417                           const GrDrawEffect&,
    418                           EffectKey,
    419                           const char* outputColor,
    420                           const char* inputColor,
    421                           const TransformedCoordsArray&,
    422                           const TextureSamplerArray&) SK_OVERRIDE;
    423     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
    424 
    425     static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
    426 
    427 protected:
    428     UniformHandle fParamUni;
    429 
    430     const char* fVSVaryingName;
    431     const char* fFSVaryingName;
    432 
    433     bool fIsFlipped;
    434 
    435     // @{
    436     /// Values last uploaded as uniforms
    437 
    438     SkScalar fCachedFocal;
    439 
    440     // @}
    441 
    442 private:
    443     typedef GrGLGradientEffect INHERITED;
    444 
    445 };
    446 
    447 const GrBackendEffectFactory& FocalOutside2PtConicalEffect::getFactory() const {
    448     return GrTBackendEffectFactory<FocalOutside2PtConicalEffect>::getInstance();
    449 }
    450 
    451 GR_DEFINE_EFFECT_TEST(FocalOutside2PtConicalEffect);
    452 
    453 GrEffectRef* FocalOutside2PtConicalEffect::TestCreate(SkRandom* random,
    454                                                       GrContext* context,
    455                                                       const GrDrawTargetCaps&,
    456                                                       GrTexture**) {
    457     SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
    458     SkScalar radius1 = 0.f;
    459     SkPoint center2;
    460     SkScalar radius2;
    461     do {
    462         center2.set(random->nextUScalar1(), random->nextUScalar1());
    463         // Need to make sure the centers are not the same or else focal point will be inside
    464     } while (center1 == center2);
    465         SkPoint diff = center2 - center1;
    466         SkScalar diffLen = diff.length();
    467         // Below makes sure that the focal point is not contained within circle two
    468         radius2 = random->nextRangeF(0.f, diffLen);
    469 
    470     SkColor colors[kMaxRandomGradientColors];
    471     SkScalar stopsArray[kMaxRandomGradientColors];
    472     SkScalar* stops = stopsArray;
    473     SkShader::TileMode tm;
    474     int colorCount = RandomGradientParams(random, colors, &stops, &tm);
    475     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
    476                                                                           center2, radius2,
    477                                                                           colors, stops, colorCount,
    478                                                                           tm));
    479     SkPaint paint;
    480     GrEffectRef* effect;
    481     GrColor grColor;
    482     shader->asNewEffect(context, paint, NULL, &grColor, &effect);
    483     return effect;
    484 }
    485 
    486 GLFocalOutside2PtConicalEffect::GLFocalOutside2PtConicalEffect(const GrBackendEffectFactory& factory,
    487                                                                const GrDrawEffect& drawEffect)
    488     : INHERITED(factory)
    489     , fVSVaryingName(NULL)
    490     , fFSVaryingName(NULL)
    491     , fCachedFocal(SK_ScalarMax) {
    492     const FocalOutside2PtConicalEffect& data = drawEffect.castEffect<FocalOutside2PtConicalEffect>();
    493     fIsFlipped = data.isFlipped();
    494 }
    495 
    496 void GLFocalOutside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
    497                                               const GrDrawEffect&,
    498                                               EffectKey key,
    499                                               const char* outputColor,
    500                                               const char* inputColor,
    501                                               const TransformedCoordsArray& coords,
    502                                               const TextureSamplerArray& samplers) {
    503     this->emitUniforms(builder, key);
    504     fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
    505                                          kFloat_GrSLType, "Conical2FSParams", 2);
    506     SkString tName("t");
    507     SkString p0; // focalX
    508     SkString p1; // 1 - focalX * focalX
    509 
    510     builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
    511     builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
    512 
    513     // if we have a vec3 from being in perspective, convert it to a vec2 first
    514     SkString coords2DString = builder->ensureFSCoords2D(coords, 0);
    515     const char* coords2D = coords2DString.c_str();
    516 
    517     // t = p.x * focal.x +/- sqrt(p.x^2 + (1 - focal.x^2) * p.y^2)
    518 
    519     // output will default to transparent black (we simply won't write anything
    520     // else to it if invalid, instead of discarding or returning prematurely)
    521     builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor);
    522 
    523     builder->fsCodeAppendf("\tfloat xs = %s.x * %s.x;\n", coords2D, coords2D);
    524     builder->fsCodeAppendf("\tfloat ys = %s.y * %s.y;\n", coords2D, coords2D);
    525     builder->fsCodeAppendf("\tfloat d = xs + %s * ys;\n", p1.c_str());
    526 
    527     // Must check to see if we flipped the circle order (to make sure start radius < end radius)
    528     // If so we must also flip sign on sqrt
    529     if (!fIsFlipped) {
    530         builder->fsCodeAppendf("\tfloat %s = %s.x * %s  + sqrt(d);\n", tName.c_str(),
    531                                coords2D, p0.c_str());
    532     } else {
    533         builder->fsCodeAppendf("\tfloat %s = %s.x * %s  - sqrt(d);\n", tName.c_str(),
    534                                coords2D, p0.c_str());
    535     }
    536 
    537     builder->fsCodeAppendf("\tif (%s >= 0.0 && d >= 0.0) {\n", tName.c_str());
    538     builder->fsCodeAppend("\t\t");
    539     this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
    540     builder->fsCodeAppend("\t}\n");
    541 }
    542 
    543 void GLFocalOutside2PtConicalEffect::setData(const GrGLUniformManager& uman,
    544                                              const GrDrawEffect& drawEffect) {
    545     INHERITED::setData(uman, drawEffect);
    546     const FocalOutside2PtConicalEffect& data = drawEffect.castEffect<FocalOutside2PtConicalEffect>();
    547     SkASSERT(data.isFlipped() == fIsFlipped);
    548     SkScalar focal = data.focal();
    549 
    550     if (fCachedFocal != focal) {
    551         SkScalar oneMinus2F = 1.f - SkScalarMul(focal, focal);
    552 
    553         float values[2] = {
    554             SkScalarToFloat(focal),
    555             SkScalarToFloat(oneMinus2F),
    556         };
    557 
    558         uman.set1fv(fParamUni, 2, values);
    559         fCachedFocal = focal;
    560     }
    561 }
    562 
    563 GrGLEffect::EffectKey GLFocalOutside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect,
    564                                                              const GrGLCaps&) {
    565     enum {
    566         kIsFlipped = 1 << kBaseKeyBitCnt,
    567     };
    568 
    569     EffectKey key = GenBaseGradientKey(drawEffect);
    570 
    571     if (drawEffect.castEffect<FocalOutside2PtConicalEffect>().isFlipped()) {
    572         key |= kIsFlipped;
    573     }
    574     return key;
    575 }
    576 
    577 //////////////////////////////////////////////////////////////////////////////
    578 
    579 class GLFocalInside2PtConicalEffect;
    580 
    581 class FocalInside2PtConicalEffect : public GrGradientEffect {
    582 public:
    583 
    584     static GrEffectRef* Create(GrContext* ctx,
    585                                const SkTwoPointConicalGradient& shader,
    586                                const SkMatrix& matrix,
    587                                SkShader::TileMode tm,
    588                                SkScalar focalX) {
    589         AutoEffectUnref effect(SkNEW_ARGS(FocalInside2PtConicalEffect, (ctx, shader, matrix, tm, focalX)));
    590         return CreateEffectRef(effect);
    591     }
    592 
    593     virtual ~FocalInside2PtConicalEffect() {}
    594 
    595     static const char* Name() { return "Two-Point Conical Gradient Focal Inside"; }
    596     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
    597 
    598     SkScalar focal() const { return fFocalX; }
    599 
    600     typedef GLFocalInside2PtConicalEffect GLEffect;
    601 
    602 private:
    603     virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
    604         const FocalInside2PtConicalEffect& s = CastEffect<FocalInside2PtConicalEffect>(sBase);
    605         return (INHERITED::onIsEqual(sBase) &&
    606                 this->fFocalX == s.fFocalX);
    607     }
    608 
    609     FocalInside2PtConicalEffect(GrContext* ctx,
    610                                 const SkTwoPointConicalGradient& shader,
    611                                 const SkMatrix& matrix,
    612                                 SkShader::TileMode tm,
    613                                 SkScalar focalX)
    614         : INHERITED(ctx, shader, matrix, tm), fFocalX(focalX) {}
    615 
    616     GR_DECLARE_EFFECT_TEST;
    617 
    618     SkScalar         fFocalX;
    619 
    620     typedef GrGradientEffect INHERITED;
    621 };
    622 
    623 class GLFocalInside2PtConicalEffect : public GrGLGradientEffect {
    624 public:
    625     GLFocalInside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&);
    626     virtual ~GLFocalInside2PtConicalEffect() {}
    627 
    628     virtual void emitCode(GrGLShaderBuilder*,
    629                           const GrDrawEffect&,
    630                           EffectKey,
    631                           const char* outputColor,
    632                           const char* inputColor,
    633                           const TransformedCoordsArray&,
    634                           const TextureSamplerArray&) SK_OVERRIDE;
    635     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
    636 
    637     static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
    638 
    639 protected:
    640     UniformHandle fFocalUni;
    641 
    642     const char* fVSVaryingName;
    643     const char* fFSVaryingName;
    644 
    645     // @{
    646     /// Values last uploaded as uniforms
    647 
    648     SkScalar fCachedFocal;
    649 
    650     // @}
    651 
    652 private:
    653     typedef GrGLGradientEffect INHERITED;
    654 
    655 };
    656 
    657 const GrBackendEffectFactory& FocalInside2PtConicalEffect::getFactory() const {
    658     return GrTBackendEffectFactory<FocalInside2PtConicalEffect>::getInstance();
    659 }
    660 
    661 GR_DEFINE_EFFECT_TEST(FocalInside2PtConicalEffect);
    662 
    663 GrEffectRef* FocalInside2PtConicalEffect::TestCreate(SkRandom* random,
    664                                                      GrContext* context,
    665                                                      const GrDrawTargetCaps&,
    666                                                      GrTexture**) {
    667     SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
    668     SkScalar radius1 = 0.f;
    669     SkPoint center2;
    670     SkScalar radius2;
    671     do {
    672         center2.set(random->nextUScalar1(), random->nextUScalar1());
    673         // Below makes sure radius2 is larger enouch such that the focal point
    674         // is inside the end circle
    675         SkScalar increase = random->nextUScalar1();
    676         SkPoint diff = center2 - center1;
    677         SkScalar diffLen = diff.length();
    678         radius2 = diffLen + increase;
    679         // If the circles are identical the factory will give us an empty shader.
    680     } while (radius1 == radius2 && center1 == center2);
    681 
    682     SkColor colors[kMaxRandomGradientColors];
    683     SkScalar stopsArray[kMaxRandomGradientColors];
    684     SkScalar* stops = stopsArray;
    685     SkShader::TileMode tm;
    686     int colorCount = RandomGradientParams(random, colors, &stops, &tm);
    687     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
    688                                                                           center2, radius2,
    689                                                                           colors, stops, colorCount,
    690                                                                           tm));
    691     SkPaint paint;
    692     GrColor grColor;
    693     GrEffectRef* grEffect;
    694     shader->asNewEffect(context, paint, NULL, &grColor, &grEffect);
    695     return grEffect;
    696 }
    697 
    698 GLFocalInside2PtConicalEffect::GLFocalInside2PtConicalEffect(const GrBackendEffectFactory& factory,
    699                                                              const GrDrawEffect& drawEffect)
    700     : INHERITED(factory)
    701     , fVSVaryingName(NULL)
    702     , fFSVaryingName(NULL)
    703     , fCachedFocal(SK_ScalarMax) {}
    704 
    705 void GLFocalInside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
    706                                              const GrDrawEffect&,
    707                                              EffectKey key,
    708                                              const char* outputColor,
    709                                              const char* inputColor,
    710                                              const TransformedCoordsArray& coords,
    711                                              const TextureSamplerArray& samplers) {
    712     this->emitUniforms(builder, key);
    713     fFocalUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
    714                                     kFloat_GrSLType, "Conical2FSParams");
    715     SkString tName("t");
    716 
    717     // this is the distance along x-axis from the end center to focal point in
    718     // transformed coordinates
    719     GrGLShaderVar focal = builder->getUniformVariable(fFocalUni);
    720 
    721     // if we have a vec3 from being in perspective, convert it to a vec2 first
    722     SkString coords2DString = builder->ensureFSCoords2D(coords, 0);
    723     const char* coords2D = coords2DString.c_str();
    724 
    725     // t = p.x * focalX + length(p)
    726     builder->fsCodeAppendf("\tfloat %s = %s.x * %s  + length(%s);\n", tName.c_str(),
    727                            coords2D, focal.c_str(), coords2D);
    728 
    729     this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
    730 }
    731 
    732 void GLFocalInside2PtConicalEffect::setData(const GrGLUniformManager& uman,
    733                                             const GrDrawEffect& drawEffect) {
    734     INHERITED::setData(uman, drawEffect);
    735     const FocalInside2PtConicalEffect& data = drawEffect.castEffect<FocalInside2PtConicalEffect>();
    736     SkScalar focal = data.focal();
    737 
    738     if (fCachedFocal != focal) {
    739         uman.set1f(fFocalUni, SkScalarToFloat(focal));
    740         fCachedFocal = focal;
    741     }
    742 }
    743 
    744 GrGLEffect::EffectKey GLFocalInside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect,
    745                                                             const GrGLCaps&) {
    746     return GenBaseGradientKey(drawEffect);
    747 }
    748 
    749 //////////////////////////////////////////////////////////////////////////////
    750 // Circle Conical Gradients
    751 //////////////////////////////////////////////////////////////////////////////
    752 
    753 struct CircleConicalInfo {
    754     SkPoint fCenterEnd;
    755     SkScalar fA;
    756     SkScalar fB;
    757     SkScalar fC;
    758 };
    759 
    760 // Returns focal distance along x-axis in transformed coords
    761 static ConicalType set_matrix_circle_conical(const SkTwoPointConicalGradient& shader,
    762                                              SkMatrix* invLMatrix, CircleConicalInfo* info) {
    763     // Inverse of the current local matrix is passed in then,
    764     // translate and scale such that start circle is on the origin and has radius 1
    765     const SkPoint& centerStart = shader.getStartCenter();
    766     const SkPoint& centerEnd = shader.getEndCenter();
    767     SkScalar radiusStart = shader.getStartRadius();
    768     SkScalar radiusEnd = shader.getEndRadius();
    769 
    770     SkMatrix matrix;
    771 
    772     matrix.setTranslate(-centerStart.fX, -centerStart.fY);
    773 
    774     SkScalar invStartRad = 1.f / radiusStart;
    775     matrix.postScale(invStartRad, invStartRad);
    776 
    777     radiusEnd /= radiusStart;
    778 
    779     SkPoint centerEndTrans;
    780     matrix.mapPoints(&centerEndTrans, &centerEnd, 1);
    781 
    782     SkScalar A = centerEndTrans.fX * centerEndTrans.fX + centerEndTrans.fY * centerEndTrans.fY
    783                  - radiusEnd * radiusEnd + 2 * radiusEnd - 1;
    784 
    785     // Check to see if start circle is inside end circle with edges touching.
    786     // If touching we return that it is of kEdge_ConicalType, and leave the matrix setting
    787     // to the edge shader. kEdgeErrorTol = 5 * kErrorTol was picked after manual testing
    788     // so that C = 1 / A is stable, and the linear approximation used in the Edge shader is
    789     // still accurate.
    790     if (SkScalarAbs(A) < kEdgeErrorTol) {
    791         return kEdge_ConicalType;
    792     }
    793 
    794     SkScalar C = 1.f / A;
    795     SkScalar B = (radiusEnd - 1.f) * C;
    796 
    797     matrix.postScale(C, C);
    798 
    799     invLMatrix->postConcat(matrix);
    800 
    801     info->fCenterEnd = centerEndTrans;
    802     info->fA = A;
    803     info->fB = B;
    804     info->fC = C;
    805 
    806     // if A ends up being negative, the start circle is contained completely inside the end cirlce
    807     if (A < 0.f) {
    808         return kInside_ConicalType;
    809     }
    810     return kOutside_ConicalType;
    811 }
    812 
    813 class GLCircleInside2PtConicalEffect;
    814 
    815 class CircleInside2PtConicalEffect : public GrGradientEffect {
    816 public:
    817 
    818     static GrEffectRef* Create(GrContext* ctx,
    819                                const SkTwoPointConicalGradient& shader,
    820                                const SkMatrix& matrix,
    821                                SkShader::TileMode tm,
    822                                const CircleConicalInfo& info) {
    823         AutoEffectUnref effect(SkNEW_ARGS(CircleInside2PtConicalEffect, (ctx, shader, matrix, tm, info)));
    824         return CreateEffectRef(effect);
    825     }
    826 
    827     virtual ~CircleInside2PtConicalEffect() {}
    828 
    829     static const char* Name() { return "Two-Point Conical Gradient Inside"; }
    830     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
    831 
    832     SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
    833     SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
    834     SkScalar A() const { return fInfo.fA; }
    835     SkScalar B() const { return fInfo.fB; }
    836     SkScalar C() const { return fInfo.fC; }
    837 
    838     typedef GLCircleInside2PtConicalEffect GLEffect;
    839 
    840 private:
    841     virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
    842         const CircleInside2PtConicalEffect& s = CastEffect<CircleInside2PtConicalEffect>(sBase);
    843         return (INHERITED::onIsEqual(sBase) &&
    844                 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
    845                 this->fInfo.fA == s.fInfo.fA &&
    846                 this->fInfo.fB == s.fInfo.fB &&
    847                 this->fInfo.fC == s.fInfo.fC);
    848     }
    849 
    850     CircleInside2PtConicalEffect(GrContext* ctx,
    851                                  const SkTwoPointConicalGradient& shader,
    852                                  const SkMatrix& matrix,
    853                                  SkShader::TileMode tm,
    854                                  const CircleConicalInfo& info)
    855         : INHERITED(ctx, shader, matrix, tm), fInfo(info) {}
    856 
    857     GR_DECLARE_EFFECT_TEST;
    858 
    859     const CircleConicalInfo fInfo;
    860 
    861     typedef GrGradientEffect INHERITED;
    862 };
    863 
    864 class GLCircleInside2PtConicalEffect : public GrGLGradientEffect {
    865 public:
    866     GLCircleInside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&);
    867     virtual ~GLCircleInside2PtConicalEffect() {}
    868 
    869     virtual void emitCode(GrGLShaderBuilder*,
    870                           const GrDrawEffect&,
    871                           EffectKey,
    872                           const char* outputColor,
    873                           const char* inputColor,
    874                           const TransformedCoordsArray&,
    875                           const TextureSamplerArray&) SK_OVERRIDE;
    876     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
    877 
    878     static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
    879 
    880 protected:
    881     UniformHandle fCenterUni;
    882     UniformHandle fParamUni;
    883 
    884     const char* fVSVaryingName;
    885     const char* fFSVaryingName;
    886 
    887     // @{
    888     /// Values last uploaded as uniforms
    889 
    890     SkScalar fCachedCenterX;
    891     SkScalar fCachedCenterY;
    892     SkScalar fCachedA;
    893     SkScalar fCachedB;
    894     SkScalar fCachedC;
    895 
    896     // @}
    897 
    898 private:
    899     typedef GrGLGradientEffect INHERITED;
    900 
    901 };
    902 
    903 const GrBackendEffectFactory& CircleInside2PtConicalEffect::getFactory() const {
    904     return GrTBackendEffectFactory<CircleInside2PtConicalEffect>::getInstance();
    905 }
    906 
    907 GR_DEFINE_EFFECT_TEST(CircleInside2PtConicalEffect);
    908 
    909 GrEffectRef* CircleInside2PtConicalEffect::TestCreate(SkRandom* random,
    910                                                       GrContext* context,
    911                                                       const GrDrawTargetCaps&,
    912                                                       GrTexture**) {
    913     SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
    914     SkScalar radius1 = random->nextUScalar1() + 0.0001f; // make sure radius1 != 0
    915     SkPoint center2;
    916     SkScalar radius2;
    917     do {
    918         center2.set(random->nextUScalar1(), random->nextUScalar1());
    919         // Below makes sure that circle one is contained within circle two
    920         SkScalar increase = random->nextUScalar1();
    921         SkPoint diff = center2 - center1;
    922         SkScalar diffLen = diff.length();
    923         radius2 = radius1 + diffLen + increase;
    924         // If the circles are identical the factory will give us an empty shader.
    925     } while (radius1 == radius2 && center1 == center2);
    926 
    927     SkColor colors[kMaxRandomGradientColors];
    928     SkScalar stopsArray[kMaxRandomGradientColors];
    929     SkScalar* stops = stopsArray;
    930     SkShader::TileMode tm;
    931     int colorCount = RandomGradientParams(random, colors, &stops, &tm);
    932     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
    933                                                                           center2, radius2,
    934                                                                           colors, stops, colorCount,
    935                                                                           tm));
    936     SkPaint paint;
    937     GrColor grColor;
    938     GrEffectRef* grEffect;
    939     shader->asNewEffect(context, paint, NULL, &grColor, &grEffect);
    940     return grEffect;
    941 }
    942 
    943 GLCircleInside2PtConicalEffect::GLCircleInside2PtConicalEffect(const GrBackendEffectFactory& factory,
    944                                                                const GrDrawEffect& drawEffect)
    945     : INHERITED(factory)
    946     , fVSVaryingName(NULL)
    947     , fFSVaryingName(NULL)
    948     , fCachedCenterX(SK_ScalarMax)
    949     , fCachedCenterY(SK_ScalarMax)
    950     , fCachedA(SK_ScalarMax)
    951     , fCachedB(SK_ScalarMax)
    952     , fCachedC(SK_ScalarMax) {}
    953 
    954 void GLCircleInside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
    955                                               const GrDrawEffect&,
    956                                               EffectKey key,
    957                                               const char* outputColor,
    958                                               const char* inputColor,
    959                                               const TransformedCoordsArray& coords,
    960                                               const TextureSamplerArray& samplers) {
    961     this->emitUniforms(builder, key);
    962     fCenterUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
    963                                      kVec2f_GrSLType, "Conical2FSCenter");
    964     fParamUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
    965                                     kVec3f_GrSLType, "Conical2FSParams");
    966     SkString tName("t");
    967 
    968     GrGLShaderVar center = builder->getUniformVariable(fCenterUni);
    969     // params.x = A
    970     // params.y = B
    971     // params.z = C
    972     GrGLShaderVar params = builder->getUniformVariable(fParamUni);
    973 
    974     // if we have a vec3 from being in perspective, convert it to a vec2 first
    975     SkString coords2DString = builder->ensureFSCoords2D(coords, 0);
    976     const char* coords2D = coords2DString.c_str();
    977 
    978     // p = coords2D
    979     // e = center end
    980     // r = radius end
    981     // A = dot(e, e) - r^2 + 2 * r - 1
    982     // B = (r -1) / A
    983     // C = 1 / A
    984     // d = dot(e, p) + B
    985     // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
    986     builder->fsCodeAppendf("\tfloat pDotp = dot(%s,  %s);\n", coords2D, coords2D);
    987     builder->fsCodeAppendf("\tfloat d = dot(%s,  %s) + %s.y;\n", coords2D, center.c_str(), params.c_str());
    988     builder->fsCodeAppendf("\tfloat %s = d + sqrt(d * d - %s.x * pDotp + %s.z);\n",
    989                            tName.c_str(), params.c_str(), params.c_str());
    990 
    991     this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
    992 }
    993 
    994 void GLCircleInside2PtConicalEffect::setData(const GrGLUniformManager& uman,
    995                                              const GrDrawEffect& drawEffect) {
    996     INHERITED::setData(uman, drawEffect);
    997     const CircleInside2PtConicalEffect& data = drawEffect.castEffect<CircleInside2PtConicalEffect>();
    998     SkScalar centerX = data.centerX();
    999     SkScalar centerY = data.centerY();
   1000     SkScalar A = data.A();
   1001     SkScalar B = data.B();
   1002     SkScalar C = data.C();
   1003 
   1004     if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
   1005         fCachedA != A || fCachedB != B || fCachedC != C) {
   1006 
   1007         uman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
   1008         uman.set3f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C));
   1009 
   1010         fCachedCenterX = centerX;
   1011         fCachedCenterY = centerY;
   1012         fCachedA = A;
   1013         fCachedB = B;
   1014         fCachedC = C;
   1015     }
   1016 }
   1017 
   1018 GrGLEffect::EffectKey GLCircleInside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect,
   1019                                                              const GrGLCaps&) {
   1020     EffectKey key = GenBaseGradientKey(drawEffect);
   1021     return key;
   1022 }
   1023 
   1024 //////////////////////////////////////////////////////////////////////////////
   1025 
   1026 class GLCircleOutside2PtConicalEffect;
   1027 
   1028 class CircleOutside2PtConicalEffect : public GrGradientEffect {
   1029 public:
   1030 
   1031     static GrEffectRef* Create(GrContext* ctx,
   1032                                const SkTwoPointConicalGradient& shader,
   1033                                const SkMatrix& matrix,
   1034                                SkShader::TileMode tm,
   1035                                const CircleConicalInfo& info) {
   1036         AutoEffectUnref effect(SkNEW_ARGS(CircleOutside2PtConicalEffect, (ctx, shader, matrix, tm, info)));
   1037         return CreateEffectRef(effect);
   1038     }
   1039 
   1040     virtual ~CircleOutside2PtConicalEffect() {}
   1041 
   1042     static const char* Name() { return "Two-Point Conical Gradient Outside"; }
   1043     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
   1044 
   1045     SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
   1046     SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
   1047     SkScalar A() const { return fInfo.fA; }
   1048     SkScalar B() const { return fInfo.fB; }
   1049     SkScalar C() const { return fInfo.fC; }
   1050     SkScalar tLimit() const { return fTLimit; }
   1051     bool isFlipped() const { return fIsFlipped; }
   1052 
   1053     typedef GLCircleOutside2PtConicalEffect GLEffect;
   1054 
   1055 private:
   1056     virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
   1057         const CircleOutside2PtConicalEffect& s = CastEffect<CircleOutside2PtConicalEffect>(sBase);
   1058         return (INHERITED::onIsEqual(sBase) &&
   1059                 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
   1060                 this->fInfo.fA == s.fInfo.fA &&
   1061                 this->fInfo.fB == s.fInfo.fB &&
   1062                 this->fInfo.fC == s.fInfo.fC &&
   1063                 this->fTLimit == s.fTLimit &&
   1064                 this->fIsFlipped == s.fIsFlipped);
   1065     }
   1066 
   1067     CircleOutside2PtConicalEffect(GrContext* ctx,
   1068                                   const SkTwoPointConicalGradient& shader,
   1069                                   const SkMatrix& matrix,
   1070                                   SkShader::TileMode tm,
   1071                                   const CircleConicalInfo& info)
   1072         : INHERITED(ctx, shader, matrix, tm), fInfo(info) {
   1073         if (shader.getStartRadius() != shader.getEndRadius()) {
   1074             fTLimit = SkScalarDiv(shader.getStartRadius(), (shader.getStartRadius() - shader.getEndRadius()));
   1075         } else {
   1076             fTLimit = SK_ScalarMin;
   1077         }
   1078 
   1079         fIsFlipped = shader.isFlippedGrad();
   1080     }
   1081 
   1082     GR_DECLARE_EFFECT_TEST;
   1083 
   1084     const CircleConicalInfo fInfo;
   1085     SkScalar fTLimit;
   1086     bool fIsFlipped;
   1087 
   1088     typedef GrGradientEffect INHERITED;
   1089 };
   1090 
   1091 class GLCircleOutside2PtConicalEffect : public GrGLGradientEffect {
   1092 public:
   1093     GLCircleOutside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&);
   1094     virtual ~GLCircleOutside2PtConicalEffect() {}
   1095 
   1096     virtual void emitCode(GrGLShaderBuilder*,
   1097                           const GrDrawEffect&,
   1098                           EffectKey,
   1099                           const char* outputColor,
   1100                           const char* inputColor,
   1101                           const TransformedCoordsArray&,
   1102                           const TextureSamplerArray&) SK_OVERRIDE;
   1103     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
   1104 
   1105     static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
   1106 
   1107 protected:
   1108     UniformHandle fCenterUni;
   1109     UniformHandle fParamUni;
   1110 
   1111     const char* fVSVaryingName;
   1112     const char* fFSVaryingName;
   1113 
   1114     bool fIsFlipped;
   1115 
   1116     // @{
   1117     /// Values last uploaded as uniforms
   1118 
   1119     SkScalar fCachedCenterX;
   1120     SkScalar fCachedCenterY;
   1121     SkScalar fCachedA;
   1122     SkScalar fCachedB;
   1123     SkScalar fCachedC;
   1124     SkScalar fCachedTLimit;
   1125 
   1126     // @}
   1127 
   1128 private:
   1129     typedef GrGLGradientEffect INHERITED;
   1130 
   1131 };
   1132 
   1133 const GrBackendEffectFactory& CircleOutside2PtConicalEffect::getFactory() const {
   1134     return GrTBackendEffectFactory<CircleOutside2PtConicalEffect>::getInstance();
   1135 }
   1136 
   1137 GR_DEFINE_EFFECT_TEST(CircleOutside2PtConicalEffect);
   1138 
   1139 GrEffectRef* CircleOutside2PtConicalEffect::TestCreate(SkRandom* random,
   1140                                                        GrContext* context,
   1141                                                        const GrDrawTargetCaps&,
   1142                                                        GrTexture**) {
   1143     SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
   1144     SkScalar radius1 = random->nextUScalar1() + 0.0001f; // make sure radius1 != 0
   1145     SkPoint center2;
   1146     SkScalar radius2;
   1147     SkScalar diffLen;
   1148     do {
   1149         center2.set(random->nextUScalar1(), random->nextUScalar1());
   1150         // If the circles share a center than we can't be in the outside case
   1151     } while (center1 == center2);
   1152         SkPoint diff = center2 - center1;
   1153         diffLen = diff.length();
   1154         // Below makes sure that circle one is not contained within circle two
   1155         // and have radius2 >= radius to match sorting on cpu side
   1156         radius2 = radius1 + random->nextRangeF(0.f, diffLen);
   1157 
   1158     SkColor colors[kMaxRandomGradientColors];
   1159     SkScalar stopsArray[kMaxRandomGradientColors];
   1160     SkScalar* stops = stopsArray;
   1161     SkShader::TileMode tm;
   1162     int colorCount = RandomGradientParams(random, colors, &stops, &tm);
   1163     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
   1164                                                                           center2, radius2,
   1165                                                                           colors, stops, colorCount,
   1166                                                                           tm));
   1167     SkPaint paint;
   1168     GrColor grColor;
   1169     GrEffectRef* grEffect;
   1170     shader->asNewEffect(context, paint, NULL, &grColor, &grEffect);
   1171     return grEffect;
   1172 }
   1173 
   1174 GLCircleOutside2PtConicalEffect::GLCircleOutside2PtConicalEffect(const GrBackendEffectFactory& factory,
   1175                                                                  const GrDrawEffect& drawEffect)
   1176     : INHERITED(factory)
   1177     , fVSVaryingName(NULL)
   1178     , fFSVaryingName(NULL)
   1179     , fCachedCenterX(SK_ScalarMax)
   1180     , fCachedCenterY(SK_ScalarMax)
   1181     , fCachedA(SK_ScalarMax)
   1182     , fCachedB(SK_ScalarMax)
   1183     , fCachedC(SK_ScalarMax)
   1184     , fCachedTLimit(SK_ScalarMax) {
   1185     const CircleOutside2PtConicalEffect& data = drawEffect.castEffect<CircleOutside2PtConicalEffect>();
   1186     fIsFlipped = data.isFlipped();
   1187     }
   1188 
   1189 void GLCircleOutside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
   1190                                                const GrDrawEffect&,
   1191                                                EffectKey key,
   1192                                                const char* outputColor,
   1193                                                const char* inputColor,
   1194                                                const TransformedCoordsArray& coords,
   1195                                                const TextureSamplerArray& samplers) {
   1196     this->emitUniforms(builder, key);
   1197     fCenterUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
   1198                                      kVec2f_GrSLType, "Conical2FSCenter");
   1199     fParamUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
   1200                                     kVec4f_GrSLType, "Conical2FSParams");
   1201     SkString tName("t");
   1202 
   1203     GrGLShaderVar center = builder->getUniformVariable(fCenterUni);
   1204     // params.x = A
   1205     // params.y = B
   1206     // params.z = C
   1207     GrGLShaderVar params = builder->getUniformVariable(fParamUni);
   1208 
   1209     // if we have a vec3 from being in perspective, convert it to a vec2 first
   1210     SkString coords2DString = builder->ensureFSCoords2D(coords, 0);
   1211     const char* coords2D = coords2DString.c_str();
   1212 
   1213     // output will default to transparent black (we simply won't write anything
   1214     // else to it if invalid, instead of discarding or returning prematurely)
   1215     builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor);
   1216 
   1217     // p = coords2D
   1218     // e = center end
   1219     // r = radius end
   1220     // A = dot(e, e) - r^2 + 2 * r - 1
   1221     // B = (r -1) / A
   1222     // C = 1 / A
   1223     // d = dot(e, p) + B
   1224     // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
   1225 
   1226     builder->fsCodeAppendf("\tfloat pDotp = dot(%s,  %s);\n", coords2D, coords2D);
   1227     builder->fsCodeAppendf("\tfloat d = dot(%s,  %s) + %s.y;\n", coords2D, center.c_str(), params.c_str());
   1228     builder->fsCodeAppendf("\tfloat deter = d * d - %s.x * pDotp + %s.z;\n", params.c_str(), params.c_str());
   1229 
   1230     // Must check to see if we flipped the circle order (to make sure start radius < end radius)
   1231     // If so we must also flip sign on sqrt
   1232     if (!fIsFlipped) {
   1233         builder->fsCodeAppendf("\tfloat %s = d + sqrt(deter);\n", tName.c_str());
   1234     } else {
   1235         builder->fsCodeAppendf("\tfloat %s = d - sqrt(deter);\n", tName.c_str());
   1236     }
   1237 
   1238     builder->fsCodeAppendf("\tif (%s >= %s.w && deter >= 0.0) {\n", tName.c_str(), params.c_str());
   1239     builder->fsCodeAppend("\t\t");
   1240     this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
   1241     builder->fsCodeAppend("\t}\n");
   1242 }
   1243 
   1244 void GLCircleOutside2PtConicalEffect::setData(const GrGLUniformManager& uman,
   1245                                               const GrDrawEffect& drawEffect) {
   1246     INHERITED::setData(uman, drawEffect);
   1247     const CircleOutside2PtConicalEffect& data = drawEffect.castEffect<CircleOutside2PtConicalEffect>();
   1248     SkASSERT(data.isFlipped() == fIsFlipped);
   1249     SkScalar centerX = data.centerX();
   1250     SkScalar centerY = data.centerY();
   1251     SkScalar A = data.A();
   1252     SkScalar B = data.B();
   1253     SkScalar C = data.C();
   1254     SkScalar tLimit = data.tLimit();
   1255 
   1256     if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
   1257         fCachedA != A || fCachedB != B || fCachedC != C || fCachedTLimit != tLimit) {
   1258 
   1259         uman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
   1260         uman.set4f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C),
   1261                    SkScalarToFloat(tLimit));
   1262 
   1263         fCachedCenterX = centerX;
   1264         fCachedCenterY = centerY;
   1265         fCachedA = A;
   1266         fCachedB = B;
   1267         fCachedC = C;
   1268         fCachedTLimit = tLimit;
   1269     }
   1270 }
   1271 
   1272 GrGLEffect::EffectKey GLCircleOutside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect,
   1273                                                               const GrGLCaps&) {
   1274     enum {
   1275         kIsFlipped = 1 << kBaseKeyBitCnt,
   1276     };
   1277 
   1278     EffectKey key = GenBaseGradientKey(drawEffect);
   1279 
   1280     if (drawEffect.castEffect<CircleOutside2PtConicalEffect>().isFlipped()) {
   1281         key |= kIsFlipped;
   1282     }
   1283     return key;
   1284 }
   1285 
   1286 //////////////////////////////////////////////////////////////////////////////
   1287 
   1288 GrEffectRef* Gr2PtConicalGradientEffect::Create(GrContext* ctx,
   1289                                                 const SkTwoPointConicalGradient& shader,
   1290                                                 SkShader::TileMode tm,
   1291                                                 const SkMatrix* localMatrix) {
   1292     SkMatrix matrix;
   1293     if (!shader.getLocalMatrix().invert(&matrix)) {
   1294         return NULL;
   1295     }
   1296     if (localMatrix) {
   1297         SkMatrix inv;
   1298         if (!localMatrix->invert(&inv)) {
   1299             return NULL;
   1300         }
   1301         matrix.postConcat(inv);
   1302     }
   1303 
   1304     if (shader.getStartRadius() < kErrorTol) {
   1305         SkScalar focalX;
   1306         ConicalType type = set_matrix_focal_conical(shader, &matrix, &focalX);
   1307         if (type == kInside_ConicalType) {
   1308             return FocalInside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX);
   1309         } else if(type == kEdge_ConicalType) {
   1310             set_matrix_edge_conical(shader, &matrix);
   1311             return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm);
   1312         } else {
   1313             return FocalOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX);
   1314         }
   1315     }
   1316 
   1317     CircleConicalInfo info;
   1318     ConicalType type = set_matrix_circle_conical(shader, &matrix, &info);
   1319 
   1320     if (type == kInside_ConicalType) {
   1321         return CircleInside2PtConicalEffect::Create(ctx, shader, matrix, tm, info);
   1322     } else if (type == kEdge_ConicalType) {
   1323         set_matrix_edge_conical(shader, &matrix);
   1324         return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm);
   1325     } else {
   1326         return CircleOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, info);
   1327     }
   1328 }
   1329 
   1330 #endif
   1331