diff options
author | caryclark <caryclark@google.com> | 2015-07-06 11:38:33 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-07-06 11:38:33 -0700 |
commit | 27c8eb8ffd7e221693d840c2b9279d53fe6f03d4 (patch) | |
tree | 67fbbc378aced5c5de9ec9611021db6ef7659c3c /src/pathops | |
parent | 334e588d9ed5390efb82beb37329b56a380f1d0e (diff) |
When three or more edges are coincident, the logic needs
to compute the overlapping ranges and combine the winding
into a single destination.
This computes coincidence more rigorously, fixing the
edge cases exposed by this bug.
Also, add the ability to debug and dump pathop structures
from the coincident context.
TBR=reed@google.com
BUG=skia:3651
Review URL: https://codereview.chromium.org/1182493015
Diffstat (limited to 'src/pathops')
-rwxr-xr-x | src/pathops/SkOpCoincidence.cpp | 277 | ||||
-rw-r--r-- | src/pathops/SkOpCoincidence.h | 54 | ||||
-rw-r--r-- | src/pathops/SkOpContour.h | 23 | ||||
-rw-r--r-- | src/pathops/SkOpSegment.cpp | 154 | ||||
-rw-r--r-- | src/pathops/SkOpSegment.h | 13 | ||||
-rwxr-xr-x | src/pathops/SkOpSpan.cpp | 54 | ||||
-rw-r--r-- | src/pathops/SkOpSpan.h | 29 | ||||
-rw-r--r-- | src/pathops/SkPathOpsCommon.cpp | 42 | ||||
-rw-r--r-- | src/pathops/SkPathOpsDebug.cpp | 12 | ||||
-rw-r--r-- | src/pathops/SkPathOpsDebug.h | 12 | ||||
-rw-r--r-- | src/pathops/SkPathOpsRect.h | 3 | ||||
-rw-r--r-- | src/pathops/SkPathOpsTypes.cpp | 19 | ||||
-rw-r--r-- | src/pathops/SkPathOpsTypes.h | 15 |
13 files changed, 619 insertions, 88 deletions
diff --git a/src/pathops/SkOpCoincidence.cpp b/src/pathops/SkOpCoincidence.cpp index eb0ccc1737..58ff7f3ab9 100755 --- a/src/pathops/SkOpCoincidence.cpp +++ b/src/pathops/SkOpCoincidence.cpp @@ -65,6 +65,90 @@ static void t_range(const SkOpPtT* overS, const SkOpPtT* overE, double tStart, d *coinTe = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * eRatio; } +void SkOpCoincidence::addExpanded(SkChunkAlloc* allocator + PATH_OPS_DEBUG_VALIDATE_PARAMS(SkOpGlobalState* globalState)) { +#if DEBUG_VALIDATE + globalState->setPhase(SkOpGlobalState::kIntersecting); +#endif + // for each coincident pair, match the spans + // if the spans don't match, add the mssing pt to the segment and loop it in the opposite span + SkCoincidentSpans* coin = this->fHead; + SkASSERT(coin); + do { + SkOpPtT* startPtT = coin->fCoinPtTStart; + SkOpPtT* oStartPtT = coin->fOppPtTStart; + SkASSERT(startPtT->contains(oStartPtT)); + SkASSERT(coin->fCoinPtTEnd->contains(coin->fOppPtTEnd)); + SkOpSpanBase* start = startPtT->span(); + SkOpSpanBase* oStart = oStartPtT->span(); + const SkOpSpanBase* end = coin->fCoinPtTEnd->span(); + const SkOpSpanBase* oEnd = coin->fOppPtTEnd->span(); + SkOpSpanBase* test = start->upCast()->next(); + SkOpSpanBase* oTest = coin->fFlipped ? oStart->prev() : oStart->upCast()->next(); + while (test != end || oTest != oEnd) { + if (!test->ptT()->contains(oTest->ptT())) { + // use t ranges to guess which one is missing + double startRange = coin->fCoinPtTEnd->fT - startPtT->fT; + double startPart = (test->t() - startPtT->fT) / startRange; + double oStartRange = coin->fOppPtTEnd->fT - oStartPtT->fT; + double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange; + SkASSERT(startPart != oStartPart); + SkOpPtT* newPt; + if (startPart < oStartPart) { + double newT = oStartPtT->fT + oStartRange * startPart; + newPt = oStart->segment()->addT(newT, SkOpSegment::kAllowAlias, allocator); + newPt->fPt = test->pt(); + test->ptT()->addOpp(newPt); + } else { + double newT = startPtT->fT + startRange * oStartPart; + newPt = start->segment()->addT(newT, SkOpSegment::kAllowAlias, allocator); + newPt->fPt = oTest->pt(); + oTest->ptT()->addOpp(newPt); + } + // start over + test = start; + oTest = oStart; + } + if (test != end) { + test = test->upCast()->next(); + } + if (oStart != oEnd) { + oTest = coin->fFlipped ? oTest->prev() : oTest->upCast()->next(); + } + } + } while ((coin = coin->fNext)); +#if DEBUG_VALIDATE + globalState->setPhase(SkOpGlobalState::kWalking); +#endif +} + +void SkOpCoincidence::addIfMissing(const SkCoincidentSpans* outer, SkOpPtT* over1s, + SkOpPtT* over1e, SkChunkAlloc* allocator) { + SkCoincidentSpans* check = this->fTop; + do { + if (check->fCoinPtTStart->span() == over1s->span() + && check->fOppPtTStart->span() == outer->fOppPtTStart->span()) { + SkASSERT(check->fCoinPtTEnd->span() == over1e->span() + || !fDebugState->debugRunFail()); + SkASSERT(check->fOppPtTEnd->span() == outer->fOppPtTEnd->span() + || !fDebugState->debugRunFail()); + return; + } + if (check->fCoinPtTStart->span() == outer->fCoinPtTStart->span() + && check->fOppPtTStart->span() == over1s->span()) { + SkASSERT(check->fCoinPtTEnd->span() == outer->fCoinPtTEnd->span() + || !fDebugState->debugRunFail()); + SkASSERT(check->fOppPtTEnd->span() == over1e->span() + || !fDebugState->debugRunFail()); + return; + } + } while ((check = check->fNext)); + this->add(outer->fCoinPtTStart, outer->fCoinPtTEnd, over1s, over1e, allocator); +#if 0 + // FIXME: up to four flavors could be added -- do we need only one? +#endif +} + bool SkOpCoincidence::addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e, const SkOpPtT* over2s, const SkOpPtT* over2e, double tStart, double tEnd, SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, @@ -75,7 +159,7 @@ bool SkOpCoincidence::addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e, SkOpSegment* coinSeg = coinPtTStart->segment(); SkOpSegment* oppSeg = oppPtTStart->segment(); SkASSERT(coinSeg != oppSeg); - SkCoincidentSpans* check = this->fHead; + SkCoincidentSpans* check = this->fTop; do { const SkOpSegment* checkCoinSeg = check->fCoinPtTStart->segment(); if (checkCoinSeg != coinSeg && checkCoinSeg != oppSeg) { @@ -124,52 +208,136 @@ bool SkOpCoincidence::addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e, return true; } +/* detects overlaps of different coincident runs on same segment */ +/* does not detect overlaps for pairs without any segments in common */ bool SkOpCoincidence::addMissing(SkChunkAlloc* allocator) { - SkCoincidentSpans* outer = this->fHead; + SkCoincidentSpans* outer = fHead; if (!outer) { return true; } + bool result; + fTop = outer; + fHead = NULL; do { + // addifmissing can modify the list that this is walking + // maybe save head so that walker can iterate over old data unperturbed + // and addifmissing can add to head freely then add saved head in the end + const SkOpSegment* outerCoin = outer->fCoinPtTStart->segment(); + SkASSERT(outerCoin == outer->fCoinPtTEnd->segment()); + const SkOpSegment* outerOpp = outer->fOppPtTStart->segment(); + SkASSERT(outerOpp == outer->fOppPtTEnd->segment()); SkCoincidentSpans* inner = outer; while ((inner = inner->fNext)) { double overS, overE; - if (this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd, + const SkOpSegment* innerCoin = inner->fCoinPtTStart->segment(); + SkASSERT(innerCoin == inner->fCoinPtTEnd->segment()); + const SkOpSegment* innerOpp = inner->fOppPtTStart->segment(); + SkASSERT(innerOpp == inner->fOppPtTEnd->segment()); + if (outerCoin == innerCoin + && this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd, inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) { if (!this->addIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd, inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE, outer->fOppPtTStart, outer->fOppPtTEnd, inner->fOppPtTStart, inner->fOppPtTEnd, allocator)) { - return false; + result = false; + goto returnResult; } - } else if (this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd, + } else if (outerCoin == innerOpp + && this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd, inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) { if (!this->addIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd, inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE, outer->fOppPtTStart, outer->fOppPtTEnd, inner->fCoinPtTStart, inner->fCoinPtTEnd, allocator)) { - return false; + result = false; + goto returnResult; } - } else if (this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd, + } else if (outerOpp == innerCoin + && this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd, inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) { if (!this->addIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd, inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE, outer->fCoinPtTStart, outer->fCoinPtTEnd, inner->fOppPtTStart, inner->fOppPtTEnd, allocator)) { - return false; + result = false; + goto returnResult; } - } else if (this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd, + } else if (outerOpp == innerOpp + && this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd, inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) { if (!this->addIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd, inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE, outer->fCoinPtTStart, outer->fCoinPtTEnd, inner->fCoinPtTStart, inner->fCoinPtTEnd, allocator)) { - return false; + result = false; + goto returnResult; + } + } else if (outerCoin != innerCoin) { + // check to see if outer span overlaps the inner span + // look for inner segment in pt-t list + // if present, and if t values are in coincident range + // add two pairs of new coincidence + SkOpPtT* testS = outer->fCoinPtTStart->contains(innerCoin); + SkOpPtT* testE = outer->fCoinPtTEnd->contains(innerCoin); + if (testS && testS->fT >= inner->fCoinPtTStart->fT + && testE && testE->fT <= inner->fCoinPtTEnd->fT + && this->testForCoincidence(outer, testS, testE)) { + this->addIfMissing(outer, testS, testE, allocator); + } else { + testS = inner->fCoinPtTStart->contains(outerCoin); + testE = inner->fCoinPtTEnd->contains(outerCoin); + if (testS && testS->fT >= outer->fCoinPtTStart->fT + && testE && testE->fT <= outer->fCoinPtTEnd->fT + && this->testForCoincidence(inner, testS, testE)) { + this->addIfMissing(inner, testS, testE, allocator); + } } } } - } while ((outer = outer->fNext)); - return true; + result = true; +returnResult: + SkCoincidentSpans** headPtr = &fHead; + while (*headPtr) { + SkCoincidentSpans** headNext = &(*headPtr)->fNext; + if (*headNext) { + break; + } + headPtr = headNext; + } + *headPtr = fTop; + return result; +} + +void SkOpCoincidence::addOverlap(SkOpSegment* seg1, SkOpSegment* seg1o, SkOpSegment* seg2, + SkOpSegment* seg2o, SkOpPtT* overS, SkOpPtT* overE, SkChunkAlloc* allocator) { + SkOpPtT* s1 = overS->find(seg1); + SkOpPtT* e1 = overE->find(seg1); + if (!s1->starter(e1)->span()->upCast()->windValue()) { + s1 = overS->find(seg1o); + e1 = overE->find(seg1o); + if (!s1->starter(e1)->span()->upCast()->windValue()) { + return; + } + } + SkOpPtT* s2 = overS->find(seg2); + SkOpPtT* e2 = overE->find(seg2); + if (!s2->starter(e2)->span()->upCast()->windValue()) { + s2 = overS->find(seg2o); + e2 = overE->find(seg2o); + if (!s2->starter(e2)->span()->upCast()->windValue()) { + return; + } + } + if (s1->segment() == s2->segment()) { + return; + } + if (s1->fT > e1->fT) { + SkTSwap(s1, e1); + SkTSwap(s2, e2); + } + this->add(s1, e1, s2, e2, allocator); } bool SkOpCoincidence::contains(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart, @@ -316,11 +484,12 @@ void SkOpCoincidence::detach(SkCoincidentSpans* remove) { SkASSERT(coin); } -void SkOpCoincidence::expand() { +bool SkOpCoincidence::expand() { SkCoincidentSpans* coin = fHead; if (!coin) { - return; + return false; } + bool expanded = false; do { SkOpSpan* start = coin->fCoinPtTStart->span()->upCast(); SkOpSpanBase* end = coin->fCoinPtTEnd->span(); @@ -333,6 +502,7 @@ void SkOpCoincidence::expand() { if (segment->isClose(midT, oppSegment)) { coin->fCoinPtTStart = prev->ptT(); coin->fOppPtTStart = oppPtT; + expanded = true; } } SkOpSpanBase* next = end->final() ? NULL : end->upCast()->next(); @@ -341,7 +511,61 @@ void SkOpCoincidence::expand() { if (segment->isClose(midT, oppSegment)) { coin->fCoinPtTEnd = next->ptT(); coin->fOppPtTEnd = oppPtT; + expanded = true; + } + } + } while ((coin = coin->fNext)); + return expanded; +} + +void SkOpCoincidence::findOverlaps(SkOpCoincidence* overlaps, SkChunkAlloc* allocator) const { + overlaps->fHead = overlaps->fTop = NULL; + SkDEBUGCODE_(overlaps->debugSetGlobalState(fDebugState)); + SkCoincidentSpans* outer = fHead; + while (outer) { + SkOpSegment* outerCoin = outer->fCoinPtTStart->segment(); + SkOpSegment* outerOpp = outer->fOppPtTStart->segment(); + SkCoincidentSpans* inner = outer; + while ((inner = inner->fNext)) { + SkOpSegment* innerCoin = inner->fCoinPtTStart->segment(); + if (outerCoin == innerCoin) { + continue; // both winners are the same segment, so there's no additional overlap } + SkOpSegment* innerOpp = inner->fOppPtTStart->segment(); + SkOpPtT* overlapS, * overlapE; + if ((outerOpp == innerCoin && SkOpPtT::Overlaps(outer->fOppPtTStart, outer->fOppPtTEnd, + inner->fCoinPtTStart, inner->fCoinPtTEnd, &overlapS, &overlapE)) + || (outerCoin == innerOpp && SkOpPtT::Overlaps(outer->fCoinPtTStart, + outer->fCoinPtTEnd, inner->fOppPtTStart, inner->fOppPtTEnd, + &overlapS, &overlapE)) + || (outerOpp == innerOpp && SkOpPtT::Overlaps(outer->fOppPtTStart, + outer->fOppPtTEnd, inner->fOppPtTStart, inner->fOppPtTEnd, + &overlapS, &overlapE))) { + overlaps->addOverlap(outerCoin, outerOpp, innerCoin, innerOpp, + overlapS, overlapE, allocator); + } + } + outer = outer->fNext; + } +} + +void SkOpCoincidence::fixAligned() { + SkCoincidentSpans* coin = fHead; + if (!coin) { + return; + } + do { + if (coin->fCoinPtTStart->deleted()) { + coin->fCoinPtTStart = coin->fCoinPtTStart->doppelganger(); + } + if (coin->fCoinPtTEnd->deleted()) { + coin->fCoinPtTEnd = coin->fCoinPtTEnd->doppelganger(); + } + if (coin->fOppPtTStart->deleted()) { + coin->fOppPtTStart = coin->fOppPtTStart->doppelganger(); + } + if (coin->fOppPtTEnd->deleted()) { + coin->fOppPtTEnd = coin->fOppPtTEnd->doppelganger(); } } while ((coin = coin->fNext)); } @@ -354,31 +578,36 @@ void SkOpCoincidence::fixUp(SkOpPtT* deleted, SkOpPtT* kept) { do { if (coin->fCoinPtTStart == deleted) { if (coin->fCoinPtTEnd->span() == kept->span()) { - return this->detach(coin); + this->detach(coin); + continue; } coin->fCoinPtTStart = kept; } if (coin->fCoinPtTEnd == deleted) { if (coin->fCoinPtTStart->span() == kept->span()) { - return this->detach(coin); + this->detach(coin); + continue; } coin->fCoinPtTEnd = kept; } if (coin->fOppPtTStart == deleted) { if (coin->fOppPtTEnd->span() == kept->span()) { - return this->detach(coin); + this->detach(coin); + continue; } coin->fOppPtTStart = kept; } if (coin->fOppPtTEnd == deleted) { if (coin->fOppPtTStart->span() == kept->span()) { - return this->detach(coin); + this->detach(coin); + continue; } coin->fOppPtTEnd = kept; } } while ((coin = coin->fNext)); } +/* this sets up the coincidence links in the segments when the coincidence crosses multiple spans */ void SkOpCoincidence::mark() { SkCoincidentSpans* coin = fHead; if (!coin) { @@ -397,8 +626,6 @@ void SkOpCoincidence::mark() { } SkOpSpanBase* next = start; SkOpSpanBase* oNext = oStart; - // check to see if coincident span could be bigger - do { next = next->upCast()->next(); oNext = flipped ? oNext->prev() : oNext->upCast()->next(); @@ -419,10 +646,14 @@ void SkOpCoincidence::mark() { bool SkOpCoincidence::overlap(const SkOpPtT* coin1s, const SkOpPtT* coin1e, const SkOpPtT* coin2s, const SkOpPtT* coin2e, double* overS, double* overE) const { - if (coin1s->segment() != coin2s->segment()) { - return false; - } + SkASSERT(coin1s->segment() == coin2s->segment()); *overS = SkTMax(SkTMin(coin1s->fT, coin1e->fT), SkTMin(coin2s->fT, coin2e->fT)); *overE = SkTMin(SkTMax(coin1s->fT, coin1e->fT), SkTMax(coin2s->fT, coin2e->fT)); return *overS < *overE; } + +bool SkOpCoincidence::testForCoincidence(const SkCoincidentSpans* outer, SkOpPtT* testS, + SkOpPtT* testE) const { + return testS->segment()->testForCoincidence(testS, testE, testS->span(), + testE->span(), outer->fCoinPtTStart->segment(), 120000); // FIXME: replace with tuned +} diff --git a/src/pathops/SkOpCoincidence.h b/src/pathops/SkOpCoincidence.h index ce57999b1d..83ba70a0f9 100644 --- a/src/pathops/SkOpCoincidence.h +++ b/src/pathops/SkOpCoincidence.h @@ -9,6 +9,7 @@ #include "SkOpTAllocator.h" #include "SkOpSpan.h" +#include "SkPathOpsTypes.h" class SkOpPtT; @@ -26,36 +27,81 @@ struct SkCoincidentSpans { class SkOpCoincidence { public: SkOpCoincidence() - : fHead(NULL) { + : fHead(NULL) + , fTop(NULL) + SkDEBUGPARAMS(fDebugState(NULL)) + { } void add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart, SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator); + void addExpanded(SkChunkAlloc* allocator PATH_OPS_DEBUG_VALIDATE_PARAMS(SkOpGlobalState* )); bool addMissing(SkChunkAlloc* allocator); void addMissing(SkCoincidentSpans* check, SkChunkAlloc* allocator); bool apply(); bool contains(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart, SkOpPtT* oppPtTEnd, bool flipped); + + const SkOpAngle* debugAngle(int id) const { + return SkDEBUGRELEASE(fDebugState->debugAngle(id), NULL); + } + + SkOpContour* debugContour(int id) { + return SkDEBUGRELEASE(fDebugState->debugContour(id), NULL); + } + + const SkOpPtT* debugPtT(int id) const { + return SkDEBUGRELEASE(fDebugState->debugPtT(id), NULL); + } + + const SkOpSegment* debugSegment(int id) const { + return SkDEBUGRELEASE(fDebugState->debugSegment(id), NULL); + } + + void debugSetGlobalState(SkOpGlobalState* debugState) { + SkDEBUGCODE(fDebugState = debugState); + } + void debugShowCoincidence() const; + + const SkOpSpanBase* debugSpan(int id) const { + return SkDEBUGRELEASE(fDebugState->debugSpan(id), NULL); + } + void detach(SkCoincidentSpans* ); void dump() const; - void expand(); + bool expand(); bool extend(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart, SkOpPtT* oppPtTEnd); + void findOverlaps(SkOpCoincidence* , SkChunkAlloc* allocator) const; + void fixAligned(); void fixUp(SkOpPtT* deleted, SkOpPtT* kept); + + bool isEmpty() const { + return !fHead; + } + void mark(); private: + void addIfMissing(const SkCoincidentSpans* outer, SkOpPtT* over1s, SkOpPtT* over1e, + SkChunkAlloc* ); bool addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e, - const SkOpPtT* over2s, const SkOpPtT* over2e, double tStart, double tEnd, + const SkOpPtT* over2s, const SkOpPtT* over2e, + double tStart, double tEnd, SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd, - SkChunkAlloc* allocator); + SkChunkAlloc* ); + void addOverlap(SkOpSegment* seg1, SkOpSegment* seg1o, SkOpSegment* seg2, SkOpSegment* seg2o, + SkOpPtT* overS, SkOpPtT* overE, SkChunkAlloc* ); bool overlap(const SkOpPtT* coinStart1, const SkOpPtT* coinEnd1, const SkOpPtT* coinStart2, const SkOpPtT* coinEnd2, double* overS, double* overE) const; + bool testForCoincidence(const SkCoincidentSpans* outer, SkOpPtT* testS, SkOpPtT* testE) const; SkCoincidentSpans* fHead; + SkCoincidentSpans* fTop; + SkDEBUGCODE_(SkOpGlobalState* fDebugState); }; #endif diff --git a/src/pathops/SkOpContour.h b/src/pathops/SkOpContour.h index f8143cf555..1225416eaa 100644 --- a/src/pathops/SkOpContour.h +++ b/src/pathops/SkOpContour.h @@ -34,6 +34,14 @@ public: : fBounds.fTop < rh.fBounds.fTop; } + void addAlignIntersections(SkOpContourHead* contourList, SkChunkAlloc* allocator) { + SkASSERT(fCount > 0); + SkOpSegment* segment = &fHead; + do { + segment->addAlignIntersections(contourList, allocator); + } while ((segment = segment->next())); + } + void addConic(SkPoint pts[3], SkScalar weight, SkChunkAlloc* allocator) { appendSegment(allocator).addConic(pts, weight, this); } @@ -222,16 +230,23 @@ public: } while ((segment = segment->next())); } - void missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) { + bool missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) { SkASSERT(fCount > 0); SkOpSegment* segment = &fHead; + bool result = false; do { if (fState->angleCoincidence()) { segment->checkAngleCoin(coincidences, allocator); - } else { - segment->missingCoincidence(coincidences, allocator); + } else if (segment->missingCoincidence(coincidences, allocator)) { + result = true; + // FIXME: trying again loops forever in issue3651_6 + // The continue below is speculative -- once there's an actual case that requires it, + // add the plumbing necessary to look for another missing coincidence in the same segment + // continue; // try again in case another missing coincidence is further along } - } while ((segment = segment->next())); + segment = segment->next(); + } while (segment); + return result; } bool moveMultiples() { diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp index 462cff60cf..fd8ab65acd 100644 --- a/src/pathops/SkOpSegment.cpp +++ b/src/pathops/SkOpSegment.cpp @@ -159,6 +159,87 @@ bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sum return result; } +void SkOpSegment::addAlignIntersection(SkOpPtT& endPtT, SkPoint& oldPt, + SkOpContourHead* contourList, SkChunkAlloc* allocator) { + const SkPoint& newPt = endPtT.fPt; + if (newPt == oldPt) { + return; + } + SkPoint line[2] = { newPt, oldPt }; + SkPathOpsBounds lineBounds; + lineBounds.setBounds(line, 2); + SkDLine aLine; + aLine.set(line); + SkOpContour* current = contourList; + do { + if (!SkPathOpsBounds::Intersects(current->bounds(), lineBounds)) { + continue; + } + SkOpSegment* segment = current->first(); + do { + if (!SkPathOpsBounds::Intersects(segment->bounds(), lineBounds)) { + continue; + } + if (newPt == segment->fPts[0]) { + continue; + } + if (newPt == segment->fPts[SkPathOpsVerbToPoints(segment->fVerb)]) { + continue; + } + if (oldPt == segment->fPts[0]) { + continue; + } + if (oldPt == segment->fPts[SkPathOpsVerbToPoints(segment->fVerb)]) { + continue; + } + if (endPtT.contains(segment)) { + continue; + } + SkIntersections i; + switch (segment->fVerb) { + case SkPath::kLine_Verb: { + SkDLine bLine; + bLine.set(segment->fPts); + i.intersect(bLine, aLine); + } break; + case SkPath::kQuad_Verb: { + SkDQuad bQuad; + bQuad.set(segment->fPts); + i.intersect(bQuad, aLine); + } break; + case SkPath::kConic_Verb: { + SkDConic bConic; + bConic.set(segment->fPts, segment->fWeight); + i.intersect(bConic, aLine); + } break; + case SkPath::kCubic_Verb: { + SkDCubic bCubic; + bCubic.set(segment->fPts); + i.intersect(bCubic, aLine); + } break; + default: + SkASSERT(0); + } + if (i.used()) { + SkASSERT(i.used() == 1); + SkASSERT(!zero_or_one(i[0][0])); + SkOpSpanBase* checkSpan = fHead.next(); + while (!checkSpan->final()) { + if (checkSpan->contains(segment)) { + goto nextSegment; + } + checkSpan = checkSpan->upCast()->next(); + } + SkOpPtT* ptT = segment->addT(i[0][0], SkOpSegment::kAllowAlias, allocator); + ptT->fPt = newPt; + endPtT.addOpp(ptT); + } + nextSegment: + ; + } while ((segment = segment->next())); + } while ((current = current->next())); +} + void SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end, SkPathWriter* path, bool active) const { SkOpCurve edge; @@ -770,7 +851,7 @@ SkOpSegment* SkOpSegment::findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** n SkASSERT(start != endNear); SkASSERT((start->t() < endNear->t()) ^ (step < 0)); SkOpAngle* angle = this->spanToAngle(end, start); - if (angle->unorderable()) { + if (!angle || angle->unorderable()) { *unsortable = true; markDone(start->starter(end)); return NULL; @@ -817,6 +898,8 @@ SkOpGlobalState* SkOpSegment::globalState() const { void SkOpSegment::init(SkPoint pts[], SkScalar weight, SkOpContour* contour, SkPath::Verb verb) { fContour = contour; fNext = NULL; + fOriginal[0] = pts[0]; + fOriginal[1] = pts[SkPathOpsVerbToPoints(verb)]; fPts = pts; fWeight = weight; fVerb = verb; @@ -1113,12 +1196,12 @@ static void clear_visited(SkOpSpanBase* span) { // curve/curve intersection should now do a pretty good job of finding coincident runs so // this may be only be necessary for line/curve pairs -- so skip unless this is a line and the // the opp is not a line -void SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) { +bool SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) { if (this->verb() != SkPath::kLine_Verb) { - return; + return false; } if (this->done()) { - return; + return false; } SkOpSpan* prior = NULL; SkOpSpanBase* spanBase = &fHead; @@ -1186,34 +1269,7 @@ void SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc if (coincidences->contains(priorPtT, ptT, oppStart, oppEnd, flipped)) { goto swapBack; } - { - // average t, find mid pt - double midT = (prior->t() + spanBase->t()) / 2; - SkPoint midPt = this->ptAtT(midT); - coincident = true; - // if the mid pt is not near either end pt, project perpendicular through opp seg - if (!SkDPoint::ApproximatelyEqual(priorPtT->fPt, midPt) - && !SkDPoint::ApproximatelyEqual(ptT->fPt, midPt)) { - coincident = false; - SkIntersections i; - SkVector dxdy = (*CurveSlopeAtT[fVerb])(this->pts(), this->weight(), midT); - SkDLine ray = {{{midPt.fX, midPt.fY}, - {midPt.fX + dxdy.fY, midPt.fY - dxdy.fX}}}; - (*CurveIntersectRay[opp->verb()])(opp->pts(), opp->weight(), ray, &i); - // measure distance and see if it's small enough to denote coincidence - for (int index = 0; index < i.used(); ++index) { - SkDPoint oppPt = i.pt(index); - if (oppPt.approximatelyEqual(midPt)) { - SkVector oppDxdy = (*CurveSlopeAtT[opp->verb()])(opp->pts(), - opp->weight(), i[index][0]); - oppDxdy.normalize(); - dxdy.normalize(); - SkScalar flatness = SkScalarAbs(dxdy.cross(oppDxdy) / FLT_EPSILON); - coincident |= flatness < 5000; // FIXME: replace with tuned value - } - } - } - } + coincident = testForCoincidence(priorPtT, ptT, prior, spanBase, opp, 5000); if (coincident) { // mark coincidence if (!coincidences->extend(priorPtT, ptT, oppStart, oppEnd) @@ -1221,7 +1277,7 @@ void SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc coincidences->add(priorPtT, ptT, oppStart, oppEnd, allocator); } clear_visited(&fHead); - return; + return true; } swapBack: if (swapped) { @@ -1230,6 +1286,7 @@ void SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc } } while ((spanBase = spanBase->final() ? NULL : spanBase->upCast()->next())); clear_visited(&fHead); + return false; } // if a span has more than one intersection, merge the other segments' span as needed @@ -1607,6 +1664,37 @@ bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end, return true; } +bool SkOpSegment::testForCoincidence(const SkOpPtT* priorPtT, const SkOpPtT* ptT, + const SkOpSpanBase* prior, const SkOpSpanBase* spanBase, const SkOpSegment* opp, + SkScalar flatnessLimit) const { + // average t, find mid pt + double midT = (prior->t() + spanBase->t()) / 2; + SkPoint midPt = this->ptAtT(midT); + bool coincident = true; + // if the mid pt is not near either end pt, project perpendicular through opp seg + if (!SkDPoint::ApproximatelyEqual(priorPtT->fPt, midPt) + && !SkDPoint::ApproximatelyEqual(ptT->fPt, midPt)) { + coincident = false; + SkIntersections i; + SkVector dxdy = (*CurveSlopeAtT[fVerb])(this->pts(), this->weight(), midT); + SkDLine ray = {{{midPt.fX, midPt.fY}, {midPt.fX + dxdy.fY, midPt.fY - dxdy.fX}}}; + (*CurveIntersectRay[opp->verb()])(opp->pts(), opp->weight(), ray, &i); + // measure distance and see if it's small enough to denote coincidence + for (int index = 0; index < i.used(); ++index) { + SkDPoint oppPt = i.pt(index); + if (oppPt.approximatelyEqual(midPt)) { + SkVector oppDxdy = (*CurveSlopeAtT[opp->verb()])(opp->pts(), + opp->weight(), i[index][0]); + oppDxdy.normalize(); + dxdy.normalize(); + SkScalar flatness = SkScalarAbs(dxdy.cross(oppDxdy) / FLT_EPSILON); + coincident |= flatness < flatnessLimit; + } + } + } + return coincident; +} + void SkOpSegment::undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end) { SkOpSpan* span = this->head(); do { diff --git a/src/pathops/SkOpSegment.h b/src/pathops/SkOpSegment.h index 38de40617a..6873203b25 100644 --- a/src/pathops/SkOpSegment.h +++ b/src/pathops/SkOpSegment.h @@ -45,6 +45,13 @@ public: bool activeWinding(SkOpSpanBase* start, SkOpSpanBase* end); bool activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sumWinding); + void addAlignIntersection(SkOpPtT& endPtT, SkPoint& oldPt, + SkOpContourHead* contourList, SkChunkAlloc* allocator); + + void addAlignIntersections(SkOpContourHead* contourList, SkChunkAlloc* allocator) { + this->addAlignIntersection(*fHead.ptT(), fOriginal[0], contourList, allocator); + this->addAlignIntersection(*fTail.ptT(), fOriginal[1], contourList, allocator); + } SkOpSegment* addConic(SkPoint pts[3], SkScalar weight, SkOpContour* parent) { init(pts, weight, parent, SkPath::kConic_Verb); @@ -244,7 +251,7 @@ public: bool markWinding(SkOpSpan* , int winding); bool markWinding(SkOpSpan* , int winding, int oppWinding); bool match(const SkOpPtT* span, const SkOpSegment* parent, double t, const SkPoint& pt) const; - void missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator); + bool missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator); void moveMultiples(); void moveNearby(); @@ -346,6 +353,9 @@ public: return &fTail; } + bool testForCoincidence(const SkOpPtT* priorPtT, const SkOpPtT* ptT, const SkOpSpanBase* prior, + const SkOpSpanBase* spanBase, const SkOpSegment* opp, SkScalar flatnessLimit) const; + void undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end); int updateOppWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const; int updateOppWinding(const SkOpAngle* angle) const; @@ -386,6 +396,7 @@ private: SkOpContour* fContour; SkOpSegment* fNext; // forward-only linked list used by contour to walk the segments const SkOpSegment* fPrev; + SkPoint fOriginal[2]; // if aligned, the original unaligned points are here SkPoint* fPts; // pointer into array of points owned by edge builder that may be tweaked SkPathOpsBounds fBounds; // tight bounds SkScalar fWeight; diff --git a/src/pathops/SkOpSpan.cpp b/src/pathops/SkOpSpan.cpp index ae4771cffc..f5a52099da 100755 --- a/src/pathops/SkOpSpan.cpp +++ b/src/pathops/SkOpSpan.cpp @@ -13,10 +13,64 @@ bool SkOpPtT::alias() const { return this->span()->ptT() != this; } +bool SkOpPtT::contains(const SkOpPtT* check) const { + SkASSERT(this != check); + const SkOpPtT* ptT = this; + const SkOpPtT* stopPtT = ptT; + while ((ptT = ptT->next()) != stopPtT) { + if (ptT == check) { + return true; + } + } + return false; +} + +SkOpPtT* SkOpPtT::contains(const SkOpSegment* check) { + SkASSERT(this->segment() != check); + SkOpPtT* ptT = this; + const SkOpPtT* stopPtT = ptT; + while ((ptT = ptT->next()) != stopPtT) { + if (ptT->segment() == check) { + return ptT; + } + } + return NULL; +} + SkOpContour* SkOpPtT::contour() const { return segment()->contour(); } +SkOpPtT* SkOpPtT::doppelganger() { + SkASSERT(fDeleted); + SkOpPtT* ptT = fNext; + while (ptT->fDeleted) { + ptT = ptT->fNext; + } + const SkOpPtT* stopPtT = ptT; + do { + if (ptT->fSpan == fSpan) { + return ptT; + } + ptT = ptT->fNext; + } while (stopPtT != ptT); + SkASSERT(0); + return NULL; +} + +SkOpPtT* SkOpPtT::find(SkOpSegment* segment) { + SkOpPtT* ptT = this; + const SkOpPtT* stopPtT = ptT; + do { + if (ptT->segment() == segment) { + return ptT; + } + ptT = ptT->fNext; + } while (stopPtT != ptT); + SkASSERT(0); + return NULL; +} + SkOpGlobalState* SkOpPtT::globalState() const { return contour()->globalState(); } diff --git a/src/pathops/SkOpSpan.h b/src/pathops/SkOpSpan.h index 9b44247341..ec62246c31 100644 --- a/src/pathops/SkOpSpan.h +++ b/src/pathops/SkOpSpan.h @@ -8,6 +8,7 @@ #define SkOpSpan_DEFINED #include "SkPathOpsDebug.h" +#include "SkPathOpsTypes.h" #include "SkPoint.h" class SkChunkAlloc; @@ -47,6 +48,8 @@ public: } bool alias() const; + bool contains(const SkOpPtT* ) const; + SkOpPtT* contains(const SkOpSegment* ); SkOpContour* contour() const; int debugID() const { @@ -67,6 +70,8 @@ public: return fDeleted; } + SkOpPtT* doppelganger(); + bool duplicate() const { return fDuplicatePt; } @@ -75,6 +80,7 @@ public: void dumpAll() const; void dumpBase() const; + SkOpPtT* find(SkOpSegment* ); void init(SkOpSpanBase* , double t, const SkPoint& , bool dup); void insert(SkOpPtT* span) { @@ -92,6 +98,25 @@ public: } bool onEnd() const; + + static bool Overlaps(SkOpPtT* s1, SkOpPtT* e1, SkOpPtT* s2, SkOpPtT* e2, + SkOpPtT** sOut, SkOpPtT** eOut) { + SkOpPtT* start1 = s1->fT < e1->fT ? s1 : e1; + SkOpPtT* start2 = s2->fT < e2->fT ? s2 : e2; + *sOut = between(s1->fT, start2->fT, e1->fT) ? start2 + : between(s2->fT, start1->fT, e2->fT) ? start1 : NULL; + SkOpPtT* end1 = s1->fT < e1->fT ? e1 : s1; + SkOpPtT* end2 = s2->fT < e2->fT ? e2 : s2; + *eOut = between(s1->fT, end2->fT, e1->fT) ? end2 + : between(s2->fT, end1->fT, e2->fT) ? end1 : NULL; + if (*sOut == *eOut) { + SkASSERT(start1->fT >= end2->fT || start2->fT >= end1->fT); + return false; + } + SkASSERT(!*sOut || *sOut != *eOut); + return *sOut && *eOut; + } + SkOpPtT* prev(); SkOpPtT* remove(); void removeNext(SkOpPtT* kept); @@ -112,6 +137,10 @@ public: return fSpan; } + const SkOpPtT* starter(const SkOpPtT* end) const { + return fT < end->fT ? this : end; + } + double fT; SkPoint fPt; // cache of point value at this t protected: diff --git a/src/pathops/SkPathOpsCommon.cpp b/src/pathops/SkPathOpsCommon.cpp index 98cce15fb2..4ead297f7d 100644 --- a/src/pathops/SkPathOpsCommon.cpp +++ b/src/pathops/SkPathOpsCommon.cpp @@ -394,6 +394,13 @@ static void align(SkOpContourHead* contourList) { } while ((contour = contour->next())); } +static void addAlignIntersections(SkOpContourHead* contourList, SkChunkAlloc* allocator) { + SkOpContour* contour = contourList; + do { + contour->addAlignIntersections(contourList, allocator); + } while ((contour = contour->next())); +} + static void calcAngles(SkOpContourHead* contourList, SkChunkAlloc* allocator) { SkOpContour* contour = contourList; do { @@ -401,12 +408,14 @@ static void calcAngles(SkOpContourHead* contourList, SkChunkAlloc* allocator) { } while ((contour = contour->next())); } -static void missingCoincidence(SkOpContourHead* contourList, +static bool missingCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidence, SkChunkAlloc* allocator) { SkOpContour* contour = contourList; + bool result = false; do { - contour->missingCoincidence(coincidence, allocator); + result |= contour->missingCoincidence(coincidence, allocator); } while ((contour = contour->next())); + return result; } static void moveMultiples(SkOpContourHead* contourList) { @@ -438,23 +447,42 @@ bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidenc // move t values and points together to eliminate small/tiny gaps moveNearby(contourList); align(contourList); // give all span members common values + coincidence->fixAligned(); // aligning may have marked a coincidence pt-t deleted #if DEBUG_VALIDATE globalState->setPhase(SkOpGlobalState::kIntersecting); #endif + // look for intersections on line segments formed by moving end points + addAlignIntersections(contourList, allocator); coincidence->addMissing(allocator); #if DEBUG_VALIDATE globalState->setPhase(SkOpGlobalState::kWalking); #endif - coincidence->expand(); // check to see if, loosely, coincident ranges may be expanded + // check to see if, loosely, coincident ranges may be expanded + if (coincidence->expand()) { + coincidence->addExpanded(allocator PATH_OPS_DEBUG_VALIDATE_PARAMS(globalState)); + } + // the expanded ranges may not align -- add the missing spans coincidence->mark(); // mark spans of coincident segments as coincident - missingCoincidence(contourList, coincidence, allocator); // look for coincidence missed earlier - if (!coincidence->apply()) { // adjust the winding value to account for coincident edges - return false; + // look for coincidence missed earlier + if (missingCoincidence(contourList, coincidence, allocator)) { + (void) coincidence->expand(); + coincidence->addExpanded(allocator PATH_OPS_DEBUG_VALIDATE_PARAMS(globalState)); + coincidence->mark(); } + SkOpCoincidence overlaps; + do { + SkOpCoincidence* pairs = overlaps.isEmpty() ? coincidence : &overlaps; + if (!pairs->apply()) { // adjust the winding value to account for coincident edges + return false; + } + // For each coincident pair that overlaps another, when the receivers (the 1st of the pair) + // are different, construct a new pair to resolve their mutual span + pairs->findOverlaps(&overlaps, allocator); + } while (!overlaps.isEmpty()); calcAngles(contourList, allocator); sortAngles(contourList); if (globalState->angleCoincidence()) { - missingCoincidence(contourList, coincidence, allocator); + (void) missingCoincidence(contourList, coincidence, allocator); if (!coincidence->apply()) { return false; } diff --git a/src/pathops/SkPathOpsDebug.cpp b/src/pathops/SkPathOpsDebug.cpp index 627a7c9f40..49422305a7 100644 --- a/src/pathops/SkPathOpsDebug.cpp +++ b/src/pathops/SkPathOpsDebug.cpp @@ -135,6 +135,18 @@ void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp show_op(shapeOp, "path", "pathB"); } +#include "SkPathOpsTypes.h" + +#ifdef SK_DEBUG +bool SkOpGlobalState::debugRunFail() const { +#if DEBUG_VALIDATE + return FLAGS_runFail; +#else + return false; +#endif +} +#endif + #include "SkPathOpsCubic.h" #include "SkPathOpsQuad.h" diff --git a/src/pathops/SkPathOpsDebug.h b/src/pathops/SkPathOpsDebug.h index 61c2ecab53..ee2c291869 100644 --- a/src/pathops/SkPathOpsDebug.h +++ b/src/pathops/SkPathOpsDebug.h @@ -95,6 +95,12 @@ #define SkDEBUGCODE_(...) __VA_ARGS__ // temporary until SkDEBUGCODE is fixed #endif +#if DEBUG_VALIDATE == 0 + #define PATH_OPS_DEBUG_VALIDATE_PARAMS(...) +#else + #define PATH_OPS_DEBUG_VALIDATE_PARAMS(...) , __VA_ARGS__ +#endif + #if DEBUG_T_SECT == 0 #define PATH_OPS_DEBUG_T_SECT_RELEASE(a, b) b #define PATH_OPS_DEBUG_T_SECT_PARAMS(...) @@ -179,6 +185,12 @@ public: static const class SkOpSegment* DebugContourSegment(class SkOpContour*, int id); static const class SkOpSpanBase* DebugContourSpan(class SkOpContour*, int id); + static const struct SkOpAngle* DebugCoincidenceAngle(class SkOpCoincidence*, int id); + static class SkOpContour* DebugCoincidenceContour(class SkOpCoincidence*, int id); + static const class SkOpPtT* DebugCoincidencePtT(class SkOpCoincidence*, int id); + static const class SkOpSegment* DebugCoincidenceSegment(class SkOpCoincidence*, int id); + static const class SkOpSpanBase* DebugCoincidenceSpan(class SkOpCoincidence*, int id); + static const struct SkOpAngle* DebugPtTAngle(const class SkOpPtT*, int id); static class SkOpContour* DebugPtTContour(class SkOpPtT*, int id); static const class SkOpPtT* DebugPtTPtT(const class SkOpPtT*, int id); diff --git a/src/pathops/SkPathOpsRect.h b/src/pathops/SkPathOpsRect.h index 7fec70a32a..2b1e6393f4 100644 --- a/src/pathops/SkPathOpsRect.h +++ b/src/pathops/SkPathOpsRect.h @@ -25,9 +25,6 @@ struct SkDRect { } bool intersects(const SkDRect& r) const { - if (fLeft > fRight) { - SkDebugf("!"); - } SkASSERT(fLeft <= fRight); SkASSERT(fTop <= fBottom); SkASSERT(r.fLeft <= r.fRight); diff --git a/src/pathops/SkPathOpsTypes.cpp b/src/pathops/SkPathOpsTypes.cpp index 2c8d778c69..dcd33c9d73 100644 --- a/src/pathops/SkPathOpsTypes.cpp +++ b/src/pathops/SkPathOpsTypes.cpp @@ -5,6 +5,7 @@ * found in the LICENSE file. */ #include "SkFloatBits.h" +#include "SkOpCoincidence.h" #include "SkPathOpsTypes.h" static bool arguments_denormalized(float a, float b, int epsilon) { @@ -198,3 +199,21 @@ double SkDCubeRoot(double x) { } return result; } + +SkOpGlobalState::SkOpGlobalState(SkOpCoincidence* coincidence, SkOpContourHead* head) + : fCoincidence(coincidence) + , fContourHead(head) + , fNested(0) + , fWindingFailed(false) + , fAngleCoincidence(false) + , fPhase(kIntersecting) + SkDEBUGPARAMS(fAngleID(0)) + SkDEBUGPARAMS(fContourID(0)) + SkDEBUGPARAMS(fPtTID(0)) + SkDEBUGPARAMS(fSegmentID(0)) + SkDEBUGPARAMS(fSpanID(0)) { + if (coincidence) { + coincidence->debugSetGlobalState(this); + } +} + diff --git a/src/pathops/SkPathOpsTypes.h b/src/pathops/SkPathOpsTypes.h index 13ebfee84d..9550aa1ead 100644 --- a/src/pathops/SkPathOpsTypes.h +++ b/src/pathops/SkPathOpsTypes.h @@ -28,19 +28,7 @@ class SkOpContourHead; class SkOpGlobalState { public: - SkOpGlobalState(SkOpCoincidence* coincidence, SkOpContourHead* head) - : fCoincidence(coincidence) - , fContourHead(head) - , fNested(0) - , fWindingFailed(false) - , fAngleCoincidence(false) - , fPhase(kIntersecting) - SkDEBUGPARAMS(fAngleID(0)) - SkDEBUGPARAMS(fContourID(0)) - SkDEBUGPARAMS(fPtTID(0)) - SkDEBUGPARAMS(fSegmentID(0)) - SkDEBUGPARAMS(fSpanID(0)) { - } + SkOpGlobalState(SkOpCoincidence* coincidence, SkOpContourHead* head); enum Phase { kIntersecting, @@ -76,6 +64,7 @@ public: const struct SkOpAngle* debugAngle(int id) const; SkOpContour* debugContour(int id); const class SkOpPtT* debugPtT(int id) const; + bool debugRunFail() const; const class SkOpSegment* debugSegment(int id) const; const class SkOpSpanBase* debugSpan(int id) const; #endif |