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

Unified Diff: src/pathops/SkOpSegment.cpp

Issue 1037573004: cumulative pathops patch (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: fix pathopsinverse gm 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 1fb5afa028af33e894a82fe890e8b03af185f52a..c6ccf93003de931ba2fc5f72e58ff379f69b88d3 100644
--- a/src/pathops/SkOpSegment.cpp
+++ b/src/pathops/SkOpSegment.cpp
@@ -4,11 +4,20 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
-#include "SkIntersections.h"
+#include "SkOpCoincidence.h"
#include "SkOpContour.h"
#include "SkOpSegment.h"
#include "SkPathWriter.h"
-#include "SkTSort.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.
+ */
#define F (false) // discard the edge
#define T (true) // keep the edge
@@ -19,7 +28,7 @@ static const bool gUnaryActiveEdge[2][2] = {
{F, T}, {T, F},
};
-static const bool gActiveEdge[kXOR_PathOp + 1][2][2][2][2] = {
+static const bool gActiveEdge[kXOR_SkPathOp + 1][2][2][2][2] = {
// miFrom=0 miFrom=1
// miTo=0 miTo=1 miTo=0 miTo=1
// suFrom=0 1 suFrom=0 1 suFrom=0 1 suFrom=0 1
@@ -33,147 +42,125 @@ static const bool gActiveEdge[kXOR_PathOp + 1][2][2][2][2] = {
#undef F
#undef T
-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)) {
+SkOpAngle* SkOpSegment::activeAngle(SkOpSpanBase* start, SkOpSpanBase** startPtr,
+ SkOpSpanBase** endPtr, bool* done, bool* sortable) {
+ if (SkOpAngle* result = activeAngleInner(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;
- }
+ if (SkOpAngle* result = activeAngleOther(start, startPtr, endPtr, 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;
}
-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;
+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.fDone) {
- if (upSpan.fWindSum != SK_MinS32) {
- return spanToAngle(index, next);
+ if (!upSpan->done()) {
+ if (upSpan->windSum() != SK_MinS32) {
+ return spanToAngle(start, next);
}
*done = false;
}
} else {
- SkASSERT(upSpan.fDone);
+ SkASSERT(upSpan->done());
}
}
- int prev = nextExactSpan(index, -1);
+ SkOpSpan* downSpan = start->prev();
// edge leading into junction
- 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);
+ 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);
}
*done = false;
}
} else {
- SkASSERT(downSpan.fDone);
+ SkASSERT(downSpan->done());
}
}
return NULL;
}
-const SkOpAngle* SkOpSegment::activeAngleOther(int index, int* start, int* end, bool* done,
- bool* sortable) const {
- const SkOpSpan* span = &fTs[index];
- SkOpSegment* other = span->fOther;
- int oIndex = span->fOtherIndex;
- return other->activeAngleInner(oIndex, start, end, done, sortable);
+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(int* firstT) const {
+SkPoint SkOpSegment::activeLeftTop(SkOpSpanBase** firstSpan) {
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;
- for (int index = 0; index < count; ++index) {
- const SkOpSpan& span = fTs[index];
- if (span.fDone && lastDone) {
- goto next;
- }
- if (approximately_negative(span.fT - lastT)) {
+ SkOpSpanBase* span = &fHead;
+ do {
+ if (lastDone && (span->final() || span->upCast()->done())) {
goto next;
}
{
- const SkPoint& xy = xyAtT(&span);
+ const SkPoint& xy = span->pt();
if (topPt.fY > xy.fY || (topPt.fY == xy.fY && topPt.fX > xy.fX)) {
topPt = xy;
- if (firstT) {
- *firstT = index;
+ if (firstSpan) {
+ *firstSpan = span;
}
}
if (fVerb != SkPath::kLine_Verb && !lastDone) {
- SkPoint curveTop = (*CurveTop[SkPathOpsVerbToPoints(fVerb)])(fPts, lastT, span.fT);
+ SkPoint curveTop = (*CurveTop[SkPathOpsVerbToPoints(fVerb)])(fPts, lastT,
+ span->t());
if (topPt.fY > curveTop.fY || (topPt.fY == curveTop.fY
&& topPt.fX > curveTop.fX)) {
topPt = curveTop;
- if (firstT) {
- *firstT = index;
+ if (firstSpan) {
+ *firstSpan = span;
}
}
}
- lastT = span.fT;
+ lastT = span->t();
}
next:
- lastDone = span.fDone;
- }
+ if (span->final()) {
+ break;
+ }
+ lastDone = span->upCast()->done();
+ } while ((span = span->upCast()->next()));
return topPt;
}
-bool SkOpSegment::activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, SkPathOp op) {
- int sumMiWinding = updateWinding(endIndex, index);
- int sumSuWinding = updateOppWinding(endIndex, index);
+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);
#if DEBUG_LIMIT_WIND_SUM
SkASSERT(abs(sumMiWinding) <= DEBUG_LIMIT_WIND_SUM);
SkASSERT(abs(sumSuWinding) <= DEBUG_LIMIT_WIND_SUM);
#endif
- if (fOperand) {
+ if (this->operand()) {
SkTSwap<int>(sumMiWinding, sumSuWinding);
}
- return activeOp(xorMiMask, xorSuMask, index, endIndex, op, &sumMiWinding, &sumSuWinding);
+ return this->activeOp(xorMiMask, xorSuMask, start, end, op, &sumMiWinding, &sumSuWinding);
}
-bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, SkPathOp op,
- int* sumMiWinding, int* sumSuWinding) {
+bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, SkOpSpanBase* start, SkOpSpanBase* end,
+ SkPathOp op, int* sumMiWinding, int* sumSuWinding) {
int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
- setUpWindings(index, endIndex, sumMiWinding, sumSuWinding,
+ this->setUpWindings(start, end, sumMiWinding, sumSuWinding,
&maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
bool miFrom;
bool miTo;
@@ -193,178 +180,31 @@ bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, int index, int endIndex
bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo];
#if DEBUG_ACTIVE_OP
SkDebugf("%s id=%d t=%1.9g tEnd=%1.9g op=%s miFrom=%d miTo=%d suFrom=%d suTo=%d result=%d\n",
- __FUNCTION__, debugID(), span(index).fT, span(endIndex).fT,
+ __FUNCTION__, debugID(), start->t(), end->t(),
SkPathOpsDebug::kPathOpStr[op], miFrom, miTo, suFrom, suTo, result);
#endif
return result;
}
-bool SkOpSegment::activeWinding(int index, int endIndex) {
- int sumWinding = updateWinding(endIndex, index);
- return activeWinding(index, endIndex, &sumWinding);
+bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end) {
+ int sumWinding = updateWinding(end, start);
+ return activeWinding(start, end, &sumWinding);
}
-bool SkOpSegment::activeWinding(int index, int endIndex, int* sumWinding) {
+bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sumWinding) {
int maxWinding;
- setUpWinding(index, endIndex, &maxWinding, sumWinding);
+ setUpWinding(start, end, &maxWinding, sumWinding);
bool from = maxWinding != 0;
bool to = *sumWinding != 0;
bool result = gUnaryActiveEdge[from][to];
return result;
}
-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 {
+void SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
+ SkPathWriter* path, bool active) const {
SkPoint edge[4];
const SkPoint* ePtr;
- int lastT = fTs.count() - 1;
- if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
+ if ((start == &fHead && end == &fTail) || (start == &fTail && end == &fHead)) {
ePtr = fPts;
} else {
// OPTIMIZE? if not active, skip remainder and return xyAtT(end)
@@ -372,7 +212,7 @@ void SkOpSegment::addCurveTo(int start, int end, SkPathWriter* path, bool active
ePtr = edge;
}
if (active) {
- bool reverse = ePtr == fPts && start != 0;
+ bool reverse = ePtr == fPts && start != &fHead;
if (reverse) {
path->deferredMoveLine(ePtr[SkPathOpsVerbToPoints(fVerb)]);
switch (fVerb) {
@@ -388,7 +228,6 @@ void SkOpSegment::addCurveTo(int start, int end, SkPathWriter* path, bool active
default:
SkASSERT(0);
}
- // return ePtr[0];
} else {
path->deferredMoveLine(ePtr[0]);
switch (fVerb) {
@@ -406,1473 +245,350 @@ void SkOpSegment::addCurveTo(int start, int end, SkPathWriter* path, bool active
}
}
}
- // 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();
+SkOpPtT* SkOpSegment::addMissing(double t, SkOpSegment* opp, SkChunkAlloc* allocator) {
+ SkOpSpanBase* existing = NULL;
+ SkOpSpanBase* test = &fHead;
+ double testT;
do {
- fTs[endIndex].fFromAngle = angle;
- } while (++endIndex < spanCount);
-}
-
-void SkOpSegment::addLine(const SkPoint pts[2], bool operand, bool evenOdd) {
- init(pts, SkPath::kLine_Verb, operand, evenOdd);
- fBounds.set(pts, 2);
-}
-
-// 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;
+ if ((testT = test->ptT()->fT) >= t) {
+ if (testT == t) {
+ existing = test;
+ }
+ break;
+ }
+ } while ((test = test->upCast()->next()));
+ SkOpPtT* result;
+ if (existing && existing->contains(opp)) {
+ result = existing->ptT();
+ } else {
+ result = this->addT(t, SkOpSegment::kNoAlias, allocator);
}
- 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);
+ SkASSERT(result);
+ return result;
}
-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) {
+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();
break;
}
- oEndIndex = oStartIndex;
- oStartIndex = other->nextExactSpan(oEndIndex, -1);
- --spanIndex;
- } while (oStartIndex < 0 || !other->span(oStartIndex).fWindSum);
- SkOpAngle& oAngle = other->fAngles.push_back();
- oAngle.set(other, oStartIndex, oEndIndex);
- other->setToAngle(oEndIndex, &oAngle);
- *otherPtr = other;
- return &oAngle;
-}
-
-SkOpAngle* SkOpSegment::addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** anglePtr) {
- int endIndex = nextExactSpan(0, 1);
- SkASSERT(endIndex > 0);
- SkOpAngle& angle = fAngles.push_back();
- *anglePtr = &angle;
- angle.set(this, 0, endIndex);
- setToAngle(endIndex, &angle);
- int spanIndex = 0;
- SkOpSegment* other;
- int oStartIndex, oEndIndex;
- do {
- const SkOpSpan& span = fTs[spanIndex];
- SkASSERT(span.fT < 1);
- other = span.fOther;
- oEndIndex = span.fOtherIndex;
- oStartIndex = other->nextExactSpan(oEndIndex, -1);
- if (oStartIndex >= 0 && other->span(oStartIndex).fWindValue) {
+ oEndSpan = ptT->span();
+ oStartSpan = oEndSpan->prev();
+ if (oStartSpan && oStartSpan->windValue()) {
break;
}
- oStartIndex = oEndIndex;
- oEndIndex = other->nextExactSpan(oStartIndex, 1);
- ++spanIndex;
- } while (oEndIndex < 0 || !other->span(oStartIndex).fWindValue);
- SkOpAngle& oAngle = other->fAngles.push_back();
- oAngle.set(other, oEndIndex, oStartIndex);
- other->setFromAngle(oEndIndex, &oAngle);
+ }
+ SkOpAngle* oAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
+ oAngle->set(oStartSpan, oEndSpan);
+ oStartSpan->setToAngle(oAngle);
*otherPtr = other;
- return &oAngle;
+ return oAngle;
}
-SkOpAngle* SkOpSegment::addSingletonAngles(int step) {
+SkOpAngle* SkOpSegment::addSingletonAngles(int step, SkChunkAlloc* allocator) {
SkOpSegment* other;
SkOpAngle* angle, * otherAngle;
if (step > 0) {
- otherAngle = addSingletonAngleUp(&other, &angle);
+ otherAngle = addSingletonAngleUp(&other, &angle, allocator);
} else {
- otherAngle = addSingletonAngleDown(&other, &angle);
+ otherAngle = addSingletonAngleDown(&other, &angle, allocator);
}
angle->insert(otherAngle);
return angle;
}
-void SkOpSegment::addStartSpan(int endIndex) {
- int index = 0;
- SkOpAngle& angle = fAngles.push_back();
- angle.set(this, index, endIndex);
-#if DEBUG_ANGLE
- debugCheckPointsEqualish(index, endIndex);
-#endif
- setToAngle(endIndex, &angle);
+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()) {
+ break;
+ }
+ oStartSpan = oEndSpan->upCastable();
+ if (oStartSpan && oStartSpan->windValue()) {
+ oEndSpan = oStartSpan->next();
+ break;
+ }
+ }
+ SkOpAngle* oAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
+ oAngle->set(oEndSpan, oStartSpan);
+ oEndSpan->setFromAngle(oAngle);
+ *otherPtr = other;
+ return oAngle;
}
-void SkOpSegment::setToAngle(int endIndex, SkOpAngle* angle) {
- int index = 0;
+SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* allocator) {
+ debugValidate();
+ SkPoint pt = this->ptAtT(t);
+ SkOpSpanBase* span = &fHead;
do {
- fTs[index].fToAngle = angle;
- } while (++index < endIndex);
+ 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;
}
- // 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;
+// 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) {
break;
}
- if (newT == span.fT) {
- if (pt == span.fPt) {
- insertedAt = index;
- break;
- }
- if ((pt == firstPt && newT == 0) || (span.fPt == lastPt && newT == 1)) {
- insertedAt = index;
- break;
- }
- }
+ span->align();
}
- SkOpSpan* span;
- if (insertedAt >= 0) {
- span = fTs.insert(insertedAt);
- } else {
- insertedAt = tCount;
- span = fTs.append();
+ if (!span->aligned()) {
+ span->alignEnd(1, fPts[SkPathOpsVerbToPoints(fVerb)]);
}
- 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;
+ debugValidate();
+}
+
+bool SkOpSegment::BetweenTs(const SkOpSpanBase* lesser, double testT,
+ const SkOpSpanBase* greater) {
+ if (lesser->t() > greater->t()) {
+ SkTSwap<const SkOpSpanBase*>(lesser, greater);
}
- setSpanFlags(pt, newT, span);
- return insertedAt;
+ return approximately_between(lesser->t(), testT, greater->t());
}
-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;
+void SkOpSegment::calcAngles(SkChunkAlloc* allocator) {
+ bool activePrior = !fHead.isCanceled();
+ if (activePrior && !fHead.simple()) {
+ addStartSpan(allocator);
}
- 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;
- }
+ 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);
}
- ++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;
+ 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 (precisely_negative(span[more].fT - span[less].fT)) {
- return;
+ if (activePrior && !fTail.simple()) {
+ addEndSpan(allocator);
}
-// 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;
+void SkOpSegment::checkAngleCoin(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
+ SkOpSpanBase* base = &fHead;
+ SkOpSpan* span;
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;
+ SkOpAngle* angle = base->fromAngle();
+ if (angle && angle->fCheckCoincidence) {
+ angle->checkNearCoincidence();
}
- 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 (base->final()) {
+ break;
}
- if (!startSpan->fWindValue) {
- --fDoneSpans; // added back below
+ span = base->upCast();
+ angle = span->toAngle();
+ if (angle && angle->fCheckCoincidence) {
+ angle->checkNearCoincidence();
}
- 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);
+ } while ((base = span->next()));
}
-// 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--;
+// 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;
+ }
} else {
- other->decrementSpan(oTest);
+ if (inflectionT != startT) {
+ endT = inflectionT;
+ }
}
- } 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);
+ SkDCubic part = cubic.subDivide(startT, endT);
+ for (int index = 0; index < 4; ++index) {
+ edge[index] = part[index].asSkPoint();
+ }
+ } else {
+ subDivide(start, end, edge);
}
-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);
+ 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 (!other->done() && oOutsidePts.count()) {
- other->addCancelOutsides(oOutsidePts[0], oOutsidePts[1], this);
+ 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();
}
- 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;
+ return sum <= 0;
}
-// 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;
- }
- if (oSpan->fT == 0) {
- continue;
- }
- oFrom = other->nextExactSpan(oFrom, -1);
- SkOpSpan* oFromSpan = &other->fTs[oFrom];
- SkASSERT(oFromSpan->fT < 1);
- if (oFromSpan->fWindValue) {
- break;
+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);
}
- } 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;
+ }
+ 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 {
- 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;
+ nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
+ &maxWinding, &sumWinding);
+ last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
}
- angle->insert(oAngle);
+ nextAngle->setLastMarked(last);
}
-void SkOpSegment::alignMultiples(SkTDArray<AlignedSpan>* alignedArray) {
- debugValidate();
- int count = this->count();
- for (int index = 0; index < count; ++index) {
- SkOpSpan& span = fTs[index];
- if (!span.fMultiple) {
- continue;
- }
- int end = nextExactSpan(index, 1);
- SkASSERT(end > index + 1);
- const SkPoint& thisPt = span.fPt;
- while (index < end - 1) {
- SkOpSegment* other1 = span.fOther;
- int oCnt = other1->count();
- for (int idx2 = index + 1; idx2 < end; ++idx2) {
- SkOpSpan& span2 = fTs[idx2];
- SkOpSegment* other2 = span2.fOther;
- for (int oIdx = 0; oIdx < oCnt; ++oIdx) {
- SkOpSpan& oSpan = other1->fTs[oIdx];
- if (oSpan.fOther != other2) {
- continue;
- }
- if (oSpan.fPt == thisPt) {
- goto skipExactMatches;
- }
- }
- for (int oIdx = 0; oIdx < oCnt; ++oIdx) {
- SkOpSpan& oSpan = other1->fTs[oIdx];
- if (oSpan.fOther != other2) {
- continue;
- }
- if (SkDPoint::RoughlyEqual(oSpan.fPt, thisPt)) {
- SkOpSpan& oSpan2 = other2->fTs[oSpan.fOtherIndex];
- if (zero_or_one(span.fOtherT) || zero_or_one(oSpan.fT)
- || zero_or_one(span2.fOtherT) || zero_or_one(oSpan2.fT)) {
- return;
- }
- if (!way_roughly_equal(span.fOtherT, oSpan.fT)
- || !way_roughly_equal(span2.fOtherT, oSpan2.fT)
- || !way_roughly_equal(span2.fOtherT, oSpan.fOtherT)
- || !way_roughly_equal(span.fOtherT, oSpan2.fOtherT)) {
- return;
- }
- alignSpan(thisPt, span.fOtherT, other1, span2.fOtherT,
- other2, &oSpan, alignedArray);
- alignSpan(thisPt, span2.fOtherT, other2, span.fOtherT,
- other1, &oSpan2, alignedArray);
- break;
- }
- }
- skipExactMatches:
- ;
- }
- ++index;
- }
- }
- debugValidate();
-}
-
-void SkOpSegment::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 {
- SkOpSpan& span = this->fTs[--last];
- if (span.fT != 1 && !span.fSmall) {
- break;
- }
- span.fCoincident = true;
- } while (true);
- int oIndex = other->count();
- do {
- SkOpSpan& oSpan = other->fTs[--oIndex];
- if (oSpan.fT != 1 && !oSpan.fSmall) {
- break;
- }
- oSpan.fCoincident = true;
- } while (true);
- do {
- SkOpSpan* test = &this->fTs[index];
- int baseWind = test->fWindValue;
- int baseOpp = test->fOppValue;
- int endIndex = index;
- while (++endIndex <= last) {
- SkOpSpan* endSpan = &this->fTs[endIndex];
- SkASSERT(endSpan->fT < 1);
- if (endSpan->fWindValue != baseWind || endSpan->fOppValue != baseOpp) {
- break;
- }
- endSpan->fCoincident = true;
- }
- SkOpSpan* oTest = &other->fTs[oIndex];
- int oBaseWind = oTest->fWindValue;
- int oBaseOpp = oTest->fOppValue;
- int oStartIndex = oIndex;
- while (--oStartIndex >= 0) {
- SkOpSpan* oStartSpan = &other->fTs[oStartIndex];
- if (oStartSpan->fWindValue != oBaseWind || oStartSpan->fOppValue != oBaseOpp) {
- break;
- }
- oStartSpan->fCoincident = true;
- }
- bool decrement = baseWind && oBaseWind;
- bool bigger = baseWind >= oBaseWind;
- do {
- SkASSERT(test->fT < 1);
- if (decrement) {
- if (binary && bigger) {
- test->fOppValue--;
- } else {
- decrementSpan(test);
- }
- }
- test->fCoincident = true;
- test = &fTs[++index];
- } while (index < endIndex);
- do {
- SkASSERT(oTest->fT < 1);
- if (decrement) {
- if (binary && !bigger) {
- oTest->fOppValue--;
- } else {
- other->decrementSpan(oTest);
- }
- }
- oTest->fCoincident = true;
- oTest = &other->fTs[--oIndex];
- } while (oIndex > oStartIndex);
- } while (index <= last && oIndex >= 0);
- SkASSERT(index > last);
- SkASSERT(oIndex < 0);
-}
-
-void SkOpSegment::blindCoincident(const SkCoincidence& coincidence, SkOpSegment* other) {
- bool binary = fOperand != other->fOperand;
- int index = 0;
- int last = this->count();
- do {
- SkOpSpan& span = this->fTs[--last];
- if (span.fT != 1 && !span.fSmall) {
- break;
- }
- span.fCoincident = true;
- } while (true);
- int oIndex = 0;
- int oLast = other->count();
- do {
- SkOpSpan& oSpan = other->fTs[--oLast];
- if (oSpan.fT != 1 && !oSpan.fSmall) {
- break;
- }
- oSpan.fCoincident = true;
- } while (true);
- do {
- SkOpSpan* test = &this->fTs[index];
- int baseWind = test->fWindValue;
- int baseOpp = test->fOppValue;
- int endIndex = index;
- SkOpSpan* endSpan;
- while (++endIndex <= last) {
- endSpan = &this->fTs[endIndex];
- SkASSERT(endSpan->fT < 1);
- if (endSpan->fWindValue != baseWind || endSpan->fOppValue != baseOpp) {
- break;
- }
- endSpan->fCoincident = true;
- }
- SkOpSpan* oTest = &other->fTs[oIndex];
- int oBaseWind = oTest->fWindValue;
- int oBaseOpp = oTest->fOppValue;
- int oEndIndex = oIndex;
- SkOpSpan* oEndSpan;
- while (++oEndIndex <= oLast) {
- oEndSpan = &this->fTs[oEndIndex];
- SkASSERT(oEndSpan->fT < 1);
- if (oEndSpan->fWindValue != oBaseWind || oEndSpan->fOppValue != oBaseOpp) {
- break;
- }
- oEndSpan->fCoincident = true;
- }
- // consolidate the winding count even if done
- if ((test->fWindValue || test->fOppValue) && (oTest->fWindValue || oTest->fOppValue)) {
- if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
- bumpCoincidentBlind(binary, index, endIndex);
- other->bumpCoincidentOBlind(oIndex, oEndIndex);
- } else {
- other->bumpCoincidentBlind(binary, oIndex, oEndIndex);
- bumpCoincidentOBlind(index, endIndex);
- }
- }
- index = endIndex;
- oIndex = oEndIndex;
- } while (index <= last && oIndex <= oLast);
- SkASSERT(index > last);
- SkASSERT(oIndex > oLast);
-}
-
-void SkOpSegment::bumpCoincidentBlind(bool binary, int index, int endIndex) {
- const SkOpSpan& oTest = fTs[index];
- int oWindValue = oTest.fWindValue;
- int oOppValue = oTest.fOppValue;
- if (binary) {
- SkTSwap<int>(oWindValue, oOppValue);
- }
- do {
- (void) bumpSpan(&fTs[index], oWindValue, oOppValue);
- } while (++index < endIndex);
-}
-
-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;
+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) {
- 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 (!other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts)) {
- return false;
- }
- }
- 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);
+ sumSuWinding = baseSegment->updateOppWinding(baseAngle);
+ if (baseSegment->operand()) {
+ SkTSwap<int>(sumMiWinding, sumSuWinding);
}
}
- 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);
+ 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 {
- 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];
+ nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
+ &maxWinding, &sumWinding);
+ last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
}
- return isCanceled(tIndex) ? -1 : tIndex;
+ nextAngle->setLastMarked(last);
}
// at this point, the span is already ordered, or unorderable
-int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType includeType) {
+int SkOpSegment::computeSum(SkOpSpanBase* start, SkOpSpanBase* end,
+ SkOpAngle::IncludeType includeType) {
SkASSERT(includeType != SkOpAngle::kUnaryXor);
- SkOpAngle* firstAngle = spanToAngle(endIndex, startIndex);
+ SkOpAngle* firstAngle = this->spanToAngle(end, start);
if (NULL == firstAngle || NULL == firstAngle->next()) {
return SK_NaN32;
}
@@ -1898,7 +614,7 @@ int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType
baseAngle = NULL;
continue;
}
- int testWinding = angle->segment()->windSum(angle);
+ int testWinding = angle->starter()->windSum();
if (SK_MinS32 != testWinding) {
baseAngle = angle;
tryReverse = true;
@@ -1906,10 +622,10 @@ int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType
}
if (baseAngle) {
ComputeOneSum(baseAngle, angle, includeType);
- baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
+ baseAngle = SK_MinS32 != angle->starter()->windSum() ? angle : NULL;
}
} while (next != firstAngle);
- if (baseAngle && SK_MinS32 == firstAngle->segment()->windSum(firstAngle)) {
+ if (baseAngle && SK_MinS32 == firstAngle->starter()->windSum()) {
firstAngle = baseAngle;
tryReverse = true;
}
@@ -1925,1101 +641,145 @@ int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType
baseAngle = NULL;
continue;
}
- int testWinding = angle->segment()->windSum(angle);
+ int testWinding = angle->starter()->windSum();
if (SK_MinS32 != testWinding) {
baseAngle = angle;
continue;
}
if (baseAngle) {
ComputeOneSumReverse(baseAngle, angle, includeType);
- baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
+ baseAngle = SK_MinS32 != angle->starter()->windSum() ? angle : NULL;
}
} while (prior != firstAngle);
}
- int minIndex = SkMin32(startIndex, endIndex);
- return windSum(minIndex);
+ return start->starter(end)->windSum();
}
-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 {
+SkOpSpan* SkOpSegment::crossedSpanY(const SkPoint& basePt, double mid, bool opp, bool current,
+ SkScalar* bestY, double* hitT, bool* hitSomething, bool* vertical) {
SkScalar bottom = fBounds.fBottom;
- int bestTIndex = -1;
+ *vertical = false;
if (bottom <= *bestY) {
- return bestTIndex;
+ return NULL;
}
SkScalar top = fBounds.fTop;
if (top >= basePt.fY) {
- return bestTIndex;
+ return NULL;
}
if (fBounds.fLeft > basePt.fX) {
- return bestTIndex;
+ return NULL;
}
if (fBounds.fRight < basePt.fX) {
- return bestTIndex;
+ return NULL;
}
if (fBounds.fLeft == fBounds.fRight) {
- // if vertical, and directly above test point, wait for another one
- return AlmostEqualUlps(basePt.fX, fBounds.fLeft) ? SK_MinS32 : bestTIndex;
- }
- // intersect ray starting at basePt with edge
- SkIntersections intersections;
- // OPTIMIZE: use specialty function that intersects ray with curve,
- // returning t values only for curve (we don't care about t on ray)
- intersections.allowNear(false);
- int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)])
- (fPts, top, bottom, basePt.fX, false);
- if (pts == 0 || (current && pts == 1)) {
- return bestTIndex;
- }
- if (current) {
- SkASSERT(pts > 1);
- int closestIdx = 0;
- double closest = fabs(intersections[0][0] - mid);
- for (int idx = 1; idx < pts; ++idx) {
- double test = fabs(intersections[0][idx] - mid);
- if (closest > test) {
- closestIdx = idx;
- closest = test;
- }
- }
- intersections.quickRemoveOne(closestIdx, --pts);
- }
- double bestT = -1;
- for (int index = 0; index < pts; ++index) {
- double foundT = intersections[0][index];
- if (approximately_less_than_zero(foundT)
- || approximately_greater_than_one(foundT)) {
- continue;
- }
- SkScalar testY = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fY;
- if (approximately_negative(testY - *bestY)
- || approximately_negative(basePt.fY - testY)) {
- continue;
- }
- if (pts > 1 && fVerb == SkPath::kLine_Verb) {
- 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)) {
- return SK_MinS32; // hit vertical, wait for another one
- }
- }
- *bestY = testY;
- bestT = foundT;
- }
- if (bestT < 0) {
- return bestTIndex;
- }
- SkASSERT(bestT >= 0);
- SkASSERT(bestT <= 1);
- int start;
- int end = 0;
- do {
- 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;
- }
- }
- testSpan = &lastSpan + 1;
- while (--testSpan >= &firstSpan) {
- if (testSpan->fLoop) {
- lastLoopT = testSpan->fT;
- break;
- }
- }
- SkASSERT((firstLoopT == -1) == (lastLoopT == -1));
- if (firstLoopT == -1) {
- return false;
- }
- SkASSERT(firstLoopT < lastLoopT);
- testSpan = &firstSpan - 1;
- smallCounts[0] = smallCounts[1] = 0;
- while (++testSpan <= &lastSpan) {
- SkASSERT(approximately_equal(testSpan->fT, firstLoopT) +
- approximately_equal(testSpan->fT, lastLoopT) == 1);
- smallCounts[approximately_equal(testSpan->fT, lastLoopT)]++;
- }
- return true;
-}
-
-double SkOpSegment::calcMissingTEnd(const SkOpSegment* ref, double loEnd, double min, double max,
- double hiEnd, const SkOpSegment* other, int thisStart) {
- if (max >= hiEnd) {
- return -1;
- }
- int end = findOtherT(hiEnd, ref);
- if (end < 0) {
- return -1;
- }
- double tHi = span(end).fT;
- double tLo, refLo;
- if (thisStart >= 0) {
- tLo = span(thisStart).fT;
- refLo = min;
- } else {
- int start1 = findOtherT(loEnd, ref);
- SkASSERT(start1 >= 0);
- tLo = span(start1).fT;
- refLo = loEnd;
- }
- double missingT = (max - refLo) / (hiEnd - refLo);
- missingT = tLo + missingT * (tHi - tLo);
- return missingT;
-}
-
-double SkOpSegment::calcMissingTStart(const SkOpSegment* ref, double loEnd, double min, double max,
- double hiEnd, const SkOpSegment* other, int thisEnd) {
- if (min <= loEnd) {
- return -1;
- }
- int start = findOtherT(loEnd, ref);
- if (start < 0) {
- return -1;
- }
- double tLo = span(start).fT;
- double tHi, refHi;
- if (thisEnd >= 0) {
- tHi = span(thisEnd).fT;
- refHi = max;
- } else {
- int end1 = findOtherT(hiEnd, ref);
- if (end1 < 0) {
- return -1;
- }
- tHi = span(end1).fT;
- refHi = hiEnd;
- }
- double missingT = (min - loEnd) / (refHi - loEnd);
- missingT = tLo + missingT * (tHi - tLo);
- return missingT;
-}
-
-// see if spans with two or more intersections have the same number on the other end
-void SkOpSegment::checkDuplicates() {
- debugValidate();
- SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
- int index;
- int endIndex = 0;
- bool endFound;
- do {
- index = endIndex;
- endIndex = nextExactSpan(index, 1);
- if ((endFound = endIndex < 0)) {
- endIndex = count();
- }
- int dupCount = endIndex - index;
- if (dupCount < 2) {
- continue;
- }
- do {
- const SkOpSpan* thisSpan = &fTs[index];
- if (thisSpan->fNear) {
- continue;
- }
- SkOpSegment* other = thisSpan->fOther;
- int oIndex = thisSpan->fOtherIndex;
- int oStart = other->nextExactSpan(oIndex, -1) + 1;
- int oEnd = other->nextExactSpan(oIndex, 1);
- if (oEnd < 0) {
- oEnd = other->count();
- }
- int oCount = oEnd - oStart;
- // force the other to match its t and this pt if not on an end point
- if (oCount != dupCount) {
- MissingSpan& missing = missingSpans.push_back();
- missing.fOther = NULL;
- SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
- missing.fPt = thisSpan->fPt;
- const SkOpSpan& oSpan = other->span(oIndex);
- if (oCount > dupCount) {
- missing.fSegment = this;
- missing.fT = thisSpan->fT;
- other->checkLinks(&oSpan, &missingSpans);
- } else {
- missing.fSegment = other;
- missing.fT = oSpan.fT;
- checkLinks(thisSpan, &missingSpans);
- }
- if (!missingSpans.back().fOther) {
- missingSpans.pop_back();
- }
- }
- } while (++index < endIndex);
- } while (!endFound);
- int missingCount = missingSpans.count();
- if (missingCount == 0) {
- return;
- }
- SkSTArray<kMissingSpanCount, MissingSpan, true> missingCoincidence;
- for (index = 0; index < missingCount; ++index) {
- MissingSpan& missing = missingSpans[index];
- SkOpSegment* missingOther = missing.fOther;
- if (missing.fSegment == missing.fOther) {
- continue;
- }
-#if 0 // FIXME: this eliminates spurious data from skpwww_argus_presse_fr_41 but breaks
- // skpwww_fashionscandal_com_94 -- calcAngles complains, but I don't understand why
- if (missing.fSegment->containsT(missing.fT, missing.fOther, missing.fOtherT)) {
-#if DEBUG_DUPLICATES
- SkDebugf("skip 1 id=%d t=%1.9g other=%d otherT=%1.9g\n", missing.fSegment->fID,
- missing.fT, missing.fOther->fID, missing.fOtherT);
-#endif
- continue;
- }
- if (missing.fOther->containsT(missing.fOtherT, missing.fSegment, missing.fT)) {
-#if DEBUG_DUPLICATES
- SkDebugf("skip 2 id=%d t=%1.9g other=%d otherT=%1.9g\n", missing.fOther->fID,
- missing.fOtherT, missing.fSegment->fID, missing.fT);
-#endif
- continue;
- }
-#endif
- // skip if adding would insert point into an existing coincindent span
- if (missing.fSegment->inCoincidentSpan(missing.fT, missingOther)
- && missingOther->inCoincidentSpan(missing.fOtherT, this)) {
- continue;
- }
- // skip if the created coincident spans are small
- if (missing.fSegment->coincidentSmall(missing.fPt, missing.fT, missingOther)
- && missingOther->coincidentSmall(missing.fPt, missing.fOtherT, missing.fSegment)) {
- continue;
- }
- const SkOpSpan* added = missing.fSegment->addTPair(missing.fT, missingOther,
- missing.fOtherT, false, missing.fPt);
- if (added && added->fSmall) {
- missing.fSegment->checkSmallCoincidence(*added, &missingCoincidence);
- }
- }
- for (index = 0; index < missingCount; ++index) {
- MissingSpan& missing = missingSpans[index];
- missing.fSegment->fixOtherTIndex();
- missing.fOther->fixOtherTIndex();
- }
- for (index = 0; index < missingCoincidence.count(); ++index) {
- MissingSpan& missing = missingCoincidence[index];
- missing.fSegment->fixOtherTIndex();
- }
- debugValidate();
-}
-
-// look to see if the curve end intersects an intermediary that intersects the other
-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;
+ // if vertical, and directly above test point, wait for another one
+ *vertical = AlmostEqualUlps(basePt.fX, fBounds.fLeft);
+ return NULL;
}
- // 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
+ // intersect ray starting at basePt with edge
+ SkIntersections intersections;
+ // OPTIMIZE: use specialty function that intersects ray with curve,
+ // returning t values only for curve (we don't care about t on ray)
+ intersections.allowNear(false);
+ int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)])
+ (fPts, top, bottom, basePt.fX, false);
+ if (pts == 0 || (current && pts == 1)) {
+ return NULL;
}
- // 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 (current) {
+ SkASSERT(pts > 1);
+ int closestIdx = 0;
+ double closest = fabs(intersections[0][0] - mid);
+ for (int idx = 1; idx < pts; ++idx) {
+ double test = fabs(intersections[0][idx] - mid);
+ if (closest > test) {
+ closestIdx = idx;
+ closest = test;
}
-
}
+ intersections.quickRemoveOne(closestIdx, --pts);
}
-}
-
-// 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) {
+ double bestT = -1;
+ for (int index = 0; index < pts; ++index) {
+ double foundT = intersections[0][index];
+ if (approximately_less_than_zero(foundT)
+ || approximately_greater_than_one(foundT)) {
continue;
}
- SkOpSpan* nextSpan = thisSpan + 1;
- double thisT = thisSpan->fT;
- double nextT = nextSpan->fT;
- if (thisT == nextT) {
+ SkScalar testY = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fY;
+ if (approximately_negative(testY - *bestY)
+ || approximately_negative(basePt.fY - testY)) {
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;
+ if (pts > 1 && fVerb == SkPath::kLine_Verb) {
+ *vertical = true;
+ return NULL; // 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
}
}
+ *bestY = testY;
+ bestT = foundT;
}
- int missingCount = missingSpans.count();
- if (!missingCount) {
- return;
+ if (bestT < 0) {
+ return NULL;
}
- 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);
+ SkASSERT(bestT >= 0);
+ SkASSERT(bestT < 1);
+ SkOpSpanBase* testTSpanBase = &this->fHead;
+ SkOpSpanBase* nextTSpan;
+ double endT = 0;
+ do {
+ nextTSpan = testTSpanBase->upCast()->next();
+ endT = nextTSpan->t();
+ if (endT >= bestT) {
+ break;
}
+ testTSpanBase = nextTSpan;
+ } while (testTSpanBase);
+ SkOpSpan* bestTSpan = NULL;
+ SkOpSpan* testTSpan = testTSpanBase->upCast();
+ if (!testTSpan->isCanceled()) {
+ *hitT = bestT;
+ bestTSpan = testTSpan;
+ *hitSomething = true;
}
- // 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();
- }
+ return bestTSpan;
}
-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));
+void SkOpSegment::detach(const SkOpSpan* span) {
+ if (span->done()) {
+ --this->fDoneCount;
}
- return false;
+ --this->fCount;
}
-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;
+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())) {
+ continue;
}
- if (otherSpan == lastSpan) {
- break;
+ double testDistSq = testPt.distanceSquared(i.pt(index));
+ if (closestDistSq > testDistSq) {
+ closestDistSq = testDistSq;
}
- 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;
+ }
+ return closestDistSq;
}
/*
@@ -3029,71 +789,57 @@ int SkOpSegment::findEndSpan(int endIndex) const {
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<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)
- {
+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) {
// 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
- int min = SkMin32(startIndex, endIndex);
- if (fTs[min].fDone) {
- return NULL;
- }
- 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;
+ SkOpSpan* startSpan = start->starter(end);
+ if (startSpan->done()) {
return NULL;
}
+ markDone(startSpan);
+ *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
return other;
}
- const int end = nextExactSpan(startIndex, step);
- SkASSERT(end >= 0);
- SkASSERT(startIndex - endIndex != 0);
- SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
+ 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));
// more than one viable candidate -- measure angles to find best
-
- int calcWinding = computeSum(startIndex, end, SkOpAngle::kBinaryOpp);
+ int calcWinding = computeSum(start, endNear, SkOpAngle::kBinaryOpp);
bool sortable = calcWinding != SK_NaN32;
if (!sortable) {
*unsortable = true;
- markDoneBinary(SkMin32(startIndex, endIndex));
+ markDone(start->starter(end));
return NULL;
}
- SkOpAngle* angle = spanToAngle(end, startIndex);
+ SkOpAngle* angle = this->spanToAngle(end, start);
if (angle->unorderable()) {
*unsortable = true;
- markDoneBinary(SkMin32(startIndex, endIndex));
+ markDone(start->starter(end));
return NULL;
}
#if DEBUG_SORT
SkDebugf("%s\n", __FUNCTION__);
angle->debugLoop();
#endif
- int sumMiWinding = updateWinding(endIndex, startIndex);
+ int sumMiWinding = updateWinding(end, start);
if (sumMiWinding == SK_MinS32) {
*unsortable = true;
- markDoneBinary(SkMin32(startIndex, endIndex));
+ markDone(start->starter(end));
return NULL;
}
- int sumSuWinding = updateOppWinding(endIndex, startIndex);
+ int sumSuWinding = updateOppWinding(end, start);
if (operand()) {
SkTSwap<int>(sumMiWinding, sumSuWinding);
}
@@ -3110,11 +856,6 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart
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);
}
@@ -3122,30 +863,24 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart
if (nextSegment->done()) {
continue;
}
- if (nextSegment->isTiny(nextAngle)) {
- continue;
- }
if (!activeAngle) {
- (void) nextSegment->markAndChaseDoneBinary(nextAngle->start(), nextAngle->end());
+ (void) nextSegment->markAndChaseDone(nextAngle->start(), nextAngle->end());
}
- SkOpSpan* last = nextAngle->lastMarked();
+ SkOpSpanBase* last = nextAngle->lastMarked();
if (last) {
SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
*chase->append() = last;
#if DEBUG_WINDING
- SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
- last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
- last->fSmall);
+ 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");
#endif
}
} while ((nextAngle = nextAngle->next()) != angle);
-#if DEBUG_ANGLE
- if (foundAngle) {
- foundAngle->debugSameAs(foundAngle);
- }
-#endif
-
- markDoneBinary(SkMin32(startIndex, endIndex));
+ start->segment()->markDone(start->starter(end));
if (!foundAngle) {
return NULL;
}
@@ -3159,62 +894,55 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart
return nextSegment;
}
-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)
- {
+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) {
// 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
- int min = SkMin32(startIndex, endIndex);
- if (fTs[min].fDone) {
- return NULL;
- }
- 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;
+ SkOpSpan* startSpan = start->starter(end);
+ if (startSpan->done()) {
return NULL;
}
+ markDone(startSpan);
+ *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
return other;
}
- const int end = nextExactSpan(startIndex, step);
- SkASSERT(end >= 0);
- SkASSERT(startIndex - endIndex != 0);
- SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
+ 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));
// more than one viable candidate -- measure angles to find best
-
- int calcWinding = computeSum(startIndex, end, SkOpAngle::kUnaryWinding);
+ int calcWinding = computeSum(start, endNear, SkOpAngle::kUnaryWinding);
bool sortable = calcWinding != SK_NaN32;
if (!sortable) {
*unsortable = true;
- markDoneUnary(SkMin32(startIndex, endIndex));
+ markDone(start->starter(end));
+ 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(endIndex, startIndex);
+ int sumWinding = updateWinding(end, start);
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 {
@@ -3224,11 +952,6 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* next
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);
}
@@ -3236,24 +959,24 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* next
if (nextSegment->done()) {
continue;
}
- if (nextSegment->isTiny(nextAngle)) {
- continue;
- }
if (!activeAngle) {
- nextSegment->markAndChaseDoneUnary(nextAngle->start(), nextAngle->end());
+ (void) nextSegment->markAndChaseDone(nextAngle->start(), nextAngle->end());
}
- SkOpSpan* last = nextAngle->lastMarked();
+ SkOpSpanBase* last = nextAngle->lastMarked();
if (last) {
SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
*chase->append() = last;
#if DEBUG_WINDING
- SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
- last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
- last->fSmall);
+ 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");
#endif
}
} while ((nextAngle = nextAngle->next()) != angle);
- markDoneUnary(SkMin32(startIndex, endIndex));
+ start->segment()->markDone(start->starter(end));
if (!foundAngle) {
return NULL;
}
@@ -3267,57 +990,39 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* next
return nextSegment;
}
-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)
- {
+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)
#if DEBUG_WINDING
SkDebugf("%s simple\n", __FUNCTION__);
#endif
- int min = SkMin32(startIndex, endIndex);
- if (fTs[min].fDone) {
+ SkOpSpan* startSpan = start->starter(end);
+ if (startSpan->done()) {
return NULL;
}
- 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());
+ markDone(startSpan);
+ *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
return other;
}
- 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);
+ 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;
+ }
#if DEBUG_SORT
SkDebugf("%s\n", __FUNCTION__);
angle->debugLoop();
@@ -3325,16 +1030,13 @@ SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsort
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;
@@ -3342,7 +1044,7 @@ SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsort
}
nextAngle = nextAngle->next();
} while (nextAngle != angle);
- markDone(SkMin32(startIndex, endIndex), 1);
+ start->segment()->markDone(start->starter(end));
if (!foundAngle) {
return NULL;
}
@@ -3356,105 +1058,39 @@ SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsort
return nextSegment;
}
-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) {
+SkOpSegment* SkOpSegment::findTop(bool firstPass, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr,
+ bool* unsortable, SkChunkAlloc* allocator) {
// iterate through T intersections and return topmost
// topmost tangent from y-min to first pt is closer to horizontal
SkASSERT(!done());
- int firstT = -1;
- /* SkPoint topPt = */ activeLeftTop(&firstT);
- if (firstT < 0) {
+ SkOpSpanBase* firstT = NULL;
+ (void) this->activeLeftTop(&firstT);
+ if (!firstT) {
*unsortable = !firstPass;
- firstT = 0;
- while (fTs[firstT].fDone) {
- SkASSERT(firstT < fTs.count());
- ++firstT;
+ firstT = &fHead;
+ while (firstT->upCast()->done()) {
+ firstT = firstT->upCast()->next();
}
- *tIndexPtr = firstT;
- *endIndexPtr = nextExactSpan(firstT, 1);
+ *startPtr = firstT;
+ *endPtr = firstT->upCast()->next();
return this;
}
// sort the edges to find the leftmost
int step = 1;
- int end;
- if (span(firstT).fDone || (end = nextSpan(firstT, step)) == -1) {
+ SkOpSpanBase* end;
+ if (firstT->final() || firstT->upCast()->done()) {
step = -1;
- end = nextSpan(firstT, step);
- SkASSERT(end != -1);
+ end = firstT->prev();
+ SkASSERT(end);
+ } else {
+ end = firstT->upCast()->next();
}
// 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 != 0);
+ SkASSERT(firstT != end);
SkOpAngle* markAngle = spanToAngle(firstT, end);
if (!markAngle) {
- markAngle = addSingletonAngles(step);
+ markAngle = addSingletonAngles(step, allocator);
}
markAngle->markStops();
const SkOpAngle* baseAngle = markAngle->next() == markAngle && !isVertical() ? markAngle
@@ -3467,7 +1103,7 @@ SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsort
const SkOpAngle* angle = baseAngle;
do {
if (!angle->unorderable()) {
- SkOpSegment* next = angle->segment();
+ const SkOpSegment* next = angle->segment();
SkPathOpsBounds bounds;
next->subDivideBounds(angle->end(), angle->start(), &bounds);
bool nearSame = AlmostEqualUlps(top, bounds.top());
@@ -3495,9 +1131,10 @@ SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsort
*unsortable = angle->unorderable();
if (firstPass || !*unsortable) {
leftSegment = angle->segment();
- *tIndexPtr = angle->end();
- *endIndexPtr = angle->start();
- if (!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fDone) {
+ *startPtr = angle->end();
+ *endPtr = angle->start();
+ const SkOpSpan* firstSpan = (*startPtr)->starter(*endPtr);
+ if (!firstSpan->done()) {
break;
}
}
@@ -3508,157 +1145,52 @@ SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsort
return NULL;
}
if (leftSegment->verb() >= SkPath::kQuad_Verb) {
- const int tIndex = *tIndexPtr;
- const int endIndex = *endIndexPtr;
+ SkOpSpanBase* start = *startPtr;
+ SkOpSpanBase* end = *endPtr;
bool swap;
- if (!leftSegment->clockwise(tIndex, endIndex, &swap)) {
+ if (!leftSegment->clockwise(start, end, &swap)) {
#if DEBUG_SWAP_TOP
- SkDebugf("%s swap=%d inflections=%d serpentine=%d controlledbyends=%d monotonic=%d\n",
+ SkDebugf("%s swap=%d inflections=%d monotonic=%d\n",
__FUNCTION__,
- swap, leftSegment->debugInflections(tIndex, endIndex),
- leftSegment->serpentine(tIndex, endIndex),
- leftSegment->controlsContainedByEnds(tIndex, endIndex),
- leftSegment->monotonicInY(tIndex, endIndex));
+ swap, leftSegment->debugInflections(start, end),
+ leftSegment->monotonicInY(start, end));
#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(*tIndexPtr, *endIndexPtr);
+ SkTSwap(*startPtr, *endPtr);
}
}
}
- SkASSERT(!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fTiny);
return leftSegment;
}
-int SkOpSegment::firstActive(int tIndex) const {
- while (fTs[tIndex].fTiny) {
- SkASSERT(!isCanceled(tIndex));
- ++tIndex;
- }
- return tIndex;
-}
-
-// FIXME: not crazy about this
-// when the intersections are performed, the other index is into an
-// incomplete array. As the array grows, the indices become incorrect
-// 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);
+SkOpGlobalState* SkOpSegment::globalState() const {
+ return contour()->globalState();
}
-void SkOpSegment::init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd) {
- fDoneSpans = 0;
- fOperand = operand;
- fXor = evenOdd;
+void SkOpSegment::init(SkPoint pts[], SkOpContour* contour, SkPath::Verb verb) {
+ fContour = contour;
+ fNext = NULL;
fPts = pts;
fVerb = verb;
- fLoop = fMultiples = fSmall = fTiny = false;
-}
-
-void SkOpSegment::initWinding(int start, int end, SkOpAngle::IncludeType angleIncludeType) {
- int local = spanSign(start, end);
+ 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);
SkDEBUGCODE(bool success);
if (angleIncludeType == SkOpAngle::kBinarySingle) {
- int oppLocal = oppSign(start, end);
+ int oppLocal = SkOpSegment::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);
@@ -3679,12 +1211,13 @@ If there was a winding, then it may or may not need adjusting. If the span the w
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(int start, int end, double tHit, int winding, SkScalar hitDx,
- int oppWind, SkScalar hitOppDx) {
+bool SkOpSegment::initWinding(SkOpSpanBase* start, SkOpSpanBase* end, double tHit,
+ int winding, SkScalar hitDx, int oppWind, SkScalar hitOppDx) {
+ SkASSERT(this == start->segment());
SkASSERT(hitDx || !winding);
SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
- SkASSERT(dx);
- int windVal = windValue(SkMin32(start, end));
+// SkASSERT(dx);
+ int windVal = start->starter(end)->windValue();
#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);
@@ -3693,9 +1226,9 @@ bool SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkSc
if (abs(winding) < abs(sideWind)) {
winding = sideWind;
}
- SkDEBUGCODE(int oppLocal = oppSign(start, end));
+ SkDEBUGCODE(int oppLocal = SkOpSegment::OppSign(start, end));
SkASSERT(hitOppDx || !oppWind || !oppLocal);
- int oppWindVal = oppValue(SkMin32(start, end));
+ int oppWindVal = start->starter(end)->oppValue();
if (!oppWind) {
oppWind = dx < 0 ? oppWindVal : -oppWindVal;
} else if (hitOppDx * dx >= 0) {
@@ -3714,149 +1247,57 @@ bool SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkSc
return success;
}
-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;
- }
- }
+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))) {
return true;
}
- } while (fTs[++tIndex].fT == 0);
+ }
return false;
}
-// 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(&index, &step, &min, &last))) {
- if (other->done()) {
- SkASSERT(!last);
- break;
- }
- other->markDoneBinary(min);
- }
- return last;
+bool SkOpSegment::isXor() const {
+ return fContour->isXor();
}
-SkOpSpan* SkOpSegment::markAndChaseDoneUnary(int index, int endIndex) {
- int step = SkSign32(endIndex - index);
- int min = SkMin32(index, endIndex);
- markDoneUnary(min);
- SkOpSpan* last = NULL;
+SkOpSpanBase* SkOpSegment::markAndChaseDone(SkOpSpanBase* start, SkOpSpanBase* end) {
+ int step = start->step(end);
+ SkOpSpan* minSpan = start->starter(end);
+ markDone(minSpan);
+ SkOpSpanBase* last = NULL;
SkOpSegment* other = this;
- while ((other = other->nextChase(&index, &step, &min, &last))) {
+ while ((other = other->nextChase(&start, &step, &minSpan, &last))) {
if (other->done()) {
SkASSERT(!last);
break;
}
- other->markDoneUnary(min);
+ other->markDone(minSpan);
}
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;
+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;
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);
+ while ((other = other->nextChase(&start, &step, &spanStart, &last))) {
+ if (spanStart->windSum() != SK_MinS32) {
+ SkASSERT(spanStart->windSum() == winding);
SkASSERT(!last);
break;
}
- (void) other->markWinding(min, winding);
+ (void) other->markWinding(spanStart, winding);
}
if (lastPtr) {
*lastPtr = last;
@@ -3864,37 +1305,32 @@ bool SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, SkOp
return success;
}
-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;
+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;
SkOpSegment* other = this;
- 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);
+ 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);
}
SkASSERT(!last);
-#endif
break;
}
- if (fOperand == other->fOperand) {
- (void) other->markWinding(min, winding, oppWinding);
+ if (this->operand() == other->operand()) {
+ (void) other->markWinding(spanStart, winding, oppWinding);
} else {
- (void) other->markWinding(min, oppWinding, winding);
+ (void) other->markWinding(spanStart, oppWinding, winding);
}
}
if (lastPtr) {
@@ -3903,33 +1339,29 @@ bool SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, int
return success;
}
-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) {
+SkOpSpanBase* SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle) {
SkASSERT(angle->segment() == this);
if (UseInnerWinding(maxWinding, sumWinding)) {
maxWinding = sumWinding;
}
- SkOpSpan* last;
- SkAssertResult(markAndChaseWinding(angle, maxWinding, &last));
+ SkOpSpanBase* last;
+ (void) markAndChaseWinding(angle->start(), angle->end(), maxWinding, &last);
#if DEBUG_WINDING
if (last) {
- SkDebugf("%s last id=%d windSum=", __FUNCTION__,
- last->fOther->fTs[last->fOtherIndex].fOther->debugID());
- SkPathOpsDebug::WindingPrintf(last->fWindSum);
- SkDebugf(" small=%d\n", last->fSmall);
+ 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");
}
#endif
return last;
}
-SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
- int oppSumWinding, const SkOpAngle* angle) {
+SkOpSpanBase* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
+ int oppSumWinding, const SkOpAngle* angle) {
SkASSERT(angle->segment() == this);
if (UseInnerWinding(maxWinding, sumWinding)) {
maxWinding = sumWinding;
@@ -3937,440 +1369,161 @@ SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWindi
if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
oppMaxWinding = oppSumWinding;
}
- SkOpSpan* last;
+ SkOpSpanBase* last = NULL;
// caller doesn't require that this marks anything
- (void) markAndChaseWinding(angle, maxWinding, oppMaxWinding, &last);
+ (void) markAndChaseWinding(angle->start(), angle->end(), maxWinding, oppMaxWinding, &last);
#if DEBUG_WINDING
if (last) {
- SkDebugf("%s last id=%d windSum=", __FUNCTION__,
- last->fOther->fTs[last->fOtherIndex].fOther->debugID());
- SkPathOpsDebug::WindingPrintf(last->fWindSum);
- SkDebugf(" small=%d\n", last->fSmall);
+ 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");
}
#endif
return last;
}
-// 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) {
+void SkOpSegment::markDone(SkOpSpan* span) {
+ SkASSERT(this == span->segment());
+ if (span->done()) {
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;
+#if DEBUG_MARK_DONE
+ debugShowNewWinding(__FUNCTION__, span, span->windSum(), span->oppSum());
+#endif
+ span->setDone(true);
+ ++fDoneCount;
+ debugValidate();
}
-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) {
+bool SkOpSegment::markWinding(SkOpSpan* span, int winding) {
+ SkASSERT(this == span->segment());
+ SkASSERT(winding);
+ if (span->done()) {
return false;
}
#if DEBUG_MARK_DONE
- 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);
+ debugShowNewWinding(__FUNCTION__, span, winding);
#endif
- span->fWindSum = winding;
+ span->setWindSum(winding);
+ debugValidate();
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) {
+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(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);
+ debugShowNewWinding(__FUNCTION__, span, winding, oppWinding);
#endif
- span->fOppSum = oppWinding;
+ span->setWindSum(winding);
+ span->setOppSum(oppWinding);
debugValidate();
- if (lastPtr) {
- *lastPtr = span;
- }
return true;
}
-// 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;
- }
- }
+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 (!sumSet) {
- for (int idx = 0; idx < points; ++idx){
- sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY);
- }
+ if (!SkDPoint::ApproximatelyEqual(testPt, base->fPt)) {
+ return false;
}
- 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 !ptsDisjoint(base->fT, base->fPt, testT, testPt);
+}
+
+static SkOpSegment* set_last(SkOpSpanBase** last, SkOpSpanBase* endSpan) {
+ if (last) {
+ *last = endSpan;
}
- return sum <= 0;
+ return NULL;
}
-bool SkOpSegment::monotonicInY(int tStart, int tEnd) const {
+bool SkOpSegment::monotonicInY(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
SkASSERT(fVerb != SkPath::kLine_Verb);
if (fVerb == SkPath::kQuad_Verb) {
- SkDQuad dst = SkDQuad::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
+ SkDQuad dst = SkDQuad::SubDivide(fPts, start->t(), end->t());
return dst.monotonicInY();
}
SkASSERT(fVerb == SkPath::kCubic_Verb);
- SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
+ SkDCubic dst = SkDCubic::SubDivide(fPts, start->t(), end->t());
return dst.monotonicInY();
}
-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) {
+bool SkOpSegment::NextCandidate(SkOpSpanBase* span, SkOpSpanBase** start,
+ SkOpSpanBase** end) {
+ while (span->final() || span->upCast()->done()) {
+ if (span->final()) {
return false;
}
- ++(*end);
+ span = span->upCast()->next();
}
- *start = *end;
- *end = nextExactSpan(*start, 1);
+ *start = span;
+ *end = span->upCast()->next();
return true;
}
-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;
+SkOpSegment* SkOpSegment::nextChase(SkOpSpanBase** startPtr, int* stepPtr, SkOpSpan** minPtr,
+ SkOpSpanBase** last) const {
+ SkOpSpanBase* origStart = *startPtr;
int step = *stepPtr;
- 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;
+ SkOpSpanBase* endSpan = step > 0 ? origStart->upCast()->next() : origStart->prev();
+ SkASSERT(endSpan);
+ SkOpAngle* angle = step > 0 ? endSpan->fromAngle() : endSpan->upCast()->toAngle();
+ SkOpSpanBase* foundSpan;
+ SkOpSpanBase* otherEnd;
SkOpSegment* other;
if (angle == NULL) {
- if (endSpan.fT != 0 && endSpan.fT != 1) {
+ if (endSpan->t() != 0 && endSpan->t() != 1) {
return NULL;
}
- other = endSpan.fOther;
- foundIndex = endSpan.fOtherIndex;
- otherEnd = other->nextExactSpan(foundIndex, step);
+ SkOpPtT* otherPtT = endSpan->ptT()->next();
+ other = otherPtT->segment();
+ foundSpan = otherPtT->span();
+ otherEnd = step > 0 ? foundSpan->upCast()->next() : foundSpan->prev();
} 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
- // return set_last(last, &endSpan);
}
+#endif
other = next->segment();
- foundIndex = end = next->start();
+ foundSpan = endSpan = next->start();
otherEnd = next->end();
}
- int foundStep = foundIndex < otherEnd ? 1 : -1;
+ int foundStep = foundSpan->step(otherEnd);
if (*stepPtr != foundStep) {
- return set_last(last, &endSpan);
+ return set_last(last, endSpan);
}
- SkASSERT(*indexPtr >= 0);
- if (otherEnd < 0) {
+ SkASSERT(*startPtr);
+ if (!otherEnd) {
return NULL;
}
// SkASSERT(otherEnd >= 0);
-#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);
+ 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);
}
-#endif
- *indexPtr = foundIndex;
+ *startPtr = foundSpan;
*stepPtr = foundStep;
if (minPtr) {
*minPtr = foundMin;
@@ -4378,101 +1531,217 @@ SkOpSegment* SkOpSegment::nextChase(int* indexPtr, int* stepPtr, int* minPtr,
return other;
}
-// 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;
+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();
}
- return to;
- }
- return -1;
+ } while ((span = span->next()->upCastable()));
}
-// 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) {
+// 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()) {
continue;
}
- return to;
- }
- } else {
- while (fTs[from].fTiny) {
- from++;
+ if (opp->verb() == SkPath::kLine_Verb) {
+ 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);
+ }
}
- const SkOpSpan& fromSpan = fTs[from];
- int count = fTs.count();
- while (++to < count) {
- const SkOpSpan& span = fTs[to];
- if (precisely_negative(span.fT - fromSpan.fT)) {
+ } 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;
}
- return to;
}
- }
- return -1;
+ 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;
}
-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::operand() const {
+ return fContour->operand();
}
-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;
+bool SkOpSegment::oppXor() const {
+ return fContour->oppXor();
}
-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;
- }
+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(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);
+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);
if (operand()) {
*maxWinding = *sumSuWinding;
*sumWinding = *sumSuWinding -= deltaSum;
@@ -4484,130 +1753,94 @@ void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding, int*
*oppMaxWinding = *sumSuWinding;
*oppSumWinding = *sumSuWinding -= oppDeltaSum;
}
-#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
+ SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
+ SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*oppSumWinding) <= DEBUG_LIMIT_WIND_SUM);
}
void SkOpSegment::sortAngles() {
- int spanCount = fTs.count();
- if (spanCount <= 2) {
- return;
- }
- int index = 0;
+ SkOpSpanBase* span = &this->fHead;
do {
- SkOpAngle* fromAngle = fTs[index].fFromAngle;
- SkOpAngle* toAngle = fTs[index].fToAngle;
+ SkOpAngle* fromAngle = span->fromAngle();
+ SkOpAngle* toAngle = span->final() ? NULL : span->upCast()->toAngle();
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
- if (toAngle) {
- if (!baseAngle) {
- baseAngle = toAngle;
- if (inLoop(baseAngle, spanCount, &index)) {
- continue;
- }
- } else {
- SkDEBUGCODE(int newIndex = index);
- SkASSERT(!inLoop(baseAngle, spanCount, &newIndex) && newIndex == index);
+ SkOpAngle* baseAngle = fromAngle;
+ if (fromAngle && toAngle) {
#if DEBUG_ANGLE
- SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
- index);
- wroteAfterHeader = true;
+ SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), span->t(),
+ span->debugID());
+ wroteAfterHeader = true;
#endif
- baseAngle->insert(toAngle);
- }
+ fromAngle->insert(toAngle);
+ } else if (!fromAngle) {
+ baseAngle = toAngle;
}
- SkOpAngle* nextFrom, * nextTo;
- int firstIndex = index;
+ SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
do {
- SkOpSpan& span = fTs[index];
- SkOpSegment* other = span.fOther;
- SkOpSpan& oSpan = other->fTs[span.fOtherIndex];
- SkOpAngle* oAngle = oSpan.fFromAngle;
+ SkOpSpanBase* oSpan = ptT->span();
+ if (oSpan == span) {
+ continue;
+ }
+ SkOpAngle* oAngle = oSpan->fromAngle();
if (oAngle) {
#if DEBUG_ANGLE
if (!wroteAfterHeader) {
- SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
- index);
+ SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(),
+ span->t(), span->debugID());
wroteAfterHeader = true;
}
#endif
- if (!oAngle->loopContains(*baseAngle)) {
+ if (!oAngle->loopContains(baseAngle)) {
baseAngle->insert(oAngle);
}
}
- oAngle = oSpan.fToAngle;
- if (oAngle) {
+ if (!oSpan->final()) {
+ oAngle = oSpan->upCast()->toAngle();
+ if (oAngle) {
#if DEBUG_ANGLE
- if (!wroteAfterHeader) {
- SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
- index);
- wroteAfterHeader = true;
- }
+ 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);
+ if (!oAngle->loopContains(baseAngle)) {
+ baseAngle->insert(oAngle);
+ }
}
}
- if (++index == spanCount) {
- break;
+ } while ((ptT = ptT->next()) != stopPtT);
+ if (baseAngle->loopCount() == 1) {
+ span->setFromAngle(NULL);
+ if (toAngle) {
+ span->upCast()->setToAngle(NULL);
}
- nextFrom = fTs[index].fFromAngle;
- nextTo = fTs[index].fToAngle;
- } while (fromAngle == nextFrom && toAngle == nextTo);
- if (baseAngle && baseAngle->loopCount() == 1) {
- index = firstIndex;
- do {
- SkOpSpan& span = fTs[index];
- span.fFromAngle = span.fToAngle = NULL;
- if (++index == spanCount) {
- break;
- }
- nextFrom = fTs[index].fFromAngle;
- nextTo = fTs[index].fToAngle;
- } while (fromAngle == nextFrom && toAngle == nextTo);
baseAngle = NULL;
}
#if DEBUG_SORT
SkASSERT(!baseAngle || baseAngle->loopCount() > 1);
#endif
- } while (index < spanCount);
+ } while (!span->final() && (span = span->upCast()->next()));
}
// return true if midpoints were computed
-bool SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const {
+bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
+ SkPoint edge[4]) const {
SkASSERT(start != end);
- edge[0] = fTs[start].fPt;
+ const SkOpPtT& startPtT = *start->ptT();
+ const SkOpPtT& endPtT = *end->ptT();
+ edge[0] = startPtT.fPt;
int points = SkPathOpsVerbToPoints(fVerb);
- edge[points] = fTs[end].fPt;
+ edge[points] = endPtT.fPt;
if (fVerb == SkPath::kLine_Verb) {
return false;
}
- double startT = fTs[start].fT;
- double endT = fTs[end].fT;
+ double startT = startPtT.fT;
+ double endT = endPtT.fT;
if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
// don't compute midpoints if we already have them
if (fVerb == SkPath::kQuad_Verb) {
@@ -4637,17 +1870,19 @@ bool SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const {
return true;
}
-// return true if midpoints were computed
-bool SkOpSegment::subDivide(int start, int end, SkDCubic* result) const {
+bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
+ SkDCubic* result) const {
SkASSERT(start != end);
- (*result)[0].set(fTs[start].fPt);
+ const SkOpPtT& startPtT = *start->ptT();
+ const SkOpPtT& endPtT = *end->ptT();
+ (*result)[0].set(startPtT.fPt);
int points = SkPathOpsVerbToPoints(fVerb);
- (*result)[points].set(fTs[end].fPt);
+ (*result)[points].set(endPtT.fPt);
if (fVerb == SkPath::kLine_Verb) {
return false;
}
- double startT = fTs[start].fT;
- double endT = fTs[end].fT;
+ double startT = startPtT.fT;
+ double endT = endPtT.fT;
if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
// don't compute midpoints if we already have them
if (fVerb == SkPath::kQuad_Verb) {
@@ -4655,7 +1890,7 @@ bool SkOpSegment::subDivide(int start, int end, SkDCubic* result) const {
return false;
}
SkASSERT(fVerb == SkPath::kCubic_Verb);
- if (start < end) {
+ if (startT == 0) {
(*result)[1].set(fPts[1]);
(*result)[2].set(fPts[2]);
return false;
@@ -4673,49 +1908,29 @@ bool SkOpSegment::subDivide(int start, int end, SkDCubic* result) const {
return true;
}
-void SkOpSegment::subDivideBounds(int start, int end, SkPathOpsBounds* bounds) const {
+void SkOpSegment::subDivideBounds(const SkOpSpanBase* start, const SkOpSpanBase* end,
+ SkPathOpsBounds* bounds) const {
SkPoint edge[4];
subDivide(start, end, edge);
(bounds->*SetCurveBounds[SkPathOpsVerbToPoints(fVerb)])(edge);
}
-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) {
+void SkOpSegment::undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end) {
+ SkOpSpan* span = this->head();
+ do {
+ if (!span->done()) {
break;
}
- }
- 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;
+ } while ((span = span->next()->upCastable()));
+ SkASSERT(span);
+ *start = span;
+ *end = span->next();
}
-int SkOpSegment::updateOppWinding(int index, int endIndex) const {
- int lesser = SkMin32(index, endIndex);
- int oppWinding = oppSum(lesser);
- int oppSpanWinding = oppSign(index, endIndex);
+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);
if (oppSpanWinding && UseInnerWinding(oppWinding - oppSpanWinding, oppWinding)
&& oppWinding != SK_MaxS32) {
oppWinding -= oppSpanWinding;
@@ -4724,24 +1939,24 @@ int SkOpSegment::updateOppWinding(int index, int endIndex) const {
}
int SkOpSegment::updateOppWinding(const SkOpAngle* angle) const {
- int startIndex = angle->start();
- int endIndex = angle->end();
- return updateOppWinding(endIndex, startIndex);
+ const SkOpSpanBase* startSpan = angle->start();
+ const SkOpSpanBase* endSpan = angle->end();
+ return updateOppWinding(endSpan, startSpan);
}
int SkOpSegment::updateOppWindingReverse(const SkOpAngle* angle) const {
- int startIndex = angle->start();
- int endIndex = angle->end();
- return updateOppWinding(startIndex, endIndex);
+ const SkOpSpanBase* startSpan = angle->start();
+ const SkOpSpanBase* endSpan = angle->end();
+ return updateOppWinding(startSpan, endSpan);
}
-int SkOpSegment::updateWinding(int index, int endIndex) const {
- int lesser = SkMin32(index, endIndex);
- int winding = windSum(lesser);
+int SkOpSegment::updateWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
+ const SkOpSpan* lesser = start->starter(end);
+ int winding = lesser->windSum();
if (winding == SK_MinS32) {
return winding;
}
- int spanWinding = spanSign(index, endIndex);
+ int spanWinding = SkOpSegment::SpanSign(start, end);
if (winding && UseInnerWinding(winding - spanWinding, winding)
&& winding != SK_MaxS32) {
winding -= spanWinding;
@@ -4750,26 +1965,15 @@ int SkOpSegment::updateWinding(int index, int endIndex) const {
}
int SkOpSegment::updateWinding(const SkOpAngle* angle) const {
- 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;
+ const SkOpSpanBase* startSpan = angle->start();
+ const SkOpSpanBase* endSpan = angle->end();
+ return updateWinding(endSpan, startSpan);
}
int SkOpSegment::updateWindingReverse(const SkOpAngle* angle) const {
- int startIndex = angle->start();
- int endIndex = angle->end();
- return updateWindingReverse(endIndex, startIndex);
+ const SkOpSpanBase* startSpan = angle->start();
+ const SkOpSpanBase* endSpan = angle->end();
+ return updateWinding(startSpan, endSpan);
}
// OPTIMIZATION: does the following also work, and is it any faster?
@@ -4784,25 +1988,17 @@ bool SkOpSegment::UseInnerWinding(int outerWinding, int innerWinding) {
return result;
}
-bool SkOpSegment::UseInnerWindingReverse(int outerWinding, int innerWinding) {
- SkASSERT(outerWinding != SK_MaxS32);
- SkASSERT(innerWinding != SK_MaxS32);
- int absOut = abs(outerWinding);
- int absIn = abs(innerWinding);
- bool result = absOut == absIn ? true : absOut < absIn;
- return result;
-}
-
-int SkOpSegment::windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar* dx) const {
- if (approximately_zero(tHit - t(tIndex))) { // if we hit the end of a span, disregard
+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
return SK_MinS32;
}
- int winding = crossOpp ? oppSum(tIndex) : windSum(tIndex);
+ int winding = crossOpp ? span->oppSum() : span->windSum();
SkASSERT(winding != SK_MinS32);
- int windVal = crossOpp ? oppValue(tIndex) : windValue(tIndex);
+ int windVal = crossOpp ? span->oppValue() : span->windValue();
#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, t(tIndex), winding, windVal);
+ debugID(), crossOpp, tHit, span->t(), 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;
@@ -4828,20 +2024,6 @@ int SkOpSegment::windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar* dx
}
int SkOpSegment::windSum(const SkOpAngle* angle) const {
- 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;
+ const SkOpSpan* minSpan = angle->start()->starter(angle->end());
+ return minSpan->windSum();
}
« 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