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