Index: src/pathops/SkOpSegment.cpp |
diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp |
index 00963403b7b04de2503d5448d7ce4fdfe4b902ce..727da4a5f5584d9db08ce87abc04252e17a46e06 100644 |
--- a/src/pathops/SkOpSegment.cpp |
+++ b/src/pathops/SkOpSegment.cpp |
@@ -20,8 +20,8 @@ static const bool gUnaryActiveEdge[2][2] = { |
static const bool gActiveEdge[kXOR_PathOp + 1][2][2][2][2] = { |
// miFrom=0 miFrom=1 |
-// miTo=0 miTo=1 miTo=0 miTo=1 |
-// suFrom=0 1 suFrom=0 1 suFrom=0 1 suFrom=0 1 |
+// miTo=0 miTo=1 miTo=0 miTo=1 |
+// suFrom=0 1 suFrom=0 1 suFrom=0 1 suFrom=0 1 |
// suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 |
{{{{F, F}, {F, F}}, {{T, F}, {T, F}}}, {{{T, T}, {F, F}}, {{F, T}, {T, F}}}}, // mi - su |
{{{{F, F}, {F, F}}, {{F, T}, {F, T}}}, {{{F, F}, {T, T}}, {{F, T}, {T, F}}}}, // mi & su |
@@ -37,22 +37,22 @@ enum { |
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; |
+const SkOpAngle* SkOpSegment::activeAngle(int index, int* start, int* end, bool* done, |
+ bool* sortable) const { |
+ if (const SkOpAngle* result = activeAngleInner(index, start, end, done, sortable)) { |
+ return result; |
} |
double referenceT = fTs[index].fT; |
int lesser = index; |
while (--lesser >= 0 |
&& (precisely_negative(referenceT - fTs[lesser].fT) || fTs[lesser].fTiny)) { |
- if (activeAngleOther(lesser, done, angles)) { |
- return true; |
+ if (const SkOpAngle* result = activeAngleOther(lesser, start, end, done, sortable)) { |
+ return result; |
} |
} |
do { |
- if (activeAngleOther(index, done, angles)) { |
- return true; |
+ if (const SkOpAngle* result = activeAngleOther(index, start, end, done, sortable)) { |
+ return result; |
} |
if (++index == fTs.count()) { |
break; |
@@ -62,49 +62,65 @@ bool SkOpSegment::activeAngle(int index, int* done, SkTArray<SkOpAngle, true>* a |
continue; |
} |
} while (precisely_negative(fTs[index].fT - referenceT)); |
- return false; |
-} |
- |
-bool SkOpSegment::activeAngleOther(int index, int* done, SkTArray<SkOpAngle, true>* angles) { |
- SkOpSpan* span = &fTs[index]; |
- SkOpSegment* other = span->fOther; |
- int oIndex = span->fOtherIndex; |
- return other->activeAngleInner(oIndex, done, angles); |
+ return NULL; |
} |
-bool SkOpSegment::activeAngleInner(int index, int* done, SkTArray<SkOpAngle, true>* angles) { |
+const SkOpAngle* SkOpSegment::activeAngleInner(int index, int* start, int* end, bool* done, |
+ bool* sortable) const { |
int next = nextExactSpan(index, 1); |
if (next > 0) { |
- SkOpSpan& upSpan = fTs[index]; |
+ const SkOpSpan& upSpan = fTs[index]; |
+ if (upSpan.fUnsortableStart) { |
+ *sortable = false; |
+ return NULL; |
+ } |
if (upSpan.fWindValue || upSpan.fOppValue) { |
- addAngle(angles, index, next); |
- if (upSpan.fDone || upSpan.fUnsortableEnd) { |
- (*done)++; |
- } else if (upSpan.fWindSum != SK_MinS32) { |
- return true; |
+ if (*end < 0) { |
+ *start = index; |
+ *end = next; |
} |
- } else if (!upSpan.fDone) { |
- upSpan.fDone = true; |
- fDoneSpans++; |
+ if (!upSpan.fDone && !upSpan.fUnsortableEnd) { |
+ if (upSpan.fWindSum != SK_MinS32) { |
+ return spanToAngle(index, next); |
+ } |
+ *done = false; |
+ } |
+ } else { |
+ SkASSERT(upSpan.fDone); |
} |
} |
int prev = nextExactSpan(index, -1); |
// edge leading into junction |
if (prev >= 0) { |
- SkOpSpan& downSpan = fTs[prev]; |
+ const SkOpSpan& downSpan = fTs[prev]; |
+ if (downSpan.fUnsortableEnd) { |
+ *sortable = false; |
+ return NULL; |
+ } |
if (downSpan.fWindValue || downSpan.fOppValue) { |
- addAngle(angles, index, prev); |
- if (downSpan.fDone) { |
- (*done)++; |
- } else if (downSpan.fWindSum != SK_MinS32) { |
- return true; |
+ if (*end < 0) { |
+ *start = index; |
+ *end = prev; |
+ } |
+ if (!downSpan.fDone) { |
+ if (downSpan.fWindSum != SK_MinS32) { |
+ return spanToAngle(index, prev); |
+ } |
+ *done = false; |
} |
- } else if (!downSpan.fDone) { |
- downSpan.fDone = true; |
- fDoneSpans++; |
+ } else { |
+ SkASSERT(downSpan.fDone); |
} |
} |
- return false; |
+ return NULL; |
+} |
+ |
+const SkOpAngle* SkOpSegment::activeAngleOther(int index, int* start, int* end, bool* done, |
+ bool* sortable) const { |
+ const SkOpSpan* span = &fTs[index]; |
+ SkOpSegment* other = span->fOther; |
+ int oIndex = span->fOtherIndex; |
+ return other->activeAngleInner(oIndex, start, end, done, sortable); |
} |
SkPoint SkOpSegment::activeLeftTop(bool onlySortable, int* firstT) const { |
@@ -159,30 +175,28 @@ bool SkOpSegment::activeOp(int index, int endIndex, int xorMiMask, int xorSuMask |
if (fOperand) { |
SkTSwap<int>(sumMiWinding, sumSuWinding); |
} |
- int maxWinding, sumWinding, oppMaxWinding, oppSumWinding; |
- return activeOp(xorMiMask, xorSuMask, index, endIndex, op, &sumMiWinding, &sumSuWinding, |
- &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding); |
+ return activeOp(xorMiMask, xorSuMask, index, endIndex, op, &sumMiWinding, &sumSuWinding); |
} |
bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, SkPathOp op, |
- int* sumMiWinding, int* sumSuWinding, |
- int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding) { |
+ int* sumMiWinding, int* sumSuWinding) { |
+ int maxWinding, sumWinding, oppMaxWinding, oppSumWinding; |
setUpWindings(index, endIndex, sumMiWinding, sumSuWinding, |
- maxWinding, sumWinding, oppMaxWinding, oppSumWinding); |
+ &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding); |
bool miFrom; |
bool miTo; |
bool suFrom; |
bool suTo; |
if (operand()) { |
- miFrom = (*oppMaxWinding & xorMiMask) != 0; |
- miTo = (*oppSumWinding & xorMiMask) != 0; |
- suFrom = (*maxWinding & xorSuMask) != 0; |
- suTo = (*sumWinding & xorSuMask) != 0; |
+ miFrom = (oppMaxWinding & xorMiMask) != 0; |
+ miTo = (oppSumWinding & xorMiMask) != 0; |
+ suFrom = (maxWinding & xorSuMask) != 0; |
+ suTo = (sumWinding & xorSuMask) != 0; |
} else { |
- miFrom = (*maxWinding & xorMiMask) != 0; |
- miTo = (*sumWinding & xorMiMask) != 0; |
- suFrom = (*oppMaxWinding & xorSuMask) != 0; |
- suTo = (*oppSumWinding & xorSuMask) != 0; |
+ miFrom = (maxWinding & xorMiMask) != 0; |
+ miTo = (sumWinding & xorMiMask) != 0; |
+ suFrom = (oppMaxWinding & xorSuMask) != 0; |
+ suTo = (oppSumWinding & xorSuMask) != 0; |
} |
bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo]; |
#if DEBUG_ACTIVE_OP |
@@ -194,24 +208,18 @@ bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, int index, int endIndex |
bool SkOpSegment::activeWinding(int index, int endIndex) { |
int sumWinding = updateWinding(endIndex, index); |
- int maxWinding; |
- return activeWinding(index, endIndex, &maxWinding, &sumWinding); |
+ return activeWinding(index, endIndex, &sumWinding); |
} |
-bool SkOpSegment::activeWinding(int index, int endIndex, int* maxWinding, int* sumWinding) { |
- setUpWinding(index, endIndex, maxWinding, sumWinding); |
- bool from = *maxWinding != 0; |
+bool SkOpSegment::activeWinding(int index, int endIndex, int* sumWinding) { |
+ int maxWinding; |
+ setUpWinding(index, endIndex, &maxWinding, sumWinding); |
+ bool from = maxWinding != 0; |
bool to = *sumWinding != 0; |
bool result = gUnaryActiveEdge[from][to]; |
return result; |
} |
-void SkOpSegment::addAngle(SkTArray<SkOpAngle, true>* anglesPtr, int start, int end) const { |
- SkASSERT(start != end); |
- SkOpAngle& angle = anglesPtr->push_back(); |
- angle.set(this, start, end); |
-} |
- |
void SkOpSegment::addCancelOutsides(const SkPoint& startPt, const SkPoint& endPt, |
SkOpSegment* other) { |
int tIndex = -1; |
@@ -299,10 +307,36 @@ void SkOpSegment::addCoinOutsides(const SkPoint& startPt, const SkPoint& endPt, |
do { |
++tIndex; |
} while (startPt != fTs[tIndex].fPt); |
+ int ttIndex = tIndex; |
+ bool checkOtherTMatch = false; |
+ do { |
+ const SkOpSpan& span = fTs[ttIndex]; |
+ if (startPt != span.fPt) { |
+ break; |
+ } |
+ if (span.fOther == other && span.fPt == startPt) { |
+ checkOtherTMatch = true; |
+ break; |
+ } |
+ } while (++ttIndex < count()); |
do { |
++oIndex; |
} while (startPt != other->fTs[oIndex].fPt); |
- if (tIndex > 0 || oIndex > 0 || fOperand != other->fOperand) { |
+ bool skipAdd = false; |
+ if (checkOtherTMatch) { |
+ int ooIndex = oIndex; |
+ do { |
+ const SkOpSpan& oSpan = other->fTs[ooIndex]; |
+ if (startPt != oSpan.fPt) { |
+ break; |
+ } |
+ if (oSpan.fT == fTs[ttIndex].fOtherT) { |
+ skipAdd = true; |
+ break; |
+ } |
+ } while (++ooIndex < other->count()); |
+ } |
+ if ((tIndex > 0 || oIndex > 0 || fOperand != other->fOperand) && !skipAdd) { |
addTPair(fTs[tIndex].fT, other, other->fTs[oIndex].fT, false, startPt); |
} |
SkPoint nextPt = startPt; |
@@ -378,6 +412,30 @@ void SkOpSegment::addCurveTo(int start, int end, SkPathWriter* path, bool active |
// return ePtr[SkPathOpsVerbToPoints(fVerb)]; |
} |
+void SkOpSegment::addEndSpan(int endIndex) { |
+ int spanCount = fTs.count(); |
+ int angleIndex = fAngles.count(); |
+ int startIndex = endIndex - 1; |
+ while (fTs[startIndex].fT == 1 || fTs[startIndex].fTiny) { |
+ ++startIndex; |
+ SkASSERT(startIndex < spanCount - 1); |
+ ++endIndex; |
+ } |
+ fAngles.push_back().set(this, spanCount - 1, startIndex); |
+#if DEBUG_ANGLE |
+ fAngles.back().setID(angleIndex); |
+ debugCheckPointsEqualish(endIndex, spanCount); |
+#endif |
+ setFromAngleIndex(endIndex, angleIndex); |
+} |
+ |
+void SkOpSegment::setFromAngleIndex(int endIndex, int angleIndex) { |
+ int spanCount = fTs.count(); |
+ do { |
+ fTs[endIndex].fFromAngleIndex = angleIndex; |
+ } while (++endIndex < spanCount); |
+} |
+ |
void SkOpSegment::addLine(const SkPoint pts[2], bool operand, bool evenOdd) { |
init(pts, SkPath::kLine_Verb, operand, evenOdd); |
fBounds.set(pts, 2); |
@@ -400,6 +458,92 @@ void SkOpSegment::addQuad(const SkPoint pts[3], bool operand, bool evenOdd) { |
fBounds.setQuadBounds(pts); |
} |
+int SkOpSegment::addSingletonAngleDown(int endIndex, SkOpSegment** otherPtr) { |
+ int startIndex = findEndSpan(endIndex); |
+ SkASSERT(startIndex > 0); |
+ int spanIndex = endIndex - 1; |
+ fSingletonAngles.push_back().set(this, spanIndex, startIndex - 1); |
+ setFromAngleIndex(startIndex, fAngles.count() + fSingletonAngles.count() - 1); |
+ SkOpSegment* other; |
+ do { |
+ other = fTs[spanIndex].fOther; |
+ if (other->fTs[0].fWindValue) { |
+ break; |
+ } |
+ --spanIndex; |
+ SkASSERT(fTs[spanIndex].fT == 1); |
+ } while (true); |
+ int oEndIndex = other->findStartSpan(0); |
+ SkASSERT(oEndIndex > 0); |
+ int otherIndex = other->fSingletonAngles.count(); |
+ other->fSingletonAngles.push_back().set(other, 0, oEndIndex); |
+ other->setToAngleIndex(oEndIndex, other->fAngles.count() + otherIndex); |
+ *otherPtr = other; |
+ return otherIndex; |
+} |
+ |
+int SkOpSegment::addSingletonAngleUp(int start, SkOpSegment** otherPtr) { |
+ int endIndex = findStartSpan(start); |
+ SkASSERT(endIndex > 0); |
+ int thisIndex = fSingletonAngles.count(); |
+ fSingletonAngles.push_back().set(this, start, endIndex); |
+ setToAngleIndex(endIndex, fAngles.count() + thisIndex); |
+ int spanIndex = start; |
+ SkOpSegment* other; |
+ int oCount, oStartIndex; |
+ do { |
+ other = fTs[spanIndex].fOther; |
+ oCount = other->count(); |
+ oStartIndex = other->findEndSpan(oCount); |
+ SkASSERT(oStartIndex > 0); |
+ if (other->fTs[oStartIndex - 1].fWindValue) { |
+ break; |
+ } |
+ ++spanIndex; |
+ SkASSERT(fTs[spanIndex].fT == 0); |
+ } while (true); |
+ int otherIndex = other->fSingletonAngles.count(); |
+ other->fSingletonAngles.push_back().set(other, oCount - 1, oStartIndex - 1); |
+ other->setFromAngleIndex(oStartIndex, other->fAngles.count() + otherIndex); |
+ *otherPtr = other; |
+ return otherIndex; |
+} |
+ |
+SkOpAngle* SkOpSegment::addSingletonAngles(int start, int step) { |
+ int thisIndex = fSingletonAngles.count(); |
+ SkOpSegment* other; |
+ int otherIndex; |
+ if (step > 0) { |
+ otherIndex = addSingletonAngleUp(start, &other); |
+ } else { |
+ otherIndex = addSingletonAngleDown(start + 1, &other); |
+ } |
+ fSingletonAngles[thisIndex].insert(&other->fSingletonAngles[otherIndex]); |
+#if DEBUG_ANGLE |
+ fSingletonAngles[thisIndex].setID(fAngles.count() + thisIndex); |
+ other->fSingletonAngles[otherIndex].setID(other->fAngles.count() + otherIndex); |
+#endif |
+ return &fSingletonAngles[thisIndex]; |
+} |
+ |
+void SkOpSegment::addStartSpan(int endIndex) { |
+ int angleIndex = fAngles.count(); |
+ int index = 0; |
+ fAngles.push_back().set(this, index, endIndex); |
+#if DEBUG_ANGLE |
+ fAngles.back().setID(angleIndex); |
+ debugCheckPointsEqualish(index, endIndex); |
+#endif |
+ setToAngleIndex(endIndex, angleIndex); |
+} |
+ |
+void SkOpSegment::setToAngleIndex(int endIndex, int angleIndex) { |
+ int index = 0; |
+ do { |
+ fTs[index].fToAngleIndex = angleIndex; |
+ } while (++index < endIndex); |
+} |
+ |
// Defer all coincident edge processing until |
// after normal intersections have been computed |
@@ -408,6 +552,10 @@ void SkOpSegment::addQuad(const SkPoint pts[3], bool operand, bool evenOdd) { |
// add non-coincident intersection. Resulting edges are sorted in T. |
int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) { |
+ SkASSERT(this != other || fVerb == SkPath::kCubic_Verb); |
+ #if 0 // this needs an even rougher association to be useful |
+ SkASSERT(SkDPoint::RoughlyEqual(ptAtT(newT), pt)); |
+ #endif |
if (precisely_zero(newT)) { |
newT = 0; |
} else if (precisely_equal(newT, 1)) { |
@@ -416,10 +564,10 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) { |
// FIXME: in the pathological case where there is a ton of intercepts, |
// binary search? |
int insertedAt = -1; |
- size_t tCount = fTs.count(); |
+ int tCount = fTs.count(); |
const SkPoint& firstPt = fPts[0]; |
const SkPoint& lastPt = fPts[SkPathOpsVerbToPoints(fVerb)]; |
- for (size_t index = 0; index < tCount; ++index) { |
+ for (int index = 0; index < tCount; ++index) { |
// OPTIMIZATION: if there are three or more identical Ts, then |
// the fourth and following could be further insertion-sorted so |
// that all the edges are clockwise or counterclockwise. |
@@ -457,93 +605,69 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) { |
SkASSERT(approximately_equal(xyAtT(newT).fX, pt.fX) |
&& approximately_equal(xyAtT(newT).fY, pt.fY)); |
#endif |
+ span->fFromAngleIndex = -1; |
+ span->fToAngleIndex = -1; |
span->fWindSum = SK_MinS32; |
span->fOppSum = SK_MinS32; |
span->fWindValue = 1; |
span->fOppValue = 0; |
- span->fSmall = false; |
- span->fTiny = false; |
- span->fLoop = false; |
+ span->fChased = false; |
if ((span->fDone = newT == 1)) { |
++fDoneSpans; |
} |
+ span->fLoop = false; |
+ span->fSmall = false; |
+ span->fTiny = false; |
span->fUnsortableStart = false; |
span->fUnsortableEnd = false; |
int less = -1; |
- while (&span[less + 1] - fTs.begin() > 0 && AlmostEqualUlps(span[less].fPt, span->fPt)) { |
- if (span[less].fDone) { |
- break; |
- } |
- double tInterval = newT - span[less].fT; |
- if (precisely_negative(tInterval)) { |
- break; |
- } |
+// find range of spans with nearly the same point as this one |
+ while (&span[less + 1] - fTs.begin() > 0 && AlmostEqualUlps(span[less].fPt, pt)) { |
if (fVerb == SkPath::kCubic_Verb) { |
+ double tInterval = newT - span[less].fT; |
double tMid = newT - tInterval / 2; |
SkDPoint midPt = dcubic_xy_at_t(fPts, tMid); |
if (!midPt.approximatelyEqual(xyAtT(span))) { |
break; |
} |
} |
- 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) && tiny) { |
- if (approximately_greater_than_one(newT)) { |
- SkASSERT(&span[less] - fTs.begin() < fTs.count()); |
- span[less].fUnsortableStart = true; |
- if (&span[less - 1] - fTs.begin() >= 0) { |
- span[less - 1].fUnsortableEnd = true; |
- } |
- } |
- if (approximately_less_than_zero(span[less].fT)) { |
- SkASSERT(&span[less + 1] - fTs.begin() < fTs.count()); |
- SkASSERT(&span[less] - fTs.begin() >= 0); |
- span[less + 1].fUnsortableStart = true; |
- span[less].fUnsortableEnd = true; |
- } |
- } |
- ++fDoneSpans; |
--less; |
} |
int more = 1; |
- while (fTs.end() - &span[more - 1] > 1 && AlmostEqualUlps(span[more].fPt, span->fPt)) { |
- if (span[more - 1].fDone) { |
- break; |
- } |
- double tEndInterval = span[more].fT - newT; |
- if (precisely_negative(tEndInterval)) { |
- if ((span->fTiny = span[more].fTiny)) { |
- span->fDone = true; |
- ++fDoneSpans; |
- } |
- break; |
- } |
+ while (fTs.end() - &span[more - 1] > 1 && AlmostEqualUlps(span[more].fPt, pt)) { |
if (fVerb == SkPath::kCubic_Verb) { |
+ double tEndInterval = span[more].fT - newT; |
double tMid = newT - tEndInterval / 2; |
SkDPoint midEndPt = dcubic_xy_at_t(fPts, tMid); |
if (!midEndPt.approximatelyEqual(xyAtT(span))) { |
break; |
} |
} |
- 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) && tiny) { |
- if (approximately_greater_than_one(span[more].fT)) { |
- span[more + 1].fUnsortableStart = true; |
- span[more].fUnsortableEnd = true; |
- } |
- if (approximately_less_than_zero(newT)) { |
- span[more].fUnsortableStart = true; |
- span[more - 1].fUnsortableEnd = true; |
- } |
- } |
- ++fDoneSpans; |
++more; |
} |
+ ++less; |
+ --more; |
+ while (more - 1 > less && span[more].fPt == span[more - 1].fPt |
+ && span[more].fT == span[more - 1].fT) { |
+ --more; |
+ } |
+ if (less == more) { |
+ return insertedAt; |
+ } |
+ if (precisely_negative(span[more].fT - span[less].fT)) { |
+ return insertedAt; |
+ } |
+// if the total range of t values is big enough, mark all tiny |
+ bool tiny = span[less].fPt == span[more].fPt; |
+ int index = less; |
+ do { |
+ fSmall = span[index].fSmall = true; |
+ fTiny |= span[index].fTiny = tiny; |
+ if (!span[index].fDone) { |
+ span[index].fDone = true; |
+ ++fDoneSpans; |
+ } |
+ } while (++index < more); |
return insertedAt; |
} |
@@ -572,7 +696,7 @@ void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpS |
SkASSERT(index < fTs.count()); |
++index; |
} |
- while (index > 0 && fTs[index].fT == fTs[index - 1].fT) { |
+ while (index > 0 && precisely_equal(fTs[index].fT, fTs[index - 1].fT)) { |
--index; |
} |
int oIndex = other->fTs.count(); |
@@ -581,7 +705,7 @@ void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpS |
} |
double oStartT = other->fTs[oIndex].fT; |
// look for first point beyond match |
- while (startPt == other->fTs[--oIndex].fPt || oStartT == other->fTs[oIndex].fT) { |
+ while (startPt == other->fTs[--oIndex].fPt || precisely_equal(oStartT, other->fTs[oIndex].fT)) { |
SkASSERT(oIndex > 0); |
} |
SkOpSpan* test = &fTs[index]; |
@@ -610,7 +734,7 @@ void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpS |
} |
SkASSERT(index < fTs.count() - 1); |
test = &fTs[++index]; |
- } while (testPt == test->fPt || testT == test->fT); |
+ } while (testPt == test->fPt || precisely_equal(testT, test->fT)); |
SkDEBUGCODE(int originalWindValue = oTest->fWindValue); |
do { |
SkASSERT(oTest->fT < 1); |
@@ -628,7 +752,7 @@ void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpS |
break; |
} |
oTest = &other->fTs[--oIndex]; |
- } while (oTestPt == oTest->fPt || oTestT == oTest->fT); |
+ } while (oTestPt == oTest->fPt || precisely_equal(oTestT, oTest->fT)); |
} while (endPt != test->fPt && test->fT < 1); |
// FIXME: determine if canceled edges need outside ts added |
int outCount = outsidePts.count(); |
@@ -643,14 +767,119 @@ void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpS |
} |
} |
-int SkOpSegment::addSelfT(SkOpSegment* other, const SkPoint& pt, double newT) { |
+int SkOpSegment::addSelfT(const SkPoint& pt, double newT) { |
// if the tail nearly intersects itself but not quite, the caller records this separately |
- int result = addT(other, pt, newT); |
+ int result = addT(this, pt, newT); |
SkOpSpan* span = &fTs[result]; |
- span->fLoop = true; |
+ fLoop = span->fLoop = true; |
return result; |
} |
+void SkOpSegment::addSimpleAngle(int index) { |
+ if (index == 0) { |
+ SkASSERT(!fTs[index].fTiny && fTs[index + 1].fT > 0); |
+ addStartSpan(1); |
+ } else { |
+ SkASSERT(!fTs[index - 1].fTiny && fTs[index - 1].fT < 1); |
+ addEndSpan(index); |
+ } |
+ SkOpSpan& span = fTs[index]; |
+ SkOpSegment* other = span.fOther; |
+ SkOpSpan& oSpan = other->fTs[span.fOtherIndex]; |
+ SkOpAngle* angle, * oAngle; |
+ if (index == 0) { |
+ SkASSERT(span.fOtherIndex - 1 >= 0); |
+ SkASSERT(span.fOtherT == 1); |
+ SkDEBUGCODE(SkOpSpan& oPrior = other->fTs[span.fOtherIndex - 1]); |
+ SkASSERT(!oPrior.fTiny && oPrior.fT < 1); |
+ other->addEndSpan(span.fOtherIndex); |
+ angle = &this->angle(span.fToAngleIndex); |
+ oAngle = &other->angle(oSpan.fFromAngleIndex); |
+ } else { |
+ SkASSERT(span.fOtherIndex + 1 < other->count()); |
+ SkASSERT(span.fOtherT == 0); |
+ SkASSERT(!oSpan.fTiny && (other->fTs[span.fOtherIndex + 1].fT > 0 |
+ || (other->fTs[span.fOtherIndex + 1].fFromAngleIndex < 0 |
+ && other->fTs[span.fOtherIndex + 1].fToAngleIndex < 0))); |
+ int oIndex = 1; |
+ do { |
+ const SkOpSpan& osSpan = other->span(oIndex); |
+ if (osSpan.fFromAngleIndex >= 0 || osSpan.fT > 0) { |
+ break; |
+ } |
+ ++oIndex; |
+ SkASSERT(oIndex < other->count()); |
+ } while (true); |
+ other->addStartSpan(oIndex); |
+ angle = &this->angle(span.fFromAngleIndex); |
+ oAngle = &other->angle(oSpan.fToAngleIndex); |
+ } |
+ angle->insert(oAngle); |
+} |
+ |
+bool SkOpSegment::alignSpan(int index, double thisT, const SkPoint& thisPt) { |
+ bool aligned = false; |
+ SkOpSpan* span = &fTs[index]; |
+ SkOpSegment* other = span->fOther; |
+ int oIndex = span->fOtherIndex; |
+ SkOpSpan* oSpan = &other->fTs[oIndex]; |
+ if (span->fT != thisT) { |
+ span->fT = thisT; |
+ oSpan->fOtherT = thisT; |
+ aligned = true; |
+ } |
+ if (span->fPt != thisPt) { |
+ span->fPt = thisPt; |
+ oSpan->fPt = thisPt; |
+ aligned = true; |
+ } |
+ double oT = oSpan->fT; |
+ if (oT == 0 || oT == 1) { |
+ return aligned; |
+ } |
+ int oStart = other->nextSpan(oIndex, -1) + 1; |
+ int oEnd = other->nextSpan(oIndex, 1); |
+ oSpan = &other->fTs[oStart]; |
+ oT = oSpan->fT; |
+ bool oAligned = false; |
+ if (oSpan->fPt != thisPt) { |
+ oAligned |= other->alignSpan(oStart, oT, thisPt); |
+ } |
+ int otherIndex = oStart; |
+ while (++otherIndex < oEnd) { |
+ SkOpSpan* oNextSpan = &other->fTs[otherIndex]; |
+ if (oNextSpan->fT != oT || oNextSpan->fPt != thisPt) { |
+ oAligned |= other->alignSpan(otherIndex, oT, thisPt); |
+ } |
+ } |
+ if (oAligned) { |
+ other->alignSpanState(oStart, oEnd); |
+ } |
+ return aligned; |
+} |
+ |
+void SkOpSegment::alignSpanState(int start, int end) { |
+ SkOpSpan* lastSpan = &fTs[--end]; |
+ bool allSmall = lastSpan->fSmall; |
+ bool allTiny = lastSpan->fTiny; |
+ bool allDone = lastSpan->fDone; |
+ SkDEBUGCODE(int winding = lastSpan->fWindValue); |
+ SkDEBUGCODE(int oppWinding = lastSpan->fOppValue); |
+ int index = start; |
+ while (index < end) { |
+ SkOpSpan* span = &fTs[index]; |
+ span->fSmall = allSmall; |
+ span->fTiny = allTiny; |
+ if (span->fDone != allDone) { |
+ span->fDone = allDone; |
+ fDoneSpans += allDone ? 1 : -1; |
+ } |
+ SkASSERT(span->fWindValue == winding); |
+ SkASSERT(span->fOppValue == oppWinding); |
+ ++index; |
+ } |
+} |
+ |
void SkOpSegment::bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* indexPtr, |
SkTArray<SkPoint, true>* outsideTs) { |
int index = *indexPtr; |
@@ -667,7 +896,7 @@ void SkOpSegment::bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* in |
TrackOutside(outsideTs, oStartPt); |
} |
end = &fTs[++index]; |
- } while ((end->fPt == test->fPt || end->fT == test->fT) && end->fT < 1); |
+ } while ((end->fPt == test->fPt || precisely_equal(end->fT, test->fT)) && end->fT < 1); |
*indexPtr = index; |
} |
@@ -680,13 +909,16 @@ void SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, int* oIndexPtr, |
int oIndex = *oIndexPtr; |
SkOpSpan* const oTest = &fTs[oIndex]; |
SkOpSpan* oEnd = oTest; |
- const SkPoint& startPt = test.fPt; |
const SkPoint& oStartPt = oTest->fPt; |
double oStartT = oTest->fT; |
- if (oStartPt == oEnd->fPt || oStartT == oEnd->fT) { |
+#if 0 // FIXME : figure out what disabling this breaks |
+ const SkPoint& startPt = test.fPt; |
+ // this is always true since oEnd == oTest && oStartPt == oTest->fPt -- find proper condition |
+ if (oStartPt == oEnd->fPt || precisely_equal(oStartT, oEnd->fT)) { |
TrackOutside(oOutsidePts, startPt); |
} |
- while (oStartPt == oEnd->fPt || oStartT == oEnd->fT) { |
+#endif |
+ while (oStartPt == oEnd->fPt || precisely_equal(oStartT, oEnd->fT)) { |
zeroSpan(oEnd); |
oEnd = &fTs[++oIndex]; |
} |
@@ -711,7 +943,7 @@ void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, d |
++index; |
} |
double startT = fTs[index].fT; |
- while (index > 0 && fTs[index - 1].fT == startT) { |
+ while (index > 0 && precisely_equal(fTs[index - 1].fT, startT)) { |
--index; |
} |
int oIndex = 0; |
@@ -720,7 +952,7 @@ void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, d |
++oIndex; |
} |
double oStartT = other->fTs[oIndex].fT; |
- while (oIndex > 0 && other->fTs[oIndex - 1].fT == oStartT) { |
+ while (oIndex > 0 && precisely_equal(other->fTs[oIndex - 1].fT, oStartT)) { |
--oIndex; |
} |
SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts; |
@@ -734,12 +966,10 @@ void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, d |
do { |
SkASSERT(test->fT < 1); |
SkASSERT(oTest->fT < 1); |
-#if 0 |
- if (test->fDone || oTest->fDone) { |
-#else // consolidate the winding count even if done |
+ |
+ // 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 { |
@@ -768,14 +998,15 @@ void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, d |
test = &fTs[index]; |
testPt = &test->fPt; |
testT = test->fT; |
- if (endPt == *testPt || endT == testT) { |
- break; |
- } |
oTest = &other->fTs[oIndex]; |
oTestPt = &oTest->fPt; |
- SkASSERT(AlmostEqualUlps(*testPt, *oTestPt)); |
+ if (endPt == *testPt || precisely_equal(endT, testT)) { |
+ break; |
+ } |
+// SkASSERT(AlmostEqualUlps(*testPt, *oTestPt)); |
} while (endPt != *oTestPt); |
- if (endPt != *testPt && endT != testT) { // in rare cases, one may have ended before the other |
+ // in rare cases, one may have ended before the other |
+ if (endPt != *testPt && !precisely_equal(endT, testT)) { |
int lastWind = test[-1].fWindValue; |
int lastOpp = test[-1].fOppValue; |
bool zero = lastWind == 0 && lastOpp == 0; |
@@ -791,7 +1022,43 @@ void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, d |
test = &fTs[++index]; |
testPt = &test->fPt; |
} while (endPt != *testPt); |
- } |
+ } |
+ if (endPt != *oTestPt) { |
+ // look ahead to see if zeroing more spans will allows us to catch up |
+ int oPeekIndex = oIndex; |
+ bool success = true; |
+ SkOpSpan* oPeek; |
+ do { |
+ oPeek = &other->fTs[oPeekIndex]; |
+ if (oPeek->fT == 1) { |
+ success = false; |
+ break; |
+ } |
+ ++oPeekIndex; |
+ } while (endPt != oPeek->fPt); |
+ if (success) { |
+ // make sure the matching point completes the coincidence span |
+ success = false; |
+ do { |
+ if (oPeek->fOther == this) { |
+ success = true; |
+ break; |
+ } |
+ oPeek = &other->fTs[++oPeekIndex]; |
+ } while (endPt == oPeek->fPt); |
+ } |
+ if (success) { |
+ do { |
+ if (!binary || test->fWindValue + oTest->fOppValue >= 0) { |
+ other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts); |
+ } else { |
+ other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts); |
+ } |
+ oTest = &other->fTs[oIndex]; |
+ oTestPt = &oTest->fPt; |
+ } while (endPt != *oTestPt); |
+ } |
+ } |
int outCount = outsidePts.count(); |
if (!done() && outCount) { |
addCoinOutsides(outsidePts[0], endPt, other); |
@@ -803,7 +1070,7 @@ void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, d |
// FIXME: this doesn't prevent the same span from being added twice |
// fix in caller, SkASSERT here? |
-void SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind, |
+const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind, |
const SkPoint& pt) { |
int tCount = fTs.count(); |
for (int tIndex = 0; tIndex < tCount; ++tIndex) { |
@@ -817,7 +1084,7 @@ void SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool bor |
SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n", |
__FUNCTION__, fID, t, other->fID, otherT); |
#endif |
- return; |
+ return NULL; |
} |
} |
#if DEBUG_ADD_T_PAIR |
@@ -830,21 +1097,19 @@ void SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool bor |
other->addOtherT(otherInsertedAt, t, insertedAt); |
matchWindingValue(insertedAt, t, borrowWind); |
other->matchWindingValue(otherInsertedAt, otherT, borrowWind); |
+ return &span(insertedAt); |
} |
-void SkOpSegment::addTwoAngles(int start, int end, SkTArray<SkOpAngle, true>* angles) const { |
- // add edge leading into junction |
- int min = SkMin32(end, start); |
- 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)) { |
- addAngle(angles, end, tIndex); |
+const SkOpAngle& SkOpSegment::angle(int index) const { |
+ SkASSERT(index >= 0); |
+ int count = fAngles.count(); |
+ if (index < count) { |
+ return fAngles[index]; |
} |
+ index -= count; |
+ count = fSingletonAngles.count(); |
+ SkASSERT(index < count); |
+ return fSingletonAngles[index]; |
} |
bool SkOpSegment::betweenPoints(double midT, const SkPoint& pt1, const SkPoint& pt2) const { |
@@ -862,164 +1127,168 @@ bool SkOpSegment::betweenTs(int lesser, double testT, int greater) const { |
return approximately_between(fTs[lesser].fT, testT, fTs[greater].fT); |
} |
-// 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 && (allowOpp || fTs[lesser].fOther->fOperand == fOperand) |
- && (precisely_negative(referenceT - fTs[lesser].fT) || fTs[lesser].fTiny)) { |
- buildAnglesInner(lesser, angles); |
- } |
- do { |
- buildAnglesInner(index, angles); |
- if (++index == fTs.count()) { |
- break; |
+// in extreme cases (like the buffer overflow test) return false to abort |
+// for now, if one t value represents two different points, then the values are too extreme |
+// to generate meaningful results |
+bool SkOpSegment::calcAngles() { |
+ int spanCount = fTs.count(); |
+ if (spanCount <= 2) { |
+ return spanCount == 2; |
+ } |
+ int index = 1; |
+ const SkOpSpan* firstSpan = &fTs[index]; |
+ int activePrior = checkSetAngle(0); |
+ const SkOpSpan* span = &fTs[0]; |
+ if (firstSpan->fT == 0 || span->fTiny || span->fOtherT != 1 || span->fOther->multipleEnds()) { |
+ index = findStartSpan(0); // curve start intersects |
+ if (index < 0) { |
+ return false; |
} |
- if (!allowOpp && fTs[index].fOther->fOperand != fOperand) { |
- break; |
+ if (activePrior >= 0) { |
+ addStartSpan(index); |
} |
- 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"); |
+ } |
+ bool addEnd; |
+ int endIndex = spanCount - 1; |
+ span = &fTs[endIndex - 1]; |
+ if ((addEnd = span->fT == 1 || span->fTiny)) { // if curve end intersects |
+ endIndex = findEndSpan(endIndex); |
+ if (endIndex < 0) { |
return false; |
} |
- } while (precisely_negative(fTs[index].fT - referenceT)); |
- return true; |
-} |
- |
-void SkOpSegment::buildAnglesInner(int index, SkTArray<SkOpAngle, true>* angles) const { |
- const SkOpSpan* span = &fTs[index]; |
- SkOpSegment* other = span->fOther; |
-// if there is only one live crossing, and no coincidence, continue |
-// in the same direction |
-// if there is coincidence, the only choice may be to reverse direction |
- // find edge on either side of intersection |
- int oIndex = span->fOtherIndex; |
- // if done == -1, prior span has already been processed |
- int step = 1; |
- int next = other->nextExactSpan(oIndex, step); |
- if (next < 0) { |
- step = -step; |
- next = other->nextExactSpan(oIndex, step); |
- } |
- // add candidate into and away from junction |
- other->addTwoAngles(next, oIndex, angles); |
-} |
- |
-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(); |
- // 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) { |
+ } else { |
+ addEnd = fTs[endIndex].fOtherT != 0 || fTs[endIndex].fOther->multipleStarts(); |
+ } |
+ SkASSERT(endIndex >= index); |
+ int prior = 0; |
+ while (index < endIndex) { |
+ const SkOpSpan& fromSpan = fTs[index]; // for each intermediate intersection |
+ const SkOpSpan* lastSpan; |
+ span = &fromSpan; |
+ int start = index; |
+ do { |
+ lastSpan = span; |
+ span = &fTs[++index]; |
+ SkASSERT(index < spanCount); |
+ if (!precisely_negative(span->fT - lastSpan->fT) && !lastSpan->fTiny) { |
break; |
} |
+ if (!SkDPoint::ApproximatelyEqual(lastSpan->fPt, span->fPt)) { |
+ return false; |
+ } |
+ } while (true); |
+ int angleIndex = fAngles.count(); |
+ int priorAngleIndex; |
+ if (activePrior >= 0) { |
+ int pActive = firstActive(prior); |
+ SkASSERT(pActive < start); |
+ fAngles.push_back().set(this, start, pActive); |
+ priorAngleIndex = angleIndex++; |
+ #if DEBUG_ANGLE |
+ fAngles.back().setID(priorAngleIndex); |
+ #endif |
} |
- if (firstIndex == angleCount) { |
- return SK_MinS32; |
+ int active = checkSetAngle(start); |
+ if (active >= 0) { |
+ SkASSERT(active < index); |
+ fAngles.push_back().set(this, active, index); |
+ #if DEBUG_ANGLE |
+ fAngles.back().setID(angleIndex); |
+ #endif |
} |
+ #if DEBUG_ANGLE |
+ debugCheckPointsEqualish(start, index); |
+ #endif |
+ prior = start; |
+ do { |
+ const SkOpSpan* startSpan = &fTs[start - 1]; |
+ if (!startSpan->fSmall || startSpan->fFromAngleIndex >= 0 |
+ || startSpan->fToAngleIndex >= 0) { |
+ break; |
+ } |
+ --start; |
+ } while (start > 0); |
+ do { |
+ if (activePrior >= 0) { |
+ SkASSERT(fTs[start].fFromAngleIndex == -1); |
+ fTs[start].fFromAngleIndex = priorAngleIndex; |
+ } |
+ if (active >= 0) { |
+ SkASSERT(fTs[start].fToAngleIndex == -1); |
+ fTs[start].fToAngleIndex = angleIndex; |
+ } |
+ } while (++start < index); |
+ activePrior = active; |
} |
- bool sortable = SortAngles2(*angles, sorted); |
-#if DEBUG_SORT_RAW |
- if (sorted->count() > 0) { |
- (*sorted)[0]->segment()->debugShowSort(__FUNCTION__, *sorted, 0, 0, 0, sortable); |
+ if (addEnd && activePrior >= 0) { |
+ addEndSpan(endIndex); |
} |
-#endif |
- if (!sortable) { |
- return SK_NaN32; |
+ return true; |
+} |
+ |
+int SkOpSegment::checkSetAngle(int tIndex) const { |
+ const SkOpSpan* span = &fTs[tIndex]; |
+ while (span->fTiny /* || span->fSmall */) { |
+ span = &fTs[++tIndex]; |
} |
- if (includeType == SkOpAngle::kUnaryXor) { |
- return SK_MinS32; |
+ return isCanceled(tIndex) ? -1 : tIndex; |
+} |
+ |
+// at this point, the span is already ordered, or unorderable, or unsortable |
+int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType includeType) { |
+ SkASSERT(includeType != SkOpAngle::kUnaryXor); |
+ SkOpAngle* firstAngle = spanToAngle(endIndex, startIndex); |
+ if (NULL == firstAngle) { |
+ return SK_NaN32; |
} |
// 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; |
+ SkOpAngle* baseAngle = NULL; |
bool tryReverse = false; |
// look for counterclockwise transfers |
+ SkOpAngle* angle = firstAngle; |
do { |
- int index = 0; |
+ int testWinding = angle->segment()->windSum(angle); |
+ if (SK_MinS32 != testWinding && !angle->unorderable()) { |
+ baseAngle = angle; |
+ continue; |
+ } |
+ if (angle->unorderable()) { |
+ baseAngle = NULL; |
+ tryReverse = true; |
+ continue; |
+ } |
+ if (baseAngle) { |
+ ComputeOneSum(baseAngle, angle, includeType); |
+ baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL; |
+ tryReverse |= !baseAngle; |
+ } |
+ } while ((angle = angle->next()) != firstAngle); |
+ if (baseAngle && SK_MinS32 == firstAngle->segment()->windSum(angle)) { |
+ firstAngle = baseAngle; |
+ tryReverse = true; |
+ } |
+ if (tryReverse) { |
+ baseAngle = NULL; |
+ angle = firstAngle; |
do { |
- SkOpAngle* testAngle = (*sorted)[index]; |
- int testWinding = testAngle->segment()->windSum(testAngle); |
- if (SK_MinS32 != testWinding && !testAngle->unorderable()) { |
- baseAngle = testAngle; |
+ int testWinding = angle->segment()->windSum(angle); |
+ if (SK_MinS32 != testWinding) { |
+ baseAngle = angle; |
continue; |
} |
- if (testAngle->unorderable()) { |
+ if (angle->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; |
+ ComputeOneSumReverse(baseAngle, angle, includeType); |
+ baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL; |
} |
- } while (++index != last); |
- if (finalInitialOrderable < 0) { |
- break; |
- } |
- 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 (testAngle->unorderable()) { |
- baseAngle = NULL; |
- continue; |
- } |
- if (baseAngle) { |
- ComputeOneSumReverse(baseAngle, testAngle, includeType); |
- baseAngle = SK_MinS32 != testAngle->segment()->windSum(testAngle) ? testAngle |
- : NULL; |
- continue; |
- } |
- if (finalInitialOrderable - 1 == index) { |
- finalInitialOrderable = index; |
- } |
- } |
- if (finalInitialOrderable >= angleCount) { |
- break; |
- } |
- last = finalInitialOrderable; |
- finalInitialOrderable = angleCount + 1; // make this inactive 2nd time through |
- } while (baseAngle); |
+ } while ((angle = angle->previous()) != firstAngle); |
} |
int minIndex = SkMin32(startIndex, endIndex); |
return windSum(minIndex); |
@@ -1083,6 +1352,16 @@ void SkOpSegment::ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* ne |
nextAngle->setLastMarked(last); |
} |
+void SkOpSegment::constructLine(SkPoint shortLine[2]) { |
+ addLine(shortLine, false, false); |
+ addT(NULL, shortLine[0], 0); |
+ addT(NULL, shortLine[1], 1); |
+ addStartSpan(1); |
+ addEndSpan(1); |
+ SkOpAngle& angle = fAngles.push_back(); |
+ angle.set(this, 0, 1); |
+} |
+ |
int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hitT, |
bool* hitSomething, double mid, bool opp, bool current) const { |
SkScalar bottom = fBounds.fBottom; |
@@ -1206,6 +1485,141 @@ bool SkOpSegment::bumpSpan(SkOpSpan* span, int windDelta, int oppDelta) { |
return false; |
} |
+const SkOpSpan& SkOpSegment::firstSpan(const SkOpSpan& thisSpan) const { |
+ const SkOpSpan* firstSpan = &thisSpan; // rewind to the start |
+ const SkOpSpan* beginSpan = fTs.begin(); |
+ const SkPoint& testPt = thisSpan.fPt; |
+ while (firstSpan > beginSpan && firstSpan[-1].fPt == testPt) { |
+ --firstSpan; |
+ } |
+ return *firstSpan; |
+} |
+ |
+const SkOpSpan& SkOpSegment::lastSpan(const SkOpSpan& thisSpan) const { |
+ const SkOpSpan* endSpan = fTs.end() - 1; // last can't be small |
+ const SkOpSpan* lastSpan = &thisSpan; // find the end |
+ const SkPoint& testPt = thisSpan.fPt; |
+ while (lastSpan < endSpan && lastSpan[1].fPt == testPt) { |
+ ++lastSpan; |
+ } |
+ return *lastSpan; |
+} |
+ |
+// with a loop, the comparison is move involved |
+// scan backwards and forwards to count all matching points |
+// (verify that there are twp scans marked as loops) |
+// compare that against 2 matching scans for loop plus other results |
+bool SkOpSegment::calcLoopSpanCount(const SkOpSpan& thisSpan, int* smallCounts) { |
+ const SkOpSpan& firstSpan = this->firstSpan(thisSpan); // rewind to the start |
+ const SkOpSpan& lastSpan = this->lastSpan(thisSpan); // find the end |
+ double firstLoopT = -1, lastLoopT = -1; |
+ const SkOpSpan* testSpan = &firstSpan - 1; |
+ while (++testSpan <= &lastSpan) { |
+ if (testSpan->fLoop) { |
+ firstLoopT = testSpan->fT; |
+ break; |
+ } |
+ } |
+ testSpan = &lastSpan + 1; |
+ while (--testSpan >= &firstSpan) { |
+ if (testSpan->fLoop) { |
+ lastLoopT = testSpan->fT; |
+ break; |
+ } |
+ } |
+ SkASSERT((firstLoopT == -1) == (lastLoopT == -1)); |
+ if (firstLoopT == -1) { |
+ return false; |
+ } |
+ SkASSERT(firstLoopT < lastLoopT); |
+ testSpan = &firstSpan - 1; |
+ smallCounts[0] = smallCounts[1] = 0; |
+ while (++testSpan <= &lastSpan) { |
+ SkASSERT(approximately_equal(testSpan->fT, firstLoopT) + |
+ approximately_equal(testSpan->fT, lastLoopT) == 1); |
+ smallCounts[approximately_equal(testSpan->fT, lastLoopT)]++; |
+ } |
+ return true; |
+} |
+ |
+// see if spans with two or more intersections have the same number on the other end |
+void SkOpSegment::checkDuplicates() { |
+ debugValidate(); |
+ SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans; |
+ int index; |
+ int endIndex = 0; |
+ bool endFound; |
+ do { |
+ index = endIndex; |
+ endIndex = nextExactSpan(index, 1); |
+ if ((endFound = endIndex < 0)) { |
+ endIndex = count(); |
+ } |
+ int dupCount = endIndex - index; |
+ if (dupCount < 2) { |
+ continue; |
+ } |
+ do { |
+ const SkOpSpan* thisSpan = &fTs[index]; |
+ SkOpSegment* other = thisSpan->fOther; |
+ int oIndex = thisSpan->fOtherIndex; |
+ int oStart = other->nextExactSpan(oIndex, -1) + 1; |
+ int oEnd = other->nextExactSpan(oIndex, 1); |
+ if (oEnd < 0) { |
+ oEnd = other->count(); |
+ } |
+ int oCount = oEnd - oStart; |
+ // force the other to match its t and this pt if not on an end point |
+ if (oCount != dupCount) { |
+ MissingSpan& missing = missingSpans.push_back(); |
+ missing.fOther = NULL; |
+ SkDEBUGCODE(sk_bzero(&missing, sizeof(missing))); |
+ missing.fPt = thisSpan->fPt; |
+ const SkOpSpan& oSpan = other->span(oIndex); |
+ if (oCount > dupCount) { |
+ missing.fSegment = this; |
+ missing.fT = thisSpan->fT; |
+ other->checkLinks(&oSpan, &missingSpans); |
+ } else { |
+ missing.fSegment = other; |
+ missing.fT = oSpan.fT; |
+ checkLinks(thisSpan, &missingSpans); |
+ } |
+ if (!missingSpans.back().fOther) { |
+ missingSpans.pop_back(); |
+ } |
+ } |
+ } while (++index < endIndex); |
+ } while (!endFound); |
+ int missingCount = missingSpans.count(); |
+ if (missingCount == 0) { |
+ return; |
+ } |
+ SkSTArray<kMissingSpanCount, MissingSpan, true> missingCoincidence; |
+ for (index = 0; index < missingCount; ++index) { |
+ MissingSpan& missing = missingSpans[index]; |
+ SkOpSegment* missingOther = missing.fOther; |
+ if (missing.fSegment == missing.fOther) { |
+ continue; |
+ } |
+ const SkOpSpan* added = missing.fSegment->addTPair(missing.fT, missingOther, |
+ missing.fOtherT, false, missing.fPt); |
+ if (added && added->fSmall) { |
+ missing.fSegment->checkSmallCoincidence(*added, &missingCoincidence); |
+ } |
+ } |
+ for (index = 0; index < missingCount; ++index) { |
+ MissingSpan& missing = missingSpans[index]; |
+ missing.fSegment->fixOtherTIndex(); |
+ missing.fOther->fixOtherTIndex(); |
+ } |
+ for (index = 0; index < missingCoincidence.count(); ++index) { |
+ MissingSpan& missing = missingCoincidence[index]; |
+ missing.fSegment->fixOtherTIndex(); |
+ } |
+ debugValidate(); |
+} |
+ |
// look to see if the curve end intersects an intermediary that intersects the other |
void SkOpSegment::checkEnds() { |
debugValidate(); |
@@ -1313,7 +1727,9 @@ nextPeekIndex: |
int missingCount = missingSpans.count(); |
for (int index = 0; index < missingCount; ++index) { |
MissingSpan& missing = missingSpans[index]; |
- addTPair(missing.fT, missing.fOther, missing.fOtherT, false, missing.fPt); |
+ if (this != missing.fOther) { |
+ addTPair(missing.fT, missing.fOther, missing.fOtherT, false, missing.fPt); |
+ } |
} |
fixOtherTIndex(); |
// OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to |
@@ -1324,6 +1740,84 @@ nextPeekIndex: |
debugValidate(); |
} |
+void SkOpSegment::checkLinks(const SkOpSpan* base, |
+ SkTArray<MissingSpan, true>* missingSpans) const { |
+ const SkOpSpan* first = fTs.begin(); |
+ const SkOpSpan* last = fTs.end() - 1; |
+ SkASSERT(base >= first && last >= base); |
+ const SkOpSegment* other = base->fOther; |
+ const SkOpSpan* oFirst = other->fTs.begin(); |
+ const SkOpSpan* oLast = other->fTs.end() - 1; |
+ const SkOpSpan* oSpan = &other->fTs[base->fOtherIndex]; |
+ const SkOpSpan* test = base; |
+ const SkOpSpan* missing = NULL; |
+ while (test > first && (--test)->fPt == base->fPt) { |
+ CheckOneLink(test, oSpan, oFirst, oLast, &missing, missingSpans); |
+ } |
+ test = base; |
+ while (test < last && (++test)->fPt == base->fPt) { |
+ CheckOneLink(test, oSpan, oFirst, oLast, &missing, missingSpans); |
+ } |
+} |
+ |
+// see if spans with two or more intersections all agree on common t and point values |
+void SkOpSegment::checkMultiples() { |
+ debugValidate(); |
+ int index; |
+ int end = 0; |
+ while (fTs[++end].fT == 0) |
+ ; |
+ while (fTs[end].fT < 1) { |
+ int start = index = end; |
+ end = nextExactSpan(index, 1); |
+ if (end <= index) { |
+ return; // buffer overflow example triggers this |
+ } |
+ if (index + 1 == end) { |
+ continue; |
+ } |
+ // force the duplicates to agree on t and pt if not on the end |
+ double thisT = fTs[index].fT; |
+ const SkPoint& thisPt = fTs[index].fPt; |
+ bool aligned = false; |
+ while (++index < end) { |
+ aligned |= alignSpan(index, thisT, thisPt); |
+ } |
+ if (aligned) { |
+ alignSpanState(start, end); |
+ } |
+ } |
+ debugValidate(); |
+} |
+ |
+void SkOpSegment::CheckOneLink(const SkOpSpan* test, const SkOpSpan* oSpan, |
+ const SkOpSpan* oFirst, const SkOpSpan* oLast, const SkOpSpan** missingPtr, |
+ SkTArray<MissingSpan, true>* missingSpans) { |
+ SkASSERT(oSpan->fPt == test->fPt); |
+ const SkOpSpan* oTest = oSpan; |
+ while (oTest > oFirst && (--oTest)->fPt == test->fPt) { |
+ if (oTest->fOther == test->fOther && oTest->fOtherT == test->fOtherT) { |
+ return; |
+ } |
+ } |
+ oTest = oSpan; |
+ while (oTest < oLast && (++oTest)->fPt == test->fPt) { |
+ if (oTest->fOther == test->fOther && oTest->fOtherT == test->fOtherT) { |
+ return; |
+ } |
+ } |
+ if (*missingPtr) { |
+ missingSpans->push_back(); |
+ } |
+ MissingSpan& lastMissing = missingSpans->back(); |
+ if (*missingPtr) { |
+ lastMissing = missingSpans->end()[-2]; |
+ } |
+ *missingPtr = test; |
+ lastMissing.fOther = test->fOther; |
+ lastMissing.fOtherT = test->fOtherT; |
+} |
+ |
bool SkOpSegment::checkSmall(int index) const { |
if (fTs[index].fSmall) { |
return true; |
@@ -1334,9 +1828,245 @@ bool SkOpSegment::checkSmall(int index) const { |
return fTs[index].fSmall; |
} |
+// a pair of curves may turn into coincident lines -- small may be a hint that that happened |
+// if a cubic contains a loop, the counts must be adjusted |
+void SkOpSegment::checkSmall() { |
+ SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans; |
+ const SkOpSpan* beginSpan = fTs.begin(); |
+ const SkOpSpan* thisSpan = beginSpan - 1; |
+ const SkOpSpan* endSpan = fTs.end() - 1; // last can't be small |
+ while (++thisSpan < endSpan) { |
+ if (!thisSpan->fSmall) { |
+ continue; |
+ } |
+ if (!thisSpan->fWindValue) { |
+ continue; |
+ } |
+ const SkOpSpan& firstSpan = this->firstSpan(*thisSpan); |
+ const SkOpSpan& lastSpan = this->lastSpan(*thisSpan); |
+ ptrdiff_t smallCount = &lastSpan - &firstSpan + 1; |
+ SkASSERT(1 <= smallCount && smallCount < count()); |
+ if (smallCount <= 1) { |
+ SkASSERT(1 == smallCount); |
+ checkSmallCoincidence(firstSpan, NULL); |
+ continue; |
+ } |
+ // at this point, check for missing computed intersections |
+ const SkPoint& testPt = firstSpan.fPt; |
+ thisSpan = &firstSpan - 1; |
+ SkOpSegment* other = NULL; |
+ while (++thisSpan <= &lastSpan) { |
+ other = thisSpan->fOther; |
+ if (other != this) { |
+ break; |
+ } |
+ } |
+ SkASSERT(other != this); |
+ int oIndex = thisSpan->fOtherIndex; |
+ const SkOpSpan& oSpan = other->span(oIndex); |
+ const SkOpSpan& oFirstSpan = other->firstSpan(oSpan); |
+ const SkOpSpan& oLastSpan = other->lastSpan(oSpan); |
+ ptrdiff_t oCount = &oLastSpan - &oFirstSpan + 1; |
+ if (fLoop) { |
+ int smallCounts[2]; |
+ SkASSERT(!other->fLoop); // FIXME: we need more complicated logic for pair of loops |
+ if (calcLoopSpanCount(*thisSpan, smallCounts)) { |
+ if (smallCounts[0] && oCount != smallCounts[0]) { |
+ SkASSERT(0); // FIXME: need a working test case to properly code & debug |
+ } |
+ if (smallCounts[1] && oCount != smallCounts[1]) { |
+ SkASSERT(0); // FIXME: need a working test case to properly code & debug |
+ } |
+ goto nextSmallCheck; |
+ } |
+ } |
+ if (other->fLoop) { |
+ int otherCounts[2]; |
+ if (other->calcLoopSpanCount(other->span(oIndex), otherCounts)) { |
+ if (otherCounts[0] && otherCounts[0] != smallCount) { |
+ SkASSERT(0); // FIXME: need a working test case to properly code & debug |
+ } |
+ if (otherCounts[1] && otherCounts[1] != smallCount) { |
+ SkASSERT(0); // FIXME: need a working test case to properly code & debug |
+ } |
+ goto nextSmallCheck; |
+ } |
+ } |
+ if (oCount != smallCount) { // check if number of pts in this match other |
+ MissingSpan& missing = missingSpans.push_back(); |
+ missing.fOther = NULL; |
+ SkDEBUGCODE(sk_bzero(&missing, sizeof(missing))); |
+ missing.fPt = testPt; |
+ const SkOpSpan& oSpan = other->span(oIndex); |
+ if (oCount > smallCount) { |
+ missing.fSegment = this; |
+ missing.fT = thisSpan->fT; |
+ other->checkLinks(&oSpan, &missingSpans); |
+ } else { |
+ missing.fSegment = other; |
+ missing.fT = oSpan.fT; |
+ checkLinks(thisSpan, &missingSpans); |
+ } |
+ if (!missingSpans.back().fOther || missing.fSegment->done()) { |
+ missingSpans.pop_back(); |
+ } |
+ } |
+nextSmallCheck: |
+ thisSpan = &lastSpan; |
+ } |
+ int missingCount = missingSpans.count(); |
+ for (int index = 0; index < missingCount; ++index) { |
+ MissingSpan& missing = missingSpans[index]; |
+ SkOpSegment* missingOther = missing.fOther; |
+ // note that add t pair may edit span arrays, so prior pointers to spans are no longer valid |
+ if (!missing.fSegment->addTPair(missing.fT, missingOther, missing.fOtherT, false, |
+ missing.fPt)) { |
+ continue; |
+ } |
+ int otherTIndex = missingOther->findT(missing.fOtherT, missing.fSegment); |
+ const SkOpSpan& otherSpan = missingOther->span(otherTIndex); |
+ if (otherSpan.fSmall) { |
+ const SkOpSpan* nextSpan = &otherSpan; |
+ do { |
+ ++nextSpan; |
+ } while (nextSpan->fSmall); |
+ missing.fSegment->addTCoincident(missing.fPt, nextSpan->fPt, nextSpan->fT, |
+ missingOther); |
+ } else if (otherSpan.fT > 0) { |
+ const SkOpSpan* priorSpan = &otherSpan; |
+ do { |
+ --priorSpan; |
+ } while (priorSpan->fT == otherSpan.fT); |
+ if (priorSpan->fSmall) { |
+ missing.fSegment->addTCancel(missing.fPt, priorSpan->fPt, missingOther); |
+ } |
+ } |
+ } |
+ // OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to |
+ // avoid this |
+ for (int index = 0; index < missingCount; ++index) { |
+ MissingSpan& missing = missingSpans[index]; |
+ missing.fSegment->fixOtherTIndex(); |
+ missing.fOther->fixOtherTIndex(); |
+ } |
+ debugValidate(); |
+} |
+ |
+void SkOpSegment::checkSmallCoincidence(const SkOpSpan& span, |
+ SkTArray<MissingSpan, true>* checkMultiple) { |
+ SkASSERT(span.fSmall); |
+ SkASSERT(span.fWindValue); |
+ SkASSERT(&span < fTs.end() - 1); |
+ const SkOpSpan* next = &span + 1; |
+ SkASSERT(!next->fSmall || checkMultiple); |
+ if (checkMultiple) { |
+ while (next->fSmall) { |
+ ++next; |
+ SkASSERT(next < fTs.end()); |
+ } |
+ } |
+ SkOpSegment* other = span.fOther; |
+ while (other != next->fOther) { |
+ if (!checkMultiple) { |
+ return; |
+ } |
+ const SkOpSpan* test = next + 1; |
+ if (test == fTs.end()) { |
+ return; |
+ } |
+ if (test->fPt != next->fPt || !precisely_equal(test->fT, next->fT)) { |
+ return; |
+ } |
+ next = test; |
+ } |
+ SkASSERT(span.fT < next->fT); |
+ int oStartIndex = other->findExactT(span.fOtherT, this); |
+ int oEndIndex = other->findExactT(next->fOtherT, this); |
+ // FIXME: be overly conservative by limiting this to the caller that allows multiple smalls |
+ if (!checkMultiple || fVerb != SkPath::kLine_Verb || other->fVerb != SkPath::kLine_Verb) { |
+ SkPoint mid = ptAtT((span.fT + next->fT) / 2); |
+ const SkOpSpan& oSpanStart = other->fTs[oStartIndex]; |
+ const SkOpSpan& oSpanEnd = other->fTs[oEndIndex]; |
+ SkPoint oMid = other->ptAtT((oSpanStart.fT + oSpanEnd.fT) / 2); |
+ if (!SkDPoint::ApproximatelyEqual(mid, oMid)) { |
+ return; |
+ } |
+ } |
+ // FIXME: again, be overly conservative to avoid breaking existing tests |
+ const SkOpSpan& oSpan = oStartIndex < oEndIndex ? other->fTs[oStartIndex] |
+ : other->fTs[oEndIndex]; |
+ if (checkMultiple && !oSpan.fSmall) { |
+ return; |
+ } |
+ SkASSERT(oSpan.fSmall); |
+ if (oStartIndex < oEndIndex) { |
+ addTCoincident(span.fPt, next->fPt, next->fT, other); |
+ } else { |
+ addTCancel(span.fPt, next->fPt, other); |
+ } |
+ if (!checkMultiple) { |
+ return; |
+ } |
+ // check to see if either segment is coincident with a third segment -- if it is, and if |
+ // the opposite segment is not already coincident with the third, make it so |
+ // OPTIMIZE: to make this check easier, add coincident and cancel could set a coincident bit |
+ if (span.fWindValue != 1 || span.fOppValue != 0) { |
+// start here; |
+ // iterate through the spans, looking for the third coincident case |
+ // if we find one, we need to return state to the caller so that the indices can be fixed |
+ // this also suggests that all of this function is fragile since it relies on a valid index |
+ } |
+ // probably should make this a common function rather than copy/paste code |
+ if (oSpan.fWindValue != 1 || oSpan.fOppValue != 0) { |
+ const SkOpSpan* oTest = &oSpan; |
+ while (--oTest >= other->fTs.begin()) { |
+ if (oTest->fPt != oSpan.fPt || !precisely_equal(oTest->fT, oSpan.fT)) { |
+ break; |
+ } |
+ SkOpSegment* testOther = oTest->fOther; |
+ SkASSERT(testOther != this); |
+ // look in both directions to see if there is a coincident span |
+ const SkOpSpan* tTest = testOther->fTs.begin(); |
+ for (int testIndex = 0; testIndex < testOther->count(); ++testIndex) { |
+ if (tTest->fPt != span.fPt) { |
+ ++tTest; |
+ continue; |
+ } |
+ if (testOther->verb() != SkPath::kLine_Verb |
+ || other->verb() != SkPath::kLine_Verb) { |
+ SkPoint mid = ptAtT((span.fT + next->fT) / 2); |
+ SkPoint oMid = other->ptAtT((oTest->fOtherT + tTest->fT) / 2); |
+ if (!SkDPoint::ApproximatelyEqual(mid, oMid)) { |
+ continue; |
+ } |
+ } |
+#if DEBUG_CONCIDENT |
+ SkDebugf("%s coincident found=%d %1.9g %1.9g\n", __FUNCTION__, testOther->fID, |
+ oTest->fOtherT, tTest->fT); |
+#endif |
+ if (tTest->fT < oTest->fOtherT) { |
+ addTCoincident(span.fPt, next->fPt, next->fT, testOther); |
+ } else { |
+ addTCancel(span.fPt, next->fPt, testOther); |
+ } |
+ MissingSpan missing; |
+ missing.fSegment = testOther; |
+ checkMultiple->push_back(missing); |
+ break; |
+ } |
+ } |
+ oTest = &oSpan; |
+ while (++oTest < other->fTs.end()) { |
+ if (oTest->fPt != oSpan.fPt || !precisely_equal(oTest->fT, oSpan.fT)) { |
+ break; |
+ } |
+ |
+ } |
+ } |
+} |
+ |
// 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; |
@@ -1401,8 +2131,12 @@ void SkOpSegment::checkTiny() { |
} |
for (int index = 0; index < missingCount; ++index) { |
MissingSpan& missing = missingSpans[index]; |
- missing.fSegment->addTPair(missing.fT, missing.fOther, missing.fOtherT, false, missing.fPt); |
+ if (missing.fSegment != missing.fOther) { |
+ missing.fSegment->addTPair(missing.fT, missing.fOther, missing.fOtherT, false, |
+ missing.fPt); |
+ } |
} |
+ // OPTIMIZE: consolidate to avoid multiple calls to fix index |
for (int index = 0; index < missingCount; ++index) { |
MissingSpan& missing = missingSpans[index]; |
missing.fSegment->fixOtherTIndex(); |
@@ -1474,6 +2208,16 @@ bool SkOpSegment::findCoincidentMatch(const SkOpSpan* span, const SkOpSegment* o |
return false; |
} |
+int SkOpSegment::findEndSpan(int endIndex) const { |
+ const SkOpSpan* span = &fTs[--endIndex]; |
+ const SkPoint& lastPt = span->fPt; |
+ double endT = span->fT; |
+ do { |
+ span = &fTs[--endIndex]; |
+ } while (SkDPoint::ApproximatelyEqual(span->fPt, lastPt) && (span->fT == endT || span->fTiny)); |
+ return endIndex + 1; |
+} |
+ |
/* |
The M and S variable name parts stand for the operators. |
Mi stands for Minuend (see wiki subtraction, analogous to difference) |
@@ -1519,56 +2263,40 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart |
} |
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)); |
- SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted; |
- int calcWinding = computeSum(startIndex, end, SkOpAngle::kBinaryOpp, &angles, &sorted); |
+ // more than one viable candidate -- measure angles to find best |
+ |
+ int calcWinding = computeSum(startIndex, end, SkOpAngle::kBinaryOpp); |
bool sortable = calcWinding != SK_NaN32; |
- if (sortable && sorted.count() == 0) { |
- // if no edge has a computed winding sum, we can go no further |
+ if (!sortable) { |
*unsortable = true; |
return NULL; |
} |
- int angleCount = angles.count(); |
- int firstIndex = findStartingEdge(sorted, startIndex, end); |
- SkASSERT(!sortable || firstIndex >= 0); |
-#if DEBUG_SORT |
- debugShowSort(__FUNCTION__, sorted, firstIndex, sortable); |
-#endif |
- if (!sortable) { |
+ SkOpAngle* angle = spanToAngle(end, startIndex); |
+ if (angle->unorderable()) { |
*unsortable = true; |
return NULL; |
} |
- SkASSERT(sorted[firstIndex]->segment() == this); |
-#if DEBUG_WINDING |
- SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex, |
- sorted[firstIndex]->sign()); |
+#if DEBUG_SORT |
+ SkDebugf("%s\n", __FUNCTION__); |
+ angle->debugLoop(); |
#endif |
int sumMiWinding = updateWinding(endIndex, startIndex); |
int sumSuWinding = updateOppWinding(endIndex, startIndex); |
if (operand()) { |
SkTSwap<int>(sumMiWinding, sumSuWinding); |
} |
- int nextIndex = firstIndex + 1; |
- int lastIndex = firstIndex != 0 ? firstIndex : angleCount; |
+ SkOpAngle* nextAngle = angle->next(); |
const SkOpAngle* foundAngle = NULL; |
bool foundDone = false; |
// iterate through the angle, and compute everyone's winding |
SkOpSegment* nextSegment; |
int activeCount = 0; |
do { |
- SkASSERT(nextIndex != firstIndex); |
- if (nextIndex == angleCount) { |
- nextIndex = 0; |
- } |
- const SkOpAngle* nextAngle = sorted[nextIndex]; |
nextSegment = nextAngle->segment(); |
- int maxWinding, sumWinding, oppMaxWinding, oppSumWinding; |
bool activeAngle = nextSegment->activeOp(xorMiMask, xorSuMask, nextAngle->start(), |
- nextAngle->end(), op, &sumMiWinding, &sumSuWinding, |
- &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding); |
+ nextAngle->end(), op, &sumMiWinding, &sumSuWinding); |
if (activeAngle) { |
++activeCount; |
if (!foundAngle || (foundDone && activeCount & 1)) { |
@@ -1591,6 +2319,7 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart |
} |
SkOpSpan* last = nextAngle->lastMarked(); |
if (last) { |
+ SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last)); |
*chase->append() = last; |
#if DEBUG_WINDING |
SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__, |
@@ -1598,7 +2327,13 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart |
last->fSmall); |
#endif |
} |
- } while (++nextIndex != lastIndex); |
+ } while ((nextAngle = nextAngle->next()) != angle); |
+#if DEBUG_ANGLE |
+ if (foundAngle) { |
+ foundAngle->debugSameAs(foundAngle); |
+ } |
+#endif |
+ |
markDoneBinary(SkMin32(startIndex, endIndex)); |
if (!foundAngle) { |
return NULL; |
@@ -1650,46 +2385,31 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* next |
} |
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)); |
- SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted; |
- int calcWinding = computeSum(startIndex, end, SkOpAngle::kUnaryWinding, &angles, &sorted); |
+ // more than one viable candidate -- measure angles to find best |
+ |
+ int calcWinding = computeSum(startIndex, end, SkOpAngle::kUnaryWinding); |
bool sortable = calcWinding != SK_NaN32; |
- int angleCount = angles.count(); |
- int firstIndex = findStartingEdge(sorted, startIndex, end); |
- SkASSERT(!sortable || firstIndex >= 0); |
-#if DEBUG_SORT |
- debugShowSort(__FUNCTION__, sorted, firstIndex, 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()); |
+ SkOpAngle* angle = spanToAngle(end, startIndex); |
+#if DEBUG_SORT |
+ SkDebugf("%s\n", __FUNCTION__); |
+ angle->debugLoop(); |
#endif |
int sumWinding = updateWinding(endIndex, startIndex); |
- int nextIndex = firstIndex + 1; |
- int lastIndex = firstIndex != 0 ? firstIndex : angleCount; |
+ SkOpAngle* nextAngle = angle->next(); |
const SkOpAngle* foundAngle = NULL; |
bool foundDone = false; |
- // iterate through the angle, and compute everyone's winding |
SkOpSegment* nextSegment; |
int activeCount = 0; |
do { |
- SkASSERT(nextIndex != firstIndex); |
- if (nextIndex == angleCount) { |
- nextIndex = 0; |
- } |
- const SkOpAngle* nextAngle = sorted[nextIndex]; |
nextSegment = nextAngle->segment(); |
- int maxWinding; |
bool activeAngle = nextSegment->activeWinding(nextAngle->start(), nextAngle->end(), |
- &maxWinding, &sumWinding); |
+ &sumWinding); |
if (activeAngle) { |
++activeCount; |
if (!foundAngle || (foundDone && activeCount & 1)) { |
@@ -1712,6 +2432,8 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* next |
} |
SkOpSpan* last = nextAngle->lastMarked(); |
if (last) { |
+ SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last)); |
+ // assert here that span isn't already in array |
*chase->append() = last; |
#if DEBUG_WINDING |
SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__, |
@@ -1719,7 +2441,7 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* next |
last->fSmall); |
#endif |
} |
- } while (++nextIndex != lastIndex); |
+ } while ((nextAngle = nextAngle->next()) != angle); |
markDoneUnary(SkMin32(startIndex, endIndex)); |
if (!foundAngle) { |
return NULL; |
@@ -1745,7 +2467,9 @@ SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsort |
SkASSERT(end >= 0); |
SkOpSpan* endSpan = &fTs[end]; |
SkOpSegment* other; |
- if (isSimple(end)) { |
+// Detect cases where all the ends canceled out (e.g., |
+// there is no angle) and therefore there's only one valid connection |
+ if (isSimple(end) || !spanToAngle(end, startIndex)) { |
#if DEBUG_WINDING |
SkDebugf("%s simple\n", __FUNCTION__); |
#endif |
@@ -1779,39 +2503,21 @@ SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsort |
SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count()); |
return other; |
} |
- SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles; |
SkASSERT(startIndex - endIndex != 0); |
- SkASSERT((startIndex - endIndex < 0) ^ (step < 0)); |
- SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted; |
- 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(!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()); |
+ SkASSERT((startIndex - endIndex < 0) ^ (step < 0)); |
+ // parallel block above with presorted version |
+ SkOpAngle* angle = spanToAngle(end, startIndex); |
+ SkASSERT(angle); |
+#if DEBUG_SORT |
+ SkDebugf("%s\n", __FUNCTION__); |
+ angle->debugLoop(); |
#endif |
- int nextIndex = firstIndex + 1; |
- int lastIndex = firstIndex != 0 ? firstIndex : angleCount; |
+ SkOpAngle* nextAngle = angle->next(); |
const SkOpAngle* foundAngle = NULL; |
bool foundDone = false; |
SkOpSegment* nextSegment; |
int activeCount = 0; |
do { |
- SkASSERT(nextIndex != firstIndex); |
- if (nextIndex == angleCount) { |
- nextIndex = 0; |
- } |
- const SkOpAngle* nextAngle = sorted[nextIndex]; |
nextSegment = nextAngle->segment(); |
++activeCount; |
if (!foundAngle || (foundDone && activeCount & 1)) { |
@@ -1820,12 +2526,12 @@ SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsort |
return NULL; |
} |
foundAngle = nextAngle; |
- foundDone = nextSegment->done(nextAngle); |
- } |
- if (nextSegment->done()) { |
- continue; |
+ if (!(foundDone = nextSegment->done(nextAngle))) { |
+ break; |
+ } |
} |
- } while (++nextIndex != lastIndex); |
+ nextAngle = nextAngle->next(); |
+ } while (nextAngle != angle); |
markDone(SkMin32(startIndex, endIndex), 1); |
if (!foundAngle) { |
return NULL; |
@@ -1840,21 +2546,33 @@ SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsort |
return nextSegment; |
} |
-int SkOpSegment::findStartingEdge(const SkTArray<SkOpAngle*, true>& sorted, int start, int end) { |
- int angleCount = sorted.count(); |
- int firstIndex = -1; |
- for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) { |
- const SkOpAngle* angle = sorted[angleIndex]; |
- if (angle->segment() == this && angle->start() == end && |
- angle->end() == start) { |
- firstIndex = angleIndex; |
- break; |
+int SkOpSegment::findStartSpan(int startIndex) const { |
+ int index = startIndex; |
+ const SkOpSpan* span = &fTs[index]; |
+ const SkPoint& firstPt = span->fPt; |
+ double firstT = span->fT; |
+ do { |
+ if (index > 0) { |
+ if (span->fT != firstT) { |
+ break; |
+ } |
+ if (!SkDPoint::ApproximatelyEqual(firstPt, fTs[index].fPt)) { |
+ return -1; |
+ } |
} |
- } |
- return firstIndex; |
+ ++index; |
+ if (span->fTiny) { |
+ if (!SkDPoint::ApproximatelyEqual(firstPt, fTs[index].fPt)) { |
+ return -1; |
+ } |
+ firstT = fTs[index++].fT; |
+ } |
+ span = &fTs[index]; |
+ } while (true); |
+ return index; |
} |
-int SkOpSegment::findT(double t, const SkOpSegment* match) const { |
+int SkOpSegment::findExactT(double t, const SkOpSegment* match) const { |
int count = this->count(); |
for (int index = 0; index < count; ++index) { |
const SkOpSpan& span = fTs[index]; |
@@ -1866,21 +2584,24 @@ int SkOpSegment::findT(double t, const SkOpSegment* match) const { |
return -1; |
} |
-// FIXME: either: |
-// a) mark spans with either end unsortable as done, or |
-// b) rewrite findTop / findTopSegment / findTopContour to iterate further |
-// when encountering an unsortable span |
+int SkOpSegment::findT(double t, const SkOpSegment* match) const { |
+ int count = this->count(); |
+ for (int index = 0; index < count; ++index) { |
+ const SkOpSpan& span = fTs[index]; |
+ if (approximately_equal_orderable(span.fT, t) && span.fOther == match) { |
+ return index; |
+ } |
+ } |
+ SkASSERT(0); |
+ return -1; |
+} |
-// OPTIMIZATION : for a pair of lines, can we compute points at T (cached) |
-// and use more concise logic like the old edge walker code? |
-// FIXME: this needs to deal with coincident edges |
-SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsortable, |
- bool onlySortable) { |
+SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsortable) { |
// iterate through T intersections and return topmost |
// topmost tangent from y-min to first pt is closer to horizontal |
SkASSERT(!done()); |
int firstT = -1; |
- /* SkPoint topPt = */ activeLeftTop(onlySortable, &firstT); |
+ /* SkPoint topPt = */ activeLeftTop(true, &firstT); |
if (firstT < 0) { |
*unsortable = true; |
firstT = 0; |
@@ -1894,59 +2615,53 @@ SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsort |
} |
// sort the edges to find the leftmost |
int step = 1; |
- int end = nextSpan(firstT, step); |
- if (end == -1) { |
+ int end; |
+ if (span(firstT).fDone || (end = nextSpan(firstT, step)) == -1) { |
step = -1; |
end = nextSpan(firstT, step); |
SkASSERT(end != -1); |
} |
// if the topmost T is not on end, or is three-way or more, find left |
// look for left-ness from tLeft to firstT (matching y of other) |
- SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles; |
SkASSERT(firstT - end != 0); |
- addTwoAngles(end, firstT, &angles); |
- 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; |
+ SkOpAngle* markAngle = spanToAngle(firstT, end); |
+ if (!markAngle) { |
+ markAngle = addSingletonAngles(firstT, step); |
+ } |
+ markAngle->markStops(); |
+ const SkOpAngle* baseAngle = markAngle->findFirst(); |
+ if (!baseAngle) { |
+ return NULL; // nothing to do |
} |
- 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); |
- if (approximately_greater(top, bounds.fTop)) { |
- top = bounds.fTop; |
- first = index; |
+ const SkOpAngle* firstAngle = NULL; |
+ const SkOpAngle* angle = baseAngle; |
+ do { |
+ if (!angle->unorderable()) { |
+ SkOpSegment* next = angle->segment(); |
+ SkPathOpsBounds bounds; |
+ next->subDivideBounds(angle->end(), angle->start(), &bounds); |
+ if (approximately_greater(top, bounds.fTop)) { |
+ top = bounds.fTop; |
+ firstAngle = angle; |
+ } |
} |
- } |
- SkASSERT(first < SK_MaxS32); |
-#if DEBUG_SORT // || DEBUG_SWAP_TOP |
- sorted[first]->segment()->debugShowSort(__FUNCTION__, sorted, first, 0, 0, sortable); |
+ angle = angle->next(); |
+ } while (angle != baseAngle); |
+ SkASSERT(firstAngle); |
+#if DEBUG_SORT |
+ SkDebugf("%s\n", __FUNCTION__); |
+ firstAngle->debugLoop(); |
#endif |
// skip edges that have already been processed |
- firstT = first - 1; |
+ angle = firstAngle; |
SkOpSegment* leftSegment; |
do { |
- if (++firstT == count) { |
- firstT = 0; |
- } |
- const SkOpAngle* angle = sorted[firstT]; |
- SkASSERT(!onlySortable || !angle->unsortable()); |
+// SkASSERT(!angle->unsortable()); |
leftSegment = angle->segment(); |
*tIndexPtr = angle->end(); |
*endIndexPtr = angle->start(); |
+ angle = angle->next(); |
} while (leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fDone); |
if (leftSegment->verb() >= SkPath::kQuad_Verb) { |
const int tIndex = *tIndexPtr; |
@@ -1972,6 +2687,14 @@ SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsort |
return leftSegment; |
} |
+int SkOpSegment::firstActive(int tIndex) const { |
+ while (fTs[tIndex].fTiny) { |
+ SkASSERT(!isCanceled(tIndex)); |
+ ++tIndex; |
+ } |
+ return tIndex; |
+} |
+ |
// FIXME: not crazy about this |
// when the intersections are performed, the other index is into an |
// incomplete array. As the array grows, the indices become incorrect |
@@ -2005,14 +2728,21 @@ void SkOpSegment::init(const SkPoint pts[], SkPath::Verb verb, bool operand, boo |
fXor = evenOdd; |
fPts = pts; |
fVerb = verb; |
+ fLoop = fSmall = fTiny = false; |
} |
-void SkOpSegment::initWinding(int start, int end) { |
+void SkOpSegment::initWinding(int start, int end, SkOpAngle::IncludeType angleIncludeType) { |
int local = spanSign(start, end); |
- int oppLocal = oppSign(start, end); |
- (void) markAndChaseWinding(start, end, local, oppLocal); |
+ if (angleIncludeType == SkOpAngle::kBinarySingle) { |
+ int oppLocal = oppSign(start, end); |
+ (void) markAndChaseWinding(start, end, local, oppLocal); |
// OPTIMIZATION: the reverse mark and chase could skip the first marking |
- (void) markAndChaseWinding(end, start, local, oppLocal); |
+ (void) markAndChaseWinding(end, start, local, oppLocal); |
+ } else { |
+ (void) markAndChaseWinding(start, end, local); |
+ // OPTIMIZATION: the reverse mark and chase could skip the first marking |
+ (void) markAndChaseWinding(end, start, local); |
+ } |
} |
/* |
@@ -2034,13 +2764,9 @@ void SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkSc |
SkDebugf("%s oldWinding=%d hitDx=%c dx=%c windVal=%d", __FUNCTION__, winding, |
hitDx ? hitDx > 0 ? '+' : '-' : '0', dx > 0 ? '+' : '-', windVal); |
#endif |
- if (!winding) { |
- winding = dx < 0 ? windVal : -windVal; |
- } else if (winding * dx < 0) { |
- int sideWind = winding + (dx < 0 ? windVal : -windVal); |
- if (abs(winding) < abs(sideWind)) { |
- winding = sideWind; |
- } |
+ int sideWind = winding + (dx < 0 ? windVal : -windVal); |
+ if (abs(winding) < abs(sideWind)) { |
+ winding = sideWind; |
} |
#if DEBUG_WINDING_AT_T |
SkDebugf(" winding=%d\n", winding); |
@@ -2061,11 +2787,29 @@ void SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkSc |
(void) markAndChaseWinding(end, start, winding, oppWind); |
} |
+bool SkOpSegment::inLoop(const SkOpAngle* baseAngle, int spanCount, int* indexPtr) const { |
+ if (!baseAngle->inLoop()) { |
+ return false; |
+ } |
+ int index = *indexPtr; |
+ int froIndex = fTs[index].fFromAngleIndex; |
+ int toIndex = fTs[index].fToAngleIndex; |
+ while (++index < spanCount) { |
+ int nextFro = fTs[index].fFromAngleIndex; |
+ int nextTo = fTs[index].fToAngleIndex; |
+ if (froIndex != nextFro || toIndex != nextTo) { |
+ break; |
+ } |
+ } |
+ *indexPtr = index; |
+ return true; |
+} |
+ |
// 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 SkPoint& pt) const { |
- size_t tCount = fTs.count(); |
- for (size_t index = 0; index < tCount; ++index) { |
+ int tCount = fTs.count(); |
+ for (int index = 0; index < tCount; ++index) { |
const SkOpSpan& span = fTs[index]; |
if (approximately_zero(startT - span.fT) && pt == span.fPt) { |
return false; |
@@ -2075,6 +2819,7 @@ bool SkOpSegment::isMissing(double startT, const SkPoint& pt) const { |
} |
bool SkOpSegment::isSimple(int end) const { |
+#if 1 |
int count = fTs.count(); |
if (count == 2) { |
return true; |
@@ -2087,6 +2832,19 @@ bool SkOpSegment::isSimple(int end) const { |
return !approximately_greater_than_one(fTs[count - 2].fT); |
} |
return false; |
+#else |
+ // OPTIMIZE: code could be rejiggered to use this simpler test. To make this work, a little |
+ // more logic is required to ignore the dangling canceled out spans |
+ const SkOpSpan& span = fTs[end]; |
+ return span.fFromAngleIndex < 0 && span.fToAngleIndex < 0; |
+#endif |
+} |
+ |
+bool SkOpSegment::isSmall(const SkOpAngle* angle) const { |
+ int start = angle->start(); |
+ int end = angle->end(); |
+ const SkOpSpan& mSpan = fTs[SkMin32(start, end)]; |
+ return mSpan.fSmall; |
} |
bool SkOpSegment::isTiny(const SkOpAngle* angle) const { |
@@ -2103,13 +2861,12 @@ bool SkOpSegment::isTiny(int index) const { |
// look pair of active edges going away from coincident edge |
// one of them should be the continuation of other |
// if both are active, look to see if they both the connect to another coincident pair |
-// if one at least one is a line, then make the pair coincident |
+// if at least one is a line, then make the pair coincident |
// if neither is a line, test for coincidence |
-bool SkOpSegment::joinCoincidence(SkOpSegment* other, double otherT, int step, |
- bool cancel) { |
+bool SkOpSegment::joinCoincidence(SkOpSegment* other, double otherT, int step, bool cancel) { |
int otherTIndex = other->findT(otherT, this); |
int next = other->nextExactSpan(otherTIndex, step); |
- int otherMin = SkTMin(otherTIndex, next); |
+ int otherMin = SkMin32(otherTIndex, next); |
int otherWind = other->span(otherMin).fWindValue; |
if (otherWind == 0) { |
return false; |
@@ -2171,7 +2928,7 @@ SkOpSpan* SkOpSegment::markAndChaseDoneUnary(int index, int endIndex) { |
return last; |
} |
-SkOpSpan* SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, const int winding) { |
+SkOpSpan* SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding) { |
int index = angle->start(); |
int endIndex = angle->end(); |
int step = SkSign32(endIndex - index); |
@@ -2189,6 +2946,22 @@ SkOpSpan* SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, const int win |
return last; |
} |
+SkOpSpan* SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding) { |
+ int min = SkMin32(index, endIndex); |
+ int step = SkSign32(endIndex - index); |
+ markWinding(min, winding); |
+ SkOpSpan* last; |
+ SkOpSegment* other = this; |
+ while ((other = other->nextChase(&index, step, &min, &last))) { |
+ if (other->fTs[min].fWindSum != SK_MinS32) { |
+ SkASSERT(other->fTs[min].fWindSum == winding || other->fTs[min].fLoop); |
+ return NULL; |
+ } |
+ other->markWinding(min, winding); |
+ } |
+ return last; |
+} |
+ |
SkOpSpan* SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, int oppWinding) { |
int min = SkMin32(index, endIndex); |
int step = SkSign32(endIndex - index); |
@@ -2265,6 +3038,7 @@ void SkOpSegment::markDone(int index, int winding) { |
do { |
markOneDone(__FUNCTION__, index, winding); |
} while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT)); |
+ debugValidate(); |
} |
void SkOpSegment::markDoneBinary(int index) { |
@@ -2276,6 +3050,7 @@ void SkOpSegment::markDoneBinary(int index) { |
do { |
markOneDoneBinary(__FUNCTION__, index); |
} while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT)); |
+ debugValidate(); |
} |
void SkOpSegment::markDoneUnary(int index) { |
@@ -2287,11 +3062,12 @@ void SkOpSegment::markDoneUnary(int index) { |
do { |
markOneDoneUnary(__FUNCTION__, index); |
} while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT)); |
+ debugValidate(); |
} |
void SkOpSegment::markOneDone(const char* funName, int tIndex, int winding) { |
SkOpSpan* span = markOneWinding(funName, tIndex, winding); |
- if (!span) { |
+ if (!span || span->fDone) { |
return; |
} |
span->fDone = true; |
@@ -2303,6 +3079,7 @@ void SkOpSegment::markOneDoneBinary(const char* funName, int tIndex) { |
if (!span) { |
return; |
} |
+ SkASSERT(!span->fDone); |
span->fDone = true; |
fDoneSpans++; |
} |
@@ -2312,13 +3089,17 @@ void SkOpSegment::markOneDoneUnary(const char* funName, int tIndex) { |
if (!span) { |
return; |
} |
+ if (span->fWindSum == SK_MinS32) { |
+ SkDebugf("%s uncomputed\n", __FUNCTION__); |
+ } |
+ SkASSERT(!span->fDone); |
span->fDone = true; |
fDoneSpans++; |
} |
SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding) { |
SkOpSpan& span = fTs[tIndex]; |
- if (span.fDone) { |
+ if (span.fDone && !span.fSmall) { |
return NULL; |
} |
#if DEBUG_MARK_DONE |
@@ -2345,6 +3126,7 @@ SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int windi |
SkASSERT(span.fOppSum == SK_MinS32 || span.fOppSum == oppWinding); |
SkASSERT(abs(oppWinding) <= SkPathOpsDebug::gMaxWindSum); |
span.fOppSum = oppWinding; |
+ debugValidate(); |
return &span; |
} |
@@ -2447,10 +3229,12 @@ void SkOpSegment::markUnsortable(int start, int end) { |
span->fUnsortableEnd = true; |
} |
if (!span->fUnsortableStart || !span->fUnsortableEnd || span->fDone) { |
+ debugValidate(); |
return; |
} |
span->fDone = true; |
fDoneSpans++; |
+ debugValidate(); |
} |
void SkOpSegment::markWinding(int index, int winding) { |
@@ -2464,6 +3248,7 @@ void SkOpSegment::markWinding(int index, int winding) { |
do { |
markOneWinding(__FUNCTION__, index, winding); |
} while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT)); |
+ debugValidate(); |
} |
void SkOpSegment::markWinding(int index, int winding, int oppWinding) { |
@@ -2477,13 +3262,29 @@ void SkOpSegment::markWinding(int index, int winding, int oppWinding) { |
do { |
markOneWinding(__FUNCTION__, index, winding, oppWinding); |
} while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT)); |
+ debugValidate(); |
} |
void SkOpSegment::matchWindingValue(int tIndex, double t, bool borrowWind) { |
int nextDoorWind = SK_MaxS32; |
int nextOppWind = SK_MaxS32; |
+ // prefer exact matches |
if (tIndex > 0) { |
const SkOpSpan& below = fTs[tIndex - 1]; |
+ if (below.fT == t) { |
+ nextDoorWind = below.fWindValue; |
+ nextOppWind = below.fOppValue; |
+ } |
+ } |
+ if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) { |
+ const SkOpSpan& above = fTs[tIndex + 1]; |
+ if (above.fT == t) { |
+ nextDoorWind = above.fWindValue; |
+ nextOppWind = above.fOppValue; |
+ } |
+ } |
+ if (nextDoorWind == SK_MaxS32 && tIndex > 0) { |
+ const SkOpSpan& below = fTs[tIndex - 1]; |
if (approximately_negative(t - below.fT)) { |
nextDoorWind = below.fWindValue; |
nextOppWind = below.fOppValue; |
@@ -2637,70 +3438,98 @@ void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding, |
SkASSERT(abs(*sumWinding) <= SkPathOpsDebug::gMaxWindSum); |
} |
-// This marks all spans unsortable so that this info is available for early |
-// exclusion in find top and others. This could be optimized to only mark |
-// adjacent spans that unsortable. However, this makes it difficult to later |
-// determine starting points for edge detection in find top and the like. |
-bool SkOpSegment::SortAngles(const SkTArray<SkOpAngle, true>& angles, |
- SkTArray<SkOpAngle*, true>* angleList, |
- SortAngleKind orderKind) { |
- bool sortable = true; |
- int angleCount = angles.count(); |
- int angleIndex; |
- for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) { |
- const SkOpAngle& angle = angles[angleIndex]; |
- angleList->push_back(const_cast<SkOpAngle*>(&angle)); |
+void SkOpSegment::sortAngles() { |
+ int spanCount = fTs.count(); |
+ if (spanCount <= 2) { |
+ return; |
+ } |
+ int index = 0; |
+ do { |
+ int fromIndex = fTs[index].fFromAngleIndex; |
+ int toIndex = fTs[index].fToAngleIndex; |
+ if (fromIndex < 0 && toIndex < 0) { |
+ index += 1; |
+ continue; |
+ } |
+ SkOpAngle* baseAngle = NULL; |
+ if (fromIndex >= 0) { |
+ baseAngle = &this->angle(fromIndex); |
+ if (inLoop(baseAngle, spanCount, &index)) { |
+ continue; |
+ } |
+ } |
#if DEBUG_ANGLE |
- (*(angleList->end() - 1))->setID(angleIndex); |
+ bool wroteAfterHeader = false; |
#endif |
- sortable &= !(angle.unsortable() || (orderKind == kMustBeOrdered_SortAngleKind |
- && angle.unorderable())); |
- } |
- if (sortable) { |
- SkTQSort<SkOpAngle>(angleList->begin(), angleList->end() - 1); |
- for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) { |
- if (angles[angleIndex].unsortable() || (orderKind == kMustBeOrdered_SortAngleKind |
- && angles[angleIndex].unorderable())) { |
- sortable = false; |
- break; |
+ if (toIndex >= 0) { |
+ SkOpAngle* toAngle = &this->angle(toIndex); |
+ if (!baseAngle) { |
+ baseAngle = toAngle; |
+ if (inLoop(baseAngle, spanCount, &index)) { |
+ continue; |
+ } |
+ } else { |
+ SkDEBUGCODE(int newIndex = index); |
+ SkASSERT(!inLoop(baseAngle, spanCount, &newIndex) && newIndex == index); |
+#if DEBUG_ANGLE |
+ SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT, |
+ index); |
+ wroteAfterHeader = true; |
+#endif |
+ baseAngle->insert(toAngle); |
} |
} |
- } |
- if (!sortable) { |
- for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) { |
- const SkOpAngle& angle = angles[angleIndex]; |
- angle.segment()->markUnsortable(angle.start(), angle.end()); |
- } |
- } |
- 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)); |
+ int nextFrom, nextTo; |
+ int firstIndex = index; |
+ do { |
+ SkOpSpan& span = fTs[index]; |
+ SkOpSegment* other = span.fOther; |
+ SkOpSpan& oSpan = other->fTs[span.fOtherIndex]; |
+ int otherAngleIndex = oSpan.fFromAngleIndex; |
+ if (otherAngleIndex >= 0) { |
#if DEBUG_ANGLE |
- (*(angleList->end() - 1))->setID(angleIndex); |
+ if (!wroteAfterHeader) { |
+ SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT, |
+ index); |
+ wroteAfterHeader = true; |
+ } |
#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; |
+ baseAngle->insert(&other->angle(otherAngleIndex)); |
+ } |
+ otherAngleIndex = oSpan.fToAngleIndex; |
+ if (otherAngleIndex >= 0) { |
+#if DEBUG_ANGLE |
+ if (!wroteAfterHeader) { |
+ SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT, |
+ index); |
+ wroteAfterHeader = true; |
+ } |
+#endif |
+ baseAngle->insert(&other->angle(otherAngleIndex)); |
+ } |
+ if (++index == spanCount) { |
+ break; |
+ } |
+ nextFrom = fTs[index].fFromAngleIndex; |
+ nextTo = fTs[index].fToAngleIndex; |
+ } while (fromIndex == nextFrom && toIndex == nextTo); |
+ if (baseAngle && baseAngle->loopCount() == 1) { |
+ index = firstIndex; |
+ do { |
+ SkOpSpan& span = fTs[index]; |
+ span.fFromAngleIndex = span.fToAngleIndex = -1; |
+ if (++index == spanCount) { |
+ break; |
+ } |
+ nextFrom = fTs[index].fFromAngleIndex; |
+ nextTo = fTs[index].fToAngleIndex; |
+ } while (fromIndex == nextFrom && toIndex == nextTo); |
+ baseAngle = NULL; |
+ } |
+#if DEBUG_SORT |
+ SkASSERT(!baseAngle || baseAngle->loopCount() > 1); |
+#endif |
+ } while (index < spanCount); |
} |
// return true if midpoints were computed |
@@ -2802,8 +3631,8 @@ void SkOpSegment::TrackOutside(SkTArray<SkPoint, true>* outsidePts, const SkPoin |
} |
void SkOpSegment::undoneSpan(int* start, int* end) { |
- size_t tCount = fTs.count(); |
- size_t index; |
+ int tCount = fTs.count(); |
+ int index; |
for (index = 0; index < tCount; ++index) { |
if (!fTs[index].fDone) { |
break; |
@@ -2947,382 +3776,3 @@ void SkOpSegment::zeroSpan(SkOpSpan* span) { |
span->fDone = true; |
++fDoneSpans; |
} |
- |
-#if DEBUG_SWAP_TOP |
-bool SkOpSegment::controlsContainedByEnds(int tStart, int tEnd) const { |
- if (fVerb != SkPath::kCubic_Verb) { |
- return false; |
- } |
- SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT); |
- return dst.controlsContainedByEnds(); |
-} |
-#endif |
- |
-#if DEBUG_CONCIDENT |
-// SkASSERT if pair has not already been added |
-void SkOpSegment::debugAddTPair(double t, const SkOpSegment& other, double otherT) const { |
- for (int i = 0; i < fTs.count(); ++i) { |
- if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) { |
- return; |
- } |
- } |
- SkASSERT(0); |
-} |
-#endif |
- |
-#if DEBUG_CONCIDENT |
-void SkOpSegment::debugShowTs(const char* prefix) const { |
- SkDebugf("%s %s id=%d", __FUNCTION__, prefix, fID); |
- int lastWind = -1; |
- int lastOpp = -1; |
- double lastT = -1; |
- int i; |
- for (i = 0; i < fTs.count(); ++i) { |
- bool change = lastT != fTs[i].fT || lastWind != fTs[i].fWindValue |
- || lastOpp != fTs[i].fOppValue; |
- if (change && lastWind >= 0) { |
- SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]", |
- lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp); |
- } |
- if (change) { |
- SkDebugf(" [o=%d", fTs[i].fOther->fID); |
- lastWind = fTs[i].fWindValue; |
- lastOpp = fTs[i].fOppValue; |
- lastT = fTs[i].fT; |
- } else { |
- SkDebugf(",%d", fTs[i].fOther->fID); |
- } |
- } |
- if (i <= 0) { |
- return; |
- } |
- SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]", |
- lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp); |
- if (fOperand) { |
- SkDebugf(" operand"); |
- } |
- if (done()) { |
- SkDebugf(" done"); |
- } |
- SkDebugf("\n"); |
-} |
-#endif |
- |
-#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY |
-void SkOpSegment::debugShowActiveSpans() const { |
- debugValidate(); |
- if (done()) { |
- return; |
- } |
-#if DEBUG_ACTIVE_SPANS_SHORT_FORM |
- int lastId = -1; |
- double lastT = -1; |
-#endif |
- for (int i = 0; i < fTs.count(); ++i) { |
- if (fTs[i].fDone) { |
- continue; |
- } |
- SkASSERT(i < fTs.count() - 1); |
-#if DEBUG_ACTIVE_SPANS_SHORT_FORM |
- if (lastId == fID && lastT == fTs[i].fT) { |
- continue; |
- } |
- lastId = fID; |
- lastT = fTs[i].fT; |
-#endif |
- SkDebugf("%s id=%d", __FUNCTION__, fID); |
- SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY); |
- for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) { |
- SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY); |
- } |
- const SkOpSpan* span = &fTs[i]; |
- SkDebugf(") t=%1.9g (%1.9g,%1.9g)", span->fT, xAtT(span), yAtT(span)); |
- int iEnd = i + 1; |
- while (fTs[iEnd].fT < 1 && approximately_equal(fTs[i].fT, fTs[iEnd].fT)) { |
- ++iEnd; |
- } |
- SkDebugf(" tEnd=%1.9g", fTs[iEnd].fT); |
- const SkOpSegment* other = fTs[i].fOther; |
- SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=", |
- other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex); |
- if (fTs[i].fWindSum == SK_MinS32) { |
- SkDebugf("?"); |
- } else { |
- SkDebugf("%d", fTs[i].fWindSum); |
- } |
- SkDebugf(" windValue=%d oppValue=%d\n", fTs[i].fWindValue, fTs[i].fOppValue); |
- } |
-} |
-#endif |
- |
- |
-#if DEBUG_MARK_DONE || DEBUG_UNSORTABLE |
-void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding) { |
- const SkPoint& pt = xyAtT(&span); |
- SkDebugf("%s id=%d", fun, fID); |
- SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY); |
- for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) { |
- SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY); |
- } |
- SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther-> |
- fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]); |
- SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d windSum=", |
- span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, |
- (&span)[1].fT, winding); |
- if (span.fWindSum == SK_MinS32) { |
- SkDebugf("?"); |
- } else { |
- SkDebugf("%d", span.fWindSum); |
- } |
- SkDebugf(" windValue=%d\n", span.fWindValue); |
-} |
- |
-void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding, |
- int oppWinding) { |
- const SkPoint& pt = xyAtT(&span); |
- SkDebugf("%s id=%d", fun, fID); |
- SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY); |
- for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) { |
- SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY); |
- } |
- SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther-> |
- fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]); |
- SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d newOppSum=%d oppSum=", |
- span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, |
- (&span)[1].fT, winding, oppWinding); |
- if (span.fOppSum == SK_MinS32) { |
- SkDebugf("?"); |
- } else { |
- SkDebugf("%d", span.fOppSum); |
- } |
- SkDebugf(" windSum="); |
- if (span.fWindSum == SK_MinS32) { |
- SkDebugf("?"); |
- } else { |
- SkDebugf("%d", span.fWindSum); |
- } |
- SkDebugf(" windValue=%d\n", span.fWindValue); |
-} |
-#endif |
- |
-#if DEBUG_SORT || DEBUG_SWAP_TOP |
-void SkOpSegment::debugShowSort(const char* fun, const SkTArray<SkOpAngle*, true>& angles, |
- int first, const int contourWinding, |
- const int oppContourWinding, bool sortable) const { |
- 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; |
- int oppLastSum = oppContourWinding; |
- const SkOpAngle* firstAngle = angles[first]; |
- int windSum = lastSum - spanSign(firstAngle); |
- int oppoSign = oppSign(firstAngle); |
- int oppWindSum = oppLastSum - oppoSign; |
- #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__, |
- contourWindingStr, oppContourWindingStr, spanSign(angles[first])); |
- int index = first; |
- bool firstTime = true; |
- do { |
- const SkOpAngle& angle = *angles[index]; |
- const SkOpSegment& segment = *angle.segment(); |
- int start = angle.start(); |
- int end = angle.end(); |
- const SkOpSpan& sSpan = segment.fTs[start]; |
- const SkOpSpan& eSpan = segment.fTs[end]; |
- const SkOpSpan& mSpan = segment.fTs[SkMin32(start, end)]; |
- bool opp = segment.fOperand ^ fOperand; |
- if (!firstTime) { |
- oppoSign = segment.oppSign(&angle); |
- if (opp) { |
- oppLastSum = oppWindSum; |
- oppWindSum -= segment.spanSign(&angle); |
- if (oppoSign) { |
- lastSum = windSum; |
- windSum -= oppoSign; |
- } |
- } else { |
- lastSum = windSum; |
- windSum -= segment.spanSign(&angle); |
- if (oppoSign) { |
- oppLastSum = oppWindSum; |
- oppWindSum -= oppoSign; |
- } |
- } |
- } |
- SkDebugf("%s [%d] %s", __FUNCTION__, index, |
- angle.unsortable() ? "*** UNSORTABLE *** " : ""); |
- #if DEBUG_SORT_COMPACT |
- SkDebugf("id=%d %s start=%d (%1.9g,%1.9g) end=%d (%1.9g,%1.9g)", |
- segment.fID, kLVerbStr[SkPathOpsVerbToPoints(segment.fVerb)], |
- start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end, |
- segment.xAtT(&eSpan), segment.yAtT(&eSpan)); |
- #else |
- switch (segment.fVerb) { |
- case SkPath::kLine_Verb: |
- SkDebugf(LINE_DEBUG_STR, LINE_DEBUG_DATA(segment.fPts)); |
- break; |
- case SkPath::kQuad_Verb: |
- SkDebugf(QUAD_DEBUG_STR, QUAD_DEBUG_DATA(segment.fPts)); |
- break; |
- case SkPath::kCubic_Verb: |
- SkDebugf(CUBIC_DEBUG_STR, CUBIC_DEBUG_DATA(segment.fPts)); |
- break; |
- default: |
- SkASSERT(0); |
- } |
- SkDebugf(" tStart=%1.9g tEnd=%1.9g", sSpan.fT, eSpan.fT); |
- #endif |
- SkDebugf(" sign=%d windValue=%d windSum=", angle.sign(), mSpan.fWindValue); |
- SkPathOpsDebug::WindingPrintf(mSpan.fWindSum); |
- int last, wind; |
- if (opp) { |
- last = oppLastSum; |
- wind = oppWindSum; |
- } else { |
- last = lastSum; |
- wind = windSum; |
- } |
- bool useInner = SkPathOpsDebug::ValidWind(last) && SkPathOpsDebug::ValidWind(wind) |
- && UseInnerWinding(last, wind); |
- WIND_AS_STRING(last); |
- WIND_AS_STRING(wind); |
- WIND_AS_STRING(lastSum); |
- WIND_AS_STRING(oppLastSum); |
- WIND_AS_STRING(windSum); |
- WIND_AS_STRING(oppWindSum); |
- #undef WIND_AS_STRING |
- if (!oppoSign) { |
- SkDebugf(" %s->%s (max=%s)", lastStr, windStr, useInner ? windStr : lastStr); |
- } else { |
- SkDebugf(" %s->%s (%s->%s)", lastStr, windStr, opp ? lastSumStr : oppLastSumStr, |
- opp ? windSumStr : oppWindSumStr); |
- } |
- 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; |
- } |
- if (firstTime) { |
- firstTime = false; |
- } |
- } while (index != first); |
-} |
- |
-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); |
- int oppWinding = segment->updateOppWinding(firstAngle); |
- debugShowSort(fun, angles, first, winding, oppWinding, sortable); |
-} |
- |
-#endif |
- |
-#if DEBUG_SHOW_WINDING |
-int SkOpSegment::debugShowWindingValues(int slotCount, int ofInterest) const { |
- if (!(1 << fID & ofInterest)) { |
- return 0; |
- } |
- int sum = 0; |
- SkTArray<char, true> slots(slotCount * 2); |
- memset(slots.begin(), ' ', slotCount * 2); |
- for (int i = 0; i < fTs.count(); ++i) { |
- // if (!(1 << fTs[i].fOther->fID & ofInterest)) { |
- // continue; |
- // } |
- sum += fTs[i].fWindValue; |
- slots[fTs[i].fOther->fID - 1] = as_digit(fTs[i].fWindValue); |
- sum += fTs[i].fOppValue; |
- slots[slotCount + fTs[i].fOther->fID - 1] = as_digit(fTs[i].fOppValue); |
- } |
- SkDebugf("%s id=%2d %.*s | %.*s\n", __FUNCTION__, fID, slotCount, slots.begin(), slotCount, |
- slots.begin() + slotCount); |
- return sum; |
-} |
-#endif |
- |
-void SkOpSegment::debugValidate() const { |
-#if DEBUG_VALIDATE |
- int count = fTs.count(); |
- SkASSERT(count >= 2); |
- SkASSERT(fTs[0].fT == 0); |
- SkASSERT(fTs[count - 1].fT == 1); |
- int done = 0; |
- double t = -1; |
- for (int i = 0; i < count; ++i) { |
- const SkOpSpan& span = fTs[i]; |
- SkASSERT(t <= span.fT); |
- t = span.fT; |
- int otherIndex = span.fOtherIndex; |
- const SkOpSegment* other = span.fOther; |
- const SkOpSpan& otherSpan = other->fTs[otherIndex]; |
- SkASSERT(otherSpan.fPt == span.fPt); |
- SkASSERT(otherSpan.fOtherT == t); |
- SkASSERT(&fTs[i] == &otherSpan.fOther->fTs[otherSpan.fOtherIndex]); |
- done += span.fDone; |
- } |
- SkASSERT(done == fDoneSpans); |
-#endif |
-} |
- |
-#ifdef SK_DEBUG |
-void SkOpSegment::dumpPts() const { |
- int last = SkPathOpsVerbToPoints(fVerb); |
- SkDebugf("{{"); |
- int index = 0; |
- do { |
- SkDPoint::dump(fPts[index]); |
- SkDebugf(", "); |
- } while (++index < last); |
- SkDPoint::dump(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 |