blob: fce668399905a69d2e676c7ce93635891a808444 [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
reed@google.come36707a2011-10-04 21:38:55 +000039static inline bool x_in_rect(int x, const SkIRect& rect) {
40 return (unsigned)(x - rect.fLeft) < (unsigned)rect.width();
41}
42
43static inline bool y_in_rect(int y, const SkIRect& rect) {
44 return (unsigned)(y - rect.fTop) < (unsigned)rect.height();
45}
46
47/*
48 * Data runs are packed [count, alpha]
49 */
50
51struct SkAAClip::YOffset {
52 int32_t fY;
53 uint32_t fOffset;
54};
55
56struct SkAAClip::RunHead {
57 int32_t fRefCnt;
58 int32_t fRowCount;
59 int32_t fDataSize;
rmistry@google.comfbfcd562012-08-23 18:09:54 +000060
reed@google.come36707a2011-10-04 21:38:55 +000061 YOffset* yoffsets() {
62 return (YOffset*)((char*)this + sizeof(RunHead));
63 }
64 const YOffset* yoffsets() const {
65 return (const YOffset*)((const char*)this + sizeof(RunHead));
66 }
67 uint8_t* data() {
68 return (uint8_t*)(this->yoffsets() + fRowCount);
69 }
70 const uint8_t* data() const {
71 return (const uint8_t*)(this->yoffsets() + fRowCount);
72 }
73
74 static RunHead* Alloc(int rowCount, size_t dataSize) {
75 size_t size = sizeof(RunHead) + rowCount * sizeof(YOffset) + dataSize;
76 RunHead* head = (RunHead*)sk_malloc_throw(size);
77 head->fRefCnt = 1;
78 head->fRowCount = rowCount;
79 head->fDataSize = dataSize;
80 return head;
81 }
reed@google.com045e62d2011-10-24 12:19:46 +000082
83 static int ComputeRowSizeForWidth(int width) {
84 // 2 bytes per segment, where each segment can store up to 255 for count
85 int segments = 0;
86 while (width > 0) {
87 segments += 1;
88 int n = SkMin32(width, 255);
89 width -= n;
90 }
91 return segments * 2; // each segment is row[0] + row[1] (n + alpha)
92 }
93
94 static RunHead* AllocRect(const SkIRect& bounds) {
95 SkASSERT(!bounds.isEmpty());
96 int width = bounds.width();
97 size_t rowSize = ComputeRowSizeForWidth(width);
98 RunHead* head = RunHead::Alloc(1, rowSize);
99 YOffset* yoff = head->yoffsets();
100 yoff->fY = bounds.height() - 1;
101 yoff->fOffset = 0;
102 uint8_t* row = head->data();
103 while (width > 0) {
104 int n = SkMin32(width, 255);
105 row[0] = n;
106 row[1] = 0xFF;
107 width -= n;
108 row += 2;
109 }
110 return head;
111 }
reed@google.come36707a2011-10-04 21:38:55 +0000112};
113
reed@google.com32287892011-10-05 16:27:44 +0000114class SkAAClip::Iter {
115public:
116 Iter(const SkAAClip&);
117
118 bool done() const { return fDone; }
reed@google.com1c04bf92011-10-10 12:57:12 +0000119 int top() const { return fTop; }
120 int bottom() const { return fBottom; }
121 const uint8_t* data() const { return fData; }
reed@google.com32287892011-10-05 16:27:44 +0000122 void next();
123
124private:
125 const YOffset* fCurrYOff;
126 const YOffset* fStopYOff;
127 const uint8_t* fData;
128
129 int fTop, fBottom;
130 bool fDone;
131};
132
133SkAAClip::Iter::Iter(const SkAAClip& clip) {
134 if (clip.isEmpty()) {
135 fDone = true;
reed@google.com1c04bf92011-10-10 12:57:12 +0000136 fTop = fBottom = clip.fBounds.fBottom;
137 fData = NULL;
reed@google.coma4c6e4d2012-06-20 14:29:50 +0000138 fStopYOff = NULL;
reed@google.com32287892011-10-05 16:27:44 +0000139 return;
140 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000141
reed@google.com32287892011-10-05 16:27:44 +0000142 const RunHead* head = clip.fRunHead;
143 fCurrYOff = head->yoffsets();
144 fStopYOff = fCurrYOff + head->fRowCount;
145 fData = head->data() + fCurrYOff->fOffset;
146
147 // setup first value
148 fTop = clip.fBounds.fTop;
149 fBottom = clip.fBounds.fTop + fCurrYOff->fY + 1;
150 fDone = false;
151}
152
153void SkAAClip::Iter::next() {
reed@google.com1c04bf92011-10-10 12:57:12 +0000154 if (!fDone) {
155 const YOffset* prev = fCurrYOff;
156 const YOffset* curr = prev + 1;
157 SkASSERT(curr <= fStopYOff);
reed@google.com32287892011-10-05 16:27:44 +0000158
reed@google.com32287892011-10-05 16:27:44 +0000159 fTop = fBottom;
reed@google.com1c04bf92011-10-10 12:57:12 +0000160 if (curr >= fStopYOff) {
161 fDone = true;
162 fBottom = kMaxInt32;
163 fData = NULL;
164 } else {
165 fBottom += curr->fY - prev->fY;
166 fData += curr->fOffset - prev->fOffset;
167 fCurrYOff = curr;
168 }
reed@google.com32287892011-10-05 16:27:44 +0000169 }
170}
171
reed@google.com045e62d2011-10-24 12:19:46 +0000172#ifdef SK_DEBUG
reed@google.comc9041912011-10-27 16:58:46 +0000173// assert we're exactly width-wide, and then return the number of bytes used
reed@google.com045e62d2011-10-24 12:19:46 +0000174static size_t compute_row_length(const uint8_t row[], int width) {
175 const uint8_t* origRow = row;
176 while (width > 0) {
177 int n = row[0];
reed@google.comc9041912011-10-27 16:58:46 +0000178 SkASSERT(n > 0);
reed@google.com045e62d2011-10-24 12:19:46 +0000179 SkASSERT(n <= width);
180 row += 2;
181 width -= n;
182 }
183 SkASSERT(0 == width);
184 return row - origRow;
185}
186
187void SkAAClip::validate() const {
188 if (NULL == fRunHead) {
189 SkASSERT(fBounds.isEmpty());
190 return;
191 }
192
193 const RunHead* head = fRunHead;
194 SkASSERT(head->fRefCnt > 0);
195 SkASSERT(head->fRowCount > 0);
196 SkASSERT(head->fDataSize > 0);
197
198 const YOffset* yoff = head->yoffsets();
199 const YOffset* ystop = yoff + head->fRowCount;
reed@google.comc9041912011-10-27 16:58:46 +0000200 const int lastY = fBounds.height() - 1;
201
202 // Y and offset must be monotonic
203 int prevY = -1;
204 int32_t prevOffset = -1;
reed@google.com045e62d2011-10-24 12:19:46 +0000205 while (yoff < ystop) {
reed@google.comc9041912011-10-27 16:58:46 +0000206 SkASSERT(prevY < yoff->fY);
207 SkASSERT(yoff->fY <= lastY);
208 prevY = yoff->fY;
209 SkASSERT(prevOffset < (int32_t)yoff->fOffset);
210 prevOffset = yoff->fOffset;
211 const uint8_t* row = head->data() + yoff->fOffset;
reed@google.com045e62d2011-10-24 12:19:46 +0000212 size_t rowLength = compute_row_length(row, fBounds.width());
tomhudson@google.comf74ad8c2011-11-09 22:15:08 +0000213 SkASSERT(yoff->fOffset + rowLength <= (size_t) head->fDataSize);
reed@google.comc9041912011-10-27 16:58:46 +0000214 yoff += 1;
reed@google.com045e62d2011-10-24 12:19:46 +0000215 }
reed@google.com045e62d2011-10-24 12:19:46 +0000216 // check the last entry;
217 --yoff;
reed@google.comc9041912011-10-27 16:58:46 +0000218 SkASSERT(yoff->fY == lastY);
reed@google.com045e62d2011-10-24 12:19:46 +0000219}
220#endif
221
222///////////////////////////////////////////////////////////////////////////////
223
robertphillips@google.com768fee82012-08-02 12:42:43 +0000224// Count the number of zeros on the left and right edges of the passed in
225// RLE row. If 'row' is all zeros return 'width' in both variables.
reed@google.comc9041912011-10-27 16:58:46 +0000226static void count_left_right_zeros(const uint8_t* row, int width,
227 int* leftZ, int* riteZ) {
228 int zeros = 0;
229 do {
230 if (row[1]) {
231 break;
232 }
233 int n = row[0];
234 SkASSERT(n > 0);
235 SkASSERT(n <= width);
236 zeros += n;
237 row += 2;
238 width -= n;
239 } while (width > 0);
240 *leftZ = zeros;
241
robertphillips@google.com768fee82012-08-02 12:42:43 +0000242 if (0 == width) {
243 // this line is completely empty return 'width' in both variables
244 *riteZ = *leftZ;
245 return;
246 }
247
reed@google.comc9041912011-10-27 16:58:46 +0000248 zeros = 0;
249 while (width > 0) {
250 int n = row[0];
251 SkASSERT(n > 0);
252 if (0 == row[1]) {
253 zeros += n;
254 } else {
255 zeros = 0;
256 }
257 row += 2;
258 width -= n;
259 }
260 *riteZ = zeros;
261}
262
263#ifdef SK_DEBUG
264static void test_count_left_right_zeros() {
265 static bool gOnce;
266 if (gOnce) {
267 return;
268 }
269 gOnce = true;
270
271 const uint8_t data0[] = { 0, 0, 10, 0xFF };
272 const uint8_t data1[] = { 0, 0, 5, 0xFF, 2, 0, 3, 0xFF };
273 const uint8_t data2[] = { 7, 0, 5, 0, 2, 0, 3, 0xFF };
274 const uint8_t data3[] = { 0, 5, 5, 0xFF, 2, 0, 3, 0 };
275 const uint8_t data4[] = { 2, 3, 2, 0, 5, 0xFF, 3, 0 };
robertphillips@google.com768fee82012-08-02 12:42:43 +0000276 const uint8_t data5[] = { 10, 10, 10, 0 };
reed@google.comc9041912011-10-27 16:58:46 +0000277 const uint8_t data6[] = { 2, 2, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
278
279 const uint8_t* array[] = {
280 data0, data1, data2, data3, data4, data5, data6
281 };
282
283 for (size_t i = 0; i < SK_ARRAY_COUNT(array); ++i) {
284 const uint8_t* data = array[i];
285 const int expectedL = *data++;
286 const int expectedR = *data++;
287 int L = 12345, R = 12345;
288 count_left_right_zeros(data, 10, &L, &R);
289 SkASSERT(expectedL == L);
290 SkASSERT(expectedR == R);
291 }
292}
293#endif
294
295// modify row in place, trimming off (zeros) from the left and right sides.
296// return the number of bytes that were completely eliminated from the left
297static int trim_row_left_right(uint8_t* row, int width, int leftZ, int riteZ) {
298 int trim = 0;
299 while (leftZ > 0) {
300 SkASSERT(0 == row[1]);
301 int n = row[0];
302 SkASSERT(n > 0);
303 SkASSERT(n <= width);
304 width -= n;
305 row += 2;
306 if (n > leftZ) {
307 row[-2] = n - leftZ;
308 break;
309 }
310 trim += 2;
311 leftZ -= n;
312 SkASSERT(leftZ >= 0);
313 }
314
315 if (riteZ) {
316 // walk row to the end, and then we'll back up to trim riteZ
317 while (width > 0) {
318 int n = row[0];
319 SkASSERT(n <= width);
320 width -= n;
321 row += 2;
322 }
323 // now skip whole runs of zeros
324 do {
325 row -= 2;
326 SkASSERT(0 == row[1]);
327 int n = row[0];
328 SkASSERT(n > 0);
329 if (n > riteZ) {
330 row[0] = n - riteZ;
331 break;
332 }
333 riteZ -= n;
334 SkASSERT(riteZ >= 0);
335 } while (riteZ > 0);
336 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000337
reed@google.comc9041912011-10-27 16:58:46 +0000338 return trim;
339}
340
341#ifdef SK_DEBUG
342// assert that this row is exactly this width
reed@google.comc5507bf2011-10-27 21:15:36 +0000343static void assert_row_width(const uint8_t* row, int width) {
reed@google.comc9041912011-10-27 16:58:46 +0000344 while (width > 0) {
345 int n = row[0];
346 SkASSERT(n > 0);
347 SkASSERT(n <= width);
348 width -= n;
349 row += 2;
350 }
351 SkASSERT(0 == width);
352}
353
354static void test_trim_row_left_right() {
355 static bool gOnce;
356 if (gOnce) {
357 return;
358 }
359 gOnce = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000360
reed@google.comc9041912011-10-27 16:58:46 +0000361 uint8_t data0[] = { 0, 0, 0, 10, 10, 0xFF };
362 uint8_t data1[] = { 2, 0, 0, 10, 5, 0, 2, 0, 3, 0xFF };
363 uint8_t data2[] = { 5, 0, 2, 10, 5, 0, 2, 0, 3, 0xFF };
364 uint8_t data3[] = { 6, 0, 2, 10, 5, 0, 2, 0, 3, 0xFF };
365 uint8_t data4[] = { 0, 0, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
366 uint8_t data5[] = { 1, 0, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
367 uint8_t data6[] = { 0, 1, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
368 uint8_t data7[] = { 1, 1, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
369 uint8_t data8[] = { 2, 2, 2, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
370 uint8_t data9[] = { 5, 2, 4, 10, 2, 0, 2, 0, 2, 0, 2, 0xFF, 2, 0 };
371 uint8_t data10[] ={ 74, 0, 4, 150, 9, 0, 65, 0, 76, 0xFF };
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000372
reed@google.comc9041912011-10-27 16:58:46 +0000373 uint8_t* array[] = {
374 data0, data1, data2, data3, data4,
375 data5, data6, data7, data8, data9,
376 data10
377 };
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000378
reed@google.comc9041912011-10-27 16:58:46 +0000379 for (size_t i = 0; i < SK_ARRAY_COUNT(array); ++i) {
380 uint8_t* data = array[i];
381 const int trimL = *data++;
382 const int trimR = *data++;
383 const int expectedSkip = *data++;
384 const int origWidth = *data++;
385 assert_row_width(data, origWidth);
386 int skip = trim_row_left_right(data, origWidth, trimL, trimR);
387 SkASSERT(expectedSkip == skip);
388 int expectedWidth = origWidth - trimL - trimR;
389 assert_row_width(data + skip, expectedWidth);
390 }
391}
392#endif
393
394bool SkAAClip::trimLeftRight() {
395 SkDEBUGCODE(test_trim_row_left_right();)
396
397 if (this->isEmpty()) {
398 return false;
399 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000400
reed@google.comc9041912011-10-27 16:58:46 +0000401 AUTO_AACLIP_VALIDATE(*this);
402
403 const int width = fBounds.width();
404 RunHead* head = fRunHead;
405 YOffset* yoff = head->yoffsets();
406 YOffset* stop = yoff + head->fRowCount;
407 uint8_t* base = head->data();
408
robertphillips@google.com768fee82012-08-02 12:42:43 +0000409 // After this loop, 'leftZeros' & 'rightZeros' will contain the minimum
410 // number of zeros on the left and right of the clip. This information
411 // can be used to shrink the bounding box.
reed@google.comc9041912011-10-27 16:58:46 +0000412 int leftZeros = width;
413 int riteZeros = width;
414 while (yoff < stop) {
415 int L, R;
416 count_left_right_zeros(base + yoff->fOffset, width, &L, &R);
robertphillips@google.com768fee82012-08-02 12:42:43 +0000417 SkASSERT(L + R < width || (L == width && R == width));
reed@google.comc9041912011-10-27 16:58:46 +0000418 if (L < leftZeros) {
419 leftZeros = L;
420 }
421 if (R < riteZeros) {
422 riteZeros = R;
423 }
424 if (0 == (leftZeros | riteZeros)) {
425 // no trimming to do
426 return true;
427 }
428 yoff += 1;
429 }
430
431 SkASSERT(leftZeros || riteZeros);
robertphillips@google.com768fee82012-08-02 12:42:43 +0000432 if (width == leftZeros) {
433 SkASSERT(width == riteZeros);
reed@google.comc9041912011-10-27 16:58:46 +0000434 return this->setEmpty();
435 }
436
437 this->validate();
438
439 fBounds.fLeft += leftZeros;
440 fBounds.fRight -= riteZeros;
441 SkASSERT(!fBounds.isEmpty());
442
443 // For now we don't realloc the storage (for time), we just shrink in place
444 // This means we don't have to do any memmoves either, since we can just
445 // play tricks with the yoff->fOffset for each row
446 yoff = head->yoffsets();
447 while (yoff < stop) {
448 uint8_t* row = base + yoff->fOffset;
449 SkDEBUGCODE((void)compute_row_length(row, width);)
450 yoff->fOffset += trim_row_left_right(row, width, leftZeros, riteZeros);
451 SkDEBUGCODE((void)compute_row_length(base + yoff->fOffset, width - leftZeros - riteZeros);)
452 yoff += 1;
453 }
454 return true;
455}
456
457static bool row_is_all_zeros(const uint8_t* row, int width) {
458 SkASSERT(width > 0);
459 do {
460 if (row[1]) {
461 return false;
462 }
463 int n = row[0];
464 SkASSERT(n <= width);
465 width -= n;
466 row += 2;
467 } while (width > 0);
468 SkASSERT(0 == width);
469 return true;
470}
471
472bool SkAAClip::trimTopBottom() {
473 if (this->isEmpty()) {
474 return false;
475 }
476
reed@google.comd6040f62011-10-28 02:39:17 +0000477 this->validate();
478
reed@google.comc9041912011-10-27 16:58:46 +0000479 const int width = fBounds.width();
480 RunHead* head = fRunHead;
481 YOffset* yoff = head->yoffsets();
482 YOffset* stop = yoff + head->fRowCount;
483 const uint8_t* base = head->data();
484
485 // Look to trim away empty rows from the top.
486 //
487 int skip = 0;
488 while (yoff < stop) {
489 const uint8_t* data = base + yoff->fOffset;
490 if (!row_is_all_zeros(data, width)) {
491 break;
492 }
493 skip += 1;
494 yoff += 1;
495 }
496 SkASSERT(skip <= head->fRowCount);
497 if (skip == head->fRowCount) {
498 return this->setEmpty();
499 }
500 if (skip > 0) {
501 // adjust fRowCount and fBounds.fTop, and slide all the data up
502 // as we remove [skip] number of YOffset entries
503 yoff = head->yoffsets();
504 int dy = yoff[skip - 1].fY + 1;
505 for (int i = skip; i < head->fRowCount; ++i) {
506 SkASSERT(yoff[i].fY >= dy);
507 yoff[i].fY -= dy;
508 }
509 YOffset* dst = head->yoffsets();
510 size_t size = head->fRowCount * sizeof(YOffset) + head->fDataSize;
511 memmove(dst, dst + skip, size - skip * sizeof(YOffset));
512
513 fBounds.fTop += dy;
514 SkASSERT(!fBounds.isEmpty());
515 head->fRowCount -= skip;
516 SkASSERT(head->fRowCount > 0);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000517
reed@google.comd6040f62011-10-28 02:39:17 +0000518 this->validate();
519 // need to reset this after the memmove
520 base = head->data();
reed@google.comc9041912011-10-27 16:58:46 +0000521 }
522
523 // Look to trim away empty rows from the bottom.
524 // We know that we have at least one non-zero row, so we can just walk
525 // backwards without checking for running past the start.
526 //
527 stop = yoff = head->yoffsets() + head->fRowCount;
528 do {
529 yoff -= 1;
530 } while (row_is_all_zeros(base + yoff->fOffset, width));
531 skip = stop - yoff - 1;
532 SkASSERT(skip >= 0 && skip < head->fRowCount);
533 if (skip > 0) {
534 // removing from the bottom is easier than from the top, as we don't
535 // have to adjust any of the Y values, we just have to trim the array
536 memmove(stop - skip, stop, head->fDataSize);
537
538 fBounds.fBottom = fBounds.fTop + yoff->fY + 1;
539 SkASSERT(!fBounds.isEmpty());
540 head->fRowCount -= skip;
541 SkASSERT(head->fRowCount > 0);
542 }
reed@google.comd6040f62011-10-28 02:39:17 +0000543 this->validate();
reed@google.comc9041912011-10-27 16:58:46 +0000544
545 return true;
546}
547
reed@google.com045e62d2011-10-24 12:19:46 +0000548// can't validate before we're done, since trimming is part of the process of
549// making us valid after the Builder. Since we build from top to bottom, its
550// possible our fBounds.fBottom is bigger than our last scanline of data, so
551// we trim fBounds.fBottom back up.
552//
reed@google.com045e62d2011-10-24 12:19:46 +0000553// TODO: check for duplicates in X and Y to further compress our data
554//
555bool SkAAClip::trimBounds() {
556 if (this->isEmpty()) {
557 return false;
558 }
559
560 const RunHead* head = fRunHead;
561 const YOffset* yoff = head->yoffsets();
562
563 SkASSERT(head->fRowCount > 0);
564 const YOffset& lastY = yoff[head->fRowCount - 1];
565 SkASSERT(lastY.fY + 1 <= fBounds.height());
566 fBounds.fBottom = fBounds.fTop + lastY.fY + 1;
567 SkASSERT(lastY.fY + 1 == fBounds.height());
reed@google.comc9041912011-10-27 16:58:46 +0000568 SkASSERT(!fBounds.isEmpty());
569
570 return this->trimTopBottom() && this->trimLeftRight();
reed@google.com045e62d2011-10-24 12:19:46 +0000571}
572
reed@google.come36707a2011-10-04 21:38:55 +0000573///////////////////////////////////////////////////////////////////////////////
574
575void SkAAClip::freeRuns() {
reed@google.com47ac84e2011-10-06 13:11:25 +0000576 if (fRunHead) {
reed@google.come36707a2011-10-04 21:38:55 +0000577 SkASSERT(fRunHead->fRefCnt >= 1);
578 if (1 == sk_atomic_dec(&fRunHead->fRefCnt)) {
579 sk_free(fRunHead);
580 }
581 }
582}
583
584SkAAClip::SkAAClip() {
585 fBounds.setEmpty();
reed@google.com47ac84e2011-10-06 13:11:25 +0000586 fRunHead = NULL;
reed@google.come36707a2011-10-04 21:38:55 +0000587}
588
589SkAAClip::SkAAClip(const SkAAClip& src) {
reed@google.com045e62d2011-10-24 12:19:46 +0000590 SkDEBUGCODE(fBounds.setEmpty();) // need this for validate
reed@google.com47ac84e2011-10-06 13:11:25 +0000591 fRunHead = NULL;
reed@google.come36707a2011-10-04 21:38:55 +0000592 *this = src;
593}
594
595SkAAClip::~SkAAClip() {
596 this->freeRuns();
597}
598
599SkAAClip& SkAAClip::operator=(const SkAAClip& src) {
reed@google.com045e62d2011-10-24 12:19:46 +0000600 AUTO_AACLIP_VALIDATE(*this);
601 src.validate();
602
reed@google.come36707a2011-10-04 21:38:55 +0000603 if (this != &src) {
604 this->freeRuns();
605 fBounds = src.fBounds;
606 fRunHead = src.fRunHead;
reed@google.com47ac84e2011-10-06 13:11:25 +0000607 if (fRunHead) {
reed@google.come36707a2011-10-04 21:38:55 +0000608 sk_atomic_inc(&fRunHead->fRefCnt);
609 }
610 }
611 return *this;
612}
613
614bool operator==(const SkAAClip& a, const SkAAClip& b) {
reed@google.com045e62d2011-10-24 12:19:46 +0000615 a.validate();
616 b.validate();
617
reed@google.come36707a2011-10-04 21:38:55 +0000618 if (&a == &b) {
619 return true;
620 }
621 if (a.fBounds != b.fBounds) {
622 return false;
623 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000624
reed@google.come36707a2011-10-04 21:38:55 +0000625 const SkAAClip::RunHead* ah = a.fRunHead;
626 const SkAAClip::RunHead* bh = b.fRunHead;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000627
reed@google.come36707a2011-10-04 21:38:55 +0000628 // this catches empties and rects being equal
629 if (ah == bh) {
630 return true;
631 }
632
633 // now we insist that both are complex (but different ptrs)
reed@google.com47ac84e2011-10-06 13:11:25 +0000634 if (!a.fRunHead || !b.fRunHead) {
reed@google.come36707a2011-10-04 21:38:55 +0000635 return false;
636 }
637
638 return ah->fRowCount == bh->fRowCount &&
639 ah->fDataSize == bh->fDataSize &&
640 !memcmp(ah->data(), bh->data(), ah->fDataSize);
641}
642
643void SkAAClip::swap(SkAAClip& other) {
reed@google.com045e62d2011-10-24 12:19:46 +0000644 AUTO_AACLIP_VALIDATE(*this);
645 other.validate();
646
reed@google.come36707a2011-10-04 21:38:55 +0000647 SkTSwap(fBounds, other.fBounds);
648 SkTSwap(fRunHead, other.fRunHead);
649}
650
reed@google.com32287892011-10-05 16:27:44 +0000651bool SkAAClip::set(const SkAAClip& src) {
652 *this = src;
653 return !this->isEmpty();
654}
655
reed@google.come36707a2011-10-04 21:38:55 +0000656bool SkAAClip::setEmpty() {
657 this->freeRuns();
658 fBounds.setEmpty();
reed@google.com47ac84e2011-10-06 13:11:25 +0000659 fRunHead = NULL;
reed@google.come36707a2011-10-04 21:38:55 +0000660 return false;
661}
662
663bool SkAAClip::setRect(const SkIRect& bounds) {
664 if (bounds.isEmpty()) {
665 return this->setEmpty();
666 }
reed@google.com47ac84e2011-10-06 13:11:25 +0000667
reed@google.com045e62d2011-10-24 12:19:46 +0000668 AUTO_AACLIP_VALIDATE(*this);
reed@google.com47ac84e2011-10-06 13:11:25 +0000669
reed@google.com045e62d2011-10-24 12:19:46 +0000670#if 0
reed@google.com47ac84e2011-10-06 13:11:25 +0000671 SkRect r;
672 r.set(bounds);
673 SkPath path;
674 path.addRect(r);
675 return this->setPath(path);
reed@google.com045e62d2011-10-24 12:19:46 +0000676#else
677 this->freeRuns();
678 fBounds = bounds;
679 fRunHead = RunHead::AllocRect(bounds);
680 SkASSERT(!this->isEmpty());
681 return true;
682#endif
reed@google.come36707a2011-10-04 21:38:55 +0000683}
684
reed@google.comf3c1da12011-10-10 19:35:47 +0000685bool SkAAClip::setRect(const SkRect& r, bool doAA) {
reed@google.come36707a2011-10-04 21:38:55 +0000686 if (r.isEmpty()) {
687 return this->setEmpty();
688 }
689
reed@google.com045e62d2011-10-24 12:19:46 +0000690 AUTO_AACLIP_VALIDATE(*this);
691
692 // TODO: special case this
693
reed@google.come36707a2011-10-04 21:38:55 +0000694 SkPath path;
695 path.addRect(r);
reed@google.comf3c1da12011-10-10 19:35:47 +0000696 return this->setPath(path, NULL, doAA);
697}
698
reed@google.coma069c8f2011-11-28 19:54:56 +0000699static void append_run(SkTDArray<uint8_t>& array, uint8_t value, int count) {
700 SkASSERT(count >= 0);
701 while (count > 0) {
702 int n = count;
703 if (n > 255) {
704 n = 255;
705 }
706 uint8_t* data = array.append(2);
707 data[0] = n;
708 data[1] = value;
709 count -= n;
710 }
711}
712
reed@google.comf3c1da12011-10-10 19:35:47 +0000713bool SkAAClip::setRegion(const SkRegion& rgn) {
714 if (rgn.isEmpty()) {
715 return this->setEmpty();
716 }
717 if (rgn.isRect()) {
718 return this->setRect(rgn.getBounds());
719 }
reed@google.coma069c8f2011-11-28 19:54:56 +0000720
721#if 0
reed@google.comf3c1da12011-10-10 19:35:47 +0000722 SkAAClip clip;
723 SkRegion::Iterator iter(rgn);
724 for (; !iter.done(); iter.next()) {
725 clip.op(iter.rect(), SkRegion::kUnion_Op);
726 }
727 this->swap(clip);
reed@google.com3771a032011-10-11 17:18:04 +0000728 return !this->isEmpty();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000729#else
reed@google.coma069c8f2011-11-28 19:54:56 +0000730 const SkIRect& bounds = rgn.getBounds();
731 const int offsetX = bounds.fLeft;
732 const int offsetY = bounds.fTop;
733
734 SkTDArray<YOffset> yArray;
735 SkTDArray<uint8_t> xArray;
736
737 yArray.setReserve(SkMin32(bounds.height(), 1024));
738 xArray.setReserve(SkMin32(bounds.width() * 128, 64 * 1024));
739
740 SkRegion::Iterator iter(rgn);
741 int prevRight = 0;
742 int prevBot = 0;
743 YOffset* currY = NULL;
744
745 for (; !iter.done(); iter.next()) {
746 const SkIRect& r = iter.rect();
747 SkASSERT(bounds.contains(r));
748
749 int bot = r.fBottom - offsetY;
750 SkASSERT(bot >= prevBot);
751 if (bot > prevBot) {
752 if (currY) {
753 // flush current row
754 append_run(xArray, 0, bounds.width() - prevRight);
755 }
756 // did we introduce an empty-gap from the prev row?
757 int top = r.fTop - offsetY;
758 if (top > prevBot) {
759 currY = yArray.append();
760 currY->fY = top - 1;
761 currY->fOffset = xArray.count();
762 append_run(xArray, 0, bounds.width());
763 }
764 // create a new record for this Y value
765 currY = yArray.append();
766 currY->fY = bot - 1;
767 currY->fOffset = xArray.count();
768 prevRight = 0;
769 prevBot = bot;
770 }
771
772 int x = r.fLeft - offsetX;
773 append_run(xArray, 0, x - prevRight);
774
775 int w = r.fRight - r.fLeft;
776 append_run(xArray, 0xFF, w);
777 prevRight = x + w;
778 SkASSERT(prevRight <= bounds.width());
779 }
780 // flush last row
781 append_run(xArray, 0, bounds.width() - prevRight);
782
783 // now pack everything into a RunHead
784 RunHead* head = RunHead::Alloc(yArray.count(), xArray.bytes());
785 memcpy(head->yoffsets(), yArray.begin(), yArray.bytes());
786 memcpy(head->data(), xArray.begin(), xArray.bytes());
787
788 this->setEmpty();
789 fBounds = bounds;
790 fRunHead = head;
791 this->validate();
792 return true;
793#endif
reed@google.come36707a2011-10-04 21:38:55 +0000794}
795
796///////////////////////////////////////////////////////////////////////////////
797
798const uint8_t* SkAAClip::findRow(int y, int* lastYForRow) const {
reed@google.com47ac84e2011-10-06 13:11:25 +0000799 SkASSERT(fRunHead);
reed@google.come36707a2011-10-04 21:38:55 +0000800
801 if (!y_in_rect(y, fBounds)) {
802 return NULL;
803 }
804 y -= fBounds.y(); // our yoffs values are relative to the top
805
806 const YOffset* yoff = fRunHead->yoffsets();
807 while (yoff->fY < y) {
808 yoff += 1;
809 SkASSERT(yoff - fRunHead->yoffsets() < fRunHead->fRowCount);
810 }
811
812 if (lastYForRow) {
reed@google.com045e62d2011-10-24 12:19:46 +0000813 *lastYForRow = fBounds.y() + yoff->fY;
reed@google.come36707a2011-10-04 21:38:55 +0000814 }
815 return fRunHead->data() + yoff->fOffset;
816}
817
818const uint8_t* SkAAClip::findX(const uint8_t data[], int x, int* initialCount) const {
819 SkASSERT(x_in_rect(x, fBounds));
820 x -= fBounds.x();
821
822 // first skip up to X
823 for (;;) {
824 int n = data[0];
825 if (x < n) {
reed@google.coma4c6e4d2012-06-20 14:29:50 +0000826 if (initialCount) {
827 *initialCount = n - x;
828 }
reed@google.come36707a2011-10-04 21:38:55 +0000829 break;
830 }
831 data += 2;
832 x -= n;
833 }
834 return data;
835}
836
837bool SkAAClip::quickContains(int left, int top, int right, int bottom) const {
838 if (this->isEmpty()) {
839 return false;
840 }
841 if (!fBounds.contains(left, top, right, bottom)) {
842 return false;
843 }
reed@google.com32287892011-10-05 16:27:44 +0000844#if 0
reed@google.come36707a2011-10-04 21:38:55 +0000845 if (this->isRect()) {
846 return true;
847 }
reed@google.com32287892011-10-05 16:27:44 +0000848#endif
reed@google.come36707a2011-10-04 21:38:55 +0000849
reed@google.coma4c6e4d2012-06-20 14:29:50 +0000850 int lastY SK_INIT_TO_AVOID_WARNING;
reed@google.come36707a2011-10-04 21:38:55 +0000851 const uint8_t* row = this->findRow(top, &lastY);
852 if (lastY < bottom) {
853 return false;
854 }
855 // now just need to check in X
reed@google.com045e62d2011-10-24 12:19:46 +0000856 int count;
857 row = this->findX(row, left, &count);
858#if 0
859 return count >= (right - left) && 0xFF == row[1];
860#else
861 int rectWidth = right - left;
862 while (0xFF == row[1]) {
863 if (count >= rectWidth) {
864 return true;
865 }
866 rectWidth -= count;
867 row += 2;
868 count = row[0];
869 }
870 return false;
871#endif
reed@google.come36707a2011-10-04 21:38:55 +0000872}
873
874///////////////////////////////////////////////////////////////////////////////
875
876class SkAAClip::Builder {
877 SkIRect fBounds;
878 struct Row {
879 int fY;
880 int fWidth;
881 SkTDArray<uint8_t>* fData;
882 };
883 SkTDArray<Row> fRows;
884 Row* fCurrRow;
885 int fPrevY;
886 int fWidth;
reed@google.com209c4152011-10-26 15:03:48 +0000887 int fMinY;
reed@google.come36707a2011-10-04 21:38:55 +0000888
889public:
890 Builder(const SkIRect& bounds) : fBounds(bounds) {
891 fPrevY = -1;
892 fWidth = bounds.width();
893 fCurrRow = NULL;
reed@google.com209c4152011-10-26 15:03:48 +0000894 fMinY = bounds.fTop;
reed@google.come36707a2011-10-04 21:38:55 +0000895 }
896
897 ~Builder() {
898 Row* row = fRows.begin();
899 Row* stop = fRows.end();
900 while (row < stop) {
901 delete row->fData;
902 row += 1;
903 }
904 }
905
reed@google.com32287892011-10-05 16:27:44 +0000906 const SkIRect& getBounds() const { return fBounds; }
907
reed@google.come36707a2011-10-04 21:38:55 +0000908 void addRun(int x, int y, U8CPU alpha, int count) {
909 SkASSERT(count > 0);
910 SkASSERT(fBounds.contains(x, y));
911 SkASSERT(fBounds.contains(x + count - 1, y));
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000912
reed@google.come36707a2011-10-04 21:38:55 +0000913 x -= fBounds.left();
914 y -= fBounds.top();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000915
reed@google.come36707a2011-10-04 21:38:55 +0000916 Row* row = fCurrRow;
917 if (y != fPrevY) {
918 SkASSERT(y > fPrevY);
919 fPrevY = y;
920 row = this->flushRow(true);
921 row->fY = y;
922 row->fWidth = 0;
923 SkASSERT(row->fData);
924 SkASSERT(0 == row->fData->count());
925 fCurrRow = row;
926 }
927
928 SkASSERT(row->fWidth <= x);
929 SkASSERT(row->fWidth < fBounds.width());
930
931 SkTDArray<uint8_t>& data = *row->fData;
932
933 int gap = x - row->fWidth;
934 if (gap) {
935 AppendRun(data, 0, gap);
936 row->fWidth += gap;
937 SkASSERT(row->fWidth < fBounds.width());
938 }
939
940 AppendRun(data, alpha, count);
941 row->fWidth += count;
942 SkASSERT(row->fWidth <= fBounds.width());
943 }
944
tomhudson@google.com49eac192011-12-27 13:59:20 +0000945 void addColumn(int x, int y, U8CPU alpha, int height) {
946 SkASSERT(fBounds.contains(x, y + height - 1));
947
948 this->addRun(x, y, alpha, 1);
949 this->flushRowH(fCurrRow);
950 y -= fBounds.fTop;
951 SkASSERT(y == fCurrRow->fY);
952 fCurrRow->fY = y + height - 1;
953 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000954
reed@google.com9154eb02011-10-31 16:07:28 +0000955 void addRectRun(int x, int y, int width, int height) {
956 SkASSERT(fBounds.contains(x + width - 1, y + height - 1));
957 this->addRun(x, y, 0xFF, width);
958
reed@google.coma89c77b2011-12-01 21:47:26 +0000959 // we assum the rect must be all we'll see for these scanlines
reed@google.com9154eb02011-10-31 16:07:28 +0000960 // so we ensure our row goes all the way to our right
961 this->flushRowH(fCurrRow);
962
963 y -= fBounds.fTop;
964 SkASSERT(y == fCurrRow->fY);
965 fCurrRow->fY = y + height - 1;
966 }
967
tomhudson@google.com49eac192011-12-27 13:59:20 +0000968 void addAntiRectRun(int x, int y, int width, int height,
969 SkAlpha leftAlpha, SkAlpha rightAlpha) {
970 SkASSERT(fBounds.contains(x + width - 1 +
971 (leftAlpha > 0 ? 1 : 0) + (rightAlpha > 0 ? 1 : 0),
972 y + height - 1));
973 SkASSERT(width >= 0);
974
975 // Conceptually we're always adding 3 runs, but we should
976 // merge or omit them if possible.
977 if (leftAlpha == 0xFF) {
978 width++;
979 } else if (leftAlpha > 0) {
980 this->addRun(x++, y, leftAlpha, 1);
981 }
982 if (rightAlpha == 0xFF) {
983 width++;
984 }
985 if (width > 0) {
986 this->addRun(x, y, 0xFF, width);
987 }
988 if (rightAlpha > 0 && rightAlpha < 255) {
989 this->addRun(x + width, y, rightAlpha, 1);
990 }
991
992 // we assume the rect must be all we'll see for these scanlines
993 // so we ensure our row goes all the way to our right
994 this->flushRowH(fCurrRow);
995
996 y -= fBounds.fTop;
997 SkASSERT(y == fCurrRow->fY);
998 fCurrRow->fY = y + height - 1;
999 }
1000
reed@google.com045e62d2011-10-24 12:19:46 +00001001 bool finish(SkAAClip* target) {
reed@google.come36707a2011-10-04 21:38:55 +00001002 this->flushRow(false);
1003
1004 const Row* row = fRows.begin();
1005 const Row* stop = fRows.end();
1006
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001007 size_t dataSize = 0;
reed@google.come36707a2011-10-04 21:38:55 +00001008 while (row < stop) {
1009 dataSize += row->fData->count();
1010 row += 1;
1011 }
1012
reed@google.com045e62d2011-10-24 12:19:46 +00001013 if (0 == dataSize) {
1014 return target->setEmpty();
1015 }
1016
reed@google.com209c4152011-10-26 15:03:48 +00001017 SkASSERT(fMinY >= fBounds.fTop);
1018 SkASSERT(fMinY < fBounds.fBottom);
1019 int adjustY = fMinY - fBounds.fTop;
1020 fBounds.fTop = fMinY;
1021
reed@google.come36707a2011-10-04 21:38:55 +00001022 RunHead* head = RunHead::Alloc(fRows.count(), dataSize);
1023 YOffset* yoffset = head->yoffsets();
1024 uint8_t* data = head->data();
1025 uint8_t* baseData = data;
1026
1027 row = fRows.begin();
reed@google.comc9041912011-10-27 16:58:46 +00001028 SkDEBUGCODE(int prevY = row->fY - 1;)
reed@google.come36707a2011-10-04 21:38:55 +00001029 while (row < stop) {
reed@google.comc9041912011-10-27 16:58:46 +00001030 SkASSERT(prevY < row->fY); // must be monotonic
1031 SkDEBUGCODE(prevY = row->fY);
1032
reed@google.com209c4152011-10-26 15:03:48 +00001033 yoffset->fY = row->fY - adjustY;
reed@google.come36707a2011-10-04 21:38:55 +00001034 yoffset->fOffset = data - baseData;
1035 yoffset += 1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001036
reed@google.come36707a2011-10-04 21:38:55 +00001037 size_t n = row->fData->count();
1038 memcpy(data, row->fData->begin(), n);
reed@google.comc9041912011-10-27 16:58:46 +00001039#ifdef SK_DEBUG
tomhudson@google.comf74ad8c2011-11-09 22:15:08 +00001040 size_t bytesNeeded = compute_row_length(data, fBounds.width());
reed@google.comc9041912011-10-27 16:58:46 +00001041 SkASSERT(bytesNeeded == n);
1042#endif
reed@google.come36707a2011-10-04 21:38:55 +00001043 data += n;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001044
reed@google.come36707a2011-10-04 21:38:55 +00001045 row += 1;
1046 }
1047
reed@google.com045e62d2011-10-24 12:19:46 +00001048 target->freeRuns();
1049 target->fBounds = fBounds;
1050 target->fRunHead = head;
1051 return target->trimBounds();
reed@google.come36707a2011-10-04 21:38:55 +00001052 }
1053
1054 void dump() {
1055 this->validate();
1056 int y;
1057 for (y = 0; y < fRows.count(); ++y) {
1058 const Row& row = fRows[y];
1059 SkDebugf("Y:%3d W:%3d", row.fY, row.fWidth);
1060 const SkTDArray<uint8_t>& data = *row.fData;
1061 int count = data.count();
1062 SkASSERT(!(count & 1));
1063 const uint8_t* ptr = data.begin();
1064 for (int x = 0; x < count; x += 2) {
1065 SkDebugf(" [%3d:%02X]", ptr[0], ptr[1]);
1066 ptr += 2;
1067 }
1068 SkDebugf("\n");
1069 }
reed@google.come36707a2011-10-04 21:38:55 +00001070 }
1071
1072 void validate() {
1073#ifdef SK_DEBUG
caryclark@google.com803eceb2012-06-06 12:09:34 +00001074 if (false) { // avoid bit rot, suppress warning
1075 test_count_left_right_zeros();
1076 }
reed@google.come36707a2011-10-04 21:38:55 +00001077 int prevY = -1;
1078 for (int i = 0; i < fRows.count(); ++i) {
1079 const Row& row = fRows[i];
1080 SkASSERT(prevY < row.fY);
1081 SkASSERT(fWidth == row.fWidth);
1082 int count = row.fData->count();
1083 const uint8_t* ptr = row.fData->begin();
1084 SkASSERT(!(count & 1));
1085 int w = 0;
1086 for (int x = 0; x < count; x += 2) {
reed@google.comd6040f62011-10-28 02:39:17 +00001087 int n = ptr[0];
1088 SkASSERT(n > 0);
1089 w += n;
reed@google.come36707a2011-10-04 21:38:55 +00001090 SkASSERT(w <= fWidth);
1091 ptr += 2;
1092 }
1093 SkASSERT(w == fWidth);
1094 prevY = row.fY;
1095 }
1096#endif
1097 }
1098
reed@google.com209c4152011-10-26 15:03:48 +00001099 // only called by BuilderBlitter
1100 void setMinY(int y) {
1101 fMinY = y;
1102 }
1103
reed@google.come36707a2011-10-04 21:38:55 +00001104private:
reed@google.com9154eb02011-10-31 16:07:28 +00001105 void flushRowH(Row* row) {
1106 // flush current row if needed
1107 if (row->fWidth < fWidth) {
1108 AppendRun(*row->fData, 0, fWidth - row->fWidth);
1109 row->fWidth = fWidth;
1110 }
1111 }
reed@google.com209c4152011-10-26 15:03:48 +00001112
reed@google.come36707a2011-10-04 21:38:55 +00001113 Row* flushRow(bool readyForAnother) {
1114 Row* next = NULL;
1115 int count = fRows.count();
1116 if (count > 0) {
reed@google.com9154eb02011-10-31 16:07:28 +00001117 this->flushRowH(&fRows[count - 1]);
reed@google.come36707a2011-10-04 21:38:55 +00001118 }
1119 if (count > 1) {
1120 // are our last two runs the same?
1121 Row* prev = &fRows[count - 2];
1122 Row* curr = &fRows[count - 1];
1123 SkASSERT(prev->fWidth == fWidth);
1124 SkASSERT(curr->fWidth == fWidth);
1125 if (*prev->fData == *curr->fData) {
1126 prev->fY = curr->fY;
1127 if (readyForAnother) {
1128 curr->fData->rewind();
1129 next = curr;
1130 } else {
1131 delete curr->fData;
1132 fRows.removeShuffle(count - 1);
1133 }
1134 } else {
1135 if (readyForAnother) {
1136 next = fRows.append();
1137 next->fData = new SkTDArray<uint8_t>;
1138 }
1139 }
1140 } else {
1141 if (readyForAnother) {
1142 next = fRows.append();
1143 next->fData = new SkTDArray<uint8_t>;
1144 }
1145 }
1146 return next;
1147 }
1148
1149 static void AppendRun(SkTDArray<uint8_t>& data, U8CPU alpha, int count) {
1150 do {
1151 int n = count;
1152 if (n > 255) {
1153 n = 255;
1154 }
1155 uint8_t* ptr = data.append(2);
1156 ptr[0] = n;
1157 ptr[1] = alpha;
1158 count -= n;
1159 } while (count > 0);
1160 }
1161};
1162
1163class SkAAClip::BuilderBlitter : public SkBlitter {
reed@google.com80cdb9a2012-02-16 18:56:17 +00001164 int fLastY;
1165
1166 /*
1167 If we see a gap of 1 or more empty scanlines while building in Y-order,
1168 we inject an explicit empty scanline (alpha==0)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001169
reed@google.com80cdb9a2012-02-16 18:56:17 +00001170 See AAClipTest.cpp : test_path_with_hole()
1171 */
1172 void checkForYGap(int y) {
1173 SkASSERT(y >= fLastY);
1174 if (fLastY > -SK_MaxS32) {
1175 int gap = y - fLastY;
1176 if (gap > 1) {
1177 fBuilder->addRun(fLeft, y - 1, 0, fRight - fLeft);
1178 }
1179 }
1180 fLastY = y;
1181 }
1182
reed@google.come36707a2011-10-04 21:38:55 +00001183public:
reed@google.com80cdb9a2012-02-16 18:56:17 +00001184
reed@google.come36707a2011-10-04 21:38:55 +00001185 BuilderBlitter(Builder* builder) {
1186 fBuilder = builder;
reed@google.com17785642011-10-12 20:23:55 +00001187 fLeft = builder->getBounds().fLeft;
1188 fRight = builder->getBounds().fRight;
reed@google.com209c4152011-10-26 15:03:48 +00001189 fMinY = SK_MaxS32;
reed@google.com80cdb9a2012-02-16 18:56:17 +00001190 fLastY = -SK_MaxS32; // sentinel
reed@google.com209c4152011-10-26 15:03:48 +00001191 }
1192
1193 void finish() {
1194 if (fMinY < SK_MaxS32) {
1195 fBuilder->setMinY(fMinY);
1196 }
reed@google.come36707a2011-10-04 21:38:55 +00001197 }
1198
tomhudson@google.com49eac192011-12-27 13:59:20 +00001199 /**
1200 Must evaluate clips in scan-line order, so don't want to allow blitV(),
1201 but an AAClip can be clipped down to a single pixel wide, so we
1202 must support it (given AntiRect semantics: minimum width is 2).
1203 Instead we'll rely on the runtime asserts to guarantee Y monotonicity;
1204 any failure cases that misses may have minor artifacts.
1205 */
1206 virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE {
1207 this->recordMinY(y);
1208 fBuilder->addColumn(x, y, alpha, height);
reed@google.com9b0da232012-02-29 13:59:15 +00001209 fLastY = y + height - 1;
tomhudson@google.com49eac192011-12-27 13:59:20 +00001210 }
reed@google.com045e62d2011-10-24 12:19:46 +00001211
reed@google.com562a2ac2011-10-31 14:14:18 +00001212 virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE {
reed@google.com9154eb02011-10-31 16:07:28 +00001213 this->recordMinY(y);
reed@google.com80cdb9a2012-02-16 18:56:17 +00001214 this->checkForYGap(y);
reed@google.com9154eb02011-10-31 16:07:28 +00001215 fBuilder->addRectRun(x, y, width, height);
reed@google.com302b8612012-02-16 19:30:13 +00001216 fLastY = y + height - 1;
reed@google.com562a2ac2011-10-31 14:14:18 +00001217 }
reed@google.com045e62d2011-10-24 12:19:46 +00001218
tomhudson@google.com49eac192011-12-27 13:59:20 +00001219 virtual void blitAntiRect(int x, int y, int width, int height,
1220 SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE {
1221 this->recordMinY(y);
reed@google.com302b8612012-02-16 19:30:13 +00001222 this->checkForYGap(y);
tomhudson@google.com49eac192011-12-27 13:59:20 +00001223 fBuilder->addAntiRectRun(x, y, width, height, leftAlpha, rightAlpha);
reed@google.com302b8612012-02-16 19:30:13 +00001224 fLastY = y + height - 1;
tomhudson@google.com49eac192011-12-27 13:59:20 +00001225 }
1226
reed@google.come36707a2011-10-04 21:38:55 +00001227 virtual void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE
1228 { unexpected(); }
1229
1230 virtual const SkBitmap* justAnOpaqueColor(uint32_t*) SK_OVERRIDE {
reed@google.com3771a032011-10-11 17:18:04 +00001231 return NULL;
reed@google.come36707a2011-10-04 21:38:55 +00001232 }
1233
1234 virtual void blitH(int x, int y, int width) SK_OVERRIDE {
reed@google.com209c4152011-10-26 15:03:48 +00001235 this->recordMinY(y);
reed@google.com80cdb9a2012-02-16 18:56:17 +00001236 this->checkForYGap(y);
reed@google.come36707a2011-10-04 21:38:55 +00001237 fBuilder->addRun(x, y, 0xFF, width);
1238 }
1239
1240 virtual void blitAntiH(int x, int y, const SkAlpha alpha[],
1241 const int16_t runs[]) SK_OVERRIDE {
reed@google.com209c4152011-10-26 15:03:48 +00001242 this->recordMinY(y);
reed@google.com80cdb9a2012-02-16 18:56:17 +00001243 this->checkForYGap(y);
reed@google.come36707a2011-10-04 21:38:55 +00001244 for (;;) {
1245 int count = *runs;
1246 if (count <= 0) {
1247 return;
1248 }
reed@google.com17785642011-10-12 20:23:55 +00001249
1250 // The supersampler's buffer can be the width of the device, so
1251 // we may have to trim the run to our bounds. If so, we assert that
1252 // the extra spans are always alpha==0
1253 int localX = x;
1254 int localCount = count;
1255 if (x < fLeft) {
1256 SkASSERT(0 == *alpha);
1257 int gap = fLeft - x;
1258 SkASSERT(gap <= count);
1259 localX += gap;
1260 localCount -= gap;
1261 }
1262 int right = x + count;
1263 if (right > fRight) {
1264 SkASSERT(0 == *alpha);
1265 localCount -= right - fRight;
1266 SkASSERT(localCount >= 0);
1267 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001268
reed@google.com17785642011-10-12 20:23:55 +00001269 if (localCount) {
1270 fBuilder->addRun(localX, y, *alpha, localCount);
1271 }
bsalomon@google.com820e80a2011-10-24 21:09:40 +00001272 // Next run
reed@google.come36707a2011-10-04 21:38:55 +00001273 runs += count;
1274 alpha += count;
1275 x += count;
1276 }
1277 }
1278
1279private:
1280 Builder* fBuilder;
reed@google.com17785642011-10-12 20:23:55 +00001281 int fLeft; // cache of builder's bounds' left edge
1282 int fRight;
reed@google.com209c4152011-10-26 15:03:48 +00001283 int fMinY;
1284
1285 /*
1286 * We track this, in case the scan converter skipped some number of
1287 * scanlines at the (relative to the bounds it was given). This allows
1288 * the builder, during its finish, to trip its bounds down to the "real"
1289 * top.
1290 */
1291 void recordMinY(int y) {
1292 if (y < fMinY) {
1293 fMinY = y;
1294 }
1295 }
reed@google.come36707a2011-10-04 21:38:55 +00001296
1297 void unexpected() {
1298 SkDebugf("---- did not expect to get called here");
1299 sk_throw();
1300 }
1301};
1302
reed@google.comf3c1da12011-10-10 19:35:47 +00001303bool SkAAClip::setPath(const SkPath& path, const SkRegion* clip, bool doAA) {
reed@google.com045e62d2011-10-24 12:19:46 +00001304 AUTO_AACLIP_VALIDATE(*this);
1305
reed@google.com32287892011-10-05 16:27:44 +00001306 if (clip && clip->isEmpty()) {
reed@google.come36707a2011-10-04 21:38:55 +00001307 return this->setEmpty();
1308 }
1309
1310 SkIRect ibounds;
reed@google.com32287892011-10-05 16:27:44 +00001311 path.getBounds().roundOut(&ibounds);
reed@google.come36707a2011-10-04 21:38:55 +00001312
reed@google.com32287892011-10-05 16:27:44 +00001313 SkRegion tmpClip;
1314 if (NULL == clip) {
1315 tmpClip.setRect(ibounds);
1316 clip = &tmpClip;
1317 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001318
reed@google.com045e62d2011-10-24 12:19:46 +00001319 if (path.isInverseFillType()) {
1320 ibounds = clip->getBounds();
1321 } else {
reed@google.com32287892011-10-05 16:27:44 +00001322 if (ibounds.isEmpty() || !ibounds.intersect(clip->getBounds())) {
reed@google.come36707a2011-10-04 21:38:55 +00001323 return this->setEmpty();
1324 }
reed@google.come36707a2011-10-04 21:38:55 +00001325 }
1326
1327 Builder builder(ibounds);
1328 BuilderBlitter blitter(&builder);
1329
reed@google.comf3c1da12011-10-10 19:35:47 +00001330 if (doAA) {
1331 SkScan::AntiFillPath(path, *clip, &blitter, true);
1332 } else {
1333 SkScan::FillPath(path, *clip, &blitter);
1334 }
reed@google.come36707a2011-10-04 21:38:55 +00001335
reed@google.com209c4152011-10-26 15:03:48 +00001336 blitter.finish();
reed@google.com045e62d2011-10-24 12:19:46 +00001337 return builder.finish(this);
reed@google.come36707a2011-10-04 21:38:55 +00001338}
1339
1340///////////////////////////////////////////////////////////////////////////////
1341
reed@google.com32287892011-10-05 16:27:44 +00001342typedef void (*RowProc)(SkAAClip::Builder&, int bottom,
1343 const uint8_t* rowA, const SkIRect& rectA,
1344 const uint8_t* rowB, const SkIRect& rectB);
1345
reed@google.com32287892011-10-05 16:27:44 +00001346typedef U8CPU (*AlphaProc)(U8CPU alphaA, U8CPU alphaB);
1347
1348static U8CPU sectAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1349 // Multiply
1350 return SkMulDiv255Round(alphaA, alphaB);
1351}
1352
1353static U8CPU unionAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1354 // SrcOver
1355 return alphaA + alphaB - SkMulDiv255Round(alphaA, alphaB);
1356}
1357
1358static U8CPU diffAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1359 // SrcOut
1360 return SkMulDiv255Round(alphaA, 0xFF - alphaB);
1361}
1362
1363static U8CPU xorAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1364 // XOR
1365 return alphaA + alphaB - 2 * SkMulDiv255Round(alphaA, alphaB);
1366}
1367
1368static AlphaProc find_alpha_proc(SkRegion::Op op) {
1369 switch (op) {
1370 case SkRegion::kIntersect_Op:
1371 return sectAlphaProc;
1372 case SkRegion::kDifference_Op:
1373 return diffAlphaProc;
1374 case SkRegion::kUnion_Op:
1375 return unionAlphaProc;
1376 case SkRegion::kXOR_Op:
1377 return xorAlphaProc;
1378 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001379 SkDEBUGFAIL("unexpected region op");
reed@google.com32287892011-10-05 16:27:44 +00001380 return sectAlphaProc;
1381 }
1382}
1383
1384static const uint8_t gEmptyRow[] = {
1385 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
1386 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
1387 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
1388 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
1389 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
1390 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
1391 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
1392 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
1393};
1394
1395class RowIter {
1396public:
1397 RowIter(const uint8_t* row, const SkIRect& bounds) {
1398 fRow = row;
1399 fLeft = bounds.fLeft;
reed@google.com32287892011-10-05 16:27:44 +00001400 fBoundsRight = bounds.fRight;
reed@google.com1c04bf92011-10-10 12:57:12 +00001401 if (row) {
1402 fRight = bounds.fLeft + row[0];
1403 SkASSERT(fRight <= fBoundsRight);
1404 fAlpha = row[1];
1405 fDone = false;
1406 } else {
1407 fDone = true;
1408 fRight = kMaxInt32;
1409 fAlpha = 0;
1410 }
reed@google.com32287892011-10-05 16:27:44 +00001411 }
1412
1413 bool done() const { return fDone; }
reed@google.com1c04bf92011-10-10 12:57:12 +00001414 int left() const { return fLeft; }
1415 int right() const { return fRight; }
1416 U8CPU alpha() const { return fAlpha; }
reed@google.com32287892011-10-05 16:27:44 +00001417 void next() {
reed@google.com1c04bf92011-10-10 12:57:12 +00001418 if (!fDone) {
reed@google.com32287892011-10-05 16:27:44 +00001419 fLeft = fRight;
reed@google.com1c04bf92011-10-10 12:57:12 +00001420 if (fRight == fBoundsRight) {
1421 fDone = true;
1422 fRight = kMaxInt32;
1423 fAlpha = 0;
1424 } else {
1425 fRow += 2;
1426 fRight += fRow[0];
1427 fAlpha = fRow[1];
1428 SkASSERT(fRight <= fBoundsRight);
1429 }
reed@google.com32287892011-10-05 16:27:44 +00001430 }
1431 }
1432
1433private:
1434 const uint8_t* fRow;
1435 int fLeft;
1436 int fRight;
1437 int fBoundsRight;
1438 bool fDone;
reed@google.com1c04bf92011-10-10 12:57:12 +00001439 uint8_t fAlpha;
reed@google.com32287892011-10-05 16:27:44 +00001440};
1441
reed@google.com1c04bf92011-10-10 12:57:12 +00001442static void adjust_row(RowIter& iter, int& leftA, int& riteA, int rite) {
1443 if (rite == riteA) {
1444 iter.next();
1445 leftA = iter.left();
1446 riteA = iter.right();
reed@google.com32287892011-10-05 16:27:44 +00001447 }
1448}
1449
caryclark@google.com803eceb2012-06-06 12:09:34 +00001450#if 0 // UNUSED
reed@google.com1c04bf92011-10-10 12:57:12 +00001451static bool intersect(int& min, int& max, int boundsMin, int boundsMax) {
1452 SkASSERT(min < max);
1453 SkASSERT(boundsMin < boundsMax);
1454 if (min >= boundsMax || max <= boundsMin) {
1455 return false;
1456 }
1457 if (min < boundsMin) {
1458 min = boundsMin;
1459 }
1460 if (max > boundsMax) {
1461 max = boundsMax;
1462 }
1463 return true;
1464}
caryclark@google.com803eceb2012-06-06 12:09:34 +00001465#endif
reed@google.com1c04bf92011-10-10 12:57:12 +00001466
reed@google.com32287892011-10-05 16:27:44 +00001467static void operatorX(SkAAClip::Builder& builder, int lastY,
1468 RowIter& iterA, RowIter& iterB,
1469 AlphaProc proc, const SkIRect& bounds) {
reed@google.com32287892011-10-05 16:27:44 +00001470 int leftA = iterA.left();
1471 int riteA = iterA.right();
reed@google.com32287892011-10-05 16:27:44 +00001472 int leftB = iterB.left();
1473 int riteB = iterB.right();
1474
reed@google.com1c04bf92011-10-10 12:57:12 +00001475 int prevRite = bounds.fLeft;
1476
1477 do {
reed@google.com32287892011-10-05 16:27:44 +00001478 U8CPU alphaA = 0;
1479 U8CPU alphaB = 0;
reed@google.com32287892011-10-05 16:27:44 +00001480 int left, rite;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001481
reed@google.com1c04bf92011-10-10 12:57:12 +00001482 if (leftA < leftB) {
reed@google.com32287892011-10-05 16:27:44 +00001483 left = leftA;
reed@google.com32287892011-10-05 16:27:44 +00001484 alphaA = iterA.alpha();
reed@google.com1c04bf92011-10-10 12:57:12 +00001485 if (riteA <= leftB) {
1486 rite = riteA;
1487 } else {
1488 rite = leftA = leftB;
reed@google.com32287892011-10-05 16:27:44 +00001489 }
reed@google.com1c04bf92011-10-10 12:57:12 +00001490 } else if (leftB < leftA) {
1491 left = leftB;
1492 alphaB = iterB.alpha();
1493 if (riteB <= leftA) {
1494 rite = riteB;
1495 } else {
1496 rite = leftB = leftA;
1497 }
1498 } else {
1499 left = leftA; // or leftB, since leftA == leftB
1500 rite = leftA = leftB = SkMin32(riteA, riteB);
1501 alphaA = iterA.alpha();
1502 alphaB = iterB.alpha();
reed@google.com32287892011-10-05 16:27:44 +00001503 }
1504
1505 if (left >= bounds.fRight) {
1506 break;
1507 }
reed@google.com34f7e472011-10-13 15:11:59 +00001508 if (rite > bounds.fRight) {
1509 rite = bounds.fRight;
1510 }
1511
reed@google.com32287892011-10-05 16:27:44 +00001512 if (left >= bounds.fLeft) {
reed@google.com1c04bf92011-10-10 12:57:12 +00001513 SkASSERT(rite > left);
reed@google.com32287892011-10-05 16:27:44 +00001514 builder.addRun(left, lastY, proc(alphaA, alphaB), rite - left);
reed@google.com1c04bf92011-10-10 12:57:12 +00001515 prevRite = rite;
reed@google.com32287892011-10-05 16:27:44 +00001516 }
reed@google.com1c04bf92011-10-10 12:57:12 +00001517
1518 adjust_row(iterA, leftA, riteA, rite);
1519 adjust_row(iterB, leftB, riteB, rite);
1520 } while (!iterA.done() || !iterB.done());
1521
1522 if (prevRite < bounds.fRight) {
1523 builder.addRun(prevRite, lastY, 0, bounds.fRight - prevRite);
reed@google.com32287892011-10-05 16:27:44 +00001524 }
1525}
1526
reed@google.com1c04bf92011-10-10 12:57:12 +00001527static void adjust_iter(SkAAClip::Iter& iter, int& topA, int& botA, int bot) {
1528 if (bot == botA) {
1529 iter.next();
1530 topA = botA;
1531 SkASSERT(botA == iter.top());
1532 botA = iter.bottom();
reed@google.com32287892011-10-05 16:27:44 +00001533 }
1534}
1535
1536static void operateY(SkAAClip::Builder& builder, const SkAAClip& A,
1537 const SkAAClip& B, SkRegion::Op op) {
1538 AlphaProc proc = find_alpha_proc(op);
1539 const SkIRect& bounds = builder.getBounds();
1540
1541 SkAAClip::Iter iterA(A);
1542 SkAAClip::Iter iterB(B);
1543
1544 SkASSERT(!iterA.done());
1545 int topA = iterA.top();
1546 int botA = iterA.bottom();
1547 SkASSERT(!iterB.done());
1548 int topB = iterB.top();
1549 int botB = iterB.bottom();
1550
reed@google.com1c04bf92011-10-10 12:57:12 +00001551 do {
1552 const uint8_t* rowA = NULL;
1553 const uint8_t* rowB = NULL;
reed@google.com32287892011-10-05 16:27:44 +00001554 int top, bot;
reed@google.com1c04bf92011-10-10 12:57:12 +00001555
1556 if (topA < topB) {
reed@google.com32287892011-10-05 16:27:44 +00001557 top = topA;
reed@google.com32287892011-10-05 16:27:44 +00001558 rowA = iterA.data();
reed@google.com1c04bf92011-10-10 12:57:12 +00001559 if (botA <= topB) {
1560 bot = botA;
1561 } else {
1562 bot = topA = topB;
reed@google.com32287892011-10-05 16:27:44 +00001563 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001564
reed@google.com1c04bf92011-10-10 12:57:12 +00001565 } else if (topB < topA) {
1566 top = topB;
1567 rowB = iterB.data();
1568 if (botB <= topA) {
1569 bot = botB;
1570 } else {
1571 bot = topB = topA;
1572 }
1573 } else {
1574 top = topA; // or topB, since topA == topB
1575 bot = topA = topB = SkMin32(botA, botB);
1576 rowA = iterA.data();
1577 rowB = iterB.data();
reed@google.com32287892011-10-05 16:27:44 +00001578 }
1579
1580 if (top >= bounds.fBottom) {
1581 break;
1582 }
reed@google.com34f7e472011-10-13 15:11:59 +00001583
1584 if (bot > bounds.fBottom) {
1585 bot = bounds.fBottom;
1586 }
1587 SkASSERT(top < bot);
1588
reed@google.com1c04bf92011-10-10 12:57:12 +00001589 if (!rowA && !rowB) {
1590 builder.addRun(bounds.fLeft, bot - 1, 0, bounds.width());
1591 } else if (top >= bounds.fTop) {
1592 SkASSERT(bot <= bounds.fBottom);
1593 RowIter rowIterA(rowA, rowA ? A.getBounds() : bounds);
1594 RowIter rowIterB(rowB, rowB ? B.getBounds() : bounds);
reed@google.com32287892011-10-05 16:27:44 +00001595 operatorX(builder, bot - 1, rowIterA, rowIterB, proc, bounds);
reed@google.com32287892011-10-05 16:27:44 +00001596 }
1597
reed@google.com1c04bf92011-10-10 12:57:12 +00001598 adjust_iter(iterA, topA, botA, bot);
1599 adjust_iter(iterB, topB, botB, bot);
1600 } while (!iterA.done() || !iterB.done());
reed@google.com32287892011-10-05 16:27:44 +00001601}
1602
1603bool SkAAClip::op(const SkAAClip& clipAOrig, const SkAAClip& clipBOrig,
1604 SkRegion::Op op) {
reed@google.com045e62d2011-10-24 12:19:46 +00001605 AUTO_AACLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001606
reed@google.com32287892011-10-05 16:27:44 +00001607 if (SkRegion::kReplace_Op == op) {
1608 return this->set(clipBOrig);
1609 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001610
reed@google.com32287892011-10-05 16:27:44 +00001611 const SkAAClip* clipA = &clipAOrig;
1612 const SkAAClip* clipB = &clipBOrig;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001613
reed@google.com32287892011-10-05 16:27:44 +00001614 if (SkRegion::kReverseDifference_Op == op) {
1615 SkTSwap(clipA, clipB);
1616 op = SkRegion::kDifference_Op;
1617 }
1618
1619 bool a_empty = clipA->isEmpty();
1620 bool b_empty = clipB->isEmpty();
1621
1622 SkIRect bounds;
1623 switch (op) {
1624 case SkRegion::kDifference_Op:
1625 if (a_empty) {
1626 return this->setEmpty();
1627 }
1628 if (b_empty || !SkIRect::Intersects(clipA->fBounds, clipB->fBounds)) {
1629 return this->set(*clipA);
1630 }
1631 bounds = clipA->fBounds;
1632 break;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001633
reed@google.com32287892011-10-05 16:27:44 +00001634 case SkRegion::kIntersect_Op:
1635 if ((a_empty | b_empty) || !bounds.intersect(clipA->fBounds,
1636 clipB->fBounds)) {
1637 return this->setEmpty();
1638 }
1639 break;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001640
reed@google.com32287892011-10-05 16:27:44 +00001641 case SkRegion::kUnion_Op:
1642 case SkRegion::kXOR_Op:
1643 if (a_empty) {
1644 return this->set(*clipB);
1645 }
1646 if (b_empty) {
1647 return this->set(*clipA);
1648 }
1649 bounds = clipA->fBounds;
1650 bounds.join(clipB->fBounds);
1651 break;
1652
1653 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001654 SkDEBUGFAIL("unknown region op");
reed@google.com32287892011-10-05 16:27:44 +00001655 return !this->isEmpty();
1656 }
1657
reed@google.com32287892011-10-05 16:27:44 +00001658 SkASSERT(SkIRect::Intersects(bounds, clipB->fBounds));
1659 SkASSERT(SkIRect::Intersects(bounds, clipB->fBounds));
1660
1661 Builder builder(bounds);
1662 operateY(builder, *clipA, *clipB, op);
reed@google.com045e62d2011-10-24 12:19:46 +00001663
1664 return builder.finish(this);
reed@google.come36707a2011-10-04 21:38:55 +00001665}
1666
reed@google.com045e62d2011-10-24 12:19:46 +00001667/*
1668 * It can be expensive to build a local aaclip before applying the op, so
1669 * we first see if we can restrict the bounds of new rect to our current
1670 * bounds, or note that the new rect subsumes our current clip.
1671 */
1672
1673bool SkAAClip::op(const SkIRect& rOrig, SkRegion::Op op) {
1674 SkIRect rStorage;
1675 const SkIRect* r = &rOrig;
1676
1677 switch (op) {
1678 case SkRegion::kIntersect_Op:
1679 if (!rStorage.intersect(rOrig, fBounds)) {
1680 // no overlap, so we're empty
1681 return this->setEmpty();
1682 }
1683 if (rStorage == fBounds) {
1684 // we were wholly inside the rect, no change
1685 return !this->isEmpty();
1686 }
1687 if (this->quickContains(rStorage)) {
1688 // the intersection is wholly inside us, we're a rect
1689 return this->setRect(rStorage);
1690 }
1691 r = &rStorage; // use the intersected bounds
1692 break;
1693 case SkRegion::kDifference_Op:
1694 break;
1695 case SkRegion::kUnion_Op:
1696 if (rOrig.contains(fBounds)) {
1697 return this->setRect(rOrig);
1698 }
1699 break;
1700 default:
1701 break;
1702 }
1703
reed@google.com47ac84e2011-10-06 13:11:25 +00001704 SkAAClip clip;
reed@google.com045e62d2011-10-24 12:19:46 +00001705 clip.setRect(*r);
reed@google.com47ac84e2011-10-06 13:11:25 +00001706 return this->op(*this, clip, op);
1707}
1708
reed@google.com045e62d2011-10-24 12:19:46 +00001709bool SkAAClip::op(const SkRect& rOrig, SkRegion::Op op, bool doAA) {
1710 SkRect rStorage, boundsStorage;
1711 const SkRect* r = &rOrig;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001712
reed@google.com045e62d2011-10-24 12:19:46 +00001713 boundsStorage.set(fBounds);
1714 switch (op) {
1715 case SkRegion::kIntersect_Op:
1716 case SkRegion::kDifference_Op:
1717 if (!rStorage.intersect(rOrig, boundsStorage)) {
reed@google.come56513d2012-06-25 20:06:33 +00001718 if (SkRegion::kIntersect_Op == op) {
1719 return this->setEmpty();
1720 } else { // kDifference
1721 return !this->isEmpty();
1722 }
reed@google.com045e62d2011-10-24 12:19:46 +00001723 }
1724 r = &rStorage; // use the intersected bounds
1725 break;
1726 case SkRegion::kUnion_Op:
1727 if (rOrig.contains(boundsStorage)) {
1728 return this->setRect(rOrig);
1729 }
1730 break;
1731 default:
1732 break;
1733 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001734
reed@google.com47ac84e2011-10-06 13:11:25 +00001735 SkAAClip clip;
reed@google.com045e62d2011-10-24 12:19:46 +00001736 clip.setRect(*r, doAA);
reed@google.com47ac84e2011-10-06 13:11:25 +00001737 return this->op(*this, clip, op);
1738}
1739
1740bool SkAAClip::op(const SkAAClip& clip, SkRegion::Op op) {
1741 return this->op(*this, clip, op);
1742}
1743
reed@google.come36707a2011-10-04 21:38:55 +00001744///////////////////////////////////////////////////////////////////////////////
reed@google.com045e62d2011-10-24 12:19:46 +00001745
1746bool SkAAClip::translate(int dx, int dy, SkAAClip* dst) const {
1747 if (NULL == dst) {
1748 return !this->isEmpty();
1749 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001750
reed@google.com045e62d2011-10-24 12:19:46 +00001751 if (this->isEmpty()) {
1752 return dst->setEmpty();
1753 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001754
reed@google.com045e62d2011-10-24 12:19:46 +00001755 if (this != dst) {
1756 sk_atomic_inc(&fRunHead->fRefCnt);
tomhudson@google.com19224c32012-03-28 15:46:37 +00001757 dst->freeRuns();
reed@google.com045e62d2011-10-24 12:19:46 +00001758 dst->fRunHead = fRunHead;
1759 dst->fBounds = fBounds;
1760 }
1761 dst->fBounds.offset(dx, dy);
1762 return true;
1763}
1764
1765static void expand_row_to_mask(uint8_t* SK_RESTRICT mask,
1766 const uint8_t* SK_RESTRICT row,
1767 int width) {
1768 while (width > 0) {
1769 int n = row[0];
1770 SkASSERT(width >= n);
1771 memset(mask, row[1], n);
1772 mask += n;
1773 row += 2;
1774 width -= n;
1775 }
reed@google.coma069c8f2011-11-28 19:54:56 +00001776 SkASSERT(0 == width);
reed@google.com045e62d2011-10-24 12:19:46 +00001777}
1778
1779void SkAAClip::copyToMask(SkMask* mask) const {
1780 mask->fFormat = SkMask::kA8_Format;
1781 if (this->isEmpty()) {
1782 mask->fBounds.setEmpty();
1783 mask->fImage = NULL;
1784 mask->fRowBytes = 0;
1785 return;
1786 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001787
reed@google.com045e62d2011-10-24 12:19:46 +00001788 mask->fBounds = fBounds;
1789 mask->fRowBytes = fBounds.width();
1790 size_t size = mask->computeImageSize();
1791 mask->fImage = SkMask::AllocImage(size);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001792
reed@google.com045e62d2011-10-24 12:19:46 +00001793 Iter iter(*this);
1794 uint8_t* dst = mask->fImage;
1795 const int width = fBounds.width();
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001796
reed@google.com045e62d2011-10-24 12:19:46 +00001797 int y = fBounds.fTop;
1798 while (!iter.done()) {
1799 do {
1800 expand_row_to_mask(dst, iter.data(), width);
1801 dst += mask->fRowBytes;
1802 } while (++y < iter.bottom());
1803 iter.next();
1804 }
1805}
1806
1807///////////////////////////////////////////////////////////////////////////////
reed@google.come36707a2011-10-04 21:38:55 +00001808///////////////////////////////////////////////////////////////////////////////
1809
1810static void expandToRuns(const uint8_t* SK_RESTRICT data, int initialCount, int width,
1811 int16_t* SK_RESTRICT runs, SkAlpha* SK_RESTRICT aa) {
1812 // we don't read our initial n from data, since the caller may have had to
1813 // clip it, hence the initialCount parameter.
1814 int n = initialCount;
1815 for (;;) {
1816 if (n > width) {
1817 n = width;
1818 }
1819 SkASSERT(n > 0);
1820 runs[0] = n;
1821 runs += n;
1822
1823 aa[0] = data[1];
1824 aa += n;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001825
reed@google.come36707a2011-10-04 21:38:55 +00001826 data += 2;
1827 width -= n;
1828 if (0 == width) {
1829 break;
1830 }
1831 // load the next count
1832 n = data[0];
1833 }
1834 runs[0] = 0; // sentinel
1835}
1836
1837SkAAClipBlitter::~SkAAClipBlitter() {
reed@google.com045e62d2011-10-24 12:19:46 +00001838 sk_free(fScanlineScratch);
reed@google.come36707a2011-10-04 21:38:55 +00001839}
1840
1841void SkAAClipBlitter::ensureRunsAndAA() {
reed@google.com045e62d2011-10-24 12:19:46 +00001842 if (NULL == fScanlineScratch) {
reed@google.come36707a2011-10-04 21:38:55 +00001843 // add 1 so we can store the terminating run count of 0
1844 int count = fAAClipBounds.width() + 1;
reed@google.com045e62d2011-10-24 12:19:46 +00001845 // we use this either for fRuns + fAA, or a scaline of a mask
1846 // which may be as deep as 32bits
1847 fScanlineScratch = sk_malloc_throw(count * sizeof(SkPMColor));
1848 fRuns = (int16_t*)fScanlineScratch;
reed@google.come36707a2011-10-04 21:38:55 +00001849 fAA = (SkAlpha*)(fRuns + count);
1850 }
1851}
1852
1853void SkAAClipBlitter::blitH(int x, int y, int width) {
1854 SkASSERT(width > 0);
1855 SkASSERT(fAAClipBounds.contains(x, y));
1856 SkASSERT(fAAClipBounds.contains(x + width - 1, y));
1857
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001858 const uint8_t* row = fAAClip->findRow(y);
reed@google.come36707a2011-10-04 21:38:55 +00001859 int initialCount;
1860 row = fAAClip->findX(row, x, &initialCount);
1861
1862 if (initialCount >= width) {
1863 SkAlpha alpha = row[1];
1864 if (0 == alpha) {
1865 return;
1866 }
1867 if (0xFF == alpha) {
1868 fBlitter->blitH(x, y, width);
1869 return;
1870 }
1871 }
1872
1873 this->ensureRunsAndAA();
1874 expandToRuns(row, initialCount, width, fRuns, fAA);
1875
1876 fBlitter->blitAntiH(x, y, fAA, fRuns);
1877}
1878
1879static void merge(const uint8_t* SK_RESTRICT row, int rowN,
1880 const SkAlpha* SK_RESTRICT srcAA,
1881 const int16_t* SK_RESTRICT srcRuns,
1882 SkAlpha* SK_RESTRICT dstAA,
1883 int16_t* SK_RESTRICT dstRuns,
1884 int width) {
1885 SkDEBUGCODE(int accumulated = 0;)
1886 int srcN = srcRuns[0];
reed@google.com045e62d2011-10-24 12:19:46 +00001887 // do we need this check?
1888 if (0 == srcN) {
1889 return;
1890 }
1891
reed@google.come36707a2011-10-04 21:38:55 +00001892 for (;;) {
reed@google.come36707a2011-10-04 21:38:55 +00001893 SkASSERT(rowN > 0);
1894 SkASSERT(srcN > 0);
1895
1896 unsigned newAlpha = SkMulDiv255Round(srcAA[0], row[1]);
1897 int minN = SkMin32(srcN, rowN);
1898 dstRuns[0] = minN;
1899 dstRuns += minN;
1900 dstAA[0] = newAlpha;
1901 dstAA += minN;
1902
1903 if (0 == (srcN -= minN)) {
1904 srcN = srcRuns[0]; // refresh
1905 srcRuns += srcN;
1906 srcAA += srcN;
1907 srcN = srcRuns[0]; // reload
reed@google.com045e62d2011-10-24 12:19:46 +00001908 if (0 == srcN) {
1909 break;
1910 }
reed@google.come36707a2011-10-04 21:38:55 +00001911 }
1912 if (0 == (rowN -= minN)) {
1913 row += 2;
1914 rowN = row[0]; // reload
1915 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001916
reed@google.come36707a2011-10-04 21:38:55 +00001917 SkDEBUGCODE(accumulated += minN;)
1918 SkASSERT(accumulated <= width);
1919 }
reed@google.com34f7e472011-10-13 15:11:59 +00001920 dstRuns[0] = 0;
reed@google.come36707a2011-10-04 21:38:55 +00001921}
1922
1923void SkAAClipBlitter::blitAntiH(int x, int y, const SkAlpha aa[],
1924 const int16_t runs[]) {
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001925
1926 const uint8_t* row = fAAClip->findRow(y);
reed@google.come36707a2011-10-04 21:38:55 +00001927 int initialCount;
1928 row = fAAClip->findX(row, x, &initialCount);
1929
1930 this->ensureRunsAndAA();
1931
1932 merge(row, initialCount, aa, runs, fAA, fRuns, fAAClipBounds.width());
1933 fBlitter->blitAntiH(x, y, fAA, fRuns);
1934}
1935
1936void SkAAClipBlitter::blitV(int x, int y, int height, SkAlpha alpha) {
1937 if (fAAClip->quickContains(x, y, x + 1, y + height)) {
1938 fBlitter->blitV(x, y, height, alpha);
1939 return;
1940 }
1941
reed@google.com045e62d2011-10-24 12:19:46 +00001942 for (;;) {
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001943 int lastY SK_INIT_TO_AVOID_WARNING;
reed@google.come36707a2011-10-04 21:38:55 +00001944 const uint8_t* row = fAAClip->findRow(y, &lastY);
reed@google.com045e62d2011-10-24 12:19:46 +00001945 int dy = lastY - y + 1;
1946 if (dy > height) {
1947 dy = height;
1948 }
1949 height -= dy;
1950
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001951 row = fAAClip->findX(row, x);
reed@google.come36707a2011-10-04 21:38:55 +00001952 SkAlpha newAlpha = SkMulDiv255Round(alpha, row[1]);
1953 if (newAlpha) {
reed@google.com045e62d2011-10-24 12:19:46 +00001954 fBlitter->blitV(x, y, dy, newAlpha);
1955 }
1956 SkASSERT(height >= 0);
1957 if (height <= 0) {
1958 break;
reed@google.come36707a2011-10-04 21:38:55 +00001959 }
1960 y = lastY + 1;
reed@google.com045e62d2011-10-24 12:19:46 +00001961 }
reed@google.come36707a2011-10-04 21:38:55 +00001962}
1963
1964void SkAAClipBlitter::blitRect(int x, int y, int width, int height) {
1965 if (fAAClip->quickContains(x, y, x + width, y + height)) {
1966 fBlitter->blitRect(x, y, width, height);
1967 return;
1968 }
1969
1970 while (--height >= 0) {
1971 this->blitH(x, y, width);
1972 y += 1;
1973 }
1974}
1975
reed@google.com045e62d2011-10-24 12:19:46 +00001976typedef void (*MergeAAProc)(const void* src, int width, const uint8_t* row,
1977 int initialRowCount, void* dst);
1978
1979static void small_memcpy(void* dst, const void* src, size_t n) {
1980 memcpy(dst, src, n);
1981}
1982
1983static void small_bzero(void* dst, size_t n) {
1984 sk_bzero(dst, n);
1985}
1986
1987static inline uint8_t mergeOne(uint8_t value, unsigned alpha) {
1988 return SkMulDiv255Round(value, alpha);
1989}
1990static inline uint16_t mergeOne(uint16_t value, unsigned alpha) {
1991 unsigned r = SkGetPackedR16(value);
1992 unsigned g = SkGetPackedG16(value);
1993 unsigned b = SkGetPackedB16(value);
1994 return SkPackRGB16(SkMulDiv255Round(r, alpha),
caryclark@google.com803eceb2012-06-06 12:09:34 +00001995 SkMulDiv255Round(g, alpha),
1996 SkMulDiv255Round(b, alpha));
reed@google.com045e62d2011-10-24 12:19:46 +00001997}
1998static inline SkPMColor mergeOne(SkPMColor value, unsigned alpha) {
1999 unsigned a = SkGetPackedA32(value);
2000 unsigned r = SkGetPackedR32(value);
2001 unsigned g = SkGetPackedG32(value);
2002 unsigned b = SkGetPackedB32(value);
2003 return SkPackARGB32(SkMulDiv255Round(a, alpha),
2004 SkMulDiv255Round(r, alpha),
2005 SkMulDiv255Round(g, alpha),
2006 SkMulDiv255Round(b, alpha));
2007}
2008
2009template <typename T> void mergeT(const T* SK_RESTRICT src, int srcN,
2010 const uint8_t* SK_RESTRICT row, int rowN,
2011 T* SK_RESTRICT dst) {
reed@google.com045e62d2011-10-24 12:19:46 +00002012 for (;;) {
2013 SkASSERT(rowN > 0);
2014 SkASSERT(srcN > 0);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002015
reed@google.com045e62d2011-10-24 12:19:46 +00002016 int n = SkMin32(rowN, srcN);
2017 unsigned rowA = row[1];
2018 if (0xFF == rowA) {
2019 small_memcpy(dst, src, n * sizeof(T));
2020 } else if (0 == rowA) {
2021 small_bzero(dst, n * sizeof(T));
2022 } else {
2023 for (int i = 0; i < n; ++i) {
2024 dst[i] = mergeOne(src[i], rowA);
2025 }
2026 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002027
reed@google.com045e62d2011-10-24 12:19:46 +00002028 if (0 == (srcN -= n)) {
2029 break;
2030 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002031
reed@google.com045e62d2011-10-24 12:19:46 +00002032 src += n;
2033 dst += n;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002034
reed@google.com045e62d2011-10-24 12:19:46 +00002035 SkASSERT(rowN == n);
2036 row += 2;
2037 rowN = row[0];
2038 }
2039}
2040
2041static MergeAAProc find_merge_aa_proc(SkMask::Format format) {
2042 switch (format) {
2043 case SkMask::kBW_Format:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002044 SkDEBUGFAIL("unsupported");
reed@google.com045e62d2011-10-24 12:19:46 +00002045 return NULL;
2046 case SkMask::kA8_Format:
2047 case SkMask::k3D_Format: {
2048 void (*proc8)(const uint8_t*, int, const uint8_t*, int, uint8_t*) = mergeT;
2049 return (MergeAAProc)proc8;
2050 }
2051 case SkMask::kLCD16_Format: {
2052 void (*proc16)(const uint16_t*, int, const uint8_t*, int, uint16_t*) = mergeT;
2053 return (MergeAAProc)proc16;
2054 }
2055 case SkMask::kLCD32_Format: {
2056 void (*proc32)(const SkPMColor*, int, const uint8_t*, int, SkPMColor*) = mergeT;
2057 return (MergeAAProc)proc32;
2058 }
2059 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002060 SkDEBUGFAIL("unsupported");
reed@google.com045e62d2011-10-24 12:19:46 +00002061 return NULL;
2062 }
2063}
2064
2065static U8CPU bit2byte(int bitInAByte) {
2066 SkASSERT(bitInAByte <= 0xFF);
2067 // negation turns any non-zero into 0xFFFFFF??, so we just shift down
2068 // some value >= 8 to get a full FF value
2069 return -bitInAByte >> 8;
2070}
2071
2072static void upscaleBW2A8(SkMask* dstMask, const SkMask& srcMask) {
2073 SkASSERT(SkMask::kBW_Format == srcMask.fFormat);
2074 SkASSERT(SkMask::kA8_Format == dstMask->fFormat);
2075
2076 const int width = srcMask.fBounds.width();
2077 const int height = srcMask.fBounds.height();
2078
2079 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcMask.fImage;
2080 const size_t srcRB = srcMask.fRowBytes;
2081 uint8_t* SK_RESTRICT dst = (uint8_t*)dstMask->fImage;
2082 const size_t dstRB = dstMask->fRowBytes;
2083
2084 const int wholeBytes = width >> 3;
2085 const int leftOverBits = width & 7;
2086
2087 for (int y = 0; y < height; ++y) {
2088 uint8_t* SK_RESTRICT d = dst;
2089 for (int i = 0; i < wholeBytes; ++i) {
2090 int srcByte = src[i];
2091 d[0] = bit2byte(srcByte & (1 << 7));
2092 d[1] = bit2byte(srcByte & (1 << 6));
2093 d[2] = bit2byte(srcByte & (1 << 5));
2094 d[3] = bit2byte(srcByte & (1 << 4));
2095 d[4] = bit2byte(srcByte & (1 << 3));
2096 d[5] = bit2byte(srcByte & (1 << 2));
2097 d[6] = bit2byte(srcByte & (1 << 1));
2098 d[7] = bit2byte(srcByte & (1 << 0));
2099 d += 8;
2100 }
2101 if (leftOverBits) {
2102 int srcByte = src[wholeBytes];
2103 for (int x = 0; x < leftOverBits; ++x) {
2104 *d++ = bit2byte(srcByte & 0x80);
2105 srcByte <<= 1;
2106 }
2107 }
2108 src += srcRB;
2109 dst += dstRB;
2110 }
2111}
2112
2113void SkAAClipBlitter::blitMask(const SkMask& origMask, const SkIRect& clip) {
2114 SkASSERT(fAAClip->getBounds().contains(clip));
2115
2116 if (fAAClip->quickContains(clip)) {
2117 fBlitter->blitMask(origMask, clip);
2118 return;
2119 }
2120
2121 const SkMask* mask = &origMask;
2122
2123 // if we're BW, we need to upscale to A8 (ugh)
2124 SkMask grayMask;
2125 grayMask.fImage = NULL;
2126 if (SkMask::kBW_Format == origMask.fFormat) {
2127 grayMask.fFormat = SkMask::kA8_Format;
2128 grayMask.fBounds = origMask.fBounds;
2129 grayMask.fRowBytes = origMask.fBounds.width();
2130 size_t size = grayMask.computeImageSize();
2131 grayMask.fImage = (uint8_t*)fGrayMaskScratch.reset(size,
2132 SkAutoMalloc::kReuse_OnShrink);
2133
2134 upscaleBW2A8(&grayMask, origMask);
2135 mask = &grayMask;
2136 }
2137
2138 this->ensureRunsAndAA();
2139
2140 // HACK -- we are devolving 3D into A8, need to copy the rest of the 3D
2141 // data into a temp block to support it better (ugh)
2142
2143 const void* src = mask->getAddr(clip.fLeft, clip.fTop);
2144 const size_t srcRB = mask->fRowBytes;
2145 const int width = clip.width();
2146 MergeAAProc mergeProc = find_merge_aa_proc(mask->fFormat);
2147
2148 SkMask rowMask;
2149 rowMask.fFormat = SkMask::k3D_Format == mask->fFormat ? SkMask::kA8_Format : mask->fFormat;
2150 rowMask.fBounds.fLeft = clip.fLeft;
2151 rowMask.fBounds.fRight = clip.fRight;
2152 rowMask.fRowBytes = mask->fRowBytes; // doesn't matter, since our height==1
2153 rowMask.fImage = (uint8_t*)fScanlineScratch;
2154
2155 int y = clip.fTop;
2156 const int stopY = y + clip.height();
2157
2158 do {
reed@google.coma4c6e4d2012-06-20 14:29:50 +00002159 int localStopY SK_INIT_TO_AVOID_WARNING;
reed@google.com045e62d2011-10-24 12:19:46 +00002160 const uint8_t* row = fAAClip->findRow(y, &localStopY);
2161 // findRow returns last Y, not stop, so we add 1
2162 localStopY = SkMin32(localStopY + 1, stopY);
2163
2164 int initialCount;
2165 row = fAAClip->findX(row, clip.fLeft, &initialCount);
2166 do {
2167 mergeProc(src, width, row, initialCount, rowMask.fImage);
2168 rowMask.fBounds.fTop = y;
2169 rowMask.fBounds.fBottom = y + 1;
2170 fBlitter->blitMask(rowMask, rowMask.fBounds);
2171 src = (const void*)((const char*)src + srcRB);
2172 } while (++y < localStopY);
2173 } while (y < stopY);
reed@google.come36707a2011-10-04 21:38:55 +00002174}
2175
2176const SkBitmap* SkAAClipBlitter::justAnOpaqueColor(uint32_t* value) {
2177 return NULL;
2178}
2179