Index: src/pathops/SkOpCoincidence.cpp |
diff --git a/src/pathops/SkOpCoincidence.cpp b/src/pathops/SkOpCoincidence.cpp |
index b0cb2437006ad5aea83f6a0e3af96a03f33661f1..c7a39fcd6cefbbaee3e2969624d97eca75063115 100755 |
--- a/src/pathops/SkOpCoincidence.cpp |
+++ b/src/pathops/SkOpCoincidence.cpp |
@@ -146,6 +146,37 @@ int SkCoincidentSpans::spanCount() const { |
return coinIntervals == oppIntervals ? coinIntervals : -1; |
} |
+// A coincident span is unordered if the pairs of points in the main and opposite curves' |
+// t values do not ascend or descend. For instance, if a tightly arced quadratic is |
+// coincident with another curve, it may intersect it out of order. |
+bool SkCoincidentSpans::ordered() const { |
+ const SkOpSpanBase* start = this->coinPtTStart()->span(); |
+ const SkOpSpanBase* end = this->coinPtTEnd()->span(); |
+ const SkOpSpanBase* next = start->upCast()->next(); |
+ if (next == end) { |
+ return true; |
+ } |
+ bool flipped = this->flipped(); |
+ const SkOpSegment* oppSeg = this->oppPtTStart()->segment(); |
+ double oppLastT = fOppPtTStart->fT; |
+ do { |
+ const SkOpPtT* opp = next->contains(oppSeg); |
+ if (!opp) { |
+ SkASSERT(0); // may assert if coincident span isn't fully processed |
+ continue; |
+ } |
+ if ((oppLastT > opp->fT) != flipped) { |
+ return false; |
+ } |
+ oppLastT = opp->fT; |
+ if (next == end) { |
+ break; |
+ } |
+ next = next->upCast()->next(); |
+ } while (true); |
+ return true; |
+} |
+ |
// returns true if the point is on a coincident edge, and if it is the start of that edge |
bool SkOpCoincidence::edge(const SkOpPtT* test, bool* start) const { |
SkCoincidentSpans* coinRec = fHead; |
@@ -324,8 +355,8 @@ bool SkOpCoincidence::addEndMovedSpans(const SkOpSpan* base, const SkOpSpanBase* |
SkTSwap(coinTs, coinTe); |
SkTSwap(oppTs, oppTe); |
} |
- if (!this->addOrOverlap(coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe |
- SkDEBUGPARAMS(true) /* do assert if addOrOverlap fails */ )) { |
+ bool added; |
+ if (!this->addOrOverlap(coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &added)) { |
return false; |
} |
} |
@@ -606,7 +637,7 @@ bool SkOpCoincidence::checkOverlap(SkCoincidentSpans* check, |
double oCheckTe = check->oppPtTEnd()->fT; |
if (swapOpp) { |
if (oCheckTs <= oCheckTe) { |
- return false; |
+ return false; |
} |
SkTSwap(oCheckTs, oCheckTe); |
} |
@@ -616,8 +647,8 @@ bool SkOpCoincidence::checkOverlap(SkCoincidentSpans* check, |
} |
bool coinInside = coinTe <= checkTe && coinTs >= checkTs; |
bool oppInside = oppTe <= oCheckTe && oppTs >= oCheckTs; |
- if (coinInside && oppInside) { |
- return false; // complete overlap, already included, do nothing |
+ if (coinInside && oppInside) { // already included, do nothing |
+ return false; |
} |
*overlaps->append() = check; // partial overlap, extend existing entry |
} while ((check = check->next())); |
@@ -627,7 +658,7 @@ bool SkOpCoincidence::checkOverlap(SkCoincidentSpans* check, |
/* Please keep this in sync with debugAddIfMissing() */ |
// note that over1s, over1e, over2s, over2e are ordered |
bool SkOpCoincidence::addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over2s, |
- double tStart, double tEnd, SkOpSegment* coinSeg, SkOpSegment* oppSeg |
+ double tStart, double tEnd, SkOpSegment* coinSeg, SkOpSegment* oppSeg, bool* added |
SkDEBUGPARAMS(const SkOpPtT* over1e) SkDEBUGPARAMS(const SkOpPtT* over2e)) { |
SkASSERT(tStart < tEnd); |
SkASSERT(over1s->fT < over1e->fT); |
@@ -646,35 +677,33 @@ bool SkOpCoincidence::addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over2s, |
coinTs = TRange(over1s, tStart, coinSeg SkDEBUGPARAMS(over1e)); |
coinTe = TRange(over1s, tEnd, coinSeg SkDEBUGPARAMS(over1e)); |
if (coinSeg->collapsed(coinTs, coinTe)) { |
- return false; |
+ return true; |
} |
oppTs = TRange(over2s, tStart, oppSeg SkDEBUGPARAMS(over2e)); |
oppTe = TRange(over2s, tEnd, oppSeg SkDEBUGPARAMS(over2e)); |
if (oppSeg->collapsed(oppTs, oppTe)) { |
- return false; |
+ return true; |
} |
if (coinTs > coinTe) { |
SkTSwap(coinTs, coinTe); |
SkTSwap(oppTs, oppTe); |
} |
- return this->addOrOverlap(coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe |
- SkDEBUGPARAMS(false) /* don't assert if addOrOverlap fails */ ); |
+ return this->addOrOverlap(coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, added); |
} |
/* 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 |
- SkDEBUGPARAMS(bool callerAborts)) { |
+ double coinTs, double coinTe, double oppTs, double oppTe, bool* added) { |
SkTDArray<SkCoincidentSpans*> overlaps; |
- RETURN_FALSE_IF(callerAborts, !fTop); |
+ FAIL_IF(!fTop); |
if (!this->checkOverlap(fTop, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &overlaps)) { |
- return false; |
+ return true; |
} |
if (fHead && !this->checkOverlap(fHead, coinSeg, oppSeg, coinTs, |
coinTe, oppTs, oppTe, &overlaps)) { |
- return false; |
+ return true; |
} |
SkCoincidentSpans* overlap = overlaps.count() ? overlaps[0] : nullptr; |
for (int index = 1; index < overlaps.count(); ++index) { // combine overlaps before continuing |
@@ -701,28 +730,32 @@ bool SkOpCoincidence::addOrOverlap(SkOpSegment* coinSeg, SkOpSegment* oppSeg, |
} |
const SkOpPtT* cs = coinSeg->existing(coinTs, oppSeg); |
const SkOpPtT* ce = coinSeg->existing(coinTe, oppSeg); |
- RETURN_FALSE_IF(callerAborts, overlap && cs && ce && overlap->contains(cs, ce)); |
- RETURN_FALSE_IF(callerAborts, cs == ce && cs); |
+ if (overlap && cs && ce && overlap->contains(cs, ce)) { |
+ return true; |
+ } |
+ FAIL_IF(cs == ce && cs); |
const SkOpPtT* os = oppSeg->existing(oppTs, coinSeg); |
const SkOpPtT* oe = oppSeg->existing(oppTe, coinSeg); |
- RETURN_FALSE_IF(callerAborts, overlap && os && oe && overlap->contains(os, oe)); |
+ if (overlap && os && oe && overlap->contains(os, oe)) { |
+ return true; |
+ } |
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; |
- RETURN_FALSE_IF(callerAborts, csExisting && csExisting == ceExisting); |
- RETURN_FALSE_IF(callerAborts, csExisting && (csExisting == ce || |
+ FAIL_IF(csExisting && csExisting == ceExisting); |
+ FAIL_IF(csExisting && (csExisting == ce || |
csExisting->contains(ceExisting ? ceExisting : ce))); |
- RETURN_FALSE_IF(callerAborts, ceExisting && (ceExisting == cs || |
+ FAIL_IF(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; |
- RETURN_FALSE_IF(callerAborts, osExisting && osExisting == oeExisting); |
- RETURN_FALSE_IF(callerAborts, osExisting && (osExisting == oe || |
+ FAIL_IF(osExisting && osExisting == oeExisting); |
+ FAIL_IF(osExisting && (osExisting == oe || |
osExisting->contains(oeExisting ? oeExisting : oe))); |
- RETURN_FALSE_IF(callerAborts, oeExisting && (oeExisting == os || |
+ FAIL_IF(oeExisting && (oeExisting == os || |
oeExisting->contains(osExisting ? osExisting : os))); |
// extra line in debug code |
this->debugValidate(); |
@@ -731,11 +764,11 @@ bool SkOpCoincidence::addOrOverlap(SkOpSegment* coinSeg, SkOpSegment* oppSeg, |
: coinSeg->addT(coinTs); |
SkOpPtT* osWritable = os ? const_cast<SkOpPtT*>(os) |
: oppSeg->addT(oppTs); |
- RETURN_FALSE_IF(callerAborts, !csWritable || !osWritable); |
+ FAIL_IF(!csWritable || !osWritable); |
csWritable->span()->addOpp(osWritable->span()); |
cs = csWritable; |
os = osWritable->active(); |
- RETURN_FALSE_IF(callerAborts, (ce && ce->deleted()) || (oe && oe->deleted())); |
+ FAIL_IF((ce && ce->deleted()) || (oe && oe->deleted())); |
} |
if (!ce || !oe) { |
SkOpPtT* ceWritable = ce ? const_cast<SkOpPtT*>(ce) |
@@ -747,11 +780,11 @@ bool SkOpCoincidence::addOrOverlap(SkOpSegment* coinSeg, SkOpSegment* oppSeg, |
oe = oeWritable; |
} |
this->debugValidate(); |
- 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)); |
+ FAIL_IF(cs->deleted()); |
+ FAIL_IF(os->deleted()); |
+ FAIL_IF(ce->deleted()); |
+ FAIL_IF(oe->deleted()); |
+ FAIL_IF(cs->contains(ce) || os->contains(oe)); |
bool result = true; |
if (overlap) { |
if (overlap->coinPtTStart()->segment() == coinSeg) { |
@@ -775,19 +808,22 @@ bool SkOpCoincidence::addOrOverlap(SkOpSegment* coinSeg, SkOpSegment* oppSeg, |
#endif |
} |
this->debugValidate(); |
- return result; |
+ if (result) { |
+ *added = true; |
+ } |
+ return true; |
} |
// Please keep this in sync with debugAddMissing() |
/* detects overlaps of different coincident runs on same segment */ |
/* does not detect overlaps for pairs without any segments in common */ |
// returns true if caller should loop again |
-bool SkOpCoincidence::addMissing() { |
+bool SkOpCoincidence::addMissing(bool* added) { |
SkCoincidentSpans* outer = fHead; |
+ *added = false; |
if (!outer) { |
- return false; |
+ return true; |
} |
- bool added = false; |
fTop = outer; |
fHead = nullptr; |
do { |
@@ -800,7 +836,7 @@ bool SkOpCoincidence::addMissing() { |
SkASSERT(!outerCoin->done()); // if it's done, should have already been removed from list |
const SkOpPtT* oos = outer->oppPtTStart(); |
if (oos->deleted()) { |
- return false; |
+ return true; |
} |
const SkOpSegment* outerOpp = oos->segment(); |
SkASSERT(!outerOpp->done()); |
@@ -823,13 +859,13 @@ bool SkOpCoincidence::addMissing() { |
if (outerCoin == innerCoin) { |
const SkOpPtT* oce = outer->coinPtTEnd(); |
if (oce->deleted()) { |
- return false; |
+ return true; |
} |
const SkOpPtT* ice = inner->coinPtTEnd(); |
SkASSERT(!ice->deleted()); |
if (outerOpp != innerOpp && this->overlap(ocs, oce, ics, ice, &overS, &overE)) { |
- added |= this->addIfMissing(ocs->starter(oce), ics->starter(ice), |
- overS, overE, outerOppWritable, innerOppWritable |
+ (void) this->addIfMissing(ocs->starter(oce), ics->starter(ice), |
+ overS, overE, outerOppWritable, innerOppWritable, added |
SkDEBUGPARAMS(ocs->debugEnder(oce)) |
SkDEBUGPARAMS(ics->debugEnder(ice))); |
} |
@@ -839,8 +875,8 @@ bool SkOpCoincidence::addMissing() { |
const SkOpPtT* ioe = inner->oppPtTEnd(); |
SkASSERT(!ioe->deleted()); |
if (outerOpp != innerCoin && this->overlap(ocs, oce, ios, ioe, &overS, &overE)) { |
- added |= this->addIfMissing(ocs->starter(oce), ios->starter(ioe), |
- overS, overE, outerOppWritable, innerCoinWritable |
+ (void) this->addIfMissing(ocs->starter(oce), ios->starter(ioe), |
+ overS, overE, outerOppWritable, innerCoinWritable, added |
SkDEBUGPARAMS(ocs->debugEnder(oce)) |
SkDEBUGPARAMS(ios->debugEnder(ioe))); |
} |
@@ -851,8 +887,8 @@ bool SkOpCoincidence::addMissing() { |
SkASSERT(!ice->deleted()); |
SkASSERT(outerCoin != innerOpp); |
if (this->overlap(oos, ooe, ics, ice, &overS, &overE)) { |
- added |= this->addIfMissing(oos->starter(ooe), ics->starter(ice), |
- overS, overE, outerCoinWritable, innerOppWritable |
+ (void) this->addIfMissing(oos->starter(ooe), ics->starter(ice), |
+ overS, overE, outerCoinWritable, innerOppWritable, added |
SkDEBUGPARAMS(oos->debugEnder(ooe)) |
SkDEBUGPARAMS(ics->debugEnder(ice))); |
} |
@@ -861,12 +897,12 @@ bool SkOpCoincidence::addMissing() { |
SkASSERT(!ooe->deleted()); |
const SkOpPtT* ioe = inner->oppPtTEnd(); |
if (ioe->deleted()) { |
- return false; |
+ return true; |
} |
SkASSERT(outerCoin != innerCoin); |
if (this->overlap(oos, ooe, ios, ioe, &overS, &overE)) { |
- added |= this->addIfMissing(oos->starter(ooe), ios->starter(ioe), |
- overS, overE, outerCoinWritable, innerCoinWritable |
+ (void) this->addIfMissing(oos->starter(ooe), ios->starter(ioe), |
+ overS, overE, outerCoinWritable, innerCoinWritable, added |
SkDEBUGPARAMS(oos->debugEnder(ooe)) |
SkDEBUGPARAMS(ios->debugEnder(ioe))); |
} |
@@ -875,7 +911,7 @@ bool SkOpCoincidence::addMissing() { |
} |
} while ((outer = outer->next())); |
this->restoreHead(); |
- return added; |
+ return true; |
} |
bool SkOpCoincidence::addOverlap(const SkOpSegment* seg1, const SkOpSegment* seg1o, |
@@ -1455,17 +1491,13 @@ bool SkOpCoincidence::mark() { |
return true; |
} |
do { |
- if (!coin->coinPtTStartWritable()->span()->upCastable()) { |
- return false; |
- } |
+ FAIL_IF(!coin->coinPtTStartWritable()->span()->upCastable()); |
SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast(); |
SkASSERT(!start->deleted()); |
SkOpSpanBase* end = coin->coinPtTEndWritable()->span(); |
SkASSERT(!end->deleted()); |
SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span(); |
- if (oStart->deleted()) { |
- return false; |
- } |
+ FAIL_IF(oStart->deleted()); |
SkOpSpanBase* oEnd = coin->oppPtTEndWritable()->span(); |
SkASSERT(!oEnd->deleted()); |
bool flipped = coin->flipped(); |
@@ -1480,19 +1512,16 @@ bool SkOpCoincidence::mark() { |
const SkOpSegment* oSegment = oStart->segment(); |
SkOpSpanBase* next = start; |
SkOpSpanBase* oNext = oStart; |
+ bool ordered = coin->ordered(); |
while ((next = next->upCast()->next()) != end) { |
- if (!next->upCastable()) { |
- return false; |
- } |
- if (!next->upCast()->insertCoincidence(oSegment, flipped)) { |
+ FAIL_IF(!next->upCastable()); |
+ if (!next->upCast()->insertCoincidence(oSegment, flipped, ordered)) { |
return false; |
} |
} |
while ((oNext = oNext->upCast()->next()) != oEnd) { |
- if (!oNext->upCastable()) { |
- return false; |
- } |
- if (!oNext->upCast()->insertCoincidence(segment, flipped)) { |
+ FAIL_IF(!oNext->upCastable()); |
+ if (!oNext->upCast()->insertCoincidence(segment, flipped, ordered)) { |
return false; |
} |
} |