aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/pathops/SkPathOpsDebug.cpp
diff options
context:
space:
mode:
authorGravatar caryclark <caryclark@google.com>2016-07-18 10:01:36 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-07-18 10:01:36 -0700
commit55888e44171ffd48b591d19256884a969fe4da17 (patch)
treee3aed151b6074bba5357263199a5915ec31a0b99 /src/pathops/SkPathOpsDebug.cpp
parent6451a0cea6007aff54565ec82e2f86cb1d32ecc7 (diff)
pathops coincidence and security rewrite
Most changes stem from working on an examples bracketed by #if DEBUG_UNDER_DEVELOPMENT // tiger These exposed many problems with coincident curves, as well as errors throughout the code. Fixing these errors also fixed a number of fuzzer-inspired bug reports. * Line/Curve Intersections Check to see if the end of the line nearly intersects the curve. This was a FIXME in the old code. * Performance Use a central chunk allocator. Plumb the allocator into the global variable state so that it can be shared. (Note that 'SkGlobalState' is allocated on the stack and is visible to children functions but not other threads.) * Refactor Let SkOpAngle grow up from a structure to a class. Let SkCoincidentSpans grow up from a structure to a class. Rename enum Alias to AliasMatch. * Coincidence Rewrite Add more debugging to coincidence detection. Parallel debugging routines have read-only logic to report the current coincidence state so that steps through the logic can expose whether things got better or worse. More functions can error-out and cause the pathops engine to non-destructively exit. * Accuracy Remove code that adjusted point locations. Instead, offset the curve part so that sorted curves all use the same origin. Reduce the size (and influence) of magic numbers. * Testing The debug suite with verify and the full release suite ./out/Debug/pathops_unittest -v -V ./out/Release/pathops_unittest -v -V -x expose one error. That error is captured as cubics_d3. This error exists in the checked in code as well. BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2128633003 BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2128633003 Review-Url: https://codereview.chromium.org/2128633003
Diffstat (limited to 'src/pathops/SkPathOpsDebug.cpp')
-rw-r--r--src/pathops/SkPathOpsDebug.cpp1704
1 files changed, 1163 insertions, 541 deletions
diff --git a/src/pathops/SkPathOpsDebug.cpp b/src/pathops/SkPathOpsDebug.cpp
index 546771e84c..93743f3d6c 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
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__,
+// 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;
+ }
+ 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;
+// 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