Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(957)

Unified Diff: src/pathops/SkOpSegment.cpp

Issue 1029993002: Revert of pathops version two (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/pathops/SkOpSegment.h ('k') | src/pathops/SkOpSpan.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/pathops/SkOpSegment.cpp
diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp
index 902f27374438b150739c125ec8e0d24760f442d2..1fb5afa028af33e894a82fe890e8b03af185f52a 100644
--- a/src/pathops/SkOpSegment.cpp
+++ b/src/pathops/SkOpSegment.cpp
@@ -4,20 +4,11 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
-#include "SkOpCoincidence.h"
+#include "SkIntersections.h"
#include "SkOpContour.h"
#include "SkOpSegment.h"
#include "SkPathWriter.h"
-
-/*
-After computing raw intersections, post process all segments to:
-- find small collections of points that can be collapsed to a single point
-- find missing intersections to resolve differences caused by different algorithms
-
-Consider segments containing tiny or small intervals. Consider coincident segments
-because coincidence finds intersections through distance measurement that non-coincident
-intersection tests cannot.
- */
+#include "SkTSort.h"
#define F (false) // discard the edge
#define T (true) // keep the edge
@@ -42,125 +33,147 @@
#undef F
#undef T
-SkOpAngle* SkOpSegment::activeAngle(SkOpSpanBase* start, SkOpSpanBase** startPtr,
- SkOpSpanBase** endPtr, bool* done, bool* sortable) {
- if (SkOpAngle* result = activeAngleInner(start, startPtr, endPtr, done, sortable)) {
+enum {
+ kOutsideTrackedTCount = 16, // FIXME: determine what this should be
+ kMissingSpanCount = 4, // FIXME: determine what this should be
+};
+
+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;
}
- if (SkOpAngle* result = activeAngleOther(start, startPtr, endPtr, 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 (const SkOpAngle* result = activeAngleOther(lesser, start, end, done, sortable)) {
+ return result;
+ }
+ }
+ do {
+ if (const SkOpAngle* result = activeAngleOther(index, start, end, done, sortable)) {
+ return result;
+ }
+ if (++index == fTs.count()) {
+ break;
+ }
+ if (fTs[index - 1].fTiny) {
+ referenceT = fTs[index].fT;
+ continue;
+ }
+ } while (precisely_negative(fTs[index].fT - referenceT));
return NULL;
}
-SkOpAngle* SkOpSegment::activeAngleInner(SkOpSpanBase* start, SkOpSpanBase** startPtr,
- SkOpSpanBase** endPtr, bool* done, bool* sortable) {
- SkOpSpan* upSpan = start->upCastable();
- if (upSpan) {
- if (upSpan->windValue() || upSpan->oppValue()) {
- SkOpSpanBase* next = upSpan->next();
- if (!*endPtr) {
- *startPtr = start;
- *endPtr = next;
- }
- if (!upSpan->done()) {
- if (upSpan->windSum() != SK_MinS32) {
- return spanToAngle(start, next);
+const SkOpAngle* SkOpSegment::activeAngleInner(int index, int* start, int* end, bool* done,
+ bool* sortable) const {
+ int next = nextExactSpan(index, 1);
+ if (next > 0) {
+ const SkOpSpan& upSpan = fTs[index];
+ if (upSpan.fWindValue || upSpan.fOppValue) {
+ if (*end < 0) {
+ *start = index;
+ *end = next;
+ }
+ if (!upSpan.fDone) {
+ if (upSpan.fWindSum != SK_MinS32) {
+ return spanToAngle(index, next);
}
*done = false;
}
} else {
- SkASSERT(upSpan->done());
- }
- }
- SkOpSpan* downSpan = start->prev();
+ SkASSERT(upSpan.fDone);
+ }
+ }
+ int prev = nextExactSpan(index, -1);
// edge leading into junction
- if (downSpan) {
- if (downSpan->windValue() || downSpan->oppValue()) {
- if (!*endPtr) {
- *startPtr = start;
- *endPtr = downSpan;
- }
- if (!downSpan->done()) {
- if (downSpan->windSum() != SK_MinS32) {
- return spanToAngle(start, downSpan);
+ if (prev >= 0) {
+ const SkOpSpan& downSpan = fTs[prev];
+ if (downSpan.fWindValue || downSpan.fOppValue) {
+ if (*end < 0) {
+ *start = index;
+ *end = prev;
+ }
+ if (!downSpan.fDone) {
+ if (downSpan.fWindSum != SK_MinS32) {
+ return spanToAngle(index, prev);
}
*done = false;
}
} else {
- SkASSERT(downSpan->done());
+ SkASSERT(downSpan.fDone);
}
}
return NULL;
}
-SkOpAngle* SkOpSegment::activeAngleOther(SkOpSpanBase* start, SkOpSpanBase** startPtr,
- SkOpSpanBase** endPtr, bool* done, bool* sortable) {
- SkOpPtT* oPtT = start->ptT()->next();
- SkOpSegment* other = oPtT->segment();
- SkOpSpanBase* oSpan = oPtT->span();
- return other->activeAngleInner(oSpan, startPtr, endPtr, done, sortable);
-}
-
-SkPoint SkOpSegment::activeLeftTop(SkOpSpanBase** firstSpan) {
+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(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;
double lastT = -1;
- SkOpSpanBase* span = &fHead;
- do {
- if (lastDone && (span->final() || span->upCast()->done())) {
+ for (int index = 0; index < count; ++index) {
+ const SkOpSpan& span = fTs[index];
+ if (span.fDone && lastDone) {
goto next;
}
+ if (approximately_negative(span.fT - lastT)) {
+ goto next;
+ }
{
- const SkPoint& xy = span->pt();
+ const SkPoint& xy = xyAtT(&span);
if (topPt.fY > xy.fY || (topPt.fY == xy.fY && topPt.fX > xy.fX)) {
topPt = xy;
- if (firstSpan) {
- *firstSpan = span;
+ if (firstT) {
+ *firstT = index;
}
}
if (fVerb != SkPath::kLine_Verb && !lastDone) {
- SkPoint curveTop = (*CurveTop[SkPathOpsVerbToPoints(fVerb)])(fPts, lastT,
- span->t());
+ SkPoint curveTop = (*CurveTop[SkPathOpsVerbToPoints(fVerb)])(fPts, lastT, span.fT);
if (topPt.fY > curveTop.fY || (topPt.fY == curveTop.fY
&& topPt.fX > curveTop.fX)) {
topPt = curveTop;
- if (firstSpan) {
- *firstSpan = span;
+ if (firstT) {
+ *firstT = index;
}
}
}
- lastT = span->t();
+ lastT = span.fT;
}
next:
- if (span->final()) {
- break;
- }
- lastDone = span->upCast()->done();
- } while ((span = span->upCast()->next()));
+ lastDone = span.fDone;
+ }
return topPt;
}
-bool SkOpSegment::activeOp(SkOpSpanBase* start, SkOpSpanBase* end, int xorMiMask, int xorSuMask,
- SkPathOp op) {
- int sumMiWinding = this->updateWinding(end, start);
- int sumSuWinding = this->updateOppWinding(end, start);
+bool SkOpSegment::activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, SkPathOp op) {
+ int sumMiWinding = updateWinding(endIndex, index);
+ int sumSuWinding = updateOppWinding(endIndex, index);
#if DEBUG_LIMIT_WIND_SUM
SkASSERT(abs(sumMiWinding) <= DEBUG_LIMIT_WIND_SUM);
SkASSERT(abs(sumSuWinding) <= DEBUG_LIMIT_WIND_SUM);
#endif
- if (this->operand()) {
+ if (fOperand) {
SkTSwap<int>(sumMiWinding, sumSuWinding);
}
- return this->activeOp(xorMiMask, xorSuMask, start, end, op, &sumMiWinding, &sumSuWinding);
-}
-
-bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, SkOpSpanBase* start, SkOpSpanBase* end,
- SkPathOp op, int* sumMiWinding, int* sumSuWinding) {
+ 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, sumWinding, oppMaxWinding, oppSumWinding;
- this->setUpWindings(start, end, sumMiWinding, sumSuWinding,
+ setUpWindings(index, endIndex, sumMiWinding, sumSuWinding,
&maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
bool miFrom;
bool miTo;
@@ -180,31 +193,178 @@
bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo];
#if DEBUG_ACTIVE_OP
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(), start->t(), end->t(),
+ __FUNCTION__, debugID(), span(index).fT, span(endIndex).fT,
SkPathOpsDebug::kPathOpStr[op], miFrom, miTo, suFrom, suTo, result);
#endif
return result;
}
-bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end) {
- int sumWinding = updateWinding(end, start);
- return activeWinding(start, end, &sumWinding);
-}
-
-bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sumWinding) {
+bool SkOpSegment::activeWinding(int index, int endIndex) {
+ int sumWinding = updateWinding(endIndex, index);
+ return activeWinding(index, endIndex, &sumWinding);
+}
+
+bool SkOpSegment::activeWinding(int index, int endIndex, int* sumWinding) {
int maxWinding;
- setUpWinding(start, end, &maxWinding, sumWinding);
+ setUpWinding(index, endIndex, &maxWinding, sumWinding);
bool from = maxWinding != 0;
bool to = *sumWinding != 0;
bool result = gUnaryActiveEdge[from][to];
return result;
}
-void SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
- SkPathWriter* path, bool active) const {
+void SkOpSegment::addCancelOutsides(const SkPoint& startPt, const SkPoint& endPt,
+ SkOpSegment* other) {
+ int tIndex = -1;
+ int tCount = fTs.count();
+ int oIndex = -1;
+ int oCount = other->fTs.count();
+ do {
+ ++tIndex;
+ } while (startPt != fTs[tIndex].fPt && tIndex < tCount);
+ int tIndexStart = tIndex;
+ do {
+ ++oIndex;
+ } while (endPt != other->fTs[oIndex].fPt && oIndex < oCount);
+ int oIndexStart = oIndex;
+ const SkPoint* nextPt;
+ do {
+ nextPt = &fTs[++tIndex].fPt;
+ SkASSERT(fTs[tIndex].fT < 1 || startPt != *nextPt);
+ } while (startPt == *nextPt);
+ double nextT = fTs[tIndex].fT;
+ const SkPoint* oNextPt;
+ do {
+ oNextPt = &other->fTs[++oIndex].fPt;
+ SkASSERT(other->fTs[oIndex].fT < 1 || endPt != *oNextPt);
+ } while (endPt == *oNextPt);
+ double oNextT = other->fTs[oIndex].fT;
+ // at this point, spans before and after are at:
+ // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
+ // if tIndexStart == 0, no prior span
+ // if nextT == 1, no following span
+
+ // advance the span with zero winding
+ // if the following span exists (not past the end, non-zero winding)
+ // connect the two edges
+ if (!fTs[tIndexStart].fWindValue) {
+ if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
+#if DEBUG_CONCIDENT
+ SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
+ __FUNCTION__, fID, other->fID, tIndexStart - 1,
+ fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
+ xyAtT(tIndexStart).fY);
+#endif
+ SkPoint copy = fTs[tIndexStart].fPt; // add t pair may move the point array
+ addTPair(fTs[tIndexStart].fT, other, other->fTs[oIndex].fT, false, copy);
+ }
+ if (nextT < 1 && fTs[tIndex].fWindValue) {
+#if DEBUG_CONCIDENT
+ SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
+ __FUNCTION__, fID, other->fID, tIndex,
+ fTs[tIndex].fT, xyAtT(tIndex).fX,
+ xyAtT(tIndex).fY);
+#endif
+ SkPoint copy = fTs[tIndex].fPt; // add t pair may move the point array
+ addTPair(fTs[tIndex].fT, other, other->fTs[oIndexStart].fT, false, copy);
+ }
+ } else {
+ SkASSERT(!other->fTs[oIndexStart].fWindValue);
+ if (oIndexStart > 0 && other->fTs[oIndexStart - 1].fWindValue) {
+#if DEBUG_CONCIDENT
+ SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
+ __FUNCTION__, fID, other->fID, oIndexStart - 1,
+ other->fTs[oIndexStart].fT, other->xyAtT(oIndexStart).fX,
+ other->xyAtT(oIndexStart).fY);
+ other->debugAddTPair(other->fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
+#endif
+ }
+ if (oNextT < 1 && other->fTs[oIndex].fWindValue) {
+#if DEBUG_CONCIDENT
+ SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
+ __FUNCTION__, fID, other->fID, oIndex,
+ other->fTs[oIndex].fT, other->xyAtT(oIndex).fX,
+ other->xyAtT(oIndex).fY);
+ other->debugAddTPair(other->fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
+#endif
+ }
+ }
+}
+
+void SkOpSegment::addCoinOutsides(const SkPoint& startPt, const SkPoint& endPt,
+ SkOpSegment* other) {
+ // walk this to startPt
+ // walk other to startPt
+ // if either is > 0, add a pointer to the other, copying adjacent winding
+ int tIndex = -1;
+ int oIndex = -1;
+ 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);
+ 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;
+ do {
+ const SkPoint* workPt;
+ do {
+ workPt = &fTs[++tIndex].fPt;
+ } while (nextPt == *workPt);
+ const SkPoint* oWorkPt;
+ do {
+ 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;
+ }
+ if (*workPt == *oWorkPt) {
+ addTPair(tStart, other, oStart, false, nextPt);
+ }
+ } while (endPt != nextPt);
+}
+
+void SkOpSegment::addCubic(const SkPoint pts[4], bool operand, bool evenOdd) {
+ init(pts, SkPath::kCubic_Verb, operand, evenOdd);
+ fBounds.setCubicBounds(pts);
+}
+
+void SkOpSegment::addCurveTo(int start, int end, SkPathWriter* path, bool active) const {
SkPoint edge[4];
const SkPoint* ePtr;
- if ((start == &fHead && end == &fTail) || (start == &fTail && end == &fHead)) {
+ int lastT = fTs.count() - 1;
+ if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
ePtr = fPts;
} else {
// OPTIMIZE? if not active, skip remainder and return xyAtT(end)
@@ -212,7 +372,7 @@
ePtr = edge;
}
if (active) {
- bool reverse = ePtr == fPts && start != &fHead;
+ bool reverse = ePtr == fPts && start != 0;
if (reverse) {
path->deferredMoveLine(ePtr[SkPathOpsVerbToPoints(fVerb)]);
switch (fVerb) {
@@ -228,6 +388,7 @@
default:
SkASSERT(0);
}
+ // return ePtr[0];
} else {
path->deferredMoveLine(ePtr[0]);
switch (fVerb) {
@@ -245,350 +406,1473 @@
}
}
}
-}
-
-SkOpPtT* SkOpSegment::addMissing(double t, SkOpSegment* opp, SkChunkAlloc* allocator) {
- SkOpSpanBase* existing = NULL;
- SkOpSpanBase* test = &fHead;
- double testT;
+ // return ePtr[SkPathOpsVerbToPoints(fVerb)];
+}
+
+void SkOpSegment::addEndSpan(int endIndex) {
+ SkASSERT(span(endIndex).fT == 1 || (span(endIndex).fTiny
+// && approximately_greater_than_one(span(endIndex).fT)
+ ));
+ int spanCount = fTs.count();
+ int startIndex = endIndex - 1;
+ while (fTs[startIndex].fT == 1 || fTs[startIndex].fTiny) {
+ --startIndex;
+ SkASSERT(startIndex > 0);
+ --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 {
- if ((testT = test->ptT()->fT) >= t) {
- if (testT == t) {
- existing = test;
- }
+ 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);
+}
+
+// add 2 to edge or out of range values to get T extremes
+void SkOpSegment::addOtherT(int index, double otherT, int otherIndex) {
+ SkOpSpan& span = fTs[index];
+ if (precisely_zero(otherT)) {
+ otherT = 0;
+ } else if (precisely_equal(otherT, 1)) {
+ otherT = 1;
+ }
+ span.fOtherT = otherT;
+ span.fOtherIndex = otherIndex;
+}
+
+void SkOpSegment::addQuad(const SkPoint pts[3], bool operand, bool evenOdd) {
+ init(pts, SkPath::kQuad_Verb, operand, 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;
}
- } while ((test = test->upCast()->next()));
- SkOpPtT* result;
- if (existing && existing->contains(opp)) {
- result = existing->ptT();
- } else {
- result = this->addT(t, SkOpSegment::kNoAlias, allocator);
- }
- SkASSERT(result);
- return result;
-}
-
-SkOpAngle* SkOpSegment::addSingletonAngleDown(SkOpSegment** otherPtr, SkOpAngle** anglePtr,
- SkChunkAlloc* allocator) {
- SkOpSpan* startSpan = fTail.prev();
- SkASSERT(startSpan);
- SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
- *anglePtr = angle;
- angle->set(&fTail, startSpan);
- fTail.setFromAngle(angle);
- SkOpSegment* other = NULL; // these initializations silence a release build warning
- SkOpSpan* oStartSpan = NULL;
- SkOpSpanBase* oEndSpan = NULL;
- SkOpPtT* ptT = fTail.ptT(), * startPtT = ptT;
- while ((ptT = ptT->next()) != startPtT) {
- other = ptT->segment();
- oStartSpan = ptT->span()->upCastable();
- if (oStartSpan && oStartSpan->windValue()) {
- oEndSpan = oStartSpan->next();
+ 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;
}
- oEndSpan = ptT->span();
- oStartSpan = oEndSpan->prev();
- if (oStartSpan && oStartSpan->windValue()) {
- break;
- }
- }
- SkOpAngle* oAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
- oAngle->set(oStartSpan, oEndSpan);
- oStartSpan->setToAngle(oAngle);
+ 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, SkChunkAlloc* allocator) {
+ return &oAngle;
+}
+
+SkOpAngle* SkOpSegment::addSingletonAngles(int step) {
SkOpSegment* other;
SkOpAngle* angle, * otherAngle;
if (step > 0) {
- otherAngle = addSingletonAngleUp(&other, &angle, allocator);
+ otherAngle = addSingletonAngleUp(&other, &angle);
} else {
- otherAngle = addSingletonAngleDown(&other, &angle, allocator);
+ otherAngle = addSingletonAngleDown(&other, &angle);
}
angle->insert(otherAngle);
return angle;
}
-SkOpAngle* SkOpSegment::addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** anglePtr,
- SkChunkAlloc* allocator) {
- SkOpSpanBase* endSpan = fHead.next();
- SkASSERT(endSpan);
- SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
- *anglePtr = angle;
- angle->set(&fHead, endSpan);
- fHead.setToAngle(angle);
- SkOpSegment* other = NULL; // these initializations silence a release build warning
- SkOpSpan* oStartSpan = NULL;
- SkOpSpanBase* oEndSpan = NULL;
- SkOpPtT* ptT = fHead.ptT(), * startPtT = ptT;
- while ((ptT = ptT->next()) != startPtT) {
- other = ptT->segment();
- oEndSpan = ptT->span();
- oStartSpan = oEndSpan->prev();
- if (oStartSpan && oStartSpan->windValue()) {
+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
+
+// no need to be tricky; insert in normal T order
+// resolve overlapping ts when considering coincidence later
+
+ // add non-coincident intersection. Resulting edges are sorted in T.
+int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) {
+ 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;
+ 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.
+ // This could later limit segment tests to the two adjacent
+ // neighbors, although it doesn't help with determining which
+ // circular direction to go in.
+ const SkOpSpan& span = fTs[index];
+ if (newT < span.fT) {
+ insertedAt = index;
break;
}
- oStartSpan = oEndSpan->upCastable();
- if (oStartSpan && oStartSpan->windValue()) {
- oEndSpan = oStartSpan->next();
+ if (newT == span.fT) {
+ if (pt == span.fPt) {
+ insertedAt = index;
+ break;
+ }
+ if ((pt == firstPt && newT == 0) || (span.fPt == lastPt && newT == 1)) {
+ insertedAt = index;
+ break;
+ }
+ }
+ }
+ SkOpSpan* span;
+ if (insertedAt >= 0) {
+ span = fTs.insert(insertedAt);
+ } else {
+ insertedAt = tCount;
+ span = fTs.append();
+ }
+ span->fT = newT;
+ span->fOtherT = -1;
+ span->fOther = other;
+ span->fPt = pt;
+#if 0
+ // cubics, for instance, may not be exact enough to satisfy this check (e.g., cubicOp69d)
+ SkASSERT(approximately_equal(xyAtT(newT).fX, pt.fX)
+ && approximately_equal(xyAtT(newT).fY, pt.fY));
+#endif
+ span->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;
+ if ((span->fDone = newT == 1)) {
+ ++fDoneSpans;
+ }
+ setSpanFlags(pt, newT, span);
+ return insertedAt;
+}
+
+void SkOpSegment::setSpanFlags(const SkPoint& pt, double newT, SkOpSpan* span) {
+ int less = -1;
+// FIXME: note that this relies on spans being a continguous array
+// find range of spans with nearly the same point as this one
+ // FIXME: SkDPoint::ApproximatelyEqual is better but breaks tests at the moment
+ 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;
+ }
+ }
+ --less;
+ }
+ int more = 1;
+ // FIXME: SkDPoint::ApproximatelyEqual is better but breaks tests at the moment
+ 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;
+ }
+ }
+ ++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;
+ }
+ if (precisely_negative(span[more].fT - span[less].fT)) {
+ return;
+ }
+// 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;
+}
+
+void SkOpSegment::resetSpanFlags() {
+ fSmall = fTiny = false;
+ fDoneSpans = 0;
+ int start = 0;
+ int last = this->count() - 1;
+ do {
+ SkOpSpan* startSpan = &this->fTs[start];
+ double startT = startSpan->fT;
+ startSpan->fSmall = startSpan->fTiny = false; // sets range initial
+ bool terminus = startT == 1;
+ if ((startSpan->fDone = !startSpan->fWindValue | terminus)) {
+ ++fDoneSpans;
+ }
+ ++start; // range initial + 1
+ if (terminus) {
+ continue;
+ }
+ const SkPoint& pt = startSpan->fPt;
+ int end = start; // range initial + 1
+ while (end <= last) {
+ const SkOpSpan& endSpan = this->span(end);
+ if (!AlmostEqualUlps(endSpan.fPt, pt)) {
+ break;
+ }
+ if (fVerb == SkPath::kCubic_Verb) {
+ double tMid = (startSpan->fT + endSpan.fT) / 2;
+ SkDPoint midEndPt = dcubic_xy_at_t(fPts, tMid);
+ if (!midEndPt.approximatelyEqual(xyAtT(startSpan))) {
+ break;
+ }
+ }
+ ++end;
+ }
+ if (start == end) { // end == range final + 1
+ continue;
+ }
+ while (--end >= start) { // end == range final
+ const SkOpSpan& endSpan = this->span(end);
+ const SkOpSpan& priorSpan = this->span(end - 1);
+ if (endSpan.fPt != priorSpan.fPt || endSpan.fT != priorSpan.fT) {
+ break; // end == range final + 1
+ }
+ }
+ if (end < start) { // end == range final + 1
+ continue;
+ }
+ int index = start - 1; // index == range initial
+ start = end; // start = range final + 1
+ const SkOpSpan& nextSpan = this->span(end);
+ if (precisely_negative(nextSpan.fT - startSpan->fT)) {
+ while (++index < end) {
+ startSpan = &this->fTs[index];
+ startSpan->fSmall = startSpan->fTiny = false; // sets range initial + 1
+ if ((startSpan->fDone = !startSpan->fWindValue)) {
+ ++fDoneSpans;
+ }
+ }
+ continue;
+ }
+ if (!startSpan->fWindValue) {
+ --fDoneSpans; // added back below
+ }
+ bool tiny = nextSpan.fPt == startSpan->fPt;
+ do {
+ fSmall = startSpan->fSmall = true; // sets range initial
+ fTiny |= startSpan->fTiny = tiny;
+ startSpan->fDone = true;
+ ++fDoneSpans;
+ startSpan = &this->fTs[++index];
+ } while (index < end); // loop through tiny small range end (last)
+ } while (start <= last);
+}
+
+// set spans from start to end to decrement by one
+// note this walks other backwards
+// FIXME: there's probably an edge case that can be constructed where
+// two span in one segment are separated by float epsilon on one span but
+// not the other, if one segment is very small. For this
+// case the counts asserted below may or may not be enough to separate the
+// spans. Even if the counts work out, what if the spans aren't correctly
+// sorted? It feels better in such a case to match the span's other span
+// pointer since both coincident segments must contain the same spans.
+// FIXME? It seems that decrementing by one will fail for complex paths that
+// have three or more coincident edges. Shouldn't this subtract the difference
+// between the winding values?
+/* |--> |-->
+this 0>>>>1>>>>2>>>>3>>>4 0>>>>1>>>>2>>>>3>>>4 0>>>>1>>>>2>>>>3>>>4
+other 2<<<<1<<<<0 2<<<<1<<<<0 2<<<<1<<<<0
+ ^ ^ <--| <--|
+ startPt endPt test/oTest first pos test/oTest final pos
+*/
+void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other) {
+ bool binary = fOperand != other->fOperand;
+ int index = 0;
+ while (startPt != fTs[index].fPt) {
+ SkASSERT(index < fTs.count());
+ ++index;
+ }
+ 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 || precisely_equal(oStartT, other->fTs[oIndex].fT)) {
+ if (!oIndex) {
+ return; // tiny spans may move in the wrong direction
+ }
+ }
+ 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);
+ decrement = test->fWindValue && oTest->fWindValue;
+ track = test->fWindValue || oTest->fWindValue;
+ bigger = test->fWindValue >= oTest->fWindValue;
+ testPt = &test->fPt;
+ double testT = test->fT;
+ oTestPt = &oTest->fPt;
+ double oTestT = oTest->fT;
+ do {
+ if (decrement) {
+ if (binary && bigger) {
+ test->fOppValue--;
+ } else {
+ decrementSpan(test);
+ }
+ } else if (track) {
+ TrackOutsidePair(&outsidePts, *testPt, *oTestPt);
+ }
+ SkASSERT(index < fTs.count() - 1);
+ test = &fTs[++index];
+ } while (*testPt == test->fPt || precisely_equal(testT, test->fT));
+ originalWindValue = oTest->fWindValue;
+ do {
+ SkASSERT(oTest->fT < 1);
+ 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;
+ }
+ oFoundEnd |= endPt == oTest->fPt;
+ oTest = &other->fTs[--oIndex];
+ } 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;
+ }
+ }
+ oFoundEnd = endPt == oTest->fPt;
+ 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);
+ if (outCount > 2) {
+ addCancelOutsides(outsidePts[outCount - 2], outsidePts[outCount - 1], other);
+ }
+ }
+ if (!other->done() && oOutsidePts.count()) {
+ other->addCancelOutsides(oOutsidePts[0], oOutsidePts[1], this);
+ }
+ setCoincidentRange(startPt, endPt, other);
+ other->setCoincidentRange(startPt, endPt, this);
+}
+
+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(this, pt, newT);
+ SkOpSpan* span = &fTs[result];
+ 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];
+ int idx;
+ int start, end;
+ if (span->fT == 0) {
+ idx = 0;
+ span = &fTs[0];
+ do {
+ if (span->fToAngle) {
+ SkASSERT(span->fToAngle->loopCount() == 2);
+ SkASSERT(!span->fFromAngle);
+ span->fFromAngle = span->fToAngle->next();
+ return;
+ }
+ span = &fTs[++idx];
+ } while (span->fT == 0);
+ SkASSERT(!fTs[0].fTiny && fTs[idx].fT > 0);
+ addStartSpan(idx);
+ start = 0;
+ end = idx;
+ } else {
+ idx = count() - 1;
+ span = &fTs[idx];
+ do {
+ if (span->fFromAngle) {
+ SkASSERT(span->fFromAngle->loopCount() == 2);
+ SkASSERT(!span->fToAngle);
+ span->fToAngle = span->fFromAngle->next();
+ return;
+ }
+ span = &fTs[--idx];
+ } while (span->fT == 1);
+ SkASSERT(!fTs[idx].fTiny && fTs[idx].fT < 1);
+ addEndSpan(++idx);
+ start = idx;
+ end = count();
+ }
+ SkOpSegment* other;
+ SkOpSpan* oSpan;
+ index = start;
+ do {
+ span = &fTs[index];
+ other = span->fOther;
+ int oFrom = span->fOtherIndex;
+ oSpan = &other->fTs[oFrom];
+ if (oSpan->fT < 1 && oSpan->fWindValue) {
break;
}
- }
- SkOpAngle* oAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
- oAngle->set(oEndSpan, oStartSpan);
- oEndSpan->setFromAngle(oAngle);
- *otherPtr = other;
- return oAngle;
-}
-
-SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* allocator) {
+ if (oSpan->fT == 0) {
+ continue;
+ }
+ oFrom = other->nextExactSpan(oFrom, -1);
+ SkOpSpan* oFromSpan = &other->fTs[oFrom];
+ SkASSERT(oFromSpan->fT < 1);
+ if (oFromSpan->fWindValue) {
+ break;
+ }
+ } while (++index < end);
+ SkOpAngle* angle, * oAngle;
+ if (span->fT == 0) {
+ SkASSERT(span->fOtherIndex - 1 >= 0);
+ SkASSERT(span->fOtherT == 1);
+ SkDEBUGCODE(int oPriorIndex = other->nextExactSpan(span->fOtherIndex, -1));
+ SkDEBUGCODE(const SkOpSpan& oPrior = other->span(oPriorIndex));
+ 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();
- SkPoint pt = this->ptAtT(t);
- SkOpSpanBase* span = &fHead;
+ 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::alignRange(int lower, int upper,
+ const SkOpSegment* other, int oLower, int oUpper) {
+ for (int oIndex = oLower; oIndex <= oUpper; ++oIndex) {
+ const SkOpSpan& oSpan = other->span(oIndex);
+ const SkOpSegment* oOther = oSpan.fOther;
+ if (oOther == this) {
+ continue;
+ }
+ SkOpSpan* matchSpan;
+ int matchIndex;
+ const SkOpSpan* refSpan;
+ for (int iIndex = lower; iIndex <= upper; ++iIndex) {
+ const SkOpSpan& iSpan = this->span(iIndex);
+ const SkOpSegment* iOther = iSpan.fOther;
+ if (iOther == other) {
+ continue;
+ }
+ if (iOther == oOther) {
+ goto nextI;
+ }
+ }
+ {
+ // oSpan does not have a match in this
+ int iCount = this->count();
+ const SkOpSpan* iMatch = NULL;
+ double iMatchTDiff;
+ matchIndex = -1;
+ for (int iIndex = 0; iIndex < iCount; ++iIndex) {
+ const SkOpSpan& iSpan = this->span(iIndex);
+ const SkOpSegment* iOther = iSpan.fOther;
+ if (iOther != oOther) {
+ continue;
+ }
+ double testTDiff = fabs(iSpan.fOtherT - oSpan.fOtherT);
+ if (!iMatch || testTDiff < iMatchTDiff) {
+ matchIndex = iIndex;
+ iMatch = &iSpan;
+ iMatchTDiff = testTDiff;
+ }
+ }
+ if (matchIndex < 0) {
+ continue; // the entry is missing, & will be picked up later (FIXME: fix it here?)
+ }
+ matchSpan = &this->fTs[matchIndex];
+ refSpan = &this->span(lower);
+ if (!SkDPoint::ApproximatelyEqual(matchSpan->fPt, refSpan->fPt)) {
+ goto nextI;
+ }
+ if (matchIndex != lower - 1 && matchIndex != upper + 1) {
+ // the consecutive spans need to be rearranged to get the missing one close
+ continue; // FIXME: more work to do
+ }
+ }
+ {
+ this->fixOtherTIndex();
+ SkScalar newT;
+ if (matchSpan->fT != 0 && matchSpan->fT != 1) {
+ newT = matchSpan->fT = refSpan->fT;
+ matchSpan->fOther->fTs[matchSpan->fOtherIndex].fOtherT = refSpan->fT;
+ } else { // leave span at the start or end there and adjust the neighbors
+ newT = matchSpan->fT;
+ for (int iIndex = lower; iIndex <= upper; ++iIndex) {
+ matchSpan = &this->fTs[iIndex];
+ matchSpan->fT = newT;
+ matchSpan->fOther->fTs[matchSpan->fOtherIndex].fOtherT = newT;
+ }
+ }
+ this->resetSpanFlags(); // fix up small / tiny / done
+ // align ts of other ranges with adjacent spans that match the aligned points
+ lower = SkTMin(lower, matchIndex);
+ while (lower > 0) {
+ const SkOpSpan& span = this->span(lower - 1);
+ if (span.fT != newT) {
+ break;
+ }
+ --lower;
+ }
+ upper = SkTMax(upper, matchIndex);
+ int last = this->count() - 1;
+ while (upper < last) {
+ const SkOpSpan& span = this->span(upper + 1);
+ if (span.fT != newT) {
+ break;
+ }
+ ++upper;
+ }
+ for (int iIndex = lower; iIndex <= upper; ++iIndex) {
+ const SkOpSpan& span = this->span(iIndex);
+ SkOpSegment* aOther = span.fOther;
+ int aLower = span.fOtherIndex;
+ SkScalar aT = span.fOtherT;
+ bool aResetFlags = false;
+ while (aLower > 0) {
+ SkOpSpan* aSpan = &aOther->fTs[aLower - 1];
+ for (int iIndex = lower; iIndex <= upper; ++iIndex) {
+ if (aSpan->fPt == this->fTs[iIndex].fPt) {
+ goto matchFound;
+ }
+ }
+ break;
+ matchFound:
+ --aLower;
+ }
+ int aUpper = span.fOtherIndex;
+ int aLast = aOther->count() - 1;
+ while (aUpper < aLast) {
+ SkOpSpan* aSpan = &aOther->fTs[aUpper + 1];
+ for (int iIndex = lower; iIndex <= upper; ++iIndex) {
+ if (aSpan->fPt == this->fTs[iIndex].fPt) {
+ goto matchFound2;
+ }
+ }
+ break;
+ matchFound2:
+ ++aUpper;
+ }
+ if (aOther->fTs[aLower].fT == 0) {
+ aT = 0;
+ } else if (aOther->fTs[aUpper].fT == 1) {
+ aT = 1;
+ }
+ bool aFixed = false;
+ for (int aIndex = aLower; aIndex <= aUpper; ++aIndex) {
+ SkOpSpan* aSpan = &aOther->fTs[aIndex];
+ if (aSpan->fT == aT) {
+ continue;
+ }
+ SkASSERT(way_roughly_equal(aSpan->fT, aT));
+ if (!aFixed) {
+ aOther->fixOtherTIndex();
+ aFixed = true;
+ }
+ aSpan->fT = aT;
+ aSpan->fOther->fTs[aSpan->fOtherIndex].fOtherT = aT;
+ aResetFlags = true;
+ }
+ if (aResetFlags) {
+ aOther->resetSpanFlags();
+ }
+ }
+ }
+nextI: ;
+ }
+}
+
+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 {
- SkOpPtT* result = span->ptT();
- if (t == result->fT) {
- return result;
- }
- if (this->match(result, this, t, pt)) {
- // see if any existing alias matches segment, pt, and t
- SkOpPtT* loop = result->next();
- bool duplicatePt = false;
- while (loop != result) {
- bool ptMatch = loop->fPt == pt;
- if (loop->segment() == this && loop->fT == t && ptMatch) {
- return result;
- }
- duplicatePt |= ptMatch;
- loop = loop->next();
- }
- if (kNoAlias == allowAlias) {
- return result;
- }
- SkOpPtT* alias = SkOpTAllocator<SkOpPtT>::Allocate(allocator);
- alias->init(result->span(), t, pt, duplicatePt);
- result->insert(alias);
- result->span()->unaligned();
- this->debugValidate();
-#if DEBUG_ADD_T
- SkDebugf("%s alias t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
- alias->segment()->debugID(), alias->span()->debugID());
-#endif
- return alias;
- }
- if (t < result->fT) {
- SkOpSpan* prev = result->span()->prev();
- SkOpSpan* span = insert(prev, allocator);
- span->init(this, prev, t, pt);
- this->debugValidate();
-#if DEBUG_ADD_T
- SkDebugf("%s insert t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
- span->segment()->debugID(), span->debugID());
-#endif
- return span->ptT();
- }
- SkASSERT(span != &fTail);
- } while ((span = span->upCast()->next()));
- SkASSERT(0);
- return NULL;
-}
-
-// choose a solitary t and pt value; remove aliases; align the opposite ends
-void SkOpSegment::align() {
- debugValidate();
- SkOpSpanBase* span = &fHead;
- if (!span->aligned()) {
- span->alignEnd(0, fPts[0]);
- }
- while ((span = span->upCast()->next())) {
- if (span == &fTail) {
+ SkOpSpan& span = this->fTs[--last];
+ if (span.fT != 1 && !span.fSmall) {
break;
}
- span->align();
- }
- if (!span->aligned()) {
- span->alignEnd(1, fPts[SkPathOpsVerbToPoints(fVerb)]);
- }
- debugValidate();
-}
-
-bool SkOpSegment::BetweenTs(const SkOpSpanBase* lesser, double testT,
- const SkOpSpanBase* greater) {
- if (lesser->t() > greater->t()) {
- SkTSwap<const SkOpSpanBase*>(lesser, greater);
- }
- return approximately_between(lesser->t(), testT, greater->t());
-}
-
-void SkOpSegment::calcAngles(SkChunkAlloc* allocator) {
- bool activePrior = !fHead.isCanceled();
- if (activePrior && !fHead.simple()) {
- addStartSpan(allocator);
- }
- SkOpSpan* prior = &fHead;
- SkOpSpanBase* spanBase = fHead.next();
- while (spanBase != &fTail) {
- if (activePrior) {
- SkOpAngle* priorAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
- priorAngle->set(spanBase, prior);
- spanBase->setFromAngle(priorAngle);
- }
- SkOpSpan* span = spanBase->upCast();
- bool active = !span->isCanceled();
- SkOpSpanBase* next = span->next();
- if (active) {
- SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
- angle->set(span, next);
- span->setToAngle(angle);
- }
- activePrior = active;
- prior = span;
- spanBase = next;
- }
- if (activePrior && !fTail.simple()) {
- addEndSpan(allocator);
- }
-}
-
-void SkOpSegment::checkAngleCoin(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
- SkOpSpanBase* base = &fHead;
- SkOpSpan* span;
+ span.fCoincident = true;
+ } while (true);
+ int oIndex = other->count();
do {
- SkOpAngle* angle = base->fromAngle();
- if (angle && angle->fCheckCoincidence) {
- angle->checkNearCoincidence();
- }
- if (base->final()) {
- break;
- }
- span = base->upCast();
- angle = span->toAngle();
- if (angle && angle->fCheckCoincidence) {
- angle->checkNearCoincidence();
- }
- } while ((base = span->next()));
-}
-
-// from http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
-bool SkOpSegment::clockwise(const SkOpSpanBase* start, const SkOpSpanBase* end, bool* swap) const {
- SkASSERT(fVerb != SkPath::kLine_Verb);
- SkPoint edge[4];
- if (fVerb == SkPath::kCubic_Verb) {
- double startT = start->t();
- double endT = end->t();
- bool flip = startT > endT;
- SkDCubic cubic;
- cubic.set(fPts);
- double inflectionTs[2];
- int inflections = cubic.findInflections(inflectionTs);
- for (int index = 0; index < inflections; ++index) {
- double inflectionT = inflectionTs[index];
- if (between(startT, inflectionT, endT)) {
- if (flip) {
- if (inflectionT != endT) {
- startT = inflectionT;
+ 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);
+}
+
+bool SkOpSegment::bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* indexPtr,
+ SkTArray<SkPoint, true>* outsideTs) {
+ int index = *indexPtr;
+ int oWindValue = oTest.fWindValue;
+ int oOppValue = oTest.fOppValue;
+ if (binary) {
+ SkTSwap<int>(oWindValue, oOppValue);
+ }
+ SkOpSpan* const test = &fTs[index];
+ SkOpSpan* end = test;
+ const SkPoint& oStartPt = oTest.fPt;
+ do {
+ if (end->fDone && !end->fTiny && !end->fSmall) { // extremely large paths trigger this
+ return false;
+ }
+ if (bumpSpan(end, oWindValue, oOppValue)) {
+ TrackOutside(outsideTs, oStartPt);
+ }
+ end = &fTs[++index];
+ } while ((end->fPt == test->fPt || precisely_equal(end->fT, test->fT)) && end->fT < 1);
+ *indexPtr = index;
+ return true;
+}
+
+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)
+// and walk other conditionally -- hoping that it catches up in the end
+bool SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, int* oIndexPtr,
+ SkTArray<SkPoint, true>* oOutsidePts, const SkPoint& oEndPt) {
+ int oIndex = *oIndexPtr;
+ SkOpSpan* const oTest = &fTs[oIndex];
+ SkOpSpan* oEnd = oTest;
+ const SkPoint& oStartPt = oTest->fPt;
+ double oStartT = oTest->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);
+ }
+#endif
+ bool foundEnd = false;
+ while (oStartPt == oEnd->fPt || precisely_equal(oStartT, oEnd->fT)) {
+ foundEnd |= oEndPt == oEnd->fPt;
+ zeroSpan(oEnd);
+ oEnd = &fTs[++oIndex];
+ }
+ *oIndexPtr = oIndex;
+ return foundEnd;
+}
+
+// FIXME: need to test this case:
+// contourA has two segments that are coincident
+// contourB has two segments that are coincident in the same place
+// each ends up with +2/0 pairs for winding count
+// since logic below doesn't transfer count (only increments/decrements) can this be
+// resolved to +4/0 ?
+
+// set spans from start to end to increment the greater by one and decrement
+// the lesser
+bool SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
+ SkOpSegment* other) {
+ bool binary = fOperand != other->fOperand;
+ int index = 0;
+ while (startPt != fTs[index].fPt) {
+ SkASSERT(index < fTs.count());
+ ++index;
+ }
+ double startT = fTs[index].fT;
+ while (index > 0 && precisely_equal(fTs[index - 1].fT, startT)) {
+ --index;
+ }
+ int oIndex = 0;
+ while (startPt != other->fTs[oIndex].fPt) {
+ SkASSERT(oIndex < other->fTs.count());
+ ++oIndex;
+ }
+ double oStartT = other->fTs[oIndex].fT;
+ while (oIndex > 0 && precisely_equal(other->fTs[oIndex - 1].fT, oStartT)) {
+ --oIndex;
+ }
+ SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
+ SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
+ SkOpSpan* test = &fTs[index];
+ const SkPoint* testPt = &test->fPt;
+ double testT = test->fT;
+ SkOpSpan* oTest = &other->fTs[oIndex];
+ const SkPoint* oTestPt = &oTest->fPt;
+ // paths with extreme data will fail this test and eject out of pathops altogether later on
+ // SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
+ do {
+ SkASSERT(test->fT < 1);
+ if (oTest->fT == 1) {
+ // paths with extreme data may be so mismatched that we fail here
+ return false;
+ }
+
+ // consolidate the winding count even if done
+ bool foundEnd = false;
+ if ((test->fWindValue == 0 && test->fOppValue == 0)
+ || (oTest->fWindValue == 0 && oTest->fOppValue == 0)) {
+ SkDEBUGCODE(int firstWind = test->fWindValue);
+ SkDEBUGCODE(int firstOpp = test->fOppValue);
+ do {
+ SkASSERT(firstWind == fTs[index].fWindValue);
+ SkASSERT(firstOpp == fTs[index].fOppValue);
+ ++index;
+ SkASSERT(index < fTs.count());
+ } while (*testPt == fTs[index].fPt);
+ SkDEBUGCODE(firstWind = oTest->fWindValue);
+ SkDEBUGCODE(firstOpp = oTest->fOppValue);
+ do {
+ SkASSERT(firstWind == other->fTs[oIndex].fWindValue);
+ SkASSERT(firstOpp == other->fTs[oIndex].fOppValue);
+ ++oIndex;
+ SkASSERT(oIndex < other->fTs.count());
+ } while (*oTestPt == other->fTs[oIndex].fPt);
+ } else {
+ if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
+ if (!bumpCoincidentThis(*oTest, binary, &index, &outsidePts)) {
+ return false;
+ }
+ foundEnd = other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts, endPt);
+ } else {
+ if (!other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts)) {
+ return false;
+ }
+ foundEnd = bumpCoincidentOther(*oTest, &index, &outsidePts, endPt);
+ }
+ }
+ test = &fTs[index];
+ testPt = &test->fPt;
+ testT = test->fT;
+ oTest = &other->fTs[oIndex];
+ oTestPt = &oTest->fPt;
+ if (endPt == *testPt || precisely_equal(endT, testT)) {
+ break;
+ }
+ if (0 && foundEnd) { // FIXME: this is likely needed but wait until a test case triggers it
+ break;
+ }
+// SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
+ } while (endPt != *oTestPt);
+ // 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;
+ do {
+ if (test->fWindValue || test->fOppValue) {
+ test->fWindValue = lastWind;
+ test->fOppValue = lastOpp;
+ if (zero) {
+ SkASSERT(!test->fDone);
+ test->fDone = true;
+ ++fDoneSpans;
+ }
+ }
+ 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;
+ }
+ if (++oPeekIndex == oCount) {
+ break;
+ }
+ oPeek = &other->fTs[oPeekIndex];
+ } while (endPt == oPeek->fPt);
+ }
+ if (success) {
+ do {
+ if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
+ if (other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts, endPt)) {
+ break;
}
} else {
- if (inflectionT != startT) {
- endT = inflectionT;
+ if (!other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts)) {
+ return false;
}
}
- }
- }
- SkDCubic part = cubic.subDivide(startT, endT);
- for (int index = 0; index < 4; ++index) {
- edge[index] = part[index].asSkPoint();
- }
+ oTest = &other->fTs[oIndex];
+ oTestPt = &oTest->fPt;
+ } while (endPt != *oTestPt);
+ }
+ }
+ int outCount = outsidePts.count();
+ if (!done() && outCount) {
+ addCoinOutsides(outsidePts[0], endPt, other);
+ }
+ if (!other->done() && oOutsidePts.count()) {
+ other->addCoinOutsides(oOutsidePts[0], endPt, this);
+ }
+ setCoincidentRange(startPt, endPt, other);
+ other->setCoincidentRange(startPt, endPt, this);
+ return true;
+}
+
+// FIXME: this doesn't prevent the same span from being added twice
+// fix in caller, SkASSERT here?
+// FIXME: this may erroneously reject adds for cubic loops
+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];
+ if (!approximately_negative(span.fT - t)) {
+ break;
+ }
+ if (span.fOther == other) {
+ bool tsMatch = approximately_equal(span.fT, t);
+ bool otherTsMatch = approximately_equal(span.fOtherT, otherT);
+ // FIXME: add cubic loop detecting logic here
+ // if fLoop bit is set on span, that could be enough if addOtherT copies the bit
+ // or if a new bit is added ala fOtherLoop
+ if (tsMatch || otherTsMatch) {
+#if DEBUG_ADD_T_PAIR
+ SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
+ __FUNCTION__, fID, t, other->fID, otherT);
+#endif
+ return NULL;
+ }
+ }
+ }
+ int oCount = other->count();
+ for (int oIndex = 0; oIndex < oCount; ++oIndex) {
+ const SkOpSpan& oSpan = other->span(oIndex);
+ if (!approximately_negative(oSpan.fT - otherT)) {
+ break;
+ }
+ if (oSpan.fOther == this) {
+ bool otherTsMatch = approximately_equal(oSpan.fT, otherT);
+ bool tsMatch = approximately_equal(oSpan.fOtherT, t);
+ if (otherTsMatch || tsMatch) {
+#if DEBUG_ADD_T_PAIR
+ SkDebugf("%s addTPair other duplicate this=%d %1.9g other=%d %1.9g\n",
+ __FUNCTION__, fID, t, other->fID, otherT);
+#endif
+ return NULL;
+ }
+ }
+ }
+#if DEBUG_ADD_T_PAIR
+ SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
+ __FUNCTION__, fID, t, other->fID, otherT);
+#endif
+ SkASSERT(other != this);
+ int insertedAt = addT(other, pt, t);
+ int otherInsertedAt = other->addT(this, pt2, otherT);
+ this->addOtherT(insertedAt, otherT, otherInsertedAt);
+ other->addOtherT(otherInsertedAt, t, insertedAt);
+ this->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;
+ }
+ // if the newly inserted spans match a neighbor on one but not the other, make them agree
+ int lower = this->nextExactSpan(insertedAt, -1) + 1;
+ int upper = this->nextExactSpan(insertedAt, 1) - 1;
+ if (upper < 0) {
+ upper = this->count() - 1;
+ }
+ int oLower = other->nextExactSpan(otherInsertedAt, -1) + 1;
+ int oUpper = other->nextExactSpan(otherInsertedAt, 1) - 1;
+ if (oUpper < 0) {
+ oUpper = other->count() - 1;
+ }
+ if (lower == upper && oLower == oUpper) {
+ return &span;
+ }
+#if DEBUG_CONCIDENT
+ SkDebugf("%s id=%d lower=%d upper=%d other=%d oLower=%d oUpper=%d\n", __FUNCTION__,
+ debugID(), lower, upper, other->debugID(), oLower, oUpper);
+#endif
+ // find the nearby spans in one range missing in the other
+ this->alignRange(lower, upper, other, oLower, oUpper);
+ other->alignRange(oLower, oUpper, this, lower, upper);
+ return &span;
+}
+
+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 {
+ const SkPoint midPt = ptAtT(midT);
+ SkPathOpsBounds bounds;
+ bounds.set(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
+ bounds.sort();
+ return bounds.almostContains(midPt);
+}
+
+bool SkOpSegment::betweenTs(int lesser, double testT, int greater) const {
+ if (lesser > greater) {
+ SkTSwap<int>(lesser, greater);
+ }
+ return approximately_between(fTs[lesser].fT, testT, fTs[greater].fT);
+}
+
+// in extreme cases (like the buffer overflow test) return false to abort
+// for now, if one t value represents two different points, then the values are too extreme
+// to generate meaningful results
+bool SkOpSegment::calcAngles() {
+ int spanCount = fTs.count();
+ if (spanCount <= 2) {
+ return spanCount == 2;
+ }
+ int index = 1;
+ const SkOpSpan* firstSpan = &fTs[index];
+ int activePrior = checkSetAngle(0);
+ const SkOpSpan* span = &fTs[0];
+ if (firstSpan->fT == 0 || span->fTiny || span->fOtherT != 1 || span->fOther->multipleEnds()) {
+ index = findStartSpan(0); // curve start intersects
+ if (fTs[index].fT == 0) {
+ return false;
+ }
+ 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 {
- subDivide(start, end, edge);
- }
- bool sumSet = false;
- int points = SkPathOpsVerbToPoints(fVerb);
- double sum = (edge[0].fX - edge[points].fX) * (edge[0].fY + edge[points].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();
- } else {
- SkDQuad quad;
- quad.set(edge);
- *swap = sum > 0 && !quad.monotonicInY();
- }
- return sum <= 0;
-}
-
-void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
- SkOpAngle::IncludeType includeType) {
- const SkOpSegment* baseSegment = baseAngle->segment();
- int sumMiWinding = baseSegment->updateWindingReverse(baseAngle);
- int sumSuWinding;
- bool binary = includeType >= SkOpAngle::kBinarySingle;
- if (binary) {
- sumSuWinding = baseSegment->updateOppWindingReverse(baseAngle);
- if (baseSegment->operand()) {
- SkTSwap<int>(sumMiWinding, sumSuWinding);
- }
- }
- SkOpSegment* nextSegment = nextAngle->segment();
- int maxWinding, sumWinding;
- SkOpSpanBase* last;
- if (binary) {
- int oppMaxWinding, oppSumWinding;
- nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
- &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
- last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
- nextAngle);
- } else {
- nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
- &maxWinding, &sumWinding);
- last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
- }
- nextAngle->setLastMarked(last);
-}
-
-void SkOpSegment::ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
- SkOpAngle::IncludeType includeType) {
- const SkOpSegment* baseSegment = baseAngle->segment();
- int sumMiWinding = baseSegment->updateWinding(baseAngle);
- int sumSuWinding;
- bool binary = includeType >= SkOpAngle::kBinarySingle;
- if (binary) {
- sumSuWinding = baseSegment->updateOppWinding(baseAngle);
- if (baseSegment->operand()) {
- SkTSwap<int>(sumMiWinding, sumSuWinding);
- }
- }
- SkOpSegment* nextSegment = nextAngle->segment();
- int maxWinding, sumWinding;
- SkOpSpanBase* last;
- if (binary) {
- int oppMaxWinding, oppSumWinding;
- nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
- &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
- last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
- nextAngle);
- } else {
- nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
- &maxWinding, &sumWinding);
- last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
- }
- nextAngle->setLastMarked(last);
+ 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);
+ }
+ return true;
+}
+
+int SkOpSegment::checkSetAngle(int tIndex) const {
+ const SkOpSpan* span = &fTs[tIndex];
+ while (span->fTiny /* || span->fSmall */) {
+ span = &fTs[++tIndex];
+ }
+ return isCanceled(tIndex) ? -1 : tIndex;
}
// at this point, the span is already ordered, or unorderable
-int SkOpSegment::computeSum(SkOpSpanBase* start, SkOpSpanBase* end,
- SkOpAngle::IncludeType includeType) {
+int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType includeType) {
SkASSERT(includeType != SkOpAngle::kUnaryXor);
- SkOpAngle* firstAngle = this->spanToAngle(end, start);
+ SkOpAngle* firstAngle = spanToAngle(endIndex, startIndex);
if (NULL == firstAngle || NULL == firstAngle->next()) {
return SK_NaN32;
}
@@ -614,7 +1898,7 @@
baseAngle = NULL;
continue;
}
- int testWinding = angle->starter()->windSum();
+ int testWinding = angle->segment()->windSum(angle);
if (SK_MinS32 != testWinding) {
baseAngle = angle;
tryReverse = true;
@@ -622,10 +1906,10 @@
}
if (baseAngle) {
ComputeOneSum(baseAngle, angle, includeType);
- baseAngle = SK_MinS32 != angle->starter()->windSum() ? angle : NULL;
+ baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
}
} while (next != firstAngle);
- if (baseAngle && SK_MinS32 == firstAngle->starter()->windSum()) {
+ if (baseAngle && SK_MinS32 == firstAngle->segment()->windSum(firstAngle)) {
firstAngle = baseAngle;
tryReverse = true;
}
@@ -641,41 +1925,137 @@
baseAngle = NULL;
continue;
}
- int testWinding = angle->starter()->windSum();
+ int testWinding = angle->segment()->windSum(angle);
if (SK_MinS32 != testWinding) {
baseAngle = angle;
continue;
}
if (baseAngle) {
ComputeOneSumReverse(baseAngle, angle, includeType);
- baseAngle = SK_MinS32 != angle->starter()->windSum() ? angle : NULL;
+ baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
}
} while (prior != firstAngle);
}
- return start->starter(end)->windSum();
-}
-
-SkOpSpan* SkOpSegment::crossedSpanY(const SkPoint& basePt, double mid, bool opp, bool current,
- SkScalar* bestY, double* hitT, bool* hitSomething, bool* vertical) {
+ int minIndex = SkMin32(startIndex, endIndex);
+ return windSum(minIndex);
+}
+
+void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
+ SkOpAngle::IncludeType includeType) {
+ const SkOpSegment* baseSegment = baseAngle->segment();
+ int sumMiWinding = baseSegment->updateWindingReverse(baseAngle);
+ int sumSuWinding;
+ bool binary = includeType >= SkOpAngle::kBinarySingle;
+ if (binary) {
+ sumSuWinding = baseSegment->updateOppWindingReverse(baseAngle);
+ if (baseSegment->operand()) {
+ SkTSwap<int>(sumMiWinding, sumSuWinding);
+ }
+ }
+ SkOpSegment* nextSegment = nextAngle->segment();
+ int maxWinding, sumWinding;
+ SkOpSpan* last;
+ if (binary) {
+ int oppMaxWinding, oppSumWinding;
+ nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
+ &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
+ last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
+ nextAngle);
+ } else {
+ nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
+ &maxWinding, &sumWinding);
+ last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
+ }
+ nextAngle->setLastMarked(last);
+}
+
+void SkOpSegment::ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
+ SkOpAngle::IncludeType includeType) {
+ const SkOpSegment* baseSegment = baseAngle->segment();
+ int sumMiWinding = baseSegment->updateWinding(baseAngle);
+ int sumSuWinding;
+ bool binary = includeType >= SkOpAngle::kBinarySingle;
+ if (binary) {
+ sumSuWinding = baseSegment->updateOppWinding(baseAngle);
+ if (baseSegment->operand()) {
+ SkTSwap<int>(sumMiWinding, sumSuWinding);
+ }
+ }
+ SkOpSegment* nextSegment = nextAngle->segment();
+ int maxWinding, sumWinding;
+ SkOpSpan* last;
+ if (binary) {
+ int oppMaxWinding, oppSumWinding;
+ nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
+ &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
+ last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
+ nextAngle);
+ } else {
+ nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
+ &maxWinding, &sumWinding);
+ last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
+ }
+ 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;
- *vertical = false;
+ int bestTIndex = -1;
if (bottom <= *bestY) {
- return NULL;
+ return bestTIndex;
}
SkScalar top = fBounds.fTop;
if (top >= basePt.fY) {
- return NULL;
+ return bestTIndex;
}
if (fBounds.fLeft > basePt.fX) {
- return NULL;
+ return bestTIndex;
}
if (fBounds.fRight < basePt.fX) {
- return NULL;
+ return bestTIndex;
}
if (fBounds.fLeft == fBounds.fRight) {
// if vertical, and directly above test point, wait for another one
- *vertical = AlmostEqualUlps(basePt.fX, fBounds.fLeft);
- return NULL;
+ return AlmostEqualUlps(basePt.fX, fBounds.fLeft) ? SK_MinS32 : bestTIndex;
}
// intersect ray starting at basePt with edge
SkIntersections intersections;
@@ -685,7 +2065,7 @@
int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)])
(fPts, top, bottom, basePt.fX, false);
if (pts == 0 || (current && pts == 1)) {
- return NULL;
+ return bestTIndex;
}
if (current) {
SkASSERT(pts > 1);
@@ -713,73 +2093,933 @@
continue;
}
if (pts > 1 && fVerb == SkPath::kLine_Verb) {
- *vertical = true;
- return NULL; // if the intersection is edge on, wait for another one
+ return SK_MinS32; // if the intersection is edge on, wait for another one
}
if (fVerb > SkPath::kLine_Verb) {
SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fX;
if (approximately_zero(dx)) {
- *vertical = true;
- return NULL; // hit vertical, wait for another one
+ return SK_MinS32; // hit vertical, wait for another one
}
}
*bestY = testY;
bestT = foundT;
}
if (bestT < 0) {
- return NULL;
+ return bestTIndex;
}
SkASSERT(bestT >= 0);
- SkASSERT(bestT < 1);
- SkOpSpanBase* testTSpanBase = &this->fHead;
- SkOpSpanBase* nextTSpan;
- double endT = 0;
+ SkASSERT(bestT <= 1);
+ int start;
+ int end = 0;
do {
- nextTSpan = testTSpanBase->upCast()->next();
- endT = nextTSpan->t();
- if (endT >= bestT) {
+ start = end;
+ end = nextSpan(start, 1);
+ } while (fTs[end].fT < bestT);
+ // FIXME: see next candidate for a better pattern to find the next start/end pair
+ while (start + 1 < end && fTs[start].fDone) {
+ ++start;
+ }
+ if (!isCanceled(start)) {
+ *hitT = bestT;
+ bestTIndex = start;
+ *hitSomething = true;
+ }
+ return bestTIndex;
+}
+
+bool SkOpSegment::decrementSpan(SkOpSpan* span) {
+ SkASSERT(span->fWindValue > 0);
+ if (--(span->fWindValue) == 0) {
+ if (!span->fOppValue && !span->fDone) {
+ span->fDone = true;
+ ++fDoneSpans;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SkOpSegment::bumpSpan(SkOpSpan* span, int windDelta, int oppDelta) {
+ SkASSERT(!span->fDone || span->fTiny || span->fSmall);
+ span->fWindValue += windDelta;
+ SkASSERT(span->fWindValue >= 0);
+ span->fOppValue += oppDelta;
+ SkASSERT(span->fOppValue >= 0);
+ if (fXor) {
+ span->fWindValue &= 1;
+ }
+ if (fOppXor) {
+ span->fOppValue &= 1;
+ }
+ if (!span->fWindValue && !span->fOppValue) {
+ if (!span->fDone) {
+ span->fDone = true;
+ ++fDoneSpans;
+ }
+ return true;
+ }
+ 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;
}
- testTSpanBase = nextTSpan;
- } while (testTSpanBase);
- SkOpSpan* bestTSpan = NULL;
- SkOpSpan* testTSpan = testTSpanBase->upCast();
- if (!testTSpan->isCanceled()) {
- *hitT = bestT;
- bestTSpan = testTSpan;
- *hitSomething = true;
- }
- return bestTSpan;
-}
-
-void SkOpSegment::detach(const SkOpSpan* span) {
- if (span->done()) {
- --this->fDoneCount;
- }
- --this->fCount;
-}
-
-double SkOpSegment::distSq(double t, SkOpAngle* oppAngle) {
- SkDPoint testPt = this->dPtAtT(t);
- SkDLine testPerp = {{ testPt, testPt }};
- SkDVector slope = this->dSlopeAtT(t);
- testPerp[1].fX += slope.fY;
- testPerp[1].fY -= slope.fX;
- SkIntersections i;
- SkOpSegment* oppSegment = oppAngle->segment();
- int oppPtCount = SkPathOpsVerbToPoints(oppSegment->verb());
- (*CurveIntersectRay[oppPtCount])(oppSegment->pts(), testPerp, &i);
- double closestDistSq = SK_ScalarInfinity;
- for (int index = 0; index < i.used(); ++index) {
- if (!between(oppAngle->start()->t(), i[0][index], oppAngle->end()->t())) {
+ }
+ 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;
}
- double testDistSq = testPt.distanceSquared(i.pt(index));
- if (closestDistSq > testDistSq) {
- closestDistSq = testDistSq;
- }
- }
- return closestDistSq;
+ 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
+bool SkOpSegment::checkEnds() {
+ debugValidate();
+ SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
+ int count = fTs.count();
+ for (int index = 0; index < count; ++index) {
+ const SkOpSpan& span = fTs[index];
+ double otherT = span.fOtherT;
+ if (otherT != 0 && otherT != 1) { // only check ends
+ continue;
+ }
+ const SkOpSegment* other = span.fOther;
+ // peek start/last describe the range of spans that match the other t of this span
+ int peekStart = span.fOtherIndex;
+ while (--peekStart >= 0 && other->fTs[peekStart].fT == otherT)
+ ;
+ int otherCount = other->fTs.count();
+ int peekLast = span.fOtherIndex;
+ while (++peekLast < otherCount && other->fTs[peekLast].fT == otherT)
+ ;
+ if (++peekStart == --peekLast) { // if there isn't a range, there's nothing to do
+ continue;
+ }
+ // t start/last describe the range of spans that match the t of this span
+ double t = span.fT;
+ double tBottom = -1;
+ int tStart = -1;
+ int tLast = count;
+ bool lastSmall = false;
+ double afterT = t;
+ for (int inner = 0; inner < count; ++inner) {
+ double innerT = fTs[inner].fT;
+ if (innerT <= t && innerT > tBottom) {
+ if (innerT < t || !lastSmall) {
+ tStart = inner - 1;
+ }
+ tBottom = innerT;
+ }
+ if (innerT > afterT) {
+ if (t == afterT && lastSmall) {
+ afterT = innerT;
+ } else {
+ tLast = inner;
+ break;
+ }
+ }
+ lastSmall = innerT <= t ? fTs[inner].fSmall : false;
+ }
+ for (int peekIndex = peekStart; peekIndex <= peekLast; ++peekIndex) {
+ if (peekIndex == span.fOtherIndex) { // skip the other span pointed to by this span
+ continue;
+ }
+ const SkOpSpan& peekSpan = other->fTs[peekIndex];
+ SkOpSegment* match = peekSpan.fOther;
+ if (match->done()) {
+ continue; // if the edge has already been eaten (likely coincidence), ignore it
+ }
+ const double matchT = peekSpan.fOtherT;
+ // see if any of the spans match the other spans
+ for (int tIndex = tStart + 1; tIndex < tLast; ++tIndex) {
+ const SkOpSpan& tSpan = fTs[tIndex];
+ if (tSpan.fOther == match) {
+ if (tSpan.fOtherT == matchT) {
+ goto nextPeekIndex;
+ }
+ double midT = (tSpan.fOtherT + matchT) / 2;
+ if (match->betweenPoints(midT, tSpan.fPt, peekSpan.fPt)) {
+ goto nextPeekIndex;
+ }
+ }
+ }
+ if (missingSpans.count() > 0) {
+ const MissingSpan& lastMissing = missingSpans.back();
+ if (lastMissing.fT == t
+ && lastMissing.fOther == match
+ && lastMissing.fOtherT == matchT) {
+ SkASSERT(SkDPoint::ApproximatelyEqual(lastMissing.fPt, peekSpan.fPt));
+ continue;
+ }
+ }
+ if (this == match) {
+ return false; // extremely large paths can trigger this
+ }
+#if DEBUG_CHECK_ALIGN
+ SkDebugf("%s id=%d missing t=%1.9g other=%d otherT=%1.9g pt=(%1.9g,%1.9g)\n",
+ __FUNCTION__, fID, t, match->fID, matchT, peekSpan.fPt.fX, peekSpan.fPt.fY);
+#endif
+ // this segment is missing a entry that the other contains
+ // remember so we can add the missing one and recompute the indices
+ {
+ MissingSpan& missing = missingSpans.push_back();
+ SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
+ missing.fT = t;
+ SkASSERT(this != match);
+ missing.fOther = match;
+ missing.fOtherT = matchT;
+ missing.fPt = peekSpan.fPt;
+ }
+ break;
+nextPeekIndex:
+ ;
+ }
+ }
+ if (missingSpans.count() == 0) {
+ debugValidate();
+ return true;
+ }
+ debugValidate();
+ int missingCount = missingSpans.count();
+ for (int index = 0; index < missingCount; ++index) {
+ MissingSpan& missing = missingSpans[index];
+ 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
+ // avoid this
+ for (int index = 0; index < missingCount; ++index) {
+ missingSpans[index].fOther->fixOtherTIndex();
+ }
+ debugValidate();
+ return true;
+}
+
+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) {
+ if (this == test->fOther) {
+ continue;
+ }
+ CheckOneLink(test, oSpan, oFirst, oLast, &missing, missingSpans);
+ }
+ test = base;
+ while (test < last && (++test)->fPt == base->fPt) {
+ SkASSERT(this != test->fOther || test->fLoop);
+ 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;
+ }
+ double tBase = fTs[index].fT;
+ while (index > 0 && precisely_negative(tBase - fTs[--index].fT))
+ ;
+ 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);
+ const SkOpSpan* nextSpan = &firstSpan + 1;
+ ptrdiff_t smallCount = &lastSpan - &firstSpan + 1;
+ SkASSERT(1 <= smallCount && smallCount < count());
+ if (smallCount <= 1 && !nextSpan->fSmall) {
+ 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;
+ if (nextSpan->fPt == missing.fPt) {
+ continue;
+ }
+ do {
+ ++nextSpan;
+ } while (nextSpan->fSmall);
+ if (nextSpan->fT == 1) {
+ continue;
+ }
+ SkAssertResult(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) {
+ SkAssertResult(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) {
+ SkAssertResult(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
+void SkOpSegment::checkTiny() {
+ SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
+ SkOpSpan* thisSpan = fTs.begin() - 1;
+ const SkOpSpan* endSpan = fTs.end() - 1; // last can't be tiny
+ while (++thisSpan < endSpan) {
+ if (!thisSpan->fTiny) {
+ continue;
+ }
+ SkOpSpan* nextSpan = thisSpan + 1;
+ double thisT = thisSpan->fT;
+ double nextT = nextSpan->fT;
+ if (thisT == nextT) {
+ continue;
+ }
+ SkASSERT(thisT < nextT);
+ SkASSERT(thisSpan->fPt == nextSpan->fPt);
+ SkOpSegment* thisOther = thisSpan->fOther;
+ SkOpSegment* nextOther = nextSpan->fOther;
+ int oIndex = thisSpan->fOtherIndex;
+ for (int oStep = -1; oStep <= 1; oStep += 2) {
+ int oEnd = thisOther->nextExactSpan(oIndex, oStep);
+ if (oEnd < 0) {
+ continue;
+ }
+ const SkOpSpan& oSpan = thisOther->span(oEnd);
+ int nIndex = nextSpan->fOtherIndex;
+ for (int nStep = -1; nStep <= 1; nStep += 2) {
+ int nEnd = nextOther->nextExactSpan(nIndex, nStep);
+ if (nEnd < 0) {
+ continue;
+ }
+ const SkOpSpan& nSpan = nextOther->span(nEnd);
+ if (oSpan.fPt != nSpan.fPt) {
+ continue;
+ }
+ double oMidT = (thisSpan->fOtherT + oSpan.fT) / 2;
+ const SkPoint& oPt = thisOther->ptAtT(oMidT);
+ double nMidT = (nextSpan->fOtherT + nSpan.fT) / 2;
+ const SkPoint& nPt = nextOther->ptAtT(nMidT);
+ if (!AlmostEqualUlps(oPt, nPt)) {
+ continue;
+ }
+#if DEBUG_CHECK_TINY
+ SkDebugf("%s [%d] add coincidence [%d] [%d]\n", __FUNCTION__, fID,
+ thisOther->fID, nextOther->fID);
+#endif
+ // this segment is missing a entry that the other contains
+ // remember so we can add the missing one and recompute the indices
+ MissingSpan& missing = missingSpans.push_back();
+ SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
+ missing.fSegment = thisOther;
+ missing.fT = thisSpan->fOtherT;
+ SkASSERT(this != nextOther);
+ missing.fOther = nextOther;
+ missing.fOtherT = nextSpan->fOtherT;
+ missing.fPt = thisSpan->fPt;
+ }
+ }
+ }
+ int missingCount = missingSpans.count();
+ if (!missingCount) {
+ return;
+ }
+ for (int index = 0; index < missingCount; ++index) {
+ MissingSpan& missing = missingSpans[index];
+ 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();
+ missing.fOther->fixOtherTIndex();
+ }
+}
+
+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);
+ SkASSERT(span->fOtherT == 0 || span->fOtherT == 1);
+ const SkOpSpan* otherSpan = &other->span(oEnd);
+ double refT = otherSpan->fT;
+ const SkPoint& refPt = otherSpan->fPt;
+ const SkOpSpan* lastSpan = &other->span(step > 0 ? other->count() - 1 : 0);
+ do {
+ const SkOpSegment* match = span->fOther;
+ if (match == otherSpan->fOther) {
+ // find start of respective spans and see if both have winding
+ int startIndex, endIndex;
+ if (span->fOtherT == 1) {
+ endIndex = span->fOtherIndex;
+ startIndex = match->nextExactSpan(endIndex, -1);
+ } else {
+ startIndex = span->fOtherIndex;
+ endIndex = match->nextExactSpan(startIndex, 1);
+ }
+ const SkOpSpan& startSpan = match->span(startIndex);
+ if (startSpan.fWindValue != 0) {
+ // draw ray from endSpan.fPt perpendicular to end tangent and measure distance
+ // to other segment.
+ const SkOpSpan& endSpan = match->span(endIndex);
+ SkDLine ray;
+ SkVector dxdy;
+ if (span->fOtherT == 1) {
+ ray.fPts[0].set(startSpan.fPt);
+ dxdy = match->dxdy(startIndex);
+ } else {
+ ray.fPts[0].set(endSpan.fPt);
+ dxdy = match->dxdy(endIndex);
+ }
+ ray.fPts[1].fX = ray.fPts[0].fX + dxdy.fY;
+ ray.fPts[1].fY = ray.fPts[0].fY - dxdy.fX;
+ SkIntersections i;
+ int roots = (i.*CurveRay[SkPathOpsVerbToPoints(other->verb())])(other->pts(), ray);
+ for (int index = 0; index < roots; ++index) {
+ if (ray.fPts[0].approximatelyEqual(i.pt(index))) {
+ double matchMidT = (match->span(startIndex).fT
+ + match->span(endIndex).fT) / 2;
+ SkPoint matchMidPt = match->ptAtT(matchMidT);
+ double otherMidT = (i[0][index] + other->span(oStart).fT) / 2;
+ SkPoint otherMidPt = other->ptAtT(otherMidT);
+ if (SkDPoint::ApproximatelyEqual(matchMidPt, otherMidPt)) {
+ *startPt = startSpan.fPt;
+ *endPt = endSpan.fPt;
+ *endT = endSpan.fT;
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+ if (otherSpan == lastSpan) {
+ break;
+ }
+ otherSpan += step;
+ } while (otherSpan->fT == refT || otherSpan->fPt == refPt);
+ 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;
}
/*
@@ -789,57 +3029,71 @@
The Opp variable name part designates that the value is for the Opposite operator.
Opposite values result from combining coincident spans.
*/
-SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** nextStart,
- SkOpSpanBase** nextEnd, bool* unsortable, SkPathOp op, int xorMiMask, int xorSuMask) {
- SkOpSpanBase* start = *nextStart;
- SkOpSpanBase* end = *nextEnd;
- SkASSERT(start != end);
- int step = start->step(end);
- SkOpSegment* other = this->isSimple(nextStart, &step); // advances nextStart
- if (other) {
+SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart, int* nextEnd,
+ bool* unsortable, SkPathOp op, const int xorMiMask,
+ const int xorSuMask) {
+ const int startIndex = *nextStart;
+ const int endIndex = *nextEnd;
+ SkASSERT(startIndex != endIndex);
+ SkDEBUGCODE(const int count = fTs.count());
+ SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
+ 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
SkDebugf("%s simple\n", __FUNCTION__);
#endif
- SkOpSpan* startSpan = start->starter(end);
- if (startSpan->done()) {
+ int min = SkMin32(startIndex, endIndex);
+ if (fTs[min].fDone) {
return NULL;
}
- markDone(startSpan);
- *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
+ markDoneBinary(min);
+ double startT = other->fTs[*nextStart].fT;
+ *nextEnd = *nextStart;
+ do {
+ *nextEnd += step;
+ } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
+ SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
+ if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
+ *unsortable = true;
+ return NULL;
+ }
return other;
}
- SkOpSpanBase* endNear = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
- SkASSERT(endNear == end); // is this ever not end?
- SkASSERT(endNear);
- SkASSERT(start != endNear);
- SkASSERT((start->t() < endNear->t()) ^ (step < 0));
+ const int end = nextExactSpan(startIndex, step);
+ SkASSERT(end >= 0);
+ SkASSERT(startIndex - endIndex != 0);
+ SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
// more than one viable candidate -- measure angles to find best
- int calcWinding = computeSum(start, endNear, SkOpAngle::kBinaryOpp);
+
+ int calcWinding = computeSum(startIndex, end, SkOpAngle::kBinaryOpp);
bool sortable = calcWinding != SK_NaN32;
if (!sortable) {
*unsortable = true;
- markDone(start->starter(end));
+ markDoneBinary(SkMin32(startIndex, endIndex));
return NULL;
}
- SkOpAngle* angle = this->spanToAngle(end, start);
+ SkOpAngle* angle = spanToAngle(end, startIndex);
if (angle->unorderable()) {
*unsortable = true;
- markDone(start->starter(end));
+ markDoneBinary(SkMin32(startIndex, endIndex));
return NULL;
}
#if DEBUG_SORT
SkDebugf("%s\n", __FUNCTION__);
angle->debugLoop();
#endif
- int sumMiWinding = updateWinding(end, start);
+ int sumMiWinding = updateWinding(endIndex, startIndex);
if (sumMiWinding == SK_MinS32) {
*unsortable = true;
- markDone(start->starter(end));
+ markDoneBinary(SkMin32(startIndex, endIndex));
return NULL;
}
- int sumSuWinding = updateOppWinding(end, start);
+ int sumSuWinding = updateOppWinding(endIndex, startIndex);
if (operand()) {
SkTSwap<int>(sumMiWinding, sumSuWinding);
}
@@ -856,6 +3110,11 @@
if (activeAngle) {
++activeCount;
if (!foundAngle || (foundDone && activeCount & 1)) {
+ if (nextSegment->isTiny(nextAngle)) {
+ *unsortable = true;
+ markDoneBinary(SkMin32(startIndex, endIndex));
+ return NULL;
+ }
foundAngle = nextAngle;
foundDone = nextSegment->done(nextAngle);
}
@@ -863,24 +3122,30 @@
if (nextSegment->done()) {
continue;
}
+ if (nextSegment->isTiny(nextAngle)) {
+ continue;
+ }
if (!activeAngle) {
- (void) nextSegment->markAndChaseDone(nextAngle->start(), nextAngle->end());
- }
- SkOpSpanBase* last = nextAngle->lastMarked();
+ (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 segment=%d span=%d", __FUNCTION__,
- last->segment()->debugID(), last->debugID());
- if (!last->final()) {
- SkDebugf(" windSum=%d", last->upCast()->windSum());
- }
- SkDebugf("\n");
+ SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
+ last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
+ last->fSmall);
#endif
}
} while ((nextAngle = nextAngle->next()) != angle);
- start->segment()->markDone(start->starter(end));
+#if DEBUG_ANGLE
+ if (foundAngle) {
+ foundAngle->debugSameAs(foundAngle);
+ }
+#endif
+
+ markDoneBinary(SkMin32(startIndex, endIndex));
if (!foundAngle) {
return NULL;
}
@@ -894,55 +3159,62 @@
return nextSegment;
}
-SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpanBase*>* chase,
- SkOpSpanBase** nextStart, SkOpSpanBase** nextEnd, bool* unsortable) {
- SkOpSpanBase* start = *nextStart;
- SkOpSpanBase* end = *nextEnd;
- SkASSERT(start != end);
- int step = start->step(end);
- SkOpSegment* other = this->isSimple(nextStart, &step); // advances nextStart
- if (other) {
+SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* nextStart,
+ int* nextEnd, bool* unsortable) {
+ const int startIndex = *nextStart;
+ const int endIndex = *nextEnd;
+ SkASSERT(startIndex != endIndex);
+ SkDEBUGCODE(const int count = fTs.count());
+ SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
+ 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
SkDebugf("%s simple\n", __FUNCTION__);
#endif
- SkOpSpan* startSpan = start->starter(end);
- if (startSpan->done()) {
+ int min = SkMin32(startIndex, endIndex);
+ if (fTs[min].fDone) {
return NULL;
}
- markDone(startSpan);
- *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
+ markDoneUnary(min);
+ double startT = other->fTs[*nextStart].fT;
+ *nextEnd = *nextStart;
+ do {
+ *nextEnd += step;
+ } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
+ SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
+ if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
+ *unsortable = true;
+ return NULL;
+ }
return other;
}
- SkOpSpanBase* endNear = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
- SkASSERT(endNear == end); // is this ever not end?
- SkASSERT(endNear);
- SkASSERT(start != endNear);
- SkASSERT((start->t() < endNear->t()) ^ (step < 0));
+ const int end = nextExactSpan(startIndex, step);
+ SkASSERT(end >= 0);
+ SkASSERT(startIndex - endIndex != 0);
+ SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
// more than one viable candidate -- measure angles to find best
- int calcWinding = computeSum(start, endNear, SkOpAngle::kUnaryWinding);
+
+ int calcWinding = computeSum(startIndex, end, SkOpAngle::kUnaryWinding);
bool sortable = calcWinding != SK_NaN32;
if (!sortable) {
*unsortable = true;
- markDone(start->starter(end));
+ markDoneUnary(SkMin32(startIndex, endIndex));
return NULL;
}
- SkOpAngle* angle = this->spanToAngle(end, start);
- if (angle->unorderable()) {
- *unsortable = true;
- markDone(start->starter(end));
- return NULL;
- }
+ SkOpAngle* angle = spanToAngle(end, startIndex);
#if DEBUG_SORT
SkDebugf("%s\n", __FUNCTION__);
angle->debugLoop();
#endif
- int sumWinding = updateWinding(end, start);
+ int sumWinding = updateWinding(endIndex, startIndex);
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 {
@@ -952,6 +3224,11 @@
if (activeAngle) {
++activeCount;
if (!foundAngle || (foundDone && activeCount & 1)) {
+ if (nextSegment->isTiny(nextAngle)) {
+ *unsortable = true;
+ markDoneUnary(SkMin32(startIndex, endIndex));
+ return NULL;
+ }
foundAngle = nextAngle;
foundDone = nextSegment->done(nextAngle);
}
@@ -959,24 +3236,24 @@
if (nextSegment->done()) {
continue;
}
+ if (nextSegment->isTiny(nextAngle)) {
+ continue;
+ }
if (!activeAngle) {
- (void) nextSegment->markAndChaseDone(nextAngle->start(), nextAngle->end());
- }
- SkOpSpanBase* last = nextAngle->lastMarked();
+ nextSegment->markAndChaseDoneUnary(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 segment=%d span=%d", __FUNCTION__,
- last->segment()->debugID(), last->debugID());
- if (!last->final()) {
- SkDebugf(" windSum=%d", last->upCast()->windSum());
- }
- SkDebugf("\n");
+ SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
+ last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
+ last->fSmall);
#endif
}
} while ((nextAngle = nextAngle->next()) != angle);
- start->segment()->markDone(start->starter(end));
+ markDoneUnary(SkMin32(startIndex, endIndex));
if (!foundAngle) {
return NULL;
}
@@ -990,39 +3267,57 @@
return nextSegment;
}
-SkOpSegment* SkOpSegment::findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** nextEnd,
- bool* unsortable) {
- SkOpSpanBase* start = *nextStart;
- SkOpSpanBase* end = *nextEnd;
- SkASSERT(start != end);
- int step = start->step(end);
- SkOpSegment* other = this->isSimple(nextStart, &step); // advances nextStart
- if (other) {
- // mark the smaller of startIndex, endIndex done, and all adjacent
- // spans with the same T value (but not 'other' spans)
+SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsortable) {
+ const int startIndex = *nextStart;
+ const int endIndex = *nextEnd;
+ SkASSERT(startIndex != endIndex);
+ SkDEBUGCODE(int count = fTs.count());
+ SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
+ int step = SkSign32(endIndex - startIndex);
+// 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
- SkOpSpan* startSpan = start->starter(end);
- if (startSpan->done()) {
+ int min = SkMin32(startIndex, endIndex);
+ if (fTs[min].fDone) {
return NULL;
}
- markDone(startSpan);
- *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
+ markDone(min, 1);
+ 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;)
+ if ((approximately_less_than_zero(startT) && step < 0)
+ || (approximately_greater_than_one(startT) && step > 0)) {
+ step = -step;
+ SkDEBUGCODE(firstLoop = false;)
+ }
+ do {
+ *nextEnd = *nextStart;
+ do {
+ *nextEnd += step;
+ } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
+ if (other->fTs[SkMin32(*nextStart, *nextEnd)].fWindValue) {
+ break;
+ }
+ SkASSERT(firstLoop);
+ SkDEBUGCODE(firstLoop = false;)
+ step = -step;
+ } while (true);
+ SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
return other;
}
- SkDEBUGCODE(SkOpSpanBase* endNear = step > 0 ? (*nextStart)->upCast()->next() \
- : (*nextStart)->prev());
- SkASSERT(endNear == end); // is this ever not end?
- SkASSERT(endNear);
- SkASSERT(start != endNear);
- SkASSERT((start->t() < endNear->t()) ^ (step < 0));
- SkOpAngle* angle = this->spanToAngle(end, start);
- if (angle->unorderable()) {
- *unsortable = true;
- markDone(start->starter(end));
- return NULL;
- }
+ SkASSERT(startIndex - endIndex != 0);
+ SkASSERT((startIndex - endIndex < 0) ^ (step < 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
SkDebugf("%s\n", __FUNCTION__);
angle->debugLoop();
@@ -1030,13 +3325,16 @@
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 {
nextSegment = nextAngle->segment();
++activeCount;
if (!foundAngle || (foundDone && activeCount & 1)) {
+ if (nextSegment->isTiny(nextAngle)) {
+ *unsortable = true;
+ return NULL;
+ }
foundAngle = nextAngle;
if (!(foundDone = nextSegment->done(nextAngle))) {
break;
@@ -1044,7 +3342,7 @@
}
nextAngle = nextAngle->next();
} while (nextAngle != angle);
- start->segment()->markDone(start->starter(end));
+ markDone(SkMin32(startIndex, endIndex), 1);
if (!foundAngle) {
return NULL;
}
@@ -1058,39 +3356,105 @@
return nextSegment;
}
-SkOpSegment* SkOpSegment::findTop(bool firstPass, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr,
- bool* unsortable, SkChunkAlloc* allocator) {
+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::findExactT(double t, const SkOpSegment* match) const {
+ int count = this->count();
+ for (int index = 0; index < count; ++index) {
+ const SkOpSpan& span = fTs[index];
+ if (span.fT == t && span.fOther == match) {
+ return index;
+ }
+ }
+ SkASSERT(0);
+ return -1;
+}
+
+
+
+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();
+ // prefer exact matches over approximate matches
+ for (int index = 0; index < count; ++index) {
+ const SkOpSpan& span = fTs[index];
+ if (span.fT == t && span.fOther == match) {
+ return index;
+ }
+ }
+ 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;
+}
+
+SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsortable,
+ bool firstPass) {
// iterate through T intersections and return topmost
// topmost tangent from y-min to first pt is closer to horizontal
SkASSERT(!done());
- SkOpSpanBase* firstT = NULL;
- (void) this->activeLeftTop(&firstT);
- if (!firstT) {
+ int firstT = -1;
+ /* SkPoint topPt = */ activeLeftTop(&firstT);
+ if (firstT < 0) {
*unsortable = !firstPass;
- firstT = &fHead;
- while (firstT->upCast()->done()) {
- firstT = firstT->upCast()->next();
- }
- *startPtr = firstT;
- *endPtr = firstT->upCast()->next();
+ firstT = 0;
+ while (fTs[firstT].fDone) {
+ SkASSERT(firstT < fTs.count());
+ ++firstT;
+ }
+ *tIndexPtr = firstT;
+ *endIndexPtr = nextExactSpan(firstT, 1);
return this;
}
// sort the edges to find the leftmost
int step = 1;
- SkOpSpanBase* end;
- if (firstT->final() || firstT->upCast()->done()) {
+ int end;
+ if (span(firstT).fDone || (end = nextSpan(firstT, step)) == -1) {
step = -1;
- end = firstT->prev();
- SkASSERT(end);
- } else {
- end = firstT->upCast()->next();
+ 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)
- SkASSERT(firstT != end);
+ SkASSERT(firstT - end != 0);
SkOpAngle* markAngle = spanToAngle(firstT, end);
if (!markAngle) {
- markAngle = addSingletonAngles(step, allocator);
+ markAngle = addSingletonAngles(step);
}
markAngle->markStops();
const SkOpAngle* baseAngle = markAngle->next() == markAngle && !isVertical() ? markAngle
@@ -1103,7 +3467,7 @@
const SkOpAngle* angle = baseAngle;
do {
if (!angle->unorderable()) {
- const SkOpSegment* next = angle->segment();
+ SkOpSegment* next = angle->segment();
SkPathOpsBounds bounds;
next->subDivideBounds(angle->end(), angle->start(), &bounds);
bool nearSame = AlmostEqualUlps(top, bounds.top());
@@ -1131,10 +3495,9 @@
*unsortable = angle->unorderable();
if (firstPass || !*unsortable) {
leftSegment = angle->segment();
- *startPtr = angle->end();
- *endPtr = angle->start();
- const SkOpSpan* firstSpan = (*startPtr)->starter(*endPtr);
- if (!firstSpan->done()) {
+ *tIndexPtr = angle->end();
+ *endIndexPtr = angle->start();
+ if (!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fDone) {
break;
}
}
@@ -1145,52 +3508,157 @@
return NULL;
}
if (leftSegment->verb() >= SkPath::kQuad_Verb) {
- SkOpSpanBase* start = *startPtr;
- SkOpSpanBase* end = *endPtr;
+ const int tIndex = *tIndexPtr;
+ const int endIndex = *endIndexPtr;
bool swap;
- if (!leftSegment->clockwise(start, end, &swap)) {
+ if (!leftSegment->clockwise(tIndex, endIndex, &swap)) {
#if DEBUG_SWAP_TOP
- SkDebugf("%s swap=%d inflections=%d monotonic=%d\n",
+ SkDebugf("%s swap=%d inflections=%d serpentine=%d controlledbyends=%d monotonic=%d\n",
__FUNCTION__,
- swap, leftSegment->debugInflections(start, end),
- leftSegment->monotonicInY(start, end));
+ swap, leftSegment->debugInflections(tIndex, endIndex),
+ leftSegment->serpentine(tIndex, endIndex),
+ leftSegment->controlsContainedByEnds(tIndex, endIndex),
+ leftSegment->monotonicInY(tIndex, endIndex));
#endif
if (swap) {
// FIXME: I doubt it makes sense to (necessarily) swap if the edge was not the first
// sorted but merely the first not already processed (i.e., not done)
- SkTSwap(*startPtr, *endPtr);
- }
- }
- }
+ SkTSwap(*tIndexPtr, *endIndexPtr);
+ }
+ }
+ }
+ SkASSERT(!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fTiny);
return leftSegment;
}
-SkOpGlobalState* SkOpSegment::globalState() const {
- return contour()->globalState();
-}
-
-void SkOpSegment::init(SkPoint pts[], SkOpContour* contour, SkPath::Verb verb) {
- fContour = contour;
- fNext = NULL;
+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
+// while the following fixes the indices up again, it isn't smart about
+// skipping segments whose indices are already correct
+// assuming we leave the code that wrote the index in the first place
+// FIXME: if called after remove, this needs to correct tiny
+void SkOpSegment::fixOtherTIndex() {
+ int iCount = fTs.count();
+ for (int i = 0; i < iCount; ++i) {
+ SkOpSpan& iSpan = fTs[i];
+ double oT = iSpan.fOtherT;
+ SkOpSegment* other = iSpan.fOther;
+ int oCount = other->fTs.count();
+ SkDEBUGCODE(iSpan.fOtherIndex = -1);
+ for (int o = 0; o < oCount; ++o) {
+ SkOpSpan& oSpan = other->fTs[o];
+ if (oT == oSpan.fT && this == oSpan.fOther && oSpan.fOtherT == iSpan.fT) {
+ iSpan.fOtherIndex = o;
+ oSpan.fOtherIndex = i;
+ break;
+ }
+ }
+ SkASSERT(iSpan.fOtherIndex >= 0);
+ }
+}
+
+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
+}
+
+bool SkOpSegment::inconsistentAngle(int maxWinding, int sumWinding, int oppMaxWinding,
+ int oppSumWinding, const SkOpAngle* angle) const {
+ SkASSERT(angle->segment() == this);
+ if (UseInnerWinding(maxWinding, sumWinding)) {
+ maxWinding = sumWinding;
+ }
+ if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
+ oppMaxWinding = oppSumWinding;
+ }
+ return inconsistentWinding(angle, maxWinding, oppMaxWinding);
+}
+
+bool SkOpSegment::inconsistentWinding(const SkOpAngle* angle, int winding,
+ int oppWinding) const {
+ int index = angle->start();
+ int endIndex = angle->end();
+ int min = SkMin32(index, endIndex);
+ int step = SkSign32(endIndex - index);
+ if (inconsistentWinding(min, winding, oppWinding)) {
+ return true;
+ }
+ const SkOpSegment* other = this;
+ while ((other = other->nextChase(&index, &step, &min, NULL))) {
+ if (other->fTs[min].fWindSum != SK_MinS32) {
+ break;
+ }
+ if (fOperand == other->fOperand) {
+ if (other->inconsistentWinding(min, winding, oppWinding)) {
+ return true;
+ }
+ } else {
+ if (other->inconsistentWinding(min, oppWinding, winding)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool SkOpSegment::inconsistentWinding(int index, int winding, int oppWinding) const {
+ SkASSERT(winding || oppWinding);
+ double referenceT = this->span(index).fT;
+ int lesser = index;
+ while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
+ if (inconsistentWinding(__FUNCTION__, lesser, winding, oppWinding)) {
+ return true;
+ }
+ }
+ do {
+ if (inconsistentWinding(__FUNCTION__, index, winding, oppWinding)) {
+ return true;
+ }
+ } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+ return false;
+}
+
+bool SkOpSegment::inconsistentWinding(const char* funName, int tIndex, int winding,
+ int oppWinding) const {
+ const SkOpSpan& span = this->span(tIndex);
+ if (span.fDone && !span.fSmall) {
+ return false;
+ }
+ return (span.fWindSum != SK_MinS32 && span.fWindSum != winding)
+ || (span.fOppSum != SK_MinS32 && span.fOppSum != oppWinding);
+}
+
+void SkOpSegment::init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd) {
+ fDoneSpans = 0;
+ fOperand = operand;
+ fXor = evenOdd;
fPts = pts;
fVerb = verb;
- fCount = 0;
- fDoneCount = 0;
- fVisited = false;
- SkOpSpan* zeroSpan = &fHead;
- zeroSpan->init(this, NULL, 0, fPts[0]);
- SkOpSpanBase* oneSpan = &fTail;
- zeroSpan->setNext(oneSpan);
- oneSpan->initBase(this, zeroSpan, 1, fPts[SkPathOpsVerbToPoints(fVerb)]);
- PATH_OPS_DEBUG_CODE(fID = globalState()->nextSegmentID());
-}
-
-void SkOpSegment::initWinding(SkOpSpanBase* start, SkOpSpanBase* end,
- SkOpAngle::IncludeType angleIncludeType) {
- int local = SkOpSegment::SpanSign(start, end);
+ fLoop = fMultiples = fSmall = fTiny = false;
+}
+
+void SkOpSegment::initWinding(int start, int end, SkOpAngle::IncludeType angleIncludeType) {
+ int local = spanSign(start, end);
SkDEBUGCODE(bool success);
if (angleIncludeType == SkOpAngle::kBinarySingle) {
- int oppLocal = SkOpSegment::OppSign(start, end);
+ int oppLocal = oppSign(start, end);
SkDEBUGCODE(success =) markAndChaseWinding(start, end, local, oppLocal, NULL);
// OPTIMIZATION: the reverse mark and chase could skip the first marking
SkDEBUGCODE(success |=) markAndChaseWinding(end, start, local, oppLocal, NULL);
@@ -1211,13 +3679,12 @@
from has the same x direction as this span, the winding should change. If the dx is opposite, then
the same winding is shared by both.
*/
-bool SkOpSegment::initWinding(SkOpSpanBase* start, SkOpSpanBase* end, double tHit,
- int winding, SkScalar hitDx, int oppWind, SkScalar hitOppDx) {
- SkASSERT(this == start->segment());
+bool SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkScalar hitDx,
+ int oppWind, SkScalar hitOppDx) {
SkASSERT(hitDx || !winding);
SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
-// SkASSERT(dx);
- int windVal = start->starter(end)->windValue();
+ SkASSERT(dx);
+ int windVal = windValue(SkMin32(start, end));
#if DEBUG_WINDING_AT_T
SkDebugf("%s id=%d oldWinding=%d hitDx=%c dx=%c windVal=%d", __FUNCTION__, debugID(), winding,
hitDx ? hitDx > 0 ? '+' : '-' : '0', dx > 0 ? '+' : '-', windVal);
@@ -1226,9 +3693,9 @@
if (abs(winding) < abs(sideWind)) {
winding = sideWind;
}
- SkDEBUGCODE(int oppLocal = SkOpSegment::OppSign(start, end));
+ SkDEBUGCODE(int oppLocal = oppSign(start, end));
SkASSERT(hitOppDx || !oppWind || !oppLocal);
- int oppWindVal = start->starter(end)->oppValue();
+ int oppWindVal = oppValue(SkMin32(start, end));
if (!oppWind) {
oppWind = dx < 0 ? oppWindVal : -oppWindVal;
} else if (hitOppDx * dx >= 0) {
@@ -1247,57 +3714,149 @@
return success;
}
-bool SkOpSegment::isClose(double t, const SkOpSegment* opp) const {
- SkDPoint cPt = this->dPtAtT(t);
- int pts = SkPathOpsVerbToPoints(this->verb());
- SkDVector dxdy = (*CurveDSlopeAtT[pts])(this->pts(), t);
- SkDLine perp = {{ cPt, {cPt.fX + dxdy.fY, cPt.fY - dxdy.fX} }};
- SkIntersections i;
- int oppPts = SkPathOpsVerbToPoints(opp->verb());
- (*CurveIntersectRay[oppPts])(opp->pts(), perp, &i);
- int used = i.used();
- for (int index = 0; index < used; ++index) {
- if (cPt.roughlyEqual(i.pt(index))) {
+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 {
+ 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;
+ }
+ }
+ return true;
+}
+
+
+SkOpSegment* SkOpSegment::isSimple(int* end, int* step) {
+ return nextChase(end, step, NULL, NULL);
+}
+
+bool SkOpSegment::isTiny(const SkOpAngle* angle) const {
+ int start = angle->start();
+ int end = angle->end();
+ const SkOpSpan& mSpan = fTs[SkMin32(start, end)];
+ return mSpan.fTiny;
+}
+
+bool SkOpSegment::isTiny(int index) const {
+ return fTs[index].fTiny;
+}
+
+// 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 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, const SkPoint& otherPt,
+ int step, bool cancel) {
+ int otherTIndex = other->findT(otherT, otherPt, this);
+ int next = other->nextExactSpan(otherTIndex, step);
+ int otherMin = SkMin32(otherTIndex, next);
+ int otherWind = other->span(otherMin).fWindValue;
+ if (otherWind == 0) {
+ return false;
+ }
+ if (next < 0) {
+ return false; // can happen if t values were adjusted but coincident ts were not
+ }
+ int tIndex = 0;
+ do {
+ SkOpSpan* test = &fTs[tIndex];
+ SkASSERT(test->fT == 0);
+ if (test->fOther == other || test->fOtherT != 1) {
+ continue;
+ }
+ SkPoint startPt, endPt;
+ double endT;
+ if (findCoincidentMatch(test, other, otherTIndex, next, step, &startPt, &endPt, &endT)) {
+ SkOpSegment* match = test->fOther;
+ if (cancel) {
+ match->addTCancel(startPt, endPt, other);
+ } else {
+ if (!match->addTCoincident(startPt, endPt, endT, other)) {
+ return false;
+ }
+ }
return true;
}
- }
+ } while (fTs[++tIndex].fT == 0);
return false;
}
-bool SkOpSegment::isXor() const {
- return fContour->isXor();
-}
-
-SkOpSpanBase* SkOpSegment::markAndChaseDone(SkOpSpanBase* start, SkOpSpanBase* end) {
- int step = start->step(end);
- SkOpSpan* minSpan = start->starter(end);
- markDone(minSpan);
- SkOpSpanBase* last = NULL;
+// this span is excluded by the winding rule -- chase the ends
+// as long as they are unambiguous to mark connections as done
+// and give them the same winding value
+
+SkOpSpan* SkOpSegment::markAndChaseDoneBinary(int index, int endIndex) {
+ int step = SkSign32(endIndex - index);
+ int min = SkMin32(index, endIndex);
+ markDoneBinary(min);
+ SkOpSpan* last = NULL;
SkOpSegment* other = this;
- while ((other = other->nextChase(&start, &step, &minSpan, &last))) {
+ while ((other = other->nextChase(&index, &step, &min, &last))) {
if (other->done()) {
SkASSERT(!last);
break;
}
- other->markDone(minSpan);
+ other->markDoneBinary(min);
}
return last;
}
-bool SkOpSegment::markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, int winding,
- SkOpSpanBase** lastPtr) {
- SkOpSpan* spanStart = start->starter(end);
- int step = start->step(end);
- bool success = markWinding(spanStart, winding);
- SkOpSpanBase* last = NULL;
+SkOpSpan* SkOpSegment::markAndChaseDoneUnary(int index, int endIndex) {
+ int step = SkSign32(endIndex - index);
+ int min = SkMin32(index, endIndex);
+ markDoneUnary(min);
+ SkOpSpan* last = NULL;
SkOpSegment* other = this;
- while ((other = other->nextChase(&start, &step, &spanStart, &last))) {
- if (spanStart->windSum() != SK_MinS32) {
- SkASSERT(spanStart->windSum() == winding);
+ while ((other = other->nextChase(&index, &step, &min, &last))) {
+ if (other->done()) {
SkASSERT(!last);
break;
}
- (void) other->markWinding(spanStart, winding);
+ other->markDoneUnary(min);
+ }
+ return last;
+}
+
+bool SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding, SkOpSpan** lastPtr) {
+ int index = angle->start();
+ int endIndex = angle->end();
+ return markAndChaseWinding(index, endIndex, winding, lastPtr);
+}
+
+bool SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, SkOpSpan** lastPtr) {
+ int min = SkMin32(index, endIndex);
+ int step = SkSign32(endIndex - index);
+ bool success = 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;
+ }
+ (void) other->markWinding(min, winding);
}
if (lastPtr) {
*lastPtr = last;
@@ -1305,32 +3864,37 @@
return success;
}
-bool SkOpSegment::markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end,
- int winding, int oppWinding, SkOpSpanBase** lastPtr) {
- SkOpSpan* spanStart = start->starter(end);
- int step = start->step(end);
- bool success = markWinding(spanStart, winding, oppWinding);
- SkOpSpanBase* last = NULL;
+bool SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, int oppWinding,
+ SkOpSpan** lastPtr) {
+ int min = SkMin32(index, endIndex);
+ int step = SkSign32(endIndex - index);
+ bool success = markWinding(min, winding, oppWinding);
+ SkOpSpan* last = NULL;
SkOpSegment* other = this;
- while ((other = other->nextChase(&start, &step, &spanStart, &last))) {
- if (spanStart->windSum() != SK_MinS32) {
- if (this->operand() == other->operand()) {
- SkASSERT(spanStart->windSum() == winding);
- if (spanStart->oppSum() != oppWinding) {
- this->globalState()->setWindingFailed();
- return false;
- }
- } else {
- SkASSERT(spanStart->windSum() == oppWinding);
- SkASSERT(spanStart->oppSum() == winding);
+ while ((other = other->nextChase(&index, &step, &min, &last))) {
+ if (other->fTs[min].fWindSum != SK_MinS32) {
+#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 {
+// FIXME: this is probably a bug -- issue414409b asserts here
+// 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 (this->operand() == other->operand()) {
- (void) other->markWinding(spanStart, winding, oppWinding);
+ if (fOperand == other->fOperand) {
+ (void) other->markWinding(min, winding, oppWinding);
} else {
- (void) other->markWinding(spanStart, oppWinding, winding);
+ (void) other->markWinding(min, oppWinding, winding);
}
}
if (lastPtr) {
@@ -1339,29 +3903,33 @@
return success;
}
-SkOpSpanBase* SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle) {
+bool SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding, int oppWinding,
+ SkOpSpan** lastPtr) {
+ int start = angle->start();
+ int end = angle->end();
+ return markAndChaseWinding(start, end, winding, oppWinding, lastPtr);
+}
+
+SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle) {
SkASSERT(angle->segment() == this);
if (UseInnerWinding(maxWinding, sumWinding)) {
maxWinding = sumWinding;
}
- SkOpSpanBase* last;
- (void) markAndChaseWinding(angle->start(), angle->end(), maxWinding, &last);
+ SkOpSpan* last;
+ SkAssertResult(markAndChaseWinding(angle, maxWinding, &last));
#if DEBUG_WINDING
if (last) {
- SkDebugf("%s last seg=%d span=%d", __FUNCTION__,
- last->segment()->debugID(), last->debugID());
- if (!last->final()) {
- SkDebugf(" windSum=");
- SkPathOpsDebug::WindingPrintf(last->upCast()->windSum());
- }
- SkDebugf("\n");
+ SkDebugf("%s last id=%d windSum=", __FUNCTION__,
+ last->fOther->fTs[last->fOtherIndex].fOther->debugID());
+ SkPathOpsDebug::WindingPrintf(last->fWindSum);
+ SkDebugf(" small=%d\n", last->fSmall);
}
#endif
return last;
}
-SkOpSpanBase* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
- int oppSumWinding, const SkOpAngle* angle) {
+SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
+ int oppSumWinding, const SkOpAngle* angle) {
SkASSERT(angle->segment() == this);
if (UseInnerWinding(maxWinding, sumWinding)) {
maxWinding = sumWinding;
@@ -1369,161 +3937,440 @@
if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
oppMaxWinding = oppSumWinding;
}
- SkOpSpanBase* last = NULL;
+ SkOpSpan* last;
// caller doesn't require that this marks anything
- (void) markAndChaseWinding(angle->start(), angle->end(), maxWinding, oppMaxWinding, &last);
+ (void) markAndChaseWinding(angle, maxWinding, oppMaxWinding, &last);
#if DEBUG_WINDING
if (last) {
- SkDebugf("%s last segment=%d span=%d", __FUNCTION__,
- last->segment()->debugID(), last->debugID());
- if (!last->final()) {
- SkDebugf(" windSum=");
- SkPathOpsDebug::WindingPrintf(last->upCast()->windSum());
- }
- SkDebugf(" \n");
+ SkDebugf("%s last id=%d windSum=", __FUNCTION__,
+ last->fOther->fTs[last->fOtherIndex].fOther->debugID());
+ SkPathOpsDebug::WindingPrintf(last->fWindSum);
+ SkDebugf(" small=%d\n", last->fSmall);
}
#endif
return last;
}
-void SkOpSegment::markDone(SkOpSpan* span) {
- SkASSERT(this == span->segment());
- if (span->done()) {
+// FIXME: this should also mark spans with equal (x,y)
+// This may be called when the segment is already marked done. While this
+// wastes time, it shouldn't do any more than spin through the T spans.
+// OPTIMIZATION: abort on first done found (assuming that this code is
+// always called to mark segments done).
+void SkOpSegment::markDone(int index, int winding) {
+ // SkASSERT(!done());
+ SkASSERT(winding);
+ double referenceT = fTs[index].fT;
+ int lesser = index;
+ while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
+ markOneDone(__FUNCTION__, lesser, winding);
+ }
+ do {
+ markOneDone(__FUNCTION__, index, winding);
+ } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+ debugValidate();
+}
+
+void SkOpSegment::markDoneBinary(int index) {
+ double referenceT = fTs[index].fT;
+ int lesser = index;
+ while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
+ markOneDoneBinary(__FUNCTION__, lesser);
+ }
+ do {
+ markOneDoneBinary(__FUNCTION__, index);
+ } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+ debugValidate();
+}
+
+void SkOpSegment::markDoneFinal(int index) {
+ double referenceT = fTs[index].fT;
+ int lesser = index;
+ while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
+ markOneDoneFinal(__FUNCTION__, lesser);
+ }
+ do {
+ markOneDoneFinal(__FUNCTION__, index);
+ } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+ debugValidate();
+}
+
+void SkOpSegment::markDoneUnary(int index) {
+ double referenceT = fTs[index].fT;
+ int lesser = index;
+ while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
+ markOneDoneUnary(__FUNCTION__, lesser);
+ }
+ 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;
+ (void) markOneWinding(funName, tIndex, winding, &span); // allowed to do nothing
+ if (span->fDone) {
return;
}
+ span->fDone = true;
+ ++fDoneSpans;
+}
+
+void SkOpSegment::markOneDoneFinal(const char* funName, int tIndex) {
+ SkOpSpan* span = &fTs[tIndex];
+ if (span->fDone) {
+ return;
+ }
+ span->fDone = true;
+ ++fDoneSpans;
+}
+
+void SkOpSegment::markOneDoneBinary(const char* funName, int tIndex) {
+ SkOpSpan* span = verifyOneWinding(funName, tIndex);
+ if (!span) {
+ return;
+ }
+ SkASSERT(!span->fDone);
+ span->fDone = true;
+ ++fDoneSpans;
+}
+
+void SkOpSegment::markOneDoneUnary(const char* funName, int tIndex) {
+ SkOpSpan* span = verifyOneWindingU(funName, tIndex);
+ if (!span) {
+ return;
+ }
+ if (span->fWindSum == SK_MinS32) {
+ SkDebugf("%s uncomputed\n", __FUNCTION__);
+ }
+ SkASSERT(!span->fDone);
+ span->fDone = true;
+ ++fDoneSpans;
+}
+
+bool SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding, SkOpSpan** lastPtr) {
+ SkOpSpan* span = &fTs[tIndex];
+ if (lastPtr) {
+ *lastPtr = span;
+ }
+ if (span->fDone && !span->fSmall) {
+ return false;
+ }
#if DEBUG_MARK_DONE
- debugShowNewWinding(__FUNCTION__, span, span->windSum(), span->oppSum());
-#endif
- span->setDone(true);
- ++fDoneCount;
+ debugShowNewWinding(funName, *span, winding);
+#endif
+ SkASSERT(span->fWindSum == SK_MinS32 || span->fWindSum == winding);
+#if DEBUG_LIMIT_WIND_SUM
+ SkASSERT(abs(winding) <= DEBUG_LIMIT_WIND_SUM);
+#endif
+ span->fWindSum = winding;
+ return true;
+}
+
+bool SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding,
+ int oppWinding, SkOpSpan** lastPtr) {
+ SkOpSpan* span = &fTs[tIndex];
+ if (span->fDone && !span->fSmall) {
+ return false;
+ }
+#if DEBUG_MARK_DONE
+ debugShowNewWinding(funName, *span, winding, oppWinding);
+#endif
+ SkASSERT(span->fWindSum == SK_MinS32 || span->fWindSum == winding);
+#if DEBUG_LIMIT_WIND_SUM
+ SkASSERT(abs(winding) <= DEBUG_LIMIT_WIND_SUM);
+#endif
+ span->fWindSum = winding;
+ SkASSERT(span->fOppSum == SK_MinS32 || span->fOppSum == oppWinding);
+#if DEBUG_LIMIT_WIND_SUM
+ SkASSERT(abs(oppWinding) <= DEBUG_LIMIT_WIND_SUM);
+#endif
+ span->fOppSum = oppWinding;
debugValidate();
-}
-
-bool SkOpSegment::markWinding(SkOpSpan* span, int winding) {
- SkASSERT(this == span->segment());
- SkASSERT(winding);
- if (span->done()) {
- return false;
- }
-#if DEBUG_MARK_DONE
- debugShowNewWinding(__FUNCTION__, span, winding);
-#endif
- span->setWindSum(winding);
- debugValidate();
+ if (lastPtr) {
+ *lastPtr = span;
+ }
return true;
}
-bool SkOpSegment::markWinding(SkOpSpan* span, int winding, int oppWinding) {
- SkASSERT(this == span->segment());
- SkASSERT(winding || oppWinding);
- if (span->done()) {
- return false;
- }
-#if DEBUG_MARK_DONE
- debugShowNewWinding(__FUNCTION__, span, winding, oppWinding);
-#endif
- span->setWindSum(winding);
- span->setOppSum(oppWinding);
- debugValidate();
- return true;
-}
-
-bool SkOpSegment::match(const SkOpPtT* base, const SkOpSegment* testParent, double testT,
- const SkPoint& testPt) const {
- const SkOpSegment* baseParent = base->segment();
- if (this == baseParent && this == testParent && precisely_equal(base->fT, testT)) {
- return true;
- }
- if (!SkDPoint::ApproximatelyEqual(testPt, base->fPt)) {
- return false;
- }
- return !ptsDisjoint(base->fT, base->fPt, testT, testPt);
-}
-
-static SkOpSegment* set_last(SkOpSpanBase** last, SkOpSpanBase* endSpan) {
- if (last) {
- *last = endSpan;
- }
- return NULL;
-}
-
-bool SkOpSegment::monotonicInY(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
+// 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, 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} }};
+ SkDLine tangent2 = {{ {edge[2].fX, edge[2].fY}, {edge[3].fX, edge[3].fY} }};
+ if (SkIntersections::Test(tangent1, tangent2)) {
+ 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);
+ sumSet = true;
+ }
+ }
+ }
+ 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 {
SkASSERT(fVerb != SkPath::kLine_Verb);
if (fVerb == SkPath::kQuad_Verb) {
- SkDQuad dst = SkDQuad::SubDivide(fPts, start->t(), end->t());
+ SkDQuad dst = SkDQuad::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
return dst.monotonicInY();
}
SkASSERT(fVerb == SkPath::kCubic_Verb);
- SkDCubic dst = SkDCubic::SubDivide(fPts, start->t(), end->t());
+ SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
return dst.monotonicInY();
}
-bool SkOpSegment::NextCandidate(SkOpSpanBase* span, SkOpSpanBase** start,
- SkOpSpanBase** end) {
- while (span->final() || span->upCast()->done()) {
- if (span->final()) {
+bool SkOpSegment::serpentine(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.serpentine();
+}
+
+SkOpSpan* SkOpSegment::verifyOneWinding(const char* funName, int tIndex) {
+ SkOpSpan& span = fTs[tIndex];
+ if (span.fDone) {
+ return NULL;
+ }
+#if DEBUG_MARK_DONE
+ debugShowNewWinding(funName, span, span.fWindSum, span.fOppSum);
+#endif
+// If the prior angle in the sort is unorderable, the winding sum may not be computable.
+// To enable the assert, the 'prior is unorderable' state could be
+// piped down to this test, but not sure it's worth it.
+// (Once the sort order is stored in the span, this test may be feasible.)
+// SkASSERT(span.fWindSum != SK_MinS32);
+// SkASSERT(span.fOppSum != SK_MinS32);
+ return &span;
+}
+
+SkOpSpan* SkOpSegment::verifyOneWindingU(const char* funName, int tIndex) {
+ SkOpSpan& span = fTs[tIndex];
+ if (span.fDone) {
+ return NULL;
+ }
+#if DEBUG_MARK_DONE
+ debugShowNewWinding(funName, span, span.fWindSum);
+#endif
+// If the prior angle in the sort is unorderable, the winding sum may not be computable.
+// To enable the assert, the 'prior is unorderable' state could be
+// piped down to this test, but not sure it's worth it.
+// (Once the sort order is stored in the span, this test may be feasible.)
+// SkASSERT(span.fWindSum != SK_MinS32);
+ return &span;
+}
+
+bool SkOpSegment::markWinding(int index, int winding) {
+// SkASSERT(!done());
+ SkASSERT(winding);
+ double referenceT = fTs[index].fT;
+ int lesser = index;
+ bool success = false;
+ while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
+ success |= markOneWinding(__FUNCTION__, lesser, winding, NULL);
+ }
+ do {
+ success |= markOneWinding(__FUNCTION__, index, winding, NULL);
+ } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+ debugValidate();
+ return success;
+}
+
+bool SkOpSegment::markWinding(int index, int winding, int oppWinding) {
+// SkASSERT(!done());
+ SkASSERT(winding || oppWinding);
+ double referenceT = fTs[index].fT;
+ int lesser = index;
+ bool success = false;
+ while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
+ success |= markOneWinding(__FUNCTION__, lesser, winding, oppWinding, NULL);
+ }
+ do {
+ success |= markOneWinding(__FUNCTION__, index, winding, oppWinding, NULL);
+ } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+ debugValidate();
+ return success;
+}
+
+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;
+ }
+ }
+ if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
+ const SkOpSpan& above = fTs[tIndex + 1];
+ if (approximately_negative(above.fT - t)) {
+ nextDoorWind = above.fWindValue;
+ nextOppWind = above.fOppValue;
+ }
+ }
+ if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
+ const SkOpSpan& below = fTs[tIndex - 1];
+ nextDoorWind = below.fWindValue;
+ nextOppWind = below.fOppValue;
+ }
+ if (nextDoorWind != SK_MaxS32) {
+ SkOpSpan& newSpan = fTs[tIndex];
+ newSpan.fWindValue = nextDoorWind;
+ newSpan.fOppValue = nextOppWind;
+ if (!nextDoorWind && !nextOppWind && !newSpan.fDone) {
+ newSpan.fDone = true;
+ ++fDoneSpans;
+ }
+ }
+}
+
+bool SkOpSegment::nextCandidate(int* start, int* end) const {
+ while (fTs[*end].fDone) {
+ if (fTs[*end].fT == 1) {
return false;
}
- span = span->upCast()->next();
- }
- *start = span;
- *end = span->upCast()->next();
+ ++(*end);
+ }
+ *start = *end;
+ *end = nextExactSpan(*start, 1);
return true;
}
-SkOpSegment* SkOpSegment::nextChase(SkOpSpanBase** startPtr, int* stepPtr, SkOpSpan** minPtr,
- SkOpSpanBase** last) const {
- SkOpSpanBase* origStart = *startPtr;
+static SkOpSegment* set_last(SkOpSpan** last, const SkOpSpan* endSpan) {
+ if (last && !endSpan->fSmall) {
+ *last = const_cast<SkOpSpan*>(endSpan); // FIXME: get rid of cast
+ }
+ return NULL;
+}
+
+SkOpSegment* SkOpSegment::nextChase(int* indexPtr, int* stepPtr, int* minPtr,
+ SkOpSpan** last) const {
+ int origIndex = *indexPtr;
int step = *stepPtr;
- SkOpSpanBase* endSpan = step > 0 ? origStart->upCast()->next() : origStart->prev();
- SkASSERT(endSpan);
- SkOpAngle* angle = step > 0 ? endSpan->fromAngle() : endSpan->upCast()->toAngle();
- SkOpSpanBase* foundSpan;
- SkOpSpanBase* otherEnd;
+ int end = nextExactSpan(origIndex, step);
+ SkASSERT(end >= 0);
+ const SkOpSpan& endSpan = this->span(end);
+ SkOpAngle* angle = step > 0 ? endSpan.fFromAngle : endSpan.fToAngle;
+ int foundIndex;
+ int otherEnd;
SkOpSegment* other;
if (angle == NULL) {
- if (endSpan->t() != 0 && endSpan->t() != 1) {
+ if (endSpan.fT != 0 && endSpan.fT != 1) {
return NULL;
}
- SkOpPtT* otherPtT = endSpan->ptT()->next();
- other = otherPtT->segment();
- foundSpan = otherPtT->span();
- otherEnd = step > 0 ? foundSpan->upCast()->next() : foundSpan->prev();
+ other = endSpan.fOther;
+ foundIndex = endSpan.fOtherIndex;
+ otherEnd = other->nextExactSpan(foundIndex, step);
} else {
int loopCount = angle->loopCount();
if (loopCount > 2) {
- return set_last(last, endSpan);
+ return set_last(last, &endSpan);
}
const SkOpAngle* next = angle->next();
if (NULL == next) {
return NULL;
}
+ if (angle->sign() != next->sign()) {
#if DEBUG_WINDING
- if (angle->sign() != next->sign() && !angle->segment()->contour()->isXor()
- && !next->segment()->contour()->isXor()) {
SkDebugf("%s mismatched signs\n", __FUNCTION__);
- }
-#endif
+#endif
+ // return set_last(last, &endSpan);
+ }
other = next->segment();
- foundSpan = endSpan = next->start();
+ foundIndex = end = next->start();
otherEnd = next->end();
}
- int foundStep = foundSpan->step(otherEnd);
+ int foundStep = foundIndex < otherEnd ? 1 : -1;
if (*stepPtr != foundStep) {
- return set_last(last, endSpan);
- }
- SkASSERT(*startPtr);
- if (!otherEnd) {
+ return set_last(last, &endSpan);
+ }
+ SkASSERT(*indexPtr >= 0);
+ if (otherEnd < 0) {
return NULL;
}
// SkASSERT(otherEnd >= 0);
- SkOpSpan* origMin = step < 0 ? origStart->prev() : origStart->upCast();
- SkOpSpan* foundMin = foundSpan->starter(otherEnd);
- if (foundMin->windValue() != origMin->windValue()
- || foundMin->oppValue() != origMin->oppValue()) {
- return set_last(last, endSpan);
- }
- *startPtr = foundSpan;
+#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;
@@ -1531,217 +4378,101 @@
return other;
}
-static void clear_visited(SkOpSpan* span) {
- // reset visited flag back to false
- do {
- SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
- while ((ptT = ptT->next()) != stopPtT) {
- SkOpSegment* opp = ptT->segment();
- opp->resetVisited();
- }
- } while ((span = span->next()->upCastable()));
-}
-
-// look for pairs of undetected coincident curves
-// assumes that segments going in have visited flag clear
-// curve/curve intersection should now do a pretty good job of finding coincident runs so
-// this may be only be necessary for line/curve pairs -- so skip unless this is a line and the
-// the opp is not a line
-void SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
- if (this->verb() != SkPath::kLine_Verb) {
- return;
- }
- SkOpSpan* prior = NULL;
- SkOpSpan* span = &fHead;
- do {
- SkOpPtT* ptT = span->ptT(), * spanStopPtT = ptT;
- SkASSERT(ptT->span() == span);
- while ((ptT = ptT->next()) != spanStopPtT) {
- SkOpSegment* opp = ptT->span()->segment();
- if (opp->setVisited()) {
+// This has callers for two different situations: one establishes the end
+// of the current span, and one establishes the beginning of the next span
+// (thus the name). When this is looking for the end of the current span,
+// coincidence is found when the beginning Ts contain -step and the end
+// contains step. When it is looking for the beginning of the next, the
+// first Ts found can be ignored and the last Ts should contain -step.
+// OPTIMIZATION: probably should split into two functions
+int SkOpSegment::nextSpan(int from, int step) const {
+ const SkOpSpan& fromSpan = fTs[from];
+ int count = fTs.count();
+ int to = from;
+ while (step > 0 ? ++to < count : --to >= 0) {
+ const SkOpSpan& span = fTs[to];
+ if (approximately_zero(span.fT - fromSpan.fT)) {
+ continue;
+ }
+ return to;
+ }
+ return -1;
+}
+
+// FIXME
+// this returns at any difference in T, vs. a preset minimum. It may be
+// that all callers to nextSpan should use this instead.
+int SkOpSegment::nextExactSpan(int from, int step) const {
+ int to = from;
+ if (step < 0) {
+ const SkOpSpan& fromSpan = fTs[from];
+ while (--to >= 0) {
+ const SkOpSpan& span = fTs[to];
+ if (precisely_negative(fromSpan.fT - span.fT) || span.fTiny) {
continue;
}
- if (opp->verb() == SkPath::kLine_Verb) {
+ return to;
+ }
+ } else {
+ while (fTs[from].fTiny) {
+ from++;
+ }
+ const SkOpSpan& fromSpan = fTs[from];
+ int count = fTs.count();
+ while (++to < count) {
+ const SkOpSpan& span = fTs[to];
+ if (precisely_negative(span.fT - fromSpan.fT)) {
continue;
}
- if (span->containsCoincidence(opp)) { // FIXME: this assumes that if the opposite
- // segment is coincident then no more coincidence
- // needs to be detected. This may not be true.
- continue;
- }
- if (span->containsCoinEnd(opp)) {
- continue;
- }
- // if already visited and visited again, check for coin
- if (span == &fHead) {
- continue;
- }
- SkOpPtT* priorPtT = NULL, * priorStopPtT;
- // find prior span containing opp segment
- SkOpSegment* priorOpp = NULL;
- prior = span;
- while (!priorOpp && (prior = prior->prev())) {
- priorStopPtT = priorPtT = prior->ptT();
- while ((priorPtT = priorPtT->next()) != priorStopPtT) {
- SkOpSegment* segment = priorPtT->span()->segment();
- if (segment == opp) {
- priorOpp = opp;
- break;
- }
- }
- }
- if (!priorOpp) {
- continue;
- }
- SkOpPtT* oppStart = prior->ptT();
- SkOpPtT* oppEnd = span->ptT();
- bool swapped = priorPtT->fT > ptT->fT;
- if (swapped) {
- SkTSwap(priorPtT, ptT);
- SkTSwap(oppStart, oppEnd);
- }
- bool flipped = oppStart->fT > oppEnd->fT;
- bool coincident;
- if (coincidences->contains(priorPtT, ptT, oppStart, oppEnd, flipped)) {
- goto swapBack;
- }
- {
- // average t, find mid pt
- double midT = (prior->t() + span->t()) / 2;
- SkPoint midPt = this->ptAtT(midT);
- coincident = true;
- // if the mid pt is not near either end pt, project perpendicular through opp seg
- if (!SkDPoint::ApproximatelyEqual(priorPtT->fPt, midPt)
- && !SkDPoint::ApproximatelyEqual(ptT->fPt, midPt)) {
- coincident = false;
- SkIntersections i;
- int ptCount = SkPathOpsVerbToPoints(this->verb());
- SkVector dxdy = (*CurveSlopeAtT[ptCount])(pts(), midT);
- SkDLine ray = {{{midPt.fX, midPt.fY},
- {midPt.fX + dxdy.fY, midPt.fY - dxdy.fX}}};
- int oppPtCount = SkPathOpsVerbToPoints(opp->verb());
- (*CurveIntersectRay[oppPtCount])(opp->pts(), ray, &i);
- // measure distance and see if it's small enough to denote coincidence
- for (int index = 0; index < i.used(); ++index) {
- SkDPoint oppPt = i.pt(index);
- if (oppPt.approximatelyEqual(midPt)) {
- SkVector oppDxdy = (*CurveSlopeAtT[oppPtCount])(opp->pts(),
- i[index][0]);
- oppDxdy.normalize();
- dxdy.normalize();
- SkScalar flatness = SkScalarAbs(dxdy.cross(oppDxdy) / FLT_EPSILON);
- coincident |= flatness < 5000; // FIXME: replace with tuned value
- }
- }
- }
- }
- if (coincident) {
- // mark coincidence
- coincidences->add(priorPtT, ptT, oppStart, oppEnd, allocator);
- clear_visited(&fHead);
- missingCoincidence(coincidences, allocator);
- return;
- }
- swapBack:
- if (swapped) {
- SkTSwap(priorPtT, ptT);
- }
- }
- } while ((span = span->next()->upCastable()));
- clear_visited(&fHead);
-}
-
-// Move nearby t values and pts so they all hang off the same span. Alignment happens later.
-bool SkOpSegment::moveNearby() {
- debugValidate();
- SkOpSpanBase* spanS = &fHead;
- do {
- SkOpSpanBase* test = spanS->upCast()->next();
- SkOpSpanBase* next;
- if (spanS->contains(test)) {
- if (!test->final()) {
- test->upCast()->detach(spanS->ptT());
- continue;
- } else if (spanS != &fHead) {
- spanS->upCast()->detach(test->ptT());
- spanS = test;
- continue;
- }
- }
- do { // iterate through all spans associated with start
- SkOpPtT* startBase = spanS->ptT();
- next = test->final() ? NULL : test->upCast()->next();
- do {
- SkOpPtT* testBase = test->ptT();
- do {
- if (startBase == testBase) {
- goto checkNextSpan;
- }
- if (testBase->duplicate()) {
- continue;
- }
- if (this->match(startBase, testBase->segment(), testBase->fT, testBase->fPt)) {
- if (test == &this->fTail) {
- if (spanS == &fHead) {
- debugValidate();
- return true; // if this span has collapsed, remove it from parent
- }
- this->fTail.merge(spanS->upCast());
- debugValidate();
- return true;
- }
- spanS->merge(test->upCast());
- spanS->upCast()->setNext(next);
- goto checkNextSpan;
- }
- } while ((testBase = testBase->next()) != test->ptT());
- } while ((startBase = startBase->next()) != spanS->ptT());
- checkNextSpan:
- ;
- } while ((test = next));
- spanS = spanS->upCast()->next();
- } while (!spanS->final());
- debugValidate();
- return true;
-}
-
-bool SkOpSegment::operand() const {
- return fContour->operand();
-}
-
-bool SkOpSegment::oppXor() const {
- return fContour->oppXor();
-}
-
-bool SkOpSegment::ptsDisjoint(double t1, const SkPoint& pt1, double t2, const SkPoint& pt2) const {
- if (fVerb == SkPath::kLine_Verb) {
- return false;
- }
- // quads (and cubics) can loop back to nearly a line so that an opposite curve
- // hits in two places with very different t values.
- // OPTIMIZATION: curves could be preflighted so that, for example, something like
- // 'controls contained by ends' could avoid this check for common curves
- // 'ends are extremes in x or y' is cheaper to compute and real-world common
- // on the other hand, the below check is relatively inexpensive
- double midT = (t1 + t2) / 2;
- SkPoint midPt = this->ptAtT(midT);
- double seDistSq = SkTMax(pt1.distanceToSqd(pt2) * 2, FLT_EPSILON * 2);
- return midPt.distanceToSqd(pt1) > seDistSq || midPt.distanceToSqd(pt2) > seDistSq;
-}
-
-void SkOpSegment::setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding,
- int* maxWinding, int* sumWinding) {
- int deltaSum = SpanSign(start, end);
- *maxWinding = *sumMiWinding;
- *sumWinding = *sumMiWinding -= deltaSum;
- SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
-}
-
-void SkOpSegment::setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding,
- int* sumSuWinding, int* maxWinding, int* sumWinding, int* oppMaxWinding,
- int* oppSumWinding) {
- int deltaSum = SpanSign(start, end);
- int oppDeltaSum = OppSign(start, end);
+ return to;
+ }
+ }
+ 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;
+ }
+}
+
+bool SkOpSegment::reversePoints(const SkPoint& p1, const SkPoint& p2) const {
+ SkASSERT(p1 != p2);
+ int spanCount = count();
+ int p1IndexMin = -1;
+ int p2IndexMax = spanCount;
+ for (int index = 0; index < spanCount; ++index) {
+ const SkOpSpan& span = fTs[index];
+ if (span.fPt == p1) {
+ if (p1IndexMin < 0) {
+ p1IndexMin = index;
+ }
+ } else if (span.fPt == p2) {
+ p2IndexMax = index;
+ }
+ }
+ return p1IndexMin > p2IndexMax;
+}
+
+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);
+ int oppDeltaSum = oppSign(index, endIndex);
if (operand()) {
*maxWinding = *sumSuWinding;
*sumWinding = *sumSuWinding -= deltaSum;
@@ -1753,94 +4484,130 @@
*oppMaxWinding = *sumSuWinding;
*oppSumWinding = *sumSuWinding -= oppDeltaSum;
}
- SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
- SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*oppSumWinding) <= DEBUG_LIMIT_WIND_SUM);
+#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,
+ int* maxWinding, int* sumWinding) {
+ int deltaSum = spanSign(index, endIndex);
+ *maxWinding = *sumMiWinding;
+ *sumWinding = *sumMiWinding -= deltaSum;
+#if DEBUG_LIMIT_WIND_SUM
+ SkASSERT(abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
+#endif
}
void SkOpSegment::sortAngles() {
- SkOpSpanBase* span = &this->fHead;
+ int spanCount = fTs.count();
+ if (spanCount <= 2) {
+ return;
+ }
+ int index = 0;
do {
- SkOpAngle* fromAngle = span->fromAngle();
- SkOpAngle* toAngle = span->final() ? NULL : span->upCast()->toAngle();
+ 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 DEBUG_ANGLE
bool wroteAfterHeader = false;
#endif
- SkOpAngle* baseAngle = fromAngle;
- if (fromAngle && toAngle) {
+ 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(), span->t(),
- span->debugID());
- wroteAfterHeader = true;
-#endif
- fromAngle->insert(toAngle);
- } else if (!fromAngle) {
- baseAngle = toAngle;
- }
- SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
+ SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
+ index);
+ wroteAfterHeader = true;
+#endif
+ baseAngle->insert(toAngle);
+ }
+ }
+ SkOpAngle* nextFrom, * nextTo;
+ int firstIndex = index;
do {
- SkOpSpanBase* oSpan = ptT->span();
- if (oSpan == span) {
- continue;
- }
- SkOpAngle* oAngle = oSpan->fromAngle();
+ SkOpSpan& span = fTs[index];
+ SkOpSegment* other = span.fOther;
+ SkOpSpan& oSpan = other->fTs[span.fOtherIndex];
+ SkOpAngle* oAngle = oSpan.fFromAngle;
if (oAngle) {
#if DEBUG_ANGLE
if (!wroteAfterHeader) {
- SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(),
- span->t(), span->debugID());
+ SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
+ index);
wroteAfterHeader = true;
}
#endif
- if (!oAngle->loopContains(baseAngle)) {
+ if (!oAngle->loopContains(*baseAngle)) {
baseAngle->insert(oAngle);
}
}
- if (!oSpan->final()) {
- oAngle = oSpan->upCast()->toAngle();
- if (oAngle) {
+ oAngle = oSpan.fToAngle;
+ if (oAngle) {
#if DEBUG_ANGLE
- if (!wroteAfterHeader) {
- SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(),
- span->t(), span->debugID());
- wroteAfterHeader = true;
- }
-#endif
- if (!oAngle->loopContains(baseAngle)) {
- baseAngle->insert(oAngle);
- }
- }
- }
- } while ((ptT = ptT->next()) != stopPtT);
- if (baseAngle->loopCount() == 1) {
- span->setFromAngle(NULL);
- if (toAngle) {
- span->upCast()->setToAngle(NULL);
- }
+ 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 (!span->final() && (span = span->upCast()->next()));
+ } while (index < spanCount);
}
// return true if midpoints were computed
-bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
- SkPoint edge[4]) const {
+bool SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const {
SkASSERT(start != end);
- const SkOpPtT& startPtT = *start->ptT();
- const SkOpPtT& endPtT = *end->ptT();
- edge[0] = startPtT.fPt;
+ edge[0] = fTs[start].fPt;
int points = SkPathOpsVerbToPoints(fVerb);
- edge[points] = endPtT.fPt;
+ edge[points] = fTs[end].fPt;
if (fVerb == SkPath::kLine_Verb) {
return false;
}
- double startT = startPtT.fT;
- double endT = endPtT.fT;
+ double startT = fTs[start].fT;
+ double endT = fTs[end].fT;
if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
// don't compute midpoints if we already have them
if (fVerb == SkPath::kQuad_Verb) {
@@ -1870,19 +4637,17 @@
return true;
}
-bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
- SkDCubic* result) const {
+// return true if midpoints were computed
+bool SkOpSegment::subDivide(int start, int end, SkDCubic* result) const {
SkASSERT(start != end);
- const SkOpPtT& startPtT = *start->ptT();
- const SkOpPtT& endPtT = *end->ptT();
- (*result)[0].set(startPtT.fPt);
+ (*result)[0].set(fTs[start].fPt);
int points = SkPathOpsVerbToPoints(fVerb);
- (*result)[points].set(endPtT.fPt);
+ (*result)[points].set(fTs[end].fPt);
if (fVerb == SkPath::kLine_Verb) {
return false;
}
- double startT = startPtT.fT;
- double endT = endPtT.fT;
+ double startT = fTs[start].fT;
+ double endT = fTs[end].fT;
if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
// don't compute midpoints if we already have them
if (fVerb == SkPath::kQuad_Verb) {
@@ -1890,7 +4655,7 @@
return false;
}
SkASSERT(fVerb == SkPath::kCubic_Verb);
- if (startT == 0) {
+ if (start < end) {
(*result)[1].set(fPts[1]);
(*result)[2].set(fPts[2]);
return false;
@@ -1908,29 +4673,49 @@
return true;
}
-void SkOpSegment::subDivideBounds(const SkOpSpanBase* start, const SkOpSpanBase* end,
- SkPathOpsBounds* bounds) const {
+void SkOpSegment::subDivideBounds(int start, int end, SkPathOpsBounds* bounds) const {
SkPoint edge[4];
subDivide(start, end, edge);
(bounds->*SetCurveBounds[SkPathOpsVerbToPoints(fVerb)])(edge);
}
-void SkOpSegment::undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end) {
- SkOpSpan* span = this->head();
- do {
- if (!span->done()) {
+void SkOpSegment::TrackOutsidePair(SkTArray<SkPoint, true>* outsidePts, const SkPoint& endPt,
+ const SkPoint& startPt) {
+ int outCount = outsidePts->count();
+ if (outCount == 0 || endPt != (*outsidePts)[outCount - 2]) {
+ outsidePts->push_back(endPt);
+ outsidePts->push_back(startPt);
+ }
+}
+
+void SkOpSegment::TrackOutside(SkTArray<SkPoint, true>* outsidePts, const SkPoint& startPt) {
+ int outCount = outsidePts->count();
+ if (outCount == 0 || startPt != (*outsidePts)[outCount - 1]) {
+ outsidePts->push_back(startPt);
+ }
+}
+
+void SkOpSegment::undoneSpan(int* start, int* end) {
+ int tCount = fTs.count();
+ int index;
+ for (index = 0; index < tCount; ++index) {
+ if (!fTs[index].fDone) {
break;
}
- } while ((span = span->next()->upCastable()));
- SkASSERT(span);
- *start = span;
- *end = span->next();
-}
-
-int SkOpSegment::updateOppWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
- const SkOpSpan* lesser = start->starter(end);
- int oppWinding = lesser->oppSum();
- int oppSpanWinding = SkOpSegment::OppSign(start, end);
+ }
+ SkASSERT(index < tCount - 1);
+ *start = index;
+ double startT = fTs[index].fT;
+ while (approximately_negative(fTs[++index].fT - startT))
+ SkASSERT(index < tCount);
+ SkASSERT(index < tCount);
+ *end = index;
+}
+
+int SkOpSegment::updateOppWinding(int index, int endIndex) const {
+ int lesser = SkMin32(index, endIndex);
+ int oppWinding = oppSum(lesser);
+ int oppSpanWinding = oppSign(index, endIndex);
if (oppSpanWinding && UseInnerWinding(oppWinding - oppSpanWinding, oppWinding)
&& oppWinding != SK_MaxS32) {
oppWinding -= oppSpanWinding;
@@ -1939,24 +4724,24 @@
}
int SkOpSegment::updateOppWinding(const SkOpAngle* angle) const {
- const SkOpSpanBase* startSpan = angle->start();
- const SkOpSpanBase* endSpan = angle->end();
- return updateOppWinding(endSpan, startSpan);
+ int startIndex = angle->start();
+ int endIndex = angle->end();
+ return updateOppWinding(endIndex, startIndex);
}
int SkOpSegment::updateOppWindingReverse(const SkOpAngle* angle) const {
- const SkOpSpanBase* startSpan = angle->start();
- const SkOpSpanBase* endSpan = angle->end();
- return updateOppWinding(startSpan, endSpan);
-}
-
-int SkOpSegment::updateWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
- const SkOpSpan* lesser = start->starter(end);
- int winding = lesser->windSum();
+ int startIndex = angle->start();
+ int endIndex = angle->end();
+ return updateOppWinding(startIndex, endIndex);
+}
+
+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 = SkOpSegment::SpanSign(start, end);
+ int spanWinding = spanSign(index, endIndex);
if (winding && UseInnerWinding(winding - spanWinding, winding)
&& winding != SK_MaxS32) {
winding -= spanWinding;
@@ -1965,15 +4750,26 @@
}
int SkOpSegment::updateWinding(const SkOpAngle* angle) const {
- const SkOpSpanBase* startSpan = angle->start();
- const SkOpSpanBase* endSpan = angle->end();
- return updateWinding(endSpan, startSpan);
+ int startIndex = angle->start();
+ int endIndex = angle->end();
+ return updateWinding(endIndex, startIndex);
+}
+
+int SkOpSegment::updateWindingReverse(int index, int endIndex) const {
+ int lesser = SkMin32(index, endIndex);
+ int winding = windSum(lesser);
+ int spanWinding = spanSign(endIndex, index);
+ if (winding && UseInnerWindingReverse(winding - spanWinding, winding)
+ && winding != SK_MaxS32) {
+ winding -= spanWinding;
+ }
+ return winding;
}
int SkOpSegment::updateWindingReverse(const SkOpAngle* angle) const {
- const SkOpSpanBase* startSpan = angle->start();
- const SkOpSpanBase* endSpan = angle->end();
- return updateWinding(startSpan, endSpan);
+ int startIndex = angle->start();
+ int endIndex = angle->end();
+ return updateWindingReverse(endIndex, startIndex);
}
// OPTIMIZATION: does the following also work, and is it any faster?
@@ -1988,17 +4784,25 @@
return result;
}
-int SkOpSegment::windingAtT(double tHit, const SkOpSpan* span, bool crossOpp,
- SkScalar* dx) const {
- if (approximately_zero(tHit - span->t())) { // if we hit the end of a span, disregard
+bool SkOpSegment::UseInnerWindingReverse(int outerWinding, int innerWinding) {
+ SkASSERT(outerWinding != SK_MaxS32);
+ SkASSERT(innerWinding != SK_MaxS32);
+ int absOut = abs(outerWinding);
+ int absIn = abs(innerWinding);
+ bool result = absOut == absIn ? true : absOut < absIn;
+ return result;
+}
+
+int SkOpSegment::windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar* dx) const {
+ if (approximately_zero(tHit - t(tIndex))) { // if we hit the end of a span, disregard
return SK_MinS32;
}
- int winding = crossOpp ? span->oppSum() : span->windSum();
+ int winding = crossOpp ? oppSum(tIndex) : windSum(tIndex);
SkASSERT(winding != SK_MinS32);
- int windVal = crossOpp ? span->oppValue() : span->windValue();
+ int windVal = crossOpp ? oppValue(tIndex) : windValue(tIndex);
#if DEBUG_WINDING_AT_T
SkDebugf("%s id=%d opp=%d tHit=%1.9g t=%1.9g oldWinding=%d windValue=%d", __FUNCTION__,
- debugID(), crossOpp, tHit, span->t(), winding, windVal);
+ 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;
@@ -2024,6 +4828,20 @@
}
int SkOpSegment::windSum(const SkOpAngle* angle) const {
- const SkOpSpan* minSpan = angle->start()->starter(angle->end());
- return minSpan->windSum();
-}
+ int start = angle->start();
+ int end = angle->end();
+ int index = SkMin32(start, end);
+ return windSum(index);
+}
+
+void SkOpSegment::zeroSpan(SkOpSpan* span) {
+ SkASSERT(span->fWindValue > 0 || span->fOppValue != 0);
+ span->fWindValue = 0;
+ span->fOppValue = 0;
+ if (span->fTiny || span->fSmall) {
+ return;
+ }
+ SkASSERT(!span->fDone);
+ span->fDone = true;
+ ++fDoneSpans;
+}
« no previous file with comments | « src/pathops/SkOpSegment.h ('k') | src/pathops/SkOpSpan.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698