SkRadialGradient.cpp revision 2011fe9cdfa63b83489a146cea6a724cede352c8
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
19void 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
47namespace {
48
49// GCC doesn't like using static functions as template arguments.  So force these to be non-static.
50inline SkFixed mirror_tileproc_nonstatic(SkFixed x) {
51    return mirror_tileproc(x);
52}
53
54inline SkFixed repeat_tileproc_nonstatic(SkFixed x) {
55    return repeat_tileproc(x);
56}
57
58void 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
66typedef 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
71void 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
117template <SkFixed (*TileProc)(SkFixed)>
118void 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
132void 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
138void 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
148SkRadialGradient::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
160size_t SkRadialGradient::contextSize() const {
161    return sizeof(RadialGradientContext);
162}
163
164SkShader::Context* SkRadialGradient::onCreateContext(const ContextRec& rec, void* storage) const {
165    return SkNEW_PLACEMENT_ARGS(storage, RadialGradientContext, (*this, rec));
166}
167
168SkRadialGradient::RadialGradientContext::RadialGradientContext(
169        const SkRadialGradient& shader, const ContextRec& rec)
170    : INHERITED(shader, rec) {}
171
172void 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
230SkShader::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
247SkShader::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
256SkRadialGradient::SkRadialGradient(SkReadBuffer& buffer)
257    : INHERITED(buffer),
258      fCenter(buffer.readPoint()),
259      fRadius(buffer.readScalar()) {
260}
261
262void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
263    this->INHERITED::flatten(buffer);
264    buffer.writePoint(fCenter);
265    buffer.writeScalar(fRadius);
266}
267
268namespace {
269
270inline 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.
284inline 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
309typedef 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
315void 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.
377template <SkFixed (*TileProc)(SkFixed)>
378void 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
392void 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
398void 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
406void 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
463class GrGLRadialGradient : public GrGLGradientEffect {
464public:
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
482private:
483
484    typedef GrGLGradientEffect INHERITED;
485
486};
487
488/////////////////////////////////////////////////////////////////////
489
490class GrRadialGradient : public GrGradientEffect {
491public:
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
509private:
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
524GR_DEFINE_EFFECT_TEST(GrRadialGradient);
525
526GrEffectRef* 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
550void 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
566bool 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
592bool SkRadialGradient::asNewEffect(GrContext* context, const SkPaint& paint,
593                                   const SkMatrix* localMatrix, GrColor* grColor,
594                                   GrEffect** 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
602void 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