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