#if DEBUG_DUMP_VERIFY bool SkPathOpsDebug::gDumpOp; // set to true to write op to file before a crash bool SkPathOpsDebug::gVerifyOp; // set to true to compare result against regions #endif
bool SkPathOpsDebug::gRunFail; // set to true to check for success on tests known to fail bool SkPathOpsDebug::gVeryVerbose; // set to true to run extensive checking tests
#if DEBUG_SORT int SkPathOpsDebug::gSortCountDefault = SK_MaxS32; int SkPathOpsDebug::gSortCount; #endif
#if DEBUG_COIN // commented-out lines keep this in sync with addT() const SkOpPtT* SkOpSegment::debugAddT(double t, SkPathOpsDebug::GlitchLog* log) const {
debugValidate();
SkPoint pt = this->ptAtT(t); const SkOpSpanBase* span = &fHead; do { const SkOpPtT* result = span->ptT(); if (t == result->fT || this->match(result, this, t, pt)) { // span->bumpSpanAdds(); return result;
} if (t < result->fT) { const SkOpSpan* prev = result->span()->prev();
FAIL_WITH_NULL_IF(!prev, span); // marks in global state that new op span has been allocated
this->globalState()->setAllocatedOpSpan(); // span->init(this, prev, t, pt);
this->debugValidate(); // #if DEBUG_ADD_T // SkDebugf("%s insert t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t, // span->segment()->debugID(), span->debugID()); // #endif // span->bumpSpanAdds(); return nullptr;
}
FAIL_WITH_NULL_IF(span != &fTail, span);
} while ((span = span->upCast()->next()));
SkASSERT(0); return nullptr; // we never get here, but need this to satisfy compiler
} #endif
#if DEBUG_ANGLE void SkOpSegment::debugCheckAngleCoin() const { const SkOpSpanBase* base = &fHead; const SkOpSpan* span; do { const SkOpAngle* angle = base->fromAngle(); if (angle && angle->debugCheckCoincidence()) {
angle->debugCheckNearCoincidence();
} if (base->final()) { break;
}
span = base->upCast();
angle = span->toAngle(); if (angle && angle->debugCheckCoincidence()) {
angle->debugCheckNearCoincidence();
}
} while ((base = span->next()));
} #endif
#if DEBUG_COIN // this mimics the order of the checks in handle coincidence void SkOpSegment::debugCheckHealth(SkPathOpsDebug::GlitchLog* glitches) const {
debugMoveMultiples(glitches);
debugMoveNearby(glitches);
debugMissingCoincidence(glitches);
}
// commented-out lines keep this in sync with clearAll() void SkOpSegment::debugClearAll(SkPathOpsDebug::GlitchLog* glitches) const { const SkOpSpan* span = &fHead; do {
this->debugClearOne(span, glitches);
} while ((span = span->next()->upCastable()));
this->globalState()->coincidence()->debugRelease(glitches, this);
}
// commented-out lines keep this in sync with clearOne() void SkOpSegment::debugClearOne(const SkOpSpan* span, SkPathOpsDebug::GlitchLog* glitches) const { if (span->windValue()) glitches->record(SkPathOpsDebug::kCollapsedWindValue_Glitch, span); if (span->oppValue()) glitches->record(SkPathOpsDebug::kCollapsedOppValue_Glitch, span); if (!span->done()) glitches->record(SkPathOpsDebug::kCollapsedDone_Glitch, span);
} #endif
SkOpAngle* SkOpSegment::debugLastAngle() {
SkOpAngle* result = nullptr;
SkOpSpan* span = this->head(); do { if (span->toAngle()) {
SkASSERT(!result);
result = span->toAngle();
}
} while ((span = span->next()->upCastable()));
SkASSERT(result); return result;
}
#if DEBUG_COIN // commented-out lines keep this in sync with ClearVisited void SkOpSegment::DebugClearVisited(const SkOpSpanBase* span) { // reset visited flag back to false do { const SkOpPtT* ptT = span->ptT(), * stopPtT = ptT; while ((ptT = ptT->next()) != stopPtT) { const SkOpSegment* opp = ptT->segment();
opp->resetDebugVisited();
}
} while (!span->final() && (span = span->upCast()->next()));
} #endif
#if DEBUG_COIN // commented-out lines keep this in sync with missingCoincidence() // look for pairs of undetected coincident curves // assumes that segments going in have visited flag clear // Even though pairs of curves correct detect coincident runs, a run may be missed // if the coincidence is a product of multiple intersections. For instance, given // curves A, B, and C: // A-B intersect at a point 1; A-C and B-C intersect at point 2, so near // the end of C that the intersection is replaced with the end of C. // Even though A-B correctly do not detect an intersection at point 2, // the resulting run from point 1 to point 2 is coincident on A and B. void SkOpSegment::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const { if (this->done()) { return;
} const SkOpSpan* prior = nullptr; const SkOpSpanBase* spanBase = &fHead; // bool result = false; do { const SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT;
SkASSERT(ptT->span() == spanBase); while ((ptT = ptT->next()) != spanStopPtT) { if (ptT->deleted()) { continue;
} const SkOpSegment* opp = ptT->span()->segment(); if (opp->done()) { continue;
} // when opp is encounted the 1st time, continue; on 2nd encounter, look for coincidence if (!opp->debugVisited()) { continue;
} if (spanBase == &fHead) { continue;
} if (ptT->segment() == this) { continue;
} const SkOpSpan* span = spanBase->upCastable(); // FIXME?: this assumes that if the opposite segment is coincident then no more // coincidence needs to be detected. This may not be true. if (span && span->segment() != opp && span->containsCoincidence(opp)) { // debug has additional condition since it may be called before inner duplicate points have been deleted continue;
} if (spanBase->segment() != opp && spanBase->containsCoinEnd(opp)) { // debug has additional condition since it may be called before inner duplicate points have been deleted continue;
} const SkOpPtT* priorPtT = nullptr, * priorStopPtT; // find prior span containing opp segment const SkOpSegment* priorOpp = nullptr; const SkOpSpan* priorTest = spanBase->prev(); while (!priorOpp && priorTest) {
priorStopPtT = priorPtT = priorTest->ptT(); while ((priorPtT = priorPtT->next()) != priorStopPtT) { if (priorPtT->deleted()) { continue;
} const SkOpSegment* segment = priorPtT->span()->segment(); if (segment == opp) {
prior = priorTest;
priorOpp = opp; break;
}
}
priorTest = priorTest->prev();
} if (!priorOpp) { continue;
} if (priorPtT == ptT) { continue;
} const SkOpPtT* oppStart = prior->ptT(); const SkOpPtT* oppEnd = spanBase->ptT(); bool swapped = priorPtT->fT > ptT->fT; if (swapped) {
using std::swap;
swap(priorPtT, ptT);
swap(oppStart, oppEnd);
} const SkOpCoincidence* coincidence = this->globalState()->coincidence(); const SkOpPtT* rootPriorPtT = priorPtT->span()->ptT(); const SkOpPtT* rootPtT = ptT->span()->ptT(); const SkOpPtT* rootOppStart = oppStart->span()->ptT(); const SkOpPtT* rootOppEnd = oppEnd->span()->ptT(); if (coincidence->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) { goto swapBack;
} if (testForCoincidence(rootPriorPtT, rootPtT, prior, spanBase, opp)) { // mark coincidence #if DEBUG_COINCIDENCE_VERBOSE // SkDebugf("%s coinSpan=%d endSpan=%d oppSpan=%d oppEndSpan=%d\n", __FUNCTION__, // rootPriorPtT->debugID(), rootPtT->debugID(), rootOppStart->debugID(), // rootOppEnd->debugID()); #endif
log->record(SkPathOpsDebug::kMissingCoin_Glitch, priorPtT, ptT, oppStart, oppEnd); // coincidences->add(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd); // } #if DEBUG_COINCIDENCE // SkASSERT(coincidences->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd); #endif // result = true;
}
swapBack: if (swapped) {
using std::swap;
swap(priorPtT, ptT);
}
}
} while ((spanBase = spanBase->final() ? nullptr : spanBase->upCast()->next()));
DebugClearVisited(&fHead); return;
}
// commented-out lines keep this in sync with moveMultiples() // if a span has more than one intersection, merge the other segments' span as needed void SkOpSegment::debugMoveMultiples(SkPathOpsDebug::GlitchLog* glitches) const {
debugValidate(); const SkOpSpanBase* test = &fHead; do { int addCount = test->spanAddsCount(); // SkASSERT(addCount >= 1); if (addCount <= 1) { continue;
} const SkOpPtT* startPtT = test->ptT(); const SkOpPtT* testPtT = startPtT; do { // iterate through all spans associated with start const SkOpSpanBase* oppSpan = testPtT->span(); if (oppSpan->spanAddsCount() == addCount) { continue;
} if (oppSpan->deleted()) { continue;
} const SkOpSegment* oppSegment = oppSpan->segment(); if (oppSegment == this) { continue;
} // find range of spans to consider merging const SkOpSpanBase* oppPrev = oppSpan; const SkOpSpanBase* oppFirst = oppSpan; while ((oppPrev = oppPrev->prev())) { if (!roughly_equal(oppPrev->t(), oppSpan->t())) { break;
} if (oppPrev->spanAddsCount() == addCount) { continue;
} if (oppPrev->deleted()) { continue;
}
oppFirst = oppPrev;
} const SkOpSpanBase* oppNext = oppSpan; const SkOpSpanBase* oppLast = oppSpan; while ((oppNext = oppNext->final() ? nullptr : oppNext->upCast()->next())) { if (!roughly_equal(oppNext->t(), oppSpan->t())) { break;
} if (oppNext->spanAddsCount() == addCount) { continue;
} if (oppNext->deleted()) { continue;
}
oppLast = oppNext;
} if (oppFirst == oppLast) { continue;
} const SkOpSpanBase* oppTest = oppFirst; do { if (oppTest == oppSpan) { continue;
} // check to see if the candidate meets specific criteria: // it contains spans of segments in test's loop but not including 'this' const SkOpPtT* oppStartPtT = oppTest->ptT(); const SkOpPtT* oppPtT = oppStartPtT; while ((oppPtT = oppPtT->next()) != oppStartPtT) { const SkOpSegment* oppPtTSegment = oppPtT->segment(); if (oppPtTSegment == this) { goto tryNextSpan;
} const SkOpPtT* matchPtT = startPtT; do { if (matchPtT->segment() == oppPtTSegment) { goto foundMatch;
}
} while ((matchPtT = matchPtT->next()) != startPtT); goto tryNextSpan;
foundMatch: // merge oppTest and oppSpan
oppSegment->debugValidate();
oppTest->debugMergeMatches(glitches, oppSpan);
oppTest->debugAddOpp(glitches, oppSpan);
oppSegment->debugValidate(); goto checkNextSpan;
}
tryNextSpan:
;
} while (oppTest != oppLast && (oppTest = oppTest->upCast()->next()));
} while ((testPtT = testPtT->next()) != startPtT);
checkNextSpan:
;
} while ((test = test->final() ? nullptr : test->upCast()->next()));
debugValidate(); return;
}
// commented-out lines keep this in sync with moveNearby() // Move nearby t values and pts so they all hang off the same span. Alignment happens later. void SkOpSegment::debugMoveNearby(SkPathOpsDebug::GlitchLog* glitches) const {
debugValidate(); // release undeleted spans pointing to this seg that are linked to the primary span const SkOpSpanBase* spanBase = &fHead; do { const SkOpPtT* ptT = spanBase->ptT(); const SkOpPtT* headPtT = ptT; while ((ptT = ptT->next()) != headPtT) { const SkOpSpanBase* test = ptT->span(); if (ptT->segment() == this && !ptT->deleted() && test != spanBase
&& test->ptT() == ptT) { if (test->final()) { if (spanBase == &fHead) {
glitches->record(SkPathOpsDebug::kMoveNearbyClearAll_Glitch, this); // return;
}
glitches->record(SkPathOpsDebug::kMoveNearbyReleaseFinal_Glitch, spanBase, ptT);
} elseif (test->prev()) {
glitches->record(SkPathOpsDebug::kMoveNearbyRelease_Glitch, test, headPtT);
} // break;
}
}
spanBase = spanBase->upCast()->next();
} while (!spanBase->final());
// This loop looks for adjacent spans which are near by
spanBase = &fHead; do { // iterate through all spans associated with start const SkOpSpanBase* test = spanBase->upCast()->next(); bool found; if (!this->spansNearby(spanBase, test, &found)) {
glitches->record(SkPathOpsDebug::kMoveNearbyMergeFinal_Glitch, test);
} if (found) { if (test->final()) { if (spanBase->prev()) {
glitches->record(SkPathOpsDebug::kMoveNearbyMergeFinal_Glitch, test);
} else {
glitches->record(SkPathOpsDebug::kMoveNearbyClearAll2_Glitch, this); // return
}
} else {
glitches->record(SkPathOpsDebug::kMoveNearbyMerge_Glitch, spanBase);
}
}
spanBase = test;
} while (!spanBase->final());
debugValidate();
} #endif
#if DEBUG_SORT void SkOpAngle::debugLoop() const { const SkOpAngle* first = this; const SkOpAngle* next = this; do {
next->dumpOne(true);
SkDebugf("\n");
next = next->fNext;
} while (next && next != first);
next = first; do {
next->debugValidate();
next = next->fNext;
} while (next && next != first);
} #endif
void SkOpAngle::debugValidate() const { #if DEBUG_COINCIDENCE if (this->globalState()->debugCheckHealth()) { return;
} #endif #if DEBUG_VALIDATE const SkOpAngle* first = this; const SkOpAngle* next = this; int wind = 0; int opp = 0; int lastXor = -1; int lastOppXor = -1; do { if (next->unorderable()) { return;
} const SkOpSpan* minSpan = next->start()->starter(next->end()); if (minSpan->windValue() == SK_MinS32) { return;
} bool op = next->segment()->operand(); bool isXor = next->segment()->isXor(); bool oppXor = next->segment()->oppXor();
SkASSERT(!DEBUG_LIMIT_WIND_SUM || between(0, minSpan->windValue(), DEBUG_LIMIT_WIND_SUM));
SkASSERT(!DEBUG_LIMIT_WIND_SUM
|| between(-DEBUG_LIMIT_WIND_SUM, minSpan->oppValue(), DEBUG_LIMIT_WIND_SUM)); bool useXor = op ? oppXor : isXor;
SkASSERT(lastXor == -1 || lastXor == (int) useXor);
lastXor = (int) useXor;
wind += next->debugSign() * (op ? minSpan->oppValue() : minSpan->windValue()); if (useXor) {
wind &= 1;
}
useXor = op ? isXor : oppXor;
SkASSERT(lastOppXor == -1 || lastOppXor == (int) useXor);
lastOppXor = (int) useXor;
opp += next->debugSign() * (op ? minSpan->windValue() : minSpan->oppValue()); if (useXor) {
opp &= 1;
}
next = next->fNext;
} while (next && next != first);
SkASSERT(wind == 0 || !SkPathOpsDebug::gRunFail);
SkASSERT(opp == 0 || !SkPathOpsDebug::gRunFail); #endif
}
void SkOpAngle::debugValidateNext() const { #if !FORCE_RELEASE const SkOpAngle* first = this; const SkOpAngle* next = first;
SkTDArray<const SkOpAngle*> angles; do { // SkASSERT_RELEASE(next->fSegment->debugContains(next));
angles.push_back(next);
next = next->next(); if (next == first) { break;
}
SkASSERT_RELEASE(!angles.contains(next)); if (!next) { return;
}
} while (true); #endif
}
#if DEBUG_COIN // sets the span's end to the ptT referenced by the previous-next void SkCoincidentSpans::debugCorrectOneEnd(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* (SkCoincidentSpans::* getEnd)() const, void (SkCoincidentSpans::*setEnd)(const SkOpPtT* ptT) const ) const { 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) {
log->record(SkPathOpsDebug::kCorrectEnd_Glitch, this, origPtT, testPtT);
}
}
/* Commented-out lines keep this in sync with correctEnds */ // 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::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTStart, nullptr);
this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTEnd, nullptr);
this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTStart, nullptr);
this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTEnd, nullptr);
}
/* Commented-out lines keep this in sync with expand */ // expand the range by checking adjacent spans for coincidence bool SkCoincidentSpans::debugExpand(SkPathOpsDebug::GlitchLog* log) const { 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;
} if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, prev->ptT(), oppPtT);
expanded = true;
} while (false); // actual continues while expansion is possible 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;
} if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, next->ptT(), oppPtT);
expanded = true;
} while (false); // actual continues while expansion is possible return expanded;
}
// Commented-out lines keep this in sync with expand() // expand the range by checking adjacent spans for coincidence bool SkOpCoincidence::debugExpand(SkPathOpsDebug::GlitchLog* log) const { const SkCoincidentSpans* coin = fHead; if (!coin) { returnfalse;
} bool expanded = false; do { if (coin->debugExpand(log)) { // check to see if multiple spans expanded so they are now identical const SkCoincidentSpans* test = fHead; do { if (coin == test) { continue;
} if (coin->coinPtTStart() == test->coinPtTStart()
&& coin->oppPtTStart() == test->oppPtTStart()) { if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, fHead, test->coinPtTStart()); break;
}
} while ((test = test->next()));
expanded = true;
}
} while ((coin = coin->next())); return expanded;
}
// Commented-out lines keep this in sync with mark() /* this sets up the coincidence links in the segments when the coincidence crosses multiple spans */ void SkOpCoincidence::debugMark(SkPathOpsDebug::GlitchLog* log) const { const SkCoincidentSpans* coin = fHead; if (!coin) { return;
} do {
FAIL_IF_COIN(!coin->coinPtTStartWritable()->span()->upCastable(), coin); const SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast(); // SkASSERT(start->deleted()); const SkOpSpanBase* end = coin->coinPtTEndWritable()->span(); // SkASSERT(end->deleted()); const SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span(); // SkASSERT(oStart->deleted()); const SkOpSpanBase* oEnd = coin->oppPtTEndWritable()->span(); // SkASSERT(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 */
start->debugInsertCoincidence(log, oStart->upCast());
end->debugInsertCoinEnd(log, oEnd); const SkOpSegment* segment = start->segment(); const SkOpSegment* oSegment = oStart->segment(); const SkOpSpanBase* next = start; const SkOpSpanBase* oNext = oStart; bool ordered;
FAIL_IF_COIN(!coin->ordered(&ordered), coin); while ((next = next->upCast()->next()) != end) {
FAIL_IF_COIN(!next->upCastable(), coin);
next->upCast()->debugInsertCoincidence(log, oSegment, flipped, ordered);
} while ((oNext = oNext->upCast()->next()) != oEnd) {
FAIL_IF_COIN(!oNext->upCastable(), coin);
oNext->upCast()->debugInsertCoincidence(log, segment, flipped, ordered);
}
} while ((coin = coin->next())); return;
} #endif// DEBUG_COIN
#if DEBUG_COIN // Commented-out lines keep this in sync with markCollapsed() void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkOpPtT* test) const { const SkCoincidentSpans* head = coin; while (coin) { if (coin->collapsed(test)) { if (zero_or_one(coin->coinPtTStart()->fT) && zero_or_one(coin->coinPtTEnd()->fT)) {
log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
} if (zero_or_one(coin->oppPtTStart()->fT) && zero_or_one(coin->oppPtTEnd()->fT)) {
log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
}
this->debugRelease(log, head, coin);
}
coin = coin->next();
}
}
// Commented-out lines keep this in sync with markCollapsed() void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* test) const {
this->debugMarkCollapsed(log, fHead, test);
this->debugMarkCollapsed(log, fTop, test);
} #endif// DEBUG_COIN
// Commented-out lines keep this in sync with addOpp() void SkOpSpanBase::debugAddOpp(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const { const SkOpPtT* oppPrev = this->ptT()->oppPrev(opp->ptT()); if (!oppPrev) { return;
}
this->debugMergeMatches(log, opp);
this->ptT()->debugAddOpp(opp->ptT(), oppPrev);
this->debugCheckForCollapsedCoincidence(log);
}
// Commented-out lines keep this in sync with checkForCollapsedCoincidence() void SkOpSpanBase::debugCheckForCollapsedCoincidence(SkPathOpsDebug::GlitchLog* log) const { const SkOpCoincidence* coins = this->globalState()->coincidence(); if (coins->isEmpty()) { return;
} // the insert above may have put both ends of a coincident run in the same span // for each coincident ptT in loop; see if its opposite in is also in the loop // this implementation is the motivation for marking that a ptT is referenced by a coincident span const SkOpPtT* head = this->ptT(); const SkOpPtT* test = head; do { if (!test->coincident()) { continue;
}
coins->debugMarkCollapsed(log, test);
} while ((test = test->next()) != head);
} #endif
bool SkOpSpanBase::debugCoinEndLoopCheck() const { int loop = 0; const SkOpSpanBase* next = this;
SkOpSpanBase* nextCoin; do {
nextCoin = next->fCoinEnd;
SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin); for (int check = 1; check < loop - 1; ++check) { const SkOpSpanBase* checkCoin = this->fCoinEnd; const SkOpSpanBase* innerCoin = checkCoin; for (int inner = check + 1; inner < loop; ++inner) {
innerCoin = innerCoin->fCoinEnd; if (checkCoin == innerCoin) {
SkDebugf("*** bad coincident end loop ***\n"); returnfalse;
}
}
}
++loop;
} while ((next = nextCoin) && next != this); return true;
}
// Commented-out lines keep this in sync with mergeMatches() // Look to see if pt-t linked list contains same segment more than once // if so, and if each pt-t is directly pointed to by spans in that segment, // merge them // keep the points, but remove spans so that the segment doesn't have 2 or more // spans pointing to the same pt-t loop at different loop elements void SkOpSpanBase::debugMergeMatches(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const { const SkOpPtT* test = &fPtT; const SkOpPtT* testNext; const SkOpPtT* stop = test; do {
testNext = test->next(); if (test->deleted()) { continue;
} const SkOpSpanBase* testBase = test->span();
SkASSERT(testBase->ptT() == test); const SkOpSegment* segment = test->segment(); if (segment->done()) { continue;
} const SkOpPtT* inner = opp->ptT(); const SkOpPtT* innerStop = inner; do { if (inner->segment() != segment) { continue;
} if (inner->deleted()) { continue;
} const SkOpSpanBase* innerBase = inner->span();
SkASSERT(innerBase->ptT() == inner); // when the intersection is first detected, the span base is marked if there are // more than one point in the intersection. // if (!innerBase->hasMultipleHint() && !testBase->hasMultipleHint()) { if (!zero_or_one(inner->fT)) {
log->record(SkPathOpsDebug::kMergeMatches_Glitch, innerBase, test);
} else {
SkASSERT(inner->fT != test->fT); if (!zero_or_one(test->fT)) {
log->record(SkPathOpsDebug::kMergeMatches_Glitch, testBase, inner);
} else {
log->record(SkPathOpsDebug::kMergeMatches_Glitch, segment); // SkDEBUGCODE(testBase->debugSetDeleted()); // test->setDeleted(); // SkDEBUGCODE(innerBase->debugSetDeleted()); // inner->setDeleted();
}
} #ifdef SK_DEBUG // assert if another undeleted entry points to segment const SkOpPtT* debugInner = inner; while ((debugInner = debugInner->next()) != innerStop) { if (debugInner->segment() != segment) { continue;
} if (debugInner->deleted()) { continue;
}
SkOPASSERT(0);
} #endif break; // } break;
} while ((inner = inner->next()) != innerStop);
} while ((test = testNext) != stop);
this->debugCheckForCollapsedCoincidence(log);
}
#endif
void SkOpSpanBase::debugResetCoinT() const { #if DEBUG_COINCIDENCE_ORDER const SkOpPtT* ptT = &fPtT; do {
ptT->debugResetCoinT();
ptT = ptT->next();
} while (ptT != &fPtT); #endif
}
void SkOpSpanBase::debugSetCoinT(int index) const { #if DEBUG_COINCIDENCE_ORDER const SkOpPtT* ptT = &fPtT; do { if (!ptT->deleted()) {
ptT->debugSetCoinT(index);
}
ptT = ptT->next();
} while (ptT != &fPtT); #endif
}
const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const { const SkOpSpanBase* end = *endPtr;
SkASSERT(this->segment() == end->segment()); const SkOpSpanBase* result; if (t() < end->t()) {
result = this;
} else {
result = end;
*endPtr = this;
} return result->upCast();
}
void SkOpSpanBase::debugValidate() const { #if DEBUG_COINCIDENCE if (this->globalState()->debugCheckHealth()) { return;
} #endif #if DEBUG_VALIDATE const SkOpPtT* ptT = &fPtT;
SkASSERT(ptT->span() == this); do { // SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt));
ptT->debugValidate();
ptT = ptT->next();
} while (ptT != &fPtT);
SkASSERT(this->debugCoinEndLoopCheck()); if (!this->final()) {
SkASSERT(this->upCast()->debugCoinLoopCheck());
} if (fFromAngle) {
fFromAngle->debugValidate();
} if (!this->final() && this->upCast()->toAngle()) {
this->upCast()->toAngle()->debugValidate();
} #endif
}
bool SkOpSpan::debugCoinLoopCheck() const { int loop = 0; const SkOpSpan* next = this;
SkOpSpan* nextCoin; do {
nextCoin = next->fCoincident;
SkASSERT(nextCoin == this || nextCoin->fCoincident != nextCoin); for (int check = 1; check < loop - 1; ++check) { const SkOpSpan* checkCoin = this->fCoincident; const SkOpSpan* innerCoin = checkCoin; for (int inner = check + 1; inner < loop; ++inner) {
innerCoin = innerCoin->fCoincident; if (checkCoin == innerCoin) {
SkDebugf("*** bad coincident loop ***\n"); returnfalse;
}
}
}
++loop;
} while ((next = nextCoin) && next != this); return true;
}
#if DEBUG_COIN // Commented-out lines keep this in sync with insertCoincidence() in header void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* coin) const { if (containsCoincidence(coin)) { // SkASSERT(coin->containsCoincidence(this)); return;
}
debugValidate(); // SkASSERT(this != coin);
log->record(SkPathOpsDebug::kMarkCoinStart_Glitch, this, coin); // coin->fCoincident = this->fCoincident; // this->fCoincident = coinNext;
debugValidate();
}
bool SkOpPtT::debugContains(const SkOpPtT* check) const {
SkASSERT(this != check); const SkOpPtT* ptT = this; int links = 0; do {
ptT = ptT->next(); if (ptT == check) { return true;
}
++links; const SkOpPtT* test = this; for (int index = 0; index < links; ++index) { if (ptT == test) { returnfalse;
}
test = test->next();
}
} while (true);
}
const SkOpPtT* SkOpPtT::debugContains(const SkOpSegment* check) const {
SkASSERT(this->segment() != check); const SkOpPtT* ptT = this; int links = 0; do {
ptT = ptT->next(); if (ptT->segment() == check) { return ptT;
}
++links; const SkOpPtT* test = this; for (int index = 0; index < links; ++index) { if (ptT == test) { return nullptr;
}
test = test->next();
}
} while (true);
}
const SkOpPtT* SkOpPtT::debugEnder(const SkOpPtT* end) const { return fT < end->fT ? end : this;
}
int SkOpPtT::debugLoopLimit(bool report) const { int loop = 0; const SkOpPtT* next = this; do { for (int check = 1; check < loop - 1; ++check) { const SkOpPtT* checkPtT = this->fNext; const SkOpPtT* innerPtT = checkPtT; for (int inner = check + 1; inner < loop; ++inner) {
innerPtT = innerPtT->fNext; if (checkPtT == innerPtT) { if (report) {
SkDebugf("*** bad ptT loop ***\n");
} return loop;
}
}
} // there's nothing wrong with extremely large loop counts -- but this may appear to hang // by taking a very long time to figure out that no loop entry is a duplicate // -- and it's likely that a large loop count is indicative of a bug somewhere if (++loop > 1000) {
SkDebugf("*** loop count exceeds 1000 ***\n"); return1000;
}
} while ((next = next->fNext) && next != this); return0;
}
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.