#include "SkOpSegment.h"
#include "SkPathOpsTSect.h"
-#if DEBUG_COINCIDENCE
-#define FAIL_IF(cond) SkASSERT(!(cond))
-#else
-#define FAIL_IF(cond) do { if (cond) return false; } while (false)
-#endif
-
// returns true if coincident span's start and end are the same
bool SkCoincidentSpans::collapsed(const SkOpPtT* test) const {
return (fCoinPtTStart == test && fCoinPtTEnd->contains(test))
SkTSwap(coinTs, coinTe);
SkTSwap(oppTs, oppTe);
}
- if (!this->addOrOverlap(coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe)) {
+ if (!this->addOrOverlap(coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe
+ SkDEBUGPARAMS(true) /* do assert if addOrOverlap fails */ )) {
return false;
}
}
// description below
bool SkOpCoincidence::addEndMovedSpans(const SkOpPtT* ptT) {
- if (!ptT->span()->upCastable()) {
- return false;
- }
+ FAIL_IF(!ptT->span()->upCastable());
const SkOpSpan* base = ptT->span()->upCast();
const SkOpSpan* prev = base->prev();
- if (!prev) {
- return false;
- }
+ FAIL_IF(!prev);
if (!prev->isCanceled()) {
if (!this->addEndMovedSpans(base, base->prev())) {
return false;
fHead = nullptr;
do {
if (span->coinPtTStart()->fPt != span->oppPtTStart()->fPt) {
- if (1 == span->coinPtTStart()->fT) {
- return false;
- }
+ FAIL_IF(1 == span->coinPtTStart()->fT);
bool onEnd = span->coinPtTStart()->fT == 0;
bool oOnEnd = zero_or_one(span->oppPtTStart()->fT);
if (onEnd) {
if (coinSeg == oppSeg) {
return false;
}
- return this->addOrOverlap(coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe);
+ return this->addOrOverlap(coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe
+ SkDEBUGPARAMS(false) /* don't assert if addOrOverlap fails */ );
}
/* Please keep this in sync with debugAddOrOverlap() */
+// If this is called by addEndMovedSpans(), a returned false propogates out to an abort.
+// If this is called by AddIfMissing(), a returned false indicates there was nothing to add
bool SkOpCoincidence::addOrOverlap(SkOpSegment* coinSeg, SkOpSegment* oppSeg,
- double coinTs, double coinTe, double oppTs, double oppTe) {
+ double coinTs, double coinTe, double oppTs, double oppTe
+ SkDEBUGPARAMS(bool callerAborts)) {
SkTDArray<SkCoincidentSpans*> overlaps;
- if (!fTop) {
- return false;
- }
+ RETURN_FALSE_IF(callerAborts, !fTop);
if (!this->checkOverlap(fTop, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &overlaps)) {
return false;
}
}
const SkOpPtT* cs = coinSeg->existing(coinTs, oppSeg);
const SkOpPtT* ce = coinSeg->existing(coinTe, oppSeg);
- if (overlap && cs && ce && overlap->contains(cs, ce)) {
- return false;
- }
- if (cs == ce && cs) {
- return false;
- }
+ RETURN_FALSE_IF(callerAborts, overlap && cs && ce && overlap->contains(cs, ce));
+ RETURN_FALSE_IF(callerAborts, 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 false;
- }
+ RETURN_FALSE_IF(callerAborts, overlap && os && oe && overlap->contains(os, oe));
SkASSERT(!cs || !cs->deleted());
SkASSERT(!os || !os->deleted());
SkASSERT(!ce || !ce->deleted());
SkASSERT(!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 false;
- }
- if (csExisting && (csExisting == ce || csExisting->contains(ceExisting ? ceExisting : ce))) {
- return false;
- }
- if (ceExisting && (ceExisting == cs || ceExisting->contains(csExisting ? csExisting : cs))) {
- return false;
- }
+ RETURN_FALSE_IF(callerAborts, csExisting && csExisting == ceExisting);
+ RETURN_FALSE_IF(callerAborts, csExisting && (csExisting == ce ||
+ csExisting->contains(ceExisting ? ceExisting : ce)));
+ RETURN_FALSE_IF(callerAborts, ceExisting && (ceExisting == cs ||
+ ceExisting->contains(csExisting ? csExisting : cs)));
const SkOpPtT* osExisting = !os ? oppSeg->existing(oppTs, nullptr) : nullptr;
const SkOpPtT* oeExisting = !oe ? oppSeg->existing(oppTe, nullptr) : nullptr;
- if (osExisting && osExisting == oeExisting) {
- return false;
- }
- if (osExisting && (osExisting == oe || osExisting->contains(oeExisting ? oeExisting : oe))) {
- return false;
- }
- if (oeExisting && (oeExisting == os || oeExisting->contains(osExisting ? osExisting : os))) {
- return false;
- }
+ RETURN_FALSE_IF(callerAborts, osExisting && osExisting == oeExisting);
+ RETURN_FALSE_IF(callerAborts, osExisting && (osExisting == oe ||
+ osExisting->contains(oeExisting ? oeExisting : oe)));
+ RETURN_FALSE_IF(callerAborts, oeExisting && (oeExisting == os ||
+ oeExisting->contains(osExisting ? osExisting : os)));
// extra line in debug code
this->debugValidate();
if (!cs || !os) {
: coinSeg->addT(coinTs, nullptr);
SkOpPtT* osWritable = os ? const_cast<SkOpPtT*>(os)
: oppSeg->addT(oppTs, nullptr);
- if (!csWritable || !osWritable) {
- return false;
- }
+ RETURN_FALSE_IF(callerAborts, !csWritable || !osWritable);
csWritable->span()->addOppAndMerge(osWritable->span());
cs = csWritable;
os = osWritable;
- if ((ce && ce->deleted()) || (oe && oe->deleted())) {
- return false;
- }
+ RETURN_FALSE_IF(callerAborts, (ce && ce->deleted()) || (oe && oe->deleted()));
}
if (!ce || !oe) {
SkOpPtT* ceWritable = ce ? const_cast<SkOpPtT*>(ce)
oe = oeWritable;
}
this->debugValidate();
- if (cs->deleted() || os->deleted() || ce->deleted() || oe->deleted()) {
- return false;
- }
- if (cs->contains(ce) || os->contains(oe)) {
- return false;
- }
+ RETURN_FALSE_IF(callerAborts, cs->deleted());
+ RETURN_FALSE_IF(callerAborts, os->deleted());
+ RETURN_FALSE_IF(callerAborts, ce->deleted());
+ RETURN_FALSE_IF(callerAborts, oe->deleted());
+ RETURN_FALSE_IF(callerAborts, cs->contains(ce) || os->contains(oe));
bool result = true;
if (overlap) {
if (overlap->coinPtTStart()->segment() == coinSeg) {
}
bool addOrOverlap(SkOpSegment* coinSeg, SkOpSegment* oppSeg,
- double coinTs, double coinTe, double oppTs, double oppTe);
+ double coinTs, double coinTe, double oppTs, double oppTe
+ SkDEBUGPARAMS(bool callerAborts));
bool addOverlap(const SkOpSegment* seg1, const SkOpSegment* seg1o,
const SkOpSegment* seg2, const SkOpSegment* seg2o,
const SkOpPtT* overS, const SkOpPtT* overE);
#include "SkOpSegment.h"
#include "SkPathWriter.h"
-#define FAIL_IF(cond) do { if (cond) return false; } while (false)
-
/*
After computing raw intersections, post process all segments to:
- find small collections of points that can be collapsed to a single point
bool SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
SkPathWriter* path) const {
- if (start->starter(end)->alreadyAdded()) {
- SkDEBUGF(("same curve added twice aborted pathops\n"));
- return false;
- }
+ FAIL_IF(start->starter(end)->alreadyAdded());
SkOpCurve edge;
const SkPoint* ePtr;
SkScalar eWeight;
const SkOpSpanBase* debugSpan(int id) const;
void debugValidate() const;
+#if DEBUG_COINCIDENCE_ORDER
+ void debugResetCoinT() const;
+ void debugSetCoinT(int, SkScalar ) const;
+#endif
+
#if DEBUG_COINCIDENCE
- static void SkOpSegment::DebugClearVisited(const SkOpSpanBase* span);
+ static void DebugClearVisited(const SkOpSpanBase* span);
bool debugVisited() const {
if (!fDebugVisited) {
#if DEBUG_COINCIDENCE
mutable bool fDebugVisited; // used by debug missing coincidence check
#endif
+#if DEBUG_COINCIDENCE_ORDER
+ mutable int fDebugBaseIndex;
+ mutable SkScalar fDebugBaseMin; // if > 0, the 1st t value in this seg vis-a-vis the ref seg
+ mutable SkScalar fDebugBaseMax;
+ mutable int fDebugLastIndex;
+ mutable SkScalar fDebugLastMin; // if > 0, the last t -- next t val - base has same sign
+ mutable SkScalar fDebugLastMax;
+#endif
SkDEBUGCODE(int fID);
};
int debugLoopLimit(bool report) const;
bool debugMatchID(int id) const;
const SkOpPtT* debugPtT(int id) const;
+ void debugResetCoinT() const;
const SkOpSegment* debugSegment(int id) const;
+ void debugSetCoinT(int ) const;
const SkOpSpanBase* debugSpan(int id) const;
void debugValidate() const;
const SkPathOpsBounds& bounds, bool* deleted) const;
#endif
const SkOpPtT* debugPtT(int id) const;
+ void debugResetCoinT() const;
const SkOpSegment* debugSegment(int id) const;
+ void debugSetCoinT(int ) const;
const SkOpSpanBase* debugSpan(int id) const;
const SkOpSpan* debugStarter(SkOpSpanBase const** endPtr) const;
SkOpGlobalState* globalState() const;
this->init(this->fPts, this->fWeight, this->contour(), this->verb());
}
+#if DEBUG_COINCIDENCE_ORDER
+void SkOpSegment::debugSetCoinT(int index, SkScalar t) const {
+ if (fDebugBaseMax < 0 || fDebugBaseIndex == index) {
+ fDebugBaseIndex = index;
+ fDebugBaseMin = SkTMin(t, fDebugBaseMin);
+ fDebugBaseMax = SkTMax(t, fDebugBaseMax);
+ return;
+ }
+ SkASSERT(fDebugBaseMin >= t || t >= fDebugBaseMax);
+ if (fDebugLastMax < 0 || fDebugLastIndex == index) {
+ fDebugLastIndex = index;
+ fDebugLastMin = SkTMin(t, fDebugLastMin);
+ fDebugLastMax = SkTMax(t, fDebugLastMax);
+ return;
+ }
+ SkASSERT(fDebugLastMin >= t || t >= fDebugLastMax);
+ SkASSERT((t - fDebugBaseMin > 0) == (fDebugLastMin - fDebugBaseMin > 0));
+}
+#endif
+
#if DEBUG_ACTIVE_SPANS
void SkOpSegment::debugShowActiveSpans() const {
debugValidate();
return expanded;
}
+#undef FAIL_IF
#define FAIL_IF(cond) do { if (cond) log->record(kAddExpandedFail_Glitch, id, coin); } while (false)
/* Commented-out lines keep this in sync with addExpanded */
}
#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_COINCIDENCE_ORDER
+ {
+ const SkOpSpanBase* span = &fHead;
+ do {
+ span->debugResetCoinT();
+ } while (!span->final() && (span = span->upCast()->next()));
+ span = &fHead;
+ int index = 0;
+ do {
+ span->debugSetCoinT(index++);
+ } while (!span->final() && (span = span->upCast()->next()));
+ }
+#endif
#if DEBUG_COINCIDENCE
if (this->globalState()->debugCheckHealth()) {
return;
}
#endif
+void SkOpSpanBase::debugResetCoinT() const {
+#if DEBUG_COINCIDENCE_ORDER
+ const SkOpPtT* ptT = &fPtT;
+ do {
+ ptT->debugResetCoinT();
+ ptT = ptT->next();
+ } while (ptT != &fPtT);
+#endif
+}
+
+void SkOpSpanBase::debugSetCoinT(int index) const {
+#if DEBUG_COINCIDENCE_ORDER
+ const SkOpPtT* ptT = &fPtT;
+ do {
+ if (!ptT->deleted()) {
+ ptT->debugSetCoinT(index);
+ }
+ ptT = ptT->next();
+ } while (ptT != &fPtT);
+#endif
+}
+
const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const {
const SkOpSpanBase* end = *endPtr;
SkASSERT(this->segment() == end->segment());
return 0;
}
+void SkOpPtT::debugResetCoinT() const {
+#if DEBUG_COINCIDENCE_ORDER
+ this->segment()->debugResetCoinT();
+#endif
+}
+
+void SkOpPtT::debugSetCoinT(int index) const {
+#if DEBUG_COINCIDENCE_ORDER
+ this->segment()->debugSetCoinT(index, fT);
+#endif
+}
+
void SkOpPtT::debugValidate() const {
#if DEBUG_COINCIDENCE
if (this->globalState()->debugCheckHealth()) {
#define DEBUG_ANGLE 0
#define DEBUG_ASSEMBLE 0
#define DEBUG_COINCIDENCE 0
+#define DEBUG_COINCIDENCE_ORDER 0
#define DEBUG_COINCIDENCE_VERBOSE 0
#define DEBUG_CUBIC_BINARY_SEARCH 0
#define DEBUG_CUBIC_SPLIT 0
#define DEBUG_WINDING 0
#define DEBUG_WINDING_AT_T 0
-
#else
#define DEBUG_ACTIVE_OP 1
#define DEBUG_ANGLE 1
#define DEBUG_ASSEMBLE 1
#define DEBUG_COINCIDENCE 01
+#define DEBUG_COINCIDENCE_ORDER 0
#define DEBUG_COINCIDENCE_VERBOSE 01
#define DEBUG_CUBIC_BINARY_SEARCH 0
#define DEBUG_CUBIC_SPLIT 1
#include "SkTLS.h"
#endif
+#define FAIL_IF(cond) do { bool fail = (cond); SkOPASSERT(!fail); if (fail) return false; } \
+ while (false)
+
+#define RETURN_FALSE_IF(abort, cond) \
+ do { bool fail = (cond); SkOPASSERT(!(abort) || !fail); if (fail) return false; } \
+ while (false)
+
class SkPathOpsDebug {
public:
static const char* kLVerbStr[];