Index: src/pathops/SkOpSegment.cpp |
diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp |
index 7e69bb835b478d27e630a738811f84d840760428..c0ef1f8e1170ea8989beccadf6507c267ce7d579 100644 |
--- a/src/pathops/SkOpSegment.cpp |
+++ b/src/pathops/SkOpSegment.cpp |
@@ -32,36 +32,36 @@ static const bool gActiveEdge[kXOR_PathOp + 1][2][2][2][2] = { |
#undef F |
#undef T |
-enum { kOutsideTrackedTCount = 16 }; // FIXME: determine what this should be |
- |
-// OPTIMIZATION: does the following also work, and is it any faster? |
-// return outerWinding * innerWinding > 0 |
-// || ((outerWinding + innerWinding < 0) ^ ((outerWinding - innerWinding) < 0))) |
-bool SkOpSegment::UseInnerWinding(int outerWinding, int innerWinding) { |
- SkASSERT(outerWinding != SK_MaxS32); |
- SkASSERT(innerWinding != SK_MaxS32); |
- int absOut = abs(outerWinding); |
- int absIn = abs(innerWinding); |
- bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn; |
- return result; |
-} |
+enum { |
+ kOutsideTrackedTCount = 16, // FIXME: determine what this should be |
+ kMissingSpanCount = 4, // FIXME: determine what this should be |
+}; |
+// note that this follows the same logic flow as build angles |
bool SkOpSegment::activeAngle(int index, int* done, SkTArray<SkOpAngle, true>* angles) { |
if (activeAngleInner(index, done, angles)) { |
return true; |
} |
+ double referenceT = fTs[index].fT; |
int lesser = index; |
- while (--lesser >= 0 && equalPoints(index, lesser)) { |
+ while (--lesser >= 0 |
+ && (precisely_negative(referenceT - fTs[lesser].fT) || fTs[lesser].fTiny)) { |
if (activeAngleOther(lesser, done, angles)) { |
return true; |
} |
} |
- lesser = index; |
do { |
if (activeAngleOther(index, done, angles)) { |
return true; |
} |
- } while (++index < fTs.count() && equalPoints(index, lesser)); |
+ if (++index == fTs.count()) { |
+ break; |
+ } |
+ if (fTs[index - 1].fTiny) { |
+ referenceT = fTs[index].fT; |
+ continue; |
+ } |
+ } while (precisely_negative(fTs[index].fT - referenceT)); |
return false; |
} |
@@ -187,7 +187,7 @@ bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, int index, int endIndex |
bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo]; |
#if DEBUG_ACTIVE_OP |
SkDebugf("%s op=%s miFrom=%d miTo=%d suFrom=%d suTo=%d result=%d\n", __FUNCTION__, |
- kPathOpStr[op], miFrom, miTo, suFrom, suTo, result); |
+ SkPathOpsDebug::kPathOpStr[op], miFrom, miTo, suFrom, suTo, result); |
#endif |
return result; |
} |
@@ -209,47 +209,35 @@ bool SkOpSegment::activeWinding(int index, int endIndex, int* maxWinding, int* s |
void SkOpSegment::addAngle(SkTArray<SkOpAngle, true>* anglesPtr, int start, int end) const { |
SkASSERT(start != end); |
SkOpAngle& angle = anglesPtr->push_back(); |
-#if 0 && DEBUG_ANGLE // computed pt and actual pt may differ by more than approx eq |
- SkTArray<SkOpAngle, true>& angles = *anglesPtr; |
- if (angles.count() > 1) { |
- const SkOpSegment* aSeg = angles[0].segment(); |
- int aStart = angles[0].start(); |
- if (!aSeg->fTs[aStart].fTiny) { |
- const SkPoint& angle0Pt = aSeg->xyAtT(aStart); |
- SkDPoint newPt = (*CurveDPointAtT[SkPathOpsVerbToPoints(aSeg->fVerb)])(aSeg->fPts, |
- aSeg->fTs[aStart].fT); |
-#if ONE_OFF_DEBUG |
- SkDebugf("%s t1=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g)\n", __FUNCTION__, |
- aSeg->fTs[aStart].fT, newPt.fX, newPt.fY, angle0Pt.fX, angle0Pt.fY); |
-#endif |
- SkASSERT(newPt.approximatelyEqual(angle0Pt)); |
- } |
- } |
-#endif |
angle.set(this, start, end); |
} |
-void SkOpSegment::addCancelOutsides(double tStart, double oStart, SkOpSegment* other, double oEnd) { |
+void SkOpSegment::addCancelOutsides(const SkPoint& startPt, const SkPoint& endPt, |
+ SkOpSegment* other) { |
int tIndex = -1; |
int tCount = fTs.count(); |
int oIndex = -1; |
int oCount = other->fTs.count(); |
do { |
++tIndex; |
- } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount); |
+ } while (startPt != fTs[tIndex].fPt && tIndex < tCount); |
int tIndexStart = tIndex; |
do { |
++oIndex; |
- } while (!approximately_negative(oStart - other->fTs[oIndex].fT) && oIndex < oCount); |
+ } while (endPt != other->fTs[oIndex].fPt && oIndex < oCount); |
int oIndexStart = oIndex; |
- double nextT; |
+ const SkPoint* nextPt; |
do { |
- nextT = fTs[++tIndex].fT; |
- } while (nextT < 1 && approximately_negative(nextT - tStart)); |
- double oNextT; |
+ nextPt = &fTs[++tIndex].fPt; |
+ SkASSERT(fTs[tIndex].fT < 1 || startPt != *nextPt); |
+ } while (startPt == *nextPt); |
+ double nextT = fTs[tIndex].fT; |
+ const SkPoint* oNextPt; |
do { |
- oNextT = other->fTs[++oIndex].fT; |
- } while (oNextT < 1 && approximately_negative(oNextT - oStart)); |
+ oNextPt = &other->fTs[++oIndex].fPt; |
+ SkASSERT(other->fTs[oIndex].fT < 1 || endPt != *oNextPt); |
+ } while (endPt == *oNextPt); |
+ double oNextT = other->fTs[oIndex].fT; |
// at this point, spans before and after are at: |
// fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex] |
// if tIndexStart == 0, no prior span |
@@ -301,43 +289,39 @@ void SkOpSegment::addCancelOutsides(double tStart, double oStart, SkOpSegment* o |
} |
} |
-void SkOpSegment::addCoinOutsides(const SkTArray<double, true>& outsideTs, SkOpSegment* other, |
- double oEnd) { |
- // walk this to outsideTs[0] |
- // walk other to outsideTs[1] |
+void SkOpSegment::addCoinOutsides(const SkPoint& startPt, const SkPoint& endPt, |
+ SkOpSegment* other) { |
+ // walk this to startPt |
+ // walk other to startPt |
// if either is > 0, add a pointer to the other, copying adjacent winding |
int tIndex = -1; |
int oIndex = -1; |
- double tStart = outsideTs[0]; |
- double oStart = outsideTs[1]; |
do { |
++tIndex; |
- } while (!approximately_negative(tStart - fTs[tIndex].fT)); |
- SkPoint ptStart = fTs[tIndex].fPt; |
+ } while (startPt != fTs[tIndex].fPt); |
do { |
++oIndex; |
- } while (!approximately_negative(oStart - other->fTs[oIndex].fT)); |
+ } while (startPt != other->fTs[oIndex].fPt); |
if (tIndex > 0 || oIndex > 0 || fOperand != other->fOperand) { |
- addTPair(tStart, other, oStart, false, ptStart); |
+ addTPair(fTs[tIndex].fT, other, other->fTs[oIndex].fT, false, startPt); |
} |
- tStart = fTs[tIndex].fT; |
- oStart = other->fTs[oIndex].fT; |
+ SkPoint nextPt = startPt; |
do { |
- double nextT; |
+ const SkPoint* workPt; |
do { |
- nextT = fTs[++tIndex].fT; |
- } while (approximately_negative(nextT - tStart)); |
- tStart = nextT; |
- ptStart = fTs[tIndex].fPt; |
+ workPt = &fTs[++tIndex].fPt; |
+ } while (nextPt == *workPt); |
do { |
- nextT = other->fTs[++oIndex].fT; |
- } while (approximately_negative(nextT - oStart)); |
- oStart = nextT; |
+ workPt = &other->fTs[++oIndex].fPt; |
+ } while (nextPt == *workPt); |
+ nextPt = *workPt; |
+ double tStart = fTs[tIndex].fT; |
+ double oStart = other->fTs[oIndex].fT; |
if (tStart == 1 && oStart == 1 && fOperand == other->fOperand) { |
break; |
} |
- addTPair(tStart, other, oStart, false, ptStart); |
- } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart)); |
+ addTPair(tStart, other, oStart, false, nextPt); |
+ } while (endPt != nextPt); |
} |
void SkOpSegment::addCubic(const SkPoint pts[4], bool operand, bool evenOdd) { |
@@ -423,7 +407,7 @@ void SkOpSegment::addQuad(const SkPoint pts[3], bool operand, bool evenOdd) { |
// resolve overlapping ts when considering coincidence later |
// add non-coincident intersection. Resulting edges are sorted in T. |
-int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) { |
+int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT, bool isNear) { |
if (precisely_zero(newT)) { |
newT = 0; |
} else if (precisely_equal(newT, 1)) { |
@@ -455,6 +439,7 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) { |
span->fT = newT; |
span->fOther = other; |
span->fPt = pt; |
+ span->fNear = isNear; |
#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) |
@@ -464,6 +449,7 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) { |
span->fOppSum = SK_MinS32; |
span->fWindValue = 1; |
span->fOppValue = 0; |
+ span->fSmall = false; |
span->fTiny = false; |
span->fLoop = false; |
if ((span->fDone = newT == 1)) { |
@@ -472,7 +458,7 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) { |
span->fUnsortableStart = false; |
span->fUnsortableEnd = false; |
int less = -1; |
- while (&span[less + 1] - fTs.begin() > 0 && xyAtT(&span[less]) == xyAtT(span)) { |
+ while (&span[less + 1] - fTs.begin() > 0 && AlmostEqualUlps(span[less].fPt, span->fPt)) { |
if (span[less].fDone) { |
break; |
} |
@@ -487,9 +473,11 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) { |
break; |
} |
} |
- span[less].fTiny = true; |
+ span[less].fSmall = true; |
+ bool tiny = span[less].fPt == span->fPt; |
+ span[less].fTiny = tiny; |
span[less].fDone = true; |
- if (approximately_negative(newT - span[less].fT)) { |
+ if (approximately_negative(newT - span[less].fT) && tiny) { |
if (approximately_greater_than_one(newT)) { |
SkASSERT(&span[less] - fTs.begin() < fTs.count()); |
span[less].fUnsortableStart = true; |
@@ -508,7 +496,7 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) { |
--less; |
} |
int more = 1; |
- while (fTs.end() - &span[more - 1] > 1 && xyAtT(&span[more]) == xyAtT(span)) { |
+ while (fTs.end() - &span[more - 1] > 1 && AlmostEqualUlps(span[more].fPt, span->fPt)) { |
if (span[more - 1].fDone) { |
break; |
} |
@@ -523,9 +511,11 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) { |
break; |
} |
} |
- span[more - 1].fTiny = true; |
+ span[more - 1].fSmall = true; |
+ bool tiny = span[more].fPt == span->fPt; |
+ span[more - 1].fTiny = tiny; |
span[more - 1].fDone = true; |
- if (approximately_negative(span[more].fT - newT)) { |
+ if (approximately_negative(span[more].fT - newT) && tiny) { |
if (approximately_greater_than_one(span[more].fT)) { |
span[more + 1].fUnsortableStart = true; |
span[more].fUnsortableEnd = true; |
@@ -553,148 +543,156 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) { |
// 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)); |
- SkASSERT(!approximately_negative(oEndT - oStartT)); |
+/* |--> |--> |
+this 0>>>>1>>>>2>>>>3>>>4 0>>>>1>>>>2>>>>3>>>4 0>>>>1>>>>2>>>>3>>>4 |
+other 2<<<<1<<<<0 2<<<<1<<<<0 2<<<<1<<<<0 |
+ ^ ^ <--| <--| |
+ startPt endPt test/oTest first pos test/oTest final pos |
+*/ |
+void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other) { |
bool binary = fOperand != other->fOperand; |
int index = 0; |
- while (!approximately_negative(startT - fTs[index].fT)) { |
+ while (startPt != fTs[index].fPt) { |
+ SkASSERT(index < fTs.count()); |
++index; |
} |
int oIndex = other->fTs.count(); |
- while (approximately_positive(other->fTs[--oIndex].fT - oEndT)) |
- ; |
- double tRatio = (oEndT - oStartT) / (endT - startT); |
+ while (startPt != other->fTs[--oIndex].fPt) { // look for startPt match |
+ SkASSERT(oIndex > 0); |
+ } |
+ while (startPt == other->fTs[--oIndex].fPt) { // look for first point beyond match |
+ SkASSERT(oIndex > 0); |
+ } |
SkOpSpan* test = &fTs[index]; |
SkOpSpan* oTest = &other->fTs[oIndex]; |
- SkSTArray<kOutsideTrackedTCount, double, true> outsideTs; |
- SkSTArray<kOutsideTrackedTCount, double, true> oOutsideTs; |
+ SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts; |
+ SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts; |
do { |
+ SkASSERT(test->fT < 1); |
+ SkASSERT(oTest->fT < 1); |
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; |
+ const SkPoint& testPt = test->fPt; |
+ const SkPoint& oTestPt = oTest->fPt; |
do { |
if (decrement) { |
if (binary && bigger) { |
- span->fOppValue--; |
+ test->fOppValue--; |
} else { |
- decrementSpan(span); |
+ decrementSpan(test); |
} |
- } else if (track && span->fT < 1 && oTestT < 1) { |
- TrackOutside(&outsideTs, span->fT, oTestT); |
+ } else if (track) { |
+ TrackOutsidePair(&outsidePts, testPt, oTestPt); |
} |
- span = &fTs[++index]; |
- } while (approximately_negative(span->fT - testT)); |
- SkOpSpan* oSpan = oTest; |
- double otherTMatchStart = oEndT - (span->fT - startT) * tRatio; |
- double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio; |
- SkDEBUGCODE(int originalWindValue = oSpan->fWindValue); |
- while (approximately_negative(otherTMatchStart - oSpan->fT) |
- && !approximately_negative(otherTMatchEnd - oSpan->fT)) { |
- #ifdef SK_DEBUG |
- SkASSERT(originalWindValue == oSpan->fWindValue); |
- #endif |
+ SkASSERT(index < fTs.count() - 1); |
+ test = &fTs[++index]; |
+ } while (testPt == test->fPt); |
+ SkDEBUGCODE(int originalWindValue = oTest->fWindValue); |
+ do { |
+ SkASSERT(oTest->fT < 1); |
+ SkASSERT(originalWindValue == oTest->fWindValue); |
if (decrement) { |
if (binary && !bigger) { |
- oSpan->fOppValue--; |
+ oTest->fOppValue--; |
} else { |
- other->decrementSpan(oSpan); |
+ other->decrementSpan(oTest); |
} |
- } else if (track && oSpan->fT < 1 && testT < 1) { |
- TrackOutside(&oOutsideTs, oSpan->fT, testT); |
+ } else if (track) { |
+ TrackOutsidePair(&oOutsidePts, oTestPt, testPt); |
} |
if (!oIndex) { |
break; |
} |
- oSpan = &other->fTs[--oIndex]; |
- } |
- test = span; |
- oTest = oSpan; |
- } while (!approximately_negative(endT - test->fT)); |
- SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT)); |
+ oTest = &other->fTs[--oIndex]; |
+ } while (oTestPt == oTest->fPt); |
+ SkASSERT(endPt != test->fPt || oTestPt == endPt); |
+ } while (endPt != test->fPt); |
// FIXME: determine if canceled edges need outside ts added |
- if (!done() && outsideTs.count()) { |
- double tStart = outsideTs[0]; |
- double oStart = outsideTs[1]; |
- addCancelOutsides(tStart, oStart, other, oEndT); |
- int count = outsideTs.count(); |
- if (count > 2) { |
- double tStart = outsideTs[count - 2]; |
- double oStart = outsideTs[count - 1]; |
- addCancelOutsides(tStart, oStart, other, oEndT); |
+ int outCount = outsidePts.count(); |
+ if (!done() && outCount) { |
+ addCancelOutsides(outsidePts[0], outsidePts[1], other); |
+ if (outCount > 2) { |
+ addCancelOutsides(outsidePts[outCount - 2], outsidePts[outCount - 1], other); |
} |
} |
- if (!other->done() && oOutsideTs.count()) { |
- double tStart = oOutsideTs[0]; |
- double oStart = oOutsideTs[1]; |
- other->addCancelOutsides(tStart, oStart, this, endT); |
+ if (!other->done() && oOutsidePts.count()) { |
+ other->addCancelOutsides(oOutsidePts[0], oOutsidePts[1], this); |
} |
} |
int SkOpSegment::addSelfT(SkOpSegment* other, const SkPoint& pt, double newT) { |
- int result = addT(other, pt, newT); |
+ // if the tail nearly intersects itself but not quite, the caller records this separately |
+ int result = addT(other, pt, newT, SkOpSpan::kPointIsExact); |
SkOpSpan* span = &fTs[result]; |
span->fLoop = true; |
return result; |
} |
-int SkOpSegment::addUnsortableT(SkOpSegment* other, bool start, const SkPoint& pt, double newT) { |
- int result = addT(other, pt, newT); |
- SkOpSpan* span = &fTs[result]; |
- if (start) { |
- if (result > 0) { |
- span[result - 1].fUnsortableEnd = true; |
- } |
- span[result].fUnsortableStart = true; |
- } else { |
- span[result].fUnsortableEnd = true; |
- if (result + 1 < fTs.count()) { |
- span[result + 1].fUnsortableStart = true; |
- } |
- } |
- return result; |
-} |
- |
-int SkOpSegment::bumpCoincidentThis(const SkOpSpan& oTest, bool opp, int index, |
- SkTArray<double, true>* outsideTs) { |
+void SkOpSegment::bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* indexPtr, |
+ SkTArray<SkPoint, true>* outsideTs) { |
+ int index = *indexPtr; |
int oWindValue = oTest.fWindValue; |
int oOppValue = oTest.fOppValue; |
- if (opp) { |
+ if (binary) { |
SkTSwap<int>(oWindValue, oOppValue); |
} |
SkOpSpan* const test = &fTs[index]; |
SkOpSpan* end = test; |
- const double oStartT = oTest.fT; |
+ const SkPoint& oStartPt = oTest.fPt; |
do { |
if (bumpSpan(end, oWindValue, oOppValue)) { |
- TrackOutside(outsideTs, end->fT, oStartT); |
+ TrackOutside(outsideTs, oStartPt); |
} |
end = &fTs[++index]; |
- } while (approximately_negative(end->fT - test->fT)); |
- return index; |
+ } while (end->fPt == test->fPt); |
+ *indexPtr = index; |
+} |
+ |
+bool SkOpSegment::bumpCoincident(SkOpSpan* test, bool bigger, bool binary) { |
+ if (bigger) { |
+ if (binary) { |
+ if (fOppXor) { |
+ test->fOppValue ^= 1; |
+ } else { |
+ test->fOppValue++; |
+ } |
+ } else { |
+ if (fXor) { |
+ test->fWindValue ^= 1; |
+ } else { |
+ test->fWindValue++; |
+ } |
+ } |
+ if (!test->fWindValue && !test->fOppValue) { |
+ test->fDone = true; |
+ ++fDoneSpans; |
+ return true; |
+ } |
+ return false; |
+ } |
+ return decrementSpan(test); |
} |
// because of the order in which coincidences are resolved, this and other |
// may not have the same intermediate points. Compute the corresponding |
// intermediate T values (using this as the master, other as the follower) |
// and walk other conditionally -- hoping that it catches up in the end |
-int SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, double oEndT, int& oIndex, |
- SkTArray<double, true>* oOutsideTs) { |
+void SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, int* oIndexPtr, |
+ SkTArray<SkPoint, true>* oOutsidePts) { |
+ int oIndex = *oIndexPtr; |
SkOpSpan* const oTest = &fTs[oIndex]; |
SkOpSpan* oEnd = oTest; |
- const double startT = test.fT; |
- const double oStartT = oTest->fT; |
- while (!approximately_negative(oEndT - oEnd->fT) |
- && approximately_negative(oEnd->fT - oStartT)) { |
+ const SkPoint& startPt = test.fPt; |
+ const SkPoint& oStartPt = oTest->fPt; |
+ if (oStartPt == oEnd->fPt) { |
+ TrackOutside(oOutsidePts, startPt); |
+ } |
+ while (oStartPt == oEnd->fPt) { |
zeroSpan(oEnd); |
- TrackOutside(oOutsideTs, oEnd->fT, startT); |
oEnd = &fTs[++oIndex]; |
} |
- return oIndex; |
+ *oIndexPtr = oIndex; |
} |
// FIXME: need to test this case: |
@@ -706,43 +704,75 @@ int SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, double oEndT, int& oI |
// set spans from start to end to increment the greater by one and decrement |
// the lesser |
-void SkOpSegment::addTCoincident(double startT, double endT, SkOpSegment* other, double oStartT, |
- double oEndT) { |
- SkASSERT(!approximately_negative(endT - startT)); |
- SkASSERT(!approximately_negative(oEndT - oStartT)); |
- bool opp = fOperand ^ other->fOperand; |
+void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, |
+ SkOpSegment* other) { |
+ bool binary = fOperand != other->fOperand; |
int index = 0; |
- while (!approximately_negative(startT - fTs[index].fT)) { |
+ while (startPt != fTs[index].fPt) { |
+ SkASSERT(index < fTs.count()); |
++index; |
} |
int oIndex = 0; |
- while (!approximately_negative(oStartT - other->fTs[oIndex].fT)) { |
+ while (startPt != other->fTs[oIndex].fPt) { |
+ SkASSERT(oIndex < other->fTs.count()); |
++oIndex; |
} |
+ SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts; |
+ SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts; |
SkOpSpan* test = &fTs[index]; |
+ const SkPoint* testPt = &test->fPt; |
SkOpSpan* oTest = &other->fTs[oIndex]; |
- SkSTArray<kOutsideTrackedTCount, double, true> outsideTs; |
- SkSTArray<kOutsideTrackedTCount, double, true> oOutsideTs; |
+ const SkPoint* oTestPt = &oTest->fPt; |
+ SkASSERT(AlmostEqualUlps(*testPt, *oTestPt)); |
do { |
- // if either span has an opposite value and the operands don't match, resolve first |
- // SkASSERT(!test->fDone || !oTest->fDone); |
+ SkASSERT(test->fT < 1); |
+ SkASSERT(oTest->fT < 1); |
+#if 0 |
if (test->fDone || oTest->fDone) { |
- index = advanceCoincidentThis(oTest, opp, index); |
- oIndex = other->advanceCoincidentOther(test, oEndT, oIndex); |
+#else // consolidate the winding count even if done |
+ if ((test->fWindValue == 0 && test->fOppValue == 0) |
+ || (oTest->fWindValue == 0 && oTest->fOppValue == 0)) { |
+#endif |
+ SkDEBUGCODE(int firstWind = test->fWindValue); |
+ SkDEBUGCODE(int firstOpp = test->fOppValue); |
+ do { |
+ SkASSERT(firstWind == fTs[index].fWindValue); |
+ SkASSERT(firstOpp == fTs[index].fOppValue); |
+ ++index; |
+ SkASSERT(index < fTs.count()); |
+ } while (*testPt == fTs[index].fPt); |
+ SkDEBUGCODE(firstWind = oTest->fWindValue); |
+ SkDEBUGCODE(firstOpp = oTest->fOppValue); |
+ do { |
+ SkASSERT(firstWind == other->fTs[oIndex].fWindValue); |
+ SkASSERT(firstOpp == other->fTs[oIndex].fOppValue); |
+ ++oIndex; |
+ SkASSERT(oIndex < other->fTs.count()); |
+ } while (*oTestPt == other->fTs[oIndex].fPt); |
} else { |
- index = bumpCoincidentThis(*oTest, opp, index, &outsideTs); |
- oIndex = other->bumpCoincidentOther(*test, oEndT, oIndex, &oOutsideTs); |
+ if (!binary || test->fWindValue + oTest->fOppValue >= 0) { |
+ bumpCoincidentThis(*oTest, binary, &index, &outsidePts); |
+ other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts); |
+ } else { |
+ other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts); |
+ bumpCoincidentOther(*oTest, &index, &outsidePts); |
+ } |
} |
test = &fTs[index]; |
+ testPt = &test->fPt; |
+ if (endPt == *testPt) { |
+ break; |
+ } |
oTest = &other->fTs[oIndex]; |
- } while (!approximately_negative(endT - test->fT)); |
- SkASSERT(approximately_negative(oTest->fT - oEndT)); |
- SkASSERT(approximately_negative(oEndT - oTest->fT)); |
- if (!done() && outsideTs.count()) { |
- addCoinOutsides(outsideTs, other, oEndT); |
+ oTestPt = &oTest->fPt; |
+ SkASSERT(AlmostEqualUlps(*testPt, *oTestPt)); |
+ } while (endPt != *oTestPt); |
+ int outCount = outsidePts.count(); |
+ if (!done() && outCount) { |
+ addCoinOutsides(outsidePts[0], endPt, other); |
} |
- if (!other->done() && oOutsideTs.count()) { |
- other->addCoinOutsides(oOutsideTs, this, endT); |
+ if (!other->done() && oOutsidePts.count()) { |
+ other->addCoinOutsides(oOutsidePts[0], endPt, this); |
} |
} |
@@ -769,8 +799,8 @@ void SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool bor |
SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n", |
__FUNCTION__, fID, t, other->fID, otherT); |
#endif |
- int insertedAt = addT(other, pt, t); |
- int otherInsertedAt = other->addT(this, pt, otherT); |
+ int insertedAt = addT(other, pt, t, SkOpSpan::kPointIsExact); |
+ int otherInsertedAt = other->addT(this, pt, otherT, SkOpSpan::kPointIsExact); |
addOtherT(insertedAt, otherT, otherInsertedAt); |
other->addOtherT(otherInsertedAt, t, insertedAt); |
matchWindingValue(insertedAt, t, borrowWind); |
@@ -792,7 +822,151 @@ void SkOpSegment::addTwoAngles(int start, int end, SkTArray<SkOpAngle, true>* an |
} |
} |
-int SkOpSegment::advanceCoincidentThis(const SkOpSpan* oTest, bool opp, int index) { |
+SkOpSegment::MissingSpan::Command SkOpSegment::adjustThisNear(double startT, const SkPoint& startPt, |
+ const SkPoint& endPt, SkTArray<MissingSpan, true>* missingSpans) { |
+ // see if endPt exists on this curve, and if it has the same t or a different T than the startT |
+ int count = this->count(); |
+ SkASSERT(count > 0); |
+ int startIndex, endIndex, step; |
+ if (startT == 0) { |
+ startIndex = 0; |
+ endIndex = count; |
+ step = 1; |
+ } else { |
+ SkASSERT(startT == 1); |
+ startIndex = count - 1; |
+ endIndex = -1; |
+ step = -1; |
+ } |
+ int index = startIndex; |
+ do { |
+ const SkOpSpan& span = fTs[index]; |
+ if (span.fPt != endPt) { |
+ continue; |
+ } |
+ if (span.fT == startT) { |
+ // check to see if otherT matches some other mid curve intersection |
+ int inner = startIndex; |
+ do { |
+ if (inner == index) { |
+ continue; |
+ } |
+ const SkOpSpan& matchSpan = fTs[inner]; |
+ double matchT = span.fOther->missingNear(span.fOtherT, matchSpan.fOther, startPt, |
+ endPt); |
+ if (matchT >= 0) { |
+ SkASSERT(missingSpans); |
+ MissingSpan& missingSpan = missingSpans->push_back(); |
+ SkDEBUGCODE(sk_bzero(&missingSpan, sizeof(missingSpan))); |
+ missingSpan.fCommand = MissingSpan::kRemoveNear; |
+ missingSpan.fT = startT; |
+ missingSpan.fSegment = this; |
+ missingSpan.fOther = span.fOther; |
+ missingSpan.fOtherT = matchT; |
+ return missingSpan.fCommand; |
+ } |
+ } while ((inner += step) != endIndex); |
+ break; |
+ } |
+ double midT = (startT + span.fT) / 2; |
+ if (betweenPoints(midT, startPt, endPt)) { |
+ if (!missingSpans) { |
+ return MissingSpan::kZeroSpan; |
+ } |
+ MissingSpan& missingSpan = missingSpans->push_back(); |
+ SkDEBUGCODE(sk_bzero(&missingSpan, sizeof(missingSpan))); |
+ missingSpan.fCommand = MissingSpan::kZeroSpan; |
+ missingSpan.fT = SkTMin(startT, span.fT); |
+ missingSpan.fEndT = SkTMax(startT, span.fT); |
+ missingSpan.fSegment = this; |
+ return missingSpan.fCommand; |
+ } |
+ } while ((index += step) != endIndex); |
+ return MissingSpan::kNoAction; |
+} |
+ |
+void SkOpSegment::adjustOtherNear(double startT, const SkPoint& startPt, const SkPoint& endPt, |
+ SkTArray<MissingSpan, true>* missingSpans) { |
+ int count = this->count(); |
+ SkASSERT(count > 0); |
+ int startIndex, endIndex, step; |
+ if (startT == 0) { |
+ startIndex = 0; |
+ endIndex = count; |
+ step = 1; |
+ } else { |
+ SkASSERT(startT == 1); |
+ startIndex = count - 1; |
+ endIndex = -1; |
+ step = -1; |
+ } |
+ int index = startIndex; |
+ do { |
+ const SkOpSpan& span = fTs[index]; |
+ if (span.fT != startT) { |
+ return; |
+ } |
+ SkOpSegment* other = span.fOther; |
+ if (other->fPts[0] == endPt) { |
+ other->adjustThisNear(0, endPt, startPt, missingSpans); |
+ } else if (other->fPts[0] == startPt) { |
+ other->adjustThisNear(0, startPt, endPt, missingSpans); |
+ } |
+ if (other->ptAtT(1) == endPt) { |
+ other->adjustThisNear(1, endPt, startPt, missingSpans); |
+ } else if (other->ptAtT(1) == startPt) { |
+ other->adjustThisNear(1, startPt, endPt, missingSpans); |
+ } |
+ } while ((index += step) != endIndex); |
+} |
+ |
+void SkOpSegment::adjustMissingNear(const SkPoint& startPt, const SkPoint& endPt, |
+ SkTArray<MissingSpan, true>* missingSpans) { |
+ int count = missingSpans->count(); |
+ for (int index = 0; index < count; ) { |
+ MissingSpan& missing = (*missingSpans)[index]; |
+ SkOpSegment* other = missing.fOther; |
+ MissingSpan::Command command = MissingSpan::kNoAction; |
+ if (missing.fPt == startPt) { |
+ if (missingNear(missing.fT, other, startPt, endPt) >= 0) { |
+ command = MissingSpan::kZeroSpan; |
+ } else if (other->ptAtT(0) == endPt) { |
+ command = other->adjustThisNear(0, endPt, startPt, NULL); |
+ } else if (other->ptAtT(1) == endPt) { |
+ command = other->adjustThisNear(1, endPt, startPt, NULL); |
+ } |
+ } else if (missing.fPt == endPt) { |
+ if (missingNear(missing.fT, other, endPt, startPt) >= 0) { |
+ command = MissingSpan::kZeroSpan; |
+ } else if (other->ptAtT(0) == startPt) { |
+ command = other->adjustThisNear(0, startPt, endPt, NULL); |
+ } else if (other->ptAtT(1) == startPt) { |
+ command = other->adjustThisNear(1, startPt, endPt, NULL); |
+ } |
+ } |
+ if (command == MissingSpan::kZeroSpan) { |
+#if 1 |
+ missing = missingSpans->back(); |
+ missingSpans->pop_back(); |
+#else // if this is supported in the future ... |
+ missingSpans->removeShuffle(index); |
+#endif |
+ --count; |
+ continue; |
+ } |
+ ++index; |
+ } |
+} |
+ |
+void SkOpSegment::adjustNear(double startT, const SkPoint& endPt, |
+ SkTArray<MissingSpan, true>* missingSpans) { |
+ const SkPoint startPt = ptAtT(startT); |
+ adjustMissingNear(startPt, endPt, missingSpans); |
+ adjustThisNear(startT, startPt, endPt, missingSpans); |
+ adjustOtherNear(startT, startPt, endPt, missingSpans); |
+} |
+ |
+int SkOpSegment::advanceCoincidentThis(int index) { |
SkOpSpan* const test = &fTs[index]; |
SkOpSpan* end; |
do { |
@@ -801,7 +975,7 @@ int SkOpSegment::advanceCoincidentThis(const SkOpSpan* oTest, bool opp, int inde |
return index; |
} |
-int SkOpSegment::advanceCoincidentOther(const SkOpSpan* test, double oEndT, int oIndex) { |
+int SkOpSegment::advanceCoincidentOther(double oEndT, int oIndex) { |
SkOpSpan* const oTest = &fTs[oIndex]; |
SkOpSpan* oEnd = oTest; |
const double oStartT = oTest->fT; |
@@ -812,6 +986,14 @@ int SkOpSegment::advanceCoincidentOther(const SkOpSpan* test, double oEndT, int |
return oIndex; |
} |
+bool SkOpSegment::betweenPoints(double midT, const SkPoint& pt1, const SkPoint& pt2) const { |
+ const SkPoint midPt = ptAtT(midT); |
+ SkPathOpsBounds bounds; |
+ bounds.set(pt1.fX, pt1.fY, pt2.fX, pt2.fY); |
+ bounds.sort(); |
+ return bounds.almostContains(midPt); |
+} |
+ |
bool SkOpSegment::betweenTs(int lesser, double testT, int greater) const { |
if (lesser > greater) { |
SkTSwap<int>(lesser, greater); |
@@ -819,17 +1001,37 @@ bool SkOpSegment::betweenTs(int lesser, double testT, int greater) const { |
return approximately_between(fTs[lesser].fT, testT, fTs[greater].fT); |
} |
-void SkOpSegment::buildAngles(int index, SkTArray<SkOpAngle, true>* angles, bool includeOpp) const { |
+// note that this follows the same logic flow as active angle |
+bool SkOpSegment::buildAngles(int index, SkTArray<SkOpAngle, true>* angles, bool allowOpp) const { |
double referenceT = fTs[index].fT; |
+ const SkPoint& referencePt = fTs[index].fPt; |
int lesser = index; |
- while (--lesser >= 0 && (includeOpp || fTs[lesser].fOther->fOperand == fOperand) |
- && precisely_negative(referenceT - fTs[lesser].fT)) { |
+ while (--lesser >= 0 && (allowOpp || fTs[lesser].fOther->fOperand == fOperand) |
+ && (precisely_negative(referenceT - fTs[lesser].fT) || fTs[lesser].fTiny)) { |
buildAnglesInner(lesser, angles); |
} |
do { |
buildAnglesInner(index, angles); |
- } while (++index < fTs.count() && (includeOpp || fTs[index].fOther->fOperand == fOperand) |
- && precisely_negative(fTs[index].fT - referenceT)); |
+ if (++index == fTs.count()) { |
+ break; |
+ } |
+ if (!allowOpp && fTs[index].fOther->fOperand != fOperand) { |
+ break; |
+ } |
+ if (fTs[index - 1].fTiny) { |
+ referenceT = fTs[index].fT; |
+ continue; |
+ } |
+ if (!precisely_negative(fTs[index].fT - referenceT) && fTs[index].fPt == referencePt) { |
+ // FIXME |
+ // testQuad8 generates the wrong output unless false is returned here. Other tests will |
+ // take this path although they aren't required to. This means that many go much slower |
+ // because of this sort fail. |
+ // SkDebugf("!!!\n"); |
+ return false; |
+ } |
+ } while (precisely_negative(fTs[index].fT - referenceT)); |
+ return true; |
} |
void SkOpSegment::buildAnglesInner(int index, SkTArray<SkOpAngle, true>* angles) const { |
@@ -851,115 +1053,175 @@ void SkOpSegment::buildAnglesInner(int index, SkTArray<SkOpAngle, true>* angles) |
other->addTwoAngles(next, oIndex, angles); |
} |
-int SkOpSegment::computeSum(int startIndex, int endIndex, bool binary) { |
- SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles; |
- addTwoAngles(startIndex, endIndex, &angles); |
- buildAngles(endIndex, &angles, false); |
- // OPTIMIZATION: check all angles to see if any have computed wind sum |
- // before sorting (early exit if none) |
- SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted; |
- // FIXME?: Not sure if this sort must be ordered or if the relaxed ordering is OK ... |
- bool sortable = SortAngles(angles, &sorted, SkOpSegment::kMustBeOrdered_SortAngleKind); |
-#if DEBUG_SORT |
- sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0, sortable); |
-#endif |
- if (!sortable) { |
- return SK_MinS32; |
+int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType includeType, |
+ SkTArray<SkOpAngle, true>* angles, SkTArray<SkOpAngle*, true>* sorted) { |
+ addTwoAngles(startIndex, endIndex, angles); |
+ if (!buildAngles(endIndex, angles, includeType == SkOpAngle::kBinaryOpp)) { |
+ return SK_NaN32; |
} |
- int angleCount = angles.count(); |
- const SkOpAngle* angle; |
- const SkOpSegment* base; |
- int winding; |
- int oWinding; |
- int firstIndex = 0; |
- do { |
- angle = sorted[firstIndex]; |
- base = angle->segment(); |
- winding = base->windSum(angle); |
- if (winding != SK_MinS32) { |
- oWinding = base->oppSum(angle); |
- break; |
+ int angleCount = angles->count(); |
+ // abort early before sorting if an unsortable (not an unorderable) angle is found |
+ if (includeType != SkOpAngle::kUnaryXor) { |
+ int firstIndex = -1; |
+ while (++firstIndex < angleCount) { |
+ const SkOpAngle& angle = (*angles)[firstIndex]; |
+ if (angle.segment()->windSum(&angle) != SK_MinS32) { |
+ break; |
+ } |
} |
- if (++firstIndex == angleCount) { |
+ if (firstIndex == angleCount) { |
return SK_MinS32; |
} |
- } while (true); |
- // turn winding into contourWinding |
- int spanWinding = base->spanSign(angle); |
- bool inner = UseInnerWinding(winding + spanWinding, winding); |
-#if DEBUG_WINDING |
- SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__, |
- spanWinding, winding, angle->sign(), inner, |
- inner ? winding + spanWinding : winding); |
-#endif |
- if (inner) { |
- winding += spanWinding; |
} |
-#if DEBUG_SORT |
- base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, oWinding, sortable); |
+ bool sortable = SortAngles2(*angles, sorted); |
+#if DEBUG_SORT_RAW |
+ if (sorted->count() > 0) { |
+ (*sorted)[0]->segment()->debugShowSort(__FUNCTION__, *sorted, 0, 0, 0, sortable); |
+ } |
#endif |
- int nextIndex = firstIndex + 1; |
- int lastIndex = firstIndex != 0 ? firstIndex : angleCount; |
- winding -= base->spanSign(angle); |
- oWinding -= base->oppSign(angle); |
+ if (!sortable) { |
+ return SK_NaN32; |
+ } |
+ if (includeType == SkOpAngle::kUnaryXor) { |
+ return SK_MinS32; |
+ } |
+ // if all angles have a computed winding, |
+ // or if no adjacent angles are orderable, |
+ // or if adjacent orderable angles have no computed winding, |
+ // there's nothing to do |
+ // if two orderable angles are adjacent, and one has winding computed, transfer to the other |
+ const SkOpAngle* baseAngle = NULL; |
+ int last = angleCount; |
+ int finalInitialOrderable = -1; |
+ bool tryReverse = false; |
+ // look for counterclockwise transfers |
do { |
- if (nextIndex == angleCount) { |
- nextIndex = 0; |
- } |
- angle = sorted[nextIndex]; |
- SkOpSegment* segment = angle->segment(); |
- bool opp = base->fOperand ^ segment->fOperand; |
- int maxWinding, oMaxWinding; |
- int spanSign = segment->spanSign(angle); |
- int oppoSign = segment->oppSign(angle); |
- if (opp) { |
- oMaxWinding = oWinding; |
- oWinding -= spanSign; |
- maxWinding = winding; |
- if (oppoSign) { |
- winding -= oppoSign; |
+ int index = 0; |
+ do { |
+ SkOpAngle* testAngle = (*sorted)[index]; |
+ int testWinding = testAngle->segment()->windSum(testAngle); |
+ if (SK_MinS32 != testWinding && !testAngle->unorderable()) { |
+ baseAngle = testAngle; |
+ continue; |
} |
- } else { |
- maxWinding = winding; |
- winding -= spanSign; |
- oMaxWinding = oWinding; |
- if (oppoSign) { |
- oWinding -= oppoSign; |
+ if (testAngle->unorderable()) { |
+ baseAngle = NULL; |
+ tryReverse = true; |
+ continue; |
} |
+ if (baseAngle) { |
+ ComputeOneSum(baseAngle, testAngle, includeType); |
+ baseAngle = SK_MinS32 != testAngle->segment()->windSum(testAngle) ? testAngle |
+ : NULL; |
+ tryReverse |= !baseAngle; |
+ continue; |
+ } |
+ if (finalInitialOrderable + 1 == index) { |
+ finalInitialOrderable = index; |
+ } |
+ } while (++index != last); |
+ if (finalInitialOrderable < 0) { |
+ break; |
} |
- if (segment->windSum(angle) == SK_MinS32) { |
- if (opp) { |
- if (UseInnerWinding(oMaxWinding, oWinding)) { |
- oMaxWinding = oWinding; |
+ last = finalInitialOrderable + 1; |
+ finalInitialOrderable = -2; // make this always negative the second time through |
+ } while (baseAngle); |
+ if (tryReverse) { |
+ baseAngle = NULL; |
+ last = 0; |
+ finalInitialOrderable = angleCount; |
+ do { |
+ int index = angleCount; |
+ while (--index >= last) { |
+ SkOpAngle* testAngle = (*sorted)[index]; |
+ int testWinding = testAngle->segment()->windSum(testAngle); |
+ if (SK_MinS32 != testWinding) { |
+ baseAngle = testAngle; |
+ continue; |
} |
- if (oppoSign && UseInnerWinding(maxWinding, winding)) { |
- maxWinding = winding; |
+ if (testAngle->unorderable()) { |
+ baseAngle = NULL; |
+ continue; |
} |
-#ifdef SK_DEBUG |
- SkASSERT(abs(maxWinding) <= gDebugMaxWindSum); |
- SkASSERT(abs(oMaxWinding) <= gDebugMaxWindSum); |
-#endif |
- (void) segment->markAndChaseWinding(angle, oMaxWinding, maxWinding); |
- } else { |
- if (UseInnerWinding(maxWinding, winding)) { |
- maxWinding = winding; |
+ if (baseAngle) { |
+ ComputeOneSumReverse(baseAngle, testAngle, includeType); |
+ baseAngle = SK_MinS32 != testAngle->segment()->windSum(testAngle) ? testAngle |
+ : NULL; |
+ continue; |
} |
- if (oppoSign && UseInnerWinding(oMaxWinding, oWinding)) { |
- oMaxWinding = oWinding; |
+ if (finalInitialOrderable - 1 == index) { |
+ finalInitialOrderable = index; |
} |
-#ifdef SK_DEBUG |
- SkASSERT(abs(maxWinding) <= gDebugMaxWindSum); |
- SkASSERT(abs(binary ? oMaxWinding : 0) <= gDebugMaxWindSum); |
-#endif |
- (void) segment->markAndChaseWinding(angle, maxWinding, |
- binary ? oMaxWinding : 0); |
} |
- } |
- } while (++nextIndex != lastIndex); |
+ if (finalInitialOrderable >= angleCount) { |
+ break; |
+ } |
+ last = finalInitialOrderable; |
+ finalInitialOrderable = angleCount + 1; // make this inactive 2nd time through |
+ } while (baseAngle); |
+ } |
int minIndex = SkMin32(startIndex, endIndex); |
return windSum(minIndex); |
} |
+void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle, |
+ SkOpAngle::IncludeType includeType) { |
+ const SkOpSegment* baseSegment = baseAngle->segment(); |
+ int sumMiWinding = baseSegment->updateWindingReverse(baseAngle); |
+ int sumSuWinding; |
+ bool binary = includeType >= SkOpAngle::kBinarySingle; |
+ if (binary) { |
+ sumSuWinding = baseSegment->updateOppWindingReverse(baseAngle); |
+ if (baseSegment->operand()) { |
+ SkTSwap<int>(sumMiWinding, sumSuWinding); |
+ } |
+ } |
+ SkOpSegment* nextSegment = nextAngle->segment(); |
+ int maxWinding, sumWinding; |
+ SkOpSpan* last; |
+ if (binary) { |
+ int oppMaxWinding, oppSumWinding; |
+ nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding, |
+ &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding); |
+ last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding, |
+ true, nextAngle); |
+ } else { |
+ nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding, |
+ &maxWinding, &sumWinding); |
+ last = nextSegment->markAngle(maxWinding, sumWinding, true, nextAngle); |
+ } |
+ nextAngle->setLastMarked(last); |
+} |
+ |
+void SkOpSegment::ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle, |
+ SkOpAngle::IncludeType includeType) { |
+ const SkOpSegment* baseSegment = baseAngle->segment(); |
+ int sumMiWinding = baseSegment->updateWinding(baseAngle); |
+ int sumSuWinding; |
+ bool binary = includeType >= SkOpAngle::kBinarySingle; |
+ if (binary) { |
+ sumSuWinding = baseSegment->updateOppWinding(baseAngle); |
+ if (baseSegment->operand()) { |
+ SkTSwap<int>(sumMiWinding, sumSuWinding); |
+ } |
+ } |
+ SkOpSegment* nextSegment = nextAngle->segment(); |
+ int maxWinding, sumWinding; |
+ SkOpSpan* last; |
+ if (binary) { |
+ int oppMaxWinding, oppSumWinding; |
+ nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding, |
+ &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding); |
+ last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding, |
+ true, nextAngle); |
+ } else { |
+ nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding, |
+ &maxWinding, &sumWinding); |
+ last = nextSegment->markAngle(maxWinding, sumWinding, true, nextAngle); |
+ } |
+ nextAngle->setLastMarked(last); |
+} |
+ |
int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hitT, |
bool* hitSomething, double mid, bool opp, bool current) const { |
SkScalar bottom = fBounds.fBottom; |
@@ -1050,18 +1312,20 @@ int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hi |
return bestTIndex; |
} |
-void SkOpSegment::decrementSpan(SkOpSpan* span) { |
+bool SkOpSegment::decrementSpan(SkOpSpan* span) { |
SkASSERT(span->fWindValue > 0); |
if (--(span->fWindValue) == 0) { |
if (!span->fOppValue && !span->fDone) { |
span->fDone = true; |
++fDoneSpans; |
+ return true; |
} |
} |
+ return false; |
} |
bool SkOpSegment::bumpSpan(SkOpSpan* span, int windDelta, int oppDelta) { |
- SkASSERT(!span->fDone); |
+ SkASSERT(!span->fDone || span->fTiny); |
span->fWindValue += windDelta; |
SkASSERT(span->fWindValue >= 0); |
span->fOppValue += oppDelta; |
@@ -1083,98 +1347,295 @@ bool SkOpSegment::bumpSpan(SkOpSpan* span, int windDelta, int oppDelta) { |
// look to see if the curve end intersects an intermediary that intersects the other |
void SkOpSegment::checkEnds() { |
debugValidate(); |
- SkTDArray<SkOpSpan> missingSpans; |
+ SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans; |
int count = fTs.count(); |
for (int index = 0; index < count; ++index) { |
const SkOpSpan& span = fTs[index]; |
- const SkOpSegment* other = span.fOther; |
- const SkOpSpan* otherSpan = &other->fTs[span.fOtherIndex]; |
- double otherT = otherSpan->fT; |
- if (otherT != 0 && otherT != 1) { |
+ double otherT = span.fOtherT; |
+ if (otherT != 0 && otherT != 1) { // only check ends |
continue; |
} |
+ const SkOpSegment* other = span.fOther; |
+ // peek start/last describe the range of spans that match the other t of this span |
int peekStart = span.fOtherIndex; |
- while (peekStart > 0) { |
- const SkOpSpan* peeker = &other->fTs[peekStart - 1]; |
- if (peeker->fT != otherT) { |
- break; |
- } |
- --peekStart; |
- } |
- int otherLast = other->fTs.count() - 1; |
+ while (--peekStart >= 0 && other->fTs[peekStart].fT == otherT) |
+ ; |
+ int otherCount = other->fTs.count(); |
int peekLast = span.fOtherIndex; |
- while (peekLast < otherLast) { |
- const SkOpSpan* peeker = &other->fTs[peekLast + 1]; |
- if (peeker->fT != otherT) { |
- break; |
- } |
- ++peekLast; |
- } |
- if (peekStart == peekLast) { |
+ while (++peekLast < otherCount && other->fTs[peekLast].fT == otherT) |
+ ; |
+ if (++peekStart == --peekLast) { // if there isn't a range, there's nothing to do |
continue; |
} |
+ // t start/last describe the range of spans that match the t of this span |
double t = span.fT; |
int tStart = index; |
- while (tStart > 0 && t == fTs[tStart - 1].fT) { |
- --tStart; |
- } |
+ while (--tStart >= 0 && (t == fTs[tStart].fT || fTs[tStart].fTiny)) |
+ ; |
int tLast = index; |
- int last = count - 1; |
- while (tLast < last && t == fTs[tLast + 1].fT) { |
+ while (fTs[tLast].fTiny) { |
++tLast; |
} |
+ while (++tLast < count && t == fTs[tLast].fT) |
+ ; |
for (int peekIndex = peekStart; peekIndex <= peekLast; ++peekIndex) { |
- if (peekIndex == span.fOtherIndex) { |
+ if (peekIndex == span.fOtherIndex) { // skip the other span pointed to by this span |
continue; |
} |
const SkOpSpan& peekSpan = other->fTs[peekIndex]; |
SkOpSegment* match = peekSpan.fOther; |
const double matchT = peekSpan.fOtherT; |
- for (int tIndex = tStart; tIndex <= tLast; ++tIndex) { |
+ // see if any of the spans match the other spans |
+ for (int tIndex = tStart + 1; tIndex < tLast; ++tIndex) { |
const SkOpSpan& tSpan = fTs[tIndex]; |
- if (tSpan.fOther == match && tSpan.fOtherT == matchT) { |
- goto nextPeeker; |
+ if (tSpan.fOther == match) { |
+ if (tSpan.fOtherT == matchT) { |
+ goto nextPeeker; |
+ } |
+ double midT = (tSpan.fOtherT + matchT) / 2; |
+ if (match->betweenPoints(midT, tSpan.fPt, peekSpan.fPt)) { |
+ goto nextPeeker; |
+ } |
} |
} |
+ if (missingSpans.count() > 0) { |
+ const MissingSpan& lastMissing = missingSpans.back(); |
+ if (lastMissing.fCommand == MissingSpan::kAddMissing |
+ && lastMissing.fT == t |
+ && lastMissing.fOther == match |
+ && lastMissing.fOtherT == matchT) { |
+ SkASSERT(lastMissing.fPt == peekSpan.fPt); |
+ continue; |
+ } |
+ } |
+#if DEBUG_CHECK_ENDS |
+ SkDebugf("%s id=%d missing t=%1.9g other=%d otherT=%1.9g pt=(%1.9g,%1.9g)\n", |
+ __FUNCTION__, fID, t, match->fID, matchT, peekSpan.fPt.fX, peekSpan.fPt.fY); |
+#endif |
// this segment is missing a entry that the other contains |
// remember so we can add the missing one and recompute the indices |
- SkOpSpan* missing = missingSpans.append(); |
- missing->fT = t; |
- missing->fOther = match; |
- missing->fOtherT = matchT; |
- missing->fPt = peekSpan.fPt; |
+ MissingSpan& missing = missingSpans.push_back(); |
+ SkDEBUGCODE(sk_bzero(&missing, sizeof(missing))); |
+ missing.fCommand = MissingSpan::kAddMissing; |
+ missing.fT = t; |
+ missing.fOther = match; |
+ missing.fOtherT = matchT; |
+ missing.fPt = peekSpan.fPt; |
} |
nextPeeker: |
; |
} |
- int missingCount = missingSpans.count(); |
- if (missingCount == 0) { |
+ if (missingSpans.count() == 0) { |
return; |
} |
+ // if one end is near the other point, look for a coincident span |
+ for (int index = 0; index < count; ++index) { |
+ const SkOpSpan& span = fTs[index]; |
+ if (span.fT > 0) { |
+ break; |
+ } |
+ const SkOpSpan& otherSpan = span.fOther->span(span.fOtherIndex); |
+ if (span.fNear) { |
+ SkASSERT(otherSpan.fPt == fPts[0]); |
+ adjustNear(0, span.fPt, &missingSpans); |
+ continue; |
+ } |
+ if (otherSpan.fNear) { |
+ SkASSERT(span.fPt == fPts[0]); |
+ adjustNear(0, otherSpan.fPt, &missingSpans); |
+ } |
+ } |
+ for (int index = count; --index >= 0; ) { |
+ const SkOpSpan& span = fTs[index]; |
+ if (span.fT < 1) { |
+ break; |
+ } |
+ const SkOpSegment* other = span.fOther; |
+ if (span.fNear) { |
+ SkASSERT(other->ptAtT(span.fOtherT) == ptAtT(1)); |
+ const SkPoint& otherPt = other->xyAtT(span.fOtherIndex); |
+ SkASSERT(otherPt != ptAtT(1)); |
+ adjustNear(1, otherPt, &missingSpans); |
+ continue; |
+ } |
+ const SkOpSpan& otherSpan = other->span(span.fOtherIndex); |
+ if (otherSpan.fNear) { |
+ SkASSERT(otherSpan.fPt == ptAtT(1)); |
+ SkPoint otherPt = other->ptAtT(span.fOtherT); |
+ SkASSERT(otherPt != ptAtT(1)); |
+ adjustNear(1, otherPt, &missingSpans); |
+ } |
+ } |
debugValidate(); |
+ int missingCount = missingSpans.count(); |
for (int index = 0; index < missingCount; ++index) { |
- const SkOpSpan& missing = missingSpans[index]; |
- addTPair(missing.fT, missing.fOther, missing.fOtherT, false, missing.fPt); |
+ MissingSpan& missing = missingSpans[index]; |
+ switch (missing.fCommand) { |
+ case MissingSpan::kNoAction: |
+ break; |
+ case MissingSpan::kAddMissing: |
+ addTPair(missing.fT, missing.fOther, missing.fOtherT, false, missing.fPt); |
+ break; |
+ case MissingSpan::kRemoveNear: { |
+ SkOpSegment* segment = missing.fSegment; |
+ int count = segment->count(); |
+ for (int inner = 0; inner < count; ++inner) { |
+ const SkOpSpan& span = segment->span(inner); |
+ if (span.fT != missing.fT && span.fOther != missing.fOther) { |
+ continue; |
+ } |
+ SkASSERT(span.fNear); |
+ SkOpSegment* other = span.fOther; |
+ int otherCount = other->count(); |
+ for (int otherIndex = 0; otherIndex < otherCount; ++otherIndex) { |
+ const SkOpSpan& otherSpan = other->span(otherIndex); |
+ if (otherSpan.fT == span.fOtherT && otherSpan.fOther == segment |
+ && otherSpan.fOtherT == span.fT) { |
+ if (otherSpan.fDone) { |
+ other->fDoneSpans--; |
+ } |
+ other->fTs.remove(otherIndex); |
+ // FIXME: remove may leave a tiny dangling -- recompute tiny w/index |
+ break; |
+ } |
+ } |
+ if (span.fDone) { |
+ segment->fDoneSpans--; |
+ } |
+ segment->fTs.remove(inner); |
+ // FIXME: remove may leave a tiny dangling -- recompute tiny w/index |
+ break; |
+ } |
+ break; |
+ } |
+ case MissingSpan::kZeroSpan: { |
+ SkOpSegment* segment = missing.fSegment; |
+ int count = segment->count(); |
+ for (int inner = 0; inner < count; ++inner) { |
+ SkOpSpan& span = segment->fTs[inner]; |
+ if (span.fT < missing.fT) { |
+ continue; |
+ } |
+ if (span.fT >= missing.fEndT) { |
+ break; |
+ } |
+ span.fWindValue = span.fOppValue = 0; |
+ if (!span.fDone) { |
+ span.fDone = true; |
+ ++segment->fDoneSpans; |
+ } |
+ } |
+ break; |
+ } |
+ } |
} |
fixOtherTIndex(); |
+ // OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to |
+ // avoid this |
for (int index = 0; index < missingCount; ++index) { |
- const SkOpSpan& missing = missingSpans[index]; |
- missing.fOther->fixOtherTIndex(); |
+ const MissingSpan& missing = missingSpans[index]; |
+ switch (missing.fCommand) { |
+ case MissingSpan::kNoAction: |
+ break; |
+ case MissingSpan::kAddMissing: |
+ missing.fOther->fixOtherTIndex(); |
+ break; |
+ case MissingSpan::kRemoveNear: |
+ missing.fSegment->fixOtherTIndex(); |
+ missing.fOther->fixOtherTIndex(); |
+ break; |
+ case MissingSpan::kZeroSpan: |
+ break; |
+ } |
} |
debugValidate(); |
} |
-bool SkOpSegment::equalPoints(int greaterTIndex, int lesserTIndex) { |
- SkASSERT(greaterTIndex >= lesserTIndex); |
- double greaterT = fTs[greaterTIndex].fT; |
- double lesserT = fTs[lesserTIndex].fT; |
- if (greaterT == lesserT) { |
+bool SkOpSegment::checkSmall(int index) const { |
+ if (fTs[index].fSmall) { |
return true; |
} |
- if (!approximately_negative(greaterT - lesserT)) { |
- return false; |
+ double tBase = fTs[index].fT; |
+ while (index > 0 && precisely_negative(tBase - fTs[--index].fT)) |
+ ; |
+ return fTs[index].fSmall; |
+} |
+ |
+// if pair of spans on either side of tiny have the same end point and mid point, mark |
+// them as parallel |
+// OPTIMIZATION : mark the segment to note that some span is tiny |
+void SkOpSegment::checkTiny() { |
+ SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans; |
+ SkOpSpan* thisSpan = fTs.begin() - 1; |
+ const SkOpSpan* endSpan = fTs.end() - 1; // last can't be tiny |
+ while (++thisSpan < endSpan) { |
+ if (!thisSpan->fTiny) { |
+ continue; |
+ } |
+ SkOpSpan* nextSpan = thisSpan + 1; |
+ double thisT = thisSpan->fT; |
+ double nextT = nextSpan->fT; |
+ if (thisT == nextT) { |
+ continue; |
+ } |
+ SkASSERT(thisT < nextT); |
+ SkASSERT(thisSpan->fPt == nextSpan->fPt); |
+ SkOpSegment* thisOther = thisSpan->fOther; |
+ SkOpSegment* nextOther = nextSpan->fOther; |
+ int oIndex = thisSpan->fOtherIndex; |
+ for (int oStep = -1; oStep <= 1; oStep += 2) { |
+ int oEnd = thisOther->nextExactSpan(oIndex, oStep); |
+ if (oEnd < 0) { |
+ continue; |
+ } |
+ const SkOpSpan& oSpan = thisOther->span(oEnd); |
+ int nIndex = nextSpan->fOtherIndex; |
+ for (int nStep = -1; nStep <= 1; nStep += 2) { |
+ int nEnd = nextOther->nextExactSpan(nIndex, nStep); |
+ if (nEnd < 0) { |
+ continue; |
+ } |
+ const SkOpSpan& nSpan = nextOther->span(nEnd); |
+ if (oSpan.fPt != nSpan.fPt) { |
+ continue; |
+ } |
+ double oMidT = (thisSpan->fOtherT + oSpan.fT) / 2; |
+ const SkPoint& oPt = thisOther->ptAtT(oMidT); |
+ double nMidT = (nextSpan->fOtherT + nSpan.fT) / 2; |
+ const SkPoint& nPt = nextOther->ptAtT(nMidT); |
+ if (!AlmostEqualUlps(oPt, nPt)) { |
+ continue; |
+ } |
+#if DEBUG_CHECK_TINY |
+ SkDebugf("%s [%d] add coincidence [%d] [%d]\n", __FUNCTION__, fID, |
+ thisOther->fID, nextOther->fID); |
+#endif |
+ // this segment is missing a entry that the other contains |
+ // remember so we can add the missing one and recompute the indices |
+ MissingSpan& missing = missingSpans.push_back(); |
+ SkDEBUGCODE(sk_bzero(&missing, sizeof(missing))); |
+ missing.fCommand = MissingSpan::kAddMissing; |
+ missing.fSegment = thisOther; |
+ missing.fT = thisSpan->fOtherT; |
+ missing.fOther = nextOther; |
+ missing.fOtherT = nextSpan->fOtherT; |
+ missing.fPt = thisSpan->fPt; |
+ } |
+ } |
+ } |
+ int missingCount = missingSpans.count(); |
+ if (!missingCount) { |
+ return; |
+ } |
+ for (int index = 0; index < missingCount; ++index) { |
+ MissingSpan& missing = missingSpans[index]; |
+ missing.fSegment->addTPair(missing.fT, missing.fOther, missing.fOtherT, false, missing.fPt); |
+ } |
+ for (int index = 0; index < missingCount; ++index) { |
+ MissingSpan& missing = missingSpans[index]; |
+ missing.fSegment->fixOtherTIndex(); |
+ missing.fOther->fixOtherTIndex(); |
} |
- return xyAtT(greaterTIndex) == xyAtT(lesserTIndex); |
} |
/* |
@@ -1214,8 +1675,7 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart |
*nextEnd = *nextStart; |
do { |
*nextEnd += step; |
- } |
- while (precisely_zero(startT - other->fTs[*nextEnd].fT)); |
+ } while (precisely_zero(startT - other->fTs[*nextEnd].fT)); |
SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count()); |
if (other->isTiny(SkMin32(*nextStart, *nextEnd))) { |
*unsortable = true; |
@@ -1227,13 +1687,12 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart |
SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles; |
SkASSERT(startIndex - endIndex != 0); |
SkASSERT((startIndex - endIndex < 0) ^ (step < 0)); |
- addTwoAngles(startIndex, end, &angles); |
- buildAngles(end, &angles, true); |
SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted; |
- bool sortable = SortAngles(angles, &sorted, SkOpSegment::kMustBeOrdered_SortAngleKind); |
+ int calcWinding = computeSum(startIndex, end, SkOpAngle::kBinaryOpp, &angles, &sorted); |
+ bool sortable = calcWinding != SK_NaN32; |
int angleCount = angles.count(); |
int firstIndex = findStartingEdge(sorted, startIndex, end); |
- SkASSERT(firstIndex >= 0); |
+ SkASSERT(!sortable || firstIndex >= 0); |
#if DEBUG_SORT |
debugShowSort(__FUNCTION__, sorted, firstIndex, sortable); |
#endif |
@@ -1277,22 +1736,25 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart |
return NULL; |
} |
foundAngle = nextAngle; |
- foundDone = nextSegment->done(nextAngle) && !nextSegment->isTiny(nextAngle); |
+ foundDone = nextSegment->done(nextAngle); |
} |
} |
if (nextSegment->done()) { |
continue; |
} |
- if (nextSegment->windSum(nextAngle) != SK_MinS32) { |
+ if (nextSegment->isTiny(nextAngle)) { |
continue; |
} |
- SkOpSpan* last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, |
- oppSumWinding, activeAngle, nextAngle); |
+ if (!activeAngle) { |
+ nextSegment->markAndChaseDoneBinary(nextAngle->start(), nextAngle->end()); |
+ } |
+ SkOpSpan* last = nextAngle->lastMarked(); |
if (last) { |
*chase->append() = last; |
#if DEBUG_WINDING |
- SkDebugf("%s chase.append id=%d\n", __FUNCTION__, |
- last->fOther->fTs[last->fOtherIndex].fOther->debugID()); |
+ SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__, |
+ last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum, |
+ last->fSmall); |
#endif |
} |
} while (++nextIndex != lastIndex); |
@@ -1303,7 +1765,6 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart |
*nextStart = foundAngle->start(); |
*nextEnd = foundAngle->end(); |
nextSegment = foundAngle->segment(); |
- |
#if DEBUG_WINDING |
SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n", |
__FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd); |
@@ -1340,22 +1801,24 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* next |
*nextEnd = *nextStart; |
do { |
*nextEnd += step; |
- } |
- while (precisely_zero(startT - other->fTs[*nextEnd].fT)); |
+ } while (precisely_zero(startT - other->fTs[*nextEnd].fT)); |
SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count()); |
+ if (other->isTiny(SkMin32(*nextStart, *nextEnd))) { |
+ *unsortable = true; |
+ return NULL; |
+ } |
return other; |
} |
// more than one viable candidate -- measure angles to find best |
SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles; |
SkASSERT(startIndex - endIndex != 0); |
SkASSERT((startIndex - endIndex < 0) ^ (step < 0)); |
- addTwoAngles(startIndex, end, &angles); |
- buildAngles(end, &angles, true); |
SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted; |
- bool sortable = SortAngles(angles, &sorted, SkOpSegment::kMustBeOrdered_SortAngleKind); |
+ int calcWinding = computeSum(startIndex, end, SkOpAngle::kUnaryWinding, &angles, &sorted); |
+ bool sortable = calcWinding != SK_NaN32; |
int angleCount = angles.count(); |
int firstIndex = findStartingEdge(sorted, startIndex, end); |
- SkASSERT(firstIndex >= 0); |
+ SkASSERT(!sortable || firstIndex >= 0); |
#if DEBUG_SORT |
debugShowSort(__FUNCTION__, sorted, firstIndex, sortable); |
#endif |
@@ -1400,15 +1863,19 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* next |
if (nextSegment->done()) { |
continue; |
} |
- if (nextSegment->windSum(nextAngle) != SK_MinS32) { |
+ if (nextSegment->isTiny(nextAngle)) { |
continue; |
} |
- SkOpSpan* last = nextSegment->markAngle(maxWinding, sumWinding, activeAngle, nextAngle); |
+ if (!activeAngle) { |
+ nextSegment->markAndChaseDoneUnary(nextAngle->start(), nextAngle->end()); |
+ } |
+ SkOpSpan* last = nextAngle->lastMarked(); |
if (last) { |
*chase->append() = last; |
#if DEBUG_WINDING |
- SkDebugf("%s chase.append id=%d\n", __FUNCTION__, |
- last->fOther->fTs[last->fOtherIndex].fOther->debugID()); |
+ SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__, |
+ last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum, |
+ last->fSmall); |
#endif |
} |
} while (++nextIndex != lastIndex); |
@@ -1431,8 +1898,7 @@ SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsort |
const int endIndex = *nextEnd; |
SkASSERT(startIndex != endIndex); |
SkDEBUGCODE(int count = fTs.count()); |
- SkASSERT(startIndex < endIndex ? startIndex < count - 1 |
- : startIndex > 0); |
+ SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0); |
int step = SkSign32(endIndex - startIndex); |
int end = nextExactSpan(startIndex, step); |
SkASSERT(end >= 0); |
@@ -1461,14 +1927,11 @@ SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsort |
*nextEnd = *nextStart; |
do { |
*nextEnd += step; |
- } |
- while (precisely_zero(startT - other->fTs[*nextEnd].fT)); |
+ } while (precisely_zero(startT - other->fTs[*nextEnd].fT)); |
if (other->fTs[SkMin32(*nextStart, *nextEnd)].fWindValue) { |
break; |
} |
-#ifdef SK_DEBUG |
SkASSERT(firstLoop); |
-#endif |
SkDEBUGCODE(firstLoop = false;) |
step = -step; |
} while (true); |
@@ -1478,25 +1941,24 @@ SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsort |
SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles; |
SkASSERT(startIndex - endIndex != 0); |
SkASSERT((startIndex - endIndex < 0) ^ (step < 0)); |
- addTwoAngles(startIndex, end, &angles); |
- buildAngles(end, &angles, false); |
SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted; |
- bool sortable = SortAngles(angles, &sorted, SkOpSegment::kMustBeOrdered_SortAngleKind); |
- if (!sortable) { |
- *unsortable = true; |
-#if DEBUG_SORT |
- debugShowSort(__FUNCTION__, sorted, findStartingEdge(sorted, startIndex, end), 0, 0, |
- sortable); |
-#endif |
- return NULL; |
- } |
+ int calcWinding = computeSum(startIndex, end, SkOpAngle::kUnaryXor, &angles, &sorted); |
+ bool sortable = calcWinding != SK_NaN32; |
int angleCount = angles.count(); |
int firstIndex = findStartingEdge(sorted, startIndex, end); |
- SkASSERT(firstIndex >= 0); |
+ SkASSERT(!sortable || firstIndex >= 0); |
#if DEBUG_SORT |
debugShowSort(__FUNCTION__, sorted, firstIndex, 0, 0, sortable); |
#endif |
+ if (!sortable) { |
+ *unsortable = true; |
+ return NULL; |
+ } |
SkASSERT(sorted[firstIndex]->segment() == this); |
+#if DEBUG_WINDING |
+ SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex, |
+ sorted[firstIndex]->sign()); |
+#endif |
int nextIndex = firstIndex + 1; |
int lastIndex = firstIndex != 0 ? firstIndex : angleCount; |
const SkOpAngle* foundAngle = NULL; |
@@ -1551,138 +2013,6 @@ int SkOpSegment::findStartingEdge(const SkTArray<SkOpAngle*, true>& sorted, int |
return firstIndex; |
} |
-// FIXME: this is tricky code; needs its own unit test |
-// note that fOtherIndex isn't computed yet, so it can't be used here |
-void SkOpSegment::findTooCloseToCall() { |
- int count = fTs.count(); |
- if (count < 3) { // require t=0, x, 1 at minimum |
- return; |
- } |
- int matchIndex = 0; |
- int moCount; |
- SkOpSpan* match; |
- SkOpSegment* mOther; |
- do { |
- match = &fTs[matchIndex]; |
- mOther = match->fOther; |
- // FIXME: allow quads, cubics to be near coincident? |
- if (mOther->fVerb == SkPath::kLine_Verb) { |
- moCount = mOther->fTs.count(); |
- if (moCount >= 3) { |
- break; |
- } |
- } |
- if (++matchIndex >= count) { |
- return; |
- } |
- } while (true); // require t=0, x, 1 at minimum |
- // OPTIMIZATION: defer matchPt until qualifying toCount is found? |
- const SkPoint* matchPt = &xyAtT(match); |
- // look for a pair of nearby T values that map to the same (x,y) value |
- // if found, see if the pair of other segments share a common point. If |
- // so, the span from here to there is coincident. |
- for (int index = matchIndex + 1; index < count; ++index) { |
- SkOpSpan* test = &fTs[index]; |
- if (test->fDone) { |
- continue; |
- } |
- SkOpSegment* tOther = test->fOther; |
- if (tOther->fVerb != SkPath::kLine_Verb) { |
- continue; // FIXME: allow quads, cubics to be near coincident? |
- } |
- int toCount = tOther->fTs.count(); |
- if (toCount < 3) { // require t=0, x, 1 at minimum |
- continue; |
- } |
- const SkPoint* testPt = &xyAtT(test); |
- if (*matchPt != *testPt) { |
- matchIndex = index; |
- moCount = toCount; |
- match = test; |
- mOther = tOther; |
- matchPt = testPt; |
- continue; |
- } |
- int moStart = -1; |
- int moEnd = -1; |
- double moStartT = 0; |
- double moEndT = 0; |
- for (int moIndex = 0; moIndex < moCount; ++moIndex) { |
- SkOpSpan& moSpan = mOther->fTs[moIndex]; |
- if (moSpan.fDone) { |
- continue; |
- } |
- if (moSpan.fOther == this) { |
- if (moSpan.fOtherT == match->fT) { |
- moStart = moIndex; |
- moStartT = moSpan.fT; |
- } |
- continue; |
- } |
- if (moSpan.fOther == tOther) { |
- if (tOther->windValueAt(moSpan.fOtherT) == 0) { |
- moStart = -1; |
- break; |
- } |
- SkASSERT(moEnd == -1); |
- moEnd = moIndex; |
- moEndT = moSpan.fT; |
- } |
- } |
- if (moStart < 0 || moEnd < 0) { |
- continue; |
- } |
- // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test |
- if (approximately_equal(moStartT, moEndT)) { |
- continue; |
- } |
- int toStart = -1; |
- int toEnd = -1; |
- double toStartT = 0; |
- double toEndT = 0; |
- for (int toIndex = 0; toIndex < toCount; ++toIndex) { |
- SkOpSpan& toSpan = tOther->fTs[toIndex]; |
- if (toSpan.fDone) { |
- continue; |
- } |
- if (toSpan.fOther == this) { |
- if (toSpan.fOtherT == test->fT) { |
- toStart = toIndex; |
- toStartT = toSpan.fT; |
- } |
- continue; |
- } |
- if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) { |
- if (mOther->windValueAt(toSpan.fOtherT) == 0) { |
- moStart = -1; |
- break; |
- } |
- SkASSERT(toEnd == -1); |
- toEnd = toIndex; |
- toEndT = toSpan.fT; |
- } |
- } |
- // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test |
- if (toStart <= 0 || toEnd <= 0) { |
- continue; |
- } |
- if (approximately_equal(toStartT, toEndT)) { |
- continue; |
- } |
- // test to see if the segment between there and here is linear |
- if (!mOther->isLinear(moStart, moEnd) |
- || !tOther->isLinear(toStart, toEnd)) { |
- continue; |
- } |
- bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1; |
- if (flipped) { |
- mOther->addTCancel(moStartT, moEndT, tOther, toEndT, toStartT); |
- } else { |
- mOther->addTCoincident(moStartT, moEndT, tOther, toStartT, toEndT); |
- } |
- } |
-} |
- |
// FIXME: either: |
// a) mark spans with either end unsortable as done, or |
// b) rewrite findTop / findTopSegment / findTopContour to iterate further |
@@ -1722,14 +2052,24 @@ SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsort |
SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles; |
SkASSERT(firstT - end != 0); |
addTwoAngles(end, firstT, &angles); |
- buildAngles(firstT, &angles, true); |
+ if (!buildAngles(firstT, &angles, true) && onlySortable) { |
+// *unsortable = true; |
+// return NULL; |
+ } |
SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted; |
bool sortable = SortAngles(angles, &sorted, SkOpSegment::kMayBeUnordered_SortAngleKind); |
+ if (onlySortable && !sortable) { |
+ *unsortable = true; |
+ return NULL; |
+ } |
int first = SK_MaxS32; |
SkScalar top = SK_ScalarMax; |
int count = sorted.count(); |
for (int index = 0; index < count; ++index) { |
const SkOpAngle* angle = sorted[index]; |
+ if (onlySortable && angle->unorderable()) { |
+ continue; |
+ } |
SkOpSegment* next = angle->segment(); |
SkPathOpsBounds bounds; |
next->subDivideBounds(angle->end(), angle->start(), &bounds); |
@@ -1742,10 +2082,6 @@ SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsort |
#if DEBUG_SORT // || DEBUG_SWAP_TOP |
sorted[first]->segment()->debugShowSort(__FUNCTION__, sorted, first, 0, 0, sortable); |
#endif |
- if (onlySortable && !sortable) { |
- *unsortable = true; |
- return NULL; |
- } |
// skip edges that have already been processed |
firstT = first - 1; |
SkOpSegment* leftSegment; |
@@ -1789,6 +2125,7 @@ SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsort |
// while the following fixes the indices up again, it isn't smart about |
// skipping segments whose indices are already correct |
// assuming we leave the code that wrote the index in the first place |
+// FIXME: if called after remove, this needs to correct tiny |
void SkOpSegment::fixOtherTIndex() { |
int iCount = fTs.count(); |
for (int i = 0; i < iCount; ++i) { |
@@ -1869,20 +2206,6 @@ void SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkSc |
(void) markAndChaseWinding(start, end, winding, oppWind); |
} |
-bool SkOpSegment::isLinear(int start, int end) const { |
- if (fVerb == SkPath::kLine_Verb) { |
- return true; |
- } |
- if (fVerb == SkPath::kQuad_Verb) { |
- SkDQuad qPart = SkDQuad::SubDivide(fPts, fTs[start].fT, fTs[end].fT); |
- return qPart.isLinear(0, 2); |
- } else { |
- SkASSERT(fVerb == SkPath::kCubic_Verb); |
- SkDCubic cPart = SkDCubic::SubDivide(fPts, fTs[start].fT, fTs[end].fT); |
- return cPart.isLinear(0, 3); |
- } |
-} |
- |
// OPTIMIZE: successive calls could start were the last leaves off |
// or calls could specialize to walk forwards or backwards |
bool SkOpSegment::isMissing(double startT) const { |
@@ -2027,6 +2350,13 @@ SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, bool activeAngl |
} else { |
last = markAndChaseDoneUnary(angle, maxWinding); |
} |
+#if DEBUG_WINDING |
+ if (last) { |
+ SkDebugf("%s last id=%d windSum=%d small=%d\n", __FUNCTION__, |
+ last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum, |
+ last->fSmall); |
+ } |
+#endif |
return last; |
} |
@@ -2045,6 +2375,13 @@ SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWindi |
} else { |
last = markAndChaseDoneBinary(angle, maxWinding, oppMaxWinding); |
} |
+#if DEBUG_WINDING |
+ if (last) { |
+ SkDebugf("%s last id=%d windSum=%d small=%d\n", __FUNCTION__, |
+ last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum, |
+ last->fSmall); |
+ } |
+#endif |
return last; |
} |
@@ -2146,9 +2483,7 @@ SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int windi |
debugShowNewWinding(funName, span, winding); |
#endif |
SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding); |
-#ifdef SK_DEBUG |
- SkASSERT(abs(winding) <= gDebugMaxWindSum); |
-#endif |
+ SkASSERT(abs(winding) <= SkPathOpsDebug::gMaxWindSum); |
span.fWindSum = winding; |
return &span; |
} |
@@ -2163,14 +2498,10 @@ SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int windi |
debugShowNewWinding(funName, span, winding, oppWinding); |
#endif |
SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding); |
-#ifdef SK_DEBUG |
- SkASSERT(abs(winding) <= gDebugMaxWindSum); |
-#endif |
+ SkASSERT(abs(winding) <= SkPathOpsDebug::gMaxWindSum); |
span.fWindSum = winding; |
SkASSERT(span.fOppSum == SK_MinS32 || span.fOppSum == oppWinding); |
-#ifdef SK_DEBUG |
- SkASSERT(abs(oppWinding) <= gDebugMaxWindSum); |
-#endif |
+ SkASSERT(abs(oppWinding) <= SkPathOpsDebug::gMaxWindSum); |
span.fOppSum = oppWinding; |
return &span; |
} |
@@ -2331,6 +2662,21 @@ void SkOpSegment::matchWindingValue(int tIndex, double t, bool borrowWind) { |
} |
} |
+double SkOpSegment::missingNear(double t, const SkOpSegment* other, const SkPoint& startPt, |
+ const SkPoint& endPt) const { |
+ int count = this->count(); |
+ for (int index = 0; index < count; ++index) { |
+ const SkOpSpan& span = this->span(index); |
+ if (span.fOther == other && span.fPt == startPt) { |
+ double midT = (t + span.fT) / 2; |
+ if (betweenPoints(midT, startPt, endPt)) { |
+ return span.fT; |
+ } |
+ } |
+ } |
+ return -1; |
+} |
+ |
// return span if when chasing, two or more radiating spans are not done |
// OPTIMIZATION: ? multiple spans is detected when there is only one valid |
// candidate and the remaining spans have windValue == 0 (canceled by |
@@ -2355,6 +2701,10 @@ bool SkOpSegment::nextCandidate(int* start, int* end) const { |
SkOpSegment* SkOpSegment::nextChase(int* index, const int step, int* min, SkOpSpan** last) { |
int end = nextExactSpan(*index, step); |
SkASSERT(end >= 0); |
+ if (fTs[end].fSmall) { |
+ *last = NULL; |
+ return NULL; |
+ } |
if (multipleSpans(end)) { |
*last = &fTs[end]; |
return NULL; |
@@ -2366,7 +2716,7 @@ SkOpSegment* SkOpSegment::nextChase(int* index, const int step, int* min, SkOpSp |
int otherEnd = other->nextExactSpan(*index, step); |
SkASSERT(otherEnd >= 0); |
*min = SkMin32(*index, otherEnd); |
- if (other->fTs[*min].fTiny) { |
+ if (other->fTs[*min].fSmall) { |
*last = NULL; |
return NULL; |
} |
@@ -2397,18 +2747,30 @@ int SkOpSegment::nextSpan(int from, int step) const { |
// FIXME |
// this returns at any difference in T, vs. a preset minimum. It may be |
// that all callers to nextSpan should use this instead. |
-// OPTIMIZATION splitting this into separate loops for up/down steps |
-// would allow using precisely_negative instead of precisely_zero |
int SkOpSegment::nextExactSpan(int from, int step) const { |
- const SkOpSpan& fromSpan = fTs[from]; |
- int count = fTs.count(); |
int to = from; |
- while (step > 0 ? ++to < count : --to >= 0) { |
- const SkOpSpan& span = fTs[to]; |
- if (precisely_zero(span.fT - fromSpan.fT)) { |
- continue; |
+ if (step < 0) { |
+ const SkOpSpan& fromSpan = fTs[from]; |
+ while (--to >= 0) { |
+ const SkOpSpan& span = fTs[to]; |
+ if (precisely_negative(fromSpan.fT - span.fT) || span.fTiny) { |
+ continue; |
+ } |
+ return to; |
+ } |
+ } else { |
+ while (fTs[from].fTiny) { |
+ from++; |
+ } |
+ const SkOpSpan& fromSpan = fTs[from]; |
+ int count = fTs.count(); |
+ while (++to < count) { |
+ const SkOpSpan& span = fTs[to]; |
+ if (precisely_negative(span.fT - fromSpan.fT)) { |
+ continue; |
+ } |
+ return to; |
} |
- return to; |
} |
return -1; |
} |
@@ -2428,6 +2790,16 @@ void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding, int* |
*oppMaxWinding = *sumSuWinding; |
*oppSumWinding = *sumSuWinding -= oppDeltaSum; |
} |
+ SkASSERT(abs(*sumWinding) <= SkPathOpsDebug::gMaxWindSum); |
+ SkASSERT(abs(*oppSumWinding) <= SkPathOpsDebug::gMaxWindSum); |
+} |
+ |
+void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding, |
+ int* maxWinding, int* sumWinding) { |
+ int deltaSum = spanSign(index, endIndex); |
+ *maxWinding = *sumMiWinding; |
+ *sumWinding = *sumMiWinding -= deltaSum; |
+ SkASSERT(abs(*sumWinding) <= SkPathOpsDebug::gMaxWindSum); |
} |
// This marks all spans unsortable so that this info is available for early |
@@ -2440,8 +2812,6 @@ bool SkOpSegment::SortAngles(const SkTArray<SkOpAngle, true>& angles, |
bool sortable = true; |
int angleCount = angles.count(); |
int angleIndex; |
-// FIXME: caller needs to use SkTArray constructor with reserve count |
-// angleList->setReserve(angleCount); |
for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) { |
const SkOpAngle& angle = angles[angleIndex]; |
angleList->push_back(const_cast<SkOpAngle*>(&angle)); |
@@ -2470,6 +2840,34 @@ bool SkOpSegment::SortAngles(const SkTArray<SkOpAngle, true>& angles, |
return sortable; |
} |
+// set segments to unsortable if angle is unsortable, but do not set all angles |
+// note that for a simple 4 way crossing, two of the edges may be orderable even though |
+// two edges are too short to be orderable. |
+// perhaps some classes of unsortable angles should make all shared angles unsortable, but |
+// simple lines that have tiny crossings are always sortable on the large ends |
+// OPTIMIZATION: check earlier when angles are added to input if any are unsortable |
+// may make sense then to mark all segments in angle sweep as unsortableStart/unsortableEnd |
+// solely for the purpose of short-circuiting future angle building around this center |
+bool SkOpSegment::SortAngles2(const SkTArray<SkOpAngle, true>& angles, |
+ SkTArray<SkOpAngle*, true>* angleList) { |
+ int angleCount = angles.count(); |
+ int angleIndex; |
+ for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) { |
+ const SkOpAngle& angle = angles[angleIndex]; |
+ if (angle.unsortable()) { |
+ return false; |
+ } |
+ angleList->push_back(const_cast<SkOpAngle*>(&angle)); |
+#if DEBUG_ANGLE |
+ (*(angleList->end() - 1))->setID(angleIndex); |
+#endif |
+ } |
+ SkTQSort<SkOpAngle>(angleList->begin(), angleList->end() - 1); |
+ // at this point angles are sorted but individually may not be orderable |
+ // this means that only adjcent orderable segments may transfer winding |
+ return true; |
+} |
+ |
// return true if midpoints were computed |
bool SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const { |
SkASSERT(start != end); |
@@ -2563,11 +2961,19 @@ bool SkOpSegment::isTiny(int index) const { |
return fTs[index].fTiny; |
} |
-void SkOpSegment::TrackOutside(SkTArray<double, true>* outsideTs, double end, double start) { |
- int outCount = outsideTs->count(); |
- if (outCount == 0 || !approximately_negative(end - (*outsideTs)[outCount - 2])) { |
- outsideTs->push_back(end); |
- outsideTs->push_back(start); |
+void SkOpSegment::TrackOutsidePair(SkTArray<SkPoint, true>* outsidePts, const SkPoint& endPt, |
+ const SkPoint& startPt) { |
+ int outCount = outsidePts->count(); |
+ if (outCount == 0 || endPt != (*outsidePts)[outCount - 2]) { |
+ outsidePts->push_back(endPt); |
+ outsidePts->push_back(startPt); |
+ } |
+} |
+ |
+void SkOpSegment::TrackOutside(SkTArray<SkPoint, true>* outsidePts, const SkPoint& startPt) { |
+ int outCount = outsidePts->count(); |
+ if (outCount == 0 || startPt != (*outsidePts)[outCount - 1]) { |
+ outsidePts->push_back(startPt); |
} |
} |
@@ -2615,7 +3021,8 @@ int SkOpSegment::updateWinding(int index, int endIndex) const { |
int lesser = SkMin32(index, endIndex); |
int winding = windSum(lesser); |
int spanWinding = spanSign(index, endIndex); |
- if (winding && UseInnerWinding(winding - spanWinding, winding) && winding != SK_MaxS32) { |
+ if (winding && UseInnerWinding(winding - spanWinding, winding) |
+ && winding != SK_MaxS32) { |
winding -= spanWinding; |
} |
return winding; |
@@ -2627,10 +3034,42 @@ int SkOpSegment::updateWinding(const SkOpAngle* angle) const { |
return updateWinding(endIndex, startIndex); |
} |
+int SkOpSegment::updateWindingReverse(int index, int endIndex) const { |
+ int lesser = SkMin32(index, endIndex); |
+ int winding = windSum(lesser); |
+ int spanWinding = spanSign(endIndex, index); |
+ if (winding && UseInnerWindingReverse(winding - spanWinding, winding) |
+ && winding != SK_MaxS32) { |
+ winding -= spanWinding; |
+ } |
+ return winding; |
+} |
+ |
int SkOpSegment::updateWindingReverse(const SkOpAngle* angle) const { |
int startIndex = angle->start(); |
int endIndex = angle->end(); |
- return updateWinding(startIndex, endIndex); |
+ return updateWindingReverse(endIndex, startIndex); |
+} |
+ |
+// OPTIMIZATION: does the following also work, and is it any faster? |
+// return outerWinding * innerWinding > 0 |
+// || ((outerWinding + innerWinding < 0) ^ ((outerWinding - innerWinding) < 0))) |
+bool SkOpSegment::UseInnerWinding(int outerWinding, int innerWinding) { |
+ SkASSERT(outerWinding != SK_MaxS32); |
+ SkASSERT(innerWinding != SK_MaxS32); |
+ int absOut = abs(outerWinding); |
+ int absIn = abs(innerWinding); |
+ bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn; |
+ return result; |
+} |
+ |
+bool SkOpSegment::UseInnerWindingReverse(int outerWinding, int innerWinding) { |
+ SkASSERT(outerWinding != SK_MaxS32); |
+ SkASSERT(innerWinding != SK_MaxS32); |
+ int absOut = abs(outerWinding); |
+ int absIn = abs(innerWinding); |
+ bool result = absOut == absIn ? true : absOut < absIn; |
+ return result; |
} |
int SkOpSegment::windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar* dx) const { |
@@ -2861,9 +3300,17 @@ void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int |
void SkOpSegment::debugShowSort(const char* fun, const SkTArray<SkOpAngle*, true>& angles, |
int first, const int contourWinding, |
const int oppContourWinding, bool sortable) const { |
- if (--gDebugSortCount < 0) { |
+ if (--SkPathOpsDebug::gSortCount < 0) { |
return; |
} |
+ if (!sortable) { |
+ if (angles.count() == 0) { |
+ return; |
+ } |
+ if (first < 0) { |
+ first = 0; |
+ } |
+ } |
SkASSERT(angles[first]->segment() == this); |
SkASSERT(!sortable || angles.count() > 1); |
int lastSum = contourWinding; |
@@ -2872,8 +3319,9 @@ void SkOpSegment::debugShowSort(const char* fun, const SkTArray<SkOpAngle*, true |
int windSum = lastSum - spanSign(firstAngle); |
int oppoSign = oppSign(firstAngle); |
int oppWindSum = oppLastSum - oppoSign; |
- #define WIND_AS_STRING(x) char x##Str[12]; if (!valid_wind(x)) strcpy(x##Str, "?"); \ |
- else SK_SNPRINTF(x##Str, sizeof(x##Str), "%d", x) |
+ #define WIND_AS_STRING(x) char x##Str[12]; \ |
+ if (!SkPathOpsDebug::ValidWind(x)) strcpy(x##Str, "?"); \ |
+ else SK_SNPRINTF(x##Str, sizeof(x##Str), "%d", x) |
WIND_AS_STRING(contourWinding); |
WIND_AS_STRING(oppContourWinding); |
SkDebugf("%s %s contourWinding=%s oppContourWinding=%s sign=%d\n", fun, __FUNCTION__, |
@@ -2931,7 +3379,7 @@ void SkOpSegment::debugShowSort(const char* fun, const SkTArray<SkOpAngle*, true |
SkDebugf(" tStart=%1.9g tEnd=%1.9g", sSpan.fT, eSpan.fT); |
#endif |
SkDebugf(" sign=%d windValue=%d windSum=", angle.sign(), mSpan.fWindValue); |
- winding_printf(mSpan.fWindSum); |
+ SkPathOpsDebug::WindingPrintf(mSpan.fWindSum); |
int last, wind; |
if (opp) { |
last = oppLastSum; |
@@ -2940,7 +3388,8 @@ void SkOpSegment::debugShowSort(const char* fun, const SkTArray<SkOpAngle*, true |
last = lastSum; |
wind = windSum; |
} |
- bool useInner = valid_wind(last) && valid_wind(wind) && UseInnerWinding(last, wind); |
+ bool useInner = SkPathOpsDebug::ValidWind(last) && SkPathOpsDebug::ValidWind(wind) |
+ && UseInnerWinding(last, wind); |
WIND_AS_STRING(last); |
WIND_AS_STRING(wind); |
WIND_AS_STRING(lastSum); |
@@ -2954,10 +3403,8 @@ void SkOpSegment::debugShowSort(const char* fun, const SkTArray<SkOpAngle*, true |
SkDebugf(" %s->%s (%s->%s)", lastStr, windStr, opp ? lastSumStr : oppLastSumStr, |
opp ? windSumStr : oppWindSumStr); |
} |
- SkDebugf(" done=%d tiny=%d opp=%d\n", mSpan.fDone, mSpan.fTiny, opp); |
-#if 0 && DEBUG_ANGLE |
- angle.debugShow(segment.xyAtT(&sSpan)); |
-#endif |
+ SkDebugf(" done=%d unord=%d small=%d tiny=%d opp=%d\n", |
+ mSpan.fDone, angle.unorderable(), mSpan.fSmall, mSpan.fTiny, opp); |
++index; |
if (index == angles.count()) { |
index = 0; |
@@ -2970,6 +3417,14 @@ void SkOpSegment::debugShowSort(const char* fun, const SkTArray<SkOpAngle*, true |
void SkOpSegment::debugShowSort(const char* fun, const SkTArray<SkOpAngle*, true>& angles, |
int first, bool sortable) { |
+ if (!sortable) { |
+ if (angles.count() == 0) { |
+ return; |
+ } |
+ if (first < 0) { |
+ first = 0; |
+ } |
+ } |
const SkOpAngle* firstAngle = angles[first]; |
const SkOpSegment* segment = firstAngle->segment(); |
int winding = segment->updateWinding(firstAngle); |
@@ -3025,3 +3480,40 @@ void SkOpSegment::debugValidate() const { |
SkASSERT(done == fDoneSpans); |
#endif |
} |
+ |
+#ifdef SK_DEBUG |
+void SkOpSegment::dumpPts() const { |
+ int last = SkPathOpsVerbToPoints(fVerb); |
+ SkDebugf("{{"); |
+ int index = 0; |
+ do { |
+ SkDPoint::DumpSkPoint(fPts[index]); |
+ SkDebugf(", "); |
+ } while (++index < last); |
+ SkDPoint::DumpSkPoint(fPts[index]); |
+ SkDebugf("}}\n"); |
+} |
+ |
+void SkOpSegment::dumpDPts() const { |
+ int count = SkPathOpsVerbToPoints(fVerb); |
+ SkDebugf("{{"); |
+ int index = 0; |
+ do { |
+ SkDPoint dPt = {fPts[index].fX, fPts[index].fY}; |
+ dPt.dump(); |
+ if (index != count) { |
+ SkDebugf(", "); |
+ } |
+ } while (++index <= count); |
+ SkDebugf("}}\n"); |
+} |
+ |
+void SkOpSegment::dumpSpans() const { |
+ int count = this->count(); |
+ for (int index = 0; index < count; ++index) { |
+ const SkOpSpan& span = this->span(index); |
+ SkDebugf("[%d] ", index); |
+ span.dump(); |
+ } |
+} |
+#endif |