blob: 07ee680acad3df254d9830070a51c47183a4d377 [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,
commit-bot@chromium.org9c9005a2014-04-28 14:55:39 +0000149 const Descriptor& desc, const SkMatrix* localMatrix)
150 : SkGradientShaderBase(desc, localMatrix),
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
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000160size_t SkRadialGradient::contextSize() const {
161 return sizeof(RadialGradientContext);
162}
163
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000164SkShader::Context* SkRadialGradient::createContext(const ContextRec& rec, void* storage) const {
165 if (!this->validContext(rec)) {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000166 return NULL;
167 }
168
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000169 return SkNEW_PLACEMENT_ARGS(storage, RadialGradientContext, (*this, rec));
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000170}
171
172SkRadialGradient::RadialGradientContext::RadialGradientContext(
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000173 const SkRadialGradient& shader, const ContextRec& rec)
174 : INHERITED(shader, rec) {}
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000175
176void SkRadialGradient::RadialGradientContext::shadeSpan16(int x, int y, uint16_t* dstCParam,
177 int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000178 SkASSERT(count > 0);
179
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000180 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
181
rileya@google.com589708b2012-07-26 20:04:23 +0000182 uint16_t* SK_RESTRICT dstC = dstCParam;
183
184 SkPoint srcPt;
185 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000186 TileProc proc = radialGradient.fTileProc;
187 const uint16_t* SK_RESTRICT cache = fCache->getCache16();
reed@google.com55853db2013-02-01 19:34:59 +0000188 int toggle = init_dither_toggle16(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000189
190 if (fDstToIndexClass != kPerspective_MatrixClass) {
191 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
192 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
193
194 SkScalar sdx = fDstToIndex.getScaleX();
195 SkScalar sdy = fDstToIndex.getSkewY();
196
197 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
198 SkFixed storage[2];
199 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
200 &storage[0], &storage[1]);
201 sdx = SkFixedToScalar(storage[0]);
202 sdy = SkFixedToScalar(storage[1]);
203 } else {
204 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
205 }
206
207 RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000208 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000209 shadeProc = shadeSpan16_radial_clamp;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000210 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000211 shadeProc = shadeSpan16_radial_mirror;
212 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000213 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000214 }
215 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
216 cache, toggle, count);
217 } else { // perspective case
218 SkScalar dstX = SkIntToScalar(x);
219 SkScalar dstY = SkIntToScalar(y);
220 do {
221 dstProc(fDstToIndex, dstX, dstY, &srcPt);
222 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
223 SkASSERT(fi <= 0xFFFF);
224
225 int index = fi >> (16 - kCache16Bits);
226 *dstC++ = cache[toggle + index];
reed@google.com55853db2013-02-01 19:34:59 +0000227 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000228
229 dstX += SK_Scalar1;
230 } while (--count != 0);
231 }
232}
233
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000234SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap,
rileya@google.com589708b2012-07-26 20:04:23 +0000235 SkMatrix* matrix, SkShader::TileMode* xy) const {
236 if (bitmap) {
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000237 this->getGradientTableBitmap(bitmap);
rileya@google.com589708b2012-07-26 20:04:23 +0000238 }
239 if (matrix) {
reed@google.com3c2102c2013-02-01 12:59:40 +0000240 matrix->setScale(SkIntToScalar(kCache32Count),
241 SkIntToScalar(kCache32Count));
rileya@google.com589708b2012-07-26 20:04:23 +0000242 matrix->preConcat(fPtsToUnit);
243 }
244 if (xy) {
245 xy[0] = fTileMode;
246 xy[1] = kClamp_TileMode;
247 }
248 return kRadial_BitmapType;
249}
250
251SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
252 if (info) {
253 commonAsAGradient(info);
254 info->fPoint[0] = fCenter;
255 info->fRadius[0] = fRadius;
256 }
257 return kRadial_GradientType;
258}
259
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000260SkRadialGradient::SkRadialGradient(SkReadBuffer& buffer)
rileya@google.com589708b2012-07-26 20:04:23 +0000261 : INHERITED(buffer),
262 fCenter(buffer.readPoint()),
263 fRadius(buffer.readScalar()) {
264}
265
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000266void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000267 this->INHERITED::flatten(buffer);
268 buffer.writePoint(fCenter);
269 buffer.writeScalar(fRadius);
270}
271
272namespace {
273
274inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
275 // fast, overly-conservative test: checks unit square instead
276 // of unit circle
277 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
278 (fx <= -SK_FixedHalf && dx <= 0);
279 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
280 (fy <= -SK_FixedHalf && dy <= 0);
281
282 return xClamped || yClamped;
283}
284
285// Return true if (fx * fy) is always inside the unit circle
286// SkPin32 is expensive, but so are all the SkFixedMul in this test,
287// so it shouldn't be run if count is small.
288inline bool no_need_for_radial_pin(int fx, int dx,
289 int fy, int dy, int count) {
290 SkASSERT(count > 0);
291 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
292 return false;
293 }
294 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
295 return false;
296 }
297 fx += (count - 1) * dx;
298 fy += (count - 1) * dy;
299 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
300 return false;
301 }
302 return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
303}
304
305#define UNPINNED_RADIAL_STEP \
306 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
307 *dstC++ = cache[toggle + \
308 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
reed@google.com55853db2013-02-01 19:34:59 +0000309 toggle = next_dither_toggle(toggle); \
rileya@google.com589708b2012-07-26 20:04:23 +0000310 fx += dx; \
311 fy += dy;
312
313typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
314 SkScalar sfy, SkScalar sdy,
315 SkPMColor* dstC, const SkPMColor* cache,
316 int count, int toggle);
317
318// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
319void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
320 SkScalar sfy, SkScalar sdy,
321 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
322 int count, int toggle) {
323 // Floating point seems to be slower than fixed point,
324 // even when we have float hardware.
325 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
326 SkFixed fx = SkScalarToFixed(sfx) >> 1;
327 SkFixed dx = SkScalarToFixed(sdx) >> 1;
328 SkFixed fy = SkScalarToFixed(sfy) >> 1;
329 SkFixed dy = SkScalarToFixed(sdy) >> 1;
330 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
reed@google.com3c2102c2013-02-01 12:59:40 +0000331 unsigned fi = SkGradientShaderBase::kCache32Count - 1;
rileya@google.com589708b2012-07-26 20:04:23 +0000332 sk_memset32_dither(dstC,
333 cache[toggle + fi],
reed@google.com55853db2013-02-01 19:34:59 +0000334 cache[next_dither_toggle(toggle) + fi],
rileya@google.com589708b2012-07-26 20:04:23 +0000335 count);
336 } else if ((count > 4) &&
337 no_need_for_radial_pin(fx, dx, fy, dy, count)) {
338 unsigned fi;
339 // 4x unroll appears to be no faster than 2x unroll on Linux
340 while (count > 1) {
341 UNPINNED_RADIAL_STEP;
342 UNPINNED_RADIAL_STEP;
343 count -= 2;
344 }
345 if (count) {
346 UNPINNED_RADIAL_STEP;
347 }
reed@google.com60040292013-02-04 18:21:23 +0000348 } else {
rileya@google.com589708b2012-07-26 20:04:23 +0000349 // Specializing for dy == 0 gains us 25% on Skia benchmarks
350 if (dy == 0) {
351 unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
352 yy *= yy;
353 do {
354 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
355 unsigned fi = (xx * xx + yy) >> (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 } while (--count != 0);
362 } else {
363 do {
364 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
365 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
366 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
367 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
368 *dstC++ = cache[toggle + (sqrt_table[fi] >>
369 SkGradientShaderBase::kSqrt32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000370 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000371 fx += dx;
372 fy += dy;
373 } while (--count != 0);
374 }
375 }
376}
377
378// Unrolling this loop doesn't seem to help (when float); we're stalling to
379// get the results of the sqrt (?), and don't have enough extra registers to
380// have many in flight.
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000381template <SkFixed (*TileProc)(SkFixed)>
382void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
383 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
384 int count, int toggle) {
rileya@google.com589708b2012-07-26 20:04:23 +0000385 do {
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000386 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
387 const unsigned fi = TileProc(dist);
rileya@google.com589708b2012-07-26 20:04:23 +0000388 SkASSERT(fi <= 0xFFFF);
389 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000390 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000391 fx += dx;
392 fy += dy;
393 } while (--count != 0);
394}
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000395
396void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
397 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
398 int count, int toggle) {
399 shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000400}
401
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000402void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
403 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
404 int count, int toggle) {
405 shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
406}
407
408} // namespace
409
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000410void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y,
411 SkPMColor* SK_RESTRICT dstC, int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000412 SkASSERT(count > 0);
413
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000414 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
415
rileya@google.com589708b2012-07-26 20:04:23 +0000416 SkPoint srcPt;
417 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000418 TileProc proc = radialGradient.fTileProc;
419 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
reed@google.com55853db2013-02-01 19:34:59 +0000420 int toggle = init_dither_toggle(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000421
422 if (fDstToIndexClass != kPerspective_MatrixClass) {
423 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
424 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
425 SkScalar sdx = fDstToIndex.getScaleX();
426 SkScalar sdy = fDstToIndex.getSkewY();
427
428 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
429 SkFixed storage[2];
430 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
431 &storage[0], &storage[1]);
432 sdx = SkFixedToScalar(storage[0]);
433 sdy = SkFixedToScalar(storage[1]);
434 } else {
435 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
436 }
437
438 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000439 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000440 shadeProc = shadeSpan_radial_clamp;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000441 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000442 shadeProc = shadeSpan_radial_mirror;
443 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000444 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000445 }
446 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
447 } else { // perspective case
448 SkScalar dstX = SkIntToScalar(x);
449 SkScalar dstY = SkIntToScalar(y);
450 do {
451 dstProc(fDstToIndex, dstX, dstY, &srcPt);
452 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
453 SkASSERT(fi <= 0xFFFF);
454 *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
455 dstX += SK_Scalar1;
456 } while (--count != 0);
457 }
458}
459
rileya@google.comd7cc6512012-07-27 14:00:39 +0000460/////////////////////////////////////////////////////////////////////
461
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000462#if SK_SUPPORT_GPU
463
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000464#include "GrTBackendEffectFactory.h"
465
bsalomon@google.com0707c292012-10-25 21:45:42 +0000466class GrGLRadialGradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000467public:
468
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000469 GrGLRadialGradient(const GrBackendEffectFactory& factory,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000470 const GrDrawEffect&) : INHERITED (factory) { }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000471 virtual ~GrGLRadialGradient() { }
472
bsalomon@google.comf78df332012-10-29 12:43:38 +0000473 virtual void emitCode(GrGLShaderBuilder*,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000474 const GrDrawEffect&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000475 EffectKey,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000476 const char* outputColor,
477 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000478 const TransformedCoordsArray&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000479 const TextureSamplerArray&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000480
bsalomon@google.comc7818882013-03-20 19:19:53 +0000481 static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
bsalomon@google.com82d12232013-09-09 15:36:26 +0000482 return GenBaseGradientKey(drawEffect);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000483 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000484
485private:
486
bsalomon@google.com0707c292012-10-25 21:45:42 +0000487 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000488
489};
490
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000491/////////////////////////////////////////////////////////////////////
492
493class GrRadialGradient : public GrGradientEffect {
494public:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000495 static GrEffectRef* Create(GrContext* ctx,
496 const SkRadialGradient& shader,
497 const SkMatrix& matrix,
498 SkShader::TileMode tm) {
bsalomon@google.com6340a412013-01-22 19:55:59 +0000499 AutoEffectUnref effect(SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm)));
bsalomon@google.coma1ebbe42013-01-16 15:51:47 +0000500 return CreateEffectRef(effect);
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000501 }
502
503 virtual ~GrRadialGradient() { }
504
505 static const char* Name() { return "Radial Gradient"; }
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000506 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
507 return GrTBackendEffectFactory<GrRadialGradient>::getInstance();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000508 }
509
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000510 typedef GrGLRadialGradient GLEffect;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000511
512private:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000513 GrRadialGradient(GrContext* ctx,
514 const SkRadialGradient& shader,
515 const SkMatrix& matrix,
516 SkShader::TileMode tm)
517 : INHERITED(ctx, shader, matrix, tm) {
518 }
519
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000520 GR_DECLARE_EFFECT_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000521
522 typedef GrGradientEffect INHERITED;
523};
524
525/////////////////////////////////////////////////////////////////////
526
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000527GR_DEFINE_EFFECT_TEST(GrRadialGradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000528
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000529GrEffectRef* GrRadialGradient::TestCreate(SkRandom* random,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000530 GrContext* context,
bsalomon@google.comc26d94f2013-03-25 18:19:00 +0000531 const GrDrawTargetCaps&,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000532 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000533 SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
534 SkScalar radius = random->nextUScalar1();
535
536 SkColor colors[kMaxRandomGradientColors];
537 SkScalar stopsArray[kMaxRandomGradientColors];
538 SkScalar* stops = stopsArray;
539 SkShader::TileMode tm;
540 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
541 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
542 colors, stops, colorCount,
543 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000544 SkPaint paint;
545 return shader->asNewEffect(context, paint);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000546}
547
548/////////////////////////////////////////////////////////////////////
549
bsalomon@google.comf78df332012-10-29 12:43:38 +0000550void GrGLRadialGradient::emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000551 const GrDrawEffect&,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000552 EffectKey key,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000553 const char* outputColor,
554 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000555 const TransformedCoordsArray& coords,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000556 const TextureSamplerArray& samplers) {
bsalomon@google.com82d12232013-09-09 15:36:26 +0000557 this->emitUniforms(builder, key);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000558 SkString t("length(");
bsalomon@google.com77af6802013-10-02 13:04:56 +0000559 t.append(builder->ensureFSCoords2D(coords, 0));
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000560 t.append(")");
bsalomon@google.com82d12232013-09-09 15:36:26 +0000561 this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000562}
563
rileya@google.comd7cc6512012-07-27 14:00:39 +0000564/////////////////////////////////////////////////////////////////////
565
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000566GrEffectRef* SkRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
bsalomon@google.com00835cc2013-01-14 17:07:22 +0000567 SkASSERT(NULL != context);
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000568
569 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000570 if (!this->getLocalMatrix().invert(&matrix)) {
humper@google.com84831ac2013-01-14 22:09:54 +0000571 return NULL;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000572 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000573 matrix.postConcat(fPtsToUnit);
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000574 return GrRadialGradient::Create(context, *this, matrix, fTileMode);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000575}
576
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000577#else
578
bsalomon@google.com5d2cd202013-01-16 15:31:06 +0000579GrEffectRef* SkRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000580 SkDEBUGFAIL("Should not call in GPU-less build");
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000581 return NULL;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000582}
583
584#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000585
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000586#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000587void SkRadialGradient::toString(SkString* str) const {
588 str->append("SkRadialGradient: (");
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000589
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000590 str->append("center: (");
591 str->appendScalar(fCenter.fX);
592 str->append(", ");
593 str->appendScalar(fCenter.fY);
594 str->append(") radius: ");
595 str->appendScalar(fRadius);
596 str->append(" ");
597
598 this->INHERITED::toString(str);
599
600 str->append(")");
601}
602#endif