path ops -- fix skp bugs
This fixes a series of bugs discovered by running
the small set of Skia skp files through pathops
to flatten the clips.
Review URL: https://codereview.chromium.org/14798004
git-svn-id: http://skia.googlecode.com/svn/trunk@9042 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp
index b5702cb..bcefd71 100644
--- a/src/pathops/SkOpSegment.cpp
+++ b/src/pathops/SkOpSegment.cpp
@@ -450,6 +450,11 @@
span->fT = newT;
span->fOther = other;
span->fPt = pt;
+#if 0
+ // cubics, for instance, may not be exact enough to satisfy this check (e.g., cubicOp69d)
+ SkASSERT(approximately_equal(xyAtT(newT).fX, pt.fX)
+ && approximately_equal(xyAtT(newT).fY, pt.fY));
+#endif
span->fWindSum = SK_MinS32;
span->fOppSum = SK_MinS32;
span->fWindValue = 1;
@@ -533,13 +538,16 @@
// set spans from start to end to decrement by one
// note this walks other backwards
-// FIMXE: there's probably an edge case that can be constructed where
+// FIXME: there's probably an edge case that can be constructed where
// two span in one segment are separated by float epsilon on one span but
// not the other, if one segment is very small. For this
// case the counts asserted below may or may not be enough to separate the
// spans. Even if the counts work out, what if the spans aren't correctly
// sorted? It feels better in such a case to match the span's other span
// pointer since both coincident segments must contain the same spans.
+// FIXME? It seems that decrementing by one will fail for complex paths that
+// have three or more coincident edges. Shouldn't this subtract the difference
+// between the winding values?
void SkOpSegment::addTCancel(double startT, double endT, SkOpSegment* other,
double oStartT, double oEndT) {
SkASSERT(!approximately_negative(endT - startT));
@@ -558,14 +566,19 @@
SkTDArray<double> outsideTs;
SkTDArray<double> oOutsideTs;
do {
- bool decrement = test->fWindValue && oTest->fWindValue && !binary;
+ bool decrement = test->fWindValue && oTest->fWindValue;
bool track = test->fWindValue || oTest->fWindValue;
+ bool bigger = test->fWindValue >= oTest->fWindValue;
double testT = test->fT;
double oTestT = oTest->fT;
SkOpSpan* span = test;
do {
if (decrement) {
- decrementSpan(span);
+ if (binary && bigger) {
+ span->fOppValue--;
+ } else {
+ decrementSpan(span);
+ }
} else if (track && span->fT < 1 && oTestT < 1) {
TrackOutside(&outsideTs, span->fT, oTestT);
}
@@ -581,7 +594,11 @@
SkASSERT(originalWindValue == oSpan->fWindValue);
#endif
if (decrement) {
- other->decrementSpan(oSpan);
+ if (binary && !bigger) {
+ oSpan->fOppValue--;
+ } else {
+ other->decrementSpan(oSpan);
+ }
} else if (track && oSpan->fT < 1 && testT < 1) {
TrackOutside(&oOutsideTs, oSpan->fT, testT);
}
@@ -758,14 +775,14 @@
void SkOpSegment::addTwoAngles(int start, int end, SkTDArray<SkOpAngle>* angles) const {
// add edge leading into junction
int min = SkMin32(end, start);
- if (fTs[min].fWindValue > 0 || fTs[min].fOppValue > 0) {
+ if (fTs[min].fWindValue > 0 || fTs[min].fOppValue != 0) {
addAngle(angles, end, start);
}
// add edge leading away from junction
int step = SkSign32(end - start);
int tIndex = nextExactSpan(end, step);
min = SkMin32(end, tIndex);
- if (tIndex >= 0 && (fTs[min].fWindValue > 0 || fTs[min].fOppValue > 0)) {
+ if (tIndex >= 0 && (fTs[min].fWindValue > 0 || fTs[min].fOppValue != 0)) {
addAngle(angles, end, tIndex);
}
}
@@ -912,6 +929,10 @@
if (oppoSign && UseInnerWinding(maxWinding, winding)) {
maxWinding = winding;
}
+#ifdef SK_DEBUG
+ SkASSERT(abs(maxWinding) <= gDebugMaxWindSum);
+ SkASSERT(abs(oMaxWinding) <= gDebugMaxWindSum);
+#endif
(void) segment->markAndChaseWinding(angle, oMaxWinding, maxWinding);
} else {
if (UseInnerWinding(maxWinding, winding)) {
@@ -920,6 +941,10 @@
if (oppoSign && UseInnerWinding(oMaxWinding, oWinding)) {
oMaxWinding = oWinding;
}
+#ifdef SK_DEBUG
+ SkASSERT(abs(maxWinding) <= gDebugMaxWindSum);
+ SkASSERT(abs(binary ? oMaxWinding : 0) <= gDebugMaxWindSum);
+#endif
(void) segment->markAndChaseWinding(angle, maxWinding,
binary ? oMaxWinding : 0);
}
@@ -2241,6 +2266,10 @@
int otherEnd = other->nextExactSpan(*index, step);
SkASSERT(otherEnd >= 0);
*min = SkMin32(*index, otherEnd);
+ if (other->fTs[*min].fTiny) {
+ *last = NULL;
+ return NULL;
+ }
return other;
}
@@ -2489,7 +2518,7 @@
}
void SkOpSegment::zeroSpan(SkOpSpan* span) {
- SkASSERT(span->fWindValue > 0 || span->fOppValue > 0);
+ SkASSERT(span->fWindValue > 0 || span->fOppValue != 0);
span->fWindValue = 0;
span->fOppValue = 0;
SkASSERT(!span->fDone);
@@ -2557,7 +2586,7 @@
}
#endif
-#if DEBUG_ACTIVE_SPANS
+#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
void SkOpSegment::debugShowActiveSpans() const {
if (done()) {
return;
@@ -2572,6 +2601,7 @@
if (fTs[i].fDone) {
continue;
}
+ SkASSERT(i < fTs.count() - 1);
#if DEBUG_ACTIVE_SPANS_SHORT_FORM
if (lastId == fID && lastT == fTs[i].fT) {
continue;