5ea2e72a9de78ce7a4d5333c43976c025cc4bba0
[platform/upstream/libSkiaSharp.git] / 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 SK_COMPILE_ASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE, SqrtTableSizesMatch);
16
17 #if 0
18
19 #include <stdio.h>
20
21 void SkRadialGradient_BuildTable() {
22     // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
23
24     FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
25     SkASSERT(file);
26     ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
27
28     for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
29         if ((i & 15) == 0) {
30             ::fprintf(file, "\t");
31         }
32
33         uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
34
35         ::fprintf(file, "0x%02X", value);
36         if (i < kSQRT_TABLE_SIZE-1) {
37             ::fprintf(file, ", ");
38         }
39         if ((i & 15) == 15) {
40             ::fprintf(file, "\n");
41         }
42     }
43     ::fprintf(file, "};\n");
44     ::fclose(file);
45 }
46
47 #endif
48
49 namespace {
50
51 // GCC doesn't like using static functions as template arguments.  So force these to be non-static.
52 inline SkFixed mirror_tileproc_nonstatic(SkFixed x) {
53     return mirror_tileproc(x);
54 }
55
56 inline SkFixed repeat_tileproc_nonstatic(SkFixed x) {
57     return repeat_tileproc(x);
58 }
59
60 SkMatrix rad_to_unit_matrix(const SkPoint& center, SkScalar radius) {
61     SkScalar    inv = SkScalarInvert(radius);
62
63     SkMatrix matrix;
64     matrix.setTranslate(-center.fX, -center.fY);
65     matrix.postScale(inv, inv);
66     return matrix;
67 }
68
69 typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx,
70         SkScalar sfy, SkScalar sdy,
71         uint16_t* dstC, const uint16_t* cache,
72         int toggle, int count);
73
74 void shadeSpan16_radial_clamp(SkScalar sfx, SkScalar sdx,
75         SkScalar sfy, SkScalar sdy,
76         uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
77         int toggle, int count) {
78     const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
79
80     /* knock these down so we can pin against +- 0x7FFF, which is an
81        immediate load, rather than 0xFFFF which is slower. This is a
82        compromise, since it reduces our precision, but that appears
83        to be visually OK. If we decide this is OK for all of our cases,
84        we could (it seems) put this scale-down into fDstToIndex,
85        to avoid having to do these extra shifts each time.
86     */
87     SkFixed fx = SkScalarToFixed(sfx) >> 1;
88     SkFixed dx = SkScalarToFixed(sdx) >> 1;
89     SkFixed fy = SkScalarToFixed(sfy) >> 1;
90     SkFixed dy = SkScalarToFixed(sdy) >> 1;
91     // might perform this check for the other modes,
92     // but the win will be a smaller % of the total
93     if (dy == 0) {
94         fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
95         fy *= fy;
96         do {
97             unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
98             unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
99             fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
100             fx += dx;
101             *dstC++ = cache[toggle +
102                             (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
103             toggle = next_dither_toggle16(toggle);
104         } while (--count != 0);
105     } else {
106         do {
107             unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
108             unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
109             fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
110             fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
111             fx += dx;
112             fy += dy;
113             *dstC++ = cache[toggle +
114                             (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
115             toggle = next_dither_toggle16(toggle);
116         } while (--count != 0);
117     }
118 }
119
120 template <SkFixed (*TileProc)(SkFixed)>
121 void shadeSpan16_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
122                         uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
123                         int toggle, int count) {
124     do {
125         const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
126         const unsigned fi = TileProc(dist);
127         SkASSERT(fi <= 0xFFFF);
128         *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)];
129         toggle = next_dither_toggle16(toggle);
130         fx += dx;
131         fy += dy;
132     } while (--count != 0);
133 }
134
135 void shadeSpan16_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
136                                uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
137                                int toggle, int count) {
138     shadeSpan16_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, toggle, count);
139 }
140
141 void shadeSpan16_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
142                                uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
143                                int toggle, int count) {
144     shadeSpan16_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, toggle, count);
145 }
146
147 }  // namespace
148
149 /////////////////////////////////////////////////////////////////////
150
151 SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor& desc)
152     : SkGradientShaderBase(desc, rad_to_unit_matrix(center, radius))
153     , fCenter(center)
154     , fRadius(radius) {
155 }
156
157 size_t SkRadialGradient::contextSize() const {
158     return sizeof(RadialGradientContext);
159 }
160
161 SkShader::Context* SkRadialGradient::onCreateContext(const ContextRec& rec, void* storage) const {
162     return SkNEW_PLACEMENT_ARGS(storage, RadialGradientContext, (*this, rec));
163 }
164
165 SkRadialGradient::RadialGradientContext::RadialGradientContext(
166         const SkRadialGradient& shader, const ContextRec& rec)
167     : INHERITED(shader, rec) {}
168
169 void SkRadialGradient::RadialGradientContext::shadeSpan16(int x, int y, uint16_t* dstCParam,
170                                                           int count) {
171     SkASSERT(count > 0);
172
173     const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
174
175     uint16_t* SK_RESTRICT dstC = dstCParam;
176
177     SkPoint             srcPt;
178     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
179     TileProc            proc = radialGradient.fTileProc;
180     const uint16_t* SK_RESTRICT cache = fCache->getCache16();
181     int                 toggle = init_dither_toggle16(x, y);
182
183     if (fDstToIndexClass != kPerspective_MatrixClass) {
184         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
185                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
186
187         SkScalar sdx = fDstToIndex.getScaleX();
188         SkScalar sdy = fDstToIndex.getSkewY();
189
190         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
191             SkFixed storage[2];
192             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
193                                            &storage[0], &storage[1]);
194             sdx = SkFixedToScalar(storage[0]);
195             sdy = SkFixedToScalar(storage[1]);
196         } else {
197             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
198         }
199
200         RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
201         if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
202             shadeProc = shadeSpan16_radial_clamp;
203         } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
204             shadeProc = shadeSpan16_radial_mirror;
205         } else {
206             SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
207         }
208         (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
209                      cache, toggle, count);
210     } else {    // perspective case
211         SkScalar dstX = SkIntToScalar(x);
212         SkScalar dstY = SkIntToScalar(y);
213         do {
214             dstProc(fDstToIndex, dstX, dstY, &srcPt);
215             unsigned fi = proc(SkScalarToFixed(srcPt.length()));
216             SkASSERT(fi <= 0xFFFF);
217
218             int index = fi >> (16 - kCache16Bits);
219             *dstC++ = cache[toggle + index];
220             toggle = next_dither_toggle16(toggle);
221
222             dstX += SK_Scalar1;
223         } while (--count != 0);
224     }
225 }
226
227 SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap,
228     SkMatrix* matrix, SkShader::TileMode* xy) const {
229     if (bitmap) {
230         this->getGradientTableBitmap(bitmap);
231     }
232     if (matrix) {
233         matrix->setScale(SkIntToScalar(kCache32Count),
234                          SkIntToScalar(kCache32Count));
235         matrix->preConcat(fPtsToUnit);
236     }
237     if (xy) {
238         xy[0] = fTileMode;
239         xy[1] = kClamp_TileMode;
240     }
241     return kRadial_BitmapType;
242 }
243
244 SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
245     if (info) {
246         commonAsAGradient(info);
247         info->fPoint[0] = fCenter;
248         info->fRadius[0] = fRadius;
249     }
250     return kRadial_GradientType;
251 }
252
253 SkFlattenable* SkRadialGradient::CreateProc(SkReadBuffer& buffer) {
254     DescriptorScope desc;
255     if (!desc.unflatten(buffer)) {
256         return NULL;
257     }
258     const SkPoint center = buffer.readPoint();
259     const SkScalar radius = buffer.readScalar();
260     return SkGradientShader::CreateRadial(center, radius, desc.fColors, desc.fPos, desc.fCount,
261                                           desc.fTileMode, desc.fGradFlags, desc.fLocalMatrix);
262 }
263
264 void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
265     this->INHERITED::flatten(buffer);
266     buffer.writePoint(fCenter);
267     buffer.writeScalar(fRadius);
268 }
269
270 namespace {
271
272 inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
273     // fast, overly-conservative test: checks unit square instead
274     // of unit circle
275     bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
276                     (fx <= -SK_FixedHalf && dx <= 0);
277     bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
278                     (fy <= -SK_FixedHalf && dy <= 0);
279
280     return xClamped || yClamped;
281 }
282
283 // Return true if (fx * fy) is always inside the unit circle
284 // SkPin32 is expensive, but so are all the SkFixedMul in this test,
285 // so it shouldn't be run if count is small.
286 inline bool no_need_for_radial_pin(int fx, int dx,
287                                           int fy, int dy, int count) {
288     SkASSERT(count > 0);
289     if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
290         return false;
291     }
292     if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
293         return false;
294     }
295     fx += (count - 1) * dx;
296     fy += (count - 1) * dy;
297     if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
298         return false;
299     }
300     return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
301 }
302
303 #define UNPINNED_RADIAL_STEP \
304     fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
305     *dstC++ = cache[toggle + \
306                     (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
307     toggle = next_dither_toggle(toggle); \
308     fx += dx; \
309     fy += dy;
310
311 typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
312         SkScalar sfy, SkScalar sdy,
313         SkPMColor* dstC, const SkPMColor* cache,
314         int count, int toggle);
315
316 // On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
317 void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
318         SkScalar sfy, SkScalar sdy,
319         SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
320         int count, int toggle) {
321     // Floating point seems to be slower than fixed point,
322     // even when we have float hardware.
323     const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
324     SkFixed fx = SkScalarToFixed(sfx) >> 1;
325     SkFixed dx = SkScalarToFixed(sdx) >> 1;
326     SkFixed fy = SkScalarToFixed(sfy) >> 1;
327     SkFixed dy = SkScalarToFixed(sdy) >> 1;
328     if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
329         unsigned fi = SkGradientShaderBase::kCache32Count - 1;
330         sk_memset32_dither(dstC,
331             cache[toggle + fi],
332             cache[next_dither_toggle(toggle) + fi],
333             count);
334     } else if ((count > 4) &&
335                no_need_for_radial_pin(fx, dx, fy, dy, count)) {
336         unsigned fi;
337         // 4x unroll appears to be no faster than 2x unroll on Linux
338         while (count > 1) {
339             UNPINNED_RADIAL_STEP;
340             UNPINNED_RADIAL_STEP;
341             count -= 2;
342         }
343         if (count) {
344             UNPINNED_RADIAL_STEP;
345         }
346     } else  {
347         // Specializing for dy == 0 gains us 25% on Skia benchmarks
348         if (dy == 0) {
349             unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
350             yy *= yy;
351             do {
352                 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
353                 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
354                 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
355                 *dstC++ = cache[toggle + (sqrt_table[fi] >>
356                     SkGradientShaderBase::kSqrt32Shift)];
357                 toggle = next_dither_toggle(toggle);
358                 fx += dx;
359             } while (--count != 0);
360         } else {
361             do {
362                 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
363                 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
364                 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
365                 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
366                 *dstC++ = cache[toggle + (sqrt_table[fi] >>
367                     SkGradientShaderBase::kSqrt32Shift)];
368                 toggle = next_dither_toggle(toggle);
369                 fx += dx;
370                 fy += dy;
371             } while (--count != 0);
372         }
373     }
374 }
375
376 // Unrolling this loop doesn't seem to help (when float); we're stalling to
377 // get the results of the sqrt (?), and don't have enough extra registers to
378 // have many in flight.
379 template <SkFixed (*TileProc)(SkFixed)>
380 void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
381                       SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
382                       int count, int toggle) {
383     do {
384         const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
385         const unsigned fi = TileProc(dist);
386         SkASSERT(fi <= 0xFFFF);
387         *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
388         toggle = next_dither_toggle(toggle);
389         fx += dx;
390         fy += dy;
391     } while (--count != 0);
392 }
393
394 void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
395                              SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
396                              int count, int toggle) {
397     shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
398 }
399
400 void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
401                              SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
402                              int count, int toggle) {
403     shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
404 }
405
406 }  // namespace
407
408 void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y,
409                                                         SkPMColor* SK_RESTRICT dstC, int count) {
410     SkASSERT(count > 0);
411
412     const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
413
414     SkPoint             srcPt;
415     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
416     TileProc            proc = radialGradient.fTileProc;
417     const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
418     int toggle = init_dither_toggle(x, y);
419
420     if (fDstToIndexClass != kPerspective_MatrixClass) {
421         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
422                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
423         SkScalar sdx = fDstToIndex.getScaleX();
424         SkScalar sdy = fDstToIndex.getSkewY();
425
426         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
427             SkFixed storage[2];
428             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
429                                            &storage[0], &storage[1]);
430             sdx = SkFixedToScalar(storage[0]);
431             sdy = SkFixedToScalar(storage[1]);
432         } else {
433             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
434         }
435
436         RadialShadeProc shadeProc = shadeSpan_radial_repeat;
437         if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
438             shadeProc = shadeSpan_radial_clamp;
439         } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
440             shadeProc = shadeSpan_radial_mirror;
441         } else {
442             SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
443         }
444         (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
445     } else {    // perspective case
446         SkScalar dstX = SkIntToScalar(x);
447         SkScalar dstY = SkIntToScalar(y);
448         do {
449             dstProc(fDstToIndex, dstX, dstY, &srcPt);
450             unsigned fi = proc(SkScalarToFixed(srcPt.length()));
451             SkASSERT(fi <= 0xFFFF);
452             *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
453             dstX += SK_Scalar1;
454         } while (--count != 0);
455     }
456 }
457
458 /////////////////////////////////////////////////////////////////////
459
460 #if SK_SUPPORT_GPU
461
462 #include "SkGr.h"
463 #include "gl/builders/GrGLProgramBuilder.h"
464
465 class GrGLRadialGradient : public GrGLGradientEffect {
466 public:
467
468     GrGLRadialGradient(const GrProcessor&) {}
469     virtual ~GrGLRadialGradient() { }
470
471     virtual void emitCode(GrGLFPBuilder*,
472                           const GrFragmentProcessor&,
473                           const char* outputColor,
474                           const char* inputColor,
475                           const TransformedCoordsArray&,
476                           const TextureSamplerArray&) SK_OVERRIDE;
477
478     static void GenKey(const GrProcessor& processor, const GrGLCaps&, GrProcessorKeyBuilder* b) {
479         b->add32(GenBaseGradientKey(processor));
480     }
481
482 private:
483
484     typedef GrGLGradientEffect INHERITED;
485
486 };
487
488 /////////////////////////////////////////////////////////////////////
489
490 class GrRadialGradient : public GrGradientEffect {
491 public:
492     static GrFragmentProcessor* 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     const char* name() const SK_OVERRIDE { return "Radial Gradient"; }
502
503     virtual void getGLProcessorKey(const GrGLCaps& caps,
504                                    GrProcessorKeyBuilder* b) const SK_OVERRIDE {
505         GrGLRadialGradient::GenKey(*this, caps, b);
506     }
507
508     GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE {
509         return SkNEW_ARGS(GrGLRadialGradient, (*this));
510     }
511
512 private:
513     GrRadialGradient(GrContext* ctx,
514                      const SkRadialGradient& shader,
515                      const SkMatrix& matrix,
516                      SkShader::TileMode tm)
517         : INHERITED(ctx, shader, matrix, tm) {
518         this->initClassID<GrRadialGradient>();
519     }
520
521     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
522
523     typedef GrGradientEffect INHERITED;
524 };
525
526 /////////////////////////////////////////////////////////////////////
527
528 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadialGradient);
529
530 GrFragmentProcessor* GrRadialGradient::TestCreate(SkRandom* random,
531                                                   GrContext* context,
532                                                   const GrDrawTargetCaps&,
533                                                   GrTexture**) {
534     SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
535     SkScalar radius = random->nextUScalar1();
536
537     SkColor colors[kMaxRandomGradientColors];
538     SkScalar stopsArray[kMaxRandomGradientColors];
539     SkScalar* stops = stopsArray;
540     SkShader::TileMode tm;
541     int colorCount = RandomGradientParams(random, colors, &stops, &tm);
542     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
543                                                                  colors, stops, colorCount,
544                                                                  tm));
545     SkPaint paint;
546     GrColor paintColor;
547     GrFragmentProcessor* fp;
548     SkAssertResult(shader->asFragmentProcessor(context, paint,
549                                                GrProcessorUnitTest::TestMatrix(random), NULL,
550                                                &paintColor, &fp));
551     return fp;
552 }
553
554 /////////////////////////////////////////////////////////////////////
555
556 void GrGLRadialGradient::emitCode(GrGLFPBuilder* builder,
557                                   const GrFragmentProcessor& fp,
558                                   const char* outputColor,
559                                   const char* inputColor,
560                                   const TransformedCoordsArray& coords,
561                                   const TextureSamplerArray& samplers) {
562     const GrRadialGradient& ge = fp.cast<GrRadialGradient>();
563     this->emitUniforms(builder, ge);
564     SkString t("length(");
565     t.append(builder->getFragmentShaderBuilder()->ensureFSCoords2D(coords, 0));
566     t.append(")");
567     this->emitColor(builder, ge, t.c_str(), outputColor, inputColor, samplers);
568 }
569
570 /////////////////////////////////////////////////////////////////////
571
572 bool SkRadialGradient::asFragmentProcessor(GrContext* context, const SkPaint& paint,
573                                            const SkMatrix& viewM,
574                                            const SkMatrix* localMatrix, GrColor* paintColor,
575                                            GrFragmentProcessor** fp) const {
576     SkASSERT(context);
577
578     SkMatrix matrix;
579     if (!this->getLocalMatrix().invert(&matrix)) {
580         return false;
581     }
582     if (localMatrix) {
583         SkMatrix inv;
584         if (!localMatrix->invert(&inv)) {
585             return false;
586         }
587         matrix.postConcat(inv);
588     }
589     matrix.postConcat(fPtsToUnit);
590
591     *paintColor = SkColor2GrColorJustAlpha(paint.getColor());
592     *fp = GrRadialGradient::Create(context, *this, matrix, fTileMode);
593
594     return true;
595 }
596
597 #else
598
599 bool SkRadialGradient::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix&,
600                                            const SkMatrix*, GrColor*,
601                                            GrFragmentProcessor**) const {
602     SkDEBUGFAIL("Should not call in GPU-less build");
603     return false;
604 }
605
606 #endif
607
608 #ifndef SK_IGNORE_TO_STRING
609 void SkRadialGradient::toString(SkString* str) const {
610     str->append("SkRadialGradient: (");
611
612     str->append("center: (");
613     str->appendScalar(fCenter.fX);
614     str->append(", ");
615     str->appendScalar(fCenter.fY);
616     str->append(") radius: ");
617     str->appendScalar(fRadius);
618     str->append(" ");
619
620     this->INHERITED::toString(str);
621
622     str->append(")");
623 }
624 #endif