blob: c8764e27bd06b8606d553a64991f55bb37e6df65 [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/core/SkAAClip.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -04009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkPath.h"
11#include "include/private/SkColorData.h"
12#include "include/private/SkMacros.h"
13#include "include/private/SkTo.h"
14#include "src/core/SkBlitter.h"
15#include "src/core/SkRectPriv.h"
16#include "src/core/SkScan.h"
Mike Kleinde2244c2018-12-04 11:16:08 -050017#include <atomic>
Ben Wagnerf08d1d02018-06-18 15:11:00 -040018#include <utility>
19
reed@google.com045e62d2011-10-24 12:19:46 +000020class AutoAAClipValidate {
21public:
22 AutoAAClipValidate(const SkAAClip& clip) : fClip(clip) {
23 fClip.validate();
24 }
25 ~AutoAAClipValidate() {
26 fClip.validate();
27 }
28private:
29 const SkAAClip& fClip;
30};
31
32#ifdef SK_DEBUG
33 #define AUTO_AACLIP_VALIDATE(clip) AutoAAClipValidate acv(clip)
34#else
35 #define AUTO_AACLIP_VALIDATE(clip)
36#endif
37
38///////////////////////////////////////////////////////////////////////////////
39
reed@google.com1c04bf92011-10-10 12:57:12 +000040#define kMaxInt32 0x7FFFFFFF
41
commit-bot@chromium.org4b7d6732013-10-21 16:41:00 +000042#ifdef SK_DEBUG
reed@google.come36707a2011-10-04 21:38:55 +000043static inline bool x_in_rect(int x, const SkIRect& rect) {
44 return (unsigned)(x - rect.fLeft) < (unsigned)rect.width();
45}
commit-bot@chromium.org4b7d6732013-10-21 16:41:00 +000046#endif
reed@google.come36707a2011-10-04 21:38:55 +000047
48static inline bool y_in_rect(int y, const SkIRect& rect) {
49 return (unsigned)(y - rect.fTop) < (unsigned)rect.height();
50}
51
52/*
53 * Data runs are packed [count, alpha]
54 */
55
56struct SkAAClip::YOffset {
57 int32_t fY;
58 uint32_t fOffset;
59};
60
61struct SkAAClip::RunHead {
Mike Kleinde2244c2018-12-04 11:16:08 -050062 std::atomic<int32_t> fRefCnt;
reed@google.come36707a2011-10-04 21:38:55 +000063 int32_t fRowCount;
scroggo@google.com493c65f2013-02-05 18:49:00 +000064 size_t fDataSize;
rmistry@google.comfbfcd562012-08-23 18:09:54 +000065
reed@google.come36707a2011-10-04 21:38:55 +000066 YOffset* yoffsets() {
67 return (YOffset*)((char*)this + sizeof(RunHead));
68 }
69 const YOffset* yoffsets() const {
70 return (const YOffset*)((const char*)this + sizeof(RunHead));
71 }
72 uint8_t* data() {
73 return (uint8_t*)(this->yoffsets() + fRowCount);
74 }
75 const uint8_t* data() const {
76 return (const uint8_t*)(this->yoffsets() + fRowCount);
77 }
78
79 static RunHead* Alloc(int rowCount, size_t dataSize) {
80 size_t size = sizeof(RunHead) + rowCount * sizeof(YOffset) + dataSize;
81 RunHead* head = (RunHead*)sk_malloc_throw(size);
Mike Kleinde2244c2018-12-04 11:16:08 -050082 head->fRefCnt.store(1);
reed@google.come36707a2011-10-04 21:38:55 +000083 head->fRowCount = rowCount;
84 head->fDataSize = dataSize;
85 return head;
86 }
reed@google.com045e62d2011-10-24 12:19:46 +000087
88 static int ComputeRowSizeForWidth(int width) {
89 // 2 bytes per segment, where each segment can store up to 255 for count
90 int segments = 0;
91 while (width > 0) {
92 segments += 1;
Brian Osman7f364052020-02-06 11:25:43 -050093 int n = std::min(width, 255);
reed@google.com045e62d2011-10-24 12:19:46 +000094 width -= n;
95 }
96 return segments * 2; // each segment is row[0] + row[1] (n + alpha)
97 }
98
99 static RunHead* AllocRect(const SkIRect& bounds) {
100 SkASSERT(!bounds.isEmpty());
101 int width = bounds.width();
102 size_t rowSize = ComputeRowSizeForWidth(width);
103 RunHead* head = RunHead::Alloc(1, rowSize);
104 YOffset* yoff = head->yoffsets();
105 yoff->fY = bounds.height() - 1;
106 yoff->fOffset = 0;
107 uint8_t* row = head->data();
108 while (width > 0) {
Brian Osman7f364052020-02-06 11:25:43 -0500109 int n = std::min(width, 255);
reed@google.com045e62d2011-10-24 12:19:46 +0000110 row[0] = n;
111 row[1] = 0xFF;
112 width -= n;
113 row += 2;
114 }
115 return head;
116 }
reed@google.come36707a2011-10-04 21:38:55 +0000117};
118
reed@google.com32287892011-10-05 16:27:44 +0000119class SkAAClip::Iter {
120public:
121 Iter(const SkAAClip&);
122
123 bool done() const { return fDone; }
reed@google.com1c04bf92011-10-10 12:57:12 +0000124 int top() const { return fTop; }
125 int bottom() const { return fBottom; }
126 const uint8_t* data() const { return fData; }
reed@google.com32287892011-10-05 16:27:44 +0000127 void next();
128
129private:
130 const YOffset* fCurrYOff;
131 const YOffset* fStopYOff;
132 const uint8_t* fData;
133
134 int fTop, fBottom;
135 bool fDone;
136};
137
138SkAAClip::Iter::Iter(const SkAAClip& clip) {
139 if (clip.isEmpty()) {
140 fDone = true;
reed@google.com1c04bf92011-10-10 12:57:12 +0000141 fTop = fBottom = clip.fBounds.fBottom;
halcanary96fcdcc2015-08-27 07:41:13 -0700142 fData = nullptr;
143 fCurrYOff = nullptr;
144 fStopYOff = nullptr;
reed@google.com32287892011-10-05 16:27:44 +0000145 return;
146 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000147
reed@google.com32287892011-10-05 16:27:44 +0000148 const RunHead* head = clip.fRunHead;
149 fCurrYOff = head->yoffsets();
150 fStopYOff = fCurrYOff + head->fRowCount;
151 fData = head->data() + fCurrYOff->fOffset;
152
153 // setup first value
154 fTop = clip.fBounds.fTop;
155 fBottom = clip.fBounds.fTop + fCurrYOff->fY + 1;
156 fDone = false;
157}
158
159void SkAAClip::Iter::next() {
reed@google.com1c04bf92011-10-10 12:57:12 +0000160 if (!fDone) {
161 const YOffset* prev = fCurrYOff;
162 const YOffset* curr = prev + 1;
163 SkASSERT(curr <= fStopYOff);
reed@google.com32287892011-10-05 16:27:44 +0000164
reed@google.com32287892011-10-05 16:27:44 +0000165 fTop = fBottom;
reed@google.com1c04bf92011-10-10 12:57:12 +0000166 if (curr >= fStopYOff) {
167 fDone = true;
168 fBottom = kMaxInt32;
halcanary96fcdcc2015-08-27 07:41:13 -0700169 fData = nullptr;
reed@google.com1c04bf92011-10-10 12:57:12 +0000170 } else {
171 fBottom += curr->fY - prev->fY;
172 fData += curr->fOffset - prev->fOffset;
173 fCurrYOff = curr;
174 }
reed@google.com32287892011-10-05 16:27:44 +0000175 }
176}
177
reed@google.com045e62d2011-10-24 12:19:46 +0000178#ifdef SK_DEBUG
reed@google.comc9041912011-10-27 16:58:46 +0000179// assert we're exactly width-wide, and then return the number of bytes used
reed@google.com045e62d2011-10-24 12:19:46 +0000180static size_t compute_row_length(const uint8_t row[], int width) {
181 const uint8_t* origRow = row;
182 while (width > 0) {
183 int n = row[0];
reed@google.comc9041912011-10-27 16:58:46 +0000184 SkASSERT(n > 0);
reed@google.com045e62d2011-10-24 12:19:46 +0000185 SkASSERT(n <= width);
186 row += 2;
187 width -= n;
188 }
189 SkASSERT(0 == width);
190 return row - origRow;
191}
192
193void SkAAClip::validate() const {
halcanary96fcdcc2015-08-27 07:41:13 -0700194 if (nullptr == fRunHead) {
reed@google.com045e62d2011-10-24 12:19:46 +0000195 SkASSERT(fBounds.isEmpty());
196 return;
197 }
reedd7ec12e2016-06-20 10:21:24 -0700198 SkASSERT(!fBounds.isEmpty());
reed@google.com045e62d2011-10-24 12:19:46 +0000199
200 const RunHead* head = fRunHead;
Mike Kleinde2244c2018-12-04 11:16:08 -0500201 SkASSERT(head->fRefCnt.load() > 0);
reed@google.com045e62d2011-10-24 12:19:46 +0000202 SkASSERT(head->fRowCount > 0);
reed@google.com045e62d2011-10-24 12:19:46 +0000203
204 const YOffset* yoff = head->yoffsets();
205 const YOffset* ystop = yoff + head->fRowCount;
reed@google.comc9041912011-10-27 16:58:46 +0000206 const int lastY = fBounds.height() - 1;
207
208 // Y and offset must be monotonic
209 int prevY = -1;
210 int32_t prevOffset = -1;
reed@google.com045e62d2011-10-24 12:19:46 +0000211 while (yoff < ystop) {
reed@google.comc9041912011-10-27 16:58:46 +0000212 SkASSERT(prevY < yoff->fY);
213 SkASSERT(yoff->fY <= lastY);
214 prevY = yoff->fY;
215 SkASSERT(prevOffset < (int32_t)yoff->fOffset);
216 prevOffset = yoff->fOffset;
217 const uint8_t* row = head->data() + yoff->fOffset;
reed@google.com045e62d2011-10-24 12:19:46 +0000218 size_t rowLength = compute_row_length(row, fBounds.width());
scroggo@google.com493c65f2013-02-05 18:49:00 +0000219 SkASSERT(yoff->fOffset + rowLength <= head->fDataSize);
reed@google.comc9041912011-10-27 16:58:46 +0000220 yoff += 1;
reed@google.com045e62d2011-10-24 12:19:46 +0000221 }
reed@google.com045e62d2011-10-24 12:19:46 +0000222 // check the last entry;
223 --yoff;
reed@google.comc9041912011-10-27 16:58:46 +0000224 SkASSERT(yoff->fY == lastY);
reed@google.com045e62d2011-10-24 12:19:46 +0000225}
humper6d42d9c2014-08-08 11:45:46 -0700226
227static void dump_one_row(const uint8_t* SK_RESTRICT row,
228 int width, int leading_num) {
229 if (leading_num) {
230 SkDebugf( "%03d ", leading_num );
231 }
232 while (width > 0) {
233 int n = row[0];
234 int val = row[1];
235 char out = '.';
236 if (val == 0xff) {
237 out = '*';
238 } else if (val > 0) {
239 out = '+';
240 }
241 for (int i = 0 ; i < n ; i++) {
242 SkDebugf( "%c", out );
243 }
244 row += 2;
245 width -= n;
246 }
247 SkDebugf( "\n" );
248}
249
250void SkAAClip::debug(bool compress_y) const {
251 Iter iter(*this);
252 const int width = fBounds.width();
253
254 int y = fBounds.fTop;
255 while (!iter.done()) {
256 if (compress_y) {
257 dump_one_row(iter.data(), width, iter.bottom() - iter.top() + 1);
258 } else {
259 do {
260 dump_one_row(iter.data(), width, 0);
261 } while (++y < iter.bottom());
262 }
263 iter.next();
264 }
265}
reed@google.com045e62d2011-10-24 12:19:46 +0000266#endif
267
268///////////////////////////////////////////////////////////////////////////////
269
robertphillips@google.com768fee82012-08-02 12:42:43 +0000270// Count the number of zeros on the left and right edges of the passed in
271// RLE row. If 'row' is all zeros return 'width' in both variables.
reed@google.comc9041912011-10-27 16:58:46 +0000272static void count_left_right_zeros(const uint8_t* row, int width,
273 int* leftZ, int* riteZ) {
274 int zeros = 0;
275 do {
276 if (row[1]) {
277 break;
278 }
279 int n = row[0];
280 SkASSERT(n > 0);
281 SkASSERT(n <= width);
282 zeros += n;
283 row += 2;
284 width -= n;
285 } while (width > 0);
286 *leftZ = zeros;
287
robertphillips@google.com768fee82012-08-02 12:42:43 +0000288 if (0 == width) {
289 // this line is completely empty return 'width' in both variables
290 *riteZ = *leftZ;
291 return;
292 }
293
reed@google.comc9041912011-10-27 16:58:46 +0000294 zeros = 0;
295 while (width > 0) {
296 int n = row[0];
297 SkASSERT(n > 0);
298 if (0 == row[1]) {
299 zeros += n;
300 } else {
301 zeros = 0;
302 }
303 row += 2;
304 width -= n;
305 }
306 *riteZ = zeros;
307}
308
reed@google.comc9041912011-10-27 16:58:46 +0000309// modify row in place, trimming off (zeros) from the left and right sides.
310// return the number of bytes that were completely eliminated from the left
311static int trim_row_left_right(uint8_t* row, int width, int leftZ, int riteZ) {
312 int trim = 0;
313 while (leftZ > 0) {
314 SkASSERT(0 == row[1]);
315 int n = row[0];
316 SkASSERT(n > 0);
317 SkASSERT(n <= width);
318 width -= n;
319 row += 2;
320 if (n > leftZ) {
321 row[-2] = n - leftZ;
322 break;
323 }
324 trim += 2;
325 leftZ -= n;
326 SkASSERT(leftZ >= 0);
327 }
328
329 if (riteZ) {
330 // walk row to the end, and then we'll back up to trim riteZ
331 while (width > 0) {
332 int n = row[0];
333 SkASSERT(n <= width);
334 width -= n;
335 row += 2;
336 }
337 // now skip whole runs of zeros
338 do {
339 row -= 2;
340 SkASSERT(0 == row[1]);
341 int n = row[0];
342 SkASSERT(n > 0);
343 if (n > riteZ) {
344 row[0] = n - riteZ;
345 break;
346 }
347 riteZ -= n;
348 SkASSERT(riteZ >= 0);
349 } while (riteZ > 0);
350 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000351
reed@google.comc9041912011-10-27 16:58:46 +0000352 return trim;
353}
354
reed@google.comc9041912011-10-27 16:58:46 +0000355bool SkAAClip::trimLeftRight() {
reed@google.comc9041912011-10-27 16:58:46 +0000356 if (this->isEmpty()) {
357 return false;
358 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000359
reed@google.comc9041912011-10-27 16:58:46 +0000360 AUTO_AACLIP_VALIDATE(*this);
361
362 const int width = fBounds.width();
363 RunHead* head = fRunHead;
364 YOffset* yoff = head->yoffsets();
365 YOffset* stop = yoff + head->fRowCount;
366 uint8_t* base = head->data();
367
robertphillips@google.com768fee82012-08-02 12:42:43 +0000368 // After this loop, 'leftZeros' & 'rightZeros' will contain the minimum
369 // number of zeros on the left and right of the clip. This information
370 // can be used to shrink the bounding box.
reed@google.comc9041912011-10-27 16:58:46 +0000371 int leftZeros = width;
372 int riteZeros = width;
373 while (yoff < stop) {
374 int L, R;
375 count_left_right_zeros(base + yoff->fOffset, width, &L, &R);
robertphillips@google.com768fee82012-08-02 12:42:43 +0000376 SkASSERT(L + R < width || (L == width && R == width));
reed@google.comc9041912011-10-27 16:58:46 +0000377 if (L < leftZeros) {
378 leftZeros = L;
379 }
380 if (R < riteZeros) {
381 riteZeros = R;
382 }
383 if (0 == (leftZeros | riteZeros)) {
384 // no trimming to do
385 return true;
386 }
387 yoff += 1;
388 }
389
390 SkASSERT(leftZeros || riteZeros);
robertphillips@google.com768fee82012-08-02 12:42:43 +0000391 if (width == leftZeros) {
392 SkASSERT(width == riteZeros);
reed@google.comc9041912011-10-27 16:58:46 +0000393 return this->setEmpty();
394 }
395
396 this->validate();
397
398 fBounds.fLeft += leftZeros;
399 fBounds.fRight -= riteZeros;
400 SkASSERT(!fBounds.isEmpty());
401
402 // For now we don't realloc the storage (for time), we just shrink in place
403 // This means we don't have to do any memmoves either, since we can just
404 // play tricks with the yoff->fOffset for each row
405 yoff = head->yoffsets();
406 while (yoff < stop) {
407 uint8_t* row = base + yoff->fOffset;
408 SkDEBUGCODE((void)compute_row_length(row, width);)
409 yoff->fOffset += trim_row_left_right(row, width, leftZeros, riteZeros);
410 SkDEBUGCODE((void)compute_row_length(base + yoff->fOffset, width - leftZeros - riteZeros);)
411 yoff += 1;
412 }
413 return true;
414}
415
416static bool row_is_all_zeros(const uint8_t* row, int width) {
417 SkASSERT(width > 0);
418 do {
419 if (row[1]) {
420 return false;
421 }
422 int n = row[0];
423 SkASSERT(n <= width);
424 width -= n;
425 row += 2;
426 } while (width > 0);
427 SkASSERT(0 == width);
428 return true;
429}
430
431bool SkAAClip::trimTopBottom() {
432 if (this->isEmpty()) {
433 return false;
434 }
435
reed@google.comd6040f62011-10-28 02:39:17 +0000436 this->validate();
437
reed@google.comc9041912011-10-27 16:58:46 +0000438 const int width = fBounds.width();
439 RunHead* head = fRunHead;
440 YOffset* yoff = head->yoffsets();
441 YOffset* stop = yoff + head->fRowCount;
442 const uint8_t* base = head->data();
443
444 // Look to trim away empty rows from the top.
445 //
446 int skip = 0;
447 while (yoff < stop) {
448 const uint8_t* data = base + yoff->fOffset;
449 if (!row_is_all_zeros(data, width)) {
450 break;
451 }
452 skip += 1;
453 yoff += 1;
454 }
455 SkASSERT(skip <= head->fRowCount);
456 if (skip == head->fRowCount) {
457 return this->setEmpty();
458 }
459 if (skip > 0) {
460 // adjust fRowCount and fBounds.fTop, and slide all the data up
461 // as we remove [skip] number of YOffset entries
462 yoff = head->yoffsets();
463 int dy = yoff[skip - 1].fY + 1;
464 for (int i = skip; i < head->fRowCount; ++i) {
465 SkASSERT(yoff[i].fY >= dy);
466 yoff[i].fY -= dy;
467 }
468 YOffset* dst = head->yoffsets();
469 size_t size = head->fRowCount * sizeof(YOffset) + head->fDataSize;
470 memmove(dst, dst + skip, size - skip * sizeof(YOffset));
471
472 fBounds.fTop += dy;
473 SkASSERT(!fBounds.isEmpty());
474 head->fRowCount -= skip;
475 SkASSERT(head->fRowCount > 0);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000476
reed@google.comd6040f62011-10-28 02:39:17 +0000477 this->validate();
478 // need to reset this after the memmove
479 base = head->data();
reed@google.comc9041912011-10-27 16:58:46 +0000480 }
481
482 // Look to trim away empty rows from the bottom.
483 // We know that we have at least one non-zero row, so we can just walk
484 // backwards without checking for running past the start.
485 //
486 stop = yoff = head->yoffsets() + head->fRowCount;
487 do {
488 yoff -= 1;
489 } while (row_is_all_zeros(base + yoff->fOffset, width));
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000490 skip = SkToInt(stop - yoff - 1);
reed@google.comc9041912011-10-27 16:58:46 +0000491 SkASSERT(skip >= 0 && skip < head->fRowCount);
492 if (skip > 0) {
493 // removing from the bottom is easier than from the top, as we don't
494 // have to adjust any of the Y values, we just have to trim the array
495 memmove(stop - skip, stop, head->fDataSize);
496
497 fBounds.fBottom = fBounds.fTop + yoff->fY + 1;
498 SkASSERT(!fBounds.isEmpty());
499 head->fRowCount -= skip;
500 SkASSERT(head->fRowCount > 0);
501 }
reed@google.comd6040f62011-10-28 02:39:17 +0000502 this->validate();
reed@google.comc9041912011-10-27 16:58:46 +0000503
504 return true;
505}
506
reed@google.com045e62d2011-10-24 12:19:46 +0000507// can't validate before we're done, since trimming is part of the process of
508// making us valid after the Builder. Since we build from top to bottom, its
509// possible our fBounds.fBottom is bigger than our last scanline of data, so
510// we trim fBounds.fBottom back up.
511//
reed@google.com045e62d2011-10-24 12:19:46 +0000512// TODO: check for duplicates in X and Y to further compress our data
513//
514bool SkAAClip::trimBounds() {
515 if (this->isEmpty()) {
516 return false;
517 }
518
519 const RunHead* head = fRunHead;
520 const YOffset* yoff = head->yoffsets();
521
522 SkASSERT(head->fRowCount > 0);
523 const YOffset& lastY = yoff[head->fRowCount - 1];
524 SkASSERT(lastY.fY + 1 <= fBounds.height());
525 fBounds.fBottom = fBounds.fTop + lastY.fY + 1;
526 SkASSERT(lastY.fY + 1 == fBounds.height());
reed@google.comc9041912011-10-27 16:58:46 +0000527 SkASSERT(!fBounds.isEmpty());
528
529 return this->trimTopBottom() && this->trimLeftRight();
reed@google.com045e62d2011-10-24 12:19:46 +0000530}
531
reed@google.come36707a2011-10-04 21:38:55 +0000532///////////////////////////////////////////////////////////////////////////////
533
534void SkAAClip::freeRuns() {
reed@google.com47ac84e2011-10-06 13:11:25 +0000535 if (fRunHead) {
Mike Kleinde2244c2018-12-04 11:16:08 -0500536 SkASSERT(fRunHead->fRefCnt.load() >= 1);
537 if (1 == fRunHead->fRefCnt--) {
reed@google.come36707a2011-10-04 21:38:55 +0000538 sk_free(fRunHead);
539 }
540 }
541}
542
543SkAAClip::SkAAClip() {
544 fBounds.setEmpty();
halcanary96fcdcc2015-08-27 07:41:13 -0700545 fRunHead = nullptr;
reed@google.come36707a2011-10-04 21:38:55 +0000546}
547
548SkAAClip::SkAAClip(const SkAAClip& src) {
reed@google.com045e62d2011-10-24 12:19:46 +0000549 SkDEBUGCODE(fBounds.setEmpty();) // need this for validate
halcanary96fcdcc2015-08-27 07:41:13 -0700550 fRunHead = nullptr;
reed@google.come36707a2011-10-04 21:38:55 +0000551 *this = src;
552}
553
554SkAAClip::~SkAAClip() {
555 this->freeRuns();
556}
557
558SkAAClip& SkAAClip::operator=(const SkAAClip& src) {
reed@google.com045e62d2011-10-24 12:19:46 +0000559 AUTO_AACLIP_VALIDATE(*this);
560 src.validate();
561
reed@google.come36707a2011-10-04 21:38:55 +0000562 if (this != &src) {
563 this->freeRuns();
564 fBounds = src.fBounds;
565 fRunHead = src.fRunHead;
reed@google.com47ac84e2011-10-06 13:11:25 +0000566 if (fRunHead) {
Mike Kleinde2244c2018-12-04 11:16:08 -0500567 fRunHead->fRefCnt++;
reed@google.come36707a2011-10-04 21:38:55 +0000568 }
569 }
570 return *this;
571}
572
573bool operator==(const SkAAClip& a, const SkAAClip& b) {
reed@google.com045e62d2011-10-24 12:19:46 +0000574 a.validate();
575 b.validate();
576
reed@google.come36707a2011-10-04 21:38:55 +0000577 if (&a == &b) {
578 return true;
579 }
580 if (a.fBounds != b.fBounds) {
581 return false;
582 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000583
reed@google.come36707a2011-10-04 21:38:55 +0000584 const SkAAClip::RunHead* ah = a.fRunHead;
585 const SkAAClip::RunHead* bh = b.fRunHead;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000586
reed@google.come36707a2011-10-04 21:38:55 +0000587 // this catches empties and rects being equal
588 if (ah == bh) {
589 return true;
590 }
591
592 // now we insist that both are complex (but different ptrs)
reed@google.com47ac84e2011-10-06 13:11:25 +0000593 if (!a.fRunHead || !b.fRunHead) {
reed@google.come36707a2011-10-04 21:38:55 +0000594 return false;
595 }
596
597 return ah->fRowCount == bh->fRowCount &&
598 ah->fDataSize == bh->fDataSize &&
599 !memcmp(ah->data(), bh->data(), ah->fDataSize);
600}
601
602void SkAAClip::swap(SkAAClip& other) {
reed@google.com045e62d2011-10-24 12:19:46 +0000603 AUTO_AACLIP_VALIDATE(*this);
604 other.validate();
605
Ben Wagnerf08d1d02018-06-18 15:11:00 -0400606 using std::swap;
607 swap(fBounds, other.fBounds);
608 swap(fRunHead, other.fRunHead);
reed@google.come36707a2011-10-04 21:38:55 +0000609}
610
reed@google.com32287892011-10-05 16:27:44 +0000611bool SkAAClip::set(const SkAAClip& src) {
612 *this = src;
613 return !this->isEmpty();
614}
615
reed@google.come36707a2011-10-04 21:38:55 +0000616bool SkAAClip::setEmpty() {
617 this->freeRuns();
618 fBounds.setEmpty();
halcanary96fcdcc2015-08-27 07:41:13 -0700619 fRunHead = nullptr;
reed@google.come36707a2011-10-04 21:38:55 +0000620 return false;
621}
622
623bool SkAAClip::setRect(const SkIRect& bounds) {
Mike Reeda766ca92018-01-09 11:31:53 -0500624 if (bounds.isEmpty()) {
reed@google.come36707a2011-10-04 21:38:55 +0000625 return this->setEmpty();
626 }
reed@google.com47ac84e2011-10-06 13:11:25 +0000627
reed@google.com045e62d2011-10-24 12:19:46 +0000628 AUTO_AACLIP_VALIDATE(*this);
reed@google.com47ac84e2011-10-06 13:11:25 +0000629
reed@google.com045e62d2011-10-24 12:19:46 +0000630#if 0
reed@google.com47ac84e2011-10-06 13:11:25 +0000631 SkRect r;
632 r.set(bounds);
633 SkPath path;
634 path.addRect(r);
635 return this->setPath(path);
reed@google.com045e62d2011-10-24 12:19:46 +0000636#else
637 this->freeRuns();
638 fBounds = bounds;
639 fRunHead = RunHead::AllocRect(bounds);
640 SkASSERT(!this->isEmpty());
641 return true;
642#endif
reed@google.come36707a2011-10-04 21:38:55 +0000643}
644
reed202ab2a2014-08-07 11:48:10 -0700645bool SkAAClip::isRect() const {
646 if (this->isEmpty()) {
647 return false;
648 }
649
650 const RunHead* head = fRunHead;
651 if (head->fRowCount != 1) {
652 return false;
653 }
654 const YOffset* yoff = head->yoffsets();
655 if (yoff->fY != fBounds.fBottom - 1) {
656 return false;
657 }
658
659 const uint8_t* row = head->data() + yoff->fOffset;
660 int width = fBounds.width();
661 do {
662 if (row[1] != 0xFF) {
663 return false;
664 }
665 int n = row[0];
666 SkASSERT(n <= width);
667 width -= n;
668 row += 2;
669 } while (width > 0);
670 return true;
671}
672
reed@google.comf3c1da12011-10-10 19:35:47 +0000673bool SkAAClip::setRect(const SkRect& r, bool doAA) {
reed@google.come36707a2011-10-04 21:38:55 +0000674 if (r.isEmpty()) {
675 return this->setEmpty();
676 }
677
reed@google.com045e62d2011-10-24 12:19:46 +0000678 AUTO_AACLIP_VALIDATE(*this);
679
680 // TODO: special case this
681
Mike Reed5a1ebc62020-08-27 11:03:05 -0400682 return this->setPath(SkPath::Rect(r), nullptr, doAA);
reed@google.comf3c1da12011-10-10 19:35:47 +0000683}
684
reed@google.coma069c8f2011-11-28 19:54:56 +0000685static void append_run(SkTDArray<uint8_t>& array, uint8_t value, int count) {
686 SkASSERT(count >= 0);
687 while (count > 0) {
688 int n = count;
689 if (n > 255) {
690 n = 255;
691 }
692 uint8_t* data = array.append(2);
693 data[0] = n;
694 data[1] = value;
695 count -= n;
696 }
697}
698
reed@google.comf3c1da12011-10-10 19:35:47 +0000699bool SkAAClip::setRegion(const SkRegion& rgn) {
700 if (rgn.isEmpty()) {
701 return this->setEmpty();
702 }
703 if (rgn.isRect()) {
704 return this->setRect(rgn.getBounds());
705 }
reed@google.coma069c8f2011-11-28 19:54:56 +0000706
707#if 0
reed@google.comf3c1da12011-10-10 19:35:47 +0000708 SkAAClip clip;
709 SkRegion::Iterator iter(rgn);
710 for (; !iter.done(); iter.next()) {
711 clip.op(iter.rect(), SkRegion::kUnion_Op);
712 }
713 this->swap(clip);
reed@google.com3771a032011-10-11 17:18:04 +0000714 return !this->isEmpty();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000715#else
reed@google.coma069c8f2011-11-28 19:54:56 +0000716 const SkIRect& bounds = rgn.getBounds();
717 const int offsetX = bounds.fLeft;
718 const int offsetY = bounds.fTop;
719
720 SkTDArray<YOffset> yArray;
721 SkTDArray<uint8_t> xArray;
722
Brian Osman7f364052020-02-06 11:25:43 -0500723 yArray.setReserve(std::min(bounds.height(), 1024));
724 xArray.setReserve(std::min(bounds.width(), 512) * 128);
reed@google.coma069c8f2011-11-28 19:54:56 +0000725
726 SkRegion::Iterator iter(rgn);
727 int prevRight = 0;
728 int prevBot = 0;
halcanary96fcdcc2015-08-27 07:41:13 -0700729 YOffset* currY = nullptr;
reed@google.coma069c8f2011-11-28 19:54:56 +0000730
731 for (; !iter.done(); iter.next()) {
732 const SkIRect& r = iter.rect();
733 SkASSERT(bounds.contains(r));
734
735 int bot = r.fBottom - offsetY;
736 SkASSERT(bot >= prevBot);
737 if (bot > prevBot) {
738 if (currY) {
739 // flush current row
740 append_run(xArray, 0, bounds.width() - prevRight);
741 }
742 // did we introduce an empty-gap from the prev row?
743 int top = r.fTop - offsetY;
744 if (top > prevBot) {
745 currY = yArray.append();
746 currY->fY = top - 1;
747 currY->fOffset = xArray.count();
748 append_run(xArray, 0, bounds.width());
749 }
750 // create a new record for this Y value
751 currY = yArray.append();
752 currY->fY = bot - 1;
753 currY->fOffset = xArray.count();
754 prevRight = 0;
755 prevBot = bot;
756 }
757
758 int x = r.fLeft - offsetX;
759 append_run(xArray, 0, x - prevRight);
760
761 int w = r.fRight - r.fLeft;
762 append_run(xArray, 0xFF, w);
763 prevRight = x + w;
764 SkASSERT(prevRight <= bounds.width());
765 }
766 // flush last row
767 append_run(xArray, 0, bounds.width() - prevRight);
768
769 // now pack everything into a RunHead
770 RunHead* head = RunHead::Alloc(yArray.count(), xArray.bytes());
771 memcpy(head->yoffsets(), yArray.begin(), yArray.bytes());
772 memcpy(head->data(), xArray.begin(), xArray.bytes());
773
774 this->setEmpty();
775 fBounds = bounds;
776 fRunHead = head;
777 this->validate();
778 return true;
779#endif
reed@google.come36707a2011-10-04 21:38:55 +0000780}
781
782///////////////////////////////////////////////////////////////////////////////
783
784const uint8_t* SkAAClip::findRow(int y, int* lastYForRow) const {
reed@google.com47ac84e2011-10-06 13:11:25 +0000785 SkASSERT(fRunHead);
reed@google.come36707a2011-10-04 21:38:55 +0000786
787 if (!y_in_rect(y, fBounds)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700788 return nullptr;
reed@google.come36707a2011-10-04 21:38:55 +0000789 }
790 y -= fBounds.y(); // our yoffs values are relative to the top
791
792 const YOffset* yoff = fRunHead->yoffsets();
793 while (yoff->fY < y) {
794 yoff += 1;
795 SkASSERT(yoff - fRunHead->yoffsets() < fRunHead->fRowCount);
796 }
797
798 if (lastYForRow) {
reed@google.com045e62d2011-10-24 12:19:46 +0000799 *lastYForRow = fBounds.y() + yoff->fY;
reed@google.come36707a2011-10-04 21:38:55 +0000800 }
801 return fRunHead->data() + yoff->fOffset;
802}
803
804const uint8_t* SkAAClip::findX(const uint8_t data[], int x, int* initialCount) const {
805 SkASSERT(x_in_rect(x, fBounds));
806 x -= fBounds.x();
807
808 // first skip up to X
809 for (;;) {
810 int n = data[0];
811 if (x < n) {
reed@google.coma4c6e4d2012-06-20 14:29:50 +0000812 if (initialCount) {
813 *initialCount = n - x;
814 }
reed@google.come36707a2011-10-04 21:38:55 +0000815 break;
816 }
817 data += 2;
818 x -= n;
819 }
820 return data;
821}
822
823bool SkAAClip::quickContains(int left, int top, int right, int bottom) const {
824 if (this->isEmpty()) {
825 return false;
826 }
Mike Reed92b33352019-08-24 19:39:13 -0400827 if (!fBounds.contains(SkIRect{left, top, right, bottom})) {
reed@google.come36707a2011-10-04 21:38:55 +0000828 return false;
829 }
reed@google.com32287892011-10-05 16:27:44 +0000830#if 0
reed@google.come36707a2011-10-04 21:38:55 +0000831 if (this->isRect()) {
832 return true;
833 }
reed@google.com32287892011-10-05 16:27:44 +0000834#endif
reed@google.come36707a2011-10-04 21:38:55 +0000835
reed@google.coma4c6e4d2012-06-20 14:29:50 +0000836 int lastY SK_INIT_TO_AVOID_WARNING;
reed@google.come36707a2011-10-04 21:38:55 +0000837 const uint8_t* row = this->findRow(top, &lastY);
838 if (lastY < bottom) {
839 return false;
840 }
841 // now just need to check in X
reed@google.com045e62d2011-10-24 12:19:46 +0000842 int count;
843 row = this->findX(row, left, &count);
844#if 0
845 return count >= (right - left) && 0xFF == row[1];
846#else
847 int rectWidth = right - left;
848 while (0xFF == row[1]) {
849 if (count >= rectWidth) {
850 return true;
851 }
852 rectWidth -= count;
853 row += 2;
854 count = row[0];
855 }
856 return false;
857#endif
reed@google.come36707a2011-10-04 21:38:55 +0000858}
859
860///////////////////////////////////////////////////////////////////////////////
861
862class SkAAClip::Builder {
863 SkIRect fBounds;
864 struct Row {
865 int fY;
866 int fWidth;
867 SkTDArray<uint8_t>* fData;
868 };
869 SkTDArray<Row> fRows;
870 Row* fCurrRow;
871 int fPrevY;
872 int fWidth;
reed@google.com209c4152011-10-26 15:03:48 +0000873 int fMinY;
reed@google.come36707a2011-10-04 21:38:55 +0000874
875public:
876 Builder(const SkIRect& bounds) : fBounds(bounds) {
877 fPrevY = -1;
878 fWidth = bounds.width();
halcanary96fcdcc2015-08-27 07:41:13 -0700879 fCurrRow = nullptr;
reed@google.com209c4152011-10-26 15:03:48 +0000880 fMinY = bounds.fTop;
reed@google.come36707a2011-10-04 21:38:55 +0000881 }
882
883 ~Builder() {
884 Row* row = fRows.begin();
885 Row* stop = fRows.end();
886 while (row < stop) {
887 delete row->fData;
888 row += 1;
889 }
890 }
891
reed@google.com32287892011-10-05 16:27:44 +0000892 const SkIRect& getBounds() const { return fBounds; }
893
reed@google.come36707a2011-10-04 21:38:55 +0000894 void addRun(int x, int y, U8CPU alpha, int count) {
895 SkASSERT(count > 0);
896 SkASSERT(fBounds.contains(x, y));
897 SkASSERT(fBounds.contains(x + count - 1, y));
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000898
reed@google.come36707a2011-10-04 21:38:55 +0000899 x -= fBounds.left();
900 y -= fBounds.top();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000901
reed@google.come36707a2011-10-04 21:38:55 +0000902 Row* row = fCurrRow;
903 if (y != fPrevY) {
904 SkASSERT(y > fPrevY);
905 fPrevY = y;
906 row = this->flushRow(true);
907 row->fY = y;
908 row->fWidth = 0;
909 SkASSERT(row->fData);
910 SkASSERT(0 == row->fData->count());
911 fCurrRow = row;
912 }
913
914 SkASSERT(row->fWidth <= x);
915 SkASSERT(row->fWidth < fBounds.width());
916
917 SkTDArray<uint8_t>& data = *row->fData;
918
919 int gap = x - row->fWidth;
920 if (gap) {
921 AppendRun(data, 0, gap);
922 row->fWidth += gap;
923 SkASSERT(row->fWidth < fBounds.width());
924 }
925
926 AppendRun(data, alpha, count);
927 row->fWidth += count;
928 SkASSERT(row->fWidth <= fBounds.width());
929 }
930
tomhudson@google.com49eac192011-12-27 13:59:20 +0000931 void addColumn(int x, int y, U8CPU alpha, int height) {
932 SkASSERT(fBounds.contains(x, y + height - 1));
933
934 this->addRun(x, y, alpha, 1);
935 this->flushRowH(fCurrRow);
936 y -= fBounds.fTop;
937 SkASSERT(y == fCurrRow->fY);
938 fCurrRow->fY = y + height - 1;
939 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000940
reed@google.com9154eb02011-10-31 16:07:28 +0000941 void addRectRun(int x, int y, int width, int height) {
942 SkASSERT(fBounds.contains(x + width - 1, y + height - 1));
943 this->addRun(x, y, 0xFF, width);
944
reed@google.coma89c77b2011-12-01 21:47:26 +0000945 // we assum the rect must be all we'll see for these scanlines
reed@google.com9154eb02011-10-31 16:07:28 +0000946 // so we ensure our row goes all the way to our right
947 this->flushRowH(fCurrRow);
948
949 y -= fBounds.fTop;
950 SkASSERT(y == fCurrRow->fY);
951 fCurrRow->fY = y + height - 1;
952 }
953
tomhudson@google.com49eac192011-12-27 13:59:20 +0000954 void addAntiRectRun(int x, int y, int width, int height,
955 SkAlpha leftAlpha, SkAlpha rightAlpha) {
liyuqian2add0ff2016-10-20 11:04:39 -0700956 // According to SkBlitter.cpp, no matter whether leftAlpha is 0 or positive,
957 // we should always consider [x, x+1] as the left-most column and [x+1, x+1+width]
958 // as the rect with full alpha.
959 SkASSERT(fBounds.contains(x + width + (rightAlpha > 0 ? 1 : 0),
tomhudson@google.com49eac192011-12-27 13:59:20 +0000960 y + height - 1));
961 SkASSERT(width >= 0);
962
963 // Conceptually we're always adding 3 runs, but we should
964 // merge or omit them if possible.
965 if (leftAlpha == 0xFF) {
966 width++;
967 } else if (leftAlpha > 0) {
968 this->addRun(x++, y, leftAlpha, 1);
liyuqian2add0ff2016-10-20 11:04:39 -0700969 } else {
970 // leftAlpha is 0, ignore the left column
971 x++;
tomhudson@google.com49eac192011-12-27 13:59:20 +0000972 }
973 if (rightAlpha == 0xFF) {
974 width++;
975 }
976 if (width > 0) {
977 this->addRun(x, y, 0xFF, width);
978 }
979 if (rightAlpha > 0 && rightAlpha < 255) {
980 this->addRun(x + width, y, rightAlpha, 1);
981 }
982
Mike Reed1b7cd332018-06-05 09:55:17 -0400983 // if we never called addRun, we might not have a fCurrRow yet
984 if (fCurrRow) {
985 // we assume the rect must be all we'll see for these scanlines
986 // so we ensure our row goes all the way to our right
987 this->flushRowH(fCurrRow);
tomhudson@google.com49eac192011-12-27 13:59:20 +0000988
Mike Reed1b7cd332018-06-05 09:55:17 -0400989 y -= fBounds.fTop;
990 SkASSERT(y == fCurrRow->fY);
991 fCurrRow->fY = y + height - 1;
992 }
tomhudson@google.com49eac192011-12-27 13:59:20 +0000993 }
994
reed@google.com045e62d2011-10-24 12:19:46 +0000995 bool finish(SkAAClip* target) {
reed@google.come36707a2011-10-04 21:38:55 +0000996 this->flushRow(false);
997
998 const Row* row = fRows.begin();
999 const Row* stop = fRows.end();
1000
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001001 size_t dataSize = 0;
reed@google.come36707a2011-10-04 21:38:55 +00001002 while (row < stop) {
1003 dataSize += row->fData->count();
1004 row += 1;
1005 }
1006
reed@google.com045e62d2011-10-24 12:19:46 +00001007 if (0 == dataSize) {
1008 return target->setEmpty();
1009 }
1010
reed@google.com209c4152011-10-26 15:03:48 +00001011 SkASSERT(fMinY >= fBounds.fTop);
1012 SkASSERT(fMinY < fBounds.fBottom);
1013 int adjustY = fMinY - fBounds.fTop;
1014 fBounds.fTop = fMinY;
1015
reed@google.come36707a2011-10-04 21:38:55 +00001016 RunHead* head = RunHead::Alloc(fRows.count(), dataSize);
1017 YOffset* yoffset = head->yoffsets();
1018 uint8_t* data = head->data();
1019 uint8_t* baseData = data;
1020
1021 row = fRows.begin();
reed@google.comc9041912011-10-27 16:58:46 +00001022 SkDEBUGCODE(int prevY = row->fY - 1;)
reed@google.come36707a2011-10-04 21:38:55 +00001023 while (row < stop) {
reed@google.comc9041912011-10-27 16:58:46 +00001024 SkASSERT(prevY < row->fY); // must be monotonic
1025 SkDEBUGCODE(prevY = row->fY);
1026
reed@google.com209c4152011-10-26 15:03:48 +00001027 yoffset->fY = row->fY - adjustY;
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001028 yoffset->fOffset = SkToU32(data - baseData);
reed@google.come36707a2011-10-04 21:38:55 +00001029 yoffset += 1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001030
reed@google.come36707a2011-10-04 21:38:55 +00001031 size_t n = row->fData->count();
1032 memcpy(data, row->fData->begin(), n);
reed@google.comc9041912011-10-27 16:58:46 +00001033#ifdef SK_DEBUG
tomhudson@google.comf74ad8c2011-11-09 22:15:08 +00001034 size_t bytesNeeded = compute_row_length(data, fBounds.width());
reed@google.comc9041912011-10-27 16:58:46 +00001035 SkASSERT(bytesNeeded == n);
1036#endif
reed@google.come36707a2011-10-04 21:38:55 +00001037 data += n;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001038
reed@google.come36707a2011-10-04 21:38:55 +00001039 row += 1;
1040 }
1041
reed@google.com045e62d2011-10-24 12:19:46 +00001042 target->freeRuns();
1043 target->fBounds = fBounds;
1044 target->fRunHead = head;
1045 return target->trimBounds();
reed@google.come36707a2011-10-04 21:38:55 +00001046 }
1047
1048 void dump() {
1049 this->validate();
1050 int y;
1051 for (y = 0; y < fRows.count(); ++y) {
1052 const Row& row = fRows[y];
1053 SkDebugf("Y:%3d W:%3d", row.fY, row.fWidth);
1054 const SkTDArray<uint8_t>& data = *row.fData;
1055 int count = data.count();
1056 SkASSERT(!(count & 1));
1057 const uint8_t* ptr = data.begin();
1058 for (int x = 0; x < count; x += 2) {
1059 SkDebugf(" [%3d:%02X]", ptr[0], ptr[1]);
1060 ptr += 2;
1061 }
1062 SkDebugf("\n");
1063 }
reed@google.come36707a2011-10-04 21:38:55 +00001064 }
1065
1066 void validate() {
1067#ifdef SK_DEBUG
1068 int prevY = -1;
1069 for (int i = 0; i < fRows.count(); ++i) {
1070 const Row& row = fRows[i];
1071 SkASSERT(prevY < row.fY);
1072 SkASSERT(fWidth == row.fWidth);
1073 int count = row.fData->count();
1074 const uint8_t* ptr = row.fData->begin();
1075 SkASSERT(!(count & 1));
1076 int w = 0;
1077 for (int x = 0; x < count; x += 2) {
reed@google.comd6040f62011-10-28 02:39:17 +00001078 int n = ptr[0];
1079 SkASSERT(n > 0);
1080 w += n;
reed@google.come36707a2011-10-04 21:38:55 +00001081 SkASSERT(w <= fWidth);
1082 ptr += 2;
1083 }
1084 SkASSERT(w == fWidth);
1085 prevY = row.fY;
1086 }
1087#endif
1088 }
1089
reed@google.com209c4152011-10-26 15:03:48 +00001090 // only called by BuilderBlitter
1091 void setMinY(int y) {
1092 fMinY = y;
1093 }
1094
reed@google.come36707a2011-10-04 21:38:55 +00001095private:
reed@google.com9154eb02011-10-31 16:07:28 +00001096 void flushRowH(Row* row) {
1097 // flush current row if needed
1098 if (row->fWidth < fWidth) {
1099 AppendRun(*row->fData, 0, fWidth - row->fWidth);
1100 row->fWidth = fWidth;
1101 }
1102 }
reed@google.com209c4152011-10-26 15:03:48 +00001103
reed@google.come36707a2011-10-04 21:38:55 +00001104 Row* flushRow(bool readyForAnother) {
halcanary96fcdcc2015-08-27 07:41:13 -07001105 Row* next = nullptr;
reed@google.come36707a2011-10-04 21:38:55 +00001106 int count = fRows.count();
1107 if (count > 0) {
reed@google.com9154eb02011-10-31 16:07:28 +00001108 this->flushRowH(&fRows[count - 1]);
reed@google.come36707a2011-10-04 21:38:55 +00001109 }
1110 if (count > 1) {
1111 // are our last two runs the same?
1112 Row* prev = &fRows[count - 2];
1113 Row* curr = &fRows[count - 1];
1114 SkASSERT(prev->fWidth == fWidth);
1115 SkASSERT(curr->fWidth == fWidth);
1116 if (*prev->fData == *curr->fData) {
1117 prev->fY = curr->fY;
1118 if (readyForAnother) {
1119 curr->fData->rewind();
1120 next = curr;
1121 } else {
1122 delete curr->fData;
1123 fRows.removeShuffle(count - 1);
1124 }
1125 } else {
1126 if (readyForAnother) {
1127 next = fRows.append();
1128 next->fData = new SkTDArray<uint8_t>;
1129 }
1130 }
1131 } else {
1132 if (readyForAnother) {
1133 next = fRows.append();
1134 next->fData = new SkTDArray<uint8_t>;
1135 }
1136 }
1137 return next;
1138 }
1139
1140 static void AppendRun(SkTDArray<uint8_t>& data, U8CPU alpha, int count) {
1141 do {
1142 int n = count;
1143 if (n > 255) {
1144 n = 255;
1145 }
1146 uint8_t* ptr = data.append(2);
1147 ptr[0] = n;
1148 ptr[1] = alpha;
1149 count -= n;
1150 } while (count > 0);
1151 }
1152};
1153
1154class SkAAClip::BuilderBlitter : public SkBlitter {
reed@google.com80cdb9a2012-02-16 18:56:17 +00001155 int fLastY;
1156
1157 /*
1158 If we see a gap of 1 or more empty scanlines while building in Y-order,
1159 we inject an explicit empty scanline (alpha==0)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001160
reed@google.com80cdb9a2012-02-16 18:56:17 +00001161 See AAClipTest.cpp : test_path_with_hole()
1162 */
1163 void checkForYGap(int y) {
1164 SkASSERT(y >= fLastY);
1165 if (fLastY > -SK_MaxS32) {
1166 int gap = y - fLastY;
1167 if (gap > 1) {
1168 fBuilder->addRun(fLeft, y - 1, 0, fRight - fLeft);
1169 }
1170 }
1171 fLastY = y;
1172 }
1173
reed@google.come36707a2011-10-04 21:38:55 +00001174public:
reed@google.com80cdb9a2012-02-16 18:56:17 +00001175
reed@google.come36707a2011-10-04 21:38:55 +00001176 BuilderBlitter(Builder* builder) {
1177 fBuilder = builder;
reed@google.com17785642011-10-12 20:23:55 +00001178 fLeft = builder->getBounds().fLeft;
1179 fRight = builder->getBounds().fRight;
reed@google.com209c4152011-10-26 15:03:48 +00001180 fMinY = SK_MaxS32;
reed@google.com80cdb9a2012-02-16 18:56:17 +00001181 fLastY = -SK_MaxS32; // sentinel
reed@google.com209c4152011-10-26 15:03:48 +00001182 }
1183
1184 void finish() {
1185 if (fMinY < SK_MaxS32) {
1186 fBuilder->setMinY(fMinY);
1187 }
reed@google.come36707a2011-10-04 21:38:55 +00001188 }
1189
tomhudson@google.com49eac192011-12-27 13:59:20 +00001190 /**
1191 Must evaluate clips in scan-line order, so don't want to allow blitV(),
1192 but an AAClip can be clipped down to a single pixel wide, so we
1193 must support it (given AntiRect semantics: minimum width is 2).
1194 Instead we'll rely on the runtime asserts to guarantee Y monotonicity;
1195 any failure cases that misses may have minor artifacts.
1196 */
mtklein36352bf2015-03-25 18:17:31 -07001197 void blitV(int x, int y, int height, SkAlpha alpha) override {
liyuqian2add0ff2016-10-20 11:04:39 -07001198 if (height == 1) {
1199 // We're still in scan-line order if height is 1
1200 // This is useful for Analytic AA
1201 const SkAlpha alphas[2] = {alpha, 0};
1202 const int16_t runs[2] = {1, 0};
1203 this->blitAntiH(x, y, alphas, runs);
1204 } else {
1205 this->recordMinY(y);
1206 fBuilder->addColumn(x, y, alpha, height);
1207 fLastY = y + height - 1;
1208 }
tomhudson@google.com49eac192011-12-27 13:59:20 +00001209 }
reed@google.com045e62d2011-10-24 12:19:46 +00001210
mtklein36352bf2015-03-25 18:17:31 -07001211 void blitRect(int x, int y, int width, int height) override {
reed@google.com9154eb02011-10-31 16:07:28 +00001212 this->recordMinY(y);
reed@google.com80cdb9a2012-02-16 18:56:17 +00001213 this->checkForYGap(y);
reed@google.com9154eb02011-10-31 16:07:28 +00001214 fBuilder->addRectRun(x, y, width, height);
reed@google.com302b8612012-02-16 19:30:13 +00001215 fLastY = y + height - 1;
reed@google.com562a2ac2011-10-31 14:14:18 +00001216 }
reed@google.com045e62d2011-10-24 12:19:46 +00001217
John Stiles1cf2c8d2020-08-13 22:58:04 -04001218 void blitAntiRect(int x, int y, int width, int height,
1219 SkAlpha leftAlpha, SkAlpha rightAlpha) override {
tomhudson@google.com49eac192011-12-27 13:59:20 +00001220 this->recordMinY(y);
reed@google.com302b8612012-02-16 19:30:13 +00001221 this->checkForYGap(y);
tomhudson@google.com49eac192011-12-27 13:59:20 +00001222 fBuilder->addAntiRectRun(x, y, width, height, leftAlpha, rightAlpha);
reed@google.com302b8612012-02-16 19:30:13 +00001223 fLastY = y + height - 1;
tomhudson@google.com49eac192011-12-27 13:59:20 +00001224 }
1225
mtklein36352bf2015-03-25 18:17:31 -07001226 void blitMask(const SkMask&, const SkIRect& clip) override
reed@google.come36707a2011-10-04 21:38:55 +00001227 { unexpected(); }
1228
reed41e010c2015-06-09 12:16:53 -07001229 const SkPixmap* justAnOpaqueColor(uint32_t*) override {
halcanary96fcdcc2015-08-27 07:41:13 -07001230 return nullptr;
reed@google.come36707a2011-10-04 21:38:55 +00001231 }
1232
mtklein36352bf2015-03-25 18:17:31 -07001233 void blitH(int x, int y, int width) override {
reed@google.com209c4152011-10-26 15:03:48 +00001234 this->recordMinY(y);
reed@google.com80cdb9a2012-02-16 18:56:17 +00001235 this->checkForYGap(y);
reed@google.come36707a2011-10-04 21:38:55 +00001236 fBuilder->addRun(x, y, 0xFF, width);
1237 }
1238
John Stiles1cf2c8d2020-08-13 22:58:04 -04001239 void blitAntiH(int x, int y, const SkAlpha alpha[],
1240 const int16_t runs[]) override {
reed@google.com209c4152011-10-26 15:03:48 +00001241 this->recordMinY(y);
reed@google.com80cdb9a2012-02-16 18:56:17 +00001242 this->checkForYGap(y);
reed@google.come36707a2011-10-04 21:38:55 +00001243 for (;;) {
1244 int count = *runs;
1245 if (count <= 0) {
1246 return;
1247 }
reed@google.com17785642011-10-12 20:23:55 +00001248
1249 // The supersampler's buffer can be the width of the device, so
liyuqian1d3ab122016-11-08 13:55:15 -08001250 // we may have to trim the run to our bounds. Previously, we assert that
1251 // the extra spans are always alpha==0.
1252 // However, the analytic AA is too sensitive to precision errors
1253 // so it may have extra spans with very tiny alpha because after several
1254 // arithmatic operations, the edge may bleed the path boundary a little bit.
1255 // Therefore, instead of always asserting alpha==0, we assert alpha < 0x10.
reed@google.com17785642011-10-12 20:23:55 +00001256 int localX = x;
1257 int localCount = count;
1258 if (x < fLeft) {
liyuqian1d3ab122016-11-08 13:55:15 -08001259 SkASSERT(0x10 > *alpha);
reed@google.com17785642011-10-12 20:23:55 +00001260 int gap = fLeft - x;
1261 SkASSERT(gap <= count);
1262 localX += gap;
1263 localCount -= gap;
1264 }
1265 int right = x + count;
1266 if (right > fRight) {
liyuqian1d3ab122016-11-08 13:55:15 -08001267 SkASSERT(0x10 > *alpha);
reed@google.com17785642011-10-12 20:23:55 +00001268 localCount -= right - fRight;
1269 SkASSERT(localCount >= 0);
1270 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001271
reed@google.com17785642011-10-12 20:23:55 +00001272 if (localCount) {
1273 fBuilder->addRun(localX, y, *alpha, localCount);
1274 }
bsalomon@google.com820e80a2011-10-24 21:09:40 +00001275 // Next run
reed@google.come36707a2011-10-04 21:38:55 +00001276 runs += count;
1277 alpha += count;
1278 x += count;
1279 }
1280 }
1281
1282private:
1283 Builder* fBuilder;
reed@google.com17785642011-10-12 20:23:55 +00001284 int fLeft; // cache of builder's bounds' left edge
1285 int fRight;
reed@google.com209c4152011-10-26 15:03:48 +00001286 int fMinY;
1287
1288 /*
1289 * We track this, in case the scan converter skipped some number of
1290 * scanlines at the (relative to the bounds it was given). This allows
1291 * the builder, during its finish, to trip its bounds down to the "real"
1292 * top.
1293 */
1294 void recordMinY(int y) {
1295 if (y < fMinY) {
1296 fMinY = y;
1297 }
1298 }
reed@google.come36707a2011-10-04 21:38:55 +00001299
1300 void unexpected() {
Ben Wagner7ca9a742017-08-17 14:05:04 -04001301 SK_ABORT("---- did not expect to get called here");
reed@google.come36707a2011-10-04 21:38:55 +00001302 }
1303};
1304
reed@google.comf3c1da12011-10-10 19:35:47 +00001305bool SkAAClip::setPath(const SkPath& path, const SkRegion* clip, bool doAA) {
reed@google.com045e62d2011-10-24 12:19:46 +00001306 AUTO_AACLIP_VALIDATE(*this);
1307
reed@google.com32287892011-10-05 16:27:44 +00001308 if (clip && clip->isEmpty()) {
reed@google.come36707a2011-10-04 21:38:55 +00001309 return this->setEmpty();
1310 }
1311
1312 SkIRect ibounds;
reed@google.com32287892011-10-05 16:27:44 +00001313 path.getBounds().roundOut(&ibounds);
reed@google.come36707a2011-10-04 21:38:55 +00001314
reed@google.com32287892011-10-05 16:27:44 +00001315 SkRegion tmpClip;
halcanary96fcdcc2015-08-27 07:41:13 -07001316 if (nullptr == clip) {
reed@google.com32287892011-10-05 16:27:44 +00001317 tmpClip.setRect(ibounds);
1318 clip = &tmpClip;
1319 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001320
Yuqian Lica85fc32016-11-10 09:44:55 -05001321 // Since we assert that the BuilderBlitter will never blit outside the intersection
1322 // of clip and ibounds, we create this snugClip to be that intersection and send it
1323 // to the scan-converter.
1324 SkRegion snugClip(*clip);
1325
reed@google.com045e62d2011-10-24 12:19:46 +00001326 if (path.isInverseFillType()) {
1327 ibounds = clip->getBounds();
1328 } else {
reed@google.com32287892011-10-05 16:27:44 +00001329 if (ibounds.isEmpty() || !ibounds.intersect(clip->getBounds())) {
reed@google.come36707a2011-10-04 21:38:55 +00001330 return this->setEmpty();
1331 }
Yuqian Lica85fc32016-11-10 09:44:55 -05001332 snugClip.op(ibounds, SkRegion::kIntersect_Op);
reed@google.come36707a2011-10-04 21:38:55 +00001333 }
1334
1335 Builder builder(ibounds);
1336 BuilderBlitter blitter(&builder);
1337
reed@google.comf3c1da12011-10-10 19:35:47 +00001338 if (doAA) {
Mike Klein71f12662020-10-28 09:07:09 -05001339 SkScan::AntiFillPath(path, snugClip, &blitter, true);
reed@google.comf3c1da12011-10-10 19:35:47 +00001340 } else {
Mike Klein71f12662020-10-28 09:07:09 -05001341 SkScan::FillPath(path, snugClip, &blitter);
reed@google.comf3c1da12011-10-10 19:35:47 +00001342 }
reed@google.come36707a2011-10-04 21:38:55 +00001343
reed@google.com209c4152011-10-26 15:03:48 +00001344 blitter.finish();
reed@google.com045e62d2011-10-24 12:19:46 +00001345 return builder.finish(this);
reed@google.come36707a2011-10-04 21:38:55 +00001346}
1347
1348///////////////////////////////////////////////////////////////////////////////
1349
reed@google.com32287892011-10-05 16:27:44 +00001350typedef void (*RowProc)(SkAAClip::Builder&, int bottom,
1351 const uint8_t* rowA, const SkIRect& rectA,
1352 const uint8_t* rowB, const SkIRect& rectB);
1353
reed@google.com32287892011-10-05 16:27:44 +00001354typedef U8CPU (*AlphaProc)(U8CPU alphaA, U8CPU alphaB);
1355
1356static U8CPU sectAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1357 // Multiply
1358 return SkMulDiv255Round(alphaA, alphaB);
1359}
1360
1361static U8CPU unionAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1362 // SrcOver
1363 return alphaA + alphaB - SkMulDiv255Round(alphaA, alphaB);
1364}
1365
1366static U8CPU diffAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1367 // SrcOut
1368 return SkMulDiv255Round(alphaA, 0xFF - alphaB);
1369}
1370
1371static U8CPU xorAlphaProc(U8CPU alphaA, U8CPU alphaB) {
1372 // XOR
1373 return alphaA + alphaB - 2 * SkMulDiv255Round(alphaA, alphaB);
1374}
1375
1376static AlphaProc find_alpha_proc(SkRegion::Op op) {
1377 switch (op) {
1378 case SkRegion::kIntersect_Op:
1379 return sectAlphaProc;
1380 case SkRegion::kDifference_Op:
1381 return diffAlphaProc;
1382 case SkRegion::kUnion_Op:
1383 return unionAlphaProc;
1384 case SkRegion::kXOR_Op:
1385 return xorAlphaProc;
1386 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001387 SkDEBUGFAIL("unexpected region op");
reed@google.com32287892011-10-05 16:27:44 +00001388 return sectAlphaProc;
1389 }
1390}
1391
reed@google.com32287892011-10-05 16:27:44 +00001392class RowIter {
1393public:
1394 RowIter(const uint8_t* row, const SkIRect& bounds) {
1395 fRow = row;
1396 fLeft = bounds.fLeft;
reed@google.com32287892011-10-05 16:27:44 +00001397 fBoundsRight = bounds.fRight;
reed@google.com1c04bf92011-10-10 12:57:12 +00001398 if (row) {
1399 fRight = bounds.fLeft + row[0];
1400 SkASSERT(fRight <= fBoundsRight);
1401 fAlpha = row[1];
1402 fDone = false;
1403 } else {
1404 fDone = true;
1405 fRight = kMaxInt32;
1406 fAlpha = 0;
1407 }
reed@google.com32287892011-10-05 16:27:44 +00001408 }
1409
1410 bool done() const { return fDone; }
reed@google.com1c04bf92011-10-10 12:57:12 +00001411 int left() const { return fLeft; }
1412 int right() const { return fRight; }
1413 U8CPU alpha() const { return fAlpha; }
reed@google.com32287892011-10-05 16:27:44 +00001414 void next() {
reed@google.com1c04bf92011-10-10 12:57:12 +00001415 if (!fDone) {
reed@google.com32287892011-10-05 16:27:44 +00001416 fLeft = fRight;
reed@google.com1c04bf92011-10-10 12:57:12 +00001417 if (fRight == fBoundsRight) {
1418 fDone = true;
1419 fRight = kMaxInt32;
1420 fAlpha = 0;
1421 } else {
1422 fRow += 2;
1423 fRight += fRow[0];
1424 fAlpha = fRow[1];
1425 SkASSERT(fRight <= fBoundsRight);
1426 }
reed@google.com32287892011-10-05 16:27:44 +00001427 }
1428 }
1429
1430private:
1431 const uint8_t* fRow;
1432 int fLeft;
1433 int fRight;
1434 int fBoundsRight;
1435 bool fDone;
reed@google.com1c04bf92011-10-10 12:57:12 +00001436 uint8_t fAlpha;
reed@google.com32287892011-10-05 16:27:44 +00001437};
1438
reed@google.com1c04bf92011-10-10 12:57:12 +00001439static void adjust_row(RowIter& iter, int& leftA, int& riteA, int rite) {
1440 if (rite == riteA) {
1441 iter.next();
1442 leftA = iter.left();
1443 riteA = iter.right();
reed@google.com32287892011-10-05 16:27:44 +00001444 }
1445}
1446
caryclark@google.com803eceb2012-06-06 12:09:34 +00001447#if 0 // UNUSED
reed@google.com1c04bf92011-10-10 12:57:12 +00001448static bool intersect(int& min, int& max, int boundsMin, int boundsMax) {
1449 SkASSERT(min < max);
1450 SkASSERT(boundsMin < boundsMax);
1451 if (min >= boundsMax || max <= boundsMin) {
1452 return false;
1453 }
1454 if (min < boundsMin) {
1455 min = boundsMin;
1456 }
1457 if (max > boundsMax) {
1458 max = boundsMax;
1459 }
1460 return true;
1461}
caryclark@google.com803eceb2012-06-06 12:09:34 +00001462#endif
reed@google.com1c04bf92011-10-10 12:57:12 +00001463
reed@google.com32287892011-10-05 16:27:44 +00001464static void operatorX(SkAAClip::Builder& builder, int lastY,
1465 RowIter& iterA, RowIter& iterB,
1466 AlphaProc proc, const SkIRect& bounds) {
reed@google.com32287892011-10-05 16:27:44 +00001467 int leftA = iterA.left();
1468 int riteA = iterA.right();
reed@google.com32287892011-10-05 16:27:44 +00001469 int leftB = iterB.left();
1470 int riteB = iterB.right();
1471
reed@google.com1c04bf92011-10-10 12:57:12 +00001472 int prevRite = bounds.fLeft;
1473
1474 do {
reed@google.com32287892011-10-05 16:27:44 +00001475 U8CPU alphaA = 0;
1476 U8CPU alphaB = 0;
reed@google.com32287892011-10-05 16:27:44 +00001477 int left, rite;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001478
reed@google.com1c04bf92011-10-10 12:57:12 +00001479 if (leftA < leftB) {
reed@google.com32287892011-10-05 16:27:44 +00001480 left = leftA;
reed@google.com32287892011-10-05 16:27:44 +00001481 alphaA = iterA.alpha();
reed@google.com1c04bf92011-10-10 12:57:12 +00001482 if (riteA <= leftB) {
1483 rite = riteA;
1484 } else {
1485 rite = leftA = leftB;
reed@google.com32287892011-10-05 16:27:44 +00001486 }
reed@google.com1c04bf92011-10-10 12:57:12 +00001487 } else if (leftB < leftA) {
1488 left = leftB;
1489 alphaB = iterB.alpha();
1490 if (riteB <= leftA) {
1491 rite = riteB;
1492 } else {
1493 rite = leftB = leftA;
1494 }
1495 } else {
1496 left = leftA; // or leftB, since leftA == leftB
Brian Osman7f364052020-02-06 11:25:43 -05001497 rite = leftA = leftB = std::min(riteA, riteB);
reed@google.com1c04bf92011-10-10 12:57:12 +00001498 alphaA = iterA.alpha();
1499 alphaB = iterB.alpha();
reed@google.com32287892011-10-05 16:27:44 +00001500 }
1501
1502 if (left >= bounds.fRight) {
1503 break;
1504 }
reed@google.com34f7e472011-10-13 15:11:59 +00001505 if (rite > bounds.fRight) {
1506 rite = bounds.fRight;
1507 }
1508
reed@google.com32287892011-10-05 16:27:44 +00001509 if (left >= bounds.fLeft) {
reed@google.com1c04bf92011-10-10 12:57:12 +00001510 SkASSERT(rite > left);
reed@google.com32287892011-10-05 16:27:44 +00001511 builder.addRun(left, lastY, proc(alphaA, alphaB), rite - left);
reed@google.com1c04bf92011-10-10 12:57:12 +00001512 prevRite = rite;
reed@google.com32287892011-10-05 16:27:44 +00001513 }
reed@google.com1c04bf92011-10-10 12:57:12 +00001514
1515 adjust_row(iterA, leftA, riteA, rite);
1516 adjust_row(iterB, leftB, riteB, rite);
1517 } while (!iterA.done() || !iterB.done());
1518
1519 if (prevRite < bounds.fRight) {
1520 builder.addRun(prevRite, lastY, 0, bounds.fRight - prevRite);
reed@google.com32287892011-10-05 16:27:44 +00001521 }
1522}
1523
reed@google.com1c04bf92011-10-10 12:57:12 +00001524static void adjust_iter(SkAAClip::Iter& iter, int& topA, int& botA, int bot) {
1525 if (bot == botA) {
1526 iter.next();
1527 topA = botA;
1528 SkASSERT(botA == iter.top());
1529 botA = iter.bottom();
reed@google.com32287892011-10-05 16:27:44 +00001530 }
1531}
1532
1533static void operateY(SkAAClip::Builder& builder, const SkAAClip& A,
1534 const SkAAClip& B, SkRegion::Op op) {
1535 AlphaProc proc = find_alpha_proc(op);
1536 const SkIRect& bounds = builder.getBounds();
1537
1538 SkAAClip::Iter iterA(A);
1539 SkAAClip::Iter iterB(B);
1540
1541 SkASSERT(!iterA.done());
1542 int topA = iterA.top();
1543 int botA = iterA.bottom();
1544 SkASSERT(!iterB.done());
1545 int topB = iterB.top();
1546 int botB = iterB.bottom();
1547
Kevin Lubick493f89e2020-09-14 08:37:35 -04001548#if defined(SK_BUILD_FOR_FUZZER)
1549 if ((botA - topA) > 100000 || (botB - topB) > 100000) {
1550 return;
1551 }
1552#endif
1553
reed@google.com1c04bf92011-10-10 12:57:12 +00001554 do {
halcanary96fcdcc2015-08-27 07:41:13 -07001555 const uint8_t* rowA = nullptr;
1556 const uint8_t* rowB = nullptr;
reed@google.com32287892011-10-05 16:27:44 +00001557 int top, bot;
reed@google.com1c04bf92011-10-10 12:57:12 +00001558
1559 if (topA < topB) {
reed@google.com32287892011-10-05 16:27:44 +00001560 top = topA;
reed@google.com32287892011-10-05 16:27:44 +00001561 rowA = iterA.data();
reed@google.com1c04bf92011-10-10 12:57:12 +00001562 if (botA <= topB) {
1563 bot = botA;
1564 } else {
1565 bot = topA = topB;
reed@google.com32287892011-10-05 16:27:44 +00001566 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001567
reed@google.com1c04bf92011-10-10 12:57:12 +00001568 } else if (topB < topA) {
1569 top = topB;
1570 rowB = iterB.data();
1571 if (botB <= topA) {
1572 bot = botB;
1573 } else {
1574 bot = topB = topA;
1575 }
1576 } else {
1577 top = topA; // or topB, since topA == topB
Brian Osman7f364052020-02-06 11:25:43 -05001578 bot = topA = topB = std::min(botA, botB);
reed@google.com1c04bf92011-10-10 12:57:12 +00001579 rowA = iterA.data();
1580 rowB = iterB.data();
reed@google.com32287892011-10-05 16:27:44 +00001581 }
1582
1583 if (top >= bounds.fBottom) {
1584 break;
1585 }
reed@google.com34f7e472011-10-13 15:11:59 +00001586
1587 if (bot > bounds.fBottom) {
1588 bot = bounds.fBottom;
1589 }
1590 SkASSERT(top < bot);
1591
reed@google.com1c04bf92011-10-10 12:57:12 +00001592 if (!rowA && !rowB) {
1593 builder.addRun(bounds.fLeft, bot - 1, 0, bounds.width());
1594 } else if (top >= bounds.fTop) {
1595 SkASSERT(bot <= bounds.fBottom);
1596 RowIter rowIterA(rowA, rowA ? A.getBounds() : bounds);
1597 RowIter rowIterB(rowB, rowB ? B.getBounds() : bounds);
reed@google.com32287892011-10-05 16:27:44 +00001598 operatorX(builder, bot - 1, rowIterA, rowIterB, proc, bounds);
reed@google.com32287892011-10-05 16:27:44 +00001599 }
1600
reed@google.com1c04bf92011-10-10 12:57:12 +00001601 adjust_iter(iterA, topA, botA, bot);
1602 adjust_iter(iterB, topB, botB, bot);
1603 } while (!iterA.done() || !iterB.done());
reed@google.com32287892011-10-05 16:27:44 +00001604}
1605
1606bool SkAAClip::op(const SkAAClip& clipAOrig, const SkAAClip& clipBOrig,
1607 SkRegion::Op op) {
reed@google.com045e62d2011-10-24 12:19:46 +00001608 AUTO_AACLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001609
reed@google.com32287892011-10-05 16:27:44 +00001610 if (SkRegion::kReplace_Op == op) {
1611 return this->set(clipBOrig);
1612 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001613
reed@google.com32287892011-10-05 16:27:44 +00001614 const SkAAClip* clipA = &clipAOrig;
1615 const SkAAClip* clipB = &clipBOrig;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001616
reed@google.com32287892011-10-05 16:27:44 +00001617 if (SkRegion::kReverseDifference_Op == op) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001618 using std::swap;
1619 swap(clipA, clipB);
reed@google.com32287892011-10-05 16:27:44 +00001620 op = SkRegion::kDifference_Op;
1621 }
1622
1623 bool a_empty = clipA->isEmpty();
1624 bool b_empty = clipB->isEmpty();
1625
1626 SkIRect bounds;
1627 switch (op) {
1628 case SkRegion::kDifference_Op:
1629 if (a_empty) {
1630 return this->setEmpty();
1631 }
1632 if (b_empty || !SkIRect::Intersects(clipA->fBounds, clipB->fBounds)) {
1633 return this->set(*clipA);
1634 }
1635 bounds = clipA->fBounds;
1636 break;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001637
reed@google.com32287892011-10-05 16:27:44 +00001638 case SkRegion::kIntersect_Op:
1639 if ((a_empty | b_empty) || !bounds.intersect(clipA->fBounds,
1640 clipB->fBounds)) {
1641 return this->setEmpty();
1642 }
1643 break;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001644
reed@google.com32287892011-10-05 16:27:44 +00001645 case SkRegion::kUnion_Op:
1646 case SkRegion::kXOR_Op:
1647 if (a_empty) {
1648 return this->set(*clipB);
1649 }
1650 if (b_empty) {
1651 return this->set(*clipA);
1652 }
1653 bounds = clipA->fBounds;
1654 bounds.join(clipB->fBounds);
1655 break;
1656
1657 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001658 SkDEBUGFAIL("unknown region op");
reed@google.com32287892011-10-05 16:27:44 +00001659 return !this->isEmpty();
1660 }
1661
reed@google.com32287892011-10-05 16:27:44 +00001662 SkASSERT(SkIRect::Intersects(bounds, clipB->fBounds));
1663 SkASSERT(SkIRect::Intersects(bounds, clipB->fBounds));
1664
1665 Builder builder(bounds);
1666 operateY(builder, *clipA, *clipB, op);
reed@google.com045e62d2011-10-24 12:19:46 +00001667
1668 return builder.finish(this);
reed@google.come36707a2011-10-04 21:38:55 +00001669}
1670
reed@google.com045e62d2011-10-24 12:19:46 +00001671/*
1672 * It can be expensive to build a local aaclip before applying the op, so
1673 * we first see if we can restrict the bounds of new rect to our current
1674 * bounds, or note that the new rect subsumes our current clip.
1675 */
1676
1677bool SkAAClip::op(const SkIRect& rOrig, SkRegion::Op op) {
1678 SkIRect rStorage;
1679 const SkIRect* r = &rOrig;
1680
1681 switch (op) {
1682 case SkRegion::kIntersect_Op:
1683 if (!rStorage.intersect(rOrig, fBounds)) {
1684 // no overlap, so we're empty
1685 return this->setEmpty();
1686 }
1687 if (rStorage == fBounds) {
1688 // we were wholly inside the rect, no change
1689 return !this->isEmpty();
1690 }
1691 if (this->quickContains(rStorage)) {
1692 // the intersection is wholly inside us, we're a rect
1693 return this->setRect(rStorage);
1694 }
1695 r = &rStorage; // use the intersected bounds
1696 break;
1697 case SkRegion::kDifference_Op:
1698 break;
1699 case SkRegion::kUnion_Op:
1700 if (rOrig.contains(fBounds)) {
1701 return this->setRect(rOrig);
1702 }
1703 break;
1704 default:
1705 break;
1706 }
1707
reed@google.com47ac84e2011-10-06 13:11:25 +00001708 SkAAClip clip;
reed@google.com045e62d2011-10-24 12:19:46 +00001709 clip.setRect(*r);
reed@google.com47ac84e2011-10-06 13:11:25 +00001710 return this->op(*this, clip, op);
1711}
1712
reed@google.com045e62d2011-10-24 12:19:46 +00001713bool SkAAClip::op(const SkRect& rOrig, SkRegion::Op op, bool doAA) {
1714 SkRect rStorage, boundsStorage;
1715 const SkRect* r = &rOrig;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001716
reed@google.com045e62d2011-10-24 12:19:46 +00001717 boundsStorage.set(fBounds);
1718 switch (op) {
1719 case SkRegion::kIntersect_Op:
1720 case SkRegion::kDifference_Op:
1721 if (!rStorage.intersect(rOrig, boundsStorage)) {
reed@google.come56513d2012-06-25 20:06:33 +00001722 if (SkRegion::kIntersect_Op == op) {
1723 return this->setEmpty();
1724 } else { // kDifference
1725 return !this->isEmpty();
1726 }
reed@google.com045e62d2011-10-24 12:19:46 +00001727 }
1728 r = &rStorage; // use the intersected bounds
1729 break;
1730 case SkRegion::kUnion_Op:
1731 if (rOrig.contains(boundsStorage)) {
1732 return this->setRect(rOrig);
1733 }
1734 break;
1735 default:
1736 break;
1737 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001738
reed@google.com47ac84e2011-10-06 13:11:25 +00001739 SkAAClip clip;
reed@google.com045e62d2011-10-24 12:19:46 +00001740 clip.setRect(*r, doAA);
reed@google.com47ac84e2011-10-06 13:11:25 +00001741 return this->op(*this, clip, op);
1742}
1743
1744bool SkAAClip::op(const SkAAClip& clip, SkRegion::Op op) {
1745 return this->op(*this, clip, op);
1746}
1747
reed@google.come36707a2011-10-04 21:38:55 +00001748///////////////////////////////////////////////////////////////////////////////
reed@google.com045e62d2011-10-24 12:19:46 +00001749
1750bool SkAAClip::translate(int dx, int dy, SkAAClip* dst) const {
halcanary96fcdcc2015-08-27 07:41:13 -07001751 if (nullptr == dst) {
reed@google.com045e62d2011-10-24 12:19:46 +00001752 return !this->isEmpty();
1753 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001754
reed@google.com045e62d2011-10-24 12:19:46 +00001755 if (this->isEmpty()) {
1756 return dst->setEmpty();
1757 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001758
reed@google.com045e62d2011-10-24 12:19:46 +00001759 if (this != dst) {
Mike Kleinde2244c2018-12-04 11:16:08 -05001760 fRunHead->fRefCnt++;
tomhudson@google.com19224c32012-03-28 15:46:37 +00001761 dst->freeRuns();
reed@google.com045e62d2011-10-24 12:19:46 +00001762 dst->fRunHead = fRunHead;
1763 dst->fBounds = fBounds;
1764 }
1765 dst->fBounds.offset(dx, dy);
1766 return true;
1767}
1768
1769static void expand_row_to_mask(uint8_t* SK_RESTRICT mask,
1770 const uint8_t* SK_RESTRICT row,
1771 int width) {
1772 while (width > 0) {
1773 int n = row[0];
1774 SkASSERT(width >= n);
1775 memset(mask, row[1], n);
1776 mask += n;
1777 row += 2;
1778 width -= n;
1779 }
reed@google.coma069c8f2011-11-28 19:54:56 +00001780 SkASSERT(0 == width);
reed@google.com045e62d2011-10-24 12:19:46 +00001781}
1782
1783void SkAAClip::copyToMask(SkMask* mask) const {
1784 mask->fFormat = SkMask::kA8_Format;
1785 if (this->isEmpty()) {
1786 mask->fBounds.setEmpty();
halcanary96fcdcc2015-08-27 07:41:13 -07001787 mask->fImage = nullptr;
reed@google.com045e62d2011-10-24 12:19:46 +00001788 mask->fRowBytes = 0;
1789 return;
1790 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001791
reed@google.com045e62d2011-10-24 12:19:46 +00001792 mask->fBounds = fBounds;
1793 mask->fRowBytes = fBounds.width();
1794 size_t size = mask->computeImageSize();
1795 mask->fImage = SkMask::AllocImage(size);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001796
reed@google.com045e62d2011-10-24 12:19:46 +00001797 Iter iter(*this);
1798 uint8_t* dst = mask->fImage;
1799 const int width = fBounds.width();
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001800
reed@google.com045e62d2011-10-24 12:19:46 +00001801 int y = fBounds.fTop;
1802 while (!iter.done()) {
1803 do {
1804 expand_row_to_mask(dst, iter.data(), width);
1805 dst += mask->fRowBytes;
1806 } while (++y < iter.bottom());
1807 iter.next();
1808 }
1809}
1810
1811///////////////////////////////////////////////////////////////////////////////
reed@google.come36707a2011-10-04 21:38:55 +00001812///////////////////////////////////////////////////////////////////////////////
1813
1814static void expandToRuns(const uint8_t* SK_RESTRICT data, int initialCount, int width,
1815 int16_t* SK_RESTRICT runs, SkAlpha* SK_RESTRICT aa) {
1816 // we don't read our initial n from data, since the caller may have had to
1817 // clip it, hence the initialCount parameter.
1818 int n = initialCount;
1819 for (;;) {
1820 if (n > width) {
1821 n = width;
1822 }
1823 SkASSERT(n > 0);
1824 runs[0] = n;
1825 runs += n;
1826
1827 aa[0] = data[1];
1828 aa += n;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001829
reed@google.come36707a2011-10-04 21:38:55 +00001830 data += 2;
1831 width -= n;
1832 if (0 == width) {
1833 break;
1834 }
1835 // load the next count
1836 n = data[0];
1837 }
1838 runs[0] = 0; // sentinel
1839}
1840
1841SkAAClipBlitter::~SkAAClipBlitter() {
reed@google.com045e62d2011-10-24 12:19:46 +00001842 sk_free(fScanlineScratch);
reed@google.come36707a2011-10-04 21:38:55 +00001843}
1844
1845void SkAAClipBlitter::ensureRunsAndAA() {
halcanary96fcdcc2015-08-27 07:41:13 -07001846 if (nullptr == fScanlineScratch) {
reed@google.come36707a2011-10-04 21:38:55 +00001847 // add 1 so we can store the terminating run count of 0
1848 int count = fAAClipBounds.width() + 1;
reed@google.com045e62d2011-10-24 12:19:46 +00001849 // we use this either for fRuns + fAA, or a scaline of a mask
1850 // which may be as deep as 32bits
1851 fScanlineScratch = sk_malloc_throw(count * sizeof(SkPMColor));
1852 fRuns = (int16_t*)fScanlineScratch;
reed@google.come36707a2011-10-04 21:38:55 +00001853 fAA = (SkAlpha*)(fRuns + count);
1854 }
1855}
1856
1857void SkAAClipBlitter::blitH(int x, int y, int width) {
1858 SkASSERT(width > 0);
1859 SkASSERT(fAAClipBounds.contains(x, y));
1860 SkASSERT(fAAClipBounds.contains(x + width - 1, y));
1861
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001862 const uint8_t* row = fAAClip->findRow(y);
reed@google.come36707a2011-10-04 21:38:55 +00001863 int initialCount;
1864 row = fAAClip->findX(row, x, &initialCount);
1865
1866 if (initialCount >= width) {
1867 SkAlpha alpha = row[1];
1868 if (0 == alpha) {
1869 return;
1870 }
1871 if (0xFF == alpha) {
1872 fBlitter->blitH(x, y, width);
1873 return;
1874 }
1875 }
1876
1877 this->ensureRunsAndAA();
1878 expandToRuns(row, initialCount, width, fRuns, fAA);
1879
1880 fBlitter->blitAntiH(x, y, fAA, fRuns);
1881}
1882
1883static void merge(const uint8_t* SK_RESTRICT row, int rowN,
1884 const SkAlpha* SK_RESTRICT srcAA,
1885 const int16_t* SK_RESTRICT srcRuns,
1886 SkAlpha* SK_RESTRICT dstAA,
1887 int16_t* SK_RESTRICT dstRuns,
1888 int width) {
1889 SkDEBUGCODE(int accumulated = 0;)
1890 int srcN = srcRuns[0];
reed@google.com045e62d2011-10-24 12:19:46 +00001891 // do we need this check?
1892 if (0 == srcN) {
1893 return;
1894 }
1895
reed@google.come36707a2011-10-04 21:38:55 +00001896 for (;;) {
reed@google.come36707a2011-10-04 21:38:55 +00001897 SkASSERT(rowN > 0);
1898 SkASSERT(srcN > 0);
1899
1900 unsigned newAlpha = SkMulDiv255Round(srcAA[0], row[1]);
Brian Osman7f364052020-02-06 11:25:43 -05001901 int minN = std::min(srcN, rowN);
reed@google.come36707a2011-10-04 21:38:55 +00001902 dstRuns[0] = minN;
1903 dstRuns += minN;
1904 dstAA[0] = newAlpha;
1905 dstAA += minN;
1906
1907 if (0 == (srcN -= minN)) {
1908 srcN = srcRuns[0]; // refresh
1909 srcRuns += srcN;
1910 srcAA += srcN;
1911 srcN = srcRuns[0]; // reload
reed@google.com045e62d2011-10-24 12:19:46 +00001912 if (0 == srcN) {
1913 break;
1914 }
reed@google.come36707a2011-10-04 21:38:55 +00001915 }
1916 if (0 == (rowN -= minN)) {
1917 row += 2;
1918 rowN = row[0]; // reload
1919 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001920
reed@google.come36707a2011-10-04 21:38:55 +00001921 SkDEBUGCODE(accumulated += minN;)
1922 SkASSERT(accumulated <= width);
1923 }
reed@google.com34f7e472011-10-13 15:11:59 +00001924 dstRuns[0] = 0;
reed@google.come36707a2011-10-04 21:38:55 +00001925}
1926
1927void SkAAClipBlitter::blitAntiH(int x, int y, const SkAlpha aa[],
1928 const int16_t runs[]) {
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001929
1930 const uint8_t* row = fAAClip->findRow(y);
reed@google.come36707a2011-10-04 21:38:55 +00001931 int initialCount;
1932 row = fAAClip->findX(row, x, &initialCount);
1933
1934 this->ensureRunsAndAA();
1935
1936 merge(row, initialCount, aa, runs, fAA, fRuns, fAAClipBounds.width());
1937 fBlitter->blitAntiH(x, y, fAA, fRuns);
1938}
1939
1940void SkAAClipBlitter::blitV(int x, int y, int height, SkAlpha alpha) {
1941 if (fAAClip->quickContains(x, y, x + 1, y + height)) {
1942 fBlitter->blitV(x, y, height, alpha);
1943 return;
1944 }
1945
reed@google.com045e62d2011-10-24 12:19:46 +00001946 for (;;) {
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001947 int lastY SK_INIT_TO_AVOID_WARNING;
reed@google.come36707a2011-10-04 21:38:55 +00001948 const uint8_t* row = fAAClip->findRow(y, &lastY);
reed@google.com045e62d2011-10-24 12:19:46 +00001949 int dy = lastY - y + 1;
1950 if (dy > height) {
1951 dy = height;
1952 }
1953 height -= dy;
1954
reed@google.coma4c6e4d2012-06-20 14:29:50 +00001955 row = fAAClip->findX(row, x);
reed@google.come36707a2011-10-04 21:38:55 +00001956 SkAlpha newAlpha = SkMulDiv255Round(alpha, row[1]);
1957 if (newAlpha) {
reed@google.com045e62d2011-10-24 12:19:46 +00001958 fBlitter->blitV(x, y, dy, newAlpha);
1959 }
1960 SkASSERT(height >= 0);
1961 if (height <= 0) {
1962 break;
reed@google.come36707a2011-10-04 21:38:55 +00001963 }
1964 y = lastY + 1;
reed@google.com045e62d2011-10-24 12:19:46 +00001965 }
reed@google.come36707a2011-10-04 21:38:55 +00001966}
1967
1968void SkAAClipBlitter::blitRect(int x, int y, int width, int height) {
1969 if (fAAClip->quickContains(x, y, x + width, y + height)) {
1970 fBlitter->blitRect(x, y, width, height);
1971 return;
1972 }
1973
1974 while (--height >= 0) {
1975 this->blitH(x, y, width);
1976 y += 1;
1977 }
1978}
1979
reed@google.com045e62d2011-10-24 12:19:46 +00001980typedef void (*MergeAAProc)(const void* src, int width, const uint8_t* row,
1981 int initialRowCount, void* dst);
1982
1983static void small_memcpy(void* dst, const void* src, size_t n) {
1984 memcpy(dst, src, n);
1985}
1986
1987static void small_bzero(void* dst, size_t n) {
1988 sk_bzero(dst, n);
1989}
1990
1991static inline uint8_t mergeOne(uint8_t value, unsigned alpha) {
1992 return SkMulDiv255Round(value, alpha);
1993}
reedd54d3fc2014-11-13 14:39:58 -08001994
reed@google.com045e62d2011-10-24 12:19:46 +00001995static inline uint16_t mergeOne(uint16_t value, unsigned alpha) {
1996 unsigned r = SkGetPackedR16(value);
1997 unsigned g = SkGetPackedG16(value);
1998 unsigned b = SkGetPackedB16(value);
1999 return SkPackRGB16(SkMulDiv255Round(r, alpha),
caryclark@google.com803eceb2012-06-06 12:09:34 +00002000 SkMulDiv255Round(g, alpha),
2001 SkMulDiv255Round(b, alpha));
reed@google.com045e62d2011-10-24 12:19:46 +00002002}
reed@google.com045e62d2011-10-24 12:19:46 +00002003
herb94496162015-12-10 14:17:41 -08002004template <typename T>
2005void mergeT(const void* inSrc, int srcN, const uint8_t* SK_RESTRICT row, int rowN, void* inDst) {
2006 const T* SK_RESTRICT src = static_cast<const T*>(inSrc);
2007 T* SK_RESTRICT dst = static_cast<T*>(inDst);
reed@google.com045e62d2011-10-24 12:19:46 +00002008 for (;;) {
2009 SkASSERT(rowN > 0);
2010 SkASSERT(srcN > 0);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002011
Brian Osman7f364052020-02-06 11:25:43 -05002012 int n = std::min(rowN, srcN);
reed@google.com045e62d2011-10-24 12:19:46 +00002013 unsigned rowA = row[1];
2014 if (0xFF == rowA) {
2015 small_memcpy(dst, src, n * sizeof(T));
2016 } else if (0 == rowA) {
2017 small_bzero(dst, n * sizeof(T));
2018 } else {
2019 for (int i = 0; i < n; ++i) {
2020 dst[i] = mergeOne(src[i], rowA);
2021 }
2022 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002023
reed@google.com045e62d2011-10-24 12:19:46 +00002024 if (0 == (srcN -= n)) {
2025 break;
2026 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002027
reed@google.com045e62d2011-10-24 12:19:46 +00002028 src += n;
2029 dst += n;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002030
reed@google.com045e62d2011-10-24 12:19:46 +00002031 SkASSERT(rowN == n);
2032 row += 2;
2033 rowN = row[0];
2034 }
2035}
2036
2037static MergeAAProc find_merge_aa_proc(SkMask::Format format) {
2038 switch (format) {
2039 case SkMask::kBW_Format:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002040 SkDEBUGFAIL("unsupported");
halcanary96fcdcc2015-08-27 07:41:13 -07002041 return nullptr;
reed@google.com045e62d2011-10-24 12:19:46 +00002042 case SkMask::kA8_Format:
herb94496162015-12-10 14:17:41 -08002043 case SkMask::k3D_Format:
2044 return mergeT<uint8_t> ;
2045 case SkMask::kLCD16_Format:
2046 return mergeT<uint16_t>;
reed@google.com045e62d2011-10-24 12:19:46 +00002047 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00002048 SkDEBUGFAIL("unsupported");
halcanary96fcdcc2015-08-27 07:41:13 -07002049 return nullptr;
reed@google.com045e62d2011-10-24 12:19:46 +00002050 }
2051}
2052
2053static U8CPU bit2byte(int bitInAByte) {
2054 SkASSERT(bitInAByte <= 0xFF);
2055 // negation turns any non-zero into 0xFFFFFF??, so we just shift down
2056 // some value >= 8 to get a full FF value
2057 return -bitInAByte >> 8;
2058}
2059
2060static void upscaleBW2A8(SkMask* dstMask, const SkMask& srcMask) {
2061 SkASSERT(SkMask::kBW_Format == srcMask.fFormat);
2062 SkASSERT(SkMask::kA8_Format == dstMask->fFormat);
2063
2064 const int width = srcMask.fBounds.width();
2065 const int height = srcMask.fBounds.height();
2066
2067 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcMask.fImage;
2068 const size_t srcRB = srcMask.fRowBytes;
2069 uint8_t* SK_RESTRICT dst = (uint8_t*)dstMask->fImage;
2070 const size_t dstRB = dstMask->fRowBytes;
2071
2072 const int wholeBytes = width >> 3;
2073 const int leftOverBits = width & 7;
2074
2075 for (int y = 0; y < height; ++y) {
2076 uint8_t* SK_RESTRICT d = dst;
2077 for (int i = 0; i < wholeBytes; ++i) {
2078 int srcByte = src[i];
2079 d[0] = bit2byte(srcByte & (1 << 7));
2080 d[1] = bit2byte(srcByte & (1 << 6));
2081 d[2] = bit2byte(srcByte & (1 << 5));
2082 d[3] = bit2byte(srcByte & (1 << 4));
2083 d[4] = bit2byte(srcByte & (1 << 3));
2084 d[5] = bit2byte(srcByte & (1 << 2));
2085 d[6] = bit2byte(srcByte & (1 << 1));
2086 d[7] = bit2byte(srcByte & (1 << 0));
2087 d += 8;
2088 }
2089 if (leftOverBits) {
2090 int srcByte = src[wholeBytes];
2091 for (int x = 0; x < leftOverBits; ++x) {
2092 *d++ = bit2byte(srcByte & 0x80);
2093 srcByte <<= 1;
2094 }
2095 }
2096 src += srcRB;
2097 dst += dstRB;
2098 }
2099}
2100
2101void SkAAClipBlitter::blitMask(const SkMask& origMask, const SkIRect& clip) {
2102 SkASSERT(fAAClip->getBounds().contains(clip));
2103
2104 if (fAAClip->quickContains(clip)) {
2105 fBlitter->blitMask(origMask, clip);
2106 return;
2107 }
2108
2109 const SkMask* mask = &origMask;
2110
2111 // if we're BW, we need to upscale to A8 (ugh)
2112 SkMask grayMask;
reed@google.com045e62d2011-10-24 12:19:46 +00002113 if (SkMask::kBW_Format == origMask.fFormat) {
2114 grayMask.fFormat = SkMask::kA8_Format;
2115 grayMask.fBounds = origMask.fBounds;
2116 grayMask.fRowBytes = origMask.fBounds.width();
2117 size_t size = grayMask.computeImageSize();
2118 grayMask.fImage = (uint8_t*)fGrayMaskScratch.reset(size,
2119 SkAutoMalloc::kReuse_OnShrink);
2120
2121 upscaleBW2A8(&grayMask, origMask);
2122 mask = &grayMask;
2123 }
2124
2125 this->ensureRunsAndAA();
2126
2127 // HACK -- we are devolving 3D into A8, need to copy the rest of the 3D
2128 // data into a temp block to support it better (ugh)
2129
2130 const void* src = mask->getAddr(clip.fLeft, clip.fTop);
2131 const size_t srcRB = mask->fRowBytes;
2132 const int width = clip.width();
2133 MergeAAProc mergeProc = find_merge_aa_proc(mask->fFormat);
2134
2135 SkMask rowMask;
2136 rowMask.fFormat = SkMask::k3D_Format == mask->fFormat ? SkMask::kA8_Format : mask->fFormat;
2137 rowMask.fBounds.fLeft = clip.fLeft;
2138 rowMask.fBounds.fRight = clip.fRight;
2139 rowMask.fRowBytes = mask->fRowBytes; // doesn't matter, since our height==1
2140 rowMask.fImage = (uint8_t*)fScanlineScratch;
2141
2142 int y = clip.fTop;
2143 const int stopY = y + clip.height();
2144
2145 do {
reed@google.coma4c6e4d2012-06-20 14:29:50 +00002146 int localStopY SK_INIT_TO_AVOID_WARNING;
reed@google.com045e62d2011-10-24 12:19:46 +00002147 const uint8_t* row = fAAClip->findRow(y, &localStopY);
2148 // findRow returns last Y, not stop, so we add 1
Brian Osman7f364052020-02-06 11:25:43 -05002149 localStopY = std::min(localStopY + 1, stopY);
reed@google.com045e62d2011-10-24 12:19:46 +00002150
2151 int initialCount;
2152 row = fAAClip->findX(row, clip.fLeft, &initialCount);
2153 do {
2154 mergeProc(src, width, row, initialCount, rowMask.fImage);
2155 rowMask.fBounds.fTop = y;
2156 rowMask.fBounds.fBottom = y + 1;
2157 fBlitter->blitMask(rowMask, rowMask.fBounds);
2158 src = (const void*)((const char*)src + srcRB);
2159 } while (++y < localStopY);
2160 } while (y < stopY);
reed@google.come36707a2011-10-04 21:38:55 +00002161}
2162
reed41e010c2015-06-09 12:16:53 -07002163const SkPixmap* SkAAClipBlitter::justAnOpaqueColor(uint32_t* value) {
halcanary96fcdcc2015-08-27 07:41:13 -07002164 return nullptr;
reed@google.come36707a2011-10-04 21:38:55 +00002165}