blob: b25a8750a2329cf799528f3c3c217151c2858e78 [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
mtkleincc695fe2014-12-10 10:29:19 -080015SK_COMPILE_ASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE, SqrtTableSizesMatch);
16
reed@google.com4ec5c952013-12-19 15:00:18 +000017#if 0
rileya@google.com589708b2012-07-26 20:04:23 +000018
19#include <stdio.h>
20
21void SkRadialGradient_BuildTable() {
22 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
23
24 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
25 SkASSERT(file);
26 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
27
28 for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
29 if ((i & 15) == 0) {
30 ::fprintf(file, "\t");
31 }
32
33 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
34
35 ::fprintf(file, "0x%02X", value);
36 if (i < kSQRT_TABLE_SIZE-1) {
37 ::fprintf(file, ", ");
38 }
39 if ((i & 15) == 15) {
40 ::fprintf(file, "\n");
41 }
42 }
43 ::fprintf(file, "};\n");
44 ::fclose(file);
45}
46
47#endif
48
49namespace {
50
commit-bot@chromium.org34150b42013-10-16 18:59:44 +000051// GCC doesn't like using static functions as template arguments. So force these to be non-static.
52inline SkFixed mirror_tileproc_nonstatic(SkFixed x) {
53 return mirror_tileproc(x);
54}
55
56inline SkFixed repeat_tileproc_nonstatic(SkFixed x) {
57 return repeat_tileproc(x);
58}
59
mtkleincc695fe2014-12-10 10:29:19 -080060SkMatrix rad_to_unit_matrix(const SkPoint& center, SkScalar radius) {
rileya@google.com589708b2012-07-26 20:04:23 +000061 SkScalar inv = SkScalarInvert(radius);
62
mtkleincc695fe2014-12-10 10:29:19 -080063 SkMatrix matrix;
64 matrix.setTranslate(-center.fX, -center.fY);
65 matrix.postScale(inv, inv);
66 return matrix;
rileya@google.com589708b2012-07-26 20:04:23 +000067}
68
69typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx,
70 SkScalar sfy, SkScalar sdy,
71 uint16_t* dstC, const uint16_t* cache,
72 int toggle, int count);
73
74void shadeSpan16_radial_clamp(SkScalar sfx, SkScalar sdx,
75 SkScalar sfy, SkScalar sdy,
76 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
77 int toggle, int count) {
78 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
79
80 /* knock these down so we can pin against +- 0x7FFF, which is an
81 immediate load, rather than 0xFFFF which is slower. This is a
82 compromise, since it reduces our precision, but that appears
83 to be visually OK. If we decide this is OK for all of our cases,
84 we could (it seems) put this scale-down into fDstToIndex,
85 to avoid having to do these extra shifts each time.
86 */
87 SkFixed fx = SkScalarToFixed(sfx) >> 1;
88 SkFixed dx = SkScalarToFixed(sdx) >> 1;
89 SkFixed fy = SkScalarToFixed(sfy) >> 1;
90 SkFixed dy = SkScalarToFixed(sdy) >> 1;
91 // might perform this check for the other modes,
92 // but the win will be a smaller % of the total
93 if (dy == 0) {
94 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
95 fy *= fy;
96 do {
97 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
98 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
99 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
100 fx += dx;
101 *dstC++ = cache[toggle +
102 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000103 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000104 } while (--count != 0);
105 } else {
106 do {
107 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
108 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
109 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
110 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
111 fx += dx;
112 fy += dy;
113 *dstC++ = cache[toggle +
114 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000115 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000116 } while (--count != 0);
117 }
118}
119
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000120template <SkFixed (*TileProc)(SkFixed)>
121void shadeSpan16_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
122 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
123 int toggle, int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000124 do {
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000125 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
126 const unsigned fi = TileProc(dist);
rileya@google.com589708b2012-07-26 20:04:23 +0000127 SkASSERT(fi <= 0xFFFF);
128 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000129 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000130 fx += dx;
131 fy += dy;
rileya@google.com589708b2012-07-26 20:04:23 +0000132 } while (--count != 0);
133}
134
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000135void shadeSpan16_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
136 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
137 int toggle, int count) {
138 shadeSpan16_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, toggle, count);
rileya@google.com589708b2012-07-26 20:04:23 +0000139}
140
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000141void shadeSpan16_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
142 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
143 int toggle, int count) {
144 shadeSpan16_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, toggle, count);
145}
146
147} // namespace
148
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000149/////////////////////////////////////////////////////////////////////
150
reedaddf2ed2014-08-11 08:28:24 -0700151SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor& desc)
mtkleincc695fe2014-12-10 10:29:19 -0800152 : SkGradientShaderBase(desc, rad_to_unit_matrix(center, radius))
reedaddf2ed2014-08-11 08:28:24 -0700153 , fCenter(center)
mtkleincc695fe2014-12-10 10:29:19 -0800154 , fRadius(radius) {
rileya@google.com589708b2012-07-26 20:04:23 +0000155}
156
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000157size_t SkRadialGradient::contextSize() const {
158 return sizeof(RadialGradientContext);
159}
160
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +0000161SkShader::Context* SkRadialGradient::onCreateContext(const ContextRec& rec, void* storage) const {
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000162 return SkNEW_PLACEMENT_ARGS(storage, RadialGradientContext, (*this, rec));
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000163}
164
165SkRadialGradient::RadialGradientContext::RadialGradientContext(
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000166 const SkRadialGradient& shader, const ContextRec& rec)
167 : INHERITED(shader, rec) {}
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000168
169void SkRadialGradient::RadialGradientContext::shadeSpan16(int x, int y, uint16_t* dstCParam,
170 int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000171 SkASSERT(count > 0);
172
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000173 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
174
rileya@google.com589708b2012-07-26 20:04:23 +0000175 uint16_t* SK_RESTRICT dstC = dstCParam;
176
177 SkPoint srcPt;
178 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000179 TileProc proc = radialGradient.fTileProc;
180 const uint16_t* SK_RESTRICT cache = fCache->getCache16();
reed@google.com55853db2013-02-01 19:34:59 +0000181 int toggle = init_dither_toggle16(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000182
183 if (fDstToIndexClass != kPerspective_MatrixClass) {
184 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
185 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
186
187 SkScalar sdx = fDstToIndex.getScaleX();
188 SkScalar sdy = fDstToIndex.getSkewY();
189
190 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
191 SkFixed storage[2];
192 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
193 &storage[0], &storage[1]);
194 sdx = SkFixedToScalar(storage[0]);
195 sdy = SkFixedToScalar(storage[1]);
196 } else {
197 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
198 }
199
200 RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000201 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000202 shadeProc = shadeSpan16_radial_clamp;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000203 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000204 shadeProc = shadeSpan16_radial_mirror;
205 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000206 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000207 }
208 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
209 cache, toggle, count);
210 } else { // perspective case
211 SkScalar dstX = SkIntToScalar(x);
212 SkScalar dstY = SkIntToScalar(y);
213 do {
214 dstProc(fDstToIndex, dstX, dstY, &srcPt);
215 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
216 SkASSERT(fi <= 0xFFFF);
217
218 int index = fi >> (16 - kCache16Bits);
219 *dstC++ = cache[toggle + index];
reed@google.com55853db2013-02-01 19:34:59 +0000220 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000221
222 dstX += SK_Scalar1;
223 } while (--count != 0);
224 }
225}
226
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000227SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap,
rileya@google.com589708b2012-07-26 20:04:23 +0000228 SkMatrix* matrix, SkShader::TileMode* xy) const {
229 if (bitmap) {
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000230 this->getGradientTableBitmap(bitmap);
rileya@google.com589708b2012-07-26 20:04:23 +0000231 }
232 if (matrix) {
reed@google.com3c2102c2013-02-01 12:59:40 +0000233 matrix->setScale(SkIntToScalar(kCache32Count),
234 SkIntToScalar(kCache32Count));
rileya@google.com589708b2012-07-26 20:04:23 +0000235 matrix->preConcat(fPtsToUnit);
236 }
237 if (xy) {
238 xy[0] = fTileMode;
239 xy[1] = kClamp_TileMode;
240 }
241 return kRadial_BitmapType;
242}
243
244SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
245 if (info) {
246 commonAsAGradient(info);
247 info->fPoint[0] = fCenter;
248 info->fRadius[0] = fRadius;
249 }
250 return kRadial_GradientType;
251}
252
reed9fa60da2014-08-21 07:59:51 -0700253SkFlattenable* SkRadialGradient::CreateProc(SkReadBuffer& buffer) {
254 DescriptorScope desc;
255 if (!desc.unflatten(buffer)) {
256 return NULL;
257 }
258 const SkPoint center = buffer.readPoint();
259 const SkScalar radius = buffer.readScalar();
260 return SkGradientShader::CreateRadial(center, radius, desc.fColors, desc.fPos, desc.fCount,
261 desc.fTileMode, desc.fGradFlags, desc.fLocalMatrix);
262}
rileya@google.com589708b2012-07-26 20:04:23 +0000263
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000264void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000265 this->INHERITED::flatten(buffer);
266 buffer.writePoint(fCenter);
267 buffer.writeScalar(fRadius);
268}
269
270namespace {
271
272inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
273 // fast, overly-conservative test: checks unit square instead
274 // of unit circle
275 bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
276 (fx <= -SK_FixedHalf && dx <= 0);
277 bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
278 (fy <= -SK_FixedHalf && dy <= 0);
279
280 return xClamped || yClamped;
281}
282
283// Return true if (fx * fy) is always inside the unit circle
284// SkPin32 is expensive, but so are all the SkFixedMul in this test,
285// so it shouldn't be run if count is small.
286inline bool no_need_for_radial_pin(int fx, int dx,
287 int fy, int dy, int count) {
288 SkASSERT(count > 0);
289 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
290 return false;
291 }
292 if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
293 return false;
294 }
295 fx += (count - 1) * dx;
296 fy += (count - 1) * dy;
297 if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
298 return false;
299 }
300 return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
301}
302
303#define UNPINNED_RADIAL_STEP \
304 fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
305 *dstC++ = cache[toggle + \
306 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
reed@google.com55853db2013-02-01 19:34:59 +0000307 toggle = next_dither_toggle(toggle); \
rileya@google.com589708b2012-07-26 20:04:23 +0000308 fx += dx; \
309 fy += dy;
310
311typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
312 SkScalar sfy, SkScalar sdy,
313 SkPMColor* dstC, const SkPMColor* cache,
314 int count, int toggle);
315
316// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
317void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
318 SkScalar sfy, SkScalar sdy,
319 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
320 int count, int toggle) {
321 // Floating point seems to be slower than fixed point,
322 // even when we have float hardware.
323 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
324 SkFixed fx = SkScalarToFixed(sfx) >> 1;
325 SkFixed dx = SkScalarToFixed(sdx) >> 1;
326 SkFixed fy = SkScalarToFixed(sfy) >> 1;
327 SkFixed dy = SkScalarToFixed(sdy) >> 1;
328 if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
reed@google.com3c2102c2013-02-01 12:59:40 +0000329 unsigned fi = SkGradientShaderBase::kCache32Count - 1;
rileya@google.com589708b2012-07-26 20:04:23 +0000330 sk_memset32_dither(dstC,
331 cache[toggle + fi],
reed@google.com55853db2013-02-01 19:34:59 +0000332 cache[next_dither_toggle(toggle) + fi],
rileya@google.com589708b2012-07-26 20:04:23 +0000333 count);
334 } else if ((count > 4) &&
335 no_need_for_radial_pin(fx, dx, fy, dy, count)) {
336 unsigned fi;
337 // 4x unroll appears to be no faster than 2x unroll on Linux
338 while (count > 1) {
339 UNPINNED_RADIAL_STEP;
340 UNPINNED_RADIAL_STEP;
341 count -= 2;
342 }
343 if (count) {
344 UNPINNED_RADIAL_STEP;
345 }
reed@google.com60040292013-02-04 18:21:23 +0000346 } else {
rileya@google.com589708b2012-07-26 20:04:23 +0000347 // Specializing for dy == 0 gains us 25% on Skia benchmarks
348 if (dy == 0) {
349 unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
350 yy *= yy;
351 do {
352 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
353 unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
354 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
355 *dstC++ = cache[toggle + (sqrt_table[fi] >>
356 SkGradientShaderBase::kSqrt32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000357 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000358 fx += dx;
359 } while (--count != 0);
360 } else {
361 do {
362 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
363 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
364 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
365 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
366 *dstC++ = cache[toggle + (sqrt_table[fi] >>
367 SkGradientShaderBase::kSqrt32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000368 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000369 fx += dx;
370 fy += dy;
371 } while (--count != 0);
372 }
373 }
374}
375
376// Unrolling this loop doesn't seem to help (when float); we're stalling to
377// get the results of the sqrt (?), and don't have enough extra registers to
378// have many in flight.
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000379template <SkFixed (*TileProc)(SkFixed)>
380void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
381 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
382 int count, int toggle) {
rileya@google.com589708b2012-07-26 20:04:23 +0000383 do {
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000384 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
385 const unsigned fi = TileProc(dist);
rileya@google.com589708b2012-07-26 20:04:23 +0000386 SkASSERT(fi <= 0xFFFF);
387 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000388 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000389 fx += dx;
390 fy += dy;
391 } while (--count != 0);
392}
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000393
394void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
395 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
396 int count, int toggle) {
397 shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000398}
399
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000400void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
401 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
402 int count, int toggle) {
403 shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
404}
405
406} // namespace
407
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000408void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y,
409 SkPMColor* SK_RESTRICT dstC, int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000410 SkASSERT(count > 0);
411
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000412 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
413
rileya@google.com589708b2012-07-26 20:04:23 +0000414 SkPoint srcPt;
415 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000416 TileProc proc = radialGradient.fTileProc;
417 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
reed@google.com55853db2013-02-01 19:34:59 +0000418 int toggle = init_dither_toggle(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000419
420 if (fDstToIndexClass != kPerspective_MatrixClass) {
421 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
422 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
423 SkScalar sdx = fDstToIndex.getScaleX();
424 SkScalar sdy = fDstToIndex.getSkewY();
425
426 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
427 SkFixed storage[2];
428 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
429 &storage[0], &storage[1]);
430 sdx = SkFixedToScalar(storage[0]);
431 sdy = SkFixedToScalar(storage[1]);
432 } else {
433 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
434 }
435
436 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000437 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000438 shadeProc = shadeSpan_radial_clamp;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000439 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000440 shadeProc = shadeSpan_radial_mirror;
441 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000442 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000443 }
444 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
445 } else { // perspective case
446 SkScalar dstX = SkIntToScalar(x);
447 SkScalar dstY = SkIntToScalar(y);
448 do {
449 dstProc(fDstToIndex, dstX, dstY, &srcPt);
450 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
451 SkASSERT(fi <= 0xFFFF);
452 *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
453 dstX += SK_Scalar1;
454 } while (--count != 0);
455 }
456}
457
rileya@google.comd7cc6512012-07-27 14:00:39 +0000458/////////////////////////////////////////////////////////////////////
459
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000460#if SK_SUPPORT_GPU
461
dandov9de5b512014-06-10 14:38:28 -0700462#include "SkGr.h"
joshualitteb2a6762014-12-04 11:35:33 -0800463#include "gl/builders/GrGLProgramBuilder.h"
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000464
bsalomon@google.com0707c292012-10-25 21:45:42 +0000465class GrGLRadialGradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000466public:
467
joshualitteb2a6762014-12-04 11:35:33 -0800468 GrGLRadialGradient(const GrProcessor&) {}
rileya@google.comd7cc6512012-07-27 14:00:39 +0000469 virtual ~GrGLRadialGradient() { }
470
joshualitt15988992014-10-09 15:04:05 -0700471 virtual void emitCode(GrGLFPBuilder*,
joshualittb0a8a372014-09-23 09:50:21 -0700472 const GrFragmentProcessor&,
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&,
mtklein36352bf2015-03-25 18:17:31 -0700476 const TextureSamplerArray&) override;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000477
joshualittb0a8a372014-09-23 09:50:21 -0700478 static void GenKey(const GrProcessor& processor, const GrGLCaps&, GrProcessorKeyBuilder* b) {
479 b->add32(GenBaseGradientKey(processor));
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:
joshualittb0a8a372014-09-23 09:50:21 -0700492 static GrFragmentProcessor* 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
mtklein36352bf2015-03-25 18:17:31 -0700501 const char* name() const override { return "Radial Gradient"; }
joshualitteb2a6762014-12-04 11:35:33 -0800502
503 virtual void getGLProcessorKey(const GrGLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700504 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800505 GrGLRadialGradient::GenKey(*this, caps, b);
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000506 }
507
mtklein36352bf2015-03-25 18:17:31 -0700508 GrGLFragmentProcessor* createGLInstance() const override {
joshualitteb2a6762014-12-04 11:35:33 -0800509 return SkNEW_ARGS(GrGLRadialGradient, (*this));
510 }
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) {
joshualitteb2a6762014-12-04 11:35:33 -0800518 this->initClassID<GrRadialGradient>();
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000519 }
520
joshualittb0a8a372014-09-23 09:50:21 -0700521 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000522
523 typedef GrGradientEffect INHERITED;
524};
525
526/////////////////////////////////////////////////////////////////////
527
joshualittb0a8a372014-09-23 09:50:21 -0700528GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadialGradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000529
joshualittb0a8a372014-09-23 09:50:21 -0700530GrFragmentProcessor* GrRadialGradient::TestCreate(SkRandom* random,
531 GrContext* context,
532 const GrDrawTargetCaps&,
533 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000534 SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
535 SkScalar radius = random->nextUScalar1();
536
537 SkColor colors[kMaxRandomGradientColors];
538 SkScalar stopsArray[kMaxRandomGradientColors];
539 SkScalar* stops = stopsArray;
540 SkShader::TileMode tm;
541 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
542 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
543 colors, stops, colorCount,
544 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000545 SkPaint paint;
bsalomon83d081a2014-07-08 09:56:10 -0700546 GrColor paintColor;
joshualittb0a8a372014-09-23 09:50:21 -0700547 GrFragmentProcessor* fp;
joshualitt5531d512014-12-17 15:50:11 -0800548 SkAssertResult(shader->asFragmentProcessor(context, paint,
549 GrProcessorUnitTest::TestMatrix(random), NULL,
550 &paintColor, &fp));
joshualittb0a8a372014-09-23 09:50:21 -0700551 return fp;
bsalomon@google.comd4726202012-08-03 14:34:46 +0000552}
553
554/////////////////////////////////////////////////////////////////////
555
joshualitt15988992014-10-09 15:04:05 -0700556void GrGLRadialGradient::emitCode(GrGLFPBuilder* builder,
joshualitt60030bc2014-11-25 14:21:55 -0800557 const GrFragmentProcessor& fp,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000558 const char* outputColor,
559 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000560 const TransformedCoordsArray& coords,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000561 const TextureSamplerArray& samplers) {
joshualitteb2a6762014-12-04 11:35:33 -0800562 const GrRadialGradient& ge = fp.cast<GrRadialGradient>();
joshualitt60030bc2014-11-25 14:21:55 -0800563 this->emitUniforms(builder, ge);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000564 SkString t("length(");
joshualitt30ba4362014-08-21 20:18:45 -0700565 t.append(builder->getFragmentShaderBuilder()->ensureFSCoords2D(coords, 0));
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000566 t.append(")");
joshualitt60030bc2014-11-25 14:21:55 -0800567 this->emitColor(builder, ge, t.c_str(), outputColor, inputColor, samplers);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000568}
569
rileya@google.comd7cc6512012-07-27 14:00:39 +0000570/////////////////////////////////////////////////////////////////////
571
joshualittb0a8a372014-09-23 09:50:21 -0700572bool SkRadialGradient::asFragmentProcessor(GrContext* context, const SkPaint& paint,
joshualitt5531d512014-12-17 15:50:11 -0800573 const SkMatrix& viewM,
joshualittb0a8a372014-09-23 09:50:21 -0700574 const SkMatrix* localMatrix, GrColor* paintColor,
575 GrFragmentProcessor** fp) const {
bsalomon49f085d2014-09-05 13:34:00 -0700576 SkASSERT(context);
mtklein3f3b3d02014-12-01 11:47:08 -0800577
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000578 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000579 if (!this->getLocalMatrix().invert(&matrix)) {
dandov9de5b512014-06-10 14:38:28 -0700580 return false;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000581 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000582 if (localMatrix) {
583 SkMatrix inv;
584 if (!localMatrix->invert(&inv)) {
dandov9de5b512014-06-10 14:38:28 -0700585 return false;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000586 }
587 matrix.postConcat(inv);
588 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000589 matrix.postConcat(fPtsToUnit);
mtklein3f3b3d02014-12-01 11:47:08 -0800590
bsalomon83d081a2014-07-08 09:56:10 -0700591 *paintColor = SkColor2GrColorJustAlpha(paint.getColor());
joshualittb0a8a372014-09-23 09:50:21 -0700592 *fp = GrRadialGradient::Create(context, *this, matrix, fTileMode);
mtklein3f3b3d02014-12-01 11:47:08 -0800593
dandov9de5b512014-06-10 14:38:28 -0700594 return true;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000595}
596
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000597#else
598
joshualitt5531d512014-12-17 15:50:11 -0800599bool SkRadialGradient::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix&,
600 const SkMatrix*, GrColor*,
joshualittb0a8a372014-09-23 09:50:21 -0700601 GrFragmentProcessor**) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000602 SkDEBUGFAIL("Should not call in GPU-less build");
dandov9de5b512014-06-10 14:38:28 -0700603 return false;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000604}
605
606#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000607
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000608#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000609void SkRadialGradient::toString(SkString* str) const {
610 str->append("SkRadialGradient: (");
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000611
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000612 str->append("center: (");
613 str->appendScalar(fCenter.fX);
614 str->append(", ");
615 str->appendScalar(fCenter.fY);
616 str->append(") radius: ");
617 str->appendScalar(fRadius);
618 str->append(" ");
619
620 this->INHERITED::toString(str);
621
622 str->append(")");
623}
624#endif