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