Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkAAClip.cpp
Go to the documentation of this file.
1/*
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 "src/core/SkAAClip.h"
9
11#include "include/core/SkPath.h"
22#include "src/core/SkBlitter.h"
23#include "src/core/SkMask.h"
24#include "src/core/SkScan.h"
25
26#include <algorithm>
27#include <atomic>
28#include <cstring>
29
30namespace {
31
32class AutoAAClipValidate {
33public:
34 AutoAAClipValidate(const SkAAClip& clip) : fClip(clip) {
35 fClip.validate();
36 }
37 ~AutoAAClipValidate() {
38 fClip.validate();
39 }
40private:
41 const SkAAClip& fClip;
42};
43
44#ifdef SK_DEBUG
45 #define AUTO_AACLIP_VALIDATE(clip) AutoAAClipValidate acv(clip)
46#else
47 #define AUTO_AACLIP_VALIDATE(clip)
48#endif
49
50///////////////////////////////////////////////////////////////////////////////
51
52static constexpr int32_t kMaxInt32 = 0x7FFFFFFF;
53
54#ifdef SK_DEBUG
55// assert we're exactly width-wide, and then return the number of bytes used
56static size_t compute_row_length(const uint8_t row[], int width) {
57 const uint8_t* origRow = row;
58 while (width > 0) {
59 int n = row[0];
60 SkASSERT(n > 0);
61 SkASSERT(n <= width);
62 row += 2;
63 width -= n;
64 }
65 SkASSERT(0 == width);
66 return row - origRow;
67}
68#endif
69
70/*
71 * Data runs are packed [count, alpha]
72 */
73struct YOffset {
74 int32_t fY;
75 uint32_t fOffset;
76};
77
78class RowIter {
79public:
80 RowIter(const uint8_t* row, const SkIRect& bounds) {
81 fRow = row;
82 fLeft = bounds.fLeft;
83 fBoundsRight = bounds.fRight;
84 if (row) {
85 fRight = bounds.fLeft + row[0];
86 SkASSERT(fRight <= fBoundsRight);
87 fAlpha = row[1];
88 fDone = false;
89 } else {
90 fDone = true;
91 fRight = kMaxInt32;
92 fAlpha = 0;
93 }
94 }
95
96 bool done() const { return fDone; }
97 int left() const { return fLeft; }
98 int right() const { return fRight; }
99 U8CPU alpha() const { return fAlpha; }
100 void next() {
101 if (!fDone) {
102 fLeft = fRight;
103 if (fRight == fBoundsRight) {
104 fDone = true;
105 fRight = kMaxInt32;
106 fAlpha = 0;
107 } else {
108 fRow += 2;
109 fRight += fRow[0];
110 fAlpha = fRow[1];
111 SkASSERT(fRight <= fBoundsRight);
112 }
113 }
114 }
115
116private:
117 const uint8_t* fRow;
118 int fLeft;
119 int fRight;
120 int fBoundsRight;
121 bool fDone;
122 uint8_t fAlpha;
123};
124
125class Iter {
126public:
127 Iter() = default;
128
129 Iter(int y, const uint8_t* data, const YOffset* start, const YOffset* end)
130 : fCurrYOff(start)
131 , fStopYOff(end)
132 , fData(data + start->fOffset)
133 , fTop(y)
134 , fBottom(y + start->fY + 1)
135 , fDone(false) {}
136
137 bool done() const { return fDone; }
138 int top() const { return fTop; }
139 int bottom() const { return fBottom; }
140 const uint8_t* data() const { return fData; }
141
142 void next() {
143 if (!fDone) {
144 const YOffset* prev = fCurrYOff;
145 const YOffset* curr = prev + 1;
146 SkASSERT(curr <= fStopYOff);
147
148 fTop = fBottom;
149 if (curr >= fStopYOff) {
150 fDone = true;
151 fBottom = kMaxInt32;
152 fData = nullptr;
153 } else {
154 fBottom += curr->fY - prev->fY;
155 fData += curr->fOffset - prev->fOffset;
156 fCurrYOff = curr;
157 }
158 }
159 }
160
161private:
162 const YOffset* fCurrYOff = nullptr;
163 const YOffset* fStopYOff = nullptr;
164 const uint8_t* fData = nullptr;
165
166 int fTop = kMaxInt32;
167 int fBottom = kMaxInt32;
168 bool fDone = true;
169};
170
171} // namespace
172
173///////////////////////////////////////////////////////////////////////////////
174
176 std::atomic<int32_t> fRefCnt;
177 int32_t fRowCount;
178 size_t fDataSize;
179
180 YOffset* yoffsets() {
181 return (YOffset*)((char*)this + sizeof(RunHead));
182 }
183 const YOffset* yoffsets() const {
184 return (const YOffset*)((const char*)this + sizeof(RunHead));
185 }
186 uint8_t* data() {
187 return (uint8_t*)(this->yoffsets() + fRowCount);
188 }
189 const uint8_t* data() const {
190 return (const uint8_t*)(this->yoffsets() + fRowCount);
191 }
192
193 static RunHead* Alloc(int rowCount, size_t dataSize) {
194 size_t size = sizeof(RunHead) + rowCount * sizeof(YOffset) + dataSize;
195 RunHead* head = (RunHead*)sk_malloc_throw(size);
196 head->fRefCnt.store(1);
197 head->fRowCount = rowCount;
198 head->fDataSize = dataSize;
199 return head;
200 }
201
203 // 2 bytes per segment, where each segment can store up to 255 for count
204 int segments = 0;
205 while (width > 0) {
206 segments += 1;
207 int n = std::min(width, 255);
208 width -= n;
209 }
210 return segments * 2; // each segment is row[0] + row[1] (n + alpha)
211 }
212
213 static RunHead* AllocRect(const SkIRect& bounds) {
214 SkASSERT(!bounds.isEmpty());
215 int width = bounds.width();
216 size_t rowSize = ComputeRowSizeForWidth(width);
217 RunHead* head = RunHead::Alloc(1, rowSize);
218 YOffset* yoff = head->yoffsets();
219 yoff->fY = bounds.height() - 1;
220 yoff->fOffset = 0;
221 uint8_t* row = head->data();
222 while (width > 0) {
223 int n = std::min(width, 255);
224 row[0] = n;
225 row[1] = 0xFF;
226 width -= n;
227 row += 2;
228 }
229 return head;
230 }
231
232 static Iter Iterate(const SkAAClip& clip) {
233 const RunHead* head = clip.fRunHead;
234 if (!clip.fRunHead) {
235 // A null run head is an empty clip, so return aan already finished iterator.
236 return Iter();
237 }
238
239 return Iter(clip.getBounds().fTop, head->data(), head->yoffsets(),
240 head->yoffsets() + head->fRowCount);
241 }
242};
243
244///////////////////////////////////////////////////////////////////////////////
245
247 class Blitter;
248
250 struct Row {
251 int fY;
252 int fWidth;
253 SkTDArray<uint8_t>* fData;
254 };
255 SkTDArray<Row> fRows;
256 Row* fCurrRow;
257 int fPrevY;
258 int fWidth;
259 int fMinY;
260
261public:
262 Builder(const SkIRect& bounds) : fBounds(bounds) {
263 fPrevY = -1;
264 fWidth = bounds.width();
265 fCurrRow = nullptr;
266 fMinY = bounds.fTop;
267 }
268
270 Row* row = fRows.begin();
271 Row* stop = fRows.end();
272 while (row < stop) {
273 delete row->fData;
274 row += 1;
275 }
276 }
277
278 bool applyClipOp(SkAAClip* target, const SkAAClip& other, SkClipOp op);
279 bool blitPath(SkAAClip* target, const SkPath& path, bool doAA);
280
281private:
282 using AlphaProc = U8CPU (*)(U8CPU alphaA, U8CPU alphaB);
283 void operateX(int lastY, RowIter& iterA, RowIter& iterB, AlphaProc proc);
284 void operateY(const SkAAClip& A, const SkAAClip& B, SkClipOp op);
285
286 void addRun(int x, int y, U8CPU alpha, int count) {
287 SkASSERT(count > 0);
289 SkASSERT(fBounds.contains(x + count - 1, y));
290
291 x -= fBounds.left();
292 y -= fBounds.top();
293
294 Row* row = fCurrRow;
295 if (y != fPrevY) {
296 SkASSERT(y > fPrevY);
297 fPrevY = y;
298 row = this->flushRow(true);
299 row->fY = y;
300 row->fWidth = 0;
301 SkASSERT(row->fData);
302 SkASSERT(row->fData->empty());
303 fCurrRow = row;
304 }
305
306 SkASSERT(row->fWidth <= x);
307 SkASSERT(row->fWidth < fBounds.width());
308
309 SkTDArray<uint8_t>& data = *row->fData;
310
311 int gap = x - row->fWidth;
312 if (gap) {
313 AppendRun(data, 0, gap);
314 row->fWidth += gap;
315 SkASSERT(row->fWidth < fBounds.width());
316 }
317
318 AppendRun(data, alpha, count);
319 row->fWidth += count;
320 SkASSERT(row->fWidth <= fBounds.width());
321 }
322
323 void addColumn(int x, int y, U8CPU alpha, int height) {
325
326 this->addRun(x, y, alpha, 1);
327 this->flushRowH(fCurrRow);
328 y -= fBounds.fTop;
329 SkASSERT(y == fCurrRow->fY);
330 fCurrRow->fY = y + height - 1;
331 }
332
333 void addRectRun(int x, int y, int width, int height) {
334 SkASSERT(fBounds.contains(x + width - 1, y + height - 1));
335 this->addRun(x, y, 0xFF, width);
336
337 // we assum the rect must be all we'll see for these scanlines
338 // so we ensure our row goes all the way to our right
339 this->flushRowH(fCurrRow);
340
341 y -= fBounds.fTop;
342 SkASSERT(y == fCurrRow->fY);
343 fCurrRow->fY = y + height - 1;
344 }
345
346 void addAntiRectRun(int x, int y, int width, int height,
347 SkAlpha leftAlpha, SkAlpha rightAlpha) {
348 // According to SkBlitter.cpp, no matter whether leftAlpha is 0 or positive,
349 // we should always consider [x, x+1] as the left-most column and [x+1, x+1+width]
350 // as the rect with full alpha.
351 SkASSERT(fBounds.contains(x + width + (rightAlpha > 0 ? 1 : 0),
352 y + height - 1));
353 SkASSERT(width >= 0);
354
355 // Conceptually we're always adding 3 runs, but we should
356 // merge or omit them if possible.
357 if (leftAlpha == 0xFF) {
358 width++;
359 } else if (leftAlpha > 0) {
360 this->addRun(x++, y, leftAlpha, 1);
361 } else {
362 // leftAlpha is 0, ignore the left column
363 x++;
364 }
365 if (rightAlpha == 0xFF) {
366 width++;
367 }
368 if (width > 0) {
369 this->addRun(x, y, 0xFF, width);
370 }
371 if (rightAlpha > 0 && rightAlpha < 255) {
372 this->addRun(x + width, y, rightAlpha, 1);
373 }
374
375 // if we never called addRun, we might not have a fCurrRow yet
376 if (fCurrRow) {
377 // we assume the rect must be all we'll see for these scanlines
378 // so we ensure our row goes all the way to our right
379 this->flushRowH(fCurrRow);
380
381 y -= fBounds.fTop;
382 SkASSERT(y == fCurrRow->fY);
383 fCurrRow->fY = y + height - 1;
384 }
385 }
386
387 bool finish(SkAAClip* target) {
388 this->flushRow(false);
389
390 const Row* row = fRows.begin();
391 const Row* stop = fRows.end();
392
393 size_t dataSize = 0;
394 while (row < stop) {
395 dataSize += row->fData->size();
396 row += 1;
397 }
398
399 if (0 == dataSize) {
400 return target->setEmpty();
401 }
402
403 SkASSERT(fMinY >= fBounds.fTop);
404 SkASSERT(fMinY < fBounds.fBottom);
405 int adjustY = fMinY - fBounds.fTop;
406 fBounds.fTop = fMinY;
407
408 RunHead* head = RunHead::Alloc(fRows.size(), dataSize);
409 YOffset* yoffset = head->yoffsets();
410 uint8_t* data = head->data();
411 uint8_t* baseData = data;
412
413 row = fRows.begin();
414 SkDEBUGCODE(int prevY = row->fY - 1;)
415 while (row < stop) {
416 SkASSERT(prevY < row->fY); // must be monotonic
417 SkDEBUGCODE(prevY = row->fY);
418
419 yoffset->fY = row->fY - adjustY;
420 yoffset->fOffset = SkToU32(data - baseData);
421 yoffset += 1;
422
423 size_t n = row->fData->size();
424 memcpy(data, row->fData->begin(), n);
425 SkASSERT(compute_row_length(data, fBounds.width()) == n);
426 data += n;
427
428 row += 1;
429 }
430
431 target->freeRuns();
432 target->fBounds = fBounds;
433 target->fRunHead = head;
434 return target->trimBounds();
435 }
436
437 void dump() {
438 this->validate();
439 int y;
440 for (y = 0; y < fRows.size(); ++y) {
441 const Row& row = fRows[y];
442 SkDebugf("Y:%3d W:%3d", row.fY, row.fWidth);
443 const SkTDArray<uint8_t>& data = *row.fData;
444 int count = data.size();
445 SkASSERT(!(count & 1));
446 const uint8_t* ptr = data.begin();
447 for (int x = 0; x < count; x += 2) {
448 SkDebugf(" [%3d:%02X]", ptr[0], ptr[1]);
449 ptr += 2;
450 }
451 SkDebugf("\n");
452 }
453 }
454
455 void validate() {
456#ifdef SK_DEBUG
457 int prevY = -1;
458 for (int i = 0; i < fRows.size(); ++i) {
459 const Row& row = fRows[i];
460 SkASSERT(prevY < row.fY);
461 SkASSERT(fWidth == row.fWidth);
462 int count = row.fData->size();
463 const uint8_t* ptr = row.fData->begin();
464 SkASSERT(!(count & 1));
465 int w = 0;
466 for (int x = 0; x < count; x += 2) {
467 int n = ptr[0];
468 SkASSERT(n > 0);
469 w += n;
470 SkASSERT(w <= fWidth);
471 ptr += 2;
472 }
473 SkASSERT(w == fWidth);
474 prevY = row.fY;
475 }
476#endif
477 }
478
479 void flushRowH(Row* row) {
480 // flush current row if needed
481 if (row->fWidth < fWidth) {
482 AppendRun(*row->fData, 0, fWidth - row->fWidth);
483 row->fWidth = fWidth;
484 }
485 }
486
487 Row* flushRow(bool readyForAnother) {
488 Row* next = nullptr;
489 int count = fRows.size();
490 if (count > 0) {
491 this->flushRowH(&fRows[count - 1]);
492 }
493 if (count > 1) {
494 // are our last two runs the same?
495 Row* prev = &fRows[count - 2];
496 Row* curr = &fRows[count - 1];
497 SkASSERT(prev->fWidth == fWidth);
498 SkASSERT(curr->fWidth == fWidth);
499 if (*prev->fData == *curr->fData) {
500 prev->fY = curr->fY;
501 if (readyForAnother) {
502 curr->fData->clear();
503 next = curr;
504 } else {
505 delete curr->fData;
506 fRows.removeShuffle(count - 1);
507 }
508 } else {
509 if (readyForAnother) {
510 next = fRows.append();
511 next->fData = new SkTDArray<uint8_t>;
512 }
513 }
514 } else {
515 if (readyForAnother) {
516 next = fRows.append();
517 next->fData = new SkTDArray<uint8_t>;
518 }
519 }
520 return next;
521 }
522
523 static void AppendRun(SkTDArray<uint8_t>& data, U8CPU alpha, int count) {
524 do {
525 int n = count;
526 if (n > 255) {
527 n = 255;
528 }
529 uint8_t* ptr = data.append(2);
530 ptr[0] = n;
531 ptr[1] = alpha;
532 count -= n;
533 } while (count > 0);
534 }
535};
536
537void SkAAClip::Builder::operateX(int lastY, RowIter& iterA, RowIter& iterB, AlphaProc proc) {
538 auto advanceRowIter = [](RowIter& iter, int& iterLeft, int& iterRite, int rite) {
539 if (rite == iterRite) {
540 iter.next();
541 iterLeft = iter.left();
542 iterRite = iter.right();
543 }
544 };
545
546 int leftA = iterA.left();
547 int riteA = iterA.right();
548 int leftB = iterB.left();
549 int riteB = iterB.right();
550
551 int prevRite = fBounds.fLeft;
552
553 do {
554 U8CPU alphaA = 0;
555 U8CPU alphaB = 0;
556 int left, rite;
557
558 if (leftA < leftB) {
559 left = leftA;
560 alphaA = iterA.alpha();
561 if (riteA <= leftB) {
562 rite = riteA;
563 } else {
564 rite = leftA = leftB;
565 }
566 } else if (leftB < leftA) {
567 left = leftB;
568 alphaB = iterB.alpha();
569 if (riteB <= leftA) {
570 rite = riteB;
571 } else {
572 rite = leftB = leftA;
573 }
574 } else {
575 left = leftA; // or leftB, since leftA == leftB
576 rite = leftA = leftB = std::min(riteA, riteB);
577 alphaA = iterA.alpha();
578 alphaB = iterB.alpha();
579 }
580
581 if (left >= fBounds.fRight) {
582 break;
583 }
584 if (rite > fBounds.fRight) {
585 rite = fBounds.fRight;
586 }
587
588 if (left >= fBounds.fLeft) {
589 SkASSERT(rite > left);
590 this->addRun(left, lastY, proc(alphaA, alphaB), rite - left);
591 prevRite = rite;
592 }
593
594 advanceRowIter(iterA, leftA, riteA, rite);
595 advanceRowIter(iterB, leftB, riteB, rite);
596 } while (!iterA.done() || !iterB.done());
597
598 if (prevRite < fBounds.fRight) {
599 this->addRun(prevRite, lastY, 0, fBounds.fRight - prevRite);
600 }
601}
602
603void SkAAClip::Builder::operateY(const SkAAClip& A, const SkAAClip& B, SkClipOp op) {
604 static const AlphaProc kDiff = [](U8CPU a, U8CPU b) { return SkMulDiv255Round(a, 0xFF - b); };
605 static const AlphaProc kIntersect = [](U8CPU a, U8CPU b) { return SkMulDiv255Round(a, b); };
606 AlphaProc proc = (op == SkClipOp::kDifference) ? kDiff : kIntersect;
607
608 Iter iterA = RunHead::Iterate(A);
609 Iter iterB = RunHead::Iterate(B);
610
611 SkASSERT(!iterA.done());
612 int topA = iterA.top();
613 int botA = iterA.bottom();
614 SkASSERT(!iterB.done());
615 int topB = iterB.top();
616 int botB = iterB.bottom();
617
618 auto advanceIter = [](Iter& iter, int& iterTop, int& iterBot, int bot) {
619 if (bot == iterBot) {
620 iter.next();
621 iterTop = iterBot;
622 SkASSERT(iterBot == iter.top());
623 iterBot = iter.bottom();
624 }
625 };
626
627#if defined(SK_BUILD_FOR_FUZZER)
628 if ((botA - topA) > 100000 || (botB - topB) > 100000) {
629 return;
630 }
631#endif
632
633 do {
634 const uint8_t* rowA = nullptr;
635 const uint8_t* rowB = nullptr;
636 int top, bot;
637
638 if (topA < topB) {
639 top = topA;
640 rowA = iterA.data();
641 if (botA <= topB) {
642 bot = botA;
643 } else {
644 bot = topA = topB;
645 }
646
647 } else if (topB < topA) {
648 top = topB;
649 rowB = iterB.data();
650 if (botB <= topA) {
651 bot = botB;
652 } else {
653 bot = topB = topA;
654 }
655 } else {
656 top = topA; // or topB, since topA == topB
657 bot = topA = topB = std::min(botA, botB);
658 rowA = iterA.data();
659 rowB = iterB.data();
660 }
661
662 if (top >= fBounds.fBottom) {
663 break;
664 }
665
666 if (bot > fBounds.fBottom) {
667 bot = fBounds.fBottom;
668 }
669 SkASSERT(top < bot);
670
671 if (!rowA && !rowB) {
672 this->addRun(fBounds.fLeft, bot - 1, 0, fBounds.width());
673 } else if (top >= fBounds.fTop) {
674 SkASSERT(bot <= fBounds.fBottom);
675 RowIter rowIterA(rowA, rowA ? A.getBounds() : fBounds);
676 RowIter rowIterB(rowB, rowB ? B.getBounds() : fBounds);
677 this->operateX(bot - 1, rowIterA, rowIterB, proc);
678 }
679
680 advanceIter(iterA, topA, botA, bot);
681 advanceIter(iterB, topB, botB, bot);
682 } while (!iterA.done() || !iterB.done());
683}
684
686 int fLastY;
687
688 /*
689 If we see a gap of 1 or more empty scanlines while building in Y-order,
690 we inject an explicit empty scanline (alpha==0)
691
692 See AAClipTest.cpp : test_path_with_hole()
693 */
694 void checkForYGap(int y) {
695 SkASSERT(y >= fLastY);
696 if (fLastY > -SK_MaxS32) {
697 int gap = y - fLastY;
698 if (gap > 1) {
699 fBuilder->addRun(fLeft, y - 1, 0, fRight - fLeft);
700 }
701 }
702 fLastY = y;
703 }
704
705public:
706 Blitter(Builder* builder) {
707 fBuilder = builder;
708 fLeft = builder->fBounds.fLeft;
709 fRight = builder->fBounds.fRight;
710 fMinY = SK_MaxS32;
711 fLastY = -SK_MaxS32; // sentinel
712 }
713
714 void finish() {
715 if (fMinY < SK_MaxS32) {
716 fBuilder->fMinY = fMinY;
717 }
718 }
719
720 /**
721 Must evaluate clips in scan-line order, so don't want to allow blitV(),
722 but an AAClip can be clipped down to a single pixel wide, so we
723 must support it (given AntiRect semantics: minimum width is 2).
724 Instead we'll rely on the runtime asserts to guarantee Y monotonicity;
725 any failure cases that misses may have minor artifacts.
726 */
727 void blitV(int x, int y, int height, SkAlpha alpha) override {
728 if (height == 1) {
729 // We're still in scan-line order if height is 1
730 // This is useful for Analytic AA
731 const SkAlpha alphas[2] = {alpha, 0};
732 const int16_t runs[2] = {1, 0};
733 this->blitAntiH(x, y, alphas, runs);
734 } else {
735 this->recordMinY(y);
736 fBuilder->addColumn(x, y, alpha, height);
737 fLastY = y + height - 1;
738 }
739 }
740
741 void blitRect(int x, int y, int width, int height) override {
742 this->recordMinY(y);
743 this->checkForYGap(y);
744 fBuilder->addRectRun(x, y, width, height);
745 fLastY = y + height - 1;
746 }
747
748 void blitAntiRect(int x, int y, int width, int height,
749 SkAlpha leftAlpha, SkAlpha rightAlpha) override {
750 this->recordMinY(y);
751 this->checkForYGap(y);
752 fBuilder->addAntiRectRun(x, y, width, height, leftAlpha, rightAlpha);
753 fLastY = y + height - 1;
754 }
755
756 void blitMask(const SkMask&, const SkIRect& clip) override
757 { unexpected(); }
758
759 void blitH(int x, int y, int width) override {
760 this->recordMinY(y);
761 this->checkForYGap(y);
762 fBuilder->addRun(x, y, 0xFF, width);
763 }
764
765 void blitAntiH(int x, int y, const SkAlpha alpha[], const int16_t runs[]) override {
766 this->recordMinY(y);
767 this->checkForYGap(y);
768 for (;;) {
769 int count = *runs;
770 if (count <= 0) {
771 return;
772 }
773
774 // The supersampler's buffer can be the width of the device, so
775 // we may have to trim the run to our bounds. Previously, we assert that
776 // the extra spans are always alpha==0.
777 // However, the analytic AA is too sensitive to precision errors
778 // so it may have extra spans with very tiny alpha because after several
779 // arithmatic operations, the edge may bleed the path boundary a little bit.
780 // Therefore, instead of always asserting alpha==0, we assert alpha < 0x10.
781 int localX = x;
782 int localCount = count;
783 if (x < fLeft) {
784 SkASSERT(0x10 > *alpha);
785 int gap = fLeft - x;
786 SkASSERT(gap <= count);
787 localX += gap;
788 localCount -= gap;
789 }
790 int right = x + count;
791 if (right > fRight) {
792 SkASSERT(0x10 > *alpha);
793 localCount -= right - fRight;
794 SkASSERT(localCount >= 0);
795 }
796
797 if (localCount) {
798 fBuilder->addRun(localX, y, *alpha, localCount);
799 }
800 // Next run
801 runs += count;
802 alpha += count;
803 x += count;
804 }
805 }
806
807private:
808 Builder* fBuilder;
809 int fLeft; // cache of builder's bounds' left edge
810 int fRight;
811 int fMinY;
812
813 /*
814 * We track this, in case the scan converter skipped some number of
815 * scanlines at the (relative to the bounds it was given). This allows
816 * the builder, during its finish, to trip its bounds down to the "real"
817 * top.
818 */
819 void recordMinY(int y) {
820 if (y < fMinY) {
821 fMinY = y;
822 }
823 }
824
825 void unexpected() {
826 SK_ABORT("---- did not expect to get called here");
827 }
828};
829
831 this->operateY(*target, other, op);
832 return this->finish(target);
833}
834
835bool SkAAClip::Builder::blitPath(SkAAClip* target, const SkPath& path, bool doAA) {
836 Blitter blitter(this);
838
839 if (doAA) {
840 SkScan::AntiFillPath(path, clip, &blitter, true);
841 } else {
842 SkScan::FillPath(path, clip, &blitter);
843 }
844
845 blitter.finish();
846 return this->finish(target);
847}
848
849///////////////////////////////////////////////////////////////////////////////
850
852 auto expandRowToMask = [](uint8_t* dst, const uint8_t* row, int width) {
853 while (width > 0) {
854 int n = row[0];
855 SkASSERT(width >= n);
856 memset(dst, row[1], n);
857 dst += n;
858 row += 2;
859 width -= n;
860 }
861 SkASSERT(0 == width);
862 };
863
864 mask->format() = SkMask::kA8_Format;
865 if (this->isEmpty()) {
866 mask->bounds().setEmpty();
867 mask->image() = nullptr;
868 mask->rowBytes() = 0;
869 return;
870 }
871
872 mask->bounds() = fBounds;
873 mask->rowBytes() = fBounds.width();
874 size_t size = mask->computeImageSize();
875 mask->image() = SkMaskBuilder::AllocImage(size);
876
877 Iter iter = RunHead::Iterate(*this);
878 uint8_t* dst = mask->image();
879 const int width = fBounds.width();
880
881 int y = fBounds.fTop;
882 while (!iter.done()) {
883 do {
884 expandRowToMask(dst, iter.data(), width);
885 dst += mask->fRowBytes;
886 } while (++y < iter.bottom());
887 iter.next();
888 }
889}
890
891#ifdef SK_DEBUG
892
893void SkAAClip::validate() const {
894 if (nullptr == fRunHead) {
896 return;
897 }
899
900 const RunHead* head = fRunHead;
901 SkASSERT(head->fRefCnt.load() > 0);
902 SkASSERT(head->fRowCount > 0);
903
904 const YOffset* yoff = head->yoffsets();
905 const YOffset* ystop = yoff + head->fRowCount;
906 const int lastY = fBounds.height() - 1;
907
908 // Y and offset must be monotonic
909 int prevY = -1;
910 int32_t prevOffset = -1;
911 while (yoff < ystop) {
912 SkASSERT(prevY < yoff->fY);
913 SkASSERT(yoff->fY <= lastY);
914 prevY = yoff->fY;
915 SkASSERT(prevOffset < (int32_t)yoff->fOffset);
916 prevOffset = yoff->fOffset;
917 const uint8_t* row = head->data() + yoff->fOffset;
918 size_t rowLength = compute_row_length(row, fBounds.width());
919 SkASSERT(yoff->fOffset + rowLength <= head->fDataSize);
920 yoff += 1;
921 }
922 // check the last entry;
923 --yoff;
924 SkASSERT(yoff->fY == lastY);
925}
926
927static void dump_one_row(const uint8_t* SK_RESTRICT row,
928 int width, int leading_num) {
929 if (leading_num) {
930 SkDebugf( "%03d ", leading_num );
931 }
932 while (width > 0) {
933 int n = row[0];
934 int val = row[1];
935 char out = '.';
936 if (val == 0xff) {
937 out = '*';
938 } else if (val > 0) {
939 out = '+';
940 }
941 for (int i = 0 ; i < n ; i++) {
942 SkDebugf( "%c", out );
943 }
944 row += 2;
945 width -= n;
946 }
947 SkDebugf( "\n" );
948}
949
950void SkAAClip::debug(bool compress_y) const {
951 Iter iter = RunHead::Iterate(*this);
952 const int width = fBounds.width();
953
954 int y = fBounds.fTop;
955 while (!iter.done()) {
956 if (compress_y) {
957 dump_one_row(iter.data(), width, iter.bottom() - iter.top() + 1);
958 } else {
959 do {
960 dump_one_row(iter.data(), width, 0);
961 } while (++y < iter.bottom());
962 }
963 iter.next();
964 }
965}
966#endif
967
968///////////////////////////////////////////////////////////////////////////////
969
970// Count the number of zeros on the left and right edges of the passed in
971// RLE row. If 'row' is all zeros return 'width' in both variables.
972static void count_left_right_zeros(const uint8_t* row, int width,
973 int* leftZ, int* riteZ) {
974 int zeros = 0;
975 do {
976 if (row[1]) {
977 break;
978 }
979 int n = row[0];
980 SkASSERT(n > 0);
981 SkASSERT(n <= width);
982 zeros += n;
983 row += 2;
984 width -= n;
985 } while (width > 0);
986 *leftZ = zeros;
987
988 if (0 == width) {
989 // this line is completely empty return 'width' in both variables
990 *riteZ = *leftZ;
991 return;
992 }
993
994 zeros = 0;
995 while (width > 0) {
996 int n = row[0];
997 SkASSERT(n > 0);
998 if (0 == row[1]) {
999 zeros += n;
1000 } else {
1001 zeros = 0;
1002 }
1003 row += 2;
1004 width -= n;
1005 }
1006 *riteZ = zeros;
1007}
1008
1009// modify row in place, trimming off (zeros) from the left and right sides.
1010// return the number of bytes that were completely eliminated from the left
1011static int trim_row_left_right(uint8_t* row, int width, int leftZ, int riteZ) {
1012 int trim = 0;
1013 while (leftZ > 0) {
1014 SkASSERT(0 == row[1]);
1015 int n = row[0];
1016 SkASSERT(n > 0);
1017 SkASSERT(n <= width);
1018 width -= n;
1019 row += 2;
1020 if (n > leftZ) {
1021 row[-2] = n - leftZ;
1022 break;
1023 }
1024 trim += 2;
1025 leftZ -= n;
1026 SkASSERT(leftZ >= 0);
1027 }
1028
1029 if (riteZ) {
1030 // walk row to the end, and then we'll back up to trim riteZ
1031 while (width > 0) {
1032 int n = row[0];
1033 SkASSERT(n <= width);
1034 width -= n;
1035 row += 2;
1036 }
1037 // now skip whole runs of zeros
1038 do {
1039 row -= 2;
1040 SkASSERT(0 == row[1]);
1041 int n = row[0];
1042 SkASSERT(n > 0);
1043 if (n > riteZ) {
1044 row[0] = n - riteZ;
1045 break;
1046 }
1047 riteZ -= n;
1048 SkASSERT(riteZ >= 0);
1049 } while (riteZ > 0);
1050 }
1051
1052 return trim;
1053}
1054
1055bool SkAAClip::trimLeftRight() {
1056 if (this->isEmpty()) {
1057 return false;
1058 }
1059
1060 AUTO_AACLIP_VALIDATE(*this);
1061
1062 const int width = fBounds.width();
1063 RunHead* head = fRunHead;
1064 YOffset* yoff = head->yoffsets();
1065 YOffset* stop = yoff + head->fRowCount;
1066 uint8_t* base = head->data();
1067
1068 // After this loop, 'leftZeros' & 'rightZeros' will contain the minimum
1069 // number of zeros on the left and right of the clip. This information
1070 // can be used to shrink the bounding box.
1071 int leftZeros = width;
1072 int riteZeros = width;
1073 while (yoff < stop) {
1074 int L, R;
1075 count_left_right_zeros(base + yoff->fOffset, width, &L, &R);
1076 SkASSERT(L + R < width || (L == width && R == width));
1077 if (L < leftZeros) {
1078 leftZeros = L;
1079 }
1080 if (R < riteZeros) {
1081 riteZeros = R;
1082 }
1083 if (0 == (leftZeros | riteZeros)) {
1084 // no trimming to do
1085 return true;
1086 }
1087 yoff += 1;
1088 }
1089
1090 SkASSERT(leftZeros || riteZeros);
1091 if (width == leftZeros) {
1092 SkASSERT(width == riteZeros);
1093 return this->setEmpty();
1094 }
1095
1096 this->validate();
1097
1098 fBounds.fLeft += leftZeros;
1099 fBounds.fRight -= riteZeros;
1101
1102 // For now we don't realloc the storage (for time), we just shrink in place
1103 // This means we don't have to do any memmoves either, since we can just
1104 // play tricks with the yoff->fOffset for each row
1105 yoff = head->yoffsets();
1106 while (yoff < stop) {
1107 uint8_t* row = base + yoff->fOffset;
1108 SkDEBUGCODE((void)compute_row_length(row, width);)
1109 yoff->fOffset += trim_row_left_right(row, width, leftZeros, riteZeros);
1110 SkDEBUGCODE((void)compute_row_length(base + yoff->fOffset, width - leftZeros - riteZeros);)
1111 yoff += 1;
1112 }
1113 return true;
1114}
1115
1116static bool row_is_all_zeros(const uint8_t* row, int width) {
1117 SkASSERT(width > 0);
1118 do {
1119 if (row[1]) {
1120 return false;
1121 }
1122 int n = row[0];
1123 SkASSERT(n <= width);
1124 width -= n;
1125 row += 2;
1126 } while (width > 0);
1127 SkASSERT(0 == width);
1128 return true;
1129}
1130
1131bool SkAAClip::trimTopBottom() {
1132 if (this->isEmpty()) {
1133 return false;
1134 }
1135
1136 this->validate();
1137
1138 const int width = fBounds.width();
1139 RunHead* head = fRunHead;
1140 YOffset* yoff = head->yoffsets();
1141 YOffset* stop = yoff + head->fRowCount;
1142 const uint8_t* base = head->data();
1143
1144 // Look to trim away empty rows from the top.
1145 //
1146 int skip = 0;
1147 while (yoff < stop) {
1148 const uint8_t* data = base + yoff->fOffset;
1149 if (!row_is_all_zeros(data, width)) {
1150 break;
1151 }
1152 skip += 1;
1153 yoff += 1;
1154 }
1155 SkASSERT(skip <= head->fRowCount);
1156 if (skip == head->fRowCount) {
1157 return this->setEmpty();
1158 }
1159 if (skip > 0) {
1160 // adjust fRowCount and fBounds.fTop, and slide all the data up
1161 // as we remove [skip] number of YOffset entries
1162 yoff = head->yoffsets();
1163 int dy = yoff[skip - 1].fY + 1;
1164 for (int i = skip; i < head->fRowCount; ++i) {
1165 SkASSERT(yoff[i].fY >= dy);
1166 yoff[i].fY -= dy;
1167 }
1168 YOffset* dst = head->yoffsets();
1169 size_t size = head->fRowCount * sizeof(YOffset) + head->fDataSize;
1170 memmove(dst, dst + skip, size - skip * sizeof(YOffset));
1171
1172 fBounds.fTop += dy;
1174 head->fRowCount -= skip;
1175 SkASSERT(head->fRowCount > 0);
1176
1177 this->validate();
1178 // need to reset this after the memmove
1179 base = head->data();
1180 }
1181
1182 // Look to trim away empty rows from the bottom.
1183 // We know that we have at least one non-zero row, so we can just walk
1184 // backwards without checking for running past the start.
1185 //
1186 stop = yoff = head->yoffsets() + head->fRowCount;
1187 do {
1188 yoff -= 1;
1189 } while (row_is_all_zeros(base + yoff->fOffset, width));
1190 skip = SkToInt(stop - yoff - 1);
1191 SkASSERT(skip >= 0 && skip < head->fRowCount);
1192 if (skip > 0) {
1193 // removing from the bottom is easier than from the top, as we don't
1194 // have to adjust any of the Y values, we just have to trim the array
1195 memmove(stop - skip, stop, head->fDataSize);
1196
1197 fBounds.fBottom = fBounds.fTop + yoff->fY + 1;
1199 head->fRowCount -= skip;
1200 SkASSERT(head->fRowCount > 0);
1201 }
1202 this->validate();
1203
1204 return true;
1205}
1206
1207// can't validate before we're done, since trimming is part of the process of
1208// making us valid after the Builder. Since we build from top to bottom, its
1209// possible our fBounds.fBottom is bigger than our last scanline of data, so
1210// we trim fBounds.fBottom back up.
1211//
1212// TODO: check for duplicates in X and Y to further compress our data
1213//
1214bool SkAAClip::trimBounds() {
1215 if (this->isEmpty()) {
1216 return false;
1217 }
1218
1219 const RunHead* head = fRunHead;
1220 const YOffset* yoff = head->yoffsets();
1221
1222 SkASSERT(head->fRowCount > 0);
1223 const YOffset& lastY = yoff[head->fRowCount - 1];
1224 SkASSERT(lastY.fY + 1 <= fBounds.height());
1225 fBounds.fBottom = fBounds.fTop + lastY.fY + 1;
1226 SkASSERT(lastY.fY + 1 == fBounds.height());
1228
1229 return this->trimTopBottom() && this->trimLeftRight();
1230}
1231
1232///////////////////////////////////////////////////////////////////////////////
1233
1235 fBounds.setEmpty();
1236 fRunHead = nullptr;
1237}
1238
1240 SkDEBUGCODE(fBounds.setEmpty();) // need this for validate
1241 fRunHead = nullptr;
1242 *this = src;
1243}
1244
1246 this->freeRuns();
1247}
1248
1250 AUTO_AACLIP_VALIDATE(*this);
1251 src.validate();
1252
1253 if (this != &src) {
1254 this->freeRuns();
1255 fBounds = src.fBounds;
1256 fRunHead = src.fRunHead;
1257 if (fRunHead) {
1258 fRunHead->fRefCnt++;
1259 }
1260 }
1261 return *this;
1262}
1263
1265 this->freeRuns();
1266 fBounds.setEmpty();
1267 fRunHead = nullptr;
1268 return false;
1269}
1270
1271bool SkAAClip::setRect(const SkIRect& bounds) {
1272 if (bounds.isEmpty()) {
1273 return this->setEmpty();
1274 }
1275
1276 AUTO_AACLIP_VALIDATE(*this);
1277
1278 this->freeRuns();
1279 fBounds = bounds;
1280 fRunHead = RunHead::AllocRect(bounds);
1281 SkASSERT(!this->isEmpty());
1282 return true;
1283}
1284
1285bool SkAAClip::isRect() const {
1286 if (this->isEmpty()) {
1287 return false;
1288 }
1289
1290 const RunHead* head = fRunHead;
1291 if (head->fRowCount != 1) {
1292 return false;
1293 }
1294 const YOffset* yoff = head->yoffsets();
1295 if (yoff->fY != fBounds.fBottom - 1) {
1296 return false;
1297 }
1298
1299 const uint8_t* row = head->data() + yoff->fOffset;
1300 int width = fBounds.width();
1301 do {
1302 if (row[1] != 0xFF) {
1303 return false;
1304 }
1305 int n = row[0];
1306 SkASSERT(n <= width);
1307 width -= n;
1308 row += 2;
1309 } while (width > 0);
1310 return true;
1311}
1312
1314 if (rgn.isEmpty()) {
1315 return this->setEmpty();
1316 }
1317 if (rgn.isRect()) {
1318 return this->setRect(rgn.getBounds());
1319 }
1320
1321
1322 const SkIRect& bounds = rgn.getBounds();
1323 const int offsetX = bounds.fLeft;
1324 const int offsetY = bounds.fTop;
1325
1326 SkTDArray<YOffset> yArray;
1327 SkTDArray<uint8_t> xArray;
1328
1329 yArray.reserve(std::min(bounds.height(), 1024));
1330 xArray.reserve(std::min(bounds.width(), 512) * 128);
1331
1332 auto appendXRun = [&xArray](uint8_t value, int count) {
1333 SkASSERT(count >= 0);
1334 while (count > 0) {
1335 int n = count;
1336 if (n > 255) {
1337 n = 255;
1338 }
1339 uint8_t* data = xArray.append(2);
1340 data[0] = n;
1341 data[1] = value;
1342 count -= n;
1343 }
1344 };
1345
1346 SkRegion::Iterator iter(rgn);
1347 int prevRight = 0;
1348 int prevBot = 0;
1349 YOffset* currY = nullptr;
1350
1351 for (; !iter.done(); iter.next()) {
1352 const SkIRect& r = iter.rect();
1353 SkASSERT(bounds.contains(r));
1354
1355 int bot = r.fBottom - offsetY;
1356 SkASSERT(bot >= prevBot);
1357 if (bot > prevBot) {
1358 if (currY) {
1359 // flush current row
1360 appendXRun(0, bounds.width() - prevRight);
1361 }
1362 // did we introduce an empty-gap from the prev row?
1363 int top = r.fTop - offsetY;
1364 if (top > prevBot) {
1365 currY = yArray.append();
1366 currY->fY = top - 1;
1367 currY->fOffset = xArray.size();
1368 appendXRun(0, bounds.width());
1369 }
1370 // create a new record for this Y value
1371 currY = yArray.append();
1372 currY->fY = bot - 1;
1373 currY->fOffset = xArray.size();
1374 prevRight = 0;
1375 prevBot = bot;
1376 }
1377
1378 int x = r.fLeft - offsetX;
1379 appendXRun(0, x - prevRight);
1380
1381 int w = r.fRight - r.fLeft;
1382 appendXRun(0xFF, w);
1383 prevRight = x + w;
1384 SkASSERT(prevRight <= bounds.width());
1385 }
1386 // flush last row
1387 appendXRun(0, bounds.width() - prevRight);
1388
1389 // now pack everything into a RunHead
1390 RunHead* head = RunHead::Alloc(yArray.size(), xArray.size_bytes());
1391 memcpy(head->yoffsets(), yArray.begin(), yArray.size_bytes());
1392 memcpy(head->data(), xArray.begin(), xArray.size_bytes());
1393
1394 this->setEmpty();
1395 fBounds = bounds;
1396 fRunHead = head;
1397 this->validate();
1398 return true;
1399}
1400
1401bool SkAAClip::setPath(const SkPath& path, const SkIRect& clip, bool doAA) {
1402 AUTO_AACLIP_VALIDATE(*this);
1403
1404 if (clip.isEmpty()) {
1405 return this->setEmpty();
1406 }
1407
1408 SkIRect ibounds;
1409 // Since we assert that the BuilderBlitter will never blit outside the intersection
1410 // of clip and ibounds, we create the builder with the snug bounds.
1411 if (path.isInverseFillType()) {
1412 ibounds = clip;
1413 } else {
1414 path.getBounds().roundOut(&ibounds);
1415 if (ibounds.isEmpty() || !ibounds.intersect(clip)) {
1416 return this->setEmpty();
1417 }
1418 }
1419
1420 Builder builder(ibounds);
1421 return builder.blitPath(this, path, doAA);
1422}
1423
1424///////////////////////////////////////////////////////////////////////////////
1425
1426bool SkAAClip::op(const SkAAClip& other, SkClipOp op) {
1427 AUTO_AACLIP_VALIDATE(*this);
1428
1429 if (this->isEmpty()) {
1430 // Once the clip is empty, it cannot become un-empty.
1431 return false;
1432 }
1433
1434 SkIRect bounds = fBounds;
1435 switch(op) {
1437 if (other.isEmpty() || !SkIRect::Intersects(fBounds, other.fBounds)) {
1438 // this remains unmodified and isn't empty
1439 return true;
1440 }
1441 break;
1442
1444 if (other.isEmpty() || !bounds.intersect(other.fBounds)) {
1445 // the intersected clip becomes empty
1446 return this->setEmpty();
1447 }
1448 break;
1449 }
1450
1451
1453 SkASSERT(SkIRect::Intersects(bounds, other.fBounds));
1454
1455 Builder builder(bounds);
1456 return builder.applyClipOp(this, other, op);
1457}
1458
1459bool SkAAClip::op(const SkIRect& rect, SkClipOp op) {
1460 // It can be expensive to build a local aaclip before applying the op, so
1461 // we first see if we can restrict the bounds of new rect to our current
1462 // bounds, or note that the new rect subsumes our current clip.
1463 SkIRect pixelBounds = fBounds;
1464 if (!pixelBounds.intersect(rect)) {
1465 // No change or clip becomes empty depending on 'op'
1466 switch(op) {
1467 case SkClipOp::kDifference: return !this->isEmpty();
1468 case SkClipOp::kIntersect: return this->setEmpty();
1469 }
1471 } else if (pixelBounds == fBounds) {
1472 // Wholly inside 'rect', so clip becomes empty or remains unchanged
1473 switch(op) {
1474 case SkClipOp::kDifference: return this->setEmpty();
1475 case SkClipOp::kIntersect: return !this->isEmpty();
1476 }
1478 } else if (op == SkClipOp::kIntersect && this->quickContains(pixelBounds)) {
1479 // We become just the remaining rectangle
1480 return this->setRect(pixelBounds);
1481 } else {
1482 SkAAClip clip;
1483 clip.setRect(rect);
1484 return this->op(clip, op);
1485 }
1486}
1487
1488bool SkAAClip::op(const SkRect& rect, SkClipOp op, bool doAA) {
1489 if (!doAA) {
1490 return this->op(rect.round(), op);
1491 } else {
1492 // Tighten bounds for "path" aaclip of the rect
1493 SkIRect pixelBounds = fBounds;
1494 if (!pixelBounds.intersect(rect.roundOut())) {
1495 // No change or clip becomes empty depending on 'op'
1496 switch(op) {
1497 case SkClipOp::kDifference: return !this->isEmpty();
1498 case SkClipOp::kIntersect: return this->setEmpty();
1499 }
1501 } else if (rect.contains(SkRect::Make(fBounds))) {
1502 // Wholly inside 'rect', so clip becomes empty or remains unchanged
1503 switch(op) {
1504 case SkClipOp::kDifference: return this->setEmpty();
1505 case SkClipOp::kIntersect: return !this->isEmpty();
1506 }
1508 } else if (op == SkClipOp::kIntersect && this->quickContains(pixelBounds)) {
1509 // We become just the rect intersected with pixel bounds (preserving fractional coords
1510 // for AA edges).
1511 return this->setPath(SkPath::Rect(rect), pixelBounds, /*doAA=*/true);
1512 } else {
1513 SkAAClip rectClip;
1514 rectClip.setPath(SkPath::Rect(rect),
1515 op == SkClipOp::kDifference ? fBounds : pixelBounds,
1516 /*doAA=*/true);
1517 return this->op(rectClip, op);
1518 }
1519 }
1520}
1521
1522///////////////////////////////////////////////////////////////////////////////
1523
1524bool SkAAClip::translate(int dx, int dy, SkAAClip* dst) const {
1525 if (nullptr == dst) {
1526 return !this->isEmpty();
1527 }
1528
1529 if (this->isEmpty()) {
1530 return dst->setEmpty();
1531 }
1532
1533 if (this != dst) {
1534 fRunHead->fRefCnt++;
1535 dst->freeRuns();
1536 dst->fRunHead = fRunHead;
1537 dst->fBounds = fBounds;
1538 }
1539 dst->fBounds.offset(dx, dy);
1540 return true;
1541}
1542
1543void SkAAClip::freeRuns() {
1544 if (fRunHead) {
1545 SkASSERT(fRunHead->fRefCnt.load() >= 1);
1546 if (1 == fRunHead->fRefCnt--) {
1547 sk_free(fRunHead);
1548 }
1549 }
1550}
1551
1552const uint8_t* SkAAClip::findRow(int y, int* lastYForRow) const {
1553 SkASSERT(fRunHead);
1554
1555 if (y < fBounds.fTop || y >= fBounds.fBottom) {
1556 return nullptr;
1557 }
1558 y -= fBounds.y(); // our yoffs values are relative to the top
1559
1560 const YOffset* yoff = fRunHead->yoffsets();
1561 while (yoff->fY < y) {
1562 yoff += 1;
1563 SkASSERT(yoff - fRunHead->yoffsets() < fRunHead->fRowCount);
1564 }
1565
1566 if (lastYForRow) {
1567 *lastYForRow = fBounds.y() + yoff->fY;
1568 }
1569 return fRunHead->data() + yoff->fOffset;
1570}
1571
1572const uint8_t* SkAAClip::findX(const uint8_t data[], int x, int* initialCount) const {
1574 x -= fBounds.x();
1575
1576 // first skip up to X
1577 for (;;) {
1578 int n = data[0];
1579 if (x < n) {
1580 if (initialCount) {
1581 *initialCount = n - x;
1582 }
1583 break;
1584 }
1585 data += 2;
1586 x -= n;
1587 }
1588 return data;
1589}
1590
1591bool SkAAClip::quickContains(int left, int top, int right, int bottom) const {
1592 if (this->isEmpty()) {
1593 return false;
1594 }
1595 if (!fBounds.contains(SkIRect{left, top, right, bottom})) {
1596 return false;
1597 }
1598
1599 int lastY SK_INIT_TO_AVOID_WARNING;
1600 const uint8_t* row = this->findRow(top, &lastY);
1601 if (lastY < bottom) {
1602 return false;
1603 }
1604 // now just need to check in X
1605 int count;
1606 row = this->findX(row, left, &count);
1607
1608 int rectWidth = right - left;
1609 while (0xFF == row[1]) {
1610 if (count >= rectWidth) {
1611 return true;
1612 }
1613 rectWidth -= count;
1614 row += 2;
1615 count = row[0];
1616 }
1617 return false;
1618}
1619
1620///////////////////////////////////////////////////////////////////////////////
1621
1622static void expandToRuns(const uint8_t* SK_RESTRICT data, int initialCount, int width,
1623 int16_t* SK_RESTRICT runs, SkAlpha* SK_RESTRICT aa) {
1624 // we don't read our initial n from data, since the caller may have had to
1625 // clip it, hence the initialCount parameter.
1626 int n = initialCount;
1627 for (;;) {
1628 if (n > width) {
1629 n = width;
1630 }
1631 SkASSERT(n > 0);
1632 runs[0] = n;
1633 runs += n;
1634
1635 aa[0] = data[1];
1636 aa += n;
1637
1638 data += 2;
1639 width -= n;
1640 if (0 == width) {
1641 break;
1642 }
1643 // load the next count
1644 n = data[0];
1645 }
1646 runs[0] = 0; // sentinel
1647}
1648
1650 sk_free(fScanlineScratch);
1651}
1652
1653void SkAAClipBlitter::ensureRunsAndAA() {
1654 if (nullptr == fScanlineScratch) {
1655 // add 1 so we can store the terminating run count of 0
1656 int count = fAAClipBounds.width() + 1;
1657 // we use this either for fRuns + fAA, or a scaline of a mask
1658 // which may be as deep as 32bits
1659 fScanlineScratch = sk_malloc_throw(count * sizeof(SkPMColor));
1660 fRuns = (int16_t*)fScanlineScratch;
1661 fAA = (SkAlpha*)(fRuns + count);
1662 }
1663}
1664
1665void SkAAClipBlitter::blitH(int x, int y, int width) {
1666 SkASSERT(width > 0);
1667 SkASSERT(fAAClipBounds.contains(x, y));
1668 SkASSERT(fAAClipBounds.contains(x + width - 1, y));
1669
1670 const uint8_t* row = fAAClip->findRow(y);
1671 int initialCount;
1672 row = fAAClip->findX(row, x, &initialCount);
1673
1674 if (initialCount >= width) {
1675 SkAlpha alpha = row[1];
1676 if (0 == alpha) {
1677 return;
1678 }
1679 if (0xFF == alpha) {
1680 fBlitter->blitH(x, y, width);
1681 return;
1682 }
1683 }
1684
1685 this->ensureRunsAndAA();
1686 expandToRuns(row, initialCount, width, fRuns, fAA);
1687
1688 fBlitter->blitAntiH(x, y, fAA, fRuns);
1689}
1690
1691static void merge(const uint8_t* SK_RESTRICT row, int rowN,
1692 const SkAlpha* SK_RESTRICT srcAA,
1693 const int16_t* SK_RESTRICT srcRuns,
1694 SkAlpha* SK_RESTRICT dstAA,
1695 int16_t* SK_RESTRICT dstRuns,
1696 int width) {
1697 SkDEBUGCODE(int accumulated = 0;)
1698 int srcN = srcRuns[0];
1699 // do we need this check?
1700 if (0 == srcN) {
1701 return;
1702 }
1703
1704 for (;;) {
1705 SkASSERT(rowN > 0);
1706 SkASSERT(srcN > 0);
1707
1708 unsigned newAlpha = SkMulDiv255Round(srcAA[0], row[1]);
1709 int minN = std::min(srcN, rowN);
1710 dstRuns[0] = minN;
1711 dstRuns += minN;
1712 dstAA[0] = newAlpha;
1713 dstAA += minN;
1714
1715 if (0 == (srcN -= minN)) {
1716 srcN = srcRuns[0]; // refresh
1717 srcRuns += srcN;
1718 srcAA += srcN;
1719 srcN = srcRuns[0]; // reload
1720 if (0 == srcN) {
1721 break;
1722 }
1723 }
1724 if (0 == (rowN -= minN)) {
1725 row += 2;
1726 rowN = row[0]; // reload
1727 }
1728
1729 SkDEBUGCODE(accumulated += minN;)
1730 SkASSERT(accumulated <= width);
1731 }
1732 dstRuns[0] = 0;
1733}
1734
1735void SkAAClipBlitter::blitAntiH(int x, int y, const SkAlpha aa[],
1736 const int16_t runs[]) {
1737
1738 const uint8_t* row = fAAClip->findRow(y);
1739 int initialCount;
1740 row = fAAClip->findX(row, x, &initialCount);
1741
1742 this->ensureRunsAndAA();
1743
1744 merge(row, initialCount, aa, runs, fAA, fRuns, fAAClipBounds.width());
1745 fBlitter->blitAntiH(x, y, fAA, fRuns);
1746}
1747
1748void SkAAClipBlitter::blitV(int x, int y, int height, SkAlpha alpha) {
1749 if (fAAClip->quickContains(x, y, x + 1, y + height)) {
1750 fBlitter->blitV(x, y, height, alpha);
1751 return;
1752 }
1753
1754 for (;;) {
1755 int lastY SK_INIT_TO_AVOID_WARNING;
1756 const uint8_t* row = fAAClip->findRow(y, &lastY);
1757 int dy = lastY - y + 1;
1758 if (dy > height) {
1759 dy = height;
1760 }
1761 height -= dy;
1762
1763 row = fAAClip->findX(row, x);
1764 SkAlpha newAlpha = SkMulDiv255Round(alpha, row[1]);
1765 if (newAlpha) {
1766 fBlitter->blitV(x, y, dy, newAlpha);
1767 }
1768 SkASSERT(height >= 0);
1769 if (height <= 0) {
1770 break;
1771 }
1772 y = lastY + 1;
1773 }
1774}
1775
1776void SkAAClipBlitter::blitRect(int x, int y, int width, int height) {
1777 if (fAAClip->quickContains(x, y, x + width, y + height)) {
1778 fBlitter->blitRect(x, y, width, height);
1779 return;
1780 }
1781
1782 while (--height >= 0) {
1783 this->blitH(x, y, width);
1784 y += 1;
1785 }
1786}
1787
1788typedef void (*MergeAAProc)(const void* src, int width, const uint8_t* row,
1789 int initialRowCount, void* dst);
1790
1791static void small_memcpy(void* dst, const void* src, size_t n) {
1792 memcpy(dst, src, n);
1793}
1794
1795static void small_bzero(void* dst, size_t n) {
1796 sk_bzero(dst, n);
1797}
1798
1799static inline uint8_t mergeOne(uint8_t value, unsigned alpha) {
1800 return SkMulDiv255Round(value, alpha);
1801}
1802
1803static inline uint16_t mergeOne(uint16_t value, unsigned alpha) {
1804 unsigned r = SkGetPackedR16(value);
1805 unsigned g = SkGetPackedG16(value);
1806 unsigned b = SkGetPackedB16(value);
1807 return SkPackRGB16(SkMulDiv255Round(r, alpha),
1808 SkMulDiv255Round(g, alpha),
1809 SkMulDiv255Round(b, alpha));
1810}
1811
1812template <typename T>
1813void mergeT(const void* inSrc, int srcN, const uint8_t* SK_RESTRICT row, int rowN, void* inDst) {
1814 const T* SK_RESTRICT src = static_cast<const T*>(inSrc);
1815 T* SK_RESTRICT dst = static_cast<T*>(inDst);
1816 for (;;) {
1817 SkASSERT(rowN > 0);
1818 SkASSERT(srcN > 0);
1819
1820 int n = std::min(rowN, srcN);
1821 unsigned rowA = row[1];
1822 if (0xFF == rowA) {
1823 small_memcpy(dst, src, n * sizeof(T));
1824 } else if (0 == rowA) {
1825 small_bzero(dst, n * sizeof(T));
1826 } else {
1827 for (int i = 0; i < n; ++i) {
1828 dst[i] = mergeOne(src[i], rowA);
1829 }
1830 }
1831
1832 if (0 == (srcN -= n)) {
1833 break;
1834 }
1835
1836 src += n;
1837 dst += n;
1838
1839 SkASSERT(rowN == n);
1840 row += 2;
1841 rowN = row[0];
1842 }
1843}
1844
1846 switch (format) {
1847 case SkMask::kBW_Format:
1848 SkDEBUGFAIL("unsupported");
1849 return nullptr;
1850 case SkMask::kA8_Format:
1851 case SkMask::k3D_Format:
1852 return mergeT<uint8_t> ;
1854 return mergeT<uint16_t>;
1855 default:
1856 SkDEBUGFAIL("unsupported");
1857 return nullptr;
1858 }
1859}
1860
1861static U8CPU bit2byte(int bitInAByte) {
1862 SkASSERT(bitInAByte <= 0xFF);
1863 // negation turns any non-zero into 0xFFFFFF??, so we just shift down
1864 // some value >= 8 to get a full FF value
1865 return -bitInAByte >> 8;
1866}
1867
1868static void upscaleBW2A8(SkMask* dstMask, const SkMask& srcMask) {
1870 SkASSERT(SkMask::kA8_Format == dstMask->fFormat);
1871
1872 const int width = srcMask.fBounds.width();
1873 const int height = srcMask.fBounds.height();
1874
1875 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcMask.fImage;
1876 const size_t srcRB = srcMask.fRowBytes;
1877 uint8_t* SK_RESTRICT dst = const_cast<uint8_t*>(dstMask->fImage);
1878 const size_t dstRB = dstMask->fRowBytes;
1879
1880 const int wholeBytes = width >> 3;
1881 const int leftOverBits = width & 7;
1882
1883 for (int y = 0; y < height; ++y) {
1884 uint8_t* SK_RESTRICT d = dst;
1885 for (int i = 0; i < wholeBytes; ++i) {
1886 int srcByte = src[i];
1887 d[0] = bit2byte(srcByte & (1 << 7));
1888 d[1] = bit2byte(srcByte & (1 << 6));
1889 d[2] = bit2byte(srcByte & (1 << 5));
1890 d[3] = bit2byte(srcByte & (1 << 4));
1891 d[4] = bit2byte(srcByte & (1 << 3));
1892 d[5] = bit2byte(srcByte & (1 << 2));
1893 d[6] = bit2byte(srcByte & (1 << 1));
1894 d[7] = bit2byte(srcByte & (1 << 0));
1895 d += 8;
1896 }
1897 if (leftOverBits) {
1898 int srcByte = src[wholeBytes];
1899 for (int x = 0; x < leftOverBits; ++x) {
1900 *d++ = bit2byte(srcByte & 0x80);
1901 srcByte <<= 1;
1902 }
1903 }
1904 src += srcRB;
1905 dst += dstRB;
1906 }
1907}
1908
1909void SkAAClipBlitter::blitMask(const SkMask& origMask, const SkIRect& clip) {
1910 SkASSERT(fAAClip->getBounds().contains(clip));
1911
1912 if (fAAClip->quickContains(clip)) {
1913 fBlitter->blitMask(origMask, clip);
1914 return;
1915 }
1916
1917 const SkMask* mask = &origMask;
1918
1919 // if we're BW, we need to upscale to A8 (ugh)
1920 SkMaskBuilder grayMask;
1921 if (SkMask::kBW_Format == origMask.fFormat) {
1922 grayMask.format() = SkMask::kA8_Format;
1923 grayMask.bounds() = origMask.fBounds;
1924 grayMask.rowBytes() = origMask.fBounds.width();
1925 size_t size = grayMask.computeImageSize();
1926 grayMask.image() = reinterpret_cast<uint8_t*>(
1927 fGrayMaskScratch.reset(size, SkAutoMalloc::kReuse_OnShrink));
1928
1929 upscaleBW2A8(&grayMask, origMask);
1930 mask = &grayMask;
1931 }
1932
1933 this->ensureRunsAndAA();
1934
1935 // HACK -- we are devolving 3D into A8, need to copy the rest of the 3D
1936 // data into a temp block to support it better (ugh)
1937
1938 const void* src = mask->getAddr(clip.fLeft, clip.fTop);
1939 const size_t srcRB = mask->fRowBytes;
1940 const int width = clip.width();
1941 MergeAAProc mergeProc = find_merge_aa_proc(mask->fFormat);
1942
1943 SkMaskBuilder rowMask;
1944 rowMask.format() = SkMask::k3D_Format == mask->fFormat ? SkMask::kA8_Format : mask->fFormat;
1945 rowMask.bounds().fLeft = clip.fLeft;
1946 rowMask.bounds().fRight = clip.fRight;
1947 rowMask.rowBytes() = mask->fRowBytes; // doesn't matter, since our height==1
1948 rowMask.image() = (uint8_t*)fScanlineScratch;
1949
1950 int y = clip.fTop;
1951 const int stopY = y + clip.height();
1952
1953 do {
1954 int localStopY SK_INIT_TO_AVOID_WARNING;
1955 const uint8_t* row = fAAClip->findRow(y, &localStopY);
1956 // findRow returns last Y, not stop, so we add 1
1957 localStopY = std::min(localStopY + 1, stopY);
1958
1959 int initialCount;
1960 row = fAAClip->findX(row, clip.fLeft, &initialCount);
1961 do {
1962 mergeProc(src, width, row, initialCount, rowMask.image());
1963 rowMask.bounds().fTop = y;
1964 rowMask.bounds().fBottom = y + 1;
1965 fBlitter->blitMask(rowMask, rowMask.fBounds);
1966 src = (const void*)((const char*)src + srcRB);
1967 } while (++y < localStopY);
1968 } while (y < stopY);
1969}
static void done(const char *config, const char *src, const char *srcOptions, const char *name)
Definition DM.cpp:263
int count
const SkRect fBounds
static float next(float f)
static float prev(float f)
static void count_left_right_zeros(const uint8_t *row, int width, int *leftZ, int *riteZ)
Definition SkAAClip.cpp:972
static bool row_is_all_zeros(const uint8_t *row, int width)
static void small_memcpy(void *dst, const void *src, size_t n)
static U8CPU bit2byte(int bitInAByte)
static MergeAAProc find_merge_aa_proc(SkMask::Format format)
void mergeT(const void *inSrc, int srcN, const uint8_t *SK_RESTRICT row, int rowN, void *inDst)
static void merge(const uint8_t *SK_RESTRICT row, int rowN, const SkAlpha *SK_RESTRICT srcAA, const int16_t *SK_RESTRICT srcRuns, SkAlpha *SK_RESTRICT dstAA, int16_t *SK_RESTRICT dstRuns, int width)
static uint8_t mergeOne(uint8_t value, unsigned alpha)
#define AUTO_AACLIP_VALIDATE(clip)
Definition SkAAClip.cpp:47
static void upscaleBW2A8(SkMask *dstMask, const SkMask &srcMask)
void(* MergeAAProc)(const void *src, int width, const uint8_t *row, int initialRowCount, void *dst)
static void expandToRuns(const uint8_t *SK_RESTRICT data, int initialCount, int width, int16_t *SK_RESTRICT runs, SkAlpha *SK_RESTRICT aa)
static int trim_row_left_right(uint8_t *row, int width, int leftZ, int riteZ)
static void small_bzero(void *dst, size_t n)
#define SkUNREACHABLE
Definition SkAssert.h:135
#define SkDEBUGFAIL(message)
Definition SkAssert.h:118
#define SK_ABORT(message,...)
Definition SkAssert.h:70
#define SkASSERT(cond)
Definition SkAssert.h:116
uint8_t(*)(uint8_t src, uint8_t dst) AlphaProc
unsigned U8CPU
Definition SkCPUTypes.h:18
SkClipOp
Definition SkClipOp.h:13
#define SkGetPackedB16(color)
Definition SkColorData.h:32
#define SkGetPackedG16(color)
Definition SkColorData.h:31
static uint16_t SkPackRGB16(unsigned r, unsigned g, unsigned b)
#define SkGetPackedR16(color)
Definition SkColorData.h:30
uint8_t SkAlpha
Definition SkColor.h:26
uint32_t SkPMColor
Definition SkColor.h:205
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
static SkCanvas * trim(SkCanvas *canvas, SkScalar width, SkScalar height, const SkRect *content)
#define SK_RESTRICT
Definition SkFeatures.h:42
static bool skip(SkStream *stream, size_t amount)
#define SK_INIT_TO_AVOID_WARNING
Definition SkMacros.h:58
static void sk_bzero(void *buffer, size_t size)
Definition SkMalloc.h:105
SK_API void sk_free(void *)
static void * sk_malloc_throw(size_t size)
Definition SkMalloc.h:67
static U8CPU SkMulDiv255Round(U16CPU a, U16CPU b)
Definition SkMath.h:73
static constexpr int32_t SK_MaxS32
Definition SkMath.h:21
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition SkPath.cpp:3824
static bool left(const SkPoint &p0, const SkPoint &p1)
static bool right(const SkPoint &p0, const SkPoint &p1)
constexpr int SkToInt(S x)
Definition SkTo.h:29
constexpr uint32_t SkToU32(S x)
Definition SkTo.h:26
void blitAntiH(int x, int y, const SkAlpha[], const int16_t runs[]) override
void blitRect(int x, int y, int width, int height) override
Blit a solid rectangle one or more pixels wide.
void blitH(int x, int y, int width) override
Blit a horizontal run of one or more pixels.
~SkAAClipBlitter() override
void blitMask(const SkMask &, const SkIRect &clip) override
void blitV(int x, int y, int height, SkAlpha alpha) override
Blit a vertical run of pixels with a constant alpha value.
void blitAntiRect(int x, int y, int width, int height, SkAlpha leftAlpha, SkAlpha rightAlpha) override
Definition SkAAClip.cpp:748
void blitAntiH(int x, int y, const SkAlpha alpha[], const int16_t runs[]) override
Definition SkAAClip.cpp:765
Blitter(Builder *builder)
Definition SkAAClip.cpp:706
void blitH(int x, int y, int width) override
Blit a horizontal run of one or more pixels.
Definition SkAAClip.cpp:759
void blitV(int x, int y, int height, SkAlpha alpha) override
Definition SkAAClip.cpp:727
void blitRect(int x, int y, int width, int height) override
Blit a solid rectangle one or more pixels wide.
Definition SkAAClip.cpp:741
void blitMask(const SkMask &, const SkIRect &clip) override
Definition SkAAClip.cpp:756
Builder(const SkIRect &bounds)
Definition SkAAClip.cpp:262
bool blitPath(SkAAClip *target, const SkPath &path, bool doAA)
Definition SkAAClip.cpp:835
bool applyClipOp(SkAAClip *target, const SkAAClip &other, SkClipOp op)
Definition SkAAClip.cpp:830
void validate() const
Definition SkAAClip.h:64
void debug(bool compress_y=false) const
Definition SkAAClip.h:65
bool setEmpty()
SkAAClip & operator=(const SkAAClip &)
bool setRect(const SkIRect &)
bool translate(int dx, int dy, SkAAClip *dst) const
bool setPath(const SkPath &, const SkIRect &bounds, bool doAA=true)
bool op(const SkIRect &, SkClipOp)
void copyToMask(SkMaskBuilder *) const
Definition SkAAClip.cpp:851
bool quickContains(const SkIRect &r) const
Definition SkAAClip.h:56
bool isEmpty() const
Definition SkAAClip.h:32
bool isRect() const
bool setRegion(const SkRegion &)
bool isEmpty() const
Definition SkPath.cpp:406
static SkPath Rect(const SkRect &, SkPathDirection=SkPathDirection::kCW, unsigned startIndex=0)
Definition SkPath.cpp:3518
const SkRect & getBounds() const
Definition SkPath.cpp:420
const SkIRect & rect() const
Definition SkRegion.h:501
bool done() const
Definition SkRegion.h:488
bool isRect() const
Definition SkRegion.h:152
const SkIRect & getBounds() const
Definition SkRegion.h:165
bool isEmpty() const
Definition SkRegion.h:146
static void AntiFillPath(const SkPath &, const SkRasterClip &, SkBlitter *)
static void FillPath(const SkPath &, const SkIRect &, SkBlitter *)
T * end()
Definition SkTDArray.h:152
int size() const
Definition SkTDArray.h:138
void reserve(int n)
Definition SkTDArray.h:187
T * begin()
Definition SkTDArray.h:150
T * append()
Definition SkTDArray.h:191
size_t size_bytes() const
Definition SkTDArray.h:146
void removeShuffle(int index)
Definition SkTDArray.h:214
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition main.cc:19
static bool b
struct MyStruct a[10]
glong glong end
uint8_t value
uint32_t uint32_t * format
uint32_t * target
#define R(r)
double y
double x
Optional< SkRect > bounds
Definition SkRecords.h:189
constexpr int32_t kMaxInt32
Definition globals.h:483
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition switches.h:259
dst
Definition cp.py:12
SkScalar offsetX
SkScalar w
SkScalar offsetY
#define T
int32_t height
int32_t width
const YOffset * yoffsets() const
Definition SkAAClip.cpp:183
YOffset * yoffsets()
Definition SkAAClip.cpp:180
std::atomic< int32_t > fRefCnt
Definition SkAAClip.cpp:176
uint8_t * data()
Definition SkAAClip.cpp:186
static RunHead * AllocRect(const SkIRect &bounds)
Definition SkAAClip.cpp:213
static Iter Iterate(const SkAAClip &clip)
Definition SkAAClip.cpp:232
static int ComputeRowSizeForWidth(int width)
Definition SkAAClip.cpp:202
static RunHead * Alloc(int rowCount, size_t dataSize)
Definition SkAAClip.cpp:193
const uint8_t * data() const
Definition SkAAClip.cpp:189
bool intersect(const SkIRect &r)
Definition SkRect.h:513
static bool Intersects(const SkIRect &a, const SkIRect &b)
Definition SkRect.h:535
int32_t fBottom
larger y-axis bounds
Definition SkRect.h:36
constexpr int32_t height() const
Definition SkRect.h:165
int32_t fTop
smaller y-axis bounds
Definition SkRect.h:34
constexpr int32_t width() const
Definition SkRect.h:158
void setEmpty()
Definition SkRect.h:242
bool isEmpty() const
Definition SkRect.h:202
int32_t fLeft
smaller x-axis bounds
Definition SkRect.h:33
bool contains(int32_t x, int32_t y) const
Definition SkRect.h:463
int32_t fRight
larger x-axis bounds
Definition SkRect.h:35
Format & format()
Definition SkMask.h:239
uint32_t & rowBytes()
Definition SkMask.h:238
static uint8_t * AllocImage(size_t bytes, AllocType=kUninit_Alloc)
Definition SkMask.cpp:45
SkIRect & bounds()
Definition SkMask.h:237
uint8_t *& image()
Definition SkMask.h:236
const uint32_t fRowBytes
Definition SkMask.h:43
Format
Definition SkMask.h:26
@ k3D_Format
3 8bit per pixl planes: alpha, mul, add
Definition SkMask.h:29
@ kA8_Format
8bits per pixel mask (e.g. antialiasing)
Definition SkMask.h:28
@ kLCD16_Format
565 alpha for r/g/b
Definition SkMask.h:31
@ kBW_Format
1bit per pixel mask (e.g. monochrome)
Definition SkMask.h:27
const void * getAddr(int x, int y) const
Definition SkMask.cpp:112
uint8_t const *const fImage
Definition SkMask.h:41
const SkIRect fBounds
Definition SkMask.h:42
size_t computeImageSize() const
Definition SkMask.cpp:30
const Format fFormat
Definition SkMask.h:44
static SkRect Make(const SkISize &size)
Definition SkRect.h:669
SkScalar fBottom
larger y-axis bounds
Definition extension.cpp:17
constexpr float left() const
Definition SkRect.h:734
constexpr float top() const
Definition SkRect.h:741
SkScalar fLeft
smaller x-axis bounds
Definition extension.cpp:14
constexpr float x() const
Definition SkRect.h:720
constexpr float y() const
Definition SkRect.h:727
SkScalar fRight
larger x-axis bounds
Definition extension.cpp:16
bool contains(SkScalar x, SkScalar y) const
Definition extension.cpp:19
void round(SkIRect *dst) const
Definition SkRect.h:1228
constexpr float height() const
Definition SkRect.h:769
constexpr float width() const
Definition SkRect.h:762
bool isEmpty() const
Definition SkRect.h:693
SkScalar fTop
smaller y-axis bounds
Definition extension.cpp:15
void setEmpty()
Definition SkRect.h:842