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