Index: src/pathops/SkOpSpan.cpp |
diff --git a/src/pathops/SkOpSpan.cpp b/src/pathops/SkOpSpan.cpp |
index f3362235d8c928e652fd0027a3e96189b5b2b10d..577a9db32669670449b66d84005bf6433ce7f5a4 100755 |
--- a/src/pathops/SkOpSpan.cpp |
+++ b/src/pathops/SkOpSpan.cpp |
@@ -35,54 +35,60 @@ bool SkOpPtT::contains(const SkOpPtT* check) const { |
return false; |
} |
-SkOpPtT* SkOpPtT::contains(const SkOpSegment* check) { |
- SkASSERT(this->segment() != check); |
- SkOpPtT* ptT = this; |
+bool SkOpPtT::contains(const SkOpSegment* segment, const SkPoint& pt) const { |
+ SkASSERT(this->segment() != segment); |
+ const SkOpPtT* ptT = this; |
const SkOpPtT* stopPtT = ptT; |
while ((ptT = ptT->next()) != stopPtT) { |
- if (ptT->segment() == check) { |
- return ptT; |
+ if (ptT->fPt == pt && ptT->segment() == segment) { |
+ return true; |
} |
} |
- return nullptr; |
+ return false; |
} |
-SkOpContour* SkOpPtT::contour() const { |
- return segment()->contour(); |
+bool SkOpPtT::contains(const SkOpSegment* segment, double t) const { |
+ const SkOpPtT* ptT = this; |
+ const SkOpPtT* stopPtT = ptT; |
+ while ((ptT = ptT->next()) != stopPtT) { |
+ if (ptT->fT == t && ptT->segment() == segment) { |
+ return true; |
+ } |
+ } |
+ return false; |
} |
-SkOpPtT* SkOpPtT::doppelganger() { |
- SkASSERT(fDeleted); |
- SkOpPtT* ptT = fNext; |
- while (ptT->fDeleted) { |
- ptT = ptT->fNext; |
- } |
+const SkOpPtT* SkOpPtT::contains(const SkOpSegment* check) const { |
+ SkASSERT(this->segment() != check); |
+ const SkOpPtT* ptT = this; |
const SkOpPtT* stopPtT = ptT; |
- do { |
- if (ptT->fSpan == fSpan) { |
+ while ((ptT = ptT->next()) != stopPtT) { |
+ if (ptT->segment() == check && !ptT->deleted()) { |
return ptT; |
} |
- ptT = ptT->fNext; |
- } while (stopPtT != ptT); |
- SkASSERT(0); |
+ } |
return nullptr; |
} |
-SkOpPtT* SkOpPtT::find(SkOpSegment* segment) { |
- SkOpPtT* ptT = this; |
+SkOpContour* SkOpPtT::contour() const { |
+ return segment()->contour(); |
+} |
+ |
+const SkOpPtT* SkOpPtT::find(const SkOpSegment* segment) const { |
+ const SkOpPtT* ptT = this; |
const SkOpPtT* stopPtT = ptT; |
do { |
- if (ptT->segment() == segment) { |
+ if (ptT->segment() == segment && !ptT->deleted()) { |
return ptT; |
} |
ptT = ptT->fNext; |
} while (stopPtT != ptT); |
- SkASSERT(0); |
+// SkASSERT(0); |
return nullptr; |
} |
SkOpGlobalState* SkOpPtT::globalState() const { |
- return contour()->globalState(); |
+ return contour()->globalState(); |
} |
void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplicate) { |
@@ -92,6 +98,7 @@ void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplica |
fNext = this; |
fDuplicatePt = duplicate; |
fDeleted = false; |
+ fCoincident = false; |
SkDEBUGCODE(fID = span->globalState()->nextPtTID()); |
} |
@@ -104,6 +111,16 @@ bool SkOpPtT::onEnd() const { |
return span == segment->head() || span == segment->tail(); |
} |
+bool SkOpPtT::ptAlreadySeen(const SkOpPtT* check) const { |
+ while (this != check) { |
+ if (this->fPt == check->fPt) { |
+ return true; |
+ } |
+ check = check->fNext; |
+ } |
+ return false; |
+} |
+ |
SkOpPtT* SkOpPtT::prev() { |
SkOpPtT* result = this; |
SkOpPtT* next = this; |
@@ -114,12 +131,12 @@ SkOpPtT* SkOpPtT::prev() { |
return result; |
} |
-SkOpPtT* SkOpPtT::remove() { |
+SkOpPtT* SkOpPtT::remove(const SkOpPtT* kept) { |
SkOpPtT* prev = this; |
do { |
SkOpPtT* next = prev->fNext; |
if (next == this) { |
- prev->removeNext(this); |
+ prev->removeNext(kept); |
SkASSERT(prev->fNext != prev); |
fDeleted = true; |
return prev; |
@@ -130,12 +147,16 @@ SkOpPtT* SkOpPtT::remove() { |
return nullptr; |
} |
-void SkOpPtT::removeNext(SkOpPtT* kept) { |
+void SkOpPtT::removeNext(const SkOpPtT* kept) { |
SkASSERT(this->fNext); |
SkOpPtT* next = this->fNext; |
SkASSERT(this != next->fNext); |
this->fNext = next->fNext; |
SkOpSpanBase* span = next->span(); |
+ SkOpCoincidence* coincidence = span->globalState()->coincidence(); |
+ if (coincidence) { |
+ coincidence->fixUp(next, kept); |
+ } |
next->setDeleted(); |
if (span->ptT() == next) { |
span->upCast()->release(kept); |
@@ -150,85 +171,68 @@ SkOpSegment* SkOpPtT::segment() { |
return span()->segment(); |
} |
-void SkOpSpanBase::align() { |
- if (this->fAligned) { |
+void SkOpPtT::setDeleted() { |
+ SkASSERT(this->span()->debugDeleted() || this->span()->ptT() != this); |
+ SkASSERT(this->globalState()->debugSkipAssert() || !fDeleted); |
+ fDeleted = true; |
+} |
+ |
+// 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())) { |
+ this->checkForCollapsedCoincidence(); |
+ } |
+ // compute bounds of points in span |
+ SkPathOpsBounds bounds; |
+ bounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMin, SK_ScalarMin); |
+ const SkOpPtT* head = this->ptT(); |
+ const SkOpPtT* nextPt = head; |
+ do { |
+ bounds.add(nextPt->fPt); |
+ } while ((nextPt = nextPt->next()) != head); |
+ if (!bounds.width() && !bounds.height()) { |
return; |
} |
- SkASSERT(!zero_or_one(this->fPtT.fT)); |
- SkASSERT(this->fPtT.next()); |
- // if a linked pt/t pair has a t of zero or one, use it as the base for alignment |
- SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT; |
- while ((ptT = ptT->next()) != stopPtT) { |
- if (zero_or_one(ptT->fT)) { |
- SkOpSegment* segment = ptT->segment(); |
- SkASSERT(this->segment() != segment); |
- SkASSERT(segment->head()->ptT() == ptT || segment->tail()->ptT() == ptT); |
- if (ptT->fT) { |
- segment->tail()->alignEnd(1, segment->lastPt()); |
- } else { |
- segment->head()->alignEnd(0, segment->pts()[0]); |
- } |
+ this->mergeContained(bounds); |
+ opp->mergeContained(bounds); |
+} |
+ |
+// Please keep this in sync with debugMergeContained() |
+void SkOpSpanBase::mergeContained(const SkPathOpsBounds& bounds) { |
+ // while adjacent spans' points are contained by the bounds, merge them |
+ SkOpSpanBase* prev = this; |
+ SkOpSegment* seg = this->segment(); |
+ while ((prev = prev->prev()) && bounds.contains(prev->pt()) && !seg->ptsDisjoint(prev, this)) { |
+ if (prev->prev()) { |
+ this->merge(prev->upCast()); |
+ prev = this; |
+ } else if (this->final()) { |
+ seg->clearAll(); |
return; |
+ } else { |
+ prev->merge(this->upCast()); |
} |
} |
- alignInner(); |
- this->fAligned = true; |
-} |
- |
- |
-// FIXME: delete spans that collapse |
-// delete segments that collapse |
-// delete contours that collapse |
-void SkOpSpanBase::alignEnd(double t, const SkPoint& pt) { |
- SkASSERT(zero_or_one(t)); |
- SkOpSegment* segment = this->segment(); |
- SkASSERT(t ? segment->lastPt() == pt : segment->pts()[0] == pt); |
- alignInner(); |
- *segment->writablePt(!!t) = pt; |
- SkOpPtT* ptT = &this->fPtT; |
- SkASSERT(t == ptT->fT); |
- SkASSERT(pt == ptT->fPt); |
- SkOpPtT* test = ptT, * stopPtT = ptT; |
- while ((test = test->next()) != stopPtT) { |
- SkOpSegment* other = test->segment(); |
- if (other == this->segment()) { |
- continue; |
+ SkOpSpanBase* current = this; |
+ SkOpSpanBase* next = this; |
+ while (next->upCastable() && (next = next->upCast()->next()) |
+ && bounds.contains(next->pt()) && !seg->ptsDisjoint(this, next)) { |
+ if (!current->prev() && next->final()) { |
+ seg->clearAll(); |
+ return; |
} |
- if (!zero_or_one(test->fT)) { |
- continue; |
+ if (current->prev()) { |
+ next->merge(current->upCast()); |
+ current = next; |
+ } else { |
+ current->merge(next->upCast()); |
+ // extra line in debug version |
} |
- *other->writablePt(!!test->fT) = pt; |
} |
- this->fAligned = true; |
-} |
- |
-void SkOpSpanBase::alignInner() { |
- // force the spans to share points and t values |
- SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT; |
- const SkPoint& pt = ptT->fPt; |
- do { |
- ptT->fPt = pt; |
- const SkOpSpanBase* span = ptT->span(); |
- SkOpPtT* test = ptT; |
- do { |
- SkOpPtT* prev = test; |
- if ((test = test->next()) == stopPtT) { |
- break; |
- } |
- if (span == test->span() && !span->segment()->ptsDisjoint(*ptT, *test)) { |
- // omit aliases that alignment makes redundant |
- if ((!ptT->alias() || test->alias()) && (ptT->onEnd() || !test->onEnd())) { |
- SkASSERT(test->alias()); |
- prev->removeNext(ptT); |
- test = prev; |
- } else { |
- SkASSERT(ptT->alias()); |
- stopPtT = ptT = ptT->remove(); |
- break; |
- } |
- } |
- } while (true); |
- } while ((ptT = ptT->next()) != stopPtT); |
+#if DEBUG_COINCIDENCE |
+ this->globalState()->coincidence()->debugValidate(); |
+#endif |
} |
bool SkOpSpanBase::contains(const SkOpSpanBase* span) const { |
@@ -244,11 +248,14 @@ bool SkOpSpanBase::contains(const SkOpSpanBase* span) const { |
return false; |
} |
-SkOpPtT* SkOpSpanBase::contains(const SkOpSegment* segment) { |
- SkOpPtT* start = &fPtT; |
- SkOpPtT* walk = start; |
+const SkOpPtT* SkOpSpanBase::contains(const SkOpSegment* segment) const { |
+ const SkOpPtT* start = &fPtT; |
+ const SkOpPtT* walk = start; |
while ((walk = walk->next()) != start) { |
- if (walk->segment() == segment) { |
+ if (walk->deleted()) { |
+ continue; |
+ } |
+ if (walk->segment() == segment && walk->span()->ptT() == walk) { |
return walk; |
} |
} |
@@ -271,7 +278,7 @@ SkOpContour* SkOpSpanBase::contour() const { |
} |
SkOpGlobalState* SkOpSpanBase::globalState() const { |
- return contour()->globalState(); |
+ return contour()->globalState(); |
} |
void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) { |
@@ -285,6 +292,7 @@ void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, cons |
fChased = false; |
SkDEBUGCODE(fCount = 1); |
SkDEBUGCODE(fID = globalState()->nextSpanID()); |
+ SkDEBUGCODE(fDeleted = false); |
} |
// this pair of spans share a common t value or point; merge them and eliminate duplicates |
@@ -294,8 +302,11 @@ void SkOpSpanBase::merge(SkOpSpan* span) { |
SkASSERT(this->t() != spanPtT->fT); |
SkASSERT(!zero_or_one(spanPtT->fT)); |
span->release(this->ptT()); |
+ if (this->contains(span)) { |
+ return; // merge is already in the ptT loop |
+ } |
SkOpPtT* remainder = spanPtT->next(); |
- ptT()->insert(spanPtT); |
+ this->ptT()->insert(spanPtT); |
while (remainder != spanPtT) { |
SkOpPtT* next = remainder->next(); |
SkOpPtT* compare = spanPtT->next(); |
@@ -311,6 +322,26 @@ tryNextRemainder: |
remainder = next; |
} |
fSpanAdds += span->fSpanAdds; |
+ this->checkForCollapsedCoincidence(); |
+} |
+ |
+// please keep in sync with debugCheckForCollapsedCoincidence() |
+void SkOpSpanBase::checkForCollapsedCoincidence() { |
+ SkOpCoincidence* coins = this->globalState()->coincidence(); |
+ if (coins->isEmpty()) { |
+ return; |
+ } |
+// the insert above may have put both ends of a coincident run in the same span |
+// for each coincident ptT in loop; see if its opposite in is also in the loop |
+// this implementation is the motivation for marking that a ptT is referenced by a coincident span |
+ SkOpPtT* head = this->ptT(); |
+ SkOpPtT* test = head; |
+ do { |
+ if (!test->coincident()) { |
+ continue; |
+ } |
+ coins->markCollapsed(test); |
+ } while ((test = test->next()) != head); |
} |
int SkOpSpan::computeWindSum() { |
@@ -334,7 +365,45 @@ bool SkOpSpan::containsCoincidence(const SkOpSegment* segment) const { |
return false; |
} |
-void SkOpSpan::release(SkOpPtT* kept) { |
+void SkOpSpan::init(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) { |
+ SkASSERT(t != 1); |
+ initBase(segment, prev, t, pt); |
+ fCoincident = this; |
+ fToAngle = nullptr; |
+ fWindSum = fOppSum = SK_MinS32; |
+ fWindValue = 1; |
+ fOppValue = 0; |
+ fTopTTry = 0; |
+ fChased = fDone = false; |
+ segment->bumpCount(); |
+ fAlreadyAdded = false; |
+} |
+ |
+// Please keep this in sync with debugInsertCoincidence() |
+bool SkOpSpan::insertCoincidence(const SkOpSegment* segment, bool flipped) { |
+ if (this->containsCoincidence(segment)) { |
+ return true; |
+ } |
+ SkOpPtT* next = &fPtT; |
+ while ((next = next->next()) != &fPtT) { |
+ if (next->segment() == segment) { |
+ SkOpSpan* span = flipped ? next->span()->prev() : next->span()->upCast(); |
+ if (!span) { |
+ return false; |
+ } |
+ this->insertCoincidence(span); |
+ return true; |
+ } |
+ } |
+#if DEBUG_COINCIDENCE |
+ SkASSERT(0); // FIXME? if we get here, the span is missing its opposite segment... |
+#endif |
+ return true; |
+} |
+ |
+void SkOpSpan::release(const SkOpPtT* kept) { |
+ SkDEBUGCODE(fDeleted = true); |
+ SkASSERT(kept->span() != this); |
SkASSERT(!final()); |
SkOpSpan* prev = this->prev(); |
SkASSERT(prev); |
@@ -348,20 +417,14 @@ void SkOpSpan::release(SkOpPtT* kept) { |
coincidence->fixUp(this->ptT(), kept); |
} |
this->ptT()->setDeleted(); |
-} |
- |
-void SkOpSpan::init(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) { |
- SkASSERT(t != 1); |
- initBase(segment, prev, t, pt); |
- fCoincident = this; |
- fToAngle = nullptr; |
- fWindSum = fOppSum = SK_MinS32; |
- fWindValue = 1; |
- fOppValue = 0; |
- fTopTTry = 0; |
- fChased = fDone = false; |
- segment->bumpCount(); |
- fAlreadyAdded = false; |
+ SkOpPtT* stopPtT = this->ptT(); |
+ SkOpPtT* testPtT = stopPtT; |
+ const SkOpSpanBase* keptSpan = kept->span(); |
+ do { |
+ if (this == testPtT->span()) { |
+ testPtT->setSpan(keptSpan); |
+ } |
+ } while ((testPtT = testPtT->next()) != stopPtT); |
} |
void SkOpSpan::setOppSum(int oppSum) { |