blob: a30de6a937199e7e9db92f60784fe6158477be2f [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
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001111/* Two-point radial gradients are specified by two circles, each with a center
1112 point and radius. The gradient can be considered to be a series of
1113 concentric circles, with the color interpolated from the start circle
1114 (at t=0) to the end circle (at t=1).
1115
1116 For each point (x, y) in the span, we want to find the
1117 interpolated circle that intersects that point. The center
1118 of the desired circle (Cx, Cy) falls at some distance t
1119 along the line segment between the start point (Sx, Sy) and
1120 end point (Ex, Ey):
1121
1122 Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1)
1123 Cy = (1 - t) * Sy + t * Ey
1124
1125 The radius of the desired circle (r) is also a linear interpolation t
1126 between the start and end radii (Sr and Er):
1127
1128 r = (1 - t) * Sr + t * Er
1129
1130 But
1131
1132 (x - Cx)^2 + (y - Cy)^2 = r^2
1133
1134 so
1135
1136 (x - ((1 - t) * Sx + t * Ex))^2
1137 + (y - ((1 - t) * Sy + t * Ey))^2
1138 = ((1 - t) * Sr + t * Er)^2
1139
1140 Solving for t yields
1141
1142 [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2
1143 + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t
1144 + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0
1145
1146 To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
1147
1148 [Dx^2 + Dy^2 - Dr^2)] * t^2
1149 + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t
1150 + [dx^2 + dy^2 - Sr^2] = 0
1151
1152 A quadratic in t. The two roots of the quadratic reflect the two
1153 possible circles on which the point may fall. Solving for t yields
1154 the gradient value to use.
1155
1156 If a<0, the start circle is entirely contained in the
1157 end circle, and one of the roots will be <0 or >1 (off the line
1158 segment). If a>0, the start circle falls at least partially
1159 outside the end circle (or vice versa), and the gradient
1160 defines a "tube" where a point may be on one circle (on the
1161 inside of the tube) or the other (outside of the tube). We choose
1162 one arbitrarily.
1163
1164 In order to keep the math to within the limits of fixed point,
1165 we divide the entire quadratic by Dr^2, and replace
1166 (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
1167
1168 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
1169 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
1170 + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
1171
1172 (x' and y' are computed by appending the subtract and scale to the
1173 fDstToIndex matrix in the constructor).
1174
1175 Since the 'A' component of the quadratic is independent of x' and y', it
1176 is precomputed in the constructor. Since the 'B' component is linear in
1177 x' and y', if x and y are linear in the span, 'B' can be computed
1178 incrementally with a simple delta (db below). If it is not (e.g.,
1179 a perspective projection), it must be computed in the loop.
1180
1181*/
1182
1183static inline SkFixed two_point_radial(SkFixed b, SkFixed fx, SkFixed fy, SkFixed sr2d2, SkFixed foura, SkFixed oneOverTwoA, bool posRoot) {
1184 SkFixed c = SkFixedSquare(fx) + SkFixedSquare(fy) - sr2d2;
1185 SkFixed discrim = SkFixedSquare(b) - SkFixedMul(foura, c);
1186 if (discrim < 0) {
1187 discrim = -discrim;
1188 }
1189 SkFixed rootDiscrim = SkFixedSqrt(discrim);
1190 if (posRoot) {
1191 return SkFixedMul(-b + rootDiscrim, oneOverTwoA);
1192 } else {
1193 return SkFixedMul(-b - rootDiscrim, oneOverTwoA);
1194 }
1195}
1196
1197class Two_Point_Radial_Gradient : public Gradient_Shader {
1198public:
1199 Two_Point_Radial_Gradient(const SkPoint& start, SkScalar startRadius,
1200 const SkPoint& end, SkScalar endRadius,
1201 const SkColor colors[], const SkScalar pos[],
1202 int colorCount, SkShader::TileMode mode,
1203 SkUnitMapper* mapper)
1204 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
1205 {
1206 fDiff = start - end;
1207 fDiffRadius = endRadius - startRadius;
1208 SkScalar inv = SkScalarInvert(fDiffRadius);
1209 fDiff.fX = SkScalarMul(fDiff.fX, inv);
1210 fDiff.fY = SkScalarMul(fDiff.fY, inv);
1211 fStartRadius = SkScalarMul(startRadius, inv);
1212 fSr2D2 = SkScalarSquare(fStartRadius);
1213 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
1214 fOneOverTwoA = SkScalarInvert(fA * 2);
1215
1216 fPtsToUnit.setTranslate(-start.fX, -start.fY);
1217 fPtsToUnit.postScale(inv, inv);
1218 }
1219 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
1220 {
1221 SkASSERT(count > 0);
1222
1223 // Zero difference between radii: fill with transparent black.
1224 if (fDiffRadius == 0) {
1225 sk_bzero(dstC, count * sizeof(*dstC));
1226 return;
1227 }
1228 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1229 TileProc proc = fTileProc;
1230 const SkPMColor* cache = this->getCache32();
1231 SkFixed diffx = SkScalarToFixed(fDiff.fX);
1232 SkFixed diffy = SkScalarToFixed(fDiff.fY);
1233 SkFixed foura = SkScalarToFixed(SkScalarMul(fA, 4));
1234 SkFixed startRadius = SkScalarToFixed(fStartRadius);
1235 SkFixed sr2D2 = SkScalarToFixed(fSr2D2);
1236 SkFixed oneOverTwoA = SkScalarToFixed(fOneOverTwoA);
1237 bool posRoot = fDiffRadius < 0;
1238 if (fDstToIndexClass != kPerspective_MatrixClass)
1239 {
1240 SkPoint srcPt;
1241 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
1242 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1243 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1244
1245 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1246 {
1247 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &dx, &dy);
1248 }
1249 else
1250 {
1251 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1252 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1253 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1254 }
1255 SkFixed b = (SkFixedMul(diffx, fx) +
1256 SkFixedMul(diffy, fy) - startRadius) << 1;
1257 SkFixed db = (SkFixedMul(diffx, dx) +
1258 SkFixedMul(diffy, dy)) << 1;
1259 if (proc == clamp_tileproc)
1260 {
1261 for (; count > 0; --count) {
1262 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1263 SkFixed index = SkClampMax(t, 0xFFFF);
1264 SkASSERT(index <= 0xFFFF);
1265 *dstC++ = cache[index >> (16 - kCache32Bits)];
1266 fx += dx;
1267 fy += dy;
1268 b += db;
1269 }
1270 }
1271 else if (proc == mirror_tileproc)
1272 {
1273 for (; count > 0; --count) {
1274 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1275 SkFixed index = mirror_tileproc(t);
1276 SkASSERT(index <= 0xFFFF);
1277 *dstC++ = cache[index >> (16 - kCache32Bits)];
1278 fx += dx;
1279 fy += dy;
1280 b += db;
1281 }
1282 }
1283 else
1284 {
1285 SkASSERT(proc == repeat_tileproc);
1286 for (; count > 0; --count) {
1287 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1288 SkFixed index = repeat_tileproc(t);
1289 SkASSERT(index <= 0xFFFF);
1290 *dstC++ = cache[index >> (16 - kCache32Bits)];
1291 fx += dx;
1292 fy += dy;
1293 b += db;
1294 }
1295 }
1296 }
1297 else // perspective case
1298 {
reed@android.com6c59a172009-09-22 20:24:05 +00001299 SkScalar dstX = SkIntToScalar(x);
1300 SkScalar dstY = SkIntToScalar(y);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001301 for (; count > 0; --count) {
1302 SkPoint srcPt;
reed@android.com6c59a172009-09-22 20:24:05 +00001303 dstProc(fDstToIndex, dstX, dstY, &srcPt);
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001304 SkFixed fx = SkScalarToFixed(srcPt.fX);
1305 SkFixed fy = SkScalarToFixed(srcPt.fY);
1306 SkFixed b = (SkFixedMul(diffx, fx) +
1307 SkFixedMul(diffy, fy) - startRadius) << 1;
1308 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1309 SkFixed index = proc(t);
1310 SkASSERT(index <= 0xFFFF);
1311 *dstC++ = cache[index >> (16 - kCache32Bits)];
reed@android.com6c59a172009-09-22 20:24:05 +00001312 dstX += SK_Scalar1;
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001313 }
1314 }
1315 }
1316
reed@android.com6c59a172009-09-22 20:24:05 +00001317 virtual bool setContext(const SkBitmap& device,
1318 const SkPaint& paint,
1319 const SkMatrix& matrix) {
1320 if (!this->INHERITED::setContext(device, paint, matrix)) {
1321 return false;
1322 }
1323
1324 // we don't have a span16 proc
1325 fFlags &= ~kHasSpan16_Flag;
1326 return true;
1327 }
1328
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001329 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1330 return SkNEW_ARGS(Two_Point_Radial_Gradient, (buffer));
1331 }
1332
1333protected:
1334 Two_Point_Radial_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {};
1335 virtual Factory getFactory() { return CreateProc; }
1336 virtual void onCacheReset() {}
1337
1338private:
1339 typedef Gradient_Shader INHERITED;
1340 SkPoint fDiff;
1341 SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA;
1342};
1343
reed@android.com8a1c16f2008-12-17 15:59:43 +00001344///////////////////////////////////////////////////////////////////////////////
1345
1346class Sweep_Gradient : public Gradient_Shader {
1347public:
1348 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
1349 const SkScalar pos[], int count, SkUnitMapper* mapper)
1350 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper)
1351 {
1352 fPtsToUnit.setTranslate(-cx, -cy);
1353 }
1354 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
1355 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
1356
1357 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1358 return SkNEW_ARGS(Sweep_Gradient, (buffer));
1359 }
1360
1361protected:
1362 Sweep_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001363 virtual Factory getFactory() { return CreateProc; }
reed@android.comec3d6e52009-06-05 14:43:55 +00001364 virtual void onCacheReset() {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001365
1366private:
1367 typedef Gradient_Shader INHERITED;
1368};
1369
1370#ifdef COMPUTE_SWEEP_TABLE
1371#define PI 3.14159265
1372static bool gSweepTableReady;
1373static uint8_t gSweepTable[65];
1374
1375/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
1376 We scale the results to [0..32]
1377*/
1378static const uint8_t* build_sweep_table()
1379{
1380 if (!gSweepTableReady)
1381 {
1382 const int N = 65;
1383 const double DENOM = N - 1;
1384
1385 for (int i = 0; i < N; i++)
1386 {
1387 double arg = i / DENOM;
1388 double v = atan(arg);
1389 int iv = (int)round(v * DENOM * 2 / PI);
1390// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
1391 printf("%d, ", iv);
1392 gSweepTable[i] = iv;
1393 }
1394 gSweepTableReady = true;
1395 }
1396 return gSweepTable;
1397}
1398#else
1399static const uint8_t gSweepTable[] = {
1400 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
1401 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
1402 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
1403 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
1404 32
1405};
1406static const uint8_t* build_sweep_table() { return gSweepTable; }
1407#endif
1408
1409// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
1410// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
1411// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
1412
1413//unsigned div_64(int numer, int denom);
1414static unsigned div_64(int numer, int denom)
1415{
1416 SkASSERT(numer <= denom);
1417 SkASSERT(numer > 0);
1418 SkASSERT(denom > 0);
1419
1420 int nbits = SkCLZ(numer);
1421 int dbits = SkCLZ(denom);
1422 int bits = 6 - nbits + dbits;
1423 SkASSERT(bits <= 6);
1424
1425 if (bits < 0) // detect underflow
1426 return 0;
1427
1428 denom <<= dbits - 1;
1429 numer <<= nbits - 1;
1430
1431 unsigned result = 0;
1432
1433 // do the first one
1434 if ((numer -= denom) >= 0)
1435 result = 1;
1436 else
1437 numer += denom;
1438
1439 // Now fall into our switch statement if there are more bits to compute
1440 if (bits > 0)
1441 {
1442 // make room for the rest of the answer bits
1443 result <<= bits;
1444 switch (bits) {
1445 case 6:
1446 if ((numer = (numer << 1) - denom) >= 0)
1447 result |= 32;
1448 else
1449 numer += denom;
1450 case 5:
1451 if ((numer = (numer << 1) - denom) >= 0)
1452 result |= 16;
1453 else
1454 numer += denom;
1455 case 4:
1456 if ((numer = (numer << 1) - denom) >= 0)
1457 result |= 8;
1458 else
1459 numer += denom;
1460 case 3:
1461 if ((numer = (numer << 1) - denom) >= 0)
1462 result |= 4;
1463 else
1464 numer += denom;
1465 case 2:
1466 if ((numer = (numer << 1) - denom) >= 0)
1467 result |= 2;
1468 else
1469 numer += denom;
1470 case 1:
1471 default: // not strictly need, but makes GCC make better ARM code
1472 if ((numer = (numer << 1) - denom) >= 0)
1473 result |= 1;
1474 else
1475 numer += denom;
1476 }
1477 }
1478 return result;
1479}
1480
1481// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
1482static unsigned atan_0_90(SkFixed y, SkFixed x)
1483{
1484#ifdef SK_DEBUG
1485 {
1486 static bool gOnce;
1487 if (!gOnce)
1488 {
1489 gOnce = true;
1490 SkASSERT(div_64(55, 55) == 64);
1491 SkASSERT(div_64(128, 256) == 32);
1492 SkASSERT(div_64(2326528, 4685824) == 31);
1493 SkASSERT(div_64(753664, 5210112) == 9);
1494 SkASSERT(div_64(229376, 4882432) == 3);
1495 SkASSERT(div_64(2, 64) == 2);
1496 SkASSERT(div_64(1, 64) == 1);
1497 // test that we handle underflow correctly
1498 SkASSERT(div_64(12345, 0x54321234) == 0);
1499 }
1500 }
1501#endif
1502
1503 SkASSERT(y > 0 && x > 0);
1504 const uint8_t* table = build_sweep_table();
1505
1506 unsigned result;
1507 bool swap = (x < y);
1508 if (swap)
1509 {
1510 // first part of the atan(v) = PI/2 - atan(1/v) identity
1511 // since our div_64 and table want v <= 1, where v = y/x
1512 SkTSwap<SkFixed>(x, y);
1513 }
1514
1515 result = div_64(y, x);
1516
1517#ifdef SK_DEBUG
1518 {
1519 unsigned result2 = SkDivBits(y, x, 6);
1520 SkASSERT(result2 == result ||
1521 (result == 1 && result2 == 0));
1522 }
1523#endif
1524
1525 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
1526 result = table[result];
1527
1528 if (swap)
1529 {
1530 // complete the atan(v) = PI/2 - atan(1/v) identity
1531 result = 64 - result;
1532 // pin to 63
1533 result -= result >> 6;
1534 }
1535
1536 SkASSERT(result <= 63);
1537 return result;
1538}
1539
1540// returns angle in a circle [0..2PI) -> [0..255]
1541static unsigned SkATan2_255(SkFixed y, SkFixed x)
1542{
1543 if (x == 0)
1544 {
1545 if (y == 0)
1546 return 0;
1547 return y < 0 ? 192 : 64;
1548 }
1549 if (y == 0)
1550 return x < 0 ? 128 : 0;
1551
1552 /* Find the right quadrant for x,y
1553 Since atan_0_90 only handles the first quadrant, we rotate x,y
1554 appropriately before calling it, and then add the right amount
1555 to account for the real quadrant.
1556 quadrant 0 : add 0 | x > 0 && y > 0
1557 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
1558 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
1559 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
1560
1561 map x<0 to (1 << 6)
1562 map y<0 to (3 << 6)
1563 add = map_x ^ map_y
1564 */
1565 int xsign = x >> 31;
1566 int ysign = y >> 31;
1567 int add = ((-xsign) ^ (ysign & 3)) << 6;
1568
1569#ifdef SK_DEBUG
1570 if (0 == add)
1571 SkASSERT(x > 0 && y > 0);
1572 else if (64 == add)
1573 SkASSERT(x < 0 && y > 0);
1574 else if (128 == add)
1575 SkASSERT(x < 0 && y < 0);
1576 else if (192 == add)
1577 SkASSERT(x > 0 && y < 0);
1578 else
1579 SkASSERT(!"bad value for add");
1580#endif
1581
1582 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
1583 where we need to rotate x,y by 90 or -90
1584 */
1585 x = (x ^ xsign) - xsign;
1586 y = (y ^ ysign) - ysign;
1587 if (add & 64) // quads 1 or 3 need to swap x,y
1588 SkTSwap<SkFixed>(x, y);
1589
1590 unsigned result = add + atan_0_90(y, x);
1591 SkASSERT(result < 256);
1592 return result;
1593}
1594
1595void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
1596{
1597 SkMatrix::MapXYProc proc = fDstToIndexProc;
1598 const SkMatrix& matrix = fDstToIndex;
1599 const SkPMColor* cache = this->getCache32();
1600 SkPoint srcPt;
1601
1602 if (fDstToIndexClass != kPerspective_MatrixClass)
1603 {
1604 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1605 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1606 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1607 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1608
1609 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1610 {
1611 SkFixed storage[2];
1612 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1613 &storage[0], &storage[1]);
1614 dx = storage[0];
1615 dy = storage[1];
1616 }
1617 else
1618 {
1619 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1620 dx = SkScalarToFixed(matrix.getScaleX());
1621 dy = SkScalarToFixed(matrix.getSkewY());
1622 }
1623
1624 for (; count > 0; --count)
1625 {
1626 *dstC++ = cache[SkATan2_255(fy, fx)];
1627 fx += dx;
1628 fy += dy;
1629 }
1630 }
1631 else // perspective case
1632 {
1633 for (int stop = x + count; x < stop; x++)
1634 {
1635 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1636 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1637
1638 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1639 SkScalarToFixed(srcPt.fX));
1640 *dstC++ = cache[index];
1641 }
1642 }
1643}
1644
1645void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
1646{
1647 SkMatrix::MapXYProc proc = fDstToIndexProc;
1648 const SkMatrix& matrix = fDstToIndex;
1649 const uint16_t* cache = this->getCache16();
1650 int toggle = ((x ^ y) & 1) << kCache16Bits;
1651 SkPoint srcPt;
1652
1653 if (fDstToIndexClass != kPerspective_MatrixClass)
1654 {
1655 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1656 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1657 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1658 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1659
1660 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1661 {
1662 SkFixed storage[2];
1663 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1664 &storage[0], &storage[1]);
1665 dx = storage[0];
1666 dy = storage[1];
1667 }
1668 else
1669 {
1670 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1671 dx = SkScalarToFixed(matrix.getScaleX());
1672 dy = SkScalarToFixed(matrix.getSkewY());
1673 }
1674
1675 for (; count > 0; --count)
1676 {
1677 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
1678 *dstC++ = cache[toggle + index];
1679 toggle ^= (1 << kCache16Bits);
1680 fx += dx;
1681 fy += dy;
1682 }
1683 }
1684 else // perspective case
1685 {
1686 for (int stop = x + count; x < stop; x++)
1687 {
1688 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1689 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1690
1691 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1692 SkScalarToFixed(srcPt.fX));
1693 index >>= (8 - kCache16Bits);
1694 *dstC++ = cache[toggle + index];
1695 toggle ^= (1 << kCache16Bits);
1696 }
1697 }
1698}
1699
1700///////////////////////////////////////////////////////////////////////////
1701///////////////////////////////////////////////////////////////////////////
1702
1703// assumes colors is SkColor* and pos is SkScalar*
1704#define EXPAND_1_COLOR(count) \
1705 SkColor tmp[2]; \
1706 do { \
1707 if (1 == count) { \
1708 tmp[0] = tmp[1] = colors[0]; \
1709 colors = tmp; \
1710 pos = NULL; \
1711 count = 2; \
1712 } \
1713 } while (0)
1714
1715SkShader* SkGradientShader::CreateLinear( const SkPoint pts[2],
1716 const SkColor colors[], const SkScalar pos[], int colorCount,
1717 SkShader::TileMode mode, SkUnitMapper* mapper)
1718{
1719 if (NULL == pts || NULL == colors || colorCount < 1) {
1720 return NULL;
1721 }
1722 EXPAND_1_COLOR(colorCount);
1723
reed@android.comab840b82009-07-01 17:00:03 +00001724 return SkNEW_ARGS(Linear_Gradient,
1725 (pts, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001726}
1727
1728SkShader* SkGradientShader::CreateRadial( const SkPoint& center, SkScalar radius,
1729 const SkColor colors[], const SkScalar pos[], int colorCount,
1730 SkShader::TileMode mode, SkUnitMapper* mapper)
1731{
1732 if (radius <= 0 || NULL == colors || colorCount < 1) {
1733 return NULL;
1734 }
1735 EXPAND_1_COLOR(colorCount);
1736
reed@android.comab840b82009-07-01 17:00:03 +00001737 return SkNEW_ARGS(Radial_Gradient,
1738 (center, radius, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001739}
1740
senorblanco@chromium.org7ef071f2009-09-22 17:25:29 +00001741SkShader* SkGradientShader::CreateTwoPointRadial(const SkPoint& start,
1742 SkScalar startRadius,
1743 const SkPoint& end,
1744 SkScalar endRadius,
1745 const SkColor colors[],
1746 const SkScalar pos[],
1747 int colorCount,
1748 SkShader::TileMode mode,
1749 SkUnitMapper* mapper)
1750{
1751 if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
1752 return NULL;
1753 }
1754 EXPAND_1_COLOR(colorCount);
1755
1756 return SkNEW_ARGS(Two_Point_Radial_Gradient,
1757 (start, startRadius, end, endRadius, colors, pos, colorCount, mode, mapper));
1758}
1759
reed@android.com8a1c16f2008-12-17 15:59:43 +00001760SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
1761 const SkColor colors[],
1762 const SkScalar pos[],
1763 int count, SkUnitMapper* mapper)
1764{
1765 if (NULL == colors || count < 1) {
1766 return NULL;
1767 }
1768 EXPAND_1_COLOR(count);
1769
1770 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
1771}
1772
1773static SkFlattenable::Registrar gLinearGradientReg("Linear_Gradient",
1774 Linear_Gradient::CreateProc);
1775
1776static SkFlattenable::Registrar gRadialGradientReg("Radial_Gradient",
1777 Radial_Gradient::CreateProc);
1778
1779static SkFlattenable::Registrar gSweepGradientReg("Sweep_Gradient",
1780 Sweep_Gradient::CreateProc);
1781