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