blob: 7c0e8f603174bd87f02746ebc3f28450bc067e59 [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
reed9fa60da2014-08-21 07:59:51 -0700255SkFlattenable* SkRadialGradient::CreateProc(SkReadBuffer& buffer) {
256 DescriptorScope desc;
257 if (!desc.unflatten(buffer)) {
258 return NULL;
259 }
260 const SkPoint center = buffer.readPoint();
261 const SkScalar radius = buffer.readScalar();
262 return SkGradientShader::CreateRadial(center, radius, desc.fColors, desc.fPos, desc.fCount,
263 desc.fTileMode, desc.fGradFlags, desc.fLocalMatrix);
264}
rileya@google.com589708b2012-07-26 20:04:23 +0000265
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
joshualittb0a8a372014-09-23 09:50:21 -0700464#include "GrTBackendProcessorFactory.h"
joshualitt30ba4362014-08-21 20:18:45 -0700465#include "gl/builders/GrGLProgramBuilder.h"
dandov9de5b512014-06-10 14:38:28 -0700466#include "SkGr.h"
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000467
bsalomon@google.com0707c292012-10-25 21:45:42 +0000468class GrGLRadialGradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000469public:
470
joshualittb0a8a372014-09-23 09:50:21 -0700471 GrGLRadialGradient(const GrBackendProcessorFactory& factory,
472 const GrProcessor&) : INHERITED (factory) { }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000473 virtual ~GrGLRadialGradient() { }
474
joshualitt15988992014-10-09 15:04:05 -0700475 virtual void emitCode(GrGLFPBuilder*,
joshualittb0a8a372014-09-23 09:50:21 -0700476 const GrFragmentProcessor&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000477 const char* outputColor,
478 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000479 const TransformedCoordsArray&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000480 const TextureSamplerArray&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000481
joshualittb0a8a372014-09-23 09:50:21 -0700482 static void GenKey(const GrProcessor& processor, const GrGLCaps&, GrProcessorKeyBuilder* b) {
483 b->add32(GenBaseGradientKey(processor));
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000484 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000485
486private:
487
bsalomon@google.com0707c292012-10-25 21:45:42 +0000488 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000489
490};
491
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000492/////////////////////////////////////////////////////////////////////
493
494class GrRadialGradient : public GrGradientEffect {
495public:
joshualittb0a8a372014-09-23 09:50:21 -0700496 static GrFragmentProcessor* Create(GrContext* ctx,
497 const SkRadialGradient& shader,
498 const SkMatrix& matrix,
499 SkShader::TileMode tm) {
bsalomon55fad7a2014-07-08 07:34:20 -0700500 return SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm));
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000501 }
502
503 virtual ~GrRadialGradient() { }
504
505 static const char* Name() { return "Radial Gradient"; }
joshualittb0a8a372014-09-23 09:50:21 -0700506 virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE {
507 return GrTBackendFragmentProcessorFactory<GrRadialGradient>::getInstance();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000508 }
509
joshualittb0a8a372014-09-23 09:50:21 -0700510 typedef GrGLRadialGradient GLProcessor;
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
joshualittb0a8a372014-09-23 09:50:21 -0700520 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000521
522 typedef GrGradientEffect INHERITED;
523};
524
525/////////////////////////////////////////////////////////////////////
526
joshualittb0a8a372014-09-23 09:50:21 -0700527GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadialGradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000528
joshualittb0a8a372014-09-23 09:50:21 -0700529GrFragmentProcessor* GrRadialGradient::TestCreate(SkRandom* random,
530 GrContext* context,
531 const GrDrawTargetCaps&,
532 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;
bsalomon83d081a2014-07-08 09:56:10 -0700545 GrColor paintColor;
joshualittb0a8a372014-09-23 09:50:21 -0700546 GrFragmentProcessor* fp;
547 SkAssertResult(shader->asFragmentProcessor(context, paint, NULL, &paintColor, &fp));
548 return fp;
bsalomon@google.comd4726202012-08-03 14:34:46 +0000549}
550
551/////////////////////////////////////////////////////////////////////
552
joshualitt15988992014-10-09 15:04:05 -0700553void GrGLRadialGradient::emitCode(GrGLFPBuilder* builder,
joshualitt60030bc2014-11-25 14:21:55 -0800554 const GrFragmentProcessor& fp,
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) {
joshualitt60030bc2014-11-25 14:21:55 -0800559 const GrGradientEffect& ge = fp.cast<GrGradientEffect>();
560 this->emitUniforms(builder, ge);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000561 SkString t("length(");
joshualitt30ba4362014-08-21 20:18:45 -0700562 t.append(builder->getFragmentShaderBuilder()->ensureFSCoords2D(coords, 0));
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000563 t.append(")");
joshualitt60030bc2014-11-25 14:21:55 -0800564 this->emitColor(builder, ge, t.c_str(), outputColor, inputColor, samplers);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000565}
566
rileya@google.comd7cc6512012-07-27 14:00:39 +0000567/////////////////////////////////////////////////////////////////////
568
joshualittb0a8a372014-09-23 09:50:21 -0700569bool SkRadialGradient::asFragmentProcessor(GrContext* context, const SkPaint& paint,
570 const SkMatrix* localMatrix, GrColor* paintColor,
571 GrFragmentProcessor** fp) const {
bsalomon49f085d2014-09-05 13:34:00 -0700572 SkASSERT(context);
mtklein3f3b3d02014-12-01 11:47:08 -0800573
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000574 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000575 if (!this->getLocalMatrix().invert(&matrix)) {
dandov9de5b512014-06-10 14:38:28 -0700576 return false;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000577 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000578 if (localMatrix) {
579 SkMatrix inv;
580 if (!localMatrix->invert(&inv)) {
dandov9de5b512014-06-10 14:38:28 -0700581 return false;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000582 }
583 matrix.postConcat(inv);
584 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000585 matrix.postConcat(fPtsToUnit);
mtklein3f3b3d02014-12-01 11:47:08 -0800586
bsalomon83d081a2014-07-08 09:56:10 -0700587 *paintColor = SkColor2GrColorJustAlpha(paint.getColor());
joshualittb0a8a372014-09-23 09:50:21 -0700588 *fp = GrRadialGradient::Create(context, *this, matrix, fTileMode);
mtklein3f3b3d02014-12-01 11:47:08 -0800589
dandov9de5b512014-06-10 14:38:28 -0700590 return true;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000591}
592
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000593#else
594
joshualittb0a8a372014-09-23 09:50:21 -0700595bool SkRadialGradient::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix*, GrColor*,
596 GrFragmentProcessor**) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000597 SkDEBUGFAIL("Should not call in GPU-less build");
dandov9de5b512014-06-10 14:38:28 -0700598 return false;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000599}
600
601#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000602
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000603#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000604void SkRadialGradient::toString(SkString* str) const {
605 str->append("SkRadialGradient: (");
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000606
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000607 str->append("center: (");
608 str->appendScalar(fCenter.fX);
609 str->append(", ");
610 str->appendScalar(fCenter.fY);
611 str->append(") radius: ");
612 str->appendScalar(fRadius);
613 str->append(" ");
614
615 this->INHERITED::toString(str);
616
617 str->append(")");
618}
619#endif