SkAAClip.cpp revision e56513d2a9eebabf93d7824eebf9e64441c485ba
1 2/* 3 * Copyright 2011 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9#include "SkAAClip.h" 10#include "SkBlitter.h" 11#include "SkColorPriv.h" 12#include "SkPath.h" 13#include "SkScan.h" 14#include "SkThread.h" 15#include "SkUtils.h" 16 17class AutoAAClipValidate { 18public: 19 AutoAAClipValidate(const SkAAClip& clip) : fClip(clip) { 20 fClip.validate(); 21 } 22 ~AutoAAClipValidate() { 23 fClip.validate(); 24 } 25private: 26 const SkAAClip& fClip; 27}; 28 29#ifdef SK_DEBUG 30 #define AUTO_AACLIP_VALIDATE(clip) AutoAAClipValidate acv(clip) 31#else 32 #define AUTO_AACLIP_VALIDATE(clip) 33#endif 34 35/////////////////////////////////////////////////////////////////////////////// 36 37#define kMaxInt32 0x7FFFFFFF 38 39static inline bool x_in_rect(int x, const SkIRect& rect) { 40 return (unsigned)(x - rect.fLeft) < (unsigned)rect.width(); 41} 42 43static inline bool y_in_rect(int y, const SkIRect& rect) { 44 return (unsigned)(y - rect.fTop) < (unsigned)rect.height(); 45} 46 47/* 48 * Data runs are packed [count, alpha] 49 */ 50 51struct SkAAClip::YOffset { 52 int32_t fY; 53 uint32_t fOffset; 54}; 55 56struct SkAAClip::RunHead { 57 int32_t fRefCnt; 58 int32_t fRowCount; 59 int32_t fDataSize; 60 61 YOffset* yoffsets() { 62 return (YOffset*)((char*)this + sizeof(RunHead)); 63 } 64 const YOffset* yoffsets() const { 65 return (const YOffset*)((const char*)this + sizeof(RunHead)); 66 } 67 uint8_t* data() { 68 return (uint8_t*)(this->yoffsets() + fRowCount); 69 } 70 const uint8_t* data() const { 71 return (const uint8_t*)(this->yoffsets() + fRowCount); 72 } 73 74 static RunHead* Alloc(int rowCount, size_t dataSize) { 75 size_t size = sizeof(RunHead) + rowCount * sizeof(YOffset) + dataSize; 76 RunHead* head = (RunHead*)sk_malloc_throw(size); 77 head->fRefCnt = 1; 78 head->fRowCount = rowCount; 79 head->fDataSize = dataSize; 80 return head; 81 } 82 83 static int ComputeRowSizeForWidth(int width) { 84 // 2 bytes per segment, where each segment can store up to 255 for count 85 int segments = 0; 86 while (width > 0) { 87 segments += 1; 88 int n = SkMin32(width, 255); 89 width -= n; 90 } 91 return segments * 2; // each segment is row[0] + row[1] (n + alpha) 92 } 93 94 static RunHead* AllocRect(const SkIRect& bounds) { 95 SkASSERT(!bounds.isEmpty()); 96 int width = bounds.width(); 97 size_t rowSize = ComputeRowSizeForWidth(width); 98 RunHead* head = RunHead::Alloc(1, rowSize); 99 YOffset* yoff = head->yoffsets(); 100 yoff->fY = bounds.height() - 1; 101 yoff->fOffset = 0; 102 uint8_t* row = head->data(); 103 while (width > 0) { 104 int n = SkMin32(width, 255); 105 row[0] = n; 106 row[1] = 0xFF; 107 width -= n; 108 row += 2; 109 } 110 return head; 111 } 112}; 113 114class SkAAClip::Iter { 115public: 116 Iter(const SkAAClip&); 117 118 bool done() const { return fDone; } 119 int top() const { return fTop; } 120 int bottom() const { return fBottom; } 121 const uint8_t* data() const { return fData; } 122 void next(); 123 124private: 125 const YOffset* fCurrYOff; 126 const YOffset* fStopYOff; 127 const uint8_t* fData; 128 129 int fTop, fBottom; 130 bool fDone; 131}; 132 133SkAAClip::Iter::Iter(const SkAAClip& clip) { 134 if (clip.isEmpty()) { 135 fDone = true; 136 fTop = fBottom = clip.fBounds.fBottom; 137 fData = NULL; 138 fStopYOff = NULL; 139 return; 140 } 141 142 const RunHead* head = clip.fRunHead; 143 fCurrYOff = head->yoffsets(); 144 fStopYOff = fCurrYOff + head->fRowCount; 145 fData = head->data() + fCurrYOff->fOffset; 146 147 // setup first value 148 fTop = clip.fBounds.fTop; 149 fBottom = clip.fBounds.fTop + fCurrYOff->fY + 1; 150 fDone = false; 151} 152 153void SkAAClip::Iter::next() { 154 if (!fDone) { 155 const YOffset* prev = fCurrYOff; 156 const YOffset* curr = prev + 1; 157 SkASSERT(curr <= fStopYOff); 158 159 fTop = fBottom; 160 if (curr >= fStopYOff) { 161 fDone = true; 162 fBottom = kMaxInt32; 163 fData = NULL; 164 } else { 165 fBottom += curr->fY - prev->fY; 166 fData += curr->fOffset - prev->fOffset; 167 fCurrYOff = curr; 168 } 169 } 170} 171 172#ifdef SK_DEBUG 173// assert we're exactly width-wide, and then return the number of bytes used 174static size_t compute_row_length(const uint8_t row[], int width) { 175 const uint8_t* origRow = row; 176 while (width > 0) { 177 int n = row[0]; 178 SkASSERT(n > 0); 179 SkASSERT(n <= width); 180 row += 2; 181 width -= n; 182 } 183 SkASSERT(0 == width); 184 return row - origRow; 185} 186 187void SkAAClip::validate() const { 188 if (NULL == fRunHead) { 189 SkASSERT(fBounds.isEmpty()); 190 return; 191 } 192 193 const RunHead* head = fRunHead; 194 SkASSERT(head->fRefCnt > 0); 195 SkASSERT(head->fRowCount > 0); 196 SkASSERT(head->fDataSize > 0); 197 198 const YOffset* yoff = head->yoffsets(); 199 const YOffset* ystop = yoff + head->fRowCount; 200 const int lastY = fBounds.height() - 1; 201 202 // Y and offset must be monotonic 203 int prevY = -1; 204 int32_t prevOffset = -1; 205 while (yoff < ystop) { 206 SkASSERT(prevY < yoff->fY); 207 SkASSERT(yoff->fY <= lastY); 208 prevY = yoff->fY; 209 SkASSERT(prevOffset < (int32_t)yoff->fOffset); 210 prevOffset = yoff->fOffset; 211 const uint8_t* row = head->data() + yoff->fOffset; 212 size_t rowLength = compute_row_length(row, fBounds.width()); 213 SkASSERT(yoff->fOffset + rowLength <= (size_t) head->fDataSize); 214 yoff += 1; 215 } 216 // check the last entry; 217 --yoff; 218 SkASSERT(yoff->fY == lastY); 219} 220#endif 221 222/////////////////////////////////////////////////////////////////////////////// 223 224static void count_left_right_zeros(const uint8_t* row, int width, 225 int* leftZ, int* riteZ) { 226 int zeros = 0; 227 do { 228 if (row[1]) { 229 break; 230 } 231 int n = row[0]; 232 SkASSERT(n > 0); 233 SkASSERT(n <= width); 234 zeros += n; 235 row += 2; 236 width -= n; 237 } while (width > 0); 238 *leftZ = zeros; 239 240 zeros = 0; 241 while (width > 0) { 242 int n = row[0]; 243 SkASSERT(n > 0); 244 if (0 == row[1]) { 245 zeros += n; 246 } else { 247 zeros = 0; 248 } 249 row += 2; 250 width -= n; 251 } 252 *riteZ = zeros; 253} 254 255#ifdef SK_DEBUG 256static void test_count_left_right_zeros() { 257 static bool gOnce; 258 if (gOnce) { 259 return; 260 } 261 gOnce = true; 262 263 const uint8_t data0[] = { 0, 0, 10, 0xFF }; 264 const uint8_t data1[] = { 0, 0, 5, 0xFF, 2, 0, 3, 0xFF }; 265 const uint8_t data2[] = { 7, 0, 5, 0, 2, 0, 3, 0xFF }; 266 const uint8_t data3[] = { 0, 5, 5, 0xFF, 2, 0, 3, 0 }; 267 const uint8_t data4[] = { 2, 3, 2, 0, 5, 0xFF, 3, 0 }; 268 const uint8_t data5[] = { 10, 0, 10, 0 }; 269 const uint8_t data6[] = { 2, 2, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 }; 270 271 const uint8_t* array[] = { 272 data0, data1, data2, data3, data4, data5, data6 273 }; 274 275 for (size_t i = 0; i < SK_ARRAY_COUNT(array); ++i) { 276 const uint8_t* data = array[i]; 277 const int expectedL = *data++; 278 const int expectedR = *data++; 279 int L = 12345, R = 12345; 280 count_left_right_zeros(data, 10, &L, &R); 281 SkASSERT(expectedL == L); 282 SkASSERT(expectedR == R); 283 } 284} 285#endif 286 287// modify row in place, trimming off (zeros) from the left and right sides. 288// return the number of bytes that were completely eliminated from the left 289static int trim_row_left_right(uint8_t* row, int width, int leftZ, int riteZ) { 290 int trim = 0; 291 while (leftZ > 0) { 292 SkASSERT(0 == row[1]); 293 int n = row[0]; 294 SkASSERT(n > 0); 295 SkASSERT(n <= width); 296 width -= n; 297 row += 2; 298 if (n > leftZ) { 299 row[-2] = n - leftZ; 300 break; 301 } 302 trim += 2; 303 leftZ -= n; 304 SkASSERT(leftZ >= 0); 305 } 306 307 if (riteZ) { 308 // walk row to the end, and then we'll back up to trim riteZ 309 while (width > 0) { 310 int n = row[0]; 311 SkASSERT(n <= width); 312 width -= n; 313 row += 2; 314 } 315 // now skip whole runs of zeros 316 do { 317 row -= 2; 318 SkASSERT(0 == row[1]); 319 int n = row[0]; 320 SkASSERT(n > 0); 321 if (n > riteZ) { 322 row[0] = n - riteZ; 323 break; 324 } 325 riteZ -= n; 326 SkASSERT(riteZ >= 0); 327 } while (riteZ > 0); 328 } 329 330 return trim; 331} 332 333#ifdef SK_DEBUG 334// assert that this row is exactly this width 335static void assert_row_width(const uint8_t* row, int width) { 336 while (width > 0) { 337 int n = row[0]; 338 SkASSERT(n > 0); 339 SkASSERT(n <= width); 340 width -= n; 341 row += 2; 342 } 343 SkASSERT(0 == width); 344} 345 346static void test_trim_row_left_right() { 347 static bool gOnce; 348 if (gOnce) { 349 return; 350 } 351 gOnce = true; 352 353 uint8_t data0[] = { 0, 0, 0, 10, 10, 0xFF }; 354 uint8_t data1[] = { 2, 0, 0, 10, 5, 0, 2, 0, 3, 0xFF }; 355 uint8_t data2[] = { 5, 0, 2, 10, 5, 0, 2, 0, 3, 0xFF }; 356 uint8_t data3[] = { 6, 0, 2, 10, 5, 0, 2, 0, 3, 0xFF }; 357 uint8_t data4[] = { 0, 0, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 }; 358 uint8_t data5[] = { 1, 0, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 }; 359 uint8_t data6[] = { 0, 1, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 }; 360 uint8_t data7[] = { 1, 1, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 }; 361 uint8_t data8[] = { 2, 2, 2, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 }; 362 uint8_t data9[] = { 5, 2, 4, 10, 2, 0, 2, 0, 2, 0, 2, 0xFF, 2, 0 }; 363 uint8_t data10[] ={ 74, 0, 4, 150, 9, 0, 65, 0, 76, 0xFF }; 364 365 uint8_t* array[] = { 366 data0, data1, data2, data3, data4, 367 data5, data6, data7, data8, data9, 368 data10 369 }; 370 371 for (size_t i = 0; i < SK_ARRAY_COUNT(array); ++i) { 372 uint8_t* data = array[i]; 373 const int trimL = *data++; 374 const int trimR = *data++; 375 const int expectedSkip = *data++; 376 const int origWidth = *data++; 377 assert_row_width(data, origWidth); 378 int skip = trim_row_left_right(data, origWidth, trimL, trimR); 379 SkASSERT(expectedSkip == skip); 380 int expectedWidth = origWidth - trimL - trimR; 381 assert_row_width(data + skip, expectedWidth); 382 } 383} 384#endif 385 386bool SkAAClip::trimLeftRight() { 387 SkDEBUGCODE(test_trim_row_left_right();) 388 389 if (this->isEmpty()) { 390 return false; 391 } 392 393 AUTO_AACLIP_VALIDATE(*this); 394 395 const int width = fBounds.width(); 396 RunHead* head = fRunHead; 397 YOffset* yoff = head->yoffsets(); 398 YOffset* stop = yoff + head->fRowCount; 399 uint8_t* base = head->data(); 400 401 int leftZeros = width; 402 int riteZeros = width; 403 while (yoff < stop) { 404 int L, R; 405 count_left_right_zeros(base + yoff->fOffset, width, &L, &R); 406 if (L < leftZeros) { 407 leftZeros = L; 408 } 409 if (R < riteZeros) { 410 riteZeros = R; 411 } 412 if (0 == (leftZeros | riteZeros)) { 413 // no trimming to do 414 return true; 415 } 416 yoff += 1; 417 } 418 419 SkASSERT(leftZeros || riteZeros); 420 if (width == (leftZeros + riteZeros)) { 421 return this->setEmpty(); 422 } 423 424 this->validate(); 425 426 fBounds.fLeft += leftZeros; 427 fBounds.fRight -= riteZeros; 428 SkASSERT(!fBounds.isEmpty()); 429 430 // For now we don't realloc the storage (for time), we just shrink in place 431 // This means we don't have to do any memmoves either, since we can just 432 // play tricks with the yoff->fOffset for each row 433 yoff = head->yoffsets(); 434 while (yoff < stop) { 435 uint8_t* row = base + yoff->fOffset; 436 SkDEBUGCODE((void)compute_row_length(row, width);) 437 yoff->fOffset += trim_row_left_right(row, width, leftZeros, riteZeros); 438 SkDEBUGCODE((void)compute_row_length(base + yoff->fOffset, width - leftZeros - riteZeros);) 439 yoff += 1; 440 } 441 return true; 442} 443 444static bool row_is_all_zeros(const uint8_t* row, int width) { 445 SkASSERT(width > 0); 446 do { 447 if (row[1]) { 448 return false; 449 } 450 int n = row[0]; 451 SkASSERT(n <= width); 452 width -= n; 453 row += 2; 454 } while (width > 0); 455 SkASSERT(0 == width); 456 return true; 457} 458 459bool SkAAClip::trimTopBottom() { 460 if (this->isEmpty()) { 461 return false; 462 } 463 464 this->validate(); 465 466 const int width = fBounds.width(); 467 RunHead* head = fRunHead; 468 YOffset* yoff = head->yoffsets(); 469 YOffset* stop = yoff + head->fRowCount; 470 const uint8_t* base = head->data(); 471 472 // Look to trim away empty rows from the top. 473 // 474 int skip = 0; 475 while (yoff < stop) { 476 const uint8_t* data = base + yoff->fOffset; 477 if (!row_is_all_zeros(data, width)) { 478 break; 479 } 480 skip += 1; 481 yoff += 1; 482 } 483 SkASSERT(skip <= head->fRowCount); 484 if (skip == head->fRowCount) { 485 return this->setEmpty(); 486 } 487 if (skip > 0) { 488 // adjust fRowCount and fBounds.fTop, and slide all the data up 489 // as we remove [skip] number of YOffset entries 490 yoff = head->yoffsets(); 491 int dy = yoff[skip - 1].fY + 1; 492 for (int i = skip; i < head->fRowCount; ++i) { 493 SkASSERT(yoff[i].fY >= dy); 494 yoff[i].fY -= dy; 495 } 496 YOffset* dst = head->yoffsets(); 497 size_t size = head->fRowCount * sizeof(YOffset) + head->fDataSize; 498 memmove(dst, dst + skip, size - skip * sizeof(YOffset)); 499 500 fBounds.fTop += dy; 501 SkASSERT(!fBounds.isEmpty()); 502 head->fRowCount -= skip; 503 SkASSERT(head->fRowCount > 0); 504 505 this->validate(); 506 // need to reset this after the memmove 507 base = head->data(); 508 } 509 510 // Look to trim away empty rows from the bottom. 511 // We know that we have at least one non-zero row, so we can just walk 512 // backwards without checking for running past the start. 513 // 514 stop = yoff = head->yoffsets() + head->fRowCount; 515 do { 516 yoff -= 1; 517 } while (row_is_all_zeros(base + yoff->fOffset, width)); 518 skip = stop - yoff - 1; 519 SkASSERT(skip >= 0 && skip < head->fRowCount); 520 if (skip > 0) { 521 // removing from the bottom is easier than from the top, as we don't 522 // have to adjust any of the Y values, we just have to trim the array 523 memmove(stop - skip, stop, head->fDataSize); 524 525 fBounds.fBottom = fBounds.fTop + yoff->fY + 1; 526 SkASSERT(!fBounds.isEmpty()); 527 head->fRowCount -= skip; 528 SkASSERT(head->fRowCount > 0); 529 } 530 this->validate(); 531 532 return true; 533} 534 535// can't validate before we're done, since trimming is part of the process of 536// making us valid after the Builder. Since we build from top to bottom, its 537// possible our fBounds.fBottom is bigger than our last scanline of data, so 538// we trim fBounds.fBottom back up. 539// 540// TODO: check for duplicates in X and Y to further compress our data 541// 542bool SkAAClip::trimBounds() { 543 if (this->isEmpty()) { 544 return false; 545 } 546 547 const RunHead* head = fRunHead; 548 const YOffset* yoff = head->yoffsets(); 549 550 SkASSERT(head->fRowCount > 0); 551 const YOffset& lastY = yoff[head->fRowCount - 1]; 552 SkASSERT(lastY.fY + 1 <= fBounds.height()); 553 fBounds.fBottom = fBounds.fTop + lastY.fY + 1; 554 SkASSERT(lastY.fY + 1 == fBounds.height()); 555 SkASSERT(!fBounds.isEmpty()); 556 557 return this->trimTopBottom() && this->trimLeftRight(); 558} 559 560/////////////////////////////////////////////////////////////////////////////// 561 562void SkAAClip::freeRuns() { 563 if (fRunHead) { 564 SkASSERT(fRunHead->fRefCnt >= 1); 565 if (1 == sk_atomic_dec(&fRunHead->fRefCnt)) { 566 sk_free(fRunHead); 567 } 568 } 569} 570 571SkAAClip::SkAAClip() { 572 fBounds.setEmpty(); 573 fRunHead = NULL; 574} 575 576SkAAClip::SkAAClip(const SkAAClip& src) { 577 SkDEBUGCODE(fBounds.setEmpty();) // need this for validate 578 fRunHead = NULL; 579 *this = src; 580} 581 582SkAAClip::~SkAAClip() { 583 this->freeRuns(); 584} 585 586SkAAClip& SkAAClip::operator=(const SkAAClip& src) { 587 AUTO_AACLIP_VALIDATE(*this); 588 src.validate(); 589 590 if (this != &src) { 591 this->freeRuns(); 592 fBounds = src.fBounds; 593 fRunHead = src.fRunHead; 594 if (fRunHead) { 595 sk_atomic_inc(&fRunHead->fRefCnt); 596 } 597 } 598 return *this; 599} 600 601bool operator==(const SkAAClip& a, const SkAAClip& b) { 602 a.validate(); 603 b.validate(); 604 605 if (&a == &b) { 606 return true; 607 } 608 if (a.fBounds != b.fBounds) { 609 return false; 610 } 611 612 const SkAAClip::RunHead* ah = a.fRunHead; 613 const SkAAClip::RunHead* bh = b.fRunHead; 614 615 // this catches empties and rects being equal 616 if (ah == bh) { 617 return true; 618 } 619 620 // now we insist that both are complex (but different ptrs) 621 if (!a.fRunHead || !b.fRunHead) { 622 return false; 623 } 624 625 return ah->fRowCount == bh->fRowCount && 626 ah->fDataSize == bh->fDataSize && 627 !memcmp(ah->data(), bh->data(), ah->fDataSize); 628} 629 630void SkAAClip::swap(SkAAClip& other) { 631 AUTO_AACLIP_VALIDATE(*this); 632 other.validate(); 633 634 SkTSwap(fBounds, other.fBounds); 635 SkTSwap(fRunHead, other.fRunHead); 636} 637 638bool SkAAClip::set(const SkAAClip& src) { 639 *this = src; 640 return !this->isEmpty(); 641} 642 643bool SkAAClip::setEmpty() { 644 this->freeRuns(); 645 fBounds.setEmpty(); 646 fRunHead = NULL; 647 return false; 648} 649 650bool SkAAClip::setRect(const SkIRect& bounds) { 651 if (bounds.isEmpty()) { 652 return this->setEmpty(); 653 } 654 655 AUTO_AACLIP_VALIDATE(*this); 656 657#if 0 658 SkRect r; 659 r.set(bounds); 660 SkPath path; 661 path.addRect(r); 662 return this->setPath(path); 663#else 664 this->freeRuns(); 665 fBounds = bounds; 666 fRunHead = RunHead::AllocRect(bounds); 667 SkASSERT(!this->isEmpty()); 668 return true; 669#endif 670} 671 672bool SkAAClip::setRect(const SkRect& r, bool doAA) { 673 if (r.isEmpty()) { 674 return this->setEmpty(); 675 } 676 677 AUTO_AACLIP_VALIDATE(*this); 678 679 // TODO: special case this 680 681 SkPath path; 682 path.addRect(r); 683 return this->setPath(path, NULL, doAA); 684} 685 686static void append_run(SkTDArray<uint8_t>& array, uint8_t value, int count) { 687 SkASSERT(count >= 0); 688 while (count > 0) { 689 int n = count; 690 if (n > 255) { 691 n = 255; 692 } 693 uint8_t* data = array.append(2); 694 data[0] = n; 695 data[1] = value; 696 count -= n; 697 } 698} 699 700bool SkAAClip::setRegion(const SkRegion& rgn) { 701 if (rgn.isEmpty()) { 702 return this->setEmpty(); 703 } 704 if (rgn.isRect()) { 705 return this->setRect(rgn.getBounds()); 706 } 707 708#if 0 709 SkAAClip clip; 710 SkRegion::Iterator iter(rgn); 711 for (; !iter.done(); iter.next()) { 712 clip.op(iter.rect(), SkRegion::kUnion_Op); 713 } 714 this->swap(clip); 715 return !this->isEmpty(); 716#else 717 const SkIRect& bounds = rgn.getBounds(); 718 const int offsetX = bounds.fLeft; 719 const int offsetY = bounds.fTop; 720 721 SkTDArray<YOffset> yArray; 722 SkTDArray<uint8_t> xArray; 723 724 yArray.setReserve(SkMin32(bounds.height(), 1024)); 725 xArray.setReserve(SkMin32(bounds.width() * 128, 64 * 1024)); 726 727 SkRegion::Iterator iter(rgn); 728 int prevRight = 0; 729 int prevBot = 0; 730 YOffset* currY = NULL; 731 732 for (; !iter.done(); iter.next()) { 733 const SkIRect& r = iter.rect(); 734 SkASSERT(bounds.contains(r)); 735 736 int bot = r.fBottom - offsetY; 737 SkASSERT(bot >= prevBot); 738 if (bot > prevBot) { 739 if (currY) { 740 // flush current row 741 append_run(xArray, 0, bounds.width() - prevRight); 742 } 743 // did we introduce an empty-gap from the prev row? 744 int top = r.fTop - offsetY; 745 if (top > prevBot) { 746 currY = yArray.append(); 747 currY->fY = top - 1; 748 currY->fOffset = xArray.count(); 749 append_run(xArray, 0, bounds.width()); 750 } 751 // create a new record for this Y value 752 currY = yArray.append(); 753 currY->fY = bot - 1; 754 currY->fOffset = xArray.count(); 755 prevRight = 0; 756 prevBot = bot; 757 } 758 759 int x = r.fLeft - offsetX; 760 append_run(xArray, 0, x - prevRight); 761 762 int w = r.fRight - r.fLeft; 763 append_run(xArray, 0xFF, w); 764 prevRight = x + w; 765 SkASSERT(prevRight <= bounds.width()); 766 } 767 // flush last row 768 append_run(xArray, 0, bounds.width() - prevRight); 769 770 // now pack everything into a RunHead 771 RunHead* head = RunHead::Alloc(yArray.count(), xArray.bytes()); 772 memcpy(head->yoffsets(), yArray.begin(), yArray.bytes()); 773 memcpy(head->data(), xArray.begin(), xArray.bytes()); 774 775 this->setEmpty(); 776 fBounds = bounds; 777 fRunHead = head; 778 this->validate(); 779 return true; 780#endif 781} 782 783/////////////////////////////////////////////////////////////////////////////// 784 785const uint8_t* SkAAClip::findRow(int y, int* lastYForRow) const { 786 SkASSERT(fRunHead); 787 788 if (!y_in_rect(y, fBounds)) { 789 return NULL; 790 } 791 y -= fBounds.y(); // our yoffs values are relative to the top 792 793 const YOffset* yoff = fRunHead->yoffsets(); 794 while (yoff->fY < y) { 795 yoff += 1; 796 SkASSERT(yoff - fRunHead->yoffsets() < fRunHead->fRowCount); 797 } 798 799 if (lastYForRow) { 800 *lastYForRow = fBounds.y() + yoff->fY; 801 } 802 return fRunHead->data() + yoff->fOffset; 803} 804 805const uint8_t* SkAAClip::findX(const uint8_t data[], int x, int* initialCount) const { 806 SkASSERT(x_in_rect(x, fBounds)); 807 x -= fBounds.x(); 808 809 // first skip up to X 810 for (;;) { 811 int n = data[0]; 812 if (x < n) { 813 if (initialCount) { 814 *initialCount = n - x; 815 } 816 break; 817 } 818 data += 2; 819 x -= n; 820 } 821 return data; 822} 823 824bool SkAAClip::quickContains(int left, int top, int right, int bottom) const { 825 if (this->isEmpty()) { 826 return false; 827 } 828 if (!fBounds.contains(left, top, right, bottom)) { 829 return false; 830 } 831#if 0 832 if (this->isRect()) { 833 return true; 834 } 835#endif 836 837 int lastY SK_INIT_TO_AVOID_WARNING; 838 const uint8_t* row = this->findRow(top, &lastY); 839 if (lastY < bottom) { 840 return false; 841 } 842 // now just need to check in X 843 int count; 844 row = this->findX(row, left, &count); 845#if 0 846 return count >= (right - left) && 0xFF == row[1]; 847#else 848 int rectWidth = right - left; 849 while (0xFF == row[1]) { 850 if (count >= rectWidth) { 851 return true; 852 } 853 rectWidth -= count; 854 row += 2; 855 count = row[0]; 856 } 857 return false; 858#endif 859} 860 861/////////////////////////////////////////////////////////////////////////////// 862 863class SkAAClip::Builder { 864 SkIRect fBounds; 865 struct Row { 866 int fY; 867 int fWidth; 868 SkTDArray<uint8_t>* fData; 869 }; 870 SkTDArray<Row> fRows; 871 Row* fCurrRow; 872 int fPrevY; 873 int fWidth; 874 int fMinY; 875 876public: 877 Builder(const SkIRect& bounds) : fBounds(bounds) { 878 fPrevY = -1; 879 fWidth = bounds.width(); 880 fCurrRow = NULL; 881 fMinY = bounds.fTop; 882 } 883 884 ~Builder() { 885 Row* row = fRows.begin(); 886 Row* stop = fRows.end(); 887 while (row < stop) { 888 delete row->fData; 889 row += 1; 890 } 891 } 892 893 const SkIRect& getBounds() const { return fBounds; } 894 895 void addRun(int x, int y, U8CPU alpha, int count) { 896 SkASSERT(count > 0); 897 SkASSERT(fBounds.contains(x, y)); 898 SkASSERT(fBounds.contains(x + count - 1, y)); 899 900 x -= fBounds.left(); 901 y -= fBounds.top(); 902 903 Row* row = fCurrRow; 904 if (y != fPrevY) { 905 SkASSERT(y > fPrevY); 906 fPrevY = y; 907 row = this->flushRow(true); 908 row->fY = y; 909 row->fWidth = 0; 910 SkASSERT(row->fData); 911 SkASSERT(0 == row->fData->count()); 912 fCurrRow = row; 913 } 914 915 SkASSERT(row->fWidth <= x); 916 SkASSERT(row->fWidth < fBounds.width()); 917 918 SkTDArray<uint8_t>& data = *row->fData; 919 920 int gap = x - row->fWidth; 921 if (gap) { 922 AppendRun(data, 0, gap); 923 row->fWidth += gap; 924 SkASSERT(row->fWidth < fBounds.width()); 925 } 926 927 AppendRun(data, alpha, count); 928 row->fWidth += count; 929 SkASSERT(row->fWidth <= fBounds.width()); 930 } 931 932 void addColumn(int x, int y, U8CPU alpha, int height) { 933 SkASSERT(fBounds.contains(x, y + height - 1)); 934 935 this->addRun(x, y, alpha, 1); 936 this->flushRowH(fCurrRow); 937 y -= fBounds.fTop; 938 SkASSERT(y == fCurrRow->fY); 939 fCurrRow->fY = y + height - 1; 940 } 941 942 void addRectRun(int x, int y, int width, int height) { 943 SkASSERT(fBounds.contains(x + width - 1, y + height - 1)); 944 this->addRun(x, y, 0xFF, width); 945 946 // we assum the rect must be all we'll see for these scanlines 947 // so we ensure our row goes all the way to our right 948 this->flushRowH(fCurrRow); 949 950 y -= fBounds.fTop; 951 SkASSERT(y == fCurrRow->fY); 952 fCurrRow->fY = y + height - 1; 953 } 954 955 void addAntiRectRun(int x, int y, int width, int height, 956 SkAlpha leftAlpha, SkAlpha rightAlpha) { 957 SkASSERT(fBounds.contains(x + width - 1 + 958 (leftAlpha > 0 ? 1 : 0) + (rightAlpha > 0 ? 1 : 0), 959 y + height - 1)); 960 SkASSERT(width >= 0); 961 962 // Conceptually we're always adding 3 runs, but we should 963 // merge or omit them if possible. 964 if (leftAlpha == 0xFF) { 965 width++; 966 } else if (leftAlpha > 0) { 967 this->addRun(x++, y, leftAlpha, 1); 968 } 969 if (rightAlpha == 0xFF) { 970 width++; 971 } 972 if (width > 0) { 973 this->addRun(x, y, 0xFF, width); 974 } 975 if (rightAlpha > 0 && rightAlpha < 255) { 976 this->addRun(x + width, y, rightAlpha, 1); 977 } 978 979 // we assume the rect must be all we'll see for these scanlines 980 // so we ensure our row goes all the way to our right 981 this->flushRowH(fCurrRow); 982 983 y -= fBounds.fTop; 984 SkASSERT(y == fCurrRow->fY); 985 fCurrRow->fY = y + height - 1; 986 } 987 988 bool finish(SkAAClip* target) { 989 this->flushRow(false); 990 991 const Row* row = fRows.begin(); 992 const Row* stop = fRows.end(); 993 994 size_t dataSize = 0; 995 while (row < stop) { 996 dataSize += row->fData->count(); 997 row += 1; 998 } 999 1000 if (0 == dataSize) { 1001 return target->setEmpty(); 1002 } 1003 1004 SkASSERT(fMinY >= fBounds.fTop); 1005 SkASSERT(fMinY < fBounds.fBottom); 1006 int adjustY = fMinY - fBounds.fTop; 1007 fBounds.fTop = fMinY; 1008 1009 RunHead* head = RunHead::Alloc(fRows.count(), dataSize); 1010 YOffset* yoffset = head->yoffsets(); 1011 uint8_t* data = head->data(); 1012 uint8_t* baseData = data; 1013 1014 row = fRows.begin(); 1015 SkDEBUGCODE(int prevY = row->fY - 1;) 1016 while (row < stop) { 1017 SkASSERT(prevY < row->fY); // must be monotonic 1018 SkDEBUGCODE(prevY = row->fY); 1019 1020 yoffset->fY = row->fY - adjustY; 1021 yoffset->fOffset = data - baseData; 1022 yoffset += 1; 1023 1024 size_t n = row->fData->count(); 1025 memcpy(data, row->fData->begin(), n); 1026#ifdef SK_DEBUG 1027 size_t bytesNeeded = compute_row_length(data, fBounds.width()); 1028 SkASSERT(bytesNeeded == n); 1029#endif 1030 data += n; 1031 1032 row += 1; 1033 } 1034 1035 target->freeRuns(); 1036 target->fBounds = fBounds; 1037 target->fRunHead = head; 1038 return target->trimBounds(); 1039 } 1040 1041 void dump() { 1042 this->validate(); 1043 int y; 1044 for (y = 0; y < fRows.count(); ++y) { 1045 const Row& row = fRows[y]; 1046 SkDebugf("Y:%3d W:%3d", row.fY, row.fWidth); 1047 const SkTDArray<uint8_t>& data = *row.fData; 1048 int count = data.count(); 1049 SkASSERT(!(count & 1)); 1050 const uint8_t* ptr = data.begin(); 1051 for (int x = 0; x < count; x += 2) { 1052 SkDebugf(" [%3d:%02X]", ptr[0], ptr[1]); 1053 ptr += 2; 1054 } 1055 SkDebugf("\n"); 1056 } 1057 } 1058 1059 void validate() { 1060#ifdef SK_DEBUG 1061 if (false) { // avoid bit rot, suppress warning 1062 test_count_left_right_zeros(); 1063 } 1064 int prevY = -1; 1065 for (int i = 0; i < fRows.count(); ++i) { 1066 const Row& row = fRows[i]; 1067 SkASSERT(prevY < row.fY); 1068 SkASSERT(fWidth == row.fWidth); 1069 int count = row.fData->count(); 1070 const uint8_t* ptr = row.fData->begin(); 1071 SkASSERT(!(count & 1)); 1072 int w = 0; 1073 for (int x = 0; x < count; x += 2) { 1074 int n = ptr[0]; 1075 SkASSERT(n > 0); 1076 w += n; 1077 SkASSERT(w <= fWidth); 1078 ptr += 2; 1079 } 1080 SkASSERT(w == fWidth); 1081 prevY = row.fY; 1082 } 1083#endif 1084 } 1085 1086 // only called by BuilderBlitter 1087 void setMinY(int y) { 1088 fMinY = y; 1089 } 1090 1091private: 1092 void flushRowH(Row* row) { 1093 // flush current row if needed 1094 if (row->fWidth < fWidth) { 1095 AppendRun(*row->fData, 0, fWidth - row->fWidth); 1096 row->fWidth = fWidth; 1097 } 1098 } 1099 1100 Row* flushRow(bool readyForAnother) { 1101 Row* next = NULL; 1102 int count = fRows.count(); 1103 if (count > 0) { 1104 this->flushRowH(&fRows[count - 1]); 1105 } 1106 if (count > 1) { 1107 // are our last two runs the same? 1108 Row* prev = &fRows[count - 2]; 1109 Row* curr = &fRows[count - 1]; 1110 SkASSERT(prev->fWidth == fWidth); 1111 SkASSERT(curr->fWidth == fWidth); 1112 if (*prev->fData == *curr->fData) { 1113 prev->fY = curr->fY; 1114 if (readyForAnother) { 1115 curr->fData->rewind(); 1116 next = curr; 1117 } else { 1118 delete curr->fData; 1119 fRows.removeShuffle(count - 1); 1120 } 1121 } else { 1122 if (readyForAnother) { 1123 next = fRows.append(); 1124 next->fData = new SkTDArray<uint8_t>; 1125 } 1126 } 1127 } else { 1128 if (readyForAnother) { 1129 next = fRows.append(); 1130 next->fData = new SkTDArray<uint8_t>; 1131 } 1132 } 1133 return next; 1134 } 1135 1136 static void AppendRun(SkTDArray<uint8_t>& data, U8CPU alpha, int count) { 1137 do { 1138 int n = count; 1139 if (n > 255) { 1140 n = 255; 1141 } 1142 uint8_t* ptr = data.append(2); 1143 ptr[0] = n; 1144 ptr[1] = alpha; 1145 count -= n; 1146 } while (count > 0); 1147 } 1148}; 1149 1150class SkAAClip::BuilderBlitter : public SkBlitter { 1151 int fLastY; 1152 1153 /* 1154 If we see a gap of 1 or more empty scanlines while building in Y-order, 1155 we inject an explicit empty scanline (alpha==0) 1156 1157 See AAClipTest.cpp : test_path_with_hole() 1158 */ 1159 void checkForYGap(int y) { 1160 SkASSERT(y >= fLastY); 1161 if (fLastY > -SK_MaxS32) { 1162 int gap = y - fLastY; 1163 if (gap > 1) { 1164 fBuilder->addRun(fLeft, y - 1, 0, fRight - fLeft); 1165 } 1166 } 1167 fLastY = y; 1168 } 1169 1170public: 1171 1172 BuilderBlitter(Builder* builder) { 1173 fBuilder = builder; 1174 fLeft = builder->getBounds().fLeft; 1175 fRight = builder->getBounds().fRight; 1176 fMinY = SK_MaxS32; 1177 fLastY = -SK_MaxS32; // sentinel 1178 } 1179 1180 void finish() { 1181 if (fMinY < SK_MaxS32) { 1182 fBuilder->setMinY(fMinY); 1183 } 1184 } 1185 1186 /** 1187 Must evaluate clips in scan-line order, so don't want to allow blitV(), 1188 but an AAClip can be clipped down to a single pixel wide, so we 1189 must support it (given AntiRect semantics: minimum width is 2). 1190 Instead we'll rely on the runtime asserts to guarantee Y monotonicity; 1191 any failure cases that misses may have minor artifacts. 1192 */ 1193 virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE { 1194 this->recordMinY(y); 1195 fBuilder->addColumn(x, y, alpha, height); 1196 fLastY = y + height - 1; 1197 } 1198 1199 virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE { 1200 this->recordMinY(y); 1201 this->checkForYGap(y); 1202 fBuilder->addRectRun(x, y, width, height); 1203 fLastY = y + height - 1; 1204 } 1205 1206 virtual void blitAntiRect(int x, int y, int width, int height, 1207 SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE { 1208 this->recordMinY(y); 1209 this->checkForYGap(y); 1210 fBuilder->addAntiRectRun(x, y, width, height, leftAlpha, rightAlpha); 1211 fLastY = y + height - 1; 1212 } 1213 1214 virtual void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE 1215 { unexpected(); } 1216 1217 virtual const SkBitmap* justAnOpaqueColor(uint32_t*) SK_OVERRIDE { 1218 return NULL; 1219 } 1220 1221 virtual void blitH(int x, int y, int width) SK_OVERRIDE { 1222 this->recordMinY(y); 1223 this->checkForYGap(y); 1224 fBuilder->addRun(x, y, 0xFF, width); 1225 } 1226 1227 virtual void blitAntiH(int x, int y, const SkAlpha alpha[], 1228 const int16_t runs[]) SK_OVERRIDE { 1229 this->recordMinY(y); 1230 this->checkForYGap(y); 1231 for (;;) { 1232 int count = *runs; 1233 if (count <= 0) { 1234 return; 1235 } 1236 1237 // The supersampler's buffer can be the width of the device, so 1238 // we may have to trim the run to our bounds. If so, we assert that 1239 // the extra spans are always alpha==0 1240 int localX = x; 1241 int localCount = count; 1242 if (x < fLeft) { 1243 SkASSERT(0 == *alpha); 1244 int gap = fLeft - x; 1245 SkASSERT(gap <= count); 1246 localX += gap; 1247 localCount -= gap; 1248 } 1249 int right = x + count; 1250 if (right > fRight) { 1251 SkASSERT(0 == *alpha); 1252 localCount -= right - fRight; 1253 SkASSERT(localCount >= 0); 1254 } 1255 1256 if (localCount) { 1257 fBuilder->addRun(localX, y, *alpha, localCount); 1258 } 1259 // Next run 1260 runs += count; 1261 alpha += count; 1262 x += count; 1263 } 1264 } 1265 1266private: 1267 Builder* fBuilder; 1268 int fLeft; // cache of builder's bounds' left edge 1269 int fRight; 1270 int fMinY; 1271 1272 /* 1273 * We track this, in case the scan converter skipped some number of 1274 * scanlines at the (relative to the bounds it was given). This allows 1275 * the builder, during its finish, to trip its bounds down to the "real" 1276 * top. 1277 */ 1278 void recordMinY(int y) { 1279 if (y < fMinY) { 1280 fMinY = y; 1281 } 1282 } 1283 1284 void unexpected() { 1285 SkDebugf("---- did not expect to get called here"); 1286 sk_throw(); 1287 } 1288}; 1289 1290bool SkAAClip::setPath(const SkPath& path, const SkRegion* clip, bool doAA) { 1291 AUTO_AACLIP_VALIDATE(*this); 1292 1293 if (clip && clip->isEmpty()) { 1294 return this->setEmpty(); 1295 } 1296 1297 SkIRect ibounds; 1298 path.getBounds().roundOut(&ibounds); 1299 1300 SkRegion tmpClip; 1301 if (NULL == clip) { 1302 tmpClip.setRect(ibounds); 1303 clip = &tmpClip; 1304 } 1305 1306 if (path.isInverseFillType()) { 1307 ibounds = clip->getBounds(); 1308 } else { 1309 if (ibounds.isEmpty() || !ibounds.intersect(clip->getBounds())) { 1310 return this->setEmpty(); 1311 } 1312 } 1313 1314 Builder builder(ibounds); 1315 BuilderBlitter blitter(&builder); 1316 1317 if (doAA) { 1318 SkScan::AntiFillPath(path, *clip, &blitter, true); 1319 } else { 1320 SkScan::FillPath(path, *clip, &blitter); 1321 } 1322 1323 blitter.finish(); 1324 return builder.finish(this); 1325} 1326 1327/////////////////////////////////////////////////////////////////////////////// 1328 1329typedef void (*RowProc)(SkAAClip::Builder&, int bottom, 1330 const uint8_t* rowA, const SkIRect& rectA, 1331 const uint8_t* rowB, const SkIRect& rectB); 1332 1333typedef U8CPU (*AlphaProc)(U8CPU alphaA, U8CPU alphaB); 1334 1335static U8CPU sectAlphaProc(U8CPU alphaA, U8CPU alphaB) { 1336 // Multiply 1337 return SkMulDiv255Round(alphaA, alphaB); 1338} 1339 1340static U8CPU unionAlphaProc(U8CPU alphaA, U8CPU alphaB) { 1341 // SrcOver 1342 return alphaA + alphaB - SkMulDiv255Round(alphaA, alphaB); 1343} 1344 1345static U8CPU diffAlphaProc(U8CPU alphaA, U8CPU alphaB) { 1346 // SrcOut 1347 return SkMulDiv255Round(alphaA, 0xFF - alphaB); 1348} 1349 1350static U8CPU xorAlphaProc(U8CPU alphaA, U8CPU alphaB) { 1351 // XOR 1352 return alphaA + alphaB - 2 * SkMulDiv255Round(alphaA, alphaB); 1353} 1354 1355static AlphaProc find_alpha_proc(SkRegion::Op op) { 1356 switch (op) { 1357 case SkRegion::kIntersect_Op: 1358 return sectAlphaProc; 1359 case SkRegion::kDifference_Op: 1360 return diffAlphaProc; 1361 case SkRegion::kUnion_Op: 1362 return unionAlphaProc; 1363 case SkRegion::kXOR_Op: 1364 return xorAlphaProc; 1365 default: 1366 SkDEBUGFAIL("unexpected region op"); 1367 return sectAlphaProc; 1368 } 1369} 1370 1371static const uint8_t gEmptyRow[] = { 1372 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 1373 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 1374 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 1375 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 1376 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 1377 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 1378 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 1379 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 1380}; 1381 1382class RowIter { 1383public: 1384 RowIter(const uint8_t* row, const SkIRect& bounds) { 1385 fRow = row; 1386 fLeft = bounds.fLeft; 1387 fBoundsRight = bounds.fRight; 1388 if (row) { 1389 fRight = bounds.fLeft + row[0]; 1390 SkASSERT(fRight <= fBoundsRight); 1391 fAlpha = row[1]; 1392 fDone = false; 1393 } else { 1394 fDone = true; 1395 fRight = kMaxInt32; 1396 fAlpha = 0; 1397 } 1398 } 1399 1400 bool done() const { return fDone; } 1401 int left() const { return fLeft; } 1402 int right() const { return fRight; } 1403 U8CPU alpha() const { return fAlpha; } 1404 void next() { 1405 if (!fDone) { 1406 fLeft = fRight; 1407 if (fRight == fBoundsRight) { 1408 fDone = true; 1409 fRight = kMaxInt32; 1410 fAlpha = 0; 1411 } else { 1412 fRow += 2; 1413 fRight += fRow[0]; 1414 fAlpha = fRow[1]; 1415 SkASSERT(fRight <= fBoundsRight); 1416 } 1417 } 1418 } 1419 1420private: 1421 const uint8_t* fRow; 1422 int fLeft; 1423 int fRight; 1424 int fBoundsRight; 1425 bool fDone; 1426 uint8_t fAlpha; 1427}; 1428 1429static void adjust_row(RowIter& iter, int& leftA, int& riteA, int rite) { 1430 if (rite == riteA) { 1431 iter.next(); 1432 leftA = iter.left(); 1433 riteA = iter.right(); 1434 } 1435} 1436 1437#if 0 // UNUSED 1438static bool intersect(int& min, int& max, int boundsMin, int boundsMax) { 1439 SkASSERT(min < max); 1440 SkASSERT(boundsMin < boundsMax); 1441 if (min >= boundsMax || max <= boundsMin) { 1442 return false; 1443 } 1444 if (min < boundsMin) { 1445 min = boundsMin; 1446 } 1447 if (max > boundsMax) { 1448 max = boundsMax; 1449 } 1450 return true; 1451} 1452#endif 1453 1454static void operatorX(SkAAClip::Builder& builder, int lastY, 1455 RowIter& iterA, RowIter& iterB, 1456 AlphaProc proc, const SkIRect& bounds) { 1457 int leftA = iterA.left(); 1458 int riteA = iterA.right(); 1459 int leftB = iterB.left(); 1460 int riteB = iterB.right(); 1461 1462 int prevRite = bounds.fLeft; 1463 1464 do { 1465 U8CPU alphaA = 0; 1466 U8CPU alphaB = 0; 1467 int left, rite; 1468 1469 if (leftA < leftB) { 1470 left = leftA; 1471 alphaA = iterA.alpha(); 1472 if (riteA <= leftB) { 1473 rite = riteA; 1474 } else { 1475 rite = leftA = leftB; 1476 } 1477 } else if (leftB < leftA) { 1478 left = leftB; 1479 alphaB = iterB.alpha(); 1480 if (riteB <= leftA) { 1481 rite = riteB; 1482 } else { 1483 rite = leftB = leftA; 1484 } 1485 } else { 1486 left = leftA; // or leftB, since leftA == leftB 1487 rite = leftA = leftB = SkMin32(riteA, riteB); 1488 alphaA = iterA.alpha(); 1489 alphaB = iterB.alpha(); 1490 } 1491 1492 if (left >= bounds.fRight) { 1493 break; 1494 } 1495 if (rite > bounds.fRight) { 1496 rite = bounds.fRight; 1497 } 1498 1499 if (left >= bounds.fLeft) { 1500 SkASSERT(rite > left); 1501 builder.addRun(left, lastY, proc(alphaA, alphaB), rite - left); 1502 prevRite = rite; 1503 } 1504 1505 adjust_row(iterA, leftA, riteA, rite); 1506 adjust_row(iterB, leftB, riteB, rite); 1507 } while (!iterA.done() || !iterB.done()); 1508 1509 if (prevRite < bounds.fRight) { 1510 builder.addRun(prevRite, lastY, 0, bounds.fRight - prevRite); 1511 } 1512} 1513 1514static void adjust_iter(SkAAClip::Iter& iter, int& topA, int& botA, int bot) { 1515 if (bot == botA) { 1516 iter.next(); 1517 topA = botA; 1518 SkASSERT(botA == iter.top()); 1519 botA = iter.bottom(); 1520 } 1521} 1522 1523static void operateY(SkAAClip::Builder& builder, const SkAAClip& A, 1524 const SkAAClip& B, SkRegion::Op op) { 1525 AlphaProc proc = find_alpha_proc(op); 1526 const SkIRect& bounds = builder.getBounds(); 1527 1528 SkAAClip::Iter iterA(A); 1529 SkAAClip::Iter iterB(B); 1530 1531 SkASSERT(!iterA.done()); 1532 int topA = iterA.top(); 1533 int botA = iterA.bottom(); 1534 SkASSERT(!iterB.done()); 1535 int topB = iterB.top(); 1536 int botB = iterB.bottom(); 1537 1538 do { 1539 const uint8_t* rowA = NULL; 1540 const uint8_t* rowB = NULL; 1541 int top, bot; 1542 1543 if (topA < topB) { 1544 top = topA; 1545 rowA = iterA.data(); 1546 if (botA <= topB) { 1547 bot = botA; 1548 } else { 1549 bot = topA = topB; 1550 } 1551 1552 } else if (topB < topA) { 1553 top = topB; 1554 rowB = iterB.data(); 1555 if (botB <= topA) { 1556 bot = botB; 1557 } else { 1558 bot = topB = topA; 1559 } 1560 } else { 1561 top = topA; // or topB, since topA == topB 1562 bot = topA = topB = SkMin32(botA, botB); 1563 rowA = iterA.data(); 1564 rowB = iterB.data(); 1565 } 1566 1567 if (top >= bounds.fBottom) { 1568 break; 1569 } 1570 1571 if (bot > bounds.fBottom) { 1572 bot = bounds.fBottom; 1573 } 1574 SkASSERT(top < bot); 1575 1576 if (!rowA && !rowB) { 1577 builder.addRun(bounds.fLeft, bot - 1, 0, bounds.width()); 1578 } else if (top >= bounds.fTop) { 1579 SkASSERT(bot <= bounds.fBottom); 1580 RowIter rowIterA(rowA, rowA ? A.getBounds() : bounds); 1581 RowIter rowIterB(rowB, rowB ? B.getBounds() : bounds); 1582 operatorX(builder, bot - 1, rowIterA, rowIterB, proc, bounds); 1583 } 1584 1585 adjust_iter(iterA, topA, botA, bot); 1586 adjust_iter(iterB, topB, botB, bot); 1587 } while (!iterA.done() || !iterB.done()); 1588} 1589 1590bool SkAAClip::op(const SkAAClip& clipAOrig, const SkAAClip& clipBOrig, 1591 SkRegion::Op op) { 1592 AUTO_AACLIP_VALIDATE(*this); 1593 1594 if (SkRegion::kReplace_Op == op) { 1595 return this->set(clipBOrig); 1596 } 1597 1598 const SkAAClip* clipA = &clipAOrig; 1599 const SkAAClip* clipB = &clipBOrig; 1600 1601 if (SkRegion::kReverseDifference_Op == op) { 1602 SkTSwap(clipA, clipB); 1603 op = SkRegion::kDifference_Op; 1604 } 1605 1606 bool a_empty = clipA->isEmpty(); 1607 bool b_empty = clipB->isEmpty(); 1608 1609 SkIRect bounds; 1610 switch (op) { 1611 case SkRegion::kDifference_Op: 1612 if (a_empty) { 1613 return this->setEmpty(); 1614 } 1615 if (b_empty || !SkIRect::Intersects(clipA->fBounds, clipB->fBounds)) { 1616 return this->set(*clipA); 1617 } 1618 bounds = clipA->fBounds; 1619 break; 1620 1621 case SkRegion::kIntersect_Op: 1622 if ((a_empty | b_empty) || !bounds.intersect(clipA->fBounds, 1623 clipB->fBounds)) { 1624 return this->setEmpty(); 1625 } 1626 break; 1627 1628 case SkRegion::kUnion_Op: 1629 case SkRegion::kXOR_Op: 1630 if (a_empty) { 1631 return this->set(*clipB); 1632 } 1633 if (b_empty) { 1634 return this->set(*clipA); 1635 } 1636 bounds = clipA->fBounds; 1637 bounds.join(clipB->fBounds); 1638 break; 1639 1640 default: 1641 SkDEBUGFAIL("unknown region op"); 1642 return !this->isEmpty(); 1643 } 1644 1645 SkASSERT(SkIRect::Intersects(bounds, clipB->fBounds)); 1646 SkASSERT(SkIRect::Intersects(bounds, clipB->fBounds)); 1647 1648 Builder builder(bounds); 1649 operateY(builder, *clipA, *clipB, op); 1650 1651 return builder.finish(this); 1652} 1653 1654/* 1655 * It can be expensive to build a local aaclip before applying the op, so 1656 * we first see if we can restrict the bounds of new rect to our current 1657 * bounds, or note that the new rect subsumes our current clip. 1658 */ 1659 1660bool SkAAClip::op(const SkIRect& rOrig, SkRegion::Op op) { 1661 SkIRect rStorage; 1662 const SkIRect* r = &rOrig; 1663 1664 switch (op) { 1665 case SkRegion::kIntersect_Op: 1666 if (!rStorage.intersect(rOrig, fBounds)) { 1667 // no overlap, so we're empty 1668 return this->setEmpty(); 1669 } 1670 if (rStorage == fBounds) { 1671 // we were wholly inside the rect, no change 1672 return !this->isEmpty(); 1673 } 1674 if (this->quickContains(rStorage)) { 1675 // the intersection is wholly inside us, we're a rect 1676 return this->setRect(rStorage); 1677 } 1678 r = &rStorage; // use the intersected bounds 1679 break; 1680 case SkRegion::kDifference_Op: 1681 break; 1682 case SkRegion::kUnion_Op: 1683 if (rOrig.contains(fBounds)) { 1684 return this->setRect(rOrig); 1685 } 1686 break; 1687 default: 1688 break; 1689 } 1690 1691 SkAAClip clip; 1692 clip.setRect(*r); 1693 return this->op(*this, clip, op); 1694} 1695 1696bool SkAAClip::op(const SkRect& rOrig, SkRegion::Op op, bool doAA) { 1697 SkRect rStorage, boundsStorage; 1698 const SkRect* r = &rOrig; 1699 1700 boundsStorage.set(fBounds); 1701 switch (op) { 1702 case SkRegion::kIntersect_Op: 1703 case SkRegion::kDifference_Op: 1704 if (!rStorage.intersect(rOrig, boundsStorage)) { 1705 if (SkRegion::kIntersect_Op == op) { 1706 return this->setEmpty(); 1707 } else { // kDifference 1708 return !this->isEmpty(); 1709 } 1710 } 1711 r = &rStorage; // use the intersected bounds 1712 break; 1713 case SkRegion::kUnion_Op: 1714 if (rOrig.contains(boundsStorage)) { 1715 return this->setRect(rOrig); 1716 } 1717 break; 1718 default: 1719 break; 1720 } 1721 1722 SkAAClip clip; 1723 clip.setRect(*r, doAA); 1724 return this->op(*this, clip, op); 1725} 1726 1727bool SkAAClip::op(const SkAAClip& clip, SkRegion::Op op) { 1728 return this->op(*this, clip, op); 1729} 1730 1731/////////////////////////////////////////////////////////////////////////////// 1732 1733bool SkAAClip::translate(int dx, int dy, SkAAClip* dst) const { 1734 if (NULL == dst) { 1735 return !this->isEmpty(); 1736 } 1737 1738 if (this->isEmpty()) { 1739 return dst->setEmpty(); 1740 } 1741 1742 if (this != dst) { 1743 sk_atomic_inc(&fRunHead->fRefCnt); 1744 dst->freeRuns(); 1745 dst->fRunHead = fRunHead; 1746 dst->fBounds = fBounds; 1747 } 1748 dst->fBounds.offset(dx, dy); 1749 return true; 1750} 1751 1752static void expand_row_to_mask(uint8_t* SK_RESTRICT mask, 1753 const uint8_t* SK_RESTRICT row, 1754 int width) { 1755 while (width > 0) { 1756 int n = row[0]; 1757 SkASSERT(width >= n); 1758 memset(mask, row[1], n); 1759 mask += n; 1760 row += 2; 1761 width -= n; 1762 } 1763 SkASSERT(0 == width); 1764} 1765 1766void SkAAClip::copyToMask(SkMask* mask) const { 1767 mask->fFormat = SkMask::kA8_Format; 1768 if (this->isEmpty()) { 1769 mask->fBounds.setEmpty(); 1770 mask->fImage = NULL; 1771 mask->fRowBytes = 0; 1772 return; 1773 } 1774 1775 mask->fBounds = fBounds; 1776 mask->fRowBytes = fBounds.width(); 1777 size_t size = mask->computeImageSize(); 1778 mask->fImage = SkMask::AllocImage(size); 1779 1780 Iter iter(*this); 1781 uint8_t* dst = mask->fImage; 1782 const int width = fBounds.width(); 1783 1784 int y = fBounds.fTop; 1785 while (!iter.done()) { 1786 do { 1787 expand_row_to_mask(dst, iter.data(), width); 1788 dst += mask->fRowBytes; 1789 } while (++y < iter.bottom()); 1790 iter.next(); 1791 } 1792} 1793 1794/////////////////////////////////////////////////////////////////////////////// 1795/////////////////////////////////////////////////////////////////////////////// 1796 1797static void expandToRuns(const uint8_t* SK_RESTRICT data, int initialCount, int width, 1798 int16_t* SK_RESTRICT runs, SkAlpha* SK_RESTRICT aa) { 1799 // we don't read our initial n from data, since the caller may have had to 1800 // clip it, hence the initialCount parameter. 1801 int n = initialCount; 1802 for (;;) { 1803 if (n > width) { 1804 n = width; 1805 } 1806 SkASSERT(n > 0); 1807 runs[0] = n; 1808 runs += n; 1809 1810 aa[0] = data[1]; 1811 aa += n; 1812 1813 data += 2; 1814 width -= n; 1815 if (0 == width) { 1816 break; 1817 } 1818 // load the next count 1819 n = data[0]; 1820 } 1821 runs[0] = 0; // sentinel 1822} 1823 1824SkAAClipBlitter::~SkAAClipBlitter() { 1825 sk_free(fScanlineScratch); 1826} 1827 1828void SkAAClipBlitter::ensureRunsAndAA() { 1829 if (NULL == fScanlineScratch) { 1830 // add 1 so we can store the terminating run count of 0 1831 int count = fAAClipBounds.width() + 1; 1832 // we use this either for fRuns + fAA, or a scaline of a mask 1833 // which may be as deep as 32bits 1834 fScanlineScratch = sk_malloc_throw(count * sizeof(SkPMColor)); 1835 fRuns = (int16_t*)fScanlineScratch; 1836 fAA = (SkAlpha*)(fRuns + count); 1837 } 1838} 1839 1840void SkAAClipBlitter::blitH(int x, int y, int width) { 1841 SkASSERT(width > 0); 1842 SkASSERT(fAAClipBounds.contains(x, y)); 1843 SkASSERT(fAAClipBounds.contains(x + width - 1, y)); 1844 1845 const uint8_t* row = fAAClip->findRow(y); 1846 int initialCount; 1847 row = fAAClip->findX(row, x, &initialCount); 1848 1849 if (initialCount >= width) { 1850 SkAlpha alpha = row[1]; 1851 if (0 == alpha) { 1852 return; 1853 } 1854 if (0xFF == alpha) { 1855 fBlitter->blitH(x, y, width); 1856 return; 1857 } 1858 } 1859 1860 this->ensureRunsAndAA(); 1861 expandToRuns(row, initialCount, width, fRuns, fAA); 1862 1863 fBlitter->blitAntiH(x, y, fAA, fRuns); 1864} 1865 1866static void merge(const uint8_t* SK_RESTRICT row, int rowN, 1867 const SkAlpha* SK_RESTRICT srcAA, 1868 const int16_t* SK_RESTRICT srcRuns, 1869 SkAlpha* SK_RESTRICT dstAA, 1870 int16_t* SK_RESTRICT dstRuns, 1871 int width) { 1872 SkDEBUGCODE(int accumulated = 0;) 1873 int srcN = srcRuns[0]; 1874 // do we need this check? 1875 if (0 == srcN) { 1876 return; 1877 } 1878 1879 for (;;) { 1880 SkASSERT(rowN > 0); 1881 SkASSERT(srcN > 0); 1882 1883 unsigned newAlpha = SkMulDiv255Round(srcAA[0], row[1]); 1884 int minN = SkMin32(srcN, rowN); 1885 dstRuns[0] = minN; 1886 dstRuns += minN; 1887 dstAA[0] = newAlpha; 1888 dstAA += minN; 1889 1890 if (0 == (srcN -= minN)) { 1891 srcN = srcRuns[0]; // refresh 1892 srcRuns += srcN; 1893 srcAA += srcN; 1894 srcN = srcRuns[0]; // reload 1895 if (0 == srcN) { 1896 break; 1897 } 1898 } 1899 if (0 == (rowN -= minN)) { 1900 row += 2; 1901 rowN = row[0]; // reload 1902 } 1903 1904 SkDEBUGCODE(accumulated += minN;) 1905 SkASSERT(accumulated <= width); 1906 } 1907 dstRuns[0] = 0; 1908} 1909 1910void SkAAClipBlitter::blitAntiH(int x, int y, const SkAlpha aa[], 1911 const int16_t runs[]) { 1912 1913 const uint8_t* row = fAAClip->findRow(y); 1914 int initialCount; 1915 row = fAAClip->findX(row, x, &initialCount); 1916 1917 this->ensureRunsAndAA(); 1918 1919 merge(row, initialCount, aa, runs, fAA, fRuns, fAAClipBounds.width()); 1920 fBlitter->blitAntiH(x, y, fAA, fRuns); 1921} 1922 1923void SkAAClipBlitter::blitV(int x, int y, int height, SkAlpha alpha) { 1924 if (fAAClip->quickContains(x, y, x + 1, y + height)) { 1925 fBlitter->blitV(x, y, height, alpha); 1926 return; 1927 } 1928 1929 for (;;) { 1930 int lastY SK_INIT_TO_AVOID_WARNING; 1931 const uint8_t* row = fAAClip->findRow(y, &lastY); 1932 int dy = lastY - y + 1; 1933 if (dy > height) { 1934 dy = height; 1935 } 1936 height -= dy; 1937 1938 row = fAAClip->findX(row, x); 1939 SkAlpha newAlpha = SkMulDiv255Round(alpha, row[1]); 1940 if (newAlpha) { 1941 fBlitter->blitV(x, y, dy, newAlpha); 1942 } 1943 SkASSERT(height >= 0); 1944 if (height <= 0) { 1945 break; 1946 } 1947 y = lastY + 1; 1948 } 1949} 1950 1951void SkAAClipBlitter::blitRect(int x, int y, int width, int height) { 1952 if (fAAClip->quickContains(x, y, x + width, y + height)) { 1953 fBlitter->blitRect(x, y, width, height); 1954 return; 1955 } 1956 1957 while (--height >= 0) { 1958 this->blitH(x, y, width); 1959 y += 1; 1960 } 1961} 1962 1963typedef void (*MergeAAProc)(const void* src, int width, const uint8_t* row, 1964 int initialRowCount, void* dst); 1965 1966static void small_memcpy(void* dst, const void* src, size_t n) { 1967 memcpy(dst, src, n); 1968} 1969 1970static void small_bzero(void* dst, size_t n) { 1971 sk_bzero(dst, n); 1972} 1973 1974static inline uint8_t mergeOne(uint8_t value, unsigned alpha) { 1975 return SkMulDiv255Round(value, alpha); 1976} 1977static inline uint16_t mergeOne(uint16_t value, unsigned alpha) { 1978 unsigned r = SkGetPackedR16(value); 1979 unsigned g = SkGetPackedG16(value); 1980 unsigned b = SkGetPackedB16(value); 1981 return SkPackRGB16(SkMulDiv255Round(r, alpha), 1982 SkMulDiv255Round(g, alpha), 1983 SkMulDiv255Round(b, alpha)); 1984} 1985static inline SkPMColor mergeOne(SkPMColor value, unsigned alpha) { 1986 unsigned a = SkGetPackedA32(value); 1987 unsigned r = SkGetPackedR32(value); 1988 unsigned g = SkGetPackedG32(value); 1989 unsigned b = SkGetPackedB32(value); 1990 return SkPackARGB32(SkMulDiv255Round(a, alpha), 1991 SkMulDiv255Round(r, alpha), 1992 SkMulDiv255Round(g, alpha), 1993 SkMulDiv255Round(b, alpha)); 1994} 1995 1996template <typename T> void mergeT(const T* SK_RESTRICT src, int srcN, 1997 const uint8_t* SK_RESTRICT row, int rowN, 1998 T* SK_RESTRICT dst) { 1999 for (;;) { 2000 SkASSERT(rowN > 0); 2001 SkASSERT(srcN > 0); 2002 2003 int n = SkMin32(rowN, srcN); 2004 unsigned rowA = row[1]; 2005 if (0xFF == rowA) { 2006 small_memcpy(dst, src, n * sizeof(T)); 2007 } else if (0 == rowA) { 2008 small_bzero(dst, n * sizeof(T)); 2009 } else { 2010 for (int i = 0; i < n; ++i) { 2011 dst[i] = mergeOne(src[i], rowA); 2012 } 2013 } 2014 2015 if (0 == (srcN -= n)) { 2016 break; 2017 } 2018 2019 src += n; 2020 dst += n; 2021 2022 SkASSERT(rowN == n); 2023 row += 2; 2024 rowN = row[0]; 2025 } 2026} 2027 2028static MergeAAProc find_merge_aa_proc(SkMask::Format format) { 2029 switch (format) { 2030 case SkMask::kBW_Format: 2031 SkDEBUGFAIL("unsupported"); 2032 return NULL; 2033 case SkMask::kA8_Format: 2034 case SkMask::k3D_Format: { 2035 void (*proc8)(const uint8_t*, int, const uint8_t*, int, uint8_t*) = mergeT; 2036 return (MergeAAProc)proc8; 2037 } 2038 case SkMask::kLCD16_Format: { 2039 void (*proc16)(const uint16_t*, int, const uint8_t*, int, uint16_t*) = mergeT; 2040 return (MergeAAProc)proc16; 2041 } 2042 case SkMask::kLCD32_Format: { 2043 void (*proc32)(const SkPMColor*, int, const uint8_t*, int, SkPMColor*) = mergeT; 2044 return (MergeAAProc)proc32; 2045 } 2046 default: 2047 SkDEBUGFAIL("unsupported"); 2048 return NULL; 2049 } 2050} 2051 2052static U8CPU bit2byte(int bitInAByte) { 2053 SkASSERT(bitInAByte <= 0xFF); 2054 // negation turns any non-zero into 0xFFFFFF??, so we just shift down 2055 // some value >= 8 to get a full FF value 2056 return -bitInAByte >> 8; 2057} 2058 2059static void upscaleBW2A8(SkMask* dstMask, const SkMask& srcMask) { 2060 SkASSERT(SkMask::kBW_Format == srcMask.fFormat); 2061 SkASSERT(SkMask::kA8_Format == dstMask->fFormat); 2062 2063 const int width = srcMask.fBounds.width(); 2064 const int height = srcMask.fBounds.height(); 2065 2066 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcMask.fImage; 2067 const size_t srcRB = srcMask.fRowBytes; 2068 uint8_t* SK_RESTRICT dst = (uint8_t*)dstMask->fImage; 2069 const size_t dstRB = dstMask->fRowBytes; 2070 2071 const int wholeBytes = width >> 3; 2072 const int leftOverBits = width & 7; 2073 2074 for (int y = 0; y < height; ++y) { 2075 uint8_t* SK_RESTRICT d = dst; 2076 for (int i = 0; i < wholeBytes; ++i) { 2077 int srcByte = src[i]; 2078 d[0] = bit2byte(srcByte & (1 << 7)); 2079 d[1] = bit2byte(srcByte & (1 << 6)); 2080 d[2] = bit2byte(srcByte & (1 << 5)); 2081 d[3] = bit2byte(srcByte & (1 << 4)); 2082 d[4] = bit2byte(srcByte & (1 << 3)); 2083 d[5] = bit2byte(srcByte & (1 << 2)); 2084 d[6] = bit2byte(srcByte & (1 << 1)); 2085 d[7] = bit2byte(srcByte & (1 << 0)); 2086 d += 8; 2087 } 2088 if (leftOverBits) { 2089 int srcByte = src[wholeBytes]; 2090 for (int x = 0; x < leftOverBits; ++x) { 2091 *d++ = bit2byte(srcByte & 0x80); 2092 srcByte <<= 1; 2093 } 2094 } 2095 src += srcRB; 2096 dst += dstRB; 2097 } 2098} 2099 2100void SkAAClipBlitter::blitMask(const SkMask& origMask, const SkIRect& clip) { 2101 SkASSERT(fAAClip->getBounds().contains(clip)); 2102 2103 if (fAAClip->quickContains(clip)) { 2104 fBlitter->blitMask(origMask, clip); 2105 return; 2106 } 2107 2108 const SkMask* mask = &origMask; 2109 2110 // if we're BW, we need to upscale to A8 (ugh) 2111 SkMask grayMask; 2112 grayMask.fImage = NULL; 2113 if (SkMask::kBW_Format == origMask.fFormat) { 2114 grayMask.fFormat = SkMask::kA8_Format; 2115 grayMask.fBounds = origMask.fBounds; 2116 grayMask.fRowBytes = origMask.fBounds.width(); 2117 size_t size = grayMask.computeImageSize(); 2118 grayMask.fImage = (uint8_t*)fGrayMaskScratch.reset(size, 2119 SkAutoMalloc::kReuse_OnShrink); 2120 2121 upscaleBW2A8(&grayMask, origMask); 2122 mask = &grayMask; 2123 } 2124 2125 this->ensureRunsAndAA(); 2126 2127 // HACK -- we are devolving 3D into A8, need to copy the rest of the 3D 2128 // data into a temp block to support it better (ugh) 2129 2130 const void* src = mask->getAddr(clip.fLeft, clip.fTop); 2131 const size_t srcRB = mask->fRowBytes; 2132 const int width = clip.width(); 2133 MergeAAProc mergeProc = find_merge_aa_proc(mask->fFormat); 2134 2135 SkMask rowMask; 2136 rowMask.fFormat = SkMask::k3D_Format == mask->fFormat ? SkMask::kA8_Format : mask->fFormat; 2137 rowMask.fBounds.fLeft = clip.fLeft; 2138 rowMask.fBounds.fRight = clip.fRight; 2139 rowMask.fRowBytes = mask->fRowBytes; // doesn't matter, since our height==1 2140 rowMask.fImage = (uint8_t*)fScanlineScratch; 2141 2142 int y = clip.fTop; 2143 const int stopY = y + clip.height(); 2144 2145 do { 2146 int localStopY SK_INIT_TO_AVOID_WARNING; 2147 const uint8_t* row = fAAClip->findRow(y, &localStopY); 2148 // findRow returns last Y, not stop, so we add 1 2149 localStopY = SkMin32(localStopY + 1, stopY); 2150 2151 int initialCount; 2152 row = fAAClip->findX(row, clip.fLeft, &initialCount); 2153 do { 2154 mergeProc(src, width, row, initialCount, rowMask.fImage); 2155 rowMask.fBounds.fTop = y; 2156 rowMask.fBounds.fBottom = y + 1; 2157 fBlitter->blitMask(rowMask, rowMask.fBounds); 2158 src = (const void*)((const char*)src + srcRB); 2159 } while (++y < localStopY); 2160 } while (y < stopY); 2161} 2162 2163const SkBitmap* SkAAClipBlitter::justAnOpaqueColor(uint32_t* value) { 2164 return NULL; 2165} 2166 2167