Index: src/core/SkRasterClip.cpp |
diff --git a/src/core/SkRasterClip.cpp b/src/core/SkRasterClip.cpp |
index dab91d8f4ce31434af80839208961509e5746761..f820c5a0a44154389b5cea7521eaef7b1b3aad42 100644 |
--- a/src/core/SkRasterClip.cpp |
+++ b/src/core/SkRasterClip.cpp |
@@ -6,18 +6,12 @@ |
*/ |
#include "SkRasterClip.h" |
- |
- |
-SkRasterClip::SkRasterClip() { |
- fIsBW = true; |
- fIsEmpty = true; |
- fIsRect = false; |
- SkDEBUGCODE(this->validate();) |
-} |
+#include "SkPath.h" |
SkRasterClip::SkRasterClip(const SkRasterClip& src) { |
AUTO_RASTERCLIP_VALIDATE(src); |
+ fForceConservativeRects = src.fForceConservativeRects; |
fIsBW = src.fIsBW; |
if (fIsBW) { |
fBW = src.fBW; |
@@ -30,13 +24,22 @@ SkRasterClip::SkRasterClip(const SkRasterClip& src) { |
SkDEBUGCODE(this->validate();) |
} |
-SkRasterClip::SkRasterClip(const SkIRect& bounds) : fBW(bounds) { |
+SkRasterClip::SkRasterClip(const SkIRect& bounds, bool forceConservativeRects) : fBW(bounds) { |
+ fForceConservativeRects = forceConservativeRects; |
fIsBW = true; |
fIsEmpty = this->computeIsEmpty(); // bounds might be empty, so compute |
fIsRect = !fIsEmpty; |
SkDEBUGCODE(this->validate();) |
} |
+SkRasterClip::SkRasterClip(bool forceConservativeRects) { |
+ fForceConservativeRects = forceConservativeRects; |
+ fIsBW = true; |
+ fIsEmpty = true; |
+ fIsRect = false; |
+ SkDEBUGCODE(this->validate();) |
+} |
+ |
SkRasterClip::~SkRasterClip() { |
SkDEBUGCODE(this->validate();) |
} |
@@ -70,9 +73,84 @@ bool SkRasterClip::setRect(const SkIRect& rect) { |
return fIsRect; |
} |
+///////////////////////////////////////////////////////////////////////////////////// |
+ |
+bool SkRasterClip::setConservativeRect(const SkRect& r, const SkIRect& clipR, bool isInverse) { |
+ SkIRect ir; |
+ r.roundOut(&ir); |
+ |
+ SkRegion::Op op; |
+ if (isInverse) { |
+ op = SkRegion::kDifference_Op; |
+ } else { |
+ op = SkRegion::kIntersect_Op; |
+ } |
+ fBW.setRect(clipR); |
+ fBW.op(ir, op); |
+ return this->updateCacheAndReturnNonEmpty(); |
+} |
+ |
+///////////////////////////////////////////////////////////////////////////////////// |
+ |
+enum MutateResult { |
+ kDoNothing_MutateResult, |
+ kReplaceClippedAgainstGlobalBounds_MutateResult, |
+ kContinue_MutateResult, |
+}; |
+ |
+static MutateResult mutate_conservative_op(SkRegion::Op* op, bool inverseFilled) { |
+ if (inverseFilled) { |
+ switch (*op) { |
+ case SkRegion::kIntersect_Op: |
+ case SkRegion::kDifference_Op: |
+ // These ops can only shrink the current clip. So leaving |
+ // the clip unchanged conservatively respects the contract. |
+ return kDoNothing_MutateResult; |
+ case SkRegion::kUnion_Op: |
+ case SkRegion::kReplace_Op: |
+ case SkRegion::kReverseDifference_Op: |
+ case SkRegion::kXOR_Op: { |
+ // These ops can grow the current clip up to the extents of |
+ // the input clip, which is inverse filled, so we just set |
+ // the current clip to the device bounds. |
+ *op = SkRegion::kReplace_Op; |
+ return kReplaceClippedAgainstGlobalBounds_MutateResult; |
+ } |
+ } |
+ } else { |
+ // Not inverse filled |
+ switch (*op) { |
+ case SkRegion::kIntersect_Op: |
+ case SkRegion::kUnion_Op: |
+ case SkRegion::kReplace_Op: |
+ return kContinue_MutateResult; |
+ case SkRegion::kDifference_Op: |
+ // Difference can only shrink the current clip. |
+ // Leaving clip unchanged conservatively fullfills the contract. |
+ return kDoNothing_MutateResult; |
+ case SkRegion::kReverseDifference_Op: |
+ // To reverse, we swap in the bounds with a replace op. |
+ // As with difference, leave it unchanged. |
+ *op = SkRegion::kReplace_Op; |
+ return kContinue_MutateResult; |
+ case SkRegion::kXOR_Op: |
+ // Be conservative, based on (A XOR B) always included in (A union B), |
+ // which is always included in (bounds(A) union bounds(B)) |
+ *op = SkRegion::kUnion_Op; |
+ return kContinue_MutateResult; |
+ } |
+ } |
+ SkFAIL("should not get here"); |
+ return kDoNothing_MutateResult; |
+} |
+ |
bool SkRasterClip::setPath(const SkPath& path, const SkRegion& clip, bool doAA) { |
AUTO_RASTERCLIP_VALIDATE(*this); |
+ if (fForceConservativeRects) { |
+ return this->setConservativeRect(path.getBounds(), clip.getBounds(), path.isInverseFillType()); |
+ } |
+ |
if (this->isBW() && !doAA) { |
(void)fBW.setPath(path, clip); |
} else { |
@@ -90,7 +168,22 @@ bool SkRasterClip::op(const SkPath& path, const SkISize& size, SkRegion::Op op, |
// base is used to limit the size (and therefore memory allocation) of the |
// region that results from scan converting devPath. |
SkRegion base; |
- |
+ |
robertphillips
2014/09/10 14:44:23
Subroutine for this and the version in SkRasterCli
|
+ if (fForceConservativeRects) { |
+ SkIRect ir; |
+ switch (mutate_conservative_op(&op, path.isInverseFillType())) { |
+ case kDoNothing_MutateResult: |
+ return !this->isEmpty(); |
+ case kReplaceClippedAgainstGlobalBounds_MutateResult: |
+ ir = SkIRect::MakeSize(size); |
+ break; |
+ case kContinue_MutateResult: |
+ path.getBounds().roundOut(&ir); |
+ break; |
+ } |
+ return this->op(ir, op); |
+ } |
+ |
if (SkRegion::kIntersect_Op == op) { |
// since we are intersect, we can do better (tighter) with currRgn's |
// bounds, than just using the device. However, if currRgn is complex, |
@@ -102,7 +195,7 @@ bool SkRasterClip::op(const SkPath& path, const SkISize& size, SkRegion::Op op, |
return this->setPath(path, this->bwRgn(), doAA); |
} else { |
base.setRect(this->getBounds()); |
- SkRasterClip clip; |
+ SkRasterClip clip(fForceConservativeRects); |
clip.setPath(path, base, doAA); |
return this->op(clip, op); |
} |
@@ -112,7 +205,7 @@ bool SkRasterClip::op(const SkPath& path, const SkISize& size, SkRegion::Op op, |
if (SkRegion::kReplace_Op == op) { |
return this->setPath(path, base, doAA); |
} else { |
- SkRasterClip clip; |
+ SkRasterClip clip(fForceConservativeRects); |
clip.setPath(path, base, doAA); |
return this->op(clip, op); |
} |
@@ -182,9 +275,24 @@ static bool nearly_integral(SkScalar x) { |
return x - SkScalarFloorToScalar(x) < domain; |
} |
-bool SkRasterClip::op(const SkRect& r, SkRegion::Op op, bool doAA) { |
+bool SkRasterClip::op(const SkRect& r, const SkISize& size, SkRegion::Op op, bool doAA) { |
AUTO_RASTERCLIP_VALIDATE(*this); |
+ if (fForceConservativeRects) { |
+ SkIRect ir; |
+ switch (mutate_conservative_op(&op, false)) { |
+ case kDoNothing_MutateResult: |
+ return !this->isEmpty(); |
+ case kReplaceClippedAgainstGlobalBounds_MutateResult: |
+ ir = SkIRect::MakeSize(size); |
+ break; |
+ case kContinue_MutateResult: |
+ r.roundOut(&ir); |
+ break; |
+ } |
+ return this->op(ir, op); |
+ } |
+ |
if (fIsBW && doAA) { |
// check that the rect really needs aa, or is it close enought to |
// integer boundaries that we can just treat it as a BW rect? |
@@ -251,7 +359,9 @@ const SkRegion& SkRasterClip::forceGetBW() { |
void SkRasterClip::convertToAA() { |
AUTO_RASTERCLIP_VALIDATE(*this); |
- |
+ |
+ SkASSERT(!fForceConservativeRects); |
+ |
SkASSERT(fIsBW); |
fAA.setRegion(fBW); |
fIsBW = false; |