summaryrefslogtreecommitdiffstats
path: root/chromium/third_party/skia/src/pathops/SkOpSegment.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/skia/src/pathops/SkOpSegment.cpp')
-rw-r--r--chromium/third_party/skia/src/pathops/SkOpSegment.cpp3137
1 files changed, 2077 insertions, 1060 deletions
diff --git a/chromium/third_party/skia/src/pathops/SkOpSegment.cpp b/chromium/third_party/skia/src/pathops/SkOpSegment.cpp
index 00963403b7b..4e8b5d28d9d 100644
--- a/chromium/third_party/skia/src/pathops/SkOpSegment.cpp
+++ b/chromium/third_party/skia/src/pathops/SkOpSegment.cpp
@@ -5,6 +5,7 @@
* found in the LICENSE file.
*/
#include "SkIntersections.h"
+#include "SkOpContour.h"
#include "SkOpSegment.h"
#include "SkPathWriter.h"
#include "SkTSort.h"
@@ -20,8 +21,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 +38,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,64 +63,68 @@ 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.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;
+ }
+ if (!upSpan.fDone) {
+ if (upSpan.fWindSum != SK_MinS32) {
+ return spanToAngle(index, next);
+ }
+ *done = false;
}
- } else if (!upSpan.fDone) {
- upSpan.fDone = true;
- fDoneSpans++;
+ } 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.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 {
+SkPoint SkOpSegment::activeLeftTop(int* firstT) const {
SkASSERT(!done());
SkPoint topPt = {SK_ScalarMax, SK_ScalarMax};
int count = fTs.count();
// see if either end is not done since we want smaller Y of the pair
bool lastDone = true;
- bool lastUnsortable = false;
double lastT = -1;
for (int index = 0; index < count; ++index) {
const SkOpSpan& span = fTs[index];
- if (onlySortable && (span.fUnsortableStart || lastUnsortable)) {
- goto next;
- }
if (span.fDone && lastDone) {
goto next;
}
@@ -148,7 +153,6 @@ SkPoint SkOpSegment::activeLeftTop(bool onlySortable, int* firstT) const {
}
next:
lastDone = span.fDone;
- lastUnsortable = span.fUnsortableEnd;
}
return topPt;
}
@@ -159,34 +163,33 @@ 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
- SkDebugf("%s op=%s miFrom=%d miTo=%d suFrom=%d suTo=%d result=%d\n", __FUNCTION__,
+ SkDebugf("%s id=%d t=%1.9g tEnd=%1.9g op=%s miFrom=%d miTo=%d suFrom=%d suTo=%d result=%d\n",
+ __FUNCTION__, debugID(), span(index).fT, span(endIndex).fT,
SkPathOpsDebug::kPathOpStr[op], miFrom, miTo, suFrom, suTo, result);
#endif
return result;
@@ -194,24 +197,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 +296,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;
@@ -311,16 +334,19 @@ void SkOpSegment::addCoinOutsides(const SkPoint& startPt, const SkPoint& endPt,
do {
workPt = &fTs[++tIndex].fPt;
} while (nextPt == *workPt);
+ const SkPoint* oWorkPt;
do {
- workPt = &other->fTs[++oIndex].fPt;
- } while (nextPt == *workPt);
+ oWorkPt = &other->fTs[++oIndex].fPt;
+ } while (nextPt == *oWorkPt);
nextPt = *workPt;
double tStart = fTs[tIndex].fT;
double oStart = other->fTs[oIndex].fT;
if (tStart == 1 && oStart == 1 && fOperand == other->fOperand) {
break;
}
- addTPair(tStart, other, oStart, false, nextPt);
+ if (*workPt == *oWorkPt) {
+ addTPair(tStart, other, oStart, false, nextPt);
+ }
} while (endPt != nextPt);
}
@@ -378,6 +404,29 @@ 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 startIndex = endIndex - 1;
+ while (fTs[startIndex].fT == 1 || fTs[startIndex].fTiny) {
+ ++startIndex;
+ SkASSERT(startIndex < spanCount - 1);
+ ++endIndex;
+ }
+ SkOpAngle& angle = fAngles.push_back();
+ angle.set(this, spanCount - 1, startIndex);
+#if DEBUG_ANGLE
+ debugCheckPointsEqualish(endIndex, spanCount);
+#endif
+ setFromAngle(endIndex, &angle);
+}
+
+void SkOpSegment::setFromAngle(int endIndex, SkOpAngle* angle) {
+ int spanCount = fTs.count();
+ do {
+ fTs[endIndex].fFromAngle = angle;
+ } 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 +449,95 @@ void SkOpSegment::addQuad(const SkPoint pts[3], bool operand, bool evenOdd) {
fBounds.setQuadBounds(pts);
}
+SkOpAngle* SkOpSegment::addSingletonAngleDown(SkOpSegment** otherPtr, SkOpAngle** anglePtr) {
+ int spanIndex = count() - 1;
+ int startIndex = nextExactSpan(spanIndex, -1);
+ SkASSERT(startIndex >= 0);
+ SkOpAngle& angle = fAngles.push_back();
+ *anglePtr = &angle;
+ angle.set(this, spanIndex, startIndex);
+ setFromAngle(spanIndex, &angle);
+ SkOpSegment* other;
+ int oStartIndex, oEndIndex;
+ do {
+ const SkOpSpan& span = fTs[spanIndex];
+ SkASSERT(span.fT > 0);
+ other = span.fOther;
+ oStartIndex = span.fOtherIndex;
+ oEndIndex = other->nextExactSpan(oStartIndex, 1);
+ if (oEndIndex > 0 && other->span(oStartIndex).fWindValue) {
+ break;
+ }
+ oEndIndex = oStartIndex;
+ oStartIndex = other->nextExactSpan(oEndIndex, -1);
+ --spanIndex;
+ } while (oStartIndex < 0 || !other->span(oStartIndex).fWindSum);
+ SkOpAngle& oAngle = other->fAngles.push_back();
+ oAngle.set(other, oStartIndex, oEndIndex);
+ other->setToAngle(oEndIndex, &oAngle);
+ *otherPtr = other;
+ return &oAngle;
+}
+
+SkOpAngle* SkOpSegment::addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** anglePtr) {
+ int endIndex = nextExactSpan(0, 1);
+ SkASSERT(endIndex > 0);
+ SkOpAngle& angle = fAngles.push_back();
+ *anglePtr = &angle;
+ angle.set(this, 0, endIndex);
+ setToAngle(endIndex, &angle);
+ int spanIndex = 0;
+ SkOpSegment* other;
+ int oStartIndex, oEndIndex;
+ do {
+ const SkOpSpan& span = fTs[spanIndex];
+ SkASSERT(span.fT < 1);
+ other = span.fOther;
+ oEndIndex = span.fOtherIndex;
+ oStartIndex = other->nextExactSpan(oEndIndex, -1);
+ if (oStartIndex >= 0 && other->span(oStartIndex).fWindValue) {
+ break;
+ }
+ oStartIndex = oEndIndex;
+ oEndIndex = other->nextExactSpan(oStartIndex, 1);
+ ++spanIndex;
+ } while (oEndIndex < 0 || !other->span(oStartIndex).fWindValue);
+ SkOpAngle& oAngle = other->fAngles.push_back();
+ oAngle.set(other, oEndIndex, oStartIndex);
+ other->setFromAngle(oEndIndex, &oAngle);
+ *otherPtr = other;
+ return &oAngle;
+}
+
+SkOpAngle* SkOpSegment::addSingletonAngles(int step) {
+ SkOpSegment* other;
+ SkOpAngle* angle, * otherAngle;
+ if (step > 0) {
+ otherAngle = addSingletonAngleUp(&other, &angle);
+ } else {
+ otherAngle = addSingletonAngleDown(&other, &angle);
+ }
+ angle->insert(otherAngle);
+ return angle;
+}
+
+void SkOpSegment::addStartSpan(int endIndex) {
+ int index = 0;
+ SkOpAngle& angle = fAngles.push_back();
+ angle.set(this, index, endIndex);
+#if DEBUG_ANGLE
+ debugCheckPointsEqualish(index, endIndex);
+#endif
+ setToAngle(endIndex, &angle);
+}
+
+void SkOpSegment::setToAngle(int endIndex, SkOpAngle* angle) {
+ int index = 0;
+ do {
+ fTs[index].fToAngle = angle;
+ } while (++index < endIndex);
+}
+
// Defer all coincident edge processing until
// after normal intersections have been computed
@@ -408,18 +546,19 @@ 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) {
- if (precisely_zero(newT)) {
- newT = 0;
- } else if (precisely_equal(newT, 1)) {
- newT = 1;
- }
+ 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
+ const SkPoint& firstPt = fPts[0];
+ const SkPoint& lastPt = fPts[SkPathOpsVerbToPoints(fVerb)];
+ SkASSERT(newT == 0 || !precisely_zero(newT));
+ SkASSERT(newT == 1 || !precisely_equal(newT, 1));
// FIXME: in the pathological case where there is a ton of intercepts,
// binary search?
int insertedAt = -1;
- size_t tCount = fTs.count();
- const SkPoint& firstPt = fPts[0];
- const SkPoint& lastPt = fPts[SkPathOpsVerbToPoints(fVerb)];
- for (size_t index = 0; index < tCount; ++index) {
+ int tCount = fTs.count();
+ 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.
@@ -450,6 +589,7 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) {
span = fTs.append();
}
span->fT = newT;
+ span->fOtherT = -1;
span->fOther = other;
span->fPt = pt;
#if 0
@@ -457,93 +597,71 @@ 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->fFromAngle = NULL;
+ span->fToAngle = NULL;
span->fWindSum = SK_MinS32;
span->fOppSum = SK_MinS32;
span->fWindValue = 1;
span->fOppValue = 0;
+ span->fChased = false;
+ span->fCoincident = false;
+ span->fLoop = false;
+ span->fNear = false;
+ span->fMultiple = false;
span->fSmall = false;
span->fTiny = false;
- span->fLoop = false;
if ((span->fDone = newT == 1)) {
++fDoneSpans;
}
- 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;
- }
+// FIXME: note that this relies on spans being a continguous array
+// 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,31 +690,36 @@ 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;
}
+ bool oFoundEnd = false;
int oIndex = other->fTs.count();
while (startPt != other->fTs[--oIndex].fPt) { // look for startPt match
SkASSERT(oIndex > 0);
}
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];
SkOpSpan* oTest = &other->fTs[oIndex];
SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
+ bool decrement, track, bigger;
+ int originalWindValue;
+ const SkPoint* testPt;
+ const SkPoint* oTestPt;
do {
SkASSERT(test->fT < 1);
SkASSERT(oTest->fT < 1);
- bool decrement = test->fWindValue && oTest->fWindValue;
- bool track = test->fWindValue || oTest->fWindValue;
- bool bigger = test->fWindValue >= oTest->fWindValue;
- const SkPoint& testPt = test->fPt;
+ decrement = test->fWindValue && oTest->fWindValue;
+ track = test->fWindValue || oTest->fWindValue;
+ bigger = test->fWindValue >= oTest->fWindValue;
+ testPt = &test->fPt;
double testT = test->fT;
- const SkPoint& oTestPt = oTest->fPt;
+ oTestPt = &oTest->fPt;
double oTestT = oTest->fT;
do {
if (decrement) {
@@ -606,12 +729,12 @@ void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpS
decrementSpan(test);
}
} else if (track) {
- TrackOutsidePair(&outsidePts, testPt, oTestPt);
+ TrackOutsidePair(&outsidePts, *testPt, *oTestPt);
}
SkASSERT(index < fTs.count() - 1);
test = &fTs[++index];
- } while (testPt == test->fPt || testT == test->fT);
- SkDEBUGCODE(int originalWindValue = oTest->fWindValue);
+ } while (*testPt == test->fPt || precisely_equal(testT, test->fT));
+ originalWindValue = oTest->fWindValue;
do {
SkASSERT(oTest->fT < 1);
SkASSERT(originalWindValue == oTest->fWindValue);
@@ -622,15 +745,48 @@ void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpS
other->decrementSpan(oTest);
}
} else if (track) {
- TrackOutsidePair(&oOutsidePts, oTestPt, testPt);
+ TrackOutsidePair(&oOutsidePts, *oTestPt, *testPt);
}
if (!oIndex) {
break;
}
+ oFoundEnd |= endPt == oTest->fPt;
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
+ if (!oFoundEnd) {
+ for (int oIdx2 = oIndex; oIdx2 >= 0; --oIdx2) {
+ SkOpSpan* oTst2 = &other->fTs[oIdx2];
+ if (originalWindValue != oTst2->fWindValue) {
+ goto skipAdvanceOtherCancel;
+ }
+ if (!oTst2->fWindValue) {
+ goto skipAdvanceOtherCancel;
+ }
+ if (endPt == other->fTs[oIdx2].fPt) {
+ break;
+ }
+ }
+ do {
+ SkASSERT(originalWindValue == oTest->fWindValue);
+ if (decrement) {
+ if (binary && !bigger) {
+ oTest->fOppValue--;
+ } else {
+ other->decrementSpan(oTest);
+ }
+ } else if (track) {
+ TrackOutsidePair(&oOutsidePts, *oTestPt, *testPt);
+ }
+ if (!oIndex) {
+ break;
+ }
+ oTest = &other->fTs[--oIndex];
+ oFoundEnd |= endPt == oTest->fPt;
+ } while (!oFoundEnd || endPt == oTest->fPt);
+ }
+skipAdvanceOtherCancel:
int outCount = outsidePts.count();
if (!done() && outCount) {
addCancelOutsides(outsidePts[0], outsidePts[1], other);
@@ -641,16 +797,387 @@ void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpS
if (!other->done() && oOutsidePts.count()) {
other->addCancelOutsides(oOutsidePts[0], oOutsidePts[1], this);
}
+ setCoincidentRange(startPt, endPt, other);
+ other->setCoincidentRange(startPt, endPt, this);
}
-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;
}
+// find the starting or ending span with an existing loop of angles
+// FIXME? replicate for all identical starting/ending spans?
+// OPTIMIZE? remove the spans pointing to windValue==0 here or earlier?
+// FIXME? assert that only one other span has a valid windValue or oppValue
+void SkOpSegment::addSimpleAngle(int index) {
+ SkOpSpan* span = &fTs[index];
+ if (index == 0) {
+ do {
+ if (span->fToAngle) {
+ SkASSERT(span->fToAngle->loopCount() == 2);
+ SkASSERT(!span->fFromAngle);
+ span->fFromAngle = span->fToAngle->next();
+ return;
+ }
+ span = &fTs[++index];
+ } while (span->fT == 0);
+ SkASSERT(index == 1);
+ index = 0;
+ SkASSERT(!fTs[0].fTiny && fTs[1].fT > 0);
+ addStartSpan(1);
+ } else {
+ do {
+ if (span->fFromAngle) {
+ SkASSERT(span->fFromAngle->loopCount() == 2);
+ SkASSERT(!span->fToAngle);
+ span->fToAngle = span->fFromAngle->next();
+ return;
+ }
+ span = &fTs[--index];
+ } while (span->fT == 1);
+ SkASSERT(index == count() - 2);
+ index = count() - 1;
+ SkASSERT(!fTs[index - 1].fTiny && fTs[index - 1].fT < 1);
+ addEndSpan(index);
+ }
+ 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 = span->fToAngle;
+ oAngle = oSpan.fFromAngle;
+ } 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].fFromAngle == NULL
+ && other->fTs[span->fOtherIndex + 1].fToAngle == NULL)));
+ int oIndex = 1;
+ do {
+ const SkOpSpan& osSpan = other->span(oIndex);
+ if (osSpan.fFromAngle || osSpan.fT > 0) {
+ break;
+ }
+ ++oIndex;
+ SkASSERT(oIndex < other->count());
+ } while (true);
+ other->addStartSpan(oIndex);
+ angle = span->fFromAngle;
+ oAngle = oSpan.fToAngle;
+ }
+ angle->insert(oAngle);
+}
+
+void SkOpSegment::alignMultiples(SkTDArray<AlignedSpan>* alignedArray) {
+ debugValidate();
+ int count = this->count();
+ for (int index = 0; index < count; ++index) {
+ SkOpSpan& span = fTs[index];
+ if (!span.fMultiple) {
+ continue;
+ }
+ int end = nextExactSpan(index, 1);
+ SkASSERT(end > index + 1);
+ const SkPoint& thisPt = span.fPt;
+ while (index < end - 1) {
+ SkOpSegment* other1 = span.fOther;
+ int oCnt = other1->count();
+ for (int idx2 = index + 1; idx2 < end; ++idx2) {
+ SkOpSpan& span2 = fTs[idx2];
+ SkOpSegment* other2 = span2.fOther;
+ for (int oIdx = 0; oIdx < oCnt; ++oIdx) {
+ SkOpSpan& oSpan = other1->fTs[oIdx];
+ if (oSpan.fOther != other2) {
+ continue;
+ }
+ if (oSpan.fPt == thisPt) {
+ goto skipExactMatches;
+ }
+ }
+ for (int oIdx = 0; oIdx < oCnt; ++oIdx) {
+ SkOpSpan& oSpan = other1->fTs[oIdx];
+ if (oSpan.fOther != other2) {
+ continue;
+ }
+ if (SkDPoint::RoughlyEqual(oSpan.fPt, thisPt)) {
+ SkOpSpan& oSpan2 = other2->fTs[oSpan.fOtherIndex];
+ if (zero_or_one(span.fOtherT) || zero_or_one(oSpan.fT)
+ || zero_or_one(span2.fOtherT) || zero_or_one(oSpan2.fT)) {
+ return;
+ }
+ if (!way_roughly_equal(span.fOtherT, oSpan.fT)
+ || !way_roughly_equal(span2.fOtherT, oSpan2.fT)
+ || !way_roughly_equal(span2.fOtherT, oSpan.fOtherT)
+ || !way_roughly_equal(span.fOtherT, oSpan2.fOtherT)) {
+ return;
+ }
+ alignSpan(thisPt, span.fOtherT, other1, span2.fOtherT,
+ other2, &oSpan, alignedArray);
+ alignSpan(thisPt, span2.fOtherT, other2, span.fOtherT,
+ other1, &oSpan2, alignedArray);
+ break;
+ }
+ }
+ skipExactMatches:
+ ;
+ }
+ ++index;
+ }
+ }
+ debugValidate();
+}
+
+void SkOpSegment::alignSpan(const SkPoint& newPt, double newT, const SkOpSegment* other,
+ double otherT, const SkOpSegment* other2, SkOpSpan* oSpan,
+ SkTDArray<AlignedSpan>* alignedArray) {
+ AlignedSpan* aligned = alignedArray->append();
+ aligned->fOldPt = oSpan->fPt;
+ aligned->fPt = newPt;
+ aligned->fOldT = oSpan->fT;
+ aligned->fT = newT;
+ aligned->fSegment = this; // OPTIMIZE: may be unused, can remove
+ aligned->fOther1 = other;
+ aligned->fOther2 = other2;
+ SkASSERT(SkDPoint::RoughlyEqual(oSpan->fPt, newPt));
+ oSpan->fPt = newPt;
+// SkASSERT(way_roughly_equal(oSpan->fT, newT));
+ oSpan->fT = newT;
+// SkASSERT(way_roughly_equal(oSpan->fOtherT, otherT));
+ oSpan->fOtherT = otherT;
+}
+
+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) {
+ return aligned;
+ }
+ int oStart = other->nextSpan(oIndex, -1) + 1;
+ oSpan = &other->fTs[oStart];
+ int otherIndex = oStart;
+ if (oT == 1) {
+ if (aligned) {
+ while (oSpan->fPt == thisPt && oSpan->fT != 1) {
+ oSpan->fTiny = true;
+ ++oSpan;
+ }
+ }
+ return aligned;
+ }
+ oT = oSpan->fT;
+ int oEnd = other->nextSpan(oIndex, 1);
+ bool oAligned = false;
+ if (oSpan->fPt != thisPt) {
+ oAligned |= other->alignSpan(oStart, oT, thisPt);
+ }
+ 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::blindCancel(const SkCoincidence& coincidence, SkOpSegment* other) {
+ bool binary = fOperand != other->fOperand;
+ int index = 0;
+ int last = this->count();
+ do {
+ SkOpSpan& span = this->fTs[--last];
+ if (span.fT != 1 && !span.fSmall) {
+ break;
+ }
+ span.fCoincident = true;
+ } while (true);
+ int oIndex = other->count();
+ do {
+ SkOpSpan& oSpan = other->fTs[--oIndex];
+ if (oSpan.fT != 1 && !oSpan.fSmall) {
+ break;
+ }
+ oSpan.fCoincident = true;
+ } while (true);
+ do {
+ SkOpSpan* test = &this->fTs[index];
+ int baseWind = test->fWindValue;
+ int baseOpp = test->fOppValue;
+ int endIndex = index;
+ while (++endIndex <= last) {
+ SkOpSpan* endSpan = &this->fTs[endIndex];
+ SkASSERT(endSpan->fT < 1);
+ if (endSpan->fWindValue != baseWind || endSpan->fOppValue != baseOpp) {
+ break;
+ }
+ endSpan->fCoincident = true;
+ }
+ SkOpSpan* oTest = &other->fTs[oIndex];
+ int oBaseWind = oTest->fWindValue;
+ int oBaseOpp = oTest->fOppValue;
+ int oStartIndex = oIndex;
+ while (--oStartIndex >= 0) {
+ SkOpSpan* oStartSpan = &other->fTs[oStartIndex];
+ if (oStartSpan->fWindValue != oBaseWind || oStartSpan->fOppValue != oBaseOpp) {
+ break;
+ }
+ oStartSpan->fCoincident = true;
+ }
+ bool decrement = baseWind && oBaseWind;
+ bool bigger = baseWind >= oBaseWind;
+ do {
+ SkASSERT(test->fT < 1);
+ if (decrement) {
+ if (binary && bigger) {
+ test->fOppValue--;
+ } else {
+ decrementSpan(test);
+ }
+ }
+ test->fCoincident = true;
+ test = &fTs[++index];
+ } while (index < endIndex);
+ do {
+ SkASSERT(oTest->fT < 1);
+ if (decrement) {
+ if (binary && !bigger) {
+ oTest->fOppValue--;
+ } else {
+ other->decrementSpan(oTest);
+ }
+ }
+ oTest->fCoincident = true;
+ oTest = &other->fTs[--oIndex];
+ } while (oIndex > oStartIndex);
+ } while (index <= last && oIndex >= 0);
+ SkASSERT(index > last);
+ SkASSERT(oIndex < 0);
+}
+
+void SkOpSegment::blindCoincident(const SkCoincidence& coincidence, SkOpSegment* other) {
+ bool binary = fOperand != other->fOperand;
+ int index = 0;
+ int last = this->count();
+ do {
+ SkOpSpan& span = this->fTs[--last];
+ if (span.fT != 1 && !span.fSmall) {
+ break;
+ }
+ span.fCoincident = true;
+ } while (true);
+ int oIndex = 0;
+ int oLast = other->count();
+ do {
+ SkOpSpan& oSpan = other->fTs[--oLast];
+ if (oSpan.fT != 1 && !oSpan.fSmall) {
+ break;
+ }
+ oSpan.fCoincident = true;
+ } while (true);
+ do {
+ SkOpSpan* test = &this->fTs[index];
+ int baseWind = test->fWindValue;
+ int baseOpp = test->fOppValue;
+ int endIndex = index;
+ SkOpSpan* endSpan;
+ while (++endIndex <= last) {
+ endSpan = &this->fTs[endIndex];
+ SkASSERT(endSpan->fT < 1);
+ if (endSpan->fWindValue != baseWind || endSpan->fOppValue != baseOpp) {
+ break;
+ }
+ endSpan->fCoincident = true;
+ }
+ SkOpSpan* oTest = &other->fTs[oIndex];
+ int oBaseWind = oTest->fWindValue;
+ int oBaseOpp = oTest->fOppValue;
+ int oEndIndex = oIndex;
+ SkOpSpan* oEndSpan;
+ while (++oEndIndex <= oLast) {
+ oEndSpan = &this->fTs[oEndIndex];
+ SkASSERT(oEndSpan->fT < 1);
+ if (oEndSpan->fWindValue != oBaseWind || oEndSpan->fOppValue != oBaseOpp) {
+ break;
+ }
+ oEndSpan->fCoincident = true;
+ }
+ // consolidate the winding count even if done
+ if ((test->fWindValue || test->fOppValue) && (oTest->fWindValue || oTest->fOppValue)) {
+ if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
+ bumpCoincidentBlind(binary, index, endIndex);
+ other->bumpCoincidentOBlind(oIndex, oEndIndex);
+ } else {
+ other->bumpCoincidentBlind(binary, oIndex, oEndIndex);
+ bumpCoincidentOBlind(index, endIndex);
+ }
+ }
+ index = endIndex;
+ oIndex = oEndIndex;
+ } while (index <= last && oIndex <= oLast);
+ SkASSERT(index > last);
+ SkASSERT(oIndex > oLast);
+}
+
+void SkOpSegment::bumpCoincidentBlind(bool binary, int index, int endIndex) {
+ const SkOpSpan& oTest = fTs[index];
+ int oWindValue = oTest.fWindValue;
+ int oOppValue = oTest.fOppValue;
+ if (binary) {
+ SkTSwap<int>(oWindValue, oOppValue);
+ }
+ do {
+ (void) bumpSpan(&fTs[index], oWindValue, oOppValue);
+ } while (++index < endIndex);
+}
+
void SkOpSegment::bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* indexPtr,
SkTArray<SkPoint, true>* outsideTs) {
int index = *indexPtr;
@@ -667,10 +1194,16 @@ 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;
}
+void SkOpSegment::bumpCoincidentOBlind(int index, int endIndex) {
+ do {
+ zeroSpan(&fTs[index]);
+ } while (++index < endIndex);
+}
+
// because of the order in which coincidences are resolved, this and other
// may not have the same intermediate points. Compute the corresponding
// intermediate T values (using this as the master, other as the follower)
@@ -680,13 +1213,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 +1247,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 +1256,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 +1270,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 +1302,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 +1326,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;
+ int oCount = other->count();
+ do {
+ oPeek = &other->fTs[oPeekIndex];
+ if (++oPeekIndex == oCount) {
+ success = false;
+ break;
+ }
+ } 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);
@@ -799,12 +1370,14 @@ void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, d
if (!other->done() && oOutsidePts.count()) {
other->addCoinOutsides(oOutsidePts[0], endPt, this);
}
+ setCoincidentRange(startPt, endPt, other);
+ other->setCoincidentRange(startPt, endPt, this);
}
// 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 SkPoint& pt) {
+const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
+ const SkPoint& pt, const SkPoint& pt2) {
int tCount = fTs.count();
for (int tIndex = 0; tIndex < tCount; ++tIndex) {
const SkOpSpan& span = fTs[tIndex];
@@ -817,7 +1390,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
@@ -825,26 +1398,23 @@ void SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool bor
__FUNCTION__, fID, t, other->fID, otherT);
#endif
int insertedAt = addT(other, pt, t);
- int otherInsertedAt = other->addT(this, pt, otherT);
+ int otherInsertedAt = other->addT(this, pt2, otherT);
addOtherT(insertedAt, otherT, otherInsertedAt);
other->addOtherT(otherInsertedAt, t, insertedAt);
matchWindingValue(insertedAt, t, borrowWind);
other->matchWindingValue(otherInsertedAt, otherT, borrowWind);
+ SkOpSpan& span = this->fTs[insertedAt];
+ if (pt != pt2) {
+ span.fNear = true;
+ SkOpSpan& oSpan = other->fTs[otherInsertedAt];
+ oSpan.fNear = true;
+ }
+ return &span;
}
-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 SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
+ const SkPoint& pt) {
+ return addTPair(t, other, otherT, borrowWind, pt, pt);
}
bool SkOpSegment::betweenPoints(double midT, const SkPoint& pt1, const SkPoint& pt2) const {
@@ -862,164 +1432,170 @@ 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);
+// 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
+ SkASSERT(index > 0);
+ if (activePrior >= 0) {
+ addStartSpan(index);
+ }
+ }
+ bool addEnd;
+ int endIndex = spanCount - 1;
+ span = &fTs[endIndex - 1];
+ if ((addEnd = span->fT == 1 || span->fTiny)) { // if curve end intersects
+ endIndex = findEndSpan(endIndex);
+ SkASSERT(endIndex > 0);
+ } 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);
+ SkOpAngle* angle = NULL;
+ SkOpAngle* priorAngle;
+ if (activePrior >= 0) {
+ int pActive = firstActive(prior);
+ SkASSERT(pActive < start);
+ priorAngle = &fAngles.push_back();
+ priorAngle->set(this, start, pActive);
+ }
+ int active = checkSetAngle(start);
+ if (active >= 0) {
+ SkASSERT(active < index);
+ angle = &fAngles.push_back();
+ angle->set(this, active, index);
+ }
+ #if DEBUG_ANGLE
+ debugCheckPointsEqualish(start, index);
+ #endif
+ prior = start;
+ do {
+ const SkOpSpan* startSpan = &fTs[start - 1];
+ if (!startSpan->fSmall || isCanceled(start - 1) || startSpan->fFromAngle
+ || startSpan->fToAngle) {
+ break;
+ }
+ --start;
+ } while (start > 0);
+ do {
+ if (activePrior >= 0) {
+ SkASSERT(fTs[start].fFromAngle == NULL);
+ fTs[start].fFromAngle = priorAngle;
+ }
+ if (active >= 0) {
+ SkASSERT(fTs[start].fToAngle == NULL);
+ fTs[start].fToAngle = angle;
+ }
+ } while (++start < index);
+ activePrior = active;
+ }
+ if (addEnd && activePrior >= 0) {
+ addEndSpan(endIndex);
}
- do {
- buildAnglesInner(index, angles);
- if (++index == fTs.count()) {
- break;
- }
- if (!allowOpp && fTs[index].fOther->fOperand != fOperand) {
- break;
- }
- if (fTs[index - 1].fTiny) {
- referenceT = fTs[index].fT;
- continue;
- }
- if (!precisely_negative(fTs[index].fT - referenceT) && fTs[index].fPt == referencePt) {
- // FIXME
- // testQuad8 generates the wrong output unless false is returned here. Other tests will
- // take this path although they aren't required to. This means that many go much slower
- // because of this sort fail.
- // SkDebugf("!!!\n");
- return false;
- }
- } while (precisely_negative(fTs[index].fT - referenceT));
return true;
}
-void SkOpSegment::buildAnglesInner(int index, SkTArray<SkOpAngle, true>* angles) const {
- 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);
+int SkOpSegment::checkSetAngle(int tIndex) const {
+ const SkOpSpan* span = &fTs[tIndex];
+ while (span->fTiny /* || span->fSmall */) {
+ span = &fTs[++tIndex];
}
- // add candidate into and away from junction
- other->addTwoAngles(next, oIndex, angles);
+ return isCanceled(tIndex) ? -1 : tIndex;
}
-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)) {
+// at this point, the span is already ordered, or unorderable
+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;
}
- int angleCount = angles->count();
- // abort early before sorting if an unsortable (not an unorderable) angle is found
- if (includeType != SkOpAngle::kUnaryXor) {
- int firstIndex = -1;
- while (++firstIndex < angleCount) {
- const SkOpAngle& angle = (*angles)[firstIndex];
- if (angle.segment()->windSum(&angle) != SK_MinS32) {
- break;
- }
- }
- if (firstIndex == angleCount) {
- return SK_MinS32;
- }
- }
- bool sortable = SortAngles2(*angles, sorted);
-#if DEBUG_SORT_RAW
- if (sorted->count() > 0) {
- (*sorted)[0]->segment()->debugShowSort(__FUNCTION__, *sorted, 0, 0, 0, sortable);
- }
-#endif
- if (!sortable) {
- return SK_NaN32;
- }
- if (includeType == SkOpAngle::kUnaryXor) {
- return SK_MinS32;
- }
// if all angles have a computed winding,
// or if no adjacent angles are orderable,
// or if adjacent orderable angles have no computed winding,
// there's nothing to do
- // if two orderable angles are adjacent, and one has winding computed, transfer to the other
- const SkOpAngle* baseAngle = NULL;
- int last = angleCount;
- int finalInitialOrderable = -1;
+ // if two orderable angles are adjacent, and both are next to orderable angles,
+ // and one has winding computed, transfer to the other
+ SkOpAngle* baseAngle = NULL;
bool tryReverse = false;
// look for counterclockwise transfers
+ SkOpAngle* angle = firstAngle->previous();
+ SkOpAngle* next = angle->next();
+ firstAngle = next;
do {
- int index = 0;
+ SkOpAngle* prior = angle;
+ angle = next;
+ next = angle->next();
+ SkASSERT(prior->next() == angle);
+ SkASSERT(angle->next() == next);
+ if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
+ baseAngle = NULL;
+ continue;
+ }
+ int testWinding = angle->segment()->windSum(angle);
+ if (SK_MinS32 != testWinding) {
+ baseAngle = angle;
+ tryReverse = true;
+ continue;
+ }
+ if (baseAngle) {
+ ComputeOneSum(baseAngle, angle, includeType);
+ baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
+ }
+ } while (next != firstAngle);
+ if (baseAngle && SK_MinS32 == firstAngle->segment()->windSum(firstAngle)) {
+ firstAngle = baseAngle;
+ tryReverse = true;
+ }
+ if (tryReverse) {
+ baseAngle = NULL;
+ SkOpAngle* prior = firstAngle;
do {
- SkOpAngle* testAngle = (*sorted)[index];
- int testWinding = testAngle->segment()->windSum(testAngle);
- if (SK_MinS32 != testWinding && !testAngle->unorderable()) {
- baseAngle = testAngle;
- continue;
- }
- if (testAngle->unorderable()) {
+ angle = prior;
+ prior = angle->previous();
+ SkASSERT(prior->next() == angle);
+ next = angle->next();
+ if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
baseAngle = NULL;
- tryReverse = true;
continue;
}
- if (baseAngle) {
- ComputeOneSum(baseAngle, testAngle, includeType);
- baseAngle = SK_MinS32 != testAngle->segment()->windSum(testAngle) ? testAngle
- : NULL;
- tryReverse |= !baseAngle;
+ int testWinding = angle->segment()->windSum(angle);
+ if (SK_MinS32 != testWinding) {
+ baseAngle = angle;
continue;
}
- if (finalInitialOrderable + 1 == index) {
- finalInitialOrderable = index;
- }
- } 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;
+ if (baseAngle) {
+ ComputeOneSumReverse(baseAngle, angle, includeType);
+ baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
}
- last = finalInitialOrderable;
- finalInitialOrderable = angleCount + 1; // make this inactive 2nd time through
- } while (baseAngle);
+ } while (prior != firstAngle);
}
int minIndex = SkMin32(startIndex, endIndex);
return windSum(minIndex);
@@ -1083,6 +1659,44 @@ void SkOpSegment::ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* ne
nextAngle->setLastMarked(last);
}
+bool SkOpSegment::containsPt(const SkPoint& pt, int index, int endIndex) const {
+ int step = index < endIndex ? 1 : -1;
+ do {
+ const SkOpSpan& span = this->span(index);
+ if (span.fPt == pt) {
+ const SkOpSpan& endSpan = this->span(endIndex);
+ return span.fT == endSpan.fT && pt != endSpan.fPt;
+ }
+ index += step;
+ } while (index != endIndex);
+ return false;
+}
+
+bool SkOpSegment::containsT(double t, const SkOpSegment* other, double otherT) const {
+ int count = this->count();
+ for (int index = 0; index < count; ++index) {
+ const SkOpSpan& span = fTs[index];
+ if (t < span.fT) {
+ return false;
+ }
+ if (t == span.fT) {
+ if (other != span.fOther) {
+ continue;
+ }
+ if (other->fVerb != SkPath::kCubic_Verb) {
+ return true;
+ }
+ if (!other->fLoop) {
+ return true;
+ }
+ double otherMidT = (otherT + span.fOtherT) / 2;
+ SkPoint otherPt = other->ptAtT(otherMidT);
+ return SkDPoint::ApproximatelyEqual(span.fPt, otherPt);
+ }
+ }
+ return false;
+}
+
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 +1820,223 @@ 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;
+}
+
+double SkOpSegment::calcMissingTEnd(const SkOpSegment* ref, double loEnd, double min, double max,
+ double hiEnd, const SkOpSegment* other, int thisStart) {
+ if (max >= hiEnd) {
+ return -1;
+ }
+ int end = findOtherT(hiEnd, ref);
+ if (end < 0) {
+ return -1;
+ }
+ double tHi = span(end).fT;
+ double tLo, refLo;
+ if (thisStart >= 0) {
+ tLo = span(thisStart).fT;
+ refLo = min;
+ } else {
+ int start1 = findOtherT(loEnd, ref);
+ SkASSERT(start1 >= 0);
+ tLo = span(start1).fT;
+ refLo = loEnd;
+ }
+ double missingT = (max - refLo) / (hiEnd - refLo);
+ missingT = tLo + missingT * (tHi - tLo);
+ return missingT;
+}
+
+double SkOpSegment::calcMissingTStart(const SkOpSegment* ref, double loEnd, double min, double max,
+ double hiEnd, const SkOpSegment* other, int thisEnd) {
+ if (min <= loEnd) {
+ return -1;
+ }
+ int start = findOtherT(loEnd, ref);
+ if (start < 0) {
+ return -1;
+ }
+ double tLo = span(start).fT;
+ double tHi, refHi;
+ if (thisEnd >= 0) {
+ tHi = span(thisEnd).fT;
+ refHi = max;
+ } else {
+ int end1 = findOtherT(hiEnd, ref);
+ if (end1 < 0) {
+ return -1;
+ }
+ tHi = span(end1).fT;
+ refHi = hiEnd;
+ }
+ double missingT = (min - loEnd) / (refHi - loEnd);
+ missingT = tLo + missingT * (tHi - tLo);
+ return missingT;
+}
+
+// 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];
+ if (thisSpan->fNear) {
+ continue;
+ }
+ 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;
+ }
+#if 0 // FIXME: this eliminates spurious data from skpwww_argus_presse_fr_41 but breaks
+ // skpwww_fashionscandal_com_94 -- calcAngles complains, but I don't understand why
+ if (missing.fSegment->containsT(missing.fT, missing.fOther, missing.fOtherT)) {
+#if DEBUG_DUPLICATES
+ SkDebugf("skip 1 id=%d t=%1.9g other=%d otherT=%1.9g\n", missing.fSegment->fID,
+ missing.fT, missing.fOther->fID, missing.fOtherT);
+#endif
+ continue;
+ }
+ if (missing.fOther->containsT(missing.fOtherT, missing.fSegment, missing.fT)) {
+#if DEBUG_DUPLICATES
+ SkDebugf("skip 2 id=%d t=%1.9g other=%d otherT=%1.9g\n", missing.fOther->fID,
+ missing.fOtherT, missing.fSegment->fID, missing.fT);
+#endif
+ continue;
+ }
+#endif
+ // skip if adding would insert point into an existing coincindent span
+ if (missing.fSegment->inCoincidentSpan(missing.fT, missingOther)
+ && missingOther->inCoincidentSpan(missing.fOtherT, this)) {
+ continue;
+ }
+ // skip if the created coincident spans are small
+ if (missing.fSegment->coincidentSmall(missing.fPt, missing.fT, missingOther)
+ && missingOther->coincidentSmall(missing.fPt, missing.fOtherT, missing.fSegment)) {
+ 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 +2144,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 +2157,87 @@ 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
+ SkOpSpan& span = fTs[index];
+ double thisT = span.fT;
+ const SkPoint& thisPt = span.fPt;
+ span.fMultiple = true;
+ bool aligned = false;
+ while (++index < end) {
+ aligned |= alignSpan(index, thisT, thisPt);
+ }
+ if (aligned) {
+ alignSpanState(start, end);
+ }
+ fMultiples = true;
+ }
+ 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 +2248,247 @@ 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.fPt, 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);
+ if (0 && !span.fWindValue) {
+ return;
+ }
+ 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 +2553,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();
@@ -1410,6 +2566,30 @@ void SkOpSegment::checkTiny() {
}
}
+bool SkOpSegment::coincidentSmall(const SkPoint& pt, double t, const SkOpSegment* other) const {
+ int count = this->count();
+ for (int index = 0; index < count; ++index) {
+ const SkOpSpan& span = this->span(index);
+ if (span.fOther != other) {
+ continue;
+ }
+ if (span.fPt == pt) {
+ continue;
+ }
+ if (!AlmostEqualUlps(span.fPt, pt)) {
+ continue;
+ }
+ if (fVerb != SkPath::kCubic_Verb) {
+ return true;
+ }
+ double tInterval = t - span.fT;
+ double tMid = t - tInterval / 2;
+ SkDPoint midPt = dcubic_xy_at_t(fPts, tMid);
+ return midPt.approximatelyEqual(xyAtT(t));
+ }
+ return false;
+}
+
bool SkOpSegment::findCoincidentMatch(const SkOpSpan* span, const SkOpSegment* other, int oStart,
int oEnd, int step, SkPoint* startPt, SkPoint* endPt, double* endT) const {
SkASSERT(span->fT == 0 || span->fT == 1);
@@ -1474,6 +2654,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)
@@ -1489,12 +2679,11 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart
SkASSERT(startIndex != endIndex);
SkDEBUGCODE(const int count = fTs.count());
SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
- const int step = SkSign32(endIndex - startIndex);
- const int end = nextExactSpan(startIndex, step);
- SkASSERT(end >= 0);
- SkOpSpan* endSpan = &fTs[end];
- SkOpSegment* other;
- if (isSimple(end)) {
+ int step = SkSign32(endIndex - startIndex);
+ *nextStart = startIndex;
+ SkOpSegment* other = isSimple(nextStart, &step);
+ if (other)
+ {
// mark the smaller of startIndex, endIndex done, and all adjacent
// spans with the same T value (but not 'other' spans)
#if DEBUG_WINDING
@@ -1505,8 +2694,6 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart
return NULL;
}
markDoneBinary(min);
- other = endSpan->fOther;
- *nextStart = endSpan->fOtherIndex;
double startT = other->fTs[*nextStart].fT;
*nextEnd = *nextStart;
do {
@@ -1519,61 +2706,55 @@ 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;
+ const int end = nextExactSpan(startIndex, step);
+ SkASSERT(end >= 0);
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;
+ markDoneBinary(SkMin32(startIndex, endIndex));
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;
+ markDoneBinary(SkMin32(startIndex, endIndex));
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);
+ if (sumMiWinding == SK_MinS32) {
+ *unsortable = true;
+ markDoneBinary(SkMin32(startIndex, endIndex));
+ return NULL;
+ }
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)) {
if (nextSegment->isTiny(nextAngle)) {
*unsortable = true;
+ markDoneBinary(SkMin32(startIndex, endIndex));
return NULL;
}
foundAngle = nextAngle;
@@ -1587,10 +2768,11 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart
continue;
}
if (!activeAngle) {
- nextSegment->markAndChaseDoneBinary(nextAngle->start(), nextAngle->end());
+ (void) nextSegment->markAndChaseDoneBinary(nextAngle->start(), nextAngle->end());
}
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 +2780,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;
@@ -1620,12 +2808,11 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* next
SkASSERT(startIndex != endIndex);
SkDEBUGCODE(const int count = fTs.count());
SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
- const int step = SkSign32(endIndex - startIndex);
- const int end = nextExactSpan(startIndex, step);
- SkASSERT(end >= 0);
- SkOpSpan* endSpan = &fTs[end];
- SkOpSegment* other;
- if (isSimple(end)) {
+ int step = SkSign32(endIndex - startIndex);
+ *nextStart = startIndex;
+ SkOpSegment* other = isSimple(nextStart, &step);
+ if (other)
+ {
// mark the smaller of startIndex, endIndex done, and all adjacent
// spans with the same T value (but not 'other' spans)
#if DEBUG_WINDING
@@ -1636,8 +2823,6 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* next
return NULL;
}
markDoneUnary(min);
- other = endSpan->fOther;
- *nextStart = endSpan->fOtherIndex;
double startT = other->fTs[*nextStart].fT;
*nextEnd = *nextStart;
do {
@@ -1650,51 +2835,40 @@ 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;
+ const int end = nextExactSpan(startIndex, step);
+ SkASSERT(end >= 0);
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;
+ markDoneUnary(SkMin32(startIndex, endIndex));
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)) {
if (nextSegment->isTiny(nextAngle)) {
*unsortable = true;
+ markDoneUnary(SkMin32(startIndex, endIndex));
return NULL;
}
foundAngle = nextAngle;
@@ -1712,6 +2886,7 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* next
}
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__,
@@ -1719,7 +2894,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;
@@ -1741,11 +2916,12 @@ SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsort
SkDEBUGCODE(int count = fTs.count());
SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
int step = SkSign32(endIndex - startIndex);
- int end = nextExactSpan(startIndex, step);
- 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
+ *nextStart = startIndex;
+ SkOpSegment* other = isSimple(nextStart, &step);
+ if (other)
+ {
#if DEBUG_WINDING
SkDebugf("%s simple\n", __FUNCTION__);
#endif
@@ -1754,8 +2930,6 @@ SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsort
return NULL;
}
markDone(min, 1);
- other = endSpan->fOther;
- *nextStart = endSpan->fOtherIndex;
double startT = other->fTs[*nextStart].fT;
// FIXME: I don't know why the logic here is difference from the winding case
SkDEBUGCODE(bool firstLoop = true;)
@@ -1779,39 +2953,23 @@ 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);
+ // parallel block above with presorted version
+ int end = nextExactSpan(startIndex, step);
+ SkASSERT(end >= 0);
+ SkOpAngle* angle = spanToAngle(end, startIndex);
+ SkASSERT(angle);
#if DEBUG_SORT
- debugShowSort(__FUNCTION__, sorted, firstIndex, 0, 0, sortable);
+ SkDebugf("%s\n", __FUNCTION__);
+ angle->debugLoop();
#endif
- if (!sortable) {
- *unsortable = true;
- return NULL;
- }
- SkASSERT(sorted[firstIndex]->segment() == this);
-#if DEBUG_WINDING
- SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex,
- sorted[firstIndex]->sign());
-#endif
- int nextIndex = firstIndex + 1;
- int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
+ 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 +2978,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 +2998,21 @@ 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;
- }
- }
- return firstIndex;
+int SkOpSegment::findStartSpan(int startIndex) const {
+ int index = startIndex;
+ const SkOpSpan* span = &fTs[index];
+ const SkPoint& firstPt = span->fPt;
+ double firstT = span->fT;
+ const SkOpSpan* prior;
+ do {
+ prior = span;
+ span = &fTs[++index];
+ } while (SkDPoint::ApproximatelyEqual(span->fPt, firstPt)
+ && (span->fT == firstT || prior->fTiny));
+ 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,23 +3024,47 @@ 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::findOtherT(double t, const SkOpSegment* match) const {
+ int count = this->count();
+ for (int index = 0; index < count; ++index) {
+ const SkOpSpan& span = fTs[index];
+ if (span.fOtherT == t && span.fOther == match) {
+ return index;
+ }
+ }
+ return -1;
+}
+
+int SkOpSegment::findT(double t, const SkPoint& pt, 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;
+ }
+ }
+ // Usually, the pair of ts are an exact match. It's possible that the t values have
+ // been adjusted to make multiple intersections align. In this rare case, look for a
+ // matching point / match pair instead.
+ for (int index = 0; index < count; ++index) {
+ const SkOpSpan& span = fTs[index];
+ if (span.fPt == pt && 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) {
+ bool firstPass) {
// 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(&firstT);
if (firstT < 0) {
- *unsortable = true;
+ *unsortable = !firstPass;
firstT = 0;
while (fTs[firstT].fDone) {
SkASSERT(firstT < fTs.count());
@@ -1894,69 +3076,74 @@ 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(step);
+ }
+ markAngle->markStops();
+ const SkOpAngle* baseAngle = markAngle->next() == markAngle && !isVertical() ? markAngle
+ : 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;
- SkOpSegment* leftSegment;
+ angle = firstAngle;
+ SkOpSegment* leftSegment = NULL;
+ bool looped = false;
do {
- if (++firstT == count) {
- firstT = 0;
- }
- const SkOpAngle* angle = sorted[firstT];
- SkASSERT(!onlySortable || !angle->unsortable());
- leftSegment = angle->segment();
- *tIndexPtr = angle->end();
- *endIndexPtr = angle->start();
- } while (leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fDone);
+ *unsortable = angle->unorderable();
+ if (firstPass || !*unsortable) {
+ leftSegment = angle->segment();
+ *tIndexPtr = angle->end();
+ *endIndexPtr = angle->start();
+ if (!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fDone) {
+ break;
+ }
+ }
+ angle = angle->next();
+ looped = true;
+ } while (angle != firstAngle);
+ if (angle == firstAngle && looped) {
+ return NULL;
+ }
if (leftSegment->verb() >= SkPath::kQuad_Verb) {
const int tIndex = *tIndexPtr;
const int endIndex = *endIndexPtr;
- if (!leftSegment->clockwise(tIndex, endIndex)) {
- bool swap = !leftSegment->monotonicInY(tIndex, endIndex)
- && !leftSegment->serpentine(tIndex, endIndex);
+ bool swap;
+ if (!leftSegment->clockwise(tIndex, endIndex, &swap)) {
#if DEBUG_SWAP_TOP
- SkDebugf("%s swap=%d serpentine=%d containedByEnds=%d monotonic=%d\n", __FUNCTION__,
- swap,
+ SkDebugf("%s swap=%d inflections=%d serpentine=%d controlledbyends=%d monotonic=%d\n",
+ __FUNCTION__,
+ swap, leftSegment->debugInflections(tIndex, endIndex),
leftSegment->serpentine(tIndex, endIndex),
leftSegment->controlsContainedByEnds(tIndex, endIndex),
leftSegment->monotonicInY(tIndex, endIndex));
@@ -1972,6 +3159,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
@@ -1999,20 +3194,40 @@ void SkOpSegment::fixOtherTIndex() {
}
}
+bool SkOpSegment::inCoincidentSpan(double t, const SkOpSegment* other) const {
+ int foundEnds = 0;
+ int count = this->count();
+ for (int index = 0; index < count; ++index) {
+ const SkOpSpan& span = this->span(index);
+ if (span.fCoincident) {
+ foundEnds |= (span.fOther == other) << ((t > span.fT) + (t >= span.fT));
+ }
+ }
+ SkASSERT(foundEnds != 7);
+ return foundEnds == 0x3 || foundEnds == 0x5 || foundEnds == 0x6; // two bits set
+}
+
void SkOpSegment::init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd) {
fDoneSpans = 0;
fOperand = operand;
fXor = evenOdd;
fPts = pts;
fVerb = verb;
+ fLoop = fMultiples = 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);
+ }
}
/*
@@ -2031,20 +3246,13 @@ void SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkSc
SkASSERT(dx);
int windVal = windValue(SkMin32(start, end));
#if DEBUG_WINDING_AT_T
- SkDebugf("%s oldWinding=%d hitDx=%c dx=%c windVal=%d", __FUNCTION__, winding,
+ SkDebugf("%s id=%d oldWinding=%d hitDx=%c dx=%c windVal=%d", __FUNCTION__, debugID(), 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);
-#endif
SkDEBUGCODE(int oppLocal = oppSign(start, end));
SkASSERT(hitOppDx || !oppWind || !oppLocal);
int oppWindVal = oppValue(SkMin32(start, end));
@@ -2056,16 +3264,37 @@ void SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkSc
oppWind = oppSideWind;
}
}
+#if DEBUG_WINDING_AT_T
+ SkDebugf(" winding=%d oppWind=%d\n", winding, oppWind);
+#endif
(void) markAndChaseWinding(start, end, winding, oppWind);
// OPTIMIZATION: the reverse mark and chase could skip the first marking
(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;
+ SkOpAngle* from = fTs[index].fFromAngle;
+ SkOpAngle* to = fTs[index].fToAngle;
+ while (++index < spanCount) {
+ SkOpAngle* nextFrom = fTs[index].fFromAngle;
+ SkOpAngle* nextTo = fTs[index].fToAngle;
+ if (from != nextFrom || to != 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;
@@ -2074,19 +3303,9 @@ bool SkOpSegment::isMissing(double startT, const SkPoint& pt) const {
return true;
}
-bool SkOpSegment::isSimple(int end) const {
- int count = fTs.count();
- if (count == 2) {
- return true;
- }
- double t = fTs[end].fT;
- if (approximately_less_than_zero(t)) {
- return !approximately_less_than_zero(fTs[1].fT);
- }
- if (approximately_greater_than_one(t)) {
- return !approximately_greater_than_one(fTs[count - 2].fT);
- }
- return false;
+
+SkOpSegment* SkOpSegment::isSimple(int* end, int* step) {
+ return nextChase(end, step, NULL, NULL);
}
bool SkOpSegment::isTiny(const SkOpAngle* angle) const {
@@ -2103,13 +3322,13 @@ 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) {
- int otherTIndex = other->findT(otherT, this);
+bool SkOpSegment::joinCoincidence(SkOpSegment* other, double otherT, const SkPoint& otherPt,
+ int step, bool cancel) {
+ int otherTIndex = other->findT(otherT, otherPt, 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;
@@ -2145,11 +3364,12 @@ SkOpSpan* SkOpSegment::markAndChaseDoneBinary(int index, int endIndex) {
int step = SkSign32(endIndex - index);
int min = SkMin32(index, endIndex);
markDoneBinary(min);
- SkOpSpan* last;
+ SkOpSpan* last = NULL;
SkOpSegment* other = this;
- while ((other = other->nextChase(&index, step, &min, &last))) {
+ while ((other = other->nextChase(&index, &step, &min, &last))) {
if (other->done()) {
- return NULL;
+ SkASSERT(!last);
+ break;
}
other->markDoneBinary(min);
}
@@ -2160,29 +3380,48 @@ SkOpSpan* SkOpSegment::markAndChaseDoneUnary(int index, int endIndex) {
int step = SkSign32(endIndex - index);
int min = SkMin32(index, endIndex);
markDoneUnary(min);
- SkOpSpan* last;
+ SkOpSpan* last = NULL;
SkOpSegment* other = this;
- while ((other = other->nextChase(&index, step, &min, &last))) {
+ while ((other = other->nextChase(&index, &step, &min, &last))) {
if (other->done()) {
- return NULL;
+ SkASSERT(!last);
+ break;
}
other->markDoneUnary(min);
}
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);
int min = SkMin32(index, endIndex);
markWinding(min, winding);
- SkOpSpan* last;
+ SkOpSpan* last = NULL;
SkOpSegment* other = this;
- while ((other = other->nextChase(&index, step, &min, &last))) {
+ while ((other = other->nextChase(&index, &step, &min, &last))) {
if (other->fTs[min].fWindSum != SK_MinS32) {
SkASSERT(other->fTs[min].fWindSum == winding);
- return NULL;
+ SkASSERT(!last);
+ break;
+ }
+ other->markWinding(min, winding);
+ }
+ 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 = NULL;
+ 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);
+ SkASSERT(!last);
+ break;
}
other->markWinding(min, winding);
}
@@ -2193,14 +3432,32 @@ SkOpSpan* SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding,
int min = SkMin32(index, endIndex);
int step = SkSign32(endIndex - index);
markWinding(min, winding, oppWinding);
- SkOpSpan* last;
+ SkOpSpan* last = NULL;
SkOpSegment* other = this;
- while ((other = other->nextChase(&index, step, &min, &last))) {
+ 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;
+#ifdef SK_DEBUG
+ if (!other->fTs[min].fLoop) {
+ if (fOperand == other->fOperand) {
+// FIXME: this is probably a bug -- rects4 asserts here
+// SkASSERT(other->fTs[min].fWindSum == winding);
+// FIXME: this is probably a bug -- rects3 asserts here
+// SkASSERT(other->fTs[min].fOppSum == oppWinding);
+ } else {
+ SkASSERT(other->fTs[min].fWindSum == oppWinding);
+// FIXME: this is probably a bug -- skpwww_joomla_org_23 asserts here
+// SkASSERT(other->fTs[min].fOppSum == winding);
+ }
+ }
+ SkASSERT(!last);
+#endif
+ break;
+ }
+ if (fOperand == other->fOperand) {
+ other->markWinding(min, winding, oppWinding);
+ } else {
+ other->markWinding(min, oppWinding, winding);
}
- other->markWinding(min, winding, oppWinding);
}
return last;
}
@@ -2265,6 +3522,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 +3534,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 +3546,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 +3563,7 @@ void SkOpSegment::markOneDoneBinary(const char* funName, int tIndex) {
if (!span) {
return;
}
+ SkASSERT(!span->fDone);
span->fDone = true;
fDoneSpans++;
}
@@ -2312,20 +3573,26 @@ 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
debugShowNewWinding(funName, span, winding);
#endif
SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
- SkASSERT(abs(winding) <= SkPathOpsDebug::gMaxWindSum);
+#if DEBUG_LIMIT_WIND_SUM
+ SkASSERT(abs(winding) <= DEBUG_LIMIT_WIND_SUM);
+#endif
span.fWindSum = winding;
return &span;
}
@@ -2340,22 +3607,59 @@ SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int windi
debugShowNewWinding(funName, span, winding, oppWinding);
#endif
SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
- SkASSERT(abs(winding) <= SkPathOpsDebug::gMaxWindSum);
+#if DEBUG_LIMIT_WIND_SUM
+ SkASSERT(abs(winding) <= DEBUG_LIMIT_WIND_SUM);
+#endif
span.fWindSum = winding;
SkASSERT(span.fOppSum == SK_MinS32 || span.fOppSum == oppWinding);
- SkASSERT(abs(oppWinding) <= SkPathOpsDebug::gMaxWindSum);
+#if DEBUG_LIMIT_WIND_SUM
+ SkASSERT(abs(oppWinding) <= DEBUG_LIMIT_WIND_SUM);
+#endif
span.fOppSum = oppWinding;
+ debugValidate();
return &span;
}
// from http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
-bool SkOpSegment::clockwise(int tStart, int tEnd) const {
+bool SkOpSegment::clockwise(int tStart, int tEnd, bool* swap) const {
SkASSERT(fVerb != SkPath::kLine_Verb);
SkPoint edge[4];
subDivide(tStart, tEnd, edge);
int points = SkPathOpsVerbToPoints(fVerb);
double sum = (edge[0].fX - edge[points].fX) * (edge[0].fY + edge[points].fY);
+ bool sumSet = false;
if (fVerb == SkPath::kCubic_Verb) {
+ SkDCubic cubic;
+ cubic.set(edge);
+ double inflectionTs[2];
+ int inflections = cubic.findInflections(inflectionTs);
+ // FIXME: this fixes cubicOp114 and breaks cubicOp58d
+ // the trouble is that cubics with inflections confuse whether the curve breaks towards
+ // or away, which in turn is used to determine if it is on the far right or left.
+ // Probably a totally different approach is in order. At one time I tried to project a
+ // horizontal ray to determine winding, but was confused by how to map the vertically
+ // oriented winding computation over.
+ if (0 && inflections) {
+ double tLo = this->span(tStart).fT;
+ double tHi = this->span(tEnd).fT;
+ double tLoStart = tLo;
+ for (int index = 0; index < inflections; ++index) {
+ if (between(tLo, inflectionTs[index], tHi)) {
+ tLo = inflectionTs[index];
+ }
+ }
+ if (tLo != tLoStart && tLo != tHi) {
+ SkDPoint sub[2];
+ sub[0] = cubic.ptAtT(tLo);
+ sub[1].set(edge[3]);
+ SkDPoint ctrl[2];
+ SkDCubic::SubDivide(fPts, sub[0], sub[1], tLo, tHi, ctrl);
+ edge[0] = sub[0].asSkPoint();
+ edge[1] = ctrl[0].asSkPoint();
+ edge[2] = ctrl[1].asSkPoint();
+ sum = (edge[0].fX - edge[3].fX) * (edge[0].fY + edge[3].fY);
+ }
+ }
SkScalar lesser = SkTMin<SkScalar>(edge[0].fY, edge[3].fY);
if (edge[1].fY < lesser && edge[2].fY < lesser) {
SkDLine tangent1 = {{ {edge[0].fX, edge[0].fY}, {edge[1].fX, edge[1].fY} }};
@@ -2364,20 +3668,29 @@ bool SkOpSegment::clockwise(int tStart, int tEnd) const {
SkPoint topPt = cubic_top(fPts, fTs[tStart].fT, fTs[tEnd].fT);
sum += (topPt.fX - edge[0].fX) * (topPt.fY + edge[0].fY);
sum += (edge[3].fX - topPt.fX) * (edge[3].fY + topPt.fY);
- return sum <= 0;
+ sumSet = true;
}
}
}
- for (int idx = 0; idx < points; ++idx){
- sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY);
+ if (!sumSet) {
+ for (int idx = 0; idx < points; ++idx){
+ sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY);
+ }
+ }
+ if (fVerb == SkPath::kCubic_Verb) {
+ SkDCubic cubic;
+ cubic.set(edge);
+ *swap = sum > 0 && !cubic.monotonicInY() && !cubic.serpentine();
+ } else {
+ SkDQuad quad;
+ quad.set(edge);
+ *swap = sum > 0 && !quad.monotonicInY();
}
return sum <= 0;
}
bool SkOpSegment::monotonicInY(int tStart, int tEnd) const {
- if (fVerb == SkPath::kLine_Verb) {
- return false;
- }
+ SkASSERT(fVerb != SkPath::kLine_Verb);
if (fVerb == SkPath::kQuad_Verb) {
SkDQuad dst = SkDQuad::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
return dst.monotonicInY();
@@ -2428,31 +3741,6 @@ SkOpSpan* SkOpSegment::verifyOneWindingU(const char* funName, int tIndex) {
return &span;
}
-// note that just because a span has one end that is unsortable, that's
-// not enough to mark it done. The other end may be sortable, allowing the
-// span to be added.
-// FIXME: if abs(start - end) > 1, mark intermediates as unsortable on both ends
-void SkOpSegment::markUnsortable(int start, int end) {
- SkOpSpan* span = &fTs[start];
- if (start < end) {
-#if DEBUG_UNSORTABLE
- debugShowNewWinding(__FUNCTION__, *span, 0);
-#endif
- span->fUnsortableStart = true;
- } else {
- --span;
-#if DEBUG_UNSORTABLE
- debugShowNewWinding(__FUNCTION__, *span, 0);
-#endif
- span->fUnsortableEnd = true;
- }
- if (!span->fUnsortableStart || !span->fUnsortableEnd || span->fDone) {
- return;
- }
- span->fDone = true;
- fDoneSpans++;
-}
-
void SkOpSegment::markWinding(int index, int winding) {
// SkASSERT(!done());
SkASSERT(winding);
@@ -2464,6 +3752,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 +3766,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;
@@ -2512,15 +3817,6 @@ void SkOpSegment::matchWindingValue(int tIndex, double t, bool borrowWind) {
}
}
-// return span if when chasing, two or more radiating spans are not done
-// OPTIMIZATION: ? multiple spans is detected when there is only one valid
-// candidate and the remaining spans have windValue == 0 (canceled by
-// coincidence). The coincident edges could either be removed altogether,
-// or this code could be more complicated in detecting this case. Worth it?
-bool SkOpSegment::multipleSpans(int end) const {
- return end > 0 && end < fTs.count() - 1;
-}
-
bool SkOpSegment::nextCandidate(int* start, int* end) const {
while (fTs[*end].fDone) {
if (fTs[*end].fT == 1) {
@@ -2533,27 +3829,67 @@ bool SkOpSegment::nextCandidate(int* start, int* end) const {
return true;
}
-SkOpSegment* SkOpSegment::nextChase(int* index, const int step, int* min, SkOpSpan** last) {
- int end = nextExactSpan(*index, step);
+static SkOpSegment* set_last(SkOpSpan** last, SkOpSpan* endSpan) {
+ if (last && !endSpan->fSmall) {
+ *last = endSpan;
+ }
+ return NULL;
+}
+
+SkOpSegment* SkOpSegment::nextChase(int* indexPtr, int* stepPtr, int* minPtr, SkOpSpan** last) {
+ int origIndex = *indexPtr;
+ int step = *stepPtr;
+ int end = nextExactSpan(origIndex, step);
SkASSERT(end >= 0);
- if (fTs[end].fSmall) {
- *last = NULL;
- return NULL;
+ SkOpSpan& endSpan = fTs[end];
+ SkOpAngle* angle = step > 0 ? endSpan.fFromAngle : endSpan.fToAngle;
+ int foundIndex;
+ int otherEnd;
+ SkOpSegment* other;
+ if (angle == NULL) {
+ if (endSpan.fT != 0 && endSpan.fT != 1) {
+ return NULL;
+ }
+ other = endSpan.fOther;
+ foundIndex = endSpan.fOtherIndex;
+ otherEnd = other->nextExactSpan(foundIndex, step);
+ } else {
+ int loopCount = angle->loopCount();
+ if (loopCount > 2) {
+ return set_last(last, &endSpan);
+ }
+ const SkOpAngle* next = angle->next();
+ if (angle->sign() != next->sign()) {
+#if DEBUG_WINDING
+ SkDebugf("%s mismatched signs\n", __FUNCTION__);
+#endif
+ // return set_last(last, &endSpan);
+ }
+ other = next->segment();
+ foundIndex = end = next->start();
+ otherEnd = next->end();
}
- if (multipleSpans(end)) {
- *last = &fTs[end];
- return NULL;
+ int foundStep = foundIndex < otherEnd ? 1 : -1;
+ if (*stepPtr != foundStep) {
+ return set_last(last, &endSpan);
}
- const SkOpSpan& endSpan = fTs[end];
- SkOpSegment* other = endSpan.fOther;
- *index = endSpan.fOtherIndex;
- SkASSERT(*index >= 0);
- int otherEnd = other->nextExactSpan(*index, step);
+ SkASSERT(*indexPtr >= 0);
SkASSERT(otherEnd >= 0);
- *min = SkMin32(*index, otherEnd);
- if (other->fTs[*min].fSmall) {
- *last = NULL;
- return NULL;
+#if 1
+ int origMin = origIndex + (step < 0 ? step : 0);
+ const SkOpSpan& orig = this->span(origMin);
+#endif
+ int foundMin = SkMin32(foundIndex, otherEnd);
+#if 1
+ const SkOpSpan& found = other->span(foundMin);
+ if (found.fWindValue != orig.fWindValue || found.fOppValue != orig.fOppValue) {
+ return set_last(last, &endSpan);
+ }
+#endif
+ *indexPtr = foundIndex;
+ *stepPtr = foundStep;
+ if (minPtr) {
+ *minPtr = foundMin;
}
return other;
}
@@ -2610,6 +3946,27 @@ int SkOpSegment::nextExactSpan(int from, int step) const {
return -1;
}
+void SkOpSegment::pinT(const SkPoint& pt, double* t) {
+ if (pt == fPts[0]) {
+ *t = 0;
+ }
+ int count = SkPathOpsVerbToPoints(fVerb);
+ if (pt == fPts[count]) {
+ *t = 1;
+ }
+}
+
+void SkOpSegment::setCoincidentRange(const SkPoint& startPt, const SkPoint& endPt,
+ SkOpSegment* other) {
+ int count = this->count();
+ for (int index = 0; index < count; ++index) {
+ SkOpSpan &span = fTs[index];
+ if ((startPt == span.fPt || endPt == span.fPt) && other == span.fOther) {
+ span.fCoincident = true;
+ }
+ }
+}
+
void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding, int* sumSuWinding,
int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding) {
int deltaSum = spanSign(index, endIndex);
@@ -2625,8 +3982,10 @@ void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding, int*
*oppMaxWinding = *sumSuWinding;
*oppSumWinding = *sumSuWinding -= oppDeltaSum;
}
- SkASSERT(abs(*sumWinding) <= SkPathOpsDebug::gMaxWindSum);
- SkASSERT(abs(*oppSumWinding) <= SkPathOpsDebug::gMaxWindSum);
+#if DEBUG_LIMIT_WIND_SUM
+ SkASSERT(abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
+ SkASSERT(abs(*oppSumWinding) <= DEBUG_LIMIT_WIND_SUM);
+#endif
}
void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding,
@@ -2634,73 +3993,106 @@ void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding,
int deltaSum = spanSign(index, endIndex);
*maxWinding = *sumMiWinding;
*sumWinding = *sumMiWinding -= deltaSum;
- SkASSERT(abs(*sumWinding) <= SkPathOpsDebug::gMaxWindSum);
-}
-
-// This marks all spans unsortable so that this info is available for early
-// 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));
-#if DEBUG_ANGLE
- (*(angleList->end() - 1))->setID(angleIndex);
+#if DEBUG_LIMIT_WIND_SUM
+ SkASSERT(abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
#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;
+}
+
+void SkOpSegment::sortAngles() {
+ int spanCount = fTs.count();
+ if (spanCount <= 2) {
+ return;
+ }
+ int index = 0;
+ do {
+ SkOpAngle* fromAngle = fTs[index].fFromAngle;
+ SkOpAngle* toAngle = fTs[index].fToAngle;
+ if (!fromAngle && !toAngle) {
+ index += 1;
+ continue;
+ }
+ SkOpAngle* baseAngle = NULL;
+ if (fromAngle) {
+ baseAngle = fromAngle;
+ if (inLoop(baseAngle, spanCount, &index)) {
+ continue;
}
}
- }
- 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;
+#if DEBUG_ANGLE
+ bool wroteAfterHeader = false;
+#endif
+ if (toAngle) {
+ 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);
+ }
}
- angleList->push_back(const_cast<SkOpAngle*>(&angle));
+ SkOpAngle* nextFrom, * nextTo;
+ int firstIndex = index;
+ do {
+ SkOpSpan& span = fTs[index];
+ SkOpSegment* other = span.fOther;
+ SkOpSpan& oSpan = other->fTs[span.fOtherIndex];
+ SkOpAngle* oAngle = oSpan.fFromAngle;
+ if (oAngle) {
#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;
+ if (!oAngle->loopContains(*baseAngle)) {
+ baseAngle->insert(oAngle);
+ }
+ }
+ oAngle = oSpan.fToAngle;
+ if (oAngle) {
+#if DEBUG_ANGLE
+ if (!wroteAfterHeader) {
+ SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
+ index);
+ wroteAfterHeader = true;
+ }
+#endif
+ if (!oAngle->loopContains(*baseAngle)) {
+ baseAngle->insert(oAngle);
+ }
+ }
+ if (++index == spanCount) {
+ break;
+ }
+ nextFrom = fTs[index].fFromAngle;
+ nextTo = fTs[index].fToAngle;
+ } while (fromAngle == nextFrom && toAngle == nextTo);
+ if (baseAngle && baseAngle->loopCount() == 1) {
+ index = firstIndex;
+ do {
+ SkOpSpan& span = fTs[index];
+ span.fFromAngle = span.fToAngle = NULL;
+ if (++index == spanCount) {
+ break;
+ }
+ nextFrom = fTs[index].fFromAngle;
+ nextTo = fTs[index].fToAngle;
+ } while (fromAngle == nextFrom && toAngle == nextTo);
+ baseAngle = NULL;
+ }
+#if DEBUG_SORT
+ SkASSERT(!baseAngle || baseAngle->loopCount() > 1);
+#endif
+ } while (index < spanCount);
}
// return true if midpoints were computed
@@ -2802,8 +4194,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;
@@ -2844,6 +4236,9 @@ int SkOpSegment::updateOppWindingReverse(const SkOpAngle* angle) const {
int SkOpSegment::updateWinding(int index, int endIndex) const {
int lesser = SkMin32(index, endIndex);
int winding = windSum(lesser);
+ if (winding == SK_MinS32) {
+ return winding;
+ }
int spanWinding = spanSign(index, endIndex);
if (winding && UseInnerWinding(winding - spanWinding, winding)
&& winding != SK_MaxS32) {
@@ -2904,7 +4299,8 @@ int SkOpSegment::windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar* dx
SkASSERT(winding != SK_MinS32);
int windVal = crossOpp ? oppValue(tIndex) : windValue(tIndex);
#if DEBUG_WINDING_AT_T
- SkDebugf("%s oldWinding=%d windValue=%d", __FUNCTION__, winding, windVal);
+ SkDebugf("%s id=%d opp=%d tHit=%1.9g t=%1.9g oldWinding=%d windValue=%d", __FUNCTION__,
+ debugID(), crossOpp, tHit, t(tIndex), winding, windVal);
#endif
// see if a + change in T results in a +/- change in X (compute x'(T))
*dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
@@ -2947,382 +4343,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