blob: df46a8fec3f9c9dfe86249243eaf1e87c60cfcc7 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/* libs/graphics/effects/SkGradientShader.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include "SkGradientShader.h"
19#include "SkColorPriv.h"
20#include "SkUnitMapper.h"
21#include "SkUtils.h"
22
23/*
24 ToDo
25
26 - not sure we still need the full Rec struct, now that we're using a cache
27 - detect const-alpha (but not opaque) in getFlags()
28*/
29
30/* dither seems to look better, but not stuningly yet, and it slows us down a little
31 so its not on by default yet.
32*/
33#define TEST_GRADIENT_DITHER
34
35///////////////////////////////////////////////////////////////////////////
36
37typedef SkFixed (*TileProc)(SkFixed);
38
reed@android.com41bccf52009-04-03 13:33:51 +000039static SkFixed clamp_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000040 return SkClampMax(x, 0xFFFF);
41}
42
reed@android.com41bccf52009-04-03 13:33:51 +000043static SkFixed repeat_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000044 return x & 0xFFFF;
45}
46
reed@android.com41bccf52009-04-03 13:33:51 +000047static inline SkFixed mirror_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000048 int s = x << 15 >> 31;
49 return (x ^ s) & 0xFFFF;
50}
51
52static const TileProc gTileProcs[] = {
53 clamp_tileproc,
54 repeat_tileproc,
55 mirror_tileproc
56};
57
58//////////////////////////////////////////////////////////////////////////////
59
reed@android.com41bccf52009-04-03 13:33:51 +000060static inline int repeat_6bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000061 return x & 63;
62}
63
reed@android.com41bccf52009-04-03 13:33:51 +000064static inline int mirror_6bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000065#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
66 if (x & 64)
67 x = ~x;
68 return x & 63;
69#else
70 int s = x << 25 >> 31;
71 return (x ^ s) & 63;
72#endif
73}
74
reed@android.com41bccf52009-04-03 13:33:51 +000075static inline int repeat_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000076 return x & 0xFF;
77}
78
reed@android.com41bccf52009-04-03 13:33:51 +000079static inline int mirror_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000080#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com41bccf52009-04-03 13:33:51 +000081 if (x & 256) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000082 x = ~x;
reed@android.com41bccf52009-04-03 13:33:51 +000083 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000084 return x & 255;
85#else
86 int s = x << 23 >> 31;
87 return (x ^ s) & 0xFF;
88#endif
89}
90
91//////////////////////////////////////////////////////////////////////////////
92
93class Gradient_Shader : public SkShader {
94public:
95 Gradient_Shader(const SkColor colors[], const SkScalar pos[],
reed@android.com41bccf52009-04-03 13:33:51 +000096 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +000097 virtual ~Gradient_Shader();
98
99 // overrides
100 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
101 virtual uint32_t getFlags() { return fFlags; }
102
103protected:
104 Gradient_Shader(SkFlattenableReadBuffer& );
105 SkUnitMapper* fMapper;
106 SkMatrix fPtsToUnit; // set by subclass
107 SkMatrix fDstToIndex;
108 SkMatrix::MapXYProc fDstToIndexProc;
109 SkPMColor* fARGB32;
110 TileMode fTileMode;
111 TileProc fTileProc;
reed@android.com41bccf52009-04-03 13:33:51 +0000112 int fColorCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000113 uint8_t fDstToIndexClass;
114 uint8_t fFlags;
115
116 struct Rec {
117 SkFixed fPos; // 0...1
118 uint32_t fScale; // (1 << 24) / range
119 };
120 Rec* fRecs;
121
122 enum {
123 kCache16Bits = 6, // seems like enough for visual accuracy
124 kCache16Count = 1 << kCache16Bits,
125 kCache32Bits = 8, // pretty much should always be 8
126 kCache32Count = 1 << kCache32Bits
127 };
128 virtual void flatten(SkFlattenableWriteBuffer& );
129 const uint16_t* getCache16();
130 const SkPMColor* getCache32();
131
132private:
133 enum {
134 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
135
136 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec))
137 };
138 SkColor fStorage[(kStorageSize + 3) >> 2];
139 SkColor* fOrigColors;
140
141 uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
142 SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
143
144 uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
145 SkPMColor* fCache32Storage; // storage for fCache32, allocated on demand
146 unsigned fCacheAlpha; // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value
147
148 typedef SkShader INHERITED;
149};
150
reed@android.com41bccf52009-04-03 13:33:51 +0000151static inline unsigned scalarToU16(SkScalar x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152 SkASSERT(x >= 0 && x <= SK_Scalar1);
153
154#ifdef SK_SCALAR_IS_FLOAT
155 return (unsigned)(x * 0xFFFF);
156#else
157 return x - (x >> 16); // probably should be x - (x > 0x7FFF) but that is slower
158#endif
159}
160
reed@android.com41bccf52009-04-03 13:33:51 +0000161Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[],
162 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163 SkASSERT(colorCount > 1);
164
165 fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return
166
167 fMapper = mapper;
168 mapper->safeRef();
169
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170 SkASSERT((unsigned)mode < SkShader::kTileModeCount);
171 SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
172 fTileMode = mode;
173 fTileProc = gTileProcs[mode];
reed@android.com41bccf52009-04-03 13:33:51 +0000174
175 fCache16 = fCache16Storage = NULL;
176 fCache32 = fCache32Storage = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177
reed@android.com41bccf52009-04-03 13:33:51 +0000178 /* Note: we let the caller skip the first and/or last position.
179 i.e. pos[0] = 0.3, pos[1] = 0.7
180 In these cases, we insert dummy entries to ensure that the final data
181 will be bracketed by [0, 1].
182 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
183
184 Thus colorCount (the caller's value, and fColorCount (our value) may
185 differ by up to 2. In the above example:
186 colorCount = 2
187 fColorCount = 4
188 */
189 fColorCount = colorCount;
190 // check if we need to add in dummy start and/or end position/colors
191 bool dummyFirst = false;
192 bool dummyLast = false;
193 if (pos) {
194 dummyFirst = pos[0] != 0;
195 dummyLast = pos[colorCount - 1] != SK_Scalar1;
196 fColorCount += dummyFirst + dummyLast;
197 }
198
199 if (fColorCount > kColorStorageCount) {
200 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
201 fOrigColors = reinterpret_cast<SkColor*>(
202 sk_malloc_throw(size * fColorCount));
203 }
204 else {
205 fOrigColors = fStorage;
206 }
207
208 // Now copy over the colors, adding the dummies as needed
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209 {
reed@android.com41bccf52009-04-03 13:33:51 +0000210 SkColor* origColors = fOrigColors;
211 if (dummyFirst) {
212 *origColors++ = colors[0];
213 }
214 memcpy(origColors, colors, colorCount * sizeof(SkColor));
215 if (dummyLast) {
216 origColors += colorCount;
217 *origColors = colors[colorCount - 1];
218 }
219 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220
reed@android.com41bccf52009-04-03 13:33:51 +0000221 // our premul colors point to the 2nd half of the array
222 // these are assigned each time in setContext
223 fARGB32 = fOrigColors + fColorCount;
224 fRecs = (Rec*)(fARGB32 + fColorCount);
225 if (fColorCount > 2) {
226 Rec* recs = fRecs;
227 recs->fPos = 0;
228 // recs->fScale = 0; // unused;
229 recs += 1;
230 if (pos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 /* We need to convert the user's array of relative positions into
232 fixed-point positions and scale factors. We need these results
233 to be strictly monotonic (no two values equal or out of order).
234 Hence this complex loop that just jams a zero for the scale
235 value if it sees a segment out of order, and it assures that
236 we start at 0 and end at 1.0
237 */
238 SkFixed prev = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000239 int startIndex = dummyFirst ? 0 : 1;
240 int count = colorCount + dummyLast;
241 for (int i = startIndex; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 // force the last value to be 1.0
243 SkFixed curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000244 if (i == colorCount) { // we're really at the dummyLast
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245 curr = SK_Fixed1;
reed@android.com41bccf52009-04-03 13:33:51 +0000246 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 curr = SkScalarToFixed(pos[i]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 }
reed@android.com41bccf52009-04-03 13:33:51 +0000249 // pin curr withing range
250 if (curr < 0) {
251 curr = 0;
252 } else if (curr > SK_Fixed1) {
253 curr = SK_Fixed1;
254 }
255 recs->fPos = curr;
256 if (curr > prev) {
257 recs->fScale = (1 << 24) / (curr - prev);
258 } else {
259 recs->fScale = 0; // ignore this segment
260 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261 // get ready for the next value
262 prev = curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000263 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 }
reed@android.com41bccf52009-04-03 13:33:51 +0000265 } else { // assume even distribution
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266 SkFixed dp = SK_Fixed1 / (colorCount - 1);
267 SkFixed p = dp;
268 SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp
reed@android.com41bccf52009-04-03 13:33:51 +0000269 for (int i = 1; i < colorCount; i++) {
270 recs->fPos = p;
271 recs->fScale = scale;
272 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273 p += dp;
274 }
275 }
276 }
277}
278
279Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
reed@android.com41bccf52009-04-03 13:33:51 +0000280 INHERITED(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 fCacheAlpha = 256;
282
283 fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
284
285 fCache16 = fCache16Storage = NULL;
286 fCache32 = fCache32Storage = NULL;
287
reed@android.com41bccf52009-04-03 13:33:51 +0000288 int colorCount = fColorCount = buffer.readU32();
289 if (colorCount > kColorStorageCount) {
290 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
291 fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
292 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 fOrigColors = fStorage;
reed@android.com41bccf52009-04-03 13:33:51 +0000294 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295 buffer.read(fOrigColors, colorCount * sizeof(SkColor));
296 fARGB32 = fOrigColors + colorCount;
297
298 fTileMode = (TileMode)buffer.readU8();
299 fTileProc = gTileProcs[fTileMode];
300 fRecs = (Rec*)(fARGB32 + colorCount);
301 if (colorCount > 2) {
302 Rec* recs = fRecs;
303 recs[0].fPos = 0;
304 for (int i = 1; i < colorCount; i++) {
305 recs[i].fPos = buffer.readS32();
306 recs[i].fScale = buffer.readU32();
307 }
308 }
309 buffer.read(&fPtsToUnit, sizeof(SkMatrix));
310}
311
reed@android.com41bccf52009-04-03 13:33:51 +0000312Gradient_Shader::~Gradient_Shader() {
313 if (fCache16Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314 sk_free(fCache16Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000315 }
316 if (fCache32Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 sk_free(fCache32Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000318 }
319 if (fOrigColors != fStorage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 sk_free(fOrigColors);
reed@android.com41bccf52009-04-03 13:33:51 +0000321 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 fMapper->safeUnref();
323}
324
reed@android.com41bccf52009-04-03 13:33:51 +0000325void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326 this->INHERITED::flatten(buffer);
327 buffer.writeFlattenable(fMapper);
reed@android.com41bccf52009-04-03 13:33:51 +0000328 buffer.write32(fColorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000329 buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
330 buffer.write8(fTileMode);
331 if (fColorCount > 2) {
332 Rec* recs = fRecs;
333 for (int i = 1; i < fColorCount; i++) {
334 buffer.write32(recs[i].fPos);
335 buffer.write32(recs[i].fScale);
336 }
337 }
338 buffer.writeMul4(&fPtsToUnit, sizeof(SkMatrix));
339}
340
341bool Gradient_Shader::setContext(const SkBitmap& device,
342 const SkPaint& paint,
reed@android.com41bccf52009-04-03 13:33:51 +0000343 const SkMatrix& matrix) {
344 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345 return false;
reed@android.com41bccf52009-04-03 13:33:51 +0000346 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347
348 const SkMatrix& inverse = this->getTotalInverse();
349
350 if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
351 return false;
352 }
353
354 fDstToIndexProc = fDstToIndex.getMapXYProc();
355 fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
356
357 // now convert our colors in to PMColors
358 unsigned paintAlpha = this->getPaintAlpha();
359 unsigned colorAlpha = 0xFF;
360
reed@android.com41bccf52009-04-03 13:33:51 +0000361 for (int i = 0; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000362 SkColor src = fOrigColors[i];
363 unsigned sa = SkColorGetA(src);
364 colorAlpha &= sa;
365
366 // now modulate it by the paint for our resulting ARGB32 array
367 sa = SkMulDiv255Round(sa, paintAlpha);
368 fARGB32[i] = SkPreMultiplyARGB(sa, SkColorGetR(src), SkColorGetG(src),
369 SkColorGetB(src));
370 }
371
372 fFlags = this->INHERITED::getFlags();
373 if ((colorAlpha & paintAlpha) == 0xFF) {
374 fFlags |= kOpaqueAlpha_Flag;
375 }
376 // we can do span16 as long as our individual colors are opaque,
377 // regardless of the paint's alpha
378 if (0xFF == colorAlpha) {
379 fFlags |= kHasSpan16_Flag;
380 }
381
382 // if the new alpha differs from the previous time we were called, inval our cache
383 // this will trigger the cache to be rebuilt.
384 // we don't care about the first time, since the cache ptrs will already be NULL
385 if (fCacheAlpha != paintAlpha) {
386 fCache16 = NULL; // inval the cache
387 fCache32 = NULL; // inval the cache
388 fCacheAlpha = paintAlpha; // record the new alpha
389 }
390 return true;
391}
392
reed@android.com41bccf52009-04-03 13:33:51 +0000393static inline int blend8(int a, int b, int scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000394 SkASSERT(a == SkToU8(a));
395 SkASSERT(b == SkToU8(b));
396 SkASSERT(scale >= 0 && scale <= 256);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397 return a + ((b - a) * scale >> 8);
398}
399
reed@android.com41bccf52009-04-03 13:33:51 +0000400static inline uint32_t dot8_blend_packed32(uint32_t s0, uint32_t s1,
401 int blend) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402#if 0
403 int a = blend8(SkGetPackedA32(s0), SkGetPackedA32(s1), blend);
404 int r = blend8(SkGetPackedR32(s0), SkGetPackedR32(s1), blend);
405 int g = blend8(SkGetPackedG32(s0), SkGetPackedG32(s1), blend);
406 int b = blend8(SkGetPackedB32(s0), SkGetPackedB32(s1), blend);
407
408 return SkPackARGB32(a, r, g, b);
409#else
410 int otherBlend = 256 - blend;
411
412#if 0
413 U32 t0 = (((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF;
414 U32 t1 = (((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00;
415 SkASSERT((t0 & t1) == 0);
416 return t0 | t1;
417#else
418 return ((((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF) |
419 ((((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00);
420#endif
421
422#endif
423}
424
425#define Fixed_To_Dot8(x) (((x) + 0x80) >> 8)
426
reed@android.com41bccf52009-04-03 13:33:51 +0000427/** We take the original colors, not our premultiplied PMColors, since we can
428 build a 16bit table as long as the original colors are opaque, even if the
429 paint specifies a non-opaque alpha.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430*/
reed@android.com41bccf52009-04-03 13:33:51 +0000431static void build_16bit_cache(uint16_t cache[], SkColor c0, SkColor c1,
432 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433 SkASSERT(count > 1);
434 SkASSERT(SkColorGetA(c0) == 0xFF);
435 SkASSERT(SkColorGetA(c1) == 0xFF);
436
437 SkFixed r = SkColorGetR(c0);
438 SkFixed g = SkColorGetG(c0);
439 SkFixed b = SkColorGetB(c0);
440
441 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
442 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
443 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
444
445 r = SkIntToFixed(r) + 0x8000;
446 g = SkIntToFixed(g) + 0x8000;
447 b = SkIntToFixed(b) + 0x8000;
448
449 do {
450 unsigned rr = r >> 16;
451 unsigned gg = g >> 16;
452 unsigned bb = b >> 16;
453 cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
454 cache[64] = SkDitherPack888ToRGB16(rr, gg, bb);
455 cache += 1;
456 r += dr;
457 g += dg;
458 b += db;
459 } while (--count != 0);
460}
461
reed@android.com41bccf52009-04-03 13:33:51 +0000462static void build_32bit_cache(SkPMColor cache[], SkPMColor c0, SkPMColor c1,
463 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000464 SkASSERT(count > 1);
465
466 SkFixed a = SkGetPackedA32(c0);
467 SkFixed r = SkGetPackedR32(c0);
468 SkFixed g = SkGetPackedG32(c0);
469 SkFixed b = SkGetPackedB32(c0);
470
471 SkFixed da = SkIntToFixed(SkGetPackedA32(c1) - a) / (count - 1);
472 SkFixed dr = SkIntToFixed(SkGetPackedR32(c1) - r) / (count - 1);
473 SkFixed dg = SkIntToFixed(SkGetPackedG32(c1) - g) / (count - 1);
474 SkFixed db = SkIntToFixed(SkGetPackedB32(c1) - b) / (count - 1);
475
476 a = SkIntToFixed(a) + 0x8000;
477 r = SkIntToFixed(r) + 0x8000;
478 g = SkIntToFixed(g) + 0x8000;
479 b = SkIntToFixed(b) + 0x8000;
480
481 do {
482 *cache++ = SkPackARGB32(a >> 16, r >> 16, g >> 16, b >> 16);
483 a += da;
484 r += dr;
485 g += dg;
486 b += db;
487 } while (--count != 0);
488}
489
reed@android.com41bccf52009-04-03 13:33:51 +0000490static inline int SkFixedToFFFF(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000491 SkASSERT((unsigned)x <= SK_Fixed1);
492 return x - (x >> 16);
493}
494
reed@android.com41bccf52009-04-03 13:33:51 +0000495static inline U16CPU dot6to16(unsigned x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000496 SkASSERT(x < 64);
497 return (x << 10) | (x << 4) | (x >> 2);
498}
499
reed@android.com41bccf52009-04-03 13:33:51 +0000500const uint16_t* Gradient_Shader::getCache16() {
501 if (fCache16 == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000502 if (fCache16Storage == NULL) // set the storage and our working ptr
503#ifdef TEST_GRADIENT_DITHER
504 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
505#else
506 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count);
507#endif
508 fCache16 = fCache16Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000509 if (fColorCount == 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510 build_16bit_cache(fCache16, fOrigColors[0], fOrigColors[1], kCache16Count);
reed@android.com41bccf52009-04-03 13:33:51 +0000511 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512 Rec* rec = fRecs;
513 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000514 for (int i = 1; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache16Bits);
516 SkASSERT(nextIndex < kCache16Count);
517
518 if (nextIndex > prevIndex)
519 build_16bit_cache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
520 prevIndex = nextIndex;
521 }
522 SkASSERT(prevIndex == kCache16Count - 1);
523 }
524
reed@android.com41bccf52009-04-03 13:33:51 +0000525 if (fMapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526#ifdef TEST_GRADIENT_DITHER
527 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
528#else
529 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count);
530#endif
531 uint16_t* linear = fCache16; // just computed linear data
532 uint16_t* mapped = fCache16Storage; // storage for mapped data
533 SkUnitMapper* map = fMapper;
reed@android.com41bccf52009-04-03 13:33:51 +0000534 for (int i = 0; i < 64; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000535 int index = map->mapUnit16(dot6to16(i)) >> 10;
536 mapped[i] = linear[index];
537#ifdef TEST_GRADIENT_DITHER
538 mapped[i + 64] = linear[index + 64];
539#endif
540 }
541 sk_free(fCache16);
542 fCache16 = fCache16Storage;
543 }
544 }
545 return fCache16;
546}
547
reed@android.com41bccf52009-04-03 13:33:51 +0000548const SkPMColor* Gradient_Shader::getCache32() {
549 if (fCache32 == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000550 if (fCache32Storage == NULL) // set the storage and our working ptr
551 fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count);
552
553 fCache32 = fCache32Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000554 if (fColorCount == 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555 build_32bit_cache(fCache32, fARGB32[0], fARGB32[1], kCache32Count);
reed@android.com41bccf52009-04-03 13:33:51 +0000556 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557 Rec* rec = fRecs;
558 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000559 for (int i = 1; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache32Bits);
561 SkASSERT(nextIndex < kCache32Count);
562
563 if (nextIndex > prevIndex)
564 build_32bit_cache(fCache32 + prevIndex, fARGB32[i-1], fARGB32[i], nextIndex - prevIndex + 1);
565 prevIndex = nextIndex;
566 }
567 SkASSERT(prevIndex == kCache32Count - 1);
568 }
569
reed@android.com41bccf52009-04-03 13:33:51 +0000570 if (fMapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000571 fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count);
572 SkPMColor* linear = fCache32; // just computed linear data
573 SkPMColor* mapped = fCache32Storage; // storage for mapped data
574 SkUnitMapper* map = fMapper;
reed@android.com41bccf52009-04-03 13:33:51 +0000575 for (int i = 0; i < 256; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576 mapped[i] = linear[map->mapUnit16((i << 8) | i) >> 8];
reed@android.com41bccf52009-04-03 13:33:51 +0000577 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000578 sk_free(fCache32);
579 fCache32 = fCache32Storage;
580 }
581 }
582 return fCache32;
583}
584
585///////////////////////////////////////////////////////////////////////////
586
reed@android.com41bccf52009-04-03 13:33:51 +0000587static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000588 SkVector vec = pts[1] - pts[0];
589 SkScalar mag = vec.length();
590 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
591
592 vec.scale(inv);
593 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
594 matrix->postTranslate(-pts[0].fX, -pts[0].fY);
595 matrix->postScale(inv, inv);
596}
597
598///////////////////////////////////////////////////////////////////////////////
599
600class Linear_Gradient : public Gradient_Shader {
601public:
602 Linear_Gradient(const SkPoint pts[2],
603 const SkColor colors[], const SkScalar pos[], int colorCount,
604 SkShader::TileMode mode, SkUnitMapper* mapper)
605 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
606 {
607 pts_to_unit_matrix(pts, &fPtsToUnit);
608 }
609 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
610 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
611 virtual bool asABitmap(SkBitmap*, SkMatrix*, TileMode*);
612
613 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
614 return SkNEW_ARGS(Linear_Gradient, (buffer));
615 }
616
617protected:
618 Linear_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {};
619 virtual Factory getFactory() { return CreateProc; }
620
621private:
622 typedef Gradient_Shader INHERITED;
623};
624
625// Return true if fx, fx+dx, fx+2*dx, ... is always in range
reed@android.comfc25abd2009-01-15 14:38:33 +0000626static inline bool no_need_for_clamp(int fx, int dx, int count)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000627{
628 SkASSERT(count > 0);
629 return (unsigned)((fx | (fx + (count - 1) * dx)) >> 8) <= 0xFF;
630}
631
632void Linear_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
633{
634 SkASSERT(count > 0);
635
636 SkPoint srcPt;
637 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
638 TileProc proc = fTileProc;
639 const SkPMColor* cache = this->getCache32();
640
641 if (fDstToIndexClass != kPerspective_MatrixClass)
642 {
643 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
644 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
645
646 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
647 {
648 SkFixed dxStorage[1];
649 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
650 dx = dxStorage[0];
651 }
652 else
653 {
654 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
655 dx = SkScalarToFixed(fDstToIndex.getScaleX());
656 }
657
658 if (SkFixedNearlyZero(dx)) // we're a vertical gradient, so no change in a span
659 {
660 unsigned fi = proc(fx);
661 SkASSERT(fi <= 0xFFFF);
662 sk_memset32(dstC, cache[fi >> (16 - kCache32Bits)], count);
663 }
664 else if (proc == clamp_tileproc)
665 {
666#if 0
667 if (no_need_for_clamp(fx, dx, count))
668 {
669 unsigned fi;
670 while ((count -= 4) >= 0)
671 {
672 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
673 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
674 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
675 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
676 }
677 SkASSERT(count <= -1 && count >= -4);
678 count += 4;
679 while (--count >= 0)
680 {
681 fi = fx >> 8;
682 SkASSERT(fi <= 0xFF);
683 fx += dx;
684 *dstC++ = cache[fi];
685 }
686 }
687 else
688#endif
689 do {
690 unsigned fi = SkClampMax(fx >> 8, 0xFF);
691 SkASSERT(fi <= 0xFF);
692 fx += dx;
693 *dstC++ = cache[fi];
694 } while (--count != 0);
695 }
696 else if (proc == mirror_tileproc)
697 {
698 do {
699 unsigned fi = mirror_8bits(fx >> 8);
700 SkASSERT(fi <= 0xFF);
701 fx += dx;
702 *dstC++ = cache[fi];
703 } while (--count != 0);
704 }
705 else
706 {
707 SkASSERT(proc == repeat_tileproc);
708 do {
709 unsigned fi = repeat_8bits(fx >> 8);
710 SkASSERT(fi <= 0xFF);
711 fx += dx;
712 *dstC++ = cache[fi];
713 } while (--count != 0);
714 }
715 }
716 else
717 {
718 SkScalar dstX = SkIntToScalar(x);
719 SkScalar dstY = SkIntToScalar(y);
720 do {
721 dstProc(fDstToIndex, dstX, dstY, &srcPt);
722 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
723 SkASSERT(fi <= 0xFFFF);
724 *dstC++ = cache[fi >> (16 - kCache32Bits)];
725 dstX += SK_Scalar1;
726 } while (--count != 0);
727 }
728}
729
730bool Linear_Gradient::asABitmap(SkBitmap* bitmap, SkMatrix* matrix,
731 TileMode xy[]) {
732 if (bitmap) {
733 bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
734 bitmap->allocPixels(); // share with shader???
735 memcpy(bitmap->getPixels(), this->getCache32(), kCache32Count * 4);
736 }
737 if (matrix) {
738 matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1);
739 matrix->preConcat(fPtsToUnit);
740 }
741 if (xy) {
742 xy[0] = fTileMode;
743 xy[1] = kClamp_TileMode;
744 }
745 return true;
746}
747
748#ifdef TEST_GRADIENT_DITHER
749static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, int count)
750{
reed@android.com0becfc52009-01-13 13:26:44 +0000751 if (reinterpret_cast<uintptr_t>(dst) & 2)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000752 {
753 *dst++ = value;
754 count -= 1;
755 SkTSwap(value, other);
756 }
757
758 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
759
760 if (count & 1)
761 dst[count - 1] = value;
762}
763#endif
764
765void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
766{
767 SkASSERT(count > 0);
768
769 SkPoint srcPt;
770 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
771 TileProc proc = fTileProc;
772 const uint16_t* cache = this->getCache16();
773#ifdef TEST_GRADIENT_DITHER
774 int toggle = ((x ^ y) & 1) << kCache16Bits;
775#endif
776
777 if (fDstToIndexClass != kPerspective_MatrixClass)
778 {
779 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
780 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
781
782 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
783 {
784 SkFixed dxStorage[1];
785 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
786 dx = dxStorage[0];
787 }
788 else
789 {
790 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
791 dx = SkScalarToFixed(fDstToIndex.getScaleX());
792 }
793
794 if (SkFixedNearlyZero(dx)) // we're a vertical gradient, so no change in a span
795 {
796 unsigned fi = proc(fx) >> 10;
797 SkASSERT(fi <= 63);
798#ifdef TEST_GRADIENT_DITHER
799 dither_memset16(dstC, cache[toggle + fi], cache[(toggle ^ (1 << kCache16Bits)) + fi], count);
800#else
801 sk_memset16(dstC, cache[fi], count);
802#endif
803 }
804 else if (proc == clamp_tileproc)
805 {
806 do {
807 unsigned fi = SkClampMax(fx >> 10, 63);
808 SkASSERT(fi <= 63);
809 fx += dx;
810#ifdef TEST_GRADIENT_DITHER
811 *dstC++ = cache[toggle + fi];
812 toggle ^= (1 << kCache16Bits);
813#else
814 *dstC++ = cache[fi];
815#endif
816 } while (--count != 0);
817 }
818 else if (proc == mirror_tileproc)
819 {
820 do {
821 unsigned fi = mirror_6bits(fx >> 10);
822 SkASSERT(fi <= 0x3F);
823 fx += dx;
824#ifdef TEST_GRADIENT_DITHER
825 *dstC++ = cache[toggle + fi];
826 toggle ^= (1 << kCache16Bits);
827#else
828 *dstC++ = cache[fi];
829#endif
830 } while (--count != 0);
831 }
832 else
833 {
834 SkASSERT(proc == repeat_tileproc);
835 do {
836 unsigned fi = repeat_6bits(fx >> 10);
837 SkASSERT(fi <= 0x3F);
838 fx += dx;
839#ifdef TEST_GRADIENT_DITHER
840 *dstC++ = cache[toggle + fi];
841 toggle ^= (1 << kCache16Bits);
842#else
843 *dstC++ = cache[fi];
844#endif
845 } while (--count != 0);
846 }
847 }
848 else
849 {
850 SkScalar dstX = SkIntToScalar(x);
851 SkScalar dstY = SkIntToScalar(y);
852 do {
853 dstProc(fDstToIndex, dstX, dstY, &srcPt);
854 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
855 SkASSERT(fi <= 0xFFFF);
856
857 int index = fi >> (16 - kCache16Bits);
858#ifdef TEST_GRADIENT_DITHER
859 *dstC++ = cache[toggle + index];
860 toggle ^= (1 << kCache16Bits);
861#else
862 *dstC++ = cache[index];
863#endif
864
865 dstX += SK_Scalar1;
866 } while (--count != 0);
867 }
868}
869
870///////////////////////////////////////////////////////////////////////////////
871
872#define kSQRT_TABLE_BITS 11
873#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
874
875#include "SkRadialGradient_Table.h"
876
877#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
878
879#include <stdio.h>
880
881void SkRadialGradient_BuildTable()
882{
883 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
884
885 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
886 SkASSERT(file);
887 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
888
889 for (int i = 0; i < kSQRT_TABLE_SIZE; i++)
890 {
891 if ((i & 15) == 0)
892 ::fprintf(file, "\t");
893
894 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
895
896 ::fprintf(file, "0x%02X", value);
897 if (i < kSQRT_TABLE_SIZE-1)
898 ::fprintf(file, ", ");
899 if ((i & 15) == 15)
900 ::fprintf(file, "\n");
901 }
902 ::fprintf(file, "};\n");
903 ::fclose(file);
904}
905
906#endif
907
908
909static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius, SkMatrix* matrix)
910{
911 SkScalar inv = SkScalarInvert(radius);
912
913 matrix->setTranslate(-center.fX, -center.fY);
914 matrix->postScale(inv, inv);
915}
916
917class Radial_Gradient : public Gradient_Shader {
918public:
919 Radial_Gradient(const SkPoint& center, SkScalar radius,
920 const SkColor colors[], const SkScalar pos[], int colorCount,
921 SkShader::TileMode mode, SkUnitMapper* mapper)
922 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
923 {
924 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
925 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
926
927 rad_to_unit_matrix(center, radius, &fPtsToUnit);
928 }
929 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
930 {
931 SkASSERT(count > 0);
932
933 SkPoint srcPt;
934 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
935 TileProc proc = fTileProc;
936 const SkPMColor* cache = this->getCache32();
937
938 if (fDstToIndexClass != kPerspective_MatrixClass)
939 {
940 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
941 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
942 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
943
944 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
945 {
946 SkFixed storage[2];
947 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
948 dx = storage[0];
949 dy = storage[1];
950 }
951 else
952 {
953 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
954 dx = SkScalarToFixed(fDstToIndex.getScaleX());
955 dy = SkScalarToFixed(fDstToIndex.getSkewY());
956 }
957
958 if (proc == clamp_tileproc)
959 {
960 const uint8_t* sqrt_table = gSqrt8Table;
961 fx >>= 1;
962 dx >>= 1;
963 fy >>= 1;
964 dy >>= 1;
965 do {
966 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
967 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
968 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
969 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
970 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache32Bits)];
971 fx += dx;
972 fy += dy;
973 } while (--count != 0);
974 }
975 else if (proc == mirror_tileproc)
976 {
977 do {
978 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
979 unsigned fi = mirror_tileproc(dist);
980 SkASSERT(fi <= 0xFFFF);
981 *dstC++ = cache[fi >> (16 - kCache32Bits)];
982 fx += dx;
983 fy += dy;
984 } while (--count != 0);
985 }
986 else
987 {
988 SkASSERT(proc == repeat_tileproc);
989 do {
990 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
991 unsigned fi = repeat_tileproc(dist);
992 SkASSERT(fi <= 0xFFFF);
993 *dstC++ = cache[fi >> (16 - kCache32Bits)];
994 fx += dx;
995 fy += dy;
996 } while (--count != 0);
997 }
998 }
999 else // perspective case
1000 {
1001 SkScalar dstX = SkIntToScalar(x);
1002 SkScalar dstY = SkIntToScalar(y);
1003 do {
1004 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1005 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1006 SkASSERT(fi <= 0xFFFF);
1007 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1008 dstX += SK_Scalar1;
1009 } while (--count != 0);
1010 }
1011 }
1012 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count)
1013 {
1014 SkASSERT(count > 0);
1015
1016 SkPoint srcPt;
1017 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1018 TileProc proc = fTileProc;
1019 const uint16_t* cache = this->getCache16();
1020#ifdef TEST_GRADIENT_DITHER
1021 int toggle = ((x ^ y) & 1) << kCache16Bits;
1022#endif
1023
1024 if (fDstToIndexClass != kPerspective_MatrixClass)
1025 {
1026 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
1027 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1028 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1029
1030 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1031 {
1032 SkFixed storage[2];
1033 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1034 dx = storage[0];
1035 dy = storage[1];
1036 }
1037 else
1038 {
1039 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1040 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1041 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1042 }
1043
1044 if (proc == clamp_tileproc)
1045 {
1046 const uint8_t* sqrt_table = gSqrt8Table;
1047
1048 /* knock these down so we can pin against +- 0x7FFF, which is an immediate load,
1049 rather than 0xFFFF which is slower. This is a compromise, since it reduces our
1050 precision, but that appears to be visually OK. If we decide this is OK for
1051 all of our cases, we could (it seems) put this scale-down into fDstToIndex,
1052 to avoid having to do these extra shifts each time.
1053 */
1054 fx >>= 1;
1055 dx >>= 1;
1056 fy >>= 1;
1057 dy >>= 1;
1058 if (dy == 0) // might perform this check for the other modes, but the win will be a smaller % of the total
1059 {
1060 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1061 fy *= fy;
1062 do {
1063 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1064 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
1065 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1066 fx += dx;
1067#ifdef TEST_GRADIENT_DITHER
1068 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1069 toggle ^= (1 << kCache16Bits);
1070#else
1071 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache16Bits)];
1072#endif
1073 } while (--count != 0);
1074 }
1075 else
1076 {
1077 do {
1078 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1079 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1080 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1081 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1082 fx += dx;
1083 fy += dy;
1084#ifdef TEST_GRADIENT_DITHER
1085 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1086 toggle ^= (1 << kCache16Bits);
1087#else
1088 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache16Bits)];
1089#endif
1090 } while (--count != 0);
1091 }
1092 }
1093 else if (proc == mirror_tileproc)
1094 {
1095 do {
1096 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1097 unsigned fi = mirror_tileproc(dist);
1098 SkASSERT(fi <= 0xFFFF);
1099 fx += dx;
1100 fy += dy;
1101#ifdef TEST_GRADIENT_DITHER
1102 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1103 toggle ^= (1 << kCache16Bits);
1104#else
1105 *dstC++ = cache[fi >> (16 - kCache16Bits)];
1106#endif
1107 } while (--count != 0);
1108 }
1109 else
1110 {
1111 SkASSERT(proc == repeat_tileproc);
1112 do {
1113 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1114 unsigned fi = repeat_tileproc(dist);
1115 SkASSERT(fi <= 0xFFFF);
1116 fx += dx;
1117 fy += dy;
1118#ifdef TEST_GRADIENT_DITHER
1119 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1120 toggle ^= (1 << kCache16Bits);
1121#else
1122 *dstC++ = cache[fi >> (16 - kCache16Bits)];
1123#endif
1124 } while (--count != 0);
1125 }
1126 }
1127 else // perspective case
1128 {
1129 SkScalar dstX = SkIntToScalar(x);
1130 SkScalar dstY = SkIntToScalar(y);
1131 do {
1132 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1133 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1134 SkASSERT(fi <= 0xFFFF);
1135
1136 int index = fi >> (16 - kCache16Bits);
1137#ifdef TEST_GRADIENT_DITHER
1138 *dstC++ = cache[toggle + index];
1139 toggle ^= (1 << kCache16Bits);
1140#else
1141 *dstC++ = cache[index];
1142#endif
1143
1144 dstX += SK_Scalar1;
1145 } while (--count != 0);
1146 }
1147 }
1148
1149 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1150 return SkNEW_ARGS(Radial_Gradient, (buffer));
1151 }
1152
1153protected:
1154 Radial_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {};
1155 virtual Factory getFactory() { return CreateProc; }
1156
1157private:
1158 typedef Gradient_Shader INHERITED;
1159};
1160
1161///////////////////////////////////////////////////////////////////////////////
1162
1163class Sweep_Gradient : public Gradient_Shader {
1164public:
1165 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
1166 const SkScalar pos[], int count, SkUnitMapper* mapper)
1167 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper)
1168 {
1169 fPtsToUnit.setTranslate(-cx, -cy);
1170 }
1171 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
1172 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
1173
1174 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1175 return SkNEW_ARGS(Sweep_Gradient, (buffer));
1176 }
1177
1178protected:
1179 Sweep_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {}
1180
1181 virtual Factory getFactory() { return CreateProc; }
1182
1183private:
1184 typedef Gradient_Shader INHERITED;
1185};
1186
1187#ifdef COMPUTE_SWEEP_TABLE
1188#define PI 3.14159265
1189static bool gSweepTableReady;
1190static uint8_t gSweepTable[65];
1191
1192/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
1193 We scale the results to [0..32]
1194*/
1195static const uint8_t* build_sweep_table()
1196{
1197 if (!gSweepTableReady)
1198 {
1199 const int N = 65;
1200 const double DENOM = N - 1;
1201
1202 for (int i = 0; i < N; i++)
1203 {
1204 double arg = i / DENOM;
1205 double v = atan(arg);
1206 int iv = (int)round(v * DENOM * 2 / PI);
1207// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
1208 printf("%d, ", iv);
1209 gSweepTable[i] = iv;
1210 }
1211 gSweepTableReady = true;
1212 }
1213 return gSweepTable;
1214}
1215#else
1216static const uint8_t gSweepTable[] = {
1217 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
1218 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
1219 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
1220 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
1221 32
1222};
1223static const uint8_t* build_sweep_table() { return gSweepTable; }
1224#endif
1225
1226// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
1227// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
1228// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
1229
1230//unsigned div_64(int numer, int denom);
1231static unsigned div_64(int numer, int denom)
1232{
1233 SkASSERT(numer <= denom);
1234 SkASSERT(numer > 0);
1235 SkASSERT(denom > 0);
1236
1237 int nbits = SkCLZ(numer);
1238 int dbits = SkCLZ(denom);
1239 int bits = 6 - nbits + dbits;
1240 SkASSERT(bits <= 6);
1241
1242 if (bits < 0) // detect underflow
1243 return 0;
1244
1245 denom <<= dbits - 1;
1246 numer <<= nbits - 1;
1247
1248 unsigned result = 0;
1249
1250 // do the first one
1251 if ((numer -= denom) >= 0)
1252 result = 1;
1253 else
1254 numer += denom;
1255
1256 // Now fall into our switch statement if there are more bits to compute
1257 if (bits > 0)
1258 {
1259 // make room for the rest of the answer bits
1260 result <<= bits;
1261 switch (bits) {
1262 case 6:
1263 if ((numer = (numer << 1) - denom) >= 0)
1264 result |= 32;
1265 else
1266 numer += denom;
1267 case 5:
1268 if ((numer = (numer << 1) - denom) >= 0)
1269 result |= 16;
1270 else
1271 numer += denom;
1272 case 4:
1273 if ((numer = (numer << 1) - denom) >= 0)
1274 result |= 8;
1275 else
1276 numer += denom;
1277 case 3:
1278 if ((numer = (numer << 1) - denom) >= 0)
1279 result |= 4;
1280 else
1281 numer += denom;
1282 case 2:
1283 if ((numer = (numer << 1) - denom) >= 0)
1284 result |= 2;
1285 else
1286 numer += denom;
1287 case 1:
1288 default: // not strictly need, but makes GCC make better ARM code
1289 if ((numer = (numer << 1) - denom) >= 0)
1290 result |= 1;
1291 else
1292 numer += denom;
1293 }
1294 }
1295 return result;
1296}
1297
1298// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
1299static unsigned atan_0_90(SkFixed y, SkFixed x)
1300{
1301#ifdef SK_DEBUG
1302 {
1303 static bool gOnce;
1304 if (!gOnce)
1305 {
1306 gOnce = true;
1307 SkASSERT(div_64(55, 55) == 64);
1308 SkASSERT(div_64(128, 256) == 32);
1309 SkASSERT(div_64(2326528, 4685824) == 31);
1310 SkASSERT(div_64(753664, 5210112) == 9);
1311 SkASSERT(div_64(229376, 4882432) == 3);
1312 SkASSERT(div_64(2, 64) == 2);
1313 SkASSERT(div_64(1, 64) == 1);
1314 // test that we handle underflow correctly
1315 SkASSERT(div_64(12345, 0x54321234) == 0);
1316 }
1317 }
1318#endif
1319
1320 SkASSERT(y > 0 && x > 0);
1321 const uint8_t* table = build_sweep_table();
1322
1323 unsigned result;
1324 bool swap = (x < y);
1325 if (swap)
1326 {
1327 // first part of the atan(v) = PI/2 - atan(1/v) identity
1328 // since our div_64 and table want v <= 1, where v = y/x
1329 SkTSwap<SkFixed>(x, y);
1330 }
1331
1332 result = div_64(y, x);
1333
1334#ifdef SK_DEBUG
1335 {
1336 unsigned result2 = SkDivBits(y, x, 6);
1337 SkASSERT(result2 == result ||
1338 (result == 1 && result2 == 0));
1339 }
1340#endif
1341
1342 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
1343 result = table[result];
1344
1345 if (swap)
1346 {
1347 // complete the atan(v) = PI/2 - atan(1/v) identity
1348 result = 64 - result;
1349 // pin to 63
1350 result -= result >> 6;
1351 }
1352
1353 SkASSERT(result <= 63);
1354 return result;
1355}
1356
1357// returns angle in a circle [0..2PI) -> [0..255]
1358static unsigned SkATan2_255(SkFixed y, SkFixed x)
1359{
1360 if (x == 0)
1361 {
1362 if (y == 0)
1363 return 0;
1364 return y < 0 ? 192 : 64;
1365 }
1366 if (y == 0)
1367 return x < 0 ? 128 : 0;
1368
1369 /* Find the right quadrant for x,y
1370 Since atan_0_90 only handles the first quadrant, we rotate x,y
1371 appropriately before calling it, and then add the right amount
1372 to account for the real quadrant.
1373 quadrant 0 : add 0 | x > 0 && y > 0
1374 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
1375 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
1376 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
1377
1378 map x<0 to (1 << 6)
1379 map y<0 to (3 << 6)
1380 add = map_x ^ map_y
1381 */
1382 int xsign = x >> 31;
1383 int ysign = y >> 31;
1384 int add = ((-xsign) ^ (ysign & 3)) << 6;
1385
1386#ifdef SK_DEBUG
1387 if (0 == add)
1388 SkASSERT(x > 0 && y > 0);
1389 else if (64 == add)
1390 SkASSERT(x < 0 && y > 0);
1391 else if (128 == add)
1392 SkASSERT(x < 0 && y < 0);
1393 else if (192 == add)
1394 SkASSERT(x > 0 && y < 0);
1395 else
1396 SkASSERT(!"bad value for add");
1397#endif
1398
1399 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
1400 where we need to rotate x,y by 90 or -90
1401 */
1402 x = (x ^ xsign) - xsign;
1403 y = (y ^ ysign) - ysign;
1404 if (add & 64) // quads 1 or 3 need to swap x,y
1405 SkTSwap<SkFixed>(x, y);
1406
1407 unsigned result = add + atan_0_90(y, x);
1408 SkASSERT(result < 256);
1409 return result;
1410}
1411
1412void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
1413{
1414 SkMatrix::MapXYProc proc = fDstToIndexProc;
1415 const SkMatrix& matrix = fDstToIndex;
1416 const SkPMColor* cache = this->getCache32();
1417 SkPoint srcPt;
1418
1419 if (fDstToIndexClass != kPerspective_MatrixClass)
1420 {
1421 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1422 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1423 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1424 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1425
1426 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1427 {
1428 SkFixed storage[2];
1429 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1430 &storage[0], &storage[1]);
1431 dx = storage[0];
1432 dy = storage[1];
1433 }
1434 else
1435 {
1436 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1437 dx = SkScalarToFixed(matrix.getScaleX());
1438 dy = SkScalarToFixed(matrix.getSkewY());
1439 }
1440
1441 for (; count > 0; --count)
1442 {
1443 *dstC++ = cache[SkATan2_255(fy, fx)];
1444 fx += dx;
1445 fy += dy;
1446 }
1447 }
1448 else // perspective case
1449 {
1450 for (int stop = x + count; x < stop; x++)
1451 {
1452 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1453 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1454
1455 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1456 SkScalarToFixed(srcPt.fX));
1457 *dstC++ = cache[index];
1458 }
1459 }
1460}
1461
1462void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
1463{
1464 SkMatrix::MapXYProc proc = fDstToIndexProc;
1465 const SkMatrix& matrix = fDstToIndex;
1466 const uint16_t* cache = this->getCache16();
1467 int toggle = ((x ^ y) & 1) << kCache16Bits;
1468 SkPoint srcPt;
1469
1470 if (fDstToIndexClass != kPerspective_MatrixClass)
1471 {
1472 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1473 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1474 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1475 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1476
1477 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1478 {
1479 SkFixed storage[2];
1480 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1481 &storage[0], &storage[1]);
1482 dx = storage[0];
1483 dy = storage[1];
1484 }
1485 else
1486 {
1487 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1488 dx = SkScalarToFixed(matrix.getScaleX());
1489 dy = SkScalarToFixed(matrix.getSkewY());
1490 }
1491
1492 for (; count > 0; --count)
1493 {
1494 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
1495 *dstC++ = cache[toggle + index];
1496 toggle ^= (1 << kCache16Bits);
1497 fx += dx;
1498 fy += dy;
1499 }
1500 }
1501 else // perspective case
1502 {
1503 for (int stop = x + count; x < stop; x++)
1504 {
1505 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1506 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1507
1508 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1509 SkScalarToFixed(srcPt.fX));
1510 index >>= (8 - kCache16Bits);
1511 *dstC++ = cache[toggle + index];
1512 toggle ^= (1 << kCache16Bits);
1513 }
1514 }
1515}
1516
1517///////////////////////////////////////////////////////////////////////////
1518///////////////////////////////////////////////////////////////////////////
1519
1520// assumes colors is SkColor* and pos is SkScalar*
1521#define EXPAND_1_COLOR(count) \
1522 SkColor tmp[2]; \
1523 do { \
1524 if (1 == count) { \
1525 tmp[0] = tmp[1] = colors[0]; \
1526 colors = tmp; \
1527 pos = NULL; \
1528 count = 2; \
1529 } \
1530 } while (0)
1531
1532SkShader* SkGradientShader::CreateLinear( const SkPoint pts[2],
1533 const SkColor colors[], const SkScalar pos[], int colorCount,
1534 SkShader::TileMode mode, SkUnitMapper* mapper)
1535{
1536 if (NULL == pts || NULL == colors || colorCount < 1) {
1537 return NULL;
1538 }
1539 EXPAND_1_COLOR(colorCount);
1540
reed@android.com41bccf52009-04-03 13:33:51 +00001541 SkScalar posStorage[2];
1542 if (colorCount == 2 && pos == NULL) {
1543 posStorage[0] = SK_Scalar1/4;
1544 posStorage[1] = 3*SK_Scalar1/4;
1545 pos = posStorage;
1546 }
1547
reed@android.com8a1c16f2008-12-17 15:59:43 +00001548 return SkNEW_ARGS(Linear_Gradient, (pts, colors, pos, colorCount, mode, mapper));
1549}
1550
1551SkShader* SkGradientShader::CreateRadial( const SkPoint& center, SkScalar radius,
1552 const SkColor colors[], const SkScalar pos[], int colorCount,
1553 SkShader::TileMode mode, SkUnitMapper* mapper)
1554{
1555 if (radius <= 0 || NULL == colors || colorCount < 1) {
1556 return NULL;
1557 }
1558 EXPAND_1_COLOR(colorCount);
1559
1560 return SkNEW_ARGS(Radial_Gradient, (center, radius, colors, pos, colorCount, mode, mapper));
1561}
1562
1563SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
1564 const SkColor colors[],
1565 const SkScalar pos[],
1566 int count, SkUnitMapper* mapper)
1567{
1568 if (NULL == colors || count < 1) {
1569 return NULL;
1570 }
1571 EXPAND_1_COLOR(count);
1572
1573 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
1574}
1575
1576static SkFlattenable::Registrar gLinearGradientReg("Linear_Gradient",
1577 Linear_Gradient::CreateProc);
1578
1579static SkFlattenable::Registrar gRadialGradientReg("Radial_Gradient",
1580 Radial_Gradient::CreateProc);
1581
1582static SkFlattenable::Registrar gSweepGradientReg("Sweep_Gradient",
1583 Sweep_Gradient::CreateProc);
1584