/* * 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; } const SkOpPtT* SkOpPtT::active() const { if (!fDeleted) { return this; } const SkOpPtT* ptT = this; const SkOpPtT* stopPtT = ptT; while ((ptT = ptT->next()) != stopPtT) { if (ptT->fSpan == fSpan && !ptT->fDeleted) { return ptT; } } return nullptr; // should never return deleted; caller must abort } bool SkOpPtT::contains(const SkOpPtT* check) const { SkOPASSERT(this != check); const SkOpPtT* ptT = this; const SkOpPtT* stopPtT = ptT; while ((ptT = ptT->next()) != stopPtT) { if (ptT == check) { return true; } } return false; } 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->fPt == pt && ptT->segment() == segment) { return true; } } return false; } 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; } const SkOpPtT* SkOpPtT::contains(const SkOpSegment* check) const { SkASSERT(this->segment() != check); const SkOpPtT* ptT = this; const SkOpPtT* stopPtT = ptT; while ((ptT = ptT->next()) != stopPtT) { if (ptT->segment() == check && !ptT->deleted()) { return ptT; } } return nullptr; } 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 && !ptT->deleted()) { return ptT; } ptT = ptT->fNext; } while (stopPtT != ptT); // SkASSERT(0); return nullptr; } SkOpGlobalState* SkOpPtT::globalState() const { return contour()->globalState(); } void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplicate) { fT = t; fPt = pt; fSpan = span; fNext = this; fDuplicatePt = duplicate; fDeleted = false; fCoincident = false; SkDEBUGCODE(fID = span->globalState()->nextPtTID()); } 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(); } 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; while ((next = next->fNext) != this) { result = next; } SkASSERT(result->fNext == this); return result; } const SkOpSegment* SkOpPtT::segment() const { return span()->segment(); } SkOpSegment* SkOpPtT::segment() { return span()->segment(); } void SkOpPtT::setDeleted() { SkASSERT(this->span()->debugDeleted() || this->span()->ptT() != this); SkOPASSERT(!fDeleted); fDeleted = true; } bool SkOpSpanBase::addOpp(SkOpSpanBase* opp) { SkOpPtT* oppPrev = this->ptT()->oppPrev(opp->ptT()); if (!oppPrev) { return true; } FAIL_IF(!this->mergeMatches(opp)); this->ptT()->addOpp(opp->ptT(), oppPrev); this->checkForCollapsedCoincidence(); return true; } SkOpSpanBase::Collapsed SkOpSpanBase::collapsed(double s, double e) const { const SkOpPtT* start = &fPtT; const SkOpPtT* startNext = nullptr; const SkOpPtT* walk = start; double min = walk->fT; double max = min; const SkOpSegment* segment = this->segment(); int safetyNet = 100000; while ((walk = walk->next()) != start) { if (!--safetyNet) { return Collapsed::kError; } if (walk == startNext) { return Collapsed::kError; } if (walk->segment() != segment) { continue; } min = SkTMin(min, walk->fT); max = SkTMax(max, walk->fT); if (between(min, s, max) && between(min, e, max)) { return Collapsed::kYes; } startNext = start->next(); } return Collapsed::kNo; } bool SkOpSpanBase::contains(const SkOpSpanBase* span) const { const SkOpPtT* start = &fPtT; const SkOpPtT* check = &span->fPtT; SkOPASSERT(start != check); const SkOpPtT* walk = start; while ((walk = walk->next()) != start) { if (walk == check) { return true; } } return false; } const SkOpPtT* SkOpSpanBase::contains(const SkOpSegment* segment) const { const SkOpPtT* start = &fPtT; const SkOpPtT* walk = start; while ((walk = walk->next()) != start) { if (walk->deleted()) { continue; } if (walk->segment() == segment && walk->span()->ptT() == walk) { return walk; } } return nullptr; } 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(); } SkOpGlobalState* SkOpSpanBase::globalState() const { return contour()->globalState(); } void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) { fSegment = segment; fPtT.init(this, t, pt, false); fCoinEnd = this; fFromAngle = nullptr; fPrev = prev; fSpanAdds = 0; fAligned = true; fChased = false; SkDEBUGCODE(fCount = 1); SkDEBUGCODE(fID = globalState()->nextSpanID()); SkDEBUGCODE(fDebugDeleted = false); } // 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->release(this->ptT()); if (this->contains(span)) { SkOPASSERT(0); // check to see if this ever happens -- should have been found earlier return; // merge is already in the ptT loop } SkOpPtT* remainder = spanPtT->next(); this->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; } fSpanAdds += span->fSpanAdds; } // 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); coins->releaseDeleted(); } // please keep in sync with debugMergeMatches() // Look to see if pt-t linked list contains same segment more than once // if so, and if each pt-t is directly pointed to by spans in that segment, // merge them // keep the points, but remove spans so that the segment doesn't have 2 or more // spans pointing to the same pt-t loop at different loop elements bool SkOpSpanBase::mergeMatches(SkOpSpanBase* opp) { SkOpPtT* test = &fPtT; SkOpPtT* testNext; const SkOpPtT* stop = test; int safetyHatch = 1000000; do { if (!--safetyHatch) { return false; } testNext = test->next(); if (test->deleted()) { continue; } SkOpSpanBase* testBase = test->span(); SkASSERT(testBase->ptT() == test); SkOpSegment* segment = test->segment(); if (segment->done()) { continue; } SkOpPtT* inner = opp->ptT(); const SkOpPtT* innerStop = inner; do { if (inner->segment() != segment) { continue; } if (inner->deleted()) { continue; } SkOpSpanBase* innerBase = inner->span(); SkASSERT(innerBase->ptT() == inner); // when the intersection is first detected, the span base is marked if there are // more than one point in the intersection. if (!zero_or_one(inner->fT)) { innerBase->upCast()->release(test); } else { SkOPASSERT(inner->fT != test->fT); if (!zero_or_one(test->fT)) { testBase->upCast()->release(inner); } else { segment->markAllDone(); // mark segment as collapsed SkDEBUGCODE(testBase->debugSetDeleted()); test->setDeleted(); SkDEBUGCODE(innerBase->debugSetDeleted()); inner->setDeleted(); } } #ifdef SK_DEBUG // assert if another undeleted entry points to segment const SkOpPtT* debugInner = inner; while ((debugInner = debugInner->next()) != innerStop) { if (debugInner->segment() != segment) { continue; } if (debugInner->deleted()) { continue; } SkOPASSERT(0); } #endif break; } while ((inner = inner->next()) != innerStop); } while ((test = testNext) != stop); this->checkForCollapsedCoincidence(); return true; } int SkOpSpan::computeWindSum() { SkOpGlobalState* globals = this->globalState(); SkOpContour* contourHead = globals->contourHead(); int windTry = 0; while (!this->sortableTop(contourHead) && ++windTry < SkOpGlobalState::kMaxWindingTries) { } return this->windSum(); } bool SkOpSpan::containsCoincidence(const SkOpSegment* segment) const { SkASSERT(this->segment() != segment); const SkOpSpan* next = fCoincident; do { if (next->segment() == segment) { return true; } } while ((next = next->fCoincident) != this); return false; } 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, bool ordered) { if (this->containsCoincidence(segment)) { return true; } SkOpPtT* next = &fPtT; while ((next = next->next()) != &fPtT) { if (next->segment() == segment) { SkOpSpan* span; SkOpSpanBase* base = next->span(); if (!ordered) { const SkOpPtT* spanEndPtT = fNext->contains(segment); FAIL_IF(!spanEndPtT); const SkOpSpanBase* spanEnd = spanEndPtT->span(); const SkOpPtT* start = base->ptT()->starter(spanEnd->ptT()); FAIL_IF(!start->span()->upCastable()); span = const_cast(start->span()->upCast()); } else if (flipped) { span = base->prev(); FAIL_IF(!span); } else { FAIL_IF(!base->upCastable()); span = base->upCast(); } 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(fDebugDeleted = true); SkOPASSERT(kept->span() != this); SkASSERT(!final()); SkOpSpan* prev = this->prev(); SkASSERT(prev); SkOpSpanBase* next = this->next(); SkASSERT(next); prev->setNext(next); next->setPrev(prev); this->segment()->release(this); SkOpCoincidence* coincidence = this->globalState()->coincidence(); if (coincidence) { coincidence->fixUp(this->ptT(), kept); } this->ptT()->setDeleted(); 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) { SkASSERT(!final()); if (fOppSum != SK_MinS32 && fOppSum != oppSum) { this->globalState()->setWindingFailed(); return; } SkASSERT(!DEBUG_LIMIT_WIND_SUM || SkTAbs(oppSum) <= DEBUG_LIMIT_WIND_SUM); fOppSum = oppSum; } void SkOpSpan::setWindSum(int windSum) { SkASSERT(!final()); if (fWindSum != SK_MinS32 && fWindSum != windSum) { this->globalState()->setWindingFailed(); return; } SkASSERT(!DEBUG_LIMIT_WIND_SUM || SkTAbs(windSum) <= DEBUG_LIMIT_WIND_SUM); fWindSum = windSum; }