blob: c4b15b6e2c8c37ea9cde2e947ae3482176743ba3 [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;
60
61 YOffset* yoffsets() {
62 return (YOffset*)((char*)this + sizeof(RunHead));
63 }
64 const YOffset* yoffsets() const {
65 return (const YOffset*)((const char*)this + sizeof(RunHead));
66 }
67 uint8_t* data() {
68 return (uint8_t*)(this->yoffsets() + fRowCount);
69 }
70 const uint8_t* data() const {
71 return (const uint8_t*)(this->yoffsets() + fRowCount);
72 }
73
74 static RunHead* Alloc(int rowCount, size_t dataSize) {
75 size_t size = sizeof(RunHead) + rowCount * sizeof(YOffset) + dataSize;
76 RunHead* head = (RunHead*)sk_malloc_throw(size);
77 head->fRefCnt = 1;
78 head->fRowCount = rowCount;
79 head->fDataSize = dataSize;
80 return head;
81 }
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 }
141
142 const RunHead* head = clip.fRunHead;
143 fCurrYOff = head->yoffsets();
144 fStopYOff = fCurrYOff + head->fRowCount;
145 fData = head->data() + fCurrYOff->fOffset;
146
147 // setup first value
148 fTop = clip.fBounds.fTop;
149 fBottom = clip.fBounds.fTop + fCurrYOff->fY + 1;
150 fDone = false;
151}
152
153void SkAAClip::Iter::next() {
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
reed@google.comc9041912011-10-27 16:58:46 +0000224static void count_left_right_zeros(const uint8_t* row, int width,
225 int* leftZ, int* riteZ) {
226 int zeros = 0;
227 do {
228 if (row[1]) {
229 break;
230 }
231 int n = row[0];
232 SkASSERT(n > 0);
233 SkASSERT(n <= width);
234 zeros += n;
235 row += 2;
236 width -= n;
237 } while (width > 0);
238 *leftZ = zeros;
239
240 zeros = 0;
241 while (width > 0) {
242 int n = row[0];
243 SkASSERT(n > 0);
244 if (0 == row[1]) {
245 zeros += n;
246 } else {
247 zeros = 0;
248 }
249 row += 2;
250 width -= n;
251 }
252 *riteZ = zeros;
253}
254
255#ifdef SK_DEBUG
256static void test_count_left_right_zeros() {
257 static bool gOnce;
258 if (gOnce) {
259 return;
260 }
261 gOnce = true;
262
263 const uint8_t data0[] = { 0, 0, 10, 0xFF };
264 const uint8_t data1[] = { 0, 0, 5, 0xFF, 2, 0, 3, 0xFF };
265 const uint8_t data2[] = { 7, 0, 5, 0, 2, 0, 3, 0xFF };
266 const uint8_t data3[] = { 0, 5, 5, 0xFF, 2, 0, 3, 0 };
267 const uint8_t data4[] = { 2, 3, 2, 0, 5, 0xFF, 3, 0 };
268 const uint8_t data5[] = { 10, 0, 10, 0 };
269 const uint8_t data6[] = { 2, 2, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
270
271 const uint8_t* array[] = {
272 data0, data1, data2, data3, data4, data5, data6
273 };
274
275 for (size_t i = 0; i < SK_ARRAY_COUNT(array); ++i) {
276 const uint8_t* data = array[i];
277 const int expectedL = *data++;
278 const int expectedR = *data++;
279 int L = 12345, R = 12345;
280 count_left_right_zeros(data, 10, &L, &R);
281 SkASSERT(expectedL == L);
282 SkASSERT(expectedR == R);
283 }
284}
285#endif
286
287// modify row in place, trimming off (zeros) from the left and right sides.
288// return the number of bytes that were completely eliminated from the left
289static int trim_row_left_right(uint8_t* row, int width, int leftZ, int riteZ) {
290 int trim = 0;
291 while (leftZ > 0) {
292 SkASSERT(0 == row[1]);
293 int n = row[0];
294 SkASSERT(n > 0);
295 SkASSERT(n <= width);
296 width -= n;
297 row += 2;
298 if (n > leftZ) {
299 row[-2] = n - leftZ;
300 break;
301 }
302 trim += 2;
303 leftZ -= n;
304 SkASSERT(leftZ >= 0);
305 }
306
307 if (riteZ) {
308 // walk row to the end, and then we'll back up to trim riteZ
309 while (width > 0) {
310 int n = row[0];
311 SkASSERT(n <= width);
312 width -= n;
313 row += 2;
314 }
315 // now skip whole runs of zeros
316 do {
317 row -= 2;
318 SkASSERT(0 == row[1]);
319 int n = row[0];
320 SkASSERT(n > 0);
321 if (n > riteZ) {
322 row[0] = n - riteZ;
323 break;
324 }
325 riteZ -= n;
326 SkASSERT(riteZ >= 0);
327 } while (riteZ > 0);
328 }
329
330 return trim;
331}
332
333#ifdef SK_DEBUG
334// assert that this row is exactly this width
reed@google.comc5507bf2011-10-27 21:15:36 +0000335static void assert_row_width(const uint8_t* row, int width) {
reed@google.comc9041912011-10-27 16:58:46 +0000336 while (width > 0) {
337 int n = row[0];
338 SkASSERT(n > 0);
339 SkASSERT(n <= width);
340 width -= n;
341 row += 2;
342 }
343 SkASSERT(0 == width);
344}
345
346static void test_trim_row_left_right() {
347 static bool gOnce;
348 if (gOnce) {
349 return;
350 }
351 gOnce = true;
352
353 uint8_t data0[] = { 0, 0, 0, 10, 10, 0xFF };
354 uint8_t data1[] = { 2, 0, 0, 10, 5, 0, 2, 0, 3, 0xFF };
355 uint8_t data2[] = { 5, 0, 2, 10, 5, 0, 2, 0, 3, 0xFF };
356 uint8_t data3[] = { 6, 0, 2, 10, 5, 0, 2, 0, 3, 0xFF };
357 uint8_t data4[] = { 0, 0, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
358 uint8_t data5[] = { 1, 0, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
359 uint8_t data6[] = { 0, 1, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
360 uint8_t data7[] = { 1, 1, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
361 uint8_t data8[] = { 2, 2, 2, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
362 uint8_t data9[] = { 5, 2, 4, 10, 2, 0, 2, 0, 2, 0, 2, 0xFF, 2, 0 };
363 uint8_t data10[] ={ 74, 0, 4, 150, 9, 0, 65, 0, 76, 0xFF };
364
365 uint8_t* array[] = {
366 data0, data1, data2, data3, data4,
367 data5, data6, data7, data8, data9,
368 data10
369 };
370
371 for (size_t i = 0; i < SK_ARRAY_COUNT(array); ++i) {
372 uint8_t* data = array[i];
373 const int trimL = *data++;
374 const int trimR = *data++;
375 const int expectedSkip = *data++;
376 const int origWidth = *data++;
377 assert_row_width(data, origWidth);
378 int skip = trim_row_left_right(data, origWidth, trimL, trimR);
379 SkASSERT(expectedSkip == skip);
380 int expectedWidth = origWidth - trimL - trimR;
381 assert_row_width(data + skip, expectedWidth);
382 }
383}
384#endif
385
386bool SkAAClip::trimLeftRight() {
387 SkDEBUGCODE(test_trim_row_left_right();)
388
389 if (this->isEmpty()) {
390 return false;
391 }
392
393 AUTO_AACLIP_VALIDATE(*this);
394
395 const int width = fBounds.width();
396 RunHead* head = fRunHead;
397 YOffset* yoff = head->yoffsets();
398 YOffset* stop = yoff + head->fRowCount;
399 uint8_t* base = head->data();
400
401 int leftZeros = width;
402 int riteZeros = width;
403 while (yoff < stop) {
404 int L, R;
405 count_left_right_zeros(base + yoff->fOffset, width, &L, &R);
406 if (L < leftZeros) {
407 leftZeros = L;
408 }
409 if (R < riteZeros) {
410 riteZeros = R;
411 }
412 if (0 == (leftZeros | riteZeros)) {
413 // no trimming to do
414 return true;
415 }
416 yoff += 1;
417 }
418
419 SkASSERT(leftZeros || riteZeros);
420 if (width == (leftZeros + riteZeros)) {
421 return this->setEmpty();
422 }
423
424 this->validate();
425
426 fBounds.fLeft += leftZeros;
427 fBounds.fRight -= riteZeros;
428 SkASSERT(!fBounds.isEmpty());
429
430 // For now we don't realloc the storage (for time), we just shrink in place
431 // This means we don't have to do any memmoves either, since we can just
432 // play tricks with the yoff->fOffset for each row
433 yoff = head->yoffsets();
434 while (yoff < stop) {
435 uint8_t* row = base + yoff->fOffset;
436 SkDEBUGCODE((void)compute_row_length(row, width);)
437 yoff->fOffset += trim_row_left_right(row, width, leftZeros, riteZeros);
438 SkDEBUGCODE((void)compute_row_length(base + yoff->fOffset, width - leftZeros - riteZeros);)
439 yoff += 1;
440 }
441 return true;
442}
443
444static bool row_is_all_zeros(const uint8_t* row, int width) {
445 SkASSERT(width > 0);
446 do {
447 if (row[1]) {
448 return false;
449 }
450 int n = row[0];
451 SkASSERT(n <= width);
452 width -= n;
453 row += 2;
454 } while (width > 0);
455 SkASSERT(0 == width);
456 return true;
457}
458
459bool SkAAClip::trimTopBottom() {
460 if (this->isEmpty()) {
461 return false;
462 }
463
reed@google.comd6040f62011-10-28 02:39:17 +0000464 this->validate();
465
reed@google.comc9041912011-10-27 16:58:46 +0000466 const int width = fBounds.width();
467 RunHead* head = fRunHead;
468 YOffset* yoff = head->yoffsets();
469 YOffset* stop = yoff + head->fRowCount;
470 const uint8_t* base = head->data();
471
472 // Look to trim away empty rows from the top.
473 //
474 int skip = 0;
475 while (yoff < stop) {
476 const uint8_t* data = base + yoff->fOffset;
477 if (!row_is_all_zeros(data, width)) {
478 break;
479 }
480 skip += 1;
481 yoff += 1;
482 }
483 SkASSERT(skip <= head->fRowCount);
484 if (skip == head->fRowCount) {
485 return this->setEmpty();
486 }
487 if (skip > 0) {
488 // adjust fRowCount and fBounds.fTop, and slide all the data up
489 // as we remove [skip] number of YOffset entries
490 yoff = head->yoffsets();
491 int dy = yoff[skip - 1].fY + 1;
492 for (int i = skip; i < head->fRowCount; ++i) {
493 SkASSERT(yoff[i].fY >= dy);
494 yoff[i].fY -= dy;
495 }
496 YOffset* dst = head->yoffsets();
497 size_t size = head->fRowCount * sizeof(YOffset) + head->fDataSize;
498 memmove(dst, dst + skip, size - skip * sizeof(YOffset));
499
500 fBounds.fTop += dy;
501 SkASSERT(!fBounds.isEmpty());
502 head->fRowCount -= skip;
503 SkASSERT(head->fRowCount > 0);
reed@google.comd6040f62011-10-28 02:39:17 +0000504
505 this->validate();
506 // need to reset this after the memmove
507 base = head->data();
reed@google.comc9041912011-10-27 16:58:46 +0000508 }
509
510 // Look to trim away empty rows from the bottom.
511 // We know that we have at least one non-zero row, so we can just walk
512 // backwards without checking for running past the start.
513 //
514 stop = yoff = head->yoffsets() + head->fRowCount;
515 do {
516 yoff -= 1;
517 } while (row_is_all_zeros(base + yoff->fOffset, width));
518 skip = stop - yoff - 1;
519 SkASSERT(skip >= 0 && skip < head->fRowCount);
520 if (skip > 0) {
521 // removing from the bottom is easier than from the top, as we don't
522 // have to adjust any of the Y values, we just have to trim the array
523 memmove(stop - skip, stop, head->fDataSize);
524
525 fBounds.fBottom = fBounds.fTop + yoff->fY + 1;
526 SkASSERT(!fBounds.isEmpty());
527 head->fRowCount -= skip;
528 SkASSERT(head->fRowCount > 0);
529 }
reed@google.comd6040f62011-10-28 02:39:17 +0000530 this->validate();
reed@google.comc9041912011-10-27 16:58:46 +0000531
532 return true;
533}
534
reed@google.com045e62d2011-10-24 12:19:46 +0000535// can't validate before we're done, since trimming is part of the process of
536// making us valid after the Builder. Since we build from top to bottom, its
537// possible our fBounds.fBottom is bigger than our last scanline of data, so
538// we trim fBounds.fBottom back up.
539//
reed@google.com045e62d2011-10-24 12:19:46 +0000540// TODO: check for duplicates in X and Y to further compress our data
541//
542bool SkAAClip::trimBounds() {
543 if (this->isEmpty()) {
544 return false;
545 }
546
547 const RunHead* head = fRunHead;
548 const YOffset* yoff = head->yoffsets();
549
550 SkASSERT(head->fRowCount > 0);
551 const YOffset& lastY = yoff[head->fRowCount - 1];
552 SkASSERT(lastY.fY + 1 <= fBounds.height());
553 fBounds.fBottom = fBounds.fTop + lastY.fY + 1;
554 SkASSERT(lastY.fY + 1 == fBounds.height());
reed@google.comc9041912011-10-27 16:58:46 +0000555 SkASSERT(!fBounds.isEmpty());
556
557 return this->trimTopBottom() && this->trimLeftRight();
reed@google.com045e62d2011-10-24 12:19:46 +0000558}
559
reed@google.come36707a2011-10-04 21:38:55 +0000560///////////////////////////////////////////////////////////////////////////////
561
562void SkAAClip::freeRuns() {
reed@google.com47ac84e2011-10-06 13:11:25 +0000563 if (fRunHead) {
reed@google.come36707a2011-10-04 21:38:55 +0000564 SkASSERT(fRunHead->fRefCnt >= 1);
565 if (1 == sk_atomic_dec(&fRunHead->fRefCnt)) {
566 sk_free(fRunHead);
567 }
568 }
569}
570
571SkAAClip::SkAAClip() {
572 fBounds.setEmpty();
reed@google.com47ac84e2011-10-06 13:11:25 +0000573 fRunHead = NULL;
reed@google.come36707a2011-10-04 21:38:55 +0000574}
575
576SkAAClip::SkAAClip(const SkAAClip& src) {
reed@google.com045e62d2011-10-24 12:19:46 +0000577 SkDEBUGCODE(fBounds.setEmpty();) // need this for validate
reed@google.com47ac84e2011-10-06 13:11:25 +0000578 fRunHead = NULL;
reed@google.come36707a2011-10-04 21:38:55 +0000579 *this = src;
580}
581
582SkAAClip::~SkAAClip() {
583 this->freeRuns();
584}
585
586SkAAClip& SkAAClip::operator=(const SkAAClip& src) {
reed@google.com045e62d2011-10-24 12:19:46 +0000587 AUTO_AACLIP_VALIDATE(*this);
588 src.validate();
589
reed@google.come36707a2011-10-04 21:38:55 +0000590 if (this != &src) {
591 this->freeRuns();
592 fBounds = src.fBounds;
593 fRunHead = src.fRunHead;
reed@google.com47ac84e2011-10-06 13:11:25 +0000594 if (fRunHead) {
reed@google.come36707a2011-10-04 21:38:55 +0000595 sk_atomic_inc(&fRunHead->fRefCnt);
596 }
597 }
598 return *this;
599}
600
601bool operator==(const SkAAClip& a, const SkAAClip& b) {
reed@google.com045e62d2011-10-24 12:19:46 +0000602 a.validate();
603 b.validate();
604
reed@google.come36707a2011-10-04 21:38:55 +0000605 if (&a == &b) {
606 return true;
607 }
608 if (a.fBounds != b.fBounds) {
609 return false;
610 }
611
612 const SkAAClip::RunHead* ah = a.fRunHead;
613 const SkAAClip::RunHead* bh = b.fRunHead;
614
615 // this catches empties and rects being equal
616 if (ah == bh) {
617 return true;
618 }
619
620 // now we insist that both are complex (but different ptrs)
reed@google.com47ac84e2011-10-06 13:11:25 +0000621 if (!a.fRunHead || !b.fRunHead) {
reed@google.come36707a2011-10-04 21:38:55 +0000622 return false;
623 }
624
625 return ah->fRowCount == bh->fRowCount &&
626 ah->fDataSize == bh->fDataSize &&
627 !memcmp(ah->data(), bh->data(), ah->fDataSize);
628}
629
630void SkAAClip::swap(SkAAClip& other) {
reed@google.com045e62d2011-10-24 12:19:46 +0000631 AUTO_AACLIP_VALIDATE(*this);
632 other.validate();
633
reed@google.come36707a2011-10-04 21:38:55 +0000634 SkTSwap(fBounds, other.fBounds);
635 SkTSwap(fRunHead, other.fRunHead);
636}
637
reed@google.com32287892011-10-05 16:27:44 +0000638bool SkAAClip::set(const SkAAClip& src) {
639 *this = src;
640 return !this->isEmpty();
641}
642
reed@google.come36707a2011-10-04 21:38:55 +0000643bool SkAAClip::setEmpty() {
644 this->freeRuns();
645 fBounds.setEmpty();
reed@google.com47ac84e2011-10-06 13:11:25 +0000646 fRunHead = NULL;
reed@google.come36707a2011-10-04 21:38:55 +0000647 return false;
648}
649
650bool SkAAClip::setRect(const SkIRect& bounds) {
651 if (bounds.isEmpty()) {
652 return this->setEmpty();
653 }
reed@google.com47ac84e2011-10-06 13:11:25 +0000654
reed@google.com045e62d2011-10-24 12:19:46 +0000655 AUTO_AACLIP_VALIDATE(*this);
reed@google.com47ac84e2011-10-06 13:11:25 +0000656
reed@google.com045e62d2011-10-24 12:19:46 +0000657#if 0
reed@google.com47ac84e2011-10-06 13:11:25 +0000658 SkRect r;
659 r.set(bounds);
660 SkPath path;
661 path.addRect(r);
662 return this->setPath(path);
reed@google.com045e62d2011-10-24 12:19:46 +0000663#else
664 this->freeRuns();
665 fBounds = bounds;
666 fRunHead = RunHead::AllocRect(bounds);
667 SkASSERT(!this->isEmpty());
668 return true;
669#endif
reed@google.come36707a2011-10-04 21:38:55 +0000670}
671
reed@google.comf3c1da12011-10-10 19:35:47 +0000672bool SkAAClip::setRect(const SkRect& r, bool doAA) {
reed@google.come36707a2011-10-04 21:38:55 +0000673 if (r.isEmpty()) {
674 return this->setEmpty();
675 }
676
reed@google.com045e62d2011-10-24 12:19:46 +0000677 AUTO_AACLIP_VALIDATE(*this);
678
679 // TODO: special case this
680
reed@google.come36707a2011-10-04 21:38:55 +0000681 SkPath path;
682 path.addRect(r);
reed@google.comf3c1da12011-10-10 19:35:47 +0000683 return this->setPath(path, NULL, doAA);
684}
685
reed@google.coma069c8f2011-11-28 19:54:56 +0000686static void append_run(SkTDArray<uint8_t>& array, uint8_t value, int count) {
687 SkASSERT(count >= 0);
688 while (count > 0) {
689 int n = count;
690 if (n > 255) {
691 n = 255;
692 }
693 uint8_t* data = array.append(2);
694 data[0] = n;
695 data[1] = value;
696 count -= n;
697 }
698}
699
reed@google.comf3c1da12011-10-10 19:35:47 +0000700bool SkAAClip::setRegion(const SkRegion& rgn) {
701 if (rgn.isEmpty()) {
702 return this->setEmpty();
703 }
704 if (rgn.isRect()) {
705 return this->setRect(rgn.getBounds());
706 }
reed@google.coma069c8f2011-11-28 19:54:56 +0000707
708#if 0
reed@google.comf3c1da12011-10-10 19:35:47 +0000709 SkAAClip clip;
710 SkRegion::Iterator iter(rgn);
711 for (; !iter.done(); iter.next()) {
712 clip.op(iter.rect(), SkRegion::kUnion_Op);
713 }
714 this->swap(clip);
reed@google.com3771a032011-10-11 17:18:04 +0000715 return !this->isEmpty();
reed@google.coma069c8f2011-11-28 19:54:56 +0000716#else
717 const SkIRect& bounds = rgn.getBounds();
718 const int offsetX = bounds.fLeft;
719 const int offsetY = bounds.fTop;
720
721 SkTDArray<YOffset> yArray;
722 SkTDArray<uint8_t> xArray;
723
724 yArray.setReserve(SkMin32(bounds.height(), 1024));
725 xArray.setReserve(SkMin32(bounds.width() * 128, 64 * 1024));
726
727 SkRegion::Iterator iter(rgn);
728 int prevRight = 0;
729 int prevBot = 0;
730 YOffset* currY = NULL;
731
732 for (; !iter.done(); iter.next()) {
733 const SkIRect& r = iter.rect();
734 SkASSERT(bounds.contains(r));
735
736 int bot = r.fBottom - offsetY;
737 SkASSERT(bot >= prevBot);
738 if (bot > prevBot) {
739 if (currY) {
740 // flush current row
741 append_run(xArray, 0, bounds.width() - prevRight);
742 }
743 // did we introduce an empty-gap from the prev row?
744 int top = r.fTop - offsetY;
745 if (top > prevBot) {
746 currY = yArray.append();
747 currY->fY = top - 1;
748 currY->fOffset = xArray.count();
749 append_run(xArray, 0, bounds.width());
750 }
751 // create a new record for this Y value
752 currY = yArray.append();
753 currY->fY = bot - 1;
754 currY->fOffset = xArray.count();
755 prevRight = 0;
756 prevBot = bot;
757 }
758
759 int x = r.fLeft - offsetX;
760 append_run(xArray, 0, x - prevRight);
761
762 int w = r.fRight - r.fLeft;
763 append_run(xArray, 0xFF, w);
764 prevRight = x + w;
765 SkASSERT(prevRight <= bounds.width());
766 }
767 // flush last row
768 append_run(xArray, 0, bounds.width() - prevRight);
769
770 // now pack everything into a RunHead
771 RunHead* head = RunHead::Alloc(yArray.count(), xArray.bytes());
772 memcpy(head->yoffsets(), yArray.begin(), yArray.bytes());
773 memcpy(head->data(), xArray.begin(), xArray.bytes());
774
775 this->setEmpty();
776 fBounds = bounds;
777 fRunHead = head;
778 this->validate();
779 return true;
780#endif
reed@google.come36707a2011-10-04 21:38:55 +0000781}
782
783///////////////////////////////////////////////////////////////////////////////
784
785const uint8_t* SkAAClip::findRow(int y, int* lastYForRow) const {
reed@google.com47ac84e2011-10-06 13:11:25 +0000786 SkASSERT(fRunHead);
reed@google.come36707a2011-10-04 21:38:55 +0000787
788 if (!y_in_rect(y, fBounds)) {
789 return NULL;
790 }
791 y -= fBounds.y(); // our yoffs values are relative to the top
792
793 const YOffset* yoff = fRunHead->yoffsets();
794 while (yoff->fY < y) {
795 yoff += 1;
796 SkASSERT(yoff - fRunHead->yoffsets() < fRunHead->fRowCount);
797 }
798
799 if (lastYForRow) {
reed@google.com045e62d2011-10-24 12:19:46 +0000800 *lastYForRow = fBounds.y() + yoff->fY;
reed@google.come36707a2011-10-04 21:38:55 +0000801 }
802 return fRunHead->data() + yoff->fOffset;
803}
804
805const uint8_t* SkAAClip::findX(const uint8_t data[], int x, int* initialCount) const {
806 SkASSERT(x_in_rect(x, fBounds));
807 x -= fBounds.x();
808
809 // first skip up to X
810 for (;;) {
811 int n = data[0];
812 if (x < n) {
reed@google.coma4c6e4d2012-06-20 14:29:50 +0000813 if (initialCount) {
814 *initialCount = n - x;
815 }
reed@google.come36707a2011-10-04 21:38:55 +0000816 break;
817 }
818 data += 2;
819 x -= n;
820 }
821 return data;
822}
823
824bool SkAAClip::quickContains(int left, int top, int right, int bottom) const {
825 if (this->isEmpty()) {
826 return false;
827 }
828 if (!fBounds.contains(left, top, right, bottom)) {
829 return false;
830 }
reed@google.com32287892011-10-05 16:27:44 +0000831#if 0
reed@google.come36707a2011-10-04 21:38:55 +0000832 if (this->isRect()) {
833 return true;
834 }
reed@google.com32287892011-10-05 16:27:44 +0000835#endif
reed@google.come36707a2011-10-04 21:38:55 +0000836
reed@google.coma4c6e4d2012-06-20 14:29:50 +0000837 int lastY SK_INIT_TO_AVOID_WARNING;
reed@google.come36707a2011-10-04 21:38:55 +0000838 const uint8_t* row = this->findRow(top, &lastY);
839 if (lastY < bottom) {
840 return false;
841 }
842 // now just need to check in X
reed@google.com045e62d2011-10-24 12:19:46 +0000843 int count;
844 row = this->findX(row, left, &count);
845#if 0
846 return count >= (right - left) && 0xFF == row[1];
847#else
848 int rectWidth = right - left;
849 while (0xFF == row[1]) {
850 if (count >= rectWidth) {
851 return true;
852 }
853 rectWidth -= count;
854 row += 2;
855 count = row[0];
856 }
857 return false;
858#endif
reed@google.come36707a2011-10-04 21:38:55 +0000859}
860
861///////////////////////////////////////////////////////////////////////////////
862
863class SkAAClip::Builder {
864 SkIRect fBounds;
865 struct Row {
866 int fY;
867 int fWidth;
868 SkTDArray<uint8_t>* fData;
869 };
870 SkTDArray<Row> fRows;
871 Row* fCurrRow;
872 int fPrevY;
873 int fWidth;
reed@google.com209c4152011-10-26 15:03:48 +0000874 int fMinY;
reed@google.come36707a2011-10-04 21:38:55 +0000875
876public:
877 Builder(const SkIRect& bounds) : fBounds(bounds) {
878 fPrevY = -1;
879 fWidth = bounds.width();
880 fCurrRow = NULL;
reed@google.com209c4152011-10-26 15:03:48 +0000881 fMinY = bounds.fTop;
reed@google.come36707a2011-10-04 21:38:55 +0000882 }
883
884 ~Builder() {
885 Row* row = fRows.begin();
886 Row* stop = fRows.end();
887 while (row < stop) {
888 delete row->fData;
889 row += 1;
890 }
891 }
892
reed@google.com32287892011-10-05 16:27:44 +0000893 const SkIRect& getBounds() const { return fBounds; }
894
reed@google.come36707a2011-10-04 21:38:55 +0000895 void addRun(int x, int y, U8CPU alpha, int count) {
896 SkASSERT(count > 0);
897 SkASSERT(fBounds.contains(x, y));
898 SkASSERT(fBounds.contains(x + count - 1, y));
reed@google.com80cdb9a2012-02-16 18:56:17 +0000899
reed@google.come36707a2011-10-04 21:38:55 +0000900 x -= fBounds.left();
901 y -= fBounds.top();
reed@google.com80cdb9a2012-02-16 18:56:17 +0000902
reed@google.come36707a2011-10-04 21:38:55 +0000903 Row* row = fCurrRow;
904 if (y != fPrevY) {
905 SkASSERT(y > fPrevY);
906 fPrevY = y;
907 row = this->flushRow(true);
908 row->fY = y;
909 row->fWidth = 0;
910 SkASSERT(row->fData);
911 SkASSERT(0 == row->fData->count());
912 fCurrRow = row;
913 }
914
915 SkASSERT(row->fWidth <= x);
916 SkASSERT(row->fWidth < fBounds.width());
917
918 SkTDArray<uint8_t>& data = *row->fData;
919
920 int gap = x - row->fWidth;
921 if (gap) {
922 AppendRun(data, 0, gap);
923 row->fWidth += gap;
924 SkASSERT(row->fWidth < fBounds.width());
925 }
926
927 AppendRun(data, alpha, count);
928 row->fWidth += count;
929 SkASSERT(row->fWidth <= fBounds.width());
930 }
931
tomhudson@google.com49eac192011-12-27 13:59:20 +0000932 void addColumn(int x, int y, U8CPU alpha, int height) {
933 SkASSERT(fBounds.contains(x, y + height - 1));
934
935 this->addRun(x, y, alpha, 1);
936 this->flushRowH(fCurrRow);
937 y -= fBounds.fTop;
938 SkASSERT(y == fCurrRow->fY);
939 fCurrRow->fY = y + height - 1;
940 }
941
reed@google.com9154eb02011-10-31 16:07:28 +0000942 void addRectRun(int x, int y, int width, int height) {
943 SkASSERT(fBounds.contains(x + width - 1, y + height - 1));
944 this->addRun(x, y, 0xFF, width);
945
reed@google.coma89c77b2011-12-01 21:47:26 +0000946 // we assum the rect must be all we'll see for these scanlines
reed@google.com9154eb02011-10-31 16:07:28 +0000947 // so we ensure our row goes all the way to our right
948 this->flushRowH(fCurrRow);
949
950 y -= fBounds.fTop;
951 SkASSERT(y == fCurrRow->fY);
952 fCurrRow->fY = y + height - 1;
953 }
954
tomhudson@google.com49eac192011-12-27 13:59:20 +0000955 void addAntiRectRun(int x, int y, int width, int height,
956 SkAlpha leftAlpha, SkAlpha rightAlpha) {
957 SkASSERT(fBounds.contains(x + width - 1 +
958 (leftAlpha > 0 ? 1 : 0) + (rightAlpha > 0 ? 1 : 0),
959 y + height - 1));
960 SkASSERT(width >= 0);
961
962 // Conceptually we're always adding 3 runs, but we should
963 // merge or omit them if possible.
964 if (leftAlpha == 0xFF) {
965 width++;
966 } else if (leftAlpha > 0) {
967 this->addRun(x++, y, leftAlpha, 1);
968 }
969 if (rightAlpha == 0xFF) {
970 width++;
971 }
972 if (width > 0) {
973 this->addRun(x, y, 0xFF, width);
974 }
975 if (rightAlpha > 0 && rightAlpha < 255) {
976 this->addRun(x + width, y, rightAlpha, 1);
977 }
978
979 // we assume the rect must be all we'll see for these scanlines
980 // so we ensure our row goes all the way to our right
981 this->flushRowH(fCurrRow);
982
983 y -= fBounds.fTop;
984 SkASSERT(y == fCurrRow->fY);
985 fCurrRow->fY = y + height - 1;
986 }
987
reed@google.com045e62d2011-10-24 12:19:46 +0000988 bool finish(SkAAClip* target) {
reed@google.come36707a2011-10-04 21:38:55 +0000989 this->flushRow(false);
990
991 const Row* row = fRows.begin();
992 const Row* stop = fRows.end();
993
994 size_t dataSize = 0;
995 while (row < stop) {
996 dataSize += row->fData->count();
997 row += 1;
998 }
999
reed@google.com045e62d2011-10-24 12:19:46 +00001000 if (0 == dataSize) {
1001 return target->setEmpty();
1002 }
1003
reed@google.com209c4152011-10-26 15:03:48 +00001004 SkASSERT(fMinY >= fBounds.fTop);
1005 SkASSERT(fMinY < fBounds.fBottom);
1006 int adjustY = fMinY - fBounds.fTop;
1007 fBounds.fTop = fMinY;
1008
reed@google.come36707a2011-10-04 21:38:55 +00001009 RunHead* head = RunHead::Alloc(fRows.count(), dataSize);
1010 YOffset* yoffset = head->yoffsets();
1011 uint8_t* data = head->data();
1012 uint8_t* baseData = data;
1013
1014 row = fRows.begin();
reed@google.comc9041912011-10-27 16:58:46 +00001015 SkDEBUGCODE(int prevY = row->fY - 1;)
reed@google.come36707a2011-10-04 21:38:55 +00001016 while (row < stop) {
reed@google.comc9041912011-10-27 16:58:46 +00001017 SkASSERT(prevY < row->fY); // must be monotonic
1018 SkDEBUGCODE(prevY = row->fY);
1019
reed@google.com209c4152011-10-26 15:03:48 +00001020 yoffset->fY = row->fY - adjustY;
reed@google.come36707a2011-10-04 21:38:55 +00001021 yoffset->fOffset = data - baseData;
1022 yoffset += 1;
1023
1024 size_t n = row->fData->count();
1025 memcpy(data, row->fData->begin(), n);
reed@google.comc9041912011-10-27 16:58:46 +00001026#ifdef SK_DEBUG
tomhudson@google.comf74ad8c2011-11-09 22:15:08 +00001027 size_t bytesNeeded = compute_row_length(data, fBounds.width());
reed@google.comc9041912011-10-27 16:58:46 +00001028 SkASSERT(bytesNeeded == n);
1029#endif
reed@google.come36707a2011-10-04 21:38:55 +00001030 data += n;
1031
1032 row += 1;
1033 }
1034
reed@google.com045e62d2011-10-24 12:19:46 +00001035 target->freeRuns();
1036 target->fBounds = fBounds;
1037 target->fRunHead = head;
1038 return target->trimBounds();
reed@google.come36707a2011-10-04 21:38:55 +00001039 }
1040
1041 void dump() {
1042 this->validate();
1043 int y;
1044 for (y = 0; y < fRows.count(); ++y) {
1045 const Row& row = fRows[y];
1046 SkDebugf("Y:%3d W:%3d", row.fY, row.fWidth);
1047 const SkTDArray<uint8_t>& data = *row.fData;
1048 int count = data.count();
1049 SkASSERT(!(count & 1));
1050 const uint8_t* ptr = data.begin();
1051 for (int x = 0; x < count; x += 2) {
1052 SkDebugf(" [%3d:%02X]", ptr[0], ptr[1]);
1053 ptr += 2;
1054 }
1055 SkDebugf("\n");
1056 }
reed@google.come36707a2011-10-04 21:38:55 +00001057 }
1058
1059 void validate() {
1060#ifdef SK_DEBUG
caryclark@google.com803eceb2012-06-06 12:09:34 +00001061 if (false) { // avoid bit rot, suppress warning
1062 test_count_left_right_zeros();
1063 }
reed@google.come36707a2011-10-04 21:38:55 +00001064 int prevY = -1;
1065 for (int i = 0; i < fRows.count(); ++i) {
1066 const Row& row = fRows[i];
1067 SkASSERT(prevY < row.fY);
1068 SkASSERT(fWidth == row.fWidth);
1069 int count = row.fData->count();
1070 const uint8_t* ptr = row.fData->begin();
1071 SkASSERT(!(count & 1));
1072 int w = 0;
1073 for (int x = 0; x < count; x += 2) {
reed@google.comd6040f62011-10-28 02:39:17 +00001074 int n = ptr[0];
1075 SkASSERT(n > 0);
1076 w += n;
reed@google.come36707a2011-10-04 21:38:55 +00001077 SkASSERT(w <= fWidth);
1078 ptr += 2;
1079 }
1080 SkASSERT(w == fWidth);
1081 prevY = row.fY;
1082 }
1083#endif
1084 }
1085
reed@google.com209c4152011-10-26 15:03:48 +00001086 // only called by BuilderBlitter
1087 void setMinY(int y) {
1088 fMinY = y;
1089 }
1090
reed@google.come36707a2011-10-04 21:38:55 +00001091private:
reed@google.com9154eb02011-10-31 16:07:28 +00001092 void flushRowH(Row* row) {
1093 // flush current row if needed
1094 if (row->fWidth < fWidth) {
1095 AppendRun(*row->fData, 0, fWidth - row->fWidth);
1096 row->fWidth = fWidth;
1097 }
1098 }
reed@google.com209c4152011-10-26 15:03:48 +00001099
reed@google.come36707a2011-10-04 21:38:55 +00001100 Row* flushRow(bool readyForAnother) {
1101 Row* next = NULL;
1102 int count = fRows.count();
1103 if (count > 0) {
reed@google.com9154eb02011-10-31 16:07:28 +00001104 this->flushRowH(&fRows[count - 1]);
reed@google.come36707a2011-10-04 21:38:55 +00001105 }
1106 if (count > 1) {
1107 // are our last two runs the same?
1108 Row* prev = &fRows[count - 2];
1109 Row* curr = &fRows[count - 1];
1110 SkASSERT(prev->fWidth == fWidth);
1111 SkASSERT(curr->fWidth == fWidth);
1112 if (*prev->fData == *curr->fData) {
1113 prev->fY = curr->fY;
1114 if (readyForAnother) {
1115 curr->fData->rewind();
1116 next = curr;
1117 } else {
1118 delete curr->fData;
1119 fRows.removeShuffle(count - 1);
1120 }
1121 } else {
1122 if (readyForAnother) {
1123 next = fRows.append();
1124 next->fData = new SkTDArray<uint8_t>;
1125 }
1126 }
1127 } else {
1128 if (readyForAnother) {
1129 next = fRows.append();
1130 next->fData = new SkTDArray<uint8_t>;
1131 }
1132 }
1133 return next;
1134 }
1135
1136 static void AppendRun(SkTDArray<uint8_t>& data, U8CPU alpha, int count) {
1137 do {
1138 int n = count;
1139 if (n > 255) {
1140 n = 255;
1141 }
1142 uint8_t* ptr = data.append(2);
1143 ptr[0] = n;
1144 ptr[1] = alpha;
1145 count -= n;
1146 } while (count > 0);
1147 }
1148};
1149
1150class SkAAClip::BuilderBlitter : public SkBlitter {
reed@google.com80cdb9a2012-02-16 18:56:17 +00001151 int fLastY;
1152
1153 /*
1154 If we see a gap of 1 or more empty scanlines while building in Y-order,
1155 we inject an explicit empty scanline (alpha==0)
1156
1157 See AAClipTest.cpp : test_path_with_hole()
1158 */
1159 void checkForYGap(int y) {
1160 SkASSERT(y >= fLastY);
1161 if (fLastY > -SK_MaxS32) {
1162 int gap = y - fLastY;
1163 if (gap > 1) {
1164 fBuilder->addRun(fLeft, y - 1, 0, fRight - fLeft);
1165 }
1166 }
1167 fLastY = y;
1168 }
1169
reed@google.come36707a2011-10-04 21:38:55 +00001170public:
reed@google.com80cdb9a2012-02-16 18:56:17 +00001171
reed@google.come36707a2011-10-04 21:38:55 +00001172 BuilderBlitter(Builder* builder) {
1173 fBuilder = builder;
reed@google.com17785642011-10-12 20:23:55 +00001174 fLeft = builder->getBounds().fLeft;
1175 fRight = builder->getBounds().fRight;
reed@google.com209c4152011-10-26 15:03:48 +00001176 fMinY = SK_MaxS32;
reed@google.com80cdb9a2012-02-16 18:56:17 +00001177 fLastY = -SK_MaxS32; // sentinel
reed@google.com209c4152011-10-26 15:03:48 +00001178 }
1179
1180 void finish() {
1181 if (fMinY < SK_MaxS32) {
1182 fBuilder->setMinY(fMinY);
1183 }
reed@google.come36707a2011-10-04 21:38:55 +00001184 }
1185
tomhudson@google.com49eac192011-12-27 13:59:20 +00001186 /**
1187 Must evaluate clips in scan-line order, so don't want to allow blitV(),
1188 but an AAClip can be clipped down to a single pixel wide, so we
1189 must support it (given AntiRect semantics: minimum width is 2).
1190 Instead we'll rely on the runtime asserts to guarantee Y monotonicity;
1191 any failure cases that misses may have minor artifacts.
1192 */
1193 virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE {
1194 this->recordMinY(y);
1195 fBuilder->addColumn(x, y, alpha, height);
reed@google.com9b0da232012-02-29 13:59:15 +00001196 fLastY = y + height - 1;
tomhudson@google.com49eac192011-12-27 13:59:20 +00001197 }
reed@google.com045e62d2011-10-24 12:19:46 +00001198
reed@google.com562a2ac2011-10-31 14:14:18 +00001199 virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE {
reed@google.com9154eb02011-10-31 16:07:28 +00001200 this->recordMinY(y);
reed@google.com80cdb9a2012-02-16 18:56:17 +00001201 this->checkForYGap(y);
reed@google.com9154eb02011-10-31 16:07:28 +00001202 fBuilder->addRectRun(x, y, width, height);
reed@google.com302b8612012-02-16 19:30:13 +00001203 fLastY = y + height - 1;
reed@google.com562a2ac2011-10-31 14:14:18 +00001204 }
reed@google.com045e62d2011-10-24 12:19:46 +00001205
tomhudson@google.com49eac192011-12-27 13:59:20 +00001206 virtual void blitAntiRect(int x, int y, int width, int height,
1207 SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE {
1208 this->recordMinY(y);
reed@google.com302b8612012-02-16 19:30:13 +00001209 this->checkForYGap(y);
tomhudson@google.com49eac192011-12-27 13:59:20 +00001210 fBuilder->addAntiRectRun(x, y, width, height, leftAlpha, rightAlpha);
reed@google.com302b8612012-02-16 19:30:13 +00001211 fLastY = y + height - 1;
tomhudson@google.com49eac192011-12-27 13:59:20 +00001212 }
1213
reed@google.come36707a2011-10-04 21:38:55 +00001214 virtual void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE
1215 { unexpected(); }
1216
1217 virtual const SkBitmap* justAnOpaqueColor(uint32_t*) SK_OVERRIDE {
reed@google.com3771a032011-10-11 17:18:04 +00001218 return NULL;
reed@google.come36707a2011-10-04 21:38:55 +00001219 }
1220
1221 virtual void blitH(int x, int y, int width) SK_OVERRIDE {
reed@google.com209c4152011-10-26 15:03:48 +00001222 this->recordMinY(y);
reed@google.com80cdb9a2012-02-16 18:56:17 +00001223 this->checkForYGap(y);
reed@google.come36707a2011-10-04 21:38:55 +00001224 fBuilder->addRun(x, y, 0xFF, width);
1225 }
1226
1227 virtual void blitAntiH(int x, int y, const SkAlpha alpha[],
1228 const int16_t runs[]) SK_OVERRIDE {
reed@google.com209c4152011-10-26 15:03:48 +00001229 this->recordMinY(y);
reed@google.com80cdb9a2012-02-16 18:56:17 +00001230 this->checkForYGap(y);
reed@google.come36707a2011-10-04 21:38:55 +00001231 for (;;) {
1232 int count = *runs;
1233 if (count <= 0) {
1234 return;
1235 }
reed@google.com17785642011-10-12 20:23:55 +00001236
1237 // The supersampler's buffer can be the width of the device, so
1238 // we may have to trim the run to our bounds. If so, we assert that
1239 // the extra spans are always alpha==0
1240 int localX = x;
1241 int localCount = count;
1242 if (x < fLeft) {
1243 SkASSERT(0 == *alpha);
1244 int gap = fLeft - x;
1245 SkASSERT(gap <= count);
1246 localX += gap;
1247 localCount -= gap;
1248 }
1249 int right = x + count;
1250 if (right > fRight) {
1251 SkASSERT(0 == *alpha);
1252 localCount -= right - fRight;
1253 SkASSERT(localCount >= 0);
1254 }
1255
1256 if (localCount) {
1257 fBuilder->addRun(localX, y, *alpha, localCount);
1258 }
bsalomon@google.com820e80a2011-10-24 21:09:40 +00001259 // Next run
reed@google.come36707a2011-10-04 21:38:55 +00001260 runs += count;
1261 alpha += count;
1262 x += count;
1263 }
1264 }
1265
1266private:
1267 Builder* fBuilder;
reed@google.com17785642011-10-12 20:23:55 +00001268 int fLeft; // cache of builder's bounds' left edge
1269 int fRight;
reed@google.com209c4152011-10-26 15:03:48 +00001270 int fMinY;
1271
1272 /*
1273 * We track this, in case the scan converter skipped some number of
1274 * scanlines at the (relative to the bounds it was given). This allows
1275 * the builder, during its finish, to trip its bounds down to the "real"
1276 * top.
1277 */
1278 void recordMinY(int y) {
1279 if (y < fMinY) {
1280 fMinY = y;
1281 }
1282 }
reed@google.come36707a2011-10-04 21:38:55 +00001283
1284 void unexpected() {
1285 SkDebugf("---- did not expect to get called here");
1286 sk_throw();
1287 }
1288};
1289
reed@google.comf3c1da12011-10-10 19:35:47 +00001290bool SkAAClip::setPath(const SkPath& path, const SkRegion* clip, bool doAA) {
reed@google.com045e62d2011-10-24 12:19:46 +00001291 AUTO_AACLIP_VALIDATE(*this);
1292
reed@google.com32287892011-10-05 16:27:44 +00001293 if (clip && clip->isEmpty()) {
reed@google.come36707a2011-10-04 21:38:55 +00001294 return this->setEmpty();
1295 }
1296
1297 SkIRect ibounds;
reed@google.com32287892011-10-05 16:27:44 +00001298 path.getBounds().roundOut(&ibounds);
reed@google.come36707a2011-10-04 21:38:55 +00001299
reed@google.com32287892011-10-05 16:27:44 +00001300 SkRegion tmpClip;
1301 if (NULL == clip) {
1302 tmpClip.setRect(ibounds);
1303 clip = &tmpClip;
1304 }
1305
reed@google.com045e62d2011-10-24 12:19:46 +00001306 if (path.isInverseFillType()) {
1307 ibounds = clip->getBounds();
1308 } else {
reed@google.com32287892011-10-05 16:27:44 +00001309 if (ibounds.isEmpty() || !ibounds.intersect(clip->getBounds())) {
reed@google.come36707a2011-10-04 21:38:55 +00001310 return this->setEmpty();
1311 }
reed@google.come36707a2011-10-04 21:38:55 +00001312 }
1313
1314 Builder builder(ibounds);
1315 BuilderBlitter blitter(&builder);
1316
reed@google.comf3c1da12011-10-10 19:35:47 +00001317 if (doAA) {
1318 SkScan::AntiFillPath(path, *clip, &blitter, true);
1319 } else {
1320 SkScan::FillPath(path, *clip, &blitter);
1321 }
reed@google.come36707a2011-10-04 21:38:55 +00001322
reed@google.com209c4152011-10-26 15:03:48 +00001323 blitter.finish();
reed@google.com045e62d2011-10-24 12:19:46 +00001324 return builder.finish(this);
reed@google.come36707a2011-10-04 21:38:55 +00001325}
1326
1327///////////////////////////////////////////////////////////////////////////////
1328
reed@google.com32287892011-10-05 16:27:44 +00001329typedef void (*RowProc)(SkAAClip::Builder&, int bottom,
1330 const uint8_t* rowA, const SkIRect& rectA,
1331 const uint8_t* rowB, const SkIRect& rectB);
1332
reed@google.com32287892011-10-05 16:27:44 +00001333typedef U8CPU (*AlphaProc)(U8CPU alphaA, U8CPU alphaB);
1334
1335static U8CPU sectAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1336 // Multiply
1337 return SkMulDiv255Round(alphaA, alphaB);
1338}
1339
1340static U8CPU unionAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1341 // SrcOver
1342 return alphaA + alphaB - SkMulDiv255Round(alphaA, alphaB);
1343}
1344
1345static U8CPU diffAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1346 // SrcOut
1347 return SkMulDiv255Round(alphaA, 0xFF - alphaB);
1348}
1349
1350static U8CPU xorAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1351 // XOR
1352 return alphaA + alphaB - 2 * SkMulDiv255Round(alphaA, alphaB);
1353}
1354
1355static AlphaProc find_alpha_proc(SkRegion::Op op) {
1356 switch (op) {
1357 case SkRegion::kIntersect_Op:
1358 return sectAlphaProc;
1359 case SkRegion::kDifference_Op:
1360 return diffAlphaProc;
1361 case SkRegion::kUnion_Op:
1362 return unionAlphaProc;
1363 case SkRegion::kXOR_Op:
1364 return xorAlphaProc;
1365 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001366 SkDEBUGFAIL("unexpected region op");
reed@google.com32287892011-10-05 16:27:44 +00001367 return sectAlphaProc;
1368 }
1369}
1370
1371static const uint8_t gEmptyRow[] = {
1372 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
1373 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
1374 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
1375 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
1376 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
1377 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
1378 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
1379 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
1380};
1381
1382class RowIter {
1383public:
1384 RowIter(const uint8_t* row, const SkIRect& bounds) {
1385 fRow = row;
1386 fLeft = bounds.fLeft;
reed@google.com32287892011-10-05 16:27:44 +00001387 fBoundsRight = bounds.fRight;
reed@google.com1c04bf92011-10-10 12:57:12 +00001388 if (row) {
1389 fRight = bounds.fLeft + row[0];
1390 SkASSERT(fRight <= fBoundsRight);
1391 fAlpha = row[1];
1392 fDone = false;
1393 } else {
1394 fDone = true;
1395 fRight = kMaxInt32;
1396 fAlpha = 0;
1397 }
reed@google.com32287892011-10-05 16:27:44 +00001398 }
1399
1400 bool done() const { return fDone; }
reed@google.com1c04bf92011-10-10 12:57:12 +00001401 int left() const { return fLeft; }
1402 int right() const { return fRight; }
1403 U8CPU alpha() const { return fAlpha; }
reed@google.com32287892011-10-05 16:27:44 +00001404 void next() {
reed@google.com1c04bf92011-10-10 12:57:12 +00001405 if (!fDone) {
reed@google.com32287892011-10-05 16:27:44 +00001406 fLeft = fRight;
reed@google.com1c04bf92011-10-10 12:57:12 +00001407 if (fRight == fBoundsRight) {
1408 fDone = true;
1409 fRight = kMaxInt32;
1410 fAlpha = 0;
1411 } else {
1412 fRow += 2;
1413 fRight += fRow[0];
1414 fAlpha = fRow[1];
1415 SkASSERT(fRight <= fBoundsRight);
1416 }
reed@google.com32287892011-10-05 16:27:44 +00001417 }
1418 }
1419
1420private:
1421 const uint8_t* fRow;
1422 int fLeft;
1423 int fRight;
1424 int fBoundsRight;
1425 bool fDone;
reed@google.com1c04bf92011-10-10 12:57:12 +00001426 uint8_t fAlpha;
reed@google.com32287892011-10-05 16:27:44 +00001427};
1428
reed@google.com1c04bf92011-10-10 12:57:12 +00001429static void adjust_row(RowIter& iter, int& leftA, int& riteA, int rite) {
1430 if (rite == riteA) {
1431 iter.next();
1432 leftA = iter.left();
1433 riteA = iter.right();
reed@google.com32287892011-10-05 16:27:44 +00001434 }
1435}
1436
caryclark@google.com803eceb2012-06-06 12:09:34 +00001437#if 0 // UNUSED
reed@google.com1c04bf92011-10-10 12:57:12 +00001438static bool intersect(int& min, int& max, int boundsMin, int boundsMax) {
1439 SkASSERT(min < max);
1440 SkASSERT(boundsMin < boundsMax);
1441 if (min >= boundsMax || max <= boundsMin) {
1442 return false;
1443 }
1444 if (min < boundsMin) {
1445 min = boundsMin;
1446 }
1447 if (max > boundsMax) {
1448 max = boundsMax;
1449 }
1450 return true;
1451}
caryclark@google.com803eceb2012-06-06 12:09:34 +00001452#endif
reed@google.com1c04bf92011-10-10 12:57:12 +00001453
reed@google.com32287892011-10-05 16:27:44 +00001454static void operatorX(SkAAClip::Builder& builder, int lastY,
1455 RowIter& iterA, RowIter& iterB,
1456 AlphaProc proc, const SkIRect& bounds) {
reed@google.com32287892011-10-05 16:27:44 +00001457 int leftA = iterA.left();
1458 int riteA = iterA.right();
reed@google.com32287892011-10-05 16:27:44 +00001459 int leftB = iterB.left();
1460 int riteB = iterB.right();
1461
reed@google.com1c04bf92011-10-10 12:57:12 +00001462 int prevRite = bounds.fLeft;
1463
1464 do {
reed@google.com32287892011-10-05 16:27:44 +00001465 U8CPU alphaA = 0;
1466 U8CPU alphaB = 0;
reed@google.com32287892011-10-05 16:27:44 +00001467 int left, rite;
reed@google.com1c04bf92011-10-10 12:57:12 +00001468
1469 if (leftA < leftB) {
reed@google.com32287892011-10-05 16:27:44 +00001470 left = leftA;
reed@google.com32287892011-10-05 16:27:44 +00001471 alphaA = iterA.alpha();
reed@google.com1c04bf92011-10-10 12:57:12 +00001472 if (riteA <= leftB) {
1473 rite = riteA;
1474 } else {
1475 rite = leftA = leftB;
reed@google.com32287892011-10-05 16:27:44 +00001476 }
reed@google.com1c04bf92011-10-10 12:57:12 +00001477 } else if (leftB < leftA) {
1478 left = leftB;
1479 alphaB = iterB.alpha();
1480 if (riteB <= leftA) {
1481 rite = riteB;
1482 } else {
1483 rite = leftB = leftA;
1484 }
1485 } else {
1486 left = leftA; // or leftB, since leftA == leftB
1487 rite = leftA = leftB = SkMin32(riteA, riteB);
1488 alphaA = iterA.alpha();
1489 alphaB = iterB.alpha();
reed@google.com32287892011-10-05 16:27:44 +00001490 }
1491
1492 if (left >= bounds.fRight) {
1493 break;
1494 }
reed@google.com34f7e472011-10-13 15:11:59 +00001495 if (rite > bounds.fRight) {
1496 rite = bounds.fRight;
1497 }
1498
reed@google.com32287892011-10-05 16:27:44 +00001499 if (left >= bounds.fLeft) {
reed@google.com1c04bf92011-10-10 12:57:12 +00001500 SkASSERT(rite > left);
reed@google.com32287892011-10-05 16:27:44 +00001501 builder.addRun(left, lastY, proc(alphaA, alphaB), rite - left);
reed@google.com1c04bf92011-10-10 12:57:12 +00001502 prevRite = rite;
reed@google.com32287892011-10-05 16:27:44 +00001503 }
reed@google.com1c04bf92011-10-10 12:57:12 +00001504
1505 adjust_row(iterA, leftA, riteA, rite);
1506 adjust_row(iterB, leftB, riteB, rite);
1507 } while (!iterA.done() || !iterB.done());
1508
1509 if (prevRite < bounds.fRight) {
1510 builder.addRun(prevRite, lastY, 0, bounds.fRight - prevRite);
reed@google.com32287892011-10-05 16:27:44 +00001511 }
1512}
1513
reed@google.com1c04bf92011-10-10 12:57:12 +00001514static void adjust_iter(SkAAClip::Iter& iter, int& topA, int& botA, int bot) {
1515 if (bot == botA) {
1516 iter.next();
1517 topA = botA;
1518 SkASSERT(botA == iter.top());
1519 botA = iter.bottom();
reed@google.com32287892011-10-05 16:27:44 +00001520 }
1521}
1522
1523static void operateY(SkAAClip::Builder& builder, const SkAAClip& A,
1524 const SkAAClip& B, SkRegion::Op op) {
1525 AlphaProc proc = find_alpha_proc(op);
1526 const SkIRect& bounds = builder.getBounds();
1527
1528 SkAAClip::Iter iterA(A);
1529 SkAAClip::Iter iterB(B);
1530
1531 SkASSERT(!iterA.done());
1532 int topA = iterA.top();
1533 int botA = iterA.bottom();
1534 SkASSERT(!iterB.done());
1535 int topB = iterB.top();
1536 int botB = iterB.bottom();
1537
reed@google.com1c04bf92011-10-10 12:57:12 +00001538 do {
1539 const uint8_t* rowA = NULL;
1540 const uint8_t* rowB = NULL;
reed@google.com32287892011-10-05 16:27:44 +00001541 int top, bot;
reed@google.com1c04bf92011-10-10 12:57:12 +00001542
1543 if (topA < topB) {
reed@google.com32287892011-10-05 16:27:44 +00001544 top = topA;
reed@google.com32287892011-10-05 16:27:44 +00001545 rowA = iterA.data();
reed@google.com1c04bf92011-10-10 12:57:12 +00001546 if (botA <= topB) {
1547 bot = botA;
1548 } else {
1549 bot = topA = topB;
reed@google.com32287892011-10-05 16:27:44 +00001550 }
reed@google.com1c04bf92011-10-10 12:57:12 +00001551
1552 } else if (topB < topA) {
1553 top = topB;
1554 rowB = iterB.data();
1555 if (botB <= topA) {
1556 bot = botB;
1557 } else {
1558 bot = topB = topA;
1559 }
1560 } else {
1561 top = topA; // or topB, since topA == topB
1562 bot = topA = topB = SkMin32(botA, botB);
1563 rowA = iterA.data();
1564 rowB = iterB.data();
reed@google.com32287892011-10-05 16:27:44 +00001565 }
1566
1567 if (top >= bounds.fBottom) {
1568 break;
1569 }
reed@google.com34f7e472011-10-13 15:11:59 +00001570
1571 if (bot > bounds.fBottom) {
1572 bot = bounds.fBottom;
1573 }
1574 SkASSERT(top < bot);
1575
reed@google.com1c04bf92011-10-10 12:57:12 +00001576 if (!rowA && !rowB) {
1577 builder.addRun(bounds.fLeft, bot - 1, 0, bounds.width());
1578 } else if (top >= bounds.fTop) {
1579 SkASSERT(bot <= bounds.fBottom);
1580 RowIter rowIterA(rowA, rowA ? A.getBounds() : bounds);
1581 RowIter rowIterB(rowB, rowB ? B.getBounds() : bounds);
reed@google.com32287892011-10-05 16:27:44 +00001582 operatorX(builder, bot - 1, rowIterA, rowIterB, proc, bounds);
reed@google.com32287892011-10-05 16:27:44 +00001583 }
1584
reed@google.com1c04bf92011-10-10 12:57:12 +00001585 adjust_iter(iterA, topA, botA, bot);
1586 adjust_iter(iterB, topB, botB, bot);
1587 } while (!iterA.done() || !iterB.done());
reed@google.com32287892011-10-05 16:27:44 +00001588}
1589
1590bool SkAAClip::op(const SkAAClip& clipAOrig, const SkAAClip& clipBOrig,
1591 SkRegion::Op op) {
reed@google.com045e62d2011-10-24 12:19:46 +00001592 AUTO_AACLIP_VALIDATE(*this);
1593
reed@google.com32287892011-10-05 16:27:44 +00001594 if (SkRegion::kReplace_Op == op) {
1595 return this->set(clipBOrig);
1596 }
1597
1598 const SkAAClip* clipA = &clipAOrig;
1599 const SkAAClip* clipB = &clipBOrig;
1600
1601 if (SkRegion::kReverseDifference_Op == op) {
1602 SkTSwap(clipA, clipB);
1603 op = SkRegion::kDifference_Op;
1604 }
1605
1606 bool a_empty = clipA->isEmpty();
1607 bool b_empty = clipB->isEmpty();
1608
1609 SkIRect bounds;
1610 switch (op) {
1611 case SkRegion::kDifference_Op:
1612 if (a_empty) {
1613 return this->setEmpty();
1614 }
1615 if (b_empty || !SkIRect::Intersects(clipA->fBounds, clipB->fBounds)) {
1616 return this->set(*clipA);
1617 }
1618 bounds = clipA->fBounds;
1619 break;
1620
1621 case SkRegion::kIntersect_Op:
1622 if ((a_empty | b_empty) || !bounds.intersect(clipA->fBounds,
1623 clipB->fBounds)) {
1624 return this->setEmpty();
1625 }
1626 break;
1627
1628 case SkRegion::kUnion_Op:
1629 case SkRegion::kXOR_Op:
1630 if (a_empty) {
1631 return this->set(*clipB);
1632 }
1633 if (b_empty) {
1634 return this->set(*clipA);
1635 }
1636 bounds = clipA->fBounds;
1637 bounds.join(clipB->fBounds);
1638 break;
1639
1640 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001641 SkDEBUGFAIL("unknown region op");
reed@google.com32287892011-10-05 16:27:44 +00001642 return !this->isEmpty();
1643 }
1644
reed@google.com32287892011-10-05 16:27:44 +00001645 SkASSERT(SkIRect::Intersects(bounds, clipB->fBounds));
1646 SkASSERT(SkIRect::Intersects(bounds, clipB->fBounds));
1647
1648 Builder builder(bounds);
1649 operateY(builder, *clipA, *clipB, op);
reed@google.com045e62d2011-10-24 12:19:46 +00001650
1651 return builder.finish(this);
reed@google.come36707a2011-10-04 21:38:55 +00001652}
1653
reed@google.com045e62d2011-10-24 12:19:46 +00001654/*
1655 * It can be expensive to build a local aaclip before applying the op, so
1656 * we first see if we can restrict the bounds of new rect to our current
1657 * bounds, or note that the new rect subsumes our current clip.
1658 */
1659
1660bool SkAAClip::op(const SkIRect& rOrig, SkRegion::Op op) {
1661 SkIRect rStorage;
1662 const SkIRect* r = &rOrig;
1663
1664 switch (op) {
1665 case SkRegion::kIntersect_Op:
1666 if (!rStorage.intersect(rOrig, fBounds)) {
1667 // no overlap, so we're empty
1668 return this->setEmpty();
1669 }
1670 if (rStorage == fBounds) {
1671 // we were wholly inside the rect, no change
1672 return !this->isEmpty();
1673 }
1674 if (this->quickContains(rStorage)) {
1675 // the intersection is wholly inside us, we're a rect
1676 return this->setRect(rStorage);
1677 }
1678 r = &rStorage; // use the intersected bounds
1679 break;
1680 case SkRegion::kDifference_Op:
1681 break;
1682 case SkRegion::kUnion_Op:
1683 if (rOrig.contains(fBounds)) {
1684 return this->setRect(rOrig);
1685 }
1686 break;
1687 default:
1688 break;
1689 }
1690
reed@google.com47ac84e2011-10-06 13:11:25 +00001691 SkAAClip clip;
reed@google.com045e62d2011-10-24 12:19:46 +00001692 clip.setRect(*r);
reed@google.com47ac84e2011-10-06 13:11:25 +00001693 return this->op(*this, clip, op);
1694}
1695
reed@google.com045e62d2011-10-24 12:19:46 +00001696bool SkAAClip::op(const SkRect& rOrig, SkRegion::Op op, bool doAA) {
1697 SkRect rStorage, boundsStorage;
1698 const SkRect* r = &rOrig;
1699
1700 boundsStorage.set(fBounds);
1701 switch (op) {
1702 case SkRegion::kIntersect_Op:
1703 case SkRegion::kDifference_Op:
1704 if (!rStorage.intersect(rOrig, boundsStorage)) {
1705 return this->setEmpty();
1706 }
1707 r = &rStorage; // use the intersected bounds
1708 break;
1709 case SkRegion::kUnion_Op:
1710 if (rOrig.contains(boundsStorage)) {
1711 return this->setRect(rOrig);
1712 }
1713 break;
1714 default:
1715 break;
1716 }
1717
reed@google.com47ac84e2011-10-06 13:11:25 +00001718 SkAAClip clip;
reed@google.com045e62d2011-10-24 12:19:46 +00001719 clip.setRect(*r, doAA);
reed@google.com47ac84e2011-10-06 13:11:25 +00001720 return this->op(*this, clip, op);
1721}
1722
1723bool SkAAClip::op(const SkAAClip& clip, SkRegion::Op op) {
1724 return this->op(*this, clip, op);
1725}
1726
reed@google.come36707a2011-10-04 21:38:55 +00001727///////////////////////////////////////////////////////////////////////////////
reed@google.com045e62d2011-10-24 12:19:46 +00001728
1729bool SkAAClip::translate(int dx, int dy, SkAAClip* dst) const {
1730 if (NULL == dst) {
1731 return !this->isEmpty();
1732 }
1733
1734 if (this->isEmpty()) {
1735 return dst->setEmpty();
1736 }
1737
1738 if (this != dst) {
1739 sk_atomic_inc(&fRunHead->fRefCnt);
tomhudson@google.com19224c32012-03-28 15:46:37 +00001740 dst->freeRuns();
reed@google.com045e62d2011-10-24 12:19:46 +00001741 dst->fRunHead = fRunHead;
1742 dst->fBounds = fBounds;
1743 }
1744 dst->fBounds.offset(dx, dy);
1745 return true;
1746}
1747
1748static void expand_row_to_mask(uint8_t* SK_RESTRICT mask,
1749 const uint8_t* SK_RESTRICT row,
1750 int width) {
1751 while (width > 0) {
1752 int n = row[0];
1753 SkASSERT(width >= n);
1754 memset(mask, row[1], n);
1755 mask += n;
1756 row += 2;
1757 width -= n;
1758 }
reed@google.coma069c8f2011-11-28 19:54:56 +00001759 SkASSERT(0 == width);
reed@google.com045e62d2011-10-24 12:19:46 +00001760}
1761
1762void SkAAClip::copyToMask(SkMask* mask) const {
1763 mask->fFormat = SkMask::kA8_Format;
1764 if (this->isEmpty()) {
1765 mask->fBounds.setEmpty();
1766 mask->fImage = NULL;
1767 mask->fRowBytes = 0;
1768 return;
1769 }
1770
1771 mask->fBounds = fBounds;
1772 mask->fRowBytes = fBounds.width();
1773 size_t size = mask->computeImageSize();
1774 mask->fImage = SkMask::AllocImage(size);
1775
1776 Iter iter(*this);
1777 uint8_t* dst = mask->fImage;
1778 const int width = fBounds.width();
1779
1780 int y = fBounds.fTop;
1781 while (!iter.done()) {
1782 do {
1783 expand_row_to_mask(dst, iter.data(), width);
1784 dst += mask->fRowBytes;
1785 } while (++y < iter.bottom());
1786 iter.next();
1787 }
1788}
1789
1790///////////////////////////////////////////////////////////////////////////////
reed@google.come36707a2011-10-04 21:38:55 +00001791///////////////////////////////////////////////////////////////////////////////
1792
1793static void expandToRuns(const uint8_t* SK_RESTRICT data, int initialCount, int width,
1794 int16_t* SK_RESTRICT runs, SkAlpha* SK_RESTRICT aa) {
1795 // we don't read our initial n from data, since the caller may have had to
1796 // clip it, hence the initialCount parameter.
1797 int n = initialCount;
1798 for (;;) {
1799 if (n > width) {
1800 n = width;
1801 }
1802 SkASSERT(n > 0);
1803 runs[0] = n;
1804 runs += n;
1805
1806 aa[0] = data[1];
1807 aa += n;
1808
1809 data += 2;
1810 width -= n;
1811 if (0 == width) {
1812 break;
1813 }
1814 // load the next count
1815 n = data[0];
1816 }
1817 runs[0] = 0; // sentinel
1818}
1819
1820SkAAClipBlitter::~SkAAClipBlitter() {
reed@google.com045e62d2011-10-24 12:19:46 +00001821 sk_free(fScanlineScratch);
reed@google.come36707a2011-10-04 21:38:55 +00001822}
1823
1824void SkAAClipBlitter::ensureRunsAndAA() {
reed@google.com045e62d2011-10-24 12:19:46 +00001825 if (NULL == fScanlineScratch) {
reed@google.come36707a2011-10-04 21:38:55 +00001826 // add 1 so we can store the terminating run count of 0
1827 int count = fAAClipBounds.width() + 1;
reed@google.com045e62d2011-10-24 12:19:46 +00001828 // we use this either for fRuns + fAA, or a scaline of a mask
1829 // which may be as deep as 32bits
1830 fScanlineScratch = sk_malloc_throw(count * sizeof(SkPMColor));
1831 fRuns = (int16_t*)fScanlineScratch;
reed@google.come36707a2011-10-04 21:38:55 +00001832 fAA = (SkAlpha*)(fRuns + count);
1833 }
1834}
1835
1836void SkAAClipBlitter::blitH(int x, int y, int width) {
1837 SkASSERT(width > 0);
1838 SkASSERT(fAAClipBounds.contains(x, y));
1839 SkASSERT(fAAClipBounds.contains(x + width - 1, y));
1840
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001841 const uint8_t* row = fAAClip->findRow(y);
reed@google.come36707a2011-10-04 21:38:55 +00001842 int initialCount;
1843 row = fAAClip->findX(row, x, &initialCount);
1844
1845 if (initialCount >= width) {
1846 SkAlpha alpha = row[1];
1847 if (0 == alpha) {
1848 return;
1849 }
1850 if (0xFF == alpha) {
1851 fBlitter->blitH(x, y, width);
1852 return;
1853 }
1854 }
1855
1856 this->ensureRunsAndAA();
1857 expandToRuns(row, initialCount, width, fRuns, fAA);
1858
1859 fBlitter->blitAntiH(x, y, fAA, fRuns);
1860}
1861
1862static void merge(const uint8_t* SK_RESTRICT row, int rowN,
1863 const SkAlpha* SK_RESTRICT srcAA,
1864 const int16_t* SK_RESTRICT srcRuns,
1865 SkAlpha* SK_RESTRICT dstAA,
1866 int16_t* SK_RESTRICT dstRuns,
1867 int width) {
1868 SkDEBUGCODE(int accumulated = 0;)
1869 int srcN = srcRuns[0];
reed@google.com045e62d2011-10-24 12:19:46 +00001870 // do we need this check?
1871 if (0 == srcN) {
1872 return;
1873 }
1874
reed@google.come36707a2011-10-04 21:38:55 +00001875 for (;;) {
reed@google.come36707a2011-10-04 21:38:55 +00001876 SkASSERT(rowN > 0);
1877 SkASSERT(srcN > 0);
1878
1879 unsigned newAlpha = SkMulDiv255Round(srcAA[0], row[1]);
1880 int minN = SkMin32(srcN, rowN);
1881 dstRuns[0] = minN;
1882 dstRuns += minN;
1883 dstAA[0] = newAlpha;
1884 dstAA += minN;
1885
1886 if (0 == (srcN -= minN)) {
1887 srcN = srcRuns[0]; // refresh
1888 srcRuns += srcN;
1889 srcAA += srcN;
1890 srcN = srcRuns[0]; // reload
reed@google.com045e62d2011-10-24 12:19:46 +00001891 if (0 == srcN) {
1892 break;
1893 }
reed@google.come36707a2011-10-04 21:38:55 +00001894 }
1895 if (0 == (rowN -= minN)) {
1896 row += 2;
1897 rowN = row[0]; // reload
1898 }
1899
1900 SkDEBUGCODE(accumulated += minN;)
1901 SkASSERT(accumulated <= width);
1902 }
reed@google.com34f7e472011-10-13 15:11:59 +00001903 dstRuns[0] = 0;
reed@google.come36707a2011-10-04 21:38:55 +00001904}
1905
1906void SkAAClipBlitter::blitAntiH(int x, int y, const SkAlpha aa[],
1907 const int16_t runs[]) {
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001908
1909 const uint8_t* row = fAAClip->findRow(y);
reed@google.come36707a2011-10-04 21:38:55 +00001910 int initialCount;
1911 row = fAAClip->findX(row, x, &initialCount);
1912
1913 this->ensureRunsAndAA();
1914
1915 merge(row, initialCount, aa, runs, fAA, fRuns, fAAClipBounds.width());
1916 fBlitter->blitAntiH(x, y, fAA, fRuns);
1917}
1918
1919void SkAAClipBlitter::blitV(int x, int y, int height, SkAlpha alpha) {
1920 if (fAAClip->quickContains(x, y, x + 1, y + height)) {
1921 fBlitter->blitV(x, y, height, alpha);
1922 return;
1923 }
1924
reed@google.com045e62d2011-10-24 12:19:46 +00001925 for (;;) {
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001926 int lastY SK_INIT_TO_AVOID_WARNING;
reed@google.come36707a2011-10-04 21:38:55 +00001927 const uint8_t* row = fAAClip->findRow(y, &lastY);
reed@google.com045e62d2011-10-24 12:19:46 +00001928 int dy = lastY - y + 1;
1929 if (dy > height) {
1930 dy = height;
1931 }
1932 height -= dy;
1933
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001934 row = fAAClip->findX(row, x);
reed@google.come36707a2011-10-04 21:38:55 +00001935 SkAlpha newAlpha = SkMulDiv255Round(alpha, row[1]);
1936 if (newAlpha) {
reed@google.com045e62d2011-10-24 12:19:46 +00001937 fBlitter->blitV(x, y, dy, newAlpha);
1938 }
1939 SkASSERT(height >= 0);
1940 if (height <= 0) {
1941 break;
reed@google.come36707a2011-10-04 21:38:55 +00001942 }
1943 y = lastY + 1;
reed@google.com045e62d2011-10-24 12:19:46 +00001944 }
reed@google.come36707a2011-10-04 21:38:55 +00001945}
1946
1947void SkAAClipBlitter::blitRect(int x, int y, int width, int height) {
1948 if (fAAClip->quickContains(x, y, x + width, y + height)) {
1949 fBlitter->blitRect(x, y, width, height);
1950 return;
1951 }
1952
1953 while (--height >= 0) {
1954 this->blitH(x, y, width);
1955 y += 1;
1956 }
1957}
1958
reed@google.com045e62d2011-10-24 12:19:46 +00001959typedef void (*MergeAAProc)(const void* src, int width, const uint8_t* row,
1960 int initialRowCount, void* dst);
1961
1962static void small_memcpy(void* dst, const void* src, size_t n) {
1963 memcpy(dst, src, n);
1964}
1965
1966static void small_bzero(void* dst, size_t n) {
1967 sk_bzero(dst, n);
1968}
1969
1970static inline uint8_t mergeOne(uint8_t value, unsigned alpha) {
1971 return SkMulDiv255Round(value, alpha);
1972}
1973static inline uint16_t mergeOne(uint16_t value, unsigned alpha) {
1974 unsigned r = SkGetPackedR16(value);
1975 unsigned g = SkGetPackedG16(value);
1976 unsigned b = SkGetPackedB16(value);
1977 return SkPackRGB16(SkMulDiv255Round(r, alpha),
caryclark@google.com803eceb2012-06-06 12:09:34 +00001978 SkMulDiv255Round(g, alpha),
1979 SkMulDiv255Round(b, alpha));
reed@google.com045e62d2011-10-24 12:19:46 +00001980}
1981static inline SkPMColor mergeOne(SkPMColor value, unsigned alpha) {
1982 unsigned a = SkGetPackedA32(value);
1983 unsigned r = SkGetPackedR32(value);
1984 unsigned g = SkGetPackedG32(value);
1985 unsigned b = SkGetPackedB32(value);
1986 return SkPackARGB32(SkMulDiv255Round(a, alpha),
1987 SkMulDiv255Round(r, alpha),
1988 SkMulDiv255Round(g, alpha),
1989 SkMulDiv255Round(b, alpha));
1990}
1991
1992template <typename T> void mergeT(const T* SK_RESTRICT src, int srcN,
1993 const uint8_t* SK_RESTRICT row, int rowN,
1994 T* SK_RESTRICT dst) {
reed@google.com045e62d2011-10-24 12:19:46 +00001995 for (;;) {
1996 SkASSERT(rowN > 0);
1997 SkASSERT(srcN > 0);
1998
1999 int n = SkMin32(rowN, srcN);
2000 unsigned rowA = row[1];
2001 if (0xFF == rowA) {
2002 small_memcpy(dst, src, n * sizeof(T));
2003 } else if (0 == rowA) {
2004 small_bzero(dst, n * sizeof(T));
2005 } else {
2006 for (int i = 0; i < n; ++i) {
2007 dst[i] = mergeOne(src[i], rowA);
2008 }
2009 }
2010
2011 if (0 == (srcN -= n)) {
2012 break;
2013 }
2014
2015 src += n;
2016 dst += n;
2017
2018 SkASSERT(rowN == n);
2019 row += 2;
2020 rowN = row[0];
2021 }
2022}
2023
2024static MergeAAProc find_merge_aa_proc(SkMask::Format format) {
2025 switch (format) {
2026 case SkMask::kBW_Format:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002027 SkDEBUGFAIL("unsupported");
reed@google.com045e62d2011-10-24 12:19:46 +00002028 return NULL;
2029 case SkMask::kA8_Format:
2030 case SkMask::k3D_Format: {
2031 void (*proc8)(const uint8_t*, int, const uint8_t*, int, uint8_t*) = mergeT;
2032 return (MergeAAProc)proc8;
2033 }
2034 case SkMask::kLCD16_Format: {
2035 void (*proc16)(const uint16_t*, int, const uint8_t*, int, uint16_t*) = mergeT;
2036 return (MergeAAProc)proc16;
2037 }
2038 case SkMask::kLCD32_Format: {
2039 void (*proc32)(const SkPMColor*, int, const uint8_t*, int, SkPMColor*) = mergeT;
2040 return (MergeAAProc)proc32;
2041 }
2042 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002043 SkDEBUGFAIL("unsupported");
reed@google.com045e62d2011-10-24 12:19:46 +00002044 return NULL;
2045 }
2046}
2047
2048static U8CPU bit2byte(int bitInAByte) {
2049 SkASSERT(bitInAByte <= 0xFF);
2050 // negation turns any non-zero into 0xFFFFFF??, so we just shift down
2051 // some value >= 8 to get a full FF value
2052 return -bitInAByte >> 8;
2053}
2054
2055static void upscaleBW2A8(SkMask* dstMask, const SkMask& srcMask) {
2056 SkASSERT(SkMask::kBW_Format == srcMask.fFormat);
2057 SkASSERT(SkMask::kA8_Format == dstMask->fFormat);
2058
2059 const int width = srcMask.fBounds.width();
2060 const int height = srcMask.fBounds.height();
2061
2062 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcMask.fImage;
2063 const size_t srcRB = srcMask.fRowBytes;
2064 uint8_t* SK_RESTRICT dst = (uint8_t*)dstMask->fImage;
2065 const size_t dstRB = dstMask->fRowBytes;
2066
2067 const int wholeBytes = width >> 3;
2068 const int leftOverBits = width & 7;
2069
2070 for (int y = 0; y < height; ++y) {
2071 uint8_t* SK_RESTRICT d = dst;
2072 for (int i = 0; i < wholeBytes; ++i) {
2073 int srcByte = src[i];
2074 d[0] = bit2byte(srcByte & (1 << 7));
2075 d[1] = bit2byte(srcByte & (1 << 6));
2076 d[2] = bit2byte(srcByte & (1 << 5));
2077 d[3] = bit2byte(srcByte & (1 << 4));
2078 d[4] = bit2byte(srcByte & (1 << 3));
2079 d[5] = bit2byte(srcByte & (1 << 2));
2080 d[6] = bit2byte(srcByte & (1 << 1));
2081 d[7] = bit2byte(srcByte & (1 << 0));
2082 d += 8;
2083 }
2084 if (leftOverBits) {
2085 int srcByte = src[wholeBytes];
2086 for (int x = 0; x < leftOverBits; ++x) {
2087 *d++ = bit2byte(srcByte & 0x80);
2088 srcByte <<= 1;
2089 }
2090 }
2091 src += srcRB;
2092 dst += dstRB;
2093 }
2094}
2095
2096void SkAAClipBlitter::blitMask(const SkMask& origMask, const SkIRect& clip) {
2097 SkASSERT(fAAClip->getBounds().contains(clip));
2098
2099 if (fAAClip->quickContains(clip)) {
2100 fBlitter->blitMask(origMask, clip);
2101 return;
2102 }
2103
2104 const SkMask* mask = &origMask;
2105
2106 // if we're BW, we need to upscale to A8 (ugh)
2107 SkMask grayMask;
2108 grayMask.fImage = NULL;
2109 if (SkMask::kBW_Format == origMask.fFormat) {
2110 grayMask.fFormat = SkMask::kA8_Format;
2111 grayMask.fBounds = origMask.fBounds;
2112 grayMask.fRowBytes = origMask.fBounds.width();
2113 size_t size = grayMask.computeImageSize();
2114 grayMask.fImage = (uint8_t*)fGrayMaskScratch.reset(size,
2115 SkAutoMalloc::kReuse_OnShrink);
2116
2117 upscaleBW2A8(&grayMask, origMask);
2118 mask = &grayMask;
2119 }
2120
2121 this->ensureRunsAndAA();
2122
2123 // HACK -- we are devolving 3D into A8, need to copy the rest of the 3D
2124 // data into a temp block to support it better (ugh)
2125
2126 const void* src = mask->getAddr(clip.fLeft, clip.fTop);
2127 const size_t srcRB = mask->fRowBytes;
2128 const int width = clip.width();
2129 MergeAAProc mergeProc = find_merge_aa_proc(mask->fFormat);
2130
2131 SkMask rowMask;
2132 rowMask.fFormat = SkMask::k3D_Format == mask->fFormat ? SkMask::kA8_Format : mask->fFormat;
2133 rowMask.fBounds.fLeft = clip.fLeft;
2134 rowMask.fBounds.fRight = clip.fRight;
2135 rowMask.fRowBytes = mask->fRowBytes; // doesn't matter, since our height==1
2136 rowMask.fImage = (uint8_t*)fScanlineScratch;
2137
2138 int y = clip.fTop;
2139 const int stopY = y + clip.height();
2140
2141 do {
reed@google.coma4c6e4d2012-06-20 14:29:50 +00002142 int localStopY SK_INIT_TO_AVOID_WARNING;
reed@google.com045e62d2011-10-24 12:19:46 +00002143 const uint8_t* row = fAAClip->findRow(y, &localStopY);
2144 // findRow returns last Y, not stop, so we add 1
2145 localStopY = SkMin32(localStopY + 1, stopY);
2146
2147 int initialCount;
2148 row = fAAClip->findX(row, clip.fLeft, &initialCount);
2149 do {
2150 mergeProc(src, width, row, initialCount, rowMask.fImage);
2151 rowMask.fBounds.fTop = y;
2152 rowMask.fBounds.fBottom = y + 1;
2153 fBlitter->blitMask(rowMask, rowMask.fBounds);
2154 src = (const void*)((const char*)src + srcRB);
2155 } while (++y < localStopY);
2156 } while (y < stopY);
reed@google.come36707a2011-10-04 21:38:55 +00002157}
2158
2159const SkBitmap* SkAAClipBlitter::justAnOpaqueColor(uint32_t* value) {
2160 return NULL;
2161}
2162