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