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