SkASSERT(ts[0][pt] >= 0 && ts[0][pt] <= 1);
SkASSERT(ts[1][pt] >= 0 && ts[1][pt] <= 1);
wt.segment()->debugValidate();
- SkOpPtT* testTAt = wt.segment()->addT(ts[swap][pt], nullptr);
+ SkOpPtT* testTAt = wt.segment()->addT(ts[swap][pt]);
wn.segment()->debugValidate();
- SkOpPtT* nextTAt = wn.segment()->addT(ts[!swap][pt], nullptr);
- if (testTAt->addOpp(nextTAt)) {
- testTAt->span()->checkForCollapsedCoincidence();
+ SkOpPtT* nextTAt = wn.segment()->addT(ts[!swap][pt]);
+ SkOpPtT* oppPrev = testTAt->oppPrev(nextTAt);
+ if (oppPrev) {
+ testTAt->addOpp(nextTAt, oppPrev);
}
if (testTAt->fPt != nextTAt->fPt) {
testTAt->span()->unaligned();
continue;
}
SkOpSegment* writableSeg = const_cast<SkOpSegment*>(testSeg);
- SkOpPtT* oppStart = writableSeg->addT(t, nullptr);
+ SkOpPtT* oppStart = writableSeg->addT(t);
SkOpSpan* writableBase = const_cast<SkOpSpan*>(base);
oppStart->span()->addOppAndMerge(writableBase);
if (oppStart->deleted()) {
this->debugValidate();
if (!cs || !os) {
SkOpPtT* csWritable = cs ? const_cast<SkOpPtT*>(cs)
- : coinSeg->addT(coinTs, nullptr);
+ : coinSeg->addT(coinTs);
SkOpPtT* osWritable = os ? const_cast<SkOpPtT*>(os)
- : oppSeg->addT(oppTs, nullptr);
+ : oppSeg->addT(oppTs);
RETURN_FALSE_IF(callerAborts, !csWritable || !osWritable);
csWritable->span()->addOppAndMerge(osWritable->span());
cs = csWritable;
}
if (!ce || !oe) {
SkOpPtT* ceWritable = ce ? const_cast<SkOpPtT*>(ce)
- : coinSeg->addT(coinTe, nullptr);
+ : coinSeg->addT(coinTe);
SkOpPtT* oeWritable = oe ? const_cast<SkOpPtT*>(oe)
- : oppSeg->addT(oppTe, nullptr);
+ : oppSeg->addT(oppTe);
ceWritable->span()->addOppAndMerge(oeWritable->span());
ce = ceWritable;
oe = oeWritable;
if (this->contains(newT)) {
return true;
}
- SkOpPtT* newPtT = this->addT(newT, startOver);
+ this->globalState()->resetAllocatedOpSpan();
+ SkOpPtT* newPtT = this->addT(newT);
+ *startOver |= this->globalState()->allocatedOpSpan();
if (!newPtT) {
return false;
}
newPtT->fPt = this->ptAtT(newT);
// const cast away to change linked list; pt/t values stays unchanged
- SkOpSpanBase* writableTest = const_cast<SkOpSpanBase*>(test);
- if (writableTest->ptT()->addOpp(newPtT)) {
+ SkOpPtT* oppPrev = test->ptT()->oppPrev(newPtT);
+ if (oppPrev) {
+ SkOpSpanBase* writableTest = const_cast<SkOpSpanBase*>(test);
+ writableTest->ptT()->addOpp(newPtT, oppPrev);
writableTest->checkForCollapsedCoincidence();
}
return true;
}
// Please keep this in sync with debugAddT()
-SkOpPtT* SkOpSegment::addT(double t, bool* allocated) {
+SkOpPtT* SkOpSegment::addT(double t) {
debugValidate();
SkPoint pt = this->ptAtT(t);
- SkOpSpanBase* span = &fHead;
+ SkOpSpanBase* spanBase = &fHead;
do {
- SkOpPtT* result = span->ptT();
- SkOpPtT* loop;
- bool duplicatePt;
- if (t == result->fT) {
- goto bumpSpan;
- }
- if (this->match(result, this, t, pt)) {
- // 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;
- }
- duplicatePt |= ptMatch;
- loop = loop->next();
- }
- bumpSpan:
- span->bumpSpanAdds();
+ SkOpPtT* result = spanBase->ptT();
+ if (t == result->fT || this->match(result, this, t, pt)) {
+ spanBase->bumpSpanAdds();
return result;
}
if (t < result->fT) {
SkOpSpan* prev = result->span()->prev();
- if (!prev) {
- return nullptr;
- }
- SkOpSpan* span = insert(prev);
+ FAIL_WITH_NULL_IF(!prev);
+ // marks in global state that new op span has been allocated
+ SkOpSpan* span = this->insert(prev);
span->init(this, prev, t, pt);
this->debugValidate();
#if DEBUG_ADD_T
span->segment()->debugID(), span->debugID());
#endif
span->bumpSpanAdds();
- if (allocated) {
- *allocated = true;
- }
return span->ptT();
}
- if (span == &fTail) {
- return nullptr;
- }
- } while ((span = span->upCast()->next()));
+ FAIL_WITH_NULL_IF(spanBase == &fTail);
+ } while ((spanBase = spanBase->upCast()->next()));
SkASSERT(0);
- return nullptr;
+ return nullptr; // we never get here, but need this to satisfy compiler
}
void SkOpSegment::calcAngles() {
return this;
}
- SkOpPtT* addT(double t, bool* allocated);
+ SkOpPtT* addT(double t);
template<typename T> T* allocateArray(int count) {
return SkOpTAllocator<T>::AllocateArray(this->globalState()->allocator(), count);
}
void debugAddAngle(double startT, double endT);
- const SkOpPtT* debugAddT(double t, bool* allocated) const;
+ const SkOpPtT* debugAddT(double t) const;
const SkOpAngle* debugAngle(int id) const;
#if DEBUG_ANGLE
void debugCheckAngleCoin() const;
void init(SkPoint pts[], SkScalar weight, SkOpContour* parent, SkPath::Verb verb);
SkOpSpan* insert(SkOpSpan* prev) {
- SkOpSpan* result = SkOpTAllocator<SkOpSpan>::Allocate(this->globalState()->allocator());
+ SkOpGlobalState* globalState = this->globalState();
+ globalState->setAllocatedOpSpan();
+ SkOpSpan* result = SkOpTAllocator<SkOpSpan>::Allocate(globalState->allocator());
SkOpSpanBase* next = prev->next();
result->setPrev(prev);
prev->setNext(result);
// please keep this in sync with debugAddOppAndMerge
// If the added points envelop adjacent spans, merge them in.
void SkOpSpanBase::addOppAndMerge(SkOpSpanBase* opp) {
- if (this->ptT()->addOpp(opp->ptT())) {
+ SkOpPtT* oppPrev = this->ptT()->oppPrev(opp->ptT());
+ if (oppPrev) {
+ this->ptT()->addOpp(opp->ptT(), oppPrev);
this->checkForCollapsedCoincidence();
}
// compute bounds of points in span
};
// please keep in sync with debugAddOpp()
- bool addOpp(SkOpPtT* opp) {
- // find the fOpp ptr to opp
- SkOpPtT* oppPrev = opp->fNext;
- if (oppPrev == this) {
- return false;
- }
- while (oppPrev->fNext != opp) {
- oppPrev = oppPrev->fNext;
- if (oppPrev == this) {
- return false;
- }
- }
+ void addOpp(SkOpPtT* opp, SkOpPtT* oppPrev) {
SkOpPtT* oldNext = this->fNext;
SkASSERT(this != opp);
this->fNext = opp;
SkASSERT(oppPrev != oldNext);
oppPrev->fNext = oldNext;
- return true;
}
bool alias() const;
return SkDEBUGRELEASE(fID, -1);
}
- bool debugAddOpp(const SkOpPtT* opp) const;
+ void debugAddOpp(const SkOpPtT* opp, const SkOpPtT* oppPrev) const;
const SkOpAngle* debugAngle(int id) const;
const SkOpCoincidence* debugCoincidence() const;
bool debugContains(const SkOpPtT* ) const;
SkOpContour* debugContour(int id);
int debugLoopLimit(bool report) const;
bool debugMatchID(int id) const;
+ const SkOpPtT* debugOppPrev(const SkOpPtT* opp) const;
const SkOpPtT* debugPtT(int id) const;
void debugResetCoinT() const;
const SkOpSegment* debugSegment(int id) const;
bool onEnd() const;
+ SkOpPtT* oppPrev(SkOpPtT* opp) const {
+ // find the fOpp ptr to opp
+ SkOpPtT* oppPrev = opp->fNext;
+ if (oppPrev == this) {
+ return nullptr;
+ }
+ while (oppPrev->fNext != opp) {
+ oppPrev = oppPrev->fNext;
+ if (oppPrev == this) {
+ return nullptr;
+ }
+ }
+ return oppPrev;
+ }
+
static bool Overlaps(const SkOpPtT* s1, const SkOpPtT* e1, const SkOpPtT* s2,
const SkOpPtT* e2, const SkOpPtT** sOut, const SkOpPtT** eOut) {
const SkOpPtT* start1 = s1->fT < e1->fT ? s1 : e1;
#if DEBUG_COINCIDENCE
// commented-out lines keep this in sync with addT()
- const SkOpPtT* SkOpSegment::debugAddT(double t, bool* allocated) const {
+ const SkOpPtT* SkOpSegment::debugAddT(double t) const {
debugValidate();
SkPoint pt = this->ptAtT(t);
const SkOpSpanBase* span = &fHead;
do {
const SkOpPtT* result = span->ptT();
- const SkOpPtT* loop;
- bool duplicatePt;
- if (t == result->fT) {
- goto bumpSpan;
- }
- if (this->match(result, this, t, pt)) {
- // 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;
- }
- duplicatePt |= ptMatch;
- loop = loop->next();
- }
- bumpSpan:
+ if (t == result->fT || this->match(result, this, t, pt)) {
// span->bumpSpanAdds();
return result;
}
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
- }
-// SkOpSpan* span = insert(prev, allocator);
+ FAIL_WITH_NULL_IF(!prev);
+ // marks in global state that new op span has been allocated
+ this->globalState()->setAllocatedOpSpan();
// span->init(this, prev, t, pt);
this->debugValidate();
// #if DEBUG_ADD_T
// span->segment()->debugID(), span->debugID());
// #endif
// span->bumpSpanAdds();
- if (allocated) {
- *allocated = true;
- }
return nullptr;
}
- SkASSERT(span != &fTail);
+ FAIL_WITH_NULL_IF(span != &fTail);
} while ((span = span->upCast()->next()));
SkASSERT(0);
- return nullptr;
+ return nullptr; // we never get here, but need this to satisfy compiler
}
#endif
this->debugValidate();
if (!cs || !os) {
if (!cs)
- cs = coinSeg->debugAddT(coinTs, nullptr);
+ cs = coinSeg->debugAddT(coinTs);
if (!os)
- os = oppSeg->debugAddT(oppTs, nullptr);
+ os = oppSeg->debugAddT(oppTs);
if (cs && os) cs->span()->debugAddOppAndMerge(id, log, os->span(), &csDeleted, &osDeleted);
// cs = csWritable;
// os = osWritable;
}
if (!ce || !oe) {
if (!ce)
- ce = coinSeg->debugAddT(coinTe, nullptr);
+ ce = coinSeg->debugAddT(coinTe);
if (!oe)
- oe = oppSeg->debugAddT(oppTe, nullptr);
+ oe = oppSeg->debugAddT(oppTe);
if (ce && oe) ce->span()->debugAddOppAndMerge(id, log, oe->span(), &ceDeleted, &oeDeleted);
// ce = ceWritable;
// oe = oeWritable;
// 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())) {
+ const SkOpPtT* oppPrev = this->ptT()->debugOppPrev(opp->ptT());
+ if (oppPrev) {
+ this->ptT()->debugAddOpp(opp->ptT(), oppPrev);
this->debugCheckForCollapsedCoincidence(id, log);
}
// compute bounds of points in span
#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;
+void SkOpPtT::debugAddOpp(const SkOpPtT* opp, const SkOpPtT* oppPrev) const {
+ SkDEBUGCODE(const SkOpPtT* oldNext = this->fNext);
SkASSERT(this != opp);
// this->fNext = opp;
-// SkASSERT(oppPrev != oldNext);
+ SkASSERT(oppPrev != oldNext);
// oppPrev->fNext = oldNext;
- return true;
}
bool SkOpPtT::debugContains(const SkOpPtT* check) const {
return 0;
}
+const SkOpPtT* SkOpPtT::debugOppPrev(const SkOpPtT* opp) const {
+ return this->oppPrev(const_cast<SkOpPtT*>(opp));
+}
+
void SkOpPtT::debugResetCoinT() const {
#if DEBUG_COINCIDENCE_ORDER
this->segment()->debugResetCoinT();
#include "SkTLS.h"
#endif
-#define FAIL_IF(cond) do { bool fail = (cond); SkOPASSERT(!fail); if (fail) return false; } \
- while (false)
+// Tests with extreme numbers may fail, but all other tests should never fail.
+#define FAIL_IF(cond) \
+ do { bool fail = (cond); SkOPASSERT(!fail); if (fail) return false; } while (false)
+#define FAIL_WITH_NULL_IF(cond) \
+ do { bool fail = (cond); SkOPASSERT(!fail); if (fail) return nullptr; } while (false)
+
+// Some functions serve two masters: one allows the function to fail, the other expects success
+// always. If abort is true, tests with normal numbers may not fail and assert if they do so.
+// If abort is false, both normal and extreme numbers may return false without asserting.
#define RETURN_FALSE_IF(abort, cond) \
- do { bool fail = (cond); SkOPASSERT(!(abort) || !fail); if (fail) return false; } \
- while (false)
+ do { bool fail = (cond); SkOPASSERT(!(abort) || !fail); if (fail) return false; \
+ } while (false)
class SkPathOpsDebug {
public:
kMaxWindingTries = 10
};
+ bool allocatedOpSpan() const {
+ return fAllocatedOpSpan;
+ }
+
SkChunkAlloc* allocator() {
return fAllocator;
}
Phase phase() const {
return fPhase;
}
+
+ void resetAllocatedOpSpan() {
+ fAllocatedOpSpan = false;
+ }
+
+ void setAllocatedOpSpan() {
+ fAllocatedOpSpan = true;
+ }
void setAngleCoincidence() {
fAngleCoincidence = true;
SkOpCoincidence* fCoincidence;
SkOpContourHead* fContourHead;
int fNested;
+ bool fAllocatedOpSpan;
bool fWindingFailed;
bool fAngleCoincidence;
Phase fPhase;
void SkOpSegment::debugAddAngle(double startT, double endT) {
SkOpPtT* startPtT = startT == 0 ? fHead.ptT() : startT == 1 ? fTail.ptT()
- : this->addT(startT, nullptr);
+ : this->addT(startT);
SkOpPtT* endPtT = endT == 0 ? fHead.ptT() : endT == 1 ? fTail.ptT()
- : this->addT(endT, nullptr);
+ : this->addT(endT);
SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(this->globalState()->allocator());
SkOpSpanBase* startSpan = &fHead;
while (startSpan->ptT() != startPtT) {