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 0
     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, const SkMatrix* localMatrix)
    150     : SkGradientShaderBase(desc, localMatrix),
    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 size_t SkRadialGradient::contextSize() const {
    161     return sizeof(RadialGradientContext);
    162 }
    163 
    164 SkShader::Context* SkRadialGradient::onCreateContext(const ContextRec& rec, void* storage) const {
    165     return SkNEW_PLACEMENT_ARGS(storage, RadialGradientContext, (*this, rec));
    166 }
    167 
    168 SkRadialGradient::RadialGradientContext::RadialGradientContext(
    169         const SkRadialGradient& shader, const ContextRec& rec)
    170     : INHERITED(shader, rec) {}
    171 
    172 void SkRadialGradient::RadialGradientContext::shadeSpan16(int x, int y, uint16_t* dstCParam,
    173                                                           int count) {
    174     SkASSERT(count > 0);
    175 
    176     const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
    177 
    178     uint16_t* SK_RESTRICT dstC = dstCParam;
    179 
    180     SkPoint             srcPt;
    181     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
    182     TileProc            proc = radialGradient.fTileProc;
    183     const uint16_t* SK_RESTRICT cache = fCache->getCache16();
    184     int                 toggle = init_dither_toggle16(x, y);
    185 
    186     if (fDstToIndexClass != kPerspective_MatrixClass) {
    187         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
    188                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
    189 
    190         SkScalar sdx = fDstToIndex.getScaleX();
    191         SkScalar sdy = fDstToIndex.getSkewY();
    192 
    193         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
    194             SkFixed storage[2];
    195             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
    196                                            &storage[0], &storage[1]);
    197             sdx = SkFixedToScalar(storage[0]);
    198             sdy = SkFixedToScalar(storage[1]);
    199         } else {
    200             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
    201         }
    202 
    203         RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
    204         if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
    205             shadeProc = shadeSpan16_radial_clamp;
    206         } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
    207             shadeProc = shadeSpan16_radial_mirror;
    208         } else {
    209             SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
    210         }
    211         (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
    212                      cache, toggle, count);
    213     } else {    // perspective case
    214         SkScalar dstX = SkIntToScalar(x);
    215         SkScalar dstY = SkIntToScalar(y);
    216         do {
    217             dstProc(fDstToIndex, dstX, dstY, &srcPt);
    218             unsigned fi = proc(SkScalarToFixed(srcPt.length()));
    219             SkASSERT(fi <= 0xFFFF);
    220 
    221             int index = fi >> (16 - kCache16Bits);
    222             *dstC++ = cache[toggle + index];
    223             toggle = next_dither_toggle16(toggle);
    224 
    225             dstX += SK_Scalar1;
    226         } while (--count != 0);
    227     }
    228 }
    229 
    230 SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap,
    231     SkMatrix* matrix, SkShader::TileMode* xy) const {
    232     if (bitmap) {
    233         this->getGradientTableBitmap(bitmap);
    234     }
    235     if (matrix) {
    236         matrix->setScale(SkIntToScalar(kCache32Count),
    237                          SkIntToScalar(kCache32Count));
    238         matrix->preConcat(fPtsToUnit);
    239     }
    240     if (xy) {
    241         xy[0] = fTileMode;
    242         xy[1] = kClamp_TileMode;
    243     }
    244     return kRadial_BitmapType;
    245 }
    246 
    247 SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
    248     if (info) {
    249         commonAsAGradient(info);
    250         info->fPoint[0] = fCenter;
    251         info->fRadius[0] = fRadius;
    252     }
    253     return kRadial_GradientType;
    254 }
    255 
    256 SkRadialGradient::SkRadialGradient(SkReadBuffer& buffer)
    257     : INHERITED(buffer),
    258       fCenter(buffer.readPoint()),
    259       fRadius(buffer.readScalar()) {
    260 }
    261 
    262 void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
    263     this->INHERITED::flatten(buffer);
    264     buffer.writePoint(fCenter);
    265     buffer.writeScalar(fRadius);
    266 }
    267 
    268 namespace {
    269 
    270 inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
    271     // fast, overly-conservative test: checks unit square instead
    272     // of unit circle
    273     bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
    274                     (fx <= -SK_FixedHalf && dx <= 0);
    275     bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
    276                     (fy <= -SK_FixedHalf && dy <= 0);
    277 
    278     return xClamped || yClamped;
    279 }
    280 
    281 // Return true if (fx * fy) is always inside the unit circle
    282 // SkPin32 is expensive, but so are all the SkFixedMul in this test,
    283 // so it shouldn't be run if count is small.
    284 inline bool no_need_for_radial_pin(int fx, int dx,
    285                                           int fy, int dy, int count) {
    286     SkASSERT(count > 0);
    287     if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
    288         return false;
    289     }
    290     if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
    291         return false;
    292     }
    293     fx += (count - 1) * dx;
    294     fy += (count - 1) * dy;
    295     if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
    296         return false;
    297     }
    298     return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
    299 }
    300 
    301 #define UNPINNED_RADIAL_STEP \
    302     fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
    303     *dstC++ = cache[toggle + \
    304                     (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
    305     toggle = next_dither_toggle(toggle); \
    306     fx += dx; \
    307     fy += dy;
    308 
    309 typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
    310         SkScalar sfy, SkScalar sdy,
    311         SkPMColor* dstC, const SkPMColor* cache,
    312         int count, int toggle);
    313 
    314 // On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
    315 void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
    316         SkScalar sfy, SkScalar sdy,
    317         SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
    318         int count, int toggle) {
    319     // Floating point seems to be slower than fixed point,
    320     // even when we have float hardware.
    321     const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
    322     SkFixed fx = SkScalarToFixed(sfx) >> 1;
    323     SkFixed dx = SkScalarToFixed(sdx) >> 1;
    324     SkFixed fy = SkScalarToFixed(sfy) >> 1;
    325     SkFixed dy = SkScalarToFixed(sdy) >> 1;
    326     if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
    327         unsigned fi = SkGradientShaderBase::kCache32Count - 1;
    328         sk_memset32_dither(dstC,
    329             cache[toggle + fi],
    330             cache[next_dither_toggle(toggle) + fi],
    331             count);
    332     } else if ((count > 4) &&
    333                no_need_for_radial_pin(fx, dx, fy, dy, count)) {
    334         unsigned fi;
    335         // 4x unroll appears to be no faster than 2x unroll on Linux
    336         while (count > 1) {
    337             UNPINNED_RADIAL_STEP;
    338             UNPINNED_RADIAL_STEP;
    339             count -= 2;
    340         }
    341         if (count) {
    342             UNPINNED_RADIAL_STEP;
    343         }
    344     } else  {
    345         // Specializing for dy == 0 gains us 25% on Skia benchmarks
    346         if (dy == 0) {
    347             unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
    348             yy *= yy;
    349             do {
    350                 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
    351                 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
    352                 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
    353                 *dstC++ = cache[toggle + (sqrt_table[fi] >>
    354                     SkGradientShaderBase::kSqrt32Shift)];
    355                 toggle = next_dither_toggle(toggle);
    356                 fx += dx;
    357             } while (--count != 0);
    358         } else {
    359             do {
    360                 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
    361                 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
    362                 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
    363                 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
    364                 *dstC++ = cache[toggle + (sqrt_table[fi] >>
    365                     SkGradientShaderBase::kSqrt32Shift)];
    366                 toggle = next_dither_toggle(toggle);
    367                 fx += dx;
    368                 fy += dy;
    369             } while (--count != 0);
    370         }
    371     }
    372 }
    373 
    374 // Unrolling this loop doesn't seem to help (when float); we're stalling to
    375 // get the results of the sqrt (?), and don't have enough extra registers to
    376 // have many in flight.
    377 template <SkFixed (*TileProc)(SkFixed)>
    378 void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
    379                       SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
    380                       int count, int toggle) {
    381     do {
    382         const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
    383         const unsigned fi = TileProc(dist);
    384         SkASSERT(fi <= 0xFFFF);
    385         *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
    386         toggle = next_dither_toggle(toggle);
    387         fx += dx;
    388         fy += dy;
    389     } while (--count != 0);
    390 }
    391 
    392 void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
    393                              SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
    394                              int count, int toggle) {
    395     shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
    396 }
    397 
    398 void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
    399                              SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
    400                              int count, int toggle) {
    401     shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
    402 }
    403 
    404 }  // namespace
    405 
    406 void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y,
    407                                                         SkPMColor* SK_RESTRICT dstC, int count) {
    408     SkASSERT(count > 0);
    409 
    410     const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
    411 
    412     SkPoint             srcPt;
    413     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
    414     TileProc            proc = radialGradient.fTileProc;
    415     const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
    416     int toggle = init_dither_toggle(x, y);
    417 
    418     if (fDstToIndexClass != kPerspective_MatrixClass) {
    419         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
    420                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
    421         SkScalar sdx = fDstToIndex.getScaleX();
    422         SkScalar sdy = fDstToIndex.getSkewY();
    423 
    424         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
    425             SkFixed storage[2];
    426             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
    427                                            &storage[0], &storage[1]);
    428             sdx = SkFixedToScalar(storage[0]);
    429             sdy = SkFixedToScalar(storage[1]);
    430         } else {
    431             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
    432         }
    433 
    434         RadialShadeProc shadeProc = shadeSpan_radial_repeat;
    435         if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
    436             shadeProc = shadeSpan_radial_clamp;
    437         } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
    438             shadeProc = shadeSpan_radial_mirror;
    439         } else {
    440             SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
    441         }
    442         (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
    443     } else {    // perspective case
    444         SkScalar dstX = SkIntToScalar(x);
    445         SkScalar dstY = SkIntToScalar(y);
    446         do {
    447             dstProc(fDstToIndex, dstX, dstY, &srcPt);
    448             unsigned fi = proc(SkScalarToFixed(srcPt.length()));
    449             SkASSERT(fi <= 0xFFFF);
    450             *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
    451             dstX += SK_Scalar1;
    452         } while (--count != 0);
    453     }
    454 }
    455 
    456 /////////////////////////////////////////////////////////////////////
    457 
    458 #if SK_SUPPORT_GPU
    459 
    460 #include "GrTBackendEffectFactory.h"
    461 #include "SkGr.h"
    462 
    463 class GrGLRadialGradient : public GrGLGradientEffect {
    464 public:
    465 
    466     GrGLRadialGradient(const GrBackendEffectFactory& factory,
    467                        const GrDrawEffect&) : INHERITED (factory) { }
    468     virtual ~GrGLRadialGradient() { }
    469 
    470     virtual void emitCode(GrGLShaderBuilder*,
    471                           const GrDrawEffect&,
    472                           EffectKey,
    473                           const char* outputColor,
    474                           const char* inputColor,
    475                           const TransformedCoordsArray&,
    476                           const TextureSamplerArray&) SK_OVERRIDE;
    477 
    478     static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
    479         return GenBaseGradientKey(drawEffect);
    480     }
    481 
    482 private:
    483 
    484     typedef GrGLGradientEffect INHERITED;
    485 
    486 };
    487 
    488 /////////////////////////////////////////////////////////////////////
    489 
    490 class GrRadialGradient : public GrGradientEffect {
    491 public:
    492     static GrEffectRef* Create(GrContext* ctx,
    493                                const SkRadialGradient& shader,
    494                                const SkMatrix& matrix,
    495                                SkShader::TileMode tm) {
    496         AutoEffectUnref effect(SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm)));
    497         return CreateEffectRef(effect);
    498     }
    499 
    500     virtual ~GrRadialGradient() { }
    501 
    502     static const char* Name() { return "Radial Gradient"; }
    503     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
    504         return GrTBackendEffectFactory<GrRadialGradient>::getInstance();
    505     }
    506 
    507     typedef GrGLRadialGradient GLEffect;
    508 
    509 private:
    510     GrRadialGradient(GrContext* ctx,
    511                      const SkRadialGradient& shader,
    512                      const SkMatrix& matrix,
    513                      SkShader::TileMode tm)
    514         : INHERITED(ctx, shader, matrix, tm) {
    515     }
    516 
    517     GR_DECLARE_EFFECT_TEST;
    518 
    519     typedef GrGradientEffect INHERITED;
    520 };
    521 
    522 /////////////////////////////////////////////////////////////////////
    523 
    524 GR_DEFINE_EFFECT_TEST(GrRadialGradient);
    525 
    526 GrEffectRef* GrRadialGradient::TestCreate(SkRandom* random,
    527                                           GrContext* context,
    528                                           const GrDrawTargetCaps&,
    529                                           GrTexture**) {
    530     SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
    531     SkScalar radius = random->nextUScalar1();
    532 
    533     SkColor colors[kMaxRandomGradientColors];
    534     SkScalar stopsArray[kMaxRandomGradientColors];
    535     SkScalar* stops = stopsArray;
    536     SkShader::TileMode tm;
    537     int colorCount = RandomGradientParams(random, colors, &stops, &tm);
    538     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
    539                                                                  colors, stops, colorCount,
    540                                                                  tm));
    541     SkPaint paint;
    542     GrColor grColor;
    543     GrEffectRef* effect;
    544     shader->asNewEffect(context, paint, NULL, &grColor, &effect);
    545     return effect;
    546 }
    547 
    548 /////////////////////////////////////////////////////////////////////
    549 
    550 void GrGLRadialGradient::emitCode(GrGLShaderBuilder* builder,
    551                                   const GrDrawEffect&,
    552                                   EffectKey key,
    553                                   const char* outputColor,
    554                                   const char* inputColor,
    555                                   const TransformedCoordsArray& coords,
    556                                   const TextureSamplerArray& samplers) {
    557     this->emitUniforms(builder, key);
    558     SkString t("length(");
    559     t.append(builder->ensureFSCoords2D(coords, 0));
    560     t.append(")");
    561     this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers);
    562 }
    563 
    564 /////////////////////////////////////////////////////////////////////
    565 
    566 bool SkRadialGradient::asNewEffect(GrContext* context, const SkPaint& paint,
    567                                    const SkMatrix* localMatrix, GrColor* grColor,
    568                                    GrEffectRef** grEffect) const {
    569     SkASSERT(NULL != context);
    570 
    571     SkMatrix matrix;
    572     if (!this->getLocalMatrix().invert(&matrix)) {
    573         return false;
    574     }
    575     if (localMatrix) {
    576         SkMatrix inv;
    577         if (!localMatrix->invert(&inv)) {
    578             return false;
    579         }
    580         matrix.postConcat(inv);
    581     }
    582     matrix.postConcat(fPtsToUnit);
    583 
    584     *grColor = SkColor2GrColorJustAlpha(paint.getColor());
    585     *grEffect = GrRadialGradient::Create(context, *this, matrix, fTileMode);
    586 
    587     return true;
    588 }
    589 
    590 #else
    591 
    592 bool SkRadialGradient::asNewEffect(GrContext* context, const SkPaint& paint,
    593                                    const SkMatrix* localMatrix, GrColor* grColor,
    594                                    GrEffectRef** grEffect) const {
    595     SkDEBUGFAIL("Should not call in GPU-less build");
    596     return false;
    597 }
    598 
    599 #endif
    600 
    601 #ifndef SK_IGNORE_TO_STRING
    602 void SkRadialGradient::toString(SkString* str) const {
    603     str->append("SkRadialGradient: (");
    604 
    605     str->append("center: (");
    606     str->appendScalar(fCenter.fX);
    607     str->append(", ");
    608     str->appendScalar(fCenter.fY);
    609     str->append(") radius: ");
    610     str->appendScalar(fRadius);
    611     str->append(" ");
    612 
    613     this->INHERITED::toString(str);
    614 
    615     str->append(")");
    616 }
    617 #endif
    618