blob: efd41c4a8a8217d260be6d5aa834f13d29b72dc9 [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"
joshualitteb2a6762014-12-04 11:35:33 -0800418#include "gl/builders/GrGLProgramBuilder.h"
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000419
bsalomon@google.com0707c292012-10-25 21:45:42 +0000420class GrGLRadialGradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000421public:
422
joshualitteb2a6762014-12-04 11:35:33 -0800423 GrGLRadialGradient(const GrProcessor&) {}
rileya@google.comd7cc6512012-07-27 14:00:39 +0000424 virtual ~GrGLRadialGradient() { }
425
wangyix7c157a92015-07-22 15:08:53 -0700426 virtual void emitCode(EmitArgs&) override;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000427
jvanverthcfc18862015-04-28 08:48:20 -0700428 static void GenKey(const GrProcessor& processor, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
joshualittb0a8a372014-09-23 09:50:21 -0700429 b->add32(GenBaseGradientKey(processor));
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000430 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000431
432private:
433
bsalomon@google.com0707c292012-10-25 21:45:42 +0000434 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000435
436};
437
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000438/////////////////////////////////////////////////////////////////////
439
440class GrRadialGradient : public GrGradientEffect {
441public:
joshualittb0a8a372014-09-23 09:50:21 -0700442 static GrFragmentProcessor* Create(GrContext* ctx,
443 const SkRadialGradient& shader,
444 const SkMatrix& matrix,
445 SkShader::TileMode tm) {
bsalomon4a339522015-10-06 08:40:50 -0700446 return new GrRadialGradient(ctx, shader, matrix, tm);
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000447 }
448
449 virtual ~GrRadialGradient() { }
450
mtklein36352bf2015-03-25 18:17:31 -0700451 const char* name() const override { return "Radial Gradient"; }
joshualitteb2a6762014-12-04 11:35:33 -0800452
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000453private:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000454 GrRadialGradient(GrContext* ctx,
455 const SkRadialGradient& shader,
456 const SkMatrix& matrix,
457 SkShader::TileMode tm)
bsalomon4a339522015-10-06 08:40:50 -0700458 : INHERITED(ctx, shader, matrix, tm) {
joshualitteb2a6762014-12-04 11:35:33 -0800459 this->initClassID<GrRadialGradient>();
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000460 }
461
egdaniel57d3b032015-11-13 11:57:27 -0800462 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
halcanary385fe4d2015-08-26 13:07:48 -0700463 return new GrGLRadialGradient(*this);
wangyixb1daa862015-08-18 11:29:31 -0700464 }
465
egdaniel57d3b032015-11-13 11:57:27 -0800466 virtual void onGetGLSLProcessorKey(const GrGLSLCaps& caps,
467 GrProcessorKeyBuilder* b) const override {
wangyix4b3050b2015-08-04 07:59:37 -0700468 GrGLRadialGradient::GenKey(*this, caps, b);
469 }
470
joshualittb0a8a372014-09-23 09:50:21 -0700471 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000472
473 typedef GrGradientEffect INHERITED;
474};
475
476/////////////////////////////////////////////////////////////////////
477
joshualittb0a8a372014-09-23 09:50:21 -0700478GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadialGradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000479
bsalomonc21b09e2015-08-28 18:46:56 -0700480const GrFragmentProcessor* GrRadialGradient::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700481 SkPoint center = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
482 SkScalar radius = d->fRandom->nextUScalar1();
bsalomon@google.comd4726202012-08-03 14:34:46 +0000483
484 SkColor colors[kMaxRandomGradientColors];
485 SkScalar stopsArray[kMaxRandomGradientColors];
486 SkScalar* stops = stopsArray;
487 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -0700488 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000489 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
490 colors, stops, colorCount,
491 tm));
bsalomonc21b09e2015-08-28 18:46:56 -0700492 const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
bsalomon4a339522015-10-06 08:40:50 -0700493 GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -0700494 GrAlwaysAssert(fp);
joshualittb0a8a372014-09-23 09:50:21 -0700495 return fp;
bsalomon@google.comd4726202012-08-03 14:34:46 +0000496}
497
498/////////////////////////////////////////////////////////////////////
499
wangyix7c157a92015-07-22 15:08:53 -0700500void GrGLRadialGradient::emitCode(EmitArgs& args) {
501 const GrRadialGradient& ge = args.fFp.cast<GrRadialGradient>();
502 this->emitUniforms(args.fBuilder, ge);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000503 SkString t("length(");
egdaniel4ca2e602015-11-18 08:01:26 -0800504 t.append(args.fFragBuilder->ensureFSCoords2D(args.fCoords, 0));
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000505 t.append(")");
egdaniel4ca2e602015-11-18 08:01:26 -0800506 this->emitColor(args.fBuilder,
507 args.fFragBuilder,
508 ge, t.c_str(),
509 args.fOutputColor,
510 args.fInputColor,
wangyix7c157a92015-07-22 15:08:53 -0700511 args.fSamplers);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000512}
513
rileya@google.comd7cc6512012-07-27 14:00:39 +0000514/////////////////////////////////////////////////////////////////////
515
bsalomonc21b09e2015-08-28 18:46:56 -0700516const GrFragmentProcessor* SkRadialGradient::asFragmentProcessor(
517 GrContext* context,
518 const SkMatrix& viewM,
519 const SkMatrix* localMatrix,
bsalomon4a339522015-10-06 08:40:50 -0700520 SkFilterQuality) const {
bsalomon49f085d2014-09-05 13:34:00 -0700521 SkASSERT(context);
mtklein3f3b3d02014-12-01 11:47:08 -0800522
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000523 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000524 if (!this->getLocalMatrix().invert(&matrix)) {
bsalomonc21b09e2015-08-28 18:46:56 -0700525 return nullptr;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000526 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000527 if (localMatrix) {
528 SkMatrix inv;
529 if (!localMatrix->invert(&inv)) {
bsalomonc21b09e2015-08-28 18:46:56 -0700530 return nullptr;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000531 }
532 matrix.postConcat(inv);
533 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000534 matrix.postConcat(fPtsToUnit);
bsalomonc21b09e2015-08-28 18:46:56 -0700535 SkAutoTUnref<const GrFragmentProcessor> inner(
bsalomon4a339522015-10-06 08:40:50 -0700536 GrRadialGradient::Create(context, *this, matrix, fTileMode));
bsalomonf1b7a1d2015-09-28 06:26:28 -0700537 return GrFragmentProcessor::MulOutputByInputAlpha(inner);
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000538}
539
540#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000541
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000542#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000543void SkRadialGradient::toString(SkString* str) const {
544 str->append("SkRadialGradient: (");
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000545
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000546 str->append("center: (");
547 str->appendScalar(fCenter.fX);
548 str->append(", ");
549 str->appendScalar(fCenter.fY);
550 str->append(") radius: ");
551 str->appendScalar(fRadius);
552 str->append(" ");
553
554 this->INHERITED::toString(str);
555
556 str->append(")");
557}
558#endif