Home | History | Annotate | Download | only in gradients
      1 
      2 /*
      3  * Copyright 2012 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 "SkTwoPointRadialGradient.h"
     10 
     11 /* Two-point radial gradients are specified by two circles, each with a center
     12    point and radius.  The gradient can be considered to be a series of
     13    concentric circles, with the color interpolated from the start circle
     14    (at t=0) to the end circle (at t=1).
     15 
     16    For each point (x, y) in the span, we want to find the
     17    interpolated circle that intersects that point.  The center
     18    of the desired circle (Cx, Cy) falls at some distance t
     19    along the line segment between the start point (Sx, Sy) and
     20    end point (Ex, Ey):
     21 
     22       Cx = (1 - t) * Sx + t * Ex        (0 <= t <= 1)
     23       Cy = (1 - t) * Sy + t * Ey
     24 
     25    The radius of the desired circle (r) is also a linear interpolation t
     26    between the start and end radii (Sr and Er):
     27 
     28       r = (1 - t) * Sr + t * Er
     29 
     30    But
     31 
     32       (x - Cx)^2 + (y - Cy)^2 = r^2
     33 
     34    so
     35 
     36      (x - ((1 - t) * Sx + t * Ex))^2
     37    + (y - ((1 - t) * Sy + t * Ey))^2
     38    = ((1 - t) * Sr + t * Er)^2
     39 
     40    Solving for t yields
     41 
     42      [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2
     43    + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t
     44    + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0
     45 
     46    To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
     47 
     48      [Dx^2 + Dy^2 - Dr^2)] * t^2
     49    + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t
     50    + [dx^2 + dy^2 - Sr^2] = 0
     51 
     52    A quadratic in t.  The two roots of the quadratic reflect the two
     53    possible circles on which the point may fall.  Solving for t yields
     54    the gradient value to use.
     55 
     56    If a<0, the start circle is entirely contained in the
     57    end circle, and one of the roots will be <0 or >1 (off the line
     58    segment).  If a>0, the start circle falls at least partially
     59    outside the end circle (or vice versa), and the gradient
     60    defines a "tube" where a point may be on one circle (on the
     61    inside of the tube) or the other (outside of the tube).  We choose
     62    one arbitrarily.
     63 
     64    In order to keep the math to within the limits of fixed point,
     65    we divide the entire quadratic by Dr^2, and replace
     66    (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
     67 
     68    [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
     69    + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
     70    + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
     71 
     72    (x' and y' are computed by appending the subtract and scale to the
     73    fDstToIndex matrix in the constructor).
     74 
     75    Since the 'A' component of the quadratic is independent of x' and y', it
     76    is precomputed in the constructor.  Since the 'B' component is linear in
     77    x' and y', if x and y are linear in the span, 'B' can be computed
     78    incrementally with a simple delta (db below).  If it is not (e.g.,
     79    a perspective projection), it must be computed in the loop.
     80 
     81 */
     82 
     83 namespace {
     84 
     85 inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy,
     86                                 SkScalar sr2d2, SkScalar foura,
     87                                 SkScalar oneOverTwoA, bool posRoot) {
     88     SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2;
     89     if (0 == foura) {
     90         return SkScalarToFixed(SkScalarDiv(-c, b));
     91     }
     92 
     93     SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c);
     94     if (discrim < 0) {
     95         discrim = -discrim;
     96     }
     97     SkScalar rootDiscrim = SkScalarSqrt(discrim);
     98     SkScalar result;
     99     if (posRoot) {
    100         result = SkScalarMul(-b + rootDiscrim, oneOverTwoA);
    101     } else {
    102         result = SkScalarMul(-b - rootDiscrim, oneOverTwoA);
    103     }
    104     return SkScalarToFixed(result);
    105 }
    106 
    107 typedef void (* TwoPointRadialShadeProc)(SkScalar fx, SkScalar dx,
    108         SkScalar fy, SkScalar dy,
    109         SkScalar b, SkScalar db,
    110         SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
    111         SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
    112         int count);
    113 
    114 void shadeSpan_twopoint_clamp(SkScalar fx, SkScalar dx,
    115         SkScalar fy, SkScalar dy,
    116         SkScalar b, SkScalar db,
    117         SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
    118         SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
    119         int count) {
    120     for (; count > 0; --count) {
    121         SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
    122                                      fOneOverTwoA, posRoot);
    123         SkFixed index = SkClampMax(t, 0xFFFF);
    124         SkASSERT(index <= 0xFFFF);
    125         *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
    126         fx += dx;
    127         fy += dy;
    128         b += db;
    129     }
    130 }
    131 void shadeSpan_twopoint_mirror(SkScalar fx, SkScalar dx,
    132         SkScalar fy, SkScalar dy,
    133         SkScalar b, SkScalar db,
    134         SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
    135         SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
    136         int count) {
    137     for (; count > 0; --count) {
    138         SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
    139                                      fOneOverTwoA, posRoot);
    140         SkFixed index = mirror_tileproc(t);
    141         SkASSERT(index <= 0xFFFF);
    142         *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
    143         fx += dx;
    144         fy += dy;
    145         b += db;
    146     }
    147 }
    148 
    149 void shadeSpan_twopoint_repeat(SkScalar fx, SkScalar dx,
    150         SkScalar fy, SkScalar dy,
    151         SkScalar b, SkScalar db,
    152         SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
    153         SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
    154         int count) {
    155     for (; count > 0; --count) {
    156         SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
    157                                      fOneOverTwoA, posRoot);
    158         SkFixed index = repeat_tileproc(t);
    159         SkASSERT(index <= 0xFFFF);
    160         *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
    161         fx += dx;
    162         fy += dy;
    163         b += db;
    164     }
    165 }
    166 }
    167 
    168 /////////////////////////////////////////////////////////////////////
    169 
    170 SkTwoPointRadialGradient::SkTwoPointRadialGradient(
    171     const SkPoint& start, SkScalar startRadius,
    172     const SkPoint& end, SkScalar endRadius,
    173     const Descriptor& desc)
    174     : SkGradientShaderBase(desc),
    175       fCenter1(start),
    176       fCenter2(end),
    177       fRadius1(startRadius),
    178       fRadius2(endRadius) {
    179     init();
    180 }
    181 
    182 SkShader::BitmapType SkTwoPointRadialGradient::asABitmap(
    183     SkBitmap* bitmap,
    184     SkMatrix* matrix,
    185     SkShader::TileMode* xy) const {
    186     if (bitmap) {
    187         this->getGradientTableBitmap(bitmap);
    188     }
    189     SkScalar diffL = 0; // just to avoid gcc warning
    190     if (matrix) {
    191         diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
    192                              SkScalarSquare(fDiff.fY));
    193     }
    194     if (matrix) {
    195         if (diffL) {
    196             SkScalar invDiffL = SkScalarInvert(diffL);
    197             matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
    198                               SkScalarMul(invDiffL, fDiff.fX));
    199         } else {
    200             matrix->reset();
    201         }
    202         matrix->preConcat(fPtsToUnit);
    203     }
    204     if (xy) {
    205         xy[0] = fTileMode;
    206         xy[1] = kClamp_TileMode;
    207     }
    208     return kTwoPointRadial_BitmapType;
    209 }
    210 
    211 SkShader::GradientType SkTwoPointRadialGradient::asAGradient(
    212     SkShader::GradientInfo* info) const {
    213     if (info) {
    214         commonAsAGradient(info);
    215         info->fPoint[0] = fCenter1;
    216         info->fPoint[1] = fCenter2;
    217         info->fRadius[0] = fRadius1;
    218         info->fRadius[1] = fRadius2;
    219     }
    220     return kRadial2_GradientType;
    221 }
    222 
    223 void SkTwoPointRadialGradient::shadeSpan(int x, int y, SkPMColor* dstCParam,
    224                                          int count) {
    225     SkASSERT(count > 0);
    226 
    227     SkPMColor* SK_RESTRICT dstC = dstCParam;
    228 
    229     // Zero difference between radii:  fill with transparent black.
    230     if (fDiffRadius == 0) {
    231       sk_bzero(dstC, count * sizeof(*dstC));
    232       return;
    233     }
    234     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
    235     TileProc            proc = fTileProc;
    236     const SkPMColor* SK_RESTRICT cache = this->getCache32();
    237 
    238     SkScalar foura = fA * 4;
    239     bool posRoot = fDiffRadius < 0;
    240     if (fDstToIndexClass != kPerspective_MatrixClass) {
    241         SkPoint srcPt;
    242         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
    243                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
    244         SkScalar dx, fx = srcPt.fX;
    245         SkScalar dy, fy = srcPt.fY;
    246 
    247         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
    248             SkFixed fixedX, fixedY;
    249             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
    250             dx = SkFixedToScalar(fixedX);
    251             dy = SkFixedToScalar(fixedY);
    252         } else {
    253             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
    254             dx = fDstToIndex.getScaleX();
    255             dy = fDstToIndex.getSkewY();
    256         }
    257         SkScalar b = (SkScalarMul(fDiff.fX, fx) +
    258                      SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
    259         SkScalar db = (SkScalarMul(fDiff.fX, dx) +
    260                       SkScalarMul(fDiff.fY, dy)) * 2;
    261 
    262         TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat;
    263         if (SkShader::kClamp_TileMode == fTileMode) {
    264             shadeProc = shadeSpan_twopoint_clamp;
    265         } else if (SkShader::kMirror_TileMode == fTileMode) {
    266             shadeProc = shadeSpan_twopoint_mirror;
    267         } else {
    268             SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
    269         }
    270         (*shadeProc)(fx, dx, fy, dy, b, db,
    271                      fSr2D2, foura, fOneOverTwoA, posRoot,
    272                      dstC, cache, count);
    273     } else {    // perspective case
    274         SkScalar dstX = SkIntToScalar(x);
    275         SkScalar dstY = SkIntToScalar(y);
    276         for (; count > 0; --count) {
    277             SkPoint             srcPt;
    278             dstProc(fDstToIndex, dstX, dstY, &srcPt);
    279             SkScalar fx = srcPt.fX;
    280             SkScalar fy = srcPt.fY;
    281             SkScalar b = (SkScalarMul(fDiff.fX, fx) +
    282                          SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
    283             SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
    284                                          fOneOverTwoA, posRoot);
    285             SkFixed index = proc(t);
    286             SkASSERT(index <= 0xFFFF);
    287             *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
    288             dstX += SK_Scalar1;
    289         }
    290     }
    291 }
    292 
    293 bool SkTwoPointRadialGradient::setContext( const SkBitmap& device,
    294                                           const SkPaint& paint,
    295                                           const SkMatrix& matrix){
    296     // For now, we might have divided by zero, so detect that
    297     if (0 == fDiffRadius) {
    298         return false;
    299     }
    300 
    301     if (!this->INHERITED::setContext(device, paint, matrix)) {
    302         return false;
    303     }
    304 
    305     // we don't have a span16 proc
    306     fFlags &= ~kHasSpan16_Flag;
    307     return true;
    308 }
    309 
    310 #ifdef SK_DEVELOPER
    311 void SkTwoPointRadialGradient::toString(SkString* str) const {
    312     str->append("SkTwoPointRadialGradient: (");
    313 
    314     str->append("center1: (");
    315     str->appendScalar(fCenter1.fX);
    316     str->append(", ");
    317     str->appendScalar(fCenter1.fY);
    318     str->append(") radius1: ");
    319     str->appendScalar(fRadius1);
    320     str->append(" ");
    321 
    322     str->append("center2: (");
    323     str->appendScalar(fCenter2.fX);
    324     str->append(", ");
    325     str->appendScalar(fCenter2.fY);
    326     str->append(") radius2: ");
    327     str->appendScalar(fRadius2);
    328     str->append(" ");
    329 
    330     this->INHERITED::toString(str);
    331 
    332     str->append(")");
    333 }
    334 #endif
    335 
    336 SkTwoPointRadialGradient::SkTwoPointRadialGradient(
    337     SkFlattenableReadBuffer& buffer)
    338     : INHERITED(buffer),
    339       fCenter1(buffer.readPoint()),
    340       fCenter2(buffer.readPoint()),
    341       fRadius1(buffer.readScalar()),
    342       fRadius2(buffer.readScalar()) {
    343     init();
    344 };
    345 
    346 void SkTwoPointRadialGradient::flatten(
    347     SkFlattenableWriteBuffer& buffer) const {
    348     this->INHERITED::flatten(buffer);
    349     buffer.writePoint(fCenter1);
    350     buffer.writePoint(fCenter2);
    351     buffer.writeScalar(fRadius1);
    352     buffer.writeScalar(fRadius2);
    353 }
    354 
    355 void SkTwoPointRadialGradient::init() {
    356     fDiff = fCenter1 - fCenter2;
    357     fDiffRadius = fRadius2 - fRadius1;
    358     // hack to avoid zero-divide for now
    359     SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0;
    360     fDiff.fX = SkScalarMul(fDiff.fX, inv);
    361     fDiff.fY = SkScalarMul(fDiff.fY, inv);
    362     fStartRadius = SkScalarMul(fRadius1, inv);
    363     fSr2D2 = SkScalarSquare(fStartRadius);
    364     fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
    365     fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
    366 
    367     fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
    368     fPtsToUnit.postScale(inv, inv);
    369 }
    370 
    371 /////////////////////////////////////////////////////////////////////
    372 
    373 #if SK_SUPPORT_GPU
    374 
    375 #include "GrTBackendEffectFactory.h"
    376 
    377 // For brevity
    378 typedef GrGLUniformManager::UniformHandle UniformHandle;
    379 static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle;
    380 
    381 class GrGLRadial2Gradient : public GrGLGradientEffect {
    382 
    383 public:
    384 
    385     GrGLRadial2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffect&);
    386     virtual ~GrGLRadial2Gradient() { }
    387 
    388     virtual void emitCode(GrGLShaderBuilder*,
    389                           const GrDrawEffect&,
    390                           EffectKey,
    391                           const char* outputColor,
    392                           const char* inputColor,
    393                           const TextureSamplerArray&) SK_OVERRIDE;
    394     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
    395 
    396     static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
    397 
    398 protected:
    399 
    400     UniformHandle   fVSParamUni;
    401     UniformHandle   fFSParamUni;
    402 
    403     const char* fVSVaryingName;
    404     const char* fFSVaryingName;
    405 
    406     bool fIsDegenerate;
    407 
    408     // @{
    409     /// Values last uploaded as uniforms
    410 
    411     SkScalar fCachedCenter;
    412     SkScalar fCachedRadius;
    413     bool     fCachedPosRoot;
    414 
    415     // @}
    416 
    417 private:
    418 
    419     typedef GrGLGradientEffect INHERITED;
    420 
    421 };
    422 
    423 /////////////////////////////////////////////////////////////////////
    424 
    425 class GrRadial2Gradient : public GrGradientEffect {
    426 public:
    427     static GrEffectRef* Create(GrContext* ctx,
    428                                const SkTwoPointRadialGradient& shader,
    429                                const SkMatrix& matrix,
    430                                SkShader::TileMode tm) {
    431         AutoEffectUnref effect(SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm)));
    432         return CreateEffectRef(effect);
    433     }
    434 
    435     virtual ~GrRadial2Gradient() { }
    436 
    437     static const char* Name() { return "Two-Point Radial Gradient"; }
    438     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
    439         return GrTBackendEffectFactory<GrRadial2Gradient>::getInstance();
    440     }
    441 
    442     // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
    443     bool isDegenerate() const { return SK_Scalar1 == fCenterX1; }
    444     SkScalar center() const { return fCenterX1; }
    445     SkScalar radius() const { return fRadius0; }
    446     bool isPosRoot() const { return SkToBool(fPosRoot); }
    447 
    448     typedef GrGLRadial2Gradient GLEffect;
    449 
    450 private:
    451     virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
    452         const GrRadial2Gradient& s = CastEffect<GrRadial2Gradient>(sBase);
    453         return (INHERITED::onIsEqual(sBase) &&
    454                 this->fCenterX1 == s.fCenterX1 &&
    455                 this->fRadius0 == s.fRadius0 &&
    456                 this->fPosRoot == s.fPosRoot);
    457     }
    458 
    459     GrRadial2Gradient(GrContext* ctx,
    460                       const SkTwoPointRadialGradient& shader,
    461                       const SkMatrix& matrix,
    462                       SkShader::TileMode tm)
    463         : INHERITED(ctx, shader, matrix, tm)
    464         , fCenterX1(shader.getCenterX1())
    465         , fRadius0(shader.getStartRadius())
    466         , fPosRoot(shader.getDiffRadius() < 0) { }
    467 
    468     GR_DECLARE_EFFECT_TEST;
    469 
    470     // @{
    471     // Cache of values - these can change arbitrarily, EXCEPT
    472     // we shouldn't change between degenerate and non-degenerate?!
    473 
    474     SkScalar fCenterX1;
    475     SkScalar fRadius0;
    476     SkBool8  fPosRoot;
    477 
    478     // @}
    479 
    480     typedef GrGradientEffect INHERITED;
    481 };
    482 
    483 /////////////////////////////////////////////////////////////////////
    484 
    485 GR_DEFINE_EFFECT_TEST(GrRadial2Gradient);
    486 
    487 GrEffectRef* GrRadial2Gradient::TestCreate(SkMWCRandom* random,
    488                                            GrContext* context,
    489                                            const GrDrawTargetCaps&,
    490                                            GrTexture**) {
    491     SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
    492     SkScalar radius1 = random->nextUScalar1();
    493     SkPoint center2;
    494     SkScalar radius2;
    495     do {
    496         center2.set(random->nextUScalar1(), random->nextUScalar1());
    497         radius2 = random->nextUScalar1 ();
    498         // There is a bug in two point radial gradients with identical radii
    499     } while (radius1 == radius2);
    500 
    501     SkColor colors[kMaxRandomGradientColors];
    502     SkScalar stopsArray[kMaxRandomGradientColors];
    503     SkScalar* stops = stopsArray;
    504     SkShader::TileMode tm;
    505     int colorCount = RandomGradientParams(random, colors, &stops, &tm);
    506     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1,
    507                                                                          center2, radius2,
    508                                                                          colors, stops, colorCount,
    509                                                                          tm));
    510     SkPaint paint;
    511     return shader->asNewEffect(context, paint);
    512 }
    513 
    514 /////////////////////////////////////////////////////////////////////
    515 
    516 GrGLRadial2Gradient::GrGLRadial2Gradient(const GrBackendEffectFactory& factory,
    517                                          const GrDrawEffect& drawEffect)
    518     : INHERITED(factory)
    519     , fVSParamUni(kInvalidUniformHandle)
    520     , fFSParamUni(kInvalidUniformHandle)
    521     , fVSVaryingName(NULL)
    522     , fFSVaryingName(NULL)
    523     , fCachedCenter(SK_ScalarMax)
    524     , fCachedRadius(-SK_ScalarMax)
    525     , fCachedPosRoot(0) {
    526 
    527     const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>();
    528     fIsDegenerate = data.isDegenerate();
    529 }
    530 
    531 void GrGLRadial2Gradient::emitCode(GrGLShaderBuilder* builder,
    532                                    const GrDrawEffect& drawEffect,
    533                                    EffectKey key,
    534                                    const char* outputColor,
    535                                    const char* inputColor,
    536                                    const TextureSamplerArray& samplers) {
    537 
    538     this->emitYCoordUniform(builder);
    539     const char* fsCoords;
    540     const char* vsCoordsVarying;
    541     GrSLType coordsVaryingType;
    542     this->setupMatrix(builder, key, &fsCoords, &vsCoordsVarying, &coordsVaryingType);
    543 
    544     // 2 copies of uniform array, 1 for each of vertex & fragment shader,
    545     // to work around Xoom bug. Doesn't seem to cause performance decrease
    546     // in test apps, but need to keep an eye on it.
    547     fVSParamUni = builder->addUniformArray(GrGLShaderBuilder::kVertex_ShaderType,
    548                                            kFloat_GrSLType, "Radial2VSParams", 6);
    549     fFSParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType,
    550                                            kFloat_GrSLType, "Radial2FSParams", 6);
    551 
    552     // For radial gradients without perspective we can pass the linear
    553     // part of the quadratic as a varying.
    554     if (kVec2f_GrSLType == coordsVaryingType) {
    555         builder->addVarying(kFloat_GrSLType, "Radial2BCoeff", &fVSVaryingName, &fFSVaryingName);
    556     }
    557 
    558     // VS
    559     {
    560         SkString p2;
    561         SkString p3;
    562         builder->getUniformVariable(fVSParamUni).appendArrayAccess(2, &p2);
    563         builder->getUniformVariable(fVSParamUni).appendArrayAccess(3, &p3);
    564 
    565         // For radial gradients without perspective we can pass the linear
    566         // part of the quadratic as a varying.
    567         if (kVec2f_GrSLType == coordsVaryingType) {
    568             // r2Var = 2 * (r2Parm[2] * varCoord.x - r2Param[3])
    569             builder->vsCodeAppendf("\t%s = 2.0 *(%s * %s.x - %s);\n",
    570                                    fVSVaryingName, p2.c_str(),
    571                                    vsCoordsVarying, p3.c_str());
    572         }
    573     }
    574 
    575     // FS
    576     {
    577         SkString cName("c");
    578         SkString ac4Name("ac4");
    579         SkString rootName("root");
    580         SkString t;
    581         SkString p0;
    582         SkString p1;
    583         SkString p2;
    584         SkString p3;
    585         SkString p4;
    586         SkString p5;
    587         builder->getUniformVariable(fFSParamUni).appendArrayAccess(0, &p0);
    588         builder->getUniformVariable(fFSParamUni).appendArrayAccess(1, &p1);
    589         builder->getUniformVariable(fFSParamUni).appendArrayAccess(2, &p2);
    590         builder->getUniformVariable(fFSParamUni).appendArrayAccess(3, &p3);
    591         builder->getUniformVariable(fFSParamUni).appendArrayAccess(4, &p4);
    592         builder->getUniformVariable(fFSParamUni).appendArrayAccess(5, &p5);
    593 
    594         // If we we're able to interpolate the linear component,
    595         // bVar is the varying; otherwise compute it
    596         SkString bVar;
    597         if (kVec2f_GrSLType == coordsVaryingType) {
    598             bVar = fFSVaryingName;
    599         } else {
    600             bVar = "b";
    601             builder->fsCodeAppendf("\tfloat %s = 2.0 * (%s * %s.x - %s);\n",
    602                                    bVar.c_str(), p2.c_str(), fsCoords, p3.c_str());
    603         }
    604 
    605         // c = (x^2)+(y^2) - params[4]
    606         builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
    607                                cName.c_str(),
    608                                fsCoords,
    609                                fsCoords,
    610                                p4.c_str());
    611 
    612         // If we aren't degenerate, emit some extra code, and accept a slightly
    613         // more complex coord.
    614         if (!fIsDegenerate) {
    615 
    616             // ac4 = 4.0 * params[0] * c
    617             builder->fsCodeAppendf("\tfloat %s = %s * 4.0 * %s;\n",
    618                                    ac4Name.c_str(), p0.c_str(),
    619                                    cName.c_str());
    620 
    621             // root = sqrt(b^2-4ac)
    622             // (abs to avoid exception due to fp precision)
    623             builder->fsCodeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
    624                                    rootName.c_str(), bVar.c_str(), bVar.c_str(),
    625                                    ac4Name.c_str());
    626 
    627             // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
    628             t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(),
    629                      rootName.c_str(), p1.c_str());
    630         } else {
    631             // t is: -c/b
    632             t.printf("-%s / %s", cName.c_str(), bVar.c_str());
    633         }
    634 
    635         this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
    636     }
    637 }
    638 
    639 void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman,
    640                                   const GrDrawEffect& drawEffect) {
    641     INHERITED::setData(uman, drawEffect);
    642     const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>();
    643     GrAssert(data.isDegenerate() == fIsDegenerate);
    644     SkScalar centerX1 = data.center();
    645     SkScalar radius0 = data.radius();
    646     if (fCachedCenter != centerX1 ||
    647         fCachedRadius != radius0 ||
    648         fCachedPosRoot != data.isPosRoot()) {
    649 
    650         SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1;
    651 
    652         // When we're in the degenerate (linear) case, the second
    653         // value will be INF but the program doesn't read it. (We
    654         // use the same 6 uniforms even though we don't need them
    655         // all in the linear case just to keep the code complexity
    656         // down).
    657         float values[6] = {
    658             SkScalarToFloat(a),
    659             1 / (2.f * SkScalarToFloat(a)),
    660             SkScalarToFloat(centerX1),
    661             SkScalarToFloat(radius0),
    662             SkScalarToFloat(SkScalarMul(radius0, radius0)),
    663             data.isPosRoot() ? 1.f : -1.f
    664         };
    665 
    666         uman.set1fv(fVSParamUni, 0, 6, values);
    667         uman.set1fv(fFSParamUni, 0, 6, values);
    668         fCachedCenter = centerX1;
    669         fCachedRadius = radius0;
    670         fCachedPosRoot = data.isPosRoot();
    671     }
    672 }
    673 
    674 GrGLEffect::EffectKey GrGLRadial2Gradient::GenKey(const GrDrawEffect& drawEffect,
    675                                                   const GrGLCaps&) {
    676     enum {
    677         kIsDegenerate = 1 << kMatrixKeyBitCnt,
    678     };
    679 
    680     EffectKey key = GenMatrixKey(drawEffect);
    681     if (drawEffect.castEffect<GrRadial2Gradient>().isDegenerate()) {
    682         key |= kIsDegenerate;
    683     }
    684     return key;
    685 }
    686 
    687 /////////////////////////////////////////////////////////////////////
    688 
    689 GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
    690     SkASSERT(NULL != context);
    691     // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis.
    692     SkMatrix matrix;
    693     if (!this->getLocalMatrix().invert(&matrix)) {
    694         return NULL;
    695     }
    696     matrix.postConcat(fPtsToUnit);
    697 
    698     SkScalar diffLen = fDiff.length();
    699     if (0 != diffLen) {
    700         SkScalar invDiffLen = SkScalarInvert(diffLen);
    701         SkMatrix rot;
    702         rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
    703                        SkScalarMul(invDiffLen, fDiff.fX));
    704         matrix.postConcat(rot);
    705     }
    706 
    707     return GrRadial2Gradient::Create(context, *this, matrix, fTileMode);
    708 }
    709 
    710 #else
    711 
    712 GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
    713     SkDEBUGFAIL("Should not call in GPU-less build");
    714     return NULL;
    715 }
    716 
    717 #endif
    718