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, const SkMatrix* localMatrix)
    174     : SkGradientShaderBase(desc, localMatrix),
    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 size_t SkTwoPointRadialGradient::contextSize() const {
    224     return sizeof(TwoPointRadialGradientContext);
    225 }
    226 
    227 SkShader::Context* SkTwoPointRadialGradient::onCreateContext(const ContextRec& rec,
    228                                                              void* storage) const {
    229     // For now, we might have divided by zero, so detect that.
    230     if (0 == fDiffRadius) {
    231         return NULL;
    232     }
    233     return SkNEW_PLACEMENT_ARGS(storage, TwoPointRadialGradientContext, (*this, rec));
    234 }
    235 
    236 SkTwoPointRadialGradient::TwoPointRadialGradientContext::TwoPointRadialGradientContext(
    237         const SkTwoPointRadialGradient& shader, const ContextRec& rec)
    238     : INHERITED(shader, rec)
    239 {
    240     // we don't have a span16 proc
    241     fFlags &= ~kHasSpan16_Flag;
    242 }
    243 
    244 void SkTwoPointRadialGradient::TwoPointRadialGradientContext::shadeSpan(
    245         int x, int y, SkPMColor* dstCParam, int count) {
    246     SkASSERT(count > 0);
    247 
    248     const SkTwoPointRadialGradient& twoPointRadialGradient =
    249             static_cast<const SkTwoPointRadialGradient&>(fShader);
    250 
    251     SkPMColor* SK_RESTRICT dstC = dstCParam;
    252 
    253     // Zero difference between radii:  fill with transparent black.
    254     if (twoPointRadialGradient.fDiffRadius == 0) {
    255       sk_bzero(dstC, count * sizeof(*dstC));
    256       return;
    257     }
    258     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
    259     TileProc            proc = twoPointRadialGradient.fTileProc;
    260     const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
    261 
    262     SkScalar foura = twoPointRadialGradient.fA * 4;
    263     bool posRoot = twoPointRadialGradient.fDiffRadius < 0;
    264     if (fDstToIndexClass != kPerspective_MatrixClass) {
    265         SkPoint srcPt;
    266         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
    267                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
    268         SkScalar dx, fx = srcPt.fX;
    269         SkScalar dy, fy = srcPt.fY;
    270 
    271         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
    272             SkFixed fixedX, fixedY;
    273             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
    274             dx = SkFixedToScalar(fixedX);
    275             dy = SkFixedToScalar(fixedY);
    276         } else {
    277             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
    278             dx = fDstToIndex.getScaleX();
    279             dy = fDstToIndex.getSkewY();
    280         }
    281         SkScalar b = (SkScalarMul(twoPointRadialGradient.fDiff.fX, fx) +
    282                      SkScalarMul(twoPointRadialGradient.fDiff.fY, fy) -
    283                      twoPointRadialGradient.fStartRadius) * 2;
    284         SkScalar db = (SkScalarMul(twoPointRadialGradient.fDiff.fX, dx) +
    285                       SkScalarMul(twoPointRadialGradient.fDiff.fY, dy)) * 2;
    286 
    287         TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat;
    288         if (SkShader::kClamp_TileMode == twoPointRadialGradient.fTileMode) {
    289             shadeProc = shadeSpan_twopoint_clamp;
    290         } else if (SkShader::kMirror_TileMode == twoPointRadialGradient.fTileMode) {
    291             shadeProc = shadeSpan_twopoint_mirror;
    292         } else {
    293             SkASSERT(SkShader::kRepeat_TileMode == twoPointRadialGradient.fTileMode);
    294         }
    295         (*shadeProc)(fx, dx, fy, dy, b, db,
    296                      twoPointRadialGradient.fSr2D2, foura,
    297                      twoPointRadialGradient.fOneOverTwoA, posRoot,
    298                      dstC, cache, count);
    299     } else {    // perspective case
    300         SkScalar dstX = SkIntToScalar(x);
    301         SkScalar dstY = SkIntToScalar(y);
    302         for (; count > 0; --count) {
    303             SkPoint             srcPt;
    304             dstProc(fDstToIndex, dstX, dstY, &srcPt);
    305             SkScalar fx = srcPt.fX;
    306             SkScalar fy = srcPt.fY;
    307             SkScalar b = (SkScalarMul(twoPointRadialGradient.fDiff.fX, fx) +
    308                          SkScalarMul(twoPointRadialGradient.fDiff.fY, fy) -
    309                          twoPointRadialGradient.fStartRadius) * 2;
    310             SkFixed t = two_point_radial(b, fx, fy, twoPointRadialGradient.fSr2D2, foura,
    311                                          twoPointRadialGradient.fOneOverTwoA, posRoot);
    312             SkFixed index = proc(t);
    313             SkASSERT(index <= 0xFFFF);
    314             *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
    315             dstX += SK_Scalar1;
    316         }
    317     }
    318 }
    319 
    320 #ifndef SK_IGNORE_TO_STRING
    321 void SkTwoPointRadialGradient::toString(SkString* str) const {
    322     str->append("SkTwoPointRadialGradient: (");
    323 
    324     str->append("center1: (");
    325     str->appendScalar(fCenter1.fX);
    326     str->append(", ");
    327     str->appendScalar(fCenter1.fY);
    328     str->append(") radius1: ");
    329     str->appendScalar(fRadius1);
    330     str->append(" ");
    331 
    332     str->append("center2: (");
    333     str->appendScalar(fCenter2.fX);
    334     str->append(", ");
    335     str->appendScalar(fCenter2.fY);
    336     str->append(") radius2: ");
    337     str->appendScalar(fRadius2);
    338     str->append(" ");
    339 
    340     this->INHERITED::toString(str);
    341 
    342     str->append(")");
    343 }
    344 #endif
    345 
    346 SkTwoPointRadialGradient::SkTwoPointRadialGradient(
    347     SkReadBuffer& buffer)
    348     : INHERITED(buffer),
    349       fCenter1(buffer.readPoint()),
    350       fCenter2(buffer.readPoint()),
    351       fRadius1(buffer.readScalar()),
    352       fRadius2(buffer.readScalar()) {
    353     init();
    354 };
    355 
    356 void SkTwoPointRadialGradient::flatten(
    357     SkWriteBuffer& buffer) const {
    358     this->INHERITED::flatten(buffer);
    359     buffer.writePoint(fCenter1);
    360     buffer.writePoint(fCenter2);
    361     buffer.writeScalar(fRadius1);
    362     buffer.writeScalar(fRadius2);
    363 }
    364 
    365 void SkTwoPointRadialGradient::init() {
    366     fDiff = fCenter1 - fCenter2;
    367     fDiffRadius = fRadius2 - fRadius1;
    368     // hack to avoid zero-divide for now
    369     SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0;
    370     fDiff.fX = SkScalarMul(fDiff.fX, inv);
    371     fDiff.fY = SkScalarMul(fDiff.fY, inv);
    372     fStartRadius = SkScalarMul(fRadius1, inv);
    373     fSr2D2 = SkScalarSquare(fStartRadius);
    374     fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
    375     fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
    376 
    377     fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
    378     fPtsToUnit.postScale(inv, inv);
    379 }
    380 
    381 /////////////////////////////////////////////////////////////////////
    382 
    383 #if SK_SUPPORT_GPU
    384 
    385 #include "GrTBackendEffectFactory.h"
    386 #include "SkGr.h"
    387 
    388 // For brevity
    389 typedef GrGLUniformManager::UniformHandle UniformHandle;
    390 
    391 class GrGLRadial2Gradient : public GrGLGradientEffect {
    392 
    393 public:
    394 
    395     GrGLRadial2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffect&);
    396     virtual ~GrGLRadial2Gradient() { }
    397 
    398     virtual void emitCode(GrGLShaderBuilder*,
    399                           const GrDrawEffect&,
    400                           EffectKey,
    401                           const char* outputColor,
    402                           const char* inputColor,
    403                           const TransformedCoordsArray&,
    404                           const TextureSamplerArray&) SK_OVERRIDE;
    405     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
    406 
    407     static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
    408 
    409 protected:
    410 
    411     UniformHandle fParamUni;
    412 
    413     const char* fVSVaryingName;
    414     const char* fFSVaryingName;
    415 
    416     bool fIsDegenerate;
    417 
    418     // @{
    419     /// Values last uploaded as uniforms
    420 
    421     SkScalar fCachedCenter;
    422     SkScalar fCachedRadius;
    423     bool     fCachedPosRoot;
    424 
    425     // @}
    426 
    427 private:
    428 
    429     typedef GrGLGradientEffect INHERITED;
    430 
    431 };
    432 
    433 /////////////////////////////////////////////////////////////////////
    434 
    435 class GrRadial2Gradient : public GrGradientEffect {
    436 public:
    437     static GrEffectRef* Create(GrContext* ctx,
    438                                const SkTwoPointRadialGradient& shader,
    439                                const SkMatrix& matrix,
    440                                SkShader::TileMode tm) {
    441         AutoEffectUnref effect(SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm)));
    442         return CreateEffectRef(effect);
    443     }
    444 
    445     virtual ~GrRadial2Gradient() { }
    446 
    447     static const char* Name() { return "Two-Point Radial Gradient"; }
    448     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
    449         return GrTBackendEffectFactory<GrRadial2Gradient>::getInstance();
    450     }
    451 
    452     // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
    453     bool isDegenerate() const { return SK_Scalar1 == fCenterX1; }
    454     SkScalar center() const { return fCenterX1; }
    455     SkScalar radius() const { return fRadius0; }
    456     bool isPosRoot() const { return SkToBool(fPosRoot); }
    457 
    458     typedef GrGLRadial2Gradient GLEffect;
    459 
    460 private:
    461     virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
    462         const GrRadial2Gradient& s = CastEffect<GrRadial2Gradient>(sBase);
    463         return (INHERITED::onIsEqual(sBase) &&
    464                 this->fCenterX1 == s.fCenterX1 &&
    465                 this->fRadius0 == s.fRadius0 &&
    466                 this->fPosRoot == s.fPosRoot);
    467     }
    468 
    469     GrRadial2Gradient(GrContext* ctx,
    470                       const SkTwoPointRadialGradient& shader,
    471                       const SkMatrix& matrix,
    472                       SkShader::TileMode tm)
    473         : INHERITED(ctx, shader, matrix, tm)
    474         , fCenterX1(shader.getCenterX1())
    475         , fRadius0(shader.getStartRadius())
    476         , fPosRoot(shader.getDiffRadius() < 0) {
    477         // We pass the linear part of the quadratic as a varying.
    478         //    float b = 2.0 * (fCenterX1 * x - fRadius0 * z)
    479         fBTransform = this->getCoordTransform();
    480         SkMatrix& bMatrix = *fBTransform.accessMatrix();
    481         bMatrix[SkMatrix::kMScaleX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) -
    482                                            SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp0]));
    483         bMatrix[SkMatrix::kMSkewX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) -
    484                                           SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp1]));
    485         bMatrix[SkMatrix::kMTransX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) -
    486                                            SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp2]));
    487         this->addCoordTransform(&fBTransform);
    488     }
    489 
    490     GR_DECLARE_EFFECT_TEST;
    491 
    492     // @{
    493     // Cache of values - these can change arbitrarily, EXCEPT
    494     // we shouldn't change between degenerate and non-degenerate?!
    495 
    496     GrCoordTransform fBTransform;
    497     SkScalar         fCenterX1;
    498     SkScalar         fRadius0;
    499     SkBool8          fPosRoot;
    500 
    501     // @}
    502 
    503     typedef GrGradientEffect INHERITED;
    504 };
    505 
    506 /////////////////////////////////////////////////////////////////////
    507 
    508 GR_DEFINE_EFFECT_TEST(GrRadial2Gradient);
    509 
    510 GrEffectRef* GrRadial2Gradient::TestCreate(SkRandom* random,
    511                                            GrContext* context,
    512                                            const GrDrawTargetCaps&,
    513                                            GrTexture**) {
    514     SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
    515     SkScalar radius1 = random->nextUScalar1();
    516     SkPoint center2;
    517     SkScalar radius2;
    518     do {
    519         center2.set(random->nextUScalar1(), random->nextUScalar1());
    520         radius2 = random->nextUScalar1 ();
    521         // There is a bug in two point radial gradients with identical radii
    522     } while (radius1 == radius2);
    523 
    524     SkColor colors[kMaxRandomGradientColors];
    525     SkScalar stopsArray[kMaxRandomGradientColors];
    526     SkScalar* stops = stopsArray;
    527     SkShader::TileMode tm;
    528     int colorCount = RandomGradientParams(random, colors, &stops, &tm);
    529     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1,
    530                                                                          center2, radius2,
    531                                                                          colors, stops, colorCount,
    532                                                                          tm));
    533     SkPaint paint;
    534     GrEffectRef* effect;
    535     GrColor grColor;
    536     shader->asNewEffect(context, paint, NULL, &grColor, &effect);
    537     return effect;
    538 }
    539 
    540 /////////////////////////////////////////////////////////////////////
    541 
    542 GrGLRadial2Gradient::GrGLRadial2Gradient(const GrBackendEffectFactory& factory,
    543                                          const GrDrawEffect& drawEffect)
    544     : INHERITED(factory)
    545     , fVSVaryingName(NULL)
    546     , fFSVaryingName(NULL)
    547     , fCachedCenter(SK_ScalarMax)
    548     , fCachedRadius(-SK_ScalarMax)
    549     , fCachedPosRoot(0) {
    550 
    551     const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>();
    552     fIsDegenerate = data.isDegenerate();
    553 }
    554 
    555 void GrGLRadial2Gradient::emitCode(GrGLShaderBuilder* builder,
    556                                    const GrDrawEffect& drawEffect,
    557                                    EffectKey key,
    558                                    const char* outputColor,
    559                                    const char* inputColor,
    560                                    const TransformedCoordsArray& coords,
    561                                    const TextureSamplerArray& samplers) {
    562 
    563     this->emitUniforms(builder, key);
    564     fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
    565                                          kFloat_GrSLType, "Radial2FSParams", 6);
    566 
    567     SkString cName("c");
    568     SkString ac4Name("ac4");
    569     SkString rootName("root");
    570     SkString t;
    571     SkString p0;
    572     SkString p1;
    573     SkString p2;
    574     SkString p3;
    575     SkString p4;
    576     SkString p5;
    577     builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
    578     builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
    579     builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
    580     builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3);
    581     builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4);
    582     builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5);
    583 
    584     // We interpolate the linear component in coords[1].
    585     SkASSERT(coords[0].type() == coords[1].type());
    586     const char* coords2D;
    587     SkString bVar;
    588     if (kVec3f_GrSLType == coords[0].type()) {
    589         builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n",
    590                                coords[0].c_str(), coords[1].c_str(), coords[0].c_str());
    591         coords2D = "interpolants.xy";
    592         bVar = "interpolants.z";
    593     } else {
    594         coords2D = coords[0].c_str();
    595         bVar.printf("%s.x", coords[1].c_str());
    596     }
    597 
    598     // c = (x^2)+(y^2) - params[4]
    599     builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
    600                            cName.c_str(), coords2D, coords2D, p4.c_str());
    601 
    602     // If we aren't degenerate, emit some extra code, and accept a slightly
    603     // more complex coord.
    604     if (!fIsDegenerate) {
    605 
    606         // ac4 = 4.0 * params[0] * c
    607         builder->fsCodeAppendf("\tfloat %s = %s * 4.0 * %s;\n",
    608                                ac4Name.c_str(), p0.c_str(),
    609                                cName.c_str());
    610 
    611         // root = sqrt(b^2-4ac)
    612         // (abs to avoid exception due to fp precision)
    613         builder->fsCodeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
    614                                rootName.c_str(), bVar.c_str(), bVar.c_str(),
    615                                ac4Name.c_str());
    616 
    617         // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
    618         t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(),
    619                  rootName.c_str(), p1.c_str());
    620     } else {
    621         // t is: -c/b
    622         t.printf("-%s / %s", cName.c_str(), bVar.c_str());
    623     }
    624 
    625     this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers);
    626 }
    627 
    628 void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman,
    629                                   const GrDrawEffect& drawEffect) {
    630     INHERITED::setData(uman, drawEffect);
    631     const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>();
    632     SkASSERT(data.isDegenerate() == fIsDegenerate);
    633     SkScalar centerX1 = data.center();
    634     SkScalar radius0 = data.radius();
    635     if (fCachedCenter != centerX1 ||
    636         fCachedRadius != radius0 ||
    637         fCachedPosRoot != data.isPosRoot()) {
    638 
    639         SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1;
    640 
    641         // When we're in the degenerate (linear) case, the second
    642         // value will be INF but the program doesn't read it. (We
    643         // use the same 6 uniforms even though we don't need them
    644         // all in the linear case just to keep the code complexity
    645         // down).
    646         float values[6] = {
    647             SkScalarToFloat(a),
    648             1 / (2.f * SkScalarToFloat(a)),
    649             SkScalarToFloat(centerX1),
    650             SkScalarToFloat(radius0),
    651             SkScalarToFloat(SkScalarMul(radius0, radius0)),
    652             data.isPosRoot() ? 1.f : -1.f
    653         };
    654 
    655         uman.set1fv(fParamUni, 6, values);
    656         fCachedCenter = centerX1;
    657         fCachedRadius = radius0;
    658         fCachedPosRoot = data.isPosRoot();
    659     }
    660 }
    661 
    662 GrGLEffect::EffectKey GrGLRadial2Gradient::GenKey(const GrDrawEffect& drawEffect,
    663                                                   const GrGLCaps&) {
    664     enum {
    665         kIsDegenerate = 1 << kBaseKeyBitCnt,
    666     };
    667 
    668     EffectKey key = GenBaseGradientKey(drawEffect);
    669     if (drawEffect.castEffect<GrRadial2Gradient>().isDegenerate()) {
    670         key |= kIsDegenerate;
    671     }
    672     return key;
    673 }
    674 
    675 /////////////////////////////////////////////////////////////////////
    676 
    677 bool SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint& paint,
    678                                            const SkMatrix* localMatrix, GrColor* grColor,
    679                                            GrEffectRef** grEffect)  const {
    680     SkASSERT(NULL != context);
    681 
    682     // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis.
    683     SkMatrix matrix;
    684     if (!this->getLocalMatrix().invert(&matrix)) {
    685         return false;
    686     }
    687     if (localMatrix) {
    688         SkMatrix inv;
    689         if (!localMatrix->invert(&inv)) {
    690             return false;
    691         }
    692         matrix.postConcat(inv);
    693     }
    694     matrix.postConcat(fPtsToUnit);
    695 
    696     SkScalar diffLen = fDiff.length();
    697     if (0 != diffLen) {
    698         SkScalar invDiffLen = SkScalarInvert(diffLen);
    699         SkMatrix rot;
    700         rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
    701                        SkScalarMul(invDiffLen, fDiff.fX));
    702         matrix.postConcat(rot);
    703     }
    704 
    705     *grColor = SkColor2GrColorJustAlpha(paint.getColor());
    706     *grEffect = GrRadial2Gradient::Create(context, *this, matrix, fTileMode);
    707 
    708     return true;
    709 }
    710 
    711 #else
    712 
    713 bool SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint& paint,
    714                                            const SkMatrix* localMatrix, GrColor* grColor,
    715                                            GrEffectRef** grEffect)  const {
    716     SkDEBUGFAIL("Should not call in GPU-less build");
    717     return false;
    718 }
    719 
    720 #endif
    721