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 "SkRasterClip.h"
9 #include "SkPath.h"
10
SkRasterClip(const SkRasterClip & src)11 SkRasterClip::SkRasterClip(const SkRasterClip& src) {
12 AUTO_RASTERCLIP_VALIDATE(src);
13
14 fForceConservativeRects = src.fForceConservativeRects;
15 fIsBW = src.fIsBW;
16 if (fIsBW) {
17 fBW = src.fBW;
18 } else {
19 fAA = src.fAA;
20 }
21
22 fIsEmpty = src.isEmpty();
23 fIsRect = src.isRect();
24 SkDEBUGCODE(this->validate();)
25 }
26
SkRasterClip(const SkIRect & bounds,bool forceConservativeRects)27 SkRasterClip::SkRasterClip(const SkIRect& bounds, bool forceConservativeRects) : fBW(bounds) {
28 fForceConservativeRects = forceConservativeRects;
29 fIsBW = true;
30 fIsEmpty = this->computeIsEmpty(); // bounds might be empty, so compute
31 fIsRect = !fIsEmpty;
32 SkDEBUGCODE(this->validate();)
33 }
34
SkRasterClip(bool forceConservativeRects)35 SkRasterClip::SkRasterClip(bool forceConservativeRects) {
36 fForceConservativeRects = forceConservativeRects;
37 fIsBW = true;
38 fIsEmpty = true;
39 fIsRect = false;
40 SkDEBUGCODE(this->validate();)
41 }
42
~SkRasterClip()43 SkRasterClip::~SkRasterClip() {
44 SkDEBUGCODE(this->validate();)
45 }
46
isComplex() const47 bool SkRasterClip::isComplex() const {
48 return fIsBW ? fBW.isComplex() : !fAA.isEmpty();
49 }
50
getBounds() const51 const SkIRect& SkRasterClip::getBounds() const {
52 return fIsBW ? fBW.getBounds() : fAA.getBounds();
53 }
54
setEmpty()55 bool SkRasterClip::setEmpty() {
56 AUTO_RASTERCLIP_VALIDATE(*this);
57
58 fIsBW = true;
59 fBW.setEmpty();
60 fAA.setEmpty();
61 fIsEmpty = true;
62 fIsRect = false;
63 return false;
64 }
65
setRect(const SkIRect & rect)66 bool SkRasterClip::setRect(const SkIRect& rect) {
67 AUTO_RASTERCLIP_VALIDATE(*this);
68
69 fIsBW = true;
70 fAA.setEmpty();
71 fIsRect = fBW.setRect(rect);
72 fIsEmpty = !fIsRect;
73 return fIsRect;
74 }
75
76 /////////////////////////////////////////////////////////////////////////////////////
77
setConservativeRect(const SkRect & r,const SkIRect & clipR,bool isInverse)78 bool SkRasterClip::setConservativeRect(const SkRect& r, const SkIRect& clipR, bool isInverse) {
79 SkRegion::Op op;
80 if (isInverse) {
81 op = SkRegion::kDifference_Op;
82 } else {
83 op = SkRegion::kIntersect_Op;
84 }
85 fBW.setRect(clipR);
86 fBW.op(r.roundOut(), op);
87 return this->updateCacheAndReturnNonEmpty();
88 }
89
90 /////////////////////////////////////////////////////////////////////////////////////
91
92 enum MutateResult {
93 kDoNothing_MutateResult,
94 kReplaceClippedAgainstGlobalBounds_MutateResult,
95 kContinue_MutateResult,
96 };
97
mutate_conservative_op(SkRegion::Op * op,bool inverseFilled)98 static MutateResult mutate_conservative_op(SkRegion::Op* op, bool inverseFilled) {
99 if (inverseFilled) {
100 switch (*op) {
101 case SkRegion::kIntersect_Op:
102 case SkRegion::kDifference_Op:
103 // These ops can only shrink the current clip. So leaving
104 // the clip unchanged conservatively respects the contract.
105 return kDoNothing_MutateResult;
106 case SkRegion::kUnion_Op:
107 case SkRegion::kReplace_Op:
108 case SkRegion::kReverseDifference_Op:
109 case SkRegion::kXOR_Op: {
110 // These ops can grow the current clip up to the extents of
111 // the input clip, which is inverse filled, so we just set
112 // the current clip to the device bounds.
113 *op = SkRegion::kReplace_Op;
114 return kReplaceClippedAgainstGlobalBounds_MutateResult;
115 }
116 }
117 } else {
118 // Not inverse filled
119 switch (*op) {
120 case SkRegion::kIntersect_Op:
121 case SkRegion::kUnion_Op:
122 case SkRegion::kReplace_Op:
123 return kContinue_MutateResult;
124 case SkRegion::kDifference_Op:
125 // Difference can only shrink the current clip.
126 // Leaving clip unchanged conservatively fullfills the contract.
127 return kDoNothing_MutateResult;
128 case SkRegion::kReverseDifference_Op:
129 // To reverse, we swap in the bounds with a replace op.
130 // As with difference, leave it unchanged.
131 *op = SkRegion::kReplace_Op;
132 return kContinue_MutateResult;
133 case SkRegion::kXOR_Op:
134 // Be conservative, based on (A XOR B) always included in (A union B),
135 // which is always included in (bounds(A) union bounds(B))
136 *op = SkRegion::kUnion_Op;
137 return kContinue_MutateResult;
138 }
139 }
140 SkFAIL("should not get here");
141 return kDoNothing_MutateResult;
142 }
143
setPath(const SkPath & path,const SkRegion & clip,bool doAA)144 bool SkRasterClip::setPath(const SkPath& path, const SkRegion& clip, bool doAA) {
145 AUTO_RASTERCLIP_VALIDATE(*this);
146
147 if (fForceConservativeRects) {
148 return this->setConservativeRect(path.getBounds(), clip.getBounds(), path.isInverseFillType());
149 }
150
151 if (this->isBW() && !doAA) {
152 (void)fBW.setPath(path, clip);
153 } else {
154 // TODO: since we are going to over-write fAA completely (aren't we?)
155 // we should just clear our BW data (if any) and set fIsAA=true
156 if (this->isBW()) {
157 this->convertToAA();
158 }
159 (void)fAA.setPath(path, &clip, doAA);
160 }
161 return this->updateCacheAndReturnNonEmpty();
162 }
163
op(const SkPath & path,const SkISize & size,SkRegion::Op op,bool doAA)164 bool SkRasterClip::op(const SkPath& path, const SkISize& size, SkRegion::Op op, bool doAA) {
165 // base is used to limit the size (and therefore memory allocation) of the
166 // region that results from scan converting devPath.
167 SkRegion base;
168
169 if (fForceConservativeRects) {
170 SkIRect ir;
171 switch (mutate_conservative_op(&op, path.isInverseFillType())) {
172 case kDoNothing_MutateResult:
173 return !this->isEmpty();
174 case kReplaceClippedAgainstGlobalBounds_MutateResult:
175 ir = SkIRect::MakeSize(size);
176 break;
177 case kContinue_MutateResult:
178 ir = path.getBounds().roundOut();
179 break;
180 }
181 return this->op(ir, op);
182 }
183
184 if (SkRegion::kIntersect_Op == op) {
185 // since we are intersect, we can do better (tighter) with currRgn's
186 // bounds, than just using the device. However, if currRgn is complex,
187 // our region blitter may hork, so we do that case in two steps.
188 if (this->isRect()) {
189 // FIXME: we should also be able to do this when this->isBW(),
190 // but relaxing the test above triggers GM asserts in
191 // SkRgnBuilder::blitH(). We need to investigate what's going on.
192 return this->setPath(path, this->bwRgn(), doAA);
193 } else {
194 base.setRect(this->getBounds());
195 SkRasterClip clip(fForceConservativeRects);
196 clip.setPath(path, base, doAA);
197 return this->op(clip, op);
198 }
199 } else {
200 base.setRect(0, 0, size.width(), size.height());
201
202 if (SkRegion::kReplace_Op == op) {
203 return this->setPath(path, base, doAA);
204 } else {
205 SkRasterClip clip(fForceConservativeRects);
206 clip.setPath(path, base, doAA);
207 return this->op(clip, op);
208 }
209 }
210 }
211
setPath(const SkPath & path,const SkIRect & clip,bool doAA)212 bool SkRasterClip::setPath(const SkPath& path, const SkIRect& clip, bool doAA) {
213 SkRegion tmp;
214 tmp.setRect(clip);
215 return this->setPath(path, tmp, doAA);
216 }
217
op(const SkIRect & rect,SkRegion::Op op)218 bool SkRasterClip::op(const SkIRect& rect, SkRegion::Op op) {
219 AUTO_RASTERCLIP_VALIDATE(*this);
220
221 fIsBW ? fBW.op(rect, op) : fAA.op(rect, op);
222 return this->updateCacheAndReturnNonEmpty();
223 }
224
op(const SkRegion & rgn,SkRegion::Op op)225 bool SkRasterClip::op(const SkRegion& rgn, SkRegion::Op op) {
226 AUTO_RASTERCLIP_VALIDATE(*this);
227
228 if (fIsBW) {
229 (void)fBW.op(rgn, op);
230 } else {
231 SkAAClip tmp;
232 tmp.setRegion(rgn);
233 (void)fAA.op(tmp, op);
234 }
235 return this->updateCacheAndReturnNonEmpty();
236 }
237
op(const SkRasterClip & clip,SkRegion::Op op)238 bool SkRasterClip::op(const SkRasterClip& clip, SkRegion::Op op) {
239 AUTO_RASTERCLIP_VALIDATE(*this);
240 clip.validate();
241
242 if (this->isBW() && clip.isBW()) {
243 (void)fBW.op(clip.fBW, op);
244 } else {
245 SkAAClip tmp;
246 const SkAAClip* other;
247
248 if (this->isBW()) {
249 this->convertToAA();
250 }
251 if (clip.isBW()) {
252 tmp.setRegion(clip.bwRgn());
253 other = &tmp;
254 } else {
255 other = &clip.aaRgn();
256 }
257 (void)fAA.op(*other, op);
258 }
259 return this->updateCacheAndReturnNonEmpty();
260 }
261
262 /**
263 * Our antialiasing currently has a granularity of 1/4 of a pixel along each
264 * axis. Thus we can treat an axis coordinate as an integer if it differs
265 * from its nearest int by < half of that value (1.8 in this case).
266 */
nearly_integral(SkScalar x)267 static bool nearly_integral(SkScalar x) {
268 static const SkScalar domain = SK_Scalar1 / 4;
269 static const SkScalar halfDomain = domain / 2;
270
271 x += halfDomain;
272 return x - SkScalarFloorToScalar(x) < domain;
273 }
274
op(const SkRect & r,const SkISize & size,SkRegion::Op op,bool doAA)275 bool SkRasterClip::op(const SkRect& r, const SkISize& size, SkRegion::Op op, bool doAA) {
276 AUTO_RASTERCLIP_VALIDATE(*this);
277
278 if (fForceConservativeRects) {
279 SkIRect ir;
280 switch (mutate_conservative_op(&op, false)) {
281 case kDoNothing_MutateResult:
282 return !this->isEmpty();
283 case kReplaceClippedAgainstGlobalBounds_MutateResult:
284 ir = SkIRect::MakeSize(size);
285 break;
286 case kContinue_MutateResult:
287 ir = r.roundOut();
288 break;
289 }
290 return this->op(ir, op);
291 }
292
293 if (fIsBW && doAA) {
294 // check that the rect really needs aa, or is it close enought to
295 // integer boundaries that we can just treat it as a BW rect?
296 if (nearly_integral(r.fLeft) && nearly_integral(r.fTop) &&
297 nearly_integral(r.fRight) && nearly_integral(r.fBottom)) {
298 doAA = false;
299 }
300 }
301
302 if (fIsBW && !doAA) {
303 SkIRect ir;
304 r.round(&ir);
305 (void)fBW.op(ir, op);
306 } else {
307 if (fIsBW) {
308 this->convertToAA();
309 }
310 (void)fAA.op(r, op, doAA);
311 }
312 return this->updateCacheAndReturnNonEmpty();
313 }
314
translate(int dx,int dy,SkRasterClip * dst) const315 void SkRasterClip::translate(int dx, int dy, SkRasterClip* dst) const {
316 if (NULL == dst) {
317 return;
318 }
319
320 AUTO_RASTERCLIP_VALIDATE(*this);
321
322 if (this->isEmpty()) {
323 dst->setEmpty();
324 return;
325 }
326 if (0 == (dx | dy)) {
327 *dst = *this;
328 return;
329 }
330
331 dst->fIsBW = fIsBW;
332 if (fIsBW) {
333 fBW.translate(dx, dy, &dst->fBW);
334 dst->fAA.setEmpty();
335 } else {
336 fAA.translate(dx, dy, &dst->fAA);
337 dst->fBW.setEmpty();
338 }
339 dst->updateCacheAndReturnNonEmpty();
340 }
341
quickContains(const SkIRect & ir) const342 bool SkRasterClip::quickContains(const SkIRect& ir) const {
343 return fIsBW ? fBW.quickContains(ir) : fAA.quickContains(ir);
344 }
345
346 ///////////////////////////////////////////////////////////////////////////////
347
forceGetBW()348 const SkRegion& SkRasterClip::forceGetBW() {
349 AUTO_RASTERCLIP_VALIDATE(*this);
350
351 if (!fIsBW) {
352 fBW.setRect(fAA.getBounds());
353 }
354 return fBW;
355 }
356
convertToAA()357 void SkRasterClip::convertToAA() {
358 AUTO_RASTERCLIP_VALIDATE(*this);
359
360 SkASSERT(!fForceConservativeRects);
361
362 SkASSERT(fIsBW);
363 fAA.setRegion(fBW);
364 fIsBW = false;
365
366 // since we are being explicitly asked to convert-to-aa, we pass false so we don't "optimize"
367 // ourselves back to BW.
368 (void)this->updateCacheAndReturnNonEmpty(false);
369 }
370
371 #ifdef SK_DEBUG
validate() const372 void SkRasterClip::validate() const {
373 // can't ever assert that fBW is empty, since we may have called forceGetBW
374 if (fIsBW) {
375 SkASSERT(fAA.isEmpty());
376 }
377
378 fBW.validate();
379 fAA.validate();
380
381 SkASSERT(this->computeIsEmpty() == fIsEmpty);
382 SkASSERT(this->computeIsRect() == fIsRect);
383 }
384 #endif
385
386 ///////////////////////////////////////////////////////////////////////////////
387
SkAAClipBlitterWrapper()388 SkAAClipBlitterWrapper::SkAAClipBlitterWrapper() {
389 SkDEBUGCODE(fClipRgn = NULL;)
390 SkDEBUGCODE(fBlitter = NULL;)
391 }
392
SkAAClipBlitterWrapper(const SkRasterClip & clip,SkBlitter * blitter)393 SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkRasterClip& clip,
394 SkBlitter* blitter) {
395 this->init(clip, blitter);
396 }
397
SkAAClipBlitterWrapper(const SkAAClip * aaclip,SkBlitter * blitter)398 SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkAAClip* aaclip,
399 SkBlitter* blitter) {
400 SkASSERT(blitter);
401 SkASSERT(aaclip);
402 fBWRgn.setRect(aaclip->getBounds());
403 fAABlitter.init(blitter, aaclip);
404 // now our return values
405 fClipRgn = &fBWRgn;
406 fBlitter = &fAABlitter;
407 }
408
init(const SkRasterClip & clip,SkBlitter * blitter)409 void SkAAClipBlitterWrapper::init(const SkRasterClip& clip, SkBlitter* blitter) {
410 SkASSERT(blitter);
411 if (clip.isBW()) {
412 fClipRgn = &clip.bwRgn();
413 fBlitter = blitter;
414 } else {
415 const SkAAClip& aaclip = clip.aaRgn();
416 fBWRgn.setRect(aaclip.getBounds());
417 fAABlitter.init(blitter, &aaclip);
418 // now our return values
419 fClipRgn = &fBWRgn;
420 fBlitter = &fAABlitter;
421 }
422 }
423