blob: 9f3230358e1dce369a1bb2145d7363dd75dda458 [file] [log] [blame]
rileya@google.com589708b2012-07-26 20:04:23 +00001
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 defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
16
17#include <stdio.h>
18
19void 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
47namespace {
48
49void rad_to_unit_matrix(const SkPoint& center, SkScalar radius,
50 SkMatrix* matrix) {
51 SkScalar inv = SkScalarInvert(radius);
52
53 matrix->setTranslate(-center.fX, -center.fY);
54 matrix->postScale(inv, inv);
55}
56
57typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx,
58 SkScalar sfy, SkScalar sdy,
59 uint16_t* dstC, const uint16_t* cache,
60 int toggle, int count);
61
62void shadeSpan16_radial_clamp(SkScalar sfx, SkScalar sdx,
63 SkScalar sfy, SkScalar sdy,
64 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
65 int toggle, int count) {
66 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
67
68 /* knock these down so we can pin against +- 0x7FFF, which is an
69 immediate load, rather than 0xFFFF which is slower. This is a
70 compromise, since it reduces our precision, but that appears
71 to be visually OK. If we decide this is OK for all of our cases,
72 we could (it seems) put this scale-down into fDstToIndex,
73 to avoid having to do these extra shifts each time.
74 */
75 SkFixed fx = SkScalarToFixed(sfx) >> 1;
76 SkFixed dx = SkScalarToFixed(sdx) >> 1;
77 SkFixed fy = SkScalarToFixed(sfy) >> 1;
78 SkFixed dy = SkScalarToFixed(sdy) >> 1;
79 // might perform this check for the other modes,
80 // but the win will be a smaller % of the total
81 if (dy == 0) {
82 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
83 fy *= fy;
84 do {
85 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
86 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
87 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
88 fx += dx;
89 *dstC++ = cache[toggle +
90 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
reed@google.com55853db2013-02-01 19:34:59 +000091 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +000092 } while (--count != 0);
93 } else {
94 do {
95 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
96 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
97 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
98 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
99 fx += dx;
100 fy += dy;
101 *dstC++ = cache[toggle +
102 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000103 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000104 } while (--count != 0);
105 }
106}
107
108void shadeSpan16_radial_mirror(SkScalar sfx, SkScalar sdx,
109 SkScalar sfy, SkScalar sdy,
110 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
111 int toggle, int count) {
112 do {
113#ifdef SK_SCALAR_IS_FLOAT
114 float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
115 SkFixed dist = SkFloatToFixed(fdist);
116#else
117 SkFixed magnitudeSquared = SkFixedSquare(sfx) +
118 SkFixedSquare(sfy);
119 if (magnitudeSquared < 0) // Overflow.
120 magnitudeSquared = SK_FixedMax;
121 SkFixed dist = SkFixedSqrt(magnitudeSquared);
122#endif
123 unsigned fi = mirror_tileproc(dist);
124 SkASSERT(fi <= 0xFFFF);
125 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000126 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000127 sfx += sdx;
128 sfy += sdy;
129 } while (--count != 0);
130}
131
132void shadeSpan16_radial_repeat(SkScalar sfx, SkScalar sdx,
133 SkScalar sfy, SkScalar sdy,
134 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
135 int toggle, int count) {
136 SkFixed fx = SkScalarToFixed(sfx);
137 SkFixed dx = SkScalarToFixed(sdx);
138 SkFixed fy = SkScalarToFixed(sfy);
139 SkFixed dy = SkScalarToFixed(sdy);
140 do {
141 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
142 unsigned fi = repeat_tileproc(dist);
143 SkASSERT(fi <= 0xFFFF);
144 fx += dx;
145 fy += dy;
146 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000147 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000148 } while (--count != 0);
149}
150
151}
152
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000153/////////////////////////////////////////////////////////////////////
154
rileya@google.com589708b2012-07-26 20:04:23 +0000155SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius,
reed@google.com437d6eb2013-05-23 19:03:05 +0000156 const Descriptor& desc)
157 : SkGradientShaderBase(desc),
rileya@google.com589708b2012-07-26 20:04:23 +0000158 fCenter(center),
159 fRadius(radius)
160{
161 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
162 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
163
164 rad_to_unit_matrix(center, radius, &fPtsToUnit);
165}
166
167void SkRadialGradient::shadeSpan16(int x, int y, uint16_t* dstCParam,
168 int count) {
169 SkASSERT(count > 0);
170
171 uint16_t* SK_RESTRICT dstC = dstCParam;
172
173 SkPoint srcPt;
174 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
175 TileProc proc = fTileProc;
176 const uint16_t* SK_RESTRICT cache = this->getCache16();
reed@google.com55853db2013-02-01 19:34:59 +0000177 int toggle = init_dither_toggle16(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000178
179 if (fDstToIndexClass != kPerspective_MatrixClass) {
180 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
181 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
182
183 SkScalar sdx = fDstToIndex.getScaleX();
184 SkScalar sdy = fDstToIndex.getSkewY();
185
186 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
187 SkFixed storage[2];
188 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
189 &storage[0], &storage[1]);
190 sdx = SkFixedToScalar(storage[0]);
191 sdy = SkFixedToScalar(storage[1]);
192 } else {
193 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
194 }
195
196 RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
197 if (SkShader::kClamp_TileMode == fTileMode) {
198 shadeProc = shadeSpan16_radial_clamp;
199 } else if (SkShader::kMirror_TileMode == fTileMode) {
200 shadeProc = shadeSpan16_radial_mirror;
201 } else {
202 SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
203 }
204 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
205 cache, toggle, count);
206 } else { // perspective case
207 SkScalar dstX = SkIntToScalar(x);
208 SkScalar dstY = SkIntToScalar(y);
209 do {
210 dstProc(fDstToIndex, dstX, dstY, &srcPt);
211 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
212 SkASSERT(fi <= 0xFFFF);
213
214 int index = fi >> (16 - kCache16Bits);
215 *dstC++ = cache[toggle + index];
reed@google.com55853db2013-02-01 19:34:59 +0000216 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000217
218 dstX += SK_Scalar1;
219 } while (--count != 0);
220 }
221}
222
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000223SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap,
rileya@google.com589708b2012-07-26 20:04:23 +0000224 SkMatrix* matrix, SkShader::TileMode* xy) const {
225 if (bitmap) {
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000226 this->getGradientTableBitmap(bitmap);
rileya@google.com589708b2012-07-26 20:04:23 +0000227 }
228 if (matrix) {
reed@google.com3c2102c2013-02-01 12:59:40 +0000229 matrix->setScale(SkIntToScalar(kCache32Count),
230 SkIntToScalar(kCache32Count));
rileya@google.com589708b2012-07-26 20:04:23 +0000231 matrix->preConcat(fPtsToUnit);
232 }
233 if (xy) {
234 xy[0] = fTileMode;
235 xy[1] = kClamp_TileMode;
236 }
237 return kRadial_BitmapType;
238}
239
240SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
241 if (info) {
242 commonAsAGradient(info);
243 info->fPoint[0] = fCenter;
244 info->fRadius[0] = fRadius;
245 }
246 return kRadial_GradientType;
247}
248
rileya@google.com589708b2012-07-26 20:04:23 +0000249SkRadialGradient::SkRadialGradient(SkFlattenableReadBuffer& buffer)
250 : INHERITED(buffer),
251 fCenter(buffer.readPoint()),
252 fRadius(buffer.readScalar()) {
253}
254
255void SkRadialGradient::flatten(SkFlattenableWriteBuffer& buffer) const {
256 this->INHERITED::flatten(buffer);
257 buffer.writePoint(fCenter);
258 buffer.writeScalar(fRadius);
259}
260
261namespace {
262
263inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
264 // fast, overly-conservative test: checks unit square instead
265 // of unit circle
266 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
267 (fx <= -SK_FixedHalf && dx <= 0);
268 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
269 (fy <= -SK_FixedHalf && dy <= 0);
270
271 return xClamped || yClamped;
272}
273
274// Return true if (fx * fy) is always inside the unit circle
275// SkPin32 is expensive, but so are all the SkFixedMul in this test,
276// so it shouldn't be run if count is small.
277inline bool no_need_for_radial_pin(int fx, int dx,
278 int fy, int dy, int count) {
279 SkASSERT(count > 0);
280 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
281 return false;
282 }
283 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
284 return false;
285 }
286 fx += (count - 1) * dx;
287 fy += (count - 1) * dy;
288 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
289 return false;
290 }
291 return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
292}
293
294#define UNPINNED_RADIAL_STEP \
295 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
296 *dstC++ = cache[toggle + \
297 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
reed@google.com55853db2013-02-01 19:34:59 +0000298 toggle = next_dither_toggle(toggle); \
rileya@google.com589708b2012-07-26 20:04:23 +0000299 fx += dx; \
300 fy += dy;
301
302typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
303 SkScalar sfy, SkScalar sdy,
304 SkPMColor* dstC, const SkPMColor* cache,
305 int count, int toggle);
306
307// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
308void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
309 SkScalar sfy, SkScalar sdy,
310 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
311 int count, int toggle) {
312 // Floating point seems to be slower than fixed point,
313 // even when we have float hardware.
314 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
315 SkFixed fx = SkScalarToFixed(sfx) >> 1;
316 SkFixed dx = SkScalarToFixed(sdx) >> 1;
317 SkFixed fy = SkScalarToFixed(sfy) >> 1;
318 SkFixed dy = SkScalarToFixed(sdy) >> 1;
319 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
reed@google.com3c2102c2013-02-01 12:59:40 +0000320 unsigned fi = SkGradientShaderBase::kCache32Count - 1;
rileya@google.com589708b2012-07-26 20:04:23 +0000321 sk_memset32_dither(dstC,
322 cache[toggle + fi],
reed@google.com55853db2013-02-01 19:34:59 +0000323 cache[next_dither_toggle(toggle) + fi],
rileya@google.com589708b2012-07-26 20:04:23 +0000324 count);
325 } else if ((count > 4) &&
326 no_need_for_radial_pin(fx, dx, fy, dy, count)) {
327 unsigned fi;
328 // 4x unroll appears to be no faster than 2x unroll on Linux
329 while (count > 1) {
330 UNPINNED_RADIAL_STEP;
331 UNPINNED_RADIAL_STEP;
332 count -= 2;
333 }
334 if (count) {
335 UNPINNED_RADIAL_STEP;
336 }
reed@google.com60040292013-02-04 18:21:23 +0000337 } else {
rileya@google.com589708b2012-07-26 20:04:23 +0000338 // Specializing for dy == 0 gains us 25% on Skia benchmarks
339 if (dy == 0) {
340 unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
341 yy *= yy;
342 do {
343 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
344 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
345 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
346 *dstC++ = cache[toggle + (sqrt_table[fi] >>
347 SkGradientShaderBase::kSqrt32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000348 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000349 fx += dx;
350 } while (--count != 0);
351 } else {
352 do {
353 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
354 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
355 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
356 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
357 *dstC++ = cache[toggle + (sqrt_table[fi] >>
358 SkGradientShaderBase::kSqrt32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000359 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000360 fx += dx;
361 fy += dy;
362 } while (--count != 0);
363 }
364 }
365}
366
367// Unrolling this loop doesn't seem to help (when float); we're stalling to
368// get the results of the sqrt (?), and don't have enough extra registers to
369// have many in flight.
370void shadeSpan_radial_mirror(SkScalar sfx, SkScalar sdx,
371 SkScalar sfy, SkScalar sdy,
372 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
373 int count, int toggle) {
374 do {
375#ifdef SK_SCALAR_IS_FLOAT
376 float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
377 SkFixed dist = SkFloatToFixed(fdist);
378#else
379 SkFixed magnitudeSquared = SkFixedSquare(sfx) +
380 SkFixedSquare(sfy);
381 if (magnitudeSquared < 0) // Overflow.
382 magnitudeSquared = SK_FixedMax;
383 SkFixed dist = SkFixedSqrt(magnitudeSquared);
384#endif
385 unsigned fi = mirror_tileproc(dist);
386 SkASSERT(fi <= 0xFFFF);
387 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000388 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000389 sfx += sdx;
390 sfy += sdy;
391 } while (--count != 0);
392}
393
394void shadeSpan_radial_repeat(SkScalar sfx, SkScalar sdx,
395 SkScalar sfy, SkScalar sdy,
396 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
397 int count, int toggle) {
398 SkFixed fx = SkScalarToFixed(sfx);
399 SkFixed dx = SkScalarToFixed(sdx);
400 SkFixed fy = SkScalarToFixed(sfy);
401 SkFixed dy = SkScalarToFixed(sdy);
402 do {
403 SkFixed magnitudeSquared = SkFixedSquare(fx) +
404 SkFixedSquare(fy);
405 if (magnitudeSquared < 0) // Overflow.
406 magnitudeSquared = SK_FixedMax;
407 SkFixed dist = SkFixedSqrt(magnitudeSquared);
408 unsigned fi = repeat_tileproc(dist);
409 SkASSERT(fi <= 0xFFFF);
410 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000411 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000412 fx += dx;
413 fy += dy;
414 } while (--count != 0);
415}
416}
417
418void SkRadialGradient::shadeSpan(int x, int y,
419 SkPMColor* SK_RESTRICT dstC, int count) {
420 SkASSERT(count > 0);
421
422 SkPoint srcPt;
423 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
424 TileProc proc = fTileProc;
425 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@google.com55853db2013-02-01 19:34:59 +0000426 int toggle = init_dither_toggle(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000427
428 if (fDstToIndexClass != kPerspective_MatrixClass) {
429 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
430 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
431 SkScalar sdx = fDstToIndex.getScaleX();
432 SkScalar sdy = fDstToIndex.getSkewY();
433
434 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
435 SkFixed storage[2];
436 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
437 &storage[0], &storage[1]);
438 sdx = SkFixedToScalar(storage[0]);
439 sdy = SkFixedToScalar(storage[1]);
440 } else {
441 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
442 }
443
444 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
445 if (SkShader::kClamp_TileMode == fTileMode) {
446 shadeProc = shadeSpan_radial_clamp;
447 } else if (SkShader::kMirror_TileMode == fTileMode) {
448 shadeProc = shadeSpan_radial_mirror;
449 } else {
450 SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
451 }
452 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
453 } else { // perspective case
454 SkScalar dstX = SkIntToScalar(x);
455 SkScalar dstY = SkIntToScalar(y);
456 do {
457 dstProc(fDstToIndex, dstX, dstY, &srcPt);
458 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
459 SkASSERT(fi <= 0xFFFF);
460 *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
461 dstX += SK_Scalar1;
462 } while (--count != 0);
463 }
464}
465
rileya@google.comd7cc6512012-07-27 14:00:39 +0000466/////////////////////////////////////////////////////////////////////
467
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000468#if SK_SUPPORT_GPU
469
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000470#include "GrTBackendEffectFactory.h"
471
bsalomon@google.com0707c292012-10-25 21:45:42 +0000472class GrGLRadialGradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000473public:
474
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000475 GrGLRadialGradient(const GrBackendEffectFactory& factory,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000476 const GrDrawEffect&) : INHERITED (factory) { }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000477 virtual ~GrGLRadialGradient() { }
478
bsalomon@google.comf78df332012-10-29 12:43:38 +0000479 virtual void emitCode(GrGLShaderBuilder*,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000480 const GrDrawEffect&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000481 EffectKey,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000482 const char* outputColor,
483 const char* inputColor,
484 const TextureSamplerArray&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000485
bsalomon@google.comc7818882013-03-20 19:19:53 +0000486 static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
bsalomon@google.com82d12232013-09-09 15:36:26 +0000487 return GenBaseGradientKey(drawEffect);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000488 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000489
490private:
491
bsalomon@google.com0707c292012-10-25 21:45:42 +0000492 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000493
494};
495
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000496/////////////////////////////////////////////////////////////////////
497
498class GrRadialGradient : public GrGradientEffect {
499public:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000500 static GrEffectRef* Create(GrContext* ctx,
501 const SkRadialGradient& shader,
502 const SkMatrix& matrix,
503 SkShader::TileMode tm) {
bsalomon@google.com6340a412013-01-22 19:55:59 +0000504 AutoEffectUnref effect(SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm)));
bsalomon@google.coma1ebbe42013-01-16 15:51:47 +0000505 return CreateEffectRef(effect);
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000506 }
507
508 virtual ~GrRadialGradient() { }
509
510 static const char* Name() { return "Radial Gradient"; }
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000511 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
512 return GrTBackendEffectFactory<GrRadialGradient>::getInstance();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000513 }
514
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000515 typedef GrGLRadialGradient GLEffect;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000516
517private:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000518 GrRadialGradient(GrContext* ctx,
519 const SkRadialGradient& shader,
520 const SkMatrix& matrix,
521 SkShader::TileMode tm)
522 : INHERITED(ctx, shader, matrix, tm) {
523 }
524
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000525 GR_DECLARE_EFFECT_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000526
527 typedef GrGradientEffect INHERITED;
528};
529
530/////////////////////////////////////////////////////////////////////
531
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000532GR_DEFINE_EFFECT_TEST(GrRadialGradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000533
bsalomon@google.com73a96942013-02-13 16:31:19 +0000534GrEffectRef* GrRadialGradient::TestCreate(SkMWCRandom* random,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000535 GrContext* context,
bsalomon@google.comc26d94f2013-03-25 18:19:00 +0000536 const GrDrawTargetCaps&,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000537 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000538 SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
539 SkScalar radius = random->nextUScalar1();
540
541 SkColor colors[kMaxRandomGradientColors];
542 SkScalar stopsArray[kMaxRandomGradientColors];
543 SkScalar* stops = stopsArray;
544 SkShader::TileMode tm;
545 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
546 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
547 colors, stops, colorCount,
548 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000549 SkPaint paint;
550 return shader->asNewEffect(context, paint);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000551}
552
553/////////////////////////////////////////////////////////////////////
554
bsalomon@google.comf78df332012-10-29 12:43:38 +0000555void GrGLRadialGradient::emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000556 const GrDrawEffect&,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000557 EffectKey key,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000558 const char* outputColor,
559 const char* inputColor,
560 const TextureSamplerArray& samplers) {
bsalomon@google.com82d12232013-09-09 15:36:26 +0000561 this->emitUniforms(builder, key);
commit-bot@chromium.org7ab7ca42013-08-28 15:59:13 +0000562 SkString coords;
bsalomon@google.comc7818882013-03-20 19:19:53 +0000563 this->setupMatrix(builder, key, &coords);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000564 SkString t("length(");
565 t.append(coords);
566 t.append(")");
bsalomon@google.com82d12232013-09-09 15:36:26 +0000567 this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000568}
569
rileya@google.comd7cc6512012-07-27 14:00:39 +0000570/////////////////////////////////////////////////////////////////////
571
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000572GrEffectRef* SkRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
bsalomon@google.com00835cc2013-01-14 17:07:22 +0000573 SkASSERT(NULL != context);
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000574
575 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000576 if (!this->getLocalMatrix().invert(&matrix)) {
humper@google.com84831ac2013-01-14 22:09:54 +0000577 return NULL;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000578 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000579 matrix.postConcat(fPtsToUnit);
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000580 return GrRadialGradient::Create(context, *this, matrix, fTileMode);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000581}
582
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000583#else
584
bsalomon@google.com5d2cd202013-01-16 15:31:06 +0000585GrEffectRef* SkRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000586 SkDEBUGFAIL("Should not call in GPU-less build");
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000587 return NULL;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000588}
589
590#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000591
592#ifdef SK_DEVELOPER
593void SkRadialGradient::toString(SkString* str) const {
594 str->append("SkRadialGradient: (");
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000595
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000596 str->append("center: (");
597 str->appendScalar(fCenter.fX);
598 str->append(", ");
599 str->appendScalar(fCenter.fY);
600 str->append(") radius: ");
601 str->appendScalar(fRadius);
602 str->append(" ");
603
604 this->INHERITED::toString(str);
605
606 str->append(")");
607}
608#endif