SkRadialGradient.cpp revision 66e534da8e2b3de928f7ce132da61947a73ab7cb
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 ^= SkGradientShaderBase::kDitherStride16;
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 ^= SkGradientShaderBase::kDitherStride16;
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 ^= SkGradientShaderBase::kDitherStride16;
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 ^= SkGradientShaderBase::kDitherStride16;
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 = ((x ^ y) & 1) * kDitherStride16;
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 ^= kDitherStride16;
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(kGradient32Length),
231                         SkIntToScalar(kGradient32Length));
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 ^= SkGradientShaderBase::kDitherStride32; \
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::kGradient32Length;
322        sk_memset32_dither(dstC,
323            cache[toggle + fi],
324            cache[(toggle ^ SkGradientShaderBase::kDitherStride32) + 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    }
339    else  {
340        // Specializing for dy == 0 gains us 25% on Skia benchmarks
341        if (dy == 0) {
342            unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
343            yy *= yy;
344            do {
345                unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
346                unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
347                fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
348                *dstC++ = cache[toggle + (sqrt_table[fi] >>
349                    SkGradientShaderBase::kSqrt32Shift)];
350                toggle ^= SkGradientShaderBase::kDitherStride32;
351                fx += dx;
352            } while (--count != 0);
353        } else {
354            do {
355                unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
356                unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
357                fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
358                fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
359                *dstC++ = cache[toggle + (sqrt_table[fi] >>
360                    SkGradientShaderBase::kSqrt32Shift)];
361                toggle ^= SkGradientShaderBase::kDitherStride32;
362                fx += dx;
363                fy += dy;
364            } while (--count != 0);
365        }
366    }
367}
368
369// Unrolling this loop doesn't seem to help (when float); we're stalling to
370// get the results of the sqrt (?), and don't have enough extra registers to
371// have many in flight.
372void shadeSpan_radial_mirror(SkScalar sfx, SkScalar sdx,
373        SkScalar sfy, SkScalar sdy,
374        SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
375        int count, int toggle) {
376    do {
377#ifdef SK_SCALAR_IS_FLOAT
378        float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
379        SkFixed dist = SkFloatToFixed(fdist);
380#else
381        SkFixed magnitudeSquared = SkFixedSquare(sfx) +
382            SkFixedSquare(sfy);
383        if (magnitudeSquared < 0) // Overflow.
384            magnitudeSquared = SK_FixedMax;
385        SkFixed dist = SkFixedSqrt(magnitudeSquared);
386#endif
387        unsigned fi = mirror_tileproc(dist);
388        SkASSERT(fi <= 0xFFFF);
389        *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
390        toggle ^= SkGradientShaderBase::kDitherStride32;
391        sfx += sdx;
392        sfy += sdy;
393    } while (--count != 0);
394}
395
396void shadeSpan_radial_repeat(SkScalar sfx, SkScalar sdx,
397        SkScalar sfy, SkScalar sdy,
398        SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
399        int count, int toggle) {
400    SkFixed fx = SkScalarToFixed(sfx);
401    SkFixed dx = SkScalarToFixed(sdx);
402    SkFixed fy = SkScalarToFixed(sfy);
403    SkFixed dy = SkScalarToFixed(sdy);
404    do {
405        SkFixed magnitudeSquared = SkFixedSquare(fx) +
406            SkFixedSquare(fy);
407        if (magnitudeSquared < 0) // Overflow.
408            magnitudeSquared = SK_FixedMax;
409        SkFixed dist = SkFixedSqrt(magnitudeSquared);
410        unsigned fi = repeat_tileproc(dist);
411        SkASSERT(fi <= 0xFFFF);
412        *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
413        toggle ^= SkGradientShaderBase::kDitherStride32;
414        fx += dx;
415        fy += dy;
416    } while (--count != 0);
417}
418}
419
420void SkRadialGradient::shadeSpan(int x, int y,
421                                SkPMColor* SK_RESTRICT dstC, int count) {
422    SkASSERT(count > 0);
423
424    SkPoint             srcPt;
425    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
426    TileProc            proc = fTileProc;
427    const SkPMColor* SK_RESTRICT cache = this->getCache32();
428#ifdef USE_DITHER_32BIT_GRADIENT
429    int toggle = ((x ^ y) & 1) * SkGradientShaderBase::kDitherStride32;
430#else
431    int toggle = 0;
432#endif
433
434    if (fDstToIndexClass != kPerspective_MatrixClass) {
435        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
436                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
437        SkScalar sdx = fDstToIndex.getScaleX();
438        SkScalar sdy = fDstToIndex.getSkewY();
439
440        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
441            SkFixed storage[2];
442            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
443                                           &storage[0], &storage[1]);
444            sdx = SkFixedToScalar(storage[0]);
445            sdy = SkFixedToScalar(storage[1]);
446        } else {
447            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
448        }
449
450        RadialShadeProc shadeProc = shadeSpan_radial_repeat;
451        if (SkShader::kClamp_TileMode == fTileMode) {
452            shadeProc = shadeSpan_radial_clamp;
453        } else if (SkShader::kMirror_TileMode == fTileMode) {
454            shadeProc = shadeSpan_radial_mirror;
455        } else {
456            SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
457        }
458        (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
459    } else {    // perspective case
460        SkScalar dstX = SkIntToScalar(x);
461        SkScalar dstY = SkIntToScalar(y);
462        do {
463            dstProc(fDstToIndex, dstX, dstY, &srcPt);
464            unsigned fi = proc(SkScalarToFixed(srcPt.length()));
465            SkASSERT(fi <= 0xFFFF);
466            *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
467            dstX += SK_Scalar1;
468        } while (--count != 0);
469    }
470}
471
472/////////////////////////////////////////////////////////////////////
473
474#if SK_SUPPORT_GPU
475
476#include "GrTBackendEffectFactory.h"
477
478class GrGLRadialGradient : public GrGLGradientEffect {
479public:
480
481    GrGLRadialGradient(const GrBackendEffectFactory& factory,
482                       const GrEffect&) : INHERITED (factory) { }
483    virtual ~GrGLRadialGradient() { }
484
485    virtual void emitCode(GrGLShaderBuilder*,
486                          const GrEffectStage&,
487                          EffectKey,
488                          const char* vertexCoords,
489                          const char* outputColor,
490                          const char* inputColor,
491                          const TextureSamplerArray&) SK_OVERRIDE;
492
493    static EffectKey GenKey(const GrEffectStage&, const GrGLCaps& caps) { return 0; }
494
495private:
496
497    typedef GrGLGradientEffect INHERITED;
498
499};
500
501/////////////////////////////////////////////////////////////////////
502
503class GrRadialGradient : public GrGradientEffect {
504public:
505
506    GrRadialGradient(GrContext* ctx, const SkRadialGradient& shader, SkShader::TileMode tm)
507        : INHERITED(ctx, shader, tm) {
508    }
509
510    virtual ~GrRadialGradient() { }
511
512    static const char* Name() { return "Radial Gradient"; }
513    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
514        return GrTBackendEffectFactory<GrRadialGradient>::getInstance();
515    }
516
517    typedef GrGLRadialGradient GLEffect;
518
519private:
520    GR_DECLARE_EFFECT_TEST;
521
522    typedef GrGradientEffect INHERITED;
523};
524
525/////////////////////////////////////////////////////////////////////
526
527GR_DEFINE_EFFECT_TEST(GrRadialGradient);
528
529GrEffect* GrRadialGradient::TestCreate(SkRandom* random,
530                                       GrContext* context,
531                                       GrTexture**) {
532    SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
533    SkScalar radius = random->nextUScalar1();
534
535    SkColor colors[kMaxRandomGradientColors];
536    SkScalar stopsArray[kMaxRandomGradientColors];
537    SkScalar* stops = stopsArray;
538    SkShader::TileMode tm;
539    int colorCount = RandomGradientParams(random, colors, &stops, &tm);
540    SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
541                                                                 colors, stops, colorCount,
542                                                                 tm));
543    GrEffectStage stage;
544    shader->asNewEffect(context, &stage);
545    GrAssert(NULL != stage.getEffect());
546    // const_cast and ref is a hack! Will remove when asNewEffect returns GrEffect*
547    stage.getEffect()->ref();
548    return const_cast<GrEffect*>(stage.getEffect());
549}
550
551/////////////////////////////////////////////////////////////////////
552
553void GrGLRadialGradient::emitCode(GrGLShaderBuilder* builder,
554                                  const GrEffectStage&,
555                                  EffectKey,
556                                  const char* vertexCoords,
557                                  const char* outputColor,
558                                  const char* inputColor,
559                                  const TextureSamplerArray& samplers) {
560    this->emitYCoordUniform(builder);
561    SkString t;
562    t.printf("length(%s.xy)", builder->defaultTexCoordsName());
563    this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
564}
565
566/////////////////////////////////////////////////////////////////////
567
568bool SkRadialGradient::asNewEffect(GrContext* context, GrEffectStage* stage) const {
569    SkASSERT(NULL != context && NULL != stage);
570
571    SkMatrix matrix;
572    if (!this->getLocalMatrix().invert(&matrix)) {
573        return false;
574    }
575    matrix.postConcat(fPtsToUnit);
576    stage->setEffect(SkNEW_ARGS(GrRadialGradient, (context, *this, fTileMode)), matrix)->unref();
577    return true;
578}
579
580#else
581
582bool SkRadialGradient::asNewEffect(GrContext*, GrEffectStage*) const {
583    SkDEBUGFAIL("Should not call in GPU-less build");
584    return false;
585}
586
587#endif
588