blob: aa7513c4d14b4f373f159ce01d1935ff0a5560cb [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;
scroggo@google.com493c65f2013-02-05 18:49:00 +000059 size_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);
reed@google.com045e62d2011-10-24 12:19:46 +0000196
197 const YOffset* yoff = head->yoffsets();
198 const YOffset* ystop = yoff + head->fRowCount;
reed@google.comc9041912011-10-27 16:58:46 +0000199 const int lastY = fBounds.height() - 1;
200
201 // Y and offset must be monotonic
202 int prevY = -1;
203 int32_t prevOffset = -1;
reed@google.com045e62d2011-10-24 12:19:46 +0000204 while (yoff < ystop) {
reed@google.comc9041912011-10-27 16:58:46 +0000205 SkASSERT(prevY < yoff->fY);
206 SkASSERT(yoff->fY <= lastY);
207 prevY = yoff->fY;
208 SkASSERT(prevOffset < (int32_t)yoff->fOffset);
209 prevOffset = yoff->fOffset;
210 const uint8_t* row = head->data() + yoff->fOffset;
reed@google.com045e62d2011-10-24 12:19:46 +0000211 size_t rowLength = compute_row_length(row, fBounds.width());
scroggo@google.com493c65f2013-02-05 18:49:00 +0000212 SkASSERT(yoff->fOffset + rowLength <= head->fDataSize);
reed@google.comc9041912011-10-27 16:58:46 +0000213 yoff += 1;
reed@google.com045e62d2011-10-24 12:19:46 +0000214 }
reed@google.com045e62d2011-10-24 12:19:46 +0000215 // check the last entry;
216 --yoff;
reed@google.comc9041912011-10-27 16:58:46 +0000217 SkASSERT(yoff->fY == lastY);
reed@google.com045e62d2011-10-24 12:19:46 +0000218}
219#endif
220
221///////////////////////////////////////////////////////////////////////////////
222
robertphillips@google.com768fee82012-08-02 12:42:43 +0000223// Count the number of zeros on the left and right edges of the passed in
224// RLE row. If 'row' is all zeros return 'width' in both variables.
reed@google.comc9041912011-10-27 16:58:46 +0000225static void count_left_right_zeros(const uint8_t* row, int width,
226 int* leftZ, int* riteZ) {
227 int zeros = 0;
228 do {
229 if (row[1]) {
230 break;
231 }
232 int n = row[0];
233 SkASSERT(n > 0);
234 SkASSERT(n <= width);
235 zeros += n;
236 row += 2;
237 width -= n;
238 } while (width > 0);
239 *leftZ = zeros;
240
robertphillips@google.com768fee82012-08-02 12:42:43 +0000241 if (0 == width) {
242 // this line is completely empty return 'width' in both variables
243 *riteZ = *leftZ;
244 return;
245 }
246
reed@google.comc9041912011-10-27 16:58:46 +0000247 zeros = 0;
248 while (width > 0) {
249 int n = row[0];
250 SkASSERT(n > 0);
251 if (0 == row[1]) {
252 zeros += n;
253 } else {
254 zeros = 0;
255 }
256 row += 2;
257 width -= n;
258 }
259 *riteZ = zeros;
260}
261
262#ifdef SK_DEBUG
263static void test_count_left_right_zeros() {
264 static bool gOnce;
265 if (gOnce) {
266 return;
267 }
268 gOnce = true;
269
270 const uint8_t data0[] = { 0, 0, 10, 0xFF };
271 const uint8_t data1[] = { 0, 0, 5, 0xFF, 2, 0, 3, 0xFF };
272 const uint8_t data2[] = { 7, 0, 5, 0, 2, 0, 3, 0xFF };
273 const uint8_t data3[] = { 0, 5, 5, 0xFF, 2, 0, 3, 0 };
274 const uint8_t data4[] = { 2, 3, 2, 0, 5, 0xFF, 3, 0 };
robertphillips@google.com768fee82012-08-02 12:42:43 +0000275 const uint8_t data5[] = { 10, 10, 10, 0 };
reed@google.comc9041912011-10-27 16:58:46 +0000276 const uint8_t data6[] = { 2, 2, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
277
278 const uint8_t* array[] = {
279 data0, data1, data2, data3, data4, data5, data6
280 };
281
282 for (size_t i = 0; i < SK_ARRAY_COUNT(array); ++i) {
283 const uint8_t* data = array[i];
284 const int expectedL = *data++;
285 const int expectedR = *data++;
286 int L = 12345, R = 12345;
287 count_left_right_zeros(data, 10, &L, &R);
288 SkASSERT(expectedL == L);
289 SkASSERT(expectedR == R);
290 }
291}
292#endif
293
294// modify row in place, trimming off (zeros) from the left and right sides.
295// return the number of bytes that were completely eliminated from the left
296static int trim_row_left_right(uint8_t* row, int width, int leftZ, int riteZ) {
297 int trim = 0;
298 while (leftZ > 0) {
299 SkASSERT(0 == row[1]);
300 int n = row[0];
301 SkASSERT(n > 0);
302 SkASSERT(n <= width);
303 width -= n;
304 row += 2;
305 if (n > leftZ) {
306 row[-2] = n - leftZ;
307 break;
308 }
309 trim += 2;
310 leftZ -= n;
311 SkASSERT(leftZ >= 0);
312 }
313
314 if (riteZ) {
315 // walk row to the end, and then we'll back up to trim riteZ
316 while (width > 0) {
317 int n = row[0];
318 SkASSERT(n <= width);
319 width -= n;
320 row += 2;
321 }
322 // now skip whole runs of zeros
323 do {
324 row -= 2;
325 SkASSERT(0 == row[1]);
326 int n = row[0];
327 SkASSERT(n > 0);
328 if (n > riteZ) {
329 row[0] = n - riteZ;
330 break;
331 }
332 riteZ -= n;
333 SkASSERT(riteZ >= 0);
334 } while (riteZ > 0);
335 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000336
reed@google.comc9041912011-10-27 16:58:46 +0000337 return trim;
338}
339
340#ifdef SK_DEBUG
341// assert that this row is exactly this width
reed@google.comc5507bf2011-10-27 21:15:36 +0000342static void assert_row_width(const uint8_t* row, int width) {
reed@google.comc9041912011-10-27 16:58:46 +0000343 while (width > 0) {
344 int n = row[0];
345 SkASSERT(n > 0);
346 SkASSERT(n <= width);
347 width -= n;
348 row += 2;
349 }
350 SkASSERT(0 == width);
351}
352
353static void test_trim_row_left_right() {
354 static bool gOnce;
355 if (gOnce) {
356 return;
357 }
358 gOnce = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000359
reed@google.comc9041912011-10-27 16:58:46 +0000360 uint8_t data0[] = { 0, 0, 0, 10, 10, 0xFF };
361 uint8_t data1[] = { 2, 0, 0, 10, 5, 0, 2, 0, 3, 0xFF };
362 uint8_t data2[] = { 5, 0, 2, 10, 5, 0, 2, 0, 3, 0xFF };
363 uint8_t data3[] = { 6, 0, 2, 10, 5, 0, 2, 0, 3, 0xFF };
364 uint8_t data4[] = { 0, 0, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
365 uint8_t data5[] = { 1, 0, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
366 uint8_t data6[] = { 0, 1, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
367 uint8_t data7[] = { 1, 1, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
368 uint8_t data8[] = { 2, 2, 2, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
369 uint8_t data9[] = { 5, 2, 4, 10, 2, 0, 2, 0, 2, 0, 2, 0xFF, 2, 0 };
370 uint8_t data10[] ={ 74, 0, 4, 150, 9, 0, 65, 0, 76, 0xFF };
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000371
reed@google.comc9041912011-10-27 16:58:46 +0000372 uint8_t* array[] = {
373 data0, data1, data2, data3, data4,
374 data5, data6, data7, data8, data9,
375 data10
376 };
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000377
reed@google.comc9041912011-10-27 16:58:46 +0000378 for (size_t i = 0; i < SK_ARRAY_COUNT(array); ++i) {
379 uint8_t* data = array[i];
380 const int trimL = *data++;
381 const int trimR = *data++;
382 const int expectedSkip = *data++;
383 const int origWidth = *data++;
384 assert_row_width(data, origWidth);
385 int skip = trim_row_left_right(data, origWidth, trimL, trimR);
386 SkASSERT(expectedSkip == skip);
387 int expectedWidth = origWidth - trimL - trimR;
388 assert_row_width(data + skip, expectedWidth);
389 }
390}
391#endif
392
393bool SkAAClip::trimLeftRight() {
394 SkDEBUGCODE(test_trim_row_left_right();)
395
396 if (this->isEmpty()) {
397 return false;
398 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000399
reed@google.comc9041912011-10-27 16:58:46 +0000400 AUTO_AACLIP_VALIDATE(*this);
401
402 const int width = fBounds.width();
403 RunHead* head = fRunHead;
404 YOffset* yoff = head->yoffsets();
405 YOffset* stop = yoff + head->fRowCount;
406 uint8_t* base = head->data();
407
robertphillips@google.com768fee82012-08-02 12:42:43 +0000408 // After this loop, 'leftZeros' & 'rightZeros' will contain the minimum
409 // number of zeros on the left and right of the clip. This information
410 // can be used to shrink the bounding box.
reed@google.comc9041912011-10-27 16:58:46 +0000411 int leftZeros = width;
412 int riteZeros = width;
413 while (yoff < stop) {
414 int L, R;
415 count_left_right_zeros(base + yoff->fOffset, width, &L, &R);
robertphillips@google.com768fee82012-08-02 12:42:43 +0000416 SkASSERT(L + R < width || (L == width && R == width));
reed@google.comc9041912011-10-27 16:58:46 +0000417 if (L < leftZeros) {
418 leftZeros = L;
419 }
420 if (R < riteZeros) {
421 riteZeros = R;
422 }
423 if (0 == (leftZeros | riteZeros)) {
424 // no trimming to do
425 return true;
426 }
427 yoff += 1;
428 }
429
430 SkASSERT(leftZeros || riteZeros);
robertphillips@google.com768fee82012-08-02 12:42:43 +0000431 if (width == leftZeros) {
432 SkASSERT(width == riteZeros);
reed@google.comc9041912011-10-27 16:58:46 +0000433 return this->setEmpty();
434 }
435
436 this->validate();
437
438 fBounds.fLeft += leftZeros;
439 fBounds.fRight -= riteZeros;
440 SkASSERT(!fBounds.isEmpty());
441
442 // For now we don't realloc the storage (for time), we just shrink in place
443 // This means we don't have to do any memmoves either, since we can just
444 // play tricks with the yoff->fOffset for each row
445 yoff = head->yoffsets();
446 while (yoff < stop) {
447 uint8_t* row = base + yoff->fOffset;
448 SkDEBUGCODE((void)compute_row_length(row, width);)
449 yoff->fOffset += trim_row_left_right(row, width, leftZeros, riteZeros);
450 SkDEBUGCODE((void)compute_row_length(base + yoff->fOffset, width - leftZeros - riteZeros);)
451 yoff += 1;
452 }
453 return true;
454}
455
456static bool row_is_all_zeros(const uint8_t* row, int width) {
457 SkASSERT(width > 0);
458 do {
459 if (row[1]) {
460 return false;
461 }
462 int n = row[0];
463 SkASSERT(n <= width);
464 width -= n;
465 row += 2;
466 } while (width > 0);
467 SkASSERT(0 == width);
468 return true;
469}
470
471bool SkAAClip::trimTopBottom() {
472 if (this->isEmpty()) {
473 return false;
474 }
475
reed@google.comd6040f62011-10-28 02:39:17 +0000476 this->validate();
477
reed@google.comc9041912011-10-27 16:58:46 +0000478 const int width = fBounds.width();
479 RunHead* head = fRunHead;
480 YOffset* yoff = head->yoffsets();
481 YOffset* stop = yoff + head->fRowCount;
482 const uint8_t* base = head->data();
483
484 // Look to trim away empty rows from the top.
485 //
486 int skip = 0;
487 while (yoff < stop) {
488 const uint8_t* data = base + yoff->fOffset;
489 if (!row_is_all_zeros(data, width)) {
490 break;
491 }
492 skip += 1;
493 yoff += 1;
494 }
495 SkASSERT(skip <= head->fRowCount);
496 if (skip == head->fRowCount) {
497 return this->setEmpty();
498 }
499 if (skip > 0) {
500 // adjust fRowCount and fBounds.fTop, and slide all the data up
501 // as we remove [skip] number of YOffset entries
502 yoff = head->yoffsets();
503 int dy = yoff[skip - 1].fY + 1;
504 for (int i = skip; i < head->fRowCount; ++i) {
505 SkASSERT(yoff[i].fY >= dy);
506 yoff[i].fY -= dy;
507 }
508 YOffset* dst = head->yoffsets();
509 size_t size = head->fRowCount * sizeof(YOffset) + head->fDataSize;
510 memmove(dst, dst + skip, size - skip * sizeof(YOffset));
511
512 fBounds.fTop += dy;
513 SkASSERT(!fBounds.isEmpty());
514 head->fRowCount -= skip;
515 SkASSERT(head->fRowCount > 0);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000516
reed@google.comd6040f62011-10-28 02:39:17 +0000517 this->validate();
518 // need to reset this after the memmove
519 base = head->data();
reed@google.comc9041912011-10-27 16:58:46 +0000520 }
521
522 // Look to trim away empty rows from the bottom.
523 // We know that we have at least one non-zero row, so we can just walk
524 // backwards without checking for running past the start.
525 //
526 stop = yoff = head->yoffsets() + head->fRowCount;
527 do {
528 yoff -= 1;
529 } while (row_is_all_zeros(base + yoff->fOffset, width));
530 skip = stop - yoff - 1;
531 SkASSERT(skip >= 0 && skip < head->fRowCount);
532 if (skip > 0) {
533 // removing from the bottom is easier than from the top, as we don't
534 // have to adjust any of the Y values, we just have to trim the array
535 memmove(stop - skip, stop, head->fDataSize);
536
537 fBounds.fBottom = fBounds.fTop + yoff->fY + 1;
538 SkASSERT(!fBounds.isEmpty());
539 head->fRowCount -= skip;
540 SkASSERT(head->fRowCount > 0);
541 }
reed@google.comd6040f62011-10-28 02:39:17 +0000542 this->validate();
reed@google.comc9041912011-10-27 16:58:46 +0000543
544 return true;
545}
546
reed@google.com045e62d2011-10-24 12:19:46 +0000547// can't validate before we're done, since trimming is part of the process of
548// making us valid after the Builder. Since we build from top to bottom, its
549// possible our fBounds.fBottom is bigger than our last scanline of data, so
550// we trim fBounds.fBottom back up.
551//
reed@google.com045e62d2011-10-24 12:19:46 +0000552// TODO: check for duplicates in X and Y to further compress our data
553//
554bool SkAAClip::trimBounds() {
555 if (this->isEmpty()) {
556 return false;
557 }
558
559 const RunHead* head = fRunHead;
560 const YOffset* yoff = head->yoffsets();
561
562 SkASSERT(head->fRowCount > 0);
563 const YOffset& lastY = yoff[head->fRowCount - 1];
564 SkASSERT(lastY.fY + 1 <= fBounds.height());
565 fBounds.fBottom = fBounds.fTop + lastY.fY + 1;
566 SkASSERT(lastY.fY + 1 == fBounds.height());
reed@google.comc9041912011-10-27 16:58:46 +0000567 SkASSERT(!fBounds.isEmpty());
568
569 return this->trimTopBottom() && this->trimLeftRight();
reed@google.com045e62d2011-10-24 12:19:46 +0000570}
571
reed@google.come36707a2011-10-04 21:38:55 +0000572///////////////////////////////////////////////////////////////////////////////
573
574void SkAAClip::freeRuns() {
reed@google.com47ac84e2011-10-06 13:11:25 +0000575 if (fRunHead) {
reed@google.come36707a2011-10-04 21:38:55 +0000576 SkASSERT(fRunHead->fRefCnt >= 1);
577 if (1 == sk_atomic_dec(&fRunHead->fRefCnt)) {
578 sk_free(fRunHead);
579 }
580 }
581}
582
583SkAAClip::SkAAClip() {
584 fBounds.setEmpty();
reed@google.com47ac84e2011-10-06 13:11:25 +0000585 fRunHead = NULL;
reed@google.come36707a2011-10-04 21:38:55 +0000586}
587
588SkAAClip::SkAAClip(const SkAAClip& src) {
reed@google.com045e62d2011-10-24 12:19:46 +0000589 SkDEBUGCODE(fBounds.setEmpty();) // need this for validate
reed@google.com47ac84e2011-10-06 13:11:25 +0000590 fRunHead = NULL;
reed@google.come36707a2011-10-04 21:38:55 +0000591 *this = src;
592}
593
594SkAAClip::~SkAAClip() {
595 this->freeRuns();
596}
597
598SkAAClip& SkAAClip::operator=(const SkAAClip& src) {
reed@google.com045e62d2011-10-24 12:19:46 +0000599 AUTO_AACLIP_VALIDATE(*this);
600 src.validate();
601
reed@google.come36707a2011-10-04 21:38:55 +0000602 if (this != &src) {
603 this->freeRuns();
604 fBounds = src.fBounds;
605 fRunHead = src.fRunHead;
reed@google.com47ac84e2011-10-06 13:11:25 +0000606 if (fRunHead) {
reed@google.come36707a2011-10-04 21:38:55 +0000607 sk_atomic_inc(&fRunHead->fRefCnt);
608 }
609 }
610 return *this;
611}
612
613bool operator==(const SkAAClip& a, const SkAAClip& b) {
reed@google.com045e62d2011-10-24 12:19:46 +0000614 a.validate();
615 b.validate();
616
reed@google.come36707a2011-10-04 21:38:55 +0000617 if (&a == &b) {
618 return true;
619 }
620 if (a.fBounds != b.fBounds) {
621 return false;
622 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000623
reed@google.come36707a2011-10-04 21:38:55 +0000624 const SkAAClip::RunHead* ah = a.fRunHead;
625 const SkAAClip::RunHead* bh = b.fRunHead;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000626
reed@google.come36707a2011-10-04 21:38:55 +0000627 // this catches empties and rects being equal
628 if (ah == bh) {
629 return true;
630 }
631
632 // now we insist that both are complex (but different ptrs)
reed@google.com47ac84e2011-10-06 13:11:25 +0000633 if (!a.fRunHead || !b.fRunHead) {
reed@google.come36707a2011-10-04 21:38:55 +0000634 return false;
635 }
636
637 return ah->fRowCount == bh->fRowCount &&
638 ah->fDataSize == bh->fDataSize &&
639 !memcmp(ah->data(), bh->data(), ah->fDataSize);
640}
641
642void SkAAClip::swap(SkAAClip& other) {
reed@google.com045e62d2011-10-24 12:19:46 +0000643 AUTO_AACLIP_VALIDATE(*this);
644 other.validate();
645
reed@google.come36707a2011-10-04 21:38:55 +0000646 SkTSwap(fBounds, other.fBounds);
647 SkTSwap(fRunHead, other.fRunHead);
648}
649
reed@google.com32287892011-10-05 16:27:44 +0000650bool SkAAClip::set(const SkAAClip& src) {
651 *this = src;
652 return !this->isEmpty();
653}
654
reed@google.come36707a2011-10-04 21:38:55 +0000655bool SkAAClip::setEmpty() {
656 this->freeRuns();
657 fBounds.setEmpty();
reed@google.com47ac84e2011-10-06 13:11:25 +0000658 fRunHead = NULL;
reed@google.come36707a2011-10-04 21:38:55 +0000659 return false;
660}
661
662bool SkAAClip::setRect(const SkIRect& bounds) {
663 if (bounds.isEmpty()) {
664 return this->setEmpty();
665 }
reed@google.com47ac84e2011-10-06 13:11:25 +0000666
reed@google.com045e62d2011-10-24 12:19:46 +0000667 AUTO_AACLIP_VALIDATE(*this);
reed@google.com47ac84e2011-10-06 13:11:25 +0000668
reed@google.com045e62d2011-10-24 12:19:46 +0000669#if 0
reed@google.com47ac84e2011-10-06 13:11:25 +0000670 SkRect r;
671 r.set(bounds);
672 SkPath path;
673 path.addRect(r);
674 return this->setPath(path);
reed@google.com045e62d2011-10-24 12:19:46 +0000675#else
676 this->freeRuns();
677 fBounds = bounds;
678 fRunHead = RunHead::AllocRect(bounds);
679 SkASSERT(!this->isEmpty());
680 return true;
681#endif
reed@google.come36707a2011-10-04 21:38:55 +0000682}
683
reed@google.comf3c1da12011-10-10 19:35:47 +0000684bool SkAAClip::setRect(const SkRect& r, bool doAA) {
reed@google.come36707a2011-10-04 21:38:55 +0000685 if (r.isEmpty()) {
686 return this->setEmpty();
687 }
688
reed@google.com045e62d2011-10-24 12:19:46 +0000689 AUTO_AACLIP_VALIDATE(*this);
690
691 // TODO: special case this
692
reed@google.come36707a2011-10-04 21:38:55 +0000693 SkPath path;
694 path.addRect(r);
reed@google.comf3c1da12011-10-10 19:35:47 +0000695 return this->setPath(path, NULL, doAA);
696}
697
reed@google.coma069c8f2011-11-28 19:54:56 +0000698static void append_run(SkTDArray<uint8_t>& array, uint8_t value, int count) {
699 SkASSERT(count >= 0);
700 while (count > 0) {
701 int n = count;
702 if (n > 255) {
703 n = 255;
704 }
705 uint8_t* data = array.append(2);
706 data[0] = n;
707 data[1] = value;
708 count -= n;
709 }
710}
711
reed@google.comf3c1da12011-10-10 19:35:47 +0000712bool SkAAClip::setRegion(const SkRegion& rgn) {
713 if (rgn.isEmpty()) {
714 return this->setEmpty();
715 }
716 if (rgn.isRect()) {
717 return this->setRect(rgn.getBounds());
718 }
reed@google.coma069c8f2011-11-28 19:54:56 +0000719
720#if 0
reed@google.comf3c1da12011-10-10 19:35:47 +0000721 SkAAClip clip;
722 SkRegion::Iterator iter(rgn);
723 for (; !iter.done(); iter.next()) {
724 clip.op(iter.rect(), SkRegion::kUnion_Op);
725 }
726 this->swap(clip);
reed@google.com3771a032011-10-11 17:18:04 +0000727 return !this->isEmpty();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000728#else
reed@google.coma069c8f2011-11-28 19:54:56 +0000729 const SkIRect& bounds = rgn.getBounds();
730 const int offsetX = bounds.fLeft;
731 const int offsetY = bounds.fTop;
732
733 SkTDArray<YOffset> yArray;
734 SkTDArray<uint8_t> xArray;
735
736 yArray.setReserve(SkMin32(bounds.height(), 1024));
737 xArray.setReserve(SkMin32(bounds.width() * 128, 64 * 1024));
738
739 SkRegion::Iterator iter(rgn);
740 int prevRight = 0;
741 int prevBot = 0;
742 YOffset* currY = NULL;
743
744 for (; !iter.done(); iter.next()) {
745 const SkIRect& r = iter.rect();
746 SkASSERT(bounds.contains(r));
747
748 int bot = r.fBottom - offsetY;
749 SkASSERT(bot >= prevBot);
750 if (bot > prevBot) {
751 if (currY) {
752 // flush current row
753 append_run(xArray, 0, bounds.width() - prevRight);
754 }
755 // did we introduce an empty-gap from the prev row?
756 int top = r.fTop - offsetY;
757 if (top > prevBot) {
758 currY = yArray.append();
759 currY->fY = top - 1;
760 currY->fOffset = xArray.count();
761 append_run(xArray, 0, bounds.width());
762 }
763 // create a new record for this Y value
764 currY = yArray.append();
765 currY->fY = bot - 1;
766 currY->fOffset = xArray.count();
767 prevRight = 0;
768 prevBot = bot;
769 }
770
771 int x = r.fLeft - offsetX;
772 append_run(xArray, 0, x - prevRight);
773
774 int w = r.fRight - r.fLeft;
775 append_run(xArray, 0xFF, w);
776 prevRight = x + w;
777 SkASSERT(prevRight <= bounds.width());
778 }
779 // flush last row
780 append_run(xArray, 0, bounds.width() - prevRight);
781
782 // now pack everything into a RunHead
783 RunHead* head = RunHead::Alloc(yArray.count(), xArray.bytes());
784 memcpy(head->yoffsets(), yArray.begin(), yArray.bytes());
785 memcpy(head->data(), xArray.begin(), xArray.bytes());
786
787 this->setEmpty();
788 fBounds = bounds;
789 fRunHead = head;
790 this->validate();
791 return true;
792#endif
reed@google.come36707a2011-10-04 21:38:55 +0000793}
794
795///////////////////////////////////////////////////////////////////////////////
796
797const uint8_t* SkAAClip::findRow(int y, int* lastYForRow) const {
reed@google.com47ac84e2011-10-06 13:11:25 +0000798 SkASSERT(fRunHead);
reed@google.come36707a2011-10-04 21:38:55 +0000799
800 if (!y_in_rect(y, fBounds)) {
801 return NULL;
802 }
803 y -= fBounds.y(); // our yoffs values are relative to the top
804
805 const YOffset* yoff = fRunHead->yoffsets();
806 while (yoff->fY < y) {
807 yoff += 1;
808 SkASSERT(yoff - fRunHead->yoffsets() < fRunHead->fRowCount);
809 }
810
811 if (lastYForRow) {
reed@google.com045e62d2011-10-24 12:19:46 +0000812 *lastYForRow = fBounds.y() + yoff->fY;
reed@google.come36707a2011-10-04 21:38:55 +0000813 }
814 return fRunHead->data() + yoff->fOffset;
815}
816
817const uint8_t* SkAAClip::findX(const uint8_t data[], int x, int* initialCount) const {
818 SkASSERT(x_in_rect(x, fBounds));
819 x -= fBounds.x();
820
821 // first skip up to X
822 for (;;) {
823 int n = data[0];
824 if (x < n) {
reed@google.coma4c6e4d2012-06-20 14:29:50 +0000825 if (initialCount) {
826 *initialCount = n - x;
827 }
reed@google.come36707a2011-10-04 21:38:55 +0000828 break;
829 }
830 data += 2;
831 x -= n;
832 }
833 return data;
834}
835
836bool SkAAClip::quickContains(int left, int top, int right, int bottom) const {
837 if (this->isEmpty()) {
838 return false;
839 }
840 if (!fBounds.contains(left, top, right, bottom)) {
841 return false;
842 }
reed@google.com32287892011-10-05 16:27:44 +0000843#if 0
reed@google.come36707a2011-10-04 21:38:55 +0000844 if (this->isRect()) {
845 return true;
846 }
reed@google.com32287892011-10-05 16:27:44 +0000847#endif
reed@google.come36707a2011-10-04 21:38:55 +0000848
reed@google.coma4c6e4d2012-06-20 14:29:50 +0000849 int lastY SK_INIT_TO_AVOID_WARNING;
reed@google.come36707a2011-10-04 21:38:55 +0000850 const uint8_t* row = this->findRow(top, &lastY);
851 if (lastY < bottom) {
852 return false;
853 }
854 // now just need to check in X
reed@google.com045e62d2011-10-24 12:19:46 +0000855 int count;
856 row = this->findX(row, left, &count);
857#if 0
858 return count >= (right - left) && 0xFF == row[1];
859#else
860 int rectWidth = right - left;
861 while (0xFF == row[1]) {
862 if (count >= rectWidth) {
863 return true;
864 }
865 rectWidth -= count;
866 row += 2;
867 count = row[0];
868 }
869 return false;
870#endif
reed@google.come36707a2011-10-04 21:38:55 +0000871}
872
873///////////////////////////////////////////////////////////////////////////////
874
875class SkAAClip::Builder {
876 SkIRect fBounds;
877 struct Row {
878 int fY;
879 int fWidth;
880 SkTDArray<uint8_t>* fData;
881 };
882 SkTDArray<Row> fRows;
883 Row* fCurrRow;
884 int fPrevY;
885 int fWidth;
reed@google.com209c4152011-10-26 15:03:48 +0000886 int fMinY;
reed@google.come36707a2011-10-04 21:38:55 +0000887
888public:
889 Builder(const SkIRect& bounds) : fBounds(bounds) {
890 fPrevY = -1;
891 fWidth = bounds.width();
892 fCurrRow = NULL;
reed@google.com209c4152011-10-26 15:03:48 +0000893 fMinY = bounds.fTop;
reed@google.come36707a2011-10-04 21:38:55 +0000894 }
895
896 ~Builder() {
897 Row* row = fRows.begin();
898 Row* stop = fRows.end();
899 while (row < stop) {
900 delete row->fData;
901 row += 1;
902 }
903 }
904
reed@google.com32287892011-10-05 16:27:44 +0000905 const SkIRect& getBounds() const { return fBounds; }
906
reed@google.come36707a2011-10-04 21:38:55 +0000907 void addRun(int x, int y, U8CPU alpha, int count) {
908 SkASSERT(count > 0);
909 SkASSERT(fBounds.contains(x, y));
910 SkASSERT(fBounds.contains(x + count - 1, y));
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000911
reed@google.come36707a2011-10-04 21:38:55 +0000912 x -= fBounds.left();
913 y -= fBounds.top();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000914
reed@google.come36707a2011-10-04 21:38:55 +0000915 Row* row = fCurrRow;
916 if (y != fPrevY) {
917 SkASSERT(y > fPrevY);
918 fPrevY = y;
919 row = this->flushRow(true);
920 row->fY = y;
921 row->fWidth = 0;
922 SkASSERT(row->fData);
923 SkASSERT(0 == row->fData->count());
924 fCurrRow = row;
925 }
926
927 SkASSERT(row->fWidth <= x);
928 SkASSERT(row->fWidth < fBounds.width());
929
930 SkTDArray<uint8_t>& data = *row->fData;
931
932 int gap = x - row->fWidth;
933 if (gap) {
934 AppendRun(data, 0, gap);
935 row->fWidth += gap;
936 SkASSERT(row->fWidth < fBounds.width());
937 }
938
939 AppendRun(data, alpha, count);
940 row->fWidth += count;
941 SkASSERT(row->fWidth <= fBounds.width());
942 }
943
tomhudson@google.com49eac192011-12-27 13:59:20 +0000944 void addColumn(int x, int y, U8CPU alpha, int height) {
945 SkASSERT(fBounds.contains(x, y + height - 1));
946
947 this->addRun(x, y, alpha, 1);
948 this->flushRowH(fCurrRow);
949 y -= fBounds.fTop;
950 SkASSERT(y == fCurrRow->fY);
951 fCurrRow->fY = y + height - 1;
952 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000953
reed@google.com9154eb02011-10-31 16:07:28 +0000954 void addRectRun(int x, int y, int width, int height) {
955 SkASSERT(fBounds.contains(x + width - 1, y + height - 1));
956 this->addRun(x, y, 0xFF, width);
957
reed@google.coma89c77b2011-12-01 21:47:26 +0000958 // we assum the rect must be all we'll see for these scanlines
reed@google.com9154eb02011-10-31 16:07:28 +0000959 // so we ensure our row goes all the way to our right
960 this->flushRowH(fCurrRow);
961
962 y -= fBounds.fTop;
963 SkASSERT(y == fCurrRow->fY);
964 fCurrRow->fY = y + height - 1;
965 }
966
tomhudson@google.com49eac192011-12-27 13:59:20 +0000967 void addAntiRectRun(int x, int y, int width, int height,
968 SkAlpha leftAlpha, SkAlpha rightAlpha) {
969 SkASSERT(fBounds.contains(x + width - 1 +
970 (leftAlpha > 0 ? 1 : 0) + (rightAlpha > 0 ? 1 : 0),
971 y + height - 1));
972 SkASSERT(width >= 0);
973
974 // Conceptually we're always adding 3 runs, but we should
975 // merge or omit them if possible.
976 if (leftAlpha == 0xFF) {
977 width++;
978 } else if (leftAlpha > 0) {
979 this->addRun(x++, y, leftAlpha, 1);
980 }
981 if (rightAlpha == 0xFF) {
982 width++;
983 }
984 if (width > 0) {
985 this->addRun(x, y, 0xFF, width);
986 }
987 if (rightAlpha > 0 && rightAlpha < 255) {
988 this->addRun(x + width, y, rightAlpha, 1);
989 }
990
991 // we assume the rect must be all we'll see for these scanlines
992 // so we ensure our row goes all the way to our right
993 this->flushRowH(fCurrRow);
994
995 y -= fBounds.fTop;
996 SkASSERT(y == fCurrRow->fY);
997 fCurrRow->fY = y + height - 1;
998 }
999
reed@google.com045e62d2011-10-24 12:19:46 +00001000 bool finish(SkAAClip* target) {
reed@google.come36707a2011-10-04 21:38:55 +00001001 this->flushRow(false);
1002
1003 const Row* row = fRows.begin();
1004 const Row* stop = fRows.end();
1005
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001006 size_t dataSize = 0;
reed@google.come36707a2011-10-04 21:38:55 +00001007 while (row < stop) {
1008 dataSize += row->fData->count();
1009 row += 1;
1010 }
1011
reed@google.com045e62d2011-10-24 12:19:46 +00001012 if (0 == dataSize) {
1013 return target->setEmpty();
1014 }
1015
reed@google.com209c4152011-10-26 15:03:48 +00001016 SkASSERT(fMinY >= fBounds.fTop);
1017 SkASSERT(fMinY < fBounds.fBottom);
1018 int adjustY = fMinY - fBounds.fTop;
1019 fBounds.fTop = fMinY;
1020
reed@google.come36707a2011-10-04 21:38:55 +00001021 RunHead* head = RunHead::Alloc(fRows.count(), dataSize);
1022 YOffset* yoffset = head->yoffsets();
1023 uint8_t* data = head->data();
1024 uint8_t* baseData = data;
1025
1026 row = fRows.begin();
reed@google.comc9041912011-10-27 16:58:46 +00001027 SkDEBUGCODE(int prevY = row->fY - 1;)
reed@google.come36707a2011-10-04 21:38:55 +00001028 while (row < stop) {
reed@google.comc9041912011-10-27 16:58:46 +00001029 SkASSERT(prevY < row->fY); // must be monotonic
1030 SkDEBUGCODE(prevY = row->fY);
1031
reed@google.com209c4152011-10-26 15:03:48 +00001032 yoffset->fY = row->fY - adjustY;
reed@google.come36707a2011-10-04 21:38:55 +00001033 yoffset->fOffset = data - baseData;
1034 yoffset += 1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001035
reed@google.come36707a2011-10-04 21:38:55 +00001036 size_t n = row->fData->count();
1037 memcpy(data, row->fData->begin(), n);
reed@google.comc9041912011-10-27 16:58:46 +00001038#ifdef SK_DEBUG
tomhudson@google.comf74ad8c2011-11-09 22:15:08 +00001039 size_t bytesNeeded = compute_row_length(data, fBounds.width());
reed@google.comc9041912011-10-27 16:58:46 +00001040 SkASSERT(bytesNeeded == n);
1041#endif
reed@google.come36707a2011-10-04 21:38:55 +00001042 data += n;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001043
reed@google.come36707a2011-10-04 21:38:55 +00001044 row += 1;
1045 }
1046
reed@google.com045e62d2011-10-24 12:19:46 +00001047 target->freeRuns();
1048 target->fBounds = fBounds;
1049 target->fRunHead = head;
1050 return target->trimBounds();
reed@google.come36707a2011-10-04 21:38:55 +00001051 }
1052
1053 void dump() {
1054 this->validate();
1055 int y;
1056 for (y = 0; y < fRows.count(); ++y) {
1057 const Row& row = fRows[y];
1058 SkDebugf("Y:%3d W:%3d", row.fY, row.fWidth);
1059 const SkTDArray<uint8_t>& data = *row.fData;
1060 int count = data.count();
1061 SkASSERT(!(count & 1));
1062 const uint8_t* ptr = data.begin();
1063 for (int x = 0; x < count; x += 2) {
1064 SkDebugf(" [%3d:%02X]", ptr[0], ptr[1]);
1065 ptr += 2;
1066 }
1067 SkDebugf("\n");
1068 }
reed@google.come36707a2011-10-04 21:38:55 +00001069 }
1070
1071 void validate() {
1072#ifdef SK_DEBUG
caryclark@google.com803eceb2012-06-06 12:09:34 +00001073 if (false) { // avoid bit rot, suppress warning
1074 test_count_left_right_zeros();
1075 }
reed@google.come36707a2011-10-04 21:38:55 +00001076 int prevY = -1;
1077 for (int i = 0; i < fRows.count(); ++i) {
1078 const Row& row = fRows[i];
1079 SkASSERT(prevY < row.fY);
1080 SkASSERT(fWidth == row.fWidth);
1081 int count = row.fData->count();
1082 const uint8_t* ptr = row.fData->begin();
1083 SkASSERT(!(count & 1));
1084 int w = 0;
1085 for (int x = 0; x < count; x += 2) {
reed@google.comd6040f62011-10-28 02:39:17 +00001086 int n = ptr[0];
1087 SkASSERT(n > 0);
1088 w += n;
reed@google.come36707a2011-10-04 21:38:55 +00001089 SkASSERT(w <= fWidth);
1090 ptr += 2;
1091 }
1092 SkASSERT(w == fWidth);
1093 prevY = row.fY;
1094 }
1095#endif
1096 }
1097
reed@google.com209c4152011-10-26 15:03:48 +00001098 // only called by BuilderBlitter
1099 void setMinY(int y) {
1100 fMinY = y;
1101 }
1102
reed@google.come36707a2011-10-04 21:38:55 +00001103private:
reed@google.com9154eb02011-10-31 16:07:28 +00001104 void flushRowH(Row* row) {
1105 // flush current row if needed
1106 if (row->fWidth < fWidth) {
1107 AppendRun(*row->fData, 0, fWidth - row->fWidth);
1108 row->fWidth = fWidth;
1109 }
1110 }
reed@google.com209c4152011-10-26 15:03:48 +00001111
reed@google.come36707a2011-10-04 21:38:55 +00001112 Row* flushRow(bool readyForAnother) {
1113 Row* next = NULL;
1114 int count = fRows.count();
1115 if (count > 0) {
reed@google.com9154eb02011-10-31 16:07:28 +00001116 this->flushRowH(&fRows[count - 1]);
reed@google.come36707a2011-10-04 21:38:55 +00001117 }
1118 if (count > 1) {
1119 // are our last two runs the same?
1120 Row* prev = &fRows[count - 2];
1121 Row* curr = &fRows[count - 1];
1122 SkASSERT(prev->fWidth == fWidth);
1123 SkASSERT(curr->fWidth == fWidth);
1124 if (*prev->fData == *curr->fData) {
1125 prev->fY = curr->fY;
1126 if (readyForAnother) {
1127 curr->fData->rewind();
1128 next = curr;
1129 } else {
1130 delete curr->fData;
1131 fRows.removeShuffle(count - 1);
1132 }
1133 } else {
1134 if (readyForAnother) {
1135 next = fRows.append();
1136 next->fData = new SkTDArray<uint8_t>;
1137 }
1138 }
1139 } else {
1140 if (readyForAnother) {
1141 next = fRows.append();
1142 next->fData = new SkTDArray<uint8_t>;
1143 }
1144 }
1145 return next;
1146 }
1147
1148 static void AppendRun(SkTDArray<uint8_t>& data, U8CPU alpha, int count) {
1149 do {
1150 int n = count;
1151 if (n > 255) {
1152 n = 255;
1153 }
1154 uint8_t* ptr = data.append(2);
1155 ptr[0] = n;
1156 ptr[1] = alpha;
1157 count -= n;
1158 } while (count > 0);
1159 }
1160};
1161
1162class SkAAClip::BuilderBlitter : public SkBlitter {
reed@google.com80cdb9a2012-02-16 18:56:17 +00001163 int fLastY;
1164
1165 /*
1166 If we see a gap of 1 or more empty scanlines while building in Y-order,
1167 we inject an explicit empty scanline (alpha==0)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001168
reed@google.com80cdb9a2012-02-16 18:56:17 +00001169 See AAClipTest.cpp : test_path_with_hole()
1170 */
1171 void checkForYGap(int y) {
1172 SkASSERT(y >= fLastY);
1173 if (fLastY > -SK_MaxS32) {
1174 int gap = y - fLastY;
1175 if (gap > 1) {
1176 fBuilder->addRun(fLeft, y - 1, 0, fRight - fLeft);
1177 }
1178 }
1179 fLastY = y;
1180 }
1181
reed@google.come36707a2011-10-04 21:38:55 +00001182public:
reed@google.com80cdb9a2012-02-16 18:56:17 +00001183
reed@google.come36707a2011-10-04 21:38:55 +00001184 BuilderBlitter(Builder* builder) {
1185 fBuilder = builder;
reed@google.com17785642011-10-12 20:23:55 +00001186 fLeft = builder->getBounds().fLeft;
1187 fRight = builder->getBounds().fRight;
reed@google.com209c4152011-10-26 15:03:48 +00001188 fMinY = SK_MaxS32;
reed@google.com80cdb9a2012-02-16 18:56:17 +00001189 fLastY = -SK_MaxS32; // sentinel
reed@google.com209c4152011-10-26 15:03:48 +00001190 }
1191
1192 void finish() {
1193 if (fMinY < SK_MaxS32) {
1194 fBuilder->setMinY(fMinY);
1195 }
reed@google.come36707a2011-10-04 21:38:55 +00001196 }
1197
tomhudson@google.com49eac192011-12-27 13:59:20 +00001198 /**
1199 Must evaluate clips in scan-line order, so don't want to allow blitV(),
1200 but an AAClip can be clipped down to a single pixel wide, so we
1201 must support it (given AntiRect semantics: minimum width is 2).
1202 Instead we'll rely on the runtime asserts to guarantee Y monotonicity;
1203 any failure cases that misses may have minor artifacts.
1204 */
1205 virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE {
1206 this->recordMinY(y);
1207 fBuilder->addColumn(x, y, alpha, height);
reed@google.com9b0da232012-02-29 13:59:15 +00001208 fLastY = y + height - 1;
tomhudson@google.com49eac192011-12-27 13:59:20 +00001209 }
reed@google.com045e62d2011-10-24 12:19:46 +00001210
reed@google.com562a2ac2011-10-31 14:14:18 +00001211 virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE {
reed@google.com9154eb02011-10-31 16:07:28 +00001212 this->recordMinY(y);
reed@google.com80cdb9a2012-02-16 18:56:17 +00001213 this->checkForYGap(y);
reed@google.com9154eb02011-10-31 16:07:28 +00001214 fBuilder->addRectRun(x, y, width, height);
reed@google.com302b8612012-02-16 19:30:13 +00001215 fLastY = y + height - 1;
reed@google.com562a2ac2011-10-31 14:14:18 +00001216 }
reed@google.com045e62d2011-10-24 12:19:46 +00001217
tomhudson@google.com49eac192011-12-27 13:59:20 +00001218 virtual void blitAntiRect(int x, int y, int width, int height,
1219 SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE {
1220 this->recordMinY(y);
reed@google.com302b8612012-02-16 19:30:13 +00001221 this->checkForYGap(y);
tomhudson@google.com49eac192011-12-27 13:59:20 +00001222 fBuilder->addAntiRectRun(x, y, width, height, leftAlpha, rightAlpha);
reed@google.com302b8612012-02-16 19:30:13 +00001223 fLastY = y + height - 1;
tomhudson@google.com49eac192011-12-27 13:59:20 +00001224 }
1225
reed@google.come36707a2011-10-04 21:38:55 +00001226 virtual void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE
1227 { unexpected(); }
1228
1229 virtual const SkBitmap* justAnOpaqueColor(uint32_t*) SK_OVERRIDE {
reed@google.com3771a032011-10-11 17:18:04 +00001230 return NULL;
reed@google.come36707a2011-10-04 21:38:55 +00001231 }
1232
1233 virtual void blitH(int x, int y, int width) SK_OVERRIDE {
reed@google.com209c4152011-10-26 15:03:48 +00001234 this->recordMinY(y);
reed@google.com80cdb9a2012-02-16 18:56:17 +00001235 this->checkForYGap(y);
reed@google.come36707a2011-10-04 21:38:55 +00001236 fBuilder->addRun(x, y, 0xFF, width);
1237 }
1238
1239 virtual void blitAntiH(int x, int y, const SkAlpha alpha[],
1240 const int16_t runs[]) SK_OVERRIDE {
reed@google.com209c4152011-10-26 15:03:48 +00001241 this->recordMinY(y);
reed@google.com80cdb9a2012-02-16 18:56:17 +00001242 this->checkForYGap(y);
reed@google.come36707a2011-10-04 21:38:55 +00001243 for (;;) {
1244 int count = *runs;
1245 if (count <= 0) {
1246 return;
1247 }
reed@google.com17785642011-10-12 20:23:55 +00001248
1249 // The supersampler's buffer can be the width of the device, so
1250 // we may have to trim the run to our bounds. If so, we assert that
1251 // the extra spans are always alpha==0
1252 int localX = x;
1253 int localCount = count;
1254 if (x < fLeft) {
1255 SkASSERT(0 == *alpha);
1256 int gap = fLeft - x;
1257 SkASSERT(gap <= count);
1258 localX += gap;
1259 localCount -= gap;
1260 }
1261 int right = x + count;
1262 if (right > fRight) {
1263 SkASSERT(0 == *alpha);
1264 localCount -= right - fRight;
1265 SkASSERT(localCount >= 0);
1266 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001267
reed@google.com17785642011-10-12 20:23:55 +00001268 if (localCount) {
1269 fBuilder->addRun(localX, y, *alpha, localCount);
1270 }
bsalomon@google.com820e80a2011-10-24 21:09:40 +00001271 // Next run
reed@google.come36707a2011-10-04 21:38:55 +00001272 runs += count;
1273 alpha += count;
1274 x += count;
1275 }
1276 }
1277
1278private:
1279 Builder* fBuilder;
reed@google.com17785642011-10-12 20:23:55 +00001280 int fLeft; // cache of builder's bounds' left edge
1281 int fRight;
reed@google.com209c4152011-10-26 15:03:48 +00001282 int fMinY;
1283
1284 /*
1285 * We track this, in case the scan converter skipped some number of
1286 * scanlines at the (relative to the bounds it was given). This allows
1287 * the builder, during its finish, to trip its bounds down to the "real"
1288 * top.
1289 */
1290 void recordMinY(int y) {
1291 if (y < fMinY) {
1292 fMinY = y;
1293 }
1294 }
reed@google.come36707a2011-10-04 21:38:55 +00001295
1296 void unexpected() {
1297 SkDebugf("---- did not expect to get called here");
1298 sk_throw();
1299 }
1300};
1301
reed@google.comf3c1da12011-10-10 19:35:47 +00001302bool SkAAClip::setPath(const SkPath& path, const SkRegion* clip, bool doAA) {
reed@google.com045e62d2011-10-24 12:19:46 +00001303 AUTO_AACLIP_VALIDATE(*this);
1304
reed@google.com32287892011-10-05 16:27:44 +00001305 if (clip && clip->isEmpty()) {
reed@google.come36707a2011-10-04 21:38:55 +00001306 return this->setEmpty();
1307 }
1308
1309 SkIRect ibounds;
reed@google.com32287892011-10-05 16:27:44 +00001310 path.getBounds().roundOut(&ibounds);
reed@google.come36707a2011-10-04 21:38:55 +00001311
reed@google.com32287892011-10-05 16:27:44 +00001312 SkRegion tmpClip;
1313 if (NULL == clip) {
1314 tmpClip.setRect(ibounds);
1315 clip = &tmpClip;
1316 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001317
reed@google.com045e62d2011-10-24 12:19:46 +00001318 if (path.isInverseFillType()) {
1319 ibounds = clip->getBounds();
1320 } else {
reed@google.com32287892011-10-05 16:27:44 +00001321 if (ibounds.isEmpty() || !ibounds.intersect(clip->getBounds())) {
reed@google.come36707a2011-10-04 21:38:55 +00001322 return this->setEmpty();
1323 }
reed@google.come36707a2011-10-04 21:38:55 +00001324 }
1325
1326 Builder builder(ibounds);
1327 BuilderBlitter blitter(&builder);
1328
reed@google.comf3c1da12011-10-10 19:35:47 +00001329 if (doAA) {
1330 SkScan::AntiFillPath(path, *clip, &blitter, true);
1331 } else {
1332 SkScan::FillPath(path, *clip, &blitter);
1333 }
reed@google.come36707a2011-10-04 21:38:55 +00001334
reed@google.com209c4152011-10-26 15:03:48 +00001335 blitter.finish();
reed@google.com045e62d2011-10-24 12:19:46 +00001336 return builder.finish(this);
reed@google.come36707a2011-10-04 21:38:55 +00001337}
1338
1339///////////////////////////////////////////////////////////////////////////////
1340
reed@google.com32287892011-10-05 16:27:44 +00001341typedef void (*RowProc)(SkAAClip::Builder&, int bottom,
1342 const uint8_t* rowA, const SkIRect& rectA,
1343 const uint8_t* rowB, const SkIRect& rectB);
1344
reed@google.com32287892011-10-05 16:27:44 +00001345typedef U8CPU (*AlphaProc)(U8CPU alphaA, U8CPU alphaB);
1346
1347static U8CPU sectAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1348 // Multiply
1349 return SkMulDiv255Round(alphaA, alphaB);
1350}
1351
1352static U8CPU unionAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1353 // SrcOver
1354 return alphaA + alphaB - SkMulDiv255Round(alphaA, alphaB);
1355}
1356
1357static U8CPU diffAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1358 // SrcOut
1359 return SkMulDiv255Round(alphaA, 0xFF - alphaB);
1360}
1361
1362static U8CPU xorAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1363 // XOR
1364 return alphaA + alphaB - 2 * SkMulDiv255Round(alphaA, alphaB);
1365}
1366
1367static AlphaProc find_alpha_proc(SkRegion::Op op) {
1368 switch (op) {
1369 case SkRegion::kIntersect_Op:
1370 return sectAlphaProc;
1371 case SkRegion::kDifference_Op:
1372 return diffAlphaProc;
1373 case SkRegion::kUnion_Op:
1374 return unionAlphaProc;
1375 case SkRegion::kXOR_Op:
1376 return xorAlphaProc;
1377 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001378 SkDEBUGFAIL("unexpected region op");
reed@google.com32287892011-10-05 16:27:44 +00001379 return sectAlphaProc;
1380 }
1381}
1382
1383static const uint8_t gEmptyRow[] = {
1384 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
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};
1393
1394class RowIter {
1395public:
1396 RowIter(const uint8_t* row, const SkIRect& bounds) {
1397 fRow = row;
1398 fLeft = bounds.fLeft;
reed@google.com32287892011-10-05 16:27:44 +00001399 fBoundsRight = bounds.fRight;
reed@google.com1c04bf92011-10-10 12:57:12 +00001400 if (row) {
1401 fRight = bounds.fLeft + row[0];
1402 SkASSERT(fRight <= fBoundsRight);
1403 fAlpha = row[1];
1404 fDone = false;
1405 } else {
1406 fDone = true;
1407 fRight = kMaxInt32;
1408 fAlpha = 0;
1409 }
reed@google.com32287892011-10-05 16:27:44 +00001410 }
1411
1412 bool done() const { return fDone; }
reed@google.com1c04bf92011-10-10 12:57:12 +00001413 int left() const { return fLeft; }
1414 int right() const { return fRight; }
1415 U8CPU alpha() const { return fAlpha; }
reed@google.com32287892011-10-05 16:27:44 +00001416 void next() {
reed@google.com1c04bf92011-10-10 12:57:12 +00001417 if (!fDone) {
reed@google.com32287892011-10-05 16:27:44 +00001418 fLeft = fRight;
reed@google.com1c04bf92011-10-10 12:57:12 +00001419 if (fRight == fBoundsRight) {
1420 fDone = true;
1421 fRight = kMaxInt32;
1422 fAlpha = 0;
1423 } else {
1424 fRow += 2;
1425 fRight += fRow[0];
1426 fAlpha = fRow[1];
1427 SkASSERT(fRight <= fBoundsRight);
1428 }
reed@google.com32287892011-10-05 16:27:44 +00001429 }
1430 }
1431
1432private:
1433 const uint8_t* fRow;
1434 int fLeft;
1435 int fRight;
1436 int fBoundsRight;
1437 bool fDone;
reed@google.com1c04bf92011-10-10 12:57:12 +00001438 uint8_t fAlpha;
reed@google.com32287892011-10-05 16:27:44 +00001439};
1440
reed@google.com1c04bf92011-10-10 12:57:12 +00001441static void adjust_row(RowIter& iter, int& leftA, int& riteA, int rite) {
1442 if (rite == riteA) {
1443 iter.next();
1444 leftA = iter.left();
1445 riteA = iter.right();
reed@google.com32287892011-10-05 16:27:44 +00001446 }
1447}
1448
caryclark@google.com803eceb2012-06-06 12:09:34 +00001449#if 0 // UNUSED
reed@google.com1c04bf92011-10-10 12:57:12 +00001450static bool intersect(int& min, int& max, int boundsMin, int boundsMax) {
1451 SkASSERT(min < max);
1452 SkASSERT(boundsMin < boundsMax);
1453 if (min >= boundsMax || max <= boundsMin) {
1454 return false;
1455 }
1456 if (min < boundsMin) {
1457 min = boundsMin;
1458 }
1459 if (max > boundsMax) {
1460 max = boundsMax;
1461 }
1462 return true;
1463}
caryclark@google.com803eceb2012-06-06 12:09:34 +00001464#endif
reed@google.com1c04bf92011-10-10 12:57:12 +00001465
reed@google.com32287892011-10-05 16:27:44 +00001466static void operatorX(SkAAClip::Builder& builder, int lastY,
1467 RowIter& iterA, RowIter& iterB,
1468 AlphaProc proc, const SkIRect& bounds) {
reed@google.com32287892011-10-05 16:27:44 +00001469 int leftA = iterA.left();
1470 int riteA = iterA.right();
reed@google.com32287892011-10-05 16:27:44 +00001471 int leftB = iterB.left();
1472 int riteB = iterB.right();
1473
reed@google.com1c04bf92011-10-10 12:57:12 +00001474 int prevRite = bounds.fLeft;
1475
1476 do {
reed@google.com32287892011-10-05 16:27:44 +00001477 U8CPU alphaA = 0;
1478 U8CPU alphaB = 0;
reed@google.com32287892011-10-05 16:27:44 +00001479 int left, rite;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001480
reed@google.com1c04bf92011-10-10 12:57:12 +00001481 if (leftA < leftB) {
reed@google.com32287892011-10-05 16:27:44 +00001482 left = leftA;
reed@google.com32287892011-10-05 16:27:44 +00001483 alphaA = iterA.alpha();
reed@google.com1c04bf92011-10-10 12:57:12 +00001484 if (riteA <= leftB) {
1485 rite = riteA;
1486 } else {
1487 rite = leftA = leftB;
reed@google.com32287892011-10-05 16:27:44 +00001488 }
reed@google.com1c04bf92011-10-10 12:57:12 +00001489 } else if (leftB < leftA) {
1490 left = leftB;
1491 alphaB = iterB.alpha();
1492 if (riteB <= leftA) {
1493 rite = riteB;
1494 } else {
1495 rite = leftB = leftA;
1496 }
1497 } else {
1498 left = leftA; // or leftB, since leftA == leftB
1499 rite = leftA = leftB = SkMin32(riteA, riteB);
1500 alphaA = iterA.alpha();
1501 alphaB = iterB.alpha();
reed@google.com32287892011-10-05 16:27:44 +00001502 }
1503
1504 if (left >= bounds.fRight) {
1505 break;
1506 }
reed@google.com34f7e472011-10-13 15:11:59 +00001507 if (rite > bounds.fRight) {
1508 rite = bounds.fRight;
1509 }
1510
reed@google.com32287892011-10-05 16:27:44 +00001511 if (left >= bounds.fLeft) {
reed@google.com1c04bf92011-10-10 12:57:12 +00001512 SkASSERT(rite > left);
reed@google.com32287892011-10-05 16:27:44 +00001513 builder.addRun(left, lastY, proc(alphaA, alphaB), rite - left);
reed@google.com1c04bf92011-10-10 12:57:12 +00001514 prevRite = rite;
reed@google.com32287892011-10-05 16:27:44 +00001515 }
reed@google.com1c04bf92011-10-10 12:57:12 +00001516
1517 adjust_row(iterA, leftA, riteA, rite);
1518 adjust_row(iterB, leftB, riteB, rite);
1519 } while (!iterA.done() || !iterB.done());
1520
1521 if (prevRite < bounds.fRight) {
1522 builder.addRun(prevRite, lastY, 0, bounds.fRight - prevRite);
reed@google.com32287892011-10-05 16:27:44 +00001523 }
1524}
1525
reed@google.com1c04bf92011-10-10 12:57:12 +00001526static void adjust_iter(SkAAClip::Iter& iter, int& topA, int& botA, int bot) {
1527 if (bot == botA) {
1528 iter.next();
1529 topA = botA;
1530 SkASSERT(botA == iter.top());
1531 botA = iter.bottom();
reed@google.com32287892011-10-05 16:27:44 +00001532 }
1533}
1534
1535static void operateY(SkAAClip::Builder& builder, const SkAAClip& A,
1536 const SkAAClip& B, SkRegion::Op op) {
1537 AlphaProc proc = find_alpha_proc(op);
1538 const SkIRect& bounds = builder.getBounds();
1539
1540 SkAAClip::Iter iterA(A);
1541 SkAAClip::Iter iterB(B);
1542
1543 SkASSERT(!iterA.done());
1544 int topA = iterA.top();
1545 int botA = iterA.bottom();
1546 SkASSERT(!iterB.done());
1547 int topB = iterB.top();
1548 int botB = iterB.bottom();
1549
reed@google.com1c04bf92011-10-10 12:57:12 +00001550 do {
1551 const uint8_t* rowA = NULL;
1552 const uint8_t* rowB = NULL;
reed@google.com32287892011-10-05 16:27:44 +00001553 int top, bot;
reed@google.com1c04bf92011-10-10 12:57:12 +00001554
1555 if (topA < topB) {
reed@google.com32287892011-10-05 16:27:44 +00001556 top = topA;
reed@google.com32287892011-10-05 16:27:44 +00001557 rowA = iterA.data();
reed@google.com1c04bf92011-10-10 12:57:12 +00001558 if (botA <= topB) {
1559 bot = botA;
1560 } else {
1561 bot = topA = topB;
reed@google.com32287892011-10-05 16:27:44 +00001562 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001563
reed@google.com1c04bf92011-10-10 12:57:12 +00001564 } else if (topB < topA) {
1565 top = topB;
1566 rowB = iterB.data();
1567 if (botB <= topA) {
1568 bot = botB;
1569 } else {
1570 bot = topB = topA;
1571 }
1572 } else {
1573 top = topA; // or topB, since topA == topB
1574 bot = topA = topB = SkMin32(botA, botB);
1575 rowA = iterA.data();
1576 rowB = iterB.data();
reed@google.com32287892011-10-05 16:27:44 +00001577 }
1578
1579 if (top >= bounds.fBottom) {
1580 break;
1581 }
reed@google.com34f7e472011-10-13 15:11:59 +00001582
1583 if (bot > bounds.fBottom) {
1584 bot = bounds.fBottom;
1585 }
1586 SkASSERT(top < bot);
1587
reed@google.com1c04bf92011-10-10 12:57:12 +00001588 if (!rowA && !rowB) {
1589 builder.addRun(bounds.fLeft, bot - 1, 0, bounds.width());
1590 } else if (top >= bounds.fTop) {
1591 SkASSERT(bot <= bounds.fBottom);
1592 RowIter rowIterA(rowA, rowA ? A.getBounds() : bounds);
1593 RowIter rowIterB(rowB, rowB ? B.getBounds() : bounds);
reed@google.com32287892011-10-05 16:27:44 +00001594 operatorX(builder, bot - 1, rowIterA, rowIterB, proc, bounds);
reed@google.com32287892011-10-05 16:27:44 +00001595 }
1596
reed@google.com1c04bf92011-10-10 12:57:12 +00001597 adjust_iter(iterA, topA, botA, bot);
1598 adjust_iter(iterB, topB, botB, bot);
1599 } while (!iterA.done() || !iterB.done());
reed@google.com32287892011-10-05 16:27:44 +00001600}
1601
1602bool SkAAClip::op(const SkAAClip& clipAOrig, const SkAAClip& clipBOrig,
1603 SkRegion::Op op) {
reed@google.com045e62d2011-10-24 12:19:46 +00001604 AUTO_AACLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001605
reed@google.com32287892011-10-05 16:27:44 +00001606 if (SkRegion::kReplace_Op == op) {
1607 return this->set(clipBOrig);
1608 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001609
reed@google.com32287892011-10-05 16:27:44 +00001610 const SkAAClip* clipA = &clipAOrig;
1611 const SkAAClip* clipB = &clipBOrig;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001612
reed@google.com32287892011-10-05 16:27:44 +00001613 if (SkRegion::kReverseDifference_Op == op) {
1614 SkTSwap(clipA, clipB);
1615 op = SkRegion::kDifference_Op;
1616 }
1617
1618 bool a_empty = clipA->isEmpty();
1619 bool b_empty = clipB->isEmpty();
1620
1621 SkIRect bounds;
1622 switch (op) {
1623 case SkRegion::kDifference_Op:
1624 if (a_empty) {
1625 return this->setEmpty();
1626 }
1627 if (b_empty || !SkIRect::Intersects(clipA->fBounds, clipB->fBounds)) {
1628 return this->set(*clipA);
1629 }
1630 bounds = clipA->fBounds;
1631 break;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001632
reed@google.com32287892011-10-05 16:27:44 +00001633 case SkRegion::kIntersect_Op:
1634 if ((a_empty | b_empty) || !bounds.intersect(clipA->fBounds,
1635 clipB->fBounds)) {
1636 return this->setEmpty();
1637 }
1638 break;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001639
reed@google.com32287892011-10-05 16:27:44 +00001640 case SkRegion::kUnion_Op:
1641 case SkRegion::kXOR_Op:
1642 if (a_empty) {
1643 return this->set(*clipB);
1644 }
1645 if (b_empty) {
1646 return this->set(*clipA);
1647 }
1648 bounds = clipA->fBounds;
1649 bounds.join(clipB->fBounds);
1650 break;
1651
1652 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001653 SkDEBUGFAIL("unknown region op");
reed@google.com32287892011-10-05 16:27:44 +00001654 return !this->isEmpty();
1655 }
1656
reed@google.com32287892011-10-05 16:27:44 +00001657 SkASSERT(SkIRect::Intersects(bounds, clipB->fBounds));
1658 SkASSERT(SkIRect::Intersects(bounds, clipB->fBounds));
1659
1660 Builder builder(bounds);
1661 operateY(builder, *clipA, *clipB, op);
reed@google.com045e62d2011-10-24 12:19:46 +00001662
1663 return builder.finish(this);
reed@google.come36707a2011-10-04 21:38:55 +00001664}
1665
reed@google.com045e62d2011-10-24 12:19:46 +00001666/*
1667 * It can be expensive to build a local aaclip before applying the op, so
1668 * we first see if we can restrict the bounds of new rect to our current
1669 * bounds, or note that the new rect subsumes our current clip.
1670 */
1671
1672bool SkAAClip::op(const SkIRect& rOrig, SkRegion::Op op) {
1673 SkIRect rStorage;
1674 const SkIRect* r = &rOrig;
1675
1676 switch (op) {
1677 case SkRegion::kIntersect_Op:
1678 if (!rStorage.intersect(rOrig, fBounds)) {
1679 // no overlap, so we're empty
1680 return this->setEmpty();
1681 }
1682 if (rStorage == fBounds) {
1683 // we were wholly inside the rect, no change
1684 return !this->isEmpty();
1685 }
1686 if (this->quickContains(rStorage)) {
1687 // the intersection is wholly inside us, we're a rect
1688 return this->setRect(rStorage);
1689 }
1690 r = &rStorage; // use the intersected bounds
1691 break;
1692 case SkRegion::kDifference_Op:
1693 break;
1694 case SkRegion::kUnion_Op:
1695 if (rOrig.contains(fBounds)) {
1696 return this->setRect(rOrig);
1697 }
1698 break;
1699 default:
1700 break;
1701 }
1702
reed@google.com47ac84e2011-10-06 13:11:25 +00001703 SkAAClip clip;
reed@google.com045e62d2011-10-24 12:19:46 +00001704 clip.setRect(*r);
reed@google.com47ac84e2011-10-06 13:11:25 +00001705 return this->op(*this, clip, op);
1706}
1707
reed@google.com045e62d2011-10-24 12:19:46 +00001708bool SkAAClip::op(const SkRect& rOrig, SkRegion::Op op, bool doAA) {
1709 SkRect rStorage, boundsStorage;
1710 const SkRect* r = &rOrig;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001711
reed@google.com045e62d2011-10-24 12:19:46 +00001712 boundsStorage.set(fBounds);
1713 switch (op) {
1714 case SkRegion::kIntersect_Op:
1715 case SkRegion::kDifference_Op:
1716 if (!rStorage.intersect(rOrig, boundsStorage)) {
reed@google.come56513d2012-06-25 20:06:33 +00001717 if (SkRegion::kIntersect_Op == op) {
1718 return this->setEmpty();
1719 } else { // kDifference
1720 return !this->isEmpty();
1721 }
reed@google.com045e62d2011-10-24 12:19:46 +00001722 }
1723 r = &rStorage; // use the intersected bounds
1724 break;
1725 case SkRegion::kUnion_Op:
1726 if (rOrig.contains(boundsStorage)) {
1727 return this->setRect(rOrig);
1728 }
1729 break;
1730 default:
1731 break;
1732 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001733
reed@google.com47ac84e2011-10-06 13:11:25 +00001734 SkAAClip clip;
reed@google.com045e62d2011-10-24 12:19:46 +00001735 clip.setRect(*r, doAA);
reed@google.com47ac84e2011-10-06 13:11:25 +00001736 return this->op(*this, clip, op);
1737}
1738
1739bool SkAAClip::op(const SkAAClip& clip, SkRegion::Op op) {
1740 return this->op(*this, clip, op);
1741}
1742
reed@google.come36707a2011-10-04 21:38:55 +00001743///////////////////////////////////////////////////////////////////////////////
reed@google.com045e62d2011-10-24 12:19:46 +00001744
1745bool SkAAClip::translate(int dx, int dy, SkAAClip* dst) const {
1746 if (NULL == dst) {
1747 return !this->isEmpty();
1748 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001749
reed@google.com045e62d2011-10-24 12:19:46 +00001750 if (this->isEmpty()) {
1751 return dst->setEmpty();
1752 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001753
reed@google.com045e62d2011-10-24 12:19:46 +00001754 if (this != dst) {
1755 sk_atomic_inc(&fRunHead->fRefCnt);
tomhudson@google.com19224c32012-03-28 15:46:37 +00001756 dst->freeRuns();
reed@google.com045e62d2011-10-24 12:19:46 +00001757 dst->fRunHead = fRunHead;
1758 dst->fBounds = fBounds;
1759 }
1760 dst->fBounds.offset(dx, dy);
1761 return true;
1762}
1763
1764static void expand_row_to_mask(uint8_t* SK_RESTRICT mask,
1765 const uint8_t* SK_RESTRICT row,
1766 int width) {
1767 while (width > 0) {
1768 int n = row[0];
1769 SkASSERT(width >= n);
1770 memset(mask, row[1], n);
1771 mask += n;
1772 row += 2;
1773 width -= n;
1774 }
reed@google.coma069c8f2011-11-28 19:54:56 +00001775 SkASSERT(0 == width);
reed@google.com045e62d2011-10-24 12:19:46 +00001776}
1777
1778void SkAAClip::copyToMask(SkMask* mask) const {
1779 mask->fFormat = SkMask::kA8_Format;
1780 if (this->isEmpty()) {
1781 mask->fBounds.setEmpty();
1782 mask->fImage = NULL;
1783 mask->fRowBytes = 0;
1784 return;
1785 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001786
reed@google.com045e62d2011-10-24 12:19:46 +00001787 mask->fBounds = fBounds;
1788 mask->fRowBytes = fBounds.width();
1789 size_t size = mask->computeImageSize();
1790 mask->fImage = SkMask::AllocImage(size);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001791
reed@google.com045e62d2011-10-24 12:19:46 +00001792 Iter iter(*this);
1793 uint8_t* dst = mask->fImage;
1794 const int width = fBounds.width();
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001795
reed@google.com045e62d2011-10-24 12:19:46 +00001796 int y = fBounds.fTop;
1797 while (!iter.done()) {
1798 do {
1799 expand_row_to_mask(dst, iter.data(), width);
1800 dst += mask->fRowBytes;
1801 } while (++y < iter.bottom());
1802 iter.next();
1803 }
1804}
1805
1806///////////////////////////////////////////////////////////////////////////////
reed@google.come36707a2011-10-04 21:38:55 +00001807///////////////////////////////////////////////////////////////////////////////
1808
1809static void expandToRuns(const uint8_t* SK_RESTRICT data, int initialCount, int width,
1810 int16_t* SK_RESTRICT runs, SkAlpha* SK_RESTRICT aa) {
1811 // we don't read our initial n from data, since the caller may have had to
1812 // clip it, hence the initialCount parameter.
1813 int n = initialCount;
1814 for (;;) {
1815 if (n > width) {
1816 n = width;
1817 }
1818 SkASSERT(n > 0);
1819 runs[0] = n;
1820 runs += n;
1821
1822 aa[0] = data[1];
1823 aa += n;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001824
reed@google.come36707a2011-10-04 21:38:55 +00001825 data += 2;
1826 width -= n;
1827 if (0 == width) {
1828 break;
1829 }
1830 // load the next count
1831 n = data[0];
1832 }
1833 runs[0] = 0; // sentinel
1834}
1835
1836SkAAClipBlitter::~SkAAClipBlitter() {
reed@google.com045e62d2011-10-24 12:19:46 +00001837 sk_free(fScanlineScratch);
reed@google.come36707a2011-10-04 21:38:55 +00001838}
1839
1840void SkAAClipBlitter::ensureRunsAndAA() {
reed@google.com045e62d2011-10-24 12:19:46 +00001841 if (NULL == fScanlineScratch) {
reed@google.come36707a2011-10-04 21:38:55 +00001842 // add 1 so we can store the terminating run count of 0
1843 int count = fAAClipBounds.width() + 1;
reed@google.com045e62d2011-10-24 12:19:46 +00001844 // we use this either for fRuns + fAA, or a scaline of a mask
1845 // which may be as deep as 32bits
1846 fScanlineScratch = sk_malloc_throw(count * sizeof(SkPMColor));
1847 fRuns = (int16_t*)fScanlineScratch;
reed@google.come36707a2011-10-04 21:38:55 +00001848 fAA = (SkAlpha*)(fRuns + count);
1849 }
1850}
1851
1852void SkAAClipBlitter::blitH(int x, int y, int width) {
1853 SkASSERT(width > 0);
1854 SkASSERT(fAAClipBounds.contains(x, y));
1855 SkASSERT(fAAClipBounds.contains(x + width - 1, y));
1856
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001857 const uint8_t* row = fAAClip->findRow(y);
reed@google.come36707a2011-10-04 21:38:55 +00001858 int initialCount;
1859 row = fAAClip->findX(row, x, &initialCount);
1860
1861 if (initialCount >= width) {
1862 SkAlpha alpha = row[1];
1863 if (0 == alpha) {
1864 return;
1865 }
1866 if (0xFF == alpha) {
1867 fBlitter->blitH(x, y, width);
1868 return;
1869 }
1870 }
1871
1872 this->ensureRunsAndAA();
1873 expandToRuns(row, initialCount, width, fRuns, fAA);
1874
1875 fBlitter->blitAntiH(x, y, fAA, fRuns);
1876}
1877
1878static void merge(const uint8_t* SK_RESTRICT row, int rowN,
1879 const SkAlpha* SK_RESTRICT srcAA,
1880 const int16_t* SK_RESTRICT srcRuns,
1881 SkAlpha* SK_RESTRICT dstAA,
1882 int16_t* SK_RESTRICT dstRuns,
1883 int width) {
1884 SkDEBUGCODE(int accumulated = 0;)
1885 int srcN = srcRuns[0];
reed@google.com045e62d2011-10-24 12:19:46 +00001886 // do we need this check?
1887 if (0 == srcN) {
1888 return;
1889 }
1890
reed@google.come36707a2011-10-04 21:38:55 +00001891 for (;;) {
reed@google.come36707a2011-10-04 21:38:55 +00001892 SkASSERT(rowN > 0);
1893 SkASSERT(srcN > 0);
1894
1895 unsigned newAlpha = SkMulDiv255Round(srcAA[0], row[1]);
1896 int minN = SkMin32(srcN, rowN);
1897 dstRuns[0] = minN;
1898 dstRuns += minN;
1899 dstAA[0] = newAlpha;
1900 dstAA += minN;
1901
1902 if (0 == (srcN -= minN)) {
1903 srcN = srcRuns[0]; // refresh
1904 srcRuns += srcN;
1905 srcAA += srcN;
1906 srcN = srcRuns[0]; // reload
reed@google.com045e62d2011-10-24 12:19:46 +00001907 if (0 == srcN) {
1908 break;
1909 }
reed@google.come36707a2011-10-04 21:38:55 +00001910 }
1911 if (0 == (rowN -= minN)) {
1912 row += 2;
1913 rowN = row[0]; // reload
1914 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001915
reed@google.come36707a2011-10-04 21:38:55 +00001916 SkDEBUGCODE(accumulated += minN;)
1917 SkASSERT(accumulated <= width);
1918 }
reed@google.com34f7e472011-10-13 15:11:59 +00001919 dstRuns[0] = 0;
reed@google.come36707a2011-10-04 21:38:55 +00001920}
1921
1922void SkAAClipBlitter::blitAntiH(int x, int y, const SkAlpha aa[],
1923 const int16_t runs[]) {
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001924
1925 const uint8_t* row = fAAClip->findRow(y);
reed@google.come36707a2011-10-04 21:38:55 +00001926 int initialCount;
1927 row = fAAClip->findX(row, x, &initialCount);
1928
1929 this->ensureRunsAndAA();
1930
1931 merge(row, initialCount, aa, runs, fAA, fRuns, fAAClipBounds.width());
1932 fBlitter->blitAntiH(x, y, fAA, fRuns);
1933}
1934
1935void SkAAClipBlitter::blitV(int x, int y, int height, SkAlpha alpha) {
1936 if (fAAClip->quickContains(x, y, x + 1, y + height)) {
1937 fBlitter->blitV(x, y, height, alpha);
1938 return;
1939 }
1940
reed@google.com045e62d2011-10-24 12:19:46 +00001941 for (;;) {
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001942 int lastY SK_INIT_TO_AVOID_WARNING;
reed@google.come36707a2011-10-04 21:38:55 +00001943 const uint8_t* row = fAAClip->findRow(y, &lastY);
reed@google.com045e62d2011-10-24 12:19:46 +00001944 int dy = lastY - y + 1;
1945 if (dy > height) {
1946 dy = height;
1947 }
1948 height -= dy;
1949
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001950 row = fAAClip->findX(row, x);
reed@google.come36707a2011-10-04 21:38:55 +00001951 SkAlpha newAlpha = SkMulDiv255Round(alpha, row[1]);
1952 if (newAlpha) {
reed@google.com045e62d2011-10-24 12:19:46 +00001953 fBlitter->blitV(x, y, dy, newAlpha);
1954 }
1955 SkASSERT(height >= 0);
1956 if (height <= 0) {
1957 break;
reed@google.come36707a2011-10-04 21:38:55 +00001958 }
1959 y = lastY + 1;
reed@google.com045e62d2011-10-24 12:19:46 +00001960 }
reed@google.come36707a2011-10-04 21:38:55 +00001961}
1962
1963void SkAAClipBlitter::blitRect(int x, int y, int width, int height) {
1964 if (fAAClip->quickContains(x, y, x + width, y + height)) {
1965 fBlitter->blitRect(x, y, width, height);
1966 return;
1967 }
1968
1969 while (--height >= 0) {
1970 this->blitH(x, y, width);
1971 y += 1;
1972 }
1973}
1974
reed@google.com045e62d2011-10-24 12:19:46 +00001975typedef void (*MergeAAProc)(const void* src, int width, const uint8_t* row,
1976 int initialRowCount, void* dst);
1977
1978static void small_memcpy(void* dst, const void* src, size_t n) {
1979 memcpy(dst, src, n);
1980}
1981
1982static void small_bzero(void* dst, size_t n) {
1983 sk_bzero(dst, n);
1984}
1985
1986static inline uint8_t mergeOne(uint8_t value, unsigned alpha) {
1987 return SkMulDiv255Round(value, alpha);
1988}
1989static inline uint16_t mergeOne(uint16_t value, unsigned alpha) {
1990 unsigned r = SkGetPackedR16(value);
1991 unsigned g = SkGetPackedG16(value);
1992 unsigned b = SkGetPackedB16(value);
1993 return SkPackRGB16(SkMulDiv255Round(r, alpha),
caryclark@google.com803eceb2012-06-06 12:09:34 +00001994 SkMulDiv255Round(g, alpha),
1995 SkMulDiv255Round(b, alpha));
reed@google.com045e62d2011-10-24 12:19:46 +00001996}
1997static inline SkPMColor mergeOne(SkPMColor value, unsigned alpha) {
1998 unsigned a = SkGetPackedA32(value);
1999 unsigned r = SkGetPackedR32(value);
2000 unsigned g = SkGetPackedG32(value);
2001 unsigned b = SkGetPackedB32(value);
2002 return SkPackARGB32(SkMulDiv255Round(a, alpha),
2003 SkMulDiv255Round(r, alpha),
2004 SkMulDiv255Round(g, alpha),
2005 SkMulDiv255Round(b, alpha));
2006}
2007
2008template <typename T> void mergeT(const T* SK_RESTRICT src, int srcN,
2009 const uint8_t* SK_RESTRICT row, int rowN,
2010 T* SK_RESTRICT dst) {
reed@google.com045e62d2011-10-24 12:19:46 +00002011 for (;;) {
2012 SkASSERT(rowN > 0);
2013 SkASSERT(srcN > 0);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002014
reed@google.com045e62d2011-10-24 12:19:46 +00002015 int n = SkMin32(rowN, srcN);
2016 unsigned rowA = row[1];
2017 if (0xFF == rowA) {
2018 small_memcpy(dst, src, n * sizeof(T));
2019 } else if (0 == rowA) {
2020 small_bzero(dst, n * sizeof(T));
2021 } else {
2022 for (int i = 0; i < n; ++i) {
2023 dst[i] = mergeOne(src[i], rowA);
2024 }
2025 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002026
reed@google.com045e62d2011-10-24 12:19:46 +00002027 if (0 == (srcN -= n)) {
2028 break;
2029 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002030
reed@google.com045e62d2011-10-24 12:19:46 +00002031 src += n;
2032 dst += n;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002033
reed@google.com045e62d2011-10-24 12:19:46 +00002034 SkASSERT(rowN == n);
2035 row += 2;
2036 rowN = row[0];
2037 }
2038}
2039
2040static MergeAAProc find_merge_aa_proc(SkMask::Format format) {
2041 switch (format) {
2042 case SkMask::kBW_Format:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002043 SkDEBUGFAIL("unsupported");
reed@google.com045e62d2011-10-24 12:19:46 +00002044 return NULL;
2045 case SkMask::kA8_Format:
2046 case SkMask::k3D_Format: {
2047 void (*proc8)(const uint8_t*, int, const uint8_t*, int, uint8_t*) = mergeT;
2048 return (MergeAAProc)proc8;
2049 }
2050 case SkMask::kLCD16_Format: {
2051 void (*proc16)(const uint16_t*, int, const uint8_t*, int, uint16_t*) = mergeT;
2052 return (MergeAAProc)proc16;
2053 }
2054 case SkMask::kLCD32_Format: {
2055 void (*proc32)(const SkPMColor*, int, const uint8_t*, int, SkPMColor*) = mergeT;
2056 return (MergeAAProc)proc32;
2057 }
2058 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002059 SkDEBUGFAIL("unsupported");
reed@google.com045e62d2011-10-24 12:19:46 +00002060 return NULL;
2061 }
2062}
2063
2064static U8CPU bit2byte(int bitInAByte) {
2065 SkASSERT(bitInAByte <= 0xFF);
2066 // negation turns any non-zero into 0xFFFFFF??, so we just shift down
2067 // some value >= 8 to get a full FF value
2068 return -bitInAByte >> 8;
2069}
2070
2071static void upscaleBW2A8(SkMask* dstMask, const SkMask& srcMask) {
2072 SkASSERT(SkMask::kBW_Format == srcMask.fFormat);
2073 SkASSERT(SkMask::kA8_Format == dstMask->fFormat);
2074
2075 const int width = srcMask.fBounds.width();
2076 const int height = srcMask.fBounds.height();
2077
2078 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcMask.fImage;
2079 const size_t srcRB = srcMask.fRowBytes;
2080 uint8_t* SK_RESTRICT dst = (uint8_t*)dstMask->fImage;
2081 const size_t dstRB = dstMask->fRowBytes;
2082
2083 const int wholeBytes = width >> 3;
2084 const int leftOverBits = width & 7;
2085
2086 for (int y = 0; y < height; ++y) {
2087 uint8_t* SK_RESTRICT d = dst;
2088 for (int i = 0; i < wholeBytes; ++i) {
2089 int srcByte = src[i];
2090 d[0] = bit2byte(srcByte & (1 << 7));
2091 d[1] = bit2byte(srcByte & (1 << 6));
2092 d[2] = bit2byte(srcByte & (1 << 5));
2093 d[3] = bit2byte(srcByte & (1 << 4));
2094 d[4] = bit2byte(srcByte & (1 << 3));
2095 d[5] = bit2byte(srcByte & (1 << 2));
2096 d[6] = bit2byte(srcByte & (1 << 1));
2097 d[7] = bit2byte(srcByte & (1 << 0));
2098 d += 8;
2099 }
2100 if (leftOverBits) {
2101 int srcByte = src[wholeBytes];
2102 for (int x = 0; x < leftOverBits; ++x) {
2103 *d++ = bit2byte(srcByte & 0x80);
2104 srcByte <<= 1;
2105 }
2106 }
2107 src += srcRB;
2108 dst += dstRB;
2109 }
2110}
2111
2112void SkAAClipBlitter::blitMask(const SkMask& origMask, const SkIRect& clip) {
2113 SkASSERT(fAAClip->getBounds().contains(clip));
2114
2115 if (fAAClip->quickContains(clip)) {
2116 fBlitter->blitMask(origMask, clip);
2117 return;
2118 }
2119
2120 const SkMask* mask = &origMask;
2121
2122 // if we're BW, we need to upscale to A8 (ugh)
2123 SkMask grayMask;
2124 grayMask.fImage = NULL;
2125 if (SkMask::kBW_Format == origMask.fFormat) {
2126 grayMask.fFormat = SkMask::kA8_Format;
2127 grayMask.fBounds = origMask.fBounds;
2128 grayMask.fRowBytes = origMask.fBounds.width();
2129 size_t size = grayMask.computeImageSize();
2130 grayMask.fImage = (uint8_t*)fGrayMaskScratch.reset(size,
2131 SkAutoMalloc::kReuse_OnShrink);
2132
2133 upscaleBW2A8(&grayMask, origMask);
2134 mask = &grayMask;
2135 }
2136
2137 this->ensureRunsAndAA();
2138
2139 // HACK -- we are devolving 3D into A8, need to copy the rest of the 3D
2140 // data into a temp block to support it better (ugh)
2141
2142 const void* src = mask->getAddr(clip.fLeft, clip.fTop);
2143 const size_t srcRB = mask->fRowBytes;
2144 const int width = clip.width();
2145 MergeAAProc mergeProc = find_merge_aa_proc(mask->fFormat);
2146
2147 SkMask rowMask;
2148 rowMask.fFormat = SkMask::k3D_Format == mask->fFormat ? SkMask::kA8_Format : mask->fFormat;
2149 rowMask.fBounds.fLeft = clip.fLeft;
2150 rowMask.fBounds.fRight = clip.fRight;
2151 rowMask.fRowBytes = mask->fRowBytes; // doesn't matter, since our height==1
2152 rowMask.fImage = (uint8_t*)fScanlineScratch;
2153
2154 int y = clip.fTop;
2155 const int stopY = y + clip.height();
2156
2157 do {
reed@google.coma4c6e4d2012-06-20 14:29:50 +00002158 int localStopY SK_INIT_TO_AVOID_WARNING;
reed@google.com045e62d2011-10-24 12:19:46 +00002159 const uint8_t* row = fAAClip->findRow(y, &localStopY);
2160 // findRow returns last Y, not stop, so we add 1
2161 localStopY = SkMin32(localStopY + 1, stopY);
2162
2163 int initialCount;
2164 row = fAAClip->findX(row, clip.fLeft, &initialCount);
2165 do {
2166 mergeProc(src, width, row, initialCount, rowMask.fImage);
2167 rowMask.fBounds.fTop = y;
2168 rowMask.fBounds.fBottom = y + 1;
2169 fBlitter->blitMask(rowMask, rowMask.fBounds);
2170 src = (const void*)((const char*)src + srcRB);
2171 } while (++y < localStopY);
2172 } while (y < stopY);
reed@google.come36707a2011-10-04 21:38:55 +00002173}
2174
2175const SkBitmap* SkAAClipBlitter::justAnOpaqueColor(uint32_t* value) {
2176 return NULL;
2177}