Home | History | Annotate | Download | only in gradients
      1 /*
      2  * Copyright 2012 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkTwoPointConicalGradient.h"
      9 
     10 static int valid_divide(float numer, float denom, float* ratio) {
     11     SkASSERT(ratio);
     12     if (0 == denom) {
     13         return 0;
     14     }
     15     *ratio = numer / denom;
     16     return 1;
     17 }
     18 
     19 // Return the number of distinct real roots, and write them into roots[] in
     20 // ascending order
     21 static int find_quad_roots(float A, float B, float C, float roots[2]) {
     22     SkASSERT(roots);
     23 
     24     if (A == 0) {
     25         return valid_divide(-C, B, roots);
     26     }
     27 
     28     float R = B*B - 4*A*C;
     29     if (R < 0) {
     30         return 0;
     31     }
     32     R = sk_float_sqrt(R);
     33 
     34 #if 1
     35     float Q = B;
     36     if (Q < 0) {
     37         Q -= R;
     38     } else {
     39         Q += R;
     40     }
     41 #else
     42     // on 10.6 this was much slower than the above branch :(
     43     float Q = B + copysignf(R, B);
     44 #endif
     45     Q *= -0.5f;
     46     if (0 == Q) {
     47         roots[0] = 0;
     48         return 1;
     49     }
     50 
     51     float r0 = Q / A;
     52     float r1 = C / Q;
     53     roots[0] = r0 < r1 ? r0 : r1;
     54     roots[1] = r0 > r1 ? r0 : r1;
     55     return 2;
     56 }
     57 
     58 static float lerp(float x, float dx, float t) {
     59     return x + t * dx;
     60 }
     61 
     62 static float sqr(float x) { return x * x; }
     63 
     64 void TwoPtRadial::init(const SkPoint& center0, SkScalar rad0,
     65                        const SkPoint& center1, SkScalar rad1) {
     66     fCenterX = SkScalarToFloat(center0.fX);
     67     fCenterY = SkScalarToFloat(center0.fY);
     68     fDCenterX = SkScalarToFloat(center1.fX) - fCenterX;
     69     fDCenterY = SkScalarToFloat(center1.fY) - fCenterY;
     70     fRadius = SkScalarToFloat(rad0);
     71     fDRadius = SkScalarToFloat(rad1) - fRadius;
     72 
     73     fA = sqr(fDCenterX) + sqr(fDCenterY) - sqr(fDRadius);
     74     fRadius2 = sqr(fRadius);
     75     fRDR = fRadius * fDRadius;
     76 }
     77 
     78 void TwoPtRadial::setup(SkScalar fx, SkScalar fy, SkScalar dfx, SkScalar dfy) {
     79     fRelX = SkScalarToFloat(fx) - fCenterX;
     80     fRelY = SkScalarToFloat(fy) - fCenterY;
     81     fIncX = SkScalarToFloat(dfx);
     82     fIncY = SkScalarToFloat(dfy);
     83     fB = -2 * (fDCenterX * fRelX + fDCenterY * fRelY + fRDR);
     84     fDB = -2 * (fDCenterX * fIncX + fDCenterY * fIncY);
     85 }
     86 
     87 SkFixed TwoPtRadial::nextT() {
     88     float roots[2];
     89 
     90     float C = sqr(fRelX) + sqr(fRelY) - fRadius2;
     91     int countRoots = find_quad_roots(fA, fB, C, roots);
     92 
     93     fRelX += fIncX;
     94     fRelY += fIncY;
     95     fB += fDB;
     96 
     97     if (0 == countRoots) {
     98         return kDontDrawT;
     99     }
    100 
    101     // Prefer the bigger t value if both give a radius(t) > 0
    102     // find_quad_roots returns the values sorted, so we start with the last
    103     float t = roots[countRoots - 1];
    104     float r = lerp(fRadius, fDRadius, t);
    105     if (r <= 0) {
    106         t = roots[0];   // might be the same as roots[countRoots-1]
    107         r = lerp(fRadius, fDRadius, t);
    108         if (r <= 0) {
    109             return kDontDrawT;
    110         }
    111     }
    112     return SkFloatToFixed(t);
    113 }
    114 
    115 typedef void (*TwoPointConicalProc)(TwoPtRadial* rec, SkPMColor* dstC,
    116                                     const SkPMColor* cache, int toggle, int count);
    117 
    118 static void twopoint_clamp(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC,
    119                            const SkPMColor* SK_RESTRICT cache, int toggle,
    120                            int count) {
    121     for (; count > 0; --count) {
    122         SkFixed t = rec->nextT();
    123         if (TwoPtRadial::DontDrawT(t)) {
    124             *dstC++ = 0;
    125         } else {
    126             SkFixed index = SkClampMax(t, 0xFFFF);
    127             SkASSERT(index <= 0xFFFF);
    128             *dstC++ = cache[toggle +
    129                             (index >> SkGradientShaderBase::kCache32Shift)];
    130         }
    131         toggle = next_dither_toggle(toggle);
    132     }
    133 }
    134 
    135 static void twopoint_repeat(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC,
    136                             const SkPMColor* SK_RESTRICT cache, int toggle,
    137                             int count) {
    138     for (; count > 0; --count) {
    139         SkFixed t = rec->nextT();
    140         if (TwoPtRadial::DontDrawT(t)) {
    141             *dstC++ = 0;
    142         } else {
    143             SkFixed index = repeat_tileproc(t);
    144             SkASSERT(index <= 0xFFFF);
    145             *dstC++ = cache[toggle +
    146                             (index >> SkGradientShaderBase::kCache32Shift)];
    147         }
    148         toggle = next_dither_toggle(toggle);
    149     }
    150 }
    151 
    152 static void twopoint_mirror(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC,
    153                             const SkPMColor* SK_RESTRICT cache, int toggle,
    154                             int count) {
    155     for (; count > 0; --count) {
    156         SkFixed t = rec->nextT();
    157         if (TwoPtRadial::DontDrawT(t)) {
    158             *dstC++ = 0;
    159         } else {
    160             SkFixed index = mirror_tileproc(t);
    161             SkASSERT(index <= 0xFFFF);
    162             *dstC++ = cache[toggle +
    163                             (index >> SkGradientShaderBase::kCache32Shift)];
    164         }
    165         toggle = next_dither_toggle(toggle);
    166     }
    167 }
    168 
    169 void SkTwoPointConicalGradient::init() {
    170     fRec.init(fCenter1, fRadius1, fCenter2, fRadius2);
    171     fPtsToUnit.reset();
    172 }
    173 
    174 /////////////////////////////////////////////////////////////////////
    175 
    176 SkTwoPointConicalGradient::SkTwoPointConicalGradient(
    177         const SkPoint& start, SkScalar startRadius,
    178         const SkPoint& end, SkScalar endRadius,
    179         const Descriptor& desc)
    180     : SkGradientShaderBase(desc),
    181     fCenter1(start),
    182     fCenter2(end),
    183     fRadius1(startRadius),
    184     fRadius2(endRadius) {
    185     // this is degenerate, and should be caught by our caller
    186     SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
    187     this->init();
    188 }
    189 
    190 bool SkTwoPointConicalGradient::isOpaque() const {
    191     // Because areas outside the cone are left untouched, we cannot treat the
    192     // shader as opaque even if the gradient itself is opaque.
    193     // TODO(junov): Compute whether the cone fills the plane crbug.com/222380
    194     return false;
    195 }
    196 
    197 void SkTwoPointConicalGradient::shadeSpan(int x, int y, SkPMColor* dstCParam,
    198                                           int count) {
    199     int toggle = init_dither_toggle(x, y);
    200 
    201     SkASSERT(count > 0);
    202 
    203     SkPMColor* SK_RESTRICT dstC = dstCParam;
    204 
    205     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
    206 
    207     const SkPMColor* SK_RESTRICT cache = this->getCache32();
    208 
    209     TwoPointConicalProc shadeProc = twopoint_repeat;
    210     if (SkShader::kClamp_TileMode == fTileMode) {
    211         shadeProc = twopoint_clamp;
    212     } else if (SkShader::kMirror_TileMode == fTileMode) {
    213         shadeProc = twopoint_mirror;
    214     } else {
    215         SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
    216     }
    217 
    218     if (fDstToIndexClass != kPerspective_MatrixClass) {
    219         SkPoint srcPt;
    220         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
    221                 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
    222         SkScalar dx, fx = srcPt.fX;
    223         SkScalar dy, fy = srcPt.fY;
    224 
    225         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
    226             SkFixed fixedX, fixedY;
    227             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
    228             dx = SkFixedToScalar(fixedX);
    229             dy = SkFixedToScalar(fixedY);
    230         } else {
    231             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
    232             dx = fDstToIndex.getScaleX();
    233             dy = fDstToIndex.getSkewY();
    234         }
    235 
    236         fRec.setup(fx, fy, dx, dy);
    237         (*shadeProc)(&fRec, dstC, cache, toggle, count);
    238     } else {    // perspective case
    239         SkScalar dstX = SkIntToScalar(x) + SK_ScalarHalf;
    240         SkScalar dstY = SkIntToScalar(y) + SK_ScalarHalf;
    241         for (; count > 0; --count) {
    242             SkPoint srcPt;
    243             dstProc(fDstToIndex, dstX, dstY, &srcPt);
    244             fRec.setup(srcPt.fX, srcPt.fY, 0, 0);
    245             (*shadeProc)(&fRec, dstC, cache, toggle, 1);
    246 
    247             dstX += SK_Scalar1;
    248             toggle = next_dither_toggle(toggle);
    249             dstC += 1;
    250         }
    251     }
    252 }
    253 
    254 bool SkTwoPointConicalGradient::setContext(const SkBitmap& device,
    255                                            const SkPaint& paint,
    256                                            const SkMatrix& matrix) {
    257     if (!this->INHERITED::setContext(device, paint, matrix)) {
    258         return false;
    259     }
    260 
    261     // we don't have a span16 proc
    262     fFlags &= ~kHasSpan16_Flag;
    263 
    264     // in general, we might discard based on computed-radius, so clear
    265     // this flag (todo: sometimes we can detect that we never discard...)
    266     fFlags &= ~kOpaqueAlpha_Flag;
    267 
    268     return true;
    269 }
    270 
    271 SkShader::BitmapType SkTwoPointConicalGradient::asABitmap(
    272     SkBitmap* bitmap, SkMatrix* matrix, SkShader::TileMode* xy) const {
    273     SkPoint diff = fCenter2 - fCenter1;
    274     SkScalar diffLen = 0;
    275 
    276     if (bitmap) {
    277         this->getGradientTableBitmap(bitmap);
    278     }
    279     if (matrix) {
    280         diffLen = diff.length();
    281     }
    282     if (matrix) {
    283         if (diffLen) {
    284             SkScalar invDiffLen = SkScalarInvert(diffLen);
    285             // rotate to align circle centers with the x-axis
    286             matrix->setSinCos(-SkScalarMul(invDiffLen, diff.fY),
    287                               SkScalarMul(invDiffLen, diff.fX));
    288         } else {
    289             matrix->reset();
    290         }
    291         matrix->preTranslate(-fCenter1.fX, -fCenter1.fY);
    292     }
    293     if (xy) {
    294         xy[0] = fTileMode;
    295         xy[1] = kClamp_TileMode;
    296     }
    297     return kTwoPointConical_BitmapType;
    298 }
    299 
    300 SkShader::GradientType SkTwoPointConicalGradient::asAGradient(
    301     GradientInfo* info) const {
    302     if (info) {
    303         commonAsAGradient(info);
    304         info->fPoint[0] = fCenter1;
    305         info->fPoint[1] = fCenter2;
    306         info->fRadius[0] = fRadius1;
    307         info->fRadius[1] = fRadius2;
    308     }
    309     return kConical_GradientType;
    310 }
    311 
    312 SkTwoPointConicalGradient::SkTwoPointConicalGradient(
    313     SkFlattenableReadBuffer& buffer)
    314     : INHERITED(buffer),
    315     fCenter1(buffer.readPoint()),
    316     fCenter2(buffer.readPoint()),
    317     fRadius1(buffer.readScalar()),
    318     fRadius2(buffer.readScalar()) {
    319     this->init();
    320 };
    321 
    322 void SkTwoPointConicalGradient::flatten(
    323     SkFlattenableWriteBuffer& buffer) const {
    324     this->INHERITED::flatten(buffer);
    325     buffer.writePoint(fCenter1);
    326     buffer.writePoint(fCenter2);
    327     buffer.writeScalar(fRadius1);
    328     buffer.writeScalar(fRadius2);
    329 }
    330 
    331 /////////////////////////////////////////////////////////////////////
    332 
    333 #if SK_SUPPORT_GPU
    334 
    335 #include "GrTBackendEffectFactory.h"
    336 
    337 // For brevity
    338 typedef GrGLUniformManager::UniformHandle UniformHandle;
    339 
    340 class GrGLConical2Gradient : public GrGLGradientEffect {
    341 public:
    342 
    343     GrGLConical2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffect&);
    344     virtual ~GrGLConical2Gradient() { }
    345 
    346     virtual void emitCode(GrGLShaderBuilder*,
    347                           const GrDrawEffect&,
    348                           EffectKey,
    349                           const char* outputColor,
    350                           const char* inputColor,
    351                           const TransformedCoordsArray&,
    352                           const TextureSamplerArray&) SK_OVERRIDE;
    353     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
    354 
    355     static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
    356 
    357 protected:
    358 
    359     UniformHandle fParamUni;
    360 
    361     const char* fVSVaryingName;
    362     const char* fFSVaryingName;
    363 
    364     bool fIsDegenerate;
    365 
    366     // @{
    367     /// Values last uploaded as uniforms
    368 
    369     SkScalar fCachedCenter;
    370     SkScalar fCachedRadius;
    371     SkScalar fCachedDiffRadius;
    372 
    373     // @}
    374 
    375 private:
    376 
    377     typedef GrGLGradientEffect INHERITED;
    378 
    379 };
    380 
    381 /////////////////////////////////////////////////////////////////////
    382 
    383 class GrConical2Gradient : public GrGradientEffect {
    384 public:
    385 
    386     static GrEffectRef* Create(GrContext* ctx,
    387                                const SkTwoPointConicalGradient& shader,
    388                                const SkMatrix& matrix,
    389                                SkShader::TileMode tm) {
    390         AutoEffectUnref effect(SkNEW_ARGS(GrConical2Gradient, (ctx, shader, matrix, tm)));
    391         return CreateEffectRef(effect);
    392     }
    393 
    394     virtual ~GrConical2Gradient() { }
    395 
    396     static const char* Name() { return "Two-Point Conical Gradient"; }
    397     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
    398         return GrTBackendEffectFactory<GrConical2Gradient>::getInstance();
    399     }
    400 
    401     // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
    402     bool isDegenerate() const { return SkScalarAbs(fDiffRadius) == SkScalarAbs(fCenterX1); }
    403     SkScalar center() const { return fCenterX1; }
    404     SkScalar diffRadius() const { return fDiffRadius; }
    405     SkScalar radius() const { return fRadius0; }
    406 
    407     typedef GrGLConical2Gradient GLEffect;
    408 
    409 private:
    410     virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
    411         const GrConical2Gradient& s = CastEffect<GrConical2Gradient>(sBase);
    412         return (INHERITED::onIsEqual(sBase) &&
    413                 this->fCenterX1 == s.fCenterX1 &&
    414                 this->fRadius0 == s.fRadius0 &&
    415                 this->fDiffRadius == s.fDiffRadius);
    416     }
    417 
    418     GrConical2Gradient(GrContext* ctx,
    419                        const SkTwoPointConicalGradient& shader,
    420                        const SkMatrix& matrix,
    421                        SkShader::TileMode tm)
    422         : INHERITED(ctx, shader, matrix, tm)
    423         , fCenterX1(shader.getCenterX1())
    424         , fRadius0(shader.getStartRadius())
    425         , fDiffRadius(shader.getDiffRadius()) {
    426         // We pass the linear part of the quadratic as a varying.
    427         //    float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z)
    428         fBTransform = this->getCoordTransform();
    429         SkMatrix& bMatrix = *fBTransform.accessMatrix();
    430         SkScalar r0dr = SkScalarMul(fRadius0, fDiffRadius);
    431         bMatrix[SkMatrix::kMScaleX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) +
    432                                             SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp0]));
    433         bMatrix[SkMatrix::kMSkewX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) +
    434                                            SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp1]));
    435         bMatrix[SkMatrix::kMTransX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) +
    436                                             SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp2]));
    437         this->addCoordTransform(&fBTransform);
    438     }
    439 
    440     GR_DECLARE_EFFECT_TEST;
    441 
    442     // @{
    443     // Cache of values - these can change arbitrarily, EXCEPT
    444     // we shouldn't change between degenerate and non-degenerate?!
    445 
    446     GrCoordTransform fBTransform;
    447     SkScalar         fCenterX1;
    448     SkScalar         fRadius0;
    449     SkScalar         fDiffRadius;
    450 
    451     // @}
    452 
    453     typedef GrGradientEffect INHERITED;
    454 };
    455 
    456 GR_DEFINE_EFFECT_TEST(GrConical2Gradient);
    457 
    458 GrEffectRef* GrConical2Gradient::TestCreate(SkRandom* random,
    459                                             GrContext* context,
    460                                             const GrDrawTargetCaps&,
    461                                             GrTexture**) {
    462     SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
    463     SkScalar radius1 = random->nextUScalar1();
    464     SkPoint center2;
    465     SkScalar radius2;
    466     do {
    467         center2.set(random->nextUScalar1(), random->nextUScalar1());
    468         radius2 = random->nextUScalar1 ();
    469         // If the circles are identical the factory will give us an empty shader.
    470     } while (radius1 == radius2 && center1 == center2);
    471 
    472     SkColor colors[kMaxRandomGradientColors];
    473     SkScalar stopsArray[kMaxRandomGradientColors];
    474     SkScalar* stops = stopsArray;
    475     SkShader::TileMode tm;
    476     int colorCount = RandomGradientParams(random, colors, &stops, &tm);
    477     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
    478                                                                           center2, radius2,
    479                                                                           colors, stops, colorCount,
    480                                                                           tm));
    481     SkPaint paint;
    482     return shader->asNewEffect(context, paint);
    483 }
    484 
    485 
    486 /////////////////////////////////////////////////////////////////////
    487 
    488 GrGLConical2Gradient::GrGLConical2Gradient(const GrBackendEffectFactory& factory,
    489                                            const GrDrawEffect& drawEffect)
    490     : INHERITED(factory)
    491     , fVSVaryingName(NULL)
    492     , fFSVaryingName(NULL)
    493     , fCachedCenter(SK_ScalarMax)
    494     , fCachedRadius(-SK_ScalarMax)
    495     , fCachedDiffRadius(-SK_ScalarMax) {
    496 
    497     const GrConical2Gradient& data = drawEffect.castEffect<GrConical2Gradient>();
    498     fIsDegenerate = data.isDegenerate();
    499 }
    500 
    501 void GrGLConical2Gradient::emitCode(GrGLShaderBuilder* builder,
    502                                     const GrDrawEffect&,
    503                                     EffectKey key,
    504                                     const char* outputColor,
    505                                     const char* inputColor,
    506                                     const TransformedCoordsArray& coords,
    507                                     const TextureSamplerArray& samplers) {
    508     this->emitUniforms(builder, key);
    509     fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
    510                                          kFloat_GrSLType, "Conical2FSParams", 6);
    511 
    512     SkString cName("c");
    513     SkString ac4Name("ac4");
    514     SkString dName("d");
    515     SkString qName("q");
    516     SkString r0Name("r0");
    517     SkString r1Name("r1");
    518     SkString tName("t");
    519     SkString p0; // 4a
    520     SkString p1; // 1/a
    521     SkString p2; // distance between centers
    522     SkString p3; // start radius
    523     SkString p4; // start radius squared
    524     SkString p5; // difference in radii (r1 - r0)
    525 
    526     builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
    527     builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
    528     builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
    529     builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3);
    530     builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4);
    531     builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5);
    532 
    533     // We interpolate the linear component in coords[1].
    534     SkASSERT(coords[0].type() == coords[1].type());
    535     const char* coords2D;
    536     SkString bVar;
    537     if (kVec3f_GrSLType == coords[0].type()) {
    538         builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n",
    539                                coords[0].c_str(), coords[1].c_str(), coords[0].c_str());
    540         coords2D = "interpolants.xy";
    541         bVar = "interpolants.z";
    542     } else {
    543         coords2D = coords[0].c_str();
    544         bVar.printf("%s.x", coords[1].c_str());
    545     }
    546 
    547     // output will default to transparent black (we simply won't write anything
    548     // else to it if invalid, instead of discarding or returning prematurely)
    549     builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor);
    550 
    551     // c = (x^2)+(y^2) - params[4]
    552     builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
    553                            cName.c_str(), coords2D, coords2D, p4.c_str());
    554 
    555     // Non-degenerate case (quadratic)
    556     if (!fIsDegenerate) {
    557 
    558         // ac4 = params[0] * c
    559         builder->fsCodeAppendf("\tfloat %s = %s * %s;\n", ac4Name.c_str(), p0.c_str(),
    560                                cName.c_str());
    561 
    562         // d = b^2 - ac4
    563         builder->fsCodeAppendf("\tfloat %s = %s * %s - %s;\n", dName.c_str(),
    564                                bVar.c_str(), bVar.c_str(), ac4Name.c_str());
    565 
    566         // only proceed if discriminant is >= 0
    567         builder->fsCodeAppendf("\tif (%s >= 0.0) {\n", dName.c_str());
    568 
    569         // intermediate value we'll use to compute the roots
    570         // q = -0.5 * (b +/- sqrt(d))
    571         builder->fsCodeAppendf("\t\tfloat %s = -0.5 * (%s + (%s < 0.0 ? -1.0 : 1.0)"
    572                                " * sqrt(%s));\n", qName.c_str(), bVar.c_str(),
    573                                bVar.c_str(), dName.c_str());
    574 
    575         // compute both roots
    576         // r0 = q * params[1]
    577         builder->fsCodeAppendf("\t\tfloat %s = %s * %s;\n", r0Name.c_str(),
    578                                qName.c_str(), p1.c_str());
    579         // r1 = c / q
    580         builder->fsCodeAppendf("\t\tfloat %s = %s / %s;\n", r1Name.c_str(),
    581                                cName.c_str(), qName.c_str());
    582 
    583         // Note: If there are two roots that both generate radius(t) > 0, the
    584         // Canvas spec says to choose the larger t.
    585 
    586         // so we'll look at the larger one first:
    587         builder->fsCodeAppendf("\t\tfloat %s = max(%s, %s);\n", tName.c_str(),
    588                                r0Name.c_str(), r1Name.c_str());
    589 
    590         // if r(t) > 0, then we're done; t will be our x coordinate
    591         builder->fsCodeAppendf("\t\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
    592                                p5.c_str(), p3.c_str());
    593 
    594         builder->fsCodeAppend("\t\t");
    595         this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
    596 
    597         // otherwise, if r(t) for the larger root was <= 0, try the other root
    598         builder->fsCodeAppend("\t\t} else {\n");
    599         builder->fsCodeAppendf("\t\t\t%s = min(%s, %s);\n", tName.c_str(),
    600                                r0Name.c_str(), r1Name.c_str());
    601 
    602         // if r(t) > 0 for the smaller root, then t will be our x coordinate
    603         builder->fsCodeAppendf("\t\t\tif (%s * %s + %s > 0.0) {\n",
    604                                tName.c_str(), p5.c_str(), p3.c_str());
    605 
    606         builder->fsCodeAppend("\t\t\t");
    607         this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
    608 
    609         // end if (r(t) > 0) for smaller root
    610         builder->fsCodeAppend("\t\t\t}\n");
    611         // end if (r(t) > 0), else, for larger root
    612         builder->fsCodeAppend("\t\t}\n");
    613         // end if (discriminant >= 0)
    614         builder->fsCodeAppend("\t}\n");
    615     } else {
    616 
    617         // linear case: t = -c/b
    618         builder->fsCodeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(),
    619                                cName.c_str(), bVar.c_str());
    620 
    621         // if r(t) > 0, then t will be the x coordinate
    622         builder->fsCodeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
    623                                p5.c_str(), p3.c_str());
    624         builder->fsCodeAppend("\t");
    625         this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
    626         builder->fsCodeAppend("\t}\n");
    627     }
    628 }
    629 
    630 void GrGLConical2Gradient::setData(const GrGLUniformManager& uman,
    631                                    const GrDrawEffect& drawEffect) {
    632     INHERITED::setData(uman, drawEffect);
    633     const GrConical2Gradient& data = drawEffect.castEffect<GrConical2Gradient>();
    634     SkASSERT(data.isDegenerate() == fIsDegenerate);
    635     SkScalar centerX1 = data.center();
    636     SkScalar radius0 = data.radius();
    637     SkScalar diffRadius = data.diffRadius();
    638 
    639     if (fCachedCenter != centerX1 ||
    640         fCachedRadius != radius0 ||
    641         fCachedDiffRadius != diffRadius) {
    642 
    643         SkScalar a = SkScalarMul(centerX1, centerX1) - diffRadius * diffRadius;
    644 
    645         // When we're in the degenerate (linear) case, the second
    646         // value will be INF but the program doesn't read it. (We
    647         // use the same 6 uniforms even though we don't need them
    648         // all in the linear case just to keep the code complexity
    649         // down).
    650         float values[6] = {
    651             SkScalarToFloat(a * 4),
    652             1.f / (SkScalarToFloat(a)),
    653             SkScalarToFloat(centerX1),
    654             SkScalarToFloat(radius0),
    655             SkScalarToFloat(SkScalarMul(radius0, radius0)),
    656             SkScalarToFloat(diffRadius)
    657         };
    658 
    659         uman.set1fv(fParamUni, 6, values);
    660         fCachedCenter = centerX1;
    661         fCachedRadius = radius0;
    662         fCachedDiffRadius = diffRadius;
    663     }
    664 }
    665 
    666 GrGLEffect::EffectKey GrGLConical2Gradient::GenKey(const GrDrawEffect& drawEffect,
    667                                                    const GrGLCaps&) {
    668     enum {
    669         kIsDegenerate = 1 << kBaseKeyBitCnt,
    670     };
    671 
    672     EffectKey key = GenBaseGradientKey(drawEffect);
    673     if (drawEffect.castEffect<GrConical2Gradient>().isDegenerate()) {
    674         key |= kIsDegenerate;
    675     }
    676     return key;
    677 }
    678 
    679 /////////////////////////////////////////////////////////////////////
    680 
    681 GrEffectRef* SkTwoPointConicalGradient::asNewEffect(GrContext* context, const SkPaint&) const {
    682     SkASSERT(NULL != context);
    683     SkASSERT(fPtsToUnit.isIdentity());
    684     // invert the localM, translate to center1, rotate so center2 is on x axis.
    685     SkMatrix matrix;
    686     if (!this->getLocalMatrix().invert(&matrix)) {
    687         return NULL;
    688     }
    689     matrix.postTranslate(-fCenter1.fX, -fCenter1.fY);
    690 
    691     SkPoint diff = fCenter2 - fCenter1;
    692     SkScalar diffLen = diff.length();
    693     if (0 != diffLen) {
    694         SkScalar invDiffLen = SkScalarInvert(diffLen);
    695         SkMatrix rot;
    696         rot.setSinCos(-SkScalarMul(invDiffLen, diff.fY),
    697                        SkScalarMul(invDiffLen, diff.fX));
    698         matrix.postConcat(rot);
    699     }
    700 
    701     return GrConical2Gradient::Create(context, *this, matrix, fTileMode);
    702 }
    703 
    704 #else
    705 
    706 GrEffectRef* SkTwoPointConicalGradient::asNewEffect(GrContext*, const SkPaint&) const {
    707     SkDEBUGFAIL("Should not call in GPU-less build");
    708     return NULL;
    709 }
    710 
    711 #endif
    712 
    713 #ifdef SK_DEVELOPER
    714 void SkTwoPointConicalGradient::toString(SkString* str) const {
    715     str->append("SkTwoPointConicalGradient: (");
    716 
    717     str->append("center1: (");
    718     str->appendScalar(fCenter1.fX);
    719     str->append(", ");
    720     str->appendScalar(fCenter1.fY);
    721     str->append(") radius1: ");
    722     str->appendScalar(fRadius1);
    723     str->append(" ");
    724 
    725     str->append("center2: (");
    726     str->appendScalar(fCenter2.fX);
    727     str->append(", ");
    728     str->appendScalar(fCenter2.fY);
    729     str->append(") radius2: ");
    730     str->appendScalar(fRadius2);
    731     str->append(" ");
    732 
    733     this->INHERITED::toString(str);
    734 
    735     str->append(")");
    736 }
    737 #endif
    738