blob: 33947382c4dee0e8d710daf1caa149c1f6727b4e [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"
Hal Canaryc640d0d2018-06-13 09:59:02 -04009
herbb906daf2015-09-29 09:37:59 -070010#include "SkAtomics.h"
reed@google.come36707a2011-10-04 21:38:55 +000011#include "SkBlitter.h"
Cary Clarka4083c92017-09-15 11:59:23 -040012#include "SkColorData.h"
Hal Canary50dbc092018-06-12 14:50:37 -040013#include "SkMacros.h"
reed@google.come36707a2011-10-04 21:38:55 +000014#include "SkPath.h"
Hal Canary2a2f6752018-06-11 21:44:01 -040015#include "SkRectPriv.h"
reed@google.come36707a2011-10-04 21:38:55 +000016#include "SkScan.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040017#include "SkTo.h"
Hal Canaryea60b952018-08-21 11:45:46 -040018#include "SkUTF.h"
reed@google.come36707a2011-10-04 21:38:55 +000019
Ben Wagnerf08d1d02018-06-18 15:11:00 -040020#include <utility>
21
reed@google.com045e62d2011-10-24 12:19:46 +000022class AutoAAClipValidate {
23public:
24 AutoAAClipValidate(const SkAAClip& clip) : fClip(clip) {
25 fClip.validate();
26 }
27 ~AutoAAClipValidate() {
28 fClip.validate();
29 }
30private:
31 const SkAAClip& fClip;
32};
33
34#ifdef SK_DEBUG
35 #define AUTO_AACLIP_VALIDATE(clip) AutoAAClipValidate acv(clip)
36#else
37 #define AUTO_AACLIP_VALIDATE(clip)
38#endif
39
40///////////////////////////////////////////////////////////////////////////////
41
reed@google.com1c04bf92011-10-10 12:57:12 +000042#define kMaxInt32 0x7FFFFFFF
43
commit-bot@chromium.org4b7d6732013-10-21 16:41:00 +000044#ifdef SK_DEBUG
reed@google.come36707a2011-10-04 21:38:55 +000045static inline bool x_in_rect(int x, const SkIRect& rect) {
46 return (unsigned)(x - rect.fLeft) < (unsigned)rect.width();
47}
commit-bot@chromium.org4b7d6732013-10-21 16:41:00 +000048#endif
reed@google.come36707a2011-10-04 21:38:55 +000049
50static inline bool y_in_rect(int y, const SkIRect& rect) {
51 return (unsigned)(y - rect.fTop) < (unsigned)rect.height();
52}
53
54/*
55 * Data runs are packed [count, alpha]
56 */
57
58struct SkAAClip::YOffset {
59 int32_t fY;
60 uint32_t fOffset;
61};
62
63struct SkAAClip::RunHead {
64 int32_t fRefCnt;
65 int32_t fRowCount;
scroggo@google.com493c65f2013-02-05 18:49:00 +000066 size_t fDataSize;
rmistry@google.comfbfcd562012-08-23 18:09:54 +000067
reed@google.come36707a2011-10-04 21:38:55 +000068 YOffset* yoffsets() {
69 return (YOffset*)((char*)this + sizeof(RunHead));
70 }
71 const YOffset* yoffsets() const {
72 return (const YOffset*)((const char*)this + sizeof(RunHead));
73 }
74 uint8_t* data() {
75 return (uint8_t*)(this->yoffsets() + fRowCount);
76 }
77 const uint8_t* data() const {
78 return (const uint8_t*)(this->yoffsets() + fRowCount);
79 }
80
81 static RunHead* Alloc(int rowCount, size_t dataSize) {
82 size_t size = sizeof(RunHead) + rowCount * sizeof(YOffset) + dataSize;
83 RunHead* head = (RunHead*)sk_malloc_throw(size);
84 head->fRefCnt = 1;
85 head->fRowCount = rowCount;
86 head->fDataSize = dataSize;
87 return head;
88 }
reed@google.com045e62d2011-10-24 12:19:46 +000089
90 static int ComputeRowSizeForWidth(int width) {
91 // 2 bytes per segment, where each segment can store up to 255 for count
92 int segments = 0;
93 while (width > 0) {
94 segments += 1;
95 int n = SkMin32(width, 255);
96 width -= n;
97 }
98 return segments * 2; // each segment is row[0] + row[1] (n + alpha)
99 }
100
101 static RunHead* AllocRect(const SkIRect& bounds) {
102 SkASSERT(!bounds.isEmpty());
103 int width = bounds.width();
104 size_t rowSize = ComputeRowSizeForWidth(width);
105 RunHead* head = RunHead::Alloc(1, rowSize);
106 YOffset* yoff = head->yoffsets();
107 yoff->fY = bounds.height() - 1;
108 yoff->fOffset = 0;
109 uint8_t* row = head->data();
110 while (width > 0) {
111 int n = SkMin32(width, 255);
112 row[0] = n;
113 row[1] = 0xFF;
114 width -= n;
115 row += 2;
116 }
117 return head;
118 }
reed@google.come36707a2011-10-04 21:38:55 +0000119};
120
reed@google.com32287892011-10-05 16:27:44 +0000121class SkAAClip::Iter {
122public:
123 Iter(const SkAAClip&);
124
125 bool done() const { return fDone; }
reed@google.com1c04bf92011-10-10 12:57:12 +0000126 int top() const { return fTop; }
127 int bottom() const { return fBottom; }
128 const uint8_t* data() const { return fData; }
reed@google.com32287892011-10-05 16:27:44 +0000129 void next();
130
131private:
132 const YOffset* fCurrYOff;
133 const YOffset* fStopYOff;
134 const uint8_t* fData;
135
136 int fTop, fBottom;
137 bool fDone;
138};
139
140SkAAClip::Iter::Iter(const SkAAClip& clip) {
141 if (clip.isEmpty()) {
142 fDone = true;
reed@google.com1c04bf92011-10-10 12:57:12 +0000143 fTop = fBottom = clip.fBounds.fBottom;
halcanary96fcdcc2015-08-27 07:41:13 -0700144 fData = nullptr;
145 fCurrYOff = nullptr;
146 fStopYOff = nullptr;
reed@google.com32287892011-10-05 16:27:44 +0000147 return;
148 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000149
reed@google.com32287892011-10-05 16:27:44 +0000150 const RunHead* head = clip.fRunHead;
151 fCurrYOff = head->yoffsets();
152 fStopYOff = fCurrYOff + head->fRowCount;
153 fData = head->data() + fCurrYOff->fOffset;
154
155 // setup first value
156 fTop = clip.fBounds.fTop;
157 fBottom = clip.fBounds.fTop + fCurrYOff->fY + 1;
158 fDone = false;
159}
160
161void SkAAClip::Iter::next() {
reed@google.com1c04bf92011-10-10 12:57:12 +0000162 if (!fDone) {
163 const YOffset* prev = fCurrYOff;
164 const YOffset* curr = prev + 1;
165 SkASSERT(curr <= fStopYOff);
reed@google.com32287892011-10-05 16:27:44 +0000166
reed@google.com32287892011-10-05 16:27:44 +0000167 fTop = fBottom;
reed@google.com1c04bf92011-10-10 12:57:12 +0000168 if (curr >= fStopYOff) {
169 fDone = true;
170 fBottom = kMaxInt32;
halcanary96fcdcc2015-08-27 07:41:13 -0700171 fData = nullptr;
reed@google.com1c04bf92011-10-10 12:57:12 +0000172 } else {
173 fBottom += curr->fY - prev->fY;
174 fData += curr->fOffset - prev->fOffset;
175 fCurrYOff = curr;
176 }
reed@google.com32287892011-10-05 16:27:44 +0000177 }
178}
179
reed@google.com045e62d2011-10-24 12:19:46 +0000180#ifdef SK_DEBUG
reed@google.comc9041912011-10-27 16:58:46 +0000181// assert we're exactly width-wide, and then return the number of bytes used
reed@google.com045e62d2011-10-24 12:19:46 +0000182static size_t compute_row_length(const uint8_t row[], int width) {
183 const uint8_t* origRow = row;
184 while (width > 0) {
185 int n = row[0];
reed@google.comc9041912011-10-27 16:58:46 +0000186 SkASSERT(n > 0);
reed@google.com045e62d2011-10-24 12:19:46 +0000187 SkASSERT(n <= width);
188 row += 2;
189 width -= n;
190 }
191 SkASSERT(0 == width);
192 return row - origRow;
193}
194
195void SkAAClip::validate() const {
halcanary96fcdcc2015-08-27 07:41:13 -0700196 if (nullptr == fRunHead) {
reed@google.com045e62d2011-10-24 12:19:46 +0000197 SkASSERT(fBounds.isEmpty());
198 return;
199 }
reedd7ec12e2016-06-20 10:21:24 -0700200 SkASSERT(!fBounds.isEmpty());
reed@google.com045e62d2011-10-24 12:19:46 +0000201
202 const RunHead* head = fRunHead;
203 SkASSERT(head->fRefCnt > 0);
204 SkASSERT(head->fRowCount > 0);
reed@google.com045e62d2011-10-24 12:19:46 +0000205
206 const YOffset* yoff = head->yoffsets();
207 const YOffset* ystop = yoff + head->fRowCount;
reed@google.comc9041912011-10-27 16:58:46 +0000208 const int lastY = fBounds.height() - 1;
209
210 // Y and offset must be monotonic
211 int prevY = -1;
212 int32_t prevOffset = -1;
reed@google.com045e62d2011-10-24 12:19:46 +0000213 while (yoff < ystop) {
reed@google.comc9041912011-10-27 16:58:46 +0000214 SkASSERT(prevY < yoff->fY);
215 SkASSERT(yoff->fY <= lastY);
216 prevY = yoff->fY;
217 SkASSERT(prevOffset < (int32_t)yoff->fOffset);
218 prevOffset = yoff->fOffset;
219 const uint8_t* row = head->data() + yoff->fOffset;
reed@google.com045e62d2011-10-24 12:19:46 +0000220 size_t rowLength = compute_row_length(row, fBounds.width());
scroggo@google.com493c65f2013-02-05 18:49:00 +0000221 SkASSERT(yoff->fOffset + rowLength <= head->fDataSize);
reed@google.comc9041912011-10-27 16:58:46 +0000222 yoff += 1;
reed@google.com045e62d2011-10-24 12:19:46 +0000223 }
reed@google.com045e62d2011-10-24 12:19:46 +0000224 // check the last entry;
225 --yoff;
reed@google.comc9041912011-10-27 16:58:46 +0000226 SkASSERT(yoff->fY == lastY);
reed@google.com045e62d2011-10-24 12:19:46 +0000227}
humper6d42d9c2014-08-08 11:45:46 -0700228
229static void dump_one_row(const uint8_t* SK_RESTRICT row,
230 int width, int leading_num) {
231 if (leading_num) {
232 SkDebugf( "%03d ", leading_num );
233 }
234 while (width > 0) {
235 int n = row[0];
236 int val = row[1];
237 char out = '.';
238 if (val == 0xff) {
239 out = '*';
240 } else if (val > 0) {
241 out = '+';
242 }
243 for (int i = 0 ; i < n ; i++) {
244 SkDebugf( "%c", out );
245 }
246 row += 2;
247 width -= n;
248 }
249 SkDebugf( "\n" );
250}
251
252void SkAAClip::debug(bool compress_y) const {
253 Iter iter(*this);
254 const int width = fBounds.width();
255
256 int y = fBounds.fTop;
257 while (!iter.done()) {
258 if (compress_y) {
259 dump_one_row(iter.data(), width, iter.bottom() - iter.top() + 1);
260 } else {
261 do {
262 dump_one_row(iter.data(), width, 0);
263 } while (++y < iter.bottom());
264 }
265 iter.next();
266 }
267}
reed@google.com045e62d2011-10-24 12:19:46 +0000268#endif
269
270///////////////////////////////////////////////////////////////////////////////
271
robertphillips@google.com768fee82012-08-02 12:42:43 +0000272// Count the number of zeros on the left and right edges of the passed in
273// RLE row. If 'row' is all zeros return 'width' in both variables.
reed@google.comc9041912011-10-27 16:58:46 +0000274static void count_left_right_zeros(const uint8_t* row, int width,
275 int* leftZ, int* riteZ) {
276 int zeros = 0;
277 do {
278 if (row[1]) {
279 break;
280 }
281 int n = row[0];
282 SkASSERT(n > 0);
283 SkASSERT(n <= width);
284 zeros += n;
285 row += 2;
286 width -= n;
287 } while (width > 0);
288 *leftZ = zeros;
289
robertphillips@google.com768fee82012-08-02 12:42:43 +0000290 if (0 == width) {
291 // this line is completely empty return 'width' in both variables
292 *riteZ = *leftZ;
293 return;
294 }
295
reed@google.comc9041912011-10-27 16:58:46 +0000296 zeros = 0;
297 while (width > 0) {
298 int n = row[0];
299 SkASSERT(n > 0);
300 if (0 == row[1]) {
301 zeros += n;
302 } else {
303 zeros = 0;
304 }
305 row += 2;
306 width -= n;
307 }
308 *riteZ = zeros;
309}
310
reed@google.comc9041912011-10-27 16:58:46 +0000311// modify row in place, trimming off (zeros) from the left and right sides.
312// return the number of bytes that were completely eliminated from the left
313static int trim_row_left_right(uint8_t* row, int width, int leftZ, int riteZ) {
314 int trim = 0;
315 while (leftZ > 0) {
316 SkASSERT(0 == row[1]);
317 int n = row[0];
318 SkASSERT(n > 0);
319 SkASSERT(n <= width);
320 width -= n;
321 row += 2;
322 if (n > leftZ) {
323 row[-2] = n - leftZ;
324 break;
325 }
326 trim += 2;
327 leftZ -= n;
328 SkASSERT(leftZ >= 0);
329 }
330
331 if (riteZ) {
332 // walk row to the end, and then we'll back up to trim riteZ
333 while (width > 0) {
334 int n = row[0];
335 SkASSERT(n <= width);
336 width -= n;
337 row += 2;
338 }
339 // now skip whole runs of zeros
340 do {
341 row -= 2;
342 SkASSERT(0 == row[1]);
343 int n = row[0];
344 SkASSERT(n > 0);
345 if (n > riteZ) {
346 row[0] = n - riteZ;
347 break;
348 }
349 riteZ -= n;
350 SkASSERT(riteZ >= 0);
351 } while (riteZ > 0);
352 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000353
reed@google.comc9041912011-10-27 16:58:46 +0000354 return trim;
355}
356
reed@google.comc9041912011-10-27 16:58:46 +0000357bool SkAAClip::trimLeftRight() {
reed@google.comc9041912011-10-27 16:58:46 +0000358 if (this->isEmpty()) {
359 return false;
360 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000361
reed@google.comc9041912011-10-27 16:58:46 +0000362 AUTO_AACLIP_VALIDATE(*this);
363
364 const int width = fBounds.width();
365 RunHead* head = fRunHead;
366 YOffset* yoff = head->yoffsets();
367 YOffset* stop = yoff + head->fRowCount;
368 uint8_t* base = head->data();
369
robertphillips@google.com768fee82012-08-02 12:42:43 +0000370 // After this loop, 'leftZeros' & 'rightZeros' will contain the minimum
371 // number of zeros on the left and right of the clip. This information
372 // can be used to shrink the bounding box.
reed@google.comc9041912011-10-27 16:58:46 +0000373 int leftZeros = width;
374 int riteZeros = width;
375 while (yoff < stop) {
376 int L, R;
377 count_left_right_zeros(base + yoff->fOffset, width, &L, &R);
robertphillips@google.com768fee82012-08-02 12:42:43 +0000378 SkASSERT(L + R < width || (L == width && R == width));
reed@google.comc9041912011-10-27 16:58:46 +0000379 if (L < leftZeros) {
380 leftZeros = L;
381 }
382 if (R < riteZeros) {
383 riteZeros = R;
384 }
385 if (0 == (leftZeros | riteZeros)) {
386 // no trimming to do
387 return true;
388 }
389 yoff += 1;
390 }
391
392 SkASSERT(leftZeros || riteZeros);
robertphillips@google.com768fee82012-08-02 12:42:43 +0000393 if (width == leftZeros) {
394 SkASSERT(width == riteZeros);
reed@google.comc9041912011-10-27 16:58:46 +0000395 return this->setEmpty();
396 }
397
398 this->validate();
399
400 fBounds.fLeft += leftZeros;
401 fBounds.fRight -= riteZeros;
402 SkASSERT(!fBounds.isEmpty());
403
404 // For now we don't realloc the storage (for time), we just shrink in place
405 // This means we don't have to do any memmoves either, since we can just
406 // play tricks with the yoff->fOffset for each row
407 yoff = head->yoffsets();
408 while (yoff < stop) {
409 uint8_t* row = base + yoff->fOffset;
410 SkDEBUGCODE((void)compute_row_length(row, width);)
411 yoff->fOffset += trim_row_left_right(row, width, leftZeros, riteZeros);
412 SkDEBUGCODE((void)compute_row_length(base + yoff->fOffset, width - leftZeros - riteZeros);)
413 yoff += 1;
414 }
415 return true;
416}
417
418static bool row_is_all_zeros(const uint8_t* row, int width) {
419 SkASSERT(width > 0);
420 do {
421 if (row[1]) {
422 return false;
423 }
424 int n = row[0];
425 SkASSERT(n <= width);
426 width -= n;
427 row += 2;
428 } while (width > 0);
429 SkASSERT(0 == width);
430 return true;
431}
432
433bool SkAAClip::trimTopBottom() {
434 if (this->isEmpty()) {
435 return false;
436 }
437
reed@google.comd6040f62011-10-28 02:39:17 +0000438 this->validate();
439
reed@google.comc9041912011-10-27 16:58:46 +0000440 const int width = fBounds.width();
441 RunHead* head = fRunHead;
442 YOffset* yoff = head->yoffsets();
443 YOffset* stop = yoff + head->fRowCount;
444 const uint8_t* base = head->data();
445
446 // Look to trim away empty rows from the top.
447 //
448 int skip = 0;
449 while (yoff < stop) {
450 const uint8_t* data = base + yoff->fOffset;
451 if (!row_is_all_zeros(data, width)) {
452 break;
453 }
454 skip += 1;
455 yoff += 1;
456 }
457 SkASSERT(skip <= head->fRowCount);
458 if (skip == head->fRowCount) {
459 return this->setEmpty();
460 }
461 if (skip > 0) {
462 // adjust fRowCount and fBounds.fTop, and slide all the data up
463 // as we remove [skip] number of YOffset entries
464 yoff = head->yoffsets();
465 int dy = yoff[skip - 1].fY + 1;
466 for (int i = skip; i < head->fRowCount; ++i) {
467 SkASSERT(yoff[i].fY >= dy);
468 yoff[i].fY -= dy;
469 }
470 YOffset* dst = head->yoffsets();
471 size_t size = head->fRowCount * sizeof(YOffset) + head->fDataSize;
472 memmove(dst, dst + skip, size - skip * sizeof(YOffset));
473
474 fBounds.fTop += dy;
475 SkASSERT(!fBounds.isEmpty());
476 head->fRowCount -= skip;
477 SkASSERT(head->fRowCount > 0);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000478
reed@google.comd6040f62011-10-28 02:39:17 +0000479 this->validate();
480 // need to reset this after the memmove
481 base = head->data();
reed@google.comc9041912011-10-27 16:58:46 +0000482 }
483
484 // Look to trim away empty rows from the bottom.
485 // We know that we have at least one non-zero row, so we can just walk
486 // backwards without checking for running past the start.
487 //
488 stop = yoff = head->yoffsets() + head->fRowCount;
489 do {
490 yoff -= 1;
491 } while (row_is_all_zeros(base + yoff->fOffset, width));
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000492 skip = SkToInt(stop - yoff - 1);
reed@google.comc9041912011-10-27 16:58:46 +0000493 SkASSERT(skip >= 0 && skip < head->fRowCount);
494 if (skip > 0) {
495 // removing from the bottom is easier than from the top, as we don't
496 // have to adjust any of the Y values, we just have to trim the array
497 memmove(stop - skip, stop, head->fDataSize);
498
499 fBounds.fBottom = fBounds.fTop + yoff->fY + 1;
500 SkASSERT(!fBounds.isEmpty());
501 head->fRowCount -= skip;
502 SkASSERT(head->fRowCount > 0);
503 }
reed@google.comd6040f62011-10-28 02:39:17 +0000504 this->validate();
reed@google.comc9041912011-10-27 16:58:46 +0000505
506 return true;
507}
508
reed@google.com045e62d2011-10-24 12:19:46 +0000509// can't validate before we're done, since trimming is part of the process of
510// making us valid after the Builder. Since we build from top to bottom, its
511// possible our fBounds.fBottom is bigger than our last scanline of data, so
512// we trim fBounds.fBottom back up.
513//
reed@google.com045e62d2011-10-24 12:19:46 +0000514// TODO: check for duplicates in X and Y to further compress our data
515//
516bool SkAAClip::trimBounds() {
517 if (this->isEmpty()) {
518 return false;
519 }
520
521 const RunHead* head = fRunHead;
522 const YOffset* yoff = head->yoffsets();
523
524 SkASSERT(head->fRowCount > 0);
525 const YOffset& lastY = yoff[head->fRowCount - 1];
526 SkASSERT(lastY.fY + 1 <= fBounds.height());
527 fBounds.fBottom = fBounds.fTop + lastY.fY + 1;
528 SkASSERT(lastY.fY + 1 == fBounds.height());
reed@google.comc9041912011-10-27 16:58:46 +0000529 SkASSERT(!fBounds.isEmpty());
530
531 return this->trimTopBottom() && this->trimLeftRight();
reed@google.com045e62d2011-10-24 12:19:46 +0000532}
533
reed@google.come36707a2011-10-04 21:38:55 +0000534///////////////////////////////////////////////////////////////////////////////
535
536void SkAAClip::freeRuns() {
reed@google.com47ac84e2011-10-06 13:11:25 +0000537 if (fRunHead) {
reed@google.come36707a2011-10-04 21:38:55 +0000538 SkASSERT(fRunHead->fRefCnt >= 1);
539 if (1 == sk_atomic_dec(&fRunHead->fRefCnt)) {
540 sk_free(fRunHead);
541 }
542 }
543}
544
545SkAAClip::SkAAClip() {
546 fBounds.setEmpty();
halcanary96fcdcc2015-08-27 07:41:13 -0700547 fRunHead = nullptr;
reed@google.come36707a2011-10-04 21:38:55 +0000548}
549
550SkAAClip::SkAAClip(const SkAAClip& src) {
reed@google.com045e62d2011-10-24 12:19:46 +0000551 SkDEBUGCODE(fBounds.setEmpty();) // need this for validate
halcanary96fcdcc2015-08-27 07:41:13 -0700552 fRunHead = nullptr;
reed@google.come36707a2011-10-04 21:38:55 +0000553 *this = src;
554}
555
556SkAAClip::~SkAAClip() {
557 this->freeRuns();
558}
559
560SkAAClip& SkAAClip::operator=(const SkAAClip& src) {
reed@google.com045e62d2011-10-24 12:19:46 +0000561 AUTO_AACLIP_VALIDATE(*this);
562 src.validate();
563
reed@google.come36707a2011-10-04 21:38:55 +0000564 if (this != &src) {
565 this->freeRuns();
566 fBounds = src.fBounds;
567 fRunHead = src.fRunHead;
reed@google.com47ac84e2011-10-06 13:11:25 +0000568 if (fRunHead) {
reed@google.come36707a2011-10-04 21:38:55 +0000569 sk_atomic_inc(&fRunHead->fRefCnt);
570 }
571 }
572 return *this;
573}
574
575bool operator==(const SkAAClip& a, const SkAAClip& b) {
reed@google.com045e62d2011-10-24 12:19:46 +0000576 a.validate();
577 b.validate();
578
reed@google.come36707a2011-10-04 21:38:55 +0000579 if (&a == &b) {
580 return true;
581 }
582 if (a.fBounds != b.fBounds) {
583 return false;
584 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000585
reed@google.come36707a2011-10-04 21:38:55 +0000586 const SkAAClip::RunHead* ah = a.fRunHead;
587 const SkAAClip::RunHead* bh = b.fRunHead;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000588
reed@google.come36707a2011-10-04 21:38:55 +0000589 // this catches empties and rects being equal
590 if (ah == bh) {
591 return true;
592 }
593
594 // now we insist that both are complex (but different ptrs)
reed@google.com47ac84e2011-10-06 13:11:25 +0000595 if (!a.fRunHead || !b.fRunHead) {
reed@google.come36707a2011-10-04 21:38:55 +0000596 return false;
597 }
598
599 return ah->fRowCount == bh->fRowCount &&
600 ah->fDataSize == bh->fDataSize &&
601 !memcmp(ah->data(), bh->data(), ah->fDataSize);
602}
603
604void SkAAClip::swap(SkAAClip& other) {
reed@google.com045e62d2011-10-24 12:19:46 +0000605 AUTO_AACLIP_VALIDATE(*this);
606 other.validate();
607
Ben Wagnerf08d1d02018-06-18 15:11:00 -0400608 using std::swap;
609 swap(fBounds, other.fBounds);
610 swap(fRunHead, other.fRunHead);
reed@google.come36707a2011-10-04 21:38:55 +0000611}
612
reed@google.com32287892011-10-05 16:27:44 +0000613bool SkAAClip::set(const SkAAClip& src) {
614 *this = src;
615 return !this->isEmpty();
616}
617
reed@google.come36707a2011-10-04 21:38:55 +0000618bool SkAAClip::setEmpty() {
619 this->freeRuns();
620 fBounds.setEmpty();
halcanary96fcdcc2015-08-27 07:41:13 -0700621 fRunHead = nullptr;
reed@google.come36707a2011-10-04 21:38:55 +0000622 return false;
623}
624
625bool SkAAClip::setRect(const SkIRect& bounds) {
Mike Reeda766ca92018-01-09 11:31:53 -0500626 if (bounds.isEmpty()) {
reed@google.come36707a2011-10-04 21:38:55 +0000627 return this->setEmpty();
628 }
reed@google.com47ac84e2011-10-06 13:11:25 +0000629
reed@google.com045e62d2011-10-24 12:19:46 +0000630 AUTO_AACLIP_VALIDATE(*this);
reed@google.com47ac84e2011-10-06 13:11:25 +0000631
reed@google.com045e62d2011-10-24 12:19:46 +0000632#if 0
reed@google.com47ac84e2011-10-06 13:11:25 +0000633 SkRect r;
634 r.set(bounds);
635 SkPath path;
636 path.addRect(r);
637 return this->setPath(path);
reed@google.com045e62d2011-10-24 12:19:46 +0000638#else
639 this->freeRuns();
640 fBounds = bounds;
641 fRunHead = RunHead::AllocRect(bounds);
642 SkASSERT(!this->isEmpty());
643 return true;
644#endif
reed@google.come36707a2011-10-04 21:38:55 +0000645}
646
reed202ab2a2014-08-07 11:48:10 -0700647bool SkAAClip::isRect() const {
648 if (this->isEmpty()) {
649 return false;
650 }
651
652 const RunHead* head = fRunHead;
653 if (head->fRowCount != 1) {
654 return false;
655 }
656 const YOffset* yoff = head->yoffsets();
657 if (yoff->fY != fBounds.fBottom - 1) {
658 return false;
659 }
660
661 const uint8_t* row = head->data() + yoff->fOffset;
662 int width = fBounds.width();
663 do {
664 if (row[1] != 0xFF) {
665 return false;
666 }
667 int n = row[0];
668 SkASSERT(n <= width);
669 width -= n;
670 row += 2;
671 } while (width > 0);
672 return true;
673}
674
reed@google.comf3c1da12011-10-10 19:35:47 +0000675bool SkAAClip::setRect(const SkRect& r, bool doAA) {
reed@google.come36707a2011-10-04 21:38:55 +0000676 if (r.isEmpty()) {
677 return this->setEmpty();
678 }
679
reed@google.com045e62d2011-10-24 12:19:46 +0000680 AUTO_AACLIP_VALIDATE(*this);
681
682 // TODO: special case this
683
reed@google.come36707a2011-10-04 21:38:55 +0000684 SkPath path;
685 path.addRect(r);
halcanary96fcdcc2015-08-27 07:41:13 -0700686 return this->setPath(path, nullptr, doAA);
reed@google.comf3c1da12011-10-10 19:35:47 +0000687}
688
reed@google.coma069c8f2011-11-28 19:54:56 +0000689static void append_run(SkTDArray<uint8_t>& array, uint8_t value, int count) {
690 SkASSERT(count >= 0);
691 while (count > 0) {
692 int n = count;
693 if (n > 255) {
694 n = 255;
695 }
696 uint8_t* data = array.append(2);
697 data[0] = n;
698 data[1] = value;
699 count -= n;
700 }
701}
702
reed@google.comf3c1da12011-10-10 19:35:47 +0000703bool SkAAClip::setRegion(const SkRegion& rgn) {
704 if (rgn.isEmpty()) {
705 return this->setEmpty();
706 }
707 if (rgn.isRect()) {
708 return this->setRect(rgn.getBounds());
709 }
reed@google.coma069c8f2011-11-28 19:54:56 +0000710
711#if 0
reed@google.comf3c1da12011-10-10 19:35:47 +0000712 SkAAClip clip;
713 SkRegion::Iterator iter(rgn);
714 for (; !iter.done(); iter.next()) {
715 clip.op(iter.rect(), SkRegion::kUnion_Op);
716 }
717 this->swap(clip);
reed@google.com3771a032011-10-11 17:18:04 +0000718 return !this->isEmpty();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000719#else
reed@google.coma069c8f2011-11-28 19:54:56 +0000720 const SkIRect& bounds = rgn.getBounds();
721 const int offsetX = bounds.fLeft;
722 const int offsetY = bounds.fTop;
723
724 SkTDArray<YOffset> yArray;
725 SkTDArray<uint8_t> xArray;
726
727 yArray.setReserve(SkMin32(bounds.height(), 1024));
Mike Reede0673952017-10-04 16:46:42 -0400728 xArray.setReserve(SkMin32(bounds.width(), 512) * 128);
reed@google.coma069c8f2011-11-28 19:54:56 +0000729
730 SkRegion::Iterator iter(rgn);
731 int prevRight = 0;
732 int prevBot = 0;
halcanary96fcdcc2015-08-27 07:41:13 -0700733 YOffset* currY = nullptr;
reed@google.coma069c8f2011-11-28 19:54:56 +0000734
735 for (; !iter.done(); iter.next()) {
736 const SkIRect& r = iter.rect();
737 SkASSERT(bounds.contains(r));
738
739 int bot = r.fBottom - offsetY;
740 SkASSERT(bot >= prevBot);
741 if (bot > prevBot) {
742 if (currY) {
743 // flush current row
744 append_run(xArray, 0, bounds.width() - prevRight);
745 }
746 // did we introduce an empty-gap from the prev row?
747 int top = r.fTop - offsetY;
748 if (top > prevBot) {
749 currY = yArray.append();
750 currY->fY = top - 1;
751 currY->fOffset = xArray.count();
752 append_run(xArray, 0, bounds.width());
753 }
754 // create a new record for this Y value
755 currY = yArray.append();
756 currY->fY = bot - 1;
757 currY->fOffset = xArray.count();
758 prevRight = 0;
759 prevBot = bot;
760 }
761
762 int x = r.fLeft - offsetX;
763 append_run(xArray, 0, x - prevRight);
764
765 int w = r.fRight - r.fLeft;
766 append_run(xArray, 0xFF, w);
767 prevRight = x + w;
768 SkASSERT(prevRight <= bounds.width());
769 }
770 // flush last row
771 append_run(xArray, 0, bounds.width() - prevRight);
772
773 // now pack everything into a RunHead
774 RunHead* head = RunHead::Alloc(yArray.count(), xArray.bytes());
775 memcpy(head->yoffsets(), yArray.begin(), yArray.bytes());
776 memcpy(head->data(), xArray.begin(), xArray.bytes());
777
778 this->setEmpty();
779 fBounds = bounds;
780 fRunHead = head;
781 this->validate();
782 return true;
783#endif
reed@google.come36707a2011-10-04 21:38:55 +0000784}
785
786///////////////////////////////////////////////////////////////////////////////
787
788const uint8_t* SkAAClip::findRow(int y, int* lastYForRow) const {
reed@google.com47ac84e2011-10-06 13:11:25 +0000789 SkASSERT(fRunHead);
reed@google.come36707a2011-10-04 21:38:55 +0000790
791 if (!y_in_rect(y, fBounds)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700792 return nullptr;
reed@google.come36707a2011-10-04 21:38:55 +0000793 }
794 y -= fBounds.y(); // our yoffs values are relative to the top
795
796 const YOffset* yoff = fRunHead->yoffsets();
797 while (yoff->fY < y) {
798 yoff += 1;
799 SkASSERT(yoff - fRunHead->yoffsets() < fRunHead->fRowCount);
800 }
801
802 if (lastYForRow) {
reed@google.com045e62d2011-10-24 12:19:46 +0000803 *lastYForRow = fBounds.y() + yoff->fY;
reed@google.come36707a2011-10-04 21:38:55 +0000804 }
805 return fRunHead->data() + yoff->fOffset;
806}
807
808const uint8_t* SkAAClip::findX(const uint8_t data[], int x, int* initialCount) const {
809 SkASSERT(x_in_rect(x, fBounds));
810 x -= fBounds.x();
811
812 // first skip up to X
813 for (;;) {
814 int n = data[0];
815 if (x < n) {
reed@google.coma4c6e4d2012-06-20 14:29:50 +0000816 if (initialCount) {
817 *initialCount = n - x;
818 }
reed@google.come36707a2011-10-04 21:38:55 +0000819 break;
820 }
821 data += 2;
822 x -= n;
823 }
824 return data;
825}
826
827bool SkAAClip::quickContains(int left, int top, int right, int bottom) const {
828 if (this->isEmpty()) {
829 return false;
830 }
831 if (!fBounds.contains(left, top, right, bottom)) {
832 return false;
833 }
reed@google.com32287892011-10-05 16:27:44 +0000834#if 0
reed@google.come36707a2011-10-04 21:38:55 +0000835 if (this->isRect()) {
836 return true;
837 }
reed@google.com32287892011-10-05 16:27:44 +0000838#endif
reed@google.come36707a2011-10-04 21:38:55 +0000839
reed@google.coma4c6e4d2012-06-20 14:29:50 +0000840 int lastY SK_INIT_TO_AVOID_WARNING;
reed@google.come36707a2011-10-04 21:38:55 +0000841 const uint8_t* row = this->findRow(top, &lastY);
842 if (lastY < bottom) {
843 return false;
844 }
845 // now just need to check in X
reed@google.com045e62d2011-10-24 12:19:46 +0000846 int count;
847 row = this->findX(row, left, &count);
848#if 0
849 return count >= (right - left) && 0xFF == row[1];
850#else
851 int rectWidth = right - left;
852 while (0xFF == row[1]) {
853 if (count >= rectWidth) {
854 return true;
855 }
856 rectWidth -= count;
857 row += 2;
858 count = row[0];
859 }
860 return false;
861#endif
reed@google.come36707a2011-10-04 21:38:55 +0000862}
863
864///////////////////////////////////////////////////////////////////////////////
865
866class SkAAClip::Builder {
867 SkIRect fBounds;
868 struct Row {
869 int fY;
870 int fWidth;
871 SkTDArray<uint8_t>* fData;
872 };
873 SkTDArray<Row> fRows;
874 Row* fCurrRow;
875 int fPrevY;
876 int fWidth;
reed@google.com209c4152011-10-26 15:03:48 +0000877 int fMinY;
reed@google.come36707a2011-10-04 21:38:55 +0000878
879public:
880 Builder(const SkIRect& bounds) : fBounds(bounds) {
881 fPrevY = -1;
882 fWidth = bounds.width();
halcanary96fcdcc2015-08-27 07:41:13 -0700883 fCurrRow = nullptr;
reed@google.com209c4152011-10-26 15:03:48 +0000884 fMinY = bounds.fTop;
reed@google.come36707a2011-10-04 21:38:55 +0000885 }
886
887 ~Builder() {
888 Row* row = fRows.begin();
889 Row* stop = fRows.end();
890 while (row < stop) {
891 delete row->fData;
892 row += 1;
893 }
894 }
895
reed@google.com32287892011-10-05 16:27:44 +0000896 const SkIRect& getBounds() const { return fBounds; }
897
reed@google.come36707a2011-10-04 21:38:55 +0000898 void addRun(int x, int y, U8CPU alpha, int count) {
899 SkASSERT(count > 0);
900 SkASSERT(fBounds.contains(x, y));
901 SkASSERT(fBounds.contains(x + count - 1, y));
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000902
reed@google.come36707a2011-10-04 21:38:55 +0000903 x -= fBounds.left();
904 y -= fBounds.top();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000905
reed@google.come36707a2011-10-04 21:38:55 +0000906 Row* row = fCurrRow;
907 if (y != fPrevY) {
908 SkASSERT(y > fPrevY);
909 fPrevY = y;
910 row = this->flushRow(true);
911 row->fY = y;
912 row->fWidth = 0;
913 SkASSERT(row->fData);
914 SkASSERT(0 == row->fData->count());
915 fCurrRow = row;
916 }
917
918 SkASSERT(row->fWidth <= x);
919 SkASSERT(row->fWidth < fBounds.width());
920
921 SkTDArray<uint8_t>& data = *row->fData;
922
923 int gap = x - row->fWidth;
924 if (gap) {
925 AppendRun(data, 0, gap);
926 row->fWidth += gap;
927 SkASSERT(row->fWidth < fBounds.width());
928 }
929
930 AppendRun(data, alpha, count);
931 row->fWidth += count;
932 SkASSERT(row->fWidth <= fBounds.width());
933 }
934
tomhudson@google.com49eac192011-12-27 13:59:20 +0000935 void addColumn(int x, int y, U8CPU alpha, int height) {
936 SkASSERT(fBounds.contains(x, y + height - 1));
937
938 this->addRun(x, y, alpha, 1);
939 this->flushRowH(fCurrRow);
940 y -= fBounds.fTop;
941 SkASSERT(y == fCurrRow->fY);
942 fCurrRow->fY = y + height - 1;
943 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000944
reed@google.com9154eb02011-10-31 16:07:28 +0000945 void addRectRun(int x, int y, int width, int height) {
946 SkASSERT(fBounds.contains(x + width - 1, y + height - 1));
947 this->addRun(x, y, 0xFF, width);
948
reed@google.coma89c77b2011-12-01 21:47:26 +0000949 // we assum the rect must be all we'll see for these scanlines
reed@google.com9154eb02011-10-31 16:07:28 +0000950 // so we ensure our row goes all the way to our right
951 this->flushRowH(fCurrRow);
952
953 y -= fBounds.fTop;
954 SkASSERT(y == fCurrRow->fY);
955 fCurrRow->fY = y + height - 1;
956 }
957
tomhudson@google.com49eac192011-12-27 13:59:20 +0000958 void addAntiRectRun(int x, int y, int width, int height,
959 SkAlpha leftAlpha, SkAlpha rightAlpha) {
liyuqian2add0ff2016-10-20 11:04:39 -0700960 // According to SkBlitter.cpp, no matter whether leftAlpha is 0 or positive,
961 // we should always consider [x, x+1] as the left-most column and [x+1, x+1+width]
962 // as the rect with full alpha.
963 SkASSERT(fBounds.contains(x + width + (rightAlpha > 0 ? 1 : 0),
tomhudson@google.com49eac192011-12-27 13:59:20 +0000964 y + height - 1));
965 SkASSERT(width >= 0);
966
967 // Conceptually we're always adding 3 runs, but we should
968 // merge or omit them if possible.
969 if (leftAlpha == 0xFF) {
970 width++;
971 } else if (leftAlpha > 0) {
972 this->addRun(x++, y, leftAlpha, 1);
liyuqian2add0ff2016-10-20 11:04:39 -0700973 } else {
974 // leftAlpha is 0, ignore the left column
975 x++;
tomhudson@google.com49eac192011-12-27 13:59:20 +0000976 }
977 if (rightAlpha == 0xFF) {
978 width++;
979 }
980 if (width > 0) {
981 this->addRun(x, y, 0xFF, width);
982 }
983 if (rightAlpha > 0 && rightAlpha < 255) {
984 this->addRun(x + width, y, rightAlpha, 1);
985 }
986
Mike Reed1b7cd332018-06-05 09:55:17 -0400987 // if we never called addRun, we might not have a fCurrRow yet
988 if (fCurrRow) {
989 // we assume the rect must be all we'll see for these scanlines
990 // so we ensure our row goes all the way to our right
991 this->flushRowH(fCurrRow);
tomhudson@google.com49eac192011-12-27 13:59:20 +0000992
Mike Reed1b7cd332018-06-05 09:55:17 -0400993 y -= fBounds.fTop;
994 SkASSERT(y == fCurrRow->fY);
995 fCurrRow->fY = y + height - 1;
996 }
tomhudson@google.com49eac192011-12-27 13:59:20 +0000997 }
998
reed@google.com045e62d2011-10-24 12:19:46 +0000999 bool finish(SkAAClip* target) {
reed@google.come36707a2011-10-04 21:38:55 +00001000 this->flushRow(false);
1001
1002 const Row* row = fRows.begin();
1003 const Row* stop = fRows.end();
1004
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001005 size_t dataSize = 0;
reed@google.come36707a2011-10-04 21:38:55 +00001006 while (row < stop) {
1007 dataSize += row->fData->count();
1008 row += 1;
1009 }
1010
reed@google.com045e62d2011-10-24 12:19:46 +00001011 if (0 == dataSize) {
1012 return target->setEmpty();
1013 }
1014
reed@google.com209c4152011-10-26 15:03:48 +00001015 SkASSERT(fMinY >= fBounds.fTop);
1016 SkASSERT(fMinY < fBounds.fBottom);
1017 int adjustY = fMinY - fBounds.fTop;
1018 fBounds.fTop = fMinY;
1019
reed@google.come36707a2011-10-04 21:38:55 +00001020 RunHead* head = RunHead::Alloc(fRows.count(), dataSize);
1021 YOffset* yoffset = head->yoffsets();
1022 uint8_t* data = head->data();
1023 uint8_t* baseData = data;
1024
1025 row = fRows.begin();
reed@google.comc9041912011-10-27 16:58:46 +00001026 SkDEBUGCODE(int prevY = row->fY - 1;)
reed@google.come36707a2011-10-04 21:38:55 +00001027 while (row < stop) {
reed@google.comc9041912011-10-27 16:58:46 +00001028 SkASSERT(prevY < row->fY); // must be monotonic
1029 SkDEBUGCODE(prevY = row->fY);
1030
reed@google.com209c4152011-10-26 15:03:48 +00001031 yoffset->fY = row->fY - adjustY;
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001032 yoffset->fOffset = SkToU32(data - baseData);
reed@google.come36707a2011-10-04 21:38:55 +00001033 yoffset += 1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001034
reed@google.come36707a2011-10-04 21:38:55 +00001035 size_t n = row->fData->count();
1036 memcpy(data, row->fData->begin(), n);
reed@google.comc9041912011-10-27 16:58:46 +00001037#ifdef SK_DEBUG
tomhudson@google.comf74ad8c2011-11-09 22:15:08 +00001038 size_t bytesNeeded = compute_row_length(data, fBounds.width());
reed@google.comc9041912011-10-27 16:58:46 +00001039 SkASSERT(bytesNeeded == n);
1040#endif
reed@google.come36707a2011-10-04 21:38:55 +00001041 data += n;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001042
reed@google.come36707a2011-10-04 21:38:55 +00001043 row += 1;
1044 }
1045
reed@google.com045e62d2011-10-24 12:19:46 +00001046 target->freeRuns();
1047 target->fBounds = fBounds;
1048 target->fRunHead = head;
1049 return target->trimBounds();
reed@google.come36707a2011-10-04 21:38:55 +00001050 }
1051
1052 void dump() {
1053 this->validate();
1054 int y;
1055 for (y = 0; y < fRows.count(); ++y) {
1056 const Row& row = fRows[y];
1057 SkDebugf("Y:%3d W:%3d", row.fY, row.fWidth);
1058 const SkTDArray<uint8_t>& data = *row.fData;
1059 int count = data.count();
1060 SkASSERT(!(count & 1));
1061 const uint8_t* ptr = data.begin();
1062 for (int x = 0; x < count; x += 2) {
1063 SkDebugf(" [%3d:%02X]", ptr[0], ptr[1]);
1064 ptr += 2;
1065 }
1066 SkDebugf("\n");
1067 }
reed@google.come36707a2011-10-04 21:38:55 +00001068 }
1069
1070 void validate() {
1071#ifdef SK_DEBUG
1072 int prevY = -1;
1073 for (int i = 0; i < fRows.count(); ++i) {
1074 const Row& row = fRows[i];
1075 SkASSERT(prevY < row.fY);
1076 SkASSERT(fWidth == row.fWidth);
1077 int count = row.fData->count();
1078 const uint8_t* ptr = row.fData->begin();
1079 SkASSERT(!(count & 1));
1080 int w = 0;
1081 for (int x = 0; x < count; x += 2) {
reed@google.comd6040f62011-10-28 02:39:17 +00001082 int n = ptr[0];
1083 SkASSERT(n > 0);
1084 w += n;
reed@google.come36707a2011-10-04 21:38:55 +00001085 SkASSERT(w <= fWidth);
1086 ptr += 2;
1087 }
1088 SkASSERT(w == fWidth);
1089 prevY = row.fY;
1090 }
1091#endif
1092 }
1093
reed@google.com209c4152011-10-26 15:03:48 +00001094 // only called by BuilderBlitter
1095 void setMinY(int y) {
1096 fMinY = y;
1097 }
1098
reed@google.come36707a2011-10-04 21:38:55 +00001099private:
reed@google.com9154eb02011-10-31 16:07:28 +00001100 void flushRowH(Row* row) {
1101 // flush current row if needed
1102 if (row->fWidth < fWidth) {
1103 AppendRun(*row->fData, 0, fWidth - row->fWidth);
1104 row->fWidth = fWidth;
1105 }
1106 }
reed@google.com209c4152011-10-26 15:03:48 +00001107
reed@google.come36707a2011-10-04 21:38:55 +00001108 Row* flushRow(bool readyForAnother) {
halcanary96fcdcc2015-08-27 07:41:13 -07001109 Row* next = nullptr;
reed@google.come36707a2011-10-04 21:38:55 +00001110 int count = fRows.count();
1111 if (count > 0) {
reed@google.com9154eb02011-10-31 16:07:28 +00001112 this->flushRowH(&fRows[count - 1]);
reed@google.come36707a2011-10-04 21:38:55 +00001113 }
1114 if (count > 1) {
1115 // are our last two runs the same?
1116 Row* prev = &fRows[count - 2];
1117 Row* curr = &fRows[count - 1];
1118 SkASSERT(prev->fWidth == fWidth);
1119 SkASSERT(curr->fWidth == fWidth);
1120 if (*prev->fData == *curr->fData) {
1121 prev->fY = curr->fY;
1122 if (readyForAnother) {
1123 curr->fData->rewind();
1124 next = curr;
1125 } else {
1126 delete curr->fData;
1127 fRows.removeShuffle(count - 1);
1128 }
1129 } else {
1130 if (readyForAnother) {
1131 next = fRows.append();
1132 next->fData = new SkTDArray<uint8_t>;
1133 }
1134 }
1135 } else {
1136 if (readyForAnother) {
1137 next = fRows.append();
1138 next->fData = new SkTDArray<uint8_t>;
1139 }
1140 }
1141 return next;
1142 }
1143
1144 static void AppendRun(SkTDArray<uint8_t>& data, U8CPU alpha, int count) {
1145 do {
1146 int n = count;
1147 if (n > 255) {
1148 n = 255;
1149 }
1150 uint8_t* ptr = data.append(2);
1151 ptr[0] = n;
1152 ptr[1] = alpha;
1153 count -= n;
1154 } while (count > 0);
1155 }
1156};
1157
1158class SkAAClip::BuilderBlitter : public SkBlitter {
reed@google.com80cdb9a2012-02-16 18:56:17 +00001159 int fLastY;
1160
1161 /*
1162 If we see a gap of 1 or more empty scanlines while building in Y-order,
1163 we inject an explicit empty scanline (alpha==0)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001164
reed@google.com80cdb9a2012-02-16 18:56:17 +00001165 See AAClipTest.cpp : test_path_with_hole()
1166 */
1167 void checkForYGap(int y) {
1168 SkASSERT(y >= fLastY);
1169 if (fLastY > -SK_MaxS32) {
1170 int gap = y - fLastY;
1171 if (gap > 1) {
1172 fBuilder->addRun(fLeft, y - 1, 0, fRight - fLeft);
1173 }
1174 }
1175 fLastY = y;
1176 }
1177
reed@google.come36707a2011-10-04 21:38:55 +00001178public:
reed@google.com80cdb9a2012-02-16 18:56:17 +00001179
reed@google.come36707a2011-10-04 21:38:55 +00001180 BuilderBlitter(Builder* builder) {
1181 fBuilder = builder;
reed@google.com17785642011-10-12 20:23:55 +00001182 fLeft = builder->getBounds().fLeft;
1183 fRight = builder->getBounds().fRight;
reed@google.com209c4152011-10-26 15:03:48 +00001184 fMinY = SK_MaxS32;
reed@google.com80cdb9a2012-02-16 18:56:17 +00001185 fLastY = -SK_MaxS32; // sentinel
reed@google.com209c4152011-10-26 15:03:48 +00001186 }
1187
1188 void finish() {
1189 if (fMinY < SK_MaxS32) {
1190 fBuilder->setMinY(fMinY);
1191 }
reed@google.come36707a2011-10-04 21:38:55 +00001192 }
1193
tomhudson@google.com49eac192011-12-27 13:59:20 +00001194 /**
1195 Must evaluate clips in scan-line order, so don't want to allow blitV(),
1196 but an AAClip can be clipped down to a single pixel wide, so we
1197 must support it (given AntiRect semantics: minimum width is 2).
1198 Instead we'll rely on the runtime asserts to guarantee Y monotonicity;
1199 any failure cases that misses may have minor artifacts.
1200 */
mtklein36352bf2015-03-25 18:17:31 -07001201 void blitV(int x, int y, int height, SkAlpha alpha) override {
liyuqian2add0ff2016-10-20 11:04:39 -07001202 if (height == 1) {
1203 // We're still in scan-line order if height is 1
1204 // This is useful for Analytic AA
1205 const SkAlpha alphas[2] = {alpha, 0};
1206 const int16_t runs[2] = {1, 0};
1207 this->blitAntiH(x, y, alphas, runs);
1208 } else {
1209 this->recordMinY(y);
1210 fBuilder->addColumn(x, y, alpha, height);
1211 fLastY = y + height - 1;
1212 }
tomhudson@google.com49eac192011-12-27 13:59:20 +00001213 }
reed@google.com045e62d2011-10-24 12:19:46 +00001214
mtklein36352bf2015-03-25 18:17:31 -07001215 void blitRect(int x, int y, int width, int height) override {
reed@google.com9154eb02011-10-31 16:07:28 +00001216 this->recordMinY(y);
reed@google.com80cdb9a2012-02-16 18:56:17 +00001217 this->checkForYGap(y);
reed@google.com9154eb02011-10-31 16:07:28 +00001218 fBuilder->addRectRun(x, y, width, height);
reed@google.com302b8612012-02-16 19:30:13 +00001219 fLastY = y + height - 1;
reed@google.com562a2ac2011-10-31 14:14:18 +00001220 }
reed@google.com045e62d2011-10-24 12:19:46 +00001221
tomhudson@google.com49eac192011-12-27 13:59:20 +00001222 virtual void blitAntiRect(int x, int y, int width, int height,
mtklein36352bf2015-03-25 18:17:31 -07001223 SkAlpha leftAlpha, SkAlpha rightAlpha) override {
tomhudson@google.com49eac192011-12-27 13:59:20 +00001224 this->recordMinY(y);
reed@google.com302b8612012-02-16 19:30:13 +00001225 this->checkForYGap(y);
tomhudson@google.com49eac192011-12-27 13:59:20 +00001226 fBuilder->addAntiRectRun(x, y, width, height, leftAlpha, rightAlpha);
reed@google.com302b8612012-02-16 19:30:13 +00001227 fLastY = y + height - 1;
tomhudson@google.com49eac192011-12-27 13:59:20 +00001228 }
1229
mtklein36352bf2015-03-25 18:17:31 -07001230 void blitMask(const SkMask&, const SkIRect& clip) override
reed@google.come36707a2011-10-04 21:38:55 +00001231 { unexpected(); }
1232
reed41e010c2015-06-09 12:16:53 -07001233 const SkPixmap* justAnOpaqueColor(uint32_t*) override {
halcanary96fcdcc2015-08-27 07:41:13 -07001234 return nullptr;
reed@google.come36707a2011-10-04 21:38:55 +00001235 }
1236
mtklein36352bf2015-03-25 18:17:31 -07001237 void blitH(int x, int y, int width) override {
reed@google.com209c4152011-10-26 15:03:48 +00001238 this->recordMinY(y);
reed@google.com80cdb9a2012-02-16 18:56:17 +00001239 this->checkForYGap(y);
reed@google.come36707a2011-10-04 21:38:55 +00001240 fBuilder->addRun(x, y, 0xFF, width);
1241 }
1242
1243 virtual void blitAntiH(int x, int y, const SkAlpha alpha[],
mtklein36352bf2015-03-25 18:17:31 -07001244 const int16_t runs[]) override {
reed@google.com209c4152011-10-26 15:03:48 +00001245 this->recordMinY(y);
reed@google.com80cdb9a2012-02-16 18:56:17 +00001246 this->checkForYGap(y);
reed@google.come36707a2011-10-04 21:38:55 +00001247 for (;;) {
1248 int count = *runs;
1249 if (count <= 0) {
1250 return;
1251 }
reed@google.com17785642011-10-12 20:23:55 +00001252
1253 // The supersampler's buffer can be the width of the device, so
liyuqian1d3ab122016-11-08 13:55:15 -08001254 // we may have to trim the run to our bounds. Previously, we assert that
1255 // the extra spans are always alpha==0.
1256 // However, the analytic AA is too sensitive to precision errors
1257 // so it may have extra spans with very tiny alpha because after several
1258 // arithmatic operations, the edge may bleed the path boundary a little bit.
1259 // Therefore, instead of always asserting alpha==0, we assert alpha < 0x10.
reed@google.com17785642011-10-12 20:23:55 +00001260 int localX = x;
1261 int localCount = count;
1262 if (x < fLeft) {
liyuqian1d3ab122016-11-08 13:55:15 -08001263 SkASSERT(0x10 > *alpha);
reed@google.com17785642011-10-12 20:23:55 +00001264 int gap = fLeft - x;
1265 SkASSERT(gap <= count);
1266 localX += gap;
1267 localCount -= gap;
1268 }
1269 int right = x + count;
1270 if (right > fRight) {
liyuqian1d3ab122016-11-08 13:55:15 -08001271 SkASSERT(0x10 > *alpha);
reed@google.com17785642011-10-12 20:23:55 +00001272 localCount -= right - fRight;
1273 SkASSERT(localCount >= 0);
1274 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001275
reed@google.com17785642011-10-12 20:23:55 +00001276 if (localCount) {
1277 fBuilder->addRun(localX, y, *alpha, localCount);
1278 }
bsalomon@google.com820e80a2011-10-24 21:09:40 +00001279 // Next run
reed@google.come36707a2011-10-04 21:38:55 +00001280 runs += count;
1281 alpha += count;
1282 x += count;
1283 }
1284 }
1285
1286private:
1287 Builder* fBuilder;
reed@google.com17785642011-10-12 20:23:55 +00001288 int fLeft; // cache of builder's bounds' left edge
1289 int fRight;
reed@google.com209c4152011-10-26 15:03:48 +00001290 int fMinY;
1291
1292 /*
1293 * We track this, in case the scan converter skipped some number of
1294 * scanlines at the (relative to the bounds it was given). This allows
1295 * the builder, during its finish, to trip its bounds down to the "real"
1296 * top.
1297 */
1298 void recordMinY(int y) {
1299 if (y < fMinY) {
1300 fMinY = y;
1301 }
1302 }
reed@google.come36707a2011-10-04 21:38:55 +00001303
1304 void unexpected() {
Ben Wagner7ca9a742017-08-17 14:05:04 -04001305 SK_ABORT("---- did not expect to get called here");
reed@google.come36707a2011-10-04 21:38:55 +00001306 }
1307};
1308
reed@google.comf3c1da12011-10-10 19:35:47 +00001309bool SkAAClip::setPath(const SkPath& path, const SkRegion* clip, bool doAA) {
reed@google.com045e62d2011-10-24 12:19:46 +00001310 AUTO_AACLIP_VALIDATE(*this);
1311
reed@google.com32287892011-10-05 16:27:44 +00001312 if (clip && clip->isEmpty()) {
reed@google.come36707a2011-10-04 21:38:55 +00001313 return this->setEmpty();
1314 }
1315
1316 SkIRect ibounds;
reed@google.com32287892011-10-05 16:27:44 +00001317 path.getBounds().roundOut(&ibounds);
reed@google.come36707a2011-10-04 21:38:55 +00001318
reed@google.com32287892011-10-05 16:27:44 +00001319 SkRegion tmpClip;
halcanary96fcdcc2015-08-27 07:41:13 -07001320 if (nullptr == clip) {
reed@google.com32287892011-10-05 16:27:44 +00001321 tmpClip.setRect(ibounds);
1322 clip = &tmpClip;
1323 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001324
Yuqian Lica85fc32016-11-10 09:44:55 -05001325 // Since we assert that the BuilderBlitter will never blit outside the intersection
1326 // of clip and ibounds, we create this snugClip to be that intersection and send it
1327 // to the scan-converter.
1328 SkRegion snugClip(*clip);
1329
reed@google.com045e62d2011-10-24 12:19:46 +00001330 if (path.isInverseFillType()) {
1331 ibounds = clip->getBounds();
1332 } else {
reed@google.com32287892011-10-05 16:27:44 +00001333 if (ibounds.isEmpty() || !ibounds.intersect(clip->getBounds())) {
reed@google.come36707a2011-10-04 21:38:55 +00001334 return this->setEmpty();
1335 }
Yuqian Lica85fc32016-11-10 09:44:55 -05001336 snugClip.op(ibounds, SkRegion::kIntersect_Op);
reed@google.come36707a2011-10-04 21:38:55 +00001337 }
1338
1339 Builder builder(ibounds);
1340 BuilderBlitter blitter(&builder);
1341
reed@google.comf3c1da12011-10-10 19:35:47 +00001342 if (doAA) {
Yuqian Li7d99dad2017-07-26 13:47:26 -04001343 SkScan::AntiFillPath(path, snugClip, &blitter, true);
reed@google.comf3c1da12011-10-10 19:35:47 +00001344 } else {
Yuqian Lica85fc32016-11-10 09:44:55 -05001345 SkScan::FillPath(path, snugClip, &blitter);
reed@google.comf3c1da12011-10-10 19:35:47 +00001346 }
reed@google.come36707a2011-10-04 21:38:55 +00001347
reed@google.com209c4152011-10-26 15:03:48 +00001348 blitter.finish();
reed@google.com045e62d2011-10-24 12:19:46 +00001349 return builder.finish(this);
reed@google.come36707a2011-10-04 21:38:55 +00001350}
1351
1352///////////////////////////////////////////////////////////////////////////////
1353
reed@google.com32287892011-10-05 16:27:44 +00001354typedef void (*RowProc)(SkAAClip::Builder&, int bottom,
1355 const uint8_t* rowA, const SkIRect& rectA,
1356 const uint8_t* rowB, const SkIRect& rectB);
1357
reed@google.com32287892011-10-05 16:27:44 +00001358typedef U8CPU (*AlphaProc)(U8CPU alphaA, U8CPU alphaB);
1359
1360static U8CPU sectAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1361 // Multiply
1362 return SkMulDiv255Round(alphaA, alphaB);
1363}
1364
1365static U8CPU unionAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1366 // SrcOver
1367 return alphaA + alphaB - SkMulDiv255Round(alphaA, alphaB);
1368}
1369
1370static U8CPU diffAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1371 // SrcOut
1372 return SkMulDiv255Round(alphaA, 0xFF - alphaB);
1373}
1374
1375static U8CPU xorAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1376 // XOR
1377 return alphaA + alphaB - 2 * SkMulDiv255Round(alphaA, alphaB);
1378}
1379
1380static AlphaProc find_alpha_proc(SkRegion::Op op) {
1381 switch (op) {
1382 case SkRegion::kIntersect_Op:
1383 return sectAlphaProc;
1384 case SkRegion::kDifference_Op:
1385 return diffAlphaProc;
1386 case SkRegion::kUnion_Op:
1387 return unionAlphaProc;
1388 case SkRegion::kXOR_Op:
1389 return xorAlphaProc;
1390 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001391 SkDEBUGFAIL("unexpected region op");
reed@google.com32287892011-10-05 16:27:44 +00001392 return sectAlphaProc;
1393 }
1394}
1395
reed@google.com32287892011-10-05 16:27:44 +00001396class RowIter {
1397public:
1398 RowIter(const uint8_t* row, const SkIRect& bounds) {
1399 fRow = row;
1400 fLeft = bounds.fLeft;
reed@google.com32287892011-10-05 16:27:44 +00001401 fBoundsRight = bounds.fRight;
reed@google.com1c04bf92011-10-10 12:57:12 +00001402 if (row) {
1403 fRight = bounds.fLeft + row[0];
1404 SkASSERT(fRight <= fBoundsRight);
1405 fAlpha = row[1];
1406 fDone = false;
1407 } else {
1408 fDone = true;
1409 fRight = kMaxInt32;
1410 fAlpha = 0;
1411 }
reed@google.com32287892011-10-05 16:27:44 +00001412 }
1413
1414 bool done() const { return fDone; }
reed@google.com1c04bf92011-10-10 12:57:12 +00001415 int left() const { return fLeft; }
1416 int right() const { return fRight; }
1417 U8CPU alpha() const { return fAlpha; }
reed@google.com32287892011-10-05 16:27:44 +00001418 void next() {
reed@google.com1c04bf92011-10-10 12:57:12 +00001419 if (!fDone) {
reed@google.com32287892011-10-05 16:27:44 +00001420 fLeft = fRight;
reed@google.com1c04bf92011-10-10 12:57:12 +00001421 if (fRight == fBoundsRight) {
1422 fDone = true;
1423 fRight = kMaxInt32;
1424 fAlpha = 0;
1425 } else {
1426 fRow += 2;
1427 fRight += fRow[0];
1428 fAlpha = fRow[1];
1429 SkASSERT(fRight <= fBoundsRight);
1430 }
reed@google.com32287892011-10-05 16:27:44 +00001431 }
1432 }
1433
1434private:
1435 const uint8_t* fRow;
1436 int fLeft;
1437 int fRight;
1438 int fBoundsRight;
1439 bool fDone;
reed@google.com1c04bf92011-10-10 12:57:12 +00001440 uint8_t fAlpha;
reed@google.com32287892011-10-05 16:27:44 +00001441};
1442
reed@google.com1c04bf92011-10-10 12:57:12 +00001443static void adjust_row(RowIter& iter, int& leftA, int& riteA, int rite) {
1444 if (rite == riteA) {
1445 iter.next();
1446 leftA = iter.left();
1447 riteA = iter.right();
reed@google.com32287892011-10-05 16:27:44 +00001448 }
1449}
1450
caryclark@google.com803eceb2012-06-06 12:09:34 +00001451#if 0 // UNUSED
reed@google.com1c04bf92011-10-10 12:57:12 +00001452static bool intersect(int& min, int& max, int boundsMin, int boundsMax) {
1453 SkASSERT(min < max);
1454 SkASSERT(boundsMin < boundsMax);
1455 if (min >= boundsMax || max <= boundsMin) {
1456 return false;
1457 }
1458 if (min < boundsMin) {
1459 min = boundsMin;
1460 }
1461 if (max > boundsMax) {
1462 max = boundsMax;
1463 }
1464 return true;
1465}
caryclark@google.com803eceb2012-06-06 12:09:34 +00001466#endif
reed@google.com1c04bf92011-10-10 12:57:12 +00001467
reed@google.com32287892011-10-05 16:27:44 +00001468static void operatorX(SkAAClip::Builder& builder, int lastY,
1469 RowIter& iterA, RowIter& iterB,
1470 AlphaProc proc, const SkIRect& bounds) {
reed@google.com32287892011-10-05 16:27:44 +00001471 int leftA = iterA.left();
1472 int riteA = iterA.right();
reed@google.com32287892011-10-05 16:27:44 +00001473 int leftB = iterB.left();
1474 int riteB = iterB.right();
1475
reed@google.com1c04bf92011-10-10 12:57:12 +00001476 int prevRite = bounds.fLeft;
1477
1478 do {
reed@google.com32287892011-10-05 16:27:44 +00001479 U8CPU alphaA = 0;
1480 U8CPU alphaB = 0;
reed@google.com32287892011-10-05 16:27:44 +00001481 int left, rite;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001482
reed@google.com1c04bf92011-10-10 12:57:12 +00001483 if (leftA < leftB) {
reed@google.com32287892011-10-05 16:27:44 +00001484 left = leftA;
reed@google.com32287892011-10-05 16:27:44 +00001485 alphaA = iterA.alpha();
reed@google.com1c04bf92011-10-10 12:57:12 +00001486 if (riteA <= leftB) {
1487 rite = riteA;
1488 } else {
1489 rite = leftA = leftB;
reed@google.com32287892011-10-05 16:27:44 +00001490 }
reed@google.com1c04bf92011-10-10 12:57:12 +00001491 } else if (leftB < leftA) {
1492 left = leftB;
1493 alphaB = iterB.alpha();
1494 if (riteB <= leftA) {
1495 rite = riteB;
1496 } else {
1497 rite = leftB = leftA;
1498 }
1499 } else {
1500 left = leftA; // or leftB, since leftA == leftB
1501 rite = leftA = leftB = SkMin32(riteA, riteB);
1502 alphaA = iterA.alpha();
1503 alphaB = iterB.alpha();
reed@google.com32287892011-10-05 16:27:44 +00001504 }
1505
1506 if (left >= bounds.fRight) {
1507 break;
1508 }
reed@google.com34f7e472011-10-13 15:11:59 +00001509 if (rite > bounds.fRight) {
1510 rite = bounds.fRight;
1511 }
1512
reed@google.com32287892011-10-05 16:27:44 +00001513 if (left >= bounds.fLeft) {
reed@google.com1c04bf92011-10-10 12:57:12 +00001514 SkASSERT(rite > left);
reed@google.com32287892011-10-05 16:27:44 +00001515 builder.addRun(left, lastY, proc(alphaA, alphaB), rite - left);
reed@google.com1c04bf92011-10-10 12:57:12 +00001516 prevRite = rite;
reed@google.com32287892011-10-05 16:27:44 +00001517 }
reed@google.com1c04bf92011-10-10 12:57:12 +00001518
1519 adjust_row(iterA, leftA, riteA, rite);
1520 adjust_row(iterB, leftB, riteB, rite);
1521 } while (!iterA.done() || !iterB.done());
1522
1523 if (prevRite < bounds.fRight) {
1524 builder.addRun(prevRite, lastY, 0, bounds.fRight - prevRite);
reed@google.com32287892011-10-05 16:27:44 +00001525 }
1526}
1527
reed@google.com1c04bf92011-10-10 12:57:12 +00001528static void adjust_iter(SkAAClip::Iter& iter, int& topA, int& botA, int bot) {
1529 if (bot == botA) {
1530 iter.next();
1531 topA = botA;
1532 SkASSERT(botA == iter.top());
1533 botA = iter.bottom();
reed@google.com32287892011-10-05 16:27:44 +00001534 }
1535}
1536
1537static void operateY(SkAAClip::Builder& builder, const SkAAClip& A,
1538 const SkAAClip& B, SkRegion::Op op) {
1539 AlphaProc proc = find_alpha_proc(op);
1540 const SkIRect& bounds = builder.getBounds();
1541
1542 SkAAClip::Iter iterA(A);
1543 SkAAClip::Iter iterB(B);
1544
1545 SkASSERT(!iterA.done());
1546 int topA = iterA.top();
1547 int botA = iterA.bottom();
1548 SkASSERT(!iterB.done());
1549 int topB = iterB.top();
1550 int botB = iterB.bottom();
1551
reed@google.com1c04bf92011-10-10 12:57:12 +00001552 do {
halcanary96fcdcc2015-08-27 07:41:13 -07001553 const uint8_t* rowA = nullptr;
1554 const uint8_t* rowB = nullptr;
reed@google.com32287892011-10-05 16:27:44 +00001555 int top, bot;
reed@google.com1c04bf92011-10-10 12:57:12 +00001556
1557 if (topA < topB) {
reed@google.com32287892011-10-05 16:27:44 +00001558 top = topA;
reed@google.com32287892011-10-05 16:27:44 +00001559 rowA = iterA.data();
reed@google.com1c04bf92011-10-10 12:57:12 +00001560 if (botA <= topB) {
1561 bot = botA;
1562 } else {
1563 bot = topA = topB;
reed@google.com32287892011-10-05 16:27:44 +00001564 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001565
reed@google.com1c04bf92011-10-10 12:57:12 +00001566 } else if (topB < topA) {
1567 top = topB;
1568 rowB = iterB.data();
1569 if (botB <= topA) {
1570 bot = botB;
1571 } else {
1572 bot = topB = topA;
1573 }
1574 } else {
1575 top = topA; // or topB, since topA == topB
1576 bot = topA = topB = SkMin32(botA, botB);
1577 rowA = iterA.data();
1578 rowB = iterB.data();
reed@google.com32287892011-10-05 16:27:44 +00001579 }
1580
1581 if (top >= bounds.fBottom) {
1582 break;
1583 }
reed@google.com34f7e472011-10-13 15:11:59 +00001584
1585 if (bot > bounds.fBottom) {
1586 bot = bounds.fBottom;
1587 }
1588 SkASSERT(top < bot);
1589
reed@google.com1c04bf92011-10-10 12:57:12 +00001590 if (!rowA && !rowB) {
1591 builder.addRun(bounds.fLeft, bot - 1, 0, bounds.width());
1592 } else if (top >= bounds.fTop) {
1593 SkASSERT(bot <= bounds.fBottom);
1594 RowIter rowIterA(rowA, rowA ? A.getBounds() : bounds);
1595 RowIter rowIterB(rowB, rowB ? B.getBounds() : bounds);
reed@google.com32287892011-10-05 16:27:44 +00001596 operatorX(builder, bot - 1, rowIterA, rowIterB, proc, bounds);
reed@google.com32287892011-10-05 16:27:44 +00001597 }
1598
reed@google.com1c04bf92011-10-10 12:57:12 +00001599 adjust_iter(iterA, topA, botA, bot);
1600 adjust_iter(iterB, topB, botB, bot);
1601 } while (!iterA.done() || !iterB.done());
reed@google.com32287892011-10-05 16:27:44 +00001602}
1603
1604bool SkAAClip::op(const SkAAClip& clipAOrig, const SkAAClip& clipBOrig,
1605 SkRegion::Op op) {
reed@google.com045e62d2011-10-24 12:19:46 +00001606 AUTO_AACLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001607
reed@google.com32287892011-10-05 16:27:44 +00001608 if (SkRegion::kReplace_Op == op) {
1609 return this->set(clipBOrig);
1610 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001611
reed@google.com32287892011-10-05 16:27:44 +00001612 const SkAAClip* clipA = &clipAOrig;
1613 const SkAAClip* clipB = &clipBOrig;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001614
reed@google.com32287892011-10-05 16:27:44 +00001615 if (SkRegion::kReverseDifference_Op == op) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001616 using std::swap;
1617 swap(clipA, clipB);
reed@google.com32287892011-10-05 16:27:44 +00001618 op = SkRegion::kDifference_Op;
1619 }
1620
1621 bool a_empty = clipA->isEmpty();
1622 bool b_empty = clipB->isEmpty();
1623
1624 SkIRect bounds;
1625 switch (op) {
1626 case SkRegion::kDifference_Op:
1627 if (a_empty) {
1628 return this->setEmpty();
1629 }
1630 if (b_empty || !SkIRect::Intersects(clipA->fBounds, clipB->fBounds)) {
1631 return this->set(*clipA);
1632 }
1633 bounds = clipA->fBounds;
1634 break;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001635
reed@google.com32287892011-10-05 16:27:44 +00001636 case SkRegion::kIntersect_Op:
1637 if ((a_empty | b_empty) || !bounds.intersect(clipA->fBounds,
1638 clipB->fBounds)) {
1639 return this->setEmpty();
1640 }
1641 break;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001642
reed@google.com32287892011-10-05 16:27:44 +00001643 case SkRegion::kUnion_Op:
1644 case SkRegion::kXOR_Op:
1645 if (a_empty) {
1646 return this->set(*clipB);
1647 }
1648 if (b_empty) {
1649 return this->set(*clipA);
1650 }
1651 bounds = clipA->fBounds;
1652 bounds.join(clipB->fBounds);
1653 break;
1654
1655 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001656 SkDEBUGFAIL("unknown region op");
reed@google.com32287892011-10-05 16:27:44 +00001657 return !this->isEmpty();
1658 }
1659
reed@google.com32287892011-10-05 16:27:44 +00001660 SkASSERT(SkIRect::Intersects(bounds, clipB->fBounds));
1661 SkASSERT(SkIRect::Intersects(bounds, clipB->fBounds));
1662
1663 Builder builder(bounds);
1664 operateY(builder, *clipA, *clipB, op);
reed@google.com045e62d2011-10-24 12:19:46 +00001665
1666 return builder.finish(this);
reed@google.come36707a2011-10-04 21:38:55 +00001667}
1668
reed@google.com045e62d2011-10-24 12:19:46 +00001669/*
1670 * It can be expensive to build a local aaclip before applying the op, so
1671 * we first see if we can restrict the bounds of new rect to our current
1672 * bounds, or note that the new rect subsumes our current clip.
1673 */
1674
1675bool SkAAClip::op(const SkIRect& rOrig, SkRegion::Op op) {
1676 SkIRect rStorage;
1677 const SkIRect* r = &rOrig;
1678
1679 switch (op) {
1680 case SkRegion::kIntersect_Op:
1681 if (!rStorage.intersect(rOrig, fBounds)) {
1682 // no overlap, so we're empty
1683 return this->setEmpty();
1684 }
1685 if (rStorage == fBounds) {
1686 // we were wholly inside the rect, no change
1687 return !this->isEmpty();
1688 }
1689 if (this->quickContains(rStorage)) {
1690 // the intersection is wholly inside us, we're a rect
1691 return this->setRect(rStorage);
1692 }
1693 r = &rStorage; // use the intersected bounds
1694 break;
1695 case SkRegion::kDifference_Op:
1696 break;
1697 case SkRegion::kUnion_Op:
1698 if (rOrig.contains(fBounds)) {
1699 return this->setRect(rOrig);
1700 }
1701 break;
1702 default:
1703 break;
1704 }
1705
reed@google.com47ac84e2011-10-06 13:11:25 +00001706 SkAAClip clip;
reed@google.com045e62d2011-10-24 12:19:46 +00001707 clip.setRect(*r);
reed@google.com47ac84e2011-10-06 13:11:25 +00001708 return this->op(*this, clip, op);
1709}
1710
reed@google.com045e62d2011-10-24 12:19:46 +00001711bool SkAAClip::op(const SkRect& rOrig, SkRegion::Op op, bool doAA) {
1712 SkRect rStorage, boundsStorage;
1713 const SkRect* r = &rOrig;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001714
reed@google.com045e62d2011-10-24 12:19:46 +00001715 boundsStorage.set(fBounds);
1716 switch (op) {
1717 case SkRegion::kIntersect_Op:
1718 case SkRegion::kDifference_Op:
1719 if (!rStorage.intersect(rOrig, boundsStorage)) {
reed@google.come56513d2012-06-25 20:06:33 +00001720 if (SkRegion::kIntersect_Op == op) {
1721 return this->setEmpty();
1722 } else { // kDifference
1723 return !this->isEmpty();
1724 }
reed@google.com045e62d2011-10-24 12:19:46 +00001725 }
1726 r = &rStorage; // use the intersected bounds
1727 break;
1728 case SkRegion::kUnion_Op:
1729 if (rOrig.contains(boundsStorage)) {
1730 return this->setRect(rOrig);
1731 }
1732 break;
1733 default:
1734 break;
1735 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001736
reed@google.com47ac84e2011-10-06 13:11:25 +00001737 SkAAClip clip;
reed@google.com045e62d2011-10-24 12:19:46 +00001738 clip.setRect(*r, doAA);
reed@google.com47ac84e2011-10-06 13:11:25 +00001739 return this->op(*this, clip, op);
1740}
1741
1742bool SkAAClip::op(const SkAAClip& clip, SkRegion::Op op) {
1743 return this->op(*this, clip, op);
1744}
1745
reed@google.come36707a2011-10-04 21:38:55 +00001746///////////////////////////////////////////////////////////////////////////////
reed@google.com045e62d2011-10-24 12:19:46 +00001747
1748bool SkAAClip::translate(int dx, int dy, SkAAClip* dst) const {
halcanary96fcdcc2015-08-27 07:41:13 -07001749 if (nullptr == dst) {
reed@google.com045e62d2011-10-24 12:19:46 +00001750 return !this->isEmpty();
1751 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001752
reed@google.com045e62d2011-10-24 12:19:46 +00001753 if (this->isEmpty()) {
1754 return dst->setEmpty();
1755 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001756
reed@google.com045e62d2011-10-24 12:19:46 +00001757 if (this != dst) {
1758 sk_atomic_inc(&fRunHead->fRefCnt);
tomhudson@google.com19224c32012-03-28 15:46:37 +00001759 dst->freeRuns();
reed@google.com045e62d2011-10-24 12:19:46 +00001760 dst->fRunHead = fRunHead;
1761 dst->fBounds = fBounds;
1762 }
1763 dst->fBounds.offset(dx, dy);
1764 return true;
1765}
1766
1767static void expand_row_to_mask(uint8_t* SK_RESTRICT mask,
1768 const uint8_t* SK_RESTRICT row,
1769 int width) {
1770 while (width > 0) {
1771 int n = row[0];
1772 SkASSERT(width >= n);
1773 memset(mask, row[1], n);
1774 mask += n;
1775 row += 2;
1776 width -= n;
1777 }
reed@google.coma069c8f2011-11-28 19:54:56 +00001778 SkASSERT(0 == width);
reed@google.com045e62d2011-10-24 12:19:46 +00001779}
1780
1781void SkAAClip::copyToMask(SkMask* mask) const {
1782 mask->fFormat = SkMask::kA8_Format;
1783 if (this->isEmpty()) {
1784 mask->fBounds.setEmpty();
halcanary96fcdcc2015-08-27 07:41:13 -07001785 mask->fImage = nullptr;
reed@google.com045e62d2011-10-24 12:19:46 +00001786 mask->fRowBytes = 0;
1787 return;
1788 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001789
reed@google.com045e62d2011-10-24 12:19:46 +00001790 mask->fBounds = fBounds;
1791 mask->fRowBytes = fBounds.width();
1792 size_t size = mask->computeImageSize();
1793 mask->fImage = SkMask::AllocImage(size);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001794
reed@google.com045e62d2011-10-24 12:19:46 +00001795 Iter iter(*this);
1796 uint8_t* dst = mask->fImage;
1797 const int width = fBounds.width();
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001798
reed@google.com045e62d2011-10-24 12:19:46 +00001799 int y = fBounds.fTop;
1800 while (!iter.done()) {
1801 do {
1802 expand_row_to_mask(dst, iter.data(), width);
1803 dst += mask->fRowBytes;
1804 } while (++y < iter.bottom());
1805 iter.next();
1806 }
1807}
1808
1809///////////////////////////////////////////////////////////////////////////////
reed@google.come36707a2011-10-04 21:38:55 +00001810///////////////////////////////////////////////////////////////////////////////
1811
1812static void expandToRuns(const uint8_t* SK_RESTRICT data, int initialCount, int width,
1813 int16_t* SK_RESTRICT runs, SkAlpha* SK_RESTRICT aa) {
1814 // we don't read our initial n from data, since the caller may have had to
1815 // clip it, hence the initialCount parameter.
1816 int n = initialCount;
1817 for (;;) {
1818 if (n > width) {
1819 n = width;
1820 }
1821 SkASSERT(n > 0);
1822 runs[0] = n;
1823 runs += n;
1824
1825 aa[0] = data[1];
1826 aa += n;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001827
reed@google.come36707a2011-10-04 21:38:55 +00001828 data += 2;
1829 width -= n;
1830 if (0 == width) {
1831 break;
1832 }
1833 // load the next count
1834 n = data[0];
1835 }
1836 runs[0] = 0; // sentinel
1837}
1838
1839SkAAClipBlitter::~SkAAClipBlitter() {
reed@google.com045e62d2011-10-24 12:19:46 +00001840 sk_free(fScanlineScratch);
reed@google.come36707a2011-10-04 21:38:55 +00001841}
1842
1843void SkAAClipBlitter::ensureRunsAndAA() {
halcanary96fcdcc2015-08-27 07:41:13 -07001844 if (nullptr == fScanlineScratch) {
reed@google.come36707a2011-10-04 21:38:55 +00001845 // add 1 so we can store the terminating run count of 0
1846 int count = fAAClipBounds.width() + 1;
reed@google.com045e62d2011-10-24 12:19:46 +00001847 // we use this either for fRuns + fAA, or a scaline of a mask
1848 // which may be as deep as 32bits
1849 fScanlineScratch = sk_malloc_throw(count * sizeof(SkPMColor));
1850 fRuns = (int16_t*)fScanlineScratch;
reed@google.come36707a2011-10-04 21:38:55 +00001851 fAA = (SkAlpha*)(fRuns + count);
1852 }
1853}
1854
1855void SkAAClipBlitter::blitH(int x, int y, int width) {
1856 SkASSERT(width > 0);
1857 SkASSERT(fAAClipBounds.contains(x, y));
1858 SkASSERT(fAAClipBounds.contains(x + width - 1, y));
1859
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001860 const uint8_t* row = fAAClip->findRow(y);
reed@google.come36707a2011-10-04 21:38:55 +00001861 int initialCount;
1862 row = fAAClip->findX(row, x, &initialCount);
1863
1864 if (initialCount >= width) {
1865 SkAlpha alpha = row[1];
1866 if (0 == alpha) {
1867 return;
1868 }
1869 if (0xFF == alpha) {
1870 fBlitter->blitH(x, y, width);
1871 return;
1872 }
1873 }
1874
1875 this->ensureRunsAndAA();
1876 expandToRuns(row, initialCount, width, fRuns, fAA);
1877
1878 fBlitter->blitAntiH(x, y, fAA, fRuns);
1879}
1880
1881static void merge(const uint8_t* SK_RESTRICT row, int rowN,
1882 const SkAlpha* SK_RESTRICT srcAA,
1883 const int16_t* SK_RESTRICT srcRuns,
1884 SkAlpha* SK_RESTRICT dstAA,
1885 int16_t* SK_RESTRICT dstRuns,
1886 int width) {
1887 SkDEBUGCODE(int accumulated = 0;)
1888 int srcN = srcRuns[0];
reed@google.com045e62d2011-10-24 12:19:46 +00001889 // do we need this check?
1890 if (0 == srcN) {
1891 return;
1892 }
1893
reed@google.come36707a2011-10-04 21:38:55 +00001894 for (;;) {
reed@google.come36707a2011-10-04 21:38:55 +00001895 SkASSERT(rowN > 0);
1896 SkASSERT(srcN > 0);
1897
1898 unsigned newAlpha = SkMulDiv255Round(srcAA[0], row[1]);
1899 int minN = SkMin32(srcN, rowN);
1900 dstRuns[0] = minN;
1901 dstRuns += minN;
1902 dstAA[0] = newAlpha;
1903 dstAA += minN;
1904
1905 if (0 == (srcN -= minN)) {
1906 srcN = srcRuns[0]; // refresh
1907 srcRuns += srcN;
1908 srcAA += srcN;
1909 srcN = srcRuns[0]; // reload
reed@google.com045e62d2011-10-24 12:19:46 +00001910 if (0 == srcN) {
1911 break;
1912 }
reed@google.come36707a2011-10-04 21:38:55 +00001913 }
1914 if (0 == (rowN -= minN)) {
1915 row += 2;
1916 rowN = row[0]; // reload
1917 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001918
reed@google.come36707a2011-10-04 21:38:55 +00001919 SkDEBUGCODE(accumulated += minN;)
1920 SkASSERT(accumulated <= width);
1921 }
reed@google.com34f7e472011-10-13 15:11:59 +00001922 dstRuns[0] = 0;
reed@google.come36707a2011-10-04 21:38:55 +00001923}
1924
1925void SkAAClipBlitter::blitAntiH(int x, int y, const SkAlpha aa[],
1926 const int16_t runs[]) {
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001927
1928 const uint8_t* row = fAAClip->findRow(y);
reed@google.come36707a2011-10-04 21:38:55 +00001929 int initialCount;
1930 row = fAAClip->findX(row, x, &initialCount);
1931
1932 this->ensureRunsAndAA();
1933
1934 merge(row, initialCount, aa, runs, fAA, fRuns, fAAClipBounds.width());
1935 fBlitter->blitAntiH(x, y, fAA, fRuns);
1936}
1937
1938void SkAAClipBlitter::blitV(int x, int y, int height, SkAlpha alpha) {
1939 if (fAAClip->quickContains(x, y, x + 1, y + height)) {
1940 fBlitter->blitV(x, y, height, alpha);
1941 return;
1942 }
1943
reed@google.com045e62d2011-10-24 12:19:46 +00001944 for (;;) {
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001945 int lastY SK_INIT_TO_AVOID_WARNING;
reed@google.come36707a2011-10-04 21:38:55 +00001946 const uint8_t* row = fAAClip->findRow(y, &lastY);
reed@google.com045e62d2011-10-24 12:19:46 +00001947 int dy = lastY - y + 1;
1948 if (dy > height) {
1949 dy = height;
1950 }
1951 height -= dy;
1952
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001953 row = fAAClip->findX(row, x);
reed@google.come36707a2011-10-04 21:38:55 +00001954 SkAlpha newAlpha = SkMulDiv255Round(alpha, row[1]);
1955 if (newAlpha) {
reed@google.com045e62d2011-10-24 12:19:46 +00001956 fBlitter->blitV(x, y, dy, newAlpha);
1957 }
1958 SkASSERT(height >= 0);
1959 if (height <= 0) {
1960 break;
reed@google.come36707a2011-10-04 21:38:55 +00001961 }
1962 y = lastY + 1;
reed@google.com045e62d2011-10-24 12:19:46 +00001963 }
reed@google.come36707a2011-10-04 21:38:55 +00001964}
1965
1966void SkAAClipBlitter::blitRect(int x, int y, int width, int height) {
1967 if (fAAClip->quickContains(x, y, x + width, y + height)) {
1968 fBlitter->blitRect(x, y, width, height);
1969 return;
1970 }
1971
1972 while (--height >= 0) {
1973 this->blitH(x, y, width);
1974 y += 1;
1975 }
1976}
1977
reed@google.com045e62d2011-10-24 12:19:46 +00001978typedef void (*MergeAAProc)(const void* src, int width, const uint8_t* row,
1979 int initialRowCount, void* dst);
1980
1981static void small_memcpy(void* dst, const void* src, size_t n) {
1982 memcpy(dst, src, n);
1983}
1984
1985static void small_bzero(void* dst, size_t n) {
1986 sk_bzero(dst, n);
1987}
1988
1989static inline uint8_t mergeOne(uint8_t value, unsigned alpha) {
1990 return SkMulDiv255Round(value, alpha);
1991}
reedd54d3fc2014-11-13 14:39:58 -08001992
reed@google.com045e62d2011-10-24 12:19:46 +00001993static inline uint16_t mergeOne(uint16_t value, unsigned alpha) {
1994 unsigned r = SkGetPackedR16(value);
1995 unsigned g = SkGetPackedG16(value);
1996 unsigned b = SkGetPackedB16(value);
1997 return SkPackRGB16(SkMulDiv255Round(r, alpha),
caryclark@google.com803eceb2012-06-06 12:09:34 +00001998 SkMulDiv255Round(g, alpha),
1999 SkMulDiv255Round(b, alpha));
reed@google.com045e62d2011-10-24 12:19:46 +00002000}
reed@google.com045e62d2011-10-24 12:19:46 +00002001
herb94496162015-12-10 14:17:41 -08002002template <typename T>
2003void mergeT(const void* inSrc, int srcN, const uint8_t* SK_RESTRICT row, int rowN, void* inDst) {
2004 const T* SK_RESTRICT src = static_cast<const T*>(inSrc);
2005 T* SK_RESTRICT dst = static_cast<T*>(inDst);
reed@google.com045e62d2011-10-24 12:19:46 +00002006 for (;;) {
2007 SkASSERT(rowN > 0);
2008 SkASSERT(srcN > 0);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002009
reed@google.com045e62d2011-10-24 12:19:46 +00002010 int n = SkMin32(rowN, srcN);
2011 unsigned rowA = row[1];
2012 if (0xFF == rowA) {
2013 small_memcpy(dst, src, n * sizeof(T));
2014 } else if (0 == rowA) {
2015 small_bzero(dst, n * sizeof(T));
2016 } else {
2017 for (int i = 0; i < n; ++i) {
2018 dst[i] = mergeOne(src[i], rowA);
2019 }
2020 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002021
reed@google.com045e62d2011-10-24 12:19:46 +00002022 if (0 == (srcN -= n)) {
2023 break;
2024 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002025
reed@google.com045e62d2011-10-24 12:19:46 +00002026 src += n;
2027 dst += n;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002028
reed@google.com045e62d2011-10-24 12:19:46 +00002029 SkASSERT(rowN == n);
2030 row += 2;
2031 rowN = row[0];
2032 }
2033}
2034
2035static MergeAAProc find_merge_aa_proc(SkMask::Format format) {
2036 switch (format) {
2037 case SkMask::kBW_Format:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002038 SkDEBUGFAIL("unsupported");
halcanary96fcdcc2015-08-27 07:41:13 -07002039 return nullptr;
reed@google.com045e62d2011-10-24 12:19:46 +00002040 case SkMask::kA8_Format:
herb94496162015-12-10 14:17:41 -08002041 case SkMask::k3D_Format:
2042 return mergeT<uint8_t> ;
2043 case SkMask::kLCD16_Format:
2044 return mergeT<uint16_t>;
reed@google.com045e62d2011-10-24 12:19:46 +00002045 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002046 SkDEBUGFAIL("unsupported");
halcanary96fcdcc2015-08-27 07:41:13 -07002047 return nullptr;
reed@google.com045e62d2011-10-24 12:19:46 +00002048 }
2049}
2050
2051static U8CPU bit2byte(int bitInAByte) {
2052 SkASSERT(bitInAByte <= 0xFF);
2053 // negation turns any non-zero into 0xFFFFFF??, so we just shift down
2054 // some value >= 8 to get a full FF value
2055 return -bitInAByte >> 8;
2056}
2057
2058static void upscaleBW2A8(SkMask* dstMask, const SkMask& srcMask) {
2059 SkASSERT(SkMask::kBW_Format == srcMask.fFormat);
2060 SkASSERT(SkMask::kA8_Format == dstMask->fFormat);
2061
2062 const int width = srcMask.fBounds.width();
2063 const int height = srcMask.fBounds.height();
2064
2065 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcMask.fImage;
2066 const size_t srcRB = srcMask.fRowBytes;
2067 uint8_t* SK_RESTRICT dst = (uint8_t*)dstMask->fImage;
2068 const size_t dstRB = dstMask->fRowBytes;
2069
2070 const int wholeBytes = width >> 3;
2071 const int leftOverBits = width & 7;
2072
2073 for (int y = 0; y < height; ++y) {
2074 uint8_t* SK_RESTRICT d = dst;
2075 for (int i = 0; i < wholeBytes; ++i) {
2076 int srcByte = src[i];
2077 d[0] = bit2byte(srcByte & (1 << 7));
2078 d[1] = bit2byte(srcByte & (1 << 6));
2079 d[2] = bit2byte(srcByte & (1 << 5));
2080 d[3] = bit2byte(srcByte & (1 << 4));
2081 d[4] = bit2byte(srcByte & (1 << 3));
2082 d[5] = bit2byte(srcByte & (1 << 2));
2083 d[6] = bit2byte(srcByte & (1 << 1));
2084 d[7] = bit2byte(srcByte & (1 << 0));
2085 d += 8;
2086 }
2087 if (leftOverBits) {
2088 int srcByte = src[wholeBytes];
2089 for (int x = 0; x < leftOverBits; ++x) {
2090 *d++ = bit2byte(srcByte & 0x80);
2091 srcByte <<= 1;
2092 }
2093 }
2094 src += srcRB;
2095 dst += dstRB;
2096 }
2097}
2098
2099void SkAAClipBlitter::blitMask(const SkMask& origMask, const SkIRect& clip) {
2100 SkASSERT(fAAClip->getBounds().contains(clip));
2101
2102 if (fAAClip->quickContains(clip)) {
2103 fBlitter->blitMask(origMask, clip);
2104 return;
2105 }
2106
2107 const SkMask* mask = &origMask;
2108
2109 // if we're BW, we need to upscale to A8 (ugh)
2110 SkMask grayMask;
reed@google.com045e62d2011-10-24 12:19:46 +00002111 if (SkMask::kBW_Format == origMask.fFormat) {
2112 grayMask.fFormat = SkMask::kA8_Format;
2113 grayMask.fBounds = origMask.fBounds;
2114 grayMask.fRowBytes = origMask.fBounds.width();
2115 size_t size = grayMask.computeImageSize();
2116 grayMask.fImage = (uint8_t*)fGrayMaskScratch.reset(size,
2117 SkAutoMalloc::kReuse_OnShrink);
2118
2119 upscaleBW2A8(&grayMask, origMask);
2120 mask = &grayMask;
2121 }
2122
2123 this->ensureRunsAndAA();
2124
2125 // HACK -- we are devolving 3D into A8, need to copy the rest of the 3D
2126 // data into a temp block to support it better (ugh)
2127
2128 const void* src = mask->getAddr(clip.fLeft, clip.fTop);
2129 const size_t srcRB = mask->fRowBytes;
2130 const int width = clip.width();
2131 MergeAAProc mergeProc = find_merge_aa_proc(mask->fFormat);
2132
2133 SkMask rowMask;
2134 rowMask.fFormat = SkMask::k3D_Format == mask->fFormat ? SkMask::kA8_Format : mask->fFormat;
2135 rowMask.fBounds.fLeft = clip.fLeft;
2136 rowMask.fBounds.fRight = clip.fRight;
2137 rowMask.fRowBytes = mask->fRowBytes; // doesn't matter, since our height==1
2138 rowMask.fImage = (uint8_t*)fScanlineScratch;
2139
2140 int y = clip.fTop;
2141 const int stopY = y + clip.height();
2142
2143 do {
reed@google.coma4c6e4d2012-06-20 14:29:50 +00002144 int localStopY SK_INIT_TO_AVOID_WARNING;
reed@google.com045e62d2011-10-24 12:19:46 +00002145 const uint8_t* row = fAAClip->findRow(y, &localStopY);
2146 // findRow returns last Y, not stop, so we add 1
2147 localStopY = SkMin32(localStopY + 1, stopY);
2148
2149 int initialCount;
2150 row = fAAClip->findX(row, clip.fLeft, &initialCount);
2151 do {
2152 mergeProc(src, width, row, initialCount, rowMask.fImage);
2153 rowMask.fBounds.fTop = y;
2154 rowMask.fBounds.fBottom = y + 1;
2155 fBlitter->blitMask(rowMask, rowMask.fBounds);
2156 src = (const void*)((const char*)src + srcRB);
2157 } while (++y < localStopY);
2158 } while (y < stopY);
reed@google.come36707a2011-10-04 21:38:55 +00002159}
2160
reed41e010c2015-06-09 12:16:53 -07002161const SkPixmap* SkAAClipBlitter::justAnOpaqueColor(uint32_t* value) {
halcanary96fcdcc2015-08-27 07:41:13 -07002162 return nullptr;
reed@google.come36707a2011-10-04 21:38:55 +00002163}