Flutter Engine
The Flutter Engine
SkScan_Antihair.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2011 The Android Open Source Project
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
10#include "include/core/SkRect.h"
20#include "src/core/SkBlitter.h"
21#include "src/core/SkFDot6.h"
24#include "src/core/SkScan.h"
25
26#include <algorithm>
27#include <cstdint>
28
29/* Our attempt to compute the worst case "bounds" for the horizontal and
30 vertical cases has some numerical bug in it, and we sometimes undervalue
31 our extends. The bug is that when this happens, we will set the clip to
32 nullptr (for speed), and thus draw outside of the clip by a pixel, which might
33 only look bad, but it might also access memory outside of the valid range
34 allcoated for the device bitmap.
35
36 This define enables our fix to outset our "bounds" by 1, thus avoiding the
37 chance of the bug, but at the cost of sometimes taking the rectblitter
38 case (i.e. not setting the clip to nullptr) when we might not actually need
39 to. If we can improve/fix the actual calculations, then we can remove this
40 step.
41 */
42#define OUTSET_BEFORE_CLIP_TEST true
43
44#define HLINE_STACK_BUFFER 100
45
46static inline int SmallDot6Scale(int value, int dot6) {
47 SkASSERT((int16_t)value == value);
48 SkASSERT((unsigned)dot6 <= 64);
49 return (value * dot6) >> 6;
50}
51
52//#define TEST_GAMMA
53
54#ifdef TEST_GAMMA
55 static uint8_t gGammaTable[256];
56 #define ApplyGamma(table, alpha) (table)[alpha]
57
58 static void build_gamma_table() {
59 static bool gInit = false;
60
61 if (gInit == false) {
62 for (int i = 0; i < 256; i++) {
63 SkFixed n = i * 257;
64 n += n >> 15;
65 SkASSERT(n >= 0 && n <= SK_Fixed1);
66 n = SkFixedSqrt(n);
67 n = n * 255 >> 16;
68 // SkDebugf("morph %d -> %d\n", i, n);
69 gGammaTable[i] = SkToU8(n);
70 }
71 gInit = true;
72 }
73 }
74#else
75 #define ApplyGamma(table, alpha) SkToU8(alpha)
76#endif
77
78///////////////////////////////////////////////////////////////////////////////
79
80static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count,
81 U8CPU alpha) {
82 SkASSERT(count > 0);
83
84 int16_t runs[HLINE_STACK_BUFFER + 1];
85 uint8_t aa[HLINE_STACK_BUFFER];
86
87 do {
88 // In theory, we should be able to just do this once (outside of the loop),
89 // since aa[] and runs[] are supposed" to be const when we call the blitter.
90 // In reality, some wrapper-blitters (e.g. SkRgnClipBlitter) cast away that
91 // constness, and modify the buffers in-place. Hence the need to be defensive
92 // here and reseed the aa value.
93 aa[0] = ApplyGamma(gGammaTable, alpha);
94
95 int n = count;
96 if (n > HLINE_STACK_BUFFER) {
98 }
99 runs[0] = SkToS16(n);
100 runs[n] = 0;
101 blitter->blitAntiH(x, y, aa, runs);
102 x += n;
103 count -= n;
104 } while (count > 0);
105}
106
108public:
109 SkAntiHairBlitter() : fBlitter(nullptr) {}
111
112 SkBlitter* getBlitter() const { return fBlitter; }
113
114 void setup(SkBlitter* blitter) {
115 fBlitter = blitter;
116 }
117
118 virtual SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) = 0;
119 virtual SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed slope) = 0;
120
121private:
122 SkBlitter* fBlitter;
123};
124
126public:
127 SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) override {
128 fy += SK_Fixed1/2;
129
130 int y = fy >> 16;
131 uint8_t a = (uint8_t)((fy >> 8) & 0xFF);
132
133 // lower line
134 unsigned ma = SmallDot6Scale(a, mod64);
135 if (ma) {
136 call_hline_blitter(this->getBlitter(), x, y, 1, ma);
137 }
138
139 // upper line
140 ma = SmallDot6Scale(255 - a, mod64);
141 if (ma) {
142 call_hline_blitter(this->getBlitter(), x, y - 1, 1, ma);
143 }
144
145 return fy - SK_Fixed1/2;
146 }
147
148 SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed slope) override {
149 SkASSERT(x < stopx);
150 int count = stopx - x;
151 fy += SK_Fixed1/2;
152
153 int y = fy >> 16;
154 uint8_t a = (uint8_t)((fy >> 8) & 0xFF);
155
156 // lower line
157 if (a) {
158 call_hline_blitter(this->getBlitter(), x, y, count, a);
159 }
160
161 // upper line
162 a = 255 - a;
163 if (a) {
164 call_hline_blitter(this->getBlitter(), x, y - 1, count, a);
165 }
166
167 return fy - SK_Fixed1/2;
168 }
169};
170
172public:
173 SkFixed drawCap(int x, SkFixed fy, SkFixed dy, int mod64) override {
174 fy += SK_Fixed1/2;
175
176 int lower_y = fy >> 16;
177 uint8_t a = (uint8_t)((fy >> 8) & 0xFF);
178 unsigned a0 = SmallDot6Scale(255 - a, mod64);
179 unsigned a1 = SmallDot6Scale(a, mod64);
180 this->getBlitter()->blitAntiV2(x, lower_y - 1, a0, a1);
181
182 return fy + dy - SK_Fixed1/2;
183 }
184
185 SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed dy) override {
186 SkASSERT(x < stopx);
187
188 fy += SK_Fixed1/2;
189 SkBlitter* blitter = this->getBlitter();
190 do {
191 int lower_y = fy >> 16;
192 uint8_t a = (uint8_t)((fy >> 8) & 0xFF);
193 blitter->blitAntiV2(x, lower_y - 1, 255 - a, a);
194 fy += dy;
195 } while (++x < stopx);
196
197 return fy - SK_Fixed1/2;
198 }
199};
200
202public:
203 SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) override {
204 SkASSERT(0 == dx);
205 fx += SK_Fixed1/2;
206
207 int x = fx >> 16;
208 int a = (uint8_t)((fx >> 8) & 0xFF);
209
210 unsigned ma = SmallDot6Scale(a, mod64);
211 if (ma) {
212 this->getBlitter()->blitV(x, y, 1, ma);
213 }
214 ma = SmallDot6Scale(255 - a, mod64);
215 if (ma) {
216 this->getBlitter()->blitV(x - 1, y, 1, ma);
217 }
218
219 return fx - SK_Fixed1/2;
220 }
221
222 SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) override {
223 SkASSERT(y < stopy);
224 SkASSERT(0 == dx);
225 fx += SK_Fixed1/2;
226
227 int x = fx >> 16;
228 int a = (uint8_t)((fx >> 8) & 0xFF);
229
230 if (a) {
231 this->getBlitter()->blitV(x, y, stopy - y, a);
232 }
233 a = 255 - a;
234 if (a) {
235 this->getBlitter()->blitV(x - 1, y, stopy - y, a);
236 }
237
238 return fx - SK_Fixed1/2;
239 }
240};
241
243public:
244 SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) override {
245 fx += SK_Fixed1/2;
246
247 int x = fx >> 16;
248 uint8_t a = (uint8_t)((fx >> 8) & 0xFF);
249 this->getBlitter()->blitAntiH2(x - 1, y,
250 SmallDot6Scale(255 - a, mod64), SmallDot6Scale(a, mod64));
251
252 return fx + dx - SK_Fixed1/2;
253 }
254
255 SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) override {
256 SkASSERT(y < stopy);
257 fx += SK_Fixed1/2;
258 do {
259 int x = fx >> 16;
260 uint8_t a = (uint8_t)((fx >> 8) & 0xFF);
261 this->getBlitter()->blitAntiH2(x - 1, y, 255 - a, a);
262 fx += dx;
263 } while (++y < stopy);
264
265 return fx - SK_Fixed1/2;
266 }
267};
268
270 SkASSERT((SkLeftShift(a, 16) >> 16) == a);
271 SkASSERT(b != 0);
272 return SkLeftShift(a, 16) / b;
273}
274
275#define SkBITCOUNT(x) (sizeof(x) << 3)
276
277#if 1
278// returns high-bit set iff x==0x8000...
279static inline int bad_int(int x) {
280 return x & -x;
281}
282
283static int any_bad_ints(int a, int b, int c, int d) {
284 return (bad_int(a) | bad_int(b) | bad_int(c) | bad_int(d)) >> (SkBITCOUNT(int) - 1);
285}
286#else
287static inline int good_int(int x) {
288 return x ^ (1 << (SkBITCOUNT(x) - 1));
289}
290
291static int any_bad_ints(int a, int b, int c, int d) {
292 return !(good_int(a) & good_int(b) & good_int(c) & good_int(d));
293}
294#endif
295
296#ifdef SK_DEBUG
297static bool canConvertFDot6ToFixed(SkFDot6 x) {
298 const int maxDot6 = SK_MaxS32 >> (16 - 6);
299 return SkAbs32(x) <= maxDot6;
300}
301#endif
302
303/*
304 * We want the fractional part of ordinate, but we want multiples of 64 to
305 * return 64, not 0, so we can't just say (ordinate & 63).
306 * We basically want to compute those bits, and if they're 0, return 64.
307 * We can do that w/o a branch with an extra sub and add.
308 */
309static int contribution_64(SkFDot6 ordinate) {
310#if 0
311 int result = ordinate & 63;
312 if (0 == result) {
313 result = 64;
314 }
315#else
316 int result = ((ordinate - 1) & 63) + 1;
317#endif
318 SkASSERT(result > 0 && result <= 64);
319 return result;
320}
321
322static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1,
323 const SkIRect* clip, SkBlitter* blitter) {
324 // check for integer NaN (0x80000000) which we can't handle (can't negate it)
325 // It appears typically from a huge float (inf or nan) being converted to int.
326 // If we see it, just don't draw.
327 if (any_bad_ints(x0, y0, x1, y1)) {
328 return;
329 }
330
331 // The caller must clip the line to [-32767.0 ... 32767.0] ahead of time
332 // (in dot6 format)
333 SkASSERT(canConvertFDot6ToFixed(x0));
334 SkASSERT(canConvertFDot6ToFixed(y0));
335 SkASSERT(canConvertFDot6ToFixed(x1));
336 SkASSERT(canConvertFDot6ToFixed(y1));
337
338 if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) {
339 /* instead of (x0 + x1) >> 1, we shift each separately. This is less
340 precise, but avoids overflowing the intermediate result if the
341 values are huge. A better fix might be to clip the original pts
342 directly (i.e. do the divide), so we don't spend time subdividing
343 huge lines at all.
344 */
345 int hx = (x0 >> 1) + (x1 >> 1);
346 int hy = (y0 >> 1) + (y1 >> 1);
347 do_anti_hairline(x0, y0, hx, hy, clip, blitter);
348 do_anti_hairline(hx, hy, x1, y1, clip, blitter);
349 return;
350 }
351
352 int scaleStart, scaleStop;
353 int istart, istop;
354 SkFixed fstart, slope;
355
356 HLine_SkAntiHairBlitter hline_blitter;
357 Horish_SkAntiHairBlitter horish_blitter;
358 VLine_SkAntiHairBlitter vline_blitter;
359 Vertish_SkAntiHairBlitter vertish_blitter;
360 SkAntiHairBlitter* hairBlitter = nullptr;
361
362 if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) { // mostly horizontal
363 if (x0 > x1) { // we want to go left-to-right
364 using std::swap;
365 swap(x0, x1);
366 swap(y0, y1);
367 }
368
369 istart = SkFDot6Floor(x0);
370 istop = SkFDot6Ceil(x1);
371 fstart = SkFDot6ToFixed(y0);
372 if (y0 == y1) { // completely horizontal, take fast case
373 slope = 0;
374 hairBlitter = &hline_blitter;
375 } else {
376 slope = fastfixdiv(y1 - y0, x1 - x0);
377 SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1);
378 fstart += (slope * (32 - (x0 & 63)) + 32) >> 6;
379 hairBlitter = &horish_blitter;
380 }
381
382 SkASSERT(istop > istart);
383 if (istop - istart == 1) {
384 // we are within a single pixel
385 scaleStart = x1 - x0;
386 SkASSERT(scaleStart >= 0 && scaleStart <= 64);
387 scaleStop = 0;
388 } else {
389 scaleStart = 64 - (x0 & 63);
390 scaleStop = x1 & 63;
391 }
392
393 if (clip){
394 if (istart >= clip->fRight || istop <= clip->fLeft) {
395 return;
396 }
397 if (istart < clip->fLeft) {
398 fstart += slope * (clip->fLeft - istart);
399 istart = clip->fLeft;
400 scaleStart = 64;
401 if (istop - istart == 1) {
402 // we are within a single pixel
403 scaleStart = contribution_64(x1);
404 scaleStop = 0;
405 }
406 }
407 if (istop > clip->fRight) {
408 istop = clip->fRight;
409 scaleStop = 0; // so we don't draw this last column
410 }
411
412 SkASSERT(istart <= istop);
413 if (istart == istop) {
414 return;
415 }
416 // now test if our Y values are completely inside the clip
417 int top, bottom;
418 if (slope >= 0) { // T2B
419 top = SkFixedFloorToInt(fstart - SK_FixedHalf);
420 bottom = SkFixedCeilToInt(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
421 } else { // B2T
422 bottom = SkFixedCeilToInt(fstart + SK_FixedHalf);
423 top = SkFixedFloorToInt(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
424 }
425#ifdef OUTSET_BEFORE_CLIP_TEST
426 top -= 1;
427 bottom += 1;
428#endif
429 if (top >= clip->fBottom || bottom <= clip->fTop) {
430 return;
431 }
432 if (clip->fTop <= top && clip->fBottom >= bottom) {
433 clip = nullptr;
434 }
435 }
436 } else { // mostly vertical
437 if (y0 > y1) { // we want to go top-to-bottom
438 using std::swap;
439 swap(x0, x1);
440 swap(y0, y1);
441 }
442
443 istart = SkFDot6Floor(y0);
444 istop = SkFDot6Ceil(y1);
445 fstart = SkFDot6ToFixed(x0);
446 if (x0 == x1) {
447 if (y0 == y1) { // are we zero length?
448 return; // nothing to do
449 }
450 slope = 0;
451 hairBlitter = &vline_blitter;
452 } else {
453 slope = fastfixdiv(x1 - x0, y1 - y0);
454 SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1);
455 fstart += (slope * (32 - (y0 & 63)) + 32) >> 6;
456 hairBlitter = &vertish_blitter;
457 }
458
459 SkASSERT(istop > istart);
460 if (istop - istart == 1) {
461 // we are within a single pixel
462 scaleStart = y1 - y0;
463 SkASSERT(scaleStart >= 0 && scaleStart <= 64);
464 scaleStop = 0;
465 } else {
466 scaleStart = 64 - (y0 & 63);
467 scaleStop = y1 & 63;
468 }
469
470 if (clip) {
471 if (istart >= clip->fBottom || istop <= clip->fTop) {
472 return;
473 }
474 if (istart < clip->fTop) {
475 fstart += slope * (clip->fTop - istart);
476 istart = clip->fTop;
477 scaleStart = 64;
478 if (istop - istart == 1) {
479 // we are within a single pixel
480 scaleStart = contribution_64(y1);
481 scaleStop = 0;
482 }
483 }
484 if (istop > clip->fBottom) {
485 istop = clip->fBottom;
486 scaleStop = 0; // so we don't draw this last row
487 }
488
489 SkASSERT(istart <= istop);
490 if (istart == istop)
491 return;
492
493 // now test if our X values are completely inside the clip
494 int left, right;
495 if (slope >= 0) { // L2R
497 right = SkFixedCeilToInt(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
498 } else { // R2L
500 left = SkFixedFloorToInt(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
501 }
502#ifdef OUTSET_BEFORE_CLIP_TEST
503 left -= 1;
504 right += 1;
505#endif
506 if (left >= clip->fRight || right <= clip->fLeft) {
507 return;
508 }
509 if (clip->fLeft <= left && clip->fRight >= right) {
510 clip = nullptr;
511 }
512 }
513 }
514
515 SkRectClipBlitter rectClipper;
516 if (clip) {
517 rectClipper.init(blitter, *clip);
518 blitter = &rectClipper;
519 }
520
521 SkASSERT(hairBlitter);
522 hairBlitter->setup(blitter);
523
524#ifdef SK_DEBUG
525 if (scaleStart > 0 && scaleStop > 0) {
526 // be sure we don't draw twice in the same pixel
527 SkASSERT(istart < istop - 1);
528 }
529#endif
530
531 fstart = hairBlitter->drawCap(istart, fstart, slope, scaleStart);
532 istart += 1;
533 int fullSpans = istop - istart - (scaleStop > 0);
534 if (fullSpans > 0) {
535 fstart = hairBlitter->drawLine(istart, istart + fullSpans, fstart, slope);
536 }
537 if (scaleStop > 0) {
538 hairBlitter->drawCap(istop - 1, fstart, slope, scaleStop);
539 }
540}
541
542void SkScan::AntiHairLineRgn(const SkPoint array[], int arrayCount, const SkRegion* clip,
543 SkBlitter* blitter) {
544 if (clip && clip->isEmpty()) {
545 return;
546 }
547
548 SkASSERT(clip == nullptr || !clip->getBounds().isEmpty());
549
550#ifdef TEST_GAMMA
551 build_gamma_table();
552#endif
553
554 const SkScalar max = SkIntToScalar(32767);
555 const SkRect fixedBounds = SkRect::MakeLTRB(-max, -max, max, max);
556
557 SkRect clipBounds;
558 if (clip) {
559 clipBounds.set(clip->getBounds());
560 /* We perform integral clipping later on, but we do a scalar clip first
561 to ensure that our coordinates are expressible in fixed/integers.
562
563 antialiased hairlines can draw up to 1/2 of a pixel outside of
564 their bounds, so we need to outset the clip before calling the
565 clipper. To make the numerics safer, we outset by a whole pixel,
566 since the 1/2 pixel boundary is important to the antihair blitter,
567 we don't want to risk numerical fate by chopping on that edge.
568 */
569 clipBounds.outset(SK_Scalar1, SK_Scalar1);
570 }
571
572 for (int i = 0; i < arrayCount - 1; ++i) {
573 SkPoint pts[2];
574
575 // We have to pre-clip the line to fit in a SkFixed, so we just chop
576 // the line. TODO find a way to actually draw beyond that range.
577 if (!SkLineClipper::IntersectLine(&array[i], fixedBounds, pts)) {
578 continue;
579 }
580
581 if (clip && !SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
582 continue;
583 }
584
585 SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
586 SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
587 SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
588 SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
589
590 if (clip) {
591 SkFDot6 left = std::min(x0, x1);
592 SkFDot6 top = std::min(y0, y1);
593 SkFDot6 right = std::max(x0, x1);
594 SkFDot6 bottom = std::max(y0, y1);
595 SkIRect ir;
596
597 ir.setLTRB(SkFDot6Floor(left) - 1,
598 SkFDot6Floor(top) - 1,
599 SkFDot6Ceil(right) + 1,
600 SkFDot6Ceil(bottom) + 1);
601
602 if (clip->quickReject(ir)) {
603 continue;
604 }
605 if (!clip->quickContains(ir)) {
606 SkRegion::Cliperator iter(*clip, ir);
607 const SkIRect* r = &iter.rect();
608
609 while (!iter.done()) {
610 do_anti_hairline(x0, y0, x1, y1, r, blitter);
611 iter.next();
612 }
613 continue;
614 }
615 // fall through to no-clip case
616 }
617 do_anti_hairline(x0, y0, x1, y1, nullptr, blitter);
618 }
619}
620
622 SkBlitter* blitter) {
623 SkPoint pts[5];
624
625 pts[0].set(rect.fLeft, rect.fTop);
626 pts[1].set(rect.fRight, rect.fTop);
627 pts[2].set(rect.fRight, rect.fBottom);
628 pts[3].set(rect.fLeft, rect.fBottom);
629 pts[4] = pts[0];
630 SkScan::AntiHairLine(pts, 5, clip, blitter);
631}
632
633///////////////////////////////////////////////////////////////////////////////
634
635typedef int FDot8; // 24.8 integer fixed point
636
638 return (x + 0x80) >> 8;
639}
640
641static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
642 SkBlitter* blitter) {
643 SkASSERT(L < R);
644
645 if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
646 blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L));
647 return;
648 }
649
650 int left = L >> 8;
651
652 if (L & 0xFF) {
653 blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF)));
654 left += 1;
655 }
656
657 int rite = R >> 8;
658 int width = rite - left;
659 if (width > 0) {
660 call_hline_blitter(blitter, left, top, width, alpha);
661 }
662 if (R & 0xFF) {
663 blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF));
664 }
665}
666
667static void antifilldot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter* blitter,
668 bool fillInner) {
669 // check for empty now that we're in our reduced precision space
670 if (L >= R || T >= B) {
671 return;
672 }
673 int top = T >> 8;
674 if (top == ((B - 1) >> 8)) { // just one scanline high
675 do_scanline(L, top, R, B - T - 1, blitter);
676 return;
677 }
678
679 if (T & 0xFF) {
680 do_scanline(L, top, R, 256 - (T & 0xFF), blitter);
681 top += 1;
682 }
683
684 int bot = B >> 8;
685 int height = bot - top;
686 if (height > 0) {
687 int left = L >> 8;
688 if (left == ((R - 1) >> 8)) { // just 1-pixel wide
689 blitter->blitV(left, top, height, R - L - 1);
690 } else {
691 if (L & 0xFF) {
692 blitter->blitV(left, top, height, 256 - (L & 0xFF));
693 left += 1;
694 }
695 int rite = R >> 8;
696 int width = rite - left;
697 if (width > 0 && fillInner) {
698 blitter->blitRect(left, top, width, height);
699 }
700 if (R & 0xFF) {
701 blitter->blitV(rite, top, height, R & 0xFF);
702 }
703 }
704 }
705
706 if (B & 0xFF) {
707 do_scanline(L, bot, R, B & 0xFF, blitter);
708 }
709}
710
711static void antifillrect(const SkXRect& xr, SkBlitter* blitter) {
714 blitter, true);
715}
716
717///////////////////////////////////////////////////////////////////////////////
718
719void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip,
720 SkBlitter* blitter) {
721 if (nullptr == clip) {
722 antifillrect(xr, blitter);
723 } else {
724 SkIRect outerBounds;
725 XRect_roundOut(xr, &outerBounds);
726
727 if (clip->isRect()) {
728 const SkIRect& clipBounds = clip->getBounds();
729
730 if (clipBounds.contains(outerBounds)) {
731 antifillrect(xr, blitter);
732 } else {
733 SkXRect tmpR;
734 // this keeps our original edges fractional
735 XRect_set(&tmpR, clipBounds);
736 if (tmpR.intersect(xr)) {
737 antifillrect(tmpR, blitter);
738 }
739 }
740 } else {
741 SkRegion::Cliperator clipper(*clip, outerBounds);
742 const SkIRect& rr = clipper.rect();
743
744 while (!clipper.done()) {
745 SkXRect tmpR;
746
747 // this keeps our original edges fractional
748 XRect_set(&tmpR, rr);
749 if (tmpR.intersect(xr)) {
750 antifillrect(tmpR, blitter);
751 }
752 clipper.next();
753 }
754 }
755 }
756}
757
759 SkBlitter* blitter) {
760 if (clip.isBW()) {
761 AntiFillXRect(xr, &clip.bwRgn(), blitter);
762 } else {
763 SkIRect outerBounds;
764 XRect_roundOut(xr, &outerBounds);
765
766 if (clip.quickContains(outerBounds)) {
767 AntiFillXRect(xr, nullptr, blitter);
768 } else {
769 SkAAClipBlitterWrapper wrapper(clip, blitter);
770 AntiFillXRect(xr, &wrapper.getRgn(), wrapper.getBlitter());
771 }
772 }
773}
774
775/* This takes a float-rect, but with the key improvement that it has
776 already been clipped, so we know that it is safe to convert it into a
777 XRect (fixedpoint), as it won't overflow.
778*/
779static void antifillrect(const SkRect& r, SkBlitter* blitter) {
780 SkXRect xr;
781
782 XRect_set(&xr, r);
783 antifillrect(xr, blitter);
784}
785
786/* We repeat the clipping logic of AntiFillXRect because the float rect might
787 overflow if we blindly converted it to an XRect. This sucks that we have to
788 repeat the clipping logic, but I don't see how to share the code/logic.
789
790 We clip r (as needed) into one or more (smaller) float rects, and then pass
791 those to our version of antifillrect, which converts it into an XRect and
792 then calls the blit.
793*/
794void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip,
795 SkBlitter* blitter) {
796 if (clip) {
797 SkRect newR;
798 newR.set(clip->getBounds());
799 if (!newR.intersect(origR)) {
800 return;
801 }
802
803 const SkIRect outerBounds = newR.roundOut();
804
805 if (clip->isRect()) {
806 antifillrect(newR, blitter);
807 } else {
808 SkRegion::Cliperator clipper(*clip, outerBounds);
809 while (!clipper.done()) {
810 newR.set(clipper.rect());
811 if (newR.intersect(origR)) {
812 antifillrect(newR, blitter);
813 }
814 clipper.next();
815 }
816 }
817 } else {
818 antifillrect(origR, blitter);
819 }
820}
821
823 SkBlitter* blitter) {
824 if (clip.isBW()) {
825 AntiFillRect(r, &clip.bwRgn(), blitter);
826 } else {
827 SkAAClipBlitterWrapper wrap(clip, blitter);
828 AntiFillRect(r, &wrap.getRgn(), wrap.getBlitter());
829 }
830}
831
832///////////////////////////////////////////////////////////////////////////////
833
834#define SkAlphaMulRound(a, b) SkMulDiv255Round(a, b)
835
836// calls blitRect() if the rectangle is non-empty
837static void fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) {
838 if (L < R && T < B) {
839 blitter->blitRect(L, T, R - L, B - T);
840 }
841}
842
844 return (int)(x * 256);
845}
846
847static inline int FDot8Floor(FDot8 x) {
848 return x >> 8;
849}
850
851static inline int FDot8Ceil(FDot8 x) {
852 return (x + 0xFF) >> 8;
853}
854
855// 1 - (1 - a)*(1 - b)
856static inline U8CPU InvAlphaMul(U8CPU a, U8CPU b) {
857 // need precise rounding (not just SkAlphaMul) so that values like
858 // a=228, b=252 don't overflow the result
859 return SkToU8(a + b - SkAlphaMulRound(a, b));
860}
861
862static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
863 SkBlitter* blitter) {
864 SkASSERT(L < R);
865
866 if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
867 FDot8 widClamp = R - L;
868 // border case clamp 256 to 255 instead of going through call_hline_blitter
869 // see skbug/4406
870 widClamp = widClamp - (widClamp >> 8);
871 blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, widClamp));
872 return;
873 }
874
875 int left = L >> 8;
876 if (L & 0xFF) {
877 blitter->blitV(left, top, 1, InvAlphaMul(alpha, L & 0xFF));
878 left += 1;
879 }
880
881 int rite = R >> 8;
882 int width = rite - left;
883 if (width > 0) {
884 call_hline_blitter(blitter, left, top, width, alpha);
885 }
886
887 if (R & 0xFF) {
888 blitter->blitV(rite, top, 1, InvAlphaMul(alpha, ~R & 0xFF));
889 }
890}
891
893 SkBlitter* blitter) {
894 SkASSERT(L < R && T < B);
895
896 int top = T >> 8;
897 if (top == ((B - 1) >> 8)) { // just one scanline high
898 // We want the inverse of B-T, since we're the inner-stroke
899 int alpha = 256 - (B - T);
900 if (alpha) {
901 inner_scanline(L, top, R, alpha, blitter);
902 }
903 return;
904 }
905
906 if (T & 0xFF) {
907 inner_scanline(L, top, R, T & 0xFF, blitter);
908 top += 1;
909 }
910
911 int bot = B >> 8;
912 int height = bot - top;
913 if (height > 0) {
914 if (L & 0xFF) {
915 blitter->blitV(L >> 8, top, height, L & 0xFF);
916 }
917 if (R & 0xFF) {
918 blitter->blitV(R >> 8, top, height, ~R & 0xFF);
919 }
920 }
921
922 if (B & 0xFF) {
923 inner_scanline(L, bot, R, ~B & 0xFF, blitter);
924 }
925}
926
927static inline void align_thin_stroke(FDot8& edge1, FDot8& edge2) {
928 SkASSERT(edge1 <= edge2);
929
930 if (FDot8Floor(edge1) == FDot8Floor(edge2)) {
931 edge2 -= (edge1 & 0xFF);
932 edge1 &= ~0xFF;
933 }
934}
935
936void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
937 const SkRegion* clip, SkBlitter* blitter) {
938 SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
939
940 SkScalar rx = SkScalarHalf(strokeSize.fX);
941 SkScalar ry = SkScalarHalf(strokeSize.fY);
942
943 // outset by the radius
944 FDot8 outerL = SkScalarToFDot8(r.fLeft - rx);
945 FDot8 outerT = SkScalarToFDot8(r.fTop - ry);
946 FDot8 outerR = SkScalarToFDot8(r.fRight + rx);
947 FDot8 outerB = SkScalarToFDot8(r.fBottom + ry);
948
949 SkIRect outer;
950 // set outer to the outer rect of the outer section
951 outer.setLTRB(FDot8Floor(outerL), FDot8Floor(outerT), FDot8Ceil(outerR), FDot8Ceil(outerB));
952
953 SkBlitterClipper clipper;
954 if (clip) {
955 if (clip->quickReject(outer)) {
956 return;
957 }
958 if (!clip->contains(outer)) {
959 blitter = clipper.apply(blitter, clip, &outer);
960 }
961 // now we can ignore clip for the rest of the function
962 }
963
964 // in case we lost a bit with diameter/2
965 rx = strokeSize.fX - rx;
966 ry = strokeSize.fY - ry;
967
968 // inset by the radius
969 FDot8 innerL = SkScalarToFDot8(r.fLeft + rx);
970 FDot8 innerT = SkScalarToFDot8(r.fTop + ry);
971 FDot8 innerR = SkScalarToFDot8(r.fRight - rx);
972 FDot8 innerB = SkScalarToFDot8(r.fBottom - ry);
973
974 // For sub-unit strokes, tweak the hulls such that one of the edges coincides with the pixel
975 // edge. This ensures that the general rect stroking logic below
976 // a) doesn't blit the same scanline twice
977 // b) computes the correct coverage when both edges fall within the same pixel
978 if (strokeSize.fX < 1 || strokeSize.fY < 1) {
979 align_thin_stroke(outerL, innerL);
980 align_thin_stroke(outerT, innerT);
981 align_thin_stroke(innerR, outerR);
982 align_thin_stroke(innerB, outerB);
983 }
984
985 // stroke the outer hull
986 antifilldot8(outerL, outerT, outerR, outerB, blitter, false);
987
988 // set outer to the outer rect of the middle section
989 outer.setLTRB(FDot8Ceil(outerL), FDot8Ceil(outerT), FDot8Floor(outerR), FDot8Floor(outerB));
990
991 if (innerL >= innerR || innerT >= innerB) {
992 fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom,
993 blitter);
994 } else {
995 SkIRect inner;
996 // set inner to the inner rect of the middle section
997 inner.setLTRB(FDot8Floor(innerL), FDot8Floor(innerT), FDot8Ceil(innerR), FDot8Ceil(innerB));
998
999 // draw the frame in 4 pieces
1000 fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, inner.fTop,
1001 blitter);
1002 fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom,
1003 blitter);
1004 fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom,
1005 blitter);
1006 fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom,
1007 blitter);
1008
1009 // now stroke the inner rect, which is similar to antifilldot8() except that
1010 // it treats the fractional coordinates with the inverse bias (since its
1011 // inner).
1012 innerstrokedot8(innerL, innerT, innerR, innerB, blitter);
1013 }
1014}
1015
1016void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
1017 const SkRasterClip& clip, SkBlitter* blitter) {
1018 if (clip.isBW()) {
1019 AntiFrameRect(r, strokeSize, &clip.bwRgn(), blitter);
1020 } else {
1021 SkAAClipBlitterWrapper wrap(clip, blitter);
1022 AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter());
1023 }
1024}
int count
Definition: FontMgrTest.cpp:50
#define SkASSERT(cond)
Definition: SkAssert.h:116
unsigned U8CPU
Definition: SkCPUTypes.h:18
#define SkAlphaMul(value, alpha256)
Definition: SkColorPriv.h:34
#define SkFDot6Ceil(x)
Definition: SkFDot6.h:53
#define SkFDot6Floor(x)
Definition: SkFDot6.h:52
int32_t SkFDot6
Definition: SkFDot6.h:16
SkFixed SkFDot6ToFixed(SkFDot6 x)
Definition: SkFDot6.h:58
#define SkIntToFDot6(x)
Definition: SkFDot6.h:49
#define SkScalarToFDot6(x)
Definition: SkFDot6.h:64
#define SkFixedCeilToInt(x)
Definition: SkFixed.h:77
int32_t SkFixed
Definition: SkFixed.h:25
#define SK_Fixed1
Definition: SkFixed.h:26
#define SK_FixedHalf
Definition: SkFixed.h:27
#define SkFixedFloorToInt(x)
Definition: SkFixed.h:78
static constexpr int32_t SkLeftShift(int32_t value, int32_t shift)
Definition: SkMath.h:37
static constexpr int32_t SK_MaxS32
Definition: SkMath.h:21
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
static bool left(const SkPoint &p0, const SkPoint &p1)
static bool right(const SkPoint &p0, const SkPoint &p1)
void swap(sk_sp< T > &a, sk_sp< T > &b)
Definition: SkRefCnt.h:341
static int32_t SkAbs32(int32_t value)
Definition: SkSafe32.h:41
#define SK_Scalar1
Definition: SkScalar.h:18
#define SkScalarHalf(a)
Definition: SkScalar.h:75
#define SkIntToScalar(x)
Definition: SkScalar.h:57
static void XRect_set(SkXRect *xr, const SkIRect &src)
Definition: SkScan.h:98
static void XRect_roundOut(const SkXRect &xr, SkIRect *dst)
Definition: SkScan.h:128
static void antifilldot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter *blitter, bool fillInner)
#define SkAlphaMulRound(a, b)
#define ApplyGamma(table, alpha)
static int any_bad_ints(int a, int b, int c, int d)
static void align_thin_stroke(FDot8 &edge1, FDot8 &edge2)
static int bad_int(int x)
static void fillcheckrect(int L, int T, int R, int B, SkBlitter *blitter)
static void call_hline_blitter(SkBlitter *blitter, int x, int y, int count, U8CPU alpha)
int FDot8
static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter *blitter)
static FDot8 SkScalarToFDot8(SkScalar x)
static FDot8 SkFixedToFDot8(SkFixed x)
#define SkBITCOUNT(x)
static SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b)
static int SmallDot6Scale(int value, int dot6)
static U8CPU InvAlphaMul(U8CPU a, U8CPU b)
static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha, SkBlitter *blitter)
static int FDot8Floor(FDot8 x)
static int FDot8Ceil(FDot8 x)
static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1, const SkIRect *clip, SkBlitter *blitter)
static int contribution_64(SkFDot6 ordinate)
static void antifillrect(const SkXRect &xr, SkBlitter *blitter)
static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha, SkBlitter *blitter)
#define HLINE_STACK_BUFFER
constexpr uint8_t SkToU8(S x)
Definition: SkTo.h:22
constexpr int16_t SkToS16(S x)
Definition: SkTo.h:23
SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed slope) override
SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) override
SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed dy) override
SkFixed drawCap(int x, SkFixed fy, SkFixed dy, int mod64) override
const SkRegion & getRgn() const
Definition: SkRasterClip.h:175
SkBlitter * getBlitter()
Definition: SkRasterClip.h:179
virtual ~SkAntiHairBlitter()
virtual SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64)=0
SkBlitter * getBlitter() const
void setup(SkBlitter *blitter)
virtual SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed slope)=0
SkBlitter * apply(SkBlitter *blitter, const SkRegion *clip, const SkIRect *bounds=nullptr)
Definition: SkBlitter.cpp:626
virtual void blitAntiH2(int x, int y, U8CPU a0, U8CPU a1)
Definition: SkBlitter.h:81
virtual void blitAntiV2(int x, int y, U8CPU a0, U8CPU a1)
Definition: SkBlitter.h:94
virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[])=0
virtual void blitV(int x, int y, int height, SkAlpha alpha)
Blit a vertical run of pixels with a constant alpha value.
Definition: SkBlitter.cpp:119
virtual void blitRect(int x, int y, int width, int height)
Blit a solid rectangle one or more pixels wide.
Definition: SkBlitter.cpp:133
static bool IntersectLine(const SkPoint src[2], const SkRect &clip, SkPoint dst[2])
bool isEmpty() const
Definition: SkPath.cpp:416
const SkRect & getBounds() const
Definition: SkPath.cpp:430
bool isRect(SkRect *rect, bool *isClosed=nullptr, SkPathDirection *direction=nullptr) const
Definition: SkPath.cpp:516
bool contains(SkScalar x, SkScalar y) const
Definition: SkPath.cpp:3118
void init(SkBlitter *blitter, const SkIRect &clipRect)
Definition: SkBlitter.h:184
static void AntiHairRect(const SkRect &, const SkRasterClip &, SkBlitter *)
static void AntiFillRect(const SkRect &, const SkRasterClip &, SkBlitter *)
static void AntiHairLine(const SkPoint[], int count, const SkRasterClip &, SkBlitter *)
static void AntiFrameRect(const SkRect &, const SkPoint &strokeSize, const SkRasterClip &, SkBlitter *)
static void AntiFillXRect(const SkXRect &, const SkRasterClip &, SkBlitter *)
SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) override
SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) override
SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) override
SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) override
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
float SkScalar
Definition: extension.cpp:12
static bool b
struct MyStruct a[10]
uint8_t value
GAsyncResult * result
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
#define R(r)
double y
double x
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
skia_private::AutoTArray< sk_sp< SkImageFilter > > filters TypedMatrix matrix TypedMatrix matrix SkScalar dx
Definition: SkRecords.h:208
#define T
Definition: precompiler.cc:65
int32_t height
int32_t width
Definition: SkRect.h:32
bool intersect(const SkIRect &r)
Definition: SkRect.h:513
int32_t fBottom
larger y-axis bounds
Definition: SkRect.h:36
int32_t fTop
smaller y-axis bounds
Definition: SkRect.h:34
int32_t fLeft
smaller x-axis bounds
Definition: SkRect.h:33
void setLTRB(int32_t left, int32_t top, int32_t right, int32_t bottom)
Definition: SkRect.h:253
bool contains(int32_t x, int32_t y) const
Definition: SkRect.h:463
int32_t fRight
larger x-axis bounds
Definition: SkRect.h:35
float fX
x-axis value
Definition: SkPoint_impl.h:164
void set(float x, float y)
Definition: SkPoint_impl.h:200
float fY
y-axis value
Definition: SkPoint_impl.h:165
SkScalar fBottom
larger y-axis bounds
Definition: extension.cpp:17
bool intersect(const SkRect &r)
Definition: SkRect.cpp:114
SkScalar fLeft
smaller x-axis bounds
Definition: extension.cpp:14
void outset(float dx, float dy)
Definition: SkRect.h:1077
SkScalar fRight
larger x-axis bounds
Definition: extension.cpp:16
void roundOut(SkIRect *dst) const
Definition: SkRect.h:1241
bool isEmpty() const
Definition: SkRect.h:693
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition: SkRect.h:646
void set(const SkIRect &src)
Definition: SkRect.h:849
SkScalar fTop
smaller y-axis bounds
Definition: extension.cpp:15