123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507 |
- /*
- * Copyright 2010 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
- #include "include/core/SkPath.h"
- #include "src/core/SkRasterClip.h"
- #include "src/core/SkRegionPriv.h"
- 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;
- }
- }
- SkASSERT(false); // unknown op
- return kDoNothing_MutateResult;
- }
- void SkConservativeClip::opRect(const SkRect& localRect, const SkMatrix& ctm,
- const SkIRect& devBounds, SkRegion::Op op, bool doAA) {
- SkIRect ir;
- switch (mutate_conservative_op(&op, false)) {
- case kDoNothing_MutateResult:
- return;
- case kReplaceClippedAgainstGlobalBounds_MutateResult:
- ir = devBounds;
- break;
- case kContinue_MutateResult: {
- SkRect devRect;
- ctm.mapRect(&devRect, localRect);
- ir = doAA ? devRect.roundOut() : devRect.round();
- } break;
- }
- this->opIRect(ir, op);
- }
- void SkConservativeClip::opRRect(const SkRRect& rrect, const SkMatrix& ctm,
- const SkIRect& devBounds, SkRegion::Op op, bool doAA) {
- this->opRect(rrect.getBounds(), ctm, devBounds, op, doAA);
- }
- void SkConservativeClip::opPath(const SkPath& path, const SkMatrix& ctm, const SkIRect& devBounds,
- SkRegion::Op op, bool doAA) {
- SkIRect ir;
- switch (mutate_conservative_op(&op, path.isInverseFillType())) {
- case kDoNothing_MutateResult:
- return;
- case kReplaceClippedAgainstGlobalBounds_MutateResult:
- ir = devBounds;
- break;
- case kContinue_MutateResult: {
- SkRect bounds = path.getBounds();
- ctm.mapRect(&bounds);
- ir = bounds.roundOut();
- break;
- }
- }
- return this->opIRect(ir, op);
- }
- void SkConservativeClip::opRegion(const SkRegion& rgn, SkRegion::Op op) {
- this->opIRect(rgn.getBounds(), op);
- }
- void SkConservativeClip::opIRect(const SkIRect& devRect, SkRegion::Op op) {
- if (SkRegion::kIntersect_Op == op) {
- if (!fBounds.intersect(devRect)) {
- fBounds.setEmpty();
- }
- return;
- }
- // This may still create a complex region (which we would then take the bounds
- // Perhaps we should inline the op-logic directly to never create the rgn...
- SkRegion result;
- result.op(SkRegion(fBounds), SkRegion(devRect), op);
- fBounds = result.getBounds();
- this->applyClipRestriction(op, &fBounds);
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- SkRasterClip::SkRasterClip(const SkRasterClip& src) {
- AUTO_RASTERCLIP_VALIDATE(src);
- fIsBW = src.fIsBW;
- if (fIsBW) {
- fBW = src.fBW;
- } else {
- fAA = src.fAA;
- }
- fIsEmpty = src.isEmpty();
- fIsRect = src.isRect();
- fClipRestrictionRect = src.fClipRestrictionRect;
- SkDEBUGCODE(this->validate();)
- }
- SkRasterClip::SkRasterClip(const SkRegion& rgn) : fBW(rgn) {
- fIsBW = true;
- fIsEmpty = this->computeIsEmpty(); // bounds might be empty, so compute
- fIsRect = !fIsEmpty;
- SkDEBUGCODE(this->validate();)
- }
- SkRasterClip::SkRasterClip(const SkIRect& bounds) : fBW(bounds) {
- fIsBW = true;
- fIsEmpty = this->computeIsEmpty(); // bounds might be empty, so compute
- fIsRect = !fIsEmpty;
- SkDEBUGCODE(this->validate();)
- }
- SkRasterClip::SkRasterClip() {
- fIsBW = true;
- fIsEmpty = true;
- fIsRect = false;
- SkDEBUGCODE(this->validate();)
- }
- SkRasterClip::~SkRasterClip() {
- SkDEBUGCODE(this->validate();)
- }
- bool SkRasterClip::operator==(const SkRasterClip& other) const {
- if (fIsBW != other.fIsBW) {
- return false;
- }
- bool isEqual = fIsBW ? fBW == other.fBW : fAA == other.fAA;
- #ifdef SK_DEBUG
- if (isEqual) {
- SkASSERT(fIsEmpty == other.fIsEmpty);
- SkASSERT(fIsRect == other.fIsRect);
- }
- #endif
- return isEqual;
- }
- bool SkRasterClip::isComplex() const {
- return fIsBW ? fBW.isComplex() : !fAA.isEmpty();
- }
- const SkIRect& SkRasterClip::getBounds() const {
- return fIsBW ? fBW.getBounds() : fAA.getBounds();
- }
- bool SkRasterClip::setEmpty() {
- AUTO_RASTERCLIP_VALIDATE(*this);
- fIsBW = true;
- fBW.setEmpty();
- fAA.setEmpty();
- fIsEmpty = true;
- fIsRect = false;
- return false;
- }
- bool SkRasterClip::setRect(const SkIRect& rect) {
- AUTO_RASTERCLIP_VALIDATE(*this);
- fIsBW = true;
- fAA.setEmpty();
- fIsRect = fBW.setRect(rect);
- fIsEmpty = !fIsRect;
- return fIsRect;
- }
- /////////////////////////////////////////////////////////////////////////////////////
- bool SkRasterClip::setConservativeRect(const SkRect& r, const SkIRect& clipR, bool isInverse) {
- SkRegion::Op op;
- if (isInverse) {
- op = SkRegion::kDifference_Op;
- } else {
- op = SkRegion::kIntersect_Op;
- }
- fBW.setRect(clipR);
- fBW.op(r.roundOut(), op);
- return this->updateCacheAndReturnNonEmpty();
- }
- /////////////////////////////////////////////////////////////////////////////////////
- bool SkRasterClip::setPath(const SkPath& path, const SkRegion& clip, bool doAA) {
- AUTO_RASTERCLIP_VALIDATE(*this);
- if (this->isBW() && !doAA) {
- (void)fBW.setPath(path, clip);
- } else {
- // TODO: since we are going to over-write fAA completely (aren't we?)
- // we should just clear our BW data (if any) and set fIsAA=true
- if (this->isBW()) {
- this->convertToAA();
- }
- (void)fAA.setPath(path, &clip, doAA);
- }
- return this->updateCacheAndReturnNonEmpty();
- }
- bool SkRasterClip::op(const SkRRect& rrect, const SkMatrix& matrix, const SkIRect& devBounds,
- SkRegion::Op op, bool doAA) {
- SkIRect bounds(devBounds);
- this->applyClipRestriction(op, &bounds);
- SkPath path;
- path.addRRect(rrect);
- return this->op(path, matrix, bounds, op, doAA);
- }
- bool SkRasterClip::op(const SkPath& path, const SkMatrix& matrix, const SkIRect& devBounds,
- SkRegion::Op op, bool doAA) {
- AUTO_RASTERCLIP_VALIDATE(*this);
- SkIRect bounds(devBounds);
- this->applyClipRestriction(op, &bounds);
- // base is used to limit the size (and therefore memory allocation) of the
- // region that results from scan converting devPath.
- SkRegion base;
- SkPath devPath;
- if (matrix.isIdentity()) {
- devPath = path;
- } else {
- path.transform(matrix, &devPath);
- devPath.setIsVolatile(true);
- }
- 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,
- // our region blitter may hork, so we do that case in two steps.
- if (this->isRect()) {
- // FIXME: we should also be able to do this when this->isBW(),
- // but relaxing the test above triggers GM asserts in
- // SkRgnBuilder::blitH(). We need to investigate what's going on.
- return this->setPath(devPath, this->bwRgn(), doAA);
- } else {
- base.setRect(this->getBounds());
- SkRasterClip clip;
- clip.setPath(devPath, base, doAA);
- return this->op(clip, op);
- }
- } else {
- base.setRect(bounds);
- if (SkRegion::kReplace_Op == op) {
- return this->setPath(devPath, base, doAA);
- } else {
- SkRasterClip clip;
- clip.setPath(devPath, base, doAA);
- return this->op(clip, op);
- }
- }
- }
- bool SkRasterClip::setPath(const SkPath& path, const SkIRect& clip, bool doAA) {
- SkRegion tmp;
- tmp.setRect(clip);
- return this->setPath(path, tmp, doAA);
- }
- bool SkRasterClip::op(const SkIRect& rect, SkRegion::Op op) {
- AUTO_RASTERCLIP_VALIDATE(*this);
- fIsBW ? fBW.op(rect, op) : fAA.op(rect, op);
- return this->updateCacheAndReturnNonEmpty();
- }
- bool SkRasterClip::op(const SkRegion& rgn, SkRegion::Op op) {
- AUTO_RASTERCLIP_VALIDATE(*this);
- if (fIsBW) {
- (void)fBW.op(rgn, op);
- } else {
- SkAAClip tmp;
- tmp.setRegion(rgn);
- (void)fAA.op(tmp, op);
- }
- return this->updateCacheAndReturnNonEmpty();
- }
- bool SkRasterClip::op(const SkRasterClip& clip, SkRegion::Op op) {
- AUTO_RASTERCLIP_VALIDATE(*this);
- clip.validate();
- if (this->isBW() && clip.isBW()) {
- (void)fBW.op(clip.fBW, op);
- } else {
- SkAAClip tmp;
- const SkAAClip* other;
- if (this->isBW()) {
- this->convertToAA();
- }
- if (clip.isBW()) {
- tmp.setRegion(clip.bwRgn());
- other = &tmp;
- } else {
- other = &clip.aaRgn();
- }
- (void)fAA.op(*other, op);
- }
- return this->updateCacheAndReturnNonEmpty();
- }
- /**
- * Our antialiasing currently has a granularity of 1/4 of a pixel along each
- * axis. Thus we can treat an axis coordinate as an integer if it differs
- * from its nearest int by < half of that value (1.8 in this case).
- */
- static bool nearly_integral(SkScalar x) {
- static const SkScalar domain = SK_Scalar1 / 4;
- static const SkScalar halfDomain = domain / 2;
- x += halfDomain;
- return x - SkScalarFloorToScalar(x) < domain;
- }
- bool SkRasterClip::op(const SkRect& localRect, const SkMatrix& matrix, const SkIRect& devBounds,
- SkRegion::Op op, bool doAA) {
- AUTO_RASTERCLIP_VALIDATE(*this);
- SkRect devRect;
- const bool isScaleTrans = matrix.isScaleTranslate();
- if (!isScaleTrans) {
- SkPath path;
- path.addRect(localRect);
- path.setIsVolatile(true);
- return this->op(path, matrix, devBounds, op, doAA);
- }
- matrix.mapRect(&devRect, localRect);
- 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?
- if (nearly_integral(devRect.fLeft) && nearly_integral(devRect.fTop) &&
- nearly_integral(devRect.fRight) && nearly_integral(devRect.fBottom)) {
- doAA = false;
- }
- }
- if (fIsBW && !doAA) {
- SkIRect ir;
- devRect.round(&ir);
- this->applyClipRestriction(op, &ir);
- (void)fBW.op(ir, op);
- } else {
- if (fIsBW) {
- this->convertToAA();
- }
- this->applyClipRestriction(op, &devRect);
- (void)fAA.op(devRect, op, doAA);
- }
- return this->updateCacheAndReturnNonEmpty();
- }
- void SkRasterClip::translate(int dx, int dy, SkRasterClip* dst) const {
- if (nullptr == dst) {
- return;
- }
- AUTO_RASTERCLIP_VALIDATE(*this);
- if (this->isEmpty()) {
- dst->setEmpty();
- return;
- }
- if (0 == (dx | dy)) {
- *dst = *this;
- return;
- }
- dst->fIsBW = fIsBW;
- if (fIsBW) {
- fBW.translate(dx, dy, &dst->fBW);
- dst->fAA.setEmpty();
- } else {
- fAA.translate(dx, dy, &dst->fAA);
- dst->fBW.setEmpty();
- }
- dst->updateCacheAndReturnNonEmpty();
- }
- bool SkRasterClip::quickContains(const SkIRect& ir) const {
- return fIsBW ? fBW.quickContains(ir) : fAA.quickContains(ir);
- }
- ///////////////////////////////////////////////////////////////////////////////
- const SkRegion& SkRasterClip::forceGetBW() {
- AUTO_RASTERCLIP_VALIDATE(*this);
- if (!fIsBW) {
- fBW.setRect(fAA.getBounds());
- }
- return fBW;
- }
- void SkRasterClip::convertToAA() {
- AUTO_RASTERCLIP_VALIDATE(*this);
- SkASSERT(fIsBW);
- fAA.setRegion(fBW);
- fIsBW = false;
- // since we are being explicitly asked to convert-to-aa, we pass false so we don't "optimize"
- // ourselves back to BW.
- (void)this->updateCacheAndReturnNonEmpty(false);
- }
- #ifdef SK_DEBUG
- void SkRasterClip::validate() const {
- // can't ever assert that fBW is empty, since we may have called forceGetBW
- if (fIsBW) {
- SkASSERT(fAA.isEmpty());
- }
- SkRegionPriv::Validate(fBW);
- fAA.validate();
- SkASSERT(this->computeIsEmpty() == fIsEmpty);
- SkASSERT(this->computeIsRect() == fIsRect);
- }
- #endif
- ///////////////////////////////////////////////////////////////////////////////
- SkAAClipBlitterWrapper::SkAAClipBlitterWrapper() {
- SkDEBUGCODE(fClipRgn = nullptr;)
- SkDEBUGCODE(fBlitter = nullptr;)
- }
- SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkRasterClip& clip,
- SkBlitter* blitter) {
- this->init(clip, blitter);
- }
- SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkAAClip* aaclip,
- SkBlitter* blitter) {
- SkASSERT(blitter);
- SkASSERT(aaclip);
- fBWRgn.setRect(aaclip->getBounds());
- fAABlitter.init(blitter, aaclip);
- // now our return values
- fClipRgn = &fBWRgn;
- fBlitter = &fAABlitter;
- }
- void SkAAClipBlitterWrapper::init(const SkRasterClip& clip, SkBlitter* blitter) {
- SkASSERT(blitter);
- if (clip.isBW()) {
- fClipRgn = &clip.bwRgn();
- fBlitter = blitter;
- } else {
- const SkAAClip& aaclip = clip.aaRgn();
- fBWRgn.setRect(aaclip.getBounds());
- fAABlitter.init(blitter, &aaclip);
- // now our return values
- fClipRgn = &fBWRgn;
- fBlitter = &fAABlitter;
- }
- }
|