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 "SkRadialGradient.h"
     10 #include "SkRadialGradient_Table.h"
     11 
     12 #define kSQRT_TABLE_BITS    11
     13 #define kSQRT_TABLE_SIZE    (1 << kSQRT_TABLE_BITS)
     14 
     15 #if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
     16 
     17 #include <stdio.h>
     18 
     19 void SkRadialGradient_BuildTable() {
     20     // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
     21 
     22     FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
     23     SkASSERT(file);
     24     ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
     25 
     26     for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
     27         if ((i & 15) == 0) {
     28             ::fprintf(file, "\t");
     29         }
     30 
     31         uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
     32 
     33         ::fprintf(file, "0x%02X", value);
     34         if (i < kSQRT_TABLE_SIZE-1) {
     35             ::fprintf(file, ", ");
     36         }
     37         if ((i & 15) == 15) {
     38             ::fprintf(file, "\n");
     39         }
     40     }
     41     ::fprintf(file, "};\n");
     42     ::fclose(file);
     43 }
     44 
     45 #endif
     46 
     47 namespace {
     48 
     49 void rad_to_unit_matrix(const SkPoint& center, SkScalar radius,
     50                                SkMatrix* matrix) {
     51     SkScalar    inv = SkScalarInvert(radius);
     52 
     53     matrix->setTranslate(-center.fX, -center.fY);
     54     matrix->postScale(inv, inv);
     55 }
     56 
     57 typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx,
     58         SkScalar sfy, SkScalar sdy,
     59         uint16_t* dstC, const uint16_t* cache,
     60         int toggle, int count);
     61 
     62 void shadeSpan16_radial_clamp(SkScalar sfx, SkScalar sdx,
     63         SkScalar sfy, SkScalar sdy,
     64         uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
     65         int toggle, int count) {
     66     const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
     67 
     68     /* knock these down so we can pin against +- 0x7FFF, which is an
     69        immediate load, rather than 0xFFFF which is slower. This is a
     70        compromise, since it reduces our precision, but that appears
     71        to be visually OK. If we decide this is OK for all of our cases,
     72        we could (it seems) put this scale-down into fDstToIndex,
     73        to avoid having to do these extra shifts each time.
     74     */
     75     SkFixed fx = SkScalarToFixed(sfx) >> 1;
     76     SkFixed dx = SkScalarToFixed(sdx) >> 1;
     77     SkFixed fy = SkScalarToFixed(sfy) >> 1;
     78     SkFixed dy = SkScalarToFixed(sdy) >> 1;
     79     // might perform this check for the other modes,
     80     // but the win will be a smaller % of the total
     81     if (dy == 0) {
     82         fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
     83         fy *= fy;
     84         do {
     85             unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
     86             unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
     87             fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
     88             fx += dx;
     89             *dstC++ = cache[toggle +
     90                             (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
     91             toggle = next_dither_toggle16(toggle);
     92         } while (--count != 0);
     93     } else {
     94         do {
     95             unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
     96             unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
     97             fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
     98             fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
     99             fx += dx;
    100             fy += dy;
    101             *dstC++ = cache[toggle +
    102                             (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
    103             toggle = next_dither_toggle16(toggle);
    104         } while (--count != 0);
    105     }
    106 }
    107 
    108 void shadeSpan16_radial_mirror(SkScalar sfx, SkScalar sdx,
    109         SkScalar sfy, SkScalar sdy,
    110         uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
    111         int toggle, int count) {
    112     do {
    113 #ifdef SK_SCALAR_IS_FLOAT
    114         float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
    115         SkFixed dist = SkFloatToFixed(fdist);
    116 #else
    117         SkFixed magnitudeSquared = SkFixedSquare(sfx) +
    118             SkFixedSquare(sfy);
    119         if (magnitudeSquared < 0) // Overflow.
    120             magnitudeSquared = SK_FixedMax;
    121         SkFixed dist = SkFixedSqrt(magnitudeSquared);
    122 #endif
    123         unsigned fi = mirror_tileproc(dist);
    124         SkASSERT(fi <= 0xFFFF);
    125         *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)];
    126         toggle = next_dither_toggle16(toggle);
    127         sfx += sdx;
    128         sfy += sdy;
    129     } while (--count != 0);
    130 }
    131 
    132 void shadeSpan16_radial_repeat(SkScalar sfx, SkScalar sdx,
    133         SkScalar sfy, SkScalar sdy,
    134         uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
    135         int toggle, int count) {
    136     SkFixed fx = SkScalarToFixed(sfx);
    137     SkFixed dx = SkScalarToFixed(sdx);
    138     SkFixed fy = SkScalarToFixed(sfy);
    139     SkFixed dy = SkScalarToFixed(sdy);
    140     do {
    141         SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
    142         unsigned fi = repeat_tileproc(dist);
    143         SkASSERT(fi <= 0xFFFF);
    144         fx += dx;
    145         fy += dy;
    146         *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)];
    147         toggle = next_dither_toggle16(toggle);
    148     } while (--count != 0);
    149 }
    150 
    151 }
    152 
    153 /////////////////////////////////////////////////////////////////////
    154 
    155 SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius,
    156                                    const Descriptor& desc)
    157     : SkGradientShaderBase(desc),
    158       fCenter(center),
    159       fRadius(radius)
    160 {
    161     // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
    162     SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
    163 
    164     rad_to_unit_matrix(center, radius, &fPtsToUnit);
    165 }
    166 
    167 void SkRadialGradient::shadeSpan16(int x, int y, uint16_t* dstCParam,
    168                          int count) {
    169     SkASSERT(count > 0);
    170 
    171     uint16_t* SK_RESTRICT dstC = dstCParam;
    172 
    173     SkPoint             srcPt;
    174     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
    175     TileProc            proc = fTileProc;
    176     const uint16_t* SK_RESTRICT cache = this->getCache16();
    177     int                 toggle = init_dither_toggle16(x, y);
    178 
    179     if (fDstToIndexClass != kPerspective_MatrixClass) {
    180         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
    181                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
    182 
    183         SkScalar sdx = fDstToIndex.getScaleX();
    184         SkScalar sdy = fDstToIndex.getSkewY();
    185 
    186         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
    187             SkFixed storage[2];
    188             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
    189                                            &storage[0], &storage[1]);
    190             sdx = SkFixedToScalar(storage[0]);
    191             sdy = SkFixedToScalar(storage[1]);
    192         } else {
    193             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
    194         }
    195 
    196         RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
    197         if (SkShader::kClamp_TileMode == fTileMode) {
    198             shadeProc = shadeSpan16_radial_clamp;
    199         } else if (SkShader::kMirror_TileMode == fTileMode) {
    200             shadeProc = shadeSpan16_radial_mirror;
    201         } else {
    202             SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
    203         }
    204         (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
    205                      cache, toggle, count);
    206     } else {    // perspective case
    207         SkScalar dstX = SkIntToScalar(x);
    208         SkScalar dstY = SkIntToScalar(y);
    209         do {
    210             dstProc(fDstToIndex, dstX, dstY, &srcPt);
    211             unsigned fi = proc(SkScalarToFixed(srcPt.length()));
    212             SkASSERT(fi <= 0xFFFF);
    213 
    214             int index = fi >> (16 - kCache16Bits);
    215             *dstC++ = cache[toggle + index];
    216             toggle = next_dither_toggle16(toggle);
    217 
    218             dstX += SK_Scalar1;
    219         } while (--count != 0);
    220     }
    221 }
    222 
    223 SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap,
    224     SkMatrix* matrix, SkShader::TileMode* xy) const {
    225     if (bitmap) {
    226         this->getGradientTableBitmap(bitmap);
    227     }
    228     if (matrix) {
    229         matrix->setScale(SkIntToScalar(kCache32Count),
    230                          SkIntToScalar(kCache32Count));
    231         matrix->preConcat(fPtsToUnit);
    232     }
    233     if (xy) {
    234         xy[0] = fTileMode;
    235         xy[1] = kClamp_TileMode;
    236     }
    237     return kRadial_BitmapType;
    238 }
    239 
    240 SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
    241     if (info) {
    242         commonAsAGradient(info);
    243         info->fPoint[0] = fCenter;
    244         info->fRadius[0] = fRadius;
    245     }
    246     return kRadial_GradientType;
    247 }
    248 
    249 SkRadialGradient::SkRadialGradient(SkFlattenableReadBuffer& buffer)
    250     : INHERITED(buffer),
    251       fCenter(buffer.readPoint()),
    252       fRadius(buffer.readScalar()) {
    253 }
    254 
    255 void SkRadialGradient::flatten(SkFlattenableWriteBuffer& buffer) const {
    256     this->INHERITED::flatten(buffer);
    257     buffer.writePoint(fCenter);
    258     buffer.writeScalar(fRadius);
    259 }
    260 
    261 namespace {
    262 
    263 inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
    264     // fast, overly-conservative test: checks unit square instead
    265     // of unit circle
    266     bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
    267                     (fx <= -SK_FixedHalf && dx <= 0);
    268     bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
    269                     (fy <= -SK_FixedHalf && dy <= 0);
    270 
    271     return xClamped || yClamped;
    272 }
    273 
    274 // Return true if (fx * fy) is always inside the unit circle
    275 // SkPin32 is expensive, but so are all the SkFixedMul in this test,
    276 // so it shouldn't be run if count is small.
    277 inline bool no_need_for_radial_pin(int fx, int dx,
    278                                           int fy, int dy, int count) {
    279     SkASSERT(count > 0);
    280     if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
    281         return false;
    282     }
    283     if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
    284         return false;
    285     }
    286     fx += (count - 1) * dx;
    287     fy += (count - 1) * dy;
    288     if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
    289         return false;
    290     }
    291     return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
    292 }
    293 
    294 #define UNPINNED_RADIAL_STEP \
    295     fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
    296     *dstC++ = cache[toggle + \
    297                     (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
    298     toggle = next_dither_toggle(toggle); \
    299     fx += dx; \
    300     fy += dy;
    301 
    302 typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
    303         SkScalar sfy, SkScalar sdy,
    304         SkPMColor* dstC, const SkPMColor* cache,
    305         int count, int toggle);
    306 
    307 // On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
    308 void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
    309         SkScalar sfy, SkScalar sdy,
    310         SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
    311         int count, int toggle) {
    312     // Floating point seems to be slower than fixed point,
    313     // even when we have float hardware.
    314     const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
    315     SkFixed fx = SkScalarToFixed(sfx) >> 1;
    316     SkFixed dx = SkScalarToFixed(sdx) >> 1;
    317     SkFixed fy = SkScalarToFixed(sfy) >> 1;
    318     SkFixed dy = SkScalarToFixed(sdy) >> 1;
    319     if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
    320         unsigned fi = SkGradientShaderBase::kCache32Count - 1;
    321         sk_memset32_dither(dstC,
    322             cache[toggle + fi],
    323             cache[next_dither_toggle(toggle) + fi],
    324             count);
    325     } else if ((count > 4) &&
    326                no_need_for_radial_pin(fx, dx, fy, dy, count)) {
    327         unsigned fi;
    328         // 4x unroll appears to be no faster than 2x unroll on Linux
    329         while (count > 1) {
    330             UNPINNED_RADIAL_STEP;
    331             UNPINNED_RADIAL_STEP;
    332             count -= 2;
    333         }
    334         if (count) {
    335             UNPINNED_RADIAL_STEP;
    336         }
    337     } else  {
    338         // Specializing for dy == 0 gains us 25% on Skia benchmarks
    339         if (dy == 0) {
    340             unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
    341             yy *= yy;
    342             do {
    343                 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
    344                 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
    345                 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
    346                 *dstC++ = cache[toggle + (sqrt_table[fi] >>
    347                     SkGradientShaderBase::kSqrt32Shift)];
    348                 toggle = next_dither_toggle(toggle);
    349                 fx += dx;
    350             } while (--count != 0);
    351         } else {
    352             do {
    353                 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
    354                 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
    355                 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
    356                 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
    357                 *dstC++ = cache[toggle + (sqrt_table[fi] >>
    358                     SkGradientShaderBase::kSqrt32Shift)];
    359                 toggle = next_dither_toggle(toggle);
    360                 fx += dx;
    361                 fy += dy;
    362             } while (--count != 0);
    363         }
    364     }
    365 }
    366 
    367 // Unrolling this loop doesn't seem to help (when float); we're stalling to
    368 // get the results of the sqrt (?), and don't have enough extra registers to
    369 // have many in flight.
    370 void shadeSpan_radial_mirror(SkScalar sfx, SkScalar sdx,
    371         SkScalar sfy, SkScalar sdy,
    372         SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
    373         int count, int toggle) {
    374     do {
    375 #ifdef SK_SCALAR_IS_FLOAT
    376         float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
    377         SkFixed dist = SkFloatToFixed(fdist);
    378 #else
    379         SkFixed magnitudeSquared = SkFixedSquare(sfx) +
    380             SkFixedSquare(sfy);
    381         if (magnitudeSquared < 0) // Overflow.
    382             magnitudeSquared = SK_FixedMax;
    383         SkFixed dist = SkFixedSqrt(magnitudeSquared);
    384 #endif
    385         unsigned fi = mirror_tileproc(dist);
    386         SkASSERT(fi <= 0xFFFF);
    387         *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
    388         toggle = next_dither_toggle(toggle);
    389         sfx += sdx;
    390         sfy += sdy;
    391     } while (--count != 0);
    392 }
    393 
    394 void shadeSpan_radial_repeat(SkScalar sfx, SkScalar sdx,
    395         SkScalar sfy, SkScalar sdy,
    396         SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
    397         int count, int toggle) {
    398     SkFixed fx = SkScalarToFixed(sfx);
    399     SkFixed dx = SkScalarToFixed(sdx);
    400     SkFixed fy = SkScalarToFixed(sfy);
    401     SkFixed dy = SkScalarToFixed(sdy);
    402     do {
    403         SkFixed magnitudeSquared = SkFixedSquare(fx) +
    404             SkFixedSquare(fy);
    405         if (magnitudeSquared < 0) // Overflow.
    406             magnitudeSquared = SK_FixedMax;
    407         SkFixed dist = SkFixedSqrt(magnitudeSquared);
    408         unsigned fi = repeat_tileproc(dist);
    409         SkASSERT(fi <= 0xFFFF);
    410         *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
    411         toggle = next_dither_toggle(toggle);
    412         fx += dx;
    413         fy += dy;
    414     } while (--count != 0);
    415 }
    416 }
    417 
    418 void SkRadialGradient::shadeSpan(int x, int y,
    419                                 SkPMColor* SK_RESTRICT dstC, int count) {
    420     SkASSERT(count > 0);
    421 
    422     SkPoint             srcPt;
    423     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
    424     TileProc            proc = fTileProc;
    425     const SkPMColor* SK_RESTRICT cache = this->getCache32();
    426     int toggle = init_dither_toggle(x, y);
    427 
    428     if (fDstToIndexClass != kPerspective_MatrixClass) {
    429         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
    430                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
    431         SkScalar sdx = fDstToIndex.getScaleX();
    432         SkScalar sdy = fDstToIndex.getSkewY();
    433 
    434         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
    435             SkFixed storage[2];
    436             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
    437                                            &storage[0], &storage[1]);
    438             sdx = SkFixedToScalar(storage[0]);
    439             sdy = SkFixedToScalar(storage[1]);
    440         } else {
    441             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
    442         }
    443 
    444         RadialShadeProc shadeProc = shadeSpan_radial_repeat;
    445         if (SkShader::kClamp_TileMode == fTileMode) {
    446             shadeProc = shadeSpan_radial_clamp;
    447         } else if (SkShader::kMirror_TileMode == fTileMode) {
    448             shadeProc = shadeSpan_radial_mirror;
    449         } else {
    450             SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
    451         }
    452         (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
    453     } else {    // perspective case
    454         SkScalar dstX = SkIntToScalar(x);
    455         SkScalar dstY = SkIntToScalar(y);
    456         do {
    457             dstProc(fDstToIndex, dstX, dstY, &srcPt);
    458             unsigned fi = proc(SkScalarToFixed(srcPt.length()));
    459             SkASSERT(fi <= 0xFFFF);
    460             *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
    461             dstX += SK_Scalar1;
    462         } while (--count != 0);
    463     }
    464 }
    465 
    466 /////////////////////////////////////////////////////////////////////
    467 
    468 #if SK_SUPPORT_GPU
    469 
    470 #include "GrTBackendEffectFactory.h"
    471 
    472 class GrGLRadialGradient : public GrGLGradientEffect {
    473 public:
    474 
    475     GrGLRadialGradient(const GrBackendEffectFactory& factory,
    476                        const GrDrawEffect&) : INHERITED (factory) { }
    477     virtual ~GrGLRadialGradient() { }
    478 
    479     virtual void emitCode(GrGLShaderBuilder*,
    480                           const GrDrawEffect&,
    481                           EffectKey,
    482                           const char* outputColor,
    483                           const char* inputColor,
    484                           const TextureSamplerArray&) SK_OVERRIDE;
    485 
    486     static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
    487         return GenMatrixKey(drawEffect);
    488     }
    489 
    490 private:
    491 
    492     typedef GrGLGradientEffect INHERITED;
    493 
    494 };
    495 
    496 /////////////////////////////////////////////////////////////////////
    497 
    498 class GrRadialGradient : public GrGradientEffect {
    499 public:
    500     static GrEffectRef* Create(GrContext* ctx,
    501                                const SkRadialGradient& shader,
    502                                const SkMatrix& matrix,
    503                                SkShader::TileMode tm) {
    504         AutoEffectUnref effect(SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm)));
    505         return CreateEffectRef(effect);
    506     }
    507 
    508     virtual ~GrRadialGradient() { }
    509 
    510     static const char* Name() { return "Radial Gradient"; }
    511     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
    512         return GrTBackendEffectFactory<GrRadialGradient>::getInstance();
    513     }
    514 
    515     typedef GrGLRadialGradient GLEffect;
    516 
    517 private:
    518     GrRadialGradient(GrContext* ctx,
    519                      const SkRadialGradient& shader,
    520                      const SkMatrix& matrix,
    521                      SkShader::TileMode tm)
    522         : INHERITED(ctx, shader, matrix, tm) {
    523     }
    524 
    525     GR_DECLARE_EFFECT_TEST;
    526 
    527     typedef GrGradientEffect INHERITED;
    528 };
    529 
    530 /////////////////////////////////////////////////////////////////////
    531 
    532 GR_DEFINE_EFFECT_TEST(GrRadialGradient);
    533 
    534 GrEffectRef* GrRadialGradient::TestCreate(SkMWCRandom* random,
    535                                           GrContext* context,
    536                                           const GrDrawTargetCaps&,
    537                                           GrTexture**) {
    538     SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
    539     SkScalar radius = random->nextUScalar1();
    540 
    541     SkColor colors[kMaxRandomGradientColors];
    542     SkScalar stopsArray[kMaxRandomGradientColors];
    543     SkScalar* stops = stopsArray;
    544     SkShader::TileMode tm;
    545     int colorCount = RandomGradientParams(random, colors, &stops, &tm);
    546     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
    547                                                                  colors, stops, colorCount,
    548                                                                  tm));
    549     SkPaint paint;
    550     return shader->asNewEffect(context, paint);
    551 }
    552 
    553 /////////////////////////////////////////////////////////////////////
    554 
    555 void GrGLRadialGradient::emitCode(GrGLShaderBuilder* builder,
    556                                   const GrDrawEffect&,
    557                                   EffectKey key,
    558                                   const char* outputColor,
    559                                   const char* inputColor,
    560                                   const TextureSamplerArray& samplers) {
    561     this->emitYCoordUniform(builder);
    562     const char* coords;
    563     this->setupMatrix(builder, key, &coords);
    564     SkString t("length(");
    565     t.append(coords);
    566     t.append(")");
    567     this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
    568 }
    569 
    570 /////////////////////////////////////////////////////////////////////
    571 
    572 GrEffectRef* SkRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
    573     SkASSERT(NULL != context);
    574 
    575     SkMatrix matrix;
    576     if (!this->getLocalMatrix().invert(&matrix)) {
    577         return NULL;
    578     }
    579     matrix.postConcat(fPtsToUnit);
    580     return GrRadialGradient::Create(context, *this, matrix, fTileMode);
    581 }
    582 
    583 #else
    584 
    585 GrEffectRef* SkRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
    586     SkDEBUGFAIL("Should not call in GPU-less build");
    587     return NULL;
    588 }
    589 
    590 #endif
    591 
    592 #ifdef SK_DEVELOPER
    593 void SkRadialGradient::toString(SkString* str) const {
    594     str->append("SkRadialGradient: (");
    595 
    596     str->append("center: (");
    597     str->appendScalar(fCenter.fX);
    598     str->append(", ");
    599     str->appendScalar(fCenter.fY);
    600     str->append(") radius: ");
    601     str->appendScalar(fRadius);
    602     str->append(" ");
    603 
    604     this->INHERITED::toString(str);
    605 
    606     str->append(")");
    607 }
    608 #endif
    609