blob: bc2ea3b92b988e152d0ad95f3a815fb00e884d50 [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
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000160size_t SkRadialGradient::contextSize() const {
161 return sizeof(RadialGradientContext);
162}
163
164SkShader::Context* SkRadialGradient::createContext(const SkBitmap& device, const SkPaint& paint,
165 const SkMatrix& matrix, void* storage) const {
166 if (!this->validContext(device, paint, matrix)) {
167 return NULL;
168 }
169
170 return SkNEW_PLACEMENT_ARGS(storage, RadialGradientContext, (*this, device, paint, matrix));
171}
172
173SkRadialGradient::RadialGradientContext::RadialGradientContext(
174 const SkRadialGradient& shader, const SkBitmap& device,
175 const SkPaint& paint, const SkMatrix& matrix)
176 : INHERITED(shader, device, paint, matrix) {}
177
178void SkRadialGradient::RadialGradientContext::shadeSpan16(int x, int y, uint16_t* dstCParam,
179 int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000180 SkASSERT(count > 0);
181
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000182 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
183
rileya@google.com589708b2012-07-26 20:04:23 +0000184 uint16_t* SK_RESTRICT dstC = dstCParam;
185
186 SkPoint srcPt;
187 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000188 TileProc proc = radialGradient.fTileProc;
189 const uint16_t* SK_RESTRICT cache = fCache->getCache16();
reed@google.com55853db2013-02-01 19:34:59 +0000190 int toggle = init_dither_toggle16(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000191
192 if (fDstToIndexClass != kPerspective_MatrixClass) {
193 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
194 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
195
196 SkScalar sdx = fDstToIndex.getScaleX();
197 SkScalar sdy = fDstToIndex.getSkewY();
198
199 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
200 SkFixed storage[2];
201 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
202 &storage[0], &storage[1]);
203 sdx = SkFixedToScalar(storage[0]);
204 sdy = SkFixedToScalar(storage[1]);
205 } else {
206 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
207 }
208
209 RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000210 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000211 shadeProc = shadeSpan16_radial_clamp;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000212 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000213 shadeProc = shadeSpan16_radial_mirror;
214 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000215 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000216 }
217 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
218 cache, toggle, count);
219 } else { // perspective case
220 SkScalar dstX = SkIntToScalar(x);
221 SkScalar dstY = SkIntToScalar(y);
222 do {
223 dstProc(fDstToIndex, dstX, dstY, &srcPt);
224 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
225 SkASSERT(fi <= 0xFFFF);
226
227 int index = fi >> (16 - kCache16Bits);
228 *dstC++ = cache[toggle + index];
reed@google.com55853db2013-02-01 19:34:59 +0000229 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000230
231 dstX += SK_Scalar1;
232 } while (--count != 0);
233 }
234}
235
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000236SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap,
rileya@google.com589708b2012-07-26 20:04:23 +0000237 SkMatrix* matrix, SkShader::TileMode* xy) const {
238 if (bitmap) {
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000239 this->getGradientTableBitmap(bitmap);
rileya@google.com589708b2012-07-26 20:04:23 +0000240 }
241 if (matrix) {
reed@google.com3c2102c2013-02-01 12:59:40 +0000242 matrix->setScale(SkIntToScalar(kCache32Count),
243 SkIntToScalar(kCache32Count));
rileya@google.com589708b2012-07-26 20:04:23 +0000244 matrix->preConcat(fPtsToUnit);
245 }
246 if (xy) {
247 xy[0] = fTileMode;
248 xy[1] = kClamp_TileMode;
249 }
250 return kRadial_BitmapType;
251}
252
253SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
254 if (info) {
255 commonAsAGradient(info);
256 info->fPoint[0] = fCenter;
257 info->fRadius[0] = fRadius;
258 }
259 return kRadial_GradientType;
260}
261
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000262SkRadialGradient::SkRadialGradient(SkReadBuffer& buffer)
rileya@google.com589708b2012-07-26 20:04:23 +0000263 : INHERITED(buffer),
264 fCenter(buffer.readPoint()),
265 fRadius(buffer.readScalar()) {
266}
267
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000268void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000269 this->INHERITED::flatten(buffer);
270 buffer.writePoint(fCenter);
271 buffer.writeScalar(fRadius);
272}
273
274namespace {
275
276inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
277 // fast, overly-conservative test: checks unit square instead
278 // of unit circle
279 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
280 (fx <= -SK_FixedHalf && dx <= 0);
281 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
282 (fy <= -SK_FixedHalf && dy <= 0);
283
284 return xClamped || yClamped;
285}
286
287// Return true if (fx * fy) is always inside the unit circle
288// SkPin32 is expensive, but so are all the SkFixedMul in this test,
289// so it shouldn't be run if count is small.
290inline bool no_need_for_radial_pin(int fx, int dx,
291 int fy, int dy, int count) {
292 SkASSERT(count > 0);
293 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
294 return false;
295 }
296 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
297 return false;
298 }
299 fx += (count - 1) * dx;
300 fy += (count - 1) * dy;
301 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
302 return false;
303 }
304 return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
305}
306
307#define UNPINNED_RADIAL_STEP \
308 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
309 *dstC++ = cache[toggle + \
310 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
reed@google.com55853db2013-02-01 19:34:59 +0000311 toggle = next_dither_toggle(toggle); \
rileya@google.com589708b2012-07-26 20:04:23 +0000312 fx += dx; \
313 fy += dy;
314
315typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
316 SkScalar sfy, SkScalar sdy,
317 SkPMColor* dstC, const SkPMColor* cache,
318 int count, int toggle);
319
320// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
321void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
322 SkScalar sfy, SkScalar sdy,
323 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
324 int count, int toggle) {
325 // Floating point seems to be slower than fixed point,
326 // even when we have float hardware.
327 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
328 SkFixed fx = SkScalarToFixed(sfx) >> 1;
329 SkFixed dx = SkScalarToFixed(sdx) >> 1;
330 SkFixed fy = SkScalarToFixed(sfy) >> 1;
331 SkFixed dy = SkScalarToFixed(sdy) >> 1;
332 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
reed@google.com3c2102c2013-02-01 12:59:40 +0000333 unsigned fi = SkGradientShaderBase::kCache32Count - 1;
rileya@google.com589708b2012-07-26 20:04:23 +0000334 sk_memset32_dither(dstC,
335 cache[toggle + fi],
reed@google.com55853db2013-02-01 19:34:59 +0000336 cache[next_dither_toggle(toggle) + fi],
rileya@google.com589708b2012-07-26 20:04:23 +0000337 count);
338 } else if ((count > 4) &&
339 no_need_for_radial_pin(fx, dx, fy, dy, count)) {
340 unsigned fi;
341 // 4x unroll appears to be no faster than 2x unroll on Linux
342 while (count > 1) {
343 UNPINNED_RADIAL_STEP;
344 UNPINNED_RADIAL_STEP;
345 count -= 2;
346 }
347 if (count) {
348 UNPINNED_RADIAL_STEP;
349 }
reed@google.com60040292013-02-04 18:21:23 +0000350 } else {
rileya@google.com589708b2012-07-26 20:04:23 +0000351 // Specializing for dy == 0 gains us 25% on Skia benchmarks
352 if (dy == 0) {
353 unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
354 yy *= yy;
355 do {
356 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
357 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
358 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
359 *dstC++ = cache[toggle + (sqrt_table[fi] >>
360 SkGradientShaderBase::kSqrt32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000361 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000362 fx += dx;
363 } while (--count != 0);
364 } else {
365 do {
366 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
367 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
368 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
369 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
370 *dstC++ = cache[toggle + (sqrt_table[fi] >>
371 SkGradientShaderBase::kSqrt32Shift)];
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 }
377 }
378}
379
380// Unrolling this loop doesn't seem to help (when float); we're stalling to
381// get the results of the sqrt (?), and don't have enough extra registers to
382// have many in flight.
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000383template <SkFixed (*TileProc)(SkFixed)>
384void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
385 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
386 int count, int toggle) {
rileya@google.com589708b2012-07-26 20:04:23 +0000387 do {
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000388 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
389 const unsigned fi = TileProc(dist);
rileya@google.com589708b2012-07-26 20:04:23 +0000390 SkASSERT(fi <= 0xFFFF);
391 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000392 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000393 fx += dx;
394 fy += dy;
395 } while (--count != 0);
396}
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000397
398void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
399 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
400 int count, int toggle) {
401 shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000402}
403
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000404void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
405 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
406 int count, int toggle) {
407 shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
408}
409
410} // namespace
411
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000412void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y,
413 SkPMColor* SK_RESTRICT dstC, int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000414 SkASSERT(count > 0);
415
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000416 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
417
rileya@google.com589708b2012-07-26 20:04:23 +0000418 SkPoint srcPt;
419 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000420 TileProc proc = radialGradient.fTileProc;
421 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
reed@google.com55853db2013-02-01 19:34:59 +0000422 int toggle = init_dither_toggle(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000423
424 if (fDstToIndexClass != kPerspective_MatrixClass) {
425 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
426 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
427 SkScalar sdx = fDstToIndex.getScaleX();
428 SkScalar sdy = fDstToIndex.getSkewY();
429
430 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
431 SkFixed storage[2];
432 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
433 &storage[0], &storage[1]);
434 sdx = SkFixedToScalar(storage[0]);
435 sdy = SkFixedToScalar(storage[1]);
436 } else {
437 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
438 }
439
440 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000441 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000442 shadeProc = shadeSpan_radial_clamp;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000443 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000444 shadeProc = shadeSpan_radial_mirror;
445 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000446 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000447 }
448 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
449 } else { // perspective case
450 SkScalar dstX = SkIntToScalar(x);
451 SkScalar dstY = SkIntToScalar(y);
452 do {
453 dstProc(fDstToIndex, dstX, dstY, &srcPt);
454 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
455 SkASSERT(fi <= 0xFFFF);
456 *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
457 dstX += SK_Scalar1;
458 } while (--count != 0);
459 }
460}
461
rileya@google.comd7cc6512012-07-27 14:00:39 +0000462/////////////////////////////////////////////////////////////////////
463
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000464#if SK_SUPPORT_GPU
465
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000466#include "GrTBackendEffectFactory.h"
467
bsalomon@google.com0707c292012-10-25 21:45:42 +0000468class GrGLRadialGradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000469public:
470
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000471 GrGLRadialGradient(const GrBackendEffectFactory& factory,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000472 const GrDrawEffect&) : INHERITED (factory) { }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000473 virtual ~GrGLRadialGradient() { }
474
bsalomon@google.comf78df332012-10-29 12:43:38 +0000475 virtual void emitCode(GrGLShaderBuilder*,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000476 const GrDrawEffect&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000477 EffectKey,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000478 const char* outputColor,
479 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000480 const TransformedCoordsArray&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000481 const TextureSamplerArray&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000482
bsalomon@google.comc7818882013-03-20 19:19:53 +0000483 static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
bsalomon@google.com82d12232013-09-09 15:36:26 +0000484 return GenBaseGradientKey(drawEffect);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000485 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000486
487private:
488
bsalomon@google.com0707c292012-10-25 21:45:42 +0000489 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000490
491};
492
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000493/////////////////////////////////////////////////////////////////////
494
495class GrRadialGradient : public GrGradientEffect {
496public:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000497 static GrEffectRef* Create(GrContext* ctx,
498 const SkRadialGradient& shader,
499 const SkMatrix& matrix,
500 SkShader::TileMode tm) {
bsalomon@google.com6340a412013-01-22 19:55:59 +0000501 AutoEffectUnref effect(SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm)));
bsalomon@google.coma1ebbe42013-01-16 15:51:47 +0000502 return CreateEffectRef(effect);
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000503 }
504
505 virtual ~GrRadialGradient() { }
506
507 static const char* Name() { return "Radial Gradient"; }
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000508 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
509 return GrTBackendEffectFactory<GrRadialGradient>::getInstance();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000510 }
511
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000512 typedef GrGLRadialGradient GLEffect;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000513
514private:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000515 GrRadialGradient(GrContext* ctx,
516 const SkRadialGradient& shader,
517 const SkMatrix& matrix,
518 SkShader::TileMode tm)
519 : INHERITED(ctx, shader, matrix, tm) {
520 }
521
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000522 GR_DECLARE_EFFECT_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000523
524 typedef GrGradientEffect INHERITED;
525};
526
527/////////////////////////////////////////////////////////////////////
528
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000529GR_DEFINE_EFFECT_TEST(GrRadialGradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000530
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000531GrEffectRef* GrRadialGradient::TestCreate(SkRandom* random,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000532 GrContext* context,
bsalomon@google.comc26d94f2013-03-25 18:19:00 +0000533 const GrDrawTargetCaps&,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000534 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000535 SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
536 SkScalar radius = random->nextUScalar1();
537
538 SkColor colors[kMaxRandomGradientColors];
539 SkScalar stopsArray[kMaxRandomGradientColors];
540 SkScalar* stops = stopsArray;
541 SkShader::TileMode tm;
542 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
543 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
544 colors, stops, colorCount,
545 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000546 SkPaint paint;
547 return shader->asNewEffect(context, paint);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000548}
549
550/////////////////////////////////////////////////////////////////////
551
bsalomon@google.comf78df332012-10-29 12:43:38 +0000552void GrGLRadialGradient::emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000553 const GrDrawEffect&,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000554 EffectKey key,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000555 const char* outputColor,
556 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000557 const TransformedCoordsArray& coords,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000558 const TextureSamplerArray& samplers) {
bsalomon@google.com82d12232013-09-09 15:36:26 +0000559 this->emitUniforms(builder, key);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000560 SkString t("length(");
bsalomon@google.com77af6802013-10-02 13:04:56 +0000561 t.append(builder->ensureFSCoords2D(coords, 0));
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000562 t.append(")");
bsalomon@google.com82d12232013-09-09 15:36:26 +0000563 this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000564}
565
rileya@google.comd7cc6512012-07-27 14:00:39 +0000566/////////////////////////////////////////////////////////////////////
567
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000568GrEffectRef* SkRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
bsalomon@google.com00835cc2013-01-14 17:07:22 +0000569 SkASSERT(NULL != context);
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000570
571 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000572 if (!this->getLocalMatrix().invert(&matrix)) {
humper@google.com84831ac2013-01-14 22:09:54 +0000573 return NULL;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000574 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000575 matrix.postConcat(fPtsToUnit);
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000576 return GrRadialGradient::Create(context, *this, matrix, fTileMode);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000577}
578
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000579#else
580
bsalomon@google.com5d2cd202013-01-16 15:31:06 +0000581GrEffectRef* SkRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000582 SkDEBUGFAIL("Should not call in GPU-less build");
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000583 return NULL;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000584}
585
586#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000587
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000588#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000589void SkRadialGradient::toString(SkString* str) const {
590 str->append("SkRadialGradient: (");
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000591
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000592 str->append("center: (");
593 str->appendScalar(fCenter.fX);
594 str->append(", ");
595 str->appendScalar(fCenter.fY);
596 str->append(") radius: ");
597 str->appendScalar(fRadius);
598 str->append(" ");
599
600 this->INHERITED::toString(str);
601
602 str->append(")");
603}
604#endif