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