michael@0: /* michael@0: * Copyright 2010 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "SkRasterClip.h" michael@0: michael@0: michael@0: SkRasterClip::SkRasterClip() { michael@0: fIsBW = true; michael@0: fIsEmpty = true; michael@0: fIsRect = false; michael@0: SkDEBUGCODE(this->validate();) michael@0: } michael@0: michael@0: SkRasterClip::SkRasterClip(const SkRasterClip& src) { michael@0: AUTO_RASTERCLIP_VALIDATE(src); michael@0: michael@0: fIsBW = src.fIsBW; michael@0: if (fIsBW) { michael@0: fBW = src.fBW; michael@0: } else { michael@0: fAA = src.fAA; michael@0: } michael@0: michael@0: fIsEmpty = src.isEmpty(); michael@0: fIsRect = src.isRect(); michael@0: SkDEBUGCODE(this->validate();) michael@0: } michael@0: michael@0: SkRasterClip::SkRasterClip(const SkIRect& bounds) : fBW(bounds) { michael@0: fIsBW = true; michael@0: fIsEmpty = this->computeIsEmpty(); // bounds might be empty, so compute michael@0: fIsRect = !fIsEmpty; michael@0: SkDEBUGCODE(this->validate();) michael@0: } michael@0: michael@0: SkRasterClip::~SkRasterClip() { michael@0: SkDEBUGCODE(this->validate();) michael@0: } michael@0: michael@0: bool SkRasterClip::isComplex() const { michael@0: return fIsBW ? fBW.isComplex() : !fAA.isEmpty(); michael@0: } michael@0: michael@0: const SkIRect& SkRasterClip::getBounds() const { michael@0: return fIsBW ? fBW.getBounds() : fAA.getBounds(); michael@0: } michael@0: michael@0: bool SkRasterClip::setEmpty() { michael@0: AUTO_RASTERCLIP_VALIDATE(*this); michael@0: michael@0: fIsBW = true; michael@0: fBW.setEmpty(); michael@0: fAA.setEmpty(); michael@0: fIsEmpty = true; michael@0: fIsRect = false; michael@0: return false; michael@0: } michael@0: michael@0: bool SkRasterClip::setRect(const SkIRect& rect) { michael@0: AUTO_RASTERCLIP_VALIDATE(*this); michael@0: michael@0: fIsBW = true; michael@0: fAA.setEmpty(); michael@0: fIsRect = fBW.setRect(rect); michael@0: fIsEmpty = !fIsRect; michael@0: return fIsRect; michael@0: } michael@0: michael@0: bool SkRasterClip::setPath(const SkPath& path, const SkRegion& clip, bool doAA) { michael@0: AUTO_RASTERCLIP_VALIDATE(*this); michael@0: michael@0: if (this->isBW() && !doAA) { michael@0: (void)fBW.setPath(path, clip); michael@0: } else { michael@0: // TODO: since we are going to over-write fAA completely (aren't we?) michael@0: // we should just clear our BW data (if any) and set fIsAA=true michael@0: if (this->isBW()) { michael@0: this->convertToAA(); michael@0: } michael@0: (void)fAA.setPath(path, &clip, doAA); michael@0: } michael@0: return this->updateCacheAndReturnNonEmpty(); michael@0: } michael@0: michael@0: bool SkRasterClip::setPath(const SkPath& path, const SkIRect& clip, bool doAA) { michael@0: SkRegion tmp; michael@0: tmp.setRect(clip); michael@0: return this->setPath(path, tmp, doAA); michael@0: } michael@0: michael@0: bool SkRasterClip::op(const SkIRect& rect, SkRegion::Op op) { michael@0: AUTO_RASTERCLIP_VALIDATE(*this); michael@0: michael@0: fIsBW ? fBW.op(rect, op) : fAA.op(rect, op); michael@0: return this->updateCacheAndReturnNonEmpty(); michael@0: } michael@0: michael@0: bool SkRasterClip::op(const SkRegion& rgn, SkRegion::Op op) { michael@0: AUTO_RASTERCLIP_VALIDATE(*this); michael@0: michael@0: if (fIsBW) { michael@0: (void)fBW.op(rgn, op); michael@0: } else { michael@0: SkAAClip tmp; michael@0: tmp.setRegion(rgn); michael@0: (void)fAA.op(tmp, op); michael@0: } michael@0: return this->updateCacheAndReturnNonEmpty(); michael@0: } michael@0: michael@0: bool SkRasterClip::op(const SkRasterClip& clip, SkRegion::Op op) { michael@0: AUTO_RASTERCLIP_VALIDATE(*this); michael@0: clip.validate(); michael@0: michael@0: if (this->isBW() && clip.isBW()) { michael@0: (void)fBW.op(clip.fBW, op); michael@0: } else { michael@0: SkAAClip tmp; michael@0: const SkAAClip* other; michael@0: michael@0: if (this->isBW()) { michael@0: this->convertToAA(); michael@0: } michael@0: if (clip.isBW()) { michael@0: tmp.setRegion(clip.bwRgn()); michael@0: other = &tmp; michael@0: } else { michael@0: other = &clip.aaRgn(); michael@0: } michael@0: (void)fAA.op(*other, op); michael@0: } michael@0: return this->updateCacheAndReturnNonEmpty(); michael@0: } michael@0: michael@0: /** michael@0: * Our antialiasing currently has a granularity of 1/4 of a pixel along each michael@0: * axis. Thus we can treat an axis coordinate as an integer if it differs michael@0: * from its nearest int by < half of that value (1.8 in this case). michael@0: */ michael@0: static bool nearly_integral(SkScalar x) { michael@0: static const SkScalar domain = SK_Scalar1 / 4; michael@0: static const SkScalar halfDomain = domain / 2; michael@0: michael@0: x += halfDomain; michael@0: return x - SkScalarFloorToScalar(x) < domain; michael@0: } michael@0: michael@0: bool SkRasterClip::op(const SkRect& r, SkRegion::Op op, bool doAA) { michael@0: AUTO_RASTERCLIP_VALIDATE(*this); michael@0: michael@0: if (fIsBW && doAA) { michael@0: // check that the rect really needs aa, or is it close enought to michael@0: // integer boundaries that we can just treat it as a BW rect? michael@0: if (nearly_integral(r.fLeft) && nearly_integral(r.fTop) && michael@0: nearly_integral(r.fRight) && nearly_integral(r.fBottom)) { michael@0: doAA = false; michael@0: } michael@0: } michael@0: michael@0: if (fIsBW && !doAA) { michael@0: SkIRect ir; michael@0: r.round(&ir); michael@0: (void)fBW.op(ir, op); michael@0: } else { michael@0: if (fIsBW) { michael@0: this->convertToAA(); michael@0: } michael@0: (void)fAA.op(r, op, doAA); michael@0: } michael@0: return this->updateCacheAndReturnNonEmpty(); michael@0: } michael@0: michael@0: void SkRasterClip::translate(int dx, int dy, SkRasterClip* dst) const { michael@0: if (NULL == dst) { michael@0: return; michael@0: } michael@0: michael@0: AUTO_RASTERCLIP_VALIDATE(*this); michael@0: michael@0: if (this->isEmpty()) { michael@0: dst->setEmpty(); michael@0: return; michael@0: } michael@0: if (0 == (dx | dy)) { michael@0: *dst = *this; michael@0: return; michael@0: } michael@0: michael@0: dst->fIsBW = fIsBW; michael@0: if (fIsBW) { michael@0: fBW.translate(dx, dy, &dst->fBW); michael@0: dst->fAA.setEmpty(); michael@0: } else { michael@0: fAA.translate(dx, dy, &dst->fAA); michael@0: dst->fBW.setEmpty(); michael@0: } michael@0: dst->updateCacheAndReturnNonEmpty(); michael@0: } michael@0: michael@0: bool SkRasterClip::quickContains(const SkIRect& ir) const { michael@0: return fIsBW ? fBW.quickContains(ir) : fAA.quickContains(ir); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: const SkRegion& SkRasterClip::forceGetBW() { michael@0: AUTO_RASTERCLIP_VALIDATE(*this); michael@0: michael@0: if (!fIsBW) { michael@0: fBW.setRect(fAA.getBounds()); michael@0: } michael@0: return fBW; michael@0: } michael@0: michael@0: void SkRasterClip::convertToAA() { michael@0: AUTO_RASTERCLIP_VALIDATE(*this); michael@0: michael@0: SkASSERT(fIsBW); michael@0: fAA.setRegion(fBW); michael@0: fIsBW = false; michael@0: (void)this->updateCacheAndReturnNonEmpty(); michael@0: } michael@0: michael@0: #ifdef SK_DEBUG michael@0: void SkRasterClip::validate() const { michael@0: // can't ever assert that fBW is empty, since we may have called forceGetBW michael@0: if (fIsBW) { michael@0: SkASSERT(fAA.isEmpty()); michael@0: } michael@0: michael@0: fBW.validate(); michael@0: fAA.validate(); michael@0: michael@0: SkASSERT(this->computeIsEmpty() == fIsEmpty); michael@0: SkASSERT(this->computeIsRect() == fIsRect); michael@0: } michael@0: #endif michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: SkAAClipBlitterWrapper::SkAAClipBlitterWrapper() { michael@0: SkDEBUGCODE(fClipRgn = NULL;) michael@0: SkDEBUGCODE(fBlitter = NULL;) michael@0: } michael@0: michael@0: SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkRasterClip& clip, michael@0: SkBlitter* blitter) { michael@0: this->init(clip, blitter); michael@0: } michael@0: michael@0: SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkAAClip* aaclip, michael@0: SkBlitter* blitter) { michael@0: SkASSERT(blitter); michael@0: SkASSERT(aaclip); michael@0: fBWRgn.setRect(aaclip->getBounds()); michael@0: fAABlitter.init(blitter, aaclip); michael@0: // now our return values michael@0: fClipRgn = &fBWRgn; michael@0: fBlitter = &fAABlitter; michael@0: } michael@0: michael@0: void SkAAClipBlitterWrapper::init(const SkRasterClip& clip, SkBlitter* blitter) { michael@0: SkASSERT(blitter); michael@0: if (clip.isBW()) { michael@0: fClipRgn = &clip.bwRgn(); michael@0: fBlitter = blitter; michael@0: } else { michael@0: const SkAAClip& aaclip = clip.aaRgn(); michael@0: fBWRgn.setRect(aaclip.getBounds()); michael@0: fAABlitter.init(blitter, &aaclip); michael@0: // now our return values michael@0: fClipRgn = &fBWRgn; michael@0: fBlitter = &fAABlitter; michael@0: } michael@0: }