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