blob: a6a2e366b22a3f90cfaf7723a061c959b61c820a [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
reedaddf2ed2014-08-11 08:28:24 -0700148SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor& desc)
149 : SkGradientShaderBase(desc)
150 , fCenter(center)
151 , fRadius(radius)
rileya@google.com589708b2012-07-26 20:04:23 +0000152{
153 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
154 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
155
156 rad_to_unit_matrix(center, radius, &fPtsToUnit);
157}
158
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000159size_t SkRadialGradient::contextSize() const {
160 return sizeof(RadialGradientContext);
161}
162
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +0000163SkShader::Context* SkRadialGradient::onCreateContext(const ContextRec& rec, void* storage) const {
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000164 return SkNEW_PLACEMENT_ARGS(storage, RadialGradientContext, (*this, rec));
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000165}
166
167SkRadialGradient::RadialGradientContext::RadialGradientContext(
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000168 const SkRadialGradient& shader, const ContextRec& rec)
169 : INHERITED(shader, rec) {}
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000170
171void SkRadialGradient::RadialGradientContext::shadeSpan16(int x, int y, uint16_t* dstCParam,
172 int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000173 SkASSERT(count > 0);
174
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000175 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
176
rileya@google.com589708b2012-07-26 20:04:23 +0000177 uint16_t* SK_RESTRICT dstC = dstCParam;
178
179 SkPoint srcPt;
180 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000181 TileProc proc = radialGradient.fTileProc;
182 const uint16_t* SK_RESTRICT cache = fCache->getCache16();
reed@google.com55853db2013-02-01 19:34:59 +0000183 int toggle = init_dither_toggle16(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000184
185 if (fDstToIndexClass != kPerspective_MatrixClass) {
186 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
187 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
188
189 SkScalar sdx = fDstToIndex.getScaleX();
190 SkScalar sdy = fDstToIndex.getSkewY();
191
192 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
193 SkFixed storage[2];
194 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
195 &storage[0], &storage[1]);
196 sdx = SkFixedToScalar(storage[0]);
197 sdy = SkFixedToScalar(storage[1]);
198 } else {
199 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
200 }
201
202 RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000203 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000204 shadeProc = shadeSpan16_radial_clamp;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000205 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000206 shadeProc = shadeSpan16_radial_mirror;
207 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000208 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000209 }
210 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
211 cache, toggle, count);
212 } else { // perspective case
213 SkScalar dstX = SkIntToScalar(x);
214 SkScalar dstY = SkIntToScalar(y);
215 do {
216 dstProc(fDstToIndex, dstX, dstY, &srcPt);
217 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
218 SkASSERT(fi <= 0xFFFF);
219
220 int index = fi >> (16 - kCache16Bits);
221 *dstC++ = cache[toggle + index];
reed@google.com55853db2013-02-01 19:34:59 +0000222 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000223
224 dstX += SK_Scalar1;
225 } while (--count != 0);
226 }
227}
228
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000229SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap,
rileya@google.com589708b2012-07-26 20:04:23 +0000230 SkMatrix* matrix, SkShader::TileMode* xy) const {
231 if (bitmap) {
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000232 this->getGradientTableBitmap(bitmap);
rileya@google.com589708b2012-07-26 20:04:23 +0000233 }
234 if (matrix) {
reed@google.com3c2102c2013-02-01 12:59:40 +0000235 matrix->setScale(SkIntToScalar(kCache32Count),
236 SkIntToScalar(kCache32Count));
rileya@google.com589708b2012-07-26 20:04:23 +0000237 matrix->preConcat(fPtsToUnit);
238 }
239 if (xy) {
240 xy[0] = fTileMode;
241 xy[1] = kClamp_TileMode;
242 }
243 return kRadial_BitmapType;
244}
245
246SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
247 if (info) {
248 commonAsAGradient(info);
249 info->fPoint[0] = fCenter;
250 info->fRadius[0] = fRadius;
251 }
252 return kRadial_GradientType;
253}
254
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000255SkRadialGradient::SkRadialGradient(SkReadBuffer& buffer)
rileya@google.com589708b2012-07-26 20:04:23 +0000256 : INHERITED(buffer),
257 fCenter(buffer.readPoint()),
258 fRadius(buffer.readScalar()) {
259}
260
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000261void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000262 this->INHERITED::flatten(buffer);
263 buffer.writePoint(fCenter);
264 buffer.writeScalar(fRadius);
265}
266
267namespace {
268
269inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
270 // fast, overly-conservative test: checks unit square instead
271 // of unit circle
272 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
273 (fx <= -SK_FixedHalf && dx <= 0);
274 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
275 (fy <= -SK_FixedHalf && dy <= 0);
276
277 return xClamped || yClamped;
278}
279
280// Return true if (fx * fy) is always inside the unit circle
281// SkPin32 is expensive, but so are all the SkFixedMul in this test,
282// so it shouldn't be run if count is small.
283inline bool no_need_for_radial_pin(int fx, int dx,
284 int fy, int dy, int count) {
285 SkASSERT(count > 0);
286 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
287 return false;
288 }
289 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
290 return false;
291 }
292 fx += (count - 1) * dx;
293 fy += (count - 1) * dy;
294 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
295 return false;
296 }
297 return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
298}
299
300#define UNPINNED_RADIAL_STEP \
301 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
302 *dstC++ = cache[toggle + \
303 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
reed@google.com55853db2013-02-01 19:34:59 +0000304 toggle = next_dither_toggle(toggle); \
rileya@google.com589708b2012-07-26 20:04:23 +0000305 fx += dx; \
306 fy += dy;
307
308typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
309 SkScalar sfy, SkScalar sdy,
310 SkPMColor* dstC, const SkPMColor* cache,
311 int count, int toggle);
312
313// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
314void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
315 SkScalar sfy, SkScalar sdy,
316 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
317 int count, int toggle) {
318 // Floating point seems to be slower than fixed point,
319 // even when we have float hardware.
320 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
321 SkFixed fx = SkScalarToFixed(sfx) >> 1;
322 SkFixed dx = SkScalarToFixed(sdx) >> 1;
323 SkFixed fy = SkScalarToFixed(sfy) >> 1;
324 SkFixed dy = SkScalarToFixed(sdy) >> 1;
325 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
reed@google.com3c2102c2013-02-01 12:59:40 +0000326 unsigned fi = SkGradientShaderBase::kCache32Count - 1;
rileya@google.com589708b2012-07-26 20:04:23 +0000327 sk_memset32_dither(dstC,
328 cache[toggle + fi],
reed@google.com55853db2013-02-01 19:34:59 +0000329 cache[next_dither_toggle(toggle) + fi],
rileya@google.com589708b2012-07-26 20:04:23 +0000330 count);
331 } else if ((count > 4) &&
332 no_need_for_radial_pin(fx, dx, fy, dy, count)) {
333 unsigned fi;
334 // 4x unroll appears to be no faster than 2x unroll on Linux
335 while (count > 1) {
336 UNPINNED_RADIAL_STEP;
337 UNPINNED_RADIAL_STEP;
338 count -= 2;
339 }
340 if (count) {
341 UNPINNED_RADIAL_STEP;
342 }
reed@google.com60040292013-02-04 18:21:23 +0000343 } else {
rileya@google.com589708b2012-07-26 20:04:23 +0000344 // Specializing for dy == 0 gains us 25% on Skia benchmarks
345 if (dy == 0) {
346 unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
347 yy *= yy;
348 do {
349 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
350 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
351 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
352 *dstC++ = cache[toggle + (sqrt_table[fi] >>
353 SkGradientShaderBase::kSqrt32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000354 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000355 fx += dx;
356 } while (--count != 0);
357 } else {
358 do {
359 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
360 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
361 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
362 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
363 *dstC++ = cache[toggle + (sqrt_table[fi] >>
364 SkGradientShaderBase::kSqrt32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000365 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000366 fx += dx;
367 fy += dy;
368 } while (--count != 0);
369 }
370 }
371}
372
373// Unrolling this loop doesn't seem to help (when float); we're stalling to
374// get the results of the sqrt (?), and don't have enough extra registers to
375// have many in flight.
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000376template <SkFixed (*TileProc)(SkFixed)>
377void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
378 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
379 int count, int toggle) {
rileya@google.com589708b2012-07-26 20:04:23 +0000380 do {
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000381 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
382 const unsigned fi = TileProc(dist);
rileya@google.com589708b2012-07-26 20:04:23 +0000383 SkASSERT(fi <= 0xFFFF);
384 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000385 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000386 fx += dx;
387 fy += dy;
388 } while (--count != 0);
389}
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000390
391void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
392 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
393 int count, int toggle) {
394 shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000395}
396
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000397void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
398 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
399 int count, int toggle) {
400 shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
401}
402
403} // namespace
404
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000405void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y,
406 SkPMColor* SK_RESTRICT dstC, int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000407 SkASSERT(count > 0);
408
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000409 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
410
rileya@google.com589708b2012-07-26 20:04:23 +0000411 SkPoint srcPt;
412 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000413 TileProc proc = radialGradient.fTileProc;
414 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
reed@google.com55853db2013-02-01 19:34:59 +0000415 int toggle = init_dither_toggle(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000416
417 if (fDstToIndexClass != kPerspective_MatrixClass) {
418 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
419 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
420 SkScalar sdx = fDstToIndex.getScaleX();
421 SkScalar sdy = fDstToIndex.getSkewY();
422
423 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
424 SkFixed storage[2];
425 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
426 &storage[0], &storage[1]);
427 sdx = SkFixedToScalar(storage[0]);
428 sdy = SkFixedToScalar(storage[1]);
429 } else {
430 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
431 }
432
433 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000434 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000435 shadeProc = shadeSpan_radial_clamp;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000436 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000437 shadeProc = shadeSpan_radial_mirror;
438 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000439 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000440 }
441 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
442 } else { // perspective case
443 SkScalar dstX = SkIntToScalar(x);
444 SkScalar dstY = SkIntToScalar(y);
445 do {
446 dstProc(fDstToIndex, dstX, dstY, &srcPt);
447 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
448 SkASSERT(fi <= 0xFFFF);
449 *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
450 dstX += SK_Scalar1;
451 } while (--count != 0);
452 }
453}
454
rileya@google.comd7cc6512012-07-27 14:00:39 +0000455/////////////////////////////////////////////////////////////////////
456
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000457#if SK_SUPPORT_GPU
458
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000459#include "GrTBackendEffectFactory.h"
bsalomon848faf02014-07-11 10:01:02 -0700460#include "gl/GrGLShaderBuilder.h"
dandov9de5b512014-06-10 14:38:28 -0700461#include "SkGr.h"
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000462
bsalomon@google.com0707c292012-10-25 21:45:42 +0000463class GrGLRadialGradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000464public:
465
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000466 GrGLRadialGradient(const GrBackendEffectFactory& factory,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000467 const GrDrawEffect&) : INHERITED (factory) { }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000468 virtual ~GrGLRadialGradient() { }
469
bsalomon@google.comf78df332012-10-29 12:43:38 +0000470 virtual void emitCode(GrGLShaderBuilder*,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000471 const GrDrawEffect&,
bsalomon63e99f72014-07-21 08:03:14 -0700472 const GrEffectKey&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000473 const char* outputColor,
474 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000475 const TransformedCoordsArray&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000476 const TextureSamplerArray&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000477
bsalomon63e99f72014-07-21 08:03:14 -0700478 static void GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&, GrEffectKeyBuilder* b) {
479 b->add32(GenBaseGradientKey(drawEffect));
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000480 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000481
482private:
483
bsalomon@google.com0707c292012-10-25 21:45:42 +0000484 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000485
486};
487
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000488/////////////////////////////////////////////////////////////////////
489
490class GrRadialGradient : public GrGradientEffect {
491public:
bsalomon83d081a2014-07-08 09:56:10 -0700492 static GrEffect* Create(GrContext* ctx,
493 const SkRadialGradient& shader,
494 const SkMatrix& matrix,
495 SkShader::TileMode tm) {
bsalomon55fad7a2014-07-08 07:34:20 -0700496 return SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm));
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000497 }
498
499 virtual ~GrRadialGradient() { }
500
501 static const char* Name() { return "Radial Gradient"; }
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000502 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
503 return GrTBackendEffectFactory<GrRadialGradient>::getInstance();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000504 }
505
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000506 typedef GrGLRadialGradient GLEffect;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000507
508private:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000509 GrRadialGradient(GrContext* ctx,
510 const SkRadialGradient& shader,
511 const SkMatrix& matrix,
512 SkShader::TileMode tm)
513 : INHERITED(ctx, shader, matrix, tm) {
514 }
515
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000516 GR_DECLARE_EFFECT_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000517
518 typedef GrGradientEffect INHERITED;
519};
520
521/////////////////////////////////////////////////////////////////////
522
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000523GR_DEFINE_EFFECT_TEST(GrRadialGradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000524
bsalomon83d081a2014-07-08 09:56:10 -0700525GrEffect* GrRadialGradient::TestCreate(SkRandom* random,
526 GrContext* context,
527 const GrDrawTargetCaps&,
528 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000529 SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
530 SkScalar radius = random->nextUScalar1();
531
532 SkColor colors[kMaxRandomGradientColors];
533 SkScalar stopsArray[kMaxRandomGradientColors];
534 SkScalar* stops = stopsArray;
535 SkShader::TileMode tm;
536 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
537 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
538 colors, stops, colorCount,
539 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000540 SkPaint paint;
bsalomon83d081a2014-07-08 09:56:10 -0700541 GrColor paintColor;
542 GrEffect* effect;
543 SkAssertResult(shader->asNewEffect(context, paint, NULL, &paintColor, &effect));
dandov9de5b512014-06-10 14:38:28 -0700544 return effect;
bsalomon@google.comd4726202012-08-03 14:34:46 +0000545}
546
547/////////////////////////////////////////////////////////////////////
548
bsalomon@google.comf78df332012-10-29 12:43:38 +0000549void GrGLRadialGradient::emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000550 const GrDrawEffect&,
bsalomon63e99f72014-07-21 08:03:14 -0700551 const GrEffectKey& key,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000552 const char* outputColor,
553 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000554 const TransformedCoordsArray& coords,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000555 const TextureSamplerArray& samplers) {
bsalomon63e99f72014-07-21 08:03:14 -0700556 uint32_t baseKey = key.get32(0);
557 this->emitUniforms(builder, baseKey);
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(")");
bsalomon63e99f72014-07-21 08:03:14 -0700561 this->emitColor(builder, t.c_str(), baseKey, outputColor, inputColor, samplers);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000562}
563
rileya@google.comd7cc6512012-07-27 14:00:39 +0000564/////////////////////////////////////////////////////////////////////
565
dandov9de5b512014-06-10 14:38:28 -0700566bool SkRadialGradient::asNewEffect(GrContext* context, const SkPaint& paint,
bsalomon83d081a2014-07-08 09:56:10 -0700567 const SkMatrix* localMatrix, GrColor* paintColor,
568 GrEffect** effect) const {
bsalomon@google.com00835cc2013-01-14 17:07:22 +0000569 SkASSERT(NULL != context);
dandov9de5b512014-06-10 14:38:28 -0700570
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000571 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000572 if (!this->getLocalMatrix().invert(&matrix)) {
dandov9de5b512014-06-10 14:38:28 -0700573 return false;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000574 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000575 if (localMatrix) {
576 SkMatrix inv;
577 if (!localMatrix->invert(&inv)) {
dandov9de5b512014-06-10 14:38:28 -0700578 return false;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000579 }
580 matrix.postConcat(inv);
581 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000582 matrix.postConcat(fPtsToUnit);
dandov9de5b512014-06-10 14:38:28 -0700583
bsalomon83d081a2014-07-08 09:56:10 -0700584 *paintColor = SkColor2GrColorJustAlpha(paint.getColor());
585 *effect = GrRadialGradient::Create(context, *this, matrix, fTileMode);
dandov9de5b512014-06-10 14:38:28 -0700586
587 return true;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000588}
589
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000590#else
591
dandov9de5b512014-06-10 14:38:28 -0700592bool SkRadialGradient::asNewEffect(GrContext* context, const SkPaint& paint,
bsalomon83d081a2014-07-08 09:56:10 -0700593 const SkMatrix* localMatrix, GrColor* paintColor,
594 GrEffect** effect) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000595 SkDEBUGFAIL("Should not call in GPU-less build");
dandov9de5b512014-06-10 14:38:28 -0700596 return false;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000597}
598
599#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000600
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000601#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000602void SkRadialGradient::toString(SkString* str) const {
603 str->append("SkRadialGradient: (");
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000604
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000605 str->append("center: (");
606 str->appendScalar(fCenter.fX);
607 str->append(", ");
608 str->appendScalar(fCenter.fY);
609 str->append(") radius: ");
610 str->appendScalar(fRadius);
611 str->append(" ");
612
613 this->INHERITED::toString(str);
614
615 str->append(")");
616}
617#endif