blob: 860e902dd239373ee2f655e0fa1dd0c2108807dd [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
reed@android.com8a1c16f2008-12-17 15:59:43 +000023///////////////////////////////////////////////////////////////////////////
24
25typedef SkFixed (*TileProc)(SkFixed);
26
reed@android.com41bccf52009-04-03 13:33:51 +000027static SkFixed clamp_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000028 return SkClampMax(x, 0xFFFF);
29}
30
reed@android.com41bccf52009-04-03 13:33:51 +000031static SkFixed repeat_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000032 return x & 0xFFFF;
33}
34
reed@android.com41bccf52009-04-03 13:33:51 +000035static inline SkFixed mirror_tileproc(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000036 int s = x << 15 >> 31;
37 return (x ^ s) & 0xFFFF;
38}
39
40static const TileProc gTileProcs[] = {
41 clamp_tileproc,
42 repeat_tileproc,
43 mirror_tileproc
44};
45
46//////////////////////////////////////////////////////////////////////////////
47
reed@android.com41bccf52009-04-03 13:33:51 +000048static inline int repeat_6bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000049 return x & 63;
50}
51
reed@android.com41bccf52009-04-03 13:33:51 +000052static inline int mirror_6bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000053#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
54 if (x & 64)
55 x = ~x;
56 return x & 63;
57#else
58 int s = x << 25 >> 31;
59 return (x ^ s) & 63;
60#endif
61}
62
reed@android.com41bccf52009-04-03 13:33:51 +000063static inline int repeat_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000064 return x & 0xFF;
65}
66
reed@android.com41bccf52009-04-03 13:33:51 +000067static inline int mirror_8bits(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000068#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
reed@android.com41bccf52009-04-03 13:33:51 +000069 if (x & 256) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000070 x = ~x;
reed@android.com41bccf52009-04-03 13:33:51 +000071 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000072 return x & 255;
73#else
74 int s = x << 23 >> 31;
75 return (x ^ s) & 0xFF;
76#endif
77}
78
79//////////////////////////////////////////////////////////////////////////////
80
81class Gradient_Shader : public SkShader {
82public:
83 Gradient_Shader(const SkColor colors[], const SkScalar pos[],
reed@android.com41bccf52009-04-03 13:33:51 +000084 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
reed@android.com8a1c16f2008-12-17 15:59:43 +000085 virtual ~Gradient_Shader();
86
87 // overrides
88 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
89 virtual uint32_t getFlags() { return fFlags; }
90
91protected:
92 Gradient_Shader(SkFlattenableReadBuffer& );
93 SkUnitMapper* fMapper;
94 SkMatrix fPtsToUnit; // set by subclass
95 SkMatrix fDstToIndex;
96 SkMatrix::MapXYProc fDstToIndexProc;
reed@android.com8a1c16f2008-12-17 15:59:43 +000097 TileMode fTileMode;
98 TileProc fTileProc;
reed@android.com41bccf52009-04-03 13:33:51 +000099 int fColorCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100 uint8_t fDstToIndexClass;
101 uint8_t fFlags;
102
103 struct Rec {
104 SkFixed fPos; // 0...1
105 uint32_t fScale; // (1 << 24) / range
106 };
107 Rec* fRecs;
108
109 enum {
110 kCache16Bits = 6, // seems like enough for visual accuracy
111 kCache16Count = 1 << kCache16Bits,
112 kCache32Bits = 8, // pretty much should always be 8
113 kCache32Count = 1 << kCache32Bits
114 };
115 virtual void flatten(SkFlattenableWriteBuffer& );
116 const uint16_t* getCache16();
117 const SkPMColor* getCache32();
118
reed@android.com9b46e772009-06-05 12:24:41 +0000119 // called when we kill our cached colors (to be rebuilt later on demand)
reed@android.comec3d6e52009-06-05 14:43:55 +0000120 virtual void onCacheReset() = 0;
reed@android.com9b46e772009-06-05 12:24:41 +0000121
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122private:
123 enum {
124 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
125
reed@android.com1c12abe2009-07-02 15:01:02 +0000126 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127 };
128 SkColor fStorage[(kStorageSize + 3) >> 2];
129 SkColor* fOrigColors;
130
131 uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
132 SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
133
134 uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
135 SkPMColor* fCache32Storage; // storage for fCache32, allocated on demand
136 unsigned fCacheAlpha; // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value
137
138 typedef SkShader INHERITED;
139};
140
reed@android.com41bccf52009-04-03 13:33:51 +0000141static inline unsigned scalarToU16(SkScalar x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142 SkASSERT(x >= 0 && x <= SK_Scalar1);
143
144#ifdef SK_SCALAR_IS_FLOAT
145 return (unsigned)(x * 0xFFFF);
146#else
147 return x - (x >> 16); // probably should be x - (x > 0x7FFF) but that is slower
148#endif
149}
150
reed@android.com41bccf52009-04-03 13:33:51 +0000151Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[],
152 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153 SkASSERT(colorCount > 1);
154
155 fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return
156
157 fMapper = mapper;
158 mapper->safeRef();
159
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160 SkASSERT((unsigned)mode < SkShader::kTileModeCount);
161 SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
162 fTileMode = mode;
163 fTileProc = gTileProcs[mode];
reed@android.com41bccf52009-04-03 13:33:51 +0000164
165 fCache16 = fCache16Storage = NULL;
166 fCache32 = fCache32Storage = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167
reed@android.com41bccf52009-04-03 13:33:51 +0000168 /* Note: we let the caller skip the first and/or last position.
169 i.e. pos[0] = 0.3, pos[1] = 0.7
170 In these cases, we insert dummy entries to ensure that the final data
171 will be bracketed by [0, 1].
172 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
173
174 Thus colorCount (the caller's value, and fColorCount (our value) may
175 differ by up to 2. In the above example:
176 colorCount = 2
177 fColorCount = 4
178 */
179 fColorCount = colorCount;
180 // check if we need to add in dummy start and/or end position/colors
181 bool dummyFirst = false;
182 bool dummyLast = false;
183 if (pos) {
184 dummyFirst = pos[0] != 0;
185 dummyLast = pos[colorCount - 1] != SK_Scalar1;
186 fColorCount += dummyFirst + dummyLast;
187 }
188
189 if (fColorCount > kColorStorageCount) {
reed@android.com1c12abe2009-07-02 15:01:02 +0000190 size_t size = sizeof(SkColor) + sizeof(Rec);
reed@android.com41bccf52009-04-03 13:33:51 +0000191 fOrigColors = reinterpret_cast<SkColor*>(
192 sk_malloc_throw(size * fColorCount));
193 }
194 else {
195 fOrigColors = fStorage;
196 }
197
198 // Now copy over the colors, adding the dummies as needed
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 {
reed@android.com41bccf52009-04-03 13:33:51 +0000200 SkColor* origColors = fOrigColors;
201 if (dummyFirst) {
202 *origColors++ = colors[0];
203 }
204 memcpy(origColors, colors, colorCount * sizeof(SkColor));
205 if (dummyLast) {
206 origColors += colorCount;
207 *origColors = colors[colorCount - 1];
208 }
209 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210
reed@android.com1c12abe2009-07-02 15:01:02 +0000211 fRecs = (Rec*)(fOrigColors + fColorCount);
reed@android.com41bccf52009-04-03 13:33:51 +0000212 if (fColorCount > 2) {
213 Rec* recs = fRecs;
214 recs->fPos = 0;
215 // recs->fScale = 0; // unused;
216 recs += 1;
217 if (pos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 /* We need to convert the user's array of relative positions into
219 fixed-point positions and scale factors. We need these results
220 to be strictly monotonic (no two values equal or out of order).
221 Hence this complex loop that just jams a zero for the scale
222 value if it sees a segment out of order, and it assures that
223 we start at 0 and end at 1.0
224 */
225 SkFixed prev = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000226 int startIndex = dummyFirst ? 0 : 1;
227 int count = colorCount + dummyLast;
228 for (int i = startIndex; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229 // force the last value to be 1.0
230 SkFixed curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000231 if (i == colorCount) { // we're really at the dummyLast
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 curr = SK_Fixed1;
reed@android.com41bccf52009-04-03 13:33:51 +0000233 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234 curr = SkScalarToFixed(pos[i]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 }
reed@android.com41bccf52009-04-03 13:33:51 +0000236 // pin curr withing range
237 if (curr < 0) {
238 curr = 0;
239 } else if (curr > SK_Fixed1) {
240 curr = SK_Fixed1;
241 }
242 recs->fPos = curr;
243 if (curr > prev) {
244 recs->fScale = (1 << 24) / (curr - prev);
245 } else {
246 recs->fScale = 0; // ignore this segment
247 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 // get ready for the next value
249 prev = curr;
reed@android.com41bccf52009-04-03 13:33:51 +0000250 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 }
reed@android.com41bccf52009-04-03 13:33:51 +0000252 } else { // assume even distribution
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 SkFixed dp = SK_Fixed1 / (colorCount - 1);
254 SkFixed p = dp;
255 SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp
reed@android.com41bccf52009-04-03 13:33:51 +0000256 for (int i = 1; i < colorCount; i++) {
257 recs->fPos = p;
258 recs->fScale = scale;
259 recs += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 p += dp;
261 }
262 }
263 }
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000264 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265}
266
267Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
reed@android.com41bccf52009-04-03 13:33:51 +0000268 INHERITED(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 fCacheAlpha = 256;
270
271 fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
272
273 fCache16 = fCache16Storage = NULL;
274 fCache32 = fCache32Storage = NULL;
275
reed@android.com41bccf52009-04-03 13:33:51 +0000276 int colorCount = fColorCount = buffer.readU32();
277 if (colorCount > kColorStorageCount) {
278 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
279 fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
280 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 fOrigColors = fStorage;
reed@android.com41bccf52009-04-03 13:33:51 +0000282 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 buffer.read(fOrigColors, colorCount * sizeof(SkColor));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284
285 fTileMode = (TileMode)buffer.readU8();
286 fTileProc = gTileProcs[fTileMode];
reed@android.com1c12abe2009-07-02 15:01:02 +0000287 fRecs = (Rec*)(fOrigColors + colorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 if (colorCount > 2) {
289 Rec* recs = fRecs;
290 recs[0].fPos = 0;
291 for (int i = 1; i < colorCount; i++) {
292 recs[i].fPos = buffer.readS32();
293 recs[i].fScale = buffer.readU32();
294 }
295 }
296 buffer.read(&fPtsToUnit, sizeof(SkMatrix));
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000297 fFlags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298}
299
reed@android.com41bccf52009-04-03 13:33:51 +0000300Gradient_Shader::~Gradient_Shader() {
301 if (fCache16Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 sk_free(fCache16Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000303 }
304 if (fCache32Storage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305 sk_free(fCache32Storage);
reed@android.com41bccf52009-04-03 13:33:51 +0000306 }
307 if (fOrigColors != fStorage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308 sk_free(fOrigColors);
reed@android.com41bccf52009-04-03 13:33:51 +0000309 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 fMapper->safeUnref();
311}
312
reed@android.com41bccf52009-04-03 13:33:51 +0000313void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314 this->INHERITED::flatten(buffer);
315 buffer.writeFlattenable(fMapper);
reed@android.com41bccf52009-04-03 13:33:51 +0000316 buffer.write32(fColorCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
318 buffer.write8(fTileMode);
319 if (fColorCount > 2) {
320 Rec* recs = fRecs;
321 for (int i = 1; i < fColorCount; i++) {
322 buffer.write32(recs[i].fPos);
323 buffer.write32(recs[i].fScale);
324 }
325 }
326 buffer.writeMul4(&fPtsToUnit, sizeof(SkMatrix));
327}
328
329bool Gradient_Shader::setContext(const SkBitmap& device,
330 const SkPaint& paint,
reed@android.com41bccf52009-04-03 13:33:51 +0000331 const SkMatrix& matrix) {
332 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333 return false;
reed@android.com41bccf52009-04-03 13:33:51 +0000334 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335
336 const SkMatrix& inverse = this->getTotalInverse();
337
338 if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
339 return false;
340 }
341
342 fDstToIndexProc = fDstToIndex.getMapXYProc();
343 fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
344
345 // now convert our colors in to PMColors
346 unsigned paintAlpha = this->getPaintAlpha();
347 unsigned colorAlpha = 0xFF;
348
reed@android.com3d06a8c2009-07-07 18:19:59 +0000349 // FIXME: record colorAlpha in constructor, since this is not affected
350 // by setContext()
reed@android.com41bccf52009-04-03 13:33:51 +0000351 for (int i = 0; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000352 SkColor src = fOrigColors[i];
353 unsigned sa = SkColorGetA(src);
354 colorAlpha &= sa;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355 }
356
357 fFlags = this->INHERITED::getFlags();
358 if ((colorAlpha & paintAlpha) == 0xFF) {
359 fFlags |= kOpaqueAlpha_Flag;
360 }
361 // we can do span16 as long as our individual colors are opaque,
362 // regardless of the paint's alpha
363 if (0xFF == colorAlpha) {
364 fFlags |= kHasSpan16_Flag;
365 }
366
367 // if the new alpha differs from the previous time we were called, inval our cache
368 // this will trigger the cache to be rebuilt.
369 // we don't care about the first time, since the cache ptrs will already be NULL
370 if (fCacheAlpha != paintAlpha) {
371 fCache16 = NULL; // inval the cache
372 fCache32 = NULL; // inval the cache
373 fCacheAlpha = paintAlpha; // record the new alpha
reed@android.com9b46e772009-06-05 12:24:41 +0000374 // inform our subclasses
375 this->onCacheReset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376 }
377 return true;
378}
379
reed@android.com41bccf52009-04-03 13:33:51 +0000380static inline int blend8(int a, int b, int scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000381 SkASSERT(a == SkToU8(a));
382 SkASSERT(b == SkToU8(b));
383 SkASSERT(scale >= 0 && scale <= 256);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000384 return a + ((b - a) * scale >> 8);
385}
386
reed@android.com41bccf52009-04-03 13:33:51 +0000387static inline uint32_t dot8_blend_packed32(uint32_t s0, uint32_t s1,
388 int blend) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389#if 0
390 int a = blend8(SkGetPackedA32(s0), SkGetPackedA32(s1), blend);
391 int r = blend8(SkGetPackedR32(s0), SkGetPackedR32(s1), blend);
392 int g = blend8(SkGetPackedG32(s0), SkGetPackedG32(s1), blend);
393 int b = blend8(SkGetPackedB32(s0), SkGetPackedB32(s1), blend);
394
395 return SkPackARGB32(a, r, g, b);
396#else
397 int otherBlend = 256 - blend;
398
399#if 0
400 U32 t0 = (((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF;
401 U32 t1 = (((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00;
402 SkASSERT((t0 & t1) == 0);
403 return t0 | t1;
404#else
405 return ((((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF) |
406 ((((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00);
407#endif
408
409#endif
410}
411
412#define Fixed_To_Dot8(x) (((x) + 0x80) >> 8)
413
reed@android.com41bccf52009-04-03 13:33:51 +0000414/** We take the original colors, not our premultiplied PMColors, since we can
415 build a 16bit table as long as the original colors are opaque, even if the
416 paint specifies a non-opaque alpha.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000417*/
reed@android.com41bccf52009-04-03 13:33:51 +0000418static void build_16bit_cache(uint16_t cache[], SkColor c0, SkColor c1,
419 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420 SkASSERT(count > 1);
421 SkASSERT(SkColorGetA(c0) == 0xFF);
422 SkASSERT(SkColorGetA(c1) == 0xFF);
423
424 SkFixed r = SkColorGetR(c0);
425 SkFixed g = SkColorGetG(c0);
426 SkFixed b = SkColorGetB(c0);
427
428 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
429 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
430 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
431
432 r = SkIntToFixed(r) + 0x8000;
433 g = SkIntToFixed(g) + 0x8000;
434 b = SkIntToFixed(b) + 0x8000;
435
436 do {
437 unsigned rr = r >> 16;
438 unsigned gg = g >> 16;
439 unsigned bb = b >> 16;
440 cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
441 cache[64] = SkDitherPack888ToRGB16(rr, gg, bb);
442 cache += 1;
443 r += dr;
444 g += dg;
445 b += db;
446 } while (--count != 0);
447}
448
reed@android.com1c12abe2009-07-02 15:01:02 +0000449static void build_32bit_cache(SkPMColor cache[], SkColor c0, SkColor c1,
450 int count, U8CPU paintAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451 SkASSERT(count > 1);
452
reed@android.com1c12abe2009-07-02 15:01:02 +0000453 // need to apply paintAlpha to our two endpoints
454 SkFixed a = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
455 SkFixed da;
456 {
457 int tmp = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
458 da = SkIntToFixed(tmp - a) / (count - 1);
459 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460
reed@android.com1c12abe2009-07-02 15:01:02 +0000461 SkFixed r = SkColorGetR(c0);
462 SkFixed g = SkColorGetG(c0);
463 SkFixed b = SkColorGetB(c0);
464 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
465 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
466 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467
468 a = SkIntToFixed(a) + 0x8000;
469 r = SkIntToFixed(r) + 0x8000;
470 g = SkIntToFixed(g) + 0x8000;
471 b = SkIntToFixed(b) + 0x8000;
472
473 do {
reed@android.com1c12abe2009-07-02 15:01:02 +0000474 *cache++ = SkPreMultiplyARGB(a >> 16, r >> 16, g >> 16, b >> 16);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000475 a += da;
476 r += dr;
477 g += dg;
478 b += db;
479 } while (--count != 0);
480}
481
reed@android.com41bccf52009-04-03 13:33:51 +0000482static inline int SkFixedToFFFF(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000483 SkASSERT((unsigned)x <= SK_Fixed1);
484 return x - (x >> 16);
485}
486
reed@android.com41bccf52009-04-03 13:33:51 +0000487static inline U16CPU dot6to16(unsigned x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000488 SkASSERT(x < 64);
489 return (x << 10) | (x << 4) | (x >> 2);
490}
491
reed@android.com41bccf52009-04-03 13:33:51 +0000492const uint16_t* Gradient_Shader::getCache16() {
493 if (fCache16 == NULL) {
reed@android.com3c9b2a42009-08-27 19:28:37 +0000494 if (fCache16Storage == NULL) { // set the storage and our working ptr
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
reed@android.com3c9b2a42009-08-27 19:28:37 +0000496 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497 fCache16 = fCache16Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000498 if (fColorCount == 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499 build_16bit_cache(fCache16, fOrigColors[0], fOrigColors[1], kCache16Count);
reed@android.com41bccf52009-04-03 13:33:51 +0000500 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000501 Rec* rec = fRecs;
502 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000503 for (int i = 1; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache16Bits);
505 SkASSERT(nextIndex < kCache16Count);
506
507 if (nextIndex > prevIndex)
508 build_16bit_cache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
509 prevIndex = nextIndex;
510 }
511 SkASSERT(prevIndex == kCache16Count - 1);
512 }
513
reed@android.com41bccf52009-04-03 13:33:51 +0000514 if (fMapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516 uint16_t* linear = fCache16; // just computed linear data
517 uint16_t* mapped = fCache16Storage; // storage for mapped data
518 SkUnitMapper* map = fMapper;
reed@android.com41bccf52009-04-03 13:33:51 +0000519 for (int i = 0; i < 64; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520 int index = map->mapUnit16(dot6to16(i)) >> 10;
521 mapped[i] = linear[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 mapped[i + 64] = linear[index + 64];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523 }
524 sk_free(fCache16);
525 fCache16 = fCache16Storage;
526 }
527 }
528 return fCache16;
529}
530
reed@android.com41bccf52009-04-03 13:33:51 +0000531const SkPMColor* Gradient_Shader::getCache32() {
532 if (fCache32 == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533 if (fCache32Storage == NULL) // set the storage and our working ptr
534 fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count);
535
536 fCache32 = fCache32Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000537 if (fColorCount == 2) {
reed@android.com1c12abe2009-07-02 15:01:02 +0000538 build_32bit_cache(fCache32, fOrigColors[0], fOrigColors[1],
539 kCache32Count, fCacheAlpha);
reed@android.com41bccf52009-04-03 13:33:51 +0000540 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000541 Rec* rec = fRecs;
542 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000543 for (int i = 1; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000544 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache32Bits);
545 SkASSERT(nextIndex < kCache32Count);
546
547 if (nextIndex > prevIndex)
reed@android.com1c12abe2009-07-02 15:01:02 +0000548 build_32bit_cache(fCache32 + prevIndex, fOrigColors[i-1],
549 fOrigColors[i],
550 nextIndex - prevIndex + 1, fCacheAlpha);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551 prevIndex = nextIndex;
552 }
553 SkASSERT(prevIndex == kCache32Count - 1);
554 }
555
reed@android.com41bccf52009-04-03 13:33:51 +0000556 if (fMapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557 fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count);
558 SkPMColor* linear = fCache32; // just computed linear data
559 SkPMColor* mapped = fCache32Storage; // storage for mapped data
560 SkUnitMapper* map = fMapper;
reed@android.com41bccf52009-04-03 13:33:51 +0000561 for (int i = 0; i < 256; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000562 mapped[i] = linear[map->mapUnit16((i << 8) | i) >> 8];
reed@android.com41bccf52009-04-03 13:33:51 +0000563 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564 sk_free(fCache32);
565 fCache32 = fCache32Storage;
566 }
567 }
568 return fCache32;
569}
570
571///////////////////////////////////////////////////////////////////////////
572
reed@android.com41bccf52009-04-03 13:33:51 +0000573static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000574 SkVector vec = pts[1] - pts[0];
575 SkScalar mag = vec.length();
576 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
577
578 vec.scale(inv);
579 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
580 matrix->postTranslate(-pts[0].fX, -pts[0].fY);
581 matrix->postScale(inv, inv);
582}
583
584///////////////////////////////////////////////////////////////////////////////
585
586class Linear_Gradient : public Gradient_Shader {
587public:
588 Linear_Gradient(const SkPoint pts[2],
589 const SkColor colors[], const SkScalar pos[], int colorCount,
590 SkShader::TileMode mode, SkUnitMapper* mapper)
591 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
592 {
reed@android.com9b46e772009-06-05 12:24:41 +0000593 fCachedBitmap = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 pts_to_unit_matrix(pts, &fPtsToUnit);
595 }
reed@android.com9b46e772009-06-05 12:24:41 +0000596 virtual ~Linear_Gradient() {
597 if (fCachedBitmap) {
598 SkDELETE(fCachedBitmap);
599 }
600 }
601
reed@android.com5119bdb2009-06-12 21:27:03 +0000602 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
604 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
605 virtual bool asABitmap(SkBitmap*, SkMatrix*, TileMode*);
reed@android.com9b46e772009-06-05 12:24:41 +0000606 virtual void onCacheReset() {
607 if (fCachedBitmap) {
608 SkDELETE(fCachedBitmap);
609 fCachedBitmap = NULL;
610 }
611 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612
613 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
614 return SkNEW_ARGS(Linear_Gradient, (buffer));
615 }
616
617protected:
reed@android.comec3d6e52009-06-05 14:43:55 +0000618 Linear_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {
619 fCachedBitmap = NULL;
620 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 virtual Factory getFactory() { return CreateProc; }
622
623private:
reed@android.com9b46e772009-06-05 12:24:41 +0000624 SkBitmap* fCachedBitmap; // allocated on demand
625
reed@android.com8a1c16f2008-12-17 15:59:43 +0000626 typedef Gradient_Shader INHERITED;
627};
628
reed@android.com5119bdb2009-06-12 21:27:03 +0000629bool Linear_Gradient::setContext(const SkBitmap& device, const SkPaint& paint,
630 const SkMatrix& matrix) {
631 if (!this->INHERITED::setContext(device, paint, matrix)) {
632 return false;
633 }
634
635 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
636 if ((fDstToIndex.getType() & ~mask) == 0) {
reed@android.com3c9b2a42009-08-27 19:28:37 +0000637 fFlags |= SkShader::kConstInY32_Flag;
638 if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
639 // only claim this if we do have a 16bit mode (i.e. none of our
640 // colors have alpha), and if we are not dithering (which obviously
641 // is not const in Y).
642 fFlags |= SkShader::kConstInY16_Flag;
643 }
reed@android.com5119bdb2009-06-12 21:27:03 +0000644 }
645 return true;
646}
647
reed@android.com8a1c16f2008-12-17 15:59:43 +0000648// Return true if fx, fx+dx, fx+2*dx, ... is always in range
reed@android.comfc25abd2009-01-15 14:38:33 +0000649static inline bool no_need_for_clamp(int fx, int dx, int count)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000650{
651 SkASSERT(count > 0);
652 return (unsigned)((fx | (fx + (count - 1) * dx)) >> 8) <= 0xFF;
653}
654
655void Linear_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
656{
657 SkASSERT(count > 0);
658
659 SkPoint srcPt;
660 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
661 TileProc proc = fTileProc;
662 const SkPMColor* cache = this->getCache32();
663
reed@android.comc552a432009-06-12 20:02:50 +0000664 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000665 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
666 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
reed@android.comc552a432009-06-12 20:02:50 +0000667 // preround fx by half the amount we throw away
668 fx += 1 << 7;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000669
reed@android.comc552a432009-06-12 20:02:50 +0000670 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000671 SkFixed dxStorage[1];
672 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
673 dx = dxStorage[0];
reed@android.comc552a432009-06-12 20:02:50 +0000674 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000675 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
676 dx = SkScalarToFixed(fDstToIndex.getScaleX());
677 }
678
reed@android.comc552a432009-06-12 20:02:50 +0000679 if (SkFixedNearlyZero(dx)) {
680 // we're a vertical gradient, so no change in a span
reed@android.com8a1c16f2008-12-17 15:59:43 +0000681 unsigned fi = proc(fx);
682 SkASSERT(fi <= 0xFFFF);
683 sk_memset32(dstC, cache[fi >> (16 - kCache32Bits)], count);
reed@android.comc552a432009-06-12 20:02:50 +0000684 } else if (proc == clamp_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000685#if 0
686 if (no_need_for_clamp(fx, dx, count))
687 {
688 unsigned fi;
689 while ((count -= 4) >= 0)
690 {
691 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
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 }
696 SkASSERT(count <= -1 && count >= -4);
697 count += 4;
698 while (--count >= 0)
699 {
700 fi = fx >> 8;
701 SkASSERT(fi <= 0xFF);
702 fx += dx;
703 *dstC++ = cache[fi];
704 }
705 }
706 else
707#endif
708 do {
709 unsigned fi = SkClampMax(fx >> 8, 0xFF);
710 SkASSERT(fi <= 0xFF);
711 fx += dx;
712 *dstC++ = cache[fi];
713 } while (--count != 0);
reed@android.comc552a432009-06-12 20:02:50 +0000714 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715 do {
716 unsigned fi = mirror_8bits(fx >> 8);
717 SkASSERT(fi <= 0xFF);
718 fx += dx;
719 *dstC++ = cache[fi];
720 } while (--count != 0);
reed@android.comc552a432009-06-12 20:02:50 +0000721 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722 SkASSERT(proc == repeat_tileproc);
723 do {
724 unsigned fi = repeat_8bits(fx >> 8);
725 SkASSERT(fi <= 0xFF);
726 fx += dx;
727 *dstC++ = cache[fi];
728 } while (--count != 0);
729 }
reed@android.comc552a432009-06-12 20:02:50 +0000730 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000731 SkScalar dstX = SkIntToScalar(x);
732 SkScalar dstY = SkIntToScalar(y);
733 do {
734 dstProc(fDstToIndex, dstX, dstY, &srcPt);
735 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
736 SkASSERT(fi <= 0xFFFF);
737 *dstC++ = cache[fi >> (16 - kCache32Bits)];
738 dstX += SK_Scalar1;
739 } while (--count != 0);
740 }
741}
742
743bool Linear_Gradient::asABitmap(SkBitmap* bitmap, SkMatrix* matrix,
744 TileMode xy[]) {
reed@android.com9b46e772009-06-05 12:24:41 +0000745 // we cache our "bitmap", so it's generationID will be const on subsequent
746 // calls to asABitmap
747 if (NULL == fCachedBitmap) {
748 fCachedBitmap = SkNEW(SkBitmap);
749 fCachedBitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
750 fCachedBitmap->setPixels((void*)this->getCache32(), NULL);
751 }
752
reed@android.com8a1c16f2008-12-17 15:59:43 +0000753 if (bitmap) {
reed@android.com9b46e772009-06-05 12:24:41 +0000754 *bitmap = *fCachedBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000755 }
756 if (matrix) {
757 matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1);
758 matrix->preConcat(fPtsToUnit);
759 }
760 if (xy) {
761 xy[0] = fTileMode;
762 xy[1] = kClamp_TileMode;
763 }
764 return true;
765}
766
reed@android.com3c9b2a42009-08-27 19:28:37 +0000767static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
768 int count) {
769 if (reinterpret_cast<uintptr_t>(dst) & 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770 *dst++ = value;
771 count -= 1;
772 SkTSwap(value, other);
773 }
774
775 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
776
reed@android.com3c9b2a42009-08-27 19:28:37 +0000777 if (count & 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778 dst[count - 1] = value;
reed@android.com3c9b2a42009-08-27 19:28:37 +0000779 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781
782void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
783{
784 SkASSERT(count > 0);
785
786 SkPoint srcPt;
787 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
788 TileProc proc = fTileProc;
789 const uint16_t* cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790 int toggle = ((x ^ y) & 1) << kCache16Bits;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000791
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000792 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000793 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
794 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000795 // preround fx by half the amount we throw away
796 fx += 1 << 7;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000797
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000798 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 SkFixed dxStorage[1];
800 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
801 dx = dxStorage[0];
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000802 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
804 dx = SkScalarToFixed(fDstToIndex.getScaleX());
805 }
806
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000807 if (SkFixedNearlyZero(dx)) {
808 // we're a vertical gradient, so no change in a span
reed@android.com8a1c16f2008-12-17 15:59:43 +0000809 unsigned fi = proc(fx) >> 10;
810 SkASSERT(fi <= 63);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811 dither_memset16(dstC, cache[toggle + fi], cache[(toggle ^ (1 << kCache16Bits)) + fi], count);
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000812 } else if (proc == clamp_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813 do {
814 unsigned fi = SkClampMax(fx >> 10, 63);
815 SkASSERT(fi <= 63);
816 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817 *dstC++ = cache[toggle + fi];
818 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819 } while (--count != 0);
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000820 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000821 do {
822 unsigned fi = mirror_6bits(fx >> 10);
823 SkASSERT(fi <= 0x3F);
824 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 *dstC++ = cache[toggle + fi];
826 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827 } while (--count != 0);
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000828 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000829 SkASSERT(proc == repeat_tileproc);
830 do {
831 unsigned fi = repeat_6bits(fx >> 10);
832 SkASSERT(fi <= 0x3F);
833 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834 *dstC++ = cache[toggle + fi];
835 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000836 } while (--count != 0);
837 }
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000838 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839 SkScalar dstX = SkIntToScalar(x);
840 SkScalar dstY = SkIntToScalar(y);
841 do {
842 dstProc(fDstToIndex, dstX, dstY, &srcPt);
843 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
844 SkASSERT(fi <= 0xFFFF);
845
846 int index = fi >> (16 - kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000847 *dstC++ = cache[toggle + index];
848 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849
850 dstX += SK_Scalar1;
851 } while (--count != 0);
852 }
853}
854
855///////////////////////////////////////////////////////////////////////////////
856
857#define kSQRT_TABLE_BITS 11
858#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
859
860#include "SkRadialGradient_Table.h"
861
862#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
863
864#include <stdio.h>
865
866void SkRadialGradient_BuildTable()
867{
868 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
869
870 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
871 SkASSERT(file);
872 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
873
874 for (int i = 0; i < kSQRT_TABLE_SIZE; i++)
875 {
876 if ((i & 15) == 0)
877 ::fprintf(file, "\t");
878
879 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
880
881 ::fprintf(file, "0x%02X", value);
882 if (i < kSQRT_TABLE_SIZE-1)
883 ::fprintf(file, ", ");
884 if ((i & 15) == 15)
885 ::fprintf(file, "\n");
886 }
887 ::fprintf(file, "};\n");
888 ::fclose(file);
889}
890
891#endif
892
893
894static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius, SkMatrix* matrix)
895{
896 SkScalar inv = SkScalarInvert(radius);
897
898 matrix->setTranslate(-center.fX, -center.fY);
899 matrix->postScale(inv, inv);
900}
901
902class Radial_Gradient : public Gradient_Shader {
903public:
904 Radial_Gradient(const SkPoint& center, SkScalar radius,
905 const SkColor colors[], const SkScalar pos[], int colorCount,
906 SkShader::TileMode mode, SkUnitMapper* mapper)
907 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
908 {
909 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
910 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
911
912 rad_to_unit_matrix(center, radius, &fPtsToUnit);
913 }
914 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
915 {
916 SkASSERT(count > 0);
917
918 SkPoint srcPt;
919 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
920 TileProc proc = fTileProc;
921 const SkPMColor* cache = this->getCache32();
922
923 if (fDstToIndexClass != kPerspective_MatrixClass)
924 {
925 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
926 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
927 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
928
929 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
930 {
931 SkFixed storage[2];
932 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
933 dx = storage[0];
934 dy = storage[1];
935 }
936 else
937 {
938 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
939 dx = SkScalarToFixed(fDstToIndex.getScaleX());
940 dy = SkScalarToFixed(fDstToIndex.getSkewY());
941 }
942
943 if (proc == clamp_tileproc)
944 {
945 const uint8_t* sqrt_table = gSqrt8Table;
946 fx >>= 1;
947 dx >>= 1;
948 fy >>= 1;
949 dy >>= 1;
950 do {
951 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
952 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
953 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
954 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
955 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache32Bits)];
956 fx += dx;
957 fy += dy;
958 } while (--count != 0);
959 }
960 else if (proc == mirror_tileproc)
961 {
962 do {
963 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
964 unsigned fi = mirror_tileproc(dist);
965 SkASSERT(fi <= 0xFFFF);
966 *dstC++ = cache[fi >> (16 - kCache32Bits)];
967 fx += dx;
968 fy += dy;
969 } while (--count != 0);
970 }
971 else
972 {
973 SkASSERT(proc == repeat_tileproc);
974 do {
975 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
976 unsigned fi = repeat_tileproc(dist);
977 SkASSERT(fi <= 0xFFFF);
978 *dstC++ = cache[fi >> (16 - kCache32Bits)];
979 fx += dx;
980 fy += dy;
981 } while (--count != 0);
982 }
983 }
984 else // perspective case
985 {
986 SkScalar dstX = SkIntToScalar(x);
987 SkScalar dstY = SkIntToScalar(y);
988 do {
989 dstProc(fDstToIndex, dstX, dstY, &srcPt);
990 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
991 SkASSERT(fi <= 0xFFFF);
992 *dstC++ = cache[fi >> (16 - kCache32Bits)];
993 dstX += SK_Scalar1;
994 } while (--count != 0);
995 }
996 }
reed@android.com3c9b2a42009-08-27 19:28:37 +0000997
998 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000999 SkASSERT(count > 0);
1000
1001 SkPoint srcPt;
1002 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1003 TileProc proc = fTileProc;
1004 const uint16_t* cache = this->getCache16();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005 int toggle = ((x ^ y) & 1) << kCache16Bits;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006
reed@android.com3c9b2a42009-08-27 19:28:37 +00001007 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001008 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
1009 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1010 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1011
reed@android.com3c9b2a42009-08-27 19:28:37 +00001012 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001013 SkFixed storage[2];
1014 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1015 dx = storage[0];
1016 dy = storage[1];
reed@android.com3c9b2a42009-08-27 19:28:37 +00001017 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001018 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1019 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1020 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1021 }
1022
reed@android.com3c9b2a42009-08-27 19:28:37 +00001023 if (proc == clamp_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001024 const uint8_t* sqrt_table = gSqrt8Table;
1025
1026 /* knock these down so we can pin against +- 0x7FFF, which is an immediate load,
1027 rather than 0xFFFF which is slower. This is a compromise, since it reduces our
1028 precision, but that appears to be visually OK. If we decide this is OK for
1029 all of our cases, we could (it seems) put this scale-down into fDstToIndex,
1030 to avoid having to do these extra shifts each time.
1031 */
1032 fx >>= 1;
1033 dx >>= 1;
1034 fy >>= 1;
1035 dy >>= 1;
reed@android.com3c9b2a42009-08-27 19:28:37 +00001036 if (dy == 0) { // might perform this check for the other modes, but the win will be a smaller % of the total
reed@android.com8a1c16f2008-12-17 15:59:43 +00001037 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1038 fy *= fy;
1039 do {
1040 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1041 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
1042 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1043 fx += dx;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001044 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1045 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001046 } while (--count != 0);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001047 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048 do {
1049 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1050 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1051 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1052 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1053 fx += dx;
1054 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1056 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001057 } while (--count != 0);
1058 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001059 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060 do {
1061 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1062 unsigned fi = mirror_tileproc(dist);
1063 SkASSERT(fi <= 0xFFFF);
1064 fx += dx;
1065 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1067 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001068 } while (--count != 0);
reed@android.com3c9b2a42009-08-27 19:28:37 +00001069 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001070 SkASSERT(proc == repeat_tileproc);
1071 do {
1072 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1073 unsigned fi = repeat_tileproc(dist);
1074 SkASSERT(fi <= 0xFFFF);
1075 fx += dx;
1076 fy += dy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001077 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1078 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001079 } while (--count != 0);
1080 }
reed@android.com3c9b2a42009-08-27 19:28:37 +00001081 } else { // perspective case
reed@android.com8a1c16f2008-12-17 15:59:43 +00001082 SkScalar dstX = SkIntToScalar(x);
1083 SkScalar dstY = SkIntToScalar(y);
1084 do {
1085 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1086 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1087 SkASSERT(fi <= 0xFFFF);
1088
1089 int index = fi >> (16 - kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001090 *dstC++ = cache[toggle + index];
1091 toggle ^= (1 << kCache16Bits);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092
1093 dstX += SK_Scalar1;
1094 } while (--count != 0);
1095 }
1096 }
1097
1098 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1099 return SkNEW_ARGS(Radial_Gradient, (buffer));
1100 }
1101
1102protected:
1103 Radial_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {};
1104 virtual Factory getFactory() { return CreateProc; }
reed@android.comec3d6e52009-06-05 14:43:55 +00001105 virtual void onCacheReset() {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001106
1107private:
1108 typedef Gradient_Shader INHERITED;
1109};
1110
1111///////////////////////////////////////////////////////////////////////////////
1112
1113class Sweep_Gradient : public Gradient_Shader {
1114public:
1115 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
1116 const SkScalar pos[], int count, SkUnitMapper* mapper)
1117 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper)
1118 {
1119 fPtsToUnit.setTranslate(-cx, -cy);
1120 }
1121 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
1122 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
1123
1124 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1125 return SkNEW_ARGS(Sweep_Gradient, (buffer));
1126 }
1127
1128protected:
1129 Sweep_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001130 virtual Factory getFactory() { return CreateProc; }
reed@android.comec3d6e52009-06-05 14:43:55 +00001131 virtual void onCacheReset() {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132
1133private:
1134 typedef Gradient_Shader INHERITED;
1135};
1136
1137#ifdef COMPUTE_SWEEP_TABLE
1138#define PI 3.14159265
1139static bool gSweepTableReady;
1140static uint8_t gSweepTable[65];
1141
1142/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
1143 We scale the results to [0..32]
1144*/
1145static const uint8_t* build_sweep_table()
1146{
1147 if (!gSweepTableReady)
1148 {
1149 const int N = 65;
1150 const double DENOM = N - 1;
1151
1152 for (int i = 0; i < N; i++)
1153 {
1154 double arg = i / DENOM;
1155 double v = atan(arg);
1156 int iv = (int)round(v * DENOM * 2 / PI);
1157// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
1158 printf("%d, ", iv);
1159 gSweepTable[i] = iv;
1160 }
1161 gSweepTableReady = true;
1162 }
1163 return gSweepTable;
1164}
1165#else
1166static const uint8_t gSweepTable[] = {
1167 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
1168 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
1169 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
1170 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
1171 32
1172};
1173static const uint8_t* build_sweep_table() { return gSweepTable; }
1174#endif
1175
1176// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
1177// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
1178// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
1179
1180//unsigned div_64(int numer, int denom);
1181static unsigned div_64(int numer, int denom)
1182{
1183 SkASSERT(numer <= denom);
1184 SkASSERT(numer > 0);
1185 SkASSERT(denom > 0);
1186
1187 int nbits = SkCLZ(numer);
1188 int dbits = SkCLZ(denom);
1189 int bits = 6 - nbits + dbits;
1190 SkASSERT(bits <= 6);
1191
1192 if (bits < 0) // detect underflow
1193 return 0;
1194
1195 denom <<= dbits - 1;
1196 numer <<= nbits - 1;
1197
1198 unsigned result = 0;
1199
1200 // do the first one
1201 if ((numer -= denom) >= 0)
1202 result = 1;
1203 else
1204 numer += denom;
1205
1206 // Now fall into our switch statement if there are more bits to compute
1207 if (bits > 0)
1208 {
1209 // make room for the rest of the answer bits
1210 result <<= bits;
1211 switch (bits) {
1212 case 6:
1213 if ((numer = (numer << 1) - denom) >= 0)
1214 result |= 32;
1215 else
1216 numer += denom;
1217 case 5:
1218 if ((numer = (numer << 1) - denom) >= 0)
1219 result |= 16;
1220 else
1221 numer += denom;
1222 case 4:
1223 if ((numer = (numer << 1) - denom) >= 0)
1224 result |= 8;
1225 else
1226 numer += denom;
1227 case 3:
1228 if ((numer = (numer << 1) - denom) >= 0)
1229 result |= 4;
1230 else
1231 numer += denom;
1232 case 2:
1233 if ((numer = (numer << 1) - denom) >= 0)
1234 result |= 2;
1235 else
1236 numer += denom;
1237 case 1:
1238 default: // not strictly need, but makes GCC make better ARM code
1239 if ((numer = (numer << 1) - denom) >= 0)
1240 result |= 1;
1241 else
1242 numer += denom;
1243 }
1244 }
1245 return result;
1246}
1247
1248// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
1249static unsigned atan_0_90(SkFixed y, SkFixed x)
1250{
1251#ifdef SK_DEBUG
1252 {
1253 static bool gOnce;
1254 if (!gOnce)
1255 {
1256 gOnce = true;
1257 SkASSERT(div_64(55, 55) == 64);
1258 SkASSERT(div_64(128, 256) == 32);
1259 SkASSERT(div_64(2326528, 4685824) == 31);
1260 SkASSERT(div_64(753664, 5210112) == 9);
1261 SkASSERT(div_64(229376, 4882432) == 3);
1262 SkASSERT(div_64(2, 64) == 2);
1263 SkASSERT(div_64(1, 64) == 1);
1264 // test that we handle underflow correctly
1265 SkASSERT(div_64(12345, 0x54321234) == 0);
1266 }
1267 }
1268#endif
1269
1270 SkASSERT(y > 0 && x > 0);
1271 const uint8_t* table = build_sweep_table();
1272
1273 unsigned result;
1274 bool swap = (x < y);
1275 if (swap)
1276 {
1277 // first part of the atan(v) = PI/2 - atan(1/v) identity
1278 // since our div_64 and table want v <= 1, where v = y/x
1279 SkTSwap<SkFixed>(x, y);
1280 }
1281
1282 result = div_64(y, x);
1283
1284#ifdef SK_DEBUG
1285 {
1286 unsigned result2 = SkDivBits(y, x, 6);
1287 SkASSERT(result2 == result ||
1288 (result == 1 && result2 == 0));
1289 }
1290#endif
1291
1292 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
1293 result = table[result];
1294
1295 if (swap)
1296 {
1297 // complete the atan(v) = PI/2 - atan(1/v) identity
1298 result = 64 - result;
1299 // pin to 63
1300 result -= result >> 6;
1301 }
1302
1303 SkASSERT(result <= 63);
1304 return result;
1305}
1306
1307// returns angle in a circle [0..2PI) -> [0..255]
1308static unsigned SkATan2_255(SkFixed y, SkFixed x)
1309{
1310 if (x == 0)
1311 {
1312 if (y == 0)
1313 return 0;
1314 return y < 0 ? 192 : 64;
1315 }
1316 if (y == 0)
1317 return x < 0 ? 128 : 0;
1318
1319 /* Find the right quadrant for x,y
1320 Since atan_0_90 only handles the first quadrant, we rotate x,y
1321 appropriately before calling it, and then add the right amount
1322 to account for the real quadrant.
1323 quadrant 0 : add 0 | x > 0 && y > 0
1324 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
1325 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
1326 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
1327
1328 map x<0 to (1 << 6)
1329 map y<0 to (3 << 6)
1330 add = map_x ^ map_y
1331 */
1332 int xsign = x >> 31;
1333 int ysign = y >> 31;
1334 int add = ((-xsign) ^ (ysign & 3)) << 6;
1335
1336#ifdef SK_DEBUG
1337 if (0 == add)
1338 SkASSERT(x > 0 && y > 0);
1339 else if (64 == add)
1340 SkASSERT(x < 0 && y > 0);
1341 else if (128 == add)
1342 SkASSERT(x < 0 && y < 0);
1343 else if (192 == add)
1344 SkASSERT(x > 0 && y < 0);
1345 else
1346 SkASSERT(!"bad value for add");
1347#endif
1348
1349 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
1350 where we need to rotate x,y by 90 or -90
1351 */
1352 x = (x ^ xsign) - xsign;
1353 y = (y ^ ysign) - ysign;
1354 if (add & 64) // quads 1 or 3 need to swap x,y
1355 SkTSwap<SkFixed>(x, y);
1356
1357 unsigned result = add + atan_0_90(y, x);
1358 SkASSERT(result < 256);
1359 return result;
1360}
1361
1362void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
1363{
1364 SkMatrix::MapXYProc proc = fDstToIndexProc;
1365 const SkMatrix& matrix = fDstToIndex;
1366 const SkPMColor* cache = this->getCache32();
1367 SkPoint srcPt;
1368
1369 if (fDstToIndexClass != kPerspective_MatrixClass)
1370 {
1371 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1372 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1373 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1374 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1375
1376 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1377 {
1378 SkFixed storage[2];
1379 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1380 &storage[0], &storage[1]);
1381 dx = storage[0];
1382 dy = storage[1];
1383 }
1384 else
1385 {
1386 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1387 dx = SkScalarToFixed(matrix.getScaleX());
1388 dy = SkScalarToFixed(matrix.getSkewY());
1389 }
1390
1391 for (; count > 0; --count)
1392 {
1393 *dstC++ = cache[SkATan2_255(fy, fx)];
1394 fx += dx;
1395 fy += dy;
1396 }
1397 }
1398 else // perspective case
1399 {
1400 for (int stop = x + count; x < stop; x++)
1401 {
1402 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1403 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1404
1405 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1406 SkScalarToFixed(srcPt.fX));
1407 *dstC++ = cache[index];
1408 }
1409 }
1410}
1411
1412void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
1413{
1414 SkMatrix::MapXYProc proc = fDstToIndexProc;
1415 const SkMatrix& matrix = fDstToIndex;
1416 const uint16_t* cache = this->getCache16();
1417 int toggle = ((x ^ y) & 1) << kCache16Bits;
1418 SkPoint srcPt;
1419
1420 if (fDstToIndexClass != kPerspective_MatrixClass)
1421 {
1422 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1423 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1424 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1425 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1426
1427 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1428 {
1429 SkFixed storage[2];
1430 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1431 &storage[0], &storage[1]);
1432 dx = storage[0];
1433 dy = storage[1];
1434 }
1435 else
1436 {
1437 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1438 dx = SkScalarToFixed(matrix.getScaleX());
1439 dy = SkScalarToFixed(matrix.getSkewY());
1440 }
1441
1442 for (; count > 0; --count)
1443 {
1444 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
1445 *dstC++ = cache[toggle + index];
1446 toggle ^= (1 << kCache16Bits);
1447 fx += dx;
1448 fy += dy;
1449 }
1450 }
1451 else // perspective case
1452 {
1453 for (int stop = x + count; x < stop; x++)
1454 {
1455 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1456 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1457
1458 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1459 SkScalarToFixed(srcPt.fX));
1460 index >>= (8 - kCache16Bits);
1461 *dstC++ = cache[toggle + index];
1462 toggle ^= (1 << kCache16Bits);
1463 }
1464 }
1465}
1466
1467///////////////////////////////////////////////////////////////////////////
1468///////////////////////////////////////////////////////////////////////////
1469
1470// assumes colors is SkColor* and pos is SkScalar*
1471#define EXPAND_1_COLOR(count) \
1472 SkColor tmp[2]; \
1473 do { \
1474 if (1 == count) { \
1475 tmp[0] = tmp[1] = colors[0]; \
1476 colors = tmp; \
1477 pos = NULL; \
1478 count = 2; \
1479 } \
1480 } while (0)
1481
1482SkShader* SkGradientShader::CreateLinear( const SkPoint pts[2],
1483 const SkColor colors[], const SkScalar pos[], int colorCount,
1484 SkShader::TileMode mode, SkUnitMapper* mapper)
1485{
1486 if (NULL == pts || NULL == colors || colorCount < 1) {
1487 return NULL;
1488 }
1489 EXPAND_1_COLOR(colorCount);
1490
reed@android.comab840b82009-07-01 17:00:03 +00001491 return SkNEW_ARGS(Linear_Gradient,
1492 (pts, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001493}
1494
1495SkShader* SkGradientShader::CreateRadial( const SkPoint& center, SkScalar radius,
1496 const SkColor colors[], const SkScalar pos[], int colorCount,
1497 SkShader::TileMode mode, SkUnitMapper* mapper)
1498{
1499 if (radius <= 0 || NULL == colors || colorCount < 1) {
1500 return NULL;
1501 }
1502 EXPAND_1_COLOR(colorCount);
1503
reed@android.comab840b82009-07-01 17:00:03 +00001504 return SkNEW_ARGS(Radial_Gradient,
1505 (center, radius, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001506}
1507
1508SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
1509 const SkColor colors[],
1510 const SkScalar pos[],
1511 int count, SkUnitMapper* mapper)
1512{
1513 if (NULL == colors || count < 1) {
1514 return NULL;
1515 }
1516 EXPAND_1_COLOR(count);
1517
1518 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
1519}
1520
1521static SkFlattenable::Registrar gLinearGradientReg("Linear_Gradient",
1522 Linear_Gradient::CreateProc);
1523
1524static SkFlattenable::Registrar gRadialGradientReg("Radial_Gradient",
1525 Radial_Gradient::CreateProc);
1526
1527static SkFlattenable::Registrar gSweepGradientReg("Sweep_Gradient",
1528 Sweep_Gradient::CreateProc);
1529