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