blob: 8065894151183376849704cc47b62bd930878dbb [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.com1c12abe2009-07-02 15:01:02 +0000361 // should record colorAlpha in constructor
reed@android.com41bccf52009-04-03 13:33:51 +0000362 for (int i = 0; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363 SkColor src = fOrigColors[i];
364 unsigned sa = SkColorGetA(src);
365 colorAlpha &= sa;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366 }
367
368 fFlags = this->INHERITED::getFlags();
369 if ((colorAlpha & paintAlpha) == 0xFF) {
370 fFlags |= kOpaqueAlpha_Flag;
371 }
372 // we can do span16 as long as our individual colors are opaque,
373 // regardless of the paint's alpha
374 if (0xFF == colorAlpha) {
375 fFlags |= kHasSpan16_Flag;
376 }
377
378 // if the new alpha differs from the previous time we were called, inval our cache
379 // this will trigger the cache to be rebuilt.
380 // we don't care about the first time, since the cache ptrs will already be NULL
381 if (fCacheAlpha != paintAlpha) {
382 fCache16 = NULL; // inval the cache
383 fCache32 = NULL; // inval the cache
384 fCacheAlpha = paintAlpha; // record the new alpha
reed@android.com9b46e772009-06-05 12:24:41 +0000385 // inform our subclasses
386 this->onCacheReset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000387 }
388 return true;
389}
390
reed@android.com41bccf52009-04-03 13:33:51 +0000391static inline int blend8(int a, int b, int scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392 SkASSERT(a == SkToU8(a));
393 SkASSERT(b == SkToU8(b));
394 SkASSERT(scale >= 0 && scale <= 256);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000395 return a + ((b - a) * scale >> 8);
396}
397
reed@android.com41bccf52009-04-03 13:33:51 +0000398static inline uint32_t dot8_blend_packed32(uint32_t s0, uint32_t s1,
399 int blend) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400#if 0
401 int a = blend8(SkGetPackedA32(s0), SkGetPackedA32(s1), blend);
402 int r = blend8(SkGetPackedR32(s0), SkGetPackedR32(s1), blend);
403 int g = blend8(SkGetPackedG32(s0), SkGetPackedG32(s1), blend);
404 int b = blend8(SkGetPackedB32(s0), SkGetPackedB32(s1), blend);
405
406 return SkPackARGB32(a, r, g, b);
407#else
408 int otherBlend = 256 - blend;
409
410#if 0
411 U32 t0 = (((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF;
412 U32 t1 = (((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00;
413 SkASSERT((t0 & t1) == 0);
414 return t0 | t1;
415#else
416 return ((((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF) |
417 ((((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00);
418#endif
419
420#endif
421}
422
423#define Fixed_To_Dot8(x) (((x) + 0x80) >> 8)
424
reed@android.com41bccf52009-04-03 13:33:51 +0000425/** We take the original colors, not our premultiplied PMColors, since we can
426 build a 16bit table as long as the original colors are opaque, even if the
427 paint specifies a non-opaque alpha.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000428*/
reed@android.com41bccf52009-04-03 13:33:51 +0000429static void build_16bit_cache(uint16_t cache[], SkColor c0, SkColor c1,
430 int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431 SkASSERT(count > 1);
432 SkASSERT(SkColorGetA(c0) == 0xFF);
433 SkASSERT(SkColorGetA(c1) == 0xFF);
434
435 SkFixed r = SkColorGetR(c0);
436 SkFixed g = SkColorGetG(c0);
437 SkFixed b = SkColorGetB(c0);
438
439 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
440 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
441 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
442
443 r = SkIntToFixed(r) + 0x8000;
444 g = SkIntToFixed(g) + 0x8000;
445 b = SkIntToFixed(b) + 0x8000;
446
447 do {
448 unsigned rr = r >> 16;
449 unsigned gg = g >> 16;
450 unsigned bb = b >> 16;
451 cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
452 cache[64] = SkDitherPack888ToRGB16(rr, gg, bb);
453 cache += 1;
454 r += dr;
455 g += dg;
456 b += db;
457 } while (--count != 0);
458}
459
reed@android.com1c12abe2009-07-02 15:01:02 +0000460static void build_32bit_cache(SkPMColor cache[], SkColor c0, SkColor c1,
461 int count, U8CPU paintAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000462 SkASSERT(count > 1);
463
reed@android.com1c12abe2009-07-02 15:01:02 +0000464 // need to apply paintAlpha to our two endpoints
465 SkFixed a = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
466 SkFixed da;
467 {
468 int tmp = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
469 da = SkIntToFixed(tmp - a) / (count - 1);
470 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000471
reed@android.com1c12abe2009-07-02 15:01:02 +0000472 SkFixed r = SkColorGetR(c0);
473 SkFixed g = SkColorGetG(c0);
474 SkFixed b = SkColorGetB(c0);
475 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
476 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
477 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000478
479 a = SkIntToFixed(a) + 0x8000;
480 r = SkIntToFixed(r) + 0x8000;
481 g = SkIntToFixed(g) + 0x8000;
482 b = SkIntToFixed(b) + 0x8000;
483
484 do {
reed@android.com1c12abe2009-07-02 15:01:02 +0000485 *cache++ = SkPreMultiplyARGB(a >> 16, r >> 16, g >> 16, b >> 16);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486 a += da;
487 r += dr;
488 g += dg;
489 b += db;
490 } while (--count != 0);
491}
492
reed@android.com41bccf52009-04-03 13:33:51 +0000493static inline int SkFixedToFFFF(SkFixed x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000494 SkASSERT((unsigned)x <= SK_Fixed1);
495 return x - (x >> 16);
496}
497
reed@android.com41bccf52009-04-03 13:33:51 +0000498static inline U16CPU dot6to16(unsigned x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499 SkASSERT(x < 64);
500 return (x << 10) | (x << 4) | (x >> 2);
501}
502
reed@android.com41bccf52009-04-03 13:33:51 +0000503const uint16_t* Gradient_Shader::getCache16() {
504 if (fCache16 == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000505 if (fCache16Storage == NULL) // set the storage and our working ptr
506#ifdef TEST_GRADIENT_DITHER
507 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
508#else
509 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count);
510#endif
511 fCache16 = fCache16Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000512 if (fColorCount == 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000513 build_16bit_cache(fCache16, fOrigColors[0], fOrigColors[1], kCache16Count);
reed@android.com41bccf52009-04-03 13:33:51 +0000514 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 Rec* rec = fRecs;
516 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000517 for (int i = 1; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000518 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache16Bits);
519 SkASSERT(nextIndex < kCache16Count);
520
521 if (nextIndex > prevIndex)
522 build_16bit_cache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
523 prevIndex = nextIndex;
524 }
525 SkASSERT(prevIndex == kCache16Count - 1);
526 }
527
reed@android.com41bccf52009-04-03 13:33:51 +0000528 if (fMapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529#ifdef TEST_GRADIENT_DITHER
530 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
531#else
532 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count);
533#endif
534 uint16_t* linear = fCache16; // just computed linear data
535 uint16_t* mapped = fCache16Storage; // storage for mapped data
536 SkUnitMapper* map = fMapper;
reed@android.com41bccf52009-04-03 13:33:51 +0000537 for (int i = 0; i < 64; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538 int index = map->mapUnit16(dot6to16(i)) >> 10;
539 mapped[i] = linear[index];
540#ifdef TEST_GRADIENT_DITHER
541 mapped[i + 64] = linear[index + 64];
542#endif
543 }
544 sk_free(fCache16);
545 fCache16 = fCache16Storage;
546 }
547 }
548 return fCache16;
549}
550
reed@android.com41bccf52009-04-03 13:33:51 +0000551const SkPMColor* Gradient_Shader::getCache32() {
552 if (fCache32 == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553 if (fCache32Storage == NULL) // set the storage and our working ptr
554 fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count);
555
556 fCache32 = fCache32Storage;
reed@android.com41bccf52009-04-03 13:33:51 +0000557 if (fColorCount == 2) {
reed@android.com1c12abe2009-07-02 15:01:02 +0000558 build_32bit_cache(fCache32, fOrigColors[0], fOrigColors[1],
559 kCache32Count, fCacheAlpha);
reed@android.com41bccf52009-04-03 13:33:51 +0000560 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561 Rec* rec = fRecs;
562 int prevIndex = 0;
reed@android.com41bccf52009-04-03 13:33:51 +0000563 for (int i = 1; i < fColorCount; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache32Bits);
565 SkASSERT(nextIndex < kCache32Count);
566
567 if (nextIndex > prevIndex)
reed@android.com1c12abe2009-07-02 15:01:02 +0000568 build_32bit_cache(fCache32 + prevIndex, fOrigColors[i-1],
569 fOrigColors[i],
570 nextIndex - prevIndex + 1, fCacheAlpha);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000571 prevIndex = nextIndex;
572 }
573 SkASSERT(prevIndex == kCache32Count - 1);
574 }
575
reed@android.com41bccf52009-04-03 13:33:51 +0000576 if (fMapper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577 fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count);
578 SkPMColor* linear = fCache32; // just computed linear data
579 SkPMColor* mapped = fCache32Storage; // storage for mapped data
580 SkUnitMapper* map = fMapper;
reed@android.com41bccf52009-04-03 13:33:51 +0000581 for (int i = 0; i < 256; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000582 mapped[i] = linear[map->mapUnit16((i << 8) | i) >> 8];
reed@android.com41bccf52009-04-03 13:33:51 +0000583 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584 sk_free(fCache32);
585 fCache32 = fCache32Storage;
586 }
587 }
588 return fCache32;
589}
590
591///////////////////////////////////////////////////////////////////////////
592
reed@android.com41bccf52009-04-03 13:33:51 +0000593static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 SkVector vec = pts[1] - pts[0];
595 SkScalar mag = vec.length();
596 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
597
598 vec.scale(inv);
599 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
600 matrix->postTranslate(-pts[0].fX, -pts[0].fY);
601 matrix->postScale(inv, inv);
602}
603
604///////////////////////////////////////////////////////////////////////////////
605
606class Linear_Gradient : public Gradient_Shader {
607public:
608 Linear_Gradient(const SkPoint pts[2],
609 const SkColor colors[], const SkScalar pos[], int colorCount,
610 SkShader::TileMode mode, SkUnitMapper* mapper)
611 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
612 {
reed@android.com9b46e772009-06-05 12:24:41 +0000613 fCachedBitmap = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614 pts_to_unit_matrix(pts, &fPtsToUnit);
615 }
reed@android.com9b46e772009-06-05 12:24:41 +0000616 virtual ~Linear_Gradient() {
617 if (fCachedBitmap) {
618 SkDELETE(fCachedBitmap);
619 }
620 }
621
reed@android.com5119bdb2009-06-12 21:27:03 +0000622 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
624 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
625 virtual bool asABitmap(SkBitmap*, SkMatrix*, TileMode*);
reed@android.com9b46e772009-06-05 12:24:41 +0000626 virtual void onCacheReset() {
627 if (fCachedBitmap) {
628 SkDELETE(fCachedBitmap);
629 fCachedBitmap = NULL;
630 }
631 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000632
633 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
634 return SkNEW_ARGS(Linear_Gradient, (buffer));
635 }
636
637protected:
reed@android.comec3d6e52009-06-05 14:43:55 +0000638 Linear_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {
639 fCachedBitmap = NULL;
640 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 virtual Factory getFactory() { return CreateProc; }
642
643private:
reed@android.com9b46e772009-06-05 12:24:41 +0000644 SkBitmap* fCachedBitmap; // allocated on demand
645
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 typedef Gradient_Shader INHERITED;
647};
648
reed@android.com5119bdb2009-06-12 21:27:03 +0000649bool Linear_Gradient::setContext(const SkBitmap& device, const SkPaint& paint,
650 const SkMatrix& matrix) {
651 if (!this->INHERITED::setContext(device, paint, matrix)) {
652 return false;
653 }
654
655 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
656 if ((fDstToIndex.getType() & ~mask) == 0) {
657 fFlags |= SkShader::kConstInY_Flag;
658 }
659 return true;
660}
661
reed@android.com8a1c16f2008-12-17 15:59:43 +0000662// Return true if fx, fx+dx, fx+2*dx, ... is always in range
reed@android.comfc25abd2009-01-15 14:38:33 +0000663static inline bool no_need_for_clamp(int fx, int dx, int count)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664{
665 SkASSERT(count > 0);
666 return (unsigned)((fx | (fx + (count - 1) * dx)) >> 8) <= 0xFF;
667}
668
669void Linear_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
670{
671 SkASSERT(count > 0);
672
673 SkPoint srcPt;
674 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
675 TileProc proc = fTileProc;
676 const SkPMColor* cache = this->getCache32();
677
reed@android.comc552a432009-06-12 20:02:50 +0000678 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000679 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
680 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
reed@android.comc552a432009-06-12 20:02:50 +0000681 // preround fx by half the amount we throw away
682 fx += 1 << 7;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000683
reed@android.comc552a432009-06-12 20:02:50 +0000684 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000685 SkFixed dxStorage[1];
686 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
687 dx = dxStorage[0];
reed@android.comc552a432009-06-12 20:02:50 +0000688 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000689 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
690 dx = SkScalarToFixed(fDstToIndex.getScaleX());
691 }
692
reed@android.comc552a432009-06-12 20:02:50 +0000693 if (SkFixedNearlyZero(dx)) {
694 // we're a vertical gradient, so no change in a span
reed@android.com8a1c16f2008-12-17 15:59:43 +0000695 unsigned fi = proc(fx);
696 SkASSERT(fi <= 0xFFFF);
697 sk_memset32(dstC, cache[fi >> (16 - kCache32Bits)], count);
reed@android.comc552a432009-06-12 20:02:50 +0000698 } else if (proc == clamp_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000699#if 0
700 if (no_need_for_clamp(fx, dx, count))
701 {
702 unsigned fi;
703 while ((count -= 4) >= 0)
704 {
705 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
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 }
710 SkASSERT(count <= -1 && count >= -4);
711 count += 4;
712 while (--count >= 0)
713 {
714 fi = fx >> 8;
715 SkASSERT(fi <= 0xFF);
716 fx += dx;
717 *dstC++ = cache[fi];
718 }
719 }
720 else
721#endif
722 do {
723 unsigned fi = SkClampMax(fx >> 8, 0xFF);
724 SkASSERT(fi <= 0xFF);
725 fx += dx;
726 *dstC++ = cache[fi];
727 } while (--count != 0);
reed@android.comc552a432009-06-12 20:02:50 +0000728 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000729 do {
730 unsigned fi = mirror_8bits(fx >> 8);
731 SkASSERT(fi <= 0xFF);
732 fx += dx;
733 *dstC++ = cache[fi];
734 } while (--count != 0);
reed@android.comc552a432009-06-12 20:02:50 +0000735 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000736 SkASSERT(proc == repeat_tileproc);
737 do {
738 unsigned fi = repeat_8bits(fx >> 8);
739 SkASSERT(fi <= 0xFF);
740 fx += dx;
741 *dstC++ = cache[fi];
742 } while (--count != 0);
743 }
reed@android.comc552a432009-06-12 20:02:50 +0000744 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000745 SkScalar dstX = SkIntToScalar(x);
746 SkScalar dstY = SkIntToScalar(y);
747 do {
748 dstProc(fDstToIndex, dstX, dstY, &srcPt);
749 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
750 SkASSERT(fi <= 0xFFFF);
751 *dstC++ = cache[fi >> (16 - kCache32Bits)];
752 dstX += SK_Scalar1;
753 } while (--count != 0);
754 }
755}
756
757bool Linear_Gradient::asABitmap(SkBitmap* bitmap, SkMatrix* matrix,
758 TileMode xy[]) {
reed@android.com9b46e772009-06-05 12:24:41 +0000759 // we cache our "bitmap", so it's generationID will be const on subsequent
760 // calls to asABitmap
761 if (NULL == fCachedBitmap) {
762 fCachedBitmap = SkNEW(SkBitmap);
763 fCachedBitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
764 fCachedBitmap->setPixels((void*)this->getCache32(), NULL);
765 }
766
reed@android.com8a1c16f2008-12-17 15:59:43 +0000767 if (bitmap) {
reed@android.com9b46e772009-06-05 12:24:41 +0000768 *bitmap = *fCachedBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769 }
770 if (matrix) {
771 matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1);
772 matrix->preConcat(fPtsToUnit);
773 }
774 if (xy) {
775 xy[0] = fTileMode;
776 xy[1] = kClamp_TileMode;
777 }
778 return true;
779}
780
781#ifdef TEST_GRADIENT_DITHER
782static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, int count)
783{
reed@android.com0becfc52009-01-13 13:26:44 +0000784 if (reinterpret_cast<uintptr_t>(dst) & 2)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785 {
786 *dst++ = value;
787 count -= 1;
788 SkTSwap(value, other);
789 }
790
791 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
792
793 if (count & 1)
794 dst[count - 1] = value;
795}
796#endif
797
798void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
799{
800 SkASSERT(count > 0);
801
802 SkPoint srcPt;
803 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
804 TileProc proc = fTileProc;
805 const uint16_t* cache = this->getCache16();
806#ifdef TEST_GRADIENT_DITHER
807 int toggle = ((x ^ y) & 1) << kCache16Bits;
808#endif
809
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000810 if (fDstToIndexClass != kPerspective_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
812 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000813 // preround fx by half the amount we throw away
814 fx += 1 << 7;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000815
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000816 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817 SkFixed dxStorage[1];
818 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
819 dx = dxStorage[0];
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000820 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000821 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
822 dx = SkScalarToFixed(fDstToIndex.getScaleX());
823 }
824
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000825 if (SkFixedNearlyZero(dx)) {
826 // we're a vertical gradient, so no change in a span
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827 unsigned fi = proc(fx) >> 10;
828 SkASSERT(fi <= 63);
829#ifdef TEST_GRADIENT_DITHER
830 dither_memset16(dstC, cache[toggle + fi], cache[(toggle ^ (1 << kCache16Bits)) + fi], count);
831#else
832 sk_memset16(dstC, cache[fi], count);
833#endif
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000834 } else if (proc == clamp_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835 do {
836 unsigned fi = SkClampMax(fx >> 10, 63);
837 SkASSERT(fi <= 63);
838 fx += dx;
839#ifdef TEST_GRADIENT_DITHER
840 *dstC++ = cache[toggle + fi];
841 toggle ^= (1 << kCache16Bits);
842#else
843 *dstC++ = cache[fi];
844#endif
845 } while (--count != 0);
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000846 } else if (proc == mirror_tileproc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000847 do {
848 unsigned fi = mirror_6bits(fx >> 10);
849 SkASSERT(fi <= 0x3F);
850 fx += dx;
851#ifdef TEST_GRADIENT_DITHER
852 *dstC++ = cache[toggle + fi];
853 toggle ^= (1 << kCache16Bits);
854#else
855 *dstC++ = cache[fi];
856#endif
857 } while (--count != 0);
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000858 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859 SkASSERT(proc == repeat_tileproc);
860 do {
861 unsigned fi = repeat_6bits(fx >> 10);
862 SkASSERT(fi <= 0x3F);
863 fx += dx;
864#ifdef TEST_GRADIENT_DITHER
865 *dstC++ = cache[toggle + fi];
866 toggle ^= (1 << kCache16Bits);
867#else
868 *dstC++ = cache[fi];
869#endif
870 } while (--count != 0);
871 }
reed@android.comb2c5f2d2009-06-12 20:09:24 +0000872 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000873 SkScalar dstX = SkIntToScalar(x);
874 SkScalar dstY = SkIntToScalar(y);
875 do {
876 dstProc(fDstToIndex, dstX, dstY, &srcPt);
877 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
878 SkASSERT(fi <= 0xFFFF);
879
880 int index = fi >> (16 - kCache16Bits);
881#ifdef TEST_GRADIENT_DITHER
882 *dstC++ = cache[toggle + index];
883 toggle ^= (1 << kCache16Bits);
884#else
885 *dstC++ = cache[index];
886#endif
887
888 dstX += SK_Scalar1;
889 } while (--count != 0);
890 }
891}
892
893///////////////////////////////////////////////////////////////////////////////
894
895#define kSQRT_TABLE_BITS 11
896#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
897
898#include "SkRadialGradient_Table.h"
899
900#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
901
902#include <stdio.h>
903
904void SkRadialGradient_BuildTable()
905{
906 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
907
908 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
909 SkASSERT(file);
910 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
911
912 for (int i = 0; i < kSQRT_TABLE_SIZE; i++)
913 {
914 if ((i & 15) == 0)
915 ::fprintf(file, "\t");
916
917 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
918
919 ::fprintf(file, "0x%02X", value);
920 if (i < kSQRT_TABLE_SIZE-1)
921 ::fprintf(file, ", ");
922 if ((i & 15) == 15)
923 ::fprintf(file, "\n");
924 }
925 ::fprintf(file, "};\n");
926 ::fclose(file);
927}
928
929#endif
930
931
932static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius, SkMatrix* matrix)
933{
934 SkScalar inv = SkScalarInvert(radius);
935
936 matrix->setTranslate(-center.fX, -center.fY);
937 matrix->postScale(inv, inv);
938}
939
940class Radial_Gradient : public Gradient_Shader {
941public:
942 Radial_Gradient(const SkPoint& center, SkScalar radius,
943 const SkColor colors[], const SkScalar pos[], int colorCount,
944 SkShader::TileMode mode, SkUnitMapper* mapper)
945 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
946 {
947 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
948 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
949
950 rad_to_unit_matrix(center, radius, &fPtsToUnit);
951 }
952 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
953 {
954 SkASSERT(count > 0);
955
956 SkPoint srcPt;
957 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
958 TileProc proc = fTileProc;
959 const SkPMColor* cache = this->getCache32();
960
961 if (fDstToIndexClass != kPerspective_MatrixClass)
962 {
963 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
964 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
965 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
966
967 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
968 {
969 SkFixed storage[2];
970 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
971 dx = storage[0];
972 dy = storage[1];
973 }
974 else
975 {
976 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
977 dx = SkScalarToFixed(fDstToIndex.getScaleX());
978 dy = SkScalarToFixed(fDstToIndex.getSkewY());
979 }
980
981 if (proc == clamp_tileproc)
982 {
983 const uint8_t* sqrt_table = gSqrt8Table;
984 fx >>= 1;
985 dx >>= 1;
986 fy >>= 1;
987 dy >>= 1;
988 do {
989 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
990 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
991 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
992 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
993 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache32Bits)];
994 fx += dx;
995 fy += dy;
996 } while (--count != 0);
997 }
998 else if (proc == mirror_tileproc)
999 {
1000 do {
1001 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1002 unsigned fi = mirror_tileproc(dist);
1003 SkASSERT(fi <= 0xFFFF);
1004 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1005 fx += dx;
1006 fy += dy;
1007 } while (--count != 0);
1008 }
1009 else
1010 {
1011 SkASSERT(proc == repeat_tileproc);
1012 do {
1013 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1014 unsigned fi = repeat_tileproc(dist);
1015 SkASSERT(fi <= 0xFFFF);
1016 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1017 fx += dx;
1018 fy += dy;
1019 } while (--count != 0);
1020 }
1021 }
1022 else // perspective case
1023 {
1024 SkScalar dstX = SkIntToScalar(x);
1025 SkScalar dstY = SkIntToScalar(y);
1026 do {
1027 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1028 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1029 SkASSERT(fi <= 0xFFFF);
1030 *dstC++ = cache[fi >> (16 - kCache32Bits)];
1031 dstX += SK_Scalar1;
1032 } while (--count != 0);
1033 }
1034 }
1035 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count)
1036 {
1037 SkASSERT(count > 0);
1038
1039 SkPoint srcPt;
1040 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1041 TileProc proc = fTileProc;
1042 const uint16_t* cache = this->getCache16();
1043#ifdef TEST_GRADIENT_DITHER
1044 int toggle = ((x ^ y) & 1) << kCache16Bits;
1045#endif
1046
1047 if (fDstToIndexClass != kPerspective_MatrixClass)
1048 {
1049 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
1050 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1051 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1052
1053 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1054 {
1055 SkFixed storage[2];
1056 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1057 dx = storage[0];
1058 dy = storage[1];
1059 }
1060 else
1061 {
1062 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1063 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1064 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1065 }
1066
1067 if (proc == clamp_tileproc)
1068 {
1069 const uint8_t* sqrt_table = gSqrt8Table;
1070
1071 /* knock these down so we can pin against +- 0x7FFF, which is an immediate load,
1072 rather than 0xFFFF which is slower. This is a compromise, since it reduces our
1073 precision, but that appears to be visually OK. If we decide this is OK for
1074 all of our cases, we could (it seems) put this scale-down into fDstToIndex,
1075 to avoid having to do these extra shifts each time.
1076 */
1077 fx >>= 1;
1078 dx >>= 1;
1079 fy >>= 1;
1080 dy >>= 1;
1081 if (dy == 0) // might perform this check for the other modes, but the win will be a smaller % of the total
1082 {
1083 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1084 fy *= fy;
1085 do {
1086 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1087 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
1088 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1089 fx += dx;
1090#ifdef TEST_GRADIENT_DITHER
1091 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1092 toggle ^= (1 << kCache16Bits);
1093#else
1094 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache16Bits)];
1095#endif
1096 } while (--count != 0);
1097 }
1098 else
1099 {
1100 do {
1101 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1102 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1103 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1104 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1105 fx += dx;
1106 fy += dy;
1107#ifdef TEST_GRADIENT_DITHER
1108 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1109 toggle ^= (1 << kCache16Bits);
1110#else
1111 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache16Bits)];
1112#endif
1113 } while (--count != 0);
1114 }
1115 }
1116 else if (proc == mirror_tileproc)
1117 {
1118 do {
1119 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1120 unsigned fi = mirror_tileproc(dist);
1121 SkASSERT(fi <= 0xFFFF);
1122 fx += dx;
1123 fy += dy;
1124#ifdef TEST_GRADIENT_DITHER
1125 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1126 toggle ^= (1 << kCache16Bits);
1127#else
1128 *dstC++ = cache[fi >> (16 - kCache16Bits)];
1129#endif
1130 } while (--count != 0);
1131 }
1132 else
1133 {
1134 SkASSERT(proc == repeat_tileproc);
1135 do {
1136 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1137 unsigned fi = repeat_tileproc(dist);
1138 SkASSERT(fi <= 0xFFFF);
1139 fx += dx;
1140 fy += dy;
1141#ifdef TEST_GRADIENT_DITHER
1142 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1143 toggle ^= (1 << kCache16Bits);
1144#else
1145 *dstC++ = cache[fi >> (16 - kCache16Bits)];
1146#endif
1147 } while (--count != 0);
1148 }
1149 }
1150 else // perspective case
1151 {
1152 SkScalar dstX = SkIntToScalar(x);
1153 SkScalar dstY = SkIntToScalar(y);
1154 do {
1155 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1156 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1157 SkASSERT(fi <= 0xFFFF);
1158
1159 int index = fi >> (16 - kCache16Bits);
1160#ifdef TEST_GRADIENT_DITHER
1161 *dstC++ = cache[toggle + index];
1162 toggle ^= (1 << kCache16Bits);
1163#else
1164 *dstC++ = cache[index];
1165#endif
1166
1167 dstX += SK_Scalar1;
1168 } while (--count != 0);
1169 }
1170 }
1171
1172 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1173 return SkNEW_ARGS(Radial_Gradient, (buffer));
1174 }
1175
1176protected:
1177 Radial_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {};
1178 virtual Factory getFactory() { return CreateProc; }
reed@android.comec3d6e52009-06-05 14:43:55 +00001179 virtual void onCacheReset() {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180
1181private:
1182 typedef Gradient_Shader INHERITED;
1183};
1184
1185///////////////////////////////////////////////////////////////////////////////
1186
1187class Sweep_Gradient : public Gradient_Shader {
1188public:
1189 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
1190 const SkScalar pos[], int count, SkUnitMapper* mapper)
1191 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper)
1192 {
1193 fPtsToUnit.setTranslate(-cx, -cy);
1194 }
1195 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
1196 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
1197
1198 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1199 return SkNEW_ARGS(Sweep_Gradient, (buffer));
1200 }
1201
1202protected:
1203 Sweep_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204 virtual Factory getFactory() { return CreateProc; }
reed@android.comec3d6e52009-06-05 14:43:55 +00001205 virtual void onCacheReset() {}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206
1207private:
1208 typedef Gradient_Shader INHERITED;
1209};
1210
1211#ifdef COMPUTE_SWEEP_TABLE
1212#define PI 3.14159265
1213static bool gSweepTableReady;
1214static uint8_t gSweepTable[65];
1215
1216/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
1217 We scale the results to [0..32]
1218*/
1219static const uint8_t* build_sweep_table()
1220{
1221 if (!gSweepTableReady)
1222 {
1223 const int N = 65;
1224 const double DENOM = N - 1;
1225
1226 for (int i = 0; i < N; i++)
1227 {
1228 double arg = i / DENOM;
1229 double v = atan(arg);
1230 int iv = (int)round(v * DENOM * 2 / PI);
1231// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
1232 printf("%d, ", iv);
1233 gSweepTable[i] = iv;
1234 }
1235 gSweepTableReady = true;
1236 }
1237 return gSweepTable;
1238}
1239#else
1240static const uint8_t gSweepTable[] = {
1241 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
1242 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
1243 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
1244 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
1245 32
1246};
1247static const uint8_t* build_sweep_table() { return gSweepTable; }
1248#endif
1249
1250// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
1251// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
1252// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
1253
1254//unsigned div_64(int numer, int denom);
1255static unsigned div_64(int numer, int denom)
1256{
1257 SkASSERT(numer <= denom);
1258 SkASSERT(numer > 0);
1259 SkASSERT(denom > 0);
1260
1261 int nbits = SkCLZ(numer);
1262 int dbits = SkCLZ(denom);
1263 int bits = 6 - nbits + dbits;
1264 SkASSERT(bits <= 6);
1265
1266 if (bits < 0) // detect underflow
1267 return 0;
1268
1269 denom <<= dbits - 1;
1270 numer <<= nbits - 1;
1271
1272 unsigned result = 0;
1273
1274 // do the first one
1275 if ((numer -= denom) >= 0)
1276 result = 1;
1277 else
1278 numer += denom;
1279
1280 // Now fall into our switch statement if there are more bits to compute
1281 if (bits > 0)
1282 {
1283 // make room for the rest of the answer bits
1284 result <<= bits;
1285 switch (bits) {
1286 case 6:
1287 if ((numer = (numer << 1) - denom) >= 0)
1288 result |= 32;
1289 else
1290 numer += denom;
1291 case 5:
1292 if ((numer = (numer << 1) - denom) >= 0)
1293 result |= 16;
1294 else
1295 numer += denom;
1296 case 4:
1297 if ((numer = (numer << 1) - denom) >= 0)
1298 result |= 8;
1299 else
1300 numer += denom;
1301 case 3:
1302 if ((numer = (numer << 1) - denom) >= 0)
1303 result |= 4;
1304 else
1305 numer += denom;
1306 case 2:
1307 if ((numer = (numer << 1) - denom) >= 0)
1308 result |= 2;
1309 else
1310 numer += denom;
1311 case 1:
1312 default: // not strictly need, but makes GCC make better ARM code
1313 if ((numer = (numer << 1) - denom) >= 0)
1314 result |= 1;
1315 else
1316 numer += denom;
1317 }
1318 }
1319 return result;
1320}
1321
1322// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
1323static unsigned atan_0_90(SkFixed y, SkFixed x)
1324{
1325#ifdef SK_DEBUG
1326 {
1327 static bool gOnce;
1328 if (!gOnce)
1329 {
1330 gOnce = true;
1331 SkASSERT(div_64(55, 55) == 64);
1332 SkASSERT(div_64(128, 256) == 32);
1333 SkASSERT(div_64(2326528, 4685824) == 31);
1334 SkASSERT(div_64(753664, 5210112) == 9);
1335 SkASSERT(div_64(229376, 4882432) == 3);
1336 SkASSERT(div_64(2, 64) == 2);
1337 SkASSERT(div_64(1, 64) == 1);
1338 // test that we handle underflow correctly
1339 SkASSERT(div_64(12345, 0x54321234) == 0);
1340 }
1341 }
1342#endif
1343
1344 SkASSERT(y > 0 && x > 0);
1345 const uint8_t* table = build_sweep_table();
1346
1347 unsigned result;
1348 bool swap = (x < y);
1349 if (swap)
1350 {
1351 // first part of the atan(v) = PI/2 - atan(1/v) identity
1352 // since our div_64 and table want v <= 1, where v = y/x
1353 SkTSwap<SkFixed>(x, y);
1354 }
1355
1356 result = div_64(y, x);
1357
1358#ifdef SK_DEBUG
1359 {
1360 unsigned result2 = SkDivBits(y, x, 6);
1361 SkASSERT(result2 == result ||
1362 (result == 1 && result2 == 0));
1363 }
1364#endif
1365
1366 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
1367 result = table[result];
1368
1369 if (swap)
1370 {
1371 // complete the atan(v) = PI/2 - atan(1/v) identity
1372 result = 64 - result;
1373 // pin to 63
1374 result -= result >> 6;
1375 }
1376
1377 SkASSERT(result <= 63);
1378 return result;
1379}
1380
1381// returns angle in a circle [0..2PI) -> [0..255]
1382static unsigned SkATan2_255(SkFixed y, SkFixed x)
1383{
1384 if (x == 0)
1385 {
1386 if (y == 0)
1387 return 0;
1388 return y < 0 ? 192 : 64;
1389 }
1390 if (y == 0)
1391 return x < 0 ? 128 : 0;
1392
1393 /* Find the right quadrant for x,y
1394 Since atan_0_90 only handles the first quadrant, we rotate x,y
1395 appropriately before calling it, and then add the right amount
1396 to account for the real quadrant.
1397 quadrant 0 : add 0 | x > 0 && y > 0
1398 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
1399 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
1400 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
1401
1402 map x<0 to (1 << 6)
1403 map y<0 to (3 << 6)
1404 add = map_x ^ map_y
1405 */
1406 int xsign = x >> 31;
1407 int ysign = y >> 31;
1408 int add = ((-xsign) ^ (ysign & 3)) << 6;
1409
1410#ifdef SK_DEBUG
1411 if (0 == add)
1412 SkASSERT(x > 0 && y > 0);
1413 else if (64 == add)
1414 SkASSERT(x < 0 && y > 0);
1415 else if (128 == add)
1416 SkASSERT(x < 0 && y < 0);
1417 else if (192 == add)
1418 SkASSERT(x > 0 && y < 0);
1419 else
1420 SkASSERT(!"bad value for add");
1421#endif
1422
1423 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
1424 where we need to rotate x,y by 90 or -90
1425 */
1426 x = (x ^ xsign) - xsign;
1427 y = (y ^ ysign) - ysign;
1428 if (add & 64) // quads 1 or 3 need to swap x,y
1429 SkTSwap<SkFixed>(x, y);
1430
1431 unsigned result = add + atan_0_90(y, x);
1432 SkASSERT(result < 256);
1433 return result;
1434}
1435
1436void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
1437{
1438 SkMatrix::MapXYProc proc = fDstToIndexProc;
1439 const SkMatrix& matrix = fDstToIndex;
1440 const SkPMColor* cache = this->getCache32();
1441 SkPoint srcPt;
1442
1443 if (fDstToIndexClass != kPerspective_MatrixClass)
1444 {
1445 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1446 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1447 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1448 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1449
1450 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1451 {
1452 SkFixed storage[2];
1453 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1454 &storage[0], &storage[1]);
1455 dx = storage[0];
1456 dy = storage[1];
1457 }
1458 else
1459 {
1460 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1461 dx = SkScalarToFixed(matrix.getScaleX());
1462 dy = SkScalarToFixed(matrix.getSkewY());
1463 }
1464
1465 for (; count > 0; --count)
1466 {
1467 *dstC++ = cache[SkATan2_255(fy, fx)];
1468 fx += dx;
1469 fy += dy;
1470 }
1471 }
1472 else // perspective case
1473 {
1474 for (int stop = x + count; x < stop; x++)
1475 {
1476 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1477 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1478
1479 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1480 SkScalarToFixed(srcPt.fX));
1481 *dstC++ = cache[index];
1482 }
1483 }
1484}
1485
1486void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
1487{
1488 SkMatrix::MapXYProc proc = fDstToIndexProc;
1489 const SkMatrix& matrix = fDstToIndex;
1490 const uint16_t* cache = this->getCache16();
1491 int toggle = ((x ^ y) & 1) << kCache16Bits;
1492 SkPoint srcPt;
1493
1494 if (fDstToIndexClass != kPerspective_MatrixClass)
1495 {
1496 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1497 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1498 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1499 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1500
1501 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1502 {
1503 SkFixed storage[2];
1504 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1505 &storage[0], &storage[1]);
1506 dx = storage[0];
1507 dy = storage[1];
1508 }
1509 else
1510 {
1511 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1512 dx = SkScalarToFixed(matrix.getScaleX());
1513 dy = SkScalarToFixed(matrix.getSkewY());
1514 }
1515
1516 for (; count > 0; --count)
1517 {
1518 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
1519 *dstC++ = cache[toggle + index];
1520 toggle ^= (1 << kCache16Bits);
1521 fx += dx;
1522 fy += dy;
1523 }
1524 }
1525 else // perspective case
1526 {
1527 for (int stop = x + count; x < stop; x++)
1528 {
1529 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1530 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1531
1532 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1533 SkScalarToFixed(srcPt.fX));
1534 index >>= (8 - kCache16Bits);
1535 *dstC++ = cache[toggle + index];
1536 toggle ^= (1 << kCache16Bits);
1537 }
1538 }
1539}
1540
1541///////////////////////////////////////////////////////////////////////////
1542///////////////////////////////////////////////////////////////////////////
1543
1544// assumes colors is SkColor* and pos is SkScalar*
1545#define EXPAND_1_COLOR(count) \
1546 SkColor tmp[2]; \
1547 do { \
1548 if (1 == count) { \
1549 tmp[0] = tmp[1] = colors[0]; \
1550 colors = tmp; \
1551 pos = NULL; \
1552 count = 2; \
1553 } \
1554 } while (0)
1555
1556SkShader* SkGradientShader::CreateLinear( const SkPoint pts[2],
1557 const SkColor colors[], const SkScalar pos[], int colorCount,
1558 SkShader::TileMode mode, SkUnitMapper* mapper)
1559{
1560 if (NULL == pts || NULL == colors || colorCount < 1) {
1561 return NULL;
1562 }
1563 EXPAND_1_COLOR(colorCount);
1564
reed@android.comab840b82009-07-01 17:00:03 +00001565 return SkNEW_ARGS(Linear_Gradient,
1566 (pts, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001567}
1568
1569SkShader* SkGradientShader::CreateRadial( const SkPoint& center, SkScalar radius,
1570 const SkColor colors[], const SkScalar pos[], int colorCount,
1571 SkShader::TileMode mode, SkUnitMapper* mapper)
1572{
1573 if (radius <= 0 || NULL == colors || colorCount < 1) {
1574 return NULL;
1575 }
1576 EXPAND_1_COLOR(colorCount);
1577
reed@android.comab840b82009-07-01 17:00:03 +00001578 return SkNEW_ARGS(Radial_Gradient,
1579 (center, radius, colors, pos, colorCount, mode, mapper));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001580}
1581
1582SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
1583 const SkColor colors[],
1584 const SkScalar pos[],
1585 int count, SkUnitMapper* mapper)
1586{
1587 if (NULL == colors || count < 1) {
1588 return NULL;
1589 }
1590 EXPAND_1_COLOR(count);
1591
1592 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
1593}
1594
1595static SkFlattenable::Registrar gLinearGradientReg("Linear_Gradient",
1596 Linear_Gradient::CreateProc);
1597
1598static SkFlattenable::Registrar gRadialGradientReg("Radial_Gradient",
1599 Radial_Gradient::CreateProc);
1600
1601static SkFlattenable::Registrar gSweepGradientReg("Sweep_Gradient",
1602 Sweep_Gradient::CreateProc);
1603