Chromium Code Reviews| 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; |