1 /* 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
25 typedef SkFixed (*TileProc)(SkFixed);
26
clamp_tileproc(SkFixed x)27 static SkFixed clamp_tileproc(SkFixed x) {
28 return SkClampMax(x, 0xFFFF);
29 }
30
repeat_tileproc(SkFixed x)31 static SkFixed repeat_tileproc(SkFixed x) {
32 return x & 0xFFFF;
33 }
34
mirror_tileproc(SkFixed x)35 static inline SkFixed mirror_tileproc(SkFixed x) {
36 int s = x << 15 >> 31;
37 return (x ^ s) & 0xFFFF;
38 }
39
40 static const TileProc gTileProcs[] = {
41 clamp_tileproc,
42 repeat_tileproc,
43 mirror_tileproc
44 };
45
46 //////////////////////////////////////////////////////////////////////////////
47
repeat_6bits(int x)48 static inline int repeat_6bits(int x) {
49 return x & 63;
50 }
51
mirror_6bits(int x)52 static inline int mirror_6bits(int x) {
53 #ifdef SK_CPU_HAS_CONDITIONAL_INSTR
54 if (x & 64)
55 x = ~x;
56 return x & 63;
57 #else
58 int s = x << 25 >> 31;
59 return (x ^ s) & 63;
60 #endif
61 }
62
repeat_8bits(int x)63 static inline int repeat_8bits(int x) {
64 return x & 0xFF;
65 }
66
mirror_8bits(int x)67 static inline int mirror_8bits(int x) {
68 #ifdef SK_CPU_HAS_CONDITIONAL_INSTR
69 if (x & 256) {
70 x = ~x;
71 }
72 return x & 255;
73 #else
74 int s = x << 23 >> 31;
75 return (x ^ s) & 0xFF;
76 #endif
77 }
78
79 //////////////////////////////////////////////////////////////////////////////
80
81 class Gradient_Shader : public SkShader {
82 public:
83 Gradient_Shader(const SkColor colors[], const SkScalar pos[],
84 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
85 virtual ~Gradient_Shader();
86
87 // overrides
88 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
getFlags()89 virtual uint32_t getFlags() { return fFlags; }
90
91 protected:
92 Gradient_Shader(SkFlattenableReadBuffer& );
93 SkUnitMapper* fMapper;
94 SkMatrix fPtsToUnit; // set by subclass
95 SkMatrix fDstToIndex;
96 SkMatrix::MapXYProc fDstToIndexProc;
97 TileMode fTileMode;
98 TileProc fTileProc;
99 int fColorCount;
100 uint8_t fDstToIndexClass;
101 uint8_t fFlags;
102
103 struct Rec {
104 SkFixed fPos; // 0...1
105 uint32_t fScale; // (1 << 24) / range
106 };
107 Rec* fRecs;
108
109 enum {
110 kCache16Bits = 6, // seems like enough for visual accuracy
111 kCache16Count = 1 << kCache16Bits,
112 kCache32Bits = 8, // pretty much should always be 8
113 kCache32Count = 1 << kCache32Bits
114 };
115 virtual void flatten(SkFlattenableWriteBuffer& );
116 const uint16_t* getCache16();
117 const SkPMColor* getCache32();
118
119 // called when we kill our cached colors (to be rebuilt later on demand)
120 virtual void onCacheReset() = 0;
121
122 private:
123 enum {
124 kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
125
126 kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
127 };
128 SkColor fStorage[(kStorageSize + 3) >> 2];
129 SkColor* fOrigColors;
130
131 uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
132 SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
133
134 uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
135 SkPMColor* fCache32Storage; // storage for fCache32, allocated on demand
136 unsigned fCacheAlpha; // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value
137
138 typedef SkShader INHERITED;
139 };
140
scalarToU16(SkScalar x)141 static inline unsigned scalarToU16(SkScalar x) {
142 SkASSERT(x >= 0 && x <= SK_Scalar1);
143
144 #ifdef SK_SCALAR_IS_FLOAT
145 return (unsigned)(x * 0xFFFF);
146 #else
147 return x - (x >> 16); // probably should be x - (x > 0x7FFF) but that is slower
148 #endif
149 }
150
Gradient_Shader(const SkColor colors[],const SkScalar pos[],int colorCount,SkShader::TileMode mode,SkUnitMapper * mapper)151 Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[],
152 int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) {
153 SkASSERT(colorCount > 1);
154
155 fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return
156
157 fMapper = mapper;
158 mapper->safeRef();
159
160 SkASSERT((unsigned)mode < SkShader::kTileModeCount);
161 SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
162 fTileMode = mode;
163 fTileProc = gTileProcs[mode];
164
165 fCache16 = fCache16Storage = NULL;
166 fCache32 = fCache32Storage = NULL;
167
168 /* Note: we let the caller skip the first and/or last position.
169 i.e. pos[0] = 0.3, pos[1] = 0.7
170 In these cases, we insert dummy entries to ensure that the final data
171 will be bracketed by [0, 1].
172 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
173
174 Thus colorCount (the caller's value, and fColorCount (our value) may
175 differ by up to 2. In the above example:
176 colorCount = 2
177 fColorCount = 4
178 */
179 fColorCount = colorCount;
180 // check if we need to add in dummy start and/or end position/colors
181 bool dummyFirst = false;
182 bool dummyLast = false;
183 if (pos) {
184 dummyFirst = pos[0] != 0;
185 dummyLast = pos[colorCount - 1] != SK_Scalar1;
186 fColorCount += dummyFirst + dummyLast;
187 }
188
189 if (fColorCount > kColorStorageCount) {
190 size_t size = sizeof(SkColor) + sizeof(Rec);
191 fOrigColors = reinterpret_cast<SkColor*>(
192 sk_malloc_throw(size * fColorCount));
193 }
194 else {
195 fOrigColors = fStorage;
196 }
197
198 // Now copy over the colors, adding the dummies as needed
199 {
200 SkColor* origColors = fOrigColors;
201 if (dummyFirst) {
202 *origColors++ = colors[0];
203 }
204 memcpy(origColors, colors, colorCount * sizeof(SkColor));
205 if (dummyLast) {
206 origColors += colorCount;
207 *origColors = colors[colorCount - 1];
208 }
209 }
210
211 fRecs = (Rec*)(fOrigColors + fColorCount);
212 if (fColorCount > 2) {
213 Rec* recs = fRecs;
214 recs->fPos = 0;
215 // recs->fScale = 0; // unused;
216 recs += 1;
217 if (pos) {
218 /* We need to convert the user's array of relative positions into
219 fixed-point positions and scale factors. We need these results
220 to be strictly monotonic (no two values equal or out of order).
221 Hence this complex loop that just jams a zero for the scale
222 value if it sees a segment out of order, and it assures that
223 we start at 0 and end at 1.0
224 */
225 SkFixed prev = 0;
226 int startIndex = dummyFirst ? 0 : 1;
227 int count = colorCount + dummyLast;
228 for (int i = startIndex; i < count; i++) {
229 // force the last value to be 1.0
230 SkFixed curr;
231 if (i == colorCount) { // we're really at the dummyLast
232 curr = SK_Fixed1;
233 } else {
234 curr = SkScalarToFixed(pos[i]);
235 }
236 // pin curr withing range
237 if (curr < 0) {
238 curr = 0;
239 } else if (curr > SK_Fixed1) {
240 curr = SK_Fixed1;
241 }
242 recs->fPos = curr;
243 if (curr > prev) {
244 recs->fScale = (1 << 24) / (curr - prev);
245 } else {
246 recs->fScale = 0; // ignore this segment
247 }
248 // get ready for the next value
249 prev = curr;
250 recs += 1;
251 }
252 } else { // assume even distribution
253 SkFixed dp = SK_Fixed1 / (colorCount - 1);
254 SkFixed p = dp;
255 SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp
256 for (int i = 1; i < colorCount; i++) {
257 recs->fPos = p;
258 recs->fScale = scale;
259 recs += 1;
260 p += dp;
261 }
262 }
263 }
264 fFlags = 0;
265 }
266
Gradient_Shader(SkFlattenableReadBuffer & buffer)267 Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
268 INHERITED(buffer) {
269 fCacheAlpha = 256;
270
271 fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
272
273 fCache16 = fCache16Storage = NULL;
274 fCache32 = fCache32Storage = NULL;
275
276 int colorCount = fColorCount = buffer.readU32();
277 if (colorCount > kColorStorageCount) {
278 size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
279 fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
280 } else {
281 fOrigColors = fStorage;
282 }
283 buffer.read(fOrigColors, colorCount * sizeof(SkColor));
284
285 fTileMode = (TileMode)buffer.readU8();
286 fTileProc = gTileProcs[fTileMode];
287 fRecs = (Rec*)(fOrigColors + colorCount);
288 if (colorCount > 2) {
289 Rec* recs = fRecs;
290 recs[0].fPos = 0;
291 for (int i = 1; i < colorCount; i++) {
292 recs[i].fPos = buffer.readS32();
293 recs[i].fScale = buffer.readU32();
294 }
295 }
296 buffer.read(&fPtsToUnit, sizeof(SkMatrix));
297 fFlags = 0;
298 }
299
~Gradient_Shader()300 Gradient_Shader::~Gradient_Shader() {
301 if (fCache16Storage) {
302 sk_free(fCache16Storage);
303 }
304 if (fCache32Storage) {
305 sk_free(fCache32Storage);
306 }
307 if (fOrigColors != fStorage) {
308 sk_free(fOrigColors);
309 }
310 fMapper->safeUnref();
311 }
312
flatten(SkFlattenableWriteBuffer & buffer)313 void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) {
314 this->INHERITED::flatten(buffer);
315 buffer.writeFlattenable(fMapper);
316 buffer.write32(fColorCount);
317 buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
318 buffer.write8(fTileMode);
319 if (fColorCount > 2) {
320 Rec* recs = fRecs;
321 for (int i = 1; i < fColorCount; i++) {
322 buffer.write32(recs[i].fPos);
323 buffer.write32(recs[i].fScale);
324 }
325 }
326 buffer.writeMul4(&fPtsToUnit, sizeof(SkMatrix));
327 }
328
setContext(const SkBitmap & device,const SkPaint & paint,const SkMatrix & matrix)329 bool Gradient_Shader::setContext(const SkBitmap& device,
330 const SkPaint& paint,
331 const SkMatrix& matrix) {
332 if (!this->INHERITED::setContext(device, paint, matrix)) {
333 return false;
334 }
335
336 const SkMatrix& inverse = this->getTotalInverse();
337
338 if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
339 return false;
340 }
341
342 fDstToIndexProc = fDstToIndex.getMapXYProc();
343 fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
344
345 // now convert our colors in to PMColors
346 unsigned paintAlpha = this->getPaintAlpha();
347 unsigned colorAlpha = 0xFF;
348
349 // FIXME: record colorAlpha in constructor, since this is not affected
350 // by setContext()
351 for (int i = 0; i < fColorCount; i++) {
352 SkColor src = fOrigColors[i];
353 unsigned sa = SkColorGetA(src);
354 colorAlpha &= sa;
355 }
356
357 fFlags = this->INHERITED::getFlags();
358 if ((colorAlpha & paintAlpha) == 0xFF) {
359 fFlags |= kOpaqueAlpha_Flag;
360 }
361 // we can do span16 as long as our individual colors are opaque,
362 // regardless of the paint's alpha
363 if (0xFF == colorAlpha) {
364 fFlags |= kHasSpan16_Flag;
365 }
366
367 // if the new alpha differs from the previous time we were called, inval our cache
368 // this will trigger the cache to be rebuilt.
369 // we don't care about the first time, since the cache ptrs will already be NULL
370 if (fCacheAlpha != paintAlpha) {
371 fCache16 = NULL; // inval the cache
372 fCache32 = NULL; // inval the cache
373 fCacheAlpha = paintAlpha; // record the new alpha
374 // inform our subclasses
375 this->onCacheReset();
376 }
377 return true;
378 }
379
blend8(int a,int b,int scale)380 static inline int blend8(int a, int b, int scale) {
381 SkASSERT(a == SkToU8(a));
382 SkASSERT(b == SkToU8(b));
383 SkASSERT(scale >= 0 && scale <= 256);
384 return a + ((b - a) * scale >> 8);
385 }
386
dot8_blend_packed32(uint32_t s0,uint32_t s1,int blend)387 static inline uint32_t dot8_blend_packed32(uint32_t s0, uint32_t s1,
388 int blend) {
389 #if 0
390 int a = blend8(SkGetPackedA32(s0), SkGetPackedA32(s1), blend);
391 int r = blend8(SkGetPackedR32(s0), SkGetPackedR32(s1), blend);
392 int g = blend8(SkGetPackedG32(s0), SkGetPackedG32(s1), blend);
393 int b = blend8(SkGetPackedB32(s0), SkGetPackedB32(s1), blend);
394
395 return SkPackARGB32(a, r, g, b);
396 #else
397 int otherBlend = 256 - blend;
398
399 #if 0
400 U32 t0 = (((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF;
401 U32 t1 = (((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00;
402 SkASSERT((t0 & t1) == 0);
403 return t0 | t1;
404 #else
405 return ((((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF) |
406 ((((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00);
407 #endif
408
409 #endif
410 }
411
412 #define Fixed_To_Dot8(x) (((x) + 0x80) >> 8)
413
414 /** We take the original colors, not our premultiplied PMColors, since we can
415 build a 16bit table as long as the original colors are opaque, even if the
416 paint specifies a non-opaque alpha.
417 */
build_16bit_cache(uint16_t cache[],SkColor c0,SkColor c1,int count)418 static void build_16bit_cache(uint16_t cache[], SkColor c0, SkColor c1,
419 int count) {
420 SkASSERT(count > 1);
421 SkASSERT(SkColorGetA(c0) == 0xFF);
422 SkASSERT(SkColorGetA(c1) == 0xFF);
423
424 SkFixed r = SkColorGetR(c0);
425 SkFixed g = SkColorGetG(c0);
426 SkFixed b = SkColorGetB(c0);
427
428 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
429 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
430 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
431
432 r = SkIntToFixed(r) + 0x8000;
433 g = SkIntToFixed(g) + 0x8000;
434 b = SkIntToFixed(b) + 0x8000;
435
436 do {
437 unsigned rr = r >> 16;
438 unsigned gg = g >> 16;
439 unsigned bb = b >> 16;
440 cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
441 cache[64] = SkDitherPack888ToRGB16(rr, gg, bb);
442 cache += 1;
443 r += dr;
444 g += dg;
445 b += db;
446 } while (--count != 0);
447 }
448
build_32bit_cache(SkPMColor cache[],SkColor c0,SkColor c1,int count,U8CPU paintAlpha)449 static void build_32bit_cache(SkPMColor cache[], SkColor c0, SkColor c1,
450 int count, U8CPU paintAlpha) {
451 SkASSERT(count > 1);
452
453 // need to apply paintAlpha to our two endpoints
454 SkFixed a = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
455 SkFixed da;
456 {
457 int tmp = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
458 da = SkIntToFixed(tmp - a) / (count - 1);
459 }
460
461 SkFixed r = SkColorGetR(c0);
462 SkFixed g = SkColorGetG(c0);
463 SkFixed b = SkColorGetB(c0);
464 SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
465 SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
466 SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
467
468 a = SkIntToFixed(a) + 0x8000;
469 r = SkIntToFixed(r) + 0x8000;
470 g = SkIntToFixed(g) + 0x8000;
471 b = SkIntToFixed(b) + 0x8000;
472
473 do {
474 *cache++ = SkPreMultiplyARGB(a >> 16, r >> 16, g >> 16, b >> 16);
475 a += da;
476 r += dr;
477 g += dg;
478 b += db;
479 } while (--count != 0);
480 }
481
SkFixedToFFFF(SkFixed x)482 static inline int SkFixedToFFFF(SkFixed x) {
483 SkASSERT((unsigned)x <= SK_Fixed1);
484 return x - (x >> 16);
485 }
486
dot6to16(unsigned x)487 static inline U16CPU dot6to16(unsigned x) {
488 SkASSERT(x < 64);
489 return (x << 10) | (x << 4) | (x >> 2);
490 }
491
getCache16()492 const uint16_t* Gradient_Shader::getCache16() {
493 if (fCache16 == NULL) {
494 if (fCache16Storage == NULL) { // set the storage and our working ptr
495 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
496 }
497 fCache16 = fCache16Storage;
498 if (fColorCount == 2) {
499 build_16bit_cache(fCache16, fOrigColors[0], fOrigColors[1], kCache16Count);
500 } else {
501 Rec* rec = fRecs;
502 int prevIndex = 0;
503 for (int i = 1; i < fColorCount; i++) {
504 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache16Bits);
505 SkASSERT(nextIndex < kCache16Count);
506
507 if (nextIndex > prevIndex)
508 build_16bit_cache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
509 prevIndex = nextIndex;
510 }
511 SkASSERT(prevIndex == kCache16Count - 1);
512 }
513
514 if (fMapper) {
515 fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
516 uint16_t* linear = fCache16; // just computed linear data
517 uint16_t* mapped = fCache16Storage; // storage for mapped data
518 SkUnitMapper* map = fMapper;
519 for (int i = 0; i < 64; i++) {
520 int index = map->mapUnit16(dot6to16(i)) >> 10;
521 mapped[i] = linear[index];
522 mapped[i + 64] = linear[index + 64];
523 }
524 sk_free(fCache16);
525 fCache16 = fCache16Storage;
526 }
527 }
528 return fCache16;
529 }
530
getCache32()531 const SkPMColor* Gradient_Shader::getCache32() {
532 if (fCache32 == NULL) {
533 if (fCache32Storage == NULL) // set the storage and our working ptr
534 fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count);
535
536 fCache32 = fCache32Storage;
537 if (fColorCount == 2) {
538 build_32bit_cache(fCache32, fOrigColors[0], fOrigColors[1],
539 kCache32Count, fCacheAlpha);
540 } else {
541 Rec* rec = fRecs;
542 int prevIndex = 0;
543 for (int i = 1; i < fColorCount; i++) {
544 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache32Bits);
545 SkASSERT(nextIndex < kCache32Count);
546
547 if (nextIndex > prevIndex)
548 build_32bit_cache(fCache32 + prevIndex, fOrigColors[i-1],
549 fOrigColors[i],
550 nextIndex - prevIndex + 1, fCacheAlpha);
551 prevIndex = nextIndex;
552 }
553 SkASSERT(prevIndex == kCache32Count - 1);
554 }
555
556 if (fMapper) {
557 fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count);
558 SkPMColor* linear = fCache32; // just computed linear data
559 SkPMColor* mapped = fCache32Storage; // storage for mapped data
560 SkUnitMapper* map = fMapper;
561 for (int i = 0; i < 256; i++) {
562 mapped[i] = linear[map->mapUnit16((i << 8) | i) >> 8];
563 }
564 sk_free(fCache32);
565 fCache32 = fCache32Storage;
566 }
567 }
568 return fCache32;
569 }
570
571 ///////////////////////////////////////////////////////////////////////////
572
pts_to_unit_matrix(const SkPoint pts[2],SkMatrix * matrix)573 static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
574 SkVector vec = pts[1] - pts[0];
575 SkScalar mag = vec.length();
576 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
577
578 vec.scale(inv);
579 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
580 matrix->postTranslate(-pts[0].fX, -pts[0].fY);
581 matrix->postScale(inv, inv);
582 }
583
584 ///////////////////////////////////////////////////////////////////////////////
585
586 class Linear_Gradient : public Gradient_Shader {
587 public:
Linear_Gradient(const SkPoint pts[2],const SkColor colors[],const SkScalar pos[],int colorCount,SkShader::TileMode mode,SkUnitMapper * mapper)588 Linear_Gradient(const SkPoint pts[2],
589 const SkColor colors[], const SkScalar pos[], int colorCount,
590 SkShader::TileMode mode, SkUnitMapper* mapper)
591 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
592 {
593 fCachedBitmap = NULL;
594 pts_to_unit_matrix(pts, &fPtsToUnit);
595 }
~Linear_Gradient()596 virtual ~Linear_Gradient() {
597 if (fCachedBitmap) {
598 SkDELETE(fCachedBitmap);
599 }
600 }
601
602 virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
603 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
604 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
605 virtual bool asABitmap(SkBitmap*, SkMatrix*, TileMode*);
onCacheReset()606 virtual void onCacheReset() {
607 if (fCachedBitmap) {
608 SkDELETE(fCachedBitmap);
609 fCachedBitmap = NULL;
610 }
611 }
612
CreateProc(SkFlattenableReadBuffer & buffer)613 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
614 return SkNEW_ARGS(Linear_Gradient, (buffer));
615 }
616
617 protected:
Linear_Gradient(SkFlattenableReadBuffer & buffer)618 Linear_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {
619 fCachedBitmap = NULL;
620 }
getFactory()621 virtual Factory getFactory() { return CreateProc; }
622
623 private:
624 SkBitmap* fCachedBitmap; // allocated on demand
625
626 typedef Gradient_Shader INHERITED;
627 };
628
setContext(const SkBitmap & device,const SkPaint & paint,const SkMatrix & matrix)629 bool Linear_Gradient::setContext(const SkBitmap& device, const SkPaint& paint,
630 const SkMatrix& matrix) {
631 if (!this->INHERITED::setContext(device, paint, matrix)) {
632 return false;
633 }
634
635 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
636 if ((fDstToIndex.getType() & ~mask) == 0) {
637 fFlags |= SkShader::kConstInY32_Flag;
638 if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
639 // only claim this if we do have a 16bit mode (i.e. none of our
640 // colors have alpha), and if we are not dithering (which obviously
641 // is not const in Y).
642 fFlags |= SkShader::kConstInY16_Flag;
643 }
644 }
645 return true;
646 }
647
648 // Return true if fx, fx+dx, fx+2*dx, ... is always in range
no_need_for_clamp(int fx,int dx,int count)649 static inline bool no_need_for_clamp(int fx, int dx, int count)
650 {
651 SkASSERT(count > 0);
652 return (unsigned)((fx | (fx + (count - 1) * dx)) >> 8) <= 0xFF;
653 }
654
shadeSpan(int x,int y,SkPMColor dstC[],int count)655 void Linear_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
656 {
657 SkASSERT(count > 0);
658
659 SkPoint srcPt;
660 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
661 TileProc proc = fTileProc;
662 const SkPMColor* cache = this->getCache32();
663
664 if (fDstToIndexClass != kPerspective_MatrixClass) {
665 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
666 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
667 // preround fx by half the amount we throw away
668 fx += 1 << 7;
669
670 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
671 SkFixed dxStorage[1];
672 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
673 dx = dxStorage[0];
674 } else {
675 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
676 dx = SkScalarToFixed(fDstToIndex.getScaleX());
677 }
678
679 if (SkFixedNearlyZero(dx)) {
680 // we're a vertical gradient, so no change in a span
681 unsigned fi = proc(fx);
682 SkASSERT(fi <= 0xFFFF);
683 sk_memset32(dstC, cache[fi >> (16 - kCache32Bits)], count);
684 } else if (proc == clamp_tileproc) {
685 #if 0
686 if (no_need_for_clamp(fx, dx, count))
687 {
688 unsigned fi;
689 while ((count -= 4) >= 0)
690 {
691 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
692 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
693 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
694 fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
695 }
696 SkASSERT(count <= -1 && count >= -4);
697 count += 4;
698 while (--count >= 0)
699 {
700 fi = fx >> 8;
701 SkASSERT(fi <= 0xFF);
702 fx += dx;
703 *dstC++ = cache[fi];
704 }
705 }
706 else
707 #endif
708 do {
709 unsigned fi = SkClampMax(fx >> 8, 0xFF);
710 SkASSERT(fi <= 0xFF);
711 fx += dx;
712 *dstC++ = cache[fi];
713 } while (--count != 0);
714 } else if (proc == mirror_tileproc) {
715 do {
716 unsigned fi = mirror_8bits(fx >> 8);
717 SkASSERT(fi <= 0xFF);
718 fx += dx;
719 *dstC++ = cache[fi];
720 } while (--count != 0);
721 } else {
722 SkASSERT(proc == repeat_tileproc);
723 do {
724 unsigned fi = repeat_8bits(fx >> 8);
725 SkASSERT(fi <= 0xFF);
726 fx += dx;
727 *dstC++ = cache[fi];
728 } while (--count != 0);
729 }
730 } else {
731 SkScalar dstX = SkIntToScalar(x);
732 SkScalar dstY = SkIntToScalar(y);
733 do {
734 dstProc(fDstToIndex, dstX, dstY, &srcPt);
735 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
736 SkASSERT(fi <= 0xFFFF);
737 *dstC++ = cache[fi >> (16 - kCache32Bits)];
738 dstX += SK_Scalar1;
739 } while (--count != 0);
740 }
741 }
742
asABitmap(SkBitmap * bitmap,SkMatrix * matrix,TileMode xy[])743 bool Linear_Gradient::asABitmap(SkBitmap* bitmap, SkMatrix* matrix,
744 TileMode xy[]) {
745 // we cache our "bitmap", so it's generationID will be const on subsequent
746 // calls to asABitmap
747 if (NULL == fCachedBitmap) {
748 fCachedBitmap = SkNEW(SkBitmap);
749 fCachedBitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
750 fCachedBitmap->setPixels((void*)this->getCache32(), NULL);
751 }
752
753 if (bitmap) {
754 *bitmap = *fCachedBitmap;
755 }
756 if (matrix) {
757 matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1);
758 matrix->preConcat(fPtsToUnit);
759 }
760 if (xy) {
761 xy[0] = fTileMode;
762 xy[1] = kClamp_TileMode;
763 }
764 return true;
765 }
766
dither_memset16(uint16_t dst[],uint16_t value,uint16_t other,int count)767 static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
768 int count) {
769 if (reinterpret_cast<uintptr_t>(dst) & 2) {
770 *dst++ = value;
771 count -= 1;
772 SkTSwap(value, other);
773 }
774
775 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
776
777 if (count & 1) {
778 dst[count - 1] = value;
779 }
780 }
781
shadeSpan16(int x,int y,uint16_t dstC[],int count)782 void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
783 {
784 SkASSERT(count > 0);
785
786 SkPoint srcPt;
787 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
788 TileProc proc = fTileProc;
789 const uint16_t* cache = this->getCache16();
790 int toggle = ((x ^ y) & 1) << kCache16Bits;
791
792 if (fDstToIndexClass != kPerspective_MatrixClass) {
793 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
794 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
795 // preround fx by half the amount we throw away
796 fx += 1 << 7;
797
798 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
799 SkFixed dxStorage[1];
800 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
801 dx = dxStorage[0];
802 } else {
803 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
804 dx = SkScalarToFixed(fDstToIndex.getScaleX());
805 }
806
807 if (SkFixedNearlyZero(dx)) {
808 // we're a vertical gradient, so no change in a span
809 unsigned fi = proc(fx) >> 10;
810 SkASSERT(fi <= 63);
811 dither_memset16(dstC, cache[toggle + fi], cache[(toggle ^ (1 << kCache16Bits)) + fi], count);
812 } else if (proc == clamp_tileproc) {
813 do {
814 unsigned fi = SkClampMax(fx >> 10, 63);
815 SkASSERT(fi <= 63);
816 fx += dx;
817 *dstC++ = cache[toggle + fi];
818 toggle ^= (1 << kCache16Bits);
819 } while (--count != 0);
820 } else if (proc == mirror_tileproc) {
821 do {
822 unsigned fi = mirror_6bits(fx >> 10);
823 SkASSERT(fi <= 0x3F);
824 fx += dx;
825 *dstC++ = cache[toggle + fi];
826 toggle ^= (1 << kCache16Bits);
827 } while (--count != 0);
828 } else {
829 SkASSERT(proc == repeat_tileproc);
830 do {
831 unsigned fi = repeat_6bits(fx >> 10);
832 SkASSERT(fi <= 0x3F);
833 fx += dx;
834 *dstC++ = cache[toggle + fi];
835 toggle ^= (1 << kCache16Bits);
836 } while (--count != 0);
837 }
838 } else {
839 SkScalar dstX = SkIntToScalar(x);
840 SkScalar dstY = SkIntToScalar(y);
841 do {
842 dstProc(fDstToIndex, dstX, dstY, &srcPt);
843 unsigned fi = proc(SkScalarToFixed(srcPt.fX));
844 SkASSERT(fi <= 0xFFFF);
845
846 int index = fi >> (16 - kCache16Bits);
847 *dstC++ = cache[toggle + index];
848 toggle ^= (1 << kCache16Bits);
849
850 dstX += SK_Scalar1;
851 } while (--count != 0);
852 }
853 }
854
855 ///////////////////////////////////////////////////////////////////////////////
856
857 #define kSQRT_TABLE_BITS 11
858 #define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
859
860 #include "SkRadialGradient_Table.h"
861
862 #if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
863
864 #include <stdio.h>
865
SkRadialGradient_BuildTable()866 void SkRadialGradient_BuildTable()
867 {
868 // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
869
870 FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
871 SkASSERT(file);
872 ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
873
874 for (int i = 0; i < kSQRT_TABLE_SIZE; i++)
875 {
876 if ((i & 15) == 0)
877 ::fprintf(file, "\t");
878
879 uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
880
881 ::fprintf(file, "0x%02X", value);
882 if (i < kSQRT_TABLE_SIZE-1)
883 ::fprintf(file, ", ");
884 if ((i & 15) == 15)
885 ::fprintf(file, "\n");
886 }
887 ::fprintf(file, "};\n");
888 ::fclose(file);
889 }
890
891 #endif
892
893
rad_to_unit_matrix(const SkPoint & center,SkScalar radius,SkMatrix * matrix)894 static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius, SkMatrix* matrix)
895 {
896 SkScalar inv = SkScalarInvert(radius);
897
898 matrix->setTranslate(-center.fX, -center.fY);
899 matrix->postScale(inv, inv);
900 }
901
902 class Radial_Gradient : public Gradient_Shader {
903 public:
Radial_Gradient(const SkPoint & center,SkScalar radius,const SkColor colors[],const SkScalar pos[],int colorCount,SkShader::TileMode mode,SkUnitMapper * mapper)904 Radial_Gradient(const SkPoint& center, SkScalar radius,
905 const SkColor colors[], const SkScalar pos[], int colorCount,
906 SkShader::TileMode mode, SkUnitMapper* mapper)
907 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
908 {
909 // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
910 SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
911
912 rad_to_unit_matrix(center, radius, &fPtsToUnit);
913 }
shadeSpan(int x,int y,SkPMColor dstC[],int count)914 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
915 {
916 SkASSERT(count > 0);
917
918 SkPoint srcPt;
919 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
920 TileProc proc = fTileProc;
921 const SkPMColor* cache = this->getCache32();
922
923 if (fDstToIndexClass != kPerspective_MatrixClass)
924 {
925 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
926 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
927 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
928
929 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
930 {
931 SkFixed storage[2];
932 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
933 dx = storage[0];
934 dy = storage[1];
935 }
936 else
937 {
938 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
939 dx = SkScalarToFixed(fDstToIndex.getScaleX());
940 dy = SkScalarToFixed(fDstToIndex.getSkewY());
941 }
942
943 if (proc == clamp_tileproc)
944 {
945 const uint8_t* sqrt_table = gSqrt8Table;
946 fx >>= 1;
947 dx >>= 1;
948 fy >>= 1;
949 dy >>= 1;
950 do {
951 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
952 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
953 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
954 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
955 *dstC++ = cache[sqrt_table[fi] >> (8 - kCache32Bits)];
956 fx += dx;
957 fy += dy;
958 } while (--count != 0);
959 }
960 else if (proc == mirror_tileproc)
961 {
962 do {
963 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
964 unsigned fi = mirror_tileproc(dist);
965 SkASSERT(fi <= 0xFFFF);
966 *dstC++ = cache[fi >> (16 - kCache32Bits)];
967 fx += dx;
968 fy += dy;
969 } while (--count != 0);
970 }
971 else
972 {
973 SkASSERT(proc == repeat_tileproc);
974 do {
975 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
976 unsigned fi = repeat_tileproc(dist);
977 SkASSERT(fi <= 0xFFFF);
978 *dstC++ = cache[fi >> (16 - kCache32Bits)];
979 fx += dx;
980 fy += dy;
981 } while (--count != 0);
982 }
983 }
984 else // perspective case
985 {
986 SkScalar dstX = SkIntToScalar(x);
987 SkScalar dstY = SkIntToScalar(y);
988 do {
989 dstProc(fDstToIndex, dstX, dstY, &srcPt);
990 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
991 SkASSERT(fi <= 0xFFFF);
992 *dstC++ = cache[fi >> (16 - kCache32Bits)];
993 dstX += SK_Scalar1;
994 } while (--count != 0);
995 }
996 }
997
shadeSpan16(int x,int y,uint16_t dstC[],int count)998 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) {
999 SkASSERT(count > 0);
1000
1001 SkPoint srcPt;
1002 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1003 TileProc proc = fTileProc;
1004 const uint16_t* cache = this->getCache16();
1005 int toggle = ((x ^ y) & 1) << kCache16Bits;
1006
1007 if (fDstToIndexClass != kPerspective_MatrixClass) {
1008 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
1009 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1010 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1011
1012 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
1013 SkFixed storage[2];
1014 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
1015 dx = storage[0];
1016 dy = storage[1];
1017 } else {
1018 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1019 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1020 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1021 }
1022
1023 if (proc == clamp_tileproc) {
1024 const uint8_t* sqrt_table = gSqrt8Table;
1025
1026 /* knock these down so we can pin against +- 0x7FFF, which is an immediate load,
1027 rather than 0xFFFF which is slower. This is a compromise, since it reduces our
1028 precision, but that appears to be visually OK. If we decide this is OK for
1029 all of our cases, we could (it seems) put this scale-down into fDstToIndex,
1030 to avoid having to do these extra shifts each time.
1031 */
1032 fx >>= 1;
1033 dx >>= 1;
1034 fy >>= 1;
1035 dy >>= 1;
1036 if (dy == 0) { // might perform this check for the other modes, but the win will be a smaller % of the total
1037 fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1038 fy *= fy;
1039 do {
1040 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1041 unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
1042 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1043 fx += dx;
1044 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1045 toggle ^= (1 << kCache16Bits);
1046 } while (--count != 0);
1047 } else {
1048 do {
1049 unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
1050 unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
1051 fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
1052 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
1053 fx += dx;
1054 fy += dy;
1055 *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
1056 toggle ^= (1 << kCache16Bits);
1057 } while (--count != 0);
1058 }
1059 } else if (proc == mirror_tileproc) {
1060 do {
1061 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1062 unsigned fi = mirror_tileproc(dist);
1063 SkASSERT(fi <= 0xFFFF);
1064 fx += dx;
1065 fy += dy;
1066 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1067 toggle ^= (1 << kCache16Bits);
1068 } while (--count != 0);
1069 } else {
1070 SkASSERT(proc == repeat_tileproc);
1071 do {
1072 SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
1073 unsigned fi = repeat_tileproc(dist);
1074 SkASSERT(fi <= 0xFFFF);
1075 fx += dx;
1076 fy += dy;
1077 *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
1078 toggle ^= (1 << kCache16Bits);
1079 } while (--count != 0);
1080 }
1081 } else { // perspective case
1082 SkScalar dstX = SkIntToScalar(x);
1083 SkScalar dstY = SkIntToScalar(y);
1084 do {
1085 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1086 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
1087 SkASSERT(fi <= 0xFFFF);
1088
1089 int index = fi >> (16 - kCache16Bits);
1090 *dstC++ = cache[toggle + index];
1091 toggle ^= (1 << kCache16Bits);
1092
1093 dstX += SK_Scalar1;
1094 } while (--count != 0);
1095 }
1096 }
1097
CreateProc(SkFlattenableReadBuffer & buffer)1098 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1099 return SkNEW_ARGS(Radial_Gradient, (buffer));
1100 }
1101
1102 protected:
Radial_Gradient(SkFlattenableReadBuffer & buffer)1103 Radial_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {};
getFactory()1104 virtual Factory getFactory() { return CreateProc; }
onCacheReset()1105 virtual void onCacheReset() {}
1106
1107 private:
1108 typedef Gradient_Shader INHERITED;
1109 };
1110
1111 /* Two-point radial gradients are specified by two circles, each with a center
1112 point and radius. The gradient can be considered to be a series of
1113 concentric circles, with the color interpolated from the start circle
1114 (at t=0) to the end circle (at t=1).
1115
1116 For each point (x, y) in the span, we want to find the
1117 interpolated circle that intersects that point. The center
1118 of the desired circle (Cx, Cy) falls at some distance t
1119 along the line segment between the start point (Sx, Sy) and
1120 end point (Ex, Ey):
1121
1122 Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1)
1123 Cy = (1 - t) * Sy + t * Ey
1124
1125 The radius of the desired circle (r) is also a linear interpolation t
1126 between the start and end radii (Sr and Er):
1127
1128 r = (1 - t) * Sr + t * Er
1129
1130 But
1131
1132 (x - Cx)^2 + (y - Cy)^2 = r^2
1133
1134 so
1135
1136 (x - ((1 - t) * Sx + t * Ex))^2
1137 + (y - ((1 - t) * Sy + t * Ey))^2
1138 = ((1 - t) * Sr + t * Er)^2
1139
1140 Solving for t yields
1141
1142 [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2
1143 + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t
1144 + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0
1145
1146 To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
1147
1148 [Dx^2 + Dy^2 - Dr^2)] * t^2
1149 + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t
1150 + [dx^2 + dy^2 - Sr^2] = 0
1151
1152 A quadratic in t. The two roots of the quadratic reflect the two
1153 possible circles on which the point may fall. Solving for t yields
1154 the gradient value to use.
1155
1156 If a<0, the start circle is entirely contained in the
1157 end circle, and one of the roots will be <0 or >1 (off the line
1158 segment). If a>0, the start circle falls at least partially
1159 outside the end circle (or vice versa), and the gradient
1160 defines a "tube" where a point may be on one circle (on the
1161 inside of the tube) or the other (outside of the tube). We choose
1162 one arbitrarily.
1163
1164 In order to keep the math to within the limits of fixed point,
1165 we divide the entire quadratic by Dr^2, and replace
1166 (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
1167
1168 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
1169 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
1170 + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
1171
1172 (x' and y' are computed by appending the subtract and scale to the
1173 fDstToIndex matrix in the constructor).
1174
1175 Since the 'A' component of the quadratic is independent of x' and y', it
1176 is precomputed in the constructor. Since the 'B' component is linear in
1177 x' and y', if x and y are linear in the span, 'B' can be computed
1178 incrementally with a simple delta (db below). If it is not (e.g.,
1179 a perspective projection), it must be computed in the loop.
1180
1181 */
1182
two_point_radial(SkFixed b,SkFixed fx,SkFixed fy,SkFixed sr2d2,SkFixed foura,SkFixed oneOverTwoA,bool posRoot)1183 static inline SkFixed two_point_radial(SkFixed b, SkFixed fx, SkFixed fy, SkFixed sr2d2, SkFixed foura, SkFixed oneOverTwoA, bool posRoot) {
1184 SkFixed c = SkFixedSquare(fx) + SkFixedSquare(fy) - sr2d2;
1185 SkFixed discrim = SkFixedSquare(b) - SkFixedMul(foura, c);
1186 if (discrim < 0) {
1187 discrim = -discrim;
1188 }
1189 SkFixed rootDiscrim = SkFixedSqrt(discrim);
1190 if (posRoot) {
1191 return SkFixedMul(-b + rootDiscrim, oneOverTwoA);
1192 } else {
1193 return SkFixedMul(-b - rootDiscrim, oneOverTwoA);
1194 }
1195 }
1196
1197 class Two_Point_Radial_Gradient : public Gradient_Shader {
1198 public:
Two_Point_Radial_Gradient(const SkPoint & start,SkScalar startRadius,const SkPoint & end,SkScalar endRadius,const SkColor colors[],const SkScalar pos[],int colorCount,SkShader::TileMode mode,SkUnitMapper * mapper)1199 Two_Point_Radial_Gradient(const SkPoint& start, SkScalar startRadius,
1200 const SkPoint& end, SkScalar endRadius,
1201 const SkColor colors[], const SkScalar pos[],
1202 int colorCount, SkShader::TileMode mode,
1203 SkUnitMapper* mapper)
1204 : Gradient_Shader(colors, pos, colorCount, mode, mapper)
1205 {
1206 fDiff = start - end;
1207 fDiffRadius = endRadius - startRadius;
1208 SkScalar inv = SkScalarInvert(fDiffRadius);
1209 fDiff.fX = SkScalarMul(fDiff.fX, inv);
1210 fDiff.fY = SkScalarMul(fDiff.fY, inv);
1211 fStartRadius = SkScalarMul(startRadius, inv);
1212 fSr2D2 = SkScalarSquare(fStartRadius);
1213 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
1214 fOneOverTwoA = SkScalarInvert(fA * 2);
1215
1216 fPtsToUnit.setTranslate(-start.fX, -start.fY);
1217 fPtsToUnit.postScale(inv, inv);
1218 }
shadeSpan(int x,int y,SkPMColor dstC[],int count)1219 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
1220 {
1221 SkASSERT(count > 0);
1222
1223 // Zero difference between radii: fill with transparent black.
1224 if (fDiffRadius == 0) {
1225 sk_bzero(dstC, count * sizeof(*dstC));
1226 return;
1227 }
1228 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
1229 TileProc proc = fTileProc;
1230 const SkPMColor* cache = this->getCache32();
1231 SkFixed diffx = SkScalarToFixed(fDiff.fX);
1232 SkFixed diffy = SkScalarToFixed(fDiff.fY);
1233 SkFixed foura = SkScalarToFixed(SkScalarMul(fA, 4));
1234 SkFixed startRadius = SkScalarToFixed(fStartRadius);
1235 SkFixed sr2D2 = SkScalarToFixed(fSr2D2);
1236 SkFixed oneOverTwoA = SkScalarToFixed(fOneOverTwoA);
1237 bool posRoot = fDiffRadius < 0;
1238 if (fDstToIndexClass != kPerspective_MatrixClass)
1239 {
1240 SkPoint srcPt;
1241 dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
1242 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1243 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1244
1245 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1246 {
1247 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &dx, &dy);
1248 }
1249 else
1250 {
1251 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1252 dx = SkScalarToFixed(fDstToIndex.getScaleX());
1253 dy = SkScalarToFixed(fDstToIndex.getSkewY());
1254 }
1255 SkFixed b = (SkFixedMul(diffx, fx) +
1256 SkFixedMul(diffy, fy) - startRadius) << 1;
1257 SkFixed db = (SkFixedMul(diffx, dx) +
1258 SkFixedMul(diffy, dy)) << 1;
1259 if (proc == clamp_tileproc)
1260 {
1261 for (; count > 0; --count) {
1262 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1263 SkFixed index = SkClampMax(t, 0xFFFF);
1264 SkASSERT(index <= 0xFFFF);
1265 *dstC++ = cache[index >> (16 - kCache32Bits)];
1266 fx += dx;
1267 fy += dy;
1268 b += db;
1269 }
1270 }
1271 else if (proc == mirror_tileproc)
1272 {
1273 for (; count > 0; --count) {
1274 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1275 SkFixed index = mirror_tileproc(t);
1276 SkASSERT(index <= 0xFFFF);
1277 *dstC++ = cache[index >> (16 - kCache32Bits)];
1278 fx += dx;
1279 fy += dy;
1280 b += db;
1281 }
1282 }
1283 else
1284 {
1285 SkASSERT(proc == repeat_tileproc);
1286 for (; count > 0; --count) {
1287 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1288 SkFixed index = repeat_tileproc(t);
1289 SkASSERT(index <= 0xFFFF);
1290 *dstC++ = cache[index >> (16 - kCache32Bits)];
1291 fx += dx;
1292 fy += dy;
1293 b += db;
1294 }
1295 }
1296 }
1297 else // perspective case
1298 {
1299 SkScalar dstX = SkIntToScalar(x);
1300 SkScalar dstY = SkIntToScalar(y);
1301 for (; count > 0; --count) {
1302 SkPoint srcPt;
1303 dstProc(fDstToIndex, dstX, dstY, &srcPt);
1304 SkFixed fx = SkScalarToFixed(srcPt.fX);
1305 SkFixed fy = SkScalarToFixed(srcPt.fY);
1306 SkFixed b = (SkFixedMul(diffx, fx) +
1307 SkFixedMul(diffy, fy) - startRadius) << 1;
1308 SkFixed t = two_point_radial(b, fx, fy, sr2D2, foura, oneOverTwoA, posRoot);
1309 SkFixed index = proc(t);
1310 SkASSERT(index <= 0xFFFF);
1311 *dstC++ = cache[index >> (16 - kCache32Bits)];
1312 dstX += SK_Scalar1;
1313 }
1314 }
1315 }
1316
setContext(const SkBitmap & device,const SkPaint & paint,const SkMatrix & matrix)1317 virtual bool setContext(const SkBitmap& device,
1318 const SkPaint& paint,
1319 const SkMatrix& matrix) {
1320 if (!this->INHERITED::setContext(device, paint, matrix)) {
1321 return false;
1322 }
1323
1324 // we don't have a span16 proc
1325 fFlags &= ~kHasSpan16_Flag;
1326 return true;
1327 }
1328
CreateProc(SkFlattenableReadBuffer & buffer)1329 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1330 return SkNEW_ARGS(Two_Point_Radial_Gradient, (buffer));
1331 }
1332
1333 protected:
Two_Point_Radial_Gradient(SkFlattenableReadBuffer & buffer)1334 Two_Point_Radial_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {};
getFactory()1335 virtual Factory getFactory() { return CreateProc; }
onCacheReset()1336 virtual void onCacheReset() {}
1337
1338 private:
1339 typedef Gradient_Shader INHERITED;
1340 SkPoint fDiff;
1341 SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA;
1342 };
1343
1344 ///////////////////////////////////////////////////////////////////////////////
1345
1346 class Sweep_Gradient : public Gradient_Shader {
1347 public:
Sweep_Gradient(SkScalar cx,SkScalar cy,const SkColor colors[],const SkScalar pos[],int count,SkUnitMapper * mapper)1348 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
1349 const SkScalar pos[], int count, SkUnitMapper* mapper)
1350 : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper)
1351 {
1352 fPtsToUnit.setTranslate(-cx, -cy);
1353 }
1354 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
1355 virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
1356
CreateProc(SkFlattenableReadBuffer & buffer)1357 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
1358 return SkNEW_ARGS(Sweep_Gradient, (buffer));
1359 }
1360
1361 protected:
Sweep_Gradient(SkFlattenableReadBuffer & buffer)1362 Sweep_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {}
getFactory()1363 virtual Factory getFactory() { return CreateProc; }
onCacheReset()1364 virtual void onCacheReset() {}
1365
1366 private:
1367 typedef Gradient_Shader INHERITED;
1368 };
1369
1370 #ifdef COMPUTE_SWEEP_TABLE
1371 #define PI 3.14159265
1372 static bool gSweepTableReady;
1373 static uint8_t gSweepTable[65];
1374
1375 /* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
1376 We scale the results to [0..32]
1377 */
build_sweep_table()1378 static const uint8_t* build_sweep_table()
1379 {
1380 if (!gSweepTableReady)
1381 {
1382 const int N = 65;
1383 const double DENOM = N - 1;
1384
1385 for (int i = 0; i < N; i++)
1386 {
1387 double arg = i / DENOM;
1388 double v = atan(arg);
1389 int iv = (int)round(v * DENOM * 2 / PI);
1390 // printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
1391 printf("%d, ", iv);
1392 gSweepTable[i] = iv;
1393 }
1394 gSweepTableReady = true;
1395 }
1396 return gSweepTable;
1397 }
1398 #else
1399 static const uint8_t gSweepTable[] = {
1400 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
1401 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
1402 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
1403 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
1404 32
1405 };
build_sweep_table()1406 static const uint8_t* build_sweep_table() { return gSweepTable; }
1407 #endif
1408
1409 // divide numer/denom, with a bias of 6bits. Assumes numer <= denom
1410 // and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
1411 // Same as (but faster than) SkFixedDiv(numer, denom) >> 10
1412
1413 //unsigned div_64(int numer, int denom);
div_64(int numer,int denom)1414 static unsigned div_64(int numer, int denom)
1415 {
1416 SkASSERT(numer <= denom);
1417 SkASSERT(numer > 0);
1418 SkASSERT(denom > 0);
1419
1420 int nbits = SkCLZ(numer);
1421 int dbits = SkCLZ(denom);
1422 int bits = 6 - nbits + dbits;
1423 SkASSERT(bits <= 6);
1424
1425 if (bits < 0) // detect underflow
1426 return 0;
1427
1428 denom <<= dbits - 1;
1429 numer <<= nbits - 1;
1430
1431 unsigned result = 0;
1432
1433 // do the first one
1434 if ((numer -= denom) >= 0)
1435 result = 1;
1436 else
1437 numer += denom;
1438
1439 // Now fall into our switch statement if there are more bits to compute
1440 if (bits > 0)
1441 {
1442 // make room for the rest of the answer bits
1443 result <<= bits;
1444 switch (bits) {
1445 case 6:
1446 if ((numer = (numer << 1) - denom) >= 0)
1447 result |= 32;
1448 else
1449 numer += denom;
1450 case 5:
1451 if ((numer = (numer << 1) - denom) >= 0)
1452 result |= 16;
1453 else
1454 numer += denom;
1455 case 4:
1456 if ((numer = (numer << 1) - denom) >= 0)
1457 result |= 8;
1458 else
1459 numer += denom;
1460 case 3:
1461 if ((numer = (numer << 1) - denom) >= 0)
1462 result |= 4;
1463 else
1464 numer += denom;
1465 case 2:
1466 if ((numer = (numer << 1) - denom) >= 0)
1467 result |= 2;
1468 else
1469 numer += denom;
1470 case 1:
1471 default: // not strictly need, but makes GCC make better ARM code
1472 if ((numer = (numer << 1) - denom) >= 0)
1473 result |= 1;
1474 else
1475 numer += denom;
1476 }
1477 }
1478 return result;
1479 }
1480
1481 // Given x,y in the first quadrant, return 0..63 for the angle [0..90]
atan_0_90(SkFixed y,SkFixed x)1482 static unsigned atan_0_90(SkFixed y, SkFixed x)
1483 {
1484 #ifdef SK_DEBUG
1485 {
1486 static bool gOnce;
1487 if (!gOnce)
1488 {
1489 gOnce = true;
1490 SkASSERT(div_64(55, 55) == 64);
1491 SkASSERT(div_64(128, 256) == 32);
1492 SkASSERT(div_64(2326528, 4685824) == 31);
1493 SkASSERT(div_64(753664, 5210112) == 9);
1494 SkASSERT(div_64(229376, 4882432) == 3);
1495 SkASSERT(div_64(2, 64) == 2);
1496 SkASSERT(div_64(1, 64) == 1);
1497 // test that we handle underflow correctly
1498 SkASSERT(div_64(12345, 0x54321234) == 0);
1499 }
1500 }
1501 #endif
1502
1503 SkASSERT(y > 0 && x > 0);
1504 const uint8_t* table = build_sweep_table();
1505
1506 unsigned result;
1507 bool swap = (x < y);
1508 if (swap)
1509 {
1510 // first part of the atan(v) = PI/2 - atan(1/v) identity
1511 // since our div_64 and table want v <= 1, where v = y/x
1512 SkTSwap<SkFixed>(x, y);
1513 }
1514
1515 result = div_64(y, x);
1516
1517 #ifdef SK_DEBUG
1518 {
1519 unsigned result2 = SkDivBits(y, x, 6);
1520 SkASSERT(result2 == result ||
1521 (result == 1 && result2 == 0));
1522 }
1523 #endif
1524
1525 SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
1526 result = table[result];
1527
1528 if (swap)
1529 {
1530 // complete the atan(v) = PI/2 - atan(1/v) identity
1531 result = 64 - result;
1532 // pin to 63
1533 result -= result >> 6;
1534 }
1535
1536 SkASSERT(result <= 63);
1537 return result;
1538 }
1539
1540 // returns angle in a circle [0..2PI) -> [0..255]
SkATan2_255(SkFixed y,SkFixed x)1541 static unsigned SkATan2_255(SkFixed y, SkFixed x)
1542 {
1543 if (x == 0)
1544 {
1545 if (y == 0)
1546 return 0;
1547 return y < 0 ? 192 : 64;
1548 }
1549 if (y == 0)
1550 return x < 0 ? 128 : 0;
1551
1552 /* Find the right quadrant for x,y
1553 Since atan_0_90 only handles the first quadrant, we rotate x,y
1554 appropriately before calling it, and then add the right amount
1555 to account for the real quadrant.
1556 quadrant 0 : add 0 | x > 0 && y > 0
1557 quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
1558 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
1559 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
1560
1561 map x<0 to (1 << 6)
1562 map y<0 to (3 << 6)
1563 add = map_x ^ map_y
1564 */
1565 int xsign = x >> 31;
1566 int ysign = y >> 31;
1567 int add = ((-xsign) ^ (ysign & 3)) << 6;
1568
1569 #ifdef SK_DEBUG
1570 if (0 == add)
1571 SkASSERT(x > 0 && y > 0);
1572 else if (64 == add)
1573 SkASSERT(x < 0 && y > 0);
1574 else if (128 == add)
1575 SkASSERT(x < 0 && y < 0);
1576 else if (192 == add)
1577 SkASSERT(x > 0 && y < 0);
1578 else
1579 SkASSERT(!"bad value for add");
1580 #endif
1581
1582 /* This ^ trick makes x, y positive, and the swap<> handles quadrants
1583 where we need to rotate x,y by 90 or -90
1584 */
1585 x = (x ^ xsign) - xsign;
1586 y = (y ^ ysign) - ysign;
1587 if (add & 64) // quads 1 or 3 need to swap x,y
1588 SkTSwap<SkFixed>(x, y);
1589
1590 unsigned result = add + atan_0_90(y, x);
1591 SkASSERT(result < 256);
1592 return result;
1593 }
1594
shadeSpan(int x,int y,SkPMColor dstC[],int count)1595 void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
1596 {
1597 SkMatrix::MapXYProc proc = fDstToIndexProc;
1598 const SkMatrix& matrix = fDstToIndex;
1599 const SkPMColor* cache = this->getCache32();
1600 SkPoint srcPt;
1601
1602 if (fDstToIndexClass != kPerspective_MatrixClass)
1603 {
1604 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1605 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1606 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1607 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1608
1609 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1610 {
1611 SkFixed storage[2];
1612 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1613 &storage[0], &storage[1]);
1614 dx = storage[0];
1615 dy = storage[1];
1616 }
1617 else
1618 {
1619 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1620 dx = SkScalarToFixed(matrix.getScaleX());
1621 dy = SkScalarToFixed(matrix.getSkewY());
1622 }
1623
1624 for (; count > 0; --count)
1625 {
1626 *dstC++ = cache[SkATan2_255(fy, fx)];
1627 fx += dx;
1628 fy += dy;
1629 }
1630 }
1631 else // perspective case
1632 {
1633 for (int stop = x + count; x < stop; x++)
1634 {
1635 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1636 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1637
1638 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1639 SkScalarToFixed(srcPt.fX));
1640 *dstC++ = cache[index];
1641 }
1642 }
1643 }
1644
shadeSpan16(int x,int y,uint16_t dstC[],int count)1645 void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
1646 {
1647 SkMatrix::MapXYProc proc = fDstToIndexProc;
1648 const SkMatrix& matrix = fDstToIndex;
1649 const uint16_t* cache = this->getCache16();
1650 int toggle = ((x ^ y) & 1) << kCache16Bits;
1651 SkPoint srcPt;
1652
1653 if (fDstToIndexClass != kPerspective_MatrixClass)
1654 {
1655 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1656 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1657 SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
1658 SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
1659
1660 if (fDstToIndexClass == kFixedStepInX_MatrixClass)
1661 {
1662 SkFixed storage[2];
1663 (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
1664 &storage[0], &storage[1]);
1665 dx = storage[0];
1666 dy = storage[1];
1667 }
1668 else
1669 {
1670 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
1671 dx = SkScalarToFixed(matrix.getScaleX());
1672 dy = SkScalarToFixed(matrix.getSkewY());
1673 }
1674
1675 for (; count > 0; --count)
1676 {
1677 int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
1678 *dstC++ = cache[toggle + index];
1679 toggle ^= (1 << kCache16Bits);
1680 fx += dx;
1681 fy += dy;
1682 }
1683 }
1684 else // perspective case
1685 {
1686 for (int stop = x + count; x < stop; x++)
1687 {
1688 proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
1689 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
1690
1691 int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
1692 SkScalarToFixed(srcPt.fX));
1693 index >>= (8 - kCache16Bits);
1694 *dstC++ = cache[toggle + index];
1695 toggle ^= (1 << kCache16Bits);
1696 }
1697 }
1698 }
1699
1700 ///////////////////////////////////////////////////////////////////////////
1701 ///////////////////////////////////////////////////////////////////////////
1702
1703 // assumes colors is SkColor* and pos is SkScalar*
1704 #define EXPAND_1_COLOR(count) \
1705 SkColor tmp[2]; \
1706 do { \
1707 if (1 == count) { \
1708 tmp[0] = tmp[1] = colors[0]; \
1709 colors = tmp; \
1710 pos = NULL; \
1711 count = 2; \
1712 } \
1713 } while (0)
1714
CreateLinear(const SkPoint pts[2],const SkColor colors[],const SkScalar pos[],int colorCount,SkShader::TileMode mode,SkUnitMapper * mapper)1715 SkShader* SkGradientShader::CreateLinear( const SkPoint pts[2],
1716 const SkColor colors[], const SkScalar pos[], int colorCount,
1717 SkShader::TileMode mode, SkUnitMapper* mapper)
1718 {
1719 if (NULL == pts || NULL == colors || colorCount < 1) {
1720 return NULL;
1721 }
1722 EXPAND_1_COLOR(colorCount);
1723
1724 return SkNEW_ARGS(Linear_Gradient,
1725 (pts, colors, pos, colorCount, mode, mapper));
1726 }
1727
CreateRadial(const SkPoint & center,SkScalar radius,const SkColor colors[],const SkScalar pos[],int colorCount,SkShader::TileMode mode,SkUnitMapper * mapper)1728 SkShader* SkGradientShader::CreateRadial( const SkPoint& center, SkScalar radius,
1729 const SkColor colors[], const SkScalar pos[], int colorCount,
1730 SkShader::TileMode mode, SkUnitMapper* mapper)
1731 {
1732 if (radius <= 0 || NULL == colors || colorCount < 1) {
1733 return NULL;
1734 }
1735 EXPAND_1_COLOR(colorCount);
1736
1737 return SkNEW_ARGS(Radial_Gradient,
1738 (center, radius, colors, pos, colorCount, mode, mapper));
1739 }
1740
CreateTwoPointRadial(const SkPoint & start,SkScalar startRadius,const SkPoint & end,SkScalar endRadius,const SkColor colors[],const SkScalar pos[],int colorCount,SkShader::TileMode mode,SkUnitMapper * mapper)1741 SkShader* SkGradientShader::CreateTwoPointRadial(const SkPoint& start,
1742 SkScalar startRadius,
1743 const SkPoint& end,
1744 SkScalar endRadius,
1745 const SkColor colors[],
1746 const SkScalar pos[],
1747 int colorCount,
1748 SkShader::TileMode mode,
1749 SkUnitMapper* mapper)
1750 {
1751 if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
1752 return NULL;
1753 }
1754 EXPAND_1_COLOR(colorCount);
1755
1756 return SkNEW_ARGS(Two_Point_Radial_Gradient,
1757 (start, startRadius, end, endRadius, colors, pos, colorCount, mode, mapper));
1758 }
1759
CreateSweep(SkScalar cx,SkScalar cy,const SkColor colors[],const SkScalar pos[],int count,SkUnitMapper * mapper)1760 SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
1761 const SkColor colors[],
1762 const SkScalar pos[],
1763 int count, SkUnitMapper* mapper)
1764 {
1765 if (NULL == colors || count < 1) {
1766 return NULL;
1767 }
1768 EXPAND_1_COLOR(count);
1769
1770 return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
1771 }
1772
1773 static SkFlattenable::Registrar gLinearGradientReg("Linear_Gradient",
1774 Linear_Gradient::CreateProc);
1775
1776 static SkFlattenable::Registrar gRadialGradientReg("Radial_Gradient",
1777 Radial_Gradient::CreateProc);
1778
1779 static SkFlattenable::Registrar gSweepGradientReg("Sweep_Gradient",
1780 Sweep_Gradient::CreateProc);
1781
1782