#include "src/pathops/SkPathOpsDebug.h"
#include "include/core/SkPath.h"
#include "include/core/SkPathTypes.h"
#include "include/core/SkPoint.h"
#include "include/core/SkScalar.h"
#include "include/core/SkString.h"
#include "include/private/base/SkDebug.h"
#include "include/private/base/SkMath.h"
#include "include/private/base/SkMutex.h"
#include "src/core/SkPathPriv.h"
#include "src/pathops/SkIntersections.h"
#include "src/pathops/SkOpAngle.h"
#include "src/pathops/SkOpCoincidence.h"
#include "src/pathops/SkOpSegment.h"
#include "src/pathops/SkOpSpan.h"
#include "src/pathops/SkPathOpsConic.h"
#include "src/pathops/SkPathOpsCubic.h"
#include "src/pathops/SkPathOpsPoint.h"
#include "src/pathops/SkPathOpsQuad.h"
#include "src/pathops/SkPathOpsRect.h"
#include "src/pathops/SkPathOpsTypes.h"
#include <cstdint>
#include <cstring>
#if DEBUG_DUMP_VERIFY
bool SkPathOpsDebug::gDumpOp;
bool SkPathOpsDebug::gVerifyOp;
#endif
bool SkPathOpsDebug::gRunFail;
bool SkPathOpsDebug::gVeryVerbose;
#define FAIL_IF_COIN(cond, coin) …
#undef FAIL_WITH_NULL_IF
#define FAIL_WITH_NULL_IF(cond, span) …
#define RETURN_FALSE_IF(cond, span) …
#if DEBUG_SORT
int SkPathOpsDebug::gSortCountDefault = SK_MaxS32;
int SkPathOpsDebug::gSortCount;
#endif
#if DEBUG_ACTIVE_OP
const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor", "rdiff"};
#endif
#if defined SK_DEBUG || !FORCE_RELEASE
int SkPathOpsDebug::gContourID = …;
int SkPathOpsDebug::gSegmentID = …;
bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase*>& chaseArray,
const SkOpSpanBase* span) { … }
#endif
#if DEBUG_ACTIVE_SPANS
SkString SkPathOpsDebug::gActiveSpans;
#endif
#if DEBUG_COIN
#include "src/pathops/SkOpContour.h"
class SkCoincidentSpans;
SkPathOpsDebug::CoinDict SkPathOpsDebug::gCoinSumChangedDict;
SkPathOpsDebug::CoinDict SkPathOpsDebug::gCoinSumVisitedDict;
static const int kGlitchType_Count = SkPathOpsDebug::kUnalignedTail_Glitch + 1;
struct SpanGlitch {
const SkOpSpanBase* fBase;
const SkOpSpanBase* fSuspect;
const SkOpSegment* fSegment;
const SkOpSegment* fOppSegment;
const SkOpPtT* fCoinSpan;
const SkOpPtT* fEndSpan;
const SkOpPtT* fOppSpan;
const SkOpPtT* fOppEndSpan;
double fStartT;
double fEndT;
double fOppStartT;
double fOppEndT;
SkPoint fPt;
SkPathOpsDebug::GlitchType fType;
void dumpType() const;
};
struct SkPathOpsDebug::GlitchLog {
void init(const SkOpGlobalState* state) {
fGlobalState = state;
}
SpanGlitch* recordCommon(GlitchType type) {
SpanGlitch* glitch = fGlitches.append();
glitch->fBase = nullptr;
glitch->fSuspect = nullptr;
glitch->fSegment = nullptr;
glitch->fOppSegment = nullptr;
glitch->fCoinSpan = nullptr;
glitch->fEndSpan = nullptr;
glitch->fOppSpan = nullptr;
glitch->fOppEndSpan = nullptr;
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;
}
void record(GlitchType type, const SkOpSpanBase* base, const SkOpSpanBase* suspect = nullptr) {
SpanGlitch* glitch = recordCommon(type);
glitch->fBase = base;
glitch->fSuspect = suspect;
}
void record(GlitchType type, const SkOpSpanBase* base, const SkOpPtT* ptT) {
SpanGlitch* glitch = recordCommon(type);
glitch->fBase = base;
glitch->fCoinSpan = ptT;
}
void record(GlitchType type,
const SkCoincidentSpans* coin,
const SkCoincidentSpans* opp = nullptr) {
SpanGlitch* glitch = recordCommon(type);
glitch->fCoinSpan = coin->coinPtTStart();
glitch->fEndSpan = coin->coinPtTEnd();
if (opp) {
glitch->fOppSpan = opp->coinPtTStart();
glitch->fOppEndSpan = opp->coinPtTEnd();
}
}
void record(GlitchType type, const SkOpSpanBase* base,
const SkOpSegment* seg, double t, SkPoint pt) {
SpanGlitch* glitch = recordCommon(type);
glitch->fBase = base;
glitch->fSegment = seg;
glitch->fStartT = t;
glitch->fPt = pt;
}
void record(GlitchType type, const SkOpSpanBase* base, double t,
SkPoint pt) {
SpanGlitch* glitch = recordCommon(type);
glitch->fBase = base;
glitch->fStartT = t;
glitch->fPt = pt;
}
void record(GlitchType type, const SkCoincidentSpans* coin,
const SkOpPtT* coinSpan, const SkOpPtT* endSpan) {
SpanGlitch* glitch = recordCommon(type);
glitch->fCoinSpan = coin->coinPtTStart();
glitch->fEndSpan = coin->coinPtTEnd();
glitch->fEndSpan = endSpan;
glitch->fOppSpan = coinSpan;
glitch->fOppEndSpan = endSpan;
}
void record(GlitchType type, const SkCoincidentSpans* coin,
const SkOpSpanBase* base) {
SpanGlitch* glitch = recordCommon(type);
glitch->fBase = base;
glitch->fCoinSpan = coin->coinPtTStart();
glitch->fEndSpan = coin->coinPtTEnd();
}
void record(GlitchType type, const SkOpPtT* ptTS, const SkOpPtT* ptTE,
const SkOpPtT* oPtTS, const SkOpPtT* oPtTE) {
SpanGlitch* glitch = recordCommon(type);
glitch->fCoinSpan = ptTS;
glitch->fEndSpan = ptTE;
glitch->fOppSpan = oPtTS;
glitch->fOppEndSpan = oPtTE;
}
void record(GlitchType type, const SkOpSegment* seg, double startT,
double endT, const SkOpSegment* oppSeg, double oppStartT, double oppEndT) {
SpanGlitch* glitch = recordCommon(type);
glitch->fSegment = seg;
glitch->fStartT = startT;
glitch->fEndT = endT;
glitch->fOppSegment = oppSeg;
glitch->fOppStartT = oppStartT;
glitch->fOppEndT = oppEndT;
}
void record(GlitchType type, const SkOpSegment* seg,
const SkOpSpan* span) {
SpanGlitch* glitch = recordCommon(type);
glitch->fSegment = seg;
glitch->fBase = span;
}
void record(GlitchType type, double t, const SkOpSpanBase* span) {
SpanGlitch* glitch = recordCommon(type);
glitch->fStartT = t;
glitch->fBase = span;
}
void record(GlitchType type, const SkOpSegment* seg) {
SpanGlitch* glitch = recordCommon(type);
glitch->fSegment = seg;
}
void record(GlitchType type, const SkCoincidentSpans* coin,
const SkOpPtT* ptT) {
SpanGlitch* glitch = recordCommon(type);
glitch->fCoinSpan = coin->coinPtTStart();
glitch->fEndSpan = ptT;
}
SkTDArray<SpanGlitch> fGlitches;
const SkOpGlobalState* fGlobalState;
};
void SkPathOpsDebug::CoinDict::add(const SkPathOpsDebug::CoinDict& dict) {
int count = dict.fDict.size();
for (int index = 0; index < count; ++index) {
this->add(dict.fDict[index]);
}
}
void SkPathOpsDebug::CoinDict::add(const CoinDictEntry& key) {
int count = fDict.size();
for (int index = 0; index < count; ++index) {
CoinDictEntry* entry = &fDict[index];
if (entry->fIteration == key.fIteration && entry->fLineNumber == key.fLineNumber) {
SkASSERT(!strcmp(entry->fFunctionName, key.fFunctionName));
if (entry->fGlitchType == kUninitialized_Glitch) {
entry->fGlitchType = key.fGlitchType;
}
return;
}
}
*fDict.append() = key;
}
#endif
#if DEBUG_COIN
static void missing_coincidence(SkPathOpsDebug::GlitchLog* glitches,
const SkOpContourHead* contourList) {
const SkOpContour* contour = contourList;
do {
contour->debugMissingCoincidence(glitches);
} while ((contour = contour->next()));
return;
}
static void move_multiples(SkPathOpsDebug::GlitchLog* glitches,
const SkOpContourHead* contourList) {
const SkOpContour* contour = contourList;
do {
contour->debugMoveMultiples(glitches);
} while ((contour = contour->next()));
return;
}
static void move_nearby(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) {
const SkOpContour* contour = contourList;
do {
contour->debugMoveNearby(glitches);
} while ((contour = contour->next()));
}
#endif
#if DEBUG_COIN
void SkOpGlobalState::debugAddToCoinChangedDict() {
#if DEBUG_COINCIDENCE
SkPathOpsDebug::CheckHealth(fContourHead);
#endif
SkPathOpsDebug::GlitchLog glitches;
const char* funcName = fCoinDictEntry.fFunctionName;
if (!strcmp("calc_angles", funcName)) {
} else if (!strcmp("missing_coincidence", funcName)) {
missing_coincidence(&glitches, fContourHead);
} else if (!strcmp("move_multiples", funcName)) {
move_multiples(&glitches, fContourHead);
} else if (!strcmp("move_nearby", funcName)) {
move_nearby(&glitches, fContourHead);
} else if (!strcmp("addExpanded", funcName)) {
fCoincidence->debugAddExpanded(&glitches);
} else if (!strcmp("addMissing", funcName)) {
bool added;
fCoincidence->debugAddMissing(&glitches, &added);
} else if (!strcmp("addEndMovedSpans", funcName)) {
fCoincidence->debugAddEndMovedSpans(&glitches);
} else if (!strcmp("correctEnds", funcName)) {
fCoincidence->debugCorrectEnds(&glitches);
} else if (!strcmp("expand", funcName)) {
fCoincidence->debugExpand(&glitches);
} else if (!strcmp("findOverlaps", funcName)) {
} else if (!strcmp("mark", funcName)) {
fCoincidence->debugMark(&glitches);
} else if (!strcmp("apply", funcName)) {
} else {
SkASSERT(0);
}
if (glitches.fGlitches.size()) {
fCoinDictEntry.fGlitchType = glitches.fGlitches[0].fType;
}
fCoinChangedDict.add(fCoinDictEntry);
}
#endif
void SkPathOpsDebug::ShowActiveSpans(SkOpContourHead* contourList) { … }
#if DEBUG_COINCIDENCE || DEBUG_COIN
void SkPathOpsDebug::CheckHealth(SkOpContourHead* contourList) {
#if DEBUG_COINCIDENCE
contourList->globalState()->debugSetCheckHealth(true);
#endif
#if DEBUG_COIN
GlitchLog glitches;
const SkOpContour* contour = contourList;
const SkOpCoincidence* coincidence = contour->globalState()->coincidence();
coincidence->debugCheckValid(&glitches);
do {
contour->debugCheckHealth(&glitches);
contour->debugMissingCoincidence(&glitches);
} while ((contour = contour->next()));
bool added;
coincidence->debugAddMissing(&glitches, &added);
coincidence->debugExpand(&glitches);
coincidence->debugAddExpanded(&glitches);
coincidence->debugMark(&glitches);
unsigned mask = 0;
for (int index = 0; index < glitches.fGlitches.size(); ++index) {
const SpanGlitch& glitch = glitches.fGlitches[index];
mask |= 1 << glitch.fType;
}
for (int index = 0; index < kGlitchType_Count; ++index) {
SkDebugf(mask & (1 << index) ? "x" : "-");
}
SkDebugf(" %s\n", contourList->globalState()->debugCoinDictEntry().fFunctionName);
for (int index = 0; index < glitches.fGlitches.size(); ++index) {
const SpanGlitch& glitch = glitches.fGlitches[index];
SkDebugf("%02d: ", index);
if (glitch.fBase) {
SkDebugf(" seg/base=%d/%d", glitch.fBase->segment()->debugID(),
glitch.fBase->debugID());
}
if (glitch.fSuspect) {
SkDebugf(" seg/base=%d/%d", glitch.fSuspect->segment()->debugID(),
glitch.fSuspect->debugID());
}
if (glitch.fSegment) {
SkDebugf(" segment=%d", glitch.fSegment->debugID());
}
if (glitch.fCoinSpan) {
SkDebugf(" coinSeg/Span/PtT=%d/%d/%d", glitch.fCoinSpan->segment()->debugID(),
glitch.fCoinSpan->span()->debugID(), glitch.fCoinSpan->debugID());
}
if (glitch.fEndSpan) {
SkDebugf(" endSpan=%d", glitch.fEndSpan->debugID());
}
if (glitch.fOppSpan) {
SkDebugf(" oppSeg/Span/PtT=%d/%d/%d", glitch.fOppSpan->segment()->debugID(),
glitch.fOppSpan->span()->debugID(), glitch.fOppSpan->debugID());
}
if (glitch.fOppEndSpan) {
SkDebugf(" oppEndSpan=%d", glitch.fOppEndSpan->debugID());
}
if (!SkIsNaN(glitch.fStartT)) {
SkDebugf(" startT=%g", glitch.fStartT);
}
if (!SkIsNaN(glitch.fEndT)) {
SkDebugf(" endT=%g", glitch.fEndT);
}
if (glitch.fOppSegment) {
SkDebugf(" segment=%d", glitch.fOppSegment->debugID());
}
if (!SkIsNaN(glitch.fOppStartT)) {
SkDebugf(" oppStartT=%g", glitch.fOppStartT);
}
if (!SkIsNaN(glitch.fOppEndT)) {
SkDebugf(" oppEndT=%g", glitch.fOppEndT);
}
if (!SkIsNaN(glitch.fPt.fX) || !SkIsNaN(glitch.fPt.fY)) {
SkDebugf(" pt=%g,%g", glitch.fPt.fX, glitch.fPt.fY);
}
DumpGlitchType(glitch.fType);
SkDebugf("\n");
}
#if DEBUG_COINCIDENCE
contourList->globalState()->debugSetCheckHealth(false);
#endif
#if 01 && DEBUG_ACTIVE_SPANS
ShowActiveSpans(contourList);
#endif
#endif
}
#endif
#if DEBUG_COIN
void SkPathOpsDebug::DumpGlitchType(GlitchType glitchType) {
switch (glitchType) {
case kAddCorruptCoin_Glitch: SkDebugf(" AddCorruptCoin"); break;
case kAddExpandedCoin_Glitch: SkDebugf(" AddExpandedCoin"); break;
case kAddExpandedFail_Glitch: SkDebugf(" AddExpandedFail"); break;
case kAddIfCollapsed_Glitch: SkDebugf(" AddIfCollapsed"); 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 kCorrectEnd_Glitch: SkDebugf(" CorrectEnd"); break;
case kDeletedCoin_Glitch: SkDebugf(" DeletedCoin"); break;
case kExpandCoin_Glitch: SkDebugf(" ExpandCoin"); break;
case kFail_Glitch: SkDebugf(" Fail"); 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 kMergeMatches_Glitch: SkDebugf(" MergeMatches"); 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 kReturnFalse_Glitch: SkDebugf(" ReturnFalse"); break;
case kUnaligned_Glitch: SkDebugf(" Unaligned"); break;
case kUnalignedHead_Glitch: SkDebugf(" UnalignedHead"); break;
case kUnalignedTail_Glitch: SkDebugf(" UnalignedTail"); break;
case kUninitialized_Glitch: break;
default: SkASSERT(0);
}
}
#endif
#if defined SK_DEBUG || !FORCE_RELEASE
void SkPathOpsDebug::MathematicaIze(char* str, size_t bufferLen) { … }
bool SkPathOpsDebug::ValidWind(int wind) { … }
void SkPathOpsDebug::WindingPrintf(int wind) { … }
#endif
static void show_function_header(const char* functionName) { … }
static const char* gOpStrs[] = …;
const char* SkPathOpsDebug::OpStr(SkPathOp op) { … }
static void show_op(SkPathOp op, const char* pathOne, const char* pathTwo) { … }
void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
const char* testName) { … }
#if DEBUG_COIN
void SkOpGlobalState::debugAddToGlobalCoinDicts() {
static SkMutex& mutex = *(new SkMutex);
SkAutoMutexExclusive ac(mutex);
SkPathOpsDebug::gCoinSumChangedDict.add(fCoinChangedDict);
SkPathOpsDebug::gCoinSumVisitedDict.add(fCoinVisitedDict);
}
#endif
#if DEBUG_T_SECT_LOOP_COUNT
void SkOpGlobalState::debugAddLoopCount(SkIntersections* i, const SkIntersectionHelper& wt,
const SkIntersectionHelper& wn) {
for (int index = 0; index < (int) std::size(fDebugLoopCount); ++index) {
SkIntersections::DebugLoop looper = (SkIntersections::DebugLoop) index;
if (fDebugLoopCount[index] >= i->debugLoopCount(looper)) {
continue;
}
fDebugLoopCount[index] = i->debugLoopCount(looper);
fDebugWorstVerb[index * 2] = wt.segment()->verb();
fDebugWorstVerb[index * 2 + 1] = wn.segment()->verb();
sk_bzero(&fDebugWorstPts[index * 8], sizeof(SkPoint) * 8);
memcpy(&fDebugWorstPts[index * 2 * 4], wt.pts(),
(SkPathOpsVerbToPoints(wt.segment()->verb()) + 1) * sizeof(SkPoint));
memcpy(&fDebugWorstPts[(index * 2 + 1) * 4], wn.pts(),
(SkPathOpsVerbToPoints(wn.segment()->verb()) + 1) * sizeof(SkPoint));
fDebugWorstWeight[index * 2] = wt.weight();
fDebugWorstWeight[index * 2 + 1] = wn.weight();
}
i->debugResetLoopCount();
}
void SkOpGlobalState::debugDoYourWorst(SkOpGlobalState* local) {
for (int index = 0; index < (int) std::size(fDebugLoopCount); ++index) {
if (fDebugLoopCount[index] >= local->fDebugLoopCount[index]) {
continue;
}
fDebugLoopCount[index] = local->fDebugLoopCount[index];
fDebugWorstVerb[index * 2] = local->fDebugWorstVerb[index * 2];
fDebugWorstVerb[index * 2 + 1] = local->fDebugWorstVerb[index * 2 + 1];
memcpy(&fDebugWorstPts[index * 2 * 4], &local->fDebugWorstPts[index * 2 * 4],
sizeof(SkPoint) * 8);
fDebugWorstWeight[index * 2] = local->fDebugWorstWeight[index * 2];
fDebugWorstWeight[index * 2 + 1] = local->fDebugWorstWeight[index * 2 + 1];
}
local->debugResetLoopCounts();
}
static void dump_curve(SkPath::Verb verb, const SkPoint& pts, float weight) {
if (!verb) {
return;
}
const char* verbs[] = { "", "line", "quad", "conic", "cubic" };
SkDebugf("%s: {{", verbs[verb]);
int ptCount = SkPathOpsVerbToPoints(verb);
for (int index = 0; index <= ptCount; ++index) {
SkDPoint::Dump((&pts)[index]);
if (index < ptCount - 1) {
SkDebugf(", ");
}
}
SkDebugf("}");
if (weight != 1) {
SkDebugf(", ");
if (weight == floorf(weight)) {
SkDebugf("%.0f", weight);
} else {
SkDebugf("%1.9gf", weight);
}
}
SkDebugf("}\n");
}
void SkOpGlobalState::debugLoopReport() {
const char* loops[] = { "iterations", "coinChecks", "perpCalcs" };
SkDebugf("\n");
for (int index = 0; index < (int) std::size(fDebugLoopCount); ++index) {
SkDebugf("%s: %d\n", loops[index], fDebugLoopCount[index]);
dump_curve(fDebugWorstVerb[index * 2], fDebugWorstPts[index * 2 * 4],
fDebugWorstWeight[index * 2]);
dump_curve(fDebugWorstVerb[index * 2 + 1], fDebugWorstPts[(index * 2 + 1) * 4],
fDebugWorstWeight[index * 2 + 1]);
}
}
void SkOpGlobalState::debugResetLoopCounts() {
sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
sk_bzero(fDebugWorstVerb, sizeof(fDebugWorstVerb));
sk_bzero(fDebugWorstPts, sizeof(fDebugWorstPts));
sk_bzero(fDebugWorstWeight, sizeof(fDebugWorstWeight));
}
#endif
bool SkOpGlobalState::DebugRunFail() { … }
#if DEBUG_VALIDATE || DEBUG_COIN
void SkOpGlobalState::debugSetPhase(const char* funcName DEBUG_COIN_DECLARE_PARAMS()) const {
auto writable = const_cast<SkOpGlobalState*>(this);
#if DEBUG_VALIDATE
writable->setPhase(phase);
#endif
#if DEBUG_COIN
SkPathOpsDebug::CoinDictEntry* entry = &writable->fCoinDictEntry;
writable->fPreviousFuncName = entry->fFunctionName;
entry->fIteration = iteration;
entry->fLineNumber = lineNo;
entry->fGlitchType = SkPathOpsDebug::kUninitialized_Glitch;
entry->fFunctionName = funcName;
writable->fCoinVisitedDict.add(*entry);
writable->debugAddToCoinChangedDict();
#endif
}
#endif
#if DEBUG_T_SECT_LOOP_COUNT
void SkIntersections::debugBumpLoopCount(DebugLoop index) {
fDebugLoopCount[index]++;
}
int SkIntersections::debugLoopCount(DebugLoop index) const {
return fDebugLoopCount[index];
}
void SkIntersections::debugResetLoopCount() {
sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
}
#endif
SkDCubic SkDQuad::debugToCubic() const { … }
void SkDQuad::debugSet(const SkDPoint* pts) { … }
void SkDCubic::debugSet(const SkDPoint* pts) { … }
void SkDConic::debugSet(const SkDPoint* pts, SkScalar weight) { … }
void SkDRect::debugInit() { … }
#if DEBUG_COIN
const SkOpPtT* SkOpSegment::debugAddT(double t, SkPathOpsDebug::GlitchLog* log) const {
debugValidate();
SkPoint pt = this->ptAtT(t);
const SkOpSpanBase* span = &fHead;
do {
const SkOpPtT* result = span->ptT();
if (t == result->fT || this->match(result, this, t, pt)) {
return result;
}
if (t < result->fT) {
const SkOpSpan* prev = result->span()->prev();
FAIL_WITH_NULL_IF(!prev, span);
this->globalState()->setAllocatedOpSpan();
this->debugValidate();
return nullptr;
}
FAIL_WITH_NULL_IF(span != &fTail, span);
} while ((span = span->upCast()->next()));
SkASSERT(0);
return nullptr;
}
#endif
#if DEBUG_ANGLE
void SkOpSegment::debugCheckAngleCoin() const {
const SkOpSpanBase* base = &fHead;
const SkOpSpan* span;
do {
const SkOpAngle* angle = base->fromAngle();
if (angle && angle->debugCheckCoincidence()) {
angle->debugCheckNearCoincidence();
}
if (base->final()) {
break;
}
span = base->upCast();
angle = span->toAngle();
if (angle && angle->debugCheckCoincidence()) {
angle->debugCheckNearCoincidence();
}
} while ((base = span->next()));
}
#endif
#if DEBUG_COIN
void SkOpSegment::debugCheckHealth(SkPathOpsDebug::GlitchLog* glitches) const {
debugMoveMultiples(glitches);
debugMoveNearby(glitches);
debugMissingCoincidence(glitches);
}
void SkOpSegment::debugClearAll(SkPathOpsDebug::GlitchLog* glitches) const {
const SkOpSpan* span = &fHead;
do {
this->debugClearOne(span, glitches);
} while ((span = span->next()->upCastable()));
this->globalState()->coincidence()->debugRelease(glitches, this);
}
void SkOpSegment::debugClearOne(const SkOpSpan* span, SkPathOpsDebug::GlitchLog* glitches) const {
if (span->windValue()) glitches->record(SkPathOpsDebug::kCollapsedWindValue_Glitch, span);
if (span->oppValue()) glitches->record(SkPathOpsDebug::kCollapsedOppValue_Glitch, span);
if (!span->done()) glitches->record(SkPathOpsDebug::kCollapsedDone_Glitch, span);
}
#endif
SkOpAngle* SkOpSegment::debugLastAngle() { … }
#if DEBUG_COIN
void SkOpSegment::DebugClearVisited(const SkOpSpanBase* span) {
do {
const SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
while ((ptT = ptT->next()) != stopPtT) {
const SkOpSegment* opp = ptT->segment();
opp->resetDebugVisited();
}
} while (!span->final() && (span = span->upCast()->next()));
}
#endif
#if DEBUG_COIN
void SkOpSegment::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const {
if (this->done()) {
return;
}
const SkOpSpan* prior = nullptr;
const SkOpSpanBase* spanBase = &fHead;
do {
const SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT;
SkASSERT(ptT->span() == spanBase);
while ((ptT = ptT->next()) != spanStopPtT) {
if (ptT->deleted()) {
continue;
}
const SkOpSegment* opp = ptT->span()->segment();
if (opp->done()) {
continue;
}
if (!opp->debugVisited()) {
continue;
}
if (spanBase == &fHead) {
continue;
}
if (ptT->segment() == this) {
continue;
}
const SkOpSpan* span = spanBase->upCastable();
if (span && span->segment() != opp && span->containsCoincidence(opp)) {
continue;
}
if (spanBase->segment() != opp && spanBase->containsCoinEnd(opp)) {
continue;
}
const SkOpPtT* priorPtT = nullptr, * priorStopPtT;
const SkOpSegment* priorOpp = nullptr;
const SkOpSpan* priorTest = spanBase->prev();
while (!priorOpp && priorTest) {
priorStopPtT = priorPtT = priorTest->ptT();
while ((priorPtT = priorPtT->next()) != priorStopPtT) {
if (priorPtT->deleted()) {
continue;
}
const SkOpSegment* segment = priorPtT->span()->segment();
if (segment == opp) {
prior = priorTest;
priorOpp = opp;
break;
}
}
priorTest = priorTest->prev();
}
if (!priorOpp) {
continue;
}
if (priorPtT == ptT) {
continue;
}
const SkOpPtT* oppStart = prior->ptT();
const SkOpPtT* oppEnd = spanBase->ptT();
bool swapped = priorPtT->fT > ptT->fT;
if (swapped) {
using std::swap;
swap(priorPtT, ptT);
swap(oppStart, oppEnd);
}
const SkOpCoincidence* coincidence = this->globalState()->coincidence();
const SkOpPtT* rootPriorPtT = priorPtT->span()->ptT();
const SkOpPtT* rootPtT = ptT->span()->ptT();
const SkOpPtT* rootOppStart = oppStart->span()->ptT();
const SkOpPtT* rootOppEnd = oppEnd->span()->ptT();
if (coincidence->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) {
goto swapBack;
}
if (testForCoincidence(rootPriorPtT, rootPtT, prior, spanBase, opp)) {
#if DEBUG_COINCIDENCE_VERBOSE
#endif
log->record(SkPathOpsDebug::kMissingCoin_Glitch, priorPtT, ptT, oppStart, oppEnd);
#if DEBUG_COINCIDENCE
#endif
}
swapBack:
if (swapped) {
using std::swap;
swap(priorPtT, ptT);
}
}
} while ((spanBase = spanBase->final() ? nullptr : spanBase->upCast()->next()));
DebugClearVisited(&fHead);
return;
}
void SkOpSegment::debugMoveMultiples(SkPathOpsDebug::GlitchLog* glitches) const {
debugValidate();
const SkOpSpanBase* test = &fHead;
do {
int addCount = test->spanAddsCount();
if (addCount <= 1) {
continue;
}
const SkOpPtT* startPtT = test->ptT();
const SkOpPtT* testPtT = startPtT;
do {
const SkOpSpanBase* oppSpan = testPtT->span();
if (oppSpan->spanAddsCount() == addCount) {
continue;
}
if (oppSpan->deleted()) {
continue;
}
const SkOpSegment* oppSegment = oppSpan->segment();
if (oppSegment == this) {
continue;
}
const SkOpSpanBase* oppPrev = oppSpan;
const SkOpSpanBase* oppFirst = oppSpan;
while ((oppPrev = oppPrev->prev())) {
if (!roughly_equal(oppPrev->t(), oppSpan->t())) {
break;
}
if (oppPrev->spanAddsCount() == addCount) {
continue;
}
if (oppPrev->deleted()) {
continue;
}
oppFirst = oppPrev;
}
const SkOpSpanBase* oppNext = oppSpan;
const SkOpSpanBase* oppLast = oppSpan;
while ((oppNext = oppNext->final() ? nullptr : oppNext->upCast()->next())) {
if (!roughly_equal(oppNext->t(), oppSpan->t())) {
break;
}
if (oppNext->spanAddsCount() == addCount) {
continue;
}
if (oppNext->deleted()) {
continue;
}
oppLast = oppNext;
}
if (oppFirst == oppLast) {
continue;
}
const SkOpSpanBase* oppTest = oppFirst;
do {
if (oppTest == oppSpan) {
continue;
}
const SkOpPtT* oppStartPtT = oppTest->ptT();
const SkOpPtT* oppPtT = oppStartPtT;
while ((oppPtT = oppPtT->next()) != oppStartPtT) {
const SkOpSegment* oppPtTSegment = oppPtT->segment();
if (oppPtTSegment == this) {
goto tryNextSpan;
}
const SkOpPtT* matchPtT = startPtT;
do {
if (matchPtT->segment() == oppPtTSegment) {
goto foundMatch;
}
} while ((matchPtT = matchPtT->next()) != startPtT);
goto tryNextSpan;
foundMatch:
oppSegment->debugValidate();
oppTest->debugMergeMatches(glitches, oppSpan);
oppTest->debugAddOpp(glitches, oppSpan);
oppSegment->debugValidate();
goto checkNextSpan;
}
tryNextSpan:
;
} while (oppTest != oppLast && (oppTest = oppTest->upCast()->next()));
} while ((testPtT = testPtT->next()) != startPtT);
checkNextSpan:
;
} while ((test = test->final() ? nullptr : test->upCast()->next()));
debugValidate();
return;
}
void SkOpSegment::debugMoveNearby(SkPathOpsDebug::GlitchLog* glitches) const {
debugValidate();
const SkOpSpanBase* spanBase = &fHead;
do {
const SkOpPtT* ptT = spanBase->ptT();
const SkOpPtT* headPtT = ptT;
while ((ptT = ptT->next()) != headPtT) {
const SkOpSpanBase* test = ptT->span();
if (ptT->segment() == this && !ptT->deleted() && test != spanBase
&& test->ptT() == ptT) {
if (test->final()) {
if (spanBase == &fHead) {
glitches->record(SkPathOpsDebug::kMoveNearbyClearAll_Glitch, this);
}
glitches->record(SkPathOpsDebug::kMoveNearbyReleaseFinal_Glitch, spanBase, ptT);
} else if (test->prev()) {
glitches->record(SkPathOpsDebug::kMoveNearbyRelease_Glitch, test, headPtT);
}
}
}
spanBase = spanBase->upCast()->next();
} while (!spanBase->final());
spanBase = &fHead;
do {
const SkOpSpanBase* test = spanBase->upCast()->next();
bool found;
if (!this->spansNearby(spanBase, test, &found)) {
glitches->record(SkPathOpsDebug::kMoveNearbyMergeFinal_Glitch, test);
}
if (found) {
if (test->final()) {
if (spanBase->prev()) {
glitches->record(SkPathOpsDebug::kMoveNearbyMergeFinal_Glitch, test);
} else {
glitches->record(SkPathOpsDebug::kMoveNearbyClearAll2_Glitch, this);
}
} else {
glitches->record(SkPathOpsDebug::kMoveNearbyMerge_Glitch, spanBase);
}
}
spanBase = test;
} while (!spanBase->final());
debugValidate();
}
#endif
void SkOpSegment::debugReset() { … }
#if DEBUG_COINCIDENCE_ORDER
void SkOpSegment::debugSetCoinT(int index, SkScalar t) const {
if (fDebugBaseMax < 0 || fDebugBaseIndex == index) {
fDebugBaseIndex = index;
fDebugBaseMin = std::min(t, fDebugBaseMin);
fDebugBaseMax = std::max(t, fDebugBaseMax);
return;
}
SkASSERT(fDebugBaseMin >= t || t >= fDebugBaseMax);
if (fDebugLastMax < 0 || fDebugLastIndex == index) {
fDebugLastIndex = index;
fDebugLastMin = std::min(t, fDebugLastMin);
fDebugLastMax = std::max(t, fDebugLastMax);
return;
}
SkASSERT(fDebugLastMin >= t || t >= fDebugLastMax);
SkASSERT((t - fDebugBaseMin > 0) == (fDebugLastMin - fDebugBaseMin > 0));
}
#endif
#if DEBUG_ACTIVE_SPANS
void SkOpSegment::debugShowActiveSpans(SkString* str) const {
debugValidate();
if (done()) {
return;
}
int lastId = -1;
double lastT = -1;
const SkOpSpan* span = &fHead;
do {
if (span->done()) {
continue;
}
if (lastId == this->debugID() && lastT == span->t()) {
continue;
}
lastId = this->debugID();
lastT = span->t();
str->appendf("%s id=%d", __FUNCTION__, this->debugID());
SkDCurve curvePart;
this->subDivide(span, span->next(), &curvePart);
const SkDPoint* pts = curvePart.fCubic.fPts;
str->appendf(" (%1.9g,%1.9g", pts[0].fX, pts[0].fY);
for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
str->appendf(" %1.9g,%1.9g", pts[vIndex].fX, pts[vIndex].fY);
}
if (SkPath::kConic_Verb == fVerb) {
str->appendf(" %1.9gf", curvePart.fConic.fWeight);
}
str->appendf(") t=%1.9g tEnd=%1.9g", span->t(), span->next()->t());
if (span->windSum() == SK_MinS32) {
str->appendf(" windSum=?");
} else {
str->appendf(" windSum=%d", span->windSum());
}
if (span->oppValue() && span->oppSum() == SK_MinS32) {
str->appendf(" oppSum=?");
} else if (span->oppValue() || span->oppSum() != SK_MinS32) {
str->appendf(" oppSum=%d", span->oppSum());
}
str->appendf(" windValue=%d", span->windValue());
if (span->oppValue() || span->oppSum() != SK_MinS32) {
str->appendf(" oppValue=%d", span->oppValue());
}
str->appendf("\n");
} while ((span = span->next()->upCastable()));
}
#endif
#if DEBUG_MARK_DONE
void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding) {
const SkPoint& pt = span->ptT()->fPt;
SkDebugf("%s id=%d", fun, this->debugID());
SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
}
SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t());
if (winding == SK_MinS32) {
SkDebugf("?");
} else {
SkDebugf("%d", winding);
}
SkDebugf(" windSum=");
if (span->windSum() == SK_MinS32) {
SkDebugf("?");
} else {
SkDebugf("%d", span->windSum());
}
SkDebugf(" windValue=%d\n", span->windValue());
}
void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding,
int oppWinding) {
const SkPoint& pt = span->ptT()->fPt;
SkDebugf("%s id=%d", fun, this->debugID());
SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
}
SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t());
if (winding == SK_MinS32) {
SkDebugf("?");
} else {
SkDebugf("%d", winding);
}
SkDebugf(" newOppSum=");
if (oppWinding == SK_MinS32) {
SkDebugf("?");
} else {
SkDebugf("%d", oppWinding);
}
SkDebugf(" oppSum=");
if (span->oppSum() == SK_MinS32) {
SkDebugf("?");
} else {
SkDebugf("%d", span->oppSum());
}
SkDebugf(" windSum=");
if (span->windSum() == SK_MinS32) {
SkDebugf("?");
} else {
SkDebugf("%d", span->windSum());
}
SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue());
}
#endif
#if DEBUG_ANGLE
void SkOpAngle::debugCheckNearCoincidence() const {
const SkOpAngle* test = this;
do {
const SkOpSegment* testSegment = test->segment();
double testStartT = test->start()->t();
SkDPoint testStartPt = testSegment->dPtAtT(testStartT);
double testEndT = test->end()->t();
SkDPoint testEndPt = testSegment->dPtAtT(testEndT);
double testLenSq = testStartPt.distanceSquared(testEndPt);
SkDebugf("%s testLenSq=%1.9g id=%d\n", __FUNCTION__, testLenSq, testSegment->debugID());
double testMidT = (testStartT + testEndT) / 2;
const SkOpAngle* next = test;
while ((next = next->fNext) != this) {
SkOpSegment* nextSegment = next->segment();
double testMidDistSq = testSegment->distSq(testMidT, next);
double testEndDistSq = testSegment->distSq(testEndT, next);
double nextStartT = next->start()->t();
SkDPoint nextStartPt = nextSegment->dPtAtT(nextStartT);
double distSq = testStartPt.distanceSquared(nextStartPt);
double nextEndT = next->end()->t();
double nextMidT = (nextStartT + nextEndT) / 2;
double nextMidDistSq = nextSegment->distSq(nextMidT, test);
double nextEndDistSq = nextSegment->distSq(nextEndT, test);
SkDebugf("%s distSq=%1.9g testId=%d nextId=%d\n", __FUNCTION__, distSq,
testSegment->debugID(), nextSegment->debugID());
SkDebugf("%s testMidDistSq=%1.9g\n", __FUNCTION__, testMidDistSq);
SkDebugf("%s testEndDistSq=%1.9g\n", __FUNCTION__, testEndDistSq);
SkDebugf("%s nextMidDistSq=%1.9g\n", __FUNCTION__, nextMidDistSq);
SkDebugf("%s nextEndDistSq=%1.9g\n", __FUNCTION__, nextEndDistSq);
SkDPoint nextEndPt = nextSegment->dPtAtT(nextEndT);
double nextLenSq = nextStartPt.distanceSquared(nextEndPt);
SkDebugf("%s nextLenSq=%1.9g\n", __FUNCTION__, nextLenSq);
SkDebugf("\n");
}
test = test->fNext;
} while (test->fNext != this);
}
#endif
#if DEBUG_ANGLE
SkString SkOpAngle::debugPart() const {
SkString result;
switch (this->segment()->verb()) {
case SkPath::kLine_Verb:
result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fPart.fCurve),
this->segment()->debugID());
break;
case SkPath::kQuad_Verb:
result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fPart.fCurve),
this->segment()->debugID());
break;
case SkPath::kConic_Verb:
result.printf(CONIC_DEBUG_STR " id=%d",
CONIC_DEBUG_DATA(fPart.fCurve, fPart.fCurve.fConic.fWeight),
this->segment()->debugID());
break;
case SkPath::kCubic_Verb:
result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fPart.fCurve),
this->segment()->debugID());
break;
default:
SkASSERT(0);
}
return result;
}
#endif
#if DEBUG_SORT
void SkOpAngle::debugLoop() const {
const SkOpAngle* first = this;
const SkOpAngle* next = this;
do {
next->dumpOne(true);
SkDebugf("\n");
next = next->fNext;
} while (next && next != first);
next = first;
do {
next->debugValidate();
next = next->fNext;
} while (next && next != first);
}
#endif
void SkOpAngle::debugValidate() const { … }
void SkOpAngle::debugValidateNext() const { … }
#ifdef SK_DEBUG
void SkCoincidentSpans::debugStartCheck(const SkOpSpanBase* outer, const SkOpSpanBase* over,
const SkOpGlobalState* debugState) const { … }
#endif
#if DEBUG_COIN
void SkCoincidentSpans::debugCorrectOneEnd(SkPathOpsDebug::GlitchLog* log,
const SkOpPtT* (SkCoincidentSpans::* getEnd)() const,
void (SkCoincidentSpans::*setEnd)(const SkOpPtT* ptT) const ) const {
const SkOpPtT* origPtT = (this->*getEnd)();
const SkOpSpanBase* origSpan = origPtT->span();
const SkOpSpan* prev = origSpan->prev();
const SkOpPtT* testPtT = prev ? prev->next()->ptT()
: origSpan->upCast()->next()->prev()->ptT();
if (origPtT != testPtT) {
log->record(SkPathOpsDebug::kCorrectEnd_Glitch, this, origPtT, testPtT);
}
}
void SkCoincidentSpans::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTStart, nullptr);
this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTEnd, nullptr);
this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTStart, nullptr);
this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTEnd, nullptr);
}
bool SkCoincidentSpans::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
bool expanded = false;
const SkOpSegment* segment = coinPtTStart()->segment();
const SkOpSegment* oppSegment = oppPtTStart()->segment();
do {
const SkOpSpan* start = coinPtTStart()->span()->upCast();
const SkOpSpan* prev = start->prev();
const SkOpPtT* oppPtT;
if (!prev || !(oppPtT = prev->contains(oppSegment))) {
break;
}
double midT = (prev->t() + start->t()) / 2;
if (!segment->isClose(midT, oppSegment)) {
break;
}
if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, prev->ptT(), oppPtT);
expanded = true;
} while (false);
do {
const SkOpSpanBase* end = coinPtTEnd()->span();
SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
if (next && next->deleted()) {
break;
}
const SkOpPtT* oppPtT;
if (!next || !(oppPtT = next->contains(oppSegment))) {
break;
}
double midT = (end->t() + next->t()) / 2;
if (!segment->isClose(midT, oppSegment)) {
break;
}
if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, next->ptT(), oppPtT);
expanded = true;
} while (false);
return expanded;
}
void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* base, const SkOpSpanBase* testSpan) const {
const SkOpPtT* testPtT = testSpan->ptT();
const SkOpPtT* stopPtT = testPtT;
const SkOpSegment* baseSeg = base->segment();
while ((testPtT = testPtT->next()) != stopPtT) {
const SkOpSegment* testSeg = testPtT->segment();
if (testPtT->deleted()) {
continue;
}
if (testSeg == baseSeg) {
continue;
}
if (testPtT->span()->ptT() != testPtT) {
continue;
}
if (this->contains(baseSeg, testSeg, testPtT->fT)) {
continue;
}
SkDVector dxdy = baseSeg->dSlopeAtT(base->t());
const SkPoint& pt = base->pt();
SkDLine ray = {{{pt.fX, pt.fY}, {pt.fX + dxdy.fY, pt.fY - dxdy.fX}}};
SkIntersections i;
(*CurveIntersectRay[testSeg->verb()])(testSeg->pts(), testSeg->weight(), ray, &i);
for (int index = 0; index < i.used(); ++index) {
double t = i[0][index];
if (!between(0, t, 1)) {
continue;
}
SkDPoint oppPt = i.pt(index);
if (!oppPt.approximatelyEqual(pt)) {
continue;
}
SkOpSegment* writableSeg = const_cast<SkOpSegment*>(testSeg);
SkOpPtT* oppStart = writableSeg->addT(t);
if (oppStart == testPtT) {
continue;
}
SkOpSpan* writableBase = const_cast<SkOpSpan*>(base);
oppStart->span()->addOpp(writableBase);
if (oppStart->deleted()) {
continue;
}
SkOpSegment* coinSeg = base->segment();
SkOpSegment* oppSeg = oppStart->segment();
double coinTs, coinTe, oppTs, oppTe;
if (Ordered(coinSeg, oppSeg)) {
coinTs = base->t();
coinTe = testSpan->t();
oppTs = oppStart->fT;
oppTe = testPtT->fT;
} else {
using std::swap;
swap(coinSeg, oppSeg);
coinTs = oppStart->fT;
coinTe = testPtT->fT;
oppTs = base->t();
oppTe = testSpan->t();
}
if (coinTs > coinTe) {
using std::swap;
swap(coinTs, coinTe);
swap(oppTs, oppTe);
}
bool added;
this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &added);
}
}
return;
}
void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* ptT) const {
FAIL_IF_COIN(!ptT->span()->upCastable(), ptT->span());
const SkOpSpan* base = ptT->span()->upCast();
const SkOpSpan* prev = base->prev();
FAIL_IF_COIN(!prev, ptT->span());
if (!prev->isCanceled()) {
this->debugAddEndMovedSpans(log, base, base->prev());
}
if (!base->isCanceled()) {
this->debugAddEndMovedSpans(log, base, base->next());
}
return;
}
void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log) const {
const SkCoincidentSpans* span = fHead;
if (!span) {
return;
}
do {
if (span->coinPtTStart()->fPt != span->oppPtTStart()->fPt) {
FAIL_IF_COIN(1 == span->coinPtTStart()->fT, span);
bool onEnd = span->coinPtTStart()->fT == 0;
bool oOnEnd = zero_or_one(span->oppPtTStart()->fT);
if (onEnd) {
if (!oOnEnd) {
this->debugAddEndMovedSpans(log, span->oppPtTStart());
}
} else if (oOnEnd) {
this->debugAddEndMovedSpans(log, span->coinPtTStart());
}
}
if (span->coinPtTEnd()->fPt != span->oppPtTEnd()->fPt) {
bool onEnd = span->coinPtTEnd()->fT == 1;
bool oOnEnd = zero_or_one(span->oppPtTEnd()->fT);
if (onEnd) {
if (!oOnEnd) {
this->debugAddEndMovedSpans(log, span->oppPtTEnd());
}
} else if (oOnEnd) {
this->debugAddEndMovedSpans(log, span->coinPtTEnd());
}
}
} while ((span = span->next()));
return;
}
void SkOpCoincidence::debugAddExpanded(SkPathOpsDebug::GlitchLog* log) const {
const SkCoincidentSpans* coin = this->fHead;
if (!coin) {
return;
}
do {
const SkOpPtT* startPtT = coin->coinPtTStart();
const SkOpPtT* oStartPtT = coin->oppPtTStart();
double priorT = startPtT->fT;
double oPriorT = oStartPtT->fT;
FAIL_IF_COIN(!startPtT->contains(oStartPtT), coin);
SkOPASSERT(coin->coinPtTEnd()->contains(coin->oppPtTEnd()));
const SkOpSpanBase* start = startPtT->span();
const SkOpSpanBase* oStart = oStartPtT->span();
const SkOpSpanBase* end = coin->coinPtTEnd()->span();
const SkOpSpanBase* oEnd = coin->oppPtTEnd()->span();
FAIL_IF_COIN(oEnd->deleted(), coin);
FAIL_IF_COIN(!start->upCastable(), coin);
const SkOpSpanBase* test = start->upCast()->next();
FAIL_IF_COIN(!coin->flipped() && !oStart->upCastable(), coin);
const SkOpSpanBase* oTest = coin->flipped() ? oStart->prev() : oStart->upCast()->next();
FAIL_IF_COIN(!oTest, coin);
const SkOpSegment* seg = start->segment();
const SkOpSegment* oSeg = oStart->segment();
while (test != end || oTest != oEnd) {
const SkOpPtT* containedOpp = test->ptT()->contains(oSeg);
const SkOpPtT* containedThis = oTest->ptT()->contains(seg);
if (!containedOpp || !containedThis) {
double nextT, oNextT;
if (containedOpp) {
nextT = test->t();
oNextT = containedOpp->fT;
} else if (containedThis) {
nextT = containedThis->fT;
oNextT = oTest->t();
} else {
const SkOpSpanBase* walk = test;
const SkOpPtT* walkOpp;
do {
FAIL_IF_COIN(!walk->upCastable(), coin);
walk = walk->upCast()->next();
} while (!(walkOpp = walk->ptT()->contains(oSeg))
&& walk != coin->coinPtTEnd()->span());
FAIL_IF_COIN(!walkOpp, coin);
nextT = walk->t();
oNextT = walkOpp->fT;
}
double startRange = nextT - priorT;
FAIL_IF_COIN(!startRange, coin);
double startPart = (test->t() - priorT) / startRange;
double oStartRange = oNextT - oPriorT;
FAIL_IF_COIN(!oStartRange, coin);
double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange;
FAIL_IF_COIN(startPart == oStartPart, coin);
bool addToOpp = !containedOpp && !containedThis ? startPart < oStartPart
: !!containedThis;
bool startOver = false;
addToOpp ? log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch,
oPriorT + oStartRange * startPart, test)
: log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch,
priorT + startRange * oStartPart, oTest);
if (startOver) {
test = start;
oTest = oStart;
}
end = coin->coinPtTEnd()->span();
oEnd = coin->oppPtTEnd()->span();
}
if (test != end) {
FAIL_IF_COIN(!test->upCastable(), coin);
priorT = test->t();
test = test->upCast()->next();
}
if (oTest != oEnd) {
oPriorT = oTest->t();
oTest = coin->flipped() ? oTest->prev() : oTest->upCast()->next();
FAIL_IF_COIN(!oTest, coin);
}
}
} while ((coin = coin->next()));
return;
}
void SkOpCoincidence::debugAddIfMissing(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* over1s, const SkOpPtT* over2s,
double tStart, double tEnd, const SkOpSegment* coinSeg, const SkOpSegment* oppSeg, bool* added,
const SkOpPtT* over1e, const SkOpPtT* over2e) const {
SkASSERT(tStart < tEnd);
SkASSERT(over1s->fT < over1e->fT);
SkASSERT(between(over1s->fT, tStart, over1e->fT));
SkASSERT(between(over1s->fT, tEnd, over1e->fT));
SkASSERT(over2s->fT < over2e->fT);
SkASSERT(between(over2s->fT, tStart, over2e->fT));
SkASSERT(between(over2s->fT, tEnd, over2e->fT));
SkASSERT(over1s->segment() == over1e->segment());
SkASSERT(over2s->segment() == over2e->segment());
SkASSERT(over1s->segment() == over2s->segment());
SkASSERT(over1s->segment() != coinSeg);
SkASSERT(over1s->segment() != oppSeg);
SkASSERT(coinSeg != oppSeg);
double coinTs, coinTe, oppTs, oppTe;
coinTs = TRange(over1s, tStart, coinSeg SkDEBUGPARAMS(over1e));
coinTe = TRange(over1s, tEnd, coinSeg SkDEBUGPARAMS(over1e));
SkOpSpanBase::Collapsed result = coinSeg->collapsed(coinTs, coinTe);
if (SkOpSpanBase::Collapsed::kNo != result) {
return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, coinSeg);
}
oppTs = TRange(over2s, tStart, oppSeg SkDEBUGPARAMS(over2e));
oppTe = TRange(over2s, tEnd, oppSeg SkDEBUGPARAMS(over2e));
result = oppSeg->collapsed(oppTs, oppTe);
if (SkOpSpanBase::Collapsed::kNo != result) {
return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, oppSeg);
}
if (coinTs > coinTe) {
using std::swap;
swap(coinTs, coinTe);
swap(oppTs, oppTe);
}
this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, added);
return;
}
void SkOpCoincidence::debugAddOrOverlap(SkPathOpsDebug::GlitchLog* log,
const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
double coinTs, double coinTe, double oppTs, double oppTe, bool* added) const {
SkTDArray<SkCoincidentSpans*> overlaps;
SkOPASSERT(!fTop);
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.size() ? overlaps[0] : nullptr;
for (int index = 1; index < overlaps.size(); ++index) {
const SkCoincidentSpans* test = overlaps[index];
if (overlap->coinPtTStart()->fT > test->coinPtTStart()->fT) {
log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTStart());
}
if (overlap->coinPtTEnd()->fT < test->coinPtTEnd()->fT) {
log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTEnd());
}
if (overlap->flipped()
? overlap->oppPtTStart()->fT < test->oppPtTStart()->fT
: overlap->oppPtTStart()->fT > test->oppPtTStart()->fT) {
log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTStart());
}
if (overlap->flipped()
? overlap->oppPtTEnd()->fT > test->oppPtTEnd()->fT
: overlap->oppPtTEnd()->fT < test->oppPtTEnd()->fT) {
log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTEnd());
}
if (!fHead) { this->debugRelease(log, fHead, test);
this->debugRelease(log, fTop, test);
}
}
const SkOpPtT* cs = coinSeg->existing(coinTs, oppSeg);
const SkOpPtT* ce = coinSeg->existing(coinTe, oppSeg);
RETURN_FALSE_IF(overlap && cs && ce && overlap->contains(cs, ce), coinSeg);
RETURN_FALSE_IF(cs != ce || !cs, coinSeg);
const SkOpPtT* os = oppSeg->existing(oppTs, coinSeg);
const SkOpPtT* oe = oppSeg->existing(oppTe, coinSeg);
RETURN_FALSE_IF(overlap && os && oe && overlap->contains(os, oe), oppSeg);
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;
RETURN_FALSE_IF(csExisting && csExisting == ceExisting, coinSeg);
RETURN_FALSE_IF(csExisting && (csExisting == ce ||
csExisting->contains(ceExisting ? ceExisting : ce)), coinSeg);
RETURN_FALSE_IF(ceExisting && (ceExisting == cs ||
ceExisting->contains(csExisting ? csExisting : cs)), coinSeg);
const SkOpPtT* osExisting = !os ? oppSeg->existing(oppTs, nullptr) : nullptr;
const SkOpPtT* oeExisting = !oe ? oppSeg->existing(oppTe, nullptr) : nullptr;
RETURN_FALSE_IF(osExisting && osExisting == oeExisting, oppSeg);
RETURN_FALSE_IF(osExisting && (osExisting == oe ||
osExisting->contains(oeExisting ? oeExisting : oe)), oppSeg);
RETURN_FALSE_IF(oeExisting && (oeExisting == os ||
oeExisting->contains(osExisting ? osExisting : os)), oppSeg);
bool csDeleted = false, osDeleted = false, ceDeleted = false, oeDeleted = false;
this->debugValidate();
if (!cs || !os) {
if (!cs)
cs = coinSeg->debugAddT(coinTs, log);
if (!os)
os = oppSeg->debugAddT(oppTs, log);
if (cs && os) cs->span()->debugAddOpp(log, os->span());
RETURN_FALSE_IF((ce && ce->deleted()) || (oe && oe->deleted()), coinSeg);
}
if (!ce || !oe) {
if (!ce)
ce = coinSeg->debugAddT(coinTe, log);
if (!oe)
oe = oppSeg->debugAddT(oppTe, log);
if (ce && oe) ce->span()->debugAddOpp(log, oe->span());
}
this->debugValidate();
RETURN_FALSE_IF(csDeleted, coinSeg);
RETURN_FALSE_IF(osDeleted, oppSeg);
RETURN_FALSE_IF(ceDeleted, coinSeg);
RETURN_FALSE_IF(oeDeleted, oppSeg);
RETURN_FALSE_IF(!cs || !ce || cs == ce || cs->contains(ce) || !os || !oe || os == oe || os->contains(oe), coinSeg);
bool result = true;
if (overlap) {
if (overlap->coinPtTStart()->segment() == coinSeg) {
log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
} else {
if (oppTs > oppTe) {
using std::swap;
swap(coinTs, coinTe);
swap(oppTs, oppTe);
}
log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, oppSeg, oppTs, oppTe, coinSeg, coinTs, coinTe);
}
#if 0 && DEBUG_COINCIDENCE_VERBOSE
if (result) {
overlap->debugShow();
}
#endif
} else {
log->record(SkPathOpsDebug::kAddMissingCoin_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
#if 0 && DEBUG_COINCIDENCE_VERBOSE
fHead->debugShow();
#endif
}
this->debugValidate();
return (void) result;
}
void SkOpCoincidence::debugAddMissing(SkPathOpsDebug::GlitchLog* log, bool* added) const {
const SkCoincidentSpans* outer = fHead;
*added = false;
if (!outer) {
return;
}
do {
const SkOpPtT* ocs = outer->coinPtTStart();
SkASSERT(!ocs->deleted());
const SkOpSegment* outerCoin = ocs->segment();
SkASSERT(!outerCoin->done());
const SkOpPtT* oos = outer->oppPtTStart();
if (oos->deleted()) {
return;
}
const SkOpSegment* outerOpp = oos->segment();
SkASSERT(!outerOpp->done());
const SkCoincidentSpans* inner = outer;
while ((inner = inner->next())) {
this->debugValidate();
double overS, overE;
const SkOpPtT* ics = inner->coinPtTStart();
SkASSERT(!ics->deleted());
const SkOpSegment* innerCoin = ics->segment();
SkASSERT(!innerCoin->done());
const SkOpPtT* ios = inner->oppPtTStart();
SkASSERT(!ios->deleted());
const SkOpSegment* innerOpp = ios->segment();
SkASSERT(!innerOpp->done());
if (outerCoin == innerCoin) {
const SkOpPtT* oce = outer->coinPtTEnd();
if (oce->deleted()) {
return;
}
const SkOpPtT* ice = inner->coinPtTEnd();
SkASSERT(!ice->deleted());
if (outerOpp != innerOpp && this->overlap(ocs, oce, ics, ice, &overS, &overE)) {
this->debugAddIfMissing(log, ocs->starter(oce), ics->starter(ice),
overS, overE, outerOpp, innerOpp, added,
ocs->debugEnder(oce),
ics->debugEnder(ice));
}
} else if (outerCoin == innerOpp) {
const SkOpPtT* oce = outer->coinPtTEnd();
SkASSERT(!oce->deleted());
const SkOpPtT* ioe = inner->oppPtTEnd();
SkASSERT(!ioe->deleted());
if (outerOpp != innerCoin && this->overlap(ocs, oce, ios, ioe, &overS, &overE)) {
this->debugAddIfMissing(log, ocs->starter(oce), ios->starter(ioe),
overS, overE, outerOpp, innerCoin, added,
ocs->debugEnder(oce),
ios->debugEnder(ioe));
}
} else if (outerOpp == innerCoin) {
const SkOpPtT* ooe = outer->oppPtTEnd();
SkASSERT(!ooe->deleted());
const SkOpPtT* ice = inner->coinPtTEnd();
SkASSERT(!ice->deleted());
SkASSERT(outerCoin != innerOpp);
if (this->overlap(oos, ooe, ics, ice, &overS, &overE)) {
this->debugAddIfMissing(log, oos->starter(ooe), ics->starter(ice),
overS, overE, outerCoin, innerOpp, added,
oos->debugEnder(ooe),
ics->debugEnder(ice));
}
} else if (outerOpp == innerOpp) {
const SkOpPtT* ooe = outer->oppPtTEnd();
SkASSERT(!ooe->deleted());
const SkOpPtT* ioe = inner->oppPtTEnd();
if (ioe->deleted()) {
return;
}
SkASSERT(outerCoin != innerCoin);
if (this->overlap(oos, ooe, ios, ioe, &overS, &overE)) {
this->debugAddIfMissing(log, oos->starter(ooe), ios->starter(ioe),
overS, overE, outerCoin, innerCoin, added,
oos->debugEnder(ooe),
ios->debugEnder(ioe));
}
}
this->debugValidate();
}
} while ((outer = outer->next()));
return;
}
void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkCoincidentSpans* remove) const {
const SkCoincidentSpans* head = coin;
const SkCoincidentSpans* prev = nullptr;
const SkCoincidentSpans* next;
do {
next = coin->next();
if (coin == remove) {
if (prev) {
} else if (head == fHead) {
} else {
}
log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
}
prev = coin;
} while ((coin = next));
return;
}
void SkOpCoincidence::debugRelease(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(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
}
} while ((coin = coin->next()));
}
bool SkOpCoincidence::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
const SkCoincidentSpans* coin = fHead;
if (!coin) {
return false;
}
bool expanded = false;
do {
if (coin->debugExpand(log)) {
const SkCoincidentSpans* test = fHead;
do {
if (coin == test) {
continue;
}
if (coin->coinPtTStart() == test->coinPtTStart()
&& coin->oppPtTStart() == test->oppPtTStart()) {
if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, fHead, test->coinPtTStart());
break;
}
} while ((test = test->next()));
expanded = true;
}
} while ((coin = coin->next()));
return expanded;
}
void SkOpCoincidence::debugMark(SkPathOpsDebug::GlitchLog* log) const {
const SkCoincidentSpans* coin = fHead;
if (!coin) {
return;
}
do {
FAIL_IF_COIN(!coin->coinPtTStartWritable()->span()->upCastable(), coin);
const SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast();
const SkOpSpanBase* end = coin->coinPtTEndWritable()->span();
const SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span();
const SkOpSpanBase* oEnd = coin->oppPtTEndWritable()->span();
bool flipped = coin->flipped();
if (flipped) {
using std::swap;
swap(oStart, oEnd);
}
start->debugInsertCoincidence(log, oStart->upCast());
end->debugInsertCoinEnd(log, oEnd);
const SkOpSegment* segment = start->segment();
const SkOpSegment* oSegment = oStart->segment();
const SkOpSpanBase* next = start;
const SkOpSpanBase* oNext = oStart;
bool ordered;
FAIL_IF_COIN(!coin->ordered(&ordered), coin);
while ((next = next->upCast()->next()) != end) {
FAIL_IF_COIN(!next->upCastable(), coin);
next->upCast()->debugInsertCoincidence(log, oSegment, flipped, ordered);
}
while ((oNext = oNext->upCast()->next()) != oEnd) {
FAIL_IF_COIN(!oNext->upCastable(), coin);
oNext->upCast()->debugInsertCoincidence(log, segment, flipped, ordered);
}
} while ((coin = coin->next()));
return;
}
#endif
#if DEBUG_COIN
void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkOpPtT* test) const {
const SkCoincidentSpans* head = coin;
while (coin) {
if (coin->collapsed(test)) {
if (zero_or_one(coin->coinPtTStart()->fT) && zero_or_one(coin->coinPtTEnd()->fT)) {
log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
}
if (zero_or_one(coin->oppPtTStart()->fT) && zero_or_one(coin->oppPtTEnd()->fT)) {
log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
}
this->debugRelease(log, head, coin);
}
coin = coin->next();
}
}
void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* test) const {
this->debugMarkCollapsed(log, fHead, test);
this->debugMarkCollapsed(log, fTop, test);
}
#endif
void SkCoincidentSpans::debugShow() const { … }
void SkOpCoincidence::debugShowCoincidence() const { … }
#if DEBUG_COIN
static void DebugCheckBetween(const SkOpSpanBase* next, const SkOpSpanBase* end,
double oStart, double oEnd, const SkOpSegment* oSegment,
SkPathOpsDebug::GlitchLog* log) {
SkASSERT(next != end);
SkASSERT(!next->contains(end) || log);
if (next->t() > end->t()) {
using std::swap;
swap(next, end);
}
do {
const SkOpPtT* ptT = next->ptT();
int index = 0;
bool somethingBetween = false;
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,
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) {
using std::swap;
swap(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) {
using std::swap;
swap(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) {
using std::swap;
swap(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,
SkPathOpsDebug::GlitchLog* log) {
const SkCoincidentSpans* test = head;
while (test) {
const SkCoincidentSpans* next = test->next();
DebugCheckOverlap(test, next, log);
DebugCheckOverlap(test, opt, log);
test = next;
}
}
static void DebugValidate(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
SkPathOpsDebug::GlitchLog* log) {
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());
coin = coin->next();
}
DebugCheckOverlapTop(head, opt, log);
}
#endif
void SkOpCoincidence::debugValidate() const { … }
#if DEBUG_COIN
static void DebugCheckBetween(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
SkPathOpsDebug::GlitchLog* log) {
const SkCoincidentSpans* coin = head;
while (coin) {
DebugCheckBetween(coin->coinPtTStart()->span(), coin->coinPtTEnd()->span(),
coin->oppPtTStart()->fT, coin->oppPtTEnd()->fT, coin->oppPtTStart()->segment(),
log);
DebugCheckBetween(coin->oppPtTStart()->span(), coin->oppPtTEnd()->span(),
coin->coinPtTStart()->fT, coin->coinPtTEnd()->fT, coin->coinPtTStart()->segment(),
log);
coin = coin->next();
}
DebugCheckOverlapTop(head, opt, log);
}
#endif
void SkOpCoincidence::debugCheckBetween() const { … }
#if DEBUG_COIN
void SkOpContour::debugCheckHealth(SkPathOpsDebug::GlitchLog* log) const {
const SkOpSegment* segment = &fHead;
do {
segment->debugCheckHealth(log);
} while ((segment = segment->next()));
}
void SkOpCoincidence::debugCheckValid(SkPathOpsDebug::GlitchLog* log) const {
#if DEBUG_VALIDATE
DebugValidate(fHead, fTop, log);
DebugValidate(fTop, nullptr, log);
#endif
}
void SkOpCoincidence::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
const SkCoincidentSpans* coin = fHead;
if (!coin) {
return;
}
do {
coin->debugCorrectEnds(log);
} while ((coin = coin->next()));
}
void SkOpContour::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const {
const SkOpSegment* segment = &fHead;
do {
segment->debugMissingCoincidence(log);
segment = segment->next();
} while (segment);
return;
}
void SkOpContour::debugMoveMultiples(SkPathOpsDebug::GlitchLog* log) const {
SkASSERT(fCount > 0);
const SkOpSegment* segment = &fHead;
do {
segment->debugMoveMultiples(log);
} while ((segment = segment->next()));
return;
}
void SkOpContour::debugMoveNearby(SkPathOpsDebug::GlitchLog* log) const {
SkASSERT(fCount > 0);
const SkOpSegment* segment = &fHead;
do {
segment->debugMoveNearby(log);
} while ((segment = segment->next()));
}
#endif
#if DEBUG_COINCIDENCE_ORDER
void SkOpSegment::debugResetCoinT() const {
fDebugBaseIndex = -1;
fDebugBaseMin = 1;
fDebugBaseMax = -1;
fDebugLastIndex = -1;
fDebugLastMin = 1;
fDebugLastMax = -1;
}
#endif
void SkOpSegment::debugValidate() const { … }
#if DEBUG_COIN
void SkOpSpanBase::debugAddOpp(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
const SkOpPtT* oppPrev = this->ptT()->oppPrev(opp->ptT());
if (!oppPrev) {
return;
}
this->debugMergeMatches(log, opp);
this->ptT()->debugAddOpp(opp->ptT(), oppPrev);
this->debugCheckForCollapsedCoincidence(log);
}
void SkOpSpanBase::debugCheckForCollapsedCoincidence(SkPathOpsDebug::GlitchLog* log) const {
const SkOpCoincidence* coins = this->globalState()->coincidence();
if (coins->isEmpty()) {
return;
}
const SkOpPtT* head = this->ptT();
const SkOpPtT* test = head;
do {
if (!test->coincident()) {
continue;
}
coins->debugMarkCollapsed(log, test);
} while ((test = test->next()) != head);
}
#endif
bool SkOpSpanBase::debugCoinEndLoopCheck() const { … }
#if DEBUG_COIN
void SkOpSpanBase::debugInsertCoinEnd(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* coin) const {
if (containsCoinEnd(coin)) {
return;
}
debugValidate();
log->record(SkPathOpsDebug::kMarkCoinEnd_Glitch, this, coin);
debugValidate();
}
void SkOpSpanBase::debugMergeMatches(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
const SkOpPtT* test = &fPtT;
const SkOpPtT* testNext;
const SkOpPtT* stop = test;
do {
testNext = test->next();
if (test->deleted()) {
continue;
}
const SkOpSpanBase* testBase = test->span();
SkASSERT(testBase->ptT() == test);
const SkOpSegment* segment = test->segment();
if (segment->done()) {
continue;
}
const SkOpPtT* inner = opp->ptT();
const SkOpPtT* innerStop = inner;
do {
if (inner->segment() != segment) {
continue;
}
if (inner->deleted()) {
continue;
}
const SkOpSpanBase* innerBase = inner->span();
SkASSERT(innerBase->ptT() == inner);
if (!zero_or_one(inner->fT)) {
log->record(SkPathOpsDebug::kMergeMatches_Glitch, innerBase, test);
} else {
SkASSERT(inner->fT != test->fT);
if (!zero_or_one(test->fT)) {
log->record(SkPathOpsDebug::kMergeMatches_Glitch, testBase, inner);
} else {
log->record(SkPathOpsDebug::kMergeMatches_Glitch, segment);
}
}
#ifdef SK_DEBUG
const SkOpPtT* debugInner = inner;
while ((debugInner = debugInner->next()) != innerStop) {
if (debugInner->segment() != segment) {
continue;
}
if (debugInner->deleted()) {
continue;
}
SkOPASSERT(0);
}
#endif
break;
break;
} while ((inner = inner->next()) != innerStop);
} while ((test = testNext) != stop);
this->debugCheckForCollapsedCoincidence(log);
}
#endif
void SkOpSpanBase::debugResetCoinT() const { … }
void SkOpSpanBase::debugSetCoinT(int index) const { … }
const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const { … }
void SkOpSpanBase::debugValidate() const { … }
bool SkOpSpan::debugCoinLoopCheck() const { … }
#if DEBUG_COIN
void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* coin) const {
if (containsCoincidence(coin)) {
return;
}
debugValidate();
log->record(SkPathOpsDebug::kMarkCoinStart_Glitch, this, coin);
debugValidate();
}
void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* segment, bool flipped, bool ordered) const {
if (this->containsCoincidence(segment)) {
return;
}
const SkOpPtT* next = &fPtT;
while ((next = next->next()) != &fPtT) {
if (next->segment() == segment) {
const SkOpSpan* span;
const SkOpSpanBase* base = next->span();
if (!ordered) {
const SkOpSpanBase* spanEnd = fNext->contains(segment)->span();
const SkOpPtT* start = base->ptT()->starter(spanEnd->ptT());
FAIL_IF_COIN(!start->span()->upCastable(), this);
span = const_cast<SkOpSpan*>(start->span()->upCast());
}
else if (flipped) {
span = base->prev();
FAIL_IF_COIN(!span, this);
}
else {
FAIL_IF_COIN(!base->upCastable(), this);
span = base->upCast();
}
log->record(SkPathOpsDebug::kMarkCoinInsert_Glitch, span);
return;
}
}
log->record(SkPathOpsDebug::kMarkCoinMissing_Glitch, segment, this);
return;
}
#endif
int SkIntersections::debugCoincidentUsed() const { … }
void SkOpPtT::debugAddOpp(const SkOpPtT* opp, const SkOpPtT* oppPrev) const { … }
bool SkOpPtT::debugContains(const SkOpPtT* check) const { … }
const SkOpPtT* SkOpPtT::debugContains(const SkOpSegment* check) const { … }
const SkOpPtT* SkOpPtT::debugEnder(const SkOpPtT* end) const { … }
int SkOpPtT::debugLoopLimit(bool report) const { … }
const SkOpPtT* SkOpPtT::debugOppPrev(const SkOpPtT* opp) const { … }
void SkOpPtT::debugResetCoinT() const { … }
void SkOpPtT::debugSetCoinT(int index) const { … }
void SkOpPtT::debugValidate() const { … }
static void output_scalar(SkScalar num) { … }
static void output_points(const SkPoint* pts, int count) { … }
static void showPathContours(const SkPath& path, const char* pathName) { … }
static const char* gFillTypeStr[] = …;
void SkPathOpsDebug::ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration) { … }
#if DEBUG_DUMP_VERIFY
#include "include/core/SkData.h"
#include "include/core/SkStream.h"
static void dump_path(FILE* file, const SkPath& path, bool dumpAsHex) {
SkDynamicMemoryWStream wStream;
path.dump(&wStream, dumpAsHex);
sk_sp<SkData> data(wStream.detachAsData());
fprintf(file, "%.*s\n", (int) data->size(), (char*) data->data());
}
static int dumpID = 0;
void DumpOp(const SkPath& one, const SkPath& two, SkPathOp op,
const char* testName) {
FILE* file = sk_fopen("op_dump.txt", kWrite_SkFILE_Flag);
DumpOp(file, one, two, op, testName);
}
void DumpOp(FILE* file, const SkPath& one, const SkPath& two, SkPathOp op,
const char* testName) {
const char* name = testName ? testName : "op";
fprintf(file,
"\nstatic void %s_%d(skiatest::Reporter* reporter, const char* filename) {\n",
name, ++dumpID);
fprintf(file, " SkPath path;\n");
fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", one.getFillType());
dump_path(file, one, true);
fprintf(file, " SkPath path1(path);\n");
fprintf(file, " path.reset();\n");
fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", two.getFillType());
dump_path(file, two, true);
fprintf(file, " SkPath path2(path);\n");
fprintf(file, " testPathOp(reporter, path1, path2, (SkPathOp) %d, filename);\n", op);
fprintf(file, "}\n\n");
fclose(file);
}
void DumpSimplify(const SkPath& path, const char* testName) {
FILE* file = sk_fopen("simplify_dump.txt", kWrite_SkFILE_Flag);
DumpSimplify(file, path, testName);
}
void DumpSimplify(FILE* file, const SkPath& path, const char* testName) {
const char* name = testName ? testName : "simplify";
fprintf(file,
"\nstatic void %s_%d(skiatest::Reporter* reporter, const char* filename) {\n",
name, ++dumpID);
fprintf(file, " SkPath path;\n");
fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", path.getFillType());
dump_path(file, path, true);
fprintf(file, " testSimplify(reporter, path, filename);\n");
fprintf(file, "}\n\n");
fclose(file);
}
#include "include/core/SkBitmap.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkPaint.h"
#include "include/core/SkRegion.h"
const int bitWidth = 64;
const int bitHeight = 64;
static void debug_scale_matrix(const SkPath& one, const SkPath* two, SkMatrix& scale) {
SkRect larger = one.getBounds();
if (two) {
larger.join(two->getBounds());
}
SkScalar largerWidth = larger.width();
if (largerWidth < 4) {
largerWidth = 4;
}
SkScalar largerHeight = larger.height();
if (largerHeight < 4) {
largerHeight = 4;
}
SkScalar hScale = (bitWidth - 2) / largerWidth;
SkScalar vScale = (bitHeight - 2) / largerHeight;
scale.reset();
scale.preScale(hScale, vScale);
larger.fLeft *= hScale;
larger.fRight *= hScale;
larger.fTop *= vScale;
larger.fBottom *= vScale;
SkScalar dx = -16000 > larger.fLeft ? -16000 - larger.fLeft
: 16000 < larger.fRight ? 16000 - larger.fRight : 0;
SkScalar dy = -16000 > larger.fTop ? -16000 - larger.fTop
: 16000 < larger.fBottom ? 16000 - larger.fBottom : 0;
scale.preTranslate(dx, dy);
}
static int debug_paths_draw_the_same(const SkPath& one, const SkPath& two, SkBitmap& bits) {
if (bits.width() == 0) {
bits.allocN32Pixels(bitWidth * 2, bitHeight);
}
SkCanvas canvas(bits);
canvas.drawColor(SK_ColorWHITE);
SkPaint paint;
canvas.save();
const SkRect& bounds1 = one.getBounds();
canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
canvas.drawPath(one, paint);
canvas.restore();
canvas.save();
canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
canvas.drawPath(two, paint);
canvas.restore();
int errors = 0;
for (int y = 0; y < bitHeight - 1; ++y) {
uint32_t* addr1 = bits.getAddr32(0, y);
uint32_t* addr2 = bits.getAddr32(0, y + 1);
uint32_t* addr3 = bits.getAddr32(bitWidth, y);
uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1);
for (int x = 0; x < bitWidth - 1; ++x) {
bool err = addr1[x] != addr3[x];
if (err) {
errors += addr1[x + 1] != addr3[x + 1]
&& addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1];
}
}
}
return errors;
}
void ReportOpFail(const SkPath& one, const SkPath& two, SkPathOp op) {
SkDebugf("// Op did not expect failure\n");
DumpOp(stderr, one, two, op, "opTest");
fflush(stderr);
}
void VerifyOp(const SkPath& one, const SkPath& two, SkPathOp op,
const SkPath& result) {
SkPath pathOut, scaledPathOut;
SkRegion rgnA, rgnB, openClip, rgnOut;
openClip.setRect({-16000, -16000, 16000, 16000});
rgnA.setPath(one, openClip);
rgnB.setPath(two, openClip);
rgnOut.op(rgnA, rgnB, (SkRegion::Op) op);
rgnOut.getBoundaryPath(&pathOut);
SkMatrix scale;
debug_scale_matrix(one, &two, scale);
SkRegion scaledRgnA, scaledRgnB, scaledRgnOut;
SkPath scaledA, scaledB;
scaledA.addPath(one, scale);
scaledA.setFillType(one.getFillType());
scaledB.addPath(two, scale);
scaledB.setFillType(two.getFillType());
scaledRgnA.setPath(scaledA, openClip);
scaledRgnB.setPath(scaledB, openClip);
scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) op);
scaledRgnOut.getBoundaryPath(&scaledPathOut);
SkBitmap bitmap;
SkPath scaledOut;
scaledOut.addPath(result, scale);
scaledOut.setFillType(result.getFillType());
int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
const int MAX_ERRORS = 9;
if (errors > MAX_ERRORS) {
fprintf(stderr, "// Op did not expect errors=%d\n", errors);
DumpOp(stderr, one, two, op, "opTest");
fflush(stderr);
}
}
void ReportSimplifyFail(const SkPath& path) {
SkDebugf("// Simplify did not expect failure\n");
DumpSimplify(stderr, path, "simplifyTest");
fflush(stderr);
}
void VerifySimplify(const SkPath& path, const SkPath& result) {
SkPath pathOut, scaledPathOut;
SkRegion rgnA, openClip, rgnOut;
openClip.setRect({-16000, -16000, 16000, 16000});
rgnA.setPath(path, openClip);
rgnOut.getBoundaryPath(&pathOut);
SkMatrix scale;
debug_scale_matrix(path, nullptr, scale);
SkRegion scaledRgnA;
SkPath scaledA;
scaledA.addPath(path, scale);
scaledA.setFillType(path.getFillType());
scaledRgnA.setPath(scaledA, openClip);
scaledRgnA.getBoundaryPath(&scaledPathOut);
SkBitmap bitmap;
SkPath scaledOut;
scaledOut.addPath(result, scale);
scaledOut.setFillType(result.getFillType());
int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
const int MAX_ERRORS = 9;
if (errors > MAX_ERRORS) {
fprintf(stderr, "// Simplify did not expect errors=%d\n", errors);
DumpSimplify(stderr, path, "simplifyTest");
fflush(stderr);
}
}
#endif
void Dump(const SkPath& path) { … }
void DumpHex(const SkPath& path) { … }