blob: 5d21a47a49470e6f2d85845021cbbed3b76b26bb [file] [log] [blame]
reed@google.come36707a2011-10-04 21:38:55 +00001
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"
reed@google.com045e62d2011-10-24 12:19:46 +000011#include "SkColorPriv.h"
reed@google.come36707a2011-10-04 21:38:55 +000012#include "SkPath.h"
13#include "SkScan.h"
14#include "SkThread.h"
reed@google.com34f7e472011-10-13 15:11:59 +000015#include "SkUtils.h"
reed@google.come36707a2011-10-04 21:38:55 +000016
reed@google.com045e62d2011-10-24 12:19:46 +000017class 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
reed@google.com1c04bf92011-10-10 12:57:12 +000037#define kMaxInt32 0x7FFFFFFF
38
commit-bot@chromium.org4b7d6732013-10-21 16:41:00 +000039#ifdef SK_DEBUG
reed@google.come36707a2011-10-04 21:38:55 +000040static inline bool x_in_rect(int x, const SkIRect& rect) {
41 return (unsigned)(x - rect.fLeft) < (unsigned)rect.width();
42}
commit-bot@chromium.org4b7d6732013-10-21 16:41:00 +000043#endif
reed@google.come36707a2011-10-04 21:38:55 +000044
45static inline bool y_in_rect(int y, const SkIRect& rect) {
46 return (unsigned)(y - rect.fTop) < (unsigned)rect.height();
47}
48
49/*
50 * Data runs are packed [count, alpha]
51 */
52
53struct SkAAClip::YOffset {
54 int32_t fY;
55 uint32_t fOffset;
56};
57
58struct SkAAClip::RunHead {
59 int32_t fRefCnt;
60 int32_t fRowCount;
scroggo@google.com493c65f2013-02-05 18:49:00 +000061 size_t fDataSize;
rmistry@google.comfbfcd562012-08-23 18:09:54 +000062
reed@google.come36707a2011-10-04 21:38:55 +000063 YOffset* yoffsets() {
64 return (YOffset*)((char*)this + sizeof(RunHead));
65 }
66 const YOffset* yoffsets() const {
67 return (const YOffset*)((const char*)this + sizeof(RunHead));
68 }
69 uint8_t* data() {
70 return (uint8_t*)(this->yoffsets() + fRowCount);
71 }
72 const uint8_t* data() const {
73 return (const uint8_t*)(this->yoffsets() + fRowCount);
74 }
75
76 static RunHead* Alloc(int rowCount, size_t dataSize) {
77 size_t size = sizeof(RunHead) + rowCount * sizeof(YOffset) + dataSize;
78 RunHead* head = (RunHead*)sk_malloc_throw(size);
79 head->fRefCnt = 1;
80 head->fRowCount = rowCount;
81 head->fDataSize = dataSize;
82 return head;
83 }
reed@google.com045e62d2011-10-24 12:19:46 +000084
85 static int ComputeRowSizeForWidth(int width) {
86 // 2 bytes per segment, where each segment can store up to 255 for count
87 int segments = 0;
88 while (width > 0) {
89 segments += 1;
90 int n = SkMin32(width, 255);
91 width -= n;
92 }
93 return segments * 2; // each segment is row[0] + row[1] (n + alpha)
94 }
95
96 static RunHead* AllocRect(const SkIRect& bounds) {
97 SkASSERT(!bounds.isEmpty());
98 int width = bounds.width();
99 size_t rowSize = ComputeRowSizeForWidth(width);
100 RunHead* head = RunHead::Alloc(1, rowSize);
101 YOffset* yoff = head->yoffsets();
102 yoff->fY = bounds.height() - 1;
103 yoff->fOffset = 0;
104 uint8_t* row = head->data();
105 while (width > 0) {
106 int n = SkMin32(width, 255);
107 row[0] = n;
108 row[1] = 0xFF;
109 width -= n;
110 row += 2;
111 }
112 return head;
113 }
reed@google.come36707a2011-10-04 21:38:55 +0000114};
115
reed@google.com32287892011-10-05 16:27:44 +0000116class SkAAClip::Iter {
117public:
118 Iter(const SkAAClip&);
119
120 bool done() const { return fDone; }
reed@google.com1c04bf92011-10-10 12:57:12 +0000121 int top() const { return fTop; }
122 int bottom() const { return fBottom; }
123 const uint8_t* data() const { return fData; }
reed@google.com32287892011-10-05 16:27:44 +0000124 void next();
125
126private:
127 const YOffset* fCurrYOff;
128 const YOffset* fStopYOff;
129 const uint8_t* fData;
130
131 int fTop, fBottom;
132 bool fDone;
133};
134
135SkAAClip::Iter::Iter(const SkAAClip& clip) {
136 if (clip.isEmpty()) {
137 fDone = true;
reed@google.com1c04bf92011-10-10 12:57:12 +0000138 fTop = fBottom = clip.fBounds.fBottom;
139 fData = NULL;
commit-bot@chromium.org6c035f62013-07-03 15:09:30 +0000140 fCurrYOff = NULL;
reed@google.coma4c6e4d2012-06-20 14:29:50 +0000141 fStopYOff = NULL;
reed@google.com32287892011-10-05 16:27:44 +0000142 return;
143 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000144
reed@google.com32287892011-10-05 16:27:44 +0000145 const RunHead* head = clip.fRunHead;
146 fCurrYOff = head->yoffsets();
147 fStopYOff = fCurrYOff + head->fRowCount;
148 fData = head->data() + fCurrYOff->fOffset;
149
150 // setup first value
151 fTop = clip.fBounds.fTop;
152 fBottom = clip.fBounds.fTop + fCurrYOff->fY + 1;
153 fDone = false;
154}
155
156void SkAAClip::Iter::next() {
reed@google.com1c04bf92011-10-10 12:57:12 +0000157 if (!fDone) {
158 const YOffset* prev = fCurrYOff;
159 const YOffset* curr = prev + 1;
160 SkASSERT(curr <= fStopYOff);
reed@google.com32287892011-10-05 16:27:44 +0000161
reed@google.com32287892011-10-05 16:27:44 +0000162 fTop = fBottom;
reed@google.com1c04bf92011-10-10 12:57:12 +0000163 if (curr >= fStopYOff) {
164 fDone = true;
165 fBottom = kMaxInt32;
166 fData = NULL;
167 } else {
168 fBottom += curr->fY - prev->fY;
169 fData += curr->fOffset - prev->fOffset;
170 fCurrYOff = curr;
171 }
reed@google.com32287892011-10-05 16:27:44 +0000172 }
173}
174
reed@google.com045e62d2011-10-24 12:19:46 +0000175#ifdef SK_DEBUG
reed@google.comc9041912011-10-27 16:58:46 +0000176// assert we're exactly width-wide, and then return the number of bytes used
reed@google.com045e62d2011-10-24 12:19:46 +0000177static size_t compute_row_length(const uint8_t row[], int width) {
178 const uint8_t* origRow = row;
179 while (width > 0) {
180 int n = row[0];
reed@google.comc9041912011-10-27 16:58:46 +0000181 SkASSERT(n > 0);
reed@google.com045e62d2011-10-24 12:19:46 +0000182 SkASSERT(n <= width);
183 row += 2;
184 width -= n;
185 }
186 SkASSERT(0 == width);
187 return row - origRow;
188}
189
190void SkAAClip::validate() const {
191 if (NULL == fRunHead) {
192 SkASSERT(fBounds.isEmpty());
193 return;
194 }
195
196 const RunHead* head = fRunHead;
197 SkASSERT(head->fRefCnt > 0);
198 SkASSERT(head->fRowCount > 0);
reed@google.com045e62d2011-10-24 12:19:46 +0000199
200 const YOffset* yoff = head->yoffsets();
201 const YOffset* ystop = yoff + head->fRowCount;
reed@google.comc9041912011-10-27 16:58:46 +0000202 const int lastY = fBounds.height() - 1;
203
204 // Y and offset must be monotonic
205 int prevY = -1;
206 int32_t prevOffset = -1;
reed@google.com045e62d2011-10-24 12:19:46 +0000207 while (yoff < ystop) {
reed@google.comc9041912011-10-27 16:58:46 +0000208 SkASSERT(prevY < yoff->fY);
209 SkASSERT(yoff->fY <= lastY);
210 prevY = yoff->fY;
211 SkASSERT(prevOffset < (int32_t)yoff->fOffset);
212 prevOffset = yoff->fOffset;
213 const uint8_t* row = head->data() + yoff->fOffset;
reed@google.com045e62d2011-10-24 12:19:46 +0000214 size_t rowLength = compute_row_length(row, fBounds.width());
scroggo@google.com493c65f2013-02-05 18:49:00 +0000215 SkASSERT(yoff->fOffset + rowLength <= head->fDataSize);
reed@google.comc9041912011-10-27 16:58:46 +0000216 yoff += 1;
reed@google.com045e62d2011-10-24 12:19:46 +0000217 }
reed@google.com045e62d2011-10-24 12:19:46 +0000218 // check the last entry;
219 --yoff;
reed@google.comc9041912011-10-27 16:58:46 +0000220 SkASSERT(yoff->fY == lastY);
reed@google.com045e62d2011-10-24 12:19:46 +0000221}
222#endif
223
224///////////////////////////////////////////////////////////////////////////////
225
robertphillips@google.com768fee82012-08-02 12:42:43 +0000226// Count the number of zeros on the left and right edges of the passed in
227// RLE row. If 'row' is all zeros return 'width' in both variables.
reed@google.comc9041912011-10-27 16:58:46 +0000228static void count_left_right_zeros(const uint8_t* row, int width,
229 int* leftZ, int* riteZ) {
230 int zeros = 0;
231 do {
232 if (row[1]) {
233 break;
234 }
235 int n = row[0];
236 SkASSERT(n > 0);
237 SkASSERT(n <= width);
238 zeros += n;
239 row += 2;
240 width -= n;
241 } while (width > 0);
242 *leftZ = zeros;
243
robertphillips@google.com768fee82012-08-02 12:42:43 +0000244 if (0 == width) {
245 // this line is completely empty return 'width' in both variables
246 *riteZ = *leftZ;
247 return;
248 }
249
reed@google.comc9041912011-10-27 16:58:46 +0000250 zeros = 0;
251 while (width > 0) {
252 int n = row[0];
253 SkASSERT(n > 0);
254 if (0 == row[1]) {
255 zeros += n;
256 } else {
257 zeros = 0;
258 }
259 row += 2;
260 width -= n;
261 }
262 *riteZ = zeros;
263}
264
265#ifdef SK_DEBUG
266static void test_count_left_right_zeros() {
267 static bool gOnce;
268 if (gOnce) {
269 return;
270 }
271 gOnce = true;
272
273 const uint8_t data0[] = { 0, 0, 10, 0xFF };
274 const uint8_t data1[] = { 0, 0, 5, 0xFF, 2, 0, 3, 0xFF };
275 const uint8_t data2[] = { 7, 0, 5, 0, 2, 0, 3, 0xFF };
276 const uint8_t data3[] = { 0, 5, 5, 0xFF, 2, 0, 3, 0 };
277 const uint8_t data4[] = { 2, 3, 2, 0, 5, 0xFF, 3, 0 };
robertphillips@google.com768fee82012-08-02 12:42:43 +0000278 const uint8_t data5[] = { 10, 10, 10, 0 };
reed@google.comc9041912011-10-27 16:58:46 +0000279 const uint8_t data6[] = { 2, 2, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
280
281 const uint8_t* array[] = {
282 data0, data1, data2, data3, data4, data5, data6
283 };
284
285 for (size_t i = 0; i < SK_ARRAY_COUNT(array); ++i) {
286 const uint8_t* data = array[i];
287 const int expectedL = *data++;
288 const int expectedR = *data++;
289 int L = 12345, R = 12345;
290 count_left_right_zeros(data, 10, &L, &R);
291 SkASSERT(expectedL == L);
292 SkASSERT(expectedR == R);
293 }
294}
295#endif
296
297// modify row in place, trimming off (zeros) from the left and right sides.
298// return the number of bytes that were completely eliminated from the left
299static int trim_row_left_right(uint8_t* row, int width, int leftZ, int riteZ) {
300 int trim = 0;
301 while (leftZ > 0) {
302 SkASSERT(0 == row[1]);
303 int n = row[0];
304 SkASSERT(n > 0);
305 SkASSERT(n <= width);
306 width -= n;
307 row += 2;
308 if (n > leftZ) {
309 row[-2] = n - leftZ;
310 break;
311 }
312 trim += 2;
313 leftZ -= n;
314 SkASSERT(leftZ >= 0);
315 }
316
317 if (riteZ) {
318 // walk row to the end, and then we'll back up to trim riteZ
319 while (width > 0) {
320 int n = row[0];
321 SkASSERT(n <= width);
322 width -= n;
323 row += 2;
324 }
325 // now skip whole runs of zeros
326 do {
327 row -= 2;
328 SkASSERT(0 == row[1]);
329 int n = row[0];
330 SkASSERT(n > 0);
331 if (n > riteZ) {
332 row[0] = n - riteZ;
333 break;
334 }
335 riteZ -= n;
336 SkASSERT(riteZ >= 0);
337 } while (riteZ > 0);
338 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000339
reed@google.comc9041912011-10-27 16:58:46 +0000340 return trim;
341}
342
343#ifdef SK_DEBUG
344// assert that this row is exactly this width
reed@google.comc5507bf2011-10-27 21:15:36 +0000345static void assert_row_width(const uint8_t* row, int width) {
reed@google.comc9041912011-10-27 16:58:46 +0000346 while (width > 0) {
347 int n = row[0];
348 SkASSERT(n > 0);
349 SkASSERT(n <= width);
350 width -= n;
351 row += 2;
352 }
353 SkASSERT(0 == width);
354}
355
356static void test_trim_row_left_right() {
357 static bool gOnce;
358 if (gOnce) {
359 return;
360 }
361 gOnce = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000362
reed@google.comc9041912011-10-27 16:58:46 +0000363 uint8_t data0[] = { 0, 0, 0, 10, 10, 0xFF };
364 uint8_t data1[] = { 2, 0, 0, 10, 5, 0, 2, 0, 3, 0xFF };
365 uint8_t data2[] = { 5, 0, 2, 10, 5, 0, 2, 0, 3, 0xFF };
366 uint8_t data3[] = { 6, 0, 2, 10, 5, 0, 2, 0, 3, 0xFF };
367 uint8_t data4[] = { 0, 0, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
368 uint8_t data5[] = { 1, 0, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
369 uint8_t data6[] = { 0, 1, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
370 uint8_t data7[] = { 1, 1, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
371 uint8_t data8[] = { 2, 2, 2, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
372 uint8_t data9[] = { 5, 2, 4, 10, 2, 0, 2, 0, 2, 0, 2, 0xFF, 2, 0 };
373 uint8_t data10[] ={ 74, 0, 4, 150, 9, 0, 65, 0, 76, 0xFF };
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000374
reed@google.comc9041912011-10-27 16:58:46 +0000375 uint8_t* array[] = {
376 data0, data1, data2, data3, data4,
377 data5, data6, data7, data8, data9,
378 data10
379 };
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000380
reed@google.comc9041912011-10-27 16:58:46 +0000381 for (size_t i = 0; i < SK_ARRAY_COUNT(array); ++i) {
382 uint8_t* data = array[i];
383 const int trimL = *data++;
384 const int trimR = *data++;
385 const int expectedSkip = *data++;
386 const int origWidth = *data++;
387 assert_row_width(data, origWidth);
388 int skip = trim_row_left_right(data, origWidth, trimL, trimR);
389 SkASSERT(expectedSkip == skip);
390 int expectedWidth = origWidth - trimL - trimR;
391 assert_row_width(data + skip, expectedWidth);
392 }
393}
394#endif
395
396bool SkAAClip::trimLeftRight() {
397 SkDEBUGCODE(test_trim_row_left_right();)
398
399 if (this->isEmpty()) {
400 return false;
401 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000402
reed@google.comc9041912011-10-27 16:58:46 +0000403 AUTO_AACLIP_VALIDATE(*this);
404
405 const int width = fBounds.width();
406 RunHead* head = fRunHead;
407 YOffset* yoff = head->yoffsets();
408 YOffset* stop = yoff + head->fRowCount;
409 uint8_t* base = head->data();
410
robertphillips@google.com768fee82012-08-02 12:42:43 +0000411 // After this loop, 'leftZeros' & 'rightZeros' will contain the minimum
412 // number of zeros on the left and right of the clip. This information
413 // can be used to shrink the bounding box.
reed@google.comc9041912011-10-27 16:58:46 +0000414 int leftZeros = width;
415 int riteZeros = width;
416 while (yoff < stop) {
417 int L, R;
418 count_left_right_zeros(base + yoff->fOffset, width, &L, &R);
robertphillips@google.com768fee82012-08-02 12:42:43 +0000419 SkASSERT(L + R < width || (L == width && R == width));
reed@google.comc9041912011-10-27 16:58:46 +0000420 if (L < leftZeros) {
421 leftZeros = L;
422 }
423 if (R < riteZeros) {
424 riteZeros = R;
425 }
426 if (0 == (leftZeros | riteZeros)) {
427 // no trimming to do
428 return true;
429 }
430 yoff += 1;
431 }
432
433 SkASSERT(leftZeros || riteZeros);
robertphillips@google.com768fee82012-08-02 12:42:43 +0000434 if (width == leftZeros) {
435 SkASSERT(width == riteZeros);
reed@google.comc9041912011-10-27 16:58:46 +0000436 return this->setEmpty();
437 }
438
439 this->validate();
440
441 fBounds.fLeft += leftZeros;
442 fBounds.fRight -= riteZeros;
443 SkASSERT(!fBounds.isEmpty());
444
445 // For now we don't realloc the storage (for time), we just shrink in place
446 // This means we don't have to do any memmoves either, since we can just
447 // play tricks with the yoff->fOffset for each row
448 yoff = head->yoffsets();
449 while (yoff < stop) {
450 uint8_t* row = base + yoff->fOffset;
451 SkDEBUGCODE((void)compute_row_length(row, width);)
452 yoff->fOffset += trim_row_left_right(row, width, leftZeros, riteZeros);
453 SkDEBUGCODE((void)compute_row_length(base + yoff->fOffset, width - leftZeros - riteZeros);)
454 yoff += 1;
455 }
456 return true;
457}
458
459static bool row_is_all_zeros(const uint8_t* row, int width) {
460 SkASSERT(width > 0);
461 do {
462 if (row[1]) {
463 return false;
464 }
465 int n = row[0];
466 SkASSERT(n <= width);
467 width -= n;
468 row += 2;
469 } while (width > 0);
470 SkASSERT(0 == width);
471 return true;
472}
473
474bool SkAAClip::trimTopBottom() {
475 if (this->isEmpty()) {
476 return false;
477 }
478
reed@google.comd6040f62011-10-28 02:39:17 +0000479 this->validate();
480
reed@google.comc9041912011-10-27 16:58:46 +0000481 const int width = fBounds.width();
482 RunHead* head = fRunHead;
483 YOffset* yoff = head->yoffsets();
484 YOffset* stop = yoff + head->fRowCount;
485 const uint8_t* base = head->data();
486
487 // Look to trim away empty rows from the top.
488 //
489 int skip = 0;
490 while (yoff < stop) {
491 const uint8_t* data = base + yoff->fOffset;
492 if (!row_is_all_zeros(data, width)) {
493 break;
494 }
495 skip += 1;
496 yoff += 1;
497 }
498 SkASSERT(skip <= head->fRowCount);
499 if (skip == head->fRowCount) {
500 return this->setEmpty();
501 }
502 if (skip > 0) {
503 // adjust fRowCount and fBounds.fTop, and slide all the data up
504 // as we remove [skip] number of YOffset entries
505 yoff = head->yoffsets();
506 int dy = yoff[skip - 1].fY + 1;
507 for (int i = skip; i < head->fRowCount; ++i) {
508 SkASSERT(yoff[i].fY >= dy);
509 yoff[i].fY -= dy;
510 }
511 YOffset* dst = head->yoffsets();
512 size_t size = head->fRowCount * sizeof(YOffset) + head->fDataSize;
513 memmove(dst, dst + skip, size - skip * sizeof(YOffset));
514
515 fBounds.fTop += dy;
516 SkASSERT(!fBounds.isEmpty());
517 head->fRowCount -= skip;
518 SkASSERT(head->fRowCount > 0);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000519
reed@google.comd6040f62011-10-28 02:39:17 +0000520 this->validate();
521 // need to reset this after the memmove
522 base = head->data();
reed@google.comc9041912011-10-27 16:58:46 +0000523 }
524
525 // Look to trim away empty rows from the bottom.
526 // We know that we have at least one non-zero row, so we can just walk
527 // backwards without checking for running past the start.
528 //
529 stop = yoff = head->yoffsets() + head->fRowCount;
530 do {
531 yoff -= 1;
532 } while (row_is_all_zeros(base + yoff->fOffset, width));
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000533 skip = SkToInt(stop - yoff - 1);
reed@google.comc9041912011-10-27 16:58:46 +0000534 SkASSERT(skip >= 0 && skip < head->fRowCount);
535 if (skip > 0) {
536 // removing from the bottom is easier than from the top, as we don't
537 // have to adjust any of the Y values, we just have to trim the array
538 memmove(stop - skip, stop, head->fDataSize);
539
540 fBounds.fBottom = fBounds.fTop + yoff->fY + 1;
541 SkASSERT(!fBounds.isEmpty());
542 head->fRowCount -= skip;
543 SkASSERT(head->fRowCount > 0);
544 }
reed@google.comd6040f62011-10-28 02:39:17 +0000545 this->validate();
reed@google.comc9041912011-10-27 16:58:46 +0000546
547 return true;
548}
549
reed@google.com045e62d2011-10-24 12:19:46 +0000550// can't validate before we're done, since trimming is part of the process of
551// making us valid after the Builder. Since we build from top to bottom, its
552// possible our fBounds.fBottom is bigger than our last scanline of data, so
553// we trim fBounds.fBottom back up.
554//
reed@google.com045e62d2011-10-24 12:19:46 +0000555// TODO: check for duplicates in X and Y to further compress our data
556//
557bool SkAAClip::trimBounds() {
558 if (this->isEmpty()) {
559 return false;
560 }
561
562 const RunHead* head = fRunHead;
563 const YOffset* yoff = head->yoffsets();
564
565 SkASSERT(head->fRowCount > 0);
566 const YOffset& lastY = yoff[head->fRowCount - 1];
567 SkASSERT(lastY.fY + 1 <= fBounds.height());
568 fBounds.fBottom = fBounds.fTop + lastY.fY + 1;
569 SkASSERT(lastY.fY + 1 == fBounds.height());
reed@google.comc9041912011-10-27 16:58:46 +0000570 SkASSERT(!fBounds.isEmpty());
571
572 return this->trimTopBottom() && this->trimLeftRight();
reed@google.com045e62d2011-10-24 12:19:46 +0000573}
574
reed@google.come36707a2011-10-04 21:38:55 +0000575///////////////////////////////////////////////////////////////////////////////
576
577void SkAAClip::freeRuns() {
reed@google.com47ac84e2011-10-06 13:11:25 +0000578 if (fRunHead) {
reed@google.come36707a2011-10-04 21:38:55 +0000579 SkASSERT(fRunHead->fRefCnt >= 1);
580 if (1 == sk_atomic_dec(&fRunHead->fRefCnt)) {
581 sk_free(fRunHead);
582 }
583 }
584}
585
586SkAAClip::SkAAClip() {
587 fBounds.setEmpty();
reed@google.com47ac84e2011-10-06 13:11:25 +0000588 fRunHead = NULL;
reed@google.come36707a2011-10-04 21:38:55 +0000589}
590
591SkAAClip::SkAAClip(const SkAAClip& src) {
reed@google.com045e62d2011-10-24 12:19:46 +0000592 SkDEBUGCODE(fBounds.setEmpty();) // need this for validate
reed@google.com47ac84e2011-10-06 13:11:25 +0000593 fRunHead = NULL;
reed@google.come36707a2011-10-04 21:38:55 +0000594 *this = src;
595}
596
597SkAAClip::~SkAAClip() {
598 this->freeRuns();
599}
600
601SkAAClip& SkAAClip::operator=(const SkAAClip& src) {
reed@google.com045e62d2011-10-24 12:19:46 +0000602 AUTO_AACLIP_VALIDATE(*this);
603 src.validate();
604
reed@google.come36707a2011-10-04 21:38:55 +0000605 if (this != &src) {
606 this->freeRuns();
607 fBounds = src.fBounds;
608 fRunHead = src.fRunHead;
reed@google.com47ac84e2011-10-06 13:11:25 +0000609 if (fRunHead) {
reed@google.come36707a2011-10-04 21:38:55 +0000610 sk_atomic_inc(&fRunHead->fRefCnt);
611 }
612 }
613 return *this;
614}
615
616bool operator==(const SkAAClip& a, const SkAAClip& b) {
reed@google.com045e62d2011-10-24 12:19:46 +0000617 a.validate();
618 b.validate();
619
reed@google.come36707a2011-10-04 21:38:55 +0000620 if (&a == &b) {
621 return true;
622 }
623 if (a.fBounds != b.fBounds) {
624 return false;
625 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000626
reed@google.come36707a2011-10-04 21:38:55 +0000627 const SkAAClip::RunHead* ah = a.fRunHead;
628 const SkAAClip::RunHead* bh = b.fRunHead;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000629
reed@google.come36707a2011-10-04 21:38:55 +0000630 // this catches empties and rects being equal
631 if (ah == bh) {
632 return true;
633 }
634
635 // now we insist that both are complex (but different ptrs)
reed@google.com47ac84e2011-10-06 13:11:25 +0000636 if (!a.fRunHead || !b.fRunHead) {
reed@google.come36707a2011-10-04 21:38:55 +0000637 return false;
638 }
639
640 return ah->fRowCount == bh->fRowCount &&
641 ah->fDataSize == bh->fDataSize &&
642 !memcmp(ah->data(), bh->data(), ah->fDataSize);
643}
644
645void SkAAClip::swap(SkAAClip& other) {
reed@google.com045e62d2011-10-24 12:19:46 +0000646 AUTO_AACLIP_VALIDATE(*this);
647 other.validate();
648
reed@google.come36707a2011-10-04 21:38:55 +0000649 SkTSwap(fBounds, other.fBounds);
650 SkTSwap(fRunHead, other.fRunHead);
651}
652
reed@google.com32287892011-10-05 16:27:44 +0000653bool SkAAClip::set(const SkAAClip& src) {
654 *this = src;
655 return !this->isEmpty();
656}
657
reed@google.come36707a2011-10-04 21:38:55 +0000658bool SkAAClip::setEmpty() {
659 this->freeRuns();
660 fBounds.setEmpty();
reed@google.com47ac84e2011-10-06 13:11:25 +0000661 fRunHead = NULL;
reed@google.come36707a2011-10-04 21:38:55 +0000662 return false;
663}
664
665bool SkAAClip::setRect(const SkIRect& bounds) {
666 if (bounds.isEmpty()) {
667 return this->setEmpty();
668 }
reed@google.com47ac84e2011-10-06 13:11:25 +0000669
reed@google.com045e62d2011-10-24 12:19:46 +0000670 AUTO_AACLIP_VALIDATE(*this);
reed@google.com47ac84e2011-10-06 13:11:25 +0000671
reed@google.com045e62d2011-10-24 12:19:46 +0000672#if 0
reed@google.com47ac84e2011-10-06 13:11:25 +0000673 SkRect r;
674 r.set(bounds);
675 SkPath path;
676 path.addRect(r);
677 return this->setPath(path);
reed@google.com045e62d2011-10-24 12:19:46 +0000678#else
679 this->freeRuns();
680 fBounds = bounds;
681 fRunHead = RunHead::AllocRect(bounds);
682 SkASSERT(!this->isEmpty());
683 return true;
684#endif
reed@google.come36707a2011-10-04 21:38:55 +0000685}
686
reed202ab2a2014-08-07 11:48:10 -0700687bool SkAAClip::isRect() const {
688 if (this->isEmpty()) {
689 return false;
690 }
691
692 const RunHead* head = fRunHead;
693 if (head->fRowCount != 1) {
694 return false;
695 }
696 const YOffset* yoff = head->yoffsets();
697 if (yoff->fY != fBounds.fBottom - 1) {
698 return false;
699 }
700
701 const uint8_t* row = head->data() + yoff->fOffset;
702 int width = fBounds.width();
703 do {
704 if (row[1] != 0xFF) {
705 return false;
706 }
707 int n = row[0];
708 SkASSERT(n <= width);
709 width -= n;
710 row += 2;
711 } while (width > 0);
712 return true;
713}
714
reed@google.comf3c1da12011-10-10 19:35:47 +0000715bool SkAAClip::setRect(const SkRect& r, bool doAA) {
reed@google.come36707a2011-10-04 21:38:55 +0000716 if (r.isEmpty()) {
717 return this->setEmpty();
718 }
719
reed@google.com045e62d2011-10-24 12:19:46 +0000720 AUTO_AACLIP_VALIDATE(*this);
721
722 // TODO: special case this
723
reed@google.come36707a2011-10-04 21:38:55 +0000724 SkPath path;
725 path.addRect(r);
reed@google.comf3c1da12011-10-10 19:35:47 +0000726 return this->setPath(path, NULL, doAA);
727}
728
reed@google.coma069c8f2011-11-28 19:54:56 +0000729static void append_run(SkTDArray<uint8_t>& array, uint8_t value, int count) {
730 SkASSERT(count >= 0);
731 while (count > 0) {
732 int n = count;
733 if (n > 255) {
734 n = 255;
735 }
736 uint8_t* data = array.append(2);
737 data[0] = n;
738 data[1] = value;
739 count -= n;
740 }
741}
742
reed@google.comf3c1da12011-10-10 19:35:47 +0000743bool SkAAClip::setRegion(const SkRegion& rgn) {
744 if (rgn.isEmpty()) {
745 return this->setEmpty();
746 }
747 if (rgn.isRect()) {
748 return this->setRect(rgn.getBounds());
749 }
reed@google.coma069c8f2011-11-28 19:54:56 +0000750
751#if 0
reed@google.comf3c1da12011-10-10 19:35:47 +0000752 SkAAClip clip;
753 SkRegion::Iterator iter(rgn);
754 for (; !iter.done(); iter.next()) {
755 clip.op(iter.rect(), SkRegion::kUnion_Op);
756 }
757 this->swap(clip);
reed@google.com3771a032011-10-11 17:18:04 +0000758 return !this->isEmpty();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000759#else
reed@google.coma069c8f2011-11-28 19:54:56 +0000760 const SkIRect& bounds = rgn.getBounds();
761 const int offsetX = bounds.fLeft;
762 const int offsetY = bounds.fTop;
763
764 SkTDArray<YOffset> yArray;
765 SkTDArray<uint8_t> xArray;
766
767 yArray.setReserve(SkMin32(bounds.height(), 1024));
768 xArray.setReserve(SkMin32(bounds.width() * 128, 64 * 1024));
769
770 SkRegion::Iterator iter(rgn);
771 int prevRight = 0;
772 int prevBot = 0;
773 YOffset* currY = NULL;
774
775 for (; !iter.done(); iter.next()) {
776 const SkIRect& r = iter.rect();
777 SkASSERT(bounds.contains(r));
778
779 int bot = r.fBottom - offsetY;
780 SkASSERT(bot >= prevBot);
781 if (bot > prevBot) {
782 if (currY) {
783 // flush current row
784 append_run(xArray, 0, bounds.width() - prevRight);
785 }
786 // did we introduce an empty-gap from the prev row?
787 int top = r.fTop - offsetY;
788 if (top > prevBot) {
789 currY = yArray.append();
790 currY->fY = top - 1;
791 currY->fOffset = xArray.count();
792 append_run(xArray, 0, bounds.width());
793 }
794 // create a new record for this Y value
795 currY = yArray.append();
796 currY->fY = bot - 1;
797 currY->fOffset = xArray.count();
798 prevRight = 0;
799 prevBot = bot;
800 }
801
802 int x = r.fLeft - offsetX;
803 append_run(xArray, 0, x - prevRight);
804
805 int w = r.fRight - r.fLeft;
806 append_run(xArray, 0xFF, w);
807 prevRight = x + w;
808 SkASSERT(prevRight <= bounds.width());
809 }
810 // flush last row
811 append_run(xArray, 0, bounds.width() - prevRight);
812
813 // now pack everything into a RunHead
814 RunHead* head = RunHead::Alloc(yArray.count(), xArray.bytes());
815 memcpy(head->yoffsets(), yArray.begin(), yArray.bytes());
816 memcpy(head->data(), xArray.begin(), xArray.bytes());
817
818 this->setEmpty();
819 fBounds = bounds;
820 fRunHead = head;
821 this->validate();
822 return true;
823#endif
reed@google.come36707a2011-10-04 21:38:55 +0000824}
825
826///////////////////////////////////////////////////////////////////////////////
827
828const uint8_t* SkAAClip::findRow(int y, int* lastYForRow) const {
reed@google.com47ac84e2011-10-06 13:11:25 +0000829 SkASSERT(fRunHead);
reed@google.come36707a2011-10-04 21:38:55 +0000830
831 if (!y_in_rect(y, fBounds)) {
832 return NULL;
833 }
834 y -= fBounds.y(); // our yoffs values are relative to the top
835
836 const YOffset* yoff = fRunHead->yoffsets();
837 while (yoff->fY < y) {
838 yoff += 1;
839 SkASSERT(yoff - fRunHead->yoffsets() < fRunHead->fRowCount);
840 }
841
842 if (lastYForRow) {
reed@google.com045e62d2011-10-24 12:19:46 +0000843 *lastYForRow = fBounds.y() + yoff->fY;
reed@google.come36707a2011-10-04 21:38:55 +0000844 }
845 return fRunHead->data() + yoff->fOffset;
846}
847
848const uint8_t* SkAAClip::findX(const uint8_t data[], int x, int* initialCount) const {
849 SkASSERT(x_in_rect(x, fBounds));
850 x -= fBounds.x();
851
852 // first skip up to X
853 for (;;) {
854 int n = data[0];
855 if (x < n) {
reed@google.coma4c6e4d2012-06-20 14:29:50 +0000856 if (initialCount) {
857 *initialCount = n - x;
858 }
reed@google.come36707a2011-10-04 21:38:55 +0000859 break;
860 }
861 data += 2;
862 x -= n;
863 }
864 return data;
865}
866
867bool SkAAClip::quickContains(int left, int top, int right, int bottom) const {
868 if (this->isEmpty()) {
869 return false;
870 }
871 if (!fBounds.contains(left, top, right, bottom)) {
872 return false;
873 }
reed@google.com32287892011-10-05 16:27:44 +0000874#if 0
reed@google.come36707a2011-10-04 21:38:55 +0000875 if (this->isRect()) {
876 return true;
877 }
reed@google.com32287892011-10-05 16:27:44 +0000878#endif
reed@google.come36707a2011-10-04 21:38:55 +0000879
reed@google.coma4c6e4d2012-06-20 14:29:50 +0000880 int lastY SK_INIT_TO_AVOID_WARNING;
reed@google.come36707a2011-10-04 21:38:55 +0000881 const uint8_t* row = this->findRow(top, &lastY);
882 if (lastY < bottom) {
883 return false;
884 }
885 // now just need to check in X
reed@google.com045e62d2011-10-24 12:19:46 +0000886 int count;
887 row = this->findX(row, left, &count);
888#if 0
889 return count >= (right - left) && 0xFF == row[1];
890#else
891 int rectWidth = right - left;
892 while (0xFF == row[1]) {
893 if (count >= rectWidth) {
894 return true;
895 }
896 rectWidth -= count;
897 row += 2;
898 count = row[0];
899 }
900 return false;
901#endif
reed@google.come36707a2011-10-04 21:38:55 +0000902}
903
904///////////////////////////////////////////////////////////////////////////////
905
906class SkAAClip::Builder {
907 SkIRect fBounds;
908 struct Row {
909 int fY;
910 int fWidth;
911 SkTDArray<uint8_t>* fData;
912 };
913 SkTDArray<Row> fRows;
914 Row* fCurrRow;
915 int fPrevY;
916 int fWidth;
reed@google.com209c4152011-10-26 15:03:48 +0000917 int fMinY;
reed@google.come36707a2011-10-04 21:38:55 +0000918
919public:
920 Builder(const SkIRect& bounds) : fBounds(bounds) {
921 fPrevY = -1;
922 fWidth = bounds.width();
923 fCurrRow = NULL;
reed@google.com209c4152011-10-26 15:03:48 +0000924 fMinY = bounds.fTop;
reed@google.come36707a2011-10-04 21:38:55 +0000925 }
926
927 ~Builder() {
928 Row* row = fRows.begin();
929 Row* stop = fRows.end();
930 while (row < stop) {
931 delete row->fData;
932 row += 1;
933 }
934 }
935
reed@google.com32287892011-10-05 16:27:44 +0000936 const SkIRect& getBounds() const { return fBounds; }
937
reed@google.come36707a2011-10-04 21:38:55 +0000938 void addRun(int x, int y, U8CPU alpha, int count) {
939 SkASSERT(count > 0);
940 SkASSERT(fBounds.contains(x, y));
941 SkASSERT(fBounds.contains(x + count - 1, y));
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000942
reed@google.come36707a2011-10-04 21:38:55 +0000943 x -= fBounds.left();
944 y -= fBounds.top();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000945
reed@google.come36707a2011-10-04 21:38:55 +0000946 Row* row = fCurrRow;
947 if (y != fPrevY) {
948 SkASSERT(y > fPrevY);
949 fPrevY = y;
950 row = this->flushRow(true);
951 row->fY = y;
952 row->fWidth = 0;
953 SkASSERT(row->fData);
954 SkASSERT(0 == row->fData->count());
955 fCurrRow = row;
956 }
957
958 SkASSERT(row->fWidth <= x);
959 SkASSERT(row->fWidth < fBounds.width());
960
961 SkTDArray<uint8_t>& data = *row->fData;
962
963 int gap = x - row->fWidth;
964 if (gap) {
965 AppendRun(data, 0, gap);
966 row->fWidth += gap;
967 SkASSERT(row->fWidth < fBounds.width());
968 }
969
970 AppendRun(data, alpha, count);
971 row->fWidth += count;
972 SkASSERT(row->fWidth <= fBounds.width());
973 }
974
tomhudson@google.com49eac192011-12-27 13:59:20 +0000975 void addColumn(int x, int y, U8CPU alpha, int height) {
976 SkASSERT(fBounds.contains(x, y + height - 1));
977
978 this->addRun(x, y, alpha, 1);
979 this->flushRowH(fCurrRow);
980 y -= fBounds.fTop;
981 SkASSERT(y == fCurrRow->fY);
982 fCurrRow->fY = y + height - 1;
983 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000984
reed@google.com9154eb02011-10-31 16:07:28 +0000985 void addRectRun(int x, int y, int width, int height) {
986 SkASSERT(fBounds.contains(x + width - 1, y + height - 1));
987 this->addRun(x, y, 0xFF, width);
988
reed@google.coma89c77b2011-12-01 21:47:26 +0000989 // we assum the rect must be all we'll see for these scanlines
reed@google.com9154eb02011-10-31 16:07:28 +0000990 // so we ensure our row goes all the way to our right
991 this->flushRowH(fCurrRow);
992
993 y -= fBounds.fTop;
994 SkASSERT(y == fCurrRow->fY);
995 fCurrRow->fY = y + height - 1;
996 }
997
tomhudson@google.com49eac192011-12-27 13:59:20 +0000998 void addAntiRectRun(int x, int y, int width, int height,
999 SkAlpha leftAlpha, SkAlpha rightAlpha) {
1000 SkASSERT(fBounds.contains(x + width - 1 +
1001 (leftAlpha > 0 ? 1 : 0) + (rightAlpha > 0 ? 1 : 0),
1002 y + height - 1));
1003 SkASSERT(width >= 0);
1004
1005 // Conceptually we're always adding 3 runs, but we should
1006 // merge or omit them if possible.
1007 if (leftAlpha == 0xFF) {
1008 width++;
1009 } else if (leftAlpha > 0) {
1010 this->addRun(x++, y, leftAlpha, 1);
1011 }
1012 if (rightAlpha == 0xFF) {
1013 width++;
1014 }
1015 if (width > 0) {
1016 this->addRun(x, y, 0xFF, width);
1017 }
1018 if (rightAlpha > 0 && rightAlpha < 255) {
1019 this->addRun(x + width, y, rightAlpha, 1);
1020 }
1021
1022 // we assume the rect must be all we'll see for these scanlines
1023 // so we ensure our row goes all the way to our right
1024 this->flushRowH(fCurrRow);
1025
1026 y -= fBounds.fTop;
1027 SkASSERT(y == fCurrRow->fY);
1028 fCurrRow->fY = y + height - 1;
1029 }
1030
reed@google.com045e62d2011-10-24 12:19:46 +00001031 bool finish(SkAAClip* target) {
reed@google.come36707a2011-10-04 21:38:55 +00001032 this->flushRow(false);
1033
1034 const Row* row = fRows.begin();
1035 const Row* stop = fRows.end();
1036
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001037 size_t dataSize = 0;
reed@google.come36707a2011-10-04 21:38:55 +00001038 while (row < stop) {
1039 dataSize += row->fData->count();
1040 row += 1;
1041 }
1042
reed@google.com045e62d2011-10-24 12:19:46 +00001043 if (0 == dataSize) {
1044 return target->setEmpty();
1045 }
1046
reed@google.com209c4152011-10-26 15:03:48 +00001047 SkASSERT(fMinY >= fBounds.fTop);
1048 SkASSERT(fMinY < fBounds.fBottom);
1049 int adjustY = fMinY - fBounds.fTop;
1050 fBounds.fTop = fMinY;
1051
reed@google.come36707a2011-10-04 21:38:55 +00001052 RunHead* head = RunHead::Alloc(fRows.count(), dataSize);
1053 YOffset* yoffset = head->yoffsets();
1054 uint8_t* data = head->data();
1055 uint8_t* baseData = data;
1056
1057 row = fRows.begin();
reed@google.comc9041912011-10-27 16:58:46 +00001058 SkDEBUGCODE(int prevY = row->fY - 1;)
reed@google.come36707a2011-10-04 21:38:55 +00001059 while (row < stop) {
reed@google.comc9041912011-10-27 16:58:46 +00001060 SkASSERT(prevY < row->fY); // must be monotonic
1061 SkDEBUGCODE(prevY = row->fY);
1062
reed@google.com209c4152011-10-26 15:03:48 +00001063 yoffset->fY = row->fY - adjustY;
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001064 yoffset->fOffset = SkToU32(data - baseData);
reed@google.come36707a2011-10-04 21:38:55 +00001065 yoffset += 1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001066
reed@google.come36707a2011-10-04 21:38:55 +00001067 size_t n = row->fData->count();
1068 memcpy(data, row->fData->begin(), n);
reed@google.comc9041912011-10-27 16:58:46 +00001069#ifdef SK_DEBUG
tomhudson@google.comf74ad8c2011-11-09 22:15:08 +00001070 size_t bytesNeeded = compute_row_length(data, fBounds.width());
reed@google.comc9041912011-10-27 16:58:46 +00001071 SkASSERT(bytesNeeded == n);
1072#endif
reed@google.come36707a2011-10-04 21:38:55 +00001073 data += n;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001074
reed@google.come36707a2011-10-04 21:38:55 +00001075 row += 1;
1076 }
1077
reed@google.com045e62d2011-10-24 12:19:46 +00001078 target->freeRuns();
1079 target->fBounds = fBounds;
1080 target->fRunHead = head;
1081 return target->trimBounds();
reed@google.come36707a2011-10-04 21:38:55 +00001082 }
1083
1084 void dump() {
1085 this->validate();
1086 int y;
1087 for (y = 0; y < fRows.count(); ++y) {
1088 const Row& row = fRows[y];
1089 SkDebugf("Y:%3d W:%3d", row.fY, row.fWidth);
1090 const SkTDArray<uint8_t>& data = *row.fData;
1091 int count = data.count();
1092 SkASSERT(!(count & 1));
1093 const uint8_t* ptr = data.begin();
1094 for (int x = 0; x < count; x += 2) {
1095 SkDebugf(" [%3d:%02X]", ptr[0], ptr[1]);
1096 ptr += 2;
1097 }
1098 SkDebugf("\n");
1099 }
reed@google.come36707a2011-10-04 21:38:55 +00001100 }
1101
1102 void validate() {
1103#ifdef SK_DEBUG
caryclark@google.com803eceb2012-06-06 12:09:34 +00001104 if (false) { // avoid bit rot, suppress warning
1105 test_count_left_right_zeros();
1106 }
reed@google.come36707a2011-10-04 21:38:55 +00001107 int prevY = -1;
1108 for (int i = 0; i < fRows.count(); ++i) {
1109 const Row& row = fRows[i];
1110 SkASSERT(prevY < row.fY);
1111 SkASSERT(fWidth == row.fWidth);
1112 int count = row.fData->count();
1113 const uint8_t* ptr = row.fData->begin();
1114 SkASSERT(!(count & 1));
1115 int w = 0;
1116 for (int x = 0; x < count; x += 2) {
reed@google.comd6040f62011-10-28 02:39:17 +00001117 int n = ptr[0];
1118 SkASSERT(n > 0);
1119 w += n;
reed@google.come36707a2011-10-04 21:38:55 +00001120 SkASSERT(w <= fWidth);
1121 ptr += 2;
1122 }
1123 SkASSERT(w == fWidth);
1124 prevY = row.fY;
1125 }
1126#endif
1127 }
1128
reed@google.com209c4152011-10-26 15:03:48 +00001129 // only called by BuilderBlitter
1130 void setMinY(int y) {
1131 fMinY = y;
1132 }
1133
reed@google.come36707a2011-10-04 21:38:55 +00001134private:
reed@google.com9154eb02011-10-31 16:07:28 +00001135 void flushRowH(Row* row) {
1136 // flush current row if needed
1137 if (row->fWidth < fWidth) {
1138 AppendRun(*row->fData, 0, fWidth - row->fWidth);
1139 row->fWidth = fWidth;
1140 }
1141 }
reed@google.com209c4152011-10-26 15:03:48 +00001142
reed@google.come36707a2011-10-04 21:38:55 +00001143 Row* flushRow(bool readyForAnother) {
1144 Row* next = NULL;
1145 int count = fRows.count();
1146 if (count > 0) {
reed@google.com9154eb02011-10-31 16:07:28 +00001147 this->flushRowH(&fRows[count - 1]);
reed@google.come36707a2011-10-04 21:38:55 +00001148 }
1149 if (count > 1) {
1150 // are our last two runs the same?
1151 Row* prev = &fRows[count - 2];
1152 Row* curr = &fRows[count - 1];
1153 SkASSERT(prev->fWidth == fWidth);
1154 SkASSERT(curr->fWidth == fWidth);
1155 if (*prev->fData == *curr->fData) {
1156 prev->fY = curr->fY;
1157 if (readyForAnother) {
1158 curr->fData->rewind();
1159 next = curr;
1160 } else {
1161 delete curr->fData;
1162 fRows.removeShuffle(count - 1);
1163 }
1164 } else {
1165 if (readyForAnother) {
1166 next = fRows.append();
1167 next->fData = new SkTDArray<uint8_t>;
1168 }
1169 }
1170 } else {
1171 if (readyForAnother) {
1172 next = fRows.append();
1173 next->fData = new SkTDArray<uint8_t>;
1174 }
1175 }
1176 return next;
1177 }
1178
1179 static void AppendRun(SkTDArray<uint8_t>& data, U8CPU alpha, int count) {
1180 do {
1181 int n = count;
1182 if (n > 255) {
1183 n = 255;
1184 }
1185 uint8_t* ptr = data.append(2);
1186 ptr[0] = n;
1187 ptr[1] = alpha;
1188 count -= n;
1189 } while (count > 0);
1190 }
1191};
1192
1193class SkAAClip::BuilderBlitter : public SkBlitter {
reed@google.com80cdb9a2012-02-16 18:56:17 +00001194 int fLastY;
1195
1196 /*
1197 If we see a gap of 1 or more empty scanlines while building in Y-order,
1198 we inject an explicit empty scanline (alpha==0)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001199
reed@google.com80cdb9a2012-02-16 18:56:17 +00001200 See AAClipTest.cpp : test_path_with_hole()
1201 */
1202 void checkForYGap(int y) {
1203 SkASSERT(y >= fLastY);
1204 if (fLastY > -SK_MaxS32) {
1205 int gap = y - fLastY;
1206 if (gap > 1) {
1207 fBuilder->addRun(fLeft, y - 1, 0, fRight - fLeft);
1208 }
1209 }
1210 fLastY = y;
1211 }
1212
reed@google.come36707a2011-10-04 21:38:55 +00001213public:
reed@google.com80cdb9a2012-02-16 18:56:17 +00001214
reed@google.come36707a2011-10-04 21:38:55 +00001215 BuilderBlitter(Builder* builder) {
1216 fBuilder = builder;
reed@google.com17785642011-10-12 20:23:55 +00001217 fLeft = builder->getBounds().fLeft;
1218 fRight = builder->getBounds().fRight;
reed@google.com209c4152011-10-26 15:03:48 +00001219 fMinY = SK_MaxS32;
reed@google.com80cdb9a2012-02-16 18:56:17 +00001220 fLastY = -SK_MaxS32; // sentinel
reed@google.com209c4152011-10-26 15:03:48 +00001221 }
1222
1223 void finish() {
1224 if (fMinY < SK_MaxS32) {
1225 fBuilder->setMinY(fMinY);
1226 }
reed@google.come36707a2011-10-04 21:38:55 +00001227 }
1228
tomhudson@google.com49eac192011-12-27 13:59:20 +00001229 /**
1230 Must evaluate clips in scan-line order, so don't want to allow blitV(),
1231 but an AAClip can be clipped down to a single pixel wide, so we
1232 must support it (given AntiRect semantics: minimum width is 2).
1233 Instead we'll rely on the runtime asserts to guarantee Y monotonicity;
1234 any failure cases that misses may have minor artifacts.
1235 */
1236 virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE {
1237 this->recordMinY(y);
1238 fBuilder->addColumn(x, y, alpha, height);
reed@google.com9b0da232012-02-29 13:59:15 +00001239 fLastY = y + height - 1;
tomhudson@google.com49eac192011-12-27 13:59:20 +00001240 }
reed@google.com045e62d2011-10-24 12:19:46 +00001241
reed@google.com562a2ac2011-10-31 14:14:18 +00001242 virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE {
reed@google.com9154eb02011-10-31 16:07:28 +00001243 this->recordMinY(y);
reed@google.com80cdb9a2012-02-16 18:56:17 +00001244 this->checkForYGap(y);
reed@google.com9154eb02011-10-31 16:07:28 +00001245 fBuilder->addRectRun(x, y, width, height);
reed@google.com302b8612012-02-16 19:30:13 +00001246 fLastY = y + height - 1;
reed@google.com562a2ac2011-10-31 14:14:18 +00001247 }
reed@google.com045e62d2011-10-24 12:19:46 +00001248
tomhudson@google.com49eac192011-12-27 13:59:20 +00001249 virtual void blitAntiRect(int x, int y, int width, int height,
1250 SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE {
1251 this->recordMinY(y);
reed@google.com302b8612012-02-16 19:30:13 +00001252 this->checkForYGap(y);
tomhudson@google.com49eac192011-12-27 13:59:20 +00001253 fBuilder->addAntiRectRun(x, y, width, height, leftAlpha, rightAlpha);
reed@google.com302b8612012-02-16 19:30:13 +00001254 fLastY = y + height - 1;
tomhudson@google.com49eac192011-12-27 13:59:20 +00001255 }
1256
reed@google.come36707a2011-10-04 21:38:55 +00001257 virtual void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE
1258 { unexpected(); }
1259
1260 virtual const SkBitmap* justAnOpaqueColor(uint32_t*) SK_OVERRIDE {
reed@google.com3771a032011-10-11 17:18:04 +00001261 return NULL;
reed@google.come36707a2011-10-04 21:38:55 +00001262 }
1263
1264 virtual void blitH(int x, int y, int width) SK_OVERRIDE {
reed@google.com209c4152011-10-26 15:03:48 +00001265 this->recordMinY(y);
reed@google.com80cdb9a2012-02-16 18:56:17 +00001266 this->checkForYGap(y);
reed@google.come36707a2011-10-04 21:38:55 +00001267 fBuilder->addRun(x, y, 0xFF, width);
1268 }
1269
1270 virtual void blitAntiH(int x, int y, const SkAlpha alpha[],
1271 const int16_t runs[]) SK_OVERRIDE {
reed@google.com209c4152011-10-26 15:03:48 +00001272 this->recordMinY(y);
reed@google.com80cdb9a2012-02-16 18:56:17 +00001273 this->checkForYGap(y);
reed@google.come36707a2011-10-04 21:38:55 +00001274 for (;;) {
1275 int count = *runs;
1276 if (count <= 0) {
1277 return;
1278 }
reed@google.com17785642011-10-12 20:23:55 +00001279
1280 // The supersampler's buffer can be the width of the device, so
1281 // we may have to trim the run to our bounds. If so, we assert that
1282 // the extra spans are always alpha==0
1283 int localX = x;
1284 int localCount = count;
1285 if (x < fLeft) {
1286 SkASSERT(0 == *alpha);
1287 int gap = fLeft - x;
1288 SkASSERT(gap <= count);
1289 localX += gap;
1290 localCount -= gap;
1291 }
1292 int right = x + count;
1293 if (right > fRight) {
1294 SkASSERT(0 == *alpha);
1295 localCount -= right - fRight;
1296 SkASSERT(localCount >= 0);
1297 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001298
reed@google.com17785642011-10-12 20:23:55 +00001299 if (localCount) {
1300 fBuilder->addRun(localX, y, *alpha, localCount);
1301 }
bsalomon@google.com820e80a2011-10-24 21:09:40 +00001302 // Next run
reed@google.come36707a2011-10-04 21:38:55 +00001303 runs += count;
1304 alpha += count;
1305 x += count;
1306 }
1307 }
1308
1309private:
1310 Builder* fBuilder;
reed@google.com17785642011-10-12 20:23:55 +00001311 int fLeft; // cache of builder's bounds' left edge
1312 int fRight;
reed@google.com209c4152011-10-26 15:03:48 +00001313 int fMinY;
1314
1315 /*
1316 * We track this, in case the scan converter skipped some number of
1317 * scanlines at the (relative to the bounds it was given). This allows
1318 * the builder, during its finish, to trip its bounds down to the "real"
1319 * top.
1320 */
1321 void recordMinY(int y) {
1322 if (y < fMinY) {
1323 fMinY = y;
1324 }
1325 }
reed@google.come36707a2011-10-04 21:38:55 +00001326
1327 void unexpected() {
1328 SkDebugf("---- did not expect to get called here");
1329 sk_throw();
1330 }
1331};
1332
reed@google.comf3c1da12011-10-10 19:35:47 +00001333bool SkAAClip::setPath(const SkPath& path, const SkRegion* clip, bool doAA) {
reed@google.com045e62d2011-10-24 12:19:46 +00001334 AUTO_AACLIP_VALIDATE(*this);
1335
reed@google.com32287892011-10-05 16:27:44 +00001336 if (clip && clip->isEmpty()) {
reed@google.come36707a2011-10-04 21:38:55 +00001337 return this->setEmpty();
1338 }
1339
1340 SkIRect ibounds;
reed@google.com32287892011-10-05 16:27:44 +00001341 path.getBounds().roundOut(&ibounds);
reed@google.come36707a2011-10-04 21:38:55 +00001342
reed@google.com32287892011-10-05 16:27:44 +00001343 SkRegion tmpClip;
1344 if (NULL == clip) {
1345 tmpClip.setRect(ibounds);
1346 clip = &tmpClip;
1347 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001348
reed@google.com045e62d2011-10-24 12:19:46 +00001349 if (path.isInverseFillType()) {
1350 ibounds = clip->getBounds();
1351 } else {
reed@google.com32287892011-10-05 16:27:44 +00001352 if (ibounds.isEmpty() || !ibounds.intersect(clip->getBounds())) {
reed@google.come36707a2011-10-04 21:38:55 +00001353 return this->setEmpty();
1354 }
reed@google.come36707a2011-10-04 21:38:55 +00001355 }
1356
1357 Builder builder(ibounds);
1358 BuilderBlitter blitter(&builder);
1359
reed@google.comf3c1da12011-10-10 19:35:47 +00001360 if (doAA) {
1361 SkScan::AntiFillPath(path, *clip, &blitter, true);
1362 } else {
1363 SkScan::FillPath(path, *clip, &blitter);
1364 }
reed@google.come36707a2011-10-04 21:38:55 +00001365
reed@google.com209c4152011-10-26 15:03:48 +00001366 blitter.finish();
reed@google.com045e62d2011-10-24 12:19:46 +00001367 return builder.finish(this);
reed@google.come36707a2011-10-04 21:38:55 +00001368}
1369
1370///////////////////////////////////////////////////////////////////////////////
1371
reed@google.com32287892011-10-05 16:27:44 +00001372typedef void (*RowProc)(SkAAClip::Builder&, int bottom,
1373 const uint8_t* rowA, const SkIRect& rectA,
1374 const uint8_t* rowB, const SkIRect& rectB);
1375
reed@google.com32287892011-10-05 16:27:44 +00001376typedef U8CPU (*AlphaProc)(U8CPU alphaA, U8CPU alphaB);
1377
1378static U8CPU sectAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1379 // Multiply
1380 return SkMulDiv255Round(alphaA, alphaB);
1381}
1382
1383static U8CPU unionAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1384 // SrcOver
1385 return alphaA + alphaB - SkMulDiv255Round(alphaA, alphaB);
1386}
1387
1388static U8CPU diffAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1389 // SrcOut
1390 return SkMulDiv255Round(alphaA, 0xFF - alphaB);
1391}
1392
1393static U8CPU xorAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1394 // XOR
1395 return alphaA + alphaB - 2 * SkMulDiv255Round(alphaA, alphaB);
1396}
1397
1398static AlphaProc find_alpha_proc(SkRegion::Op op) {
1399 switch (op) {
1400 case SkRegion::kIntersect_Op:
1401 return sectAlphaProc;
1402 case SkRegion::kDifference_Op:
1403 return diffAlphaProc;
1404 case SkRegion::kUnion_Op:
1405 return unionAlphaProc;
1406 case SkRegion::kXOR_Op:
1407 return xorAlphaProc;
1408 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001409 SkDEBUGFAIL("unexpected region op");
reed@google.com32287892011-10-05 16:27:44 +00001410 return sectAlphaProc;
1411 }
1412}
1413
reed@google.com32287892011-10-05 16:27:44 +00001414class RowIter {
1415public:
1416 RowIter(const uint8_t* row, const SkIRect& bounds) {
1417 fRow = row;
1418 fLeft = bounds.fLeft;
reed@google.com32287892011-10-05 16:27:44 +00001419 fBoundsRight = bounds.fRight;
reed@google.com1c04bf92011-10-10 12:57:12 +00001420 if (row) {
1421 fRight = bounds.fLeft + row[0];
1422 SkASSERT(fRight <= fBoundsRight);
1423 fAlpha = row[1];
1424 fDone = false;
1425 } else {
1426 fDone = true;
1427 fRight = kMaxInt32;
1428 fAlpha = 0;
1429 }
reed@google.com32287892011-10-05 16:27:44 +00001430 }
1431
1432 bool done() const { return fDone; }
reed@google.com1c04bf92011-10-10 12:57:12 +00001433 int left() const { return fLeft; }
1434 int right() const { return fRight; }
1435 U8CPU alpha() const { return fAlpha; }
reed@google.com32287892011-10-05 16:27:44 +00001436 void next() {
reed@google.com1c04bf92011-10-10 12:57:12 +00001437 if (!fDone) {
reed@google.com32287892011-10-05 16:27:44 +00001438 fLeft = fRight;
reed@google.com1c04bf92011-10-10 12:57:12 +00001439 if (fRight == fBoundsRight) {
1440 fDone = true;
1441 fRight = kMaxInt32;
1442 fAlpha = 0;
1443 } else {
1444 fRow += 2;
1445 fRight += fRow[0];
1446 fAlpha = fRow[1];
1447 SkASSERT(fRight <= fBoundsRight);
1448 }
reed@google.com32287892011-10-05 16:27:44 +00001449 }
1450 }
1451
1452private:
1453 const uint8_t* fRow;
1454 int fLeft;
1455 int fRight;
1456 int fBoundsRight;
1457 bool fDone;
reed@google.com1c04bf92011-10-10 12:57:12 +00001458 uint8_t fAlpha;
reed@google.com32287892011-10-05 16:27:44 +00001459};
1460
reed@google.com1c04bf92011-10-10 12:57:12 +00001461static void adjust_row(RowIter& iter, int& leftA, int& riteA, int rite) {
1462 if (rite == riteA) {
1463 iter.next();
1464 leftA = iter.left();
1465 riteA = iter.right();
reed@google.com32287892011-10-05 16:27:44 +00001466 }
1467}
1468
caryclark@google.com803eceb2012-06-06 12:09:34 +00001469#if 0 // UNUSED
reed@google.com1c04bf92011-10-10 12:57:12 +00001470static bool intersect(int& min, int& max, int boundsMin, int boundsMax) {
1471 SkASSERT(min < max);
1472 SkASSERT(boundsMin < boundsMax);
1473 if (min >= boundsMax || max <= boundsMin) {
1474 return false;
1475 }
1476 if (min < boundsMin) {
1477 min = boundsMin;
1478 }
1479 if (max > boundsMax) {
1480 max = boundsMax;
1481 }
1482 return true;
1483}
caryclark@google.com803eceb2012-06-06 12:09:34 +00001484#endif
reed@google.com1c04bf92011-10-10 12:57:12 +00001485
reed@google.com32287892011-10-05 16:27:44 +00001486static void operatorX(SkAAClip::Builder& builder, int lastY,
1487 RowIter& iterA, RowIter& iterB,
1488 AlphaProc proc, const SkIRect& bounds) {
reed@google.com32287892011-10-05 16:27:44 +00001489 int leftA = iterA.left();
1490 int riteA = iterA.right();
reed@google.com32287892011-10-05 16:27:44 +00001491 int leftB = iterB.left();
1492 int riteB = iterB.right();
1493
reed@google.com1c04bf92011-10-10 12:57:12 +00001494 int prevRite = bounds.fLeft;
1495
1496 do {
reed@google.com32287892011-10-05 16:27:44 +00001497 U8CPU alphaA = 0;
1498 U8CPU alphaB = 0;
reed@google.com32287892011-10-05 16:27:44 +00001499 int left, rite;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001500
reed@google.com1c04bf92011-10-10 12:57:12 +00001501 if (leftA < leftB) {
reed@google.com32287892011-10-05 16:27:44 +00001502 left = leftA;
reed@google.com32287892011-10-05 16:27:44 +00001503 alphaA = iterA.alpha();
reed@google.com1c04bf92011-10-10 12:57:12 +00001504 if (riteA <= leftB) {
1505 rite = riteA;
1506 } else {
1507 rite = leftA = leftB;
reed@google.com32287892011-10-05 16:27:44 +00001508 }
reed@google.com1c04bf92011-10-10 12:57:12 +00001509 } else if (leftB < leftA) {
1510 left = leftB;
1511 alphaB = iterB.alpha();
1512 if (riteB <= leftA) {
1513 rite = riteB;
1514 } else {
1515 rite = leftB = leftA;
1516 }
1517 } else {
1518 left = leftA; // or leftB, since leftA == leftB
1519 rite = leftA = leftB = SkMin32(riteA, riteB);
1520 alphaA = iterA.alpha();
1521 alphaB = iterB.alpha();
reed@google.com32287892011-10-05 16:27:44 +00001522 }
1523
1524 if (left >= bounds.fRight) {
1525 break;
1526 }
reed@google.com34f7e472011-10-13 15:11:59 +00001527 if (rite > bounds.fRight) {
1528 rite = bounds.fRight;
1529 }
1530
reed@google.com32287892011-10-05 16:27:44 +00001531 if (left >= bounds.fLeft) {
reed@google.com1c04bf92011-10-10 12:57:12 +00001532 SkASSERT(rite > left);
reed@google.com32287892011-10-05 16:27:44 +00001533 builder.addRun(left, lastY, proc(alphaA, alphaB), rite - left);
reed@google.com1c04bf92011-10-10 12:57:12 +00001534 prevRite = rite;
reed@google.com32287892011-10-05 16:27:44 +00001535 }
reed@google.com1c04bf92011-10-10 12:57:12 +00001536
1537 adjust_row(iterA, leftA, riteA, rite);
1538 adjust_row(iterB, leftB, riteB, rite);
1539 } while (!iterA.done() || !iterB.done());
1540
1541 if (prevRite < bounds.fRight) {
1542 builder.addRun(prevRite, lastY, 0, bounds.fRight - prevRite);
reed@google.com32287892011-10-05 16:27:44 +00001543 }
1544}
1545
reed@google.com1c04bf92011-10-10 12:57:12 +00001546static void adjust_iter(SkAAClip::Iter& iter, int& topA, int& botA, int bot) {
1547 if (bot == botA) {
1548 iter.next();
1549 topA = botA;
1550 SkASSERT(botA == iter.top());
1551 botA = iter.bottom();
reed@google.com32287892011-10-05 16:27:44 +00001552 }
1553}
1554
1555static void operateY(SkAAClip::Builder& builder, const SkAAClip& A,
1556 const SkAAClip& B, SkRegion::Op op) {
1557 AlphaProc proc = find_alpha_proc(op);
1558 const SkIRect& bounds = builder.getBounds();
1559
1560 SkAAClip::Iter iterA(A);
1561 SkAAClip::Iter iterB(B);
1562
1563 SkASSERT(!iterA.done());
1564 int topA = iterA.top();
1565 int botA = iterA.bottom();
1566 SkASSERT(!iterB.done());
1567 int topB = iterB.top();
1568 int botB = iterB.bottom();
1569
reed@google.com1c04bf92011-10-10 12:57:12 +00001570 do {
1571 const uint8_t* rowA = NULL;
1572 const uint8_t* rowB = NULL;
reed@google.com32287892011-10-05 16:27:44 +00001573 int top, bot;
reed@google.com1c04bf92011-10-10 12:57:12 +00001574
1575 if (topA < topB) {
reed@google.com32287892011-10-05 16:27:44 +00001576 top = topA;
reed@google.com32287892011-10-05 16:27:44 +00001577 rowA = iterA.data();
reed@google.com1c04bf92011-10-10 12:57:12 +00001578 if (botA <= topB) {
1579 bot = botA;
1580 } else {
1581 bot = topA = topB;
reed@google.com32287892011-10-05 16:27:44 +00001582 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001583
reed@google.com1c04bf92011-10-10 12:57:12 +00001584 } else if (topB < topA) {
1585 top = topB;
1586 rowB = iterB.data();
1587 if (botB <= topA) {
1588 bot = botB;
1589 } else {
1590 bot = topB = topA;
1591 }
1592 } else {
1593 top = topA; // or topB, since topA == topB
1594 bot = topA = topB = SkMin32(botA, botB);
1595 rowA = iterA.data();
1596 rowB = iterB.data();
reed@google.com32287892011-10-05 16:27:44 +00001597 }
1598
1599 if (top >= bounds.fBottom) {
1600 break;
1601 }
reed@google.com34f7e472011-10-13 15:11:59 +00001602
1603 if (bot > bounds.fBottom) {
1604 bot = bounds.fBottom;
1605 }
1606 SkASSERT(top < bot);
1607
reed@google.com1c04bf92011-10-10 12:57:12 +00001608 if (!rowA && !rowB) {
1609 builder.addRun(bounds.fLeft, bot - 1, 0, bounds.width());
1610 } else if (top >= bounds.fTop) {
1611 SkASSERT(bot <= bounds.fBottom);
1612 RowIter rowIterA(rowA, rowA ? A.getBounds() : bounds);
1613 RowIter rowIterB(rowB, rowB ? B.getBounds() : bounds);
reed@google.com32287892011-10-05 16:27:44 +00001614 operatorX(builder, bot - 1, rowIterA, rowIterB, proc, bounds);
reed@google.com32287892011-10-05 16:27:44 +00001615 }
1616
reed@google.com1c04bf92011-10-10 12:57:12 +00001617 adjust_iter(iterA, topA, botA, bot);
1618 adjust_iter(iterB, topB, botB, bot);
1619 } while (!iterA.done() || !iterB.done());
reed@google.com32287892011-10-05 16:27:44 +00001620}
1621
1622bool SkAAClip::op(const SkAAClip& clipAOrig, const SkAAClip& clipBOrig,
1623 SkRegion::Op op) {
reed@google.com045e62d2011-10-24 12:19:46 +00001624 AUTO_AACLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001625
reed@google.com32287892011-10-05 16:27:44 +00001626 if (SkRegion::kReplace_Op == op) {
1627 return this->set(clipBOrig);
1628 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001629
reed@google.com32287892011-10-05 16:27:44 +00001630 const SkAAClip* clipA = &clipAOrig;
1631 const SkAAClip* clipB = &clipBOrig;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001632
reed@google.com32287892011-10-05 16:27:44 +00001633 if (SkRegion::kReverseDifference_Op == op) {
1634 SkTSwap(clipA, clipB);
1635 op = SkRegion::kDifference_Op;
1636 }
1637
1638 bool a_empty = clipA->isEmpty();
1639 bool b_empty = clipB->isEmpty();
1640
1641 SkIRect bounds;
1642 switch (op) {
1643 case SkRegion::kDifference_Op:
1644 if (a_empty) {
1645 return this->setEmpty();
1646 }
1647 if (b_empty || !SkIRect::Intersects(clipA->fBounds, clipB->fBounds)) {
1648 return this->set(*clipA);
1649 }
1650 bounds = clipA->fBounds;
1651 break;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001652
reed@google.com32287892011-10-05 16:27:44 +00001653 case SkRegion::kIntersect_Op:
1654 if ((a_empty | b_empty) || !bounds.intersect(clipA->fBounds,
1655 clipB->fBounds)) {
1656 return this->setEmpty();
1657 }
1658 break;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001659
reed@google.com32287892011-10-05 16:27:44 +00001660 case SkRegion::kUnion_Op:
1661 case SkRegion::kXOR_Op:
1662 if (a_empty) {
1663 return this->set(*clipB);
1664 }
1665 if (b_empty) {
1666 return this->set(*clipA);
1667 }
1668 bounds = clipA->fBounds;
1669 bounds.join(clipB->fBounds);
1670 break;
1671
1672 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001673 SkDEBUGFAIL("unknown region op");
reed@google.com32287892011-10-05 16:27:44 +00001674 return !this->isEmpty();
1675 }
1676
reed@google.com32287892011-10-05 16:27:44 +00001677 SkASSERT(SkIRect::Intersects(bounds, clipB->fBounds));
1678 SkASSERT(SkIRect::Intersects(bounds, clipB->fBounds));
1679
1680 Builder builder(bounds);
1681 operateY(builder, *clipA, *clipB, op);
reed@google.com045e62d2011-10-24 12:19:46 +00001682
1683 return builder.finish(this);
reed@google.come36707a2011-10-04 21:38:55 +00001684}
1685
reed@google.com045e62d2011-10-24 12:19:46 +00001686/*
1687 * It can be expensive to build a local aaclip before applying the op, so
1688 * we first see if we can restrict the bounds of new rect to our current
1689 * bounds, or note that the new rect subsumes our current clip.
1690 */
1691
1692bool SkAAClip::op(const SkIRect& rOrig, SkRegion::Op op) {
1693 SkIRect rStorage;
1694 const SkIRect* r = &rOrig;
1695
1696 switch (op) {
1697 case SkRegion::kIntersect_Op:
1698 if (!rStorage.intersect(rOrig, fBounds)) {
1699 // no overlap, so we're empty
1700 return this->setEmpty();
1701 }
1702 if (rStorage == fBounds) {
1703 // we were wholly inside the rect, no change
1704 return !this->isEmpty();
1705 }
1706 if (this->quickContains(rStorage)) {
1707 // the intersection is wholly inside us, we're a rect
1708 return this->setRect(rStorage);
1709 }
1710 r = &rStorage; // use the intersected bounds
1711 break;
1712 case SkRegion::kDifference_Op:
1713 break;
1714 case SkRegion::kUnion_Op:
1715 if (rOrig.contains(fBounds)) {
1716 return this->setRect(rOrig);
1717 }
1718 break;
1719 default:
1720 break;
1721 }
1722
reed@google.com47ac84e2011-10-06 13:11:25 +00001723 SkAAClip clip;
reed@google.com045e62d2011-10-24 12:19:46 +00001724 clip.setRect(*r);
reed@google.com47ac84e2011-10-06 13:11:25 +00001725 return this->op(*this, clip, op);
1726}
1727
reed@google.com045e62d2011-10-24 12:19:46 +00001728bool SkAAClip::op(const SkRect& rOrig, SkRegion::Op op, bool doAA) {
1729 SkRect rStorage, boundsStorage;
1730 const SkRect* r = &rOrig;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001731
reed@google.com045e62d2011-10-24 12:19:46 +00001732 boundsStorage.set(fBounds);
1733 switch (op) {
1734 case SkRegion::kIntersect_Op:
1735 case SkRegion::kDifference_Op:
1736 if (!rStorage.intersect(rOrig, boundsStorage)) {
reed@google.come56513d2012-06-25 20:06:33 +00001737 if (SkRegion::kIntersect_Op == op) {
1738 return this->setEmpty();
1739 } else { // kDifference
1740 return !this->isEmpty();
1741 }
reed@google.com045e62d2011-10-24 12:19:46 +00001742 }
1743 r = &rStorage; // use the intersected bounds
1744 break;
1745 case SkRegion::kUnion_Op:
1746 if (rOrig.contains(boundsStorage)) {
1747 return this->setRect(rOrig);
1748 }
1749 break;
1750 default:
1751 break;
1752 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001753
reed@google.com47ac84e2011-10-06 13:11:25 +00001754 SkAAClip clip;
reed@google.com045e62d2011-10-24 12:19:46 +00001755 clip.setRect(*r, doAA);
reed@google.com47ac84e2011-10-06 13:11:25 +00001756 return this->op(*this, clip, op);
1757}
1758
1759bool SkAAClip::op(const SkAAClip& clip, SkRegion::Op op) {
1760 return this->op(*this, clip, op);
1761}
1762
reed@google.come36707a2011-10-04 21:38:55 +00001763///////////////////////////////////////////////////////////////////////////////
reed@google.com045e62d2011-10-24 12:19:46 +00001764
1765bool SkAAClip::translate(int dx, int dy, SkAAClip* dst) const {
1766 if (NULL == dst) {
1767 return !this->isEmpty();
1768 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001769
reed@google.com045e62d2011-10-24 12:19:46 +00001770 if (this->isEmpty()) {
1771 return dst->setEmpty();
1772 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001773
reed@google.com045e62d2011-10-24 12:19:46 +00001774 if (this != dst) {
1775 sk_atomic_inc(&fRunHead->fRefCnt);
tomhudson@google.com19224c32012-03-28 15:46:37 +00001776 dst->freeRuns();
reed@google.com045e62d2011-10-24 12:19:46 +00001777 dst->fRunHead = fRunHead;
1778 dst->fBounds = fBounds;
1779 }
1780 dst->fBounds.offset(dx, dy);
1781 return true;
1782}
1783
1784static void expand_row_to_mask(uint8_t* SK_RESTRICT mask,
1785 const uint8_t* SK_RESTRICT row,
1786 int width) {
1787 while (width > 0) {
1788 int n = row[0];
1789 SkASSERT(width >= n);
1790 memset(mask, row[1], n);
1791 mask += n;
1792 row += 2;
1793 width -= n;
1794 }
reed@google.coma069c8f2011-11-28 19:54:56 +00001795 SkASSERT(0 == width);
reed@google.com045e62d2011-10-24 12:19:46 +00001796}
1797
1798void SkAAClip::copyToMask(SkMask* mask) const {
1799 mask->fFormat = SkMask::kA8_Format;
1800 if (this->isEmpty()) {
1801 mask->fBounds.setEmpty();
1802 mask->fImage = NULL;
1803 mask->fRowBytes = 0;
1804 return;
1805 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001806
reed@google.com045e62d2011-10-24 12:19:46 +00001807 mask->fBounds = fBounds;
1808 mask->fRowBytes = fBounds.width();
1809 size_t size = mask->computeImageSize();
1810 mask->fImage = SkMask::AllocImage(size);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001811
reed@google.com045e62d2011-10-24 12:19:46 +00001812 Iter iter(*this);
1813 uint8_t* dst = mask->fImage;
1814 const int width = fBounds.width();
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001815
reed@google.com045e62d2011-10-24 12:19:46 +00001816 int y = fBounds.fTop;
1817 while (!iter.done()) {
1818 do {
1819 expand_row_to_mask(dst, iter.data(), width);
1820 dst += mask->fRowBytes;
1821 } while (++y < iter.bottom());
1822 iter.next();
1823 }
1824}
1825
1826///////////////////////////////////////////////////////////////////////////////
reed@google.come36707a2011-10-04 21:38:55 +00001827///////////////////////////////////////////////////////////////////////////////
1828
1829static void expandToRuns(const uint8_t* SK_RESTRICT data, int initialCount, int width,
1830 int16_t* SK_RESTRICT runs, SkAlpha* SK_RESTRICT aa) {
1831 // we don't read our initial n from data, since the caller may have had to
1832 // clip it, hence the initialCount parameter.
1833 int n = initialCount;
1834 for (;;) {
1835 if (n > width) {
1836 n = width;
1837 }
1838 SkASSERT(n > 0);
1839 runs[0] = n;
1840 runs += n;
1841
1842 aa[0] = data[1];
1843 aa += n;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001844
reed@google.come36707a2011-10-04 21:38:55 +00001845 data += 2;
1846 width -= n;
1847 if (0 == width) {
1848 break;
1849 }
1850 // load the next count
1851 n = data[0];
1852 }
1853 runs[0] = 0; // sentinel
1854}
1855
1856SkAAClipBlitter::~SkAAClipBlitter() {
reed@google.com045e62d2011-10-24 12:19:46 +00001857 sk_free(fScanlineScratch);
reed@google.come36707a2011-10-04 21:38:55 +00001858}
1859
1860void SkAAClipBlitter::ensureRunsAndAA() {
reed@google.com045e62d2011-10-24 12:19:46 +00001861 if (NULL == fScanlineScratch) {
reed@google.come36707a2011-10-04 21:38:55 +00001862 // add 1 so we can store the terminating run count of 0
1863 int count = fAAClipBounds.width() + 1;
reed@google.com045e62d2011-10-24 12:19:46 +00001864 // we use this either for fRuns + fAA, or a scaline of a mask
1865 // which may be as deep as 32bits
1866 fScanlineScratch = sk_malloc_throw(count * sizeof(SkPMColor));
1867 fRuns = (int16_t*)fScanlineScratch;
reed@google.come36707a2011-10-04 21:38:55 +00001868 fAA = (SkAlpha*)(fRuns + count);
1869 }
1870}
1871
1872void SkAAClipBlitter::blitH(int x, int y, int width) {
1873 SkASSERT(width > 0);
1874 SkASSERT(fAAClipBounds.contains(x, y));
1875 SkASSERT(fAAClipBounds.contains(x + width - 1, y));
1876
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001877 const uint8_t* row = fAAClip->findRow(y);
reed@google.come36707a2011-10-04 21:38:55 +00001878 int initialCount;
1879 row = fAAClip->findX(row, x, &initialCount);
1880
1881 if (initialCount >= width) {
1882 SkAlpha alpha = row[1];
1883 if (0 == alpha) {
1884 return;
1885 }
1886 if (0xFF == alpha) {
1887 fBlitter->blitH(x, y, width);
1888 return;
1889 }
1890 }
1891
1892 this->ensureRunsAndAA();
1893 expandToRuns(row, initialCount, width, fRuns, fAA);
1894
1895 fBlitter->blitAntiH(x, y, fAA, fRuns);
1896}
1897
1898static void merge(const uint8_t* SK_RESTRICT row, int rowN,
1899 const SkAlpha* SK_RESTRICT srcAA,
1900 const int16_t* SK_RESTRICT srcRuns,
1901 SkAlpha* SK_RESTRICT dstAA,
1902 int16_t* SK_RESTRICT dstRuns,
1903 int width) {
1904 SkDEBUGCODE(int accumulated = 0;)
1905 int srcN = srcRuns[0];
reed@google.com045e62d2011-10-24 12:19:46 +00001906 // do we need this check?
1907 if (0 == srcN) {
1908 return;
1909 }
1910
reed@google.come36707a2011-10-04 21:38:55 +00001911 for (;;) {
reed@google.come36707a2011-10-04 21:38:55 +00001912 SkASSERT(rowN > 0);
1913 SkASSERT(srcN > 0);
1914
1915 unsigned newAlpha = SkMulDiv255Round(srcAA[0], row[1]);
1916 int minN = SkMin32(srcN, rowN);
1917 dstRuns[0] = minN;
1918 dstRuns += minN;
1919 dstAA[0] = newAlpha;
1920 dstAA += minN;
1921
1922 if (0 == (srcN -= minN)) {
1923 srcN = srcRuns[0]; // refresh
1924 srcRuns += srcN;
1925 srcAA += srcN;
1926 srcN = srcRuns[0]; // reload
reed@google.com045e62d2011-10-24 12:19:46 +00001927 if (0 == srcN) {
1928 break;
1929 }
reed@google.come36707a2011-10-04 21:38:55 +00001930 }
1931 if (0 == (rowN -= minN)) {
1932 row += 2;
1933 rowN = row[0]; // reload
1934 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001935
reed@google.come36707a2011-10-04 21:38:55 +00001936 SkDEBUGCODE(accumulated += minN;)
1937 SkASSERT(accumulated <= width);
1938 }
reed@google.com34f7e472011-10-13 15:11:59 +00001939 dstRuns[0] = 0;
reed@google.come36707a2011-10-04 21:38:55 +00001940}
1941
1942void SkAAClipBlitter::blitAntiH(int x, int y, const SkAlpha aa[],
1943 const int16_t runs[]) {
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001944
1945 const uint8_t* row = fAAClip->findRow(y);
reed@google.come36707a2011-10-04 21:38:55 +00001946 int initialCount;
1947 row = fAAClip->findX(row, x, &initialCount);
1948
1949 this->ensureRunsAndAA();
1950
1951 merge(row, initialCount, aa, runs, fAA, fRuns, fAAClipBounds.width());
1952 fBlitter->blitAntiH(x, y, fAA, fRuns);
1953}
1954
1955void SkAAClipBlitter::blitV(int x, int y, int height, SkAlpha alpha) {
1956 if (fAAClip->quickContains(x, y, x + 1, y + height)) {
1957 fBlitter->blitV(x, y, height, alpha);
1958 return;
1959 }
1960
reed@google.com045e62d2011-10-24 12:19:46 +00001961 for (;;) {
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001962 int lastY SK_INIT_TO_AVOID_WARNING;
reed@google.come36707a2011-10-04 21:38:55 +00001963 const uint8_t* row = fAAClip->findRow(y, &lastY);
reed@google.com045e62d2011-10-24 12:19:46 +00001964 int dy = lastY - y + 1;
1965 if (dy > height) {
1966 dy = height;
1967 }
1968 height -= dy;
1969
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001970 row = fAAClip->findX(row, x);
reed@google.come36707a2011-10-04 21:38:55 +00001971 SkAlpha newAlpha = SkMulDiv255Round(alpha, row[1]);
1972 if (newAlpha) {
reed@google.com045e62d2011-10-24 12:19:46 +00001973 fBlitter->blitV(x, y, dy, newAlpha);
1974 }
1975 SkASSERT(height >= 0);
1976 if (height <= 0) {
1977 break;
reed@google.come36707a2011-10-04 21:38:55 +00001978 }
1979 y = lastY + 1;
reed@google.com045e62d2011-10-24 12:19:46 +00001980 }
reed@google.come36707a2011-10-04 21:38:55 +00001981}
1982
1983void SkAAClipBlitter::blitRect(int x, int y, int width, int height) {
1984 if (fAAClip->quickContains(x, y, x + width, y + height)) {
1985 fBlitter->blitRect(x, y, width, height);
1986 return;
1987 }
1988
1989 while (--height >= 0) {
1990 this->blitH(x, y, width);
1991 y += 1;
1992 }
1993}
1994
reed@google.com045e62d2011-10-24 12:19:46 +00001995typedef void (*MergeAAProc)(const void* src, int width, const uint8_t* row,
1996 int initialRowCount, void* dst);
1997
1998static void small_memcpy(void* dst, const void* src, size_t n) {
1999 memcpy(dst, src, n);
2000}
2001
2002static void small_bzero(void* dst, size_t n) {
2003 sk_bzero(dst, n);
2004}
2005
2006static inline uint8_t mergeOne(uint8_t value, unsigned alpha) {
2007 return SkMulDiv255Round(value, alpha);
2008}
2009static inline uint16_t mergeOne(uint16_t value, unsigned alpha) {
2010 unsigned r = SkGetPackedR16(value);
2011 unsigned g = SkGetPackedG16(value);
2012 unsigned b = SkGetPackedB16(value);
2013 return SkPackRGB16(SkMulDiv255Round(r, alpha),
caryclark@google.com803eceb2012-06-06 12:09:34 +00002014 SkMulDiv255Round(g, alpha),
2015 SkMulDiv255Round(b, alpha));
reed@google.com045e62d2011-10-24 12:19:46 +00002016}
2017static inline SkPMColor mergeOne(SkPMColor value, unsigned alpha) {
2018 unsigned a = SkGetPackedA32(value);
2019 unsigned r = SkGetPackedR32(value);
2020 unsigned g = SkGetPackedG32(value);
2021 unsigned b = SkGetPackedB32(value);
2022 return SkPackARGB32(SkMulDiv255Round(a, alpha),
2023 SkMulDiv255Round(r, alpha),
2024 SkMulDiv255Round(g, alpha),
2025 SkMulDiv255Round(b, alpha));
2026}
2027
2028template <typename T> void mergeT(const T* SK_RESTRICT src, int srcN,
2029 const uint8_t* SK_RESTRICT row, int rowN,
2030 T* SK_RESTRICT dst) {
reed@google.com045e62d2011-10-24 12:19:46 +00002031 for (;;) {
2032 SkASSERT(rowN > 0);
2033 SkASSERT(srcN > 0);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002034
reed@google.com045e62d2011-10-24 12:19:46 +00002035 int n = SkMin32(rowN, srcN);
2036 unsigned rowA = row[1];
2037 if (0xFF == rowA) {
2038 small_memcpy(dst, src, n * sizeof(T));
2039 } else if (0 == rowA) {
2040 small_bzero(dst, n * sizeof(T));
2041 } else {
2042 for (int i = 0; i < n; ++i) {
2043 dst[i] = mergeOne(src[i], rowA);
2044 }
2045 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002046
reed@google.com045e62d2011-10-24 12:19:46 +00002047 if (0 == (srcN -= n)) {
2048 break;
2049 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002050
reed@google.com045e62d2011-10-24 12:19:46 +00002051 src += n;
2052 dst += n;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002053
reed@google.com045e62d2011-10-24 12:19:46 +00002054 SkASSERT(rowN == n);
2055 row += 2;
2056 rowN = row[0];
2057 }
2058}
2059
2060static MergeAAProc find_merge_aa_proc(SkMask::Format format) {
2061 switch (format) {
2062 case SkMask::kBW_Format:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002063 SkDEBUGFAIL("unsupported");
reed@google.com045e62d2011-10-24 12:19:46 +00002064 return NULL;
2065 case SkMask::kA8_Format:
2066 case SkMask::k3D_Format: {
2067 void (*proc8)(const uint8_t*, int, const uint8_t*, int, uint8_t*) = mergeT;
2068 return (MergeAAProc)proc8;
2069 }
2070 case SkMask::kLCD16_Format: {
2071 void (*proc16)(const uint16_t*, int, const uint8_t*, int, uint16_t*) = mergeT;
2072 return (MergeAAProc)proc16;
2073 }
2074 case SkMask::kLCD32_Format: {
2075 void (*proc32)(const SkPMColor*, int, const uint8_t*, int, SkPMColor*) = mergeT;
2076 return (MergeAAProc)proc32;
2077 }
2078 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002079 SkDEBUGFAIL("unsupported");
reed@google.com045e62d2011-10-24 12:19:46 +00002080 return NULL;
2081 }
2082}
2083
2084static U8CPU bit2byte(int bitInAByte) {
2085 SkASSERT(bitInAByte <= 0xFF);
2086 // negation turns any non-zero into 0xFFFFFF??, so we just shift down
2087 // some value >= 8 to get a full FF value
2088 return -bitInAByte >> 8;
2089}
2090
2091static void upscaleBW2A8(SkMask* dstMask, const SkMask& srcMask) {
2092 SkASSERT(SkMask::kBW_Format == srcMask.fFormat);
2093 SkASSERT(SkMask::kA8_Format == dstMask->fFormat);
2094
2095 const int width = srcMask.fBounds.width();
2096 const int height = srcMask.fBounds.height();
2097
2098 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcMask.fImage;
2099 const size_t srcRB = srcMask.fRowBytes;
2100 uint8_t* SK_RESTRICT dst = (uint8_t*)dstMask->fImage;
2101 const size_t dstRB = dstMask->fRowBytes;
2102
2103 const int wholeBytes = width >> 3;
2104 const int leftOverBits = width & 7;
2105
2106 for (int y = 0; y < height; ++y) {
2107 uint8_t* SK_RESTRICT d = dst;
2108 for (int i = 0; i < wholeBytes; ++i) {
2109 int srcByte = src[i];
2110 d[0] = bit2byte(srcByte & (1 << 7));
2111 d[1] = bit2byte(srcByte & (1 << 6));
2112 d[2] = bit2byte(srcByte & (1 << 5));
2113 d[3] = bit2byte(srcByte & (1 << 4));
2114 d[4] = bit2byte(srcByte & (1 << 3));
2115 d[5] = bit2byte(srcByte & (1 << 2));
2116 d[6] = bit2byte(srcByte & (1 << 1));
2117 d[7] = bit2byte(srcByte & (1 << 0));
2118 d += 8;
2119 }
2120 if (leftOverBits) {
2121 int srcByte = src[wholeBytes];
2122 for (int x = 0; x < leftOverBits; ++x) {
2123 *d++ = bit2byte(srcByte & 0x80);
2124 srcByte <<= 1;
2125 }
2126 }
2127 src += srcRB;
2128 dst += dstRB;
2129 }
2130}
2131
2132void SkAAClipBlitter::blitMask(const SkMask& origMask, const SkIRect& clip) {
2133 SkASSERT(fAAClip->getBounds().contains(clip));
2134
2135 if (fAAClip->quickContains(clip)) {
2136 fBlitter->blitMask(origMask, clip);
2137 return;
2138 }
2139
2140 const SkMask* mask = &origMask;
2141
2142 // if we're BW, we need to upscale to A8 (ugh)
2143 SkMask grayMask;
2144 grayMask.fImage = NULL;
2145 if (SkMask::kBW_Format == origMask.fFormat) {
2146 grayMask.fFormat = SkMask::kA8_Format;
2147 grayMask.fBounds = origMask.fBounds;
2148 grayMask.fRowBytes = origMask.fBounds.width();
2149 size_t size = grayMask.computeImageSize();
2150 grayMask.fImage = (uint8_t*)fGrayMaskScratch.reset(size,
2151 SkAutoMalloc::kReuse_OnShrink);
2152
2153 upscaleBW2A8(&grayMask, origMask);
2154 mask = &grayMask;
2155 }
2156
2157 this->ensureRunsAndAA();
2158
2159 // HACK -- we are devolving 3D into A8, need to copy the rest of the 3D
2160 // data into a temp block to support it better (ugh)
2161
2162 const void* src = mask->getAddr(clip.fLeft, clip.fTop);
2163 const size_t srcRB = mask->fRowBytes;
2164 const int width = clip.width();
2165 MergeAAProc mergeProc = find_merge_aa_proc(mask->fFormat);
2166
2167 SkMask rowMask;
2168 rowMask.fFormat = SkMask::k3D_Format == mask->fFormat ? SkMask::kA8_Format : mask->fFormat;
2169 rowMask.fBounds.fLeft = clip.fLeft;
2170 rowMask.fBounds.fRight = clip.fRight;
2171 rowMask.fRowBytes = mask->fRowBytes; // doesn't matter, since our height==1
2172 rowMask.fImage = (uint8_t*)fScanlineScratch;
2173
2174 int y = clip.fTop;
2175 const int stopY = y + clip.height();
2176
2177 do {
reed@google.coma4c6e4d2012-06-20 14:29:50 +00002178 int localStopY SK_INIT_TO_AVOID_WARNING;
reed@google.com045e62d2011-10-24 12:19:46 +00002179 const uint8_t* row = fAAClip->findRow(y, &localStopY);
2180 // findRow returns last Y, not stop, so we add 1
2181 localStopY = SkMin32(localStopY + 1, stopY);
2182
2183 int initialCount;
2184 row = fAAClip->findX(row, clip.fLeft, &initialCount);
2185 do {
2186 mergeProc(src, width, row, initialCount, rowMask.fImage);
2187 rowMask.fBounds.fTop = y;
2188 rowMask.fBounds.fBottom = y + 1;
2189 fBlitter->blitMask(rowMask, rowMask.fBounds);
2190 src = (const void*)((const char*)src + srcRB);
2191 } while (++y < localStopY);
2192 } while (y < stopY);
reed@google.come36707a2011-10-04 21:38:55 +00002193}
2194
2195const SkBitmap* SkAAClipBlitter::justAnOpaqueColor(uint32_t* value) {
2196 return NULL;
2197}