const SkRect& SkClipStack::Element::getBounds() const { staticconst SkRect kEmpty = {0, 0, 0, 0}; staticconst SkRect kInfinite = SkRectPriv::MakeLargeS32(); switch (fDeviceSpaceType) { case DeviceSpaceType::kRect: // fallthrough case DeviceSpaceType::kRRect: return fDeviceSpaceRRect.getBounds(); case DeviceSpaceType::kPath: return fDeviceSpacePath->getBounds(); case DeviceSpaceType::kShader: // Shaders have infinite bounds since any pixel could have clipped or full coverage // (which is different from wide-open, where every pixel has 1.0 coverage, or empty // where every pixel has 0.0 coverage). return kInfinite; case DeviceSpaceType::kEmpty: return kEmpty; default:
SkDEBUGFAIL("Unexpected type."); return kEmpty;
}
}
bool SkClipStack::Element::contains(const SkRect& rect) const { switch (fDeviceSpaceType) { case DeviceSpaceType::kRect: return this->getDeviceSpaceRect().contains(rect); case DeviceSpaceType::kRRect: return fDeviceSpaceRRect.contains(rect); case DeviceSpaceType::kPath: return fDeviceSpacePath->conservativelyContainsRect(rect); case DeviceSpaceType::kEmpty: case DeviceSpaceType::kShader: returnfalse; default:
SkDEBUGFAIL("Unexpected type."); returnfalse;
}
}
bool SkClipStack::Element::contains(const SkRRect& rrect) const { switch (fDeviceSpaceType) { case DeviceSpaceType::kRect: return this->getDeviceSpaceRect().contains(rrect.getBounds()); case DeviceSpaceType::kRRect: // We don't currently have a generalized rrect-rrect containment. return fDeviceSpaceRRect.contains(rrect.getBounds()) || rrect == fDeviceSpaceRRect; case DeviceSpaceType::kPath: return fDeviceSpacePath->conservativelyContainsRect(rrect.getBounds()); case DeviceSpaceType::kEmpty: case DeviceSpaceType::kShader: returnfalse; default:
SkDEBUGFAIL("Unexpected type."); returnfalse;
}
}
void SkClipStack::Element::invertShapeFillType() { switch (fDeviceSpaceType) { case DeviceSpaceType::kRect:
fDeviceSpacePath.init();
fDeviceSpacePath->addRect(this->getDeviceSpaceRect());
fDeviceSpacePath->setFillType(SkPathFillType::kInverseEvenOdd);
fDeviceSpaceType = DeviceSpaceType::kPath; break; case DeviceSpaceType::kRRect:
fDeviceSpacePath.init();
fDeviceSpacePath->addRRect(fDeviceSpaceRRect);
fDeviceSpacePath->setFillType(SkPathFillType::kInverseEvenOdd);
fDeviceSpaceType = DeviceSpaceType::kPath; break; case DeviceSpaceType::kPath:
fDeviceSpacePath->toggleInverseFillType(); break; case DeviceSpaceType::kShader:
fShader = as_SB(fShader)->makeInvertAlpha(); break; case DeviceSpaceType::kEmpty: // Should this set to an empty, inverse filled path? break;
}
}
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;
fFiniteBound.setEmpty();
fIsIntersectionOfRects = false;
fGenID = kInvalidGenID;
}
bool SkClipStack::Element::canBeIntersectedInPlace(int saveCount, SkClipOp op) const { if (DeviceSpaceType::kEmpty == fDeviceSpaceType &&
(SkClipOp::kDifference == op || SkClipOp::kIntersect == op)) { returntrue;
} // Only clips within the same save/restore frame (as captured by // the save count) can be merged return fSaveCount == saveCount &&
SkClipOp::kIntersect == op &&
(SkClipOp::kIntersect == fOp || this->isReplaceOp());
}
if (fDoAA == newAA) { // if the AA setting is the same there is no issue returntrue;
}
if (!SkRect::Intersects(this->getDeviceSpaceRect(), newR)) { // The calling code will correctly set the result to the empty clip returntrue;
}
if (this->getDeviceSpaceRect().contains(newR)) { // if the new rect carves out a portion of the old one there is no // issue returntrue;
}
// So either the two overlap in some complex manner or newR contains oldR. // In the first, case the edges will require different AA. In the second, // the AA setting that would be carried forward is incorrect (e.g., oldR // is AA while newR is BW but since newR contains oldR, oldR will be // drawn BW) since the new AA setting will predominate. returnfalse;
}
// a mirror of combineBoundsRevDiff void SkClipStack::Element::combineBoundsDiff(FillCombo combination, const SkRect& prevFinite) { switch (combination) { case kInvPrev_InvCur_FillCombo: // In this case the only pixels that can remain set // are inside the current clip rect since the extensions // to infinity of both clips cancel out and whatever // is outside of the current clip is removed
fFiniteBoundType = kNormal_BoundsType; break; case kInvPrev_Cur_FillCombo: // In this case the current op is finite so the only pixels // that aren't set are whatever isn't set in the previous // clip and whatever this clip carves out
fFiniteBound.join(prevFinite);
fFiniteBoundType = kInsideOut_BoundsType; break; case kPrev_InvCur_FillCombo: // In this case everything outside of this clip's bound // is erased, so the only pixels that can remain set // occur w/in the intersection of the two finite bounds if (!fFiniteBound.intersect(prevFinite)) {
fFiniteBound.setEmpty();
fGenID = kEmptyGenID;
}
fFiniteBoundType = kNormal_BoundsType; break; case kPrev_Cur_FillCombo: // The most conservative result bound is that of the // prior clip. This could be wildly incorrect if the // second clip either exactly matches the first clip // (which should yield the empty set) or reduces the // size of the prior bound (e.g., if the second clip // exactly matched the bottom half of the prior clip). // We ignore these two possibilities.
fFiniteBound = prevFinite; break; default:
SkDEBUGFAIL("SkClipStack::Element::combineBoundsDiff Invalid fill combination"); break;
}
}
// a mirror of combineBoundsUnion void SkClipStack::Element::combineBoundsIntersection(int combination, const SkRect& prevFinite) {
switch (combination) { case kInvPrev_InvCur_FillCombo: // The only pixels that aren't writable in this case // occur in the union of the two finite bounds
fFiniteBound.join(prevFinite);
fFiniteBoundType = kInsideOut_BoundsType; break; case kInvPrev_Cur_FillCombo: // In this case the only pixels that will remain writeable // are within the current clip break; case kPrev_InvCur_FillCombo: // In this case the only pixels that will remain writeable // are with the previous clip
fFiniteBound = prevFinite;
fFiniteBoundType = kNormal_BoundsType; break; case kPrev_Cur_FillCombo: if (!fFiniteBound.intersect(prevFinite)) {
this->setEmpty();
} break; default:
SkDEBUGFAIL("SkClipStack::Element::combineBoundsIntersection Invalid fill combination"); break;
}
}
void SkClipStack::Element::updateBoundAndGenID(const Element* prior) { // We set this first here but we may overwrite it later if we determine that the clip is // either wide-open or empty.
fGenID = GetNextGenID();
// First, optimistically update the current Element's bound information // with the current clip's bound
fIsIntersectionOfRects = false; switch (fDeviceSpaceType) { case DeviceSpaceType::kRect:
fFiniteBound = this->getDeviceSpaceRect();
fFiniteBoundType = kNormal_BoundsType;
if (fDeviceSpacePath->isInverseFillType()) {
fFiniteBoundType = kInsideOut_BoundsType;
} else {
fFiniteBoundType = kNormal_BoundsType;
} break; case DeviceSpaceType::kShader: // A shader is infinite. We don't act as wide-open here (which is an empty bounds with // the inside out type). This is because when the bounds is empty and inside-out, we // know there's full coverage everywhere. With a shader, there's *unknown* coverage // everywhere.
fFiniteBound = SkRectPriv::MakeLargeS32();
fFiniteBoundType = kNormal_BoundsType; break; case DeviceSpaceType::kEmpty:
SkDEBUGFAIL("We shouldn't get here with an empty element."); break;
}
// Now determine the previous Element's bound information taking into // account that there may be no previous clip
SkRect prevFinite;
SkClipStack::BoundsType prevType;
if (nullptr == prior) { // no prior clip means the entire plane is writable
prevFinite.setEmpty(); // there are no pixels that cannot be drawn to
prevType = kInsideOut_BoundsType;
} else {
prevFinite = prior->fFiniteBound;
prevType = prior->fFiniteBoundType;
}
// Now integrate with clip with the prior clips if (!this->isReplaceOp()) { switch (fOp) { case SkClipOp::kDifference:
this->combineBoundsDiff(combination, prevFinite); break; case SkClipOp::kIntersect:
this->combineBoundsIntersection(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 // the deque. As such it needs to balance allocating too much memory vs. // incurring allocation/deallocation thrashing. It should roughly correspond to // the deepest save/restore stack we expect to see. staticconstint kDefaultElementAllocCnt = 8;
SkClipStack& SkClipStack::operator=(const SkClipStack& b) { if (this == &b) { return *this;
}
reset();
fSaveCount = b.fSaveCount;
SkDeque::F2BIter recIter(b.fDeque); for (const Element* element = (const Element*)recIter.next();
element != nullptr;
element = (const Element*)recIter.next()) { new (fDeque.push_back()) Element(*element);
}
void SkClipStack::reset() { // We used a placement new for each object in fDeque, so we're responsible // for calling the destructor on each of them as well. while (!fDeque.empty()) {
Element* element = (Element*)fDeque.back();
element->~Element();
fDeque.pop_back();
}
const Element* element = (const Element*)fDeque.back();
if (nullptr == element) { // the clip is wide open - the infinite plane w/ no pixels un-writeable
canvFiniteBound->setEmpty();
*boundType = kInsideOut_BoundsType; if (isIntersectionOfRects) {
*isIntersectionOfRects = false;
} return;
}
bool SkClipStack::internalQuickContains(const SkRect& rect) const {
Iter iter(*this, Iter::kTop_IterStart); const Element* element = iter.prev(); while (element != nullptr) { // TODO: Once expanding ops are removed, this condition is equiv. to op == kDifference. if (SkClipOp::kIntersect != element->getOp() && !element->isReplaceOp()) { returnfalse;
} if (element->isInverseFilled()) { // Part of 'rect' could be trimmed off by the inverse-filled clip element if (SkRect::Intersects(element->getBounds(), rect)) { returnfalse;
}
} else { if (!element->contains(rect)) { returnfalse;
}
} if (element->isReplaceOp()) { break;
}
element = iter.prev();
} returntrue;
}
bool SkClipStack::internalQuickContains(const SkRRect& rrect) const {
Iter iter(*this, Iter::kTop_IterStart); const Element* element = iter.prev(); while (element != nullptr) { // TODO: Once expanding ops are removed, this condition is equiv. to op == kDifference. if (SkClipOp::kIntersect != element->getOp() && !element->isReplaceOp()) { returnfalse;
} if (element->isInverseFilled()) { // Part of 'rrect' could be trimmed off by the inverse-filled clip element if (SkRect::Intersects(element->getBounds(), rrect.getBounds())) { returnfalse;
}
} else { if (!element->contains(rrect)) { returnfalse;
}
} if (element->isReplaceOp()) { break;
}
element = iter.prev();
} returntrue;
}
void SkClipStack::pushElement(const Element& element) { // Use reverse iterator instead of back because Rect path may need previous
SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
Element* prior = (Element*) iter.prev();
if (prior) { if (element.isReplaceOp()) {
this->restoreTo(fSaveCount - 1);
prior = (Element*) fDeque.back();
} elseif (prior->canBeIntersectedInPlace(fSaveCount, element.getOp())) { switch (prior->fDeviceSpaceType) { case Element::DeviceSpaceType::kEmpty:
SkDEBUGCODE(prior->checkEmpty();) return; case Element::DeviceSpaceType::kShader: if (Element::DeviceSpaceType::kShader == element.getDeviceSpaceType()) {
prior->fShader = SkShaders::Blend(SkBlendMode::kSrcIn,
element.fShader, prior->fShader);
Element* priorPrior = (Element*) iter.prev();
prior->updateBoundAndGenID(priorPrior); return;
} break; case Element::DeviceSpaceType::kRect: if (Element::DeviceSpaceType::kRect == element.getDeviceSpaceType()) { if (prior->rectRectIntersectAllowed(element.getDeviceSpaceRect(),
element.isAA())) {
SkRect isectRect; if (!isectRect.intersect(prior->getDeviceSpaceRect(),
element.getDeviceSpaceRect())) {
prior->setEmpty(); return;
}
for (element = (const SkClipStack::Element*) fIter.prev();
element;
element = (const SkClipStack::Element*) fIter.prev()) {
if (op == element->fOp) { // The Deque's iterator is actually one pace ahead of the // returned value. So while "element" is the element we want to // return, the iterator is actually pointing at (and will // return on the next "next" or "prev" call) the element // in front of it in the deque. Bump the iterator forward a // step so we get the expected result. if (nullptr == fIter.next()) { // The reverse iterator has run off the front of the deque // (i.e., the "op" clip is the first clip) and can't // recover. Reset the iterator to start at the front.
fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
} break;
}
}
if (nullptr == element) { // There were no "op" clips
fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
}
// temp starts off in canvas space here
this->getBounds(&temp, &boundType, isIntersectionOfRects); if (SkClipStack::kInsideOut_BoundsType == boundType) { return;
}
// but is converted to device space here
temp.offset(SkIntToScalar(offsetX), SkIntToScalar(offsetY));
if (!devBounds->intersect(temp)) {
devBounds->setEmpty();
}
}
bool SkClipStack::isRRect(const SkRect& bounds, SkRRect* rrect, bool* aa) const { const Element* back = static_cast<const Element*>(fDeque.back()); if (!back) { // TODO: return bounds? returnfalse;
} // First check if the entire stack is known to be a rect by the top element. if (back->fIsIntersectionOfRects && back->fFiniteBoundType == BoundsType::kNormal_BoundsType) {
rrect->setRect(back->fFiniteBound);
*aa = back->isAA(); returntrue;
}
if (back->getOp() == SkClipOp::kIntersect) {
SkRect backBounds; if (!backBounds.intersect(bounds, back->asDeviceSpaceRRect().rect())) { returnfalse;
} // We limit to 17 elements. This means the back element will be bounds checked at most 16 // times if it is an rrect. int cnt = fDeque.count(); if (cnt > 17) { returnfalse;
} if (cnt > 1) {
SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
SkAssertResult(static_cast<const Element*>(iter.prev()) == back); while (const Element* prior = (const Element*)iter.prev()) { // TODO: Once expanding clip ops are removed, this is equiv. to op == kDifference if ((prior->getOp() != SkClipOp::kIntersect && !prior->isReplaceOp()) ||
!prior->contains(backBounds)) { returnfalse;
} if (prior->isReplaceOp()) { break;
}
}
}
*rrect = back->asDeviceSpaceRRect();
*aa = back->isAA(); returntrue;
} returnfalse;
}
uint32_t SkClipStack::GetNextGenID() { // 0-2 are reserved for invalid, empty & wide-open staticconst uint32_t kFirstUnreservedGenID = 3; static std::atomic<uint32_t> nextID{kFirstUnreservedGenID};
uint32_t id; do {
id = nextID.fetch_add(1, std::memory_order_relaxed);
} while (id < kFirstUnreservedGenID); return id;
}
uint32_t SkClipStack::getTopmostGenID() const { if (fDeque.empty()) { return kWideOpenGenID;
}
const Element* back = static_cast<const Element*>(fDeque.back()); if (kInsideOut_BoundsType == back->fFiniteBoundType && back->fFiniteBound.isEmpty() &&
Element::DeviceSpaceType::kShader != back->fDeviceSpaceType) { return kWideOpenGenID;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.