xref: /aosp_15_r20/external/skia/src/core/SkRasterClip.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2010 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/core/SkRasterClip.h"
9 
10 #include "include/core/SkBlendMode.h"
11 #include "include/core/SkClipOp.h"
12 #include "include/core/SkMatrix.h"
13 #include "include/core/SkPath.h"
14 #include "include/core/SkScalar.h"
15 #include "include/private/base/SkDebug.h"
16 #include "src/core/SkRegionPriv.h"
17 
18 class SkBlitter;
19 
SkRasterClip(const SkRasterClip & that)20 SkRasterClip::SkRasterClip(const SkRasterClip& that)
21         : fIsBW(that.fIsBW)
22         , fIsEmpty(that.fIsEmpty)
23         , fIsRect(that.fIsRect)
24         , fShader(that.fShader)
25 {
26     AUTO_RASTERCLIP_VALIDATE(that);
27 
28     if (fIsBW) {
29         fBW = that.fBW;
30     } else {
31         fAA = that.fAA;
32     }
33 
34     SkDEBUGCODE(this->validate();)
35 }
36 
operator =(const SkRasterClip & that)37 SkRasterClip& SkRasterClip::operator=(const SkRasterClip& that) {
38     AUTO_RASTERCLIP_VALIDATE(that);
39 
40     fIsBW = that.fIsBW;
41     if (fIsBW) {
42         fBW = that.fBW;
43     } else {
44         fAA = that.fAA;
45     }
46 
47     fIsEmpty = that.isEmpty();
48     fIsRect = that.isRect();
49     fShader = that.fShader;
50     SkDEBUGCODE(this->validate();)
51     return *this;
52 }
53 
SkRasterClip(const SkRegion & rgn)54 SkRasterClip::SkRasterClip(const SkRegion& rgn) : fBW(rgn) {
55     fIsBW = true;
56     fIsEmpty = this->computeIsEmpty();  // bounds might be empty, so compute
57     fIsRect = !fIsEmpty;
58     SkDEBUGCODE(this->validate();)
59 }
60 
SkRasterClip(const SkIRect & bounds)61 SkRasterClip::SkRasterClip(const SkIRect& bounds) : fBW(bounds) {
62     fIsBW = true;
63     fIsEmpty = this->computeIsEmpty();  // bounds might be empty, so compute
64     fIsRect = !fIsEmpty;
65     SkDEBUGCODE(this->validate();)
66 }
67 
SkRasterClip()68 SkRasterClip::SkRasterClip() {
69     fIsBW = true;
70     fIsEmpty = true;
71     fIsRect = false;
72     SkDEBUGCODE(this->validate();)
73 }
74 
SkRasterClip(const SkPath & path,const SkIRect & bounds,bool doAA)75 SkRasterClip::SkRasterClip(const SkPath& path, const SkIRect& bounds, bool doAA) {
76     if (doAA) {
77         fIsBW = false;
78         fAA.setPath(path, bounds, true);
79     } else {
80         fIsBW = true;
81         fBW.setPath(path, SkRegion(bounds));
82     }
83     fIsEmpty = this->computeIsEmpty();  // bounds might be empty, so compute
84     fIsRect = this->computeIsRect();
85     SkDEBUGCODE(this->validate();)
86 }
87 
~SkRasterClip()88 SkRasterClip::~SkRasterClip() {
89     SkDEBUGCODE(this->validate();)
90 }
91 
setEmpty()92 bool SkRasterClip::setEmpty() {
93     AUTO_RASTERCLIP_VALIDATE(*this);
94 
95     fIsBW = true;
96     fBW.setEmpty();
97     fAA.setEmpty();
98     fIsEmpty = true;
99     fIsRect = false;
100     return false;
101 }
102 
setRect(const SkIRect & rect)103 bool SkRasterClip::setRect(const SkIRect& rect) {
104     AUTO_RASTERCLIP_VALIDATE(*this);
105 
106     fIsBW = true;
107     fAA.setEmpty();
108     fIsRect = fBW.setRect(rect);
109     fIsEmpty = !fIsRect;
110     return fIsRect;
111 }
112 
113 /////////////////////////////////////////////////////////////////////////////////////
114 
op(const SkIRect & rect,SkClipOp op)115 bool SkRasterClip::op(const SkIRect& rect, SkClipOp op) {
116     AUTO_RASTERCLIP_VALIDATE(*this);
117 
118     if (fIsBW) {
119         fBW.op(rect, (SkRegion::Op) op);
120     } else {
121         fAA.op(rect, op);
122     }
123     return this->updateCacheAndReturnNonEmpty();
124 }
125 
op(const SkRegion & rgn,SkClipOp op)126 bool SkRasterClip::op(const SkRegion& rgn, SkClipOp op) {
127     AUTO_RASTERCLIP_VALIDATE(*this);
128 
129     if (fIsBW) {
130         (void)fBW.op(rgn, (SkRegion::Op) op);
131     } else {
132         SkAAClip tmp;
133         tmp.setRegion(rgn);
134         (void)fAA.op(tmp, op);
135     }
136     return this->updateCacheAndReturnNonEmpty();
137 }
138 
139 /**
140  *  Our antialiasing currently has a granularity of 1/4 of a pixel along each
141  *  axis. Thus we can treat an axis coordinate as an integer if it differs
142  *  from its nearest int by < half of that value (1/8 in this case).
143  */
nearly_integral(SkScalar x)144 static bool nearly_integral(SkScalar x) {
145     static const SkScalar domain = SK_Scalar1 / 4;
146     static const SkScalar halfDomain = domain / 2;
147 
148     x += halfDomain;
149     return x - SkScalarFloorToScalar(x) < domain;
150 }
151 
op(const SkRect & localRect,const SkMatrix & matrix,SkClipOp op,bool doAA)152 bool SkRasterClip::op(const SkRect& localRect, const SkMatrix& matrix, SkClipOp op, bool doAA) {
153     AUTO_RASTERCLIP_VALIDATE(*this);
154 
155     const bool isScaleTrans = matrix.isScaleTranslate();
156     if (!isScaleTrans) {
157         return this->op(SkPath::Rect(localRect), matrix, op, doAA);
158     }
159 
160     SkRect devRect = matrix.mapRect(localRect);
161     if (fIsBW && doAA) {
162         // check that the rect really needs aa, or is it close enought to
163         // integer boundaries that we can just treat it as a BW rect?
164         if (nearly_integral(devRect.fLeft) && nearly_integral(devRect.fTop) &&
165             nearly_integral(devRect.fRight) && nearly_integral(devRect.fBottom)) {
166             doAA = false;
167         }
168     }
169 
170     if (fIsBW && !doAA) {
171         (void)fBW.op(devRect.round(), (SkRegion::Op) op);
172     } else {
173         if (fIsBW) {
174             this->convertToAA();
175         }
176         (void)fAA.op(devRect, op, doAA);
177     }
178     return this->updateCacheAndReturnNonEmpty();
179 }
180 
op(const SkRRect & rrect,const SkMatrix & matrix,SkClipOp op,bool doAA)181 bool SkRasterClip::op(const SkRRect& rrect, const SkMatrix& matrix, SkClipOp op, bool doAA) {
182     return this->op(SkPath::RRect(rrect), matrix, op, doAA);
183 }
184 
op(const SkPath & path,const SkMatrix & matrix,SkClipOp op,bool doAA)185 bool SkRasterClip::op(const SkPath& path, const SkMatrix& matrix, SkClipOp op, bool doAA) {
186     AUTO_RASTERCLIP_VALIDATE(*this);
187 
188     SkPath devPath;
189     path.transform(matrix, &devPath);
190 
191     // Since op is either intersect or difference, the clip is always shrinking; that means we can
192     // always use our current bounds as the limiting factor for region/aaclip operations.
193     if (this->isRect() && op == SkClipOp::kIntersect) {
194         // However, in the relatively common case of intersecting a new path with a rectangular
195         // clip, it's faster to convert the path into a region/aa-mask in place than evaluate the
196         // actual intersection. See skbug.com/12398
197         if (doAA && fIsBW) {
198             this->convertToAA();
199         }
200         if (fIsBW) {
201             fBW.setPath(devPath, SkRegion(this->getBounds()));
202         } else {
203             fAA.setPath(devPath, this->getBounds(), doAA);
204         }
205         return this->updateCacheAndReturnNonEmpty();
206     } else {
207         return this->op(SkRasterClip(devPath, this->getBounds(), doAA), op);
208     }
209 }
210 
op(sk_sp<SkShader> sh)211 bool SkRasterClip::op(sk_sp<SkShader> sh) {
212     AUTO_RASTERCLIP_VALIDATE(*this);
213 
214     if (!fShader) {
215         fShader = sh;
216     } else {
217         fShader = SkShaders::Blend(SkBlendMode::kSrcIn, sh, fShader);
218     }
219     return !this->isEmpty();
220 }
221 
op(const SkRasterClip & clip,SkClipOp op)222 bool SkRasterClip::op(const SkRasterClip& clip, SkClipOp op) {
223     AUTO_RASTERCLIP_VALIDATE(*this);
224     clip.validate();
225 
226     if (this->isBW() && clip.isBW()) {
227         (void)fBW.op(clip.fBW, (SkRegion::Op) op);
228     } else {
229         SkAAClip tmp;
230         const SkAAClip* other;
231 
232         if (this->isBW()) {
233             this->convertToAA();
234         }
235         if (clip.isBW()) {
236             tmp.setRegion(clip.bwRgn());
237             other = &tmp;
238         } else {
239             other = &clip.aaRgn();
240         }
241         (void)fAA.op(*other, op);
242     }
243     return this->updateCacheAndReturnNonEmpty();
244 }
245 
translate(int dx,int dy,SkRasterClip * dst) const246 void SkRasterClip::translate(int dx, int dy, SkRasterClip* dst) const {
247     if (nullptr == dst) {
248         return;
249     }
250 
251     AUTO_RASTERCLIP_VALIDATE(*this);
252 
253     if (this->isEmpty()) {
254         dst->setEmpty();
255         return;
256     }
257     if (0 == (dx | dy)) {
258         *dst = *this;
259         return;
260     }
261 
262     dst->fIsBW = fIsBW;
263     if (fIsBW) {
264         fBW.translate(dx, dy, &dst->fBW);
265         dst->fAA.setEmpty();
266     } else {
267         fAA.translate(dx, dy, &dst->fAA);
268         dst->fBW.setEmpty();
269     }
270     dst->updateCacheAndReturnNonEmpty();
271 }
272 
convertToAA()273 void SkRasterClip::convertToAA() {
274     AUTO_RASTERCLIP_VALIDATE(*this);
275 
276     SkASSERT(fIsBW);
277     fAA.setRegion(fBW);
278     fIsBW = false;
279 
280     // since we are being explicitly asked to convert-to-aa, we pass false so we don't "optimize"
281     // ourselves back to BW.
282     (void)this->updateCacheAndReturnNonEmpty(false);
283 }
284 
285 #ifdef SK_DEBUG
validate() const286 void SkRasterClip::validate() const {
287     // can't ever assert that fBW is empty, since we may have called forceGetBW
288     if (fIsBW) {
289         SkASSERT(fAA.isEmpty());
290     }
291 
292     SkRegionPriv::Validate(fBW);
293     fAA.validate();
294 
295     SkASSERT(this->computeIsEmpty() == fIsEmpty);
296     SkASSERT(this->computeIsRect() == fIsRect);
297 }
298 #endif
299 
300 ///////////////////////////////////////////////////////////////////////////////
301 
SkAAClipBlitterWrapper()302 SkAAClipBlitterWrapper::SkAAClipBlitterWrapper() {
303     SkDEBUGCODE(fClipRgn = nullptr;)
304     SkDEBUGCODE(fBlitter = nullptr;)
305 }
306 
SkAAClipBlitterWrapper(const SkRasterClip & clip,SkBlitter * blitter)307 SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkRasterClip& clip,
308                                                SkBlitter* blitter) {
309     this->init(clip, blitter);
310 }
311 
SkAAClipBlitterWrapper(const SkAAClip * aaclip,SkBlitter * blitter)312 SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkAAClip* aaclip,
313                                                SkBlitter* blitter) {
314     SkASSERT(blitter);
315     SkASSERT(aaclip);
316     fBWRgn.setRect(aaclip->getBounds());
317     fAABlitter.init(blitter, aaclip);
318     // now our return values
319     fClipRgn = &fBWRgn;
320     fBlitter = &fAABlitter;
321 }
322 
init(const SkRasterClip & clip,SkBlitter * blitter)323 void SkAAClipBlitterWrapper::init(const SkRasterClip& clip, SkBlitter* blitter) {
324     SkASSERT(blitter);
325     if (clip.isBW()) {
326         fClipRgn = &clip.bwRgn();
327         fBlitter = blitter;
328     } else {
329         const SkAAClip& aaclip = clip.aaRgn();
330         fBWRgn.setRect(aaclip.getBounds());
331         fAABlitter.init(blitter, &aaclip);
332         // now our return values
333         fClipRgn = &fBWRgn;
334         fBlitter = &fAABlitter;
335     }
336 }
337