blob: 52d06392e1b39085f322b88eb9a2c696cf376a32 [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"
mtklein1113da72015-04-27 12:08:01 -070011#include "SkNx.h"
rileya@google.com589708b2012-07-26 20:04:23 +000012
13#define kSQRT_TABLE_BITS 11
14#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
15
bungeman99fe8222015-08-20 07:57:51 -070016static_assert(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE, "SqrtTableSizesMatch");
mtkleincc695fe2014-12-10 10:29:19 -080017
reed@google.com4ec5c952013-12-19 15:00:18 +000018#if 0
rileya@google.com589708b2012-07-26 20:04:23 +000019
20#include <stdio.h>
21
22void SkRadialGradient_BuildTable() {
23 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
24
25 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
26 SkASSERT(file);
27 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
28
29 for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
30 if ((i & 15) == 0) {
31 ::fprintf(file, "\t");
32 }
33
34 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
35
36 ::fprintf(file, "0x%02X", value);
37 if (i < kSQRT_TABLE_SIZE-1) {
38 ::fprintf(file, ", ");
39 }
40 if ((i & 15) == 15) {
41 ::fprintf(file, "\n");
42 }
43 }
44 ::fprintf(file, "};\n");
45 ::fclose(file);
46}
47
48#endif
49
50namespace {
51
commit-bot@chromium.org34150b42013-10-16 18:59:44 +000052// GCC doesn't like using static functions as template arguments. So force these to be non-static.
53inline SkFixed mirror_tileproc_nonstatic(SkFixed x) {
54 return mirror_tileproc(x);
55}
56
57inline SkFixed repeat_tileproc_nonstatic(SkFixed x) {
58 return repeat_tileproc(x);
59}
60
mtkleincc695fe2014-12-10 10:29:19 -080061SkMatrix rad_to_unit_matrix(const SkPoint& center, SkScalar radius) {
rileya@google.com589708b2012-07-26 20:04:23 +000062 SkScalar inv = SkScalarInvert(radius);
63
mtkleincc695fe2014-12-10 10:29:19 -080064 SkMatrix matrix;
65 matrix.setTranslate(-center.fX, -center.fY);
66 matrix.postScale(inv, inv);
67 return matrix;
rileya@google.com589708b2012-07-26 20:04:23 +000068}
69
70typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx,
71 SkScalar sfy, SkScalar sdy,
72 uint16_t* dstC, const uint16_t* cache,
73 int toggle, int count);
74
75void shadeSpan16_radial_clamp(SkScalar sfx, SkScalar sdx,
76 SkScalar sfy, SkScalar sdy,
77 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
78 int toggle, int count) {
79 const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
80
81 /* knock these down so we can pin against +- 0x7FFF, which is an
82 immediate load, rather than 0xFFFF which is slower. This is a
83 compromise, since it reduces our precision, but that appears
84 to be visually OK. If we decide this is OK for all of our cases,
85 we could (it seems) put this scale-down into fDstToIndex,
86 to avoid having to do these extra shifts each time.
87 */
88 SkFixed fx = SkScalarToFixed(sfx) >> 1;
89 SkFixed dx = SkScalarToFixed(sdx) >> 1;
90 SkFixed fy = SkScalarToFixed(sfy) >> 1;
91 SkFixed dy = SkScalarToFixed(sdy) >> 1;
92 // might perform this check for the other modes,
93 // but the win will be a smaller % of the total
94 if (dy == 0) {
bungeman62ce0302015-08-28 09:09:32 -070095 fy = SkTPin(fy, -0xFFFF >> 1, 0xFFFF >> 1);
rileya@google.com589708b2012-07-26 20:04:23 +000096 fy *= fy;
97 do {
bungeman62ce0302015-08-28 09:09:32 -070098 unsigned xx = SkTPin(fx, -0xFFFF >> 1, 0xFFFF >> 1);
rileya@google.com589708b2012-07-26 20:04:23 +000099 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
100 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
101 fx += dx;
102 *dstC++ = cache[toggle +
103 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000104 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000105 } while (--count != 0);
106 } else {
107 do {
bungeman62ce0302015-08-28 09:09:32 -0700108 unsigned xx = SkTPin(fx, -0xFFFF >> 1, 0xFFFF >> 1);
109 unsigned fi = SkTPin(fy, -0xFFFF >> 1, 0xFFFF >> 1);
rileya@google.com589708b2012-07-26 20:04:23 +0000110 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
111 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
112 fx += dx;
113 fy += dy;
114 *dstC++ = cache[toggle +
115 (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000116 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000117 } while (--count != 0);
118 }
119}
120
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000121template <SkFixed (*TileProc)(SkFixed)>
122void shadeSpan16_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
123 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
124 int toggle, int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000125 do {
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000126 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
127 const unsigned fi = TileProc(dist);
rileya@google.com589708b2012-07-26 20:04:23 +0000128 SkASSERT(fi <= 0xFFFF);
129 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000130 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000131 fx += dx;
132 fy += dy;
rileya@google.com589708b2012-07-26 20:04:23 +0000133 } while (--count != 0);
134}
135
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000136void shadeSpan16_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
137 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
138 int toggle, int count) {
139 shadeSpan16_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, toggle, count);
rileya@google.com589708b2012-07-26 20:04:23 +0000140}
141
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000142void shadeSpan16_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
143 uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
144 int toggle, int count) {
145 shadeSpan16_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, toggle, count);
146}
147
148} // namespace
149
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000150/////////////////////////////////////////////////////////////////////
151
reedaddf2ed2014-08-11 08:28:24 -0700152SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor& desc)
mtkleincc695fe2014-12-10 10:29:19 -0800153 : SkGradientShaderBase(desc, rad_to_unit_matrix(center, radius))
reedaddf2ed2014-08-11 08:28:24 -0700154 , fCenter(center)
mtkleincc695fe2014-12-10 10:29:19 -0800155 , fRadius(radius) {
rileya@google.com589708b2012-07-26 20:04:23 +0000156}
157
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000158size_t SkRadialGradient::contextSize() const {
159 return sizeof(RadialGradientContext);
160}
161
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +0000162SkShader::Context* SkRadialGradient::onCreateContext(const ContextRec& rec, void* storage) const {
halcanary385fe4d2015-08-26 13:07:48 -0700163 return new (storage) RadialGradientContext(*this, rec);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000164}
165
166SkRadialGradient::RadialGradientContext::RadialGradientContext(
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000167 const SkRadialGradient& shader, const ContextRec& rec)
168 : INHERITED(shader, rec) {}
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000169
170void SkRadialGradient::RadialGradientContext::shadeSpan16(int x, int y, uint16_t* dstCParam,
171 int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000172 SkASSERT(count > 0);
173
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000174 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
175
rileya@google.com589708b2012-07-26 20:04:23 +0000176 uint16_t* SK_RESTRICT dstC = dstCParam;
177
178 SkPoint srcPt;
179 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000180 TileProc proc = radialGradient.fTileProc;
181 const uint16_t* SK_RESTRICT cache = fCache->getCache16();
reed@google.com55853db2013-02-01 19:34:59 +0000182 int toggle = init_dither_toggle16(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000183
184 if (fDstToIndexClass != kPerspective_MatrixClass) {
185 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
186 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
187
188 SkScalar sdx = fDstToIndex.getScaleX();
189 SkScalar sdy = fDstToIndex.getSkewY();
190
191 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
192 SkFixed storage[2];
193 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
194 &storage[0], &storage[1]);
195 sdx = SkFixedToScalar(storage[0]);
196 sdy = SkFixedToScalar(storage[1]);
197 } else {
198 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
199 }
200
201 RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000202 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000203 shadeProc = shadeSpan16_radial_clamp;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000204 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000205 shadeProc = shadeSpan16_radial_mirror;
206 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000207 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000208 }
209 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
210 cache, toggle, count);
211 } else { // perspective case
212 SkScalar dstX = SkIntToScalar(x);
213 SkScalar dstY = SkIntToScalar(y);
214 do {
215 dstProc(fDstToIndex, dstX, dstY, &srcPt);
216 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
217 SkASSERT(fi <= 0xFFFF);
218
219 int index = fi >> (16 - kCache16Bits);
220 *dstC++ = cache[toggle + index];
reed@google.com55853db2013-02-01 19:34:59 +0000221 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000222
223 dstX += SK_Scalar1;
224 } while (--count != 0);
225 }
226}
227
rileya@google.com589708b2012-07-26 20:04:23 +0000228SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
229 if (info) {
230 commonAsAGradient(info);
231 info->fPoint[0] = fCenter;
232 info->fRadius[0] = fRadius;
233 }
234 return kRadial_GradientType;
235}
236
reed9fa60da2014-08-21 07:59:51 -0700237SkFlattenable* SkRadialGradient::CreateProc(SkReadBuffer& buffer) {
238 DescriptorScope desc;
239 if (!desc.unflatten(buffer)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700240 return nullptr;
reed9fa60da2014-08-21 07:59:51 -0700241 }
242 const SkPoint center = buffer.readPoint();
243 const SkScalar radius = buffer.readScalar();
244 return SkGradientShader::CreateRadial(center, radius, desc.fColors, desc.fPos, desc.fCount,
245 desc.fTileMode, desc.fGradFlags, desc.fLocalMatrix);
246}
rileya@google.com589708b2012-07-26 20:04:23 +0000247
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000248void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000249 this->INHERITED::flatten(buffer);
250 buffer.writePoint(fCenter);
251 buffer.writeScalar(fRadius);
252}
253
254namespace {
255
mtklein1113da72015-04-27 12:08:01 -0700256inline bool radial_completely_pinned(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy) {
257 // fast, overly-conservative test: checks unit square instead of unit circle
258 bool xClamped = (fx >= 1 && dx >= 0) || (fx <= -1 && dx <= 0);
259 bool yClamped = (fy >= 1 && dy >= 0) || (fy <= -1 && dy <= 0);
rileya@google.com589708b2012-07-26 20:04:23 +0000260 return xClamped || yClamped;
261}
262
rileya@google.com589708b2012-07-26 20:04:23 +0000263typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
264 SkScalar sfy, SkScalar sdy,
265 SkPMColor* dstC, const SkPMColor* cache,
266 int count, int toggle);
267
mtklein1113da72015-04-27 12:08:01 -0700268static inline Sk4f fast_sqrt(const Sk4f& R) {
mtkleind7c014f2015-04-27 14:22:32 -0700269 // R * R.rsqrt0() is much faster, but it's non-monotonic, which isn't so pretty for gradients.
270 return R * R.rsqrt1();
mtklein1113da72015-04-27 12:08:01 -0700271}
272
273static inline Sk4f sum_squares(const Sk4f& a, const Sk4f& b) {
274 return a * a + b * b;
275}
276
277void shadeSpan_radial_clamp2(SkScalar sfx, SkScalar sdx, SkScalar sfy, SkScalar sdy,
278 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
279 int count, int toggle) {
280 if (radial_completely_pinned(sfx, sdx, sfy, sdy)) {
281 unsigned fi = SkGradientShaderBase::kCache32Count - 1;
282 sk_memset32_dither(dstC,
283 cache[toggle + fi],
284 cache[next_dither_toggle(toggle) + fi],
285 count);
286 } else {
287 const Sk4f max(255);
288 const float scale = 255;
289 sfx *= scale;
290 sfy *= scale;
291 sdx *= scale;
292 sdy *= scale;
293 const Sk4f fx4(sfx, sfx + sdx, sfx + 2*sdx, sfx + 3*sdx);
294 const Sk4f fy4(sfy, sfy + sdy, sfy + 2*sdy, sfy + 3*sdy);
295 const Sk4f dx4(sdx * 4);
296 const Sk4f dy4(sdy * 4);
297
298 Sk4f tmpxy = fx4 * dx4 + fy4 * dy4;
299 Sk4f tmpdxdy = sum_squares(dx4, dy4);
300 Sk4f R = sum_squares(fx4, fy4);
301 Sk4f dR = tmpxy + tmpxy + tmpdxdy;
302 const Sk4f ddR = tmpdxdy + tmpdxdy;
303
304 for (int i = 0; i < (count >> 2); ++i) {
305 Sk4f dist = Sk4f::Min(fast_sqrt(R), max);
mtkleinf2fe0e02015-06-10 08:57:28 -0700306 R = R + dR;
307 dR = dR + ddR;
mtklein1113da72015-04-27 12:08:01 -0700308
mtklein6f797092015-11-09 08:33:53 -0800309 uint8_t fi[4];
310 dist.toBytes(fi);
mtklein1113da72015-04-27 12:08:01 -0700311
312 for (int i = 0; i < 4; i++) {
313 *dstC++ = cache[toggle + fi[i]];
314 toggle = next_dither_toggle(toggle);
315 }
316 }
317 count &= 3;
318 if (count) {
319 Sk4f dist = Sk4f::Min(fast_sqrt(R), max);
320
mtklein6f797092015-11-09 08:33:53 -0800321 uint8_t fi[4];
322 dist.toBytes(fi);
mtklein1113da72015-04-27 12:08:01 -0700323 for (int i = 0; i < count; i++) {
324 *dstC++ = cache[toggle + fi[i]];
325 toggle = next_dither_toggle(toggle);
326 }
327 }
328 }
329}
330
rileya@google.com589708b2012-07-26 20:04:23 +0000331// Unrolling this loop doesn't seem to help (when float); we're stalling to
332// get the results of the sqrt (?), and don't have enough extra registers to
333// have many in flight.
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000334template <SkFixed (*TileProc)(SkFixed)>
335void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
336 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
337 int count, int toggle) {
rileya@google.com589708b2012-07-26 20:04:23 +0000338 do {
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000339 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
340 const unsigned fi = TileProc(dist);
rileya@google.com589708b2012-07-26 20:04:23 +0000341 SkASSERT(fi <= 0xFFFF);
342 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
reed@google.com55853db2013-02-01 19:34:59 +0000343 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000344 fx += dx;
345 fy += dy;
346 } while (--count != 0);
347}
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000348
349void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
350 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
351 int count, int toggle) {
352 shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000353}
354
commit-bot@chromium.org34150b42013-10-16 18:59:44 +0000355void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
356 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
357 int count, int toggle) {
358 shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
359}
360
361} // namespace
362
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000363void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y,
364 SkPMColor* SK_RESTRICT dstC, int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000365 SkASSERT(count > 0);
366
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000367 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
368
rileya@google.com589708b2012-07-26 20:04:23 +0000369 SkPoint srcPt;
370 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000371 TileProc proc = radialGradient.fTileProc;
372 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
reed@google.com55853db2013-02-01 19:34:59 +0000373 int toggle = init_dither_toggle(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000374
375 if (fDstToIndexClass != kPerspective_MatrixClass) {
376 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
377 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
378 SkScalar sdx = fDstToIndex.getScaleX();
379 SkScalar sdy = fDstToIndex.getSkewY();
380
381 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
382 SkFixed storage[2];
383 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
384 &storage[0], &storage[1]);
385 sdx = SkFixedToScalar(storage[0]);
386 sdy = SkFixedToScalar(storage[1]);
387 } else {
388 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
389 }
390
391 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000392 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
reed67383fc2015-05-05 07:55:19 -0700393 shadeProc = shadeSpan_radial_clamp2;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000394 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000395 shadeProc = shadeSpan_radial_mirror;
396 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000397 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000398 }
399 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
400 } else { // perspective case
401 SkScalar dstX = SkIntToScalar(x);
402 SkScalar dstY = SkIntToScalar(y);
403 do {
404 dstProc(fDstToIndex, dstX, dstY, &srcPt);
405 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
406 SkASSERT(fi <= 0xFFFF);
407 *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
408 dstX += SK_Scalar1;
409 } while (--count != 0);
410 }
411}
412
rileya@google.comd7cc6512012-07-27 14:00:39 +0000413/////////////////////////////////////////////////////////////////////
414
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000415#if SK_SUPPORT_GPU
416
dandov9de5b512014-06-10 14:38:28 -0700417#include "SkGr.h"
egdaniel7ea439b2015-12-03 09:20:44 -0800418#include "glsl/GrGLSLCaps.h"
419#include "glsl/GrGLSLFragmentShaderBuilder.h"
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000420
bsalomon@google.com0707c292012-10-25 21:45:42 +0000421class GrGLRadialGradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000422public:
423
joshualitteb2a6762014-12-04 11:35:33 -0800424 GrGLRadialGradient(const GrProcessor&) {}
rileya@google.comd7cc6512012-07-27 14:00:39 +0000425 virtual ~GrGLRadialGradient() { }
426
wangyix7c157a92015-07-22 15:08:53 -0700427 virtual void emitCode(EmitArgs&) override;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000428
jvanverthcfc18862015-04-28 08:48:20 -0700429 static void GenKey(const GrProcessor& processor, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
joshualittb0a8a372014-09-23 09:50:21 -0700430 b->add32(GenBaseGradientKey(processor));
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000431 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000432
433private:
434
bsalomon@google.com0707c292012-10-25 21:45:42 +0000435 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000436
437};
438
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000439/////////////////////////////////////////////////////////////////////
440
441class GrRadialGradient : public GrGradientEffect {
442public:
joshualittb0a8a372014-09-23 09:50:21 -0700443 static GrFragmentProcessor* Create(GrContext* ctx,
444 const SkRadialGradient& shader,
445 const SkMatrix& matrix,
446 SkShader::TileMode tm) {
bsalomon4a339522015-10-06 08:40:50 -0700447 return new GrRadialGradient(ctx, shader, matrix, tm);
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000448 }
449
450 virtual ~GrRadialGradient() { }
451
mtklein36352bf2015-03-25 18:17:31 -0700452 const char* name() const override { return "Radial Gradient"; }
joshualitteb2a6762014-12-04 11:35:33 -0800453
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000454private:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000455 GrRadialGradient(GrContext* ctx,
456 const SkRadialGradient& shader,
457 const SkMatrix& matrix,
458 SkShader::TileMode tm)
bsalomon4a339522015-10-06 08:40:50 -0700459 : INHERITED(ctx, shader, matrix, tm) {
joshualitteb2a6762014-12-04 11:35:33 -0800460 this->initClassID<GrRadialGradient>();
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000461 }
462
egdaniel57d3b032015-11-13 11:57:27 -0800463 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
halcanary385fe4d2015-08-26 13:07:48 -0700464 return new GrGLRadialGradient(*this);
wangyixb1daa862015-08-18 11:29:31 -0700465 }
466
egdaniel57d3b032015-11-13 11:57:27 -0800467 virtual void onGetGLSLProcessorKey(const GrGLSLCaps& caps,
468 GrProcessorKeyBuilder* b) const override {
wangyix4b3050b2015-08-04 07:59:37 -0700469 GrGLRadialGradient::GenKey(*this, caps, b);
470 }
471
joshualittb0a8a372014-09-23 09:50:21 -0700472 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000473
474 typedef GrGradientEffect INHERITED;
475};
476
477/////////////////////////////////////////////////////////////////////
478
joshualittb0a8a372014-09-23 09:50:21 -0700479GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadialGradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000480
bsalomonc21b09e2015-08-28 18:46:56 -0700481const GrFragmentProcessor* GrRadialGradient::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700482 SkPoint center = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
483 SkScalar radius = d->fRandom->nextUScalar1();
bsalomon@google.comd4726202012-08-03 14:34:46 +0000484
485 SkColor colors[kMaxRandomGradientColors];
486 SkScalar stopsArray[kMaxRandomGradientColors];
487 SkScalar* stops = stopsArray;
488 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -0700489 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000490 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
491 colors, stops, colorCount,
492 tm));
bsalomonc21b09e2015-08-28 18:46:56 -0700493 const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
bsalomon4a339522015-10-06 08:40:50 -0700494 GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -0700495 GrAlwaysAssert(fp);
joshualittb0a8a372014-09-23 09:50:21 -0700496 return fp;
bsalomon@google.comd4726202012-08-03 14:34:46 +0000497}
498
499/////////////////////////////////////////////////////////////////////
500
wangyix7c157a92015-07-22 15:08:53 -0700501void GrGLRadialGradient::emitCode(EmitArgs& args) {
502 const GrRadialGradient& ge = args.fFp.cast<GrRadialGradient>();
egdaniel7ea439b2015-12-03 09:20:44 -0800503 this->emitUniforms(args.fUniformHandler, ge);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000504 SkString t("length(");
egdaniel4ca2e602015-11-18 08:01:26 -0800505 t.append(args.fFragBuilder->ensureFSCoords2D(args.fCoords, 0));
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000506 t.append(")");
egdaniel7ea439b2015-12-03 09:20:44 -0800507 this->emitColor(args.fFragBuilder,
508 args.fUniformHandler,
egdaniela2e3e0f2015-11-19 07:23:45 -0800509 args.fGLSLCaps,
egdaniel4ca2e602015-11-18 08:01:26 -0800510 ge, t.c_str(),
511 args.fOutputColor,
512 args.fInputColor,
wangyix7c157a92015-07-22 15:08:53 -0700513 args.fSamplers);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000514}
515
rileya@google.comd7cc6512012-07-27 14:00:39 +0000516/////////////////////////////////////////////////////////////////////
517
bsalomonc21b09e2015-08-28 18:46:56 -0700518const GrFragmentProcessor* SkRadialGradient::asFragmentProcessor(
519 GrContext* context,
520 const SkMatrix& viewM,
521 const SkMatrix* localMatrix,
bsalomon4a339522015-10-06 08:40:50 -0700522 SkFilterQuality) const {
bsalomon49f085d2014-09-05 13:34:00 -0700523 SkASSERT(context);
mtklein3f3b3d02014-12-01 11:47:08 -0800524
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000525 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000526 if (!this->getLocalMatrix().invert(&matrix)) {
bsalomonc21b09e2015-08-28 18:46:56 -0700527 return nullptr;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000528 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000529 if (localMatrix) {
530 SkMatrix inv;
531 if (!localMatrix->invert(&inv)) {
bsalomonc21b09e2015-08-28 18:46:56 -0700532 return nullptr;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000533 }
534 matrix.postConcat(inv);
535 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000536 matrix.postConcat(fPtsToUnit);
bsalomonc21b09e2015-08-28 18:46:56 -0700537 SkAutoTUnref<const GrFragmentProcessor> inner(
bsalomon4a339522015-10-06 08:40:50 -0700538 GrRadialGradient::Create(context, *this, matrix, fTileMode));
bsalomonf1b7a1d2015-09-28 06:26:28 -0700539 return GrFragmentProcessor::MulOutputByInputAlpha(inner);
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000540}
541
542#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000543
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000544#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000545void SkRadialGradient::toString(SkString* str) const {
546 str->append("SkRadialGradient: (");
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000547
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000548 str->append("center: (");
549 str->appendScalar(fCenter.fX);
550 str->append(", ");
551 str->appendScalar(fCenter.fY);
552 str->append(") radius: ");
553 str->appendScalar(fRadius);
554 str->append(" ");
555
556 this->INHERITED::toString(str);
557
558 str->append(")");
559}
560#endif