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(const SkPoint& start, SkScalar startRadius,
    171                                                    const SkPoint& end, SkScalar endRadius,
    172                                                    const Descriptor& desc)
    173     : SkGradientShaderBase(desc)
    174     , fCenter1(start)
    175     , fCenter2(end)
    176     , fRadius1(startRadius)
    177     , fRadius2(endRadius)
    178 {
    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 #ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
    347 SkTwoPointRadialGradient::SkTwoPointRadialGradient(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 #endif
    356 
    357 SkFlattenable* SkTwoPointRadialGradient::CreateProc(SkReadBuffer& buffer) {
    358     DescriptorScope desc;
    359     if (!desc.unflatten(buffer)) {
    360         return NULL;
    361     }
    362     const SkPoint c1 = buffer.readPoint();
    363     const SkPoint c2 = buffer.readPoint();
    364     const SkScalar r1 = buffer.readScalar();
    365     const SkScalar r2 = buffer.readScalar();
    366     return SkGradientShader::CreateTwoPointRadial(c1, r1, c2, r2, desc.fColors, desc.fPos,
    367                                                   desc.fCount, desc.fTileMode, desc.fGradFlags,
    368                                                   desc.fLocalMatrix);
    369 }
    370 
    371 void SkTwoPointRadialGradient::flatten(
    372     SkWriteBuffer& buffer) const {
    373     this->INHERITED::flatten(buffer);
    374     buffer.writePoint(fCenter1);
    375     buffer.writePoint(fCenter2);
    376     buffer.writeScalar(fRadius1);
    377     buffer.writeScalar(fRadius2);
    378 }
    379 
    380 void SkTwoPointRadialGradient::init() {
    381     fDiff = fCenter1 - fCenter2;
    382     fDiffRadius = fRadius2 - fRadius1;
    383     // hack to avoid zero-divide for now
    384     SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0;
    385     fDiff.fX = SkScalarMul(fDiff.fX, inv);
    386     fDiff.fY = SkScalarMul(fDiff.fY, inv);
    387     fStartRadius = SkScalarMul(fRadius1, inv);
    388     fSr2D2 = SkScalarSquare(fStartRadius);
    389     fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
    390     fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
    391 
    392     fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
    393     fPtsToUnit.postScale(inv, inv);
    394 }
    395 
    396 /////////////////////////////////////////////////////////////////////
    397 
    398 #if SK_SUPPORT_GPU
    399 
    400 #include "GrTBackendProcessorFactory.h"
    401 #include "gl/builders/GrGLProgramBuilder.h"
    402 #include "SkGr.h"
    403 
    404 // For brevity
    405 typedef GrGLProgramDataManager::UniformHandle UniformHandle;
    406 
    407 class GrGLRadial2Gradient : public GrGLGradientEffect {
    408 
    409 public:
    410 
    411     GrGLRadial2Gradient(const GrBackendProcessorFactory& factory, const GrProcessor&);
    412     virtual ~GrGLRadial2Gradient() { }
    413 
    414     virtual void emitCode(GrGLProgramBuilder*,
    415                           const GrFragmentProcessor&,
    416                           const GrProcessorKey&,
    417                           const char* outputColor,
    418                           const char* inputColor,
    419                           const TransformedCoordsArray&,
    420                           const TextureSamplerArray&) SK_OVERRIDE;
    421     virtual void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
    422 
    423     static void GenKey(const GrProcessor&, const GrGLCaps& caps, GrProcessorKeyBuilder* b);
    424 
    425 protected:
    426 
    427     UniformHandle fParamUni;
    428 
    429     const char* fVSVaryingName;
    430     const char* fFSVaryingName;
    431 
    432     bool fIsDegenerate;
    433 
    434     // @{
    435     /// Values last uploaded as uniforms
    436 
    437     SkScalar fCachedCenter;
    438     SkScalar fCachedRadius;
    439     bool     fCachedPosRoot;
    440 
    441     // @}
    442 
    443 private:
    444 
    445     typedef GrGLGradientEffect INHERITED;
    446 
    447 };
    448 
    449 /////////////////////////////////////////////////////////////////////
    450 
    451 class GrRadial2Gradient : public GrGradientEffect {
    452 public:
    453     static GrFragmentProcessor* Create(GrContext* ctx,
    454                                        const SkTwoPointRadialGradient& shader,
    455                                        const SkMatrix& matrix,
    456                                        SkShader::TileMode tm) {
    457         return SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm));
    458     }
    459 
    460     virtual ~GrRadial2Gradient() { }
    461 
    462     static const char* Name() { return "Two-Point Radial Gradient"; }
    463     virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE {
    464         return GrTBackendFragmentProcessorFactory<GrRadial2Gradient>::getInstance();
    465     }
    466 
    467     // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
    468     bool isDegenerate() const { return SK_Scalar1 == fCenterX1; }
    469     SkScalar center() const { return fCenterX1; }
    470     SkScalar radius() const { return fRadius0; }
    471     bool isPosRoot() const { return SkToBool(fPosRoot); }
    472 
    473     typedef GrGLRadial2Gradient GLProcessor;
    474 
    475 private:
    476     virtual bool onIsEqual(const GrProcessor& sBase) const SK_OVERRIDE {
    477         const GrRadial2Gradient& s = sBase.cast<GrRadial2Gradient>();
    478         return (INHERITED::onIsEqual(sBase) &&
    479                 this->fCenterX1 == s.fCenterX1 &&
    480                 this->fRadius0 == s.fRadius0 &&
    481                 this->fPosRoot == s.fPosRoot);
    482     }
    483 
    484     GrRadial2Gradient(GrContext* ctx,
    485                       const SkTwoPointRadialGradient& shader,
    486                       const SkMatrix& matrix,
    487                       SkShader::TileMode tm)
    488         : INHERITED(ctx, shader, matrix, tm)
    489         , fCenterX1(shader.getCenterX1())
    490         , fRadius0(shader.getStartRadius())
    491         , fPosRoot(shader.getDiffRadius() < 0) {
    492         // We pass the linear part of the quadratic as a varying.
    493         //    float b = 2.0 * (fCenterX1 * x - fRadius0 * z)
    494         fBTransform = this->getCoordTransform();
    495         SkMatrix& bMatrix = *fBTransform.accessMatrix();
    496         bMatrix[SkMatrix::kMScaleX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) -
    497                                            SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp0]));
    498         bMatrix[SkMatrix::kMSkewX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) -
    499                                           SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp1]));
    500         bMatrix[SkMatrix::kMTransX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) -
    501                                            SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp2]));
    502         this->addCoordTransform(&fBTransform);
    503     }
    504 
    505     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
    506 
    507     // @{
    508     // Cache of values - these can change arbitrarily, EXCEPT
    509     // we shouldn't change between degenerate and non-degenerate?!
    510 
    511     GrCoordTransform fBTransform;
    512     SkScalar         fCenterX1;
    513     SkScalar         fRadius0;
    514     SkBool8          fPosRoot;
    515 
    516     // @}
    517 
    518     typedef GrGradientEffect INHERITED;
    519 };
    520 
    521 /////////////////////////////////////////////////////////////////////
    522 
    523 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadial2Gradient);
    524 
    525 GrFragmentProcessor* GrRadial2Gradient::TestCreate(SkRandom* random,
    526                                                    GrContext* context,
    527                                                    const GrDrawTargetCaps&,
    528                                                    GrTexture**) {
    529     SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
    530     SkScalar radius1 = random->nextUScalar1();
    531     SkPoint center2;
    532     SkScalar radius2;
    533     do {
    534         center2.set(random->nextUScalar1(), random->nextUScalar1());
    535         radius2 = random->nextUScalar1 ();
    536         // There is a bug in two point radial gradients with identical radii
    537     } while (radius1 == radius2);
    538 
    539     SkColor colors[kMaxRandomGradientColors];
    540     SkScalar stopsArray[kMaxRandomGradientColors];
    541     SkScalar* stops = stopsArray;
    542     SkShader::TileMode tm;
    543     int colorCount = RandomGradientParams(random, colors, &stops, &tm);
    544     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1,
    545                                                                          center2, radius2,
    546                                                                          colors, stops, colorCount,
    547                                                                          tm));
    548     SkPaint paint;
    549     GrFragmentProcessor* fp;
    550     GrColor paintColor;
    551     SkAssertResult(shader->asFragmentProcessor(context, paint, NULL, &paintColor, &fp));
    552     return fp;
    553 }
    554 
    555 /////////////////////////////////////////////////////////////////////
    556 
    557 GrGLRadial2Gradient::GrGLRadial2Gradient(const GrBackendProcessorFactory& factory,
    558                                          const GrProcessor& processor)
    559     : INHERITED(factory)
    560     , fVSVaryingName(NULL)
    561     , fFSVaryingName(NULL)
    562     , fCachedCenter(SK_ScalarMax)
    563     , fCachedRadius(-SK_ScalarMax)
    564     , fCachedPosRoot(0) {
    565 
    566     const GrRadial2Gradient& data = processor.cast<GrRadial2Gradient>();
    567     fIsDegenerate = data.isDegenerate();
    568 }
    569 
    570 void GrGLRadial2Gradient::emitCode(GrGLProgramBuilder* builder,
    571                                    const GrFragmentProcessor&,
    572                                    const GrProcessorKey& key,
    573                                    const char* outputColor,
    574                                    const char* inputColor,
    575                                    const TransformedCoordsArray& coords,
    576                                    const TextureSamplerArray& samplers) {
    577     uint32_t baseKey = key.get32(0);
    578     this->emitUniforms(builder, baseKey);
    579     fParamUni = builder->addUniformArray(GrGLProgramBuilder::kFragment_Visibility,
    580                                          kFloat_GrSLType, "Radial2FSParams", 6);
    581 
    582     SkString cName("c");
    583     SkString ac4Name("ac4");
    584     SkString rootName("root");
    585     SkString t;
    586     SkString p0;
    587     SkString p1;
    588     SkString p2;
    589     SkString p3;
    590     SkString p4;
    591     SkString p5;
    592     builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
    593     builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
    594     builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
    595     builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3);
    596     builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4);
    597     builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5);
    598 
    599     GrGLFragmentShaderBuilder* fsBuilder = builder->getFragmentShaderBuilder();
    600     // We interpolate the linear component in coords[1].
    601     SkASSERT(coords[0].getType() == coords[1].getType());
    602     const char* coords2D;
    603     SkString bVar;
    604     if (kVec3f_GrSLType == coords[0].getType()) {
    605         fsBuilder->codeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n",
    606                                coords[0].c_str(), coords[1].c_str(), coords[0].c_str());
    607         coords2D = "interpolants.xy";
    608         bVar = "interpolants.z";
    609     } else {
    610         coords2D = coords[0].c_str();
    611         bVar.printf("%s.x", coords[1].c_str());
    612     }
    613 
    614     // c = (x^2)+(y^2) - params[4]
    615     fsBuilder->codeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
    616                            cName.c_str(), coords2D, coords2D, p4.c_str());
    617 
    618     // If we aren't degenerate, emit some extra code, and accept a slightly
    619     // more complex coord.
    620     if (!fIsDegenerate) {
    621 
    622         // ac4 = 4.0 * params[0] * c
    623         fsBuilder->codeAppendf("\tfloat %s = %s * 4.0 * %s;\n",
    624                                ac4Name.c_str(), p0.c_str(),
    625                                cName.c_str());
    626 
    627         // root = sqrt(b^2-4ac)
    628         // (abs to avoid exception due to fp precision)
    629         fsBuilder->codeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
    630                                rootName.c_str(), bVar.c_str(), bVar.c_str(),
    631                                ac4Name.c_str());
    632 
    633         // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
    634         t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(),
    635                  rootName.c_str(), p1.c_str());
    636     } else {
    637         // t is: -c/b
    638         t.printf("-%s / %s", cName.c_str(), bVar.c_str());
    639     }
    640 
    641     this->emitColor(builder, t.c_str(), baseKey, outputColor, inputColor, samplers);
    642 }
    643 
    644 void GrGLRadial2Gradient::setData(const GrGLProgramDataManager& pdman,
    645                                   const GrProcessor& processor) {
    646     INHERITED::setData(pdman, processor);
    647     const GrRadial2Gradient& data = processor.cast<GrRadial2Gradient>();
    648     SkASSERT(data.isDegenerate() == fIsDegenerate);
    649     SkScalar centerX1 = data.center();
    650     SkScalar radius0 = data.radius();
    651     if (fCachedCenter != centerX1 ||
    652         fCachedRadius != radius0 ||
    653         fCachedPosRoot != data.isPosRoot()) {
    654 
    655         SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1;
    656 
    657         // When we're in the degenerate (linear) case, the second
    658         // value will be INF but the program doesn't read it. (We
    659         // use the same 6 uniforms even though we don't need them
    660         // all in the linear case just to keep the code complexity
    661         // down).
    662         float values[6] = {
    663             SkScalarToFloat(a),
    664             1 / (2.f * SkScalarToFloat(a)),
    665             SkScalarToFloat(centerX1),
    666             SkScalarToFloat(radius0),
    667             SkScalarToFloat(SkScalarMul(radius0, radius0)),
    668             data.isPosRoot() ? 1.f : -1.f
    669         };
    670 
    671         pdman.set1fv(fParamUni, 6, values);
    672         fCachedCenter = centerX1;
    673         fCachedRadius = radius0;
    674         fCachedPosRoot = data.isPosRoot();
    675     }
    676 }
    677 
    678 void GrGLRadial2Gradient::GenKey(const GrProcessor& processor,
    679                                  const GrGLCaps&, GrProcessorKeyBuilder* b) {
    680     uint32_t* key = b->add32n(2);
    681     key[0] = GenBaseGradientKey(processor);
    682     key[1] = processor.cast<GrRadial2Gradient>().isDegenerate();
    683 }
    684 
    685 /////////////////////////////////////////////////////////////////////
    686 
    687 bool SkTwoPointRadialGradient::asFragmentProcessor(GrContext* context, const SkPaint& paint,
    688                                                    const SkMatrix* localMatrix, GrColor* paintColor,
    689                                                    GrFragmentProcessor** fp)  const {
    690     SkASSERT(context);
    691 
    692     // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis.
    693     SkMatrix matrix;
    694     if (!this->getLocalMatrix().invert(&matrix)) {
    695         return false;
    696     }
    697     if (localMatrix) {
    698         SkMatrix inv;
    699         if (!localMatrix->invert(&inv)) {
    700             return false;
    701         }
    702         matrix.postConcat(inv);
    703     }
    704     matrix.postConcat(fPtsToUnit);
    705 
    706     SkScalar diffLen = fDiff.length();
    707     if (0 != diffLen) {
    708         SkScalar invDiffLen = SkScalarInvert(diffLen);
    709         SkMatrix rot;
    710         rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
    711                        SkScalarMul(invDiffLen, fDiff.fX));
    712         matrix.postConcat(rot);
    713     }
    714 
    715     *paintColor = SkColor2GrColorJustAlpha(paint.getColor());
    716     *fp = GrRadial2Gradient::Create(context, *this, matrix, fTileMode);
    717 
    718     return true;
    719 }
    720 
    721 #else
    722 
    723 bool SkTwoPointRadialGradient::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix*,
    724                                                    GrColor*, GrFragmentProcessor**)  const {
    725     SkDEBUGFAIL("Should not call in GPU-less build");
    726     return false;
    727 }
    728 
    729 #endif
    730