xref: /aosp_15_r20/external/skia/src/core/SkScan_AntiPath.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2006 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/SkPath.h"
9 
10 #include "include/core/SkRect.h"
11 #include "include/core/SkRegion.h"
12 #include "include/private/base/SkAssert.h"
13 #include "include/private/base/SkMath.h"
14 #include "src/core/SkAAClip.h"
15 #include "src/core/SkBlitter.h"
16 #include "src/core/SkRasterClip.h"
17 #include "src/core/SkScan.h"
18 #include "src/core/SkScanPriv.h"
19 
20 #include <cstdint>
21 
safeRoundOut(const SkRect & src)22 static SkIRect safeRoundOut(const SkRect& src) {
23     // roundOut will pin huge floats to max/min int
24     SkIRect dst = src.roundOut();
25 
26     // intersect with a smaller huge rect, so the rect will not be considered empty for being
27     // too large. e.g. { -SK_MaxS32 ... SK_MaxS32 } is considered empty because its width
28     // exceeds signed 32bit.
29     const int32_t limit = SK_MaxS32 >> SK_SUPERSAMPLE_SHIFT;
30     (void)dst.intersect({ -limit, -limit, limit, limit});
31 
32     return dst;
33 }
34 
overflows_short_shift(int value,int shift)35 static int overflows_short_shift(int value, int shift) {
36     const int s = 16 + shift;
37     return (SkLeftShift(value, s) >> s) - value;
38 }
39 
40 /**
41   Would any of the coordinates of this rectangle not fit in a short,
42   when left-shifted by shift?
43 */
rect_overflows_short_shift(SkIRect rect,int shift)44 static int rect_overflows_short_shift(SkIRect rect, int shift) {
45     SkASSERT(!overflows_short_shift(8191, shift));
46     SkASSERT(overflows_short_shift(8192, shift));
47     SkASSERT(!overflows_short_shift(32767, 0));
48     SkASSERT(overflows_short_shift(32768, 0));
49 
50     // Since we expect these to succeed, we bit-or together
51     // for a tiny extra bit of speed.
52     return overflows_short_shift(rect.fLeft, shift) |
53            overflows_short_shift(rect.fRight, shift) |
54            overflows_short_shift(rect.fTop, shift) |
55            overflows_short_shift(rect.fBottom, shift);
56 }
57 
AntiFillPath(const SkPath & path,const SkRegion & origClip,SkBlitter * blitter,bool forceRLE)58 void SkScan::AntiFillPath(const SkPath& path, const SkRegion& origClip,
59                           SkBlitter* blitter, bool forceRLE) {
60     if (origClip.isEmpty()) {
61         return;
62     }
63 
64     const bool isInverse = path.isInverseFillType();
65     SkIRect ir = safeRoundOut(path.getBounds());
66     if (ir.isEmpty()) {
67         if (isInverse) {
68             blitter->blitRegion(origClip);
69         }
70         return;
71     }
72 
73     // If the intersection of the path bounds and the clip bounds
74     // will overflow 32767 when << by SHIFT, we can't supersample,
75     // so draw without antialiasing.
76     SkIRect clippedIR;
77     if (isInverse) {
78        // If the path is an inverse fill, it's going to fill the entire
79        // clip, and we care whether the entire clip exceeds our limits.
80        clippedIR = origClip.getBounds();
81     } else {
82        if (!clippedIR.intersect(ir, origClip.getBounds())) {
83            return;
84        }
85     }
86     if (rect_overflows_short_shift(clippedIR, SK_SUPERSAMPLE_SHIFT)) {
87         SkScan::FillPath(path, origClip, blitter);
88         return;
89     }
90 
91     // Our antialiasing can't handle a clip larger than 32767, so we restrict
92     // the clip to that limit here. (the runs[] uses int16_t for its index).
93     //
94     // A more general solution (one that could also eliminate the need to
95     // disable aa based on ir bounds (see overflows_short_shift) would be
96     // to tile the clip/target...
97     SkRegion tmpClipStorage;
98     const SkRegion* clipRgn = &origClip;
99     {
100         static const int32_t kMaxClipCoord = 32767;
101         const SkIRect& bounds = origClip.getBounds();
102         if (bounds.fRight > kMaxClipCoord || bounds.fBottom > kMaxClipCoord) {
103             SkIRect limit = { 0, 0, kMaxClipCoord, kMaxClipCoord };
104             tmpClipStorage.op(origClip, limit, SkRegion::kIntersect_Op);
105             clipRgn = &tmpClipStorage;
106         }
107     }
108     // for here down, use clipRgn, not origClip
109 
110     SkScanClipper   clipper(blitter, clipRgn, ir);
111 
112     if (clipper.getBlitter() == nullptr) { // clipped out
113         if (isInverse) {
114             blitter->blitRegion(*clipRgn);
115         }
116         return;
117     }
118 
119     SkASSERT(clipper.getClipRect() == nullptr ||
120             *clipper.getClipRect() == clipRgn->getBounds());
121 
122     // now use the (possibly wrapped) blitter
123     blitter = clipper.getBlitter();
124 
125     if (isInverse) {
126         sk_blit_above(blitter, ir, *clipRgn);
127     }
128 
129     SkScan::AAAFillPath(path, blitter, ir, clipRgn->getBounds(), forceRLE);
130 
131     if (isInverse) {
132         sk_blit_below(blitter, ir, *clipRgn);
133     }
134 }
135 
136 ///////////////////////////////////////////////////////////////////////////////
137 
FillPath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)138 void SkScan::FillPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
139     if (clip.isEmpty() || !path.isFinite()) {
140         return;
141     }
142 
143     if (clip.isBW()) {
144         FillPath(path, clip.bwRgn(), blitter);
145     } else {
146         SkRegion        tmp;
147         SkAAClipBlitter aaBlitter;
148 
149         tmp.setRect(clip.getBounds());
150         aaBlitter.init(blitter, &clip.aaRgn());
151         SkScan::FillPath(path, tmp, &aaBlitter);
152     }
153 }
154 
AntiFillPath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)155 void SkScan::AntiFillPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
156     if (clip.isEmpty() || !path.isFinite()) {
157         return;
158     }
159 
160     if (clip.isBW()) {
161         AntiFillPath(path, clip.bwRgn(), blitter, false);
162     } else {
163         SkRegion        tmp;
164         SkAAClipBlitter aaBlitter;
165 
166         tmp.setRect(clip.getBounds());
167         aaBlitter.init(blitter, &clip.aaRgn());
168         AntiFillPath(path, tmp, &aaBlitter, true); // SkAAClipBlitter can blitMask, why forceRLE?
169     }
170 }
171