// 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; return true;
} 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; return true;
}
oppLastT = opp->fT; if (next == end) { break;
} if (!next->upCastable()) {
*result = false; return true;
}
next = next->upCast()->next();
} while (true);
*result = true; return true;
}
// 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); return true;
}
} 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));
}
} return true;
}
// 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;
}
} return true;
}
/* If A is coincident with B and B includes an endpoint, and A's matching point isnottheendpoint(i.e.,there'sanimpliedlineconnectingB-endandA) thenassumethatthesameimpliedlinemayintersectanothercurveclosetoB. Sinceweonlycareaboutcoincidencethatwasundetected,lookatthe ptTlistonB-segmentadjacenttotheB-end/AptTloop(notintheloop,but nextdoor)andseeiftheAmatchingpointiscloseenoughtoformanother coincidentpair.Ifso,checkforanewcoincidentspanbetweenB-end/AptTloop andtheadjacentptTloop.
*/ bool SkOpCoincidence::addEndMovedSpans(DEBUG_COIN_DECLARE_ONLY_PARAMS()) {
DEBUG_SET_PHASE();
SkCoincidentSpans* span = fHead; if (!span) { return true;
}
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(); return true;
}
/* 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) { return true;
} 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())); return true;
}
// given a t span, map the same range on the coincident span /* thecurvesmaynotscalelinearly,sointerpolationmayonlyhappenwithinknownpoints remapover1s,over1e,cointPtTStart,coinPtTEndtosmallestrangethatcapturesover1s thenrepeattocaptureover1e
*/ 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) { return1;
} // 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;
}
// 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) { return true;
} 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())); return true;
}
// Please keep in sync with debugMarkCollapsed() void SkOpCoincidence::markCollapsed(SkCoincidentSpans** headPtr, SkOpPtT* test) {
SkASSERT(headPtr == &fHead || headPtr == &fTop);
SkCoincidentSpans* coin = *headPtr; while (coin) {
SkCoincidentSpans* next = coin->next(); 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(headPtr, 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()) { return true;
} 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) { return true;
} if (*cPt > *oPt) { returnfalse;
}
++cPt;
++oPt;
} return true;
}
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.