xref: /aosp_15_r20/external/skia/src/core/SkScan_Antihair.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 
8 #include "include/core/SkColorPriv.h"
9 #include "include/core/SkPoint.h"
10 #include "include/core/SkRect.h"
11 #include "include/core/SkRegion.h"
12 #include "include/core/SkScalar.h"
13 #include "include/private/base/SkAssert.h"
14 #include "include/private/base/SkCPUTypes.h"
15 #include "include/private/base/SkDebug.h"
16 #include "include/private/base/SkFixed.h"
17 #include "include/private/base/SkMath.h"
18 #include "include/private/base/SkSafe32.h"
19 #include "include/private/base/SkTo.h"
20 #include "src/core/SkBlitter.h"
21 #include "src/core/SkFDot6.h"
22 #include "src/core/SkLineClipper.h"
23 #include "src/core/SkRasterClip.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 
SmallDot6Scale(int value,int dot6)46 static 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 
build_gamma_table()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 
call_hline_blitter(SkBlitter * blitter,int x,int y,int count,U8CPU alpha)80 static 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) {
97             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 
107 class SkAntiHairBlitter {
108 public:
SkAntiHairBlitter()109     SkAntiHairBlitter() : fBlitter(nullptr) {}
~SkAntiHairBlitter()110     virtual ~SkAntiHairBlitter() {}
111 
getBlitter() const112     SkBlitter* getBlitter() const { return fBlitter; }
113 
setup(SkBlitter * blitter)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 
121 private:
122     SkBlitter*  fBlitter;
123 };
124 
125 class HLine_SkAntiHairBlitter : public SkAntiHairBlitter {
126 public:
drawCap(int x,SkFixed fy,SkFixed slope,int mod64)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 
drawLine(int x,int stopx,SkFixed fy,SkFixed slope)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 
171 class Horish_SkAntiHairBlitter : public SkAntiHairBlitter {
172 public:
drawCap(int x,SkFixed fy,SkFixed dy,int mod64)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 
drawLine(int x,int stopx,SkFixed fy,SkFixed dy)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 
201 class VLine_SkAntiHairBlitter : public SkAntiHairBlitter {
202 public:
drawCap(int y,SkFixed fx,SkFixed dx,int mod64)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 
drawLine(int y,int stopy,SkFixed fx,SkFixed dx)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 
242 class Vertish_SkAntiHairBlitter : public SkAntiHairBlitter {
243 public:
drawCap(int y,SkFixed fx,SkFixed dx,int mod64)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 
drawLine(int y,int stopy,SkFixed fx,SkFixed dx)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 
fastfixdiv(SkFDot6 a,SkFDot6 b)269 static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) {
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...
bad_int(int x)279 static inline int bad_int(int x) {
280     return x & -x;
281 }
282 
any_bad_ints(int a,int b,int c,int d)283 static 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
good_int(int x)287 static inline int good_int(int x) {
288     return x ^ (1 << (SkBITCOUNT(x) - 1));
289 }
290 
any_bad_ints(int a,int b,int c,int d)291 static 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
canConvertFDot6ToFixed(SkFDot6 x)297 static 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  */
contribution_64(SkFDot6 ordinate)309 static 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 
do_anti_hairline(SkFDot6 x0,SkFDot6 y0,SkFDot6 x1,SkFDot6 y1,const SkIRect * clip,SkBlitter * blitter)322 static 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
496                 left = SkFixedFloorToInt(fstart - SK_FixedHalf);
497                 right = SkFixedCeilToInt(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
498             } else {           // R2L
499                 right = SkFixedCeilToInt(fstart + SK_FixedHalf);
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 
AntiHairLineRgn(const SkPoint array[],int arrayCount,const SkRegion * clip,SkBlitter * blitter)542 void 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 
AntiHairRect(const SkRect & rect,const SkRasterClip & clip,SkBlitter * blitter)621 void SkScan::AntiHairRect(const SkRect& rect, const SkRasterClip& clip,
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 
635 typedef int FDot8;  // 24.8 integer fixed point
636 
SkFixedToFDot8(SkFixed x)637 static inline FDot8 SkFixedToFDot8(SkFixed x) {
638     return (x + 0x80) >> 8;
639 }
640 
do_scanline(FDot8 L,int top,FDot8 R,U8CPU alpha,SkBlitter * blitter)641 static 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 
antifilldot8(FDot8 L,FDot8 T,FDot8 R,FDot8 B,SkBlitter * blitter,bool fillInner)667 static 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 
antifillrect(const SkXRect & xr,SkBlitter * blitter)711 static void antifillrect(const SkXRect& xr, SkBlitter* blitter) {
712     antifilldot8(SkFixedToFDot8(xr.fLeft), SkFixedToFDot8(xr.fTop),
713                  SkFixedToFDot8(xr.fRight), SkFixedToFDot8(xr.fBottom),
714                  blitter, true);
715 }
716 
717 ///////////////////////////////////////////////////////////////////////////////
718 
AntiFillXRect(const SkXRect & xr,const SkRegion * clip,SkBlitter * blitter)719 void 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 
AntiFillXRect(const SkXRect & xr,const SkRasterClip & clip,SkBlitter * blitter)758 void SkScan::AntiFillXRect(const SkXRect& xr, const SkRasterClip& clip,
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 */
antifillrect(const SkRect & r,SkBlitter * blitter)779 static 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 */
AntiFillRect(const SkRect & origR,const SkRegion * clip,SkBlitter * blitter)794 void 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 
AntiFillRect(const SkRect & r,const SkRasterClip & clip,SkBlitter * blitter)822 void SkScan::AntiFillRect(const SkRect& r, const SkRasterClip& clip,
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
fillcheckrect(int L,int T,int R,int B,SkBlitter * blitter)837 static 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 
SkScalarToFDot8(SkScalar x)843 static inline FDot8 SkScalarToFDot8(SkScalar x) {
844     return (int)(x * 256);
845 }
846 
FDot8Floor(FDot8 x)847 static inline int FDot8Floor(FDot8 x) {
848     return x >> 8;
849 }
850 
FDot8Ceil(FDot8 x)851 static inline int FDot8Ceil(FDot8 x) {
852     return (x + 0xFF) >> 8;
853 }
854 
855 // 1 - (1 - a)*(1 - b)
InvAlphaMul(U8CPU a,U8CPU b)856 static 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 
inner_scanline(FDot8 L,int top,FDot8 R,U8CPU alpha,SkBlitter * blitter)862 static 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 
innerstrokedot8(FDot8 L,FDot8 T,FDot8 R,FDot8 B,SkBlitter * blitter)892 static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B,
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 
align_thin_stroke(FDot8 & edge1,FDot8 & edge2)927 static 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 
AntiFrameRect(const SkRect & r,const SkPoint & strokeSize,const SkRegion * clip,SkBlitter * blitter)936 void 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 
AntiFrameRect(const SkRect & r,const SkPoint & strokeSize,const SkRasterClip & clip,SkBlitter * blitter)1016 void 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 }
1025