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