SkRadialGradient.cpp revision ae81d5c4aa1716756b2cfb4c44f27f4dce2716ef
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
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
49void 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
57typedef 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
62void 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
108void 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
132void 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
155SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius,
156                const SkColor colors[], const SkScalar pos[], int colorCount,
157                SkShader::TileMode mode, SkUnitMapper* mapper)
158    : SkGradientShaderBase(colors, pos, colorCount, mode, mapper),
159      fCenter(center),
160      fRadius(radius)
161{
162    // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
163    SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
164
165    rad_to_unit_matrix(center, radius, &fPtsToUnit);
166}
167
168void SkRadialGradient::shadeSpan16(int x, int y, uint16_t* dstCParam,
169                         int count) {
170    SkASSERT(count > 0);
171
172    uint16_t* SK_RESTRICT dstC = dstCParam;
173
174    SkPoint             srcPt;
175    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
176    TileProc            proc = fTileProc;
177    const uint16_t* SK_RESTRICT cache = this->getCache16();
178    int                 toggle = init_dither_toggle16(x, y);
179
180    if (fDstToIndexClass != kPerspective_MatrixClass) {
181        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
182                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
183
184        SkScalar sdx = fDstToIndex.getScaleX();
185        SkScalar sdy = fDstToIndex.getSkewY();
186
187        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
188            SkFixed storage[2];
189            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
190                                           &storage[0], &storage[1]);
191            sdx = SkFixedToScalar(storage[0]);
192            sdy = SkFixedToScalar(storage[1]);
193        } else {
194            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
195        }
196
197        RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
198        if (SkShader::kClamp_TileMode == fTileMode) {
199            shadeProc = shadeSpan16_radial_clamp;
200        } else if (SkShader::kMirror_TileMode == fTileMode) {
201            shadeProc = shadeSpan16_radial_mirror;
202        } else {
203            SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
204        }
205        (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
206                     cache, toggle, count);
207    } else {    // perspective case
208        SkScalar dstX = SkIntToScalar(x);
209        SkScalar dstY = SkIntToScalar(y);
210        do {
211            dstProc(fDstToIndex, dstX, dstY, &srcPt);
212            unsigned fi = proc(SkScalarToFixed(srcPt.length()));
213            SkASSERT(fi <= 0xFFFF);
214
215            int index = fi >> (16 - kCache16Bits);
216            *dstC++ = cache[toggle + index];
217            toggle = next_dither_toggle16(toggle);
218
219            dstX += SK_Scalar1;
220        } while (--count != 0);
221    }
222}
223
224SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap,
225    SkMatrix* matrix, SkShader::TileMode* xy) const {
226    if (bitmap) {
227        this->getGradientTableBitmap(bitmap);
228    }
229    if (matrix) {
230        matrix->setScale(SkIntToScalar(kCache32Count),
231                         SkIntToScalar(kCache32Count));
232        matrix->preConcat(fPtsToUnit);
233    }
234    if (xy) {
235        xy[0] = fTileMode;
236        xy[1] = kClamp_TileMode;
237    }
238    return kRadial_BitmapType;
239}
240
241SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
242    if (info) {
243        commonAsAGradient(info);
244        info->fPoint[0] = fCenter;
245        info->fRadius[0] = fRadius;
246    }
247    return kRadial_GradientType;
248}
249
250SkRadialGradient::SkRadialGradient(SkFlattenableReadBuffer& buffer)
251    : INHERITED(buffer),
252      fCenter(buffer.readPoint()),
253      fRadius(buffer.readScalar()) {
254}
255
256void SkRadialGradient::flatten(SkFlattenableWriteBuffer& buffer) const {
257    this->INHERITED::flatten(buffer);
258    buffer.writePoint(fCenter);
259    buffer.writeScalar(fRadius);
260}
261
262namespace {
263
264inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
265    // fast, overly-conservative test: checks unit square instead
266    // of unit circle
267    bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
268                    (fx <= -SK_FixedHalf && dx <= 0);
269    bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
270                    (fy <= -SK_FixedHalf && dy <= 0);
271
272    return xClamped || yClamped;
273}
274
275// Return true if (fx * fy) is always inside the unit circle
276// SkPin32 is expensive, but so are all the SkFixedMul in this test,
277// so it shouldn't be run if count is small.
278inline bool no_need_for_radial_pin(int fx, int dx,
279                                          int fy, int dy, int count) {
280    SkASSERT(count > 0);
281    if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
282        return false;
283    }
284    if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
285        return false;
286    }
287    fx += (count - 1) * dx;
288    fy += (count - 1) * dy;
289    if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
290        return false;
291    }
292    return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
293}
294
295#define UNPINNED_RADIAL_STEP \
296    fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
297    *dstC++ = cache[toggle + \
298                    (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
299    toggle = next_dither_toggle(toggle); \
300    fx += dx; \
301    fy += dy;
302
303typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
304        SkScalar sfy, SkScalar sdy,
305        SkPMColor* dstC, const SkPMColor* cache,
306        int count, int toggle);
307
308// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
309void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
310        SkScalar sfy, SkScalar sdy,
311        SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
312        int count, int toggle) {
313    // Floating point seems to be slower than fixed point,
314    // even when we have float hardware.
315    const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
316    SkFixed fx = SkScalarToFixed(sfx) >> 1;
317    SkFixed dx = SkScalarToFixed(sdx) >> 1;
318    SkFixed fy = SkScalarToFixed(sfy) >> 1;
319    SkFixed dy = SkScalarToFixed(sdy) >> 1;
320    if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
321        unsigned fi = SkGradientShaderBase::kCache32Count - 1;
322        sk_memset32_dither(dstC,
323            cache[toggle + fi],
324            cache[next_dither_toggle(toggle) + fi],
325            count);
326    } else if ((count > 4) &&
327               no_need_for_radial_pin(fx, dx, fy, dy, count)) {
328        unsigned fi;
329        // 4x unroll appears to be no faster than 2x unroll on Linux
330        while (count > 1) {
331            UNPINNED_RADIAL_STEP;
332            UNPINNED_RADIAL_STEP;
333            count -= 2;
334        }
335        if (count) {
336            UNPINNED_RADIAL_STEP;
337        }
338    } else  {
339        // Specializing for dy == 0 gains us 25% on Skia benchmarks
340        if (dy == 0) {
341            unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
342            yy *= yy;
343            do {
344                unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
345                unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
346                fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
347                *dstC++ = cache[toggle + (sqrt_table[fi] >>
348                    SkGradientShaderBase::kSqrt32Shift)];
349                toggle = next_dither_toggle(toggle);
350                fx += dx;
351            } while (--count != 0);
352        } else {
353            do {
354                unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
355                unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
356                fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
357                fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
358                *dstC++ = cache[toggle + (sqrt_table[fi] >>
359                    SkGradientShaderBase::kSqrt32Shift)];
360                toggle = next_dither_toggle(toggle);
361                fx += dx;
362                fy += dy;
363            } while (--count != 0);
364        }
365    }
366}
367
368// Unrolling this loop doesn't seem to help (when float); we're stalling to
369// get the results of the sqrt (?), and don't have enough extra registers to
370// have many in flight.
371void shadeSpan_radial_mirror(SkScalar sfx, SkScalar sdx,
372        SkScalar sfy, SkScalar sdy,
373        SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
374        int count, int toggle) {
375    do {
376#ifdef SK_SCALAR_IS_FLOAT
377        float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
378        SkFixed dist = SkFloatToFixed(fdist);
379#else
380        SkFixed magnitudeSquared = SkFixedSquare(sfx) +
381            SkFixedSquare(sfy);
382        if (magnitudeSquared < 0) // Overflow.
383            magnitudeSquared = SK_FixedMax;
384        SkFixed dist = SkFixedSqrt(magnitudeSquared);
385#endif
386        unsigned fi = mirror_tileproc(dist);
387        SkASSERT(fi <= 0xFFFF);
388        *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
389        toggle = next_dither_toggle(toggle);
390        sfx += sdx;
391        sfy += sdy;
392    } while (--count != 0);
393}
394
395void shadeSpan_radial_repeat(SkScalar sfx, SkScalar sdx,
396        SkScalar sfy, SkScalar sdy,
397        SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
398        int count, int toggle) {
399    SkFixed fx = SkScalarToFixed(sfx);
400    SkFixed dx = SkScalarToFixed(sdx);
401    SkFixed fy = SkScalarToFixed(sfy);
402    SkFixed dy = SkScalarToFixed(sdy);
403    do {
404        SkFixed magnitudeSquared = SkFixedSquare(fx) +
405            SkFixedSquare(fy);
406        if (magnitudeSquared < 0) // Overflow.
407            magnitudeSquared = SK_FixedMax;
408        SkFixed dist = SkFixedSqrt(magnitudeSquared);
409        unsigned fi = repeat_tileproc(dist);
410        SkASSERT(fi <= 0xFFFF);
411        *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
412        toggle = next_dither_toggle(toggle);
413        fx += dx;
414        fy += dy;
415    } while (--count != 0);
416}
417}
418
419void SkRadialGradient::shadeSpan(int x, int y,
420                                SkPMColor* SK_RESTRICT dstC, int count) {
421    SkASSERT(count > 0);
422
423    SkPoint             srcPt;
424    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
425    TileProc            proc = fTileProc;
426    const SkPMColor* SK_RESTRICT cache = this->getCache32();
427    int toggle = init_dither_toggle(x, y);
428
429    if (fDstToIndexClass != kPerspective_MatrixClass) {
430        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
431                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
432        SkScalar sdx = fDstToIndex.getScaleX();
433        SkScalar sdy = fDstToIndex.getSkewY();
434
435        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
436            SkFixed storage[2];
437            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
438                                           &storage[0], &storage[1]);
439            sdx = SkFixedToScalar(storage[0]);
440            sdy = SkFixedToScalar(storage[1]);
441        } else {
442            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
443        }
444
445        RadialShadeProc shadeProc = shadeSpan_radial_repeat;
446        if (SkShader::kClamp_TileMode == fTileMode) {
447            shadeProc = shadeSpan_radial_clamp;
448        } else if (SkShader::kMirror_TileMode == fTileMode) {
449            shadeProc = shadeSpan_radial_mirror;
450        } else {
451            SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
452        }
453        (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
454    } else {    // perspective case
455        SkScalar dstX = SkIntToScalar(x);
456        SkScalar dstY = SkIntToScalar(y);
457        do {
458            dstProc(fDstToIndex, dstX, dstY, &srcPt);
459            unsigned fi = proc(SkScalarToFixed(srcPt.length()));
460            SkASSERT(fi <= 0xFFFF);
461            *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
462            dstX += SK_Scalar1;
463        } while (--count != 0);
464    }
465}
466
467/////////////////////////////////////////////////////////////////////
468
469#if SK_SUPPORT_GPU
470
471#include "GrTBackendEffectFactory.h"
472
473class GrGLRadialGradient : public GrGLGradientEffect {
474public:
475
476    GrGLRadialGradient(const GrBackendEffectFactory& factory,
477                       const GrDrawEffect&) : INHERITED (factory) { }
478    virtual ~GrGLRadialGradient() { }
479
480    virtual void emitCode(GrGLShaderBuilder*,
481                          const GrDrawEffect&,
482                          EffectKey,
483                          const char* outputColor,
484                          const char* inputColor,
485                          const TextureSamplerArray&) SK_OVERRIDE;
486
487    static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
488        return GenMatrixKey(drawEffect);
489    }
490
491private:
492
493    typedef GrGLGradientEffect INHERITED;
494
495};
496
497/////////////////////////////////////////////////////////////////////
498
499class GrRadialGradient : public GrGradientEffect {
500public:
501    static GrEffectRef* Create(GrContext* ctx,
502                               const SkRadialGradient& shader,
503                               const SkMatrix& matrix,
504                               SkShader::TileMode tm) {
505        AutoEffectUnref effect(SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm)));
506        return CreateEffectRef(effect);
507    }
508
509    virtual ~GrRadialGradient() { }
510
511    static const char* Name() { return "Radial Gradient"; }
512    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
513        return GrTBackendEffectFactory<GrRadialGradient>::getInstance();
514    }
515
516    typedef GrGLRadialGradient GLEffect;
517
518private:
519    GrRadialGradient(GrContext* ctx,
520                     const SkRadialGradient& shader,
521                     const SkMatrix& matrix,
522                     SkShader::TileMode tm)
523        : INHERITED(ctx, shader, matrix, tm) {
524    }
525
526    GR_DECLARE_EFFECT_TEST;
527
528    typedef GrGradientEffect INHERITED;
529};
530
531/////////////////////////////////////////////////////////////////////
532
533GR_DEFINE_EFFECT_TEST(GrRadialGradient);
534
535GrEffectRef* GrRadialGradient::TestCreate(SkMWCRandom* random,
536                                          GrContext* context,
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
555void 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
572GrEffectRef* 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
585GrEffectRef* 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
593void 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