Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(904)

Unified Diff: src/pathops/SkOpSpan.cpp

Issue 2128633003: pathops coincidence and security rewrite (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: require resulting t to be between 0 and 1 Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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) {

Powered by Google App Engine
This is Rietveld 408576698