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 
    380 class GrGLRadial2Gradient : public GrGLGradientEffect {
    381 
    382 public:
    383 
    384     GrGLRadial2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffect&);
    385     virtual ~GrGLRadial2Gradient() { }
    386 
    387     virtual void emitCode(GrGLShaderBuilder*,
    388                           const GrDrawEffect&,
    389                           EffectKey,
    390                           const char* outputColor,
    391                           const char* inputColor,
    392                           const TransformedCoordsArray&,
    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 fParamUni;
    401 
    402     const char* fVSVaryingName;
    403     const char* fFSVaryingName;
    404 
    405     bool fIsDegenerate;
    406 
    407     // @{
    408     /// Values last uploaded as uniforms
    409 
    410     SkScalar fCachedCenter;
    411     SkScalar fCachedRadius;
    412     bool     fCachedPosRoot;
    413 
    414     // @}
    415 
    416 private:
    417 
    418     typedef GrGLGradientEffect INHERITED;
    419 
    420 };
    421 
    422 /////////////////////////////////////////////////////////////////////
    423 
    424 class GrRadial2Gradient : public GrGradientEffect {
    425 public:
    426     static GrEffectRef* Create(GrContext* ctx,
    427                                const SkTwoPointRadialGradient& shader,
    428                                const SkMatrix& matrix,
    429                                SkShader::TileMode tm) {
    430         AutoEffectUnref effect(SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm)));
    431         return CreateEffectRef(effect);
    432     }
    433 
    434     virtual ~GrRadial2Gradient() { }
    435 
    436     static const char* Name() { return "Two-Point Radial Gradient"; }
    437     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
    438         return GrTBackendEffectFactory<GrRadial2Gradient>::getInstance();
    439     }
    440 
    441     // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
    442     bool isDegenerate() const { return SK_Scalar1 == fCenterX1; }
    443     SkScalar center() const { return fCenterX1; }
    444     SkScalar radius() const { return fRadius0; }
    445     bool isPosRoot() const { return SkToBool(fPosRoot); }
    446 
    447     typedef GrGLRadial2Gradient GLEffect;
    448 
    449 private:
    450     virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
    451         const GrRadial2Gradient& s = CastEffect<GrRadial2Gradient>(sBase);
    452         return (INHERITED::onIsEqual(sBase) &&
    453                 this->fCenterX1 == s.fCenterX1 &&
    454                 this->fRadius0 == s.fRadius0 &&
    455                 this->fPosRoot == s.fPosRoot);
    456     }
    457 
    458     GrRadial2Gradient(GrContext* ctx,
    459                       const SkTwoPointRadialGradient& shader,
    460                       const SkMatrix& matrix,
    461                       SkShader::TileMode tm)
    462         : INHERITED(ctx, shader, matrix, tm)
    463         , fCenterX1(shader.getCenterX1())
    464         , fRadius0(shader.getStartRadius())
    465         , fPosRoot(shader.getDiffRadius() < 0) {
    466         // We pass the linear part of the quadratic as a varying.
    467         //    float b = 2.0 * (fCenterX1 * x - fRadius0 * z)
    468         fBTransform = this->getCoordTransform();
    469         SkMatrix& bMatrix = *fBTransform.accessMatrix();
    470         bMatrix[SkMatrix::kMScaleX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) -
    471                                            SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp0]));
    472         bMatrix[SkMatrix::kMSkewX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) -
    473                                           SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp1]));
    474         bMatrix[SkMatrix::kMTransX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) -
    475                                            SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp2]));
    476         this->addCoordTransform(&fBTransform);
    477     }
    478 
    479     GR_DECLARE_EFFECT_TEST;
    480 
    481     // @{
    482     // Cache of values - these can change arbitrarily, EXCEPT
    483     // we shouldn't change between degenerate and non-degenerate?!
    484 
    485     GrCoordTransform fBTransform;
    486     SkScalar         fCenterX1;
    487     SkScalar         fRadius0;
    488     SkBool8          fPosRoot;
    489 
    490     // @}
    491 
    492     typedef GrGradientEffect INHERITED;
    493 };
    494 
    495 /////////////////////////////////////////////////////////////////////
    496 
    497 GR_DEFINE_EFFECT_TEST(GrRadial2Gradient);
    498 
    499 GrEffectRef* GrRadial2Gradient::TestCreate(SkRandom* random,
    500                                            GrContext* context,
    501                                            const GrDrawTargetCaps&,
    502                                            GrTexture**) {
    503     SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
    504     SkScalar radius1 = random->nextUScalar1();
    505     SkPoint center2;
    506     SkScalar radius2;
    507     do {
    508         center2.set(random->nextUScalar1(), random->nextUScalar1());
    509         radius2 = random->nextUScalar1 ();
    510         // There is a bug in two point radial gradients with identical radii
    511     } while (radius1 == radius2);
    512 
    513     SkColor colors[kMaxRandomGradientColors];
    514     SkScalar stopsArray[kMaxRandomGradientColors];
    515     SkScalar* stops = stopsArray;
    516     SkShader::TileMode tm;
    517     int colorCount = RandomGradientParams(random, colors, &stops, &tm);
    518     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1,
    519                                                                          center2, radius2,
    520                                                                          colors, stops, colorCount,
    521                                                                          tm));
    522     SkPaint paint;
    523     return shader->asNewEffect(context, paint);
    524 }
    525 
    526 /////////////////////////////////////////////////////////////////////
    527 
    528 GrGLRadial2Gradient::GrGLRadial2Gradient(const GrBackendEffectFactory& factory,
    529                                          const GrDrawEffect& drawEffect)
    530     : INHERITED(factory)
    531     , fVSVaryingName(NULL)
    532     , fFSVaryingName(NULL)
    533     , fCachedCenter(SK_ScalarMax)
    534     , fCachedRadius(-SK_ScalarMax)
    535     , fCachedPosRoot(0) {
    536 
    537     const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>();
    538     fIsDegenerate = data.isDegenerate();
    539 }
    540 
    541 void GrGLRadial2Gradient::emitCode(GrGLShaderBuilder* builder,
    542                                    const GrDrawEffect& drawEffect,
    543                                    EffectKey key,
    544                                    const char* outputColor,
    545                                    const char* inputColor,
    546                                    const TransformedCoordsArray& coords,
    547                                    const TextureSamplerArray& samplers) {
    548 
    549     this->emitUniforms(builder, key);
    550     fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
    551                                          kFloat_GrSLType, "Radial2FSParams", 6);
    552 
    553     SkString cName("c");
    554     SkString ac4Name("ac4");
    555     SkString rootName("root");
    556     SkString t;
    557     SkString p0;
    558     SkString p1;
    559     SkString p2;
    560     SkString p3;
    561     SkString p4;
    562     SkString p5;
    563     builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
    564     builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
    565     builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
    566     builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3);
    567     builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4);
    568     builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5);
    569 
    570     // We interpolate the linear component in coords[1].
    571     SkASSERT(coords[0].type() == coords[1].type());
    572     const char* coords2D;
    573     SkString bVar;
    574     if (kVec3f_GrSLType == coords[0].type()) {
    575         builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n",
    576                                coords[0].c_str(), coords[1].c_str(), coords[0].c_str());
    577         coords2D = "interpolants.xy";
    578         bVar = "interpolants.z";
    579     } else {
    580         coords2D = coords[0].c_str();
    581         bVar.printf("%s.x", coords[1].c_str());
    582     }
    583 
    584     // c = (x^2)+(y^2) - params[4]
    585     builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
    586                            cName.c_str(), coords2D, coords2D, p4.c_str());
    587 
    588     // If we aren't degenerate, emit some extra code, and accept a slightly
    589     // more complex coord.
    590     if (!fIsDegenerate) {
    591 
    592         // ac4 = 4.0 * params[0] * c
    593         builder->fsCodeAppendf("\tfloat %s = %s * 4.0 * %s;\n",
    594                                ac4Name.c_str(), p0.c_str(),
    595                                cName.c_str());
    596 
    597         // root = sqrt(b^2-4ac)
    598         // (abs to avoid exception due to fp precision)
    599         builder->fsCodeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
    600                                rootName.c_str(), bVar.c_str(), bVar.c_str(),
    601                                ac4Name.c_str());
    602 
    603         // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
    604         t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(),
    605                  rootName.c_str(), p1.c_str());
    606     } else {
    607         // t is: -c/b
    608         t.printf("-%s / %s", cName.c_str(), bVar.c_str());
    609     }
    610 
    611     this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers);
    612 }
    613 
    614 void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman,
    615                                   const GrDrawEffect& drawEffect) {
    616     INHERITED::setData(uman, drawEffect);
    617     const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>();
    618     SkASSERT(data.isDegenerate() == fIsDegenerate);
    619     SkScalar centerX1 = data.center();
    620     SkScalar radius0 = data.radius();
    621     if (fCachedCenter != centerX1 ||
    622         fCachedRadius != radius0 ||
    623         fCachedPosRoot != data.isPosRoot()) {
    624 
    625         SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1;
    626 
    627         // When we're in the degenerate (linear) case, the second
    628         // value will be INF but the program doesn't read it. (We
    629         // use the same 6 uniforms even though we don't need them
    630         // all in the linear case just to keep the code complexity
    631         // down).
    632         float values[6] = {
    633             SkScalarToFloat(a),
    634             1 / (2.f * SkScalarToFloat(a)),
    635             SkScalarToFloat(centerX1),
    636             SkScalarToFloat(radius0),
    637             SkScalarToFloat(SkScalarMul(radius0, radius0)),
    638             data.isPosRoot() ? 1.f : -1.f
    639         };
    640 
    641         uman.set1fv(fParamUni, 6, values);
    642         fCachedCenter = centerX1;
    643         fCachedRadius = radius0;
    644         fCachedPosRoot = data.isPosRoot();
    645     }
    646 }
    647 
    648 GrGLEffect::EffectKey GrGLRadial2Gradient::GenKey(const GrDrawEffect& drawEffect,
    649                                                   const GrGLCaps&) {
    650     enum {
    651         kIsDegenerate = 1 << kBaseKeyBitCnt,
    652     };
    653 
    654     EffectKey key = GenBaseGradientKey(drawEffect);
    655     if (drawEffect.castEffect<GrRadial2Gradient>().isDegenerate()) {
    656         key |= kIsDegenerate;
    657     }
    658     return key;
    659 }
    660 
    661 /////////////////////////////////////////////////////////////////////
    662 
    663 GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
    664     SkASSERT(NULL != context);
    665     // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis.
    666     SkMatrix matrix;
    667     if (!this->getLocalMatrix().invert(&matrix)) {
    668         return NULL;
    669     }
    670     matrix.postConcat(fPtsToUnit);
    671 
    672     SkScalar diffLen = fDiff.length();
    673     if (0 != diffLen) {
    674         SkScalar invDiffLen = SkScalarInvert(diffLen);
    675         SkMatrix rot;
    676         rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
    677                        SkScalarMul(invDiffLen, fDiff.fX));
    678         matrix.postConcat(rot);
    679     }
    680 
    681     return GrRadial2Gradient::Create(context, *this, matrix, fTileMode);
    682 }
    683 
    684 #else
    685 
    686 GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
    687     SkDEBUGFAIL("Should not call in GPU-less build");
    688     return NULL;
    689 }
    690 
    691 #endif
    692