blob: efa82f49bf3aa3c2a8c8d93f83eb9eb185a1f243 [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
reed@google.com4ec5c952013-12-19 15:00:18 +000015#if 0
rileya@google.com589708b2012-07-26 20:04:23 +000016
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
commit-bot@chromium.org34150b42013-10-16 18:59:44 +000049// GCC doesn't like using static functions as template arguments. So force these to be non-static.
50inline SkFixed mirror_tileproc_nonstatic(SkFixed x) {
51 return mirror_tileproc(x);
52}
53
54inline SkFixed repeat_tileproc_nonstatic(SkFixed x) {
55 return repeat_tileproc(x);
56}
57
rileya@google.com589708b2012-07-26 20:04:23 +000058void rad_to_unit_matrix(const SkPoint& center, SkScalar radius,
59 SkMatrix* matrix) {
60 SkScalar inv = SkScalarInvert(radius);
61
62 matrix->setTranslate(-center.fX, -center.fY);
63 matrix->postScale(inv, inv);
64}
65
66typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx,
67 SkScalar sfy, SkScalar sdy,
68 uint16_t* dstC, const uint16_t* cache,
69 int toggle, int count);
70
71void shadeSpan16_radial_clamp(SkScalar sfx, SkScalar sdx,
72 SkScalar sfy, SkScalar sdy,
73 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
74 int toggle, int count) {
75 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
76
77 /* knock these down so we can pin against +- 0x7FFF, which is an
78 immediate load, rather than 0xFFFF which is slower. This is a
79 compromise, since it reduces our precision, but that appears
80 to be visually OK. If we decide this is OK for all of our cases,
81 we could (it seems) put this scale-down into fDstToIndex,
82 to avoid having to do these extra shifts each time.
83 */
84 SkFixed fx = SkScalarToFixed(sfx) >> 1;
85 SkFixed dx = SkScalarToFixed(sdx) >> 1;
86 SkFixed fy = SkScalarToFixed(sfy) >> 1;
87 SkFixed dy = SkScalarToFixed(sdy) >> 1;
88 // might perform this check for the other modes,
89 // but the win will be a smaller % of the total
90 if (dy == 0) {
91 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
92 fy *= fy;
93 do {
94 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
95 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
96 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
97 fx += dx;
98 *dstC++ = cache[toggle +
99 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000100 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000101 } while (--count != 0);
102 } else {
103 do {
104 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
105 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
106 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
107 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
108 fx += dx;
109 fy += dy;
110 *dstC++ = cache[toggle +
111 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000112 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000113 } while (--count != 0);
114 }
115}
116
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000117template <SkFixed (*TileProc)(SkFixed)>
118void shadeSpan16_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
119 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
120 int toggle, int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000121 do {
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000122 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
123 const unsigned fi = TileProc(dist);
rileya@google.com589708b2012-07-26 20:04:23 +0000124 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 fx += dx;
128 fy += dy;
rileya@google.com589708b2012-07-26 20:04:23 +0000129 } while (--count != 0);
130}
131
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000132void shadeSpan16_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
133 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
134 int toggle, int count) {
135 shadeSpan16_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, toggle, count);
rileya@google.com589708b2012-07-26 20:04:23 +0000136}
137
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000138void shadeSpan16_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
139 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
140 int toggle, int count) {
141 shadeSpan16_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, toggle, count);
142}
143
144} // namespace
145
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000146/////////////////////////////////////////////////////////////////////
147
rileya@google.com589708b2012-07-26 20:04:23 +0000148SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius,
reed@google.com437d6eb2013-05-23 19:03:05 +0000149 const Descriptor& desc)
150 : SkGradientShaderBase(desc),
rileya@google.com589708b2012-07-26 20:04:23 +0000151 fCenter(center),
152 fRadius(radius)
153{
154 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
155 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
156
157 rad_to_unit_matrix(center, radius, &fPtsToUnit);
158}
159
160void SkRadialGradient::shadeSpan16(int x, int y, uint16_t* dstCParam,
161 int count) {
162 SkASSERT(count > 0);
163
164 uint16_t* SK_RESTRICT dstC = dstCParam;
165
166 SkPoint srcPt;
167 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
168 TileProc proc = fTileProc;
169 const uint16_t* SK_RESTRICT cache = this->getCache16();
reed@google.com55853db2013-02-01 19:34:59 +0000170 int toggle = init_dither_toggle16(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000171
172 if (fDstToIndexClass != kPerspective_MatrixClass) {
173 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
174 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
175
176 SkScalar sdx = fDstToIndex.getScaleX();
177 SkScalar sdy = fDstToIndex.getSkewY();
178
179 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
180 SkFixed storage[2];
181 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
182 &storage[0], &storage[1]);
183 sdx = SkFixedToScalar(storage[0]);
184 sdy = SkFixedToScalar(storage[1]);
185 } else {
186 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
187 }
188
189 RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
190 if (SkShader::kClamp_TileMode == fTileMode) {
191 shadeProc = shadeSpan16_radial_clamp;
192 } else if (SkShader::kMirror_TileMode == fTileMode) {
193 shadeProc = shadeSpan16_radial_mirror;
194 } else {
195 SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
196 }
197 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
198 cache, toggle, count);
199 } else { // perspective case
200 SkScalar dstX = SkIntToScalar(x);
201 SkScalar dstY = SkIntToScalar(y);
202 do {
203 dstProc(fDstToIndex, dstX, dstY, &srcPt);
204 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
205 SkASSERT(fi <= 0xFFFF);
206
207 int index = fi >> (16 - kCache16Bits);
208 *dstC++ = cache[toggle + index];
reed@google.com55853db2013-02-01 19:34:59 +0000209 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000210
211 dstX += SK_Scalar1;
212 } while (--count != 0);
213 }
214}
215
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000216SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap,
rileya@google.com589708b2012-07-26 20:04:23 +0000217 SkMatrix* matrix, SkShader::TileMode* xy) const {
218 if (bitmap) {
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000219 this->getGradientTableBitmap(bitmap);
rileya@google.com589708b2012-07-26 20:04:23 +0000220 }
221 if (matrix) {
reed@google.com3c2102c2013-02-01 12:59:40 +0000222 matrix->setScale(SkIntToScalar(kCache32Count),
223 SkIntToScalar(kCache32Count));
rileya@google.com589708b2012-07-26 20:04:23 +0000224 matrix->preConcat(fPtsToUnit);
225 }
226 if (xy) {
227 xy[0] = fTileMode;
228 xy[1] = kClamp_TileMode;
229 }
230 return kRadial_BitmapType;
231}
232
233SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
234 if (info) {
235 commonAsAGradient(info);
236 info->fPoint[0] = fCenter;
237 info->fRadius[0] = fRadius;
238 }
239 return kRadial_GradientType;
240}
241
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000242SkRadialGradient::SkRadialGradient(SkReadBuffer& buffer)
rileya@google.com589708b2012-07-26 20:04:23 +0000243 : INHERITED(buffer),
244 fCenter(buffer.readPoint()),
245 fRadius(buffer.readScalar()) {
246}
247
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000248void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000249 this->INHERITED::flatten(buffer);
250 buffer.writePoint(fCenter);
251 buffer.writeScalar(fRadius);
252}
253
254namespace {
255
256inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
257 // fast, overly-conservative test: checks unit square instead
258 // of unit circle
259 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
260 (fx <= -SK_FixedHalf && dx <= 0);
261 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
262 (fy <= -SK_FixedHalf && dy <= 0);
263
264 return xClamped || yClamped;
265}
266
267// Return true if (fx * fy) is always inside the unit circle
268// SkPin32 is expensive, but so are all the SkFixedMul in this test,
269// so it shouldn't be run if count is small.
270inline bool no_need_for_radial_pin(int fx, int dx,
271 int fy, int dy, int count) {
272 SkASSERT(count > 0);
273 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
274 return false;
275 }
276 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
277 return false;
278 }
279 fx += (count - 1) * dx;
280 fy += (count - 1) * dy;
281 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
282 return false;
283 }
284 return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
285}
286
287#define UNPINNED_RADIAL_STEP \
288 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
289 *dstC++ = cache[toggle + \
290 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
reed@google.com55853db2013-02-01 19:34:59 +0000291 toggle = next_dither_toggle(toggle); \
rileya@google.com589708b2012-07-26 20:04:23 +0000292 fx += dx; \
293 fy += dy;
294
295typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
296 SkScalar sfy, SkScalar sdy,
297 SkPMColor* dstC, const SkPMColor* cache,
298 int count, int toggle);
299
300// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
301void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
302 SkScalar sfy, SkScalar sdy,
303 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
304 int count, int toggle) {
305 // Floating point seems to be slower than fixed point,
306 // even when we have float hardware.
307 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
308 SkFixed fx = SkScalarToFixed(sfx) >> 1;
309 SkFixed dx = SkScalarToFixed(sdx) >> 1;
310 SkFixed fy = SkScalarToFixed(sfy) >> 1;
311 SkFixed dy = SkScalarToFixed(sdy) >> 1;
312 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
reed@google.com3c2102c2013-02-01 12:59:40 +0000313 unsigned fi = SkGradientShaderBase::kCache32Count - 1;
rileya@google.com589708b2012-07-26 20:04:23 +0000314 sk_memset32_dither(dstC,
315 cache[toggle + fi],
reed@google.com55853db2013-02-01 19:34:59 +0000316 cache[next_dither_toggle(toggle) + fi],
rileya@google.com589708b2012-07-26 20:04:23 +0000317 count);
318 } else if ((count > 4) &&
319 no_need_for_radial_pin(fx, dx, fy, dy, count)) {
320 unsigned fi;
321 // 4x unroll appears to be no faster than 2x unroll on Linux
322 while (count > 1) {
323 UNPINNED_RADIAL_STEP;
324 UNPINNED_RADIAL_STEP;
325 count -= 2;
326 }
327 if (count) {
328 UNPINNED_RADIAL_STEP;
329 }
reed@google.com60040292013-02-04 18:21:23 +0000330 } else {
rileya@google.com589708b2012-07-26 20:04:23 +0000331 // Specializing for dy == 0 gains us 25% on Skia benchmarks
332 if (dy == 0) {
333 unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
334 yy *= yy;
335 do {
336 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
337 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
338 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
339 *dstC++ = cache[toggle + (sqrt_table[fi] >>
340 SkGradientShaderBase::kSqrt32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000341 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000342 fx += dx;
343 } while (--count != 0);
344 } else {
345 do {
346 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
347 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
348 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
349 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
350 *dstC++ = cache[toggle + (sqrt_table[fi] >>
351 SkGradientShaderBase::kSqrt32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000352 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000353 fx += dx;
354 fy += dy;
355 } while (--count != 0);
356 }
357 }
358}
359
360// Unrolling this loop doesn't seem to help (when float); we're stalling to
361// get the results of the sqrt (?), and don't have enough extra registers to
362// have many in flight.
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000363template <SkFixed (*TileProc)(SkFixed)>
364void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
365 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
366 int count, int toggle) {
rileya@google.com589708b2012-07-26 20:04:23 +0000367 do {
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000368 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
369 const unsigned fi = TileProc(dist);
rileya@google.com589708b2012-07-26 20:04:23 +0000370 SkASSERT(fi <= 0xFFFF);
371 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000372 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000373 fx += dx;
374 fy += dy;
375 } while (--count != 0);
376}
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000377
378void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
379 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
380 int count, int toggle) {
381 shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000382}
383
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000384void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
385 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
386 int count, int toggle) {
387 shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
388}
389
390} // namespace
391
rileya@google.com589708b2012-07-26 20:04:23 +0000392void SkRadialGradient::shadeSpan(int x, int y,
393 SkPMColor* SK_RESTRICT dstC, int count) {
394 SkASSERT(count > 0);
395
396 SkPoint srcPt;
397 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
398 TileProc proc = fTileProc;
399 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@google.com55853db2013-02-01 19:34:59 +0000400 int toggle = init_dither_toggle(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000401
402 if (fDstToIndexClass != kPerspective_MatrixClass) {
403 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
404 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
405 SkScalar sdx = fDstToIndex.getScaleX();
406 SkScalar sdy = fDstToIndex.getSkewY();
407
408 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
409 SkFixed storage[2];
410 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
411 &storage[0], &storage[1]);
412 sdx = SkFixedToScalar(storage[0]);
413 sdy = SkFixedToScalar(storage[1]);
414 } else {
415 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
416 }
417
418 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
419 if (SkShader::kClamp_TileMode == fTileMode) {
420 shadeProc = shadeSpan_radial_clamp;
421 } else if (SkShader::kMirror_TileMode == fTileMode) {
422 shadeProc = shadeSpan_radial_mirror;
423 } else {
424 SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
425 }
426 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
427 } else { // perspective case
428 SkScalar dstX = SkIntToScalar(x);
429 SkScalar dstY = SkIntToScalar(y);
430 do {
431 dstProc(fDstToIndex, dstX, dstY, &srcPt);
432 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
433 SkASSERT(fi <= 0xFFFF);
434 *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
435 dstX += SK_Scalar1;
436 } while (--count != 0);
437 }
438}
439
rileya@google.comd7cc6512012-07-27 14:00:39 +0000440/////////////////////////////////////////////////////////////////////
441
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000442#if SK_SUPPORT_GPU
443
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000444#include "GrTBackendEffectFactory.h"
445
bsalomon@google.com0707c292012-10-25 21:45:42 +0000446class GrGLRadialGradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000447public:
448
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000449 GrGLRadialGradient(const GrBackendEffectFactory& factory,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000450 const GrDrawEffect&) : INHERITED (factory) { }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000451 virtual ~GrGLRadialGradient() { }
452
bsalomon@google.comf78df332012-10-29 12:43:38 +0000453 virtual void emitCode(GrGLShaderBuilder*,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000454 const GrDrawEffect&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000455 EffectKey,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000456 const char* outputColor,
457 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000458 const TransformedCoordsArray&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000459 const TextureSamplerArray&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000460
bsalomon@google.comc7818882013-03-20 19:19:53 +0000461 static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
bsalomon@google.com82d12232013-09-09 15:36:26 +0000462 return GenBaseGradientKey(drawEffect);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000463 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000464
465private:
466
bsalomon@google.com0707c292012-10-25 21:45:42 +0000467 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000468
469};
470
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000471/////////////////////////////////////////////////////////////////////
472
473class GrRadialGradient : public GrGradientEffect {
474public:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000475 static GrEffectRef* Create(GrContext* ctx,
476 const SkRadialGradient& shader,
477 const SkMatrix& matrix,
478 SkShader::TileMode tm) {
bsalomon@google.com6340a412013-01-22 19:55:59 +0000479 AutoEffectUnref effect(SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm)));
bsalomon@google.coma1ebbe42013-01-16 15:51:47 +0000480 return CreateEffectRef(effect);
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000481 }
482
483 virtual ~GrRadialGradient() { }
484
485 static const char* Name() { return "Radial Gradient"; }
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000486 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
487 return GrTBackendEffectFactory<GrRadialGradient>::getInstance();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000488 }
489
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000490 typedef GrGLRadialGradient GLEffect;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000491
492private:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000493 GrRadialGradient(GrContext* ctx,
494 const SkRadialGradient& shader,
495 const SkMatrix& matrix,
496 SkShader::TileMode tm)
497 : INHERITED(ctx, shader, matrix, tm) {
498 }
499
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000500 GR_DECLARE_EFFECT_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000501
502 typedef GrGradientEffect INHERITED;
503};
504
505/////////////////////////////////////////////////////////////////////
506
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000507GR_DEFINE_EFFECT_TEST(GrRadialGradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000508
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000509GrEffectRef* GrRadialGradient::TestCreate(SkRandom* random,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000510 GrContext* context,
bsalomon@google.comc26d94f2013-03-25 18:19:00 +0000511 const GrDrawTargetCaps&,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000512 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000513 SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
514 SkScalar radius = random->nextUScalar1();
515
516 SkColor colors[kMaxRandomGradientColors];
517 SkScalar stopsArray[kMaxRandomGradientColors];
518 SkScalar* stops = stopsArray;
519 SkShader::TileMode tm;
520 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
521 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
522 colors, stops, colorCount,
523 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000524 SkPaint paint;
525 return shader->asNewEffect(context, paint);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000526}
527
528/////////////////////////////////////////////////////////////////////
529
bsalomon@google.comf78df332012-10-29 12:43:38 +0000530void GrGLRadialGradient::emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000531 const GrDrawEffect&,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000532 EffectKey key,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000533 const char* outputColor,
534 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000535 const TransformedCoordsArray& coords,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000536 const TextureSamplerArray& samplers) {
bsalomon@google.com82d12232013-09-09 15:36:26 +0000537 this->emitUniforms(builder, key);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000538 SkString t("length(");
bsalomon@google.com77af6802013-10-02 13:04:56 +0000539 t.append(builder->ensureFSCoords2D(coords, 0));
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000540 t.append(")");
bsalomon@google.com82d12232013-09-09 15:36:26 +0000541 this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000542}
543
rileya@google.comd7cc6512012-07-27 14:00:39 +0000544/////////////////////////////////////////////////////////////////////
545
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000546GrEffectRef* SkRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
bsalomon@google.com00835cc2013-01-14 17:07:22 +0000547 SkASSERT(NULL != context);
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000548
549 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000550 if (!this->getLocalMatrix().invert(&matrix)) {
humper@google.com84831ac2013-01-14 22:09:54 +0000551 return NULL;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000552 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000553 matrix.postConcat(fPtsToUnit);
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000554 return GrRadialGradient::Create(context, *this, matrix, fTileMode);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000555}
556
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000557#else
558
bsalomon@google.com5d2cd202013-01-16 15:31:06 +0000559GrEffectRef* SkRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000560 SkDEBUGFAIL("Should not call in GPU-less build");
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000561 return NULL;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000562}
563
564#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000565
566#ifdef SK_DEVELOPER
567void SkRadialGradient::toString(SkString* str) const {
568 str->append("SkRadialGradient: (");
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000569
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000570 str->append("center: (");
571 str->appendScalar(fCenter.fX);
572 str->append(", ");
573 str->appendScalar(fCenter.fY);
574 str->append(") radius: ");
575 str->appendScalar(fRadius);
576 str->append(" ");
577
578 this->INHERITED::toString(str);
579
580 str->append(")");
581}
582#endif