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