blob: 1e6b642260c1799e2f3e1cd03901de48ffbde25e [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 "SkSweepGradient.h"
10
11SkSweepGradient::SkSweepGradient(SkScalar cx, SkScalar cy, const SkColor colors[],
12 const SkScalar pos[], int count, SkUnitMapper* mapper)
13: SkGradientShaderBase(colors, pos, count, SkShader::kClamp_TileMode, mapper),
14 fCenter(SkPoint::Make(cx, cy))
15{
16 fPtsToUnit.setTranslate(-cx, -cy);
17}
18
19SkShader::BitmapType SkSweepGradient::asABitmap(SkBitmap* bitmap,
20 SkMatrix* matrix, SkShader::TileMode* xy) const {
21 if (bitmap) {
rileya@google.com1c6d64b2012-07-27 15:49:05 +000022 this->getGradientTableBitmap(bitmap);
rileya@google.com589708b2012-07-26 20:04:23 +000023 }
24 if (matrix) {
25 *matrix = fPtsToUnit;
26 }
27 if (xy) {
28 xy[0] = fTileMode;
29 xy[1] = kClamp_TileMode;
30 }
31 return kSweep_BitmapType;
32}
33
34SkShader::GradientType SkSweepGradient::asAGradient(GradientInfo* info) const {
35 if (info) {
36 commonAsAGradient(info);
37 info->fPoint[0] = fCenter;
38 }
39 return kSweep_GradientType;
40}
41
rileya@google.com589708b2012-07-26 20:04:23 +000042SkSweepGradient::SkSweepGradient(SkFlattenableReadBuffer& buffer)
43 : INHERITED(buffer),
44 fCenter(buffer.readPoint()) {
45}
46
47void SkSweepGradient::flatten(SkFlattenableWriteBuffer& buffer) const {
48 this->INHERITED::flatten(buffer);
49 buffer.writePoint(fCenter);
50}
51
52#ifndef SK_SCALAR_IS_FLOAT
53#ifdef COMPUTE_SWEEP_TABLE
54#define PI 3.14159265
55static bool gSweepTableReady;
56static uint8_t gSweepTable[65];
57
58/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
59 We scale the results to [0..32]
60*/
61static const uint8_t* build_sweep_table() {
62 if (!gSweepTableReady) {
63 const int N = 65;
64 const double DENOM = N - 1;
65
66 for (int i = 0; i < N; i++)
67 {
68 double arg = i / DENOM;
69 double v = atan(arg);
70 int iv = (int)round(v * DENOM * 2 / PI);
71// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
72 printf("%d, ", iv);
73 gSweepTable[i] = iv;
74 }
75 gSweepTableReady = true;
76 }
77 return gSweepTable;
78}
79#else
80static const uint8_t gSweepTable[] = {
81 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
82 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
83 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
84 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
85 32
86};
87static const uint8_t* build_sweep_table() { return gSweepTable; }
88#endif
89#endif
90
91// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
92// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
93// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
94
95//unsigned div_64(int numer, int denom);
96#ifndef SK_SCALAR_IS_FLOAT
97static unsigned div_64(int numer, int denom) {
98 SkASSERT(numer <= denom);
99 SkASSERT(numer > 0);
100 SkASSERT(denom > 0);
101
102 int nbits = SkCLZ(numer);
103 int dbits = SkCLZ(denom);
104 int bits = 6 - nbits + dbits;
105 SkASSERT(bits <= 6);
106
107 if (bits < 0) { // detect underflow
108 return 0;
109 }
110
111 denom <<= dbits - 1;
112 numer <<= nbits - 1;
113
114 unsigned result = 0;
115
116 // do the first one
117 if ((numer -= denom) >= 0) {
118 result = 1;
119 } else {
120 numer += denom;
121 }
122
123 // Now fall into our switch statement if there are more bits to compute
124 if (bits > 0) {
125 // make room for the rest of the answer bits
126 result <<= bits;
127 switch (bits) {
128 case 6:
129 if ((numer = (numer << 1) - denom) >= 0)
130 result |= 32;
131 else
132 numer += denom;
133 case 5:
134 if ((numer = (numer << 1) - denom) >= 0)
135 result |= 16;
136 else
137 numer += denom;
138 case 4:
139 if ((numer = (numer << 1) - denom) >= 0)
140 result |= 8;
141 else
142 numer += denom;
143 case 3:
144 if ((numer = (numer << 1) - denom) >= 0)
145 result |= 4;
146 else
147 numer += denom;
148 case 2:
149 if ((numer = (numer << 1) - denom) >= 0)
150 result |= 2;
151 else
152 numer += denom;
153 case 1:
154 default: // not strictly need, but makes GCC make better ARM code
155 if ((numer = (numer << 1) - denom) >= 0)
156 result |= 1;
157 else
158 numer += denom;
159 }
160 }
161 return result;
162}
163#endif
164
165// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
166#ifndef SK_SCALAR_IS_FLOAT
167static unsigned atan_0_90(SkFixed y, SkFixed x) {
168#ifdef SK_DEBUG
169 {
170 static bool gOnce;
171 if (!gOnce) {
172 gOnce = true;
173 SkASSERT(div_64(55, 55) == 64);
174 SkASSERT(div_64(128, 256) == 32);
175 SkASSERT(div_64(2326528, 4685824) == 31);
176 SkASSERT(div_64(753664, 5210112) == 9);
177 SkASSERT(div_64(229376, 4882432) == 3);
178 SkASSERT(div_64(2, 64) == 2);
179 SkASSERT(div_64(1, 64) == 1);
180 // test that we handle underflow correctly
181 SkASSERT(div_64(12345, 0x54321234) == 0);
182 }
183 }
184#endif
185
186 SkASSERT(y > 0 && x > 0);
187 const uint8_t* table = build_sweep_table();
188
189 unsigned result;
190 bool swap = (x < y);
191 if (swap) {
192 // first part of the atan(v) = PI/2 - atan(1/v) identity
193 // since our div_64 and table want v <= 1, where v = y/x
194 SkTSwap<SkFixed>(x, y);
195 }
196
197 result = div_64(y, x);
198
199#ifdef SK_DEBUG
200 {
201 unsigned result2 = SkDivBits(y, x, 6);
202 SkASSERT(result2 == result ||
203 (result == 1 && result2 == 0));
204 }
205#endif
206
207 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
208 result = table[result];
209
210 if (swap) {
211 // complete the atan(v) = PI/2 - atan(1/v) identity
212 result = 64 - result;
213 // pin to 63
214 result -= result >> 6;
215 }
216
217 SkASSERT(result <= 63);
218 return result;
219}
220#endif
221
222// returns angle in a circle [0..2PI) -> [0..255]
223#ifdef SK_SCALAR_IS_FLOAT
224static unsigned SkATan2_255(float y, float x) {
225 // static const float g255Over2PI = 255 / (2 * SK_ScalarPI);
226 static const float g255Over2PI = 40.584510488433314f;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000227
rileya@google.com589708b2012-07-26 20:04:23 +0000228 float result = sk_float_atan2(y, x);
229 if (result < 0) {
230 result += 2 * SK_ScalarPI;
231 }
232 SkASSERT(result >= 0);
233 // since our value is always >= 0, we can cast to int, which is faster than
234 // calling floorf()
235 int ir = (int)(result * g255Over2PI);
236 SkASSERT(ir >= 0 && ir <= 255);
237 return ir;
238}
239#else
240static unsigned SkATan2_255(SkFixed y, SkFixed x) {
241 if (x == 0) {
242 if (y == 0) {
243 return 0;
244 }
245 return y < 0 ? 192 : 64;
246 }
247 if (y == 0) {
248 return x < 0 ? 128 : 0;
249 }
250
251 /* Find the right quadrant for x,y
252 Since atan_0_90 only handles the first quadrant, we rotate x,y
253 appropriately before calling it, and then add the right amount
254 to account for the real quadrant.
255 quadrant 0 : add 0 | x > 0 && y > 0
256 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
257 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
258 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
259
260 map x<0 to (1 << 6)
261 map y<0 to (3 << 6)
262 add = map_x ^ map_y
263 */
264 int xsign = x >> 31;
265 int ysign = y >> 31;
266 int add = ((-xsign) ^ (ysign & 3)) << 6;
267
268#ifdef SK_DEBUG
269 if (0 == add)
270 SkASSERT(x > 0 && y > 0);
271 else if (64 == add)
272 SkASSERT(x < 0 && y > 0);
273 else if (128 == add)
274 SkASSERT(x < 0 && y < 0);
275 else if (192 == add)
276 SkASSERT(x > 0 && y < 0);
277 else
278 SkDEBUGFAIL("bad value for add");
279#endif
280
281 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
282 where we need to rotate x,y by 90 or -90
283 */
284 x = (x ^ xsign) - xsign;
285 y = (y ^ ysign) - ysign;
286 if (add & 64) { // quads 1 or 3 need to swap x,y
287 SkTSwap<SkFixed>(x, y);
288 }
289
290 unsigned result = add + atan_0_90(y, x);
291 SkASSERT(result < 256);
292 return result;
293}
294#endif
295
296void SkSweepGradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
297 int count) {
298 SkMatrix::MapXYProc proc = fDstToIndexProc;
299 const SkMatrix& matrix = fDstToIndex;
300 const SkPMColor* SK_RESTRICT cache = this->getCache32();
reed@google.com60040292013-02-04 18:21:23 +0000301 int toggle = init_dither_toggle(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000302 SkPoint srcPt;
303
304 if (fDstToIndexClass != kPerspective_MatrixClass) {
305 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
306 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
307 SkScalar dx, fx = srcPt.fX;
308 SkScalar dy, fy = srcPt.fY;
309
310 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
311 SkFixed storage[2];
312 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
313 &storage[0], &storage[1]);
314 dx = SkFixedToScalar(storage[0]);
315 dy = SkFixedToScalar(storage[1]);
316 } else {
317 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
318 dx = matrix.getScaleX();
319 dy = matrix.getSkewY();
320 }
321
322 for (; count > 0; --count) {
reed@google.com60040292013-02-04 18:21:23 +0000323 *dstC++ = cache[toggle + SkATan2_255(fy, fx)];
rileya@google.com589708b2012-07-26 20:04:23 +0000324 fx += dx;
325 fy += dy;
reed@google.com60040292013-02-04 18:21:23 +0000326 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000327 }
328 } else { // perspective case
329 for (int stop = x + count; x < stop; x++) {
330 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
331 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
reed@google.com60040292013-02-04 18:21:23 +0000332 *dstC++ = cache[toggle + SkATan2_255(srcPt.fY, srcPt.fX)];
reed@google.com60040292013-02-04 18:21:23 +0000333 toggle = next_dither_toggle(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000334 }
335 }
336}
337
338void SkSweepGradient::shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC,
339 int count) {
340 SkMatrix::MapXYProc proc = fDstToIndexProc;
341 const SkMatrix& matrix = fDstToIndex;
342 const uint16_t* SK_RESTRICT cache = this->getCache16();
reed@google.com55853db2013-02-01 19:34:59 +0000343 int toggle = init_dither_toggle16(x, y);
rileya@google.com589708b2012-07-26 20:04:23 +0000344 SkPoint srcPt;
345
346 if (fDstToIndexClass != kPerspective_MatrixClass) {
347 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
348 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
349 SkScalar dx, fx = srcPt.fX;
350 SkScalar dy, fy = srcPt.fY;
351
352 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
353 SkFixed storage[2];
354 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
355 &storage[0], &storage[1]);
356 dx = SkFixedToScalar(storage[0]);
357 dy = SkFixedToScalar(storage[1]);
358 } else {
359 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
360 dx = matrix.getScaleX();
361 dy = matrix.getSkewY();
362 }
363
364 for (; count > 0; --count) {
365 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
366 *dstC++ = cache[toggle + index];
reed@google.com55853db2013-02-01 19:34:59 +0000367 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000368 fx += dx;
369 fy += dy;
370 }
371 } else { // perspective case
372 for (int stop = x + count; x < stop; x++) {
373 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
374 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
375
376 int index = SkATan2_255(srcPt.fY, srcPt.fX);
377 index >>= (8 - kCache16Bits);
378 *dstC++ = cache[toggle + index];
reed@google.com55853db2013-02-01 19:34:59 +0000379 toggle = next_dither_toggle16(toggle);
rileya@google.com589708b2012-07-26 20:04:23 +0000380 }
381 }
382}
383
rileya@google.comd7cc6512012-07-27 14:00:39 +0000384/////////////////////////////////////////////////////////////////////
385
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000386#if SK_SUPPORT_GPU
387
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000388#include "GrTBackendEffectFactory.h"
389
bsalomon@google.com0707c292012-10-25 21:45:42 +0000390class GrGLSweepGradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000391public:
392
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000393 GrGLSweepGradient(const GrBackendEffectFactory& factory,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000394 const GrDrawEffect&) : INHERITED (factory) { }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000395 virtual ~GrGLSweepGradient() { }
396
bsalomon@google.comf78df332012-10-29 12:43:38 +0000397 virtual void emitCode(GrGLShaderBuilder*,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000398 const GrDrawEffect&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000399 EffectKey,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000400 const char* outputColor,
401 const char* inputColor,
402 const TextureSamplerArray&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000403
bsalomon@google.comc7818882013-03-20 19:19:53 +0000404 static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
405 return GenMatrixKey(drawEffect);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000406 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000407
408private:
409
bsalomon@google.com0707c292012-10-25 21:45:42 +0000410 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000411
412};
413
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000414/////////////////////////////////////////////////////////////////////
415
416class GrSweepGradient : public GrGradientEffect {
417public:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000418 static GrEffectRef* Create(GrContext* ctx,
419 const SkSweepGradient& shader,
420 const SkMatrix& matrix) {
bsalomon@google.com6340a412013-01-22 19:55:59 +0000421 AutoEffectUnref effect(SkNEW_ARGS(GrSweepGradient, (ctx, shader, matrix)));
bsalomon@google.coma1ebbe42013-01-16 15:51:47 +0000422 return CreateEffectRef(effect);
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000423 }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000424 virtual ~GrSweepGradient() { }
425
426 static const char* Name() { return "Sweep Gradient"; }
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000427 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
428 return GrTBackendEffectFactory<GrSweepGradient>::getInstance();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000429 }
430
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000431 typedef GrGLSweepGradient GLEffect;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000432
bsalomon@google.comd4726202012-08-03 14:34:46 +0000433private:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000434 GrSweepGradient(GrContext* ctx,
435 const SkSweepGradient& shader,
436 const SkMatrix& matrix)
437 : INHERITED(ctx, shader, matrix, SkShader::kClamp_TileMode) { }
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000438 GR_DECLARE_EFFECT_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000439
440 typedef GrGradientEffect INHERITED;
441};
442
443/////////////////////////////////////////////////////////////////////
444
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000445GR_DEFINE_EFFECT_TEST(GrSweepGradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000446
bsalomon@google.com73a96942013-02-13 16:31:19 +0000447GrEffectRef* GrSweepGradient::TestCreate(SkMWCRandom* random,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000448 GrContext* context,
bsalomon@google.comc26d94f2013-03-25 18:19:00 +0000449 const GrDrawTargetCaps&,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000450 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000451 SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
452
453 SkColor colors[kMaxRandomGradientColors];
454 SkScalar stopsArray[kMaxRandomGradientColors];
455 SkScalar* stops = stopsArray;
456 SkShader::TileMode tmIgnored;
457 int colorCount = RandomGradientParams(random, colors, &stops, &tmIgnored);
458 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateSweep(center.fX, center.fY,
459 colors, stops, colorCount));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000460 SkPaint paint;
461 return shader->asNewEffect(context, paint);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000462}
463
464/////////////////////////////////////////////////////////////////////
465
bsalomon@google.comf78df332012-10-29 12:43:38 +0000466void GrGLSweepGradient::emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000467 const GrDrawEffect&,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000468 EffectKey key,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000469 const char* outputColor,
470 const char* inputColor,
471 const TextureSamplerArray& samplers) {
472 this->emitYCoordUniform(builder);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000473 const char* coords;
bsalomon@google.comc7818882013-03-20 19:19:53 +0000474 this->setupMatrix(builder, key, &coords);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000475 SkString t;
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000476 t.printf("atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5", coords, coords);
bsalomon@google.comf06df1b2012-09-06 20:22:31 +0000477 this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000478}
479
480/////////////////////////////////////////////////////////////////////
481
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000482GrEffectRef* SkSweepGradient::asNewEffect(GrContext* context, const SkPaint&) const {
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000483 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000484 if (!this->getLocalMatrix().invert(&matrix)) {
humper@google.com84831ac2013-01-14 22:09:54 +0000485 return NULL;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000486 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000487 matrix.postConcat(fPtsToUnit);
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000488 return GrSweepGradient::Create(context, *this, matrix);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000489}
490
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000491#else
492
bsalomon@google.com5d2cd202013-01-16 15:31:06 +0000493GrEffectRef* SkSweepGradient::asNewEffect(GrContext*, const SkPaint&) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000494 SkDEBUGFAIL("Should not call in GPU-less build");
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000495 return NULL;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000496}
497
498#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000499
500#ifdef SK_DEVELOPER
501void SkSweepGradient::toString(SkString* str) const {
502 str->append("SkSweepGradient: (");
503
504 str->append("center: (");
505 str->appendScalar(fCenter.fX);
506 str->append(", ");
507 str->appendScalar(fCenter.fY);
508 str->append(") ");
509
510 this->INHERITED::toString(str);
511
512 str->append(")");
513}
514#endif