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