aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar caryclark <caryclark@google.com>2015-07-06 11:38:33 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2015-07-06 11:38:33 -0700
commit27c8eb8ffd7e221693d840c2b9279d53fe6f03d4 (patch)
tree67fbbc378aced5c5de9ec9611021db6ef7659c3c /src
parent334e588d9ed5390efb82beb37329b56a380f1d0e (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')
-rwxr-xr-xsrc/pathops/SkOpCoincidence.cpp277
-rw-r--r--src/pathops/SkOpCoincidence.h54
-rw-r--r--src/pathops/SkOpContour.h23
-rw-r--r--src/pathops/SkOpSegment.cpp154
-rw-r--r--src/pathops/SkOpSegment.h13
-rwxr-xr-xsrc/pathops/SkOpSpan.cpp54
-rw-r--r--src/pathops/SkOpSpan.h29
-rw-r--r--src/pathops/SkPathOpsCommon.cpp42
-rw-r--r--src/pathops/SkPathOpsDebug.cpp12
-rw-r--r--src/pathops/SkPathOpsDebug.h12
-rw-r--r--src/pathops/SkPathOpsRect.h3
-rw-r--r--src/pathops/SkPathOpsTypes.cpp19
-rw-r--r--src/pathops/SkPathOpsTypes.h15
-rw-r--r--src/ports/SkDebug_win.cpp2
14 files changed, 620 insertions, 89 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
diff --git a/src/ports/SkDebug_win.cpp b/src/ports/SkDebug_win.cpp
index fe28ee27b1..8bbbef58e0 100644
--- a/src/ports/SkDebug_win.cpp
+++ b/src/ports/SkDebug_win.cpp
@@ -25,7 +25,7 @@ void SkDebugf(const char format[], ...) {
va_end(args);
// When we crash on Windows we often are missing a lot of prints. Since we don't really care
// about SkDebugf performance we flush after every print.
- fflush(stdout);
+// fflush(stdout);
va_start(args, format);
vsnprintf(buffer, kBufferSize, format, args);