blob: 8e7ef96a15f83ebb022cb492031089d98a63cb7d [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();
301 SkPoint srcPt;
302
303 if (fDstToIndexClass != kPerspective_MatrixClass) {
304 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
305 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
306 SkScalar dx, fx = srcPt.fX;
307 SkScalar dy, fy = srcPt.fY;
308
309 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
310 SkFixed storage[2];
311 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
312 &storage[0], &storage[1]);
313 dx = SkFixedToScalar(storage[0]);
314 dy = SkFixedToScalar(storage[1]);
315 } else {
316 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
317 dx = matrix.getScaleX();
318 dy = matrix.getSkewY();
319 }
320
321 for (; count > 0; --count) {
322 *dstC++ = cache[SkATan2_255(fy, fx)];
323 fx += dx;
324 fy += dy;
325 }
326 } else { // perspective case
327 for (int stop = x + count; x < stop; x++) {
328 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
329 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
330 *dstC++ = cache[SkATan2_255(srcPt.fY, srcPt.fX)];
331 }
332 }
333}
334
335void SkSweepGradient::shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC,
336 int count) {
337 SkMatrix::MapXYProc proc = fDstToIndexProc;
338 const SkMatrix& matrix = fDstToIndex;
339 const uint16_t* SK_RESTRICT cache = this->getCache16();
340 int toggle = ((x ^ y) & 1) * kDitherStride16;
341 SkPoint srcPt;
342
343 if (fDstToIndexClass != kPerspective_MatrixClass) {
344 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
345 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
346 SkScalar dx, fx = srcPt.fX;
347 SkScalar dy, fy = srcPt.fY;
348
349 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
350 SkFixed storage[2];
351 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
352 &storage[0], &storage[1]);
353 dx = SkFixedToScalar(storage[0]);
354 dy = SkFixedToScalar(storage[1]);
355 } else {
356 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
357 dx = matrix.getScaleX();
358 dy = matrix.getSkewY();
359 }
360
361 for (; count > 0; --count) {
362 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
363 *dstC++ = cache[toggle + index];
364 toggle ^= kDitherStride16;
365 fx += dx;
366 fy += dy;
367 }
368 } else { // perspective case
369 for (int stop = x + count; x < stop; x++) {
370 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
371 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
372
373 int index = SkATan2_255(srcPt.fY, srcPt.fX);
374 index >>= (8 - kCache16Bits);
375 *dstC++ = cache[toggle + index];
376 toggle ^= kDitherStride16;
377 }
378 }
379}
380
rileya@google.comd7cc6512012-07-27 14:00:39 +0000381/////////////////////////////////////////////////////////////////////
382
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000383#if SK_SUPPORT_GPU
384
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000385#include "GrTBackendEffectFactory.h"
386
bsalomon@google.com0707c292012-10-25 21:45:42 +0000387class GrGLSweepGradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000388public:
389
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000390 GrGLSweepGradient(const GrBackendEffectFactory& factory,
bsalomon@google.coma469c282012-10-24 18:28:34 +0000391 const GrEffect&) : INHERITED (factory) { }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000392 virtual ~GrGLSweepGradient() { }
393
bsalomon@google.comf78df332012-10-29 12:43:38 +0000394 virtual void emitCode(GrGLShaderBuilder*,
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000395 const GrEffectStage&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000396 EffectKey,
397 const char* vertexCoords,
398 const char* outputColor,
399 const char* inputColor,
400 const TextureSamplerArray&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000401
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000402 static EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) {
403 return GenMatrixKey(stage);
404 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000405
406private:
407
bsalomon@google.com0707c292012-10-25 21:45:42 +0000408 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000409
410};
411
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000412/////////////////////////////////////////////////////////////////////
413
414class GrSweepGradient : public GrGradientEffect {
415public:
416
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000417 GrSweepGradient(GrContext* ctx,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000418 const SkSweepGradient& shader,
419 const SkMatrix& matrix)
420 : INHERITED(ctx, shader, matrix, SkShader::kClamp_TileMode) { }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000421 virtual ~GrSweepGradient() { }
422
423 static const char* Name() { return "Sweep Gradient"; }
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000424 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
425 return GrTBackendEffectFactory<GrSweepGradient>::getInstance();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000426 }
427
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000428 typedef GrGLSweepGradient GLEffect;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000429
bsalomon@google.comd4726202012-08-03 14:34:46 +0000430private:
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000431 GR_DECLARE_EFFECT_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000432
433 typedef GrGradientEffect INHERITED;
434};
435
436/////////////////////////////////////////////////////////////////////
437
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000438GR_DEFINE_EFFECT_TEST(GrSweepGradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000439
bsalomon@google.coma469c282012-10-24 18:28:34 +0000440GrEffect* GrSweepGradient::TestCreate(SkRandom* random,
441 GrContext* context,
442 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000443 SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
444
445 SkColor colors[kMaxRandomGradientColors];
446 SkScalar stopsArray[kMaxRandomGradientColors];
447 SkScalar* stops = stopsArray;
448 SkShader::TileMode tmIgnored;
449 int colorCount = RandomGradientParams(random, colors, &stops, &tmIgnored);
450 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateSweep(center.fX, center.fY,
451 colors, stops, colorCount));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000452 SkPaint paint;
453 return shader->asNewEffect(context, paint);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000454}
455
456/////////////////////////////////////////////////////////////////////
457
bsalomon@google.comf78df332012-10-29 12:43:38 +0000458void GrGLSweepGradient::emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000459 const GrEffectStage& stage,
460 EffectKey key,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000461 const char* vertexCoords,
462 const char* outputColor,
463 const char* inputColor,
464 const TextureSamplerArray& samplers) {
465 this->emitYCoordUniform(builder);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000466 const char* coords;
467 this->setupMatrix(builder, key, vertexCoords, &coords);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000468 SkString t;
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000469 t.printf("atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5", coords, coords);
bsalomon@google.comf06df1b2012-09-06 20:22:31 +0000470 this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000471}
472
473/////////////////////////////////////////////////////////////////////
474
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000475GrEffect* SkSweepGradient::asNewEffect(GrContext* context, const SkPaint&) const {
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000476 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000477 if (!this->getLocalMatrix().invert(&matrix)) {
humper@google.com84831ac2013-01-14 22:09:54 +0000478 return NULL;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000479 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000480 matrix.postConcat(fPtsToUnit);
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000481 return SkNEW_ARGS(GrSweepGradient, (context, *this, matrix));
rileya@google.comd7cc6512012-07-27 14:00:39 +0000482}
483
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000484#else
485
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000486GrEffect* SkSweepGradient::asNewEffect(GrContext* context, const SkPaint&) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000487 SkDEBUGFAIL("Should not call in GPU-less build");
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000488 return NULL;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000489}
490
491#endif