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
12enum MutateResult {
13 kDoNothing_MutateResult,
14 kReplaceClippedAgainstGlobalBounds_MutateResult,
15 kContinue_MutateResult,
16};
17
18static 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
64void 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
82void 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
87void 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
106void SkConservativeClip::opRegion(const SkRegion& rgn, SkRegion::Op op) {
107 this->opIRect(rgn.getBounds(), op);
108}
109
110void 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
128SkRasterClip::SkRasterClip(const SkRasterClip& that)
129 : fIsBW(that.fIsBW), fIsEmpty(that.fIsEmpty), fIsRect(that.fIsRect)
130 , fClipRestrictionRect(that.fClipRestrictionRect), fShader(that.fShader)
131{
132 AUTO_RASTERCLIP_VALIDATE(that);
133
134 if (fIsBW) {
135 fBW = that.fBW;
136 } else {
137 fAA = that.fAA;
138 }
139
140 SkDEBUGCODE(this->validate();)
141}
142
143SkRasterClip& SkRasterClip::operator=(const SkRasterClip& that) {
144 AUTO_RASTERCLIP_VALIDATE(that);
145
146 fIsBW = that.fIsBW;
147 if (fIsBW) {
148 fBW = that.fBW;
149 } else {
150 fAA = that.fAA;
151 }
152
153 fIsEmpty = that.isEmpty();
154 fIsRect = that.isRect();
155 fClipRestrictionRect = that.fClipRestrictionRect;
156 fShader = that.fShader;
157 SkDEBUGCODE(this->validate();)
158 return *this;
159}
160
161SkRasterClip::SkRasterClip(const SkRegion& rgn) : fBW(rgn) {
162 fIsBW = true;
163 fIsEmpty = this->computeIsEmpty(); // bounds might be empty, so compute
164 fIsRect = !fIsEmpty;
165 SkDEBUGCODE(this->validate();)
166}
167
168SkRasterClip::SkRasterClip(const SkIRect& bounds) : fBW(bounds) {
169 fIsBW = true;
170 fIsEmpty = this->computeIsEmpty(); // bounds might be empty, so compute
171 fIsRect = !fIsEmpty;
172 SkDEBUGCODE(this->validate();)
173}
174
175SkRasterClip::SkRasterClip() {
176 fIsBW = true;
177 fIsEmpty = true;
178 fIsRect = false;
179 SkDEBUGCODE(this->validate();)
180}
181
182SkRasterClip::~SkRasterClip() {
183 SkDEBUGCODE(this->validate();)
184}
185
186bool SkRasterClip::operator==(const SkRasterClip& other) const {
187 if (fIsBW != other.fIsBW) {
188 return false;
189 }
190 bool isEqual = fIsBW ? fBW == other.fBW : fAA == other.fAA;
191#ifdef SK_DEBUG
192 if (isEqual) {
193 SkASSERT(fIsEmpty == other.fIsEmpty);
194 SkASSERT(fIsRect == other.fIsRect);
195 }
196#endif
197 return isEqual;
198}
199
200bool SkRasterClip::isComplex() const {
201 return fIsBW ? fBW.isComplex() : !fAA.isEmpty();
202}
203
204const SkIRect& SkRasterClip::getBounds() const {
205 return fIsBW ? fBW.getBounds() : fAA.getBounds();
206}
207
208bool SkRasterClip::setEmpty() {
209 AUTO_RASTERCLIP_VALIDATE(*this);
210
211 fIsBW = true;
212 fBW.setEmpty();
213 fAA.setEmpty();
214 fIsEmpty = true;
215 fIsRect = false;
216 return false;
217}
218
219bool SkRasterClip::setRect(const SkIRect& rect) {
220 AUTO_RASTERCLIP_VALIDATE(*this);
221
222 fIsBW = true;
223 fAA.setEmpty();
224 fIsRect = fBW.setRect(rect);
225 fIsEmpty = !fIsRect;
226 return fIsRect;
227}
228
229/////////////////////////////////////////////////////////////////////////////////////
230
231bool SkRasterClip::setConservativeRect(const SkRect& r, const SkIRect& clipR, bool isInverse) {
232 SkRegion::Op op;
233 if (isInverse) {
234 op = SkRegion::kDifference_Op;
235 } else {
236 op = SkRegion::kIntersect_Op;
237 }
238 fBW.setRect(clipR);
239 fBW.op(r.roundOut(), op);
240 return this->updateCacheAndReturnNonEmpty();
241}
242
243/////////////////////////////////////////////////////////////////////////////////////
244
245bool SkRasterClip::setPath(const SkPath& path, const SkRegion& clip, bool doAA) {
246 AUTO_RASTERCLIP_VALIDATE(*this);
247
248 if (this->isBW() && !doAA) {
249 (void)fBW.setPath(path, clip);
250 } else {
251 // TODO: since we are going to over-write fAA completely (aren't we?)
252 // we should just clear our BW data (if any) and set fIsAA=true
253 if (this->isBW()) {
254 this->convertToAA();
255 }
256 (void)fAA.setPath(path, &clip, doAA);
257 }
258 return this->updateCacheAndReturnNonEmpty();
259}
260
261bool SkRasterClip::op(const SkRRect& rrect, const SkMatrix& matrix, const SkIRect& devBounds,
262 SkRegion::Op op, bool doAA) {
263 SkIRect bounds(devBounds);
264 this->applyClipRestriction(op, &bounds);
265
266 SkPath path;
267 path.addRRect(rrect);
268
269 return this->op(path, matrix, bounds, op, doAA);
270}
271
272bool SkRasterClip::op(const SkPath& path, const SkMatrix& matrix, const SkIRect& devBounds,
273 SkRegion::Op op, bool doAA) {
274 AUTO_RASTERCLIP_VALIDATE(*this);
275 SkIRect bounds(devBounds);
276 this->applyClipRestriction(op, &bounds);
277
278 // base is used to limit the size (and therefore memory allocation) of the
279 // region that results from scan converting devPath.
280 SkRegion base;
281
282 SkPath devPath;
283 if (matrix.isIdentity()) {
284 devPath = path;
285 } else {
286 path.transform(matrix, &devPath);
287 devPath.setIsVolatile(true);
288 }
289 if (SkRegion::kIntersect_Op == op) {
290 // since we are intersect, we can do better (tighter) with currRgn's
291 // bounds, than just using the device. However, if currRgn is complex,
292 // our region blitter may hork, so we do that case in two steps.
293 if (this->isRect()) {
294 // FIXME: we should also be able to do this when this->isBW(),
295 // but relaxing the test above triggers GM asserts in
296 // SkRgnBuilder::blitH(). We need to investigate what's going on.
297 return this->setPath(devPath, this->bwRgn(), doAA);
298 } else {
299 base.setRect(this->getBounds());
300 SkRasterClip clip;
301 clip.setPath(devPath, base, doAA);
302 return this->op(clip, op);
303 }
304 } else {
305 base.setRect(bounds);
306
307 if (SkRegion::kReplace_Op == op) {
308 return this->setPath(devPath, base, doAA);
309 } else {
310 SkRasterClip clip;
311 clip.setPath(devPath, base, doAA);
312 return this->op(clip, op);
313 }
314 }
315}
316
317bool SkRasterClip::setPath(const SkPath& path, const SkIRect& clip, bool doAA) {
318 SkRegion tmp;
319 tmp.setRect(clip);
320 return this->setPath(path, tmp, doAA);
321}
322
323bool SkRasterClip::op(const SkIRect& rect, SkRegion::Op op) {
324 AUTO_RASTERCLIP_VALIDATE(*this);
325
326 fIsBW ? fBW.op(rect, op) : fAA.op(rect, op);
327 return this->updateCacheAndReturnNonEmpty();
328}
329
330bool SkRasterClip::op(const SkRegion& rgn, SkRegion::Op op) {
331 AUTO_RASTERCLIP_VALIDATE(*this);
332
333 if (fIsBW) {
334 (void)fBW.op(rgn, op);
335 } else {
336 SkAAClip tmp;
337 tmp.setRegion(rgn);
338 (void)fAA.op(tmp, op);
339 }
340 return this->updateCacheAndReturnNonEmpty();
341}
342
343bool SkRasterClip::op(const SkRasterClip& clip, SkRegion::Op op) {
344 AUTO_RASTERCLIP_VALIDATE(*this);
345 clip.validate();
346
347 if (this->isBW() && clip.isBW()) {
348 (void)fBW.op(clip.fBW, op);
349 } else {
350 SkAAClip tmp;
351 const SkAAClip* other;
352
353 if (this->isBW()) {
354 this->convertToAA();
355 }
356 if (clip.isBW()) {
357 tmp.setRegion(clip.bwRgn());
358 other = &tmp;
359 } else {
360 other = &clip.aaRgn();
361 }
362 (void)fAA.op(*other, op);
363 }
364 return this->updateCacheAndReturnNonEmpty();
365}
366
367bool SkRasterClip::op(sk_sp<SkShader> sh) {
368 AUTO_RASTERCLIP_VALIDATE(*this);
369
370 if (!fShader) {
371 fShader = sh;
372 } else {
373 fShader = SkShaders::Blend(SkBlendMode::kSrcIn, sh, fShader);
374 }
375 return !this->isEmpty();
376}
377
378/**
379 * Our antialiasing currently has a granularity of 1/4 of a pixel along each
380 * axis. Thus we can treat an axis coordinate as an integer if it differs
381 * from its nearest int by < half of that value (1.8 in this case).
382 */
383static bool nearly_integral(SkScalar x) {
384 static const SkScalar domain = SK_Scalar1 / 4;
385 static const SkScalar halfDomain = domain / 2;
386
387 x += halfDomain;
388 return x - SkScalarFloorToScalar(x) < domain;
389}
390
391bool SkRasterClip::op(const SkRect& localRect, const SkMatrix& matrix, const SkIRect& devBounds,
392 SkRegion::Op op, bool doAA) {
393 AUTO_RASTERCLIP_VALIDATE(*this);
394 SkRect devRect;
395
396 const bool isScaleTrans = matrix.isScaleTranslate();
397 if (!isScaleTrans) {
398 SkPath path;
399 path.addRect(localRect);
400 path.setIsVolatile(true);
401 return this->op(path, matrix, devBounds, op, doAA);
402 }
403
404 matrix.mapRect(&devRect, localRect);
405
406 if (fIsBW && doAA) {
407 // check that the rect really needs aa, or is it close enought to
408 // integer boundaries that we can just treat it as a BW rect?
409 if (nearly_integral(devRect.fLeft) && nearly_integral(devRect.fTop) &&
410 nearly_integral(devRect.fRight) && nearly_integral(devRect.fBottom)) {
411 doAA = false;
412 }
413 }
414
415 if (fIsBW && !doAA) {
416 SkIRect ir;
417 devRect.round(&ir);
418 this->applyClipRestriction(op, &ir);
419 (void)fBW.op(ir, op);
420 } else {
421 if (fIsBW) {
422 this->convertToAA();
423 }
424 this->applyClipRestriction(op, &devRect);
425 (void)fAA.op(devRect, op, doAA);
426 }
427 return this->updateCacheAndReturnNonEmpty();
428}
429
430void SkRasterClip::translate(int dx, int dy, SkRasterClip* dst) const {
431 if (nullptr == dst) {
432 return;
433 }
434
435 AUTO_RASTERCLIP_VALIDATE(*this);
436
437 if (this->isEmpty()) {
438 dst->setEmpty();
439 return;
440 }
441 if (0 == (dx | dy)) {
442 *dst = *this;
443 return;
444 }
445
446 dst->fIsBW = fIsBW;
447 if (fIsBW) {
448 fBW.translate(dx, dy, &dst->fBW);
449 dst->fAA.setEmpty();
450 } else {
451 fAA.translate(dx, dy, &dst->fAA);
452 dst->fBW.setEmpty();
453 }
454 dst->updateCacheAndReturnNonEmpty();
455}
456
457bool SkRasterClip::quickContains(const SkIRect& ir) const {
458 return fIsBW ? fBW.quickContains(ir) : fAA.quickContains(ir);
459}
460
461///////////////////////////////////////////////////////////////////////////////
462
463const SkRegion& SkRasterClip::forceGetBW() {
464 AUTO_RASTERCLIP_VALIDATE(*this);
465
466 if (!fIsBW) {
467 fBW.setRect(fAA.getBounds());
468 }
469 return fBW;
470}
471
472void SkRasterClip::convertToAA() {
473 AUTO_RASTERCLIP_VALIDATE(*this);
474
475 SkASSERT(fIsBW);
476 fAA.setRegion(fBW);
477 fIsBW = false;
478
479 // since we are being explicitly asked to convert-to-aa, we pass false so we don't "optimize"
480 // ourselves back to BW.
481 (void)this->updateCacheAndReturnNonEmpty(false);
482}
483
484#ifdef SK_DEBUG
485void SkRasterClip::validate() const {
486 // can't ever assert that fBW is empty, since we may have called forceGetBW
487 if (fIsBW) {
488 SkASSERT(fAA.isEmpty());
489 }
490
491 SkRegionPriv::Validate(fBW);
492 fAA.validate();
493
494 SkASSERT(this->computeIsEmpty() == fIsEmpty);
495 SkASSERT(this->computeIsRect() == fIsRect);
496}
497#endif
498
499///////////////////////////////////////////////////////////////////////////////
500
501SkAAClipBlitterWrapper::SkAAClipBlitterWrapper() {
502 SkDEBUGCODE(fClipRgn = nullptr;)
503 SkDEBUGCODE(fBlitter = nullptr;)
504}
505
506SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkRasterClip& clip,
507 SkBlitter* blitter) {
508 this->init(clip, blitter);
509}
510
511SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkAAClip* aaclip,
512 SkBlitter* blitter) {
513 SkASSERT(blitter);
514 SkASSERT(aaclip);
515 fBWRgn.setRect(aaclip->getBounds());
516 fAABlitter.init(blitter, aaclip);
517 // now our return values
518 fClipRgn = &fBWRgn;
519 fBlitter = &fAABlitter;
520}
521
522void SkAAClipBlitterWrapper::init(const SkRasterClip& clip, SkBlitter* blitter) {
523 SkASSERT(blitter);
524 if (clip.isBW()) {
525 fClipRgn = &clip.bwRgn();
526 fBlitter = blitter;
527 } else {
528 const SkAAClip& aaclip = clip.aaRgn();
529 fBWRgn.setRect(aaclip.getBounds());
530 fAABlitter.init(blitter, &aaclip);
531 // now our return values
532 fClipRgn = &fBWRgn;
533 fBlitter = &fAABlitter;
534 }
535}
536