blob: a0da6de333ab16d72ccc0552761825087ec5b0a8 [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
reed@android.com9b46e772009-06-05 12:24:41 +0000132 // called when we kill our cached colors (to be rebuilt later on demand)
reed@android.comec3d6e52009-06-05 14:43:55 +0000133 virtual void onCacheReset() = 0;
reed@android.com9b46e772009-06-05 12:24:41 +0000134
reed@android.com8a1c16f2008-12-17 15:59:43 +0000135private:
136 enum {
137 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
138
139 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec))
140 };
141 SkColor fStorage[(kStorageSize + 3) >> 2];
142 SkColor* fOrigColors;
143
144 uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
145 SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
146
147 uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
148 SkPMColor* fCache32Storage; // storage for fCache32, allocated on demand
149 unsigned fCacheAlpha; // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value
150
151 typedef SkShader INHERITED;
152};
153
reed@android.com41bccf52009-04-03 13:33:51 +0000154static inline unsigned scalarToU16(SkScalar x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155 SkASSERT(x >= 0 && x <= SK_Scalar1);
156
157#ifdef SK_SCALAR_IS_FLOAT
158 return (unsigned)(x * 0xFFFF);
159#else
160 return x - (x >> 16); // probably should be x - (x > 0x7FFF) but that is slower
161#endif
162}
163
reed@android.com41bccf52009-04-03 13:33:51 +0000164Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[],
165 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166 SkASSERT(colorCount > 1);
167
168 fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return
169
170 fMapper = mapper;
171 mapper->safeRef();
172
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173 SkASSERT((unsigned)mode < SkShader::kTileModeCount);
174 SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
175 fTileMode = mode;
176 fTileProc = gTileProcs[mode];
reed@android.com41bccf52009-04-03 13:33:51 +0000177
178 fCache16 = fCache16Storage = NULL;
179 fCache32 = fCache32Storage = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180
reed@android.com41bccf52009-04-03 13:33:51 +0000181 /* Note: we let the caller skip the first and/or last position.
182 i.e. pos[0] = 0.3, pos[1] = 0.7
183 In these cases, we insert dummy entries to ensure that the final data
184 will be bracketed by [0, 1].
185 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
186
187 Thus colorCount (the caller's value, and fColorCount (our value) may
188 differ by up to 2. In the above example:
189 colorCount = 2
190 fColorCount = 4
191 */
192 fColorCount = colorCount;
193 // check if we need to add in dummy start and/or end position/colors
194 bool dummyFirst = false;
195 bool dummyLast = false;
196 if (pos) {
197 dummyFirst = pos[0] != 0;
198 dummyLast = pos[colorCount - 1] != SK_Scalar1;
199 fColorCount += dummyFirst + dummyLast;
200 }
201
202 if (fColorCount > kColorStorageCount) {
203 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
204 fOrigColors = reinterpret_cast<SkColor*>(
205 sk_malloc_throw(size * fColorCount));
206 }
207 else {
208 fOrigColors = fStorage;
209 }
210
211 // Now copy over the colors, adding the dummies as needed
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212 {
reed@android.com41bccf52009-04-03 13:33:51 +0000213 SkColor* origColors = fOrigColors;
214 if (dummyFirst) {
215 *origColors++ = colors[0];
216 }
217 memcpy(origColors, colors, colorCount * sizeof(SkColor));
218 if (dummyLast) {
219 origColors += colorCount;
220 *origColors = colors[colorCount - 1];
221 }
222 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223
reed@android.com41bccf52009-04-03 13:33:51 +0000224 // our premul colors point to the 2nd half of the array
225 // these are assigned each time in setContext
226 fARGB32 = fOrigColors + fColorCount;
227 fRecs = (Rec*)(fARGB32 + fColorCount);
228 if (fColorCount > 2) {
229 Rec* recs = fRecs;
230 recs->fPos = 0;
231 // recs->fScale = 0; // unused;
232 recs += 1;
233 if (pos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234 /* We need to convert the user's array of relative positions into
235 fixed-point positions and scale factors. We need these results
236 to be strictly monotonic (no two values equal or out of order).
237 Hence this complex loop that just jams a zero for the scale
238 value if it sees a segment out of order, and it assures that
239 we start at 0 and end at 1.0
240 */
241 SkFixed prev = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000242 int startIndex = dummyFirst ? 0 : 1;
243 int count = colorCount + dummyLast;
244 for (int i = startIndex; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245 // force the last value to be 1.0
246 SkFixed curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000247 if (i == colorCount) { // we're really at the dummyLast
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 curr = SK_Fixed1;
reed@android.com41bccf52009-04-03 13:33:51 +0000249 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250 curr = SkScalarToFixed(pos[i]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 }
reed@android.com41bccf52009-04-03 13:33:51 +0000252 // pin curr withing range
253 if (curr < 0) {
254 curr = 0;
255 } else if (curr > SK_Fixed1) {
256 curr = SK_Fixed1;
257 }
258 recs->fPos = curr;
259 if (curr > prev) {
260 recs->fScale = (1 << 24) / (curr - prev);
261 } else {
262 recs->fScale = 0; // ignore this segment
263 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 // get ready for the next value
265 prev = curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000266 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 }
reed@android.com41bccf52009-04-03 13:33:51 +0000268 } else { // assume even distribution
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 SkFixed dp = SK_Fixed1 / (colorCount - 1);
270 SkFixed p = dp;
271 SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp
reed@android.com41bccf52009-04-03 13:33:51 +0000272 for (int i = 1; i < colorCount; i++) {
273 recs->fPos = p;
274 recs->fScale = scale;
275 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276 p += dp;
277 }
278 }
279 }
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000280 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281}
282
283Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
reed@android.com41bccf52009-04-03 13:33:51 +0000284 INHERITED(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 fCacheAlpha = 256;
286
287 fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
288
289 fCache16 = fCache16Storage = NULL;
290 fCache32 = fCache32Storage = NULL;
291
reed@android.com41bccf52009-04-03 13:33:51 +0000292 int colorCount = fColorCount = buffer.readU32();
293 if (colorCount > kColorStorageCount) {
294 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
295 fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
296 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 fOrigColors = fStorage;
reed@android.com41bccf52009-04-03 13:33:51 +0000298 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 buffer.read(fOrigColors, colorCount * sizeof(SkColor));
300 fARGB32 = fOrigColors + colorCount;
301
302 fTileMode = (TileMode)buffer.readU8();
303 fTileProc = gTileProcs[fTileMode];
304 fRecs = (Rec*)(fARGB32 + colorCount);
305 if (colorCount > 2) {
306 Rec* recs = fRecs;
307 recs[0].fPos = 0;
308 for (int i = 1; i < colorCount; i++) {
309 recs[i].fPos = buffer.readS32();
310 recs[i].fScale = buffer.readU32();
311 }
312 }
313 buffer.read(&fPtsToUnit, sizeof(SkMatrix));
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000314 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315}
316
reed@android.com41bccf52009-04-03 13:33:51 +0000317Gradient_Shader::~Gradient_Shader() {
318 if (fCache16Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 sk_free(fCache16Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000320 }
321 if (fCache32Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 sk_free(fCache32Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000323 }
324 if (fOrigColors != fStorage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 sk_free(fOrigColors);
reed@android.com41bccf52009-04-03 13:33:51 +0000326 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000327 fMapper->safeUnref();
328}
329
reed@android.com41bccf52009-04-03 13:33:51 +0000330void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331 this->INHERITED::flatten(buffer);
332 buffer.writeFlattenable(fMapper);
reed@android.com41bccf52009-04-03 13:33:51 +0000333 buffer.write32(fColorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000334 buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
335 buffer.write8(fTileMode);
336 if (fColorCount > 2) {
337 Rec* recs = fRecs;
338 for (int i = 1; i < fColorCount; i++) {
339 buffer.write32(recs[i].fPos);
340 buffer.write32(recs[i].fScale);
341 }
342 }
343 buffer.writeMul4(&fPtsToUnit, sizeof(SkMatrix));
344}
345
346bool Gradient_Shader::setContext(const SkBitmap& device,
347 const SkPaint& paint,
reed@android.com41bccf52009-04-03 13:33:51 +0000348 const SkMatrix& matrix) {
349 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350 return false;
reed@android.com41bccf52009-04-03 13:33:51 +0000351 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000352
353 const SkMatrix& inverse = this->getTotalInverse();
354
355 if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
356 return false;
357 }
358
359 fDstToIndexProc = fDstToIndex.getMapXYProc();
360 fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
361
362 // now convert our colors in to PMColors
363 unsigned paintAlpha = this->getPaintAlpha();
364 unsigned colorAlpha = 0xFF;
365
reed@android.com41bccf52009-04-03 13:33:51 +0000366 for (int i = 0; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367 SkColor src = fOrigColors[i];
368 unsigned sa = SkColorGetA(src);
369 colorAlpha &= sa;
370
371 // now modulate it by the paint for our resulting ARGB32 array
372 sa = SkMulDiv255Round(sa, paintAlpha);
373 fARGB32[i] = SkPreMultiplyARGB(sa, SkColorGetR(src), SkColorGetG(src),
374 SkColorGetB(src));
375 }
376
377 fFlags = this->INHERITED::getFlags();
378 if ((colorAlpha & paintAlpha) == 0xFF) {
379 fFlags |= kOpaqueAlpha_Flag;
380 }
381 // we can do span16 as long as our individual colors are opaque,
382 // regardless of the paint's alpha
383 if (0xFF == colorAlpha) {
384 fFlags |= kHasSpan16_Flag;
385 }
386
387 // if the new alpha differs from the previous time we were called, inval our cache
388 // this will trigger the cache to be rebuilt.
389 // we don't care about the first time, since the cache ptrs will already be NULL
390 if (fCacheAlpha != paintAlpha) {
391 fCache16 = NULL; // inval the cache
392 fCache32 = NULL; // inval the cache
393 fCacheAlpha = paintAlpha; // record the new alpha
reed@android.com9b46e772009-06-05 12:24:41 +0000394 // inform our subclasses
395 this->onCacheReset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000396 }
397 return true;
398}
399
reed@android.com41bccf52009-04-03 13:33:51 +0000400static inline int blend8(int a, int b, int scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401 SkASSERT(a == SkToU8(a));
402 SkASSERT(b == SkToU8(b));
403 SkASSERT(scale >= 0 && scale <= 256);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404 return a + ((b - a) * scale >> 8);
405}
406
reed@android.com41bccf52009-04-03 13:33:51 +0000407static inline uint32_t dot8_blend_packed32(uint32_t s0, uint32_t s1,
408 int blend) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409#if 0
410 int a = blend8(SkGetPackedA32(s0), SkGetPackedA32(s1), blend);
411 int r = blend8(SkGetPackedR32(s0), SkGetPackedR32(s1), blend);
412 int g = blend8(SkGetPackedG32(s0), SkGetPackedG32(s1), blend);
413 int b = blend8(SkGetPackedB32(s0), SkGetPackedB32(s1), blend);
414
415 return SkPackARGB32(a, r, g, b);
416#else
417 int otherBlend = 256 - blend;
418
419#if 0
420 U32 t0 = (((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF;
421 U32 t1 = (((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00;
422 SkASSERT((t0 & t1) == 0);
423 return t0 | t1;
424#else
425 return ((((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF) |
426 ((((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00);
427#endif
428
429#endif
430}
431
432#define Fixed_To_Dot8(x) (((x) + 0x80) >> 8)
433
reed@android.com41bccf52009-04-03 13:33:51 +0000434/** We take the original colors, not our premultiplied PMColors, since we can
435 build a 16bit table as long as the original colors are opaque, even if the
436 paint specifies a non-opaque alpha.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437*/
reed@android.com41bccf52009-04-03 13:33:51 +0000438static void build_16bit_cache(uint16_t cache[], SkColor c0, SkColor c1,
439 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000440 SkASSERT(count > 1);
441 SkASSERT(SkColorGetA(c0) == 0xFF);
442 SkASSERT(SkColorGetA(c1) == 0xFF);
443
444 SkFixed r = SkColorGetR(c0);
445 SkFixed g = SkColorGetG(c0);
446 SkFixed b = SkColorGetB(c0);
447
448 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
449 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
450 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
451
452 r = SkIntToFixed(r) + 0x8000;
453 g = SkIntToFixed(g) + 0x8000;
454 b = SkIntToFixed(b) + 0x8000;
455
456 do {
457 unsigned rr = r >> 16;
458 unsigned gg = g >> 16;
459 unsigned bb = b >> 16;
460 cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
461 cache[64] = SkDitherPack888ToRGB16(rr, gg, bb);
462 cache += 1;
463 r += dr;
464 g += dg;
465 b += db;
466 } while (--count != 0);
467}
468
reed@android.com41bccf52009-04-03 13:33:51 +0000469static void build_32bit_cache(SkPMColor cache[], SkPMColor c0, SkPMColor c1,
470 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000471 SkASSERT(count > 1);
472
473 SkFixed a = SkGetPackedA32(c0);
474 SkFixed r = SkGetPackedR32(c0);
475 SkFixed g = SkGetPackedG32(c0);
476 SkFixed b = SkGetPackedB32(c0);
477
478 SkFixed da = SkIntToFixed(SkGetPackedA32(c1) - a) / (count - 1);
479 SkFixed dr = SkIntToFixed(SkGetPackedR32(c1) - r) / (count - 1);
480 SkFixed dg = SkIntToFixed(SkGetPackedG32(c1) - g) / (count - 1);
481 SkFixed db = SkIntToFixed(SkGetPackedB32(c1) - b) / (count - 1);
482
483 a = SkIntToFixed(a) + 0x8000;
484 r = SkIntToFixed(r) + 0x8000;
485 g = SkIntToFixed(g) + 0x8000;
486 b = SkIntToFixed(b) + 0x8000;
487
488 do {
489 *cache++ = SkPackARGB32(a >> 16, r >> 16, g >> 16, b >> 16);
490 a += da;
491 r += dr;
492 g += dg;
493 b += db;
494 } while (--count != 0);
495}
496
reed@android.com41bccf52009-04-03 13:33:51 +0000497static inline int SkFixedToFFFF(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498 SkASSERT((unsigned)x <= SK_Fixed1);
499 return x - (x >> 16);
500}
501
reed@android.com41bccf52009-04-03 13:33:51 +0000502static inline U16CPU dot6to16(unsigned x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503 SkASSERT(x < 64);
504 return (x << 10) | (x << 4) | (x >> 2);
505}
506
reed@android.com41bccf52009-04-03 13:33:51 +0000507const uint16_t* Gradient_Shader::getCache16() {
508 if (fCache16 == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509 if (fCache16Storage == NULL) // set the storage and our working ptr
510#ifdef TEST_GRADIENT_DITHER
511 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
512#else
513 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count);
514#endif
515 fCache16 = fCache16Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000516 if (fColorCount == 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 build_16bit_cache(fCache16, fOrigColors[0], fOrigColors[1], kCache16Count);
reed@android.com41bccf52009-04-03 13:33:51 +0000518 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519 Rec* rec = fRecs;
520 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000521 for (int i = 1; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache16Bits);
523 SkASSERT(nextIndex < kCache16Count);
524
525 if (nextIndex > prevIndex)
526 build_16bit_cache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
527 prevIndex = nextIndex;
528 }
529 SkASSERT(prevIndex == kCache16Count - 1);
530 }
531
reed@android.com41bccf52009-04-03 13:33:51 +0000532 if (fMapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533#ifdef TEST_GRADIENT_DITHER
534 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
535#else
536 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count);
537#endif
538 uint16_t* linear = fCache16; // just computed linear data
539 uint16_t* mapped = fCache16Storage; // storage for mapped data
540 SkUnitMapper* map = fMapper;
reed@android.com41bccf52009-04-03 13:33:51 +0000541 for (int i = 0; i < 64; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542 int index = map->mapUnit16(dot6to16(i)) >> 10;
543 mapped[i] = linear[index];
544#ifdef TEST_GRADIENT_DITHER
545 mapped[i + 64] = linear[index + 64];
546#endif
547 }
548 sk_free(fCache16);
549 fCache16 = fCache16Storage;
550 }
551 }
552 return fCache16;
553}
554
reed@android.com41bccf52009-04-03 13:33:51 +0000555const SkPMColor* Gradient_Shader::getCache32() {
556 if (fCache32 == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557 if (fCache32Storage == NULL) // set the storage and our working ptr
558 fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count);
559
560 fCache32 = fCache32Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000561 if (fColorCount == 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000562 build_32bit_cache(fCache32, fARGB32[0], fARGB32[1], kCache32Count);
reed@android.com41bccf52009-04-03 13:33:51 +0000563 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564 Rec* rec = fRecs;
565 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000566 for (int i = 1; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000567 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache32Bits);
568 SkASSERT(nextIndex < kCache32Count);
569
570 if (nextIndex > prevIndex)
571 build_32bit_cache(fCache32 + prevIndex, fARGB32[i-1], fARGB32[i], nextIndex - prevIndex + 1);
572 prevIndex = nextIndex;
573 }
574 SkASSERT(prevIndex == kCache32Count - 1);
575 }
576
reed@android.com41bccf52009-04-03 13:33:51 +0000577 if (fMapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000578 fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count);
579 SkPMColor* linear = fCache32; // just computed linear data
580 SkPMColor* mapped = fCache32Storage; // storage for mapped data
581 SkUnitMapper* map = fMapper;
reed@android.com41bccf52009-04-03 13:33:51 +0000582 for (int i = 0; i < 256; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000583 mapped[i] = linear[map->mapUnit16((i << 8) | i) >> 8];
reed@android.com41bccf52009-04-03 13:33:51 +0000584 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000585 sk_free(fCache32);
586 fCache32 = fCache32Storage;
587 }
588 }
589 return fCache32;
590}
591
592///////////////////////////////////////////////////////////////////////////
593
reed@android.com41bccf52009-04-03 13:33:51 +0000594static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000595 SkVector vec = pts[1] - pts[0];
596 SkScalar mag = vec.length();
597 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
598
599 vec.scale(inv);
600 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
601 matrix->postTranslate(-pts[0].fX, -pts[0].fY);
602 matrix->postScale(inv, inv);
603}
604
605///////////////////////////////////////////////////////////////////////////////
606
607class Linear_Gradient : public Gradient_Shader {
608public:
609 Linear_Gradient(const SkPoint pts[2],
610 const SkColor colors[], const SkScalar pos[], int colorCount,
611 SkShader::TileMode mode, SkUnitMapper* mapper)
612 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
613 {
reed@android.com9b46e772009-06-05 12:24:41 +0000614 fCachedBitmap = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615 pts_to_unit_matrix(pts, &fPtsToUnit);
616 }
reed@android.com9b46e772009-06-05 12:24:41 +0000617 virtual ~Linear_Gradient() {
618 if (fCachedBitmap) {
619 SkDELETE(fCachedBitmap);
620 }
621 }
622
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
624 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
625 virtual bool asABitmap(SkBitmap*, SkMatrix*, TileMode*);
reed@android.com9b46e772009-06-05 12:24:41 +0000626 virtual void onCacheReset() {
627 if (fCachedBitmap) {
628 SkDELETE(fCachedBitmap);
629 fCachedBitmap = NULL;
630 }
631 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000632
633 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
634 return SkNEW_ARGS(Linear_Gradient, (buffer));
635 }
636
637protected:
reed@android.comec3d6e52009-06-05 14:43:55 +0000638 Linear_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {
639 fCachedBitmap = NULL;
640 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 virtual Factory getFactory() { return CreateProc; }
642
643private:
reed@android.com9b46e772009-06-05 12:24:41 +0000644 SkBitmap* fCachedBitmap; // allocated on demand
645
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 typedef Gradient_Shader INHERITED;
647};
648
649// Return true if fx, fx+dx, fx+2*dx, ... is always in range
reed@android.comfc25abd2009-01-15 14:38:33 +0000650static inline bool no_need_for_clamp(int fx, int dx, int count)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651{
652 SkASSERT(count > 0);
653 return (unsigned)((fx | (fx + (count - 1) * dx)) >> 8) <= 0xFF;
654}
655
656void Linear_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
657{
658 SkASSERT(count > 0);
659
660 SkPoint srcPt;
661 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
662 TileProc proc = fTileProc;
663 const SkPMColor* cache = this->getCache32();
664
reed@android.comc552a432009-06-12 20:02:50 +0000665 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
667 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
reed@android.comc552a432009-06-12 20:02:50 +0000668 // preround fx by half the amount we throw away
669 fx += 1 << 7;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000670
reed@android.comc552a432009-06-12 20:02:50 +0000671 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000672 SkFixed dxStorage[1];
673 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
674 dx = dxStorage[0];
reed@android.comc552a432009-06-12 20:02:50 +0000675 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000676 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
677 dx = SkScalarToFixed(fDstToIndex.getScaleX());
678 }
679
reed@android.comc552a432009-06-12 20:02:50 +0000680 if (SkFixedNearlyZero(dx)) {
681 // we're a vertical gradient, so no change in a span
reed@android.com8a1c16f2008-12-17 15:59:43 +0000682 unsigned fi = proc(fx);
683 SkASSERT(fi <= 0xFFFF);
684 sk_memset32(dstC, cache[fi >> (16 - kCache32Bits)], count);
reed@android.comc552a432009-06-12 20:02:50 +0000685 } else if (proc == clamp_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000686#if 0
687 if (no_need_for_clamp(fx, dx, count))
688 {
689 unsigned fi;
690 while ((count -= 4) >= 0)
691 {
692 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
693 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
694 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
695 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
696 }
697 SkASSERT(count <= -1 && count >= -4);
698 count += 4;
699 while (--count >= 0)
700 {
701 fi = fx >> 8;
702 SkASSERT(fi <= 0xFF);
703 fx += dx;
704 *dstC++ = cache[fi];
705 }
706 }
707 else
708#endif
709 do {
710 unsigned fi = SkClampMax(fx >> 8, 0xFF);
711 SkASSERT(fi <= 0xFF);
712 fx += dx;
713 *dstC++ = cache[fi];
714 } while (--count != 0);
reed@android.comc552a432009-06-12 20:02:50 +0000715 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716 do {
717 unsigned fi = mirror_8bits(fx >> 8);
718 SkASSERT(fi <= 0xFF);
719 fx += dx;
720 *dstC++ = cache[fi];
721 } while (--count != 0);
reed@android.comc552a432009-06-12 20:02:50 +0000722 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000723 SkASSERT(proc == repeat_tileproc);
724 do {
725 unsigned fi = repeat_8bits(fx >> 8);
726 SkASSERT(fi <= 0xFF);
727 fx += dx;
728 *dstC++ = cache[fi];
729 } while (--count != 0);
730 }
reed@android.comc552a432009-06-12 20:02:50 +0000731 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000732 SkScalar dstX = SkIntToScalar(x);
733 SkScalar dstY = SkIntToScalar(y);
734 do {
735 dstProc(fDstToIndex, dstX, dstY, &srcPt);
736 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
737 SkASSERT(fi <= 0xFFFF);
738 *dstC++ = cache[fi >> (16 - kCache32Bits)];
739 dstX += SK_Scalar1;
740 } while (--count != 0);
741 }
742}
743
744bool Linear_Gradient::asABitmap(SkBitmap* bitmap, SkMatrix* matrix,
745 TileMode xy[]) {
reed@android.com9b46e772009-06-05 12:24:41 +0000746 // we cache our "bitmap", so it's generationID will be const on subsequent
747 // calls to asABitmap
748 if (NULL == fCachedBitmap) {
749 fCachedBitmap = SkNEW(SkBitmap);
750 fCachedBitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
751 fCachedBitmap->setPixels((void*)this->getCache32(), NULL);
752 }
753
reed@android.com8a1c16f2008-12-17 15:59:43 +0000754 if (bitmap) {
reed@android.com9b46e772009-06-05 12:24:41 +0000755 *bitmap = *fCachedBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000756 }
757 if (matrix) {
758 matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1);
759 matrix->preConcat(fPtsToUnit);
760 }
761 if (xy) {
762 xy[0] = fTileMode;
763 xy[1] = kClamp_TileMode;
764 }
765 return true;
766}
767
768#ifdef TEST_GRADIENT_DITHER
769static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, int count)
770{
reed@android.com0becfc52009-01-13 13:26:44 +0000771 if (reinterpret_cast<uintptr_t>(dst) & 2)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 {
773 *dst++ = value;
774 count -= 1;
775 SkTSwap(value, other);
776 }
777
778 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
779
780 if (count & 1)
781 dst[count - 1] = value;
782}
783#endif
784
785void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
786{
787 SkASSERT(count > 0);
788
789 SkPoint srcPt;
790 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
791 TileProc proc = fTileProc;
792 const uint16_t* cache = this->getCache16();
793#ifdef TEST_GRADIENT_DITHER
794 int toggle = ((x ^ y) & 1) << kCache16Bits;
795#endif
796
797 if (fDstToIndexClass != kPerspective_MatrixClass)
798 {
799 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
800 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
801
802 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
803 {
804 SkFixed dxStorage[1];
805 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
806 dx = dxStorage[0];
807 }
808 else
809 {
810 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
811 dx = SkScalarToFixed(fDstToIndex.getScaleX());
812 }
813
814 if (SkFixedNearlyZero(dx)) // we're a vertical gradient, so no change in a span
815 {
816 unsigned fi = proc(fx) >> 10;
817 SkASSERT(fi <= 63);
818#ifdef TEST_GRADIENT_DITHER
819 dither_memset16(dstC, cache[toggle + fi], cache[(toggle ^ (1 << kCache16Bits)) + fi], count);
820#else
821 sk_memset16(dstC, cache[fi], count);
822#endif
823 }
824 else if (proc == clamp_tileproc)
825 {
826 do {
827 unsigned fi = SkClampMax(fx >> 10, 63);
828 SkASSERT(fi <= 63);
829 fx += dx;
830#ifdef TEST_GRADIENT_DITHER
831 *dstC++ = cache[toggle + fi];
832 toggle ^= (1 << kCache16Bits);
833#else
834 *dstC++ = cache[fi];
835#endif
836 } while (--count != 0);
837 }
838 else if (proc == mirror_tileproc)
839 {
840 do {
841 unsigned fi = mirror_6bits(fx >> 10);
842 SkASSERT(fi <= 0x3F);
843 fx += dx;
844#ifdef TEST_GRADIENT_DITHER
845 *dstC++ = cache[toggle + fi];
846 toggle ^= (1 << kCache16Bits);
847#else
848 *dstC++ = cache[fi];
849#endif
850 } while (--count != 0);
851 }
852 else
853 {
854 SkASSERT(proc == repeat_tileproc);
855 do {
856 unsigned fi = repeat_6bits(fx >> 10);
857 SkASSERT(fi <= 0x3F);
858 fx += dx;
859#ifdef TEST_GRADIENT_DITHER
860 *dstC++ = cache[toggle + fi];
861 toggle ^= (1 << kCache16Bits);
862#else
863 *dstC++ = cache[fi];
864#endif
865 } while (--count != 0);
866 }
867 }
868 else
869 {
870 SkScalar dstX = SkIntToScalar(x);
871 SkScalar dstY = SkIntToScalar(y);
872 do {
873 dstProc(fDstToIndex, dstX, dstY, &srcPt);
874 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
875 SkASSERT(fi <= 0xFFFF);
876
877 int index = fi >> (16 - kCache16Bits);
878#ifdef TEST_GRADIENT_DITHER
879 *dstC++ = cache[toggle + index];
880 toggle ^= (1 << kCache16Bits);
881#else
882 *dstC++ = cache[index];
883#endif
884
885 dstX += SK_Scalar1;
886 } while (--count != 0);
887 }
888}
889
890///////////////////////////////////////////////////////////////////////////////
891
892#define kSQRT_TABLE_BITS 11
893#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
894
895#include "SkRadialGradient_Table.h"
896
897#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
898
899#include <stdio.h>
900
901void SkRadialGradient_BuildTable()
902{
903 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
904
905 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
906 SkASSERT(file);
907 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
908
909 for (int i = 0; i < kSQRT_TABLE_SIZE; i++)
910 {
911 if ((i & 15) == 0)
912 ::fprintf(file, "\t");
913
914 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
915
916 ::fprintf(file, "0x%02X", value);
917 if (i < kSQRT_TABLE_SIZE-1)
918 ::fprintf(file, ", ");
919 if ((i & 15) == 15)
920 ::fprintf(file, "\n");
921 }
922 ::fprintf(file, "};\n");
923 ::fclose(file);
924}
925
926#endif
927
928
929static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius, SkMatrix* matrix)
930{
931 SkScalar inv = SkScalarInvert(radius);
932
933 matrix->setTranslate(-center.fX, -center.fY);
934 matrix->postScale(inv, inv);
935}
936
937class Radial_Gradient : public Gradient_Shader {
938public:
939 Radial_Gradient(const SkPoint& center, SkScalar radius,
940 const SkColor colors[], const SkScalar pos[], int colorCount,
941 SkShader::TileMode mode, SkUnitMapper* mapper)
942 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
943 {
944 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
945 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
946
947 rad_to_unit_matrix(center, radius, &fPtsToUnit);
948 }
949 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
950 {
951 SkASSERT(count > 0);
952
953 SkPoint srcPt;
954 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
955 TileProc proc = fTileProc;
956 const SkPMColor* cache = this->getCache32();
957
958 if (fDstToIndexClass != kPerspective_MatrixClass)
959 {
960 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
961 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
962 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
963
964 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
965 {
966 SkFixed storage[2];
967 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
968 dx = storage[0];
969 dy = storage[1];
970 }
971 else
972 {
973 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
974 dx = SkScalarToFixed(fDstToIndex.getScaleX());
975 dy = SkScalarToFixed(fDstToIndex.getSkewY());
976 }
977
978 if (proc == clamp_tileproc)
979 {
980 const uint8_t* sqrt_table = gSqrt8Table;
981 fx >>= 1;
982 dx >>= 1;
983 fy >>= 1;
984 dy >>= 1;
985 do {
986 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
987 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
988 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
989 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
990 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache32Bits)];
991 fx += dx;
992 fy += dy;
993 } while (--count != 0);
994 }
995 else if (proc == mirror_tileproc)
996 {
997 do {
998 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
999 unsigned fi = mirror_tileproc(dist);
1000 SkASSERT(fi <= 0xFFFF);
1001 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1002 fx += dx;
1003 fy += dy;
1004 } while (--count != 0);
1005 }
1006 else
1007 {
1008 SkASSERT(proc == repeat_tileproc);
1009 do {
1010 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1011 unsigned fi = repeat_tileproc(dist);
1012 SkASSERT(fi <= 0xFFFF);
1013 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1014 fx += dx;
1015 fy += dy;
1016 } while (--count != 0);
1017 }
1018 }
1019 else // perspective case
1020 {
1021 SkScalar dstX = SkIntToScalar(x);
1022 SkScalar dstY = SkIntToScalar(y);
1023 do {
1024 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1025 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1026 SkASSERT(fi <= 0xFFFF);
1027 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1028 dstX += SK_Scalar1;
1029 } while (--count != 0);
1030 }
1031 }
1032 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count)
1033 {
1034 SkASSERT(count > 0);
1035
1036 SkPoint srcPt;
1037 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1038 TileProc proc = fTileProc;
1039 const uint16_t* cache = this->getCache16();
1040#ifdef TEST_GRADIENT_DITHER
1041 int toggle = ((x ^ y) & 1) << kCache16Bits;
1042#endif
1043
1044 if (fDstToIndexClass != kPerspective_MatrixClass)
1045 {
1046 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
1047 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1048 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1049
1050 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1051 {
1052 SkFixed storage[2];
1053 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1054 dx = storage[0];
1055 dy = storage[1];
1056 }
1057 else
1058 {
1059 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1060 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1061 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1062 }
1063
1064 if (proc == clamp_tileproc)
1065 {
1066 const uint8_t* sqrt_table = gSqrt8Table;
1067
1068 /* knock these down so we can pin against +- 0x7FFF, which is an immediate load,
1069 rather than 0xFFFF which is slower. This is a compromise, since it reduces our
1070 precision, but that appears to be visually OK. If we decide this is OK for
1071 all of our cases, we could (it seems) put this scale-down into fDstToIndex,
1072 to avoid having to do these extra shifts each time.
1073 */
1074 fx >>= 1;
1075 dx >>= 1;
1076 fy >>= 1;
1077 dy >>= 1;
1078 if (dy == 0) // might perform this check for the other modes, but the win will be a smaller % of the total
1079 {
1080 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1081 fy *= fy;
1082 do {
1083 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1084 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
1085 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1086 fx += dx;
1087#ifdef TEST_GRADIENT_DITHER
1088 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1089 toggle ^= (1 << kCache16Bits);
1090#else
1091 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache16Bits)];
1092#endif
1093 } while (--count != 0);
1094 }
1095 else
1096 {
1097 do {
1098 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1099 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1100 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1101 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1102 fx += dx;
1103 fy += dy;
1104#ifdef TEST_GRADIENT_DITHER
1105 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1106 toggle ^= (1 << kCache16Bits);
1107#else
1108 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache16Bits)];
1109#endif
1110 } while (--count != 0);
1111 }
1112 }
1113 else if (proc == mirror_tileproc)
1114 {
1115 do {
1116 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1117 unsigned fi = mirror_tileproc(dist);
1118 SkASSERT(fi <= 0xFFFF);
1119 fx += dx;
1120 fy += dy;
1121#ifdef TEST_GRADIENT_DITHER
1122 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1123 toggle ^= (1 << kCache16Bits);
1124#else
1125 *dstC++ = cache[fi >> (16 - kCache16Bits)];
1126#endif
1127 } while (--count != 0);
1128 }
1129 else
1130 {
1131 SkASSERT(proc == repeat_tileproc);
1132 do {
1133 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1134 unsigned fi = repeat_tileproc(dist);
1135 SkASSERT(fi <= 0xFFFF);
1136 fx += dx;
1137 fy += dy;
1138#ifdef TEST_GRADIENT_DITHER
1139 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1140 toggle ^= (1 << kCache16Bits);
1141#else
1142 *dstC++ = cache[fi >> (16 - kCache16Bits)];
1143#endif
1144 } while (--count != 0);
1145 }
1146 }
1147 else // perspective case
1148 {
1149 SkScalar dstX = SkIntToScalar(x);
1150 SkScalar dstY = SkIntToScalar(y);
1151 do {
1152 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1153 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1154 SkASSERT(fi <= 0xFFFF);
1155
1156 int index = fi >> (16 - kCache16Bits);
1157#ifdef TEST_GRADIENT_DITHER
1158 *dstC++ = cache[toggle + index];
1159 toggle ^= (1 << kCache16Bits);
1160#else
1161 *dstC++ = cache[index];
1162#endif
1163
1164 dstX += SK_Scalar1;
1165 } while (--count != 0);
1166 }
1167 }
1168
1169 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1170 return SkNEW_ARGS(Radial_Gradient, (buffer));
1171 }
1172
1173protected:
1174 Radial_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {};
1175 virtual Factory getFactory() { return CreateProc; }
reed@android.comec3d6e52009-06-05 14:43:55 +00001176 virtual void onCacheReset() {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001177
1178private:
1179 typedef Gradient_Shader INHERITED;
1180};
1181
1182///////////////////////////////////////////////////////////////////////////////
1183
1184class Sweep_Gradient : public Gradient_Shader {
1185public:
1186 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
1187 const SkScalar pos[], int count, SkUnitMapper* mapper)
1188 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper)
1189 {
1190 fPtsToUnit.setTranslate(-cx, -cy);
1191 }
1192 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
1193 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
1194
1195 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1196 return SkNEW_ARGS(Sweep_Gradient, (buffer));
1197 }
1198
1199protected:
1200 Sweep_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201 virtual Factory getFactory() { return CreateProc; }
reed@android.comec3d6e52009-06-05 14:43:55 +00001202 virtual void onCacheReset() {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001203
1204private:
1205 typedef Gradient_Shader INHERITED;
1206};
1207
1208#ifdef COMPUTE_SWEEP_TABLE
1209#define PI 3.14159265
1210static bool gSweepTableReady;
1211static uint8_t gSweepTable[65];
1212
1213/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
1214 We scale the results to [0..32]
1215*/
1216static const uint8_t* build_sweep_table()
1217{
1218 if (!gSweepTableReady)
1219 {
1220 const int N = 65;
1221 const double DENOM = N - 1;
1222
1223 for (int i = 0; i < N; i++)
1224 {
1225 double arg = i / DENOM;
1226 double v = atan(arg);
1227 int iv = (int)round(v * DENOM * 2 / PI);
1228// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
1229 printf("%d, ", iv);
1230 gSweepTable[i] = iv;
1231 }
1232 gSweepTableReady = true;
1233 }
1234 return gSweepTable;
1235}
1236#else
1237static const uint8_t gSweepTable[] = {
1238 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
1239 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
1240 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
1241 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
1242 32
1243};
1244static const uint8_t* build_sweep_table() { return gSweepTable; }
1245#endif
1246
1247// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
1248// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
1249// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
1250
1251//unsigned div_64(int numer, int denom);
1252static unsigned div_64(int numer, int denom)
1253{
1254 SkASSERT(numer <= denom);
1255 SkASSERT(numer > 0);
1256 SkASSERT(denom > 0);
1257
1258 int nbits = SkCLZ(numer);
1259 int dbits = SkCLZ(denom);
1260 int bits = 6 - nbits + dbits;
1261 SkASSERT(bits <= 6);
1262
1263 if (bits < 0) // detect underflow
1264 return 0;
1265
1266 denom <<= dbits - 1;
1267 numer <<= nbits - 1;
1268
1269 unsigned result = 0;
1270
1271 // do the first one
1272 if ((numer -= denom) >= 0)
1273 result = 1;
1274 else
1275 numer += denom;
1276
1277 // Now fall into our switch statement if there are more bits to compute
1278 if (bits > 0)
1279 {
1280 // make room for the rest of the answer bits
1281 result <<= bits;
1282 switch (bits) {
1283 case 6:
1284 if ((numer = (numer << 1) - denom) >= 0)
1285 result |= 32;
1286 else
1287 numer += denom;
1288 case 5:
1289 if ((numer = (numer << 1) - denom) >= 0)
1290 result |= 16;
1291 else
1292 numer += denom;
1293 case 4:
1294 if ((numer = (numer << 1) - denom) >= 0)
1295 result |= 8;
1296 else
1297 numer += denom;
1298 case 3:
1299 if ((numer = (numer << 1) - denom) >= 0)
1300 result |= 4;
1301 else
1302 numer += denom;
1303 case 2:
1304 if ((numer = (numer << 1) - denom) >= 0)
1305 result |= 2;
1306 else
1307 numer += denom;
1308 case 1:
1309 default: // not strictly need, but makes GCC make better ARM code
1310 if ((numer = (numer << 1) - denom) >= 0)
1311 result |= 1;
1312 else
1313 numer += denom;
1314 }
1315 }
1316 return result;
1317}
1318
1319// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
1320static unsigned atan_0_90(SkFixed y, SkFixed x)
1321{
1322#ifdef SK_DEBUG
1323 {
1324 static bool gOnce;
1325 if (!gOnce)
1326 {
1327 gOnce = true;
1328 SkASSERT(div_64(55, 55) == 64);
1329 SkASSERT(div_64(128, 256) == 32);
1330 SkASSERT(div_64(2326528, 4685824) == 31);
1331 SkASSERT(div_64(753664, 5210112) == 9);
1332 SkASSERT(div_64(229376, 4882432) == 3);
1333 SkASSERT(div_64(2, 64) == 2);
1334 SkASSERT(div_64(1, 64) == 1);
1335 // test that we handle underflow correctly
1336 SkASSERT(div_64(12345, 0x54321234) == 0);
1337 }
1338 }
1339#endif
1340
1341 SkASSERT(y > 0 && x > 0);
1342 const uint8_t* table = build_sweep_table();
1343
1344 unsigned result;
1345 bool swap = (x < y);
1346 if (swap)
1347 {
1348 // first part of the atan(v) = PI/2 - atan(1/v) identity
1349 // since our div_64 and table want v <= 1, where v = y/x
1350 SkTSwap<SkFixed>(x, y);
1351 }
1352
1353 result = div_64(y, x);
1354
1355#ifdef SK_DEBUG
1356 {
1357 unsigned result2 = SkDivBits(y, x, 6);
1358 SkASSERT(result2 == result ||
1359 (result == 1 && result2 == 0));
1360 }
1361#endif
1362
1363 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
1364 result = table[result];
1365
1366 if (swap)
1367 {
1368 // complete the atan(v) = PI/2 - atan(1/v) identity
1369 result = 64 - result;
1370 // pin to 63
1371 result -= result >> 6;
1372 }
1373
1374 SkASSERT(result <= 63);
1375 return result;
1376}
1377
1378// returns angle in a circle [0..2PI) -> [0..255]
1379static unsigned SkATan2_255(SkFixed y, SkFixed x)
1380{
1381 if (x == 0)
1382 {
1383 if (y == 0)
1384 return 0;
1385 return y < 0 ? 192 : 64;
1386 }
1387 if (y == 0)
1388 return x < 0 ? 128 : 0;
1389
1390 /* Find the right quadrant for x,y
1391 Since atan_0_90 only handles the first quadrant, we rotate x,y
1392 appropriately before calling it, and then add the right amount
1393 to account for the real quadrant.
1394 quadrant 0 : add 0 | x > 0 && y > 0
1395 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
1396 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
1397 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
1398
1399 map x<0 to (1 << 6)
1400 map y<0 to (3 << 6)
1401 add = map_x ^ map_y
1402 */
1403 int xsign = x >> 31;
1404 int ysign = y >> 31;
1405 int add = ((-xsign) ^ (ysign & 3)) << 6;
1406
1407#ifdef SK_DEBUG
1408 if (0 == add)
1409 SkASSERT(x > 0 && y > 0);
1410 else if (64 == add)
1411 SkASSERT(x < 0 && y > 0);
1412 else if (128 == add)
1413 SkASSERT(x < 0 && y < 0);
1414 else if (192 == add)
1415 SkASSERT(x > 0 && y < 0);
1416 else
1417 SkASSERT(!"bad value for add");
1418#endif
1419
1420 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
1421 where we need to rotate x,y by 90 or -90
1422 */
1423 x = (x ^ xsign) - xsign;
1424 y = (y ^ ysign) - ysign;
1425 if (add & 64) // quads 1 or 3 need to swap x,y
1426 SkTSwap<SkFixed>(x, y);
1427
1428 unsigned result = add + atan_0_90(y, x);
1429 SkASSERT(result < 256);
1430 return result;
1431}
1432
1433void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
1434{
1435 SkMatrix::MapXYProc proc = fDstToIndexProc;
1436 const SkMatrix& matrix = fDstToIndex;
1437 const SkPMColor* cache = this->getCache32();
1438 SkPoint srcPt;
1439
1440 if (fDstToIndexClass != kPerspective_MatrixClass)
1441 {
1442 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1443 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1444 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1445 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1446
1447 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1448 {
1449 SkFixed storage[2];
1450 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1451 &storage[0], &storage[1]);
1452 dx = storage[0];
1453 dy = storage[1];
1454 }
1455 else
1456 {
1457 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1458 dx = SkScalarToFixed(matrix.getScaleX());
1459 dy = SkScalarToFixed(matrix.getSkewY());
1460 }
1461
1462 for (; count > 0; --count)
1463 {
1464 *dstC++ = cache[SkATan2_255(fy, fx)];
1465 fx += dx;
1466 fy += dy;
1467 }
1468 }
1469 else // perspective case
1470 {
1471 for (int stop = x + count; x < stop; x++)
1472 {
1473 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1474 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1475
1476 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1477 SkScalarToFixed(srcPt.fX));
1478 *dstC++ = cache[index];
1479 }
1480 }
1481}
1482
1483void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
1484{
1485 SkMatrix::MapXYProc proc = fDstToIndexProc;
1486 const SkMatrix& matrix = fDstToIndex;
1487 const uint16_t* cache = this->getCache16();
1488 int toggle = ((x ^ y) & 1) << kCache16Bits;
1489 SkPoint srcPt;
1490
1491 if (fDstToIndexClass != kPerspective_MatrixClass)
1492 {
1493 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1494 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1495 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1496 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1497
1498 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1499 {
1500 SkFixed storage[2];
1501 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1502 &storage[0], &storage[1]);
1503 dx = storage[0];
1504 dy = storage[1];
1505 }
1506 else
1507 {
1508 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1509 dx = SkScalarToFixed(matrix.getScaleX());
1510 dy = SkScalarToFixed(matrix.getSkewY());
1511 }
1512
1513 for (; count > 0; --count)
1514 {
1515 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
1516 *dstC++ = cache[toggle + index];
1517 toggle ^= (1 << kCache16Bits);
1518 fx += dx;
1519 fy += dy;
1520 }
1521 }
1522 else // perspective case
1523 {
1524 for (int stop = x + count; x < stop; x++)
1525 {
1526 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1527 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1528
1529 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1530 SkScalarToFixed(srcPt.fX));
1531 index >>= (8 - kCache16Bits);
1532 *dstC++ = cache[toggle + index];
1533 toggle ^= (1 << kCache16Bits);
1534 }
1535 }
1536}
1537
1538///////////////////////////////////////////////////////////////////////////
1539///////////////////////////////////////////////////////////////////////////
1540
1541// assumes colors is SkColor* and pos is SkScalar*
1542#define EXPAND_1_COLOR(count) \
1543 SkColor tmp[2]; \
1544 do { \
1545 if (1 == count) { \
1546 tmp[0] = tmp[1] = colors[0]; \
1547 colors = tmp; \
1548 pos = NULL; \
1549 count = 2; \
1550 } \
1551 } while (0)
1552
1553SkShader* SkGradientShader::CreateLinear( const SkPoint pts[2],
1554 const SkColor colors[], const SkScalar pos[], int colorCount,
1555 SkShader::TileMode mode, SkUnitMapper* mapper)
1556{
1557 if (NULL == pts || NULL == colors || colorCount < 1) {
1558 return NULL;
1559 }
1560 EXPAND_1_COLOR(colorCount);
1561
reed@android.com41bccf52009-04-03 13:33:51 +00001562 SkScalar posStorage[2];
1563 if (colorCount == 2 && pos == NULL) {
1564 posStorage[0] = SK_Scalar1/4;
1565 posStorage[1] = 3*SK_Scalar1/4;
1566 pos = posStorage;
1567 }
1568
reed@android.com8a1c16f2008-12-17 15:59:43 +00001569 return SkNEW_ARGS(Linear_Gradient, (pts, colors, pos, colorCount, mode, mapper));
1570}
1571
1572SkShader* SkGradientShader::CreateRadial( const SkPoint& center, SkScalar radius,
1573 const SkColor colors[], const SkScalar pos[], int colorCount,
1574 SkShader::TileMode mode, SkUnitMapper* mapper)
1575{
1576 if (radius <= 0 || NULL == colors || colorCount < 1) {
1577 return NULL;
1578 }
1579 EXPAND_1_COLOR(colorCount);
1580
1581 return SkNEW_ARGS(Radial_Gradient, (center, radius, colors, pos, colorCount, mode, mapper));
1582}
1583
1584SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
1585 const SkColor colors[],
1586 const SkScalar pos[],
1587 int count, SkUnitMapper* mapper)
1588{
1589 if (NULL == colors || count < 1) {
1590 return NULL;
1591 }
1592 EXPAND_1_COLOR(count);
1593
1594 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
1595}
1596
1597static SkFlattenable::Registrar gLinearGradientReg("Linear_Gradient",
1598 Linear_Gradient::CreateProc);
1599
1600static SkFlattenable::Registrar gRadialGradientReg("Radial_Gradient",
1601 Radial_Gradient::CreateProc);
1602
1603static SkFlattenable::Registrar gSweepGradientReg("Sweep_Gradient",
1604 Sweep_Gradient::CreateProc);
1605