blob: 1a60754d91370a1888a6b99842bf77de24b88580 [file] [log] [blame]
reed@google.come36707a2011-10-04 21:38:55 +00001/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkAAClip.h"
herbb906daf2015-09-29 09:37:59 -07009#include "SkAtomics.h"
reed@google.come36707a2011-10-04 21:38:55 +000010#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"
reed@google.com34f7e472011-10-13 15:11:59 +000014#include "SkUtils.h"
reed@google.come36707a2011-10-04 21:38:55 +000015
reed@google.com045e62d2011-10-24 12:19:46 +000016class AutoAAClipValidate {
17public:
18 AutoAAClipValidate(const SkAAClip& clip) : fClip(clip) {
19 fClip.validate();
20 }
21 ~AutoAAClipValidate() {
22 fClip.validate();
23 }
24private:
25 const SkAAClip& fClip;
26};
27
28#ifdef SK_DEBUG
29 #define AUTO_AACLIP_VALIDATE(clip) AutoAAClipValidate acv(clip)
30#else
31 #define AUTO_AACLIP_VALIDATE(clip)
32#endif
33
34///////////////////////////////////////////////////////////////////////////////
35
reed@google.com1c04bf92011-10-10 12:57:12 +000036#define kMaxInt32 0x7FFFFFFF
37
commit-bot@chromium.org4b7d6732013-10-21 16:41:00 +000038#ifdef SK_DEBUG
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}
commit-bot@chromium.org4b7d6732013-10-21 16:41:00 +000042#endif
reed@google.come36707a2011-10-04 21:38:55 +000043
44static inline bool y_in_rect(int y, const SkIRect& rect) {
45 return (unsigned)(y - rect.fTop) < (unsigned)rect.height();
46}
47
48/*
49 * Data runs are packed [count, alpha]
50 */
51
52struct SkAAClip::YOffset {
53 int32_t fY;
54 uint32_t fOffset;
55};
56
57struct SkAAClip::RunHead {
58 int32_t fRefCnt;
59 int32_t fRowCount;
scroggo@google.com493c65f2013-02-05 18:49:00 +000060 size_t fDataSize;
rmistry@google.comfbfcd562012-08-23 18:09:54 +000061
reed@google.come36707a2011-10-04 21:38:55 +000062 YOffset* yoffsets() {
63 return (YOffset*)((char*)this + sizeof(RunHead));
64 }
65 const YOffset* yoffsets() const {
66 return (const YOffset*)((const char*)this + sizeof(RunHead));
67 }
68 uint8_t* data() {
69 return (uint8_t*)(this->yoffsets() + fRowCount);
70 }
71 const uint8_t* data() const {
72 return (const uint8_t*)(this->yoffsets() + fRowCount);
73 }
74
75 static RunHead* Alloc(int rowCount, size_t dataSize) {
76 size_t size = sizeof(RunHead) + rowCount * sizeof(YOffset) + dataSize;
77 RunHead* head = (RunHead*)sk_malloc_throw(size);
78 head->fRefCnt = 1;
79 head->fRowCount = rowCount;
80 head->fDataSize = dataSize;
81 return head;
82 }
reed@google.com045e62d2011-10-24 12:19:46 +000083
84 static int ComputeRowSizeForWidth(int width) {
85 // 2 bytes per segment, where each segment can store up to 255 for count
86 int segments = 0;
87 while (width > 0) {
88 segments += 1;
89 int n = SkMin32(width, 255);
90 width -= n;
91 }
92 return segments * 2; // each segment is row[0] + row[1] (n + alpha)
93 }
94
95 static RunHead* AllocRect(const SkIRect& bounds) {
96 SkASSERT(!bounds.isEmpty());
97 int width = bounds.width();
98 size_t rowSize = ComputeRowSizeForWidth(width);
99 RunHead* head = RunHead::Alloc(1, rowSize);
100 YOffset* yoff = head->yoffsets();
101 yoff->fY = bounds.height() - 1;
102 yoff->fOffset = 0;
103 uint8_t* row = head->data();
104 while (width > 0) {
105 int n = SkMin32(width, 255);
106 row[0] = n;
107 row[1] = 0xFF;
108 width -= n;
109 row += 2;
110 }
111 return head;
112 }
reed@google.come36707a2011-10-04 21:38:55 +0000113};
114
reed@google.com32287892011-10-05 16:27:44 +0000115class SkAAClip::Iter {
116public:
117 Iter(const SkAAClip&);
118
119 bool done() const { return fDone; }
reed@google.com1c04bf92011-10-10 12:57:12 +0000120 int top() const { return fTop; }
121 int bottom() const { return fBottom; }
122 const uint8_t* data() const { return fData; }
reed@google.com32287892011-10-05 16:27:44 +0000123 void next();
124
125private:
126 const YOffset* fCurrYOff;
127 const YOffset* fStopYOff;
128 const uint8_t* fData;
129
130 int fTop, fBottom;
131 bool fDone;
132};
133
134SkAAClip::Iter::Iter(const SkAAClip& clip) {
135 if (clip.isEmpty()) {
136 fDone = true;
reed@google.com1c04bf92011-10-10 12:57:12 +0000137 fTop = fBottom = clip.fBounds.fBottom;
halcanary96fcdcc2015-08-27 07:41:13 -0700138 fData = nullptr;
139 fCurrYOff = nullptr;
140 fStopYOff = nullptr;
reed@google.com32287892011-10-05 16:27:44 +0000141 return;
142 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000143
reed@google.com32287892011-10-05 16:27:44 +0000144 const RunHead* head = clip.fRunHead;
145 fCurrYOff = head->yoffsets();
146 fStopYOff = fCurrYOff + head->fRowCount;
147 fData = head->data() + fCurrYOff->fOffset;
148
149 // setup first value
150 fTop = clip.fBounds.fTop;
151 fBottom = clip.fBounds.fTop + fCurrYOff->fY + 1;
152 fDone = false;
153}
154
155void SkAAClip::Iter::next() {
reed@google.com1c04bf92011-10-10 12:57:12 +0000156 if (!fDone) {
157 const YOffset* prev = fCurrYOff;
158 const YOffset* curr = prev + 1;
159 SkASSERT(curr <= fStopYOff);
reed@google.com32287892011-10-05 16:27:44 +0000160
reed@google.com32287892011-10-05 16:27:44 +0000161 fTop = fBottom;
reed@google.com1c04bf92011-10-10 12:57:12 +0000162 if (curr >= fStopYOff) {
163 fDone = true;
164 fBottom = kMaxInt32;
halcanary96fcdcc2015-08-27 07:41:13 -0700165 fData = nullptr;
reed@google.com1c04bf92011-10-10 12:57:12 +0000166 } else {
167 fBottom += curr->fY - prev->fY;
168 fData += curr->fOffset - prev->fOffset;
169 fCurrYOff = curr;
170 }
reed@google.com32287892011-10-05 16:27:44 +0000171 }
172}
173
reed@google.com045e62d2011-10-24 12:19:46 +0000174#ifdef SK_DEBUG
reed@google.comc9041912011-10-27 16:58:46 +0000175// assert we're exactly width-wide, and then return the number of bytes used
reed@google.com045e62d2011-10-24 12:19:46 +0000176static size_t compute_row_length(const uint8_t row[], int width) {
177 const uint8_t* origRow = row;
178 while (width > 0) {
179 int n = row[0];
reed@google.comc9041912011-10-27 16:58:46 +0000180 SkASSERT(n > 0);
reed@google.com045e62d2011-10-24 12:19:46 +0000181 SkASSERT(n <= width);
182 row += 2;
183 width -= n;
184 }
185 SkASSERT(0 == width);
186 return row - origRow;
187}
188
189void SkAAClip::validate() const {
halcanary96fcdcc2015-08-27 07:41:13 -0700190 if (nullptr == fRunHead) {
reed@google.com045e62d2011-10-24 12:19:46 +0000191 SkASSERT(fBounds.isEmpty());
192 return;
193 }
reedd7ec12e2016-06-20 10:21:24 -0700194 SkASSERT(!fBounds.isEmpty());
reed@google.com045e62d2011-10-24 12:19:46 +0000195
196 const RunHead* head = fRunHead;
197 SkASSERT(head->fRefCnt > 0);
198 SkASSERT(head->fRowCount > 0);
reed@google.com045e62d2011-10-24 12:19:46 +0000199
200 const YOffset* yoff = head->yoffsets();
201 const YOffset* ystop = yoff + head->fRowCount;
reed@google.comc9041912011-10-27 16:58:46 +0000202 const int lastY = fBounds.height() - 1;
203
204 // Y and offset must be monotonic
205 int prevY = -1;
206 int32_t prevOffset = -1;
reed@google.com045e62d2011-10-24 12:19:46 +0000207 while (yoff < ystop) {
reed@google.comc9041912011-10-27 16:58:46 +0000208 SkASSERT(prevY < yoff->fY);
209 SkASSERT(yoff->fY <= lastY);
210 prevY = yoff->fY;
211 SkASSERT(prevOffset < (int32_t)yoff->fOffset);
212 prevOffset = yoff->fOffset;
213 const uint8_t* row = head->data() + yoff->fOffset;
reed@google.com045e62d2011-10-24 12:19:46 +0000214 size_t rowLength = compute_row_length(row, fBounds.width());
scroggo@google.com493c65f2013-02-05 18:49:00 +0000215 SkASSERT(yoff->fOffset + rowLength <= head->fDataSize);
reed@google.comc9041912011-10-27 16:58:46 +0000216 yoff += 1;
reed@google.com045e62d2011-10-24 12:19:46 +0000217 }
reed@google.com045e62d2011-10-24 12:19:46 +0000218 // check the last entry;
219 --yoff;
reed@google.comc9041912011-10-27 16:58:46 +0000220 SkASSERT(yoff->fY == lastY);
reed@google.com045e62d2011-10-24 12:19:46 +0000221}
humper6d42d9c2014-08-08 11:45:46 -0700222
223static void dump_one_row(const uint8_t* SK_RESTRICT row,
224 int width, int leading_num) {
225 if (leading_num) {
226 SkDebugf( "%03d ", leading_num );
227 }
228 while (width > 0) {
229 int n = row[0];
230 int val = row[1];
231 char out = '.';
232 if (val == 0xff) {
233 out = '*';
234 } else if (val > 0) {
235 out = '+';
236 }
237 for (int i = 0 ; i < n ; i++) {
238 SkDebugf( "%c", out );
239 }
240 row += 2;
241 width -= n;
242 }
243 SkDebugf( "\n" );
244}
245
246void SkAAClip::debug(bool compress_y) const {
247 Iter iter(*this);
248 const int width = fBounds.width();
249
250 int y = fBounds.fTop;
251 while (!iter.done()) {
252 if (compress_y) {
253 dump_one_row(iter.data(), width, iter.bottom() - iter.top() + 1);
254 } else {
255 do {
256 dump_one_row(iter.data(), width, 0);
257 } while (++y < iter.bottom());
258 }
259 iter.next();
260 }
261}
reed@google.com045e62d2011-10-24 12:19:46 +0000262#endif
263
264///////////////////////////////////////////////////////////////////////////////
265
robertphillips@google.com768fee82012-08-02 12:42:43 +0000266// Count the number of zeros on the left and right edges of the passed in
267// RLE row. If 'row' is all zeros return 'width' in both variables.
reed@google.comc9041912011-10-27 16:58:46 +0000268static void count_left_right_zeros(const uint8_t* row, int width,
269 int* leftZ, int* riteZ) {
270 int zeros = 0;
271 do {
272 if (row[1]) {
273 break;
274 }
275 int n = row[0];
276 SkASSERT(n > 0);
277 SkASSERT(n <= width);
278 zeros += n;
279 row += 2;
280 width -= n;
281 } while (width > 0);
282 *leftZ = zeros;
283
robertphillips@google.com768fee82012-08-02 12:42:43 +0000284 if (0 == width) {
285 // this line is completely empty return 'width' in both variables
286 *riteZ = *leftZ;
287 return;
288 }
289
reed@google.comc9041912011-10-27 16:58:46 +0000290 zeros = 0;
291 while (width > 0) {
292 int n = row[0];
293 SkASSERT(n > 0);
294 if (0 == row[1]) {
295 zeros += n;
296 } else {
297 zeros = 0;
298 }
299 row += 2;
300 width -= n;
301 }
302 *riteZ = zeros;
303}
304
305#ifdef SK_DEBUG
306static void test_count_left_right_zeros() {
307 static bool gOnce;
308 if (gOnce) {
309 return;
310 }
311 gOnce = true;
312
313 const uint8_t data0[] = { 0, 0, 10, 0xFF };
314 const uint8_t data1[] = { 0, 0, 5, 0xFF, 2, 0, 3, 0xFF };
315 const uint8_t data2[] = { 7, 0, 5, 0, 2, 0, 3, 0xFF };
316 const uint8_t data3[] = { 0, 5, 5, 0xFF, 2, 0, 3, 0 };
317 const uint8_t data4[] = { 2, 3, 2, 0, 5, 0xFF, 3, 0 };
robertphillips@google.com768fee82012-08-02 12:42:43 +0000318 const uint8_t data5[] = { 10, 10, 10, 0 };
reed@google.comc9041912011-10-27 16:58:46 +0000319 const uint8_t data6[] = { 2, 2, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
320
321 const uint8_t* array[] = {
322 data0, data1, data2, data3, data4, data5, data6
323 };
324
325 for (size_t i = 0; i < SK_ARRAY_COUNT(array); ++i) {
326 const uint8_t* data = array[i];
327 const int expectedL = *data++;
328 const int expectedR = *data++;
329 int L = 12345, R = 12345;
330 count_left_right_zeros(data, 10, &L, &R);
331 SkASSERT(expectedL == L);
332 SkASSERT(expectedR == R);
333 }
334}
335#endif
336
337// modify row in place, trimming off (zeros) from the left and right sides.
338// return the number of bytes that were completely eliminated from the left
339static int trim_row_left_right(uint8_t* row, int width, int leftZ, int riteZ) {
340 int trim = 0;
341 while (leftZ > 0) {
342 SkASSERT(0 == row[1]);
343 int n = row[0];
344 SkASSERT(n > 0);
345 SkASSERT(n <= width);
346 width -= n;
347 row += 2;
348 if (n > leftZ) {
349 row[-2] = n - leftZ;
350 break;
351 }
352 trim += 2;
353 leftZ -= n;
354 SkASSERT(leftZ >= 0);
355 }
356
357 if (riteZ) {
358 // walk row to the end, and then we'll back up to trim riteZ
359 while (width > 0) {
360 int n = row[0];
361 SkASSERT(n <= width);
362 width -= n;
363 row += 2;
364 }
365 // now skip whole runs of zeros
366 do {
367 row -= 2;
368 SkASSERT(0 == row[1]);
369 int n = row[0];
370 SkASSERT(n > 0);
371 if (n > riteZ) {
372 row[0] = n - riteZ;
373 break;
374 }
375 riteZ -= n;
376 SkASSERT(riteZ >= 0);
377 } while (riteZ > 0);
378 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000379
reed@google.comc9041912011-10-27 16:58:46 +0000380 return trim;
381}
382
383#ifdef SK_DEBUG
384// assert that this row is exactly this width
reed@google.comc5507bf2011-10-27 21:15:36 +0000385static void assert_row_width(const uint8_t* row, int width) {
reed@google.comc9041912011-10-27 16:58:46 +0000386 while (width > 0) {
387 int n = row[0];
388 SkASSERT(n > 0);
389 SkASSERT(n <= width);
390 width -= n;
391 row += 2;
392 }
393 SkASSERT(0 == width);
394}
395
396static void test_trim_row_left_right() {
397 static bool gOnce;
398 if (gOnce) {
399 return;
400 }
401 gOnce = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000402
reed@google.comc9041912011-10-27 16:58:46 +0000403 uint8_t data0[] = { 0, 0, 0, 10, 10, 0xFF };
404 uint8_t data1[] = { 2, 0, 0, 10, 5, 0, 2, 0, 3, 0xFF };
405 uint8_t data2[] = { 5, 0, 2, 10, 5, 0, 2, 0, 3, 0xFF };
406 uint8_t data3[] = { 6, 0, 2, 10, 5, 0, 2, 0, 3, 0xFF };
407 uint8_t data4[] = { 0, 0, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
408 uint8_t data5[] = { 1, 0, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
409 uint8_t data6[] = { 0, 1, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
410 uint8_t data7[] = { 1, 1, 0, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
411 uint8_t data8[] = { 2, 2, 2, 10, 2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
412 uint8_t data9[] = { 5, 2, 4, 10, 2, 0, 2, 0, 2, 0, 2, 0xFF, 2, 0 };
413 uint8_t data10[] ={ 74, 0, 4, 150, 9, 0, 65, 0, 76, 0xFF };
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000414
reed@google.comc9041912011-10-27 16:58:46 +0000415 uint8_t* array[] = {
416 data0, data1, data2, data3, data4,
417 data5, data6, data7, data8, data9,
418 data10
419 };
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000420
reed@google.comc9041912011-10-27 16:58:46 +0000421 for (size_t i = 0; i < SK_ARRAY_COUNT(array); ++i) {
422 uint8_t* data = array[i];
423 const int trimL = *data++;
424 const int trimR = *data++;
425 const int expectedSkip = *data++;
426 const int origWidth = *data++;
427 assert_row_width(data, origWidth);
428 int skip = trim_row_left_right(data, origWidth, trimL, trimR);
429 SkASSERT(expectedSkip == skip);
430 int expectedWidth = origWidth - trimL - trimR;
431 assert_row_width(data + skip, expectedWidth);
432 }
433}
434#endif
435
436bool SkAAClip::trimLeftRight() {
437 SkDEBUGCODE(test_trim_row_left_right();)
438
439 if (this->isEmpty()) {
440 return false;
441 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000442
reed@google.comc9041912011-10-27 16:58:46 +0000443 AUTO_AACLIP_VALIDATE(*this);
444
445 const int width = fBounds.width();
446 RunHead* head = fRunHead;
447 YOffset* yoff = head->yoffsets();
448 YOffset* stop = yoff + head->fRowCount;
449 uint8_t* base = head->data();
450
robertphillips@google.com768fee82012-08-02 12:42:43 +0000451 // After this loop, 'leftZeros' & 'rightZeros' will contain the minimum
452 // number of zeros on the left and right of the clip. This information
453 // can be used to shrink the bounding box.
reed@google.comc9041912011-10-27 16:58:46 +0000454 int leftZeros = width;
455 int riteZeros = width;
456 while (yoff < stop) {
457 int L, R;
458 count_left_right_zeros(base + yoff->fOffset, width, &L, &R);
robertphillips@google.com768fee82012-08-02 12:42:43 +0000459 SkASSERT(L + R < width || (L == width && R == width));
reed@google.comc9041912011-10-27 16:58:46 +0000460 if (L < leftZeros) {
461 leftZeros = L;
462 }
463 if (R < riteZeros) {
464 riteZeros = R;
465 }
466 if (0 == (leftZeros | riteZeros)) {
467 // no trimming to do
468 return true;
469 }
470 yoff += 1;
471 }
472
473 SkASSERT(leftZeros || riteZeros);
robertphillips@google.com768fee82012-08-02 12:42:43 +0000474 if (width == leftZeros) {
475 SkASSERT(width == riteZeros);
reed@google.comc9041912011-10-27 16:58:46 +0000476 return this->setEmpty();
477 }
478
479 this->validate();
480
481 fBounds.fLeft += leftZeros;
482 fBounds.fRight -= riteZeros;
483 SkASSERT(!fBounds.isEmpty());
484
485 // For now we don't realloc the storage (for time), we just shrink in place
486 // This means we don't have to do any memmoves either, since we can just
487 // play tricks with the yoff->fOffset for each row
488 yoff = head->yoffsets();
489 while (yoff < stop) {
490 uint8_t* row = base + yoff->fOffset;
491 SkDEBUGCODE((void)compute_row_length(row, width);)
492 yoff->fOffset += trim_row_left_right(row, width, leftZeros, riteZeros);
493 SkDEBUGCODE((void)compute_row_length(base + yoff->fOffset, width - leftZeros - riteZeros);)
494 yoff += 1;
495 }
496 return true;
497}
498
499static bool row_is_all_zeros(const uint8_t* row, int width) {
500 SkASSERT(width > 0);
501 do {
502 if (row[1]) {
503 return false;
504 }
505 int n = row[0];
506 SkASSERT(n <= width);
507 width -= n;
508 row += 2;
509 } while (width > 0);
510 SkASSERT(0 == width);
511 return true;
512}
513
514bool SkAAClip::trimTopBottom() {
515 if (this->isEmpty()) {
516 return false;
517 }
518
reed@google.comd6040f62011-10-28 02:39:17 +0000519 this->validate();
520
reed@google.comc9041912011-10-27 16:58:46 +0000521 const int width = fBounds.width();
522 RunHead* head = fRunHead;
523 YOffset* yoff = head->yoffsets();
524 YOffset* stop = yoff + head->fRowCount;
525 const uint8_t* base = head->data();
526
527 // Look to trim away empty rows from the top.
528 //
529 int skip = 0;
530 while (yoff < stop) {
531 const uint8_t* data = base + yoff->fOffset;
532 if (!row_is_all_zeros(data, width)) {
533 break;
534 }
535 skip += 1;
536 yoff += 1;
537 }
538 SkASSERT(skip <= head->fRowCount);
539 if (skip == head->fRowCount) {
540 return this->setEmpty();
541 }
542 if (skip > 0) {
543 // adjust fRowCount and fBounds.fTop, and slide all the data up
544 // as we remove [skip] number of YOffset entries
545 yoff = head->yoffsets();
546 int dy = yoff[skip - 1].fY + 1;
547 for (int i = skip; i < head->fRowCount; ++i) {
548 SkASSERT(yoff[i].fY >= dy);
549 yoff[i].fY -= dy;
550 }
551 YOffset* dst = head->yoffsets();
552 size_t size = head->fRowCount * sizeof(YOffset) + head->fDataSize;
553 memmove(dst, dst + skip, size - skip * sizeof(YOffset));
554
555 fBounds.fTop += dy;
556 SkASSERT(!fBounds.isEmpty());
557 head->fRowCount -= skip;
558 SkASSERT(head->fRowCount > 0);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000559
reed@google.comd6040f62011-10-28 02:39:17 +0000560 this->validate();
561 // need to reset this after the memmove
562 base = head->data();
reed@google.comc9041912011-10-27 16:58:46 +0000563 }
564
565 // Look to trim away empty rows from the bottom.
566 // We know that we have at least one non-zero row, so we can just walk
567 // backwards without checking for running past the start.
568 //
569 stop = yoff = head->yoffsets() + head->fRowCount;
570 do {
571 yoff -= 1;
572 } while (row_is_all_zeros(base + yoff->fOffset, width));
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000573 skip = SkToInt(stop - yoff - 1);
reed@google.comc9041912011-10-27 16:58:46 +0000574 SkASSERT(skip >= 0 && skip < head->fRowCount);
575 if (skip > 0) {
576 // removing from the bottom is easier than from the top, as we don't
577 // have to adjust any of the Y values, we just have to trim the array
578 memmove(stop - skip, stop, head->fDataSize);
579
580 fBounds.fBottom = fBounds.fTop + yoff->fY + 1;
581 SkASSERT(!fBounds.isEmpty());
582 head->fRowCount -= skip;
583 SkASSERT(head->fRowCount > 0);
584 }
reed@google.comd6040f62011-10-28 02:39:17 +0000585 this->validate();
reed@google.comc9041912011-10-27 16:58:46 +0000586
587 return true;
588}
589
reed@google.com045e62d2011-10-24 12:19:46 +0000590// can't validate before we're done, since trimming is part of the process of
591// making us valid after the Builder. Since we build from top to bottom, its
592// possible our fBounds.fBottom is bigger than our last scanline of data, so
593// we trim fBounds.fBottom back up.
594//
reed@google.com045e62d2011-10-24 12:19:46 +0000595// TODO: check for duplicates in X and Y to further compress our data
596//
597bool SkAAClip::trimBounds() {
598 if (this->isEmpty()) {
599 return false;
600 }
601
602 const RunHead* head = fRunHead;
603 const YOffset* yoff = head->yoffsets();
604
605 SkASSERT(head->fRowCount > 0);
606 const YOffset& lastY = yoff[head->fRowCount - 1];
607 SkASSERT(lastY.fY + 1 <= fBounds.height());
608 fBounds.fBottom = fBounds.fTop + lastY.fY + 1;
609 SkASSERT(lastY.fY + 1 == fBounds.height());
reed@google.comc9041912011-10-27 16:58:46 +0000610 SkASSERT(!fBounds.isEmpty());
611
612 return this->trimTopBottom() && this->trimLeftRight();
reed@google.com045e62d2011-10-24 12:19:46 +0000613}
614
reed@google.come36707a2011-10-04 21:38:55 +0000615///////////////////////////////////////////////////////////////////////////////
616
617void SkAAClip::freeRuns() {
reed@google.com47ac84e2011-10-06 13:11:25 +0000618 if (fRunHead) {
reed@google.come36707a2011-10-04 21:38:55 +0000619 SkASSERT(fRunHead->fRefCnt >= 1);
620 if (1 == sk_atomic_dec(&fRunHead->fRefCnt)) {
621 sk_free(fRunHead);
622 }
623 }
624}
625
626SkAAClip::SkAAClip() {
627 fBounds.setEmpty();
halcanary96fcdcc2015-08-27 07:41:13 -0700628 fRunHead = nullptr;
reed@google.come36707a2011-10-04 21:38:55 +0000629}
630
631SkAAClip::SkAAClip(const SkAAClip& src) {
reed@google.com045e62d2011-10-24 12:19:46 +0000632 SkDEBUGCODE(fBounds.setEmpty();) // need this for validate
halcanary96fcdcc2015-08-27 07:41:13 -0700633 fRunHead = nullptr;
reed@google.come36707a2011-10-04 21:38:55 +0000634 *this = src;
635}
636
637SkAAClip::~SkAAClip() {
638 this->freeRuns();
639}
640
641SkAAClip& SkAAClip::operator=(const SkAAClip& src) {
reed@google.com045e62d2011-10-24 12:19:46 +0000642 AUTO_AACLIP_VALIDATE(*this);
643 src.validate();
644
reed@google.come36707a2011-10-04 21:38:55 +0000645 if (this != &src) {
646 this->freeRuns();
647 fBounds = src.fBounds;
648 fRunHead = src.fRunHead;
reed@google.com47ac84e2011-10-06 13:11:25 +0000649 if (fRunHead) {
reed@google.come36707a2011-10-04 21:38:55 +0000650 sk_atomic_inc(&fRunHead->fRefCnt);
651 }
652 }
653 return *this;
654}
655
656bool operator==(const SkAAClip& a, const SkAAClip& b) {
reed@google.com045e62d2011-10-24 12:19:46 +0000657 a.validate();
658 b.validate();
659
reed@google.come36707a2011-10-04 21:38:55 +0000660 if (&a == &b) {
661 return true;
662 }
663 if (a.fBounds != b.fBounds) {
664 return false;
665 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000666
reed@google.come36707a2011-10-04 21:38:55 +0000667 const SkAAClip::RunHead* ah = a.fRunHead;
668 const SkAAClip::RunHead* bh = b.fRunHead;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000669
reed@google.come36707a2011-10-04 21:38:55 +0000670 // this catches empties and rects being equal
671 if (ah == bh) {
672 return true;
673 }
674
675 // now we insist that both are complex (but different ptrs)
reed@google.com47ac84e2011-10-06 13:11:25 +0000676 if (!a.fRunHead || !b.fRunHead) {
reed@google.come36707a2011-10-04 21:38:55 +0000677 return false;
678 }
679
680 return ah->fRowCount == bh->fRowCount &&
681 ah->fDataSize == bh->fDataSize &&
682 !memcmp(ah->data(), bh->data(), ah->fDataSize);
683}
684
685void SkAAClip::swap(SkAAClip& other) {
reed@google.com045e62d2011-10-24 12:19:46 +0000686 AUTO_AACLIP_VALIDATE(*this);
687 other.validate();
688
reed@google.come36707a2011-10-04 21:38:55 +0000689 SkTSwap(fBounds, other.fBounds);
690 SkTSwap(fRunHead, other.fRunHead);
691}
692
reed@google.com32287892011-10-05 16:27:44 +0000693bool SkAAClip::set(const SkAAClip& src) {
694 *this = src;
695 return !this->isEmpty();
696}
697
reed@google.come36707a2011-10-04 21:38:55 +0000698bool SkAAClip::setEmpty() {
699 this->freeRuns();
700 fBounds.setEmpty();
halcanary96fcdcc2015-08-27 07:41:13 -0700701 fRunHead = nullptr;
reed@google.come36707a2011-10-04 21:38:55 +0000702 return false;
703}
704
705bool SkAAClip::setRect(const SkIRect& bounds) {
706 if (bounds.isEmpty()) {
707 return this->setEmpty();
708 }
reed@google.com47ac84e2011-10-06 13:11:25 +0000709
reed@google.com045e62d2011-10-24 12:19:46 +0000710 AUTO_AACLIP_VALIDATE(*this);
reed@google.com47ac84e2011-10-06 13:11:25 +0000711
reed@google.com045e62d2011-10-24 12:19:46 +0000712#if 0
reed@google.com47ac84e2011-10-06 13:11:25 +0000713 SkRect r;
714 r.set(bounds);
715 SkPath path;
716 path.addRect(r);
717 return this->setPath(path);
reed@google.com045e62d2011-10-24 12:19:46 +0000718#else
719 this->freeRuns();
720 fBounds = bounds;
721 fRunHead = RunHead::AllocRect(bounds);
722 SkASSERT(!this->isEmpty());
723 return true;
724#endif
reed@google.come36707a2011-10-04 21:38:55 +0000725}
726
reed202ab2a2014-08-07 11:48:10 -0700727bool SkAAClip::isRect() const {
728 if (this->isEmpty()) {
729 return false;
730 }
731
732 const RunHead* head = fRunHead;
733 if (head->fRowCount != 1) {
734 return false;
735 }
736 const YOffset* yoff = head->yoffsets();
737 if (yoff->fY != fBounds.fBottom - 1) {
738 return false;
739 }
740
741 const uint8_t* row = head->data() + yoff->fOffset;
742 int width = fBounds.width();
743 do {
744 if (row[1] != 0xFF) {
745 return false;
746 }
747 int n = row[0];
748 SkASSERT(n <= width);
749 width -= n;
750 row += 2;
751 } while (width > 0);
752 return true;
753}
754
reed@google.comf3c1da12011-10-10 19:35:47 +0000755bool SkAAClip::setRect(const SkRect& r, bool doAA) {
reed@google.come36707a2011-10-04 21:38:55 +0000756 if (r.isEmpty()) {
757 return this->setEmpty();
758 }
759
reed@google.com045e62d2011-10-24 12:19:46 +0000760 AUTO_AACLIP_VALIDATE(*this);
761
762 // TODO: special case this
763
reed@google.come36707a2011-10-04 21:38:55 +0000764 SkPath path;
765 path.addRect(r);
halcanary96fcdcc2015-08-27 07:41:13 -0700766 return this->setPath(path, nullptr, doAA);
reed@google.comf3c1da12011-10-10 19:35:47 +0000767}
768
reed@google.coma069c8f2011-11-28 19:54:56 +0000769static void append_run(SkTDArray<uint8_t>& array, uint8_t value, int count) {
770 SkASSERT(count >= 0);
771 while (count > 0) {
772 int n = count;
773 if (n > 255) {
774 n = 255;
775 }
776 uint8_t* data = array.append(2);
777 data[0] = n;
778 data[1] = value;
779 count -= n;
780 }
781}
782
reed@google.comf3c1da12011-10-10 19:35:47 +0000783bool SkAAClip::setRegion(const SkRegion& rgn) {
784 if (rgn.isEmpty()) {
785 return this->setEmpty();
786 }
787 if (rgn.isRect()) {
788 return this->setRect(rgn.getBounds());
789 }
reed@google.coma069c8f2011-11-28 19:54:56 +0000790
791#if 0
reed@google.comf3c1da12011-10-10 19:35:47 +0000792 SkAAClip clip;
793 SkRegion::Iterator iter(rgn);
794 for (; !iter.done(); iter.next()) {
795 clip.op(iter.rect(), SkRegion::kUnion_Op);
796 }
797 this->swap(clip);
reed@google.com3771a032011-10-11 17:18:04 +0000798 return !this->isEmpty();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000799#else
reed@google.coma069c8f2011-11-28 19:54:56 +0000800 const SkIRect& bounds = rgn.getBounds();
801 const int offsetX = bounds.fLeft;
802 const int offsetY = bounds.fTop;
803
804 SkTDArray<YOffset> yArray;
805 SkTDArray<uint8_t> xArray;
806
807 yArray.setReserve(SkMin32(bounds.height(), 1024));
808 xArray.setReserve(SkMin32(bounds.width() * 128, 64 * 1024));
809
810 SkRegion::Iterator iter(rgn);
811 int prevRight = 0;
812 int prevBot = 0;
halcanary96fcdcc2015-08-27 07:41:13 -0700813 YOffset* currY = nullptr;
reed@google.coma069c8f2011-11-28 19:54:56 +0000814
815 for (; !iter.done(); iter.next()) {
816 const SkIRect& r = iter.rect();
817 SkASSERT(bounds.contains(r));
818
819 int bot = r.fBottom - offsetY;
820 SkASSERT(bot >= prevBot);
821 if (bot > prevBot) {
822 if (currY) {
823 // flush current row
824 append_run(xArray, 0, bounds.width() - prevRight);
825 }
826 // did we introduce an empty-gap from the prev row?
827 int top = r.fTop - offsetY;
828 if (top > prevBot) {
829 currY = yArray.append();
830 currY->fY = top - 1;
831 currY->fOffset = xArray.count();
832 append_run(xArray, 0, bounds.width());
833 }
834 // create a new record for this Y value
835 currY = yArray.append();
836 currY->fY = bot - 1;
837 currY->fOffset = xArray.count();
838 prevRight = 0;
839 prevBot = bot;
840 }
841
842 int x = r.fLeft - offsetX;
843 append_run(xArray, 0, x - prevRight);
844
845 int w = r.fRight - r.fLeft;
846 append_run(xArray, 0xFF, w);
847 prevRight = x + w;
848 SkASSERT(prevRight <= bounds.width());
849 }
850 // flush last row
851 append_run(xArray, 0, bounds.width() - prevRight);
852
853 // now pack everything into a RunHead
854 RunHead* head = RunHead::Alloc(yArray.count(), xArray.bytes());
855 memcpy(head->yoffsets(), yArray.begin(), yArray.bytes());
856 memcpy(head->data(), xArray.begin(), xArray.bytes());
857
858 this->setEmpty();
859 fBounds = bounds;
860 fRunHead = head;
861 this->validate();
862 return true;
863#endif
reed@google.come36707a2011-10-04 21:38:55 +0000864}
865
866///////////////////////////////////////////////////////////////////////////////
867
868const uint8_t* SkAAClip::findRow(int y, int* lastYForRow) const {
reed@google.com47ac84e2011-10-06 13:11:25 +0000869 SkASSERT(fRunHead);
reed@google.come36707a2011-10-04 21:38:55 +0000870
871 if (!y_in_rect(y, fBounds)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700872 return nullptr;
reed@google.come36707a2011-10-04 21:38:55 +0000873 }
874 y -= fBounds.y(); // our yoffs values are relative to the top
875
876 const YOffset* yoff = fRunHead->yoffsets();
877 while (yoff->fY < y) {
878 yoff += 1;
879 SkASSERT(yoff - fRunHead->yoffsets() < fRunHead->fRowCount);
880 }
881
882 if (lastYForRow) {
reed@google.com045e62d2011-10-24 12:19:46 +0000883 *lastYForRow = fBounds.y() + yoff->fY;
reed@google.come36707a2011-10-04 21:38:55 +0000884 }
885 return fRunHead->data() + yoff->fOffset;
886}
887
888const uint8_t* SkAAClip::findX(const uint8_t data[], int x, int* initialCount) const {
889 SkASSERT(x_in_rect(x, fBounds));
890 x -= fBounds.x();
891
892 // first skip up to X
893 for (;;) {
894 int n = data[0];
895 if (x < n) {
reed@google.coma4c6e4d2012-06-20 14:29:50 +0000896 if (initialCount) {
897 *initialCount = n - x;
898 }
reed@google.come36707a2011-10-04 21:38:55 +0000899 break;
900 }
901 data += 2;
902 x -= n;
903 }
904 return data;
905}
906
907bool SkAAClip::quickContains(int left, int top, int right, int bottom) const {
908 if (this->isEmpty()) {
909 return false;
910 }
911 if (!fBounds.contains(left, top, right, bottom)) {
912 return false;
913 }
reed@google.com32287892011-10-05 16:27:44 +0000914#if 0
reed@google.come36707a2011-10-04 21:38:55 +0000915 if (this->isRect()) {
916 return true;
917 }
reed@google.com32287892011-10-05 16:27:44 +0000918#endif
reed@google.come36707a2011-10-04 21:38:55 +0000919
reed@google.coma4c6e4d2012-06-20 14:29:50 +0000920 int lastY SK_INIT_TO_AVOID_WARNING;
reed@google.come36707a2011-10-04 21:38:55 +0000921 const uint8_t* row = this->findRow(top, &lastY);
922 if (lastY < bottom) {
923 return false;
924 }
925 // now just need to check in X
reed@google.com045e62d2011-10-24 12:19:46 +0000926 int count;
927 row = this->findX(row, left, &count);
928#if 0
929 return count >= (right - left) && 0xFF == row[1];
930#else
931 int rectWidth = right - left;
932 while (0xFF == row[1]) {
933 if (count >= rectWidth) {
934 return true;
935 }
936 rectWidth -= count;
937 row += 2;
938 count = row[0];
939 }
940 return false;
941#endif
reed@google.come36707a2011-10-04 21:38:55 +0000942}
943
944///////////////////////////////////////////////////////////////////////////////
945
946class SkAAClip::Builder {
947 SkIRect fBounds;
948 struct Row {
949 int fY;
950 int fWidth;
951 SkTDArray<uint8_t>* fData;
952 };
953 SkTDArray<Row> fRows;
954 Row* fCurrRow;
955 int fPrevY;
956 int fWidth;
reed@google.com209c4152011-10-26 15:03:48 +0000957 int fMinY;
reed@google.come36707a2011-10-04 21:38:55 +0000958
959public:
960 Builder(const SkIRect& bounds) : fBounds(bounds) {
961 fPrevY = -1;
962 fWidth = bounds.width();
halcanary96fcdcc2015-08-27 07:41:13 -0700963 fCurrRow = nullptr;
reed@google.com209c4152011-10-26 15:03:48 +0000964 fMinY = bounds.fTop;
reed@google.come36707a2011-10-04 21:38:55 +0000965 }
966
967 ~Builder() {
968 Row* row = fRows.begin();
969 Row* stop = fRows.end();
970 while (row < stop) {
971 delete row->fData;
972 row += 1;
973 }
974 }
975
reed@google.com32287892011-10-05 16:27:44 +0000976 const SkIRect& getBounds() const { return fBounds; }
977
reed@google.come36707a2011-10-04 21:38:55 +0000978 void addRun(int x, int y, U8CPU alpha, int count) {
979 SkASSERT(count > 0);
980 SkASSERT(fBounds.contains(x, y));
981 SkASSERT(fBounds.contains(x + count - 1, y));
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000982
reed@google.come36707a2011-10-04 21:38:55 +0000983 x -= fBounds.left();
984 y -= fBounds.top();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000985
reed@google.come36707a2011-10-04 21:38:55 +0000986 Row* row = fCurrRow;
987 if (y != fPrevY) {
988 SkASSERT(y > fPrevY);
989 fPrevY = y;
990 row = this->flushRow(true);
991 row->fY = y;
992 row->fWidth = 0;
993 SkASSERT(row->fData);
994 SkASSERT(0 == row->fData->count());
995 fCurrRow = row;
996 }
997
998 SkASSERT(row->fWidth <= x);
999 SkASSERT(row->fWidth < fBounds.width());
1000
1001 SkTDArray<uint8_t>& data = *row->fData;
1002
1003 int gap = x - row->fWidth;
1004 if (gap) {
1005 AppendRun(data, 0, gap);
1006 row->fWidth += gap;
1007 SkASSERT(row->fWidth < fBounds.width());
1008 }
1009
1010 AppendRun(data, alpha, count);
1011 row->fWidth += count;
1012 SkASSERT(row->fWidth <= fBounds.width());
1013 }
1014
tomhudson@google.com49eac192011-12-27 13:59:20 +00001015 void addColumn(int x, int y, U8CPU alpha, int height) {
1016 SkASSERT(fBounds.contains(x, y + height - 1));
1017
1018 this->addRun(x, y, alpha, 1);
1019 this->flushRowH(fCurrRow);
1020 y -= fBounds.fTop;
1021 SkASSERT(y == fCurrRow->fY);
1022 fCurrRow->fY = y + height - 1;
1023 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001024
reed@google.com9154eb02011-10-31 16:07:28 +00001025 void addRectRun(int x, int y, int width, int height) {
1026 SkASSERT(fBounds.contains(x + width - 1, y + height - 1));
1027 this->addRun(x, y, 0xFF, width);
1028
reed@google.coma89c77b2011-12-01 21:47:26 +00001029 // we assum the rect must be all we'll see for these scanlines
reed@google.com9154eb02011-10-31 16:07:28 +00001030 // so we ensure our row goes all the way to our right
1031 this->flushRowH(fCurrRow);
1032
1033 y -= fBounds.fTop;
1034 SkASSERT(y == fCurrRow->fY);
1035 fCurrRow->fY = y + height - 1;
1036 }
1037
tomhudson@google.com49eac192011-12-27 13:59:20 +00001038 void addAntiRectRun(int x, int y, int width, int height,
1039 SkAlpha leftAlpha, SkAlpha rightAlpha) {
liyuqian2add0ff2016-10-20 11:04:39 -07001040 // According to SkBlitter.cpp, no matter whether leftAlpha is 0 or positive,
1041 // we should always consider [x, x+1] as the left-most column and [x+1, x+1+width]
1042 // as the rect with full alpha.
1043 SkASSERT(fBounds.contains(x + width + (rightAlpha > 0 ? 1 : 0),
tomhudson@google.com49eac192011-12-27 13:59:20 +00001044 y + height - 1));
1045 SkASSERT(width >= 0);
1046
1047 // Conceptually we're always adding 3 runs, but we should
1048 // merge or omit them if possible.
1049 if (leftAlpha == 0xFF) {
1050 width++;
1051 } else if (leftAlpha > 0) {
1052 this->addRun(x++, y, leftAlpha, 1);
liyuqian2add0ff2016-10-20 11:04:39 -07001053 } else {
1054 // leftAlpha is 0, ignore the left column
1055 x++;
tomhudson@google.com49eac192011-12-27 13:59:20 +00001056 }
1057 if (rightAlpha == 0xFF) {
1058 width++;
1059 }
1060 if (width > 0) {
1061 this->addRun(x, y, 0xFF, width);
1062 }
1063 if (rightAlpha > 0 && rightAlpha < 255) {
1064 this->addRun(x + width, y, rightAlpha, 1);
1065 }
1066
1067 // we assume the rect must be all we'll see for these scanlines
1068 // so we ensure our row goes all the way to our right
1069 this->flushRowH(fCurrRow);
1070
1071 y -= fBounds.fTop;
1072 SkASSERT(y == fCurrRow->fY);
1073 fCurrRow->fY = y + height - 1;
1074 }
1075
reed@google.com045e62d2011-10-24 12:19:46 +00001076 bool finish(SkAAClip* target) {
reed@google.come36707a2011-10-04 21:38:55 +00001077 this->flushRow(false);
1078
1079 const Row* row = fRows.begin();
1080 const Row* stop = fRows.end();
1081
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001082 size_t dataSize = 0;
reed@google.come36707a2011-10-04 21:38:55 +00001083 while (row < stop) {
1084 dataSize += row->fData->count();
1085 row += 1;
1086 }
1087
reed@google.com045e62d2011-10-24 12:19:46 +00001088 if (0 == dataSize) {
1089 return target->setEmpty();
1090 }
1091
reed@google.com209c4152011-10-26 15:03:48 +00001092 SkASSERT(fMinY >= fBounds.fTop);
1093 SkASSERT(fMinY < fBounds.fBottom);
1094 int adjustY = fMinY - fBounds.fTop;
1095 fBounds.fTop = fMinY;
1096
reed@google.come36707a2011-10-04 21:38:55 +00001097 RunHead* head = RunHead::Alloc(fRows.count(), dataSize);
1098 YOffset* yoffset = head->yoffsets();
1099 uint8_t* data = head->data();
1100 uint8_t* baseData = data;
1101
1102 row = fRows.begin();
reed@google.comc9041912011-10-27 16:58:46 +00001103 SkDEBUGCODE(int prevY = row->fY - 1;)
reed@google.come36707a2011-10-04 21:38:55 +00001104 while (row < stop) {
reed@google.comc9041912011-10-27 16:58:46 +00001105 SkASSERT(prevY < row->fY); // must be monotonic
1106 SkDEBUGCODE(prevY = row->fY);
1107
reed@google.com209c4152011-10-26 15:03:48 +00001108 yoffset->fY = row->fY - adjustY;
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001109 yoffset->fOffset = SkToU32(data - baseData);
reed@google.come36707a2011-10-04 21:38:55 +00001110 yoffset += 1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001111
reed@google.come36707a2011-10-04 21:38:55 +00001112 size_t n = row->fData->count();
1113 memcpy(data, row->fData->begin(), n);
reed@google.comc9041912011-10-27 16:58:46 +00001114#ifdef SK_DEBUG
tomhudson@google.comf74ad8c2011-11-09 22:15:08 +00001115 size_t bytesNeeded = compute_row_length(data, fBounds.width());
reed@google.comc9041912011-10-27 16:58:46 +00001116 SkASSERT(bytesNeeded == n);
1117#endif
reed@google.come36707a2011-10-04 21:38:55 +00001118 data += n;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001119
reed@google.come36707a2011-10-04 21:38:55 +00001120 row += 1;
1121 }
1122
reed@google.com045e62d2011-10-24 12:19:46 +00001123 target->freeRuns();
1124 target->fBounds = fBounds;
1125 target->fRunHead = head;
1126 return target->trimBounds();
reed@google.come36707a2011-10-04 21:38:55 +00001127 }
1128
1129 void dump() {
1130 this->validate();
1131 int y;
1132 for (y = 0; y < fRows.count(); ++y) {
1133 const Row& row = fRows[y];
1134 SkDebugf("Y:%3d W:%3d", row.fY, row.fWidth);
1135 const SkTDArray<uint8_t>& data = *row.fData;
1136 int count = data.count();
1137 SkASSERT(!(count & 1));
1138 const uint8_t* ptr = data.begin();
1139 for (int x = 0; x < count; x += 2) {
1140 SkDebugf(" [%3d:%02X]", ptr[0], ptr[1]);
1141 ptr += 2;
1142 }
1143 SkDebugf("\n");
1144 }
reed@google.come36707a2011-10-04 21:38:55 +00001145 }
1146
1147 void validate() {
1148#ifdef SK_DEBUG
caryclark@google.com803eceb2012-06-06 12:09:34 +00001149 if (false) { // avoid bit rot, suppress warning
1150 test_count_left_right_zeros();
1151 }
reed@google.come36707a2011-10-04 21:38:55 +00001152 int prevY = -1;
1153 for (int i = 0; i < fRows.count(); ++i) {
1154 const Row& row = fRows[i];
1155 SkASSERT(prevY < row.fY);
1156 SkASSERT(fWidth == row.fWidth);
1157 int count = row.fData->count();
1158 const uint8_t* ptr = row.fData->begin();
1159 SkASSERT(!(count & 1));
1160 int w = 0;
1161 for (int x = 0; x < count; x += 2) {
reed@google.comd6040f62011-10-28 02:39:17 +00001162 int n = ptr[0];
1163 SkASSERT(n > 0);
1164 w += n;
reed@google.come36707a2011-10-04 21:38:55 +00001165 SkASSERT(w <= fWidth);
1166 ptr += 2;
1167 }
1168 SkASSERT(w == fWidth);
1169 prevY = row.fY;
1170 }
1171#endif
1172 }
1173
reed@google.com209c4152011-10-26 15:03:48 +00001174 // only called by BuilderBlitter
1175 void setMinY(int y) {
1176 fMinY = y;
1177 }
1178
reed@google.come36707a2011-10-04 21:38:55 +00001179private:
reed@google.com9154eb02011-10-31 16:07:28 +00001180 void flushRowH(Row* row) {
1181 // flush current row if needed
1182 if (row->fWidth < fWidth) {
1183 AppendRun(*row->fData, 0, fWidth - row->fWidth);
1184 row->fWidth = fWidth;
1185 }
1186 }
reed@google.com209c4152011-10-26 15:03:48 +00001187
reed@google.come36707a2011-10-04 21:38:55 +00001188 Row* flushRow(bool readyForAnother) {
halcanary96fcdcc2015-08-27 07:41:13 -07001189 Row* next = nullptr;
reed@google.come36707a2011-10-04 21:38:55 +00001190 int count = fRows.count();
1191 if (count > 0) {
reed@google.com9154eb02011-10-31 16:07:28 +00001192 this->flushRowH(&fRows[count - 1]);
reed@google.come36707a2011-10-04 21:38:55 +00001193 }
1194 if (count > 1) {
1195 // are our last two runs the same?
1196 Row* prev = &fRows[count - 2];
1197 Row* curr = &fRows[count - 1];
1198 SkASSERT(prev->fWidth == fWidth);
1199 SkASSERT(curr->fWidth == fWidth);
1200 if (*prev->fData == *curr->fData) {
1201 prev->fY = curr->fY;
1202 if (readyForAnother) {
1203 curr->fData->rewind();
1204 next = curr;
1205 } else {
1206 delete curr->fData;
1207 fRows.removeShuffle(count - 1);
1208 }
1209 } else {
1210 if (readyForAnother) {
1211 next = fRows.append();
1212 next->fData = new SkTDArray<uint8_t>;
1213 }
1214 }
1215 } else {
1216 if (readyForAnother) {
1217 next = fRows.append();
1218 next->fData = new SkTDArray<uint8_t>;
1219 }
1220 }
1221 return next;
1222 }
1223
1224 static void AppendRun(SkTDArray<uint8_t>& data, U8CPU alpha, int count) {
1225 do {
1226 int n = count;
1227 if (n > 255) {
1228 n = 255;
1229 }
1230 uint8_t* ptr = data.append(2);
1231 ptr[0] = n;
1232 ptr[1] = alpha;
1233 count -= n;
1234 } while (count > 0);
1235 }
1236};
1237
1238class SkAAClip::BuilderBlitter : public SkBlitter {
reed@google.com80cdb9a2012-02-16 18:56:17 +00001239 int fLastY;
1240
1241 /*
1242 If we see a gap of 1 or more empty scanlines while building in Y-order,
1243 we inject an explicit empty scanline (alpha==0)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001244
reed@google.com80cdb9a2012-02-16 18:56:17 +00001245 See AAClipTest.cpp : test_path_with_hole()
1246 */
1247 void checkForYGap(int y) {
1248 SkASSERT(y >= fLastY);
1249 if (fLastY > -SK_MaxS32) {
1250 int gap = y - fLastY;
1251 if (gap > 1) {
1252 fBuilder->addRun(fLeft, y - 1, 0, fRight - fLeft);
1253 }
1254 }
1255 fLastY = y;
1256 }
1257
reed@google.come36707a2011-10-04 21:38:55 +00001258public:
reed@google.com80cdb9a2012-02-16 18:56:17 +00001259
reed@google.come36707a2011-10-04 21:38:55 +00001260 BuilderBlitter(Builder* builder) {
1261 fBuilder = builder;
reed@google.com17785642011-10-12 20:23:55 +00001262 fLeft = builder->getBounds().fLeft;
1263 fRight = builder->getBounds().fRight;
reed@google.com209c4152011-10-26 15:03:48 +00001264 fMinY = SK_MaxS32;
reed@google.com80cdb9a2012-02-16 18:56:17 +00001265 fLastY = -SK_MaxS32; // sentinel
reed@google.com209c4152011-10-26 15:03:48 +00001266 }
1267
1268 void finish() {
1269 if (fMinY < SK_MaxS32) {
1270 fBuilder->setMinY(fMinY);
1271 }
reed@google.come36707a2011-10-04 21:38:55 +00001272 }
1273
tomhudson@google.com49eac192011-12-27 13:59:20 +00001274 /**
1275 Must evaluate clips in scan-line order, so don't want to allow blitV(),
1276 but an AAClip can be clipped down to a single pixel wide, so we
1277 must support it (given AntiRect semantics: minimum width is 2).
1278 Instead we'll rely on the runtime asserts to guarantee Y monotonicity;
1279 any failure cases that misses may have minor artifacts.
1280 */
mtklein36352bf2015-03-25 18:17:31 -07001281 void blitV(int x, int y, int height, SkAlpha alpha) override {
liyuqian2add0ff2016-10-20 11:04:39 -07001282 if (height == 1) {
1283 // We're still in scan-line order if height is 1
1284 // This is useful for Analytic AA
1285 const SkAlpha alphas[2] = {alpha, 0};
1286 const int16_t runs[2] = {1, 0};
1287 this->blitAntiH(x, y, alphas, runs);
1288 } else {
1289 this->recordMinY(y);
1290 fBuilder->addColumn(x, y, alpha, height);
1291 fLastY = y + height - 1;
1292 }
tomhudson@google.com49eac192011-12-27 13:59:20 +00001293 }
reed@google.com045e62d2011-10-24 12:19:46 +00001294
mtklein36352bf2015-03-25 18:17:31 -07001295 void blitRect(int x, int y, int width, int height) override {
reed@google.com9154eb02011-10-31 16:07:28 +00001296 this->recordMinY(y);
reed@google.com80cdb9a2012-02-16 18:56:17 +00001297 this->checkForYGap(y);
reed@google.com9154eb02011-10-31 16:07:28 +00001298 fBuilder->addRectRun(x, y, width, height);
reed@google.com302b8612012-02-16 19:30:13 +00001299 fLastY = y + height - 1;
reed@google.com562a2ac2011-10-31 14:14:18 +00001300 }
reed@google.com045e62d2011-10-24 12:19:46 +00001301
tomhudson@google.com49eac192011-12-27 13:59:20 +00001302 virtual void blitAntiRect(int x, int y, int width, int height,
mtklein36352bf2015-03-25 18:17:31 -07001303 SkAlpha leftAlpha, SkAlpha rightAlpha) override {
tomhudson@google.com49eac192011-12-27 13:59:20 +00001304 this->recordMinY(y);
reed@google.com302b8612012-02-16 19:30:13 +00001305 this->checkForYGap(y);
tomhudson@google.com49eac192011-12-27 13:59:20 +00001306 fBuilder->addAntiRectRun(x, y, width, height, leftAlpha, rightAlpha);
reed@google.com302b8612012-02-16 19:30:13 +00001307 fLastY = y + height - 1;
tomhudson@google.com49eac192011-12-27 13:59:20 +00001308 }
1309
mtklein36352bf2015-03-25 18:17:31 -07001310 void blitMask(const SkMask&, const SkIRect& clip) override
reed@google.come36707a2011-10-04 21:38:55 +00001311 { unexpected(); }
1312
reed41e010c2015-06-09 12:16:53 -07001313 const SkPixmap* justAnOpaqueColor(uint32_t*) override {
halcanary96fcdcc2015-08-27 07:41:13 -07001314 return nullptr;
reed@google.come36707a2011-10-04 21:38:55 +00001315 }
1316
mtklein36352bf2015-03-25 18:17:31 -07001317 void blitH(int x, int y, int width) override {
reed@google.com209c4152011-10-26 15:03:48 +00001318 this->recordMinY(y);
reed@google.com80cdb9a2012-02-16 18:56:17 +00001319 this->checkForYGap(y);
reed@google.come36707a2011-10-04 21:38:55 +00001320 fBuilder->addRun(x, y, 0xFF, width);
1321 }
1322
1323 virtual void blitAntiH(int x, int y, const SkAlpha alpha[],
mtklein36352bf2015-03-25 18:17:31 -07001324 const int16_t runs[]) override {
reed@google.com209c4152011-10-26 15:03:48 +00001325 this->recordMinY(y);
reed@google.com80cdb9a2012-02-16 18:56:17 +00001326 this->checkForYGap(y);
reed@google.come36707a2011-10-04 21:38:55 +00001327 for (;;) {
1328 int count = *runs;
1329 if (count <= 0) {
1330 return;
1331 }
reed@google.com17785642011-10-12 20:23:55 +00001332
1333 // The supersampler's buffer can be the width of the device, so
liyuqian1d3ab122016-11-08 13:55:15 -08001334 // we may have to trim the run to our bounds. Previously, we assert that
1335 // the extra spans are always alpha==0.
1336 // However, the analytic AA is too sensitive to precision errors
1337 // so it may have extra spans with very tiny alpha because after several
1338 // arithmatic operations, the edge may bleed the path boundary a little bit.
1339 // Therefore, instead of always asserting alpha==0, we assert alpha < 0x10.
reed@google.com17785642011-10-12 20:23:55 +00001340 int localX = x;
1341 int localCount = count;
1342 if (x < fLeft) {
liyuqian1d3ab122016-11-08 13:55:15 -08001343 SkASSERT(0x10 > *alpha);
reed@google.com17785642011-10-12 20:23:55 +00001344 int gap = fLeft - x;
1345 SkASSERT(gap <= count);
1346 localX += gap;
1347 localCount -= gap;
1348 }
1349 int right = x + count;
1350 if (right > fRight) {
liyuqian1d3ab122016-11-08 13:55:15 -08001351 SkASSERT(0x10 > *alpha);
reed@google.com17785642011-10-12 20:23:55 +00001352 localCount -= right - fRight;
1353 SkASSERT(localCount >= 0);
1354 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001355
reed@google.com17785642011-10-12 20:23:55 +00001356 if (localCount) {
1357 fBuilder->addRun(localX, y, *alpha, localCount);
1358 }
bsalomon@google.com820e80a2011-10-24 21:09:40 +00001359 // Next run
reed@google.come36707a2011-10-04 21:38:55 +00001360 runs += count;
1361 alpha += count;
1362 x += count;
1363 }
1364 }
1365
1366private:
1367 Builder* fBuilder;
reed@google.com17785642011-10-12 20:23:55 +00001368 int fLeft; // cache of builder's bounds' left edge
1369 int fRight;
reed@google.com209c4152011-10-26 15:03:48 +00001370 int fMinY;
1371
1372 /*
1373 * We track this, in case the scan converter skipped some number of
1374 * scanlines at the (relative to the bounds it was given). This allows
1375 * the builder, during its finish, to trip its bounds down to the "real"
1376 * top.
1377 */
1378 void recordMinY(int y) {
1379 if (y < fMinY) {
1380 fMinY = y;
1381 }
1382 }
reed@google.come36707a2011-10-04 21:38:55 +00001383
1384 void unexpected() {
1385 SkDebugf("---- did not expect to get called here");
1386 sk_throw();
1387 }
1388};
1389
reed@google.comf3c1da12011-10-10 19:35:47 +00001390bool SkAAClip::setPath(const SkPath& path, const SkRegion* clip, bool doAA) {
reed@google.com045e62d2011-10-24 12:19:46 +00001391 AUTO_AACLIP_VALIDATE(*this);
1392
reed@google.com32287892011-10-05 16:27:44 +00001393 if (clip && clip->isEmpty()) {
reed@google.come36707a2011-10-04 21:38:55 +00001394 return this->setEmpty();
1395 }
1396
1397 SkIRect ibounds;
reed@google.com32287892011-10-05 16:27:44 +00001398 path.getBounds().roundOut(&ibounds);
reed@google.come36707a2011-10-04 21:38:55 +00001399
reed@google.com32287892011-10-05 16:27:44 +00001400 SkRegion tmpClip;
halcanary96fcdcc2015-08-27 07:41:13 -07001401 if (nullptr == clip) {
reed@google.com32287892011-10-05 16:27:44 +00001402 tmpClip.setRect(ibounds);
1403 clip = &tmpClip;
1404 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001405
reed@google.com045e62d2011-10-24 12:19:46 +00001406 if (path.isInverseFillType()) {
1407 ibounds = clip->getBounds();
1408 } else {
reed@google.com32287892011-10-05 16:27:44 +00001409 if (ibounds.isEmpty() || !ibounds.intersect(clip->getBounds())) {
reed@google.come36707a2011-10-04 21:38:55 +00001410 return this->setEmpty();
1411 }
reed@google.come36707a2011-10-04 21:38:55 +00001412 }
1413
1414 Builder builder(ibounds);
1415 BuilderBlitter blitter(&builder);
1416
reed@google.comf3c1da12011-10-10 19:35:47 +00001417 if (doAA) {
liyuqian2add0ff2016-10-20 11:04:39 -07001418 if (gSkUseAnalyticAA.load()) {
1419 SkScan::AAAFillPath(path, *clip, &blitter, true);
1420 } else {
1421 SkScan::AntiFillPath(path, *clip, &blitter, true);
1422 }
reed@google.comf3c1da12011-10-10 19:35:47 +00001423 } else {
1424 SkScan::FillPath(path, *clip, &blitter);
1425 }
reed@google.come36707a2011-10-04 21:38:55 +00001426
reed@google.com209c4152011-10-26 15:03:48 +00001427 blitter.finish();
reed@google.com045e62d2011-10-24 12:19:46 +00001428 return builder.finish(this);
reed@google.come36707a2011-10-04 21:38:55 +00001429}
1430
1431///////////////////////////////////////////////////////////////////////////////
1432
reed@google.com32287892011-10-05 16:27:44 +00001433typedef void (*RowProc)(SkAAClip::Builder&, int bottom,
1434 const uint8_t* rowA, const SkIRect& rectA,
1435 const uint8_t* rowB, const SkIRect& rectB);
1436
reed@google.com32287892011-10-05 16:27:44 +00001437typedef U8CPU (*AlphaProc)(U8CPU alphaA, U8CPU alphaB);
1438
1439static U8CPU sectAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1440 // Multiply
1441 return SkMulDiv255Round(alphaA, alphaB);
1442}
1443
1444static U8CPU unionAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1445 // SrcOver
1446 return alphaA + alphaB - SkMulDiv255Round(alphaA, alphaB);
1447}
1448
1449static U8CPU diffAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1450 // SrcOut
1451 return SkMulDiv255Round(alphaA, 0xFF - alphaB);
1452}
1453
1454static U8CPU xorAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1455 // XOR
1456 return alphaA + alphaB - 2 * SkMulDiv255Round(alphaA, alphaB);
1457}
1458
1459static AlphaProc find_alpha_proc(SkRegion::Op op) {
1460 switch (op) {
1461 case SkRegion::kIntersect_Op:
1462 return sectAlphaProc;
1463 case SkRegion::kDifference_Op:
1464 return diffAlphaProc;
1465 case SkRegion::kUnion_Op:
1466 return unionAlphaProc;
1467 case SkRegion::kXOR_Op:
1468 return xorAlphaProc;
1469 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001470 SkDEBUGFAIL("unexpected region op");
reed@google.com32287892011-10-05 16:27:44 +00001471 return sectAlphaProc;
1472 }
1473}
1474
reed@google.com32287892011-10-05 16:27:44 +00001475class RowIter {
1476public:
1477 RowIter(const uint8_t* row, const SkIRect& bounds) {
1478 fRow = row;
1479 fLeft = bounds.fLeft;
reed@google.com32287892011-10-05 16:27:44 +00001480 fBoundsRight = bounds.fRight;
reed@google.com1c04bf92011-10-10 12:57:12 +00001481 if (row) {
1482 fRight = bounds.fLeft + row[0];
1483 SkASSERT(fRight <= fBoundsRight);
1484 fAlpha = row[1];
1485 fDone = false;
1486 } else {
1487 fDone = true;
1488 fRight = kMaxInt32;
1489 fAlpha = 0;
1490 }
reed@google.com32287892011-10-05 16:27:44 +00001491 }
1492
1493 bool done() const { return fDone; }
reed@google.com1c04bf92011-10-10 12:57:12 +00001494 int left() const { return fLeft; }
1495 int right() const { return fRight; }
1496 U8CPU alpha() const { return fAlpha; }
reed@google.com32287892011-10-05 16:27:44 +00001497 void next() {
reed@google.com1c04bf92011-10-10 12:57:12 +00001498 if (!fDone) {
reed@google.com32287892011-10-05 16:27:44 +00001499 fLeft = fRight;
reed@google.com1c04bf92011-10-10 12:57:12 +00001500 if (fRight == fBoundsRight) {
1501 fDone = true;
1502 fRight = kMaxInt32;
1503 fAlpha = 0;
1504 } else {
1505 fRow += 2;
1506 fRight += fRow[0];
1507 fAlpha = fRow[1];
1508 SkASSERT(fRight <= fBoundsRight);
1509 }
reed@google.com32287892011-10-05 16:27:44 +00001510 }
1511 }
1512
1513private:
1514 const uint8_t* fRow;
1515 int fLeft;
1516 int fRight;
1517 int fBoundsRight;
1518 bool fDone;
reed@google.com1c04bf92011-10-10 12:57:12 +00001519 uint8_t fAlpha;
reed@google.com32287892011-10-05 16:27:44 +00001520};
1521
reed@google.com1c04bf92011-10-10 12:57:12 +00001522static void adjust_row(RowIter& iter, int& leftA, int& riteA, int rite) {
1523 if (rite == riteA) {
1524 iter.next();
1525 leftA = iter.left();
1526 riteA = iter.right();
reed@google.com32287892011-10-05 16:27:44 +00001527 }
1528}
1529
caryclark@google.com803eceb2012-06-06 12:09:34 +00001530#if 0 // UNUSED
reed@google.com1c04bf92011-10-10 12:57:12 +00001531static bool intersect(int& min, int& max, int boundsMin, int boundsMax) {
1532 SkASSERT(min < max);
1533 SkASSERT(boundsMin < boundsMax);
1534 if (min >= boundsMax || max <= boundsMin) {
1535 return false;
1536 }
1537 if (min < boundsMin) {
1538 min = boundsMin;
1539 }
1540 if (max > boundsMax) {
1541 max = boundsMax;
1542 }
1543 return true;
1544}
caryclark@google.com803eceb2012-06-06 12:09:34 +00001545#endif
reed@google.com1c04bf92011-10-10 12:57:12 +00001546
reed@google.com32287892011-10-05 16:27:44 +00001547static void operatorX(SkAAClip::Builder& builder, int lastY,
1548 RowIter& iterA, RowIter& iterB,
1549 AlphaProc proc, const SkIRect& bounds) {
reed@google.com32287892011-10-05 16:27:44 +00001550 int leftA = iterA.left();
1551 int riteA = iterA.right();
reed@google.com32287892011-10-05 16:27:44 +00001552 int leftB = iterB.left();
1553 int riteB = iterB.right();
1554
reed@google.com1c04bf92011-10-10 12:57:12 +00001555 int prevRite = bounds.fLeft;
1556
1557 do {
reed@google.com32287892011-10-05 16:27:44 +00001558 U8CPU alphaA = 0;
1559 U8CPU alphaB = 0;
reed@google.com32287892011-10-05 16:27:44 +00001560 int left, rite;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001561
reed@google.com1c04bf92011-10-10 12:57:12 +00001562 if (leftA < leftB) {
reed@google.com32287892011-10-05 16:27:44 +00001563 left = leftA;
reed@google.com32287892011-10-05 16:27:44 +00001564 alphaA = iterA.alpha();
reed@google.com1c04bf92011-10-10 12:57:12 +00001565 if (riteA <= leftB) {
1566 rite = riteA;
1567 } else {
1568 rite = leftA = leftB;
reed@google.com32287892011-10-05 16:27:44 +00001569 }
reed@google.com1c04bf92011-10-10 12:57:12 +00001570 } else if (leftB < leftA) {
1571 left = leftB;
1572 alphaB = iterB.alpha();
1573 if (riteB <= leftA) {
1574 rite = riteB;
1575 } else {
1576 rite = leftB = leftA;
1577 }
1578 } else {
1579 left = leftA; // or leftB, since leftA == leftB
1580 rite = leftA = leftB = SkMin32(riteA, riteB);
1581 alphaA = iterA.alpha();
1582 alphaB = iterB.alpha();
reed@google.com32287892011-10-05 16:27:44 +00001583 }
1584
1585 if (left >= bounds.fRight) {
1586 break;
1587 }
reed@google.com34f7e472011-10-13 15:11:59 +00001588 if (rite > bounds.fRight) {
1589 rite = bounds.fRight;
1590 }
1591
reed@google.com32287892011-10-05 16:27:44 +00001592 if (left >= bounds.fLeft) {
reed@google.com1c04bf92011-10-10 12:57:12 +00001593 SkASSERT(rite > left);
reed@google.com32287892011-10-05 16:27:44 +00001594 builder.addRun(left, lastY, proc(alphaA, alphaB), rite - left);
reed@google.com1c04bf92011-10-10 12:57:12 +00001595 prevRite = rite;
reed@google.com32287892011-10-05 16:27:44 +00001596 }
reed@google.com1c04bf92011-10-10 12:57:12 +00001597
1598 adjust_row(iterA, leftA, riteA, rite);
1599 adjust_row(iterB, leftB, riteB, rite);
1600 } while (!iterA.done() || !iterB.done());
1601
1602 if (prevRite < bounds.fRight) {
1603 builder.addRun(prevRite, lastY, 0, bounds.fRight - prevRite);
reed@google.com32287892011-10-05 16:27:44 +00001604 }
1605}
1606
reed@google.com1c04bf92011-10-10 12:57:12 +00001607static void adjust_iter(SkAAClip::Iter& iter, int& topA, int& botA, int bot) {
1608 if (bot == botA) {
1609 iter.next();
1610 topA = botA;
1611 SkASSERT(botA == iter.top());
1612 botA = iter.bottom();
reed@google.com32287892011-10-05 16:27:44 +00001613 }
1614}
1615
1616static void operateY(SkAAClip::Builder& builder, const SkAAClip& A,
1617 const SkAAClip& B, SkRegion::Op op) {
1618 AlphaProc proc = find_alpha_proc(op);
1619 const SkIRect& bounds = builder.getBounds();
1620
1621 SkAAClip::Iter iterA(A);
1622 SkAAClip::Iter iterB(B);
1623
1624 SkASSERT(!iterA.done());
1625 int topA = iterA.top();
1626 int botA = iterA.bottom();
1627 SkASSERT(!iterB.done());
1628 int topB = iterB.top();
1629 int botB = iterB.bottom();
1630
reed@google.com1c04bf92011-10-10 12:57:12 +00001631 do {
halcanary96fcdcc2015-08-27 07:41:13 -07001632 const uint8_t* rowA = nullptr;
1633 const uint8_t* rowB = nullptr;
reed@google.com32287892011-10-05 16:27:44 +00001634 int top, bot;
reed@google.com1c04bf92011-10-10 12:57:12 +00001635
1636 if (topA < topB) {
reed@google.com32287892011-10-05 16:27:44 +00001637 top = topA;
reed@google.com32287892011-10-05 16:27:44 +00001638 rowA = iterA.data();
reed@google.com1c04bf92011-10-10 12:57:12 +00001639 if (botA <= topB) {
1640 bot = botA;
1641 } else {
1642 bot = topA = topB;
reed@google.com32287892011-10-05 16:27:44 +00001643 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001644
reed@google.com1c04bf92011-10-10 12:57:12 +00001645 } else if (topB < topA) {
1646 top = topB;
1647 rowB = iterB.data();
1648 if (botB <= topA) {
1649 bot = botB;
1650 } else {
1651 bot = topB = topA;
1652 }
1653 } else {
1654 top = topA; // or topB, since topA == topB
1655 bot = topA = topB = SkMin32(botA, botB);
1656 rowA = iterA.data();
1657 rowB = iterB.data();
reed@google.com32287892011-10-05 16:27:44 +00001658 }
1659
1660 if (top >= bounds.fBottom) {
1661 break;
1662 }
reed@google.com34f7e472011-10-13 15:11:59 +00001663
1664 if (bot > bounds.fBottom) {
1665 bot = bounds.fBottom;
1666 }
1667 SkASSERT(top < bot);
1668
reed@google.com1c04bf92011-10-10 12:57:12 +00001669 if (!rowA && !rowB) {
1670 builder.addRun(bounds.fLeft, bot - 1, 0, bounds.width());
1671 } else if (top >= bounds.fTop) {
1672 SkASSERT(bot <= bounds.fBottom);
1673 RowIter rowIterA(rowA, rowA ? A.getBounds() : bounds);
1674 RowIter rowIterB(rowB, rowB ? B.getBounds() : bounds);
reed@google.com32287892011-10-05 16:27:44 +00001675 operatorX(builder, bot - 1, rowIterA, rowIterB, proc, bounds);
reed@google.com32287892011-10-05 16:27:44 +00001676 }
1677
reed@google.com1c04bf92011-10-10 12:57:12 +00001678 adjust_iter(iterA, topA, botA, bot);
1679 adjust_iter(iterB, topB, botB, bot);
1680 } while (!iterA.done() || !iterB.done());
reed@google.com32287892011-10-05 16:27:44 +00001681}
1682
1683bool SkAAClip::op(const SkAAClip& clipAOrig, const SkAAClip& clipBOrig,
1684 SkRegion::Op op) {
reed@google.com045e62d2011-10-24 12:19:46 +00001685 AUTO_AACLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001686
reed@google.com32287892011-10-05 16:27:44 +00001687 if (SkRegion::kReplace_Op == op) {
1688 return this->set(clipBOrig);
1689 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001690
reed@google.com32287892011-10-05 16:27:44 +00001691 const SkAAClip* clipA = &clipAOrig;
1692 const SkAAClip* clipB = &clipBOrig;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001693
reed@google.com32287892011-10-05 16:27:44 +00001694 if (SkRegion::kReverseDifference_Op == op) {
1695 SkTSwap(clipA, clipB);
1696 op = SkRegion::kDifference_Op;
1697 }
1698
1699 bool a_empty = clipA->isEmpty();
1700 bool b_empty = clipB->isEmpty();
1701
1702 SkIRect bounds;
1703 switch (op) {
1704 case SkRegion::kDifference_Op:
1705 if (a_empty) {
1706 return this->setEmpty();
1707 }
1708 if (b_empty || !SkIRect::Intersects(clipA->fBounds, clipB->fBounds)) {
1709 return this->set(*clipA);
1710 }
1711 bounds = clipA->fBounds;
1712 break;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001713
reed@google.com32287892011-10-05 16:27:44 +00001714 case SkRegion::kIntersect_Op:
1715 if ((a_empty | b_empty) || !bounds.intersect(clipA->fBounds,
1716 clipB->fBounds)) {
1717 return this->setEmpty();
1718 }
1719 break;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001720
reed@google.com32287892011-10-05 16:27:44 +00001721 case SkRegion::kUnion_Op:
1722 case SkRegion::kXOR_Op:
1723 if (a_empty) {
1724 return this->set(*clipB);
1725 }
1726 if (b_empty) {
1727 return this->set(*clipA);
1728 }
1729 bounds = clipA->fBounds;
1730 bounds.join(clipB->fBounds);
1731 break;
1732
1733 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001734 SkDEBUGFAIL("unknown region op");
reed@google.com32287892011-10-05 16:27:44 +00001735 return !this->isEmpty();
1736 }
1737
reed@google.com32287892011-10-05 16:27:44 +00001738 SkASSERT(SkIRect::Intersects(bounds, clipB->fBounds));
1739 SkASSERT(SkIRect::Intersects(bounds, clipB->fBounds));
1740
1741 Builder builder(bounds);
1742 operateY(builder, *clipA, *clipB, op);
reed@google.com045e62d2011-10-24 12:19:46 +00001743
1744 return builder.finish(this);
reed@google.come36707a2011-10-04 21:38:55 +00001745}
1746
reed@google.com045e62d2011-10-24 12:19:46 +00001747/*
1748 * It can be expensive to build a local aaclip before applying the op, so
1749 * we first see if we can restrict the bounds of new rect to our current
1750 * bounds, or note that the new rect subsumes our current clip.
1751 */
1752
1753bool SkAAClip::op(const SkIRect& rOrig, SkRegion::Op op) {
1754 SkIRect rStorage;
1755 const SkIRect* r = &rOrig;
1756
1757 switch (op) {
1758 case SkRegion::kIntersect_Op:
1759 if (!rStorage.intersect(rOrig, fBounds)) {
1760 // no overlap, so we're empty
1761 return this->setEmpty();
1762 }
1763 if (rStorage == fBounds) {
1764 // we were wholly inside the rect, no change
1765 return !this->isEmpty();
1766 }
1767 if (this->quickContains(rStorage)) {
1768 // the intersection is wholly inside us, we're a rect
1769 return this->setRect(rStorage);
1770 }
1771 r = &rStorage; // use the intersected bounds
1772 break;
1773 case SkRegion::kDifference_Op:
1774 break;
1775 case SkRegion::kUnion_Op:
1776 if (rOrig.contains(fBounds)) {
1777 return this->setRect(rOrig);
1778 }
1779 break;
1780 default:
1781 break;
1782 }
1783
reed@google.com47ac84e2011-10-06 13:11:25 +00001784 SkAAClip clip;
reed@google.com045e62d2011-10-24 12:19:46 +00001785 clip.setRect(*r);
reed@google.com47ac84e2011-10-06 13:11:25 +00001786 return this->op(*this, clip, op);
1787}
1788
reed@google.com045e62d2011-10-24 12:19:46 +00001789bool SkAAClip::op(const SkRect& rOrig, SkRegion::Op op, bool doAA) {
1790 SkRect rStorage, boundsStorage;
1791 const SkRect* r = &rOrig;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001792
reed@google.com045e62d2011-10-24 12:19:46 +00001793 boundsStorage.set(fBounds);
1794 switch (op) {
1795 case SkRegion::kIntersect_Op:
1796 case SkRegion::kDifference_Op:
1797 if (!rStorage.intersect(rOrig, boundsStorage)) {
reed@google.come56513d2012-06-25 20:06:33 +00001798 if (SkRegion::kIntersect_Op == op) {
1799 return this->setEmpty();
1800 } else { // kDifference
1801 return !this->isEmpty();
1802 }
reed@google.com045e62d2011-10-24 12:19:46 +00001803 }
1804 r = &rStorage; // use the intersected bounds
1805 break;
1806 case SkRegion::kUnion_Op:
1807 if (rOrig.contains(boundsStorage)) {
1808 return this->setRect(rOrig);
1809 }
1810 break;
1811 default:
1812 break;
1813 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001814
reed@google.com47ac84e2011-10-06 13:11:25 +00001815 SkAAClip clip;
reed@google.com045e62d2011-10-24 12:19:46 +00001816 clip.setRect(*r, doAA);
reed@google.com47ac84e2011-10-06 13:11:25 +00001817 return this->op(*this, clip, op);
1818}
1819
1820bool SkAAClip::op(const SkAAClip& clip, SkRegion::Op op) {
1821 return this->op(*this, clip, op);
1822}
1823
reed@google.come36707a2011-10-04 21:38:55 +00001824///////////////////////////////////////////////////////////////////////////////
reed@google.com045e62d2011-10-24 12:19:46 +00001825
1826bool SkAAClip::translate(int dx, int dy, SkAAClip* dst) const {
halcanary96fcdcc2015-08-27 07:41:13 -07001827 if (nullptr == dst) {
reed@google.com045e62d2011-10-24 12:19:46 +00001828 return !this->isEmpty();
1829 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001830
reed@google.com045e62d2011-10-24 12:19:46 +00001831 if (this->isEmpty()) {
1832 return dst->setEmpty();
1833 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001834
reed@google.com045e62d2011-10-24 12:19:46 +00001835 if (this != dst) {
1836 sk_atomic_inc(&fRunHead->fRefCnt);
tomhudson@google.com19224c32012-03-28 15:46:37 +00001837 dst->freeRuns();
reed@google.com045e62d2011-10-24 12:19:46 +00001838 dst->fRunHead = fRunHead;
1839 dst->fBounds = fBounds;
1840 }
1841 dst->fBounds.offset(dx, dy);
1842 return true;
1843}
1844
1845static void expand_row_to_mask(uint8_t* SK_RESTRICT mask,
1846 const uint8_t* SK_RESTRICT row,
1847 int width) {
1848 while (width > 0) {
1849 int n = row[0];
1850 SkASSERT(width >= n);
1851 memset(mask, row[1], n);
1852 mask += n;
1853 row += 2;
1854 width -= n;
1855 }
reed@google.coma069c8f2011-11-28 19:54:56 +00001856 SkASSERT(0 == width);
reed@google.com045e62d2011-10-24 12:19:46 +00001857}
1858
1859void SkAAClip::copyToMask(SkMask* mask) const {
1860 mask->fFormat = SkMask::kA8_Format;
1861 if (this->isEmpty()) {
1862 mask->fBounds.setEmpty();
halcanary96fcdcc2015-08-27 07:41:13 -07001863 mask->fImage = nullptr;
reed@google.com045e62d2011-10-24 12:19:46 +00001864 mask->fRowBytes = 0;
1865 return;
1866 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001867
reed@google.com045e62d2011-10-24 12:19:46 +00001868 mask->fBounds = fBounds;
1869 mask->fRowBytes = fBounds.width();
1870 size_t size = mask->computeImageSize();
1871 mask->fImage = SkMask::AllocImage(size);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001872
reed@google.com045e62d2011-10-24 12:19:46 +00001873 Iter iter(*this);
1874 uint8_t* dst = mask->fImage;
1875 const int width = fBounds.width();
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001876
reed@google.com045e62d2011-10-24 12:19:46 +00001877 int y = fBounds.fTop;
1878 while (!iter.done()) {
1879 do {
1880 expand_row_to_mask(dst, iter.data(), width);
1881 dst += mask->fRowBytes;
1882 } while (++y < iter.bottom());
1883 iter.next();
1884 }
1885}
1886
1887///////////////////////////////////////////////////////////////////////////////
reed@google.come36707a2011-10-04 21:38:55 +00001888///////////////////////////////////////////////////////////////////////////////
1889
1890static void expandToRuns(const uint8_t* SK_RESTRICT data, int initialCount, int width,
1891 int16_t* SK_RESTRICT runs, SkAlpha* SK_RESTRICT aa) {
1892 // we don't read our initial n from data, since the caller may have had to
1893 // clip it, hence the initialCount parameter.
1894 int n = initialCount;
1895 for (;;) {
1896 if (n > width) {
1897 n = width;
1898 }
1899 SkASSERT(n > 0);
1900 runs[0] = n;
1901 runs += n;
1902
1903 aa[0] = data[1];
1904 aa += n;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001905
reed@google.come36707a2011-10-04 21:38:55 +00001906 data += 2;
1907 width -= n;
1908 if (0 == width) {
1909 break;
1910 }
1911 // load the next count
1912 n = data[0];
1913 }
1914 runs[0] = 0; // sentinel
1915}
1916
1917SkAAClipBlitter::~SkAAClipBlitter() {
reed@google.com045e62d2011-10-24 12:19:46 +00001918 sk_free(fScanlineScratch);
reed@google.come36707a2011-10-04 21:38:55 +00001919}
1920
1921void SkAAClipBlitter::ensureRunsAndAA() {
halcanary96fcdcc2015-08-27 07:41:13 -07001922 if (nullptr == fScanlineScratch) {
reed@google.come36707a2011-10-04 21:38:55 +00001923 // add 1 so we can store the terminating run count of 0
1924 int count = fAAClipBounds.width() + 1;
reed@google.com045e62d2011-10-24 12:19:46 +00001925 // we use this either for fRuns + fAA, or a scaline of a mask
1926 // which may be as deep as 32bits
1927 fScanlineScratch = sk_malloc_throw(count * sizeof(SkPMColor));
1928 fRuns = (int16_t*)fScanlineScratch;
reed@google.come36707a2011-10-04 21:38:55 +00001929 fAA = (SkAlpha*)(fRuns + count);
1930 }
1931}
1932
1933void SkAAClipBlitter::blitH(int x, int y, int width) {
1934 SkASSERT(width > 0);
1935 SkASSERT(fAAClipBounds.contains(x, y));
1936 SkASSERT(fAAClipBounds.contains(x + width - 1, y));
1937
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001938 const uint8_t* row = fAAClip->findRow(y);
reed@google.come36707a2011-10-04 21:38:55 +00001939 int initialCount;
1940 row = fAAClip->findX(row, x, &initialCount);
1941
1942 if (initialCount >= width) {
1943 SkAlpha alpha = row[1];
1944 if (0 == alpha) {
1945 return;
1946 }
1947 if (0xFF == alpha) {
1948 fBlitter->blitH(x, y, width);
1949 return;
1950 }
1951 }
1952
1953 this->ensureRunsAndAA();
1954 expandToRuns(row, initialCount, width, fRuns, fAA);
1955
1956 fBlitter->blitAntiH(x, y, fAA, fRuns);
1957}
1958
1959static void merge(const uint8_t* SK_RESTRICT row, int rowN,
1960 const SkAlpha* SK_RESTRICT srcAA,
1961 const int16_t* SK_RESTRICT srcRuns,
1962 SkAlpha* SK_RESTRICT dstAA,
1963 int16_t* SK_RESTRICT dstRuns,
1964 int width) {
1965 SkDEBUGCODE(int accumulated = 0;)
1966 int srcN = srcRuns[0];
reed@google.com045e62d2011-10-24 12:19:46 +00001967 // do we need this check?
1968 if (0 == srcN) {
1969 return;
1970 }
1971
reed@google.come36707a2011-10-04 21:38:55 +00001972 for (;;) {
reed@google.come36707a2011-10-04 21:38:55 +00001973 SkASSERT(rowN > 0);
1974 SkASSERT(srcN > 0);
1975
1976 unsigned newAlpha = SkMulDiv255Round(srcAA[0], row[1]);
1977 int minN = SkMin32(srcN, rowN);
1978 dstRuns[0] = minN;
1979 dstRuns += minN;
1980 dstAA[0] = newAlpha;
1981 dstAA += minN;
1982
1983 if (0 == (srcN -= minN)) {
1984 srcN = srcRuns[0]; // refresh
1985 srcRuns += srcN;
1986 srcAA += srcN;
1987 srcN = srcRuns[0]; // reload
reed@google.com045e62d2011-10-24 12:19:46 +00001988 if (0 == srcN) {
1989 break;
1990 }
reed@google.come36707a2011-10-04 21:38:55 +00001991 }
1992 if (0 == (rowN -= minN)) {
1993 row += 2;
1994 rowN = row[0]; // reload
1995 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001996
reed@google.come36707a2011-10-04 21:38:55 +00001997 SkDEBUGCODE(accumulated += minN;)
1998 SkASSERT(accumulated <= width);
1999 }
reed@google.com34f7e472011-10-13 15:11:59 +00002000 dstRuns[0] = 0;
reed@google.come36707a2011-10-04 21:38:55 +00002001}
2002
2003void SkAAClipBlitter::blitAntiH(int x, int y, const SkAlpha aa[],
2004 const int16_t runs[]) {
reed@google.coma4c6e4d2012-06-20 14:29:50 +00002005
2006 const uint8_t* row = fAAClip->findRow(y);
reed@google.come36707a2011-10-04 21:38:55 +00002007 int initialCount;
2008 row = fAAClip->findX(row, x, &initialCount);
2009
2010 this->ensureRunsAndAA();
2011
2012 merge(row, initialCount, aa, runs, fAA, fRuns, fAAClipBounds.width());
2013 fBlitter->blitAntiH(x, y, fAA, fRuns);
2014}
2015
2016void SkAAClipBlitter::blitV(int x, int y, int height, SkAlpha alpha) {
2017 if (fAAClip->quickContains(x, y, x + 1, y + height)) {
2018 fBlitter->blitV(x, y, height, alpha);
2019 return;
2020 }
2021
reed@google.com045e62d2011-10-24 12:19:46 +00002022 for (;;) {
reed@google.coma4c6e4d2012-06-20 14:29:50 +00002023 int lastY SK_INIT_TO_AVOID_WARNING;
reed@google.come36707a2011-10-04 21:38:55 +00002024 const uint8_t* row = fAAClip->findRow(y, &lastY);
reed@google.com045e62d2011-10-24 12:19:46 +00002025 int dy = lastY - y + 1;
2026 if (dy > height) {
2027 dy = height;
2028 }
2029 height -= dy;
2030
reed@google.coma4c6e4d2012-06-20 14:29:50 +00002031 row = fAAClip->findX(row, x);
reed@google.come36707a2011-10-04 21:38:55 +00002032 SkAlpha newAlpha = SkMulDiv255Round(alpha, row[1]);
2033 if (newAlpha) {
reed@google.com045e62d2011-10-24 12:19:46 +00002034 fBlitter->blitV(x, y, dy, newAlpha);
2035 }
2036 SkASSERT(height >= 0);
2037 if (height <= 0) {
2038 break;
reed@google.come36707a2011-10-04 21:38:55 +00002039 }
2040 y = lastY + 1;
reed@google.com045e62d2011-10-24 12:19:46 +00002041 }
reed@google.come36707a2011-10-04 21:38:55 +00002042}
2043
2044void SkAAClipBlitter::blitRect(int x, int y, int width, int height) {
2045 if (fAAClip->quickContains(x, y, x + width, y + height)) {
2046 fBlitter->blitRect(x, y, width, height);
2047 return;
2048 }
2049
2050 while (--height >= 0) {
2051 this->blitH(x, y, width);
2052 y += 1;
2053 }
2054}
2055
reed@google.com045e62d2011-10-24 12:19:46 +00002056typedef void (*MergeAAProc)(const void* src, int width, const uint8_t* row,
2057 int initialRowCount, void* dst);
2058
2059static void small_memcpy(void* dst, const void* src, size_t n) {
2060 memcpy(dst, src, n);
2061}
2062
2063static void small_bzero(void* dst, size_t n) {
2064 sk_bzero(dst, n);
2065}
2066
2067static inline uint8_t mergeOne(uint8_t value, unsigned alpha) {
2068 return SkMulDiv255Round(value, alpha);
2069}
reedd54d3fc2014-11-13 14:39:58 -08002070
reed@google.com045e62d2011-10-24 12:19:46 +00002071static inline uint16_t mergeOne(uint16_t value, unsigned alpha) {
2072 unsigned r = SkGetPackedR16(value);
2073 unsigned g = SkGetPackedG16(value);
2074 unsigned b = SkGetPackedB16(value);
2075 return SkPackRGB16(SkMulDiv255Round(r, alpha),
caryclark@google.com803eceb2012-06-06 12:09:34 +00002076 SkMulDiv255Round(g, alpha),
2077 SkMulDiv255Round(b, alpha));
reed@google.com045e62d2011-10-24 12:19:46 +00002078}
reed@google.com045e62d2011-10-24 12:19:46 +00002079
herb94496162015-12-10 14:17:41 -08002080template <typename T>
2081void mergeT(const void* inSrc, int srcN, const uint8_t* SK_RESTRICT row, int rowN, void* inDst) {
2082 const T* SK_RESTRICT src = static_cast<const T*>(inSrc);
2083 T* SK_RESTRICT dst = static_cast<T*>(inDst);
reed@google.com045e62d2011-10-24 12:19:46 +00002084 for (;;) {
2085 SkASSERT(rowN > 0);
2086 SkASSERT(srcN > 0);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002087
reed@google.com045e62d2011-10-24 12:19:46 +00002088 int n = SkMin32(rowN, srcN);
2089 unsigned rowA = row[1];
2090 if (0xFF == rowA) {
2091 small_memcpy(dst, src, n * sizeof(T));
2092 } else if (0 == rowA) {
2093 small_bzero(dst, n * sizeof(T));
2094 } else {
2095 for (int i = 0; i < n; ++i) {
2096 dst[i] = mergeOne(src[i], rowA);
2097 }
2098 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002099
reed@google.com045e62d2011-10-24 12:19:46 +00002100 if (0 == (srcN -= n)) {
2101 break;
2102 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002103
reed@google.com045e62d2011-10-24 12:19:46 +00002104 src += n;
2105 dst += n;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002106
reed@google.com045e62d2011-10-24 12:19:46 +00002107 SkASSERT(rowN == n);
2108 row += 2;
2109 rowN = row[0];
2110 }
2111}
2112
2113static MergeAAProc find_merge_aa_proc(SkMask::Format format) {
2114 switch (format) {
2115 case SkMask::kBW_Format:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002116 SkDEBUGFAIL("unsupported");
halcanary96fcdcc2015-08-27 07:41:13 -07002117 return nullptr;
reed@google.com045e62d2011-10-24 12:19:46 +00002118 case SkMask::kA8_Format:
herb94496162015-12-10 14:17:41 -08002119 case SkMask::k3D_Format:
2120 return mergeT<uint8_t> ;
2121 case SkMask::kLCD16_Format:
2122 return mergeT<uint16_t>;
reed@google.com045e62d2011-10-24 12:19:46 +00002123 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002124 SkDEBUGFAIL("unsupported");
halcanary96fcdcc2015-08-27 07:41:13 -07002125 return nullptr;
reed@google.com045e62d2011-10-24 12:19:46 +00002126 }
2127}
2128
2129static U8CPU bit2byte(int bitInAByte) {
2130 SkASSERT(bitInAByte <= 0xFF);
2131 // negation turns any non-zero into 0xFFFFFF??, so we just shift down
2132 // some value >= 8 to get a full FF value
2133 return -bitInAByte >> 8;
2134}
2135
2136static void upscaleBW2A8(SkMask* dstMask, const SkMask& srcMask) {
2137 SkASSERT(SkMask::kBW_Format == srcMask.fFormat);
2138 SkASSERT(SkMask::kA8_Format == dstMask->fFormat);
2139
2140 const int width = srcMask.fBounds.width();
2141 const int height = srcMask.fBounds.height();
2142
2143 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcMask.fImage;
2144 const size_t srcRB = srcMask.fRowBytes;
2145 uint8_t* SK_RESTRICT dst = (uint8_t*)dstMask->fImage;
2146 const size_t dstRB = dstMask->fRowBytes;
2147
2148 const int wholeBytes = width >> 3;
2149 const int leftOverBits = width & 7;
2150
2151 for (int y = 0; y < height; ++y) {
2152 uint8_t* SK_RESTRICT d = dst;
2153 for (int i = 0; i < wholeBytes; ++i) {
2154 int srcByte = src[i];
2155 d[0] = bit2byte(srcByte & (1 << 7));
2156 d[1] = bit2byte(srcByte & (1 << 6));
2157 d[2] = bit2byte(srcByte & (1 << 5));
2158 d[3] = bit2byte(srcByte & (1 << 4));
2159 d[4] = bit2byte(srcByte & (1 << 3));
2160 d[5] = bit2byte(srcByte & (1 << 2));
2161 d[6] = bit2byte(srcByte & (1 << 1));
2162 d[7] = bit2byte(srcByte & (1 << 0));
2163 d += 8;
2164 }
2165 if (leftOverBits) {
2166 int srcByte = src[wholeBytes];
2167 for (int x = 0; x < leftOverBits; ++x) {
2168 *d++ = bit2byte(srcByte & 0x80);
2169 srcByte <<= 1;
2170 }
2171 }
2172 src += srcRB;
2173 dst += dstRB;
2174 }
2175}
2176
2177void SkAAClipBlitter::blitMask(const SkMask& origMask, const SkIRect& clip) {
2178 SkASSERT(fAAClip->getBounds().contains(clip));
2179
2180 if (fAAClip->quickContains(clip)) {
2181 fBlitter->blitMask(origMask, clip);
2182 return;
2183 }
2184
2185 const SkMask* mask = &origMask;
2186
2187 // if we're BW, we need to upscale to A8 (ugh)
2188 SkMask grayMask;
reed@google.com045e62d2011-10-24 12:19:46 +00002189 if (SkMask::kBW_Format == origMask.fFormat) {
2190 grayMask.fFormat = SkMask::kA8_Format;
2191 grayMask.fBounds = origMask.fBounds;
2192 grayMask.fRowBytes = origMask.fBounds.width();
2193 size_t size = grayMask.computeImageSize();
2194 grayMask.fImage = (uint8_t*)fGrayMaskScratch.reset(size,
2195 SkAutoMalloc::kReuse_OnShrink);
2196
2197 upscaleBW2A8(&grayMask, origMask);
2198 mask = &grayMask;
2199 }
2200
2201 this->ensureRunsAndAA();
2202
2203 // HACK -- we are devolving 3D into A8, need to copy the rest of the 3D
2204 // data into a temp block to support it better (ugh)
2205
2206 const void* src = mask->getAddr(clip.fLeft, clip.fTop);
2207 const size_t srcRB = mask->fRowBytes;
2208 const int width = clip.width();
2209 MergeAAProc mergeProc = find_merge_aa_proc(mask->fFormat);
2210
2211 SkMask rowMask;
2212 rowMask.fFormat = SkMask::k3D_Format == mask->fFormat ? SkMask::kA8_Format : mask->fFormat;
2213 rowMask.fBounds.fLeft = clip.fLeft;
2214 rowMask.fBounds.fRight = clip.fRight;
2215 rowMask.fRowBytes = mask->fRowBytes; // doesn't matter, since our height==1
2216 rowMask.fImage = (uint8_t*)fScanlineScratch;
2217
2218 int y = clip.fTop;
2219 const int stopY = y + clip.height();
2220
2221 do {
reed@google.coma4c6e4d2012-06-20 14:29:50 +00002222 int localStopY SK_INIT_TO_AVOID_WARNING;
reed@google.com045e62d2011-10-24 12:19:46 +00002223 const uint8_t* row = fAAClip->findRow(y, &localStopY);
2224 // findRow returns last Y, not stop, so we add 1
2225 localStopY = SkMin32(localStopY + 1, stopY);
2226
2227 int initialCount;
2228 row = fAAClip->findX(row, clip.fLeft, &initialCount);
2229 do {
2230 mergeProc(src, width, row, initialCount, rowMask.fImage);
2231 rowMask.fBounds.fTop = y;
2232 rowMask.fBounds.fBottom = y + 1;
2233 fBlitter->blitMask(rowMask, rowMask.fBounds);
2234 src = (const void*)((const char*)src + srcRB);
2235 } while (++y < localStopY);
2236 } while (y < stopY);
reed@google.come36707a2011-10-04 21:38:55 +00002237}
2238
reed41e010c2015-06-09 12:16:53 -07002239const SkPixmap* SkAAClipBlitter::justAnOpaqueColor(uint32_t* value) {
halcanary96fcdcc2015-08-27 07:41:13 -07002240 return nullptr;
reed@google.come36707a2011-10-04 21:38:55 +00002241}