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