gfx/skia/trunk/src/core/SkAAClip.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial