/* * Copyright 2015 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file.
*/ #include"src/pathops/SkOpCoincidence.h"
// returns true if coincident span's start and end are the same bool SkCoincidentSpans::collapsed(const SkOpPtT* test) const { return (fCoinPtTStart == test && fCoinPtTEnd->contains(test))
|| (fCoinPtTEnd == test && fCoinPtTStart->contains(test))
|| (fOppPtTStart == test && fOppPtTEnd->contains(test))
|| (fOppPtTEnd == test && fOppPtTStart->contains(test));
}
// out of line since this function is referenced by address const SkOpPtT* SkCoincidentSpans::coinPtTEnd() const { return fCoinPtTEnd;
}
// out of line since this function is referenced by address const SkOpPtT* SkCoincidentSpans::coinPtTStart() const { return fCoinPtTStart;
}
// sets the span's end to the ptT referenced by the previous-next void SkCoincidentSpans::correctOneEnd( const SkOpPtT* (SkCoincidentSpans::* getEnd)() const, void (SkCoincidentSpans::*setEnd)(const SkOpPtT* ptT) ) { const SkOpPtT* origPtT = (this->*getEnd)(); const SkOpSpanBase* origSpan = origPtT->span(); const SkOpSpan* prev = origSpan->prev(); const SkOpPtT* testPtT = prev ? prev->next()->ptT()
: origSpan->upCast()->next()->prev()->ptT(); if (origPtT != testPtT) {
(this->*setEnd)(testPtT);
}
}
/* Please keep this in sync with debugCorrectEnds */ // FIXME: member pointers have fallen out of favor and can be replaced with // an alternative approach. // makes all span ends agree with the segment's spans that define them void SkCoincidentSpans::correctEnds() {
this->correctOneEnd(&SkCoincidentSpans::coinPtTStart, &SkCoincidentSpans::setCoinPtTStart);
this->correctOneEnd(&SkCoincidentSpans::coinPtTEnd, &SkCoincidentSpans::setCoinPtTEnd);
this->correctOneEnd(&SkCoincidentSpans::oppPtTStart, &SkCoincidentSpans::setOppPtTStart);
this->correctOneEnd(&SkCoincidentSpans::oppPtTEnd, &SkCoincidentSpans::setOppPtTEnd);
}
/* Please keep this in sync with debugExpand */ // expand the range by checking adjacent spans for coincidence bool SkCoincidentSpans::expand() { bool expanded = false; const SkOpSegment* segment = coinPtTStart()->segment(); const SkOpSegment* oppSegment = oppPtTStart()->segment(); do { const SkOpSpan* start = coinPtTStart()->span()->upCast(); const SkOpSpan* prev = start->prev(); const SkOpPtT* oppPtT; if (!prev || !(oppPtT = prev->contains(oppSegment))) { break;
} double midT = (prev->t() + start->t()) / 2; if (!segment->isClose(midT, oppSegment)) { break;
}
setStarts(prev->ptT(), oppPtT);
expanded = true;
} while (true); do { const SkOpSpanBase* end = coinPtTEnd()->span();
SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next(); if (next && next->deleted()) { break;
} const SkOpPtT* oppPtT; if (!next || !(oppPtT = next->contains(oppSegment))) { break;
} double midT = (end->t() + next->t()) / 2; if (!segment->isClose(midT, oppSegment)) { break;
}
setEnds(next->ptT(), oppPtT);
expanded = true;
} while (true); return expanded;
}
// increase the range of this span bool SkCoincidentSpans::extend(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) { bool result = false; if (fCoinPtTStart->fT > coinPtTStart->fT || (this->flipped()
? fOppPtTStart->fT < oppPtTStart->fT : fOppPtTStart->fT > oppPtTStart->fT)) {
this->setStarts(coinPtTStart, oppPtTStart);
result = true;
} if (fCoinPtTEnd->fT < coinPtTEnd->fT || (this->flipped()
? fOppPtTEnd->fT > oppPtTEnd->fT : fOppPtTEnd->fT < oppPtTEnd->fT)) {
this->setEnds(coinPtTEnd, oppPtTEnd);
result = true;
} return result;
}
// set the range of this span void SkCoincidentSpans::set(SkCoincidentSpans* next, const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) {
SkASSERT(SkOpCoincidence::Ordered(coinPtTStart, oppPtTStart));
fNext = next;
this->setStarts(coinPtTStart, oppPtTStart);
this->setEnds(coinPtTEnd, oppPtTEnd);
}
// returns true if both points are inside this bool SkCoincidentSpans::contains(const SkOpPtT* s, const SkOpPtT* e) const { if (s->fT > e->fT) { using std::swap;
swap(s, e);
} if (s->segment() == fCoinPtTStart->segment()) { return fCoinPtTStart->fT <= s->fT && e->fT <= fCoinPtTEnd->fT;
} else {
SkASSERT(s->segment() == fOppPtTStart->segment()); double oppTs = fOppPtTStart->fT; double oppTe = fOppPtTEnd->fT; if (oppTs > oppTe) { using std::swap;
swap(oppTs, oppTe);
} return oppTs <= s->fT && e->fT <= oppTe;
}
}
// out of line since this function is referenced by address const SkOpPtT* SkCoincidentSpans::oppPtTStart() const { return fOppPtTStart;
}
// out of line since this function is referenced by address const SkOpPtT* SkCoincidentSpans::oppPtTEnd() const { return fOppPtTEnd;
}
// A coincident span is unordered if the pairs of points in the main and opposite curves' // t values do not ascend or descend. For instance, if a tightly arced quadratic is // coincident with another curve, it may intersect it out of order. bool SkCoincidentSpans::ordered(bool* result) const { const SkOpSpanBase* start = this->coinPtTStart()->span(); const SkOpSpanBase* end = this->coinPtTEnd()->span(); const SkOpSpanBase* next = start->upCast()->next(); if (next == end) {
*result = true; returntrue;
} bool flipped = this->flipped(); const SkOpSegment* oppSeg = this->oppPtTStart()->segment(); double oppLastT = fOppPtTStart->fT; do { const SkOpPtT* opp = next->contains(oppSeg); if (!opp) { // SkOPOBJASSERT(start, 0); // may assert if coincident span isn't fully processed returnfalse;
} if ((oppLastT > opp->fT) != flipped) {
*result = false; returntrue;
}
oppLastT = opp->fT; if (next == end) { break;
} if (!next->upCastable()) {
*result = false; returntrue;
}
next = next->upCast()->next();
} while (true);
*result = true; returntrue;
}
// if there is an existing pair that overlaps the addition, extend it bool SkOpCoincidence::extend(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) {
SkCoincidentSpans* test = fHead; if (!test) { returnfalse;
} const SkOpSegment* coinSeg = coinPtTStart->segment(); const SkOpSegment* oppSeg = oppPtTStart->segment(); if (!Ordered(coinPtTStart, oppPtTStart)) { using std::swap;
swap(coinSeg, oppSeg);
swap(coinPtTStart, oppPtTStart);
swap(coinPtTEnd, oppPtTEnd); if (coinPtTStart->fT > coinPtTEnd->fT) {
swap(coinPtTStart, coinPtTEnd);
swap(oppPtTStart, oppPtTEnd);
}
} double oppMinT = std::min(oppPtTStart->fT, oppPtTEnd->fT);
SkDEBUGCODE(double oppMaxT = std::max(oppPtTStart->fT, oppPtTEnd->fT)); do { if (coinSeg != test->coinPtTStart()->segment()) { continue;
} if (oppSeg != test->oppPtTStart()->segment()) { continue;
} double oTestMinT = std::min(test->oppPtTStart()->fT, test->oppPtTEnd()->fT); double oTestMaxT = std::max(test->oppPtTStart()->fT, test->oppPtTEnd()->fT); // if debug check triggers, caller failed to check if extended already exists
SkASSERT(test->coinPtTStart()->fT > coinPtTStart->fT
|| coinPtTEnd->fT > test->coinPtTEnd()->fT
|| oTestMinT > oppMinT || oppMaxT > oTestMaxT); if ((test->coinPtTStart()->fT <= coinPtTEnd->fT
&& coinPtTStart->fT <= test->coinPtTEnd()->fT)
|| (oTestMinT <= oTestMaxT && oppMinT <= oTestMaxT)) {
test->extend(coinPtTStart, coinPtTEnd, oppPtTStart, oppPtTEnd); returntrue;
}
} while ((test = test->next())); returnfalse;
}
// adds a new coincident pair void SkOpCoincidence::add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
SkOpPtT* oppPtTEnd) { // OPTIMIZE: caller should have already sorted if (!Ordered(coinPtTStart, oppPtTStart)) { if (oppPtTStart->fT < oppPtTEnd->fT) {
this->add(oppPtTStart, oppPtTEnd, coinPtTStart, coinPtTEnd);
} else {
this->add(oppPtTEnd, oppPtTStart, coinPtTEnd, coinPtTStart);
} return;
}
SkASSERT(Ordered(coinPtTStart, oppPtTStart)); // choose the ptT at the front of the list to track
coinPtTStart = coinPtTStart->span()->ptT();
coinPtTEnd = coinPtTEnd->span()->ptT();
oppPtTStart = oppPtTStart->span()->ptT();
oppPtTEnd = oppPtTEnd->span()->ptT();
SkOPASSERT(coinPtTStart->fT < coinPtTEnd->fT);
SkOPASSERT(oppPtTStart->fT != oppPtTEnd->fT);
SkOPASSERT(!coinPtTStart->deleted());
SkOPASSERT(!coinPtTEnd->deleted());
SkOPASSERT(!oppPtTStart->deleted());
SkOPASSERT(!oppPtTEnd->deleted());
DebugCheckAdd(fHead, coinPtTStart, coinPtTEnd, oppPtTStart, oppPtTEnd);
DebugCheckAdd(fTop, coinPtTStart, coinPtTEnd, oppPtTStart, oppPtTEnd);
SkCoincidentSpans* coinRec = this->globalState()->allocator()->make<SkCoincidentSpans>();
coinRec->init(SkDEBUGCODE(fGlobalState));
coinRec->set(this->fHead, coinPtTStart, coinPtTEnd, oppPtTStart, oppPtTEnd);
fHead = coinRec;
}
// description below bool SkOpCoincidence::addEndMovedSpans(const SkOpSpan* base, const SkOpSpanBase* testSpan) { const SkOpPtT* testPtT = testSpan->ptT(); const SkOpPtT* stopPtT = testPtT; const SkOpSegment* baseSeg = base->segment(); int escapeHatch = 100000; // this is 100 times larger than the debugLoopLimit test while ((testPtT = testPtT->next()) != stopPtT) { if (--escapeHatch <= 0) { returnfalse; // if triggered (likely by a fuzz-generated test) too complex to succeed
} const SkOpSegment* testSeg = testPtT->segment(); if (testPtT->deleted()) { continue;
} if (testSeg == baseSeg) { continue;
} if (testPtT->span()->ptT() != testPtT) { continue;
} if (this->contains(baseSeg, testSeg, testPtT->fT)) { continue;
} // intersect perp with base->ptT() with testPtT->segment()
SkDVector dxdy = baseSeg->dSlopeAtT(base->t()); const SkPoint& pt = base->pt();
SkDLine ray = {{{pt.fX, pt.fY}, {pt.fX + dxdy.fY, pt.fY - dxdy.fX}}};
SkIntersections i SkDEBUGCODE((this->globalState()));
(*CurveIntersectRay[testSeg->verb()])(testSeg->pts(), testSeg->weight(), ray, &i); for (int index = 0; index < i.used(); ++index) { double t = i[0][index]; if (!between(0, t, 1)) { continue;
}
SkDPoint oppPt = i.pt(index); if (!oppPt.approximatelyEqual(pt)) { continue;
}
SkOpSegment* writableSeg = const_cast<SkOpSegment*>(testSeg);
SkOpPtT* oppStart = writableSeg->addT(t); if (oppStart == testPtT) { continue;
}
SkOpSpan* writableBase = const_cast<SkOpSpan*>(base);
oppStart->span()->addOpp(writableBase); if (oppStart->deleted()) { continue;
}
SkOpSegment* coinSeg = base->segment();
SkOpSegment* oppSeg = oppStart->segment(); double coinTs, coinTe, oppTs, oppTe; if (Ordered(coinSeg, oppSeg)) {
coinTs = base->t();
coinTe = testSpan->t();
oppTs = oppStart->fT;
oppTe = testPtT->fT;
} else { using std::swap;
swap(coinSeg, oppSeg);
coinTs = oppStart->fT;
coinTe = testPtT->fT;
oppTs = base->t();
oppTe = testSpan->t();
} if (coinTs > coinTe) { using std::swap;
swap(coinTs, coinTe);
swap(oppTs, oppTe);
} bool added;
FAIL_IF(!this->addOrOverlap(coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &added));
}
} returntrue;
}
// description below bool SkOpCoincidence::addEndMovedSpans(const SkOpPtT* ptT) {
FAIL_IF(!ptT->span()->upCastable()); const SkOpSpan* base = ptT->span()->upCast(); const SkOpSpan* prev = base->prev();
FAIL_IF(!prev); if (!prev->isCanceled()) { if (!this->addEndMovedSpans(base, base->prev())) { returnfalse;
}
} if (!base->isCanceled()) { if (!this->addEndMovedSpans(base, base->next())) { returnfalse;
}
} returntrue;
}
/* If A is coincident with B and B includes an endpoint, and A's matching point is not the endpoint (i.e., there's an implied line connecting B-end and A) then assume that the same implied line may intersect another curve close to B. Since we only care about coincidence that was undetected, look at the ptT list on B-segment adjacent to the B-end/A ptT loop (not in the loop, but next door) and see if the A matching point is close enough to form another coincident pair. If so, check for a new coincident span between B-end/A ptT loop and the adjacent ptT loop.
*/ bool SkOpCoincidence::addEndMovedSpans(DEBUG_COIN_DECLARE_ONLY_PARAMS()) {
DEBUG_SET_PHASE();
SkCoincidentSpans* span = fHead; if (!span) { returntrue;
}
fTop = span;
fHead = nullptr; do { if (span->coinPtTStart()->fPt != span->oppPtTStart()->fPt) {
FAIL_IF(1 == span->coinPtTStart()->fT); bool onEnd = span->coinPtTStart()->fT == 0; bool oOnEnd = zero_or_one(span->oppPtTStart()->fT); if (onEnd) { if (!oOnEnd) { // if both are on end, any nearby intersect was already found if (!this->addEndMovedSpans(span->oppPtTStart())) { returnfalse;
}
}
} elseif (oOnEnd) { if (!this->addEndMovedSpans(span->coinPtTStart())) { returnfalse;
}
}
} if (span->coinPtTEnd()->fPt != span->oppPtTEnd()->fPt) { bool onEnd = span->coinPtTEnd()->fT == 1; bool oOnEnd = zero_or_one(span->oppPtTEnd()->fT); if (onEnd) { if (!oOnEnd) { if (!this->addEndMovedSpans(span->oppPtTEnd())) { returnfalse;
}
}
} elseif (oOnEnd) { if (!this->addEndMovedSpans(span->coinPtTEnd())) { returnfalse;
}
}
}
} while ((span = span->next()));
this->restoreHead(); returntrue;
}
/* Please keep this in sync with debugAddExpanded */ // for each coincident pair, match the spans // if the spans don't match, add the missing pt to the segment and loop it in the opposite span bool SkOpCoincidence::addExpanded(DEBUG_COIN_DECLARE_ONLY_PARAMS()) {
DEBUG_SET_PHASE();
SkCoincidentSpans* coin = this->fHead; if (!coin) { returntrue;
} do { const SkOpPtT* startPtT = coin->coinPtTStart(); const SkOpPtT* oStartPtT = coin->oppPtTStart(); double priorT = startPtT->fT; double oPriorT = oStartPtT->fT;
FAIL_IF(!startPtT->contains(oStartPtT));
SkOPASSERT(coin->coinPtTEnd()->contains(coin->oppPtTEnd())); const SkOpSpanBase* start = startPtT->span(); const SkOpSpanBase* oStart = oStartPtT->span(); const SkOpSpanBase* end = coin->coinPtTEnd()->span(); const SkOpSpanBase* oEnd = coin->oppPtTEnd()->span();
FAIL_IF(oEnd->deleted());
FAIL_IF(!start->upCastable()); const SkOpSpanBase* test = start->upCast()->next();
FAIL_IF(!coin->flipped() && !oStart->upCastable()); const SkOpSpanBase* oTest = coin->flipped() ? oStart->prev() : oStart->upCast()->next();
FAIL_IF(!oTest);
SkOpSegment* seg = start->segment();
SkOpSegment* oSeg = oStart->segment(); while (test != end || oTest != oEnd) { const SkOpPtT* containedOpp = test->ptT()->contains(oSeg); const SkOpPtT* containedThis = oTest->ptT()->contains(seg); if (!containedOpp || !containedThis) { // choose the ends, or the first common pt-t list shared by both double nextT, oNextT; if (containedOpp) {
nextT = test->t();
oNextT = containedOpp->fT;
} elseif (containedThis) {
nextT = containedThis->fT;
oNextT = oTest->t();
} else { // iterate through until a pt-t list found that contains the other const SkOpSpanBase* walk = test; const SkOpPtT* walkOpp; do {
FAIL_IF(!walk->upCastable());
walk = walk->upCast()->next();
} while (!(walkOpp = walk->ptT()->contains(oSeg))
&& walk != coin->coinPtTEnd()->span());
FAIL_IF(!walkOpp);
nextT = walk->t();
oNextT = walkOpp->fT;
} // use t ranges to guess which one is missing double startRange = nextT - priorT;
FAIL_IF(!startRange); double startPart = (test->t() - priorT) / startRange; double oStartRange = oNextT - oPriorT;
FAIL_IF(!oStartRange); double oStartPart = (oTest->t() - oPriorT) / oStartRange;
FAIL_IF(startPart == oStartPart); bool addToOpp = !containedOpp && !containedThis ? startPart < oStartPart
: !!containedThis; bool startOver = false; bool success = addToOpp ? oSeg->addExpanded(
oPriorT + oStartRange * startPart, test, &startOver)
: seg->addExpanded(
priorT + startRange * oStartPart, oTest, &startOver);
FAIL_IF(!success); if (startOver) {
test = start;
oTest = oStart;
}
end = coin->coinPtTEnd()->span();
oEnd = coin->oppPtTEnd()->span();
} if (test != end) {
FAIL_IF(!test->upCastable());
priorT = test->t();
test = test->upCast()->next();
} if (oTest != oEnd) {
oPriorT = oTest->t(); if (coin->flipped()) {
oTest = oTest->prev();
} else {
FAIL_IF(!oTest->upCastable());
oTest = oTest->upCast()->next();
}
FAIL_IF(!oTest);
}
}
} while ((coin = coin->next())); returntrue;
}
// given a t span, map the same range on the coincident span /* the curves may not scale linearly, so interpolation may only happen within known points remap over1s, over1e, cointPtTStart, coinPtTEnd to smallest range that captures over1s then repeat to capture over1e
*/ double SkOpCoincidence::TRange(const SkOpPtT* overS, double t, const SkOpSegment* coinSeg SkDEBUGPARAMS(const SkOpPtT* overE)) { const SkOpSpanBase* work = overS->span(); const SkOpPtT* foundStart = nullptr; const SkOpPtT* foundEnd = nullptr; const SkOpPtT* coinStart = nullptr; const SkOpPtT* coinEnd = nullptr; do { const SkOpPtT* contained = work->contains(coinSeg); if (!contained) { if (work->final()) { break;
} continue;
} if (work->t() <= t) {
coinStart = contained;
foundStart = work->ptT();
} if (work->t() >= t) {
coinEnd = contained;
foundEnd = work->ptT(); break;
}
SkASSERT(work->ptT() != overE);
} while ((work = work->upCast()->next())); if (!coinStart || !coinEnd) { return 1;
} // while overS->fT <=t and overS contains coinSeg double denom = foundEnd->fT - foundStart->fT; double sRatio = denom ? (t - foundStart->fT) / denom : 1; return coinStart->fT + (coinEnd->fT - coinStart->fT) * sRatio;
}
void SkOpCoincidence::restoreHead() {
SkCoincidentSpans** headPtr = &fHead; while (*headPtr) {
headPtr = (*headPtr)->nextPtr();
}
*headPtr = fTop;
fTop = nullptr; // segments may have collapsed in the meantime; remove empty referenced segments
headPtr = &fHead; while (*headPtr) {
SkCoincidentSpans* test = *headPtr; if (test->coinPtTStart()->segment()->done() || test->oppPtTStart()->segment()->done()) {
*headPtr = test->next(); continue;
}
headPtr = (*headPtr)->nextPtr();
}
}
// Please keep this in sync with debugExpand() // expand the range by checking adjacent spans for coincidence bool SkOpCoincidence::expand(DEBUG_COIN_DECLARE_ONLY_PARAMS()) {
DEBUG_SET_PHASE();
SkCoincidentSpans* coin = fHead; if (!coin) { returnfalse;
} bool expanded = false; do { if (coin->expand()) { // check to see if multiple spans expanded so they are now identical
SkCoincidentSpans* test = fHead; do { if (coin == test) { continue;
} if (coin->coinPtTStart() == test->coinPtTStart()
&& coin->oppPtTStart() == test->oppPtTStart()) {
this->release(fHead, test); break;
}
} while ((test = test->next()));
expanded = true;
}
} while ((coin = coin->next())); return expanded;
}
void SkOpCoincidence::fixUp(SkCoincidentSpans* coin, SkOpPtT* deleted, const SkOpPtT* kept) {
SkCoincidentSpans* head = coin; do { if (coin->coinPtTStart() == deleted) { if (coin->coinPtTEnd()->span() == kept->span()) {
this->release(head, coin); continue;
}
coin->setCoinPtTStart(kept);
} if (coin->coinPtTEnd() == deleted) { if (coin->coinPtTStart()->span() == kept->span()) {
this->release(head, coin); continue;
}
coin->setCoinPtTEnd(kept);
} if (coin->oppPtTStart() == deleted) { if (coin->oppPtTEnd()->span() == kept->span()) {
this->release(head, coin); continue;
}
coin->setOppPtTStart(kept);
} if (coin->oppPtTEnd() == deleted) { if (coin->oppPtTStart()->span() == kept->span()) {
this->release(head, coin); continue;
}
coin->setOppPtTEnd(kept);
}
} while ((coin = coin->next()));
}
// Please keep this in sync with debugMark() /* this sets up the coincidence links in the segments when the coincidence crosses multiple spans */ bool SkOpCoincidence::mark(DEBUG_COIN_DECLARE_ONLY_PARAMS()) {
DEBUG_SET_PHASE();
SkCoincidentSpans* coin = fHead; if (!coin) { returntrue;
} do {
SkOpSpanBase* startBase = coin->coinPtTStartWritable()->span();
FAIL_IF(!startBase->upCastable());
SkOpSpan* start = startBase->upCast();
FAIL_IF(start->deleted());
SkOpSpanBase* end = coin->coinPtTEndWritable()->span();
SkOPASSERT(!end->deleted());
SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span();
SkOPASSERT(!oStart->deleted());
SkOpSpanBase* oEnd = coin->oppPtTEndWritable()->span();
FAIL_IF(oEnd->deleted()); bool flipped = coin->flipped(); if (flipped) { using std::swap;
swap(oStart, oEnd);
} /* coin and opp spans may not match up. Mark the ends, and then let the interior
get marked as many times as the spans allow */
FAIL_IF(!oStart->upCastable());
start->insertCoincidence(oStart->upCast());
end->insertCoinEnd(oEnd); const SkOpSegment* segment = start->segment(); const SkOpSegment* oSegment = oStart->segment();
SkOpSpanBase* next = start;
SkOpSpanBase* oNext = oStart; bool ordered;
FAIL_IF(!coin->ordered(&ordered)); while ((next = next->upCast()->next()) != end) {
FAIL_IF(!next->upCastable());
FAIL_IF(!next->upCast()->insertCoincidence(oSegment, flipped, ordered));
} while ((oNext = oNext->upCast()->next()) != oEnd) {
FAIL_IF(!oNext->upCastable());
FAIL_IF(!oNext->upCast()->insertCoincidence(segment, flipped, ordered));
}
} while ((coin = coin->next())); returntrue;
}
// Please keep in sync with debugMarkCollapsed() void SkOpCoincidence::markCollapsed(SkCoincidentSpans* coin, SkOpPtT* test) {
SkCoincidentSpans* head = coin; while (coin) { if (coin->collapsed(test)) { if (zero_or_one(coin->coinPtTStart()->fT) && zero_or_one(coin->coinPtTEnd()->fT)) {
coin->coinPtTStartWritable()->segment()->markAllDone();
} if (zero_or_one(coin->oppPtTStart()->fT) && zero_or_one(coin->oppPtTEnd()->fT)) {
coin->oppPtTStartWritable()->segment()->markAllDone();
}
this->release(head, coin);
}
coin = coin->next();
}
}
// Please keep in sync with debugMarkCollapsed() void SkOpCoincidence::markCollapsed(SkOpPtT* test) {
markCollapsed(fHead, test);
markCollapsed(fTop, test);
}
bool SkOpCoincidence::Ordered(const SkOpSegment* coinSeg, const SkOpSegment* oppSeg) { if (coinSeg->verb() < oppSeg->verb()) { returntrue;
} if (coinSeg->verb() > oppSeg->verb()) { returnfalse;
} int count = (SkPathOpsVerbToPoints(coinSeg->verb()) + 1) * 2; const SkScalar* cPt = &coinSeg->pts()[0].fX; const SkScalar* oPt = &oppSeg->pts()[0].fX; for (int index = 0; index < count; ++index) { if (*cPt < *oPt) { returntrue;
} if (*cPt > *oPt) { returnfalse;
}
++cPt;
++oPt;
} returntrue;
}
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.