aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/pathops/SkOpCoincidence.cpp
diff options
context:
space:
mode:
authorGravatar caryclark <caryclark@google.com>2016-09-09 09:37:57 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-09-09 09:37:57 -0700
commit81a478ca6c36aac3e53ce0373a281ac8940f4780 (patch)
tree384aeaec6831263679b6c37393a173e26613255f /src/pathops/SkOpCoincidence.cpp
parentb2796fdd9eab88836795d6a54cf6811af6ecdc81 (diff)
Skip adding coincident edges found
during curve intersection if their ends are nearly the same. Loosen conic/line intersection point check. Detect when coincident points are unordered. This means that points a/b/c on one curve may appear in b/c/a order on the opposite curve. Restructure addMissing to return success and return if a coincidence was added as a parameter. With this, tiger part a works. Tiger part b exposes bugs around tight quads that are nearly coincident with themselves, and are coincident with something else. The greedy coicident matcher may cause the point order to be out of sync. Still working out what to do in this case. TBR=reed@google.com GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2321773002 Review-Url: https://codereview.chromium.org/2321773002
Diffstat (limited to 'src/pathops/SkOpCoincidence.cpp')
-rwxr-xr-xsrc/pathops/SkOpCoincidence.cpp151
1 files changed, 90 insertions, 61 deletions
diff --git a/src/pathops/SkOpCoincidence.cpp b/src/pathops/SkOpCoincidence.cpp
index b0cb243700..c7a39fcd6c 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;
}
}