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