Upstream version 10.39.225.0
[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 #ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
256 SkRadialGradient::SkRadialGradient(SkReadBuffer& buffer)
257     : INHERITED(buffer),
258       fCenter(buffer.readPoint()),
259       fRadius(buffer.readScalar()) {
260 }
261 #endif
262
263 SkFlattenable* SkRadialGradient::CreateProc(SkReadBuffer& buffer) {
264     DescriptorScope desc;
265     if (!desc.unflatten(buffer)) {
266         return NULL;
267     }
268     const SkPoint center = buffer.readPoint();
269     const SkScalar radius = buffer.readScalar();
270     return SkGradientShader::CreateRadial(center, radius, desc.fColors, desc.fPos, desc.fCount,
271                                           desc.fTileMode, desc.fGradFlags, desc.fLocalMatrix);
272 }
273
274 void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
275     this->INHERITED::flatten(buffer);
276     buffer.writePoint(fCenter);
277     buffer.writeScalar(fRadius);
278 }
279
280 namespace {
281
282 inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
283     // fast, overly-conservative test: checks unit square instead
284     // of unit circle
285     bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
286                     (fx <= -SK_FixedHalf && dx <= 0);
287     bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
288                     (fy <= -SK_FixedHalf && dy <= 0);
289
290     return xClamped || yClamped;
291 }
292
293 // Return true if (fx * fy) is always inside the unit circle
294 // SkPin32 is expensive, but so are all the SkFixedMul in this test,
295 // so it shouldn't be run if count is small.
296 inline bool no_need_for_radial_pin(int fx, int dx,
297                                           int fy, int dy, int count) {
298     SkASSERT(count > 0);
299     if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
300         return false;
301     }
302     if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
303         return false;
304     }
305     fx += (count - 1) * dx;
306     fy += (count - 1) * dy;
307     if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
308         return false;
309     }
310     return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
311 }
312
313 #define UNPINNED_RADIAL_STEP \
314     fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
315     *dstC++ = cache[toggle + \
316                     (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
317     toggle = next_dither_toggle(toggle); \
318     fx += dx; \
319     fy += dy;
320
321 typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
322         SkScalar sfy, SkScalar sdy,
323         SkPMColor* dstC, const SkPMColor* cache,
324         int count, int toggle);
325
326 // On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
327 void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
328         SkScalar sfy, SkScalar sdy,
329         SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
330         int count, int toggle) {
331     // Floating point seems to be slower than fixed point,
332     // even when we have float hardware.
333     const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
334     SkFixed fx = SkScalarToFixed(sfx) >> 1;
335     SkFixed dx = SkScalarToFixed(sdx) >> 1;
336     SkFixed fy = SkScalarToFixed(sfy) >> 1;
337     SkFixed dy = SkScalarToFixed(sdy) >> 1;
338     if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
339         unsigned fi = SkGradientShaderBase::kCache32Count - 1;
340         sk_memset32_dither(dstC,
341             cache[toggle + fi],
342             cache[next_dither_toggle(toggle) + fi],
343             count);
344     } else if ((count > 4) &&
345                no_need_for_radial_pin(fx, dx, fy, dy, count)) {
346         unsigned fi;
347         // 4x unroll appears to be no faster than 2x unroll on Linux
348         while (count > 1) {
349             UNPINNED_RADIAL_STEP;
350             UNPINNED_RADIAL_STEP;
351             count -= 2;
352         }
353         if (count) {
354             UNPINNED_RADIAL_STEP;
355         }
356     } else  {
357         // Specializing for dy == 0 gains us 25% on Skia benchmarks
358         if (dy == 0) {
359             unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
360             yy *= yy;
361             do {
362                 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
363                 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
364                 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
365                 *dstC++ = cache[toggle + (sqrt_table[fi] >>
366                     SkGradientShaderBase::kSqrt32Shift)];
367                 toggle = next_dither_toggle(toggle);
368                 fx += dx;
369             } while (--count != 0);
370         } else {
371             do {
372                 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
373                 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
374                 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
375                 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
376                 *dstC++ = cache[toggle + (sqrt_table[fi] >>
377                     SkGradientShaderBase::kSqrt32Shift)];
378                 toggle = next_dither_toggle(toggle);
379                 fx += dx;
380                 fy += dy;
381             } while (--count != 0);
382         }
383     }
384 }
385
386 // Unrolling this loop doesn't seem to help (when float); we're stalling to
387 // get the results of the sqrt (?), and don't have enough extra registers to
388 // have many in flight.
389 template <SkFixed (*TileProc)(SkFixed)>
390 void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
391                       SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
392                       int count, int toggle) {
393     do {
394         const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
395         const unsigned fi = TileProc(dist);
396         SkASSERT(fi <= 0xFFFF);
397         *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
398         toggle = next_dither_toggle(toggle);
399         fx += dx;
400         fy += dy;
401     } while (--count != 0);
402 }
403
404 void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
405                              SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
406                              int count, int toggle) {
407     shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
408 }
409
410 void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
411                              SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
412                              int count, int toggle) {
413     shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
414 }
415
416 }  // namespace
417
418 void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y,
419                                                         SkPMColor* SK_RESTRICT dstC, int count) {
420     SkASSERT(count > 0);
421
422     const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
423
424     SkPoint             srcPt;
425     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
426     TileProc            proc = radialGradient.fTileProc;
427     const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
428     int toggle = init_dither_toggle(x, y);
429
430     if (fDstToIndexClass != kPerspective_MatrixClass) {
431         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
432                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
433         SkScalar sdx = fDstToIndex.getScaleX();
434         SkScalar sdy = fDstToIndex.getSkewY();
435
436         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
437             SkFixed storage[2];
438             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
439                                            &storage[0], &storage[1]);
440             sdx = SkFixedToScalar(storage[0]);
441             sdy = SkFixedToScalar(storage[1]);
442         } else {
443             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
444         }
445
446         RadialShadeProc shadeProc = shadeSpan_radial_repeat;
447         if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
448             shadeProc = shadeSpan_radial_clamp;
449         } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
450             shadeProc = shadeSpan_radial_mirror;
451         } else {
452             SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
453         }
454         (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
455     } else {    // perspective case
456         SkScalar dstX = SkIntToScalar(x);
457         SkScalar dstY = SkIntToScalar(y);
458         do {
459             dstProc(fDstToIndex, dstX, dstY, &srcPt);
460             unsigned fi = proc(SkScalarToFixed(srcPt.length()));
461             SkASSERT(fi <= 0xFFFF);
462             *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
463             dstX += SK_Scalar1;
464         } while (--count != 0);
465     }
466 }
467
468 /////////////////////////////////////////////////////////////////////
469
470 #if SK_SUPPORT_GPU
471
472 #include "GrTBackendProcessorFactory.h"
473 #include "gl/builders/GrGLProgramBuilder.h"
474 #include "SkGr.h"
475
476 class GrGLRadialGradient : public GrGLGradientEffect {
477 public:
478
479     GrGLRadialGradient(const GrBackendProcessorFactory& factory,
480                        const GrProcessor&) : INHERITED (factory) { }
481     virtual ~GrGLRadialGradient() { }
482
483     virtual void emitCode(GrGLProgramBuilder*,
484                           const GrFragmentProcessor&,
485                           const GrProcessorKey&,
486                           const char* outputColor,
487                           const char* inputColor,
488                           const TransformedCoordsArray&,
489                           const TextureSamplerArray&) SK_OVERRIDE;
490
491     static void GenKey(const GrProcessor& processor, const GrGLCaps&, GrProcessorKeyBuilder* b) {
492         b->add32(GenBaseGradientKey(processor));
493     }
494
495 private:
496
497     typedef GrGLGradientEffect INHERITED;
498
499 };
500
501 /////////////////////////////////////////////////////////////////////
502
503 class GrRadialGradient : public GrGradientEffect {
504 public:
505     static GrFragmentProcessor* Create(GrContext* ctx,
506                                        const SkRadialGradient& shader,
507                                        const SkMatrix& matrix,
508                                        SkShader::TileMode tm) {
509         return SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm));
510     }
511
512     virtual ~GrRadialGradient() { }
513
514     static const char* Name() { return "Radial Gradient"; }
515     virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE {
516         return GrTBackendFragmentProcessorFactory<GrRadialGradient>::getInstance();
517     }
518
519     typedef GrGLRadialGradient GLProcessor;
520
521 private:
522     GrRadialGradient(GrContext* ctx,
523                      const SkRadialGradient& shader,
524                      const SkMatrix& matrix,
525                      SkShader::TileMode tm)
526         : INHERITED(ctx, shader, matrix, tm) {
527     }
528
529     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
530
531     typedef GrGradientEffect INHERITED;
532 };
533
534 /////////////////////////////////////////////////////////////////////
535
536 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadialGradient);
537
538 GrFragmentProcessor* GrRadialGradient::TestCreate(SkRandom* random,
539                                                   GrContext* context,
540                                                   const GrDrawTargetCaps&,
541                                                   GrTexture**) {
542     SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
543     SkScalar radius = random->nextUScalar1();
544
545     SkColor colors[kMaxRandomGradientColors];
546     SkScalar stopsArray[kMaxRandomGradientColors];
547     SkScalar* stops = stopsArray;
548     SkShader::TileMode tm;
549     int colorCount = RandomGradientParams(random, colors, &stops, &tm);
550     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
551                                                                  colors, stops, colorCount,
552                                                                  tm));
553     SkPaint paint;
554     GrColor paintColor;
555     GrFragmentProcessor* fp;
556     SkAssertResult(shader->asFragmentProcessor(context, paint, NULL, &paintColor, &fp));
557     return fp;
558 }
559
560 /////////////////////////////////////////////////////////////////////
561
562 void GrGLRadialGradient::emitCode(GrGLProgramBuilder* builder,
563                                   const GrFragmentProcessor&,
564                                   const GrProcessorKey& key,
565                                   const char* outputColor,
566                                   const char* inputColor,
567                                   const TransformedCoordsArray& coords,
568                                   const TextureSamplerArray& samplers) {
569     uint32_t baseKey = key.get32(0);
570     this->emitUniforms(builder, baseKey);
571     SkString t("length(");
572     t.append(builder->getFragmentShaderBuilder()->ensureFSCoords2D(coords, 0));
573     t.append(")");
574     this->emitColor(builder, t.c_str(), baseKey, outputColor, inputColor, samplers);
575 }
576
577 /////////////////////////////////////////////////////////////////////
578
579 bool SkRadialGradient::asFragmentProcessor(GrContext* context, const SkPaint& paint,
580                                            const SkMatrix* localMatrix, GrColor* paintColor,
581                                            GrFragmentProcessor** fp) const {
582     SkASSERT(context);
583     
584     SkMatrix matrix;
585     if (!this->getLocalMatrix().invert(&matrix)) {
586         return false;
587     }
588     if (localMatrix) {
589         SkMatrix inv;
590         if (!localMatrix->invert(&inv)) {
591             return false;
592         }
593         matrix.postConcat(inv);
594     }
595     matrix.postConcat(fPtsToUnit);
596     
597     *paintColor = SkColor2GrColorJustAlpha(paint.getColor());
598     *fp = GrRadialGradient::Create(context, *this, matrix, fTileMode);
599     
600     return true;
601 }
602
603 #else
604
605 bool SkRadialGradient::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix*, GrColor*,
606                                            GrFragmentProcessor**) const {
607     SkDEBUGFAIL("Should not call in GPU-less build");
608     return false;
609 }
610
611 #endif
612
613 #ifndef SK_IGNORE_TO_STRING
614 void SkRadialGradient::toString(SkString* str) const {
615     str->append("SkRadialGradient: (");
616
617     str->append("center: (");
618     str->appendScalar(fCenter.fX);
619     str->append(", ");
620     str->appendScalar(fCenter.fY);
621     str->append(") radius: ");
622     str->appendScalar(fRadius);
623     str->append(" ");
624
625     this->INHERITED::toString(str);
626
627     str->append(")");
628 }
629 #endif