Index: src/pathops/SkPathOpsPostSect.cpp |
diff --git a/src/pathops/SkPathOpsPostSect.cpp b/src/pathops/SkPathOpsPostSect.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..15a1900ce3b1ad48a8a62768d5564379873cda51 |
--- /dev/null |
+++ b/src/pathops/SkPathOpsPostSect.cpp |
@@ -0,0 +1,502 @@ |
+/* |
+ * Copyright 2014 Google Inc. |
+ * |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+#include "SkOpCoincidence.h" |
+#include "SkOpContour.h" |
+#include "SkOpSegment.h" |
+#include "SkPathWriter.h" |
+ |
+bool SkOpPtT::alias() const { |
+ return this->span()->ptT() != this; |
+} |
+ |
+SkOpContour* SkOpPtT::contour() const { |
+ return segment()->contour(); |
+} |
+ |
+SkOpDebugState* SkOpPtT::debugState() const { |
+ return PATH_OPS_DEBUG_RELEASE(contour()->debugState(), NULL); |
+} |
+ |
+void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplicate) { |
+ fT = t; |
+ fPt = pt; |
+ fSpan = span; |
+ fNext = this; |
+ fDuplicatePt = duplicate; |
+ fDeleted = false; |
+ PATH_OPS_DEBUG_CODE(fID = ++span->debugState()->fPtTID); |
+} |
+ |
+bool SkOpPtT::onEnd() const { |
+ const SkOpSpanBase* span = this->span(); |
+ if (span->ptT() != this) { |
+ return false; |
+ } |
+ const SkOpSegment* segment = this->segment(); |
+ return span == segment->head() || span == segment->tail(); |
+} |
+ |
+SkOpPtT* SkOpPtT::remove() { |
+ SkOpPtT* prev = this; |
+ do { |
+ SkOpPtT* next = prev->fNext; |
+ if (next == this) { |
+ prev->removeNext(); |
+ fDeleted = true; |
+ return prev; |
+ } |
+ prev = next; |
+ } while (prev != this); |
+ SkASSERT(0); |
+ return NULL; |
+} |
+ |
+void SkOpPtT::removeNext() { |
+ SkASSERT(this->fNext); |
+ SkOpPtT* next = this->fNext; |
+ this->fNext = next->fNext; |
+ SkOpSpanBase* span = next->span(); |
+ next->setDeleted(); |
+ if (span->ptT() == next) { |
+ span->upCast()->detach(); |
+ } |
+} |
+ |
+const SkOpSegment* SkOpPtT::segment() const { |
+ return span()->segment(); |
+} |
+ |
+SkOpSegment* SkOpPtT::segment() { |
+ return span()->segment(); |
+} |
+ |
+// find the starting or ending span with an existing loop of angles |
+// OPTIMIZE? remove the spans pointing to windValue==0 here or earlier? |
+// FIXME? assert that only one other span has a valid windValue or oppValue |
+void SkOpSpanBase::addSimpleAngle(bool checkFrom, SkChunkAlloc* allocator) { |
+ SkOpAngle* angle; |
+ if (checkFrom) { |
+ SkASSERT(this->final()); |
+ if (this->fromAngle()) { |
+ SkASSERT(this->fromAngle()->loopCount() == 2); |
+ return; |
+ } |
+ angle = this->segment()->addEndSpan(allocator); |
+ } else { |
+ SkASSERT(this->t() == 0); |
+ SkOpSpan* span = this->upCast(); |
+ if (span->toAngle()) { |
+ SkASSERT(span->toAngle()->loopCount() == 2); |
+ SkASSERT(!span->fromAngle()); |
+ span->setFromAngle(span->toAngle()->next()); |
+ return; |
+ } |
+ angle = this->segment()->addStartSpan(allocator); |
+ } |
+ SkOpPtT* ptT = this->ptT(); |
+ SkOpSpanBase* oSpanBase; |
+ SkOpSpan* oSpan; |
+ SkOpSegment* other; |
+ do { |
+ ptT = ptT->next(); |
+ oSpanBase = ptT->span(); |
+ oSpan = oSpanBase->upCastable(); |
+ other = oSpanBase->segment(); |
+ if (oSpan && oSpan->windValue()) { |
+ break; |
+ } |
+ if (oSpanBase->t() == 0) { |
+ continue; |
+ } |
+ SkOpSpan* oFromSpan = oSpanBase->prev(); |
+ SkASSERT(oFromSpan->t() < 1); |
+ if (oFromSpan->windValue()) { |
+ break; |
+ } |
+ } while (ptT != this->ptT()); |
+ SkOpAngle* oAngle; |
+ if (checkFrom) { |
+ oAngle = other->addStartSpan(allocator); |
+ SkASSERT(oSpan && !oSpan->final()); |
+ SkASSERT(oAngle == oSpan->toAngle()); |
+ } else { |
+ oAngle = other->addEndSpan(allocator); |
+ SkASSERT(oAngle == oSpanBase->fromAngle()); |
+ } |
+ angle->insert(oAngle); |
+} |
+ |
+void SkOpSpanBase::align() { |
+ if (this->fAligned) { |
+ 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]); |
+ } |
+ return; |
+ } |
+ } |
+ 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; |
+ } |
+ if (!zero_or_one(test->fT)) { |
+ continue; |
+ } |
+ *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(); |
+ test = prev; |
+ } else { |
+ SkASSERT(ptT->alias()); |
+ stopPtT = ptT = ptT->remove(); |
+ break; |
+ } |
+ } |
+ } while (true); |
+ } while ((ptT = ptT->next()) != stopPtT); |
+} |
+ |
+bool SkOpSpanBase::contains(const SkOpSpanBase* span) const { |
+ const SkOpPtT* start = &fPtT; |
+ const SkOpPtT* check = &span->fPtT; |
+ SkASSERT(start != check); |
+ const SkOpPtT* walk = start; |
+ while ((walk = walk->next()) != start) { |
+ if (walk == check) { |
+ return true; |
+ } |
+ } |
+ return false; |
+} |
+ |
+bool SkOpSpanBase::containsCoinEnd(const SkOpSegment* segment) const { |
+ SkASSERT(this->segment() != segment); |
+ const SkOpSpanBase* next = this; |
+ while ((next = next->fCoinEnd) != this) { |
+ if (next->segment() == segment) { |
+ return true; |
+ } |
+ } |
+ return false; |
+} |
+ |
+SkOpContour* SkOpSpanBase::contour() const { |
+ return segment()->contour(); |
+} |
+ |
+SkOpDebugState* SkOpSpanBase::debugState() const { |
+ return PATH_OPS_DEBUG_RELEASE(contour()->debugState(), NULL); |
+} |
+ |
+void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) { |
+ fSegment = segment; |
+ fPtT.init(this, t, pt, false); |
+ fCoinEnd = this; |
+ fFromAngle = NULL; |
+ fPrev = prev; |
+ fAligned = true; |
+ fChased = false; |
+ PATH_OPS_DEBUG_CODE(fCount = 1); |
+ PATH_OPS_DEBUG_CODE(fID = ++debugState()->fSpanID); |
+} |
+ |
+// this pair of spans share a common t value or point; merge them and eliminate duplicates |
+// this does not compute the best t or pt value; this merely moves all data into a single list |
+void SkOpSpanBase::merge(SkOpSpan* span) { |
+ SkOpPtT* spanPtT = span->ptT(); |
+ SkASSERT(this->t() != spanPtT->fT); |
+ SkASSERT(!zero_or_one(spanPtT->fT)); |
+ span->detach(); |
+ SkOpPtT* remainder = spanPtT->next(); |
+ ptT()->insert(spanPtT); |
+ while (remainder != spanPtT) { |
+ SkOpPtT* next = remainder->next(); |
+ SkOpPtT* compare = spanPtT->next(); |
+ while (compare != spanPtT) { |
+ SkOpPtT* nextC = compare->next(); |
+ if (nextC->span() == remainder->span() && nextC->fT == remainder->fT) { |
+ goto tryNextRemainder; |
+ } |
+ compare = nextC; |
+ } |
+ spanPtT->insert(remainder); |
+tryNextRemainder: |
+ remainder = next; |
+ } |
+} |
+ |
+void SkOpSpanBase::mergeBaseAttributes(SkOpSpanBase* span) { |
+ SkASSERT(!span->fChased); |
+ SkASSERT(!span->fFromAngle); |
+ if (this->upCastable() && span->upCastable()) { |
+ this->upCast()->mergeAttributes(span->upCast()); |
+ } |
+} |
+ |
+void SkOpSpan::applyCoincidence(SkOpSpan* opp) { |
+ SkASSERT(!final()); |
+ SkASSERT(0); // incomplete |
+} |
+ |
+bool SkOpSpan::containsCoincidence(const SkOpSegment* segment) const { |
+ SkASSERT(this->segment() != segment); |
+ const SkOpSpan* next = this; |
+ while ((next = next->fCoincident) != this) { |
+ if (next->segment() == segment) { |
+ return true; |
+ } |
+ } |
+ return false; |
+} |
+ |
+void SkOpSpan::detach() { |
+ SkASSERT(!final()); |
+ SkOpSpan* prev = this->prev(); |
+ SkASSERT(prev); |
+ SkOpSpanBase* next = this->next(); |
+ SkASSERT(next); |
+ prev->setNext(next); |
+ next->setPrev(prev); |
+ this->segment()->detach(this); |
+ 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 = NULL; |
+ fWindSum = fOppSum = SK_MinS32; |
+ fWindValue = 1; |
+ fOppValue = 0; |
+ fChased = fDone = false; |
+ segment->bumpCount(); |
+} |
+ |
+void SkOpSpan::mergeAttributes(SkOpSpan* span) { |
+ SkASSERT(!span->fToAngle); |
+ if (span->fCoincident) { |
+ this->insertCoincidence(span); |
+ } |
+} |
+ |
+void SkOpCoincidence::add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart, |
+ SkOpPtT* oppPtTEnd, bool flipped, SkChunkAlloc* allocator) { |
+ SkCoincidentSpans* coinRec = SkOpTAllocator<SkCoincidentSpans>::Allocate(allocator); |
+ SkOpSpanBase* coinEnd = coinPtTEnd->span(); |
+ SkOpSpanBase* oppEnd = oppPtTEnd->span(); |
+ SkOpSpan* coinStart = coinPtTStart->span()->upCast(); |
+ SkASSERT(coinStart == coinStart->starter(coinEnd)); |
+ SkOpSpan* oppStart = (flipped ? oppPtTEnd : oppPtTStart)->span()->upCast(); |
+ SkASSERT(oppStart == oppStart->starter(oppEnd)); |
+ coinStart->insertCoincidence(oppStart); |
+ coinEnd->insertCoinEnd(oppEnd); |
+ coinRec->fNext = this->fHead; |
+ coinRec->fCoinPtTStart = coinPtTStart; |
+ coinRec->fCoinPtTEnd = coinPtTEnd; |
+ coinRec->fOppPtTStart = oppPtTStart; |
+ coinRec->fOppPtTEnd = oppPtTEnd; |
+ coinRec->fFlipped = flipped; |
+ this->fHead = coinRec; |
+} |
+ |
+bool SkOpCoincidence::contains(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart, |
+ SkOpPtT* oppPtTEnd, bool flipped) { |
+ SkCoincidentSpans* coin = fHead; |
+ if (!coin) { |
+ return false; |
+ } |
+ do { |
+ if (coin->fCoinPtTStart == coinPtTStart && coin->fCoinPtTEnd == coinPtTEnd |
+ && coin->fOppPtTStart == oppPtTStart && coin->fOppPtTEnd == oppPtTEnd |
+ && coin->fFlipped == flipped) { |
+ return true; |
+ } |
+ } while ((coin = coin->fNext)); |
+ return false; |
+} |
+ |
+// walk span sets in parallel, moving winding from one to the other |
+void SkOpCoincidence::apply() { |
+ SkCoincidentSpans* coin = fHead; |
+ if (!coin) { |
+ return; |
+ } |
+ do { |
+ SkOpSpanBase* end = coin->fCoinPtTEnd->span(); |
+ SkOpSpan* start = coin->fCoinPtTStart->span()->upCast(); |
+ SkASSERT(start == start->starter(end)); |
+ bool flipped = coin->fFlipped; |
+ SkOpSpanBase* oEnd = (flipped ? coin->fOppPtTStart : coin->fOppPtTEnd)->span(); |
+ SkOpSpan* oStart = (flipped ? coin->fOppPtTEnd : coin->fOppPtTStart)->span()->upCast(); |
+ SkASSERT(oStart == oStart->starter(oEnd)); |
+ SkOpSegment* segment = start->segment(); |
+ SkOpSegment* oSegment = oStart->segment(); |
+ bool operandSwap = segment->operand() != oSegment->operand(); |
+ if (flipped) { |
+ do { |
+ SkOpSpanBase* oNext = oStart->next(); |
+ if (oNext == oEnd) { |
+ break; |
+ } |
+ oStart = oNext->upCast(); |
+ } while (true); |
+ } |
+ bool isXor = segment->isXor(); |
+ bool oppXor = oSegment->isXor(); |
+ do { |
+ int windValue = start->windValue(); |
+ int oWindValue = oStart->windValue(); |
+ int oppValue = start->oppValue(); |
+ int oOppValue = oStart->oppValue(); |
+ // winding values are added or subtracted depending on direction and wind type |
+ // same or opposite values are summed depending on the operand value |
+ if (windValue >= oWindValue) { |
+ if (operandSwap) { |
+ SkTSwap(oWindValue, oOppValue); |
+ } |
+ if (flipped) { |
+ windValue -= oWindValue; |
+ oppValue -= oOppValue; |
+ } else { |
+ windValue += oWindValue; |
+ oppValue += oOppValue; |
+ } |
+ if (isXor) { |
+ windValue &= 1; |
+ } |
+ if (oppXor) { |
+ oppValue &= 1; |
+ } |
+ oWindValue = oOppValue = 0; |
+ } else { |
+ if (operandSwap) { |
+ SkTSwap(windValue, oppValue); |
+ } |
+ if (flipped) { |
+ oWindValue -= windValue; |
+ oOppValue -= oppValue; |
+ } else { |
+ oWindValue += windValue; |
+ oOppValue += oppValue; |
+ } |
+ if (isXor) { |
+ oOppValue &= 1; |
+ } |
+ if (oppXor) { |
+ oWindValue &= 1; |
+ } |
+ windValue = oppValue = 0; |
+ } |
+ start->setWindValue(windValue); |
+ start->setOppValue(oppValue); |
+ oStart->setWindValue(oWindValue); |
+ oStart->setOppValue(oOppValue); |
+ if (!windValue && !oppValue) { |
+ segment->markDone(start); |
+ } |
+ if (!oWindValue && !oOppValue) { |
+ oSegment->markDone(oStart); |
+ } |
+ SkOpSpanBase* next = start->next(); |
+ SkOpSpanBase* oNext = flipped ? oStart->prev() : oStart->next(); |
+ if (next == end) { |
+ break; |
+ } |
+ start = next->upCast(); |
+ oStart = oNext->upCast(); |
+ } while (true); |
+ } while ((coin = coin->fNext)); |
+} |
+ |
+void SkOpCoincidence::mark() { |
+ SkCoincidentSpans* coin = fHead; |
+ if (!coin) { |
+ return; |
+ } |
+ do { |
+ SkOpSpanBase* end = coin->fCoinPtTEnd->span(); |
+ SkOpSpanBase* oldEnd = end; |
+ SkOpSpan* start = coin->fCoinPtTStart->span()->starter(&end); |
+ SkOpSpanBase* oEnd = coin->fOppPtTEnd->span(); |
+ SkOpSpanBase* oOldEnd = oEnd; |
+ SkOpSpanBase* oStart = coin->fOppPtTStart->span()->starter(&oEnd); |
+ bool flipped = (end == oldEnd) != (oEnd == oOldEnd); |
+ if (flipped) { |
+ SkTSwap(oStart, oEnd); |
+ } |
+ SkOpSpanBase* next = start; |
+ SkOpSpanBase* oNext = oStart; |
+ do { |
+ next = next->upCast()->next(); |
+ oNext = flipped ? oNext->prev() : oNext->upCast()->next(); |
+ if (next == end) { |
+ SkASSERT(oNext == oEnd); |
+ break; |
+ } |
+ if (!next->containsCoinEnd(oNext)) { |
+ next->insertCoinEnd(oNext); |
+ } |
+ SkOpSpan* nextSpan = next->upCast(); |
+ SkOpSpan* oNextSpan = oNext->upCast(); |
+ if (!nextSpan->containsCoincidence(oNextSpan)) { |
+ nextSpan->insertCoincidence(oNextSpan); |
+ } |
+ } while (true); |
+ } while ((coin = coin->fNext)); |
+} |