Chromium Code Reviews| Index: src/pathops/SkPathOpsDebug.cpp |
| diff --git a/src/pathops/SkPathOpsDebug.cpp b/src/pathops/SkPathOpsDebug.cpp |
| index 546771e84c6be240e75837dd83f1707bd799e6ec..93743f3d6cfc3f8a18ab75ac0f5332e10d9bc206 100644 |
| --- a/src/pathops/SkPathOpsDebug.cpp |
| +++ b/src/pathops/SkPathOpsDebug.cpp |
| @@ -12,7 +12,7 @@ |
| #include "SkPathOpsDebug.h" |
| #include "SkString.h" |
| -struct SkCoincidentSpans; |
| +class SkCoincidentSpans; |
| #if DEBUG_VALIDATE |
| extern bool FLAGS_runFail; |
| @@ -46,11 +46,15 @@ bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray, |
| } |
| #endif |
| -#if DEBUG_COINCIDENCE |
| +#if DEBUG_COINCIDENCE_VERBOSE |
| enum GlitchType { |
| kAddCorruptCoin_Glitch, |
| kAddExpandedCoin_Glitch, |
| + kAddExpandedFail_Glitch, |
| + kAddIfMissingCoin_Glitch, |
| kAddMissingCoin_Glitch, |
| + kAddMissingExtend_Glitch, |
| + kAddOrOverlap_Glitch, |
| kCollapsedCoin_Glitch, |
| kCollapsedDone_Glitch, |
| kCollapsedOppValue_Glitch, |
| @@ -60,30 +64,41 @@ enum GlitchType { |
| kExpandCoin_Glitch, |
| kMarkCoinEnd_Glitch, |
| kMarkCoinInsert_Glitch, |
| + kMarkCoinMissing_Glitch, |
| + kMarkCoinStart_Glitch, |
| + kMergeContained_Glitch, |
| kMissingCoin_Glitch, |
| kMissingDone_Glitch, |
| kMissingIntersection_Glitch, |
| kMoveMultiple_Glitch, |
| + kMoveNearbyClearAll_Glitch, |
| + kMoveNearbyClearAll2_Glitch, |
| + kMoveNearbyMerge_Glitch, |
| + kMoveNearbyMergeFinal_Glitch, |
| + kMoveNearbyRelease_Glitch, |
| + kMoveNearbyReleaseFinal_Glitch, |
| + kReleasedSpan_Glitch, |
| kUnaligned_Glitch, |
| kUnalignedHead_Glitch, |
| kUnalignedTail_Glitch, |
| - kUndetachedSpan_Glitch, |
| - kUnmergedSpan_Glitch, |
| }; |
| -static const int kGlitchType_Count = kUnmergedSpan_Glitch + 1; |
| +static const int kGlitchType_Count = kUnalignedTail_Glitch + 1; |
| struct SpanGlitch { |
| const char* fStage; |
| const SkOpSpanBase* fBase; |
| const SkOpSpanBase* fSuspect; |
| - const SkCoincidentSpans* fCoin; |
| const SkOpSegment* fSegment; |
| + const SkOpSegment* fOppSegment; |
| const SkOpPtT* fCoinSpan; |
| const SkOpPtT* fEndSpan; |
| const SkOpPtT* fOppSpan; |
| const SkOpPtT* fOppEndSpan; |
| - double fT; |
| + double fStartT; |
| + double fEndT; |
| + double fOppStartT; |
| + double fOppEndT; |
| SkPoint fPt; |
| GlitchType fType; |
| }; |
| @@ -94,13 +109,16 @@ struct SkPathOpsDebug::GlitchLog { |
| glitch->fStage = stage; |
| glitch->fBase = nullptr; |
| glitch->fSuspect = nullptr; |
| - glitch->fCoin = nullptr; |
| glitch->fSegment = nullptr; |
| + glitch->fOppSegment = nullptr; |
| glitch->fCoinSpan = nullptr; |
| glitch->fEndSpan = nullptr; |
| glitch->fOppSpan = nullptr; |
| glitch->fOppEndSpan = nullptr; |
| - glitch->fT = SK_ScalarNaN; |
| + glitch->fStartT = SK_ScalarNaN; |
| + glitch->fEndT = SK_ScalarNaN; |
| + glitch->fOppStartT = SK_ScalarNaN; |
| + glitch->fOppEndT = SK_ScalarNaN; |
| glitch->fPt = { SK_ScalarNaN, SK_ScalarNaN }; |
| glitch->fType = type; |
| return glitch; |
| @@ -113,11 +131,22 @@ struct SkPathOpsDebug::GlitchLog { |
| glitch->fSuspect = suspect; |
| } |
| + void record(GlitchType type, const char* stage, const SkOpSpanBase* base, |
| + const SkOpPtT* ptT) { |
| + SpanGlitch* glitch = recordCommon(type, stage); |
| + glitch->fBase = base; |
| + glitch->fCoinSpan = ptT; |
| + } |
| + |
| void record(GlitchType type, const char* stage, const SkCoincidentSpans* coin, |
| - const SkOpPtT* coinSpan) { |
| + const SkCoincidentSpans* opp = NULL) { |
| SpanGlitch* glitch = recordCommon(type, stage); |
| - glitch->fCoin = coin; |
| - glitch->fCoinSpan = coinSpan; |
| + glitch->fCoinSpan = coin->coinPtTStart(); |
| + glitch->fEndSpan = coin->coinPtTEnd(); |
| + if (opp) { |
| + glitch->fOppSpan = opp->coinPtTStart(); |
| + glitch->fOppEndSpan = opp->coinPtTEnd(); |
| + } |
| } |
| void record(GlitchType type, const char* stage, const SkOpSpanBase* base, |
| @@ -125,7 +154,7 @@ struct SkPathOpsDebug::GlitchLog { |
| SpanGlitch* glitch = recordCommon(type, stage); |
| glitch->fBase = base; |
| glitch->fSegment = seg; |
| - glitch->fT = t; |
| + glitch->fStartT = t; |
| glitch->fPt = pt; |
| } |
| @@ -133,23 +162,26 @@ struct SkPathOpsDebug::GlitchLog { |
| SkPoint pt) { |
| SpanGlitch* glitch = recordCommon(type, stage); |
| glitch->fBase = base; |
| - glitch->fT = t; |
| + glitch->fStartT = t; |
| glitch->fPt = pt; |
| } |
| void record(GlitchType type, const char* stage, const SkCoincidentSpans* coin, |
| const SkOpPtT* coinSpan, const SkOpPtT* endSpan) { |
| SpanGlitch* glitch = recordCommon(type, stage); |
| - glitch->fCoin = coin; |
| - glitch->fCoinSpan = coinSpan; |
| + glitch->fCoinSpan = coin->coinPtTStart(); |
| + glitch->fEndSpan = coin->coinPtTEnd(); |
| glitch->fEndSpan = endSpan; |
| + glitch->fOppSpan = coinSpan; |
| + glitch->fOppEndSpan = endSpan; |
| } |
| void record(GlitchType type, const char* stage, const SkCoincidentSpans* coin, |
| - const SkOpSpanBase* suspect) { |
| + const SkOpSpanBase* base) { |
| SpanGlitch* glitch = recordCommon(type, stage); |
| - glitch->fSuspect = suspect; |
| - glitch->fCoin = coin; |
| + glitch->fBase = base; |
| + glitch->fCoinSpan = coin->coinPtTStart(); |
| + glitch->fEndSpan = coin->coinPtTEnd(); |
| } |
| void record(GlitchType type, const char* stage, const SkOpPtT* ptTS, const SkOpPtT* ptTE, |
| @@ -161,22 +193,73 @@ struct SkPathOpsDebug::GlitchLog { |
| glitch->fOppEndSpan = oPtTE; |
| } |
| + void record(GlitchType type, const char* stage, const SkOpSegment* seg, double startT, |
| + double endT, const SkOpSegment* oppSeg, double oppStartT, double oppEndT) { |
| + SpanGlitch* glitch = recordCommon(type, stage); |
| + glitch->fSegment = seg; |
| + glitch->fStartT = startT; |
| + glitch->fEndT = endT; |
| + glitch->fOppSegment = oppSeg; |
| + glitch->fOppStartT = oppStartT; |
| + glitch->fOppEndT = oppEndT; |
| + } |
| + |
| + void record(GlitchType type, const char* stage, const SkOpSegment* seg, |
| + const SkOpSpan* span) { |
| + SpanGlitch* glitch = recordCommon(type, stage); |
| + glitch->fSegment = seg; |
| + glitch->fBase = span; |
| + } |
| + |
| + void record(GlitchType type, const char* stage, double t, const SkOpSpanBase* span) { |
| + SpanGlitch* glitch = recordCommon(type, stage); |
| + glitch->fStartT = t; |
| + glitch->fBase = span; |
| + } |
| + |
| + void record(GlitchType type, const char* stage, const SkOpSegment* seg) { |
| + SpanGlitch* glitch = recordCommon(type, stage); |
| + glitch->fSegment = seg; |
| + } |
| + |
| + void record(GlitchType type, const char* stage, const SkCoincidentSpans* coin, |
| + const SkOpPtT* ptT) { |
| + SpanGlitch* glitch = recordCommon(type, stage); |
| + glitch->fCoinSpan = coin->coinPtTStart(); |
| + glitch->fEndSpan = ptT; |
| + } |
| + |
| SkTDArray<SpanGlitch> fGlitches; |
| }; |
| +#endif |
| +void SkPathOpsDebug::ShowActiveSpans(SkOpContourHead* contourList) { |
| +#if DEBUG_ACTIVE_SPANS |
| + SkOpContour* contour = contourList; |
| + do { |
| + contour->debugShowActiveSpans(); |
| + } while ((contour = contour->next())); |
| +#endif |
| +} |
| + |
| +#if DEBUG_COINCIDENCE |
| void SkPathOpsDebug::CheckHealth(SkOpContourHead* contourList, const char* id) { |
| + contourList->globalState()->debugSetCheckHealth(true); |
| +#if DEBUG_COINCIDENCE_VERBOSE |
| GlitchLog glitches; |
| const SkOpContour* contour = contourList; |
| const SkOpCoincidence* coincidence = contour->globalState()->coincidence(); |
| + coincidence->debugCheckValid(id, &glitches); // don't call validate; spans may be inconsistent |
| do { |
| contour->debugCheckHealth(id, &glitches); |
| - contour->debugMissingCoincidence(id, &glitches, coincidence); |
| + contour->debugMissingCoincidence(id, &glitches); |
| } while ((contour = contour->next())); |
| - coincidence->debugFixAligned(id, &glitches); |
| + coincidence->debugRemoveCollapsed(id, &glitches); |
| coincidence->debugAddMissing(id, &glitches); |
| coincidence->debugExpand(id, &glitches); |
| coincidence->debugAddExpanded(id, &glitches); |
| coincidence->debugMark(id, &glitches); |
| + coincidence->debugReorder(id, &glitches); |
| unsigned mask = 0; |
| for (int index = 0; index < glitches.fGlitches.count(); ++index) { |
| const SpanGlitch& glitch = glitches.fGlitches[index]; |
| @@ -186,6 +269,92 @@ void SkPathOpsDebug::CheckHealth(SkOpContourHead* contourList, const char* id) { |
| SkDebugf(mask & (1 << index) ? "x" : "-"); |
| } |
| SkDebugf(" %s\n", id); |
| + for (int index = 0; index < glitches.fGlitches.count(); ++index) { |
| + const SpanGlitch& glitch = glitches.fGlitches[index]; |
| + SkDebugf("%02d: ", index); |
| + if (glitch.fBase) { |
| + SkDebugf(" base=%d", glitch.fBase->debugID()); |
| + } |
| + if (glitch.fSuspect) { |
| + SkDebugf(" base=%d", glitch.fSuspect->debugID()); |
| + } |
| + if (glitch.fSegment) { |
| + SkDebugf(" segment=%d", glitch.fSegment->debugID()); |
| + } |
| + if (glitch.fCoinSpan) { |
| + SkDebugf(" coinSpan=%d", glitch.fCoinSpan->debugID()); |
| + } |
| + if (glitch.fEndSpan) { |
| + SkDebugf(" endSpan=%d", glitch.fEndSpan->debugID()); |
| + } |
| + if (glitch.fOppSpan) { |
| + SkDebugf(" oppSpan=%d", glitch.fOppSpan->debugID()); |
| + } |
| + if (glitch.fOppEndSpan) { |
| + SkDebugf(" oppEndSpan=%d", glitch.fOppEndSpan->debugID()); |
| + } |
| + if (!SkScalarIsNaN(glitch.fStartT)) { |
| + SkDebugf(" startT=%g", glitch.fStartT); |
| + } |
| + if (!SkScalarIsNaN(glitch.fEndT)) { |
| + SkDebugf(" endT=%g", glitch.fEndT); |
| + } |
| + if (glitch.fOppSegment) { |
| + SkDebugf(" segment=%d", glitch.fOppSegment->debugID()); |
| + } |
| + if (!SkScalarIsNaN(glitch.fOppStartT)) { |
| + SkDebugf(" oppStartT=%g", glitch.fOppStartT); |
| + } |
| + if (!SkScalarIsNaN(glitch.fOppEndT)) { |
| + SkDebugf(" oppEndT=%g", glitch.fOppEndT); |
| + } |
| + if (!SkScalarIsNaN(glitch.fPt.fX) || !SkScalarIsNaN(glitch.fPt.fY)) { |
| + SkDebugf(" pt=%g,%g", glitch.fPt.fX, glitch.fPt.fY); |
| + } |
| + switch (glitch.fType) { |
| + case kAddCorruptCoin_Glitch: SkDebugf(" AddCorruptCoin"); break; |
| + case kAddExpandedCoin_Glitch: SkDebugf(" AddExpandedCoin"); break; |
| + case kAddExpandedFail_Glitch: SkDebugf(" AddExpandedFail"); break; |
| + case kAddIfMissingCoin_Glitch: SkDebugf(" AddIfMissingCoin"); break; |
| + case kAddMissingCoin_Glitch: SkDebugf(" AddMissingCoin"); break; |
| + case kAddMissingExtend_Glitch: SkDebugf(" AddMissingExtend"); break; |
| + case kAddOrOverlap_Glitch: SkDebugf(" AAddOrOverlap"); break; |
| + case kCollapsedCoin_Glitch: SkDebugf(" CollapsedCoin"); break; |
| + case kCollapsedDone_Glitch: SkDebugf(" CollapsedDone"); break; |
| + case kCollapsedOppValue_Glitch: SkDebugf(" CollapsedOppValue"); break; |
| + case kCollapsedSpan_Glitch: SkDebugf(" CollapsedSpan"); break; |
| + case kCollapsedWindValue_Glitch: SkDebugf(" CollapsedWindValue"); break; |
| + case kDeletedCoin_Glitch: SkDebugf(" DeletedCoin"); break; |
| + case kExpandCoin_Glitch: SkDebugf(" ExpandCoin"); break; |
| + case kMarkCoinEnd_Glitch: SkDebugf(" MarkCoinEnd"); break; |
| + case kMarkCoinInsert_Glitch: SkDebugf(" MarkCoinInsert"); break; |
| + case kMarkCoinMissing_Glitch: SkDebugf(" MarkCoinMissing"); break; |
| + case kMarkCoinStart_Glitch: SkDebugf(" MarkCoinStart"); break; |
| + case kMergeContained_Glitch: SkDebugf(" MergeContained"); break; |
| + case kMissingCoin_Glitch: SkDebugf(" MissingCoin"); break; |
| + case kMissingDone_Glitch: SkDebugf(" MissingDone"); break; |
| + case kMissingIntersection_Glitch: SkDebugf(" MissingIntersection"); break; |
| + case kMoveMultiple_Glitch: SkDebugf(" MoveMultiple"); break; |
| + case kMoveNearbyClearAll_Glitch: SkDebugf(" MoveNearbyClearAll"); break; |
| + case kMoveNearbyClearAll2_Glitch: SkDebugf(" MoveNearbyClearAll2"); break; |
| + case kMoveNearbyMerge_Glitch: SkDebugf(" MoveNearbyMerge"); break; |
| + case kMoveNearbyMergeFinal_Glitch: SkDebugf(" MoveNearbyMergeFinal"); break; |
| + case kMoveNearbyRelease_Glitch: SkDebugf(" MoveNearbyRelease"); break; |
| + case kMoveNearbyReleaseFinal_Glitch: SkDebugf(" MoveNearbyReleaseFinal"); break; |
| + case kReleasedSpan_Glitch: SkDebugf(" ReleasedSpan"); break; |
| + case kUnaligned_Glitch: SkDebugf(" Unaligned"); break; |
| + case kUnalignedHead_Glitch: SkDebugf(" UnalignedHead"); break; |
| + case kUnalignedTail_Glitch: SkDebugf(" UnalignedTail"); break; |
| + default: SkASSERT(0); |
| + } |
| + SkDebugf("\n"); |
| + } |
| + contourList->globalState()->debugSetCheckHealth(false); |
| +#if DEBUG_ACTIVE_SPANS |
| + SkDebugf("active after %s:\n", id); |
| + ShowActiveSpans(contourList); |
| +#endif |
| +#endif |
| } |
| #endif |
| @@ -254,7 +423,7 @@ static const char* gOpStrs[] = { |
| "kDifference_SkPathOp", |
| "kIntersect_SkPathOp", |
| "kUnion_SkPathOp", |
| - "kXor_PathOp", |
| + "kXOR_PathOp", |
| "kReverseDifference_SkPathOp", |
| }; |
| @@ -412,132 +581,72 @@ void SkDRect::debugInit() { |
| #include "SkOpSegment.h" |
| #if DEBUG_COINCIDENCE |
| -void SkOpSegment::debugAddAlignIntersection(const char* id, SkPathOpsDebug::GlitchLog* log, |
| - const SkOpPtT& endPtT, const SkPoint& oldPt, const SkOpContourHead* contourList) const { |
| - const SkPoint& newPt = endPtT.fPt; |
| - if (newPt == oldPt) { |
| - return; |
| - } |
| - SkPoint line[2] = { newPt, oldPt }; |
| - SkPathOpsBounds lineBounds; |
| - lineBounds.setBounds(line, 2); |
| - SkDLine aLine; |
| - aLine.set(line); |
| - const SkOpContour* current = contourList; |
| +// commented-out lines keep this in sync with addT() |
| + const SkOpPtT* SkOpSegment::debugAddT(double t, AliasMatch allowAlias, bool* allocated) const { |
| + debugValidate(); |
| + SkPoint pt = this->ptAtT(t); |
| + const SkOpSpanBase* span = &fHead; |
| do { |
| - if (!SkPathOpsBounds::Intersects(current->bounds(), lineBounds)) { |
| - continue; |
| - } |
| - const SkOpSegment* segment = current->first(); |
| - do { |
| - if (!SkPathOpsBounds::Intersects(segment->bounds(), lineBounds)) { |
| - continue; |
| - } |
| - if (newPt == segment->fPts[0]) { |
| - continue; |
| - } |
| - if (newPt == segment->fPts[SkPathOpsVerbToPoints(segment->fVerb)]) { |
| - continue; |
| - } |
| - if (oldPt == segment->fPts[0]) { |
| - continue; |
| - } |
| - if (oldPt == segment->fPts[SkPathOpsVerbToPoints(segment->fVerb)]) { |
| - continue; |
| - } |
| - if (endPtT.debugContains(segment)) { |
| - continue; |
| - } |
| - SkIntersections i; |
| - switch (segment->fVerb) { |
| - case SkPath::kLine_Verb: { |
| - SkDLine bLine; |
| - bLine.set(segment->fPts); |
| - i.intersect(bLine, aLine); |
| - } break; |
| - case SkPath::kQuad_Verb: { |
| - SkDQuad bQuad; |
| - bQuad.set(segment->fPts); |
| - i.intersect(bQuad, aLine); |
| - } break; |
| - case SkPath::kConic_Verb: { |
| - SkDConic bConic; |
| - bConic.set(segment->fPts, segment->fWeight); |
| - i.intersect(bConic, aLine); |
| - } break; |
| - case SkPath::kCubic_Verb: { |
| - SkDCubic bCubic; |
| - bCubic.set(segment->fPts); |
| - i.intersect(bCubic, aLine); |
| - } break; |
| - default: |
| - SkASSERT(0); |
| - } |
| - if (i.used()) { |
| - SkASSERT(i.used() == 1); |
| - SkASSERT(!zero_or_one(i[0][0])); |
| - SkOpSpanBase* checkSpan = fHead.next(); |
| - while (!checkSpan->final()) { |
| - if (checkSpan->contains(segment)) { |
| - goto nextSegment; |
| - } |
| - checkSpan = checkSpan->upCast()->next(); |
| + const SkOpPtT* result = span->ptT(); |
| + const SkOpPtT* loop; |
| + bool duplicatePt; |
| + if (t == result->fT) { |
| + goto bumpSpan; |
| + } |
| + if (this->match(result, this, t, pt, allowAlias)) { |
| + // see if any existing alias matches segment, pt, and t |
| + loop = result->next(); |
| + duplicatePt = false; |
| + while (loop != result) { |
| + bool ptMatch = loop->fPt == pt; |
| + if (loop->segment() == this && loop->fT == t && ptMatch) { |
| + goto bumpSpan; |
| } |
| - log->record(kMissingIntersection_Glitch, id, checkSpan, segment, i[0][0], newPt); |
| + duplicatePt |= ptMatch; |
| + loop = loop->next(); |
| } |
| - nextSegment: |
| - ; |
| - } while ((segment = segment->next())); |
| - } while ((current = current->next())); |
| -} |
| - |
| -bool SkOpSegment::debugAddMissing(double t, const SkOpSegment* opp) const { |
| - const SkOpSpanBase* existing = nullptr; |
| - const SkOpSpanBase* test = &fHead; |
| - double testT; |
| - do { |
| - if ((testT = test->ptT()->fT) >= t) { |
| - if (testT == t) { |
| - existing = test; |
| + if (kNoAliasMatch == allowAlias) { |
| + bumpSpan: |
| +// span->bumpSpanAdds(); |
| + return result; |
| } |
| - break; |
| - } |
| - } while ((test = test->upCast()->next())); |
| - return !existing || !existing->debugContains(opp); |
| -} |
| - |
| -void SkOpSegment::debugAlign(const char* id, SkPathOpsDebug::GlitchLog* glitches) const { |
| - const SkOpSpanBase* span = &fHead; |
| - if (!span->aligned()) { |
| - if (!span->debugAlignedEnd(0, fPts[0])) { |
| - glitches->record(kUnalignedHead_Glitch, id, span); |
| - } |
| - } |
| - while ((span = span->upCast()->next())) { |
| - if (span == &fTail) { |
| - break; |
| - } |
| - if (!span->aligned()) { |
| - glitches->record(kUnaligned_Glitch, id, span); |
| - } |
| - } |
| - if (!span->aligned()) { |
| - span->debugAlignedEnd(1, fPts[SkPathOpsVerbToPoints(fVerb)]); |
| - } |
| - if (this->collapsed()) { |
| - const SkOpSpan* span = &fHead; |
| - do { |
| - if (span->windValue()) { |
| - glitches->record(kCollapsedWindValue_Glitch, id, span); |
| +// SkOpPtT* alias = SkOpTAllocator<SkOpPtT>::Allocate(allocator); |
| +// alias->init(result->span(), t, pt, duplicatePt); |
| +// result->insert(alias); |
| +// result->span()->unaligned(); |
| + this->debugValidate(); |
| +// #if DEBUG_ADD_T |
| +// SkDebugf("%s alias t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t, |
| +// alias->segment()->debugID(), alias->span()->debugID()); |
| +// #endif |
| +// span->bumpSpanAdds(); |
| + if (allocated) { |
| + *allocated = true; |
| } |
| - if (span->oppValue()) { |
| - glitches->record(kCollapsedOppValue_Glitch, id, span); |
| + return nullptr; |
| + } |
| + if (t < result->fT) { |
| + const SkOpSpan* prev = result->span()->prev(); |
| + if (!prev) { |
| + return nullptr; // FIXME: this is a fail case; nullptr return elsewhere means result was allocated in non-const version |
| } |
| - if (!span->done()) { |
| - glitches->record(kCollapsedDone_Glitch, id, span); |
| +// SkOpSpan* span = insert(prev, allocator); |
| +// 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(); |
| + if (allocated) { |
| + *allocated = true; |
| } |
| - } while ((span = span->next()->upCastable())); |
| - } |
| + return nullptr; |
| + } |
| + SkASSERT(span != &fTail); |
| + } while ((span = span->upCast()->next())); |
| + SkASSERT(0); |
| + return nullptr; |
| } |
| #endif |
| @@ -547,7 +656,7 @@ void SkOpSegment::debugCheckAngleCoin() const { |
| const SkOpSpan* span; |
| do { |
| const SkOpAngle* angle = base->fromAngle(); |
| - if (angle && angle->fCheckCoincidence) { |
| + if (angle && angle->debugCheckCoincidence()) { |
| angle->debugCheckNearCoincidence(); |
| } |
| if (base->final()) { |
| @@ -555,41 +664,35 @@ void SkOpSegment::debugCheckAngleCoin() const { |
| } |
| span = base->upCast(); |
| angle = span->toAngle(); |
| - if (angle && angle->fCheckCoincidence) { |
| + if (angle && angle->debugCheckCoincidence()) { |
| angle->debugCheckNearCoincidence(); |
| } |
| } while ((base = span->next())); |
| } |
| #endif |
| -#if DEBUG_COINCIDENCE |
| +#if DEBUG_COINCIDENCE_VERBOSE |
| // this mimics the order of the checks in handle coincidence |
| void SkOpSegment::debugCheckHealth(const char* id, SkPathOpsDebug::GlitchLog* glitches) const { |
| debugMoveMultiples(id, glitches); |
| - debugFindCollapsed(id, glitches); |
| debugMoveNearby(id, glitches); |
| - debugAlign(id, glitches); |
| - debugAddAlignIntersections(id, glitches, this->globalState()->contourHead()); |
| + debugMissingCoincidence(id, glitches); |
| +} |
| +// commented-out lines keep this in sync with clearAll() |
| +void SkOpSegment::debugClearAll(const char* id, SkPathOpsDebug::GlitchLog* glitches) const { |
| + const SkOpSpan* span = &fHead; |
| + do { |
| + this->debugClearOne(span, id, glitches); |
| + } while ((span = span->next()->upCastable())); |
| + this->globalState()->coincidence()->debugRelease(id, glitches, this); |
| } |
| -void SkOpSegment::debugFindCollapsed(const char* id, SkPathOpsDebug::GlitchLog* glitches) const { |
| - if (fHead.contains(&fTail)) { |
| - const SkOpSpan* span = this->head(); |
| - bool missingDone = false; |
| - do { |
| - missingDone |= !span->done(); |
| - } while ((span = span->next()->upCastable())); |
| - if (missingDone) { |
| - glitches->record(kMissingDone_Glitch, id, &fHead); |
| - } |
| - if (!fHead.debugAlignedEnd(0, fHead.pt())) { |
| - glitches->record(kUnalignedHead_Glitch, id, &fHead); |
| - } |
| - if (!fTail.aligned()) { |
| - glitches->record(kUnalignedTail_Glitch, id, &fTail); |
| - } |
| - } |
| +// commented-out lines keep this in sync with clearOne() |
| +void SkOpSegment::debugClearOne(const SkOpSpan* span, const char* id, SkPathOpsDebug::GlitchLog* glitches) const { |
| + if (span->windValue()) glitches->record(kCollapsedWindValue_Glitch, id, span); |
| + if (span->oppValue()) glitches->record(kCollapsedOppValue_Glitch, id, span); |
| + if (!span->done()) glitches->record(kCollapsedDone_Glitch, id, span); |
| } |
| #endif |
| @@ -607,16 +710,37 @@ SkOpAngle* SkOpSegment::debugLastAngle() { |
| } |
| #if DEBUG_COINCIDENCE |
| -void SkOpSegment::debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log, |
| - const SkOpCoincidence* coincidences) const { |
| - if (this->verb() != SkPath::kLine_Verb) { |
| - return; |
| - } |
| +// 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_COINCIDENCE_VERBOSE |
| +// 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(const char* id, 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); |
| @@ -624,29 +748,29 @@ void SkOpSegment::debugMissingCoincidence(const char* id, SkPathOpsDebug::Glitch |
| if (ptT->deleted()) { |
| continue; |
| } |
| - SkOpSegment* opp = ptT->span()->segment(); |
| -// if (opp->verb() == SkPath::kLine_Verb) { |
| -// 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->visited()) { |
| + 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)) { |
| + if (span && span->segment() != opp && span->containsCoincidence(opp)) { // debug has additional condition since it may be called before inner duplicate points have been deleted |
|
herb_g
2016/07/18 15:13:39
Comments on next line?
caryclark
2016/07/18 15:55:49
The crazy formatting here is so that SkOpSegment::
|
| continue; |
| } |
| - if (spanBase->segment() != opp && spanBase->containsCoinEnd(opp)) { |
| + 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; |
| @@ -669,6 +793,9 @@ void SkOpSegment::debugMissingCoincidence(const char* id, SkPathOpsDebug::Glitch |
| if (!priorOpp) { |
| continue; |
| } |
| + if (priorPtT == ptT) { |
| + continue; |
| + } |
| const SkOpPtT* oppStart = prior->ptT(); |
| const SkOpPtT* oppEnd = spanBase->ptT(); |
| bool swapped = priorPtT->fT > ptT->fT; |
| @@ -676,22 +803,28 @@ void SkOpSegment::debugMissingCoincidence(const char* id, SkPathOpsDebug::Glitch |
| SkTSwap(priorPtT, ptT); |
| SkTSwap(oppStart, oppEnd); |
| } |
| - bool flipped = oppStart->fT > oppEnd->fT; |
| - bool coincident = false; |
| - if (coincidences->contains(priorPtT, ptT, oppStart, oppEnd, flipped)) { |
| + 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 (opp->verb() == SkPath::kLine_Verb) { |
| - coincident = (SkDPoint::ApproximatelyEqual(priorPtT->fPt, oppStart->fPt) || |
| - SkDPoint::ApproximatelyEqual(priorPtT->fPt, oppEnd->fPt)) && |
| - (SkDPoint::ApproximatelyEqual(ptT->fPt, oppStart->fPt) || |
| - SkDPoint::ApproximatelyEqual(ptT->fPt, oppEnd->fPt)); |
| - } |
| - if (!coincident) { |
| - coincident = testForCoincidence(priorPtT, ptT, prior, spanBase, opp, 5000); |
| - } |
| - if (coincident) { |
| + if (testForCoincidence(rootPriorPtT, rootPtT, prior, spanBase, opp)) { |
| + // mark coincidence |
| +#if DEBUG_COINCIDENCE |
| +// SkDebugf("%s coinSpan=%d endSpan=%d oppSpan=%d oppEndSpan=%d\n", __FUNCTION__, |
|
herb_g
2016/07/18 15:13:39
Remove commented out code.
caryclark
2016/07/18 15:55:49
See above
|
| +// rootPriorPtT->debugID(), rootPtT->debugID(), rootOppStart->debugID(), |
| +// rootOppEnd->debugID()); |
| +#endif |
| log->record(kMissingCoin_Glitch, id, 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) { |
| @@ -699,9 +832,14 @@ void SkOpSegment::debugMissingCoincidence(const char* id, SkPathOpsDebug::Glitch |
| } |
| } |
| } 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(const char* id, SkPathOpsDebug::GlitchLog* glitches) const { |
| + debugValidate(); |
| const SkOpSpanBase* test = &fHead; |
| do { |
| int addCount = test->spanAddsCount(); |
| @@ -777,6 +915,7 @@ void SkOpSegment::debugMoveMultiples(const char* id, SkPathOpsDebug::GlitchLog* |
| } while ((matchPtT = matchPtT->next()) != startPtT); |
| goto tryNextSpan; |
| foundMatch: // merge oppTest and oppSpan |
| + oppSegment->debugValidate(); |
| if (oppTest == &oppSegment->fTail || oppTest == &oppSegment->fHead) { |
| SkASSERT(oppSpan != &oppSegment->fHead); // don't expect collapse |
| SkASSERT(oppSpan != &oppSegment->fTail); |
| @@ -784,60 +923,67 @@ void SkOpSegment::debugMoveMultiples(const char* id, SkPathOpsDebug::GlitchLog* |
| } else { |
| glitches->record(kMoveMultiple_Glitch, id, oppSpan, oppTest); |
| } |
| + oppSegment->debugValidate(); |
| goto checkNextSpan; |
| } |
| - tryNextSpan: |
| + tryNextSpan: |
| ; |
| } while (oppTest != oppLast && (oppTest = oppTest->upCast()->next())); |
| } while ((testPtT = testPtT->next()) != startPtT); |
| -checkNextSpan: |
| +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(const char* id, SkPathOpsDebug::GlitchLog* glitches) const { |
| - const SkOpSpanBase* spanS = &fHead; |
| + debugValidate(); |
| + // release undeleted spans pointing to this seg that are linked to the primary span |
| + const SkOpSpanBase* spanBase = &fHead; |
| do { |
| - const SkOpSpanBase* test = spanS->upCast()->next(); |
| - const SkOpSpanBase* next; |
| - if (spanS->contains(test)) { |
| - if (!test->final()) { |
| - glitches->record(kUndetachedSpan_Glitch, id, test, spanS); |
| - } else if (spanS != &fHead) { |
| - glitches->record(kUndetachedSpan_Glitch, id, spanS, test); |
| + 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(kMoveNearbyClearAll_Glitch, id, this); |
| +// return; |
|
herb_g
2016/07/18 15:13:39
Remove commented out code.
caryclark
2016/07/18 15:55:49
See comment above.
|
| + } |
| + glitches->record(kMoveNearbyReleaseFinal_Glitch, id, spanBase, ptT); |
| + } else if (test->prev()) { |
| + glitches->record(kMoveNearbyRelease_Glitch, id, test, headPtT); |
| + } |
| +// break; |
| } |
| } |
| - do { // iterate through all spans associated with start |
| - const SkOpPtT* startBase = spanS->ptT(); |
| - next = test->final() ? nullptr : test->upCast()->next(); |
| - do { |
| - const SkOpPtT* testBase = test->ptT(); |
| - do { |
| - if (startBase == testBase) { |
| - goto checkNextSpan; |
| - } |
| - if (testBase->duplicate()) { |
| - continue; |
| - } |
| - if (this->match(startBase, testBase->segment(), testBase->fT, testBase->fPt)) { |
| - if (test == &this->fTail) { |
| - if (spanS == &fHead) { |
| - glitches->record(kCollapsedSpan_Glitch, id, spanS); |
| - } else { |
| - glitches->record(kUnmergedSpan_Glitch, id, &this->fTail, spanS); |
| - } |
| - } else { |
| - glitches->record(kUnmergedSpan_Glitch, id, spanS, test); |
| - goto checkNextSpan; |
| - } |
| - } |
| - } while ((testBase = testBase->next()) != test->ptT()); |
| - } while ((startBase = startBase->next()) != spanS->ptT()); |
| - checkNextSpan: |
| - ; |
| - } while ((test = next)); |
| - spanS = spanS->upCast()->next(); |
| - } while (!spanS->final()); |
| + 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(); |
| + if (this->spansNearby(spanBase, test)) { |
| + if (test->final()) { |
| + if (spanBase->prev()) { |
| + glitches->record(kMoveNearbyMergeFinal_Glitch, id, test); |
| + } else { |
| + glitches->record(kMoveNearbyClearAll2_Glitch, id, this); |
| + // return |
| + } |
| + } else { |
| + glitches->record(kMoveNearbyMerge_Glitch, id, spanBase); |
| + } |
| + } |
| + spanBase = test; |
| + } while (!spanBase->final()); |
| + debugValidate(); |
| } |
| #endif |
| @@ -864,16 +1010,18 @@ void SkOpSegment::debugShowActiveSpans() const { |
| lastId = this->debugID(); |
| lastT = span->t(); |
| SkDebugf("%s id=%d", __FUNCTION__, this->debugID()); |
| - SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY); |
| + // since endpoints may have be adjusted, show actual computed curves |
| + SkDCurve curvePart; |
| + this->subDivide(span, span->next(), &curvePart); |
| + const SkDPoint* pts = curvePart.fCubic.fPts; |
| + SkDebugf(" (%1.9g,%1.9g", pts[0].fX, pts[0].fY); |
| for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) { |
| - SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY); |
| + SkDebugf(" %1.9g,%1.9g", pts[vIndex].fX, pts[vIndex].fY); |
| } |
| if (SkPath::kConic_Verb == fVerb) { |
| - SkDebugf(" %1.9gf", fWeight); |
| + SkDebugf(" %1.9gf", curvePart.fConic.fWeight); |
| } |
| - const SkOpPtT* ptT = span->ptT(); |
| - SkDebugf(") t=%1.9g (%1.9g,%1.9g)", ptT->fT, ptT->fPt.fX, ptT->fPt.fY); |
| - SkDebugf(" tEnd=%1.9g", span->next()->t()); |
| + SkDebugf(") t=%1.9g tEnd=%1.9g", span->t(), span->next()->t()); |
| if (span->windSum() == SK_MinS32) { |
| SkDebugf(" windSum=?"); |
| } else { |
| @@ -958,7 +1106,7 @@ void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int |
| // loop looking for a pair of angle parts that are too close to be sorted |
| /* This is called after other more simple intersection and angle sorting tests have been exhausted. |
| This should be rarely called -- the test below is thorough and time consuming. |
| - This checks the distance between start points; the distance between |
| + This checks the distance between start points; the distance between |
| */ |
| #if DEBUG_ANGLE |
| void SkOpAngle::debugCheckNearCoincidence() const { |
| @@ -996,7 +1144,7 @@ void SkOpAngle::debugCheckNearCoincidence() const { |
| SkDebugf("\n"); |
| } |
| test = test->fNext; |
| - } while (test->fNext != this); |
| + } while (test->fNext != this); |
| } |
| #endif |
| @@ -1046,6 +1194,11 @@ void SkOpAngle::debugLoop() const { |
| #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; |
| @@ -1108,243 +1261,409 @@ void SkOpAngle::debugValidateNext() const { |
| #endif |
| } |
| +#ifdef SK_DEBUG |
| +void SkCoincidentSpans::debugStartCheck(const SkOpSpanBase* outer, const SkOpSpanBase* over, |
| + const SkOpGlobalState* debugState) const { |
| + SkASSERT(coinPtTEnd()->span() == over || !debugState->debugRunFail()); |
| + SkASSERT(oppPtTEnd()->span() == outer || !debugState->debugRunFail()); |
| +} |
| +#endif |
| -#if DEBUG_COINCIDENCE |
| +#if DEBUG_COINCIDENCE_VERBOSE |
| +/* Commented-out lines keep this in sync with expand */ |
| +bool SkCoincidentSpans::debugExpand(const char* id, 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(kExpandCoin_Glitch, id, 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(); |
| + 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(kExpandCoin_Glitch, id, this, next->ptT(), oppPtT); |
| + expanded = true; |
| + } while (false); // actual continues while expansion is possible |
| + return expanded; |
| +} |
| + |
| +#define FAIL_IF(cond) do { if (cond) log->record(kAddExpandedFail_Glitch, id, coin); } while (false) |
| + |
| +/* Commented-out lines keep this in sync with addExpanded */ |
| +// for each coincident pair, match the spans |
| +// if the spans don't match, add the mssing pt to the segment and loop it in the opposite span |
| void SkOpCoincidence::debugAddExpanded(const char* id, SkPathOpsDebug::GlitchLog* log) const { |
| - // for each coincident pair, match the spans |
| - // if the spans don't match, add the mssing pt to the segment and loop it in the opposite span |
| const SkCoincidentSpans* coin = this->fHead; |
| if (!coin) { |
| - coin = this->fTop; |
| - } |
| - if (!coin) { |
| return; |
| } |
| do { |
| - const SkOpPtT* startPtT = coin->fCoinPtTStart; |
| - const SkOpPtT* oStartPtT = coin->fOppPtTStart; |
| + const SkOpPtT* startPtT = coin->coinPtTStart(); |
| + const SkOpPtT* oStartPtT = coin->oppPtTStart(); |
| SkASSERT(startPtT->contains(oStartPtT)); |
| - SkASSERT(coin->fCoinPtTEnd->contains(coin->fOppPtTEnd)); |
| + SkASSERT(coin->coinPtTEnd()->contains(coin->oppPtTEnd())); |
| const SkOpSpanBase* start = startPtT->span(); |
| const SkOpSpanBase* oStart = oStartPtT->span(); |
| - const SkOpSpanBase* end = coin->fCoinPtTEnd->span(); |
| - const SkOpSpanBase* oEnd = coin->fOppPtTEnd->span(); |
| + const SkOpSpanBase* end = coin->coinPtTEnd()->span(); |
| + const SkOpSpanBase* oEnd = coin->oppPtTEnd()->span(); |
| + FAIL_IF(oEnd->deleted()); |
| const SkOpSpanBase* test = start->upCast()->next(); |
| - const SkOpSpanBase* oTest = coin->fFlipped ? oStart->prev() : oStart->upCast()->next(); |
| + const SkOpSpanBase* oTest = coin->flipped() ? oStart->prev() : oStart->upCast()->next(); |
| + if (!oTest) { |
| + return; |
| + } |
| while (test != end || oTest != oEnd) { |
| - bool bumpTest = true; |
| - bool bumpOTest = true; |
| - if (!test->ptT()->contains(oTest->ptT())) { |
| + if (!test->ptT()->contains(oTest->segment()) |
| + || !oTest->ptT()->contains(start->segment())) { |
| // use t ranges to guess which one is missing |
| - double startRange = coin->fCoinPtTEnd->fT - startPtT->fT; |
| + double startRange = coin->coinPtTEnd()->fT - startPtT->fT; |
| + FAIL_IF(!startRange); |
| double startPart = (test->t() - startPtT->fT) / startRange; |
| - double oStartRange = coin->fOppPtTEnd->fT - oStartPtT->fT; |
| + double oStartRange = coin->oppPtTEnd()->fT - oStartPtT->fT; |
| + FAIL_IF(!oStartRange); |
| double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange; |
| - if (startPart == oStartPart) { |
| - // data is corrupt |
| - log->record(kAddCorruptCoin_Glitch, id, start, oStart); |
| - break; |
| + FAIL_IF(startPart == oStartPart); |
| + bool startOver = false; |
| + if (startPart < oStartPart) |
| + log->record(kAddExpandedCoin_Glitch, id, // strange debug formatting lines up with original |
| + oStartPtT->fT + oStartRange * startPart, test); |
| + else log->record(kAddExpandedCoin_Glitch, id, |
| + startPtT->fT + startRange * oStartPart, oTest); |
| + if (false) { |
| + SkASSERT(0); |
| + return; |
| } |
| - if (startPart < oStartPart) { |
| - double newT = oStartPtT->fT + oStartRange * startPart; |
| - log->record(kAddExpandedCoin_Glitch, id, oStart, newT, test->pt()); |
| - bumpOTest = false; |
| - } else { |
| - double newT = startPtT->fT + startRange * oStartPart; |
| - log->record(kAddExpandedCoin_Glitch, id, start, newT, oTest->pt()); |
| - bumpTest = false; |
| + if (startOver) { |
| + test = start; |
| + oTest = oStart; |
| } |
| } |
| - if (bumpTest && test != end) { |
| + if (test != end) { |
| test = test->upCast()->next(); |
| } |
| - if (bumpOTest && oTest != oEnd) { |
| - oTest = coin->fFlipped ? oTest->prev() : oTest->upCast()->next(); |
| + if (oTest != oEnd) { |
| + oTest = coin->flipped() ? oTest->prev() : oTest->upCast()->next(); |
| + if (!oTest) { |
| + return; |
| + } |
| } |
| } |
| - } while ((coin = coin->fNext)); |
| -} |
| - |
| -static void t_range(const SkOpPtT* overS, const SkOpPtT* overE, double tStart, double tEnd, |
| - const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, double* coinTs, double* coinTe) { |
| - double denom = overE->fT - overS->fT; |
| - double start = 0 < denom ? tStart : tEnd; |
| - double end = 0 < denom ? tEnd : tStart; |
| - double sRatio = (start - overS->fT) / denom; |
| - double eRatio = (end - overS->fT) / denom; |
| - *coinTs = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * sRatio; |
| - *coinTe = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * eRatio; |
| -} |
| - |
| -bool SkOpCoincidence::debugAddIfMissing(const SkCoincidentSpans* outer, const SkOpPtT* over1s, |
| - const SkOpPtT* over1e) const { |
| - const SkCoincidentSpans* check = this->fTop; |
| - while (check) { |
| - if (check->fCoinPtTStart->span() == over1s->span() |
| - && check->fOppPtTStart->span() == outer->fOppPtTStart->span()) { |
| - SkASSERT(check->fCoinPtTEnd->span() == over1e->span() |
| - || !fDebugState->debugRunFail()); |
| - SkASSERT(check->fOppPtTEnd->span() == outer->fOppPtTEnd->span() |
| - || !fDebugState->debugRunFail()); |
| - return false; |
| - } |
| - if (check->fCoinPtTStart->span() == outer->fCoinPtTStart->span() |
| - && check->fOppPtTStart->span() == over1s->span()) { |
| - SkASSERT(check->fCoinPtTEnd->span() == outer->fCoinPtTEnd->span() |
| - || !fDebugState->debugRunFail()); |
| - SkASSERT(check->fOppPtTEnd->span() == over1e->span() |
| - || !fDebugState->debugRunFail()); |
| - return false; |
| - } |
| - check = check->fNext; |
| + } while ((coin = coin->next())); |
| + return; |
| +} |
| + |
| +/* Commented-out lines keep this in sync with addIfMissing() */ |
| +void SkOpCoincidence::debugAddIfMissing(const SkCoincidentSpans* outer, const SkOpPtT* over1s, |
| + const SkOpPtT* over1e, const char* id, SkPathOpsDebug::GlitchLog* log) const { |
| +// SkASSERT(fTop); |
| + if (fTop && alreadyAdded(fTop, outer, over1s, over1e)) { // in debug, fTop may be null |
| + return; |
| } |
| - return true; |
| + if (fHead && alreadyAdded(fHead, outer, over1s, over1e)) { |
| + return; |
| + } |
| + log->record(kAddIfMissingCoin_Glitch, id, outer->coinPtTStart(), outer->coinPtTEnd(), over1s, over1e); |
| + this->debugValidate(); |
| + return; |
| } |
| -bool SkOpCoincidence::debugAddIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e, |
| - const SkOpPtT* over2s, const SkOpPtT* over2e, double tStart, double tEnd, |
| - SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, |
| - SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) const { |
| +/* Commented-out lines keep this in sync addIfMissing() */ |
| +void SkOpCoincidence::debugAddIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e, |
| + const SkOpPtT* over2s, const SkOpPtT* over2e, double tStart, double tEnd, |
| + const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, |
| + const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd, const char* id, SkPathOpsDebug::GlitchLog* log) const { |
| double coinTs, coinTe, oppTs, oppTe; |
| - t_range(over1s, over1e, tStart, tEnd, coinPtTStart, coinPtTEnd, &coinTs, &coinTe); |
| - t_range(over2s, over2e, tStart, tEnd, oppPtTStart, oppPtTEnd, &oppTs, &oppTe); |
| - const SkOpSegment* coinSeg = coinPtTStart->segment(); |
| - const SkOpSegment* oppSeg = oppPtTStart->segment(); |
| - SkASSERT(coinSeg != oppSeg); |
| - const SkCoincidentSpans* check = this->fTop; |
| - ; |
| - while (check) { |
| - const SkOpSegment* checkCoinSeg = check->fCoinPtTStart->segment(); |
| - const SkOpSegment* checkOppSeg; |
| - if (checkCoinSeg != coinSeg && checkCoinSeg != oppSeg) { |
| - goto next; |
| - } |
| - checkOppSeg = check->fOppPtTStart->segment(); |
| - if (checkOppSeg != coinSeg && checkOppSeg != oppSeg) { |
| - goto next; |
| - } |
| - { |
| - int cTs = coinTs; |
| - int cTe = coinTe; |
| - int oTs = oppTs; |
| - int oTe = oppTe; |
| - if (checkCoinSeg != coinSeg) { |
| - SkASSERT(checkOppSeg != oppSeg); |
| - SkTSwap(cTs, oTs); |
| - SkTSwap(cTe, oTe); |
| - } |
| - int tweenCount = (int) between(check->fCoinPtTStart->fT, cTs, check->fCoinPtTEnd->fT) |
| - + (int) between(check->fCoinPtTStart->fT, cTe, check->fCoinPtTEnd->fT) |
| - + (int) between(check->fOppPtTStart->fT, oTs, check->fOppPtTEnd->fT) |
| - + (int) between(check->fOppPtTStart->fT, oTe, check->fOppPtTEnd->fT); |
| - // SkASSERT(tweenCount == 0 || tweenCount == 4); |
| - if (tweenCount) { |
| - return true; |
| - } |
| - } |
| -next: |
| - check = check->fNext; |
| + TRange(over1s, over1e, tStart, tEnd, coinPtTStart, coinPtTEnd, &coinTs, &coinTe); |
| + TRange(over2s, over2e, tStart, tEnd, oppPtTStart, oppPtTEnd, &oppTs, &oppTe); |
| + bool swap = coinTs > coinTe; |
| + if (swap) { |
| + SkTSwap(coinTs, coinTe); |
| } |
| if ((over1s->fT < over1e->fT) != (over2s->fT < over2e->fT)) { |
| SkTSwap(oppTs, oppTe); |
| } |
| - if (coinTs > coinTe) { |
| - SkTSwap(coinTs, coinTe); |
| + if (swap) { |
| SkTSwap(oppTs, oppTe); |
| } |
| - bool cs = coinSeg->debugAddMissing(coinTs, oppSeg); |
| - bool ce = coinSeg->debugAddMissing(coinTe, oppSeg); |
| - if (cs == ce) { |
| - return false; |
| + const SkOpSegment* coinSeg = coinPtTStart->segment(); |
| + const SkOpSegment* oppSeg = oppPtTStart->segment(); |
| + if (coinSeg == oppSeg) { |
| + return; |
| } |
| - return true; |
| + return this->debugAddOrOverlap(coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, id, log); |
| +} |
| + |
| +/* Commented-out lines keep this in sync addOrOverlap() */ |
| +void SkOpCoincidence::debugAddOrOverlap(const SkOpSegment* coinSeg, const SkOpSegment* oppSeg, |
| + double coinTs, double coinTe, double oppTs, double oppTe, const char* id, SkPathOpsDebug::GlitchLog* log) const { |
| + SkTDArray<SkCoincidentSpans*> overlaps; |
| + SkASSERT(!fTop); // this is (correctly) reversed in addifMissing() |
| + if (fTop && !this->checkOverlap(fTop, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &overlaps)) { |
| + return; |
| + } |
| + if (fHead && !this->checkOverlap(fHead, coinSeg, oppSeg, coinTs, |
| + coinTe, oppTs, oppTe, &overlaps)) { |
| + return; |
| + } |
| + const SkCoincidentSpans* overlap = overlaps.count() ? overlaps[0] : nullptr; |
| + for (int index = 1; index < overlaps.count(); ++index) { // combine overlaps before continuing |
| + const SkCoincidentSpans* test = overlaps[index]; |
| + if (overlap->coinPtTStart()->fT > test->coinPtTStart()->fT) { |
| + log->record(kAddOrOverlap_Glitch, id, overlap, test->coinPtTStart()); |
| + } |
| + if (overlap->coinPtTEnd()->fT < test->coinPtTEnd()->fT) { |
| + log->record(kAddOrOverlap_Glitch, id, overlap, test->coinPtTEnd()); |
| + } |
| + if (overlap->flipped() |
| + ? overlap->oppPtTStart()->fT < test->oppPtTStart()->fT |
| + : overlap->oppPtTStart()->fT > test->oppPtTStart()->fT) { |
| + log->record(kAddOrOverlap_Glitch, id, overlap, test->oppPtTStart()); |
| + } |
| + if (overlap->flipped() |
| + ? overlap->oppPtTEnd()->fT > test->oppPtTEnd()->fT |
| + : overlap->oppPtTEnd()->fT < test->oppPtTEnd()->fT) { |
| + log->record(kAddOrOverlap_Glitch, id, overlap, test->oppPtTEnd()); |
| + } |
| + if (!fHead) { |
| + SkAssertResult(true); |
| + } |
| + } |
| + const SkOpPtT* cs = coinSeg->existing(coinTs, oppSeg); |
| + const SkOpPtT* ce = coinSeg->existing(coinTe, oppSeg); |
| + if (overlap && cs && ce && overlap->contains(cs, ce)) { |
| + return; |
| + } |
| + SkASSERT(cs != ce || !cs); |
| + const SkOpPtT* os = oppSeg->existing(oppTs, coinSeg); |
| + const SkOpPtT* oe = oppSeg->existing(oppTe, coinSeg); |
| + if (overlap && os && oe && overlap->contains(os, oe)) { |
| + return; |
| + } |
| + SkASSERT(true || !cs || !cs->deleted()); |
| + SkASSERT(true || !os || !os->deleted()); |
| + SkASSERT(true || !ce || !ce->deleted()); |
| + SkASSERT(true || !oe || !oe->deleted()); |
| + const SkOpPtT* csExisting = !cs ? coinSeg->existing(coinTs, nullptr) : nullptr; |
| + const SkOpPtT* ceExisting = !ce ? coinSeg->existing(coinTe, nullptr) : nullptr; |
| + if (csExisting && csExisting == ceExisting) { |
| + return; |
| + } |
| + if (csExisting && (csExisting == ce || csExisting->contains(ceExisting ? ceExisting : ce))) { |
| + return; |
| + } |
| + if (ceExisting && (ceExisting == cs || ceExisting->contains(csExisting ? csExisting : cs))) { |
| + return; |
| + } |
| + const SkOpPtT* osExisting = !os ? oppSeg->existing(oppTs, nullptr) : nullptr; |
| + const SkOpPtT* oeExisting = !oe ? oppSeg->existing(oppTe, nullptr) : nullptr; |
| + if (osExisting && osExisting == oeExisting) { |
| + return; |
| + } |
| + if (osExisting && (osExisting == oe || osExisting->contains(oeExisting ? oeExisting : oe))) { |
| + return; |
| + } |
| + if (oeExisting && (oeExisting == os || oeExisting->contains(osExisting ? osExisting : os))) { |
| + return; |
| + } |
| + bool csDeleted = false, osDeleted = false, ceDeleted = false, oeDeleted = false; |
| + this->debugValidate(); |
| + if (!cs || !os) { |
| + if (!cs) |
| + cs = coinSeg->debugAddT(coinTs, SkOpSegment::kNoAliasMatch, nullptr); |
| + if (!os) |
| + os = oppSeg->debugAddT(oppTs, SkOpSegment::kNoAliasMatch, nullptr); |
| + if (cs && os) cs->span()->debugAddOppAndMerge(id, log, os->span(), &csDeleted, &osDeleted); |
| +// cs = csWritable; |
|
herb_g
2016/07/18 15:13:39
You have a bunch of commented out code in this rou
caryclark
2016/07/18 15:55:49
See reply above.
|
| +// os = osWritable; |
| + if ((ce && ce->deleted()) || (oe && oe->deleted())) { |
| + return; |
| + } |
| + } |
| + if (!ce || !oe) { |
| + if (!ce) |
| + ce = coinSeg->debugAddT(coinTe, SkOpSegment::kNoAliasMatch, nullptr); |
| + if (!oe) |
| + oe = oppSeg->debugAddT(oppTe, SkOpSegment::kNoAliasMatch, nullptr); |
| + if (ce && oe) ce->span()->debugAddOppAndMerge(id, log, oe->span(), &ceDeleted, &oeDeleted); |
| +// ce = ceWritable; |
| +// oe = oeWritable; |
| + } |
| + this->debugValidate(); |
| + if (csDeleted || osDeleted || ceDeleted || oeDeleted) { |
| + return; |
| + } |
| + if (!cs || !ce || cs->contains(ce) || !os || !oe || os->contains(oe)) { |
| + return; |
| + } |
| +// bool result = true; |
| + if (overlap) { |
| + if (overlap->coinPtTStart()->segment() == coinSeg) { |
| + log->record(kAddMissingExtend_Glitch, id, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe); |
| + } else { |
| + if (oppTs > oppTe) { |
| + SkTSwap(coinTs, coinTe); |
| + SkTSwap(oppTs, oppTe); |
| + } |
| + log->record(kAddMissingExtend_Glitch, id, oppSeg, oppTs, oppTe, coinSeg, coinTs, coinTe); |
| + } |
| +#if DEBUG_COINCIDENCE_VERBOSE |
| +// if (result) { |
| +// overlap->debugShow(); |
| +// } |
| +#endif |
| + } else { |
| + log->record(kAddMissingCoin_Glitch, id, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe); |
| +#if DEBUG_COINCIDENCE_VERBOSE |
| +// fHead->debugShow(); |
| +#endif |
| + } |
| + this->debugValidate(); |
| + return; |
| } |
| +// Extra commented-out lines keep this in sync with addMissing() |
| +/* detects overlaps of different coincident runs on same segment */ |
| +/* does not detect overlaps for pairs without any segments in common */ |
| +// returns true if caller should loop again |
| void SkOpCoincidence::debugAddMissing(const char* id, SkPathOpsDebug::GlitchLog* log) const { |
| const SkCoincidentSpans* outer = fHead; |
| if (!outer) { |
| return; |
| } |
| + // bool added = false; |
| + // fTop = outer; |
| + // fHead = nullptr; |
| do { |
| // addifmissing can modify the list that this is walking |
| // save head so that walker can iterate over old data unperturbed |
| // addifmissing adds to head freely then add saved head in the end |
| - const SkOpSegment* outerCoin = outer->fCoinPtTStart->segment(); |
| - SkASSERT(outerCoin == outer->fCoinPtTEnd->segment()); |
| - const SkOpSegment* outerOpp = outer->fOppPtTStart->segment(); |
| - SkASSERT(outerOpp == outer->fOppPtTEnd->segment()); |
| + const SkOpSegment* outerCoin = outer->coinPtTStart()->segment(); |
| + const SkOpSegment* outerOpp = outer->oppPtTStart()->segment(); |
| + if (outerCoin->done() || outerOpp->done()) { |
| + continue; |
| + } |
| const SkCoincidentSpans* inner = outer; |
| - while ((inner = inner->fNext)) { |
| + while ((inner = inner->next())) { |
| + this->debugValidate(); |
| double overS, overE; |
| - const SkOpSegment* innerCoin = inner->fCoinPtTStart->segment(); |
| - SkASSERT(innerCoin == inner->fCoinPtTEnd->segment()); |
| - const SkOpSegment* innerOpp = inner->fOppPtTStart->segment(); |
| - SkASSERT(innerOpp == inner->fOppPtTEnd->segment()); |
| - if (outerCoin == innerCoin |
| - && this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd, |
| - inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) { |
| - if (this->debugAddIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd, |
| - inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE, |
| - outer->fOppPtTStart, outer->fOppPtTEnd, |
| - inner->fOppPtTStart, inner->fOppPtTEnd)) { |
| - log->record(kAddMissingCoin_Glitch, id, outer, inner->fCoinPtTStart); |
| + const SkOpSegment* innerCoin = inner->coinPtTStart()->segment(); |
| + const SkOpSegment* innerOpp = inner->oppPtTStart()->segment(); |
| + if (innerCoin->done() || innerOpp->done()) { |
| + continue; |
| + } |
| + if (outerCoin == innerCoin) { |
| + if (outerOpp != innerOpp |
| + && this->overlap(outer->coinPtTStart(), outer->coinPtTEnd(), |
| + inner->coinPtTStart(), inner->coinPtTEnd(), &overS, &overE)) { |
| + this->debugAddIfMissing(outer->coinPtTStart(), outer->coinPtTEnd(), |
| + inner->coinPtTStart(), inner->coinPtTEnd(), overS, overE, |
| + outer->oppPtTStart(), outer->oppPtTEnd(), |
| + inner->oppPtTStart(), inner->oppPtTEnd(), id, log); |
| } |
| - } else if (outerCoin == innerOpp |
| - && this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd, |
| - inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) { |
| - if (this->debugAddIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd, |
| - inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE, |
| - outer->fOppPtTStart, outer->fOppPtTEnd, |
| - inner->fCoinPtTStart, inner->fCoinPtTEnd)) { |
| - log->record(kAddMissingCoin_Glitch, id, outer, inner->fOppPtTStart); |
| + } else if (outerCoin == innerOpp) { |
| + if (outerOpp != innerCoin |
| + && this->overlap(outer->coinPtTStart(), outer->coinPtTEnd(), |
| + inner->oppPtTStart(), inner->oppPtTEnd(), &overS, &overE)) { |
| + this->debugAddIfMissing(outer->coinPtTStart(), outer->coinPtTEnd(), |
| + inner->oppPtTStart(), inner->oppPtTEnd(), overS, overE, |
| + outer->oppPtTStart(), outer->oppPtTEnd(), |
| + inner->coinPtTStart(), inner->coinPtTEnd(), id, log); |
| } |
| - } else if (outerOpp == innerCoin |
| - && this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd, |
| - inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) { |
| - if (this->debugAddIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd, |
| - inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE, |
| - outer->fCoinPtTStart, outer->fCoinPtTEnd, |
| - inner->fOppPtTStart, inner->fOppPtTEnd)) { |
| - log->record(kAddMissingCoin_Glitch, id, outer, inner->fCoinPtTStart); |
| + } else if (outerOpp == innerCoin) { |
| + SkASSERT(outerCoin != innerOpp); |
| + if (this->overlap(outer->oppPtTStart(), outer->oppPtTEnd(), |
| + inner->coinPtTStart(), inner->coinPtTEnd(), &overS, &overE)) { |
| + this->debugAddIfMissing(outer->oppPtTStart(), outer->oppPtTEnd(), |
| + inner->coinPtTStart(), inner->coinPtTEnd(), overS, overE, |
| + outer->coinPtTStart(), outer->coinPtTEnd(), |
| + inner->oppPtTStart(), inner->oppPtTEnd(), id, log); |
| } |
| - } else if (outerOpp == innerOpp |
| - && this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd, |
| - inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) { |
| - if (this->debugAddIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd, |
| - inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE, |
| - outer->fCoinPtTStart, outer->fCoinPtTEnd, |
| - inner->fCoinPtTStart, inner->fCoinPtTEnd)) { |
| - log->record(kAddMissingCoin_Glitch, id, outer, inner->fOppPtTStart); |
| - } |
| - } else if (outerCoin != innerCoin) { |
| - // check to see if outer span overlaps the inner span |
| - // look for inner segment in pt-t list |
| - // if present, and if t values are in coincident range |
| - // add two pairs of new coincidence |
| - const SkOpPtT* testS = outer->fCoinPtTStart->debugContains(innerCoin); |
| - const SkOpPtT* testE = outer->fCoinPtTEnd->debugContains(innerCoin); |
| - if (testS && testS->fT >= inner->fCoinPtTStart->fT |
| - && testE && testE->fT <= inner->fCoinPtTEnd->fT |
| - && this->testForCoincidence(outer, testS, testE)) { |
| - if (this->debugAddIfMissing(outer, testS, testE)) { |
| - log->record(kAddMissingCoin_Glitch, id, outer, testS, testE); |
| - } |
| - } else { |
| - testS = inner->fCoinPtTStart->debugContains(outerCoin); |
| - testE = inner->fCoinPtTEnd->debugContains(outerCoin); |
| - if (testS && testS->fT >= outer->fCoinPtTStart->fT |
| - && testE && testE->fT <= outer->fCoinPtTEnd->fT |
| - && this->testForCoincidence(inner, testS, testE)) { |
| - if (this->debugAddIfMissing(inner, testS, testE)) { |
| - log->record(kAddMissingCoin_Glitch, id, inner, testS, testE); |
| - } |
| - } |
| + } else if (outerOpp == innerOpp) { |
| + SkASSERT(outerCoin != innerCoin); |
| + if (this->overlap(outer->oppPtTStart(), outer->oppPtTEnd(), |
| + inner->oppPtTStart(), inner->oppPtTEnd(), &overS, &overE)) { |
| + this->debugAddIfMissing(outer->oppPtTStart(), outer->oppPtTEnd(), |
| + inner->oppPtTStart(), inner->oppPtTEnd(), overS, overE, |
| + outer->coinPtTStart(), outer->coinPtTEnd(), |
| + inner->coinPtTStart(), inner->coinPtTEnd(), id, log); |
| } |
| } |
| + this->debugValidate(); |
| + } |
| + } while ((outer = outer->next())); |
| + // this->restoreHead(); |
| + return; |
| +} |
| + |
| +// Commented-out lines keep this in sync with release() |
| +void SkOpCoincidence::debugRelease(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpSegment* deleted) const { |
| + const SkCoincidentSpans* coin = fHead; |
| + if (!coin) { |
| + return; |
| + } |
| + do { |
| + if (coin->coinPtTStart()->segment() == deleted |
| + || coin->coinPtTEnd()->segment() == deleted |
| + || coin->oppPtTStart()->segment() == deleted |
| + || coin->oppPtTEnd()->segment() == deleted) { |
| + log->record(kReleasedSpan_Glitch, id, coin); |
| + } |
| + } while ((coin = coin->next())); |
| +} |
| + |
| +// Commented-out lines keep this in sync with reorder() |
| +// iterate through all coincident pairs, looking for ranges greater than 1 |
| +// if found, see if the opposite pair can match it -- which may require |
| +// reordering the ptT pairs |
| +void SkOpCoincidence::debugReorder(const char* id, SkPathOpsDebug::GlitchLog* log) const { |
| + const SkCoincidentSpans* coin = fHead; |
| + if (!coin) { |
| + return; |
| + } |
| + do { |
| + // most commonly, concidence are one span long; check for that first |
| + int intervals = coin->spanCount(); |
| + if (intervals = 1) { |
| +#if DEBUG_COINCIDENCE_VERBOSE |
| + // SkASSERT(!coin->debugExpand(nullptr, nullptr)); |
| +#endif |
| + continue; |
| + } |
| + coin->debugExpand(id, log); |
| + if (coin->spanCount() <= 0) { |
| + return; |
| } |
| - } while ((outer = outer->fNext)); |
| + // check to see if every span in coin has a mate in opp |
| + const SkOpSpan* start = coin->coinPtTStart()->span()->upCast(); |
| + bool flipped = coin->flipped(); |
| + const SkOpSpanBase* oppStartBase = coin->oppPtTStart()->span(); |
| + const SkOpSpan* oppStart = flipped ? oppStartBase->prev() : oppStartBase->upCast(); |
| + SkDebugf("", start, oppStart); |
| + } while ((coin = coin->next())); |
| + return; |
| } |
| +// Commented-out lines keep this in sync with expand() |
| +// expand the range by checking adjacent spans for coincidence |
| bool SkOpCoincidence::debugExpand(const char* id, SkPathOpsDebug::GlitchLog* log) const { |
| const SkCoincidentSpans* coin = fHead; |
| if (!coin) { |
| @@ -1352,109 +1671,296 @@ bool SkOpCoincidence::debugExpand(const char* id, SkPathOpsDebug::GlitchLog* log |
| } |
| bool expanded = false; |
| do { |
| - const SkOpSpan* start = coin->fCoinPtTStart->span()->upCast(); |
| - const SkOpSpanBase* end = coin->fCoinPtTEnd->span(); |
| - const SkOpSegment* segment = coin->fCoinPtTStart->segment(); |
| - const SkOpSegment* oppSegment = coin->fOppPtTStart->segment(); |
| - const SkOpSpan* prev = start->prev(); |
| - if (prev && prev->debugContains(oppSegment)) { |
| - double midT = (prev->t() + start->t()) / 2; |
| - if (segment->isClose(midT, oppSegment)) { |
| - log->record(kExpandCoin_Glitch, id, coin, prev); |
| - } |
| - } |
| - SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next(); |
| - if (next && next->debugContains(oppSegment)) { |
| - double midT = (end->t() + next->t()) / 2; |
| - if (segment->isClose(midT, oppSegment)) { |
| - log->record(kExpandCoin_Glitch, id, coin, next); |
| - } |
| + if (coin->debugExpand(id, 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(kExpandCoin_Glitch, id, fHead, test->coinPtTStart()); |
| + break; |
| + } |
| + } while ((test = test->next())); |
| + expanded = true; |
| } |
| - } while ((coin = coin->fNext)); |
| + } while ((coin = coin->next())); |
| return expanded; |
| } |
| -void SkOpCoincidence::debugFixAligned(const char* id, SkPathOpsDebug::GlitchLog* log) const { |
| +// Commented-out lines keep this in sync with removeCollapsed() |
| +void SkOpCoincidence::debugRemoveCollapsed(const char* id, SkPathOpsDebug::GlitchLog* log) const { |
| const SkCoincidentSpans* coin = fHead; |
| if (!coin) { |
| return; |
| } |
| + // SkCoincidentSpans** priorPtr = &fHead; |
| do { |
| - if (coin->fCoinPtTStart->deleted()) { |
| - log->record(kDeletedCoin_Glitch, id, coin, coin->fCoinPtTStart); |
| - } |
| - if (coin->fCoinPtTEnd->deleted()) { |
| - log->record(kDeletedCoin_Glitch, id, coin, coin->fCoinPtTEnd); |
| - } |
| - if (coin->fOppPtTStart->deleted()) { |
| - log->record(kDeletedCoin_Glitch, id, coin, coin->fOppPtTStart); |
| + if (coin->coinPtTStart() == coin->coinPtTEnd()) { |
| + return; |
| } |
| - if (coin->fOppPtTEnd->deleted()) { |
| - log->record(kDeletedCoin_Glitch, id, coin, coin->fOppPtTEnd); |
| + if (coin->oppPtTStart() == coin->oppPtTEnd()) { |
| + return; |
| } |
| - } while ((coin = coin->fNext)); |
| - coin = fHead; |
| - do { |
| - if (coin->fCoinPtTStart->collapsed(coin->fCoinPtTEnd)) { |
| - log->record(kCollapsedCoin_Glitch, id, coin, coin->fCoinPtTStart); |
| + if (coin->coinPtTStart()->collapsed(coin->coinPtTEnd())) { |
| + log->record(kCollapsedCoin_Glitch, id, coin); |
| +// continue; |
| } |
| - if (coin->fOppPtTStart->collapsed(coin->fOppPtTEnd)) { |
| - log->record(kCollapsedCoin_Glitch, id, coin, coin->fOppPtTStart); |
| + if (coin->oppPtTStart()->collapsed(coin->oppPtTEnd())) { |
| + log->record(kCollapsedCoin_Glitch, id, coin, coin); |
| +// continue; |
| } |
| - } while ((coin = coin->fNext)); |
| + // priorPtr = &coin->nextPtr(); |
| + } while ((coin = coin->next())); |
| + return; |
| } |
| +// 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(const char* id, SkPathOpsDebug::GlitchLog* log) const { |
| const SkCoincidentSpans* coin = fHead; |
| if (!coin) { |
| return; |
| } |
| do { |
| - const SkOpSpanBase* end = coin->fCoinPtTEnd->span(); |
| - const SkOpSpanBase* oldEnd = end; |
| - const SkOpSpan* start = coin->fCoinPtTStart->span()->debugStarter(&end); |
| - const SkOpSpanBase* oEnd = coin->fOppPtTEnd->span(); |
| - const SkOpSpanBase* oOldEnd = oEnd; |
| - const SkOpSpanBase* oStart = coin->fOppPtTStart->span()->debugStarter(&oEnd); |
| - bool flipped = (end == oldEnd) != (oEnd == oOldEnd); |
| + 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) { |
| SkTSwap(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(id, log, oStart->upCast()); |
| + end->debugInsertCoinEnd(id, log, oEnd); |
| + const SkOpSegment* segment = start->segment(); |
| + const SkOpSegment* oSegment = oStart->segment(); |
| const SkOpSpanBase* next = start; |
| const SkOpSpanBase* oNext = oStart; |
| - do { |
| - next = next->upCast()->next(); |
| - oNext = flipped ? oNext->prev() : oNext->upCast()->next(); |
| - if (next == end || oNext == oEnd) { |
| - break; |
| + while ((next = next->upCast()->next()) != end) { |
| + if (next->upCast()->debugInsertCoincidence(id, log, oSegment, flipped), false) { |
| + return; |
| } |
| - if (!next->containsCoinEnd(oNext)) { |
| - log->record(kMarkCoinEnd_Glitch, id, next, oNext); |
| + } |
| + while ((oNext = oNext->upCast()->next()) != oEnd) { |
| + if (oNext->upCast()->debugInsertCoincidence(id, log, segment, flipped), false) { |
| + return; |
| } |
| - const SkOpSpan* nextSpan = next->upCast(); |
| - const SkOpSpan* oNextSpan = oNext->upCast(); |
| - if (!nextSpan->containsCoincidence(oNextSpan)) { |
| - log->record(kMarkCoinInsert_Glitch, id, nextSpan, oNextSpan); |
| + } |
| + } while ((coin = coin->next())); |
| + return; |
| +} |
| +#endif |
| + |
| +#if DEBUG_COINCIDENCE_VERBOSE |
| +// Commented-out lines keep this in sync with markCollapsed() |
| +void SkOpCoincidence::debugMarkCollapsed(const char* id, SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkOpPtT* test) const { |
| + while (coin) { |
| + if (coin->collapsed(test)) { |
| + if (zero_or_one(coin->coinPtTStart()->fT) && zero_or_one(coin->coinPtTEnd()->fT)) { |
| + log->record(kCollapsedCoin_Glitch, id, coin); |
| } |
| - } while (true); |
| - } while ((coin = coin->fNext)); |
| + if (zero_or_one(coin->oppPtTStart()->fT) && zero_or_one(coin->oppPtTEnd()->fT)) { |
| + log->record(kCollapsedCoin_Glitch, id, coin); |
| + } |
| + } |
| + coin = coin->next(); |
| + } |
| +} |
| + |
| +// Commented-out lines keep this in sync with markCollapsed() |
| +void SkOpCoincidence::debugMarkCollapsed(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpPtT* test) const { |
| + this->debugMarkCollapsed(id, log, fHead, test); |
| + this->debugMarkCollapsed(id, log, fTop, test); |
| } |
| #endif |
| +void SkCoincidentSpans::debugShow() const { |
| + SkDebugf("%s - id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__, |
| + coinPtTStart()->segment()->debugID(), |
| + coinPtTStart()->fT, coinPtTEnd()->fT); |
| + SkDebugf("%s + id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__, |
| + oppPtTStart()->segment()->debugID(), |
| + oppPtTStart()->fT, oppPtTEnd()->fT); |
| +} |
| + |
| void SkOpCoincidence::debugShowCoincidence() const { |
| - SkCoincidentSpans* span = fHead; |
| +#if DEBUG_COINCIDENCE |
| + const SkCoincidentSpans* span = fHead; |
| while (span) { |
| - SkDebugf("%s - id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__, |
| - span->fCoinPtTStart->segment()->debugID(), |
| - span->fCoinPtTStart->fT, span->fCoinPtTEnd->fT); |
| - SkDebugf("%s + id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__, |
| - span->fOppPtTStart->segment()->debugID(), |
| - span->fOppPtTStart->fT, span->fOppPtTEnd->fT); |
| - span = span->fNext; |
| + span->debugShow(); |
| + span = span->next(); |
| } |
| +#endif |
| } |
| #if DEBUG_COINCIDENCE |
| +static void DebugValidate(const SkOpSpanBase* next, const SkOpSpanBase* end, |
| + double oStart, double oEnd, const SkOpSegment* oSegment, |
| + const char* id, SkPathOpsDebug::GlitchLog* log) { |
| + SkASSERT(next != end); |
| + SkASSERT(!next->contains(end) || log); |
| + if (next->t() > end->t()) { |
| + SkTSwap(next, end); |
| + } |
| + do { |
| + const SkOpPtT* ptT = next->ptT(); |
| + int index = 0; |
| + bool somethingBetween; |
| + do { |
| + ++index; |
| + ptT = ptT->next(); |
| + const SkOpPtT* checkPtT = next->ptT(); |
| + if (ptT == checkPtT) { |
| + break; |
| + } |
| + bool looped = false; |
| + for (int check = 0; check < index; ++check) { |
| + if ((looped = checkPtT == ptT)) { |
| + break; |
| + } |
| + checkPtT = checkPtT->next(); |
| + } |
| + if (looped) { |
| + SkASSERT(0); |
| + break; |
| + } |
| + if (ptT->deleted()) { |
| + continue; |
| + } |
| + if (ptT->segment() != oSegment) { |
| + continue; |
| + } |
| + somethingBetween |= between(oStart, ptT->fT, oEnd); |
| + } while (true); |
| + SkASSERT(somethingBetween); |
| + } while (next != end && (next = next->upCast()->next())); |
| +} |
| + |
| +static void DebugCheckOverlap(const SkCoincidentSpans* test, const SkCoincidentSpans* list, |
| + const char* id, SkPathOpsDebug::GlitchLog* log) { |
| + if (!list) { |
| + return; |
| + } |
| + const SkOpSegment* coinSeg = test->coinPtTStart()->segment(); |
| + SkASSERT(coinSeg == test->coinPtTEnd()->segment()); |
| + const SkOpSegment* oppSeg = test->oppPtTStart()->segment(); |
| + SkASSERT(oppSeg == test->oppPtTEnd()->segment()); |
| + SkASSERT(coinSeg != test->oppPtTStart()->segment()); |
| + SkDEBUGCODE(double tcs = test->coinPtTStart()->fT); |
| + SkASSERT(between(0, tcs, 1)); |
| + SkDEBUGCODE(double tce = test->coinPtTEnd()->fT); |
| + SkASSERT(between(0, tce, 1)); |
| + SkASSERT(tcs < tce); |
| + double tos = test->oppPtTStart()->fT; |
| + SkASSERT(between(0, tos, 1)); |
| + double toe = test->oppPtTEnd()->fT; |
| + SkASSERT(between(0, toe, 1)); |
| + SkASSERT(tos != toe); |
| + if (tos > toe) { |
| + SkTSwap(tos, toe); |
| + } |
| + do { |
| + double lcs, lce, los, loe; |
| + if (coinSeg == list->coinPtTStart()->segment()) { |
| + if (oppSeg != list->oppPtTStart()->segment()) { |
| + continue; |
| + } |
| + lcs = list->coinPtTStart()->fT; |
| + lce = list->coinPtTEnd()->fT; |
| + los = list->oppPtTStart()->fT; |
| + loe = list->oppPtTEnd()->fT; |
| + if (los > loe) { |
| + SkTSwap(los, loe); |
| + } |
| + } else if (coinSeg == list->oppPtTStart()->segment()) { |
| + if (oppSeg != list->coinPtTStart()->segment()) { |
| + continue; |
| + } |
| + lcs = list->oppPtTStart()->fT; |
| + lce = list->oppPtTEnd()->fT; |
| + if (lcs > lce) { |
| + SkTSwap(lcs, lce); |
| + } |
| + los = list->coinPtTStart()->fT; |
| + loe = list->coinPtTEnd()->fT; |
| + } else { |
| + continue; |
| + } |
| + SkASSERT(tce < lcs || lce < tcs); |
| + SkASSERT(toe < los || loe < tos); |
| + } while ((list = list->next())); |
| +} |
| + |
| + |
| +static void DebugCheckOverlapTop(const SkCoincidentSpans* head, const SkCoincidentSpans* opt, |
| + const char* id, SkPathOpsDebug::GlitchLog* log) { |
| + // check for overlapping coincident spans |
| + const SkCoincidentSpans* test = head; |
| + while (test) { |
| + const SkCoincidentSpans* next = test->next(); |
| + DebugCheckOverlap(test, next, id, log); |
| + DebugCheckOverlap(test, opt, id, log); |
| + test = next; |
| + } |
| +} |
| + |
| +#if DEBUG_COINCIDENCE_VERBOSE |
| +void SkOpCoincidence::debugCheckOverlap(const char* id, SkPathOpsDebug::GlitchLog* log) const { |
| + DebugCheckOverlapTop(fHead, fTop, id, log); |
| + DebugCheckOverlapTop(fTop, nullptr, id, log); |
| +} |
| +#endif |
| + |
| +static void DebugValidate(const SkCoincidentSpans* head, const SkCoincidentSpans* opt, |
| + const char* id, SkPathOpsDebug::GlitchLog* log) { |
| + // look for pts inside coincident spans that are not inside the opposite spans |
| + const SkCoincidentSpans* coin = head; |
| + while (coin) { |
| + SkASSERT(SkOpCoincidence::Ordered(coin->coinPtTStart()->segment(), |
| + coin->oppPtTStart()->segment())); |
| + SkASSERT(coin->coinPtTStart()->span()->ptT() == coin->coinPtTStart()); |
| + SkASSERT(coin->coinPtTEnd()->span()->ptT() == coin->coinPtTEnd()); |
| + SkASSERT(coin->oppPtTStart()->span()->ptT() == coin->oppPtTStart()); |
| + SkASSERT(coin->oppPtTEnd()->span()->ptT() == coin->oppPtTEnd()); |
| + DebugValidate(coin->coinPtTStart()->span(), coin->coinPtTEnd()->span(), |
| + coin->oppPtTStart()->fT, coin->oppPtTEnd()->fT, coin->oppPtTStart()->segment(), |
| + id, log); |
| + DebugValidate(coin->oppPtTStart()->span(), coin->oppPtTEnd()->span(), |
| + coin->coinPtTStart()->fT, coin->coinPtTEnd()->fT, coin->coinPtTStart()->segment(), |
| + id, log); |
| + coin = coin->next(); |
| + } |
| + DebugCheckOverlapTop(head, opt, id, log); |
| +} |
| +#endif |
| + |
| +void SkOpCoincidence::debugValidate() const { |
| +#if DEBUG_COINCIDENCE |
| + // if (fGlobalState->debugCheckHealth()) { |
| +// return; |
| +// } |
| + DebugValidate(fHead, fTop, nullptr, nullptr); |
| + DebugValidate(fTop, nullptr, nullptr, nullptr); |
| +#endif |
| +} |
| + |
| +#if DEBUG_COINCIDENCE_VERBOSE |
| +void SkOpCoincidence::debugCheckValid(const char* id, SkPathOpsDebug::GlitchLog* log) const { |
| + DebugValidate(fHead, fTop, id, log); |
| + DebugValidate(fTop, nullptr, id, log); |
| +} |
| +#endif |
| + |
| +#if DEBUG_COINCIDENCE_VERBOSE |
| void SkOpContour::debugCheckHealth(const char* id, SkPathOpsDebug::GlitchLog* log) const { |
| const SkOpSegment* segment = &fHead; |
| do { |
| @@ -1462,16 +1968,36 @@ void SkOpContour::debugCheckHealth(const char* id, SkPathOpsDebug::GlitchLog* lo |
| } while ((segment = segment->next())); |
| } |
| -void SkOpContour::debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log, |
| - const SkOpCoincidence* coincidence) const { |
| +// commmented-out lines keep this aligned with missingCoincidence() |
| +void SkOpContour::debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log) const { |
| +// SkASSERT(fCount > 0); |
| const SkOpSegment* segment = &fHead; |
| +// bool result = false; |
| do { |
| - segment->debugMissingCoincidence(id, log, coincidence); |
| - } while ((segment = segment->next())); |
| + if (fState->angleCoincidence()) { |
| +// #if DEBUG_ANGLE |
| +// segment->debugCheckAngleCoin(); |
| +// #endif |
| + } else if (segment->debugMissingCoincidence(id, log), false) { |
| +// result = true; |
| +// see FIXME in missingCoincidence() |
| +// |
| +// |
| +// |
| + // continue; |
| + } |
| + segment = segment->next(); |
| + } while (segment); |
| + return; |
| } |
| #endif |
| void SkOpSegment::debugValidate() const { |
| +#if DEBUG_COINCIDENCE |
| + if (this->globalState()->debugCheckHealth()) { |
| + return; |
| + } |
| +#endif |
| #if DEBUG_VALIDATE |
| const SkOpSpanBase* span = &fHead; |
| double lastT = -1; |
| @@ -1500,56 +2026,47 @@ void SkOpSegment::debugValidate() const { |
| #endif |
| } |
| -bool SkOpSpanBase::debugAlignedEnd(double t, const SkPoint& pt) const { |
| - SkASSERT(zero_or_one(t)); |
| - const SkOpSegment* segment = this->segment(); |
| - SkASSERT(t ? segment->lastPt() == pt : segment->pts()[0] == pt); |
| - if (!debugAlignedInner()) { |
| - return false; |
| - } |
| - if ((t ? segment->lastPt() : segment->pts()[0]) != pt) { |
| - return false; |
| +#if DEBUG_COINCIDENCE_VERBOSE |
| +// Commented-out lines keep this in sync with addOppAndMerge() |
| +// If the added points envelop adjacent spans, merge them in. |
| +void SkOpSpanBase::debugAddOppAndMerge(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp, bool* spanDeleted, bool* oppDeleted) const { |
| + if (this->ptT()->debugAddOpp(opp->ptT())) { |
| + this->debugCheckForCollapsedCoincidence(id, log); |
| } |
| - const SkOpPtT* ptT = &this->fPtT; |
| - SkASSERT(t == ptT->fT); |
| - SkASSERT(pt == ptT->fPt); |
| - const SkOpPtT* test = ptT, * stopPtT = ptT; |
| - while ((test = test->next()) != stopPtT) { |
| - const SkOpSegment* other = test->segment(); |
| - if (other == this->segment()) { |
| - continue; |
| - } |
| - if (!zero_or_one(test->fT)) { |
| - continue; |
| - } |
| - if ((test->fT ? other->lastPt() : other->pts()[0]) != pt) { |
| - return false; |
| - } |
| + // compute bounds of points in span |
| + SkPathOpsBounds bounds; |
| + bounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMin, SK_ScalarMin); |
| + const SkOpPtT* head = this->ptT(); |
| + const SkOpPtT* nextPt = head; |
| + do { |
| + bounds.add(nextPt->fPt); |
| + } while ((nextPt = nextPt->next()) != head); |
| + if (!bounds.width() && !bounds.height()) { |
| + return; |
| } |
| - return this->fAligned; |
| + this->debugMergeContained(id, log, bounds, spanDeleted); |
| + opp->debugMergeContained(id, log, bounds, oppDeleted); |
| } |
| -bool SkOpSpanBase::debugAlignedInner() const { |
| - // force the spans to share points and t values |
| - const SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT; |
| - const SkPoint& pt = ptT->fPt; |
| +// Commented-out lines keep this in sync with checkForCollapsedCoincidence() |
| +void SkOpSpanBase::debugCheckForCollapsedCoincidence(const char* id, 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 (ptT->fPt != pt) { |
| - return false; |
| + if (!test->coincident()) { |
| + continue; |
| } |
| - const SkOpSpanBase* span = ptT->span(); |
| - const SkOpPtT* test = ptT; |
| - do { |
| - if ((test = test->next()) == stopPtT) { |
| - break; |
| - } |
| - if (span == test->span() && !span->segment()->ptsDisjoint(*ptT, *test)) { |
| - return false; |
| - } |
| - } while (true); |
| - } while ((ptT = ptT->next()) != stopPtT); |
| - return true; |
| + coins->debugMarkCollapsed(id, log, test); |
| + } while ((test = test->next()) != head); |
| } |
| +#endif |
| bool SkOpSpanBase::debugCoinEndLoopCheck() const { |
| int loop = 0; |
| @@ -1574,16 +2091,57 @@ bool SkOpSpanBase::debugCoinEndLoopCheck() const { |
| return true; |
| } |
| -bool SkOpSpanBase::debugContains(const SkOpSegment* segment) const { |
| - const SkOpPtT* start = &fPtT; |
| - const SkOpPtT* walk = start; |
| - while ((walk = walk->next()) != start) { |
| - if (walk->segment() == segment) { |
| - return true; |
| +#if DEBUG_COINCIDENCE_VERBOSE |
| +// Commented-out lines keep this in sync with insertCoinEnd() |
| +void SkOpSpanBase::debugInsertCoinEnd(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* coin) const { |
| + if (containsCoinEnd(coin)) { |
| +// SkASSERT(coin->containsCoinEnd(this)); |
| + return; |
| + } |
| + debugValidate(); |
| +// SkASSERT(this != coin); |
| + log->record(kMarkCoinEnd_Glitch, id, this, coin); |
| +// coin->fCoinEnd = this->fCoinEnd; |
| +// this->fCoinEnd = coinNext; |
| + debugValidate(); |
| +} |
| + |
| +// Commented-out lines keep this in sync with mergeContained() |
| +void SkOpSpanBase::debugMergeContained(const char* id, SkPathOpsDebug::GlitchLog* log, const SkPathOpsBounds& bounds, bool* deleted) const { |
| + // while adjacent spans' points are contained by the bounds, merge them |
| + const SkOpSpanBase* prev = this; |
| + const SkOpSegment* seg = this->segment(); |
| + while ((prev = prev->prev()) && bounds.contains(prev->pt()) && !seg->ptsDisjoint(prev, this)) { |
| + if (prev->prev()) { |
| + log->record(kMergeContained_Glitch, id, this, prev); |
| + } else if (this->final()) { |
| + log->record(kMergeContained_Glitch, id, this, prev); |
| + // return; |
| + } else { |
| + log->record(kMergeContained_Glitch, id, prev, this); |
| } |
| } |
| - return false; |
| + const SkOpSpanBase* current = this; |
| + const SkOpSpanBase* next = this; |
| + while (next->upCastable() && (next = next->upCast()->next()) |
| + && bounds.contains(next->pt()) && !seg->ptsDisjoint(this, next)) { |
| + if (!current->prev() && next->final()) { |
| + log->record(kMergeContained_Glitch, id, next, current); |
| + current = next; |
| + } |
| + if (current->prev()) { |
| + log->record(kMergeContained_Glitch, id, next, current); |
| + current = next; |
| + } else { |
| + log->record(kMergeContained_Glitch, id, next, current); |
| + current = next; |
| + } |
| + } |
| +#if DEBUG_COINCIDENCE |
| + // this->globalState()->coincidence()->debugValidate(); |
| +#endif |
| } |
| +#endif |
| const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const { |
| const SkOpSpanBase* end = *endPtr; |
| @@ -1599,6 +2157,11 @@ const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const { |
| } |
| void SkOpSpanBase::debugValidate() const { |
| +#if DEBUG_COINCIDENCE |
| + if (this->globalState()->debugCheckHealth()) { |
| + return; |
| + } |
| +#endif |
| #if DEBUG_VALIDATE |
| const SkOpPtT* ptT = &fPtT; |
| SkASSERT(ptT->span() == this); |
| @@ -1643,6 +2206,39 @@ bool SkOpSpan::debugCoinLoopCheck() const { |
| return true; |
| } |
| +#if DEBUG_COINCIDENCE_VERBOSE |
| +// Commented-out lines keep this in sync with insertCoincidence() in header |
| +void SkOpSpan::debugInsertCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpSpan* coin) const { |
| + if (containsCoincidence(coin)) { |
| +// SkASSERT(coin->containsCoincidence(this)); |
| + return; |
| + } |
| + debugValidate(); |
| +// SkASSERT(this != coin); |
| + log->record(kMarkCoinStart_Glitch, id, this, coin); |
| +// coin->fCoincident = this->fCoincident; |
| +// this->fCoincident = coinNext; |
| + debugValidate(); |
| +} |
| + |
| +// Commented-out lines keep this in sync with insertCoincidence() |
| +void SkOpSpan::debugInsertCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpSegment* segment, bool flipped) const { |
| + if (this->containsCoincidence(segment)) { |
| + return; |
| + } |
| + const SkOpPtT* next = &fPtT; |
| + while ((next = next->next()) != &fPtT) { |
| + if (next->segment() == segment) { |
| + log->record(kMarkCoinInsert_Glitch, id, flipped ? next->span()->prev() : next->span()); |
| + return; |
| + } |
| + } |
| +#if DEBUG_COINCIDENCE |
| + log->record(kMarkCoinMissing_Glitch, id, segment, this); |
| +#endif |
| +} |
| +#endif |
| + |
| // called only by test code |
| int SkIntersections::debugCoincidentUsed() const { |
| if (!fIsCoincident[0]) { |
| @@ -1667,6 +2263,27 @@ int SkIntersections::debugCoincidentUsed() const { |
| #include "SkOpContour.h" |
| +// Commented-out lines keep this in sync with addOpp() |
| +bool SkOpPtT::debugAddOpp(const SkOpPtT* opp) const { |
| + // find the fOpp ptr to opp |
| + const SkOpPtT* oppPrev = opp->fNext; |
| + if (oppPrev == this) { |
| + return false; |
| + } |
| + while (oppPrev->fNext != opp) { |
| + oppPrev = oppPrev->fNext; |
| + if (oppPrev == this) { |
| + return false; |
| + } |
| + } |
| +// const SkOpPtT* oldNext = this->fNext; |
| + SkASSERT(this != opp); |
| +// this->fNext = opp; |
| +// SkASSERT(oppPrev != oldNext); |
| +// oppPrev->fNext = oldNext; |
| + return true; |
| +} |
| + |
| bool SkOpPtT::debugContains(const SkOpPtT* check) const { |
| SkASSERT(this != check); |
| const SkOpPtT* ptT = this; |
| @@ -1736,6 +2353,11 @@ int SkOpPtT::debugLoopLimit(bool report) const { |
| } |
| void SkOpPtT::debugValidate() const { |
| +#if DEBUG_COINCIDENCE |
| + if (this->globalState()->debugCheckHealth()) { |
| + return; |
| + } |
| +#endif |
| #if DEBUG_VALIDATE |
| SkOpGlobalState::Phase phase = contour()->globalState()->phase(); |
| if (phase == SkOpGlobalState::kIntersecting |