Reland "Add SkClipStack::replaceClip() separate from deprecated clip op"

This reverts commit 8ba1e71a1f.

Reason for revert: had missed a few places where GrReducedClip needed to
use the equivalent region op, not skClipOp + replace bool.

TBR=robertphillips@google.com,brianosman@google.com,csmartdalton@google.com

Original change's description:
> Revert "Add SkClipStack::replaceClip() separate from deprecated clip op"
>
> This reverts commit 68587ae274.
>
> Reason for revert: breaking path clipping tests in Android?
>
> Original change's description:
> > Add SkClipStack::replaceClip() separate from deprecated clip op
> >
> > The replaceClip functionality was added to allow Android to move off of
> > generalized expanding clips. At the time, SkClipStack simply used the
> > kReplace_SkClipOp to handle it. In order to remove those expanding ops,
> > SkClipStack will need a proper implementation of replaceClip().
> >
> > The clip elements have an additional field to mark if
> > it's a replace (and it's op will be kIntersect). Adds a temporary
> > getRegionOp() function to unify elements that use this field vs.
> > elements that use the deprecated clip op (i.e. if they were deserialized
> > from an SKP that recorded an expanding op).
> >
> > Clients of SkClipOp that checked for replace ops use the new function
> > instead of referring to the enum value directly.
> >
> > Bug: skia:10209
> > Change-Id: I1c16c87fadb2becfe181db717c05e240ac87fd34
> > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/436158
> > Commit-Queue: Michael Ludwig <michaelludwig@google.com>
> > Reviewed-by: Robert Phillips <robertphillips@google.com>
> > Reviewed-by: Chris Dalton <csmartdalton@google.com>
>
> TBR=robertphillips@google.com,brianosman@google.com,csmartdalton@google.com,michaelludwig@google.com,skcq-be@skia-corp.google.com.iam.gserviceaccount.com
>
> Change-Id: If3f99a7d2f2df99c2b99d431d494ca28da66b1d8
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Bug: skia:10209
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/436956
> Reviewed-by: Michael Ludwig <michaelludwig@google.com>

# Not skipping CQ checks because this is a reland.

Bug: skia:10209
Change-Id: I9feb0f3571ec26580bcdf0fe541f43f2ee8cf8d2
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/436959
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
Michael Ludwig 2021-08-05 16:39:18 -04:00 committed by SkCQ
parent 363c6e298f
commit d59d3e179d
9 changed files with 268 additions and 243 deletions

View File

@ -46,6 +46,7 @@ SkClipStack::Element::Element(const Element& that) {
fOp = that.fOp;
fDeviceSpaceType = that.fDeviceSpaceType;
fDoAA = that.fDoAA;
fIsReplace = that.fIsReplace;
fFiniteBoundType = that.fFiniteBoundType;
fFiniteBound = that.fFiniteBound;
fIsIntersectionOfRects = that.fIsIntersectionOfRects;
@ -66,7 +67,8 @@ bool SkClipStack::Element::operator== (const Element& element) const {
return true;
}
if (fOp != element.fOp || fDeviceSpaceType != element.fDeviceSpaceType ||
fDoAA != element.fDoAA || fSaveCount != element.fSaveCount) {
fDoAA != element.fDoAA || fIsReplace != element.fIsReplace ||
fSaveCount != element.fSaveCount) {
return false;
}
switch (fDeviceSpaceType) {
@ -173,6 +175,7 @@ void SkClipStack::Element::initCommon(int saveCount, SkClipOp op, bool doAA) {
fSaveCount = saveCount;
fOp = op;
fDoAA = doAA;
fIsReplace = false;
// A default of inside-out and empty bounds means the bounds are effectively void as it
// indicates that nothing is known to be outside the clip.
fFiniteBoundType = kInsideOut_BoundsType;
@ -249,6 +252,13 @@ void SkClipStack::Element::initShader(int saveCount, sk_sp<SkShader> shader) {
this->initCommon(saveCount, SkClipOp::kIntersect, false);
}
void SkClipStack::Element::initReplaceRect(int saveCount, const SkRect& rect, bool doAA) {
fDeviceSpaceRRect.setRect(rect);
fDeviceSpaceType = DeviceSpaceType::kRect;
this->initCommon(saveCount, SkClipOp::kIntersect, doAA);
fIsReplace = true;
}
void SkClipStack::Element::asDeviceSpacePath(SkPath* path) const {
switch (fDeviceSpaceType) {
case DeviceSpaceType::kEmpty:
@ -297,14 +307,14 @@ void SkClipStack::Element::checkEmpty() const {
bool SkClipStack::Element::canBeIntersectedInPlace(int saveCount, SkClipOp op) const {
if (DeviceSpaceType::kEmpty == fDeviceSpaceType &&
(kDifference_SkClipOp == op || kIntersect_SkClipOp == op)) {
(SkClipOp::kDifference == op || SkClipOp::kIntersect == op)) {
return true;
}
// Only clips within the same save/restore frame (as captured by
// the save count) can be merged
return fSaveCount == saveCount &&
kIntersect_SkClipOp == op &&
(kIntersect_SkClipOp == fOp || kReplace_SkClipOp == fOp);
SkClipOp::kIntersect == op &&
(SkClipOp::kIntersect == fOp || this->isReplaceOp());
}
bool SkClipStack::Element::rectRectIntersectAllowed(const SkRect& newR, bool newAA) const {
@ -518,8 +528,9 @@ void SkClipStack::Element::updateBoundAndGenID(const Element* prior) {
fFiniteBound = this->getDeviceSpaceRect();
fFiniteBoundType = kNormal_BoundsType;
if (kReplace_SkClipOp == fOp || (kIntersect_SkClipOp == fOp && nullptr == prior) ||
(kIntersect_SkClipOp == fOp && prior->fIsIntersectionOfRects &&
if (this->isReplaceOp() ||
(SkClipOp::kIntersect == fOp && nullptr == prior) ||
(SkClipOp::kIntersect == fOp && prior->fIsIntersectionOfRects &&
prior->rectRectIntersectAllowed(this->getDeviceSpaceRect(), fDoAA))) {
fIsIntersectionOfRects = true;
}
@ -578,32 +589,29 @@ void SkClipStack::Element::updateBoundAndGenID(const Element* prior) {
kPrev_Cur_FillCombo == combination);
// Now integrate with clip with the prior clips
switch (fOp) {
case kDifference_SkClipOp:
this->combineBoundsDiff(combination, prevFinite);
break;
case kXOR_SkClipOp:
this->combineBoundsXOR(combination, prevFinite);
break;
case kUnion_SkClipOp:
this->combineBoundsUnion(combination, prevFinite);
break;
case kIntersect_SkClipOp:
this->combineBoundsIntersection(combination, prevFinite);
break;
case kReverseDifference_SkClipOp:
this->combineBoundsRevDiff(combination, prevFinite);
break;
case kReplace_SkClipOp:
// Replace just ignores everything prior
// The current clip's bound information is already filled in
// so nothing to do
break;
default:
SkDebugf("SkClipOp error\n");
SkASSERT(0);
break;
}
if (!this->isReplaceOp()) {
switch (fOp) {
case SkClipOp::kDifference:
this->combineBoundsDiff(combination, prevFinite);
break;
case kXOR_SkClipOp:
this->combineBoundsXOR(combination, prevFinite);
break;
case kUnion_SkClipOp:
this->combineBoundsUnion(combination, prevFinite);
break;
case SkClipOp::kIntersect:
this->combineBoundsIntersection(combination, prevFinite);
break;
case kReverseDifference_SkClipOp:
this->combineBoundsRevDiff(combination, prevFinite);
break;
default:
SkDebugf("SkClipOp error\n");
SkASSERT(0);
break;
}
} // else Replace just ignores everything prior and should already have filled in bounds.
}
// This constant determines how many Element's are allocated together as a block in
@ -742,12 +750,13 @@ void SkClipStack::getBounds(SkRect* canvFiniteBound,
}
bool SkClipStack::internalQuickContains(const SkRect& rect) const {
Iter iter(*this, Iter::kTop_IterStart);
const Element* element = iter.prev();
while (element != nullptr) {
if (kIntersect_SkClipOp != element->getOp() && kReplace_SkClipOp != element->getOp())
// TODO: Once expanding ops are removed, this condition is equiv. to op == kDifference.
if (SkClipOp::kIntersect != element->getOp() && !element->isReplaceOp()) {
return false;
}
if (element->isInverseFilled()) {
// Part of 'rect' could be trimmed off by the inverse-filled clip element
if (SkRect::Intersects(element->getBounds(), rect)) {
@ -758,7 +767,7 @@ bool SkClipStack::internalQuickContains(const SkRect& rect) const {
return false;
}
}
if (kReplace_SkClipOp == element->getOp()) {
if (element->isReplaceOp()) {
break;
}
element = iter.prev();
@ -767,12 +776,13 @@ bool SkClipStack::internalQuickContains(const SkRect& rect) const {
}
bool SkClipStack::internalQuickContains(const SkRRect& rrect) const {
Iter iter(*this, Iter::kTop_IterStart);
const Element* element = iter.prev();
while (element != nullptr) {
if (kIntersect_SkClipOp != element->getOp() && kReplace_SkClipOp != element->getOp())
// TODO: Once expanding ops are removed, this condition is equiv. to op == kDifference.
if (SkClipOp::kIntersect != element->getOp() && !element->isReplaceOp()) {
return false;
}
if (element->isInverseFilled()) {
// Part of 'rrect' could be trimmed off by the inverse-filled clip element
if (SkRect::Intersects(element->getBounds(), rrect.getBounds())) {
@ -783,7 +793,7 @@ bool SkClipStack::internalQuickContains(const SkRRect& rrect) const {
return false;
}
}
if (kReplace_SkClipOp == element->getOp()) {
if (element->isReplaceOp()) {
break;
}
element = iter.prev();
@ -797,7 +807,10 @@ void SkClipStack::pushElement(const Element& element) {
Element* prior = (Element*) iter.prev();
if (prior) {
if (prior->canBeIntersectedInPlace(fSaveCount, element.getOp())) {
if (element.isReplaceOp()) {
this->restoreTo(fSaveCount - 1);
prior = (Element*) fDeque.back();
} else if (prior->canBeIntersectedInPlace(fSaveCount, element.getOp())) {
switch (prior->fDeviceSpaceType) {
case Element::DeviceSpaceType::kEmpty:
SkDEBUGCODE(prior->checkEmpty();)
@ -838,32 +851,27 @@ void SkClipStack::pushElement(const Element& element) {
}
break;
}
} else if (kReplace_SkClipOp == element.getOp()) {
this->restoreTo(fSaveCount - 1);
prior = (Element*) fDeque.back();
}
}
Element* newElement = new (fDeque.push_back()) Element(element);
newElement->updateBoundAndGenID(prior);
}
void SkClipStack::clipRRect(const SkRRect& rrect, const SkMatrix& matrix, SkClipOp op,
bool doAA) {
void SkClipStack::clipRRect(const SkRRect& rrect, const SkMatrix& matrix, SkClipOp op, bool doAA) {
Element element(fSaveCount, rrect, matrix, op, doAA);
this->pushElement(element);
if (this->hasClipRestriction(op)) {
Element restriction(fSaveCount, fClipRestrictionRect, SkMatrix::I(), kIntersect_SkClipOp,
Element restriction(fSaveCount, fClipRestrictionRect, SkMatrix::I(), SkClipOp::kIntersect,
false);
this->pushElement(restriction);
}
}
void SkClipStack::clipRect(const SkRect& rect, const SkMatrix& matrix, SkClipOp op,
bool doAA) {
void SkClipStack::clipRect(const SkRect& rect, const SkMatrix& matrix, SkClipOp op, bool doAA) {
Element element(fSaveCount, rect, matrix, op, doAA);
this->pushElement(element);
if (this->hasClipRestriction(op)) {
Element restriction(fSaveCount, fClipRestrictionRect, SkMatrix::I(), kIntersect_SkClipOp,
Element restriction(fSaveCount, fClipRestrictionRect, SkMatrix::I(), SkClipOp::kIntersect,
false);
this->pushElement(restriction);
}
@ -874,7 +882,7 @@ void SkClipStack::clipPath(const SkPath& path, const SkMatrix& matrix, SkClipOp
Element element(fSaveCount, path, matrix, op, doAA);
this->pushElement(element);
if (this->hasClipRestriction(op)) {
Element restriction(fSaveCount, fClipRestrictionRect, SkMatrix::I(), kIntersect_SkClipOp,
Element restriction(fSaveCount, fClipRestrictionRect, SkMatrix::I(), SkClipOp::kIntersect,
false);
this->pushElement(restriction);
}
@ -888,10 +896,21 @@ void SkClipStack::clipShader(sk_sp<SkShader> shader) {
SkASSERT(fClipRestrictionRect.isEmpty());
}
void SkClipStack::replaceClip(const SkRect& rect, bool doAA) {
Element element(fSaveCount, rect, doAA);
this->pushElement(element);
// A replace is always affected by the clip restriction
if (!fClipRestrictionRect.isEmpty()) {
Element restriction(fSaveCount, fClipRestrictionRect, SkMatrix::I(), SkClipOp::kIntersect,
false);
this->pushElement(restriction);
}
}
void SkClipStack::clipEmpty() {
Element* element = (Element*) fDeque.back();
if (element && element->canBeIntersectedInPlace(fSaveCount, kIntersect_SkClipOp)) {
if (element && element->canBeIntersectedInPlace(fSaveCount, SkClipOp::kIntersect)) {
element->setEmpty();
}
new (fDeque.push_back()) Element(fSaveCount);
@ -918,7 +937,6 @@ const SkClipStack::Element* SkClipStack::Iter::prev() {
}
const SkClipStack::Element* SkClipStack::Iter::skipToTopmost(SkClipOp op) {
if (nullptr == fStack) {
return nullptr;
}
@ -1007,13 +1025,13 @@ bool SkClipStack::isRRect(const SkRect& bounds, SkRRect* rrect, bool* aa) const
back->getDeviceSpaceType() != SkClipStack::Element::DeviceSpaceType::kRRect) {
return false;
}
if (back->getOp() == kReplace_SkClipOp) {
if (back->isReplaceOp()) {
*rrect = back->asDeviceSpaceRRect();
*aa = back->isAA();
return true;
}
if (back->getOp() == kIntersect_SkClipOp) {
if (back->getOp() == SkClipOp::kIntersect) {
SkRect backBounds;
if (!backBounds.intersect(bounds, back->asDeviceSpaceRRect().rect())) {
return false;
@ -1028,12 +1046,12 @@ bool SkClipStack::isRRect(const SkRect& bounds, SkRRect* rrect, bool* aa) const
SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
SkAssertResult(static_cast<const Element*>(iter.prev()) == back);
while (const Element* prior = (const Element*)iter.prev()) {
if ((prior->getOp() != kIntersect_SkClipOp &&
prior->getOp() != kReplace_SkClipOp) ||
// TODO: Once expanding clip ops are removed, this is equiv. to op == kDifference
if ((prior->getOp() != SkClipOp::kIntersect && !prior->isReplaceOp()) ||
!prior->contains(backBounds)) {
return false;
}
if (prior->getOp() == kReplace_SkClipOp) {
if (prior->isReplaceOp()) {
break;
}
}
@ -1103,8 +1121,9 @@ void SkClipStack::Element::dump() const {
static_assert(5 == static_cast<int>(kReplace_SkClipOp), "enum mismatch");
static_assert(SK_ARRAY_COUNT(kOpStrings) == SkRegion::kOpCnt, "enum mismatch");
const char* opName = this->isReplaceOp() ? "replace" : kOpStrings[static_cast<int>(fOp)];
SkDebugf("Type: %s, Op: %s, AA: %s, Save Count: %d\n", kTypeStrings[(int)fDeviceSpaceType],
kOpStrings[static_cast<int>(fOp)], (fDoAA ? "yes" : "no"), fSaveCount);
opName, (fDoAA ? "yes" : "no"), fSaveCount);
switch (fDeviceSpaceType) {
case DeviceSpaceType::kEmpty:
SkDebugf("\n");

View File

@ -68,7 +68,7 @@ public:
static const int kTypeCnt = (int)DeviceSpaceType::kLastType + 1;
Element() {
this->initCommon(0, kReplace_SkClipOp, false);
this->initCommon(0, SkClipOp::kIntersect, false);
this->setEmpty();
}
@ -90,6 +90,10 @@ public:
this->initShader(0, std::move(shader));
}
Element(const SkRect& rect, bool doAA) {
this->initReplaceRect(0, rect, doAA);
}
~Element();
bool operator== (const Element& element) const;
@ -130,7 +134,13 @@ public:
//!< Call if getDeviceSpaceType() is not kEmpty to get the set operation used to combine
//!< this element.
SkClipOp getOp() const { return fOp; }
SkClipOp getOp() const { return fOp == kReplace_SkClipOp ? SkClipOp::kIntersect : fOp; }
// Augments getOps()'s behavior by requiring a clip reset before the op is applied.
bool isReplaceOp() const { return fIsReplace || fOp == kReplace_SkClipOp; }
SkRegion::Op getRegionOp() const {
return this->isReplaceOp() ? SkRegion::kReplace_Op : (SkRegion::Op) fOp;
}
//!< Call to get the element as a path, regardless of its type.
void asDeviceSpacePath(SkPath* path) const;
@ -149,7 +159,16 @@ public:
void invertShapeFillType();
//!< Sets the set operation represented by the element.
void setOp(SkClipOp op) { fOp = op; }
void setOp(SkClipOp op) {
fOp = op;
fIsReplace = false;
}
// TODO: This is only used by GrReducedClip, which will be deletable soon, so then this
// can also be cleaned up.
void setReplaceOp() {
fOp = SkClipOp::kIntersect;
fIsReplace = true;
}
/** The GenID can be used by clip stack clients to cache representations of the clip. The
ID corresponds to the set of clip elements up to and including this element within the
@ -215,6 +234,7 @@ public:
SkClipOp fOp;
DeviceSpaceType fDeviceSpaceType;
bool fDoAA;
bool fIsReplace;
/* fFiniteBoundType and fFiniteBound are used to incrementally update the clip stack's
bound. When fFiniteBoundType is kNormal_BoundsType, fFiniteBound represents the
@ -239,7 +259,7 @@ public:
mutable SkTArray<GrUniqueKey> fKeysToInvalidate;
#endif
Element(int saveCount) {
this->initCommon(saveCount, kReplace_SkClipOp, false);
this->initCommon(saveCount, SkClipOp::kIntersect, false);
this->setEmpty();
}
@ -259,12 +279,17 @@ public:
this->initShader(saveCount, std::move(shader));
}
Element(int saveCount, const SkRect& rect, bool doAA) {
this->initReplaceRect(saveCount, rect, doAA);
}
void initCommon(int saveCount, SkClipOp op, bool doAA);
void initRect(int saveCount, const SkRect&, const SkMatrix&, SkClipOp, bool doAA);
void initRRect(int saveCount, const SkRRect&, const SkMatrix&, SkClipOp, bool doAA);
void initPath(int saveCount, const SkPath&, const SkMatrix&, SkClipOp, bool doAA);
void initAsPath(int saveCount, const SkPath&, const SkMatrix&, SkClipOp, bool doAA);
void initShader(int saveCount, sk_sp<SkShader>);
void initReplaceRect(int saveCount, const SkRect&, bool doAA);
void setEmpty();
@ -373,6 +398,8 @@ public:
fClipRestrictionRect = SkRect::Make(rect);
}
void replaceClip(const SkRect& devRect, bool doAA);
/**
* isWideOpen returns true if the clip state corresponds to the infinite
* plane (i.e., draws are not limited at all)

View File

@ -53,10 +53,8 @@ void SkClipStackDevice::onClipRegion(const SkRegion& rgn, SkClipOp op) {
}
void SkClipStackDevice::onReplaceClip(const SkIRect& rect) {
// FIXME When the deprecated clip ops are completely removed, SkClipStack will need to be
// updated to have a better way of tracking replacement.
fClipStack.clipRect(SkRect::Make(rect), this->globalToDevice().asM33(),
kReplace_SkClipOp, false);
SkRect deviceRect = SkMatrixPriv::MapRect(this->globalToDevice(), SkRect::Make(rect));
fClipStack.replaceClip(deviceRect, /*doAA=*/false);
}
void SkClipStackDevice::onSetDeviceClipRestriction(SkIRect* clipRestriction) {
@ -104,7 +102,7 @@ void SkClipStackDevice::onAsRgnClip(SkRegion* rgn) const {
elem->asDeviceSpacePath(&tmpPath);
SkRegion tmpRgn;
tmpRgn.setPath(tmpPath, boundsRgn);
rgn->op(tmpRgn, SkRegion::Op(elem->getOp()));
rgn->op(tmpRgn, elem->getRegionOp());
}
}
}

View File

@ -335,15 +335,15 @@ static void draw_clip_elements_to_mask_helper(GrSWMaskHelper& helper, const Elem
for (ElementList::Iter iter(elements); iter.get(); iter.next()) {
const Element* element = iter.get();
SkClipOp op = element->getOp();
SkRegion::Op op = element->getRegionOp();
GrAA aa = GrAA(element->isAA());
if (kIntersect_SkClipOp == op || kReverseDifference_SkClipOp == op) {
if (SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
// Intersect and reverse difference require modifying pixels outside of the geometry
// that is being "drawn". In both cases we erase all the pixels outside of the geometry
// but leave the pixels inside the geometry alone. For reverse difference we invert all
// the pixels before clearing the ones outside the geometry.
if (kReverseDifference_SkClipOp == op) {
if (SkRegion::kReverseDifference_Op == op) {
SkRect temp = SkRect::Make(scissor);
// invert the entire scene
helper.drawRect(temp, translate, SkRegion::kXOR_Op, GrAA::kNo, 0xFF);
@ -358,13 +358,13 @@ static void draw_clip_elements_to_mask_helper(GrSWMaskHelper& helper, const Elem
// The other ops (union, xor, diff) only affect pixels inside
// the geometry so they can just be drawn normally
if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) {
helper.drawRect(element->getDeviceSpaceRect(), translate, (SkRegion::Op)op, aa, 0xFF);
helper.drawRect(element->getDeviceSpaceRect(), translate, op, aa, 0xFF);
} else if (Element::DeviceSpaceType::kRRect == element->getDeviceSpaceType()) {
helper.drawRRect(element->getDeviceSpaceRRect(), translate, (SkRegion::Op)op, aa, 0xFF);
helper.drawRRect(element->getDeviceSpaceRRect(), translate, op, aa, 0xFF);
} else {
SkPath path;
element->asDeviceSpacePath(&path);
helper.drawShape(GrShape(path), translate, (SkRegion::Op)op, aa, 0xFF);
helper.drawShape(GrShape(path), translate, op, aa, 0xFF);
}
}
}

View File

@ -122,10 +122,11 @@ GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds
ClipResult::kNotClipped == this->addAnalyticRect(fAAClipRect, Invert::kNo, GrAA::kYes)) {
if (fMaskElements.isEmpty()) {
// Use a replace since it is faster than intersect.
fMaskElements.addToHead(fAAClipRect, SkMatrix::I(), kReplace_SkClipOp, true /*doAA*/);
fMaskElements.addToHead(fAAClipRect, true /*doAA*/);
fInitialState = InitialState::kAllOut;
} else {
fMaskElements.addToTail(fAAClipRect, SkMatrix::I(), kIntersect_SkClipOp, true /*doAA*/);
fMaskElements.addToTail(fAAClipRect, SkMatrix::I(), SkClipOp::kIntersect,
true /*doAA*/);
}
fMaskRequiresAA = true;
fMaskGenID = fAAClipRectGenID;
@ -189,8 +190,8 @@ void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBound
bool skippable = false;
bool isFlip = false; // does this op just flip the in/out state of every point in the bounds
switch (element->getOp()) {
case kDifference_SkClipOp:
switch (element->getRegionOp()) {
case SkRegion::kDifference_Op:
// check if the shape subtracted either contains the entire bounds (and makes
// the clip empty) or is outside the bounds and therefore can be skipped.
if (element->isInverseFilled()) {
@ -224,7 +225,7 @@ void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBound
emsmallens = true;
}
break;
case kIntersect_SkClipOp:
case SkRegion::kIntersect_Op:
// check if the shape intersected contains the entire bounds and therefore can
// be skipped or it is outside the entire bounds and therefore makes the clip
// empty.
@ -259,7 +260,7 @@ void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBound
emsmallens = true;
}
break;
case kUnion_SkClipOp:
case SkRegion::kUnion_Op:
// If the union-ed shape contains the entire bounds then after this element
// the bounds is entirely inside the clip. If the union-ed shape is outside the
// bounds then this op can be skipped.
@ -282,7 +283,7 @@ void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBound
embiggens = true;
}
break;
case kXOR_SkClipOp:
case SkRegion::kXOR_Op:
// If the bounds is entirely inside the shape being xor-ed then the effect is
// to flip the inside/outside state of every point in the bounds. We may be
// able to take advantage of this in the forward pass. If the xor-ed shape
@ -304,7 +305,7 @@ void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBound
emsmallens = embiggens = true;
}
break;
case kReverseDifference_SkClipOp:
case SkRegion::kReverseDifference_Op:
// When the bounds is entirely within the rev-diff shape then this behaves like xor
// and reverses every point inside the bounds. If the shape is completely outside
// the bounds then we know after this element is applied that the bounds will be
@ -329,7 +330,7 @@ void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBound
}
break;
case kReplace_SkClipOp:
case SkRegion::kReplace_Op:
// Replace will always terminate our walk. We will either begin the forward walk
// at the replace op or detect here than the shape is either completely inside
// or completely outside the bounds. In this latter case it can be skipped by
@ -398,11 +399,11 @@ void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBound
// Intersecting an inverse shape is the same as differencing the non-inverse shape.
// Replacing with an inverse shape is the same as setting initialState=kAllIn and
// differencing the non-inverse shape.
bool isReplace = kReplace_SkClipOp == newElement->getOp();
bool isReplace = newElement->isReplaceOp();
if (newElement->isInverseFilled() &&
(kIntersect_SkClipOp == newElement->getOp() || isReplace)) {
(SkClipOp::kIntersect == newElement->getOp() || isReplace)) {
newElement->invertShapeFillType();
newElement->setOp(kDifference_SkClipOp);
newElement->setOp(SkClipOp::kDifference);
if (isReplace) {
SkASSERT(InitialTriState::kAllOut == initialTriState);
initialTriState = InitialTriState::kAllIn;
@ -420,37 +421,37 @@ void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBound
Element* element = fMaskElements.headIter().get();
while (element) {
bool skippable = false;
switch (element->getOp()) {
case kDifference_SkClipOp:
switch (element->getRegionOp()) {
case SkRegion::kDifference_Op:
// subtracting from the empty set yields the empty set.
skippable = InitialTriState::kAllOut == initialTriState;
break;
case kIntersect_SkClipOp:
case SkRegion::kIntersect_Op:
// intersecting with the empty set yields the empty set
if (InitialTriState::kAllOut == initialTriState) {
skippable = true;
} else {
// We can clear to zero and then simply draw the clip element.
initialTriState = InitialTriState::kAllOut;
element->setOp(kReplace_SkClipOp);
element->setReplaceOp();
}
break;
case kUnion_SkClipOp:
case SkRegion::kUnion_Op:
if (InitialTriState::kAllIn == initialTriState) {
// unioning the infinite plane with anything is a no-op.
skippable = true;
} else {
// unioning the empty set with a shape is the shape.
element->setOp(kReplace_SkClipOp);
element->setReplaceOp();
}
break;
case kXOR_SkClipOp:
case SkRegion::kXOR_Op:
if (InitialTriState::kAllOut == initialTriState) {
// xor could be changed to diff in the kAllIn case, not sure it's a win.
element->setOp(kReplace_SkClipOp);
element->setReplaceOp();
}
break;
case kReverseDifference_SkClipOp:
case SkRegion::kReverseDifference_Op:
if (InitialTriState::kAllIn == initialTriState) {
// subtracting the whole plane will yield the empty set.
skippable = true;
@ -463,11 +464,11 @@ void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBound
if (skippable) {
initialTriState = InitialTriState::kAllIn;
} else {
element->setOp(kReplace_SkClipOp);
element->setReplaceOp();
}
}
break;
case kReplace_SkClipOp:
case SkRegion::kReplace_Op:
skippable = false; // we would have skipped it in the backwards walk if we
// could've.
break;
@ -720,7 +721,7 @@ static bool stencil_element(skgpu::v1::SurfaceDrawContext* sdc,
break;
case SkClipStack::Element::DeviceSpaceType::kRect: {
GrPaint paint;
paint.setCoverageSetOpXPFactory((SkRegion::Op)element->getOp(),
paint.setCoverageSetOpXPFactory(element->getRegionOp(),
element->isInverseFilled());
sdc->stencilRect(&clip, ss, std::move(paint), aa, viewMatrix,
element->getDeviceSpaceRect());
@ -733,7 +734,7 @@ static bool stencil_element(skgpu::v1::SurfaceDrawContext* sdc,
path.toggleInverseFillType();
}
return sdc->drawAndStencilPath(&clip, ss, (SkRegion::Op)element->getOp(),
return sdc->drawAndStencilPath(&clip, ss, element->getRegionOp(),
element->isInverseFilled(), aa, viewMatrix, path);
}
}
@ -799,7 +800,7 @@ bool GrReducedClip::drawAlphaClipMask(skgpu::v1::SurfaceDrawContext* sdc) const
// walk through each clip element and perform its set op
for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) {
const Element* element = iter.get();
SkRegion::Op op = (SkRegion::Op)element->getOp();
SkRegion::Op op = element->getRegionOp();
GrAA aa = GrAA(element->isAA());
bool invert = element->isInverseFilled();
if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
@ -861,7 +862,7 @@ bool GrReducedClip::drawStencilClipMask(GrRecordingContext* rContext,
// walk through each clip element and perform its set op with the existing clip.
for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) {
const Element* element = iter.get();
SkRegion::Op op = (SkRegion::Op)element->getOp();
SkRegion::Op op = element->getRegionOp();
GrAA aa = element->isAA() ? GrAA::kYes : GrAA::kNo;
if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) {

View File

@ -39,30 +39,26 @@ static bool is_rect(const SkClipStack& clipStack, const SkRect& bounds, SkRect*
default:
return false;
}
switch (element->getOp()) {
case kReplace_SkClipOp:
currentClip = rect_intersect(bounds, elementRect);
break;
case SkClipOp::kIntersect:
currentClip = rect_intersect(currentClip, elementRect);
break;
default:
return false;
if (element->isReplaceOp()) {
currentClip = rect_intersect(bounds, elementRect);
} else if (element->getOp() == SkClipOp::kIntersect) {
currentClip = rect_intersect(currentClip, elementRect);
} else {
return false;
}
}
*dst = currentClip;
return true;
}
// TODO: When there's no expanding clip ops, this function may not be necessary anymore.
static bool is_complex_clip(const SkClipStack& stack) {
SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
while (const SkClipStack::Element* element = iter.next()) {
switch (element->getOp()) {
case SkClipOp::kDifference:
case SkClipOp::kIntersect:
break;
default:
return true;
if (element->isReplaceOp() ||
(element->getOp() != SkClipOp::kDifference &&
element->getOp() != SkClipOp::kIntersect)) {
return true;
}
}
return false;
@ -242,4 +238,3 @@ void SkPDFGraphicStackState::drainStack() {
}
SkASSERT(fStackDepth == 0);
}

View File

@ -25,8 +25,10 @@ void SkClipStack_AsPath(const SkClipStack& cs, SkPath* path) {
}
SkClipOp elementOp = element->getOp();
if (elementOp == kReplace_SkClipOp) {
if (element->isReplaceOp()) {
*path = operand;
// TODO: Once expanding clip ops are removed, we can switch the iterator to be top
// to bottom, which allows us to break here on encountering a replace op.
} else {
Op(*path, operand, (SkPathOp)elementOp, path);
}

View File

@ -12,7 +12,6 @@
#include "include/core/SkMatrix.h"
#include "include/core/SkRect.h"
#include "src/core/SkClipOpPriv.h"
#include "src/core/SkClipStack.h"
#include "src/gpu/GrClipStackClip.h"
#include "tests/Test.h"
@ -32,7 +31,7 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrClipBounds, reporter, ctxInfo) {
// create a clip stack that will (trivially) reduce to a single rect that
// is larger than the screen
SkClipStack stack;
stack.clipRect(clipRect, SkMatrix::I(), kReplace_SkClipOp, false);
stack.replaceRect(clipRect);
bool isIntersectionOfRects = true;
SkRect devStackBounds;

View File

@ -58,15 +58,15 @@ static void test_assign_and_comparison(skiatest::Reporter* reporter) {
p.lineTo(7, 8);
p.lineTo(5, 9);
p.close();
s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
s.clipPath(p, SkMatrix::I(), SkClipOp::kIntersect, doAA);
s.save();
REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
SkRect r = SkRect::MakeLTRB(1, 2, 3, 4);
s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
s.clipRect(r, SkMatrix::I(), SkClipOp::kIntersect, doAA);
r = SkRect::MakeLTRB(10, 11, 12, 13);
s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
s.clipRect(r, SkMatrix::I(), SkClipOp::kIntersect, doAA);
s.save();
REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
@ -96,7 +96,7 @@ static void test_assign_and_comparison(skiatest::Reporter* reporter) {
s.save();
REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
r = SkRect::MakeLTRB(14, 15, 16, 17);
s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
s.clipRect(r, SkMatrix::I(), SkClipOp::kIntersect, doAA);
REPORTER_ASSERT(reporter, s != copy);
// Test that version constructed with rect-path rather than a rect is still considered equal.
@ -136,7 +136,7 @@ static void test_assign_and_comparison(skiatest::Reporter* reporter) {
REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
p.addRect(r);
s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
s.clipPath(p, SkMatrix::I(), SkClipOp::kIntersect, doAA);
REPORTER_ASSERT(reporter, s != copy);
}
@ -249,8 +249,8 @@ static void test_bounds(skiatest::Reporter* reporter,
};
static const SkClipOp gOps[] = {
kIntersect_SkClipOp,
kDifference_SkClipOp,
SkClipOp::kIntersect,
SkClipOp::kDifference,
kUnion_SkClipOp,
kXOR_SkClipOp,
kReverseDifference_SkClipOp
@ -294,15 +294,15 @@ static void test_bounds(skiatest::Reporter* reporter,
SkDEBUGFAIL("Don't call this with kEmpty or kShader.");
break;
case SkClipStack::Element::DeviceSpaceType::kRect:
stack.clipRect(rectA, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipRect(rectA, SkMatrix::I(), SkClipOp::kIntersect, false);
stack.clipRect(rectB, SkMatrix::I(), gOps[op], false);
break;
case SkClipStack::Element::DeviceSpaceType::kRRect:
stack.clipRRect(rrectA, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipRRect(rrectA, SkMatrix::I(), SkClipOp::kIntersect, false);
stack.clipRRect(rrectB, SkMatrix::I(), gOps[op], false);
break;
case SkClipStack::Element::DeviceSpaceType::kPath:
stack.clipPath(pathA, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipPath(pathA, SkMatrix::I(), SkClipOp::kIntersect, false);
stack.clipPath(pathB, SkMatrix::I(), gOps[op], false);
break;
}
@ -315,7 +315,7 @@ static void test_bounds(skiatest::Reporter* reporter,
if (SkClipStack::Element::DeviceSpaceType::kRect == primType) {
REPORTER_ASSERT(reporter, isIntersectionOfRects ==
(gOps[op] == kIntersect_SkClipOp));
(gOps[op] == SkClipOp::kIntersect));
} else {
REPORTER_ASSERT(reporter, !isIntersectionOfRects);
}
@ -363,7 +363,7 @@ static void test_isWideOpen(skiatest::Reporter* reporter) {
clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
clipB.setFillType(SkPathFillType::kInverseEvenOdd);
stack.clipPath(clipA, SkMatrix::I(), kReplace_SkClipOp, false);
stack.clipPath(clipA, SkMatrix::I(), SkClipOp::kIntersect, false);
stack.clipPath(clipB, SkMatrix::I(), kUnion_SkClipOp, false);
REPORTER_ASSERT(reporter, stack.isWideOpen());
@ -387,7 +387,7 @@ static void test_isWideOpen(skiatest::Reporter* reporter) {
SkRect emptyRect;
emptyRect.setEmpty();
stack.clipRect(emptyRect, SkMatrix::I(), kDifference_SkClipOp, false);
stack.clipRect(emptyRect, SkMatrix::I(), SkClipOp::kDifference, false);
REPORTER_ASSERT(reporter, stack.isWideOpen());
REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
@ -399,7 +399,7 @@ static void test_isWideOpen(skiatest::Reporter* reporter) {
stack.save();
stack.clipRect(rectA, SkMatrix::I(), kReplace_SkClipOp, false);
stack.clipRect(rectA, SkMatrix::I(), SkClipOp::kIntersect, false);
REPORTER_ASSERT(reporter, !stack.isWideOpen());
REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
@ -432,7 +432,7 @@ static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
path.addRect(rect);
path.toggleInverseFillType();
SkClipStack stack;
stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
SkRect bounds;
SkClipStack::BoundsType boundsType;
@ -454,9 +454,9 @@ static void test_rect_replace(skiatest::Reporter* reporter) {
{
SkClipStack stack;
REPORTER_ASSERT(reporter, 0 == count(stack));
stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
stack.replaceClip(rect, false);
REPORTER_ASSERT(reporter, 1 == count(stack));
stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
stack.replaceClip(rect, false);
REPORTER_ASSERT(reporter, 1 == count(stack));
}
@ -465,9 +465,9 @@ static void test_rect_replace(skiatest::Reporter* reporter) {
{
SkClipStack stack;
REPORTER_ASSERT(reporter, 0 == count(stack));
stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
stack.replaceClip(rect, true);
REPORTER_ASSERT(reporter, 1 == count(stack));
stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
stack.replaceClip(rect, true);
REPORTER_ASSERT(reporter, 1 == count(stack));
}
@ -476,23 +476,23 @@ static void test_rect_replace(skiatest::Reporter* reporter) {
{
SkClipStack stack;
REPORTER_ASSERT(reporter, 0 == count(stack));
stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
stack.replaceClip(rect, false);
REPORTER_ASSERT(reporter, 1 == count(stack));
stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
stack.replaceClip(rect, true);
REPORTER_ASSERT(reporter, 1 == count(stack));
stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
stack.replaceClip(rect, false);
REPORTER_ASSERT(reporter, 1 == count(stack));
}
// Make sure replace clip rects don't collapse too much.
{
SkClipStack stack;
stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.replaceClip(rect, false);
stack.clipRect(rect2, SkMatrix::I(), SkClipOp::kIntersect, false);
REPORTER_ASSERT(reporter, 1 == count(stack));
stack.save();
stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
stack.replaceClip(rect, false);
REPORTER_ASSERT(reporter, 2 == count(stack));
stack.getBounds(&bound, &type, &isIntersectionOfRects);
REPORTER_ASSERT(reporter, bound == rect);
@ -500,16 +500,16 @@ static void test_rect_replace(skiatest::Reporter* reporter) {
REPORTER_ASSERT(reporter, 1 == count(stack));
stack.save();
stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
stack.replaceClip(rect, false);
stack.replaceClip(rect, false);
REPORTER_ASSERT(reporter, 2 == count(stack));
stack.restore();
REPORTER_ASSERT(reporter, 1 == count(stack));
stack.save();
stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
stack.replaceClip(rect, false);
stack.clipRect(rect2, SkMatrix::I(), SkClipOp::kIntersect, false);
stack.replaceClip(rect, false);
REPORTER_ASSERT(reporter, 2 == count(stack));
stack.restore();
REPORTER_ASSERT(reporter, 1 == count(stack));
@ -518,27 +518,35 @@ static void test_rect_replace(skiatest::Reporter* reporter) {
// Simplified path-based version of test_rect_replace.
static void test_path_replace(skiatest::Reporter* reporter) {
auto replacePath = [](SkClipStack* stack, const SkPath& path, bool doAA) {
const SkRect wideOpen = SkRect::MakeLTRB(-1000, -1000, 1000, 1000);
stack->replaceClip(wideOpen, false);
stack->clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, doAA);
};
SkRect rect = SkRect::MakeWH(100, 100);
SkPath path;
path.addCircle(50, 50, 50);
// Replace operation doesn't grow the stack.
// Emulating replace operations with more complex geometry is not atomic, it's a replace
// with a wide-open rect and then an intersection with the complex geometry. The replace can
// combine with prior elements, but the subsequent intersect cannot be combined so the stack
// continues to grow.
{
SkClipStack stack;
REPORTER_ASSERT(reporter, 0 == count(stack));
stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
REPORTER_ASSERT(reporter, 1 == count(stack));
stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
REPORTER_ASSERT(reporter, 1 == count(stack));
replacePath(&stack, path, false);
REPORTER_ASSERT(reporter, 2 == count(stack));
replacePath(&stack, path, false);
REPORTER_ASSERT(reporter, 2 == count(stack));
}
// Replacing rect with path.
{
SkClipStack stack;
stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
REPORTER_ASSERT(reporter, 1 == count(stack));
stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, true);
stack.replaceClip(rect, true);
REPORTER_ASSERT(reporter, 1 == count(stack));
replacePath(&stack, path, true);
REPORTER_ASSERT(reporter, 2 == count(stack));
}
}
@ -559,10 +567,8 @@ static void test_rect_merging(skiatest::Reporter* reporter) {
// all bw overlapping - should merge
{
SkClipStack stack;
stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, false);
stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipRect(overlapLeft, SkMatrix::I(), SkClipOp::kIntersect, false);
stack.clipRect(overlapRight, SkMatrix::I(), SkClipOp::kIntersect, false);
REPORTER_ASSERT(reporter, 1 == count(stack));
@ -574,10 +580,8 @@ static void test_rect_merging(skiatest::Reporter* reporter) {
// all aa overlapping - should merge
{
SkClipStack stack;
stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, true);
stack.clipRect(overlapLeft, SkMatrix::I(), SkClipOp::kIntersect, true);
stack.clipRect(overlapRight, SkMatrix::I(), SkClipOp::kIntersect, true);
REPORTER_ASSERT(reporter, 1 == count(stack));
@ -589,10 +593,8 @@ static void test_rect_merging(skiatest::Reporter* reporter) {
// mixed overlapping - should _not_ merge
{
SkClipStack stack;
stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipRect(overlapLeft, SkMatrix::I(), SkClipOp::kIntersect, true);
stack.clipRect(overlapRight, SkMatrix::I(), SkClipOp::kIntersect, false);
REPORTER_ASSERT(reporter, 2 == count(stack));
@ -604,10 +606,8 @@ static void test_rect_merging(skiatest::Reporter* reporter) {
// mixed nested (bw inside aa) - should merge
{
SkClipStack stack;
stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, true);
stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipRect(nestedParent, SkMatrix::I(), SkClipOp::kIntersect, true);
stack.clipRect(nestedChild, SkMatrix::I(), SkClipOp::kIntersect, false);
REPORTER_ASSERT(reporter, 1 == count(stack));
@ -619,10 +619,8 @@ static void test_rect_merging(skiatest::Reporter* reporter) {
// mixed nested (aa inside bw) - should merge
{
SkClipStack stack;
stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, false);
stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, true);
stack.clipRect(nestedParent, SkMatrix::I(), SkClipOp::kIntersect, false);
stack.clipRect(nestedChild, SkMatrix::I(), SkClipOp::kIntersect, true);
REPORTER_ASSERT(reporter, 1 == count(stack));
@ -634,10 +632,8 @@ static void test_rect_merging(skiatest::Reporter* reporter) {
// reverse nested (aa inside bw) - should _not_ merge
{
SkClipStack stack;
stack.clipRect(nestedChild, SkMatrix::I(), kReplace_SkClipOp, false);
stack.clipRect(nestedParent, SkMatrix::I(), kIntersect_SkClipOp, true);
stack.clipRect(nestedChild, SkMatrix::I(), SkClipOp::kIntersect, false);
stack.clipRect(nestedParent, SkMatrix::I(), SkClipOp::kIntersect, true);
REPORTER_ASSERT(reporter, 2 == count(stack));
@ -665,32 +661,32 @@ static void test_quickContains(skiatest::Reporter* reporter) {
{
SkClipStack stack;
stack.clipRect(outsideRect, SkMatrix::I(), kDifference_SkClipOp, false);
// return false because quickContains currently does not care for kDifference_SkClipOp
stack.clipRect(outsideRect, SkMatrix::I(), SkClipOp::kDifference, false);
// return false because quickContains currently does not care for kDifference
REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
}
// Replace Op tests
{
SkClipStack stack;
stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
stack.replaceClip(outsideRect, false);
REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
}
{
SkClipStack stack;
stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipRect(insideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
stack.save(); // To prevent in-place substitution by replace OP
stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
stack.replaceClip(outsideRect, false);
REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
stack.restore();
}
{
SkClipStack stack;
stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipRect(outsideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
stack.save(); // To prevent in-place substitution by replace OP
stack.clipRect(insideRect, SkMatrix::I(), kReplace_SkClipOp, false);
stack.replaceClip(insideRect, false);
REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
stack.restore();
}
@ -698,59 +694,59 @@ static void test_quickContains(skiatest::Reporter* reporter) {
// Verify proper traversal of multi-element clip
{
SkClipStack stack;
stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipRect(insideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
// Use a path for second clip to prevent in-place intersection
stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipPath(outsideCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
}
// Intersect Op tests with rectangles
{
SkClipStack stack;
stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipRect(outsideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
}
{
SkClipStack stack;
stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipRect(insideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
}
{
SkClipStack stack;
stack.clipRect(intersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipRect(intersectingRect, SkMatrix::I(), SkClipOp::kIntersect, false);
REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
}
{
SkClipStack stack;
stack.clipRect(nonIntersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipRect(nonIntersectingRect, SkMatrix::I(), SkClipOp::kIntersect, false);
REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
}
// Intersect Op tests with circle paths
{
SkClipStack stack;
stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipPath(outsideCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
}
{
SkClipStack stack;
stack.clipPath(insideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipPath(insideCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
}
{
SkClipStack stack;
stack.clipPath(intersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipPath(intersectingCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
}
{
SkClipStack stack;
stack.clipPath(nonIntersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipPath(nonIntersectingCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
}
@ -760,7 +756,7 @@ static void test_quickContains(skiatest::Reporter* reporter) {
SkPath path;
path.addRect(outsideRect);
path.toggleInverseFillType();
stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
}
@ -769,7 +765,7 @@ static void test_quickContains(skiatest::Reporter* reporter) {
SkPath path;
path.addRect(insideRect);
path.toggleInverseFillType();
stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
}
@ -778,7 +774,7 @@ static void test_quickContains(skiatest::Reporter* reporter) {
SkPath path;
path.addRect(intersectingRect);
path.toggleInverseFillType();
stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
}
@ -787,7 +783,7 @@ static void test_quickContains(skiatest::Reporter* reporter) {
SkPath path;
path.addRect(nonIntersectingRect);
path.toggleInverseFillType();
stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
}
@ -796,7 +792,7 @@ static void test_quickContains(skiatest::Reporter* reporter) {
SkClipStack stack;
SkPath path = outsideCircle;
path.toggleInverseFillType();
stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
}
@ -804,7 +800,7 @@ static void test_quickContains(skiatest::Reporter* reporter) {
SkClipStack stack;
SkPath path = insideCircle;
path.toggleInverseFillType();
stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
}
@ -812,7 +808,7 @@ static void test_quickContains(skiatest::Reporter* reporter) {
SkClipStack stack;
SkPath path = intersectingCircle;
path.toggleInverseFillType();
stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
}
@ -820,7 +816,7 @@ static void test_quickContains(skiatest::Reporter* reporter) {
SkClipStack stack;
SkPath path = nonIntersectingCircle;
path.toggleInverseFillType();
stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
}
}
@ -843,18 +839,18 @@ static void set_region_to_stack(const SkClipStack& stack, const SkIRect& bounds,
break;
}
region->op(elemRegion, (SkRegion::Op)element->getOp());
region->op(elemRegion, element->getRegionOp());
}
}
static void test_invfill_diff_bug(skiatest::Reporter* reporter) {
SkClipStack stack;
stack.clipRect({10, 10, 20, 20}, SkMatrix::I(), kIntersect_SkClipOp, false);
stack.clipRect({10, 10, 20, 20}, SkMatrix::I(), SkClipOp::kIntersect, false);
SkPath path;
path.addRect({30, 10, 40, 20});
path.setFillType(SkPathFillType::kInverseWinding);
stack.clipPath(path, SkMatrix::I(), kDifference_SkClipOp, false);
stack.clipPath(path, SkMatrix::I(), SkClipOp::kDifference, false);
REPORTER_ASSERT(reporter, SkClipStack::kEmptyGenID == stack.getTopmostGenID());
@ -940,6 +936,10 @@ static void add_shader(const SkRect& rect, bool invert, SkClipOp op, SkClipStack
}
static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
if (element.isReplaceOp()) {
const SkRect resetBounds = SkRect::MakeLTRB(-10000.f, -10000.f, 10000.f, 10000.f);
stack->replaceClip(resetBounds, element.isAA());
}
switch (element.getDeviceSpaceType()) {
case SkClipStack::Element::DeviceSpaceType::kRect:
stack->clipRect(element.getDeviceSpaceRect(), SkMatrix::I(), element.getOp(),
@ -983,19 +983,13 @@ static void test_reduced_clip_stack(skiatest::Reporter* reporter, bool enableCli
static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
static const SkClipOp kOps[] = {
kDifference_SkClipOp,
kIntersect_SkClipOp,
SkClipOp::kDifference,
SkClipOp::kIntersect,
kUnion_SkClipOp,
kXOR_SkClipOp,
kReverseDifference_SkClipOp,
kReplace_SkClipOp,
};
// Replace operations short-circuit the optimizer. We want to make sure that we test this code
// path a little bit but we don't want it to prevent us from testing many longer traversals in
// the optimizer.
static const int kReplaceDiv = 4 * kMaxElemsPerTest;
// We want to test inverse fills. However, they are quite rare in practice so don't over do it.
static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
@ -1019,15 +1013,9 @@ static void test_reduced_clip_stack(skiatest::Reporter* reporter, bool enableCli
int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
bool doAA = r.nextBiasedBool(kFractionAntialiased);
for (int e = 0; e < numElems; ++e) {
size_t opLimit = enableClipShader ? ((size_t) kIntersect_SkClipOp + 1)
size_t opLimit = enableClipShader ? ((size_t) SkClipOp::kIntersect + 1)
: SK_ARRAY_COUNT(kOps);
SkClipOp op = kOps[r.nextULessThan(opLimit)];
if (op == kReplace_SkClipOp) {
if (r.nextU() % kReplaceDiv) {
--e;
continue;
}
}
// saves can change the clip stack behavior when an element is added.
bool doSave = r.nextBool();
@ -1110,8 +1098,8 @@ static void test_reduced_clip_stack(skiatest::Reporter* reporter, bool enableCli
SkIRect scissor = reduced->hasScissor() ? reduced->scissor() : kIBounds;
// GrReducedClipStack assumes that the final result is clipped to the returned bounds
reducedStack.clipDevRect(scissor, kIntersect_SkClipOp);
stack.clipDevRect(scissor, kIntersect_SkClipOp);
reducedStack.clipDevRect(scissor, SkClipOp::kIntersect);
stack.clipDevRect(scissor, SkClipOp::kIntersect);
// convert both the original stack and reduced stack to SkRegions and see if they're equal
SkRegion region;
@ -1135,10 +1123,8 @@ static void test_reduced_clip_stack(skiatest::Reporter* reporter, bool enableCli
static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
{
SkClipStack stack;
stack.clipRect(SkRect::MakeXYWH(0, 0, 100, 100), SkMatrix::I(), kReplace_SkClipOp,
true);
stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkMatrix::I(),
kReplace_SkClipOp, true);
stack.replaceClip(SkRect::MakeXYWH(0, 0, 100, 100), true);
stack.replaceClip(SkRect::MakeXYWH(0, 0, 50.3f, 50.3f), true);
SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
@ -1162,8 +1148,7 @@ static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
// A B
// C D
stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
kReplace_SkClipOp, true);
stack.replaceClip(SkRect::MakeXYWH(0, 0, 25.3f, 25.3f), true);
uint32_t genIDA = stack.getTopmostGenID();
stack.clipRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
kUnion_SkClipOp, true);
@ -1250,11 +1235,10 @@ static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
}
}
}
static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) {
SkClipStack stack;
stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), kReplace_SkClipOp);
stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), kReplace_SkClipOp);
stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), SkClipOp::kIntersect);
stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), SkClipOp::kIntersect);
SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
@ -1359,7 +1343,7 @@ static void test_reduced_clip_stack_aa(skiatest::Reporter* reporter) {
// Pixel-aligned rect (iior=true).
name.printf("Pixel-aligned rect test, iter %i", i);
SkClipStack stack;
stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
stack.clipRect(alignedRect, SkMatrix::I(), SkClipOp::kIntersect, true);
test_aa_query(reporter, name, stack, m, {IL, IT, IR, IB}, ClipMethod::kIgnoreClip);
test_aa_query(reporter, name, stack, m, {IL, IT-1, IR, IT}, ClipMethod::kSkipDraw);
test_aa_query(reporter, name, stack, m, {IL, IT-2, IR, IB}, ClipMethod::kScissor);
@ -1367,7 +1351,7 @@ static void test_reduced_clip_stack_aa(skiatest::Reporter* reporter) {
// Rect (iior=true).
name.printf("Rect test, iter %i", i);
stack.reset();
stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
stack.clipRect(rect, SkMatrix::I(), SkClipOp::kIntersect, true);
test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kIgnoreClip);
test_aa_query(reporter, name, stack, m, {L-.1f, T, L, B}, ClipMethod::kSkipDraw);
test_aa_query(reporter, name, stack, m, {L-.1f, T, L+.1f, B}, ClipMethod::kAAElements, 1);
@ -1375,7 +1359,7 @@ static void test_reduced_clip_stack_aa(skiatest::Reporter* reporter) {
// Difference rect (iior=false, inside-out bounds).
name.printf("Difference rect test, iter %i", i);
stack.reset();
stack.clipRect(rect, SkMatrix::I(), kDifference_SkClipOp, true);
stack.clipRect(rect, SkMatrix::I(), SkClipOp::kDifference, true);
test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kSkipDraw);
test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T}, ClipMethod::kIgnoreClip);
test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T+.1f}, ClipMethod::kAAElements, 1);
@ -1383,7 +1367,7 @@ static void test_reduced_clip_stack_aa(skiatest::Reporter* reporter) {
// Complex clip (iior=false, normal bounds).
name.printf("Complex clip test, iter %i", i);
stack.reset();
stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
stack.clipRect(rect, SkMatrix::I(), SkClipOp::kIntersect, true);
stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
test_aa_query(reporter, name, stack, m, {r-.1f, t, R, b}, ClipMethod::kAAElements, 1);
@ -1395,7 +1379,7 @@ static void test_reduced_clip_stack_aa(skiatest::Reporter* reporter) {
// Complex clip where outer rect is pixel aligned (iior=false, normal bounds).
name.printf("Aligned Complex clip test, iter %i", i);
stack.reset();
stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
stack.clipRect(alignedRect, SkMatrix::I(), SkClipOp::kIntersect, true);
stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB}, ClipMethod::kAAElements, 1);
@ -1427,7 +1411,7 @@ static void test_tiny_query_bounds_assertion_bug(skiatest::Reporter* reporter) {
const SkRect clipBounds = SkRect::MakeXYWH(1.5f, 100, 1000, 1000);
SkClipStack rectStack;
rectStack.clipRect(clipBounds, SkMatrix::I(), kIntersect_SkClipOp, true);
rectStack.clipRect(clipBounds, SkMatrix::I(), SkClipOp::kIntersect, true);
SkPath clipPath;
clipPath.moveTo(clipBounds.left(), clipBounds.top());
@ -1436,7 +1420,7 @@ static void test_tiny_query_bounds_assertion_bug(skiatest::Reporter* reporter) {
clipPath.quadTo(clipBounds.left(), clipBounds.bottom(),
clipBounds.left(), clipBounds.top());
SkClipStack pathStack;
pathStack.clipPath(clipPath, SkMatrix::I(), kIntersect_SkClipOp, true);
pathStack.clipPath(clipPath, SkMatrix::I(), SkClipOp::kIntersect, true);
sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
const GrCaps* caps = context->priv().caps();
@ -1505,7 +1489,7 @@ DEF_TEST(ClipStack, reporter) {
{ 0, 0, 75, 75 }
};
for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
stack.clipDevRect(gRects[i], kIntersect_SkClipOp);
stack.clipDevRect(gRects[i], SkClipOp::kIntersect);
}
// all of the above rects should have been intersected, leaving only 1 rect
@ -1517,7 +1501,7 @@ DEF_TEST(ClipStack, reporter) {
REPORTER_ASSERT(reporter, element);
REPORTER_ASSERT(reporter,
SkClipStack::Element::DeviceSpaceType::kRect == element->getDeviceSpaceType());
REPORTER_ASSERT(reporter, kIntersect_SkClipOp == element->getOp());
REPORTER_ASSERT(reporter, SkClipOp::kIntersect == element->getOp());
REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == answer);
// now check that we only had one in our iterator
REPORTER_ASSERT(reporter, !iter.next());