| Index: src/pathops/SkOpSegment.cpp
|
| diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp
|
| index c0ef1f8e1170ea8989beccadf6507c267ce7d579..4d11eb39e8259b73c79e313c109e1aa314f94402 100644
|
| --- a/src/pathops/SkOpSegment.cpp
|
| +++ b/src/pathops/SkOpSegment.cpp
|
| @@ -417,6 +417,8 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT, bool i
|
| // binary search?
|
| int insertedAt = -1;
|
| size_t tCount = fTs.count();
|
| + const SkPoint& firstPt = fPts[0];
|
| + const SkPoint& lastPt = fPts[SkPathOpsVerbToPoints(fVerb)];
|
| for (size_t index = 0; index < tCount; ++index) {
|
| // OPTIMIZATION: if there are three or more identical Ts, then
|
| // the fourth and following could be further insertion-sorted so
|
| @@ -424,10 +426,21 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT, bool i
|
| // This could later limit segment tests to the two adjacent
|
| // neighbors, although it doesn't help with determining which
|
| // circular direction to go in.
|
| - if (newT < fTs[index].fT) {
|
| + const SkOpSpan& span = fTs[index];
|
| + if (newT < span.fT) {
|
| insertedAt = index;
|
| 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;
|
| + }
|
| + }
|
| }
|
| SkOpSpan* span;
|
| if (insertedAt >= 0) {
|
| @@ -502,6 +515,10 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT, bool i
|
| }
|
| double tEndInterval = span[more].fT - newT;
|
| if (precisely_negative(tEndInterval)) {
|
| + if ((span->fTiny = span[more].fTiny)) {
|
| + span->fDone = true;
|
| + ++fDoneSpans;
|
| + }
|
| break;
|
| }
|
| if (fVerb == SkPath::kCubic_Verb) {
|
| @@ -556,11 +573,16 @@ void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpS
|
| SkASSERT(index < fTs.count());
|
| ++index;
|
| }
|
| + while (index > 0 && fTs[index].fT == fTs[index - 1].fT) {
|
| + --index;
|
| + }
|
| int oIndex = other->fTs.count();
|
| while (startPt != other->fTs[--oIndex].fPt) { // look for startPt match
|
| SkASSERT(oIndex > 0);
|
| }
|
| - while (startPt == other->fTs[--oIndex].fPt) { // look for first point beyond match
|
| + double oStartT = other->fTs[oIndex].fT;
|
| + // look for first point beyond match
|
| + while (startPt == other->fTs[--oIndex].fPt || oStartT == other->fTs[oIndex].fT) {
|
| SkASSERT(oIndex > 0);
|
| }
|
| SkOpSpan* test = &fTs[index];
|
| @@ -574,7 +596,9 @@ void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpS
|
| bool track = test->fWindValue || oTest->fWindValue;
|
| bool bigger = test->fWindValue >= oTest->fWindValue;
|
| const SkPoint& testPt = test->fPt;
|
| + double testT = test->fT;
|
| const SkPoint& oTestPt = oTest->fPt;
|
| + double oTestT = oTest->fT;
|
| do {
|
| if (decrement) {
|
| if (binary && bigger) {
|
| @@ -587,7 +611,7 @@ void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpS
|
| }
|
| SkASSERT(index < fTs.count() - 1);
|
| test = &fTs[++index];
|
| - } while (testPt == test->fPt);
|
| + } while (testPt == test->fPt || testT == test->fT);
|
| SkDEBUGCODE(int originalWindValue = oTest->fWindValue);
|
| do {
|
| SkASSERT(oTest->fT < 1);
|
| @@ -605,9 +629,8 @@ void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpS
|
| break;
|
| }
|
| oTest = &other->fTs[--oIndex];
|
| - } while (oTestPt == oTest->fPt);
|
| - SkASSERT(endPt != test->fPt || oTestPt == endPt);
|
| - } while (endPt != test->fPt);
|
| + } while (oTestPt == oTest->fPt || oTestT == oTest->fT);
|
| + } while (endPt != test->fPt && test->fT < 1);
|
| // FIXME: determine if canceled edges need outside ts added
|
| int outCount = outsidePts.count();
|
| if (!done() && outCount) {
|
| @@ -645,7 +668,7 @@ void SkOpSegment::bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* in
|
| TrackOutside(outsideTs, oStartPt);
|
| }
|
| end = &fTs[++index];
|
| - } while (end->fPt == test->fPt);
|
| + } while ((end->fPt == test->fPt || end->fT == test->fT) && end->fT < 1);
|
| *indexPtr = index;
|
| }
|
|
|
| @@ -685,10 +708,11 @@ void SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, int* oIndexPtr,
|
| SkOpSpan* oEnd = oTest;
|
| const SkPoint& startPt = test.fPt;
|
| const SkPoint& oStartPt = oTest->fPt;
|
| - if (oStartPt == oEnd->fPt) {
|
| + double oStartT = oTest->fT;
|
| + if (oStartPt == oEnd->fPt || oStartT == oEnd->fT) {
|
| TrackOutside(oOutsidePts, startPt);
|
| }
|
| - while (oStartPt == oEnd->fPt) {
|
| + while (oStartPt == oEnd->fPt || oStartT == oEnd->fT) {
|
| zeroSpan(oEnd);
|
| oEnd = &fTs[++oIndex];
|
| }
|
| @@ -704,7 +728,7 @@ void SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, int* oIndexPtr,
|
|
|
| // set spans from start to end to increment the greater by one and decrement
|
| // the lesser
|
| -void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt,
|
| +void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
|
| SkOpSegment* other) {
|
| bool binary = fOperand != other->fOperand;
|
| int index = 0;
|
| @@ -712,15 +736,24 @@ void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt,
|
| SkASSERT(index < fTs.count());
|
| ++index;
|
| }
|
| + double startT = fTs[index].fT;
|
| + while (index > 0 && 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 && 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;
|
| SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
|
| @@ -760,13 +793,31 @@ void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt,
|
| }
|
| test = &fTs[index];
|
| testPt = &test->fPt;
|
| - if (endPt == *testPt) {
|
| + testT = test->fT;
|
| + if (endPt == *testPt || endT == testT) {
|
| break;
|
| }
|
| oTest = &other->fTs[oIndex];
|
| oTestPt = &oTest->fPt;
|
| SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
|
| } while (endPt != *oTestPt);
|
| + if (endPt != *testPt && endT != testT) { // in rare cases, one may have ended before the other
|
| + 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) {
|
| + test->fDone = true;
|
| + ++fDoneSpans;
|
| + }
|
| + }
|
| + test = &fTs[++index];
|
| + testPt = &test->fPt;
|
| + } while (endPt != *testPt);
|
| + }
|
| int outCount = outsidePts.count();
|
| if (!done() && outCount) {
|
| addCoinOutsides(outsidePts[0], endPt, other);
|
| @@ -1325,7 +1376,7 @@ bool SkOpSegment::decrementSpan(SkOpSpan* span) {
|
| }
|
|
|
| bool SkOpSegment::bumpSpan(SkOpSpan* span, int windDelta, int oppDelta) {
|
| - SkASSERT(!span->fDone || span->fTiny);
|
| + SkASSERT(!span->fDone || span->fTiny || span->fSmall);
|
| span->fWindValue += windDelta;
|
| SkASSERT(span->fWindValue >= 0);
|
| span->fOppValue += oppDelta;
|
| @@ -1384,17 +1435,20 @@ void SkOpSegment::checkEnds() {
|
| }
|
| 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 nextPeeker;
|
| + goto nextPeekIndex;
|
| }
|
| double midT = (tSpan.fOtherT + matchT) / 2;
|
| if (match->betweenPoints(midT, tSpan.fPt, peekSpan.fPt)) {
|
| - goto nextPeeker;
|
| + goto nextPeekIndex;
|
| }
|
| }
|
| }
|
| @@ -1414,18 +1468,22 @@ void SkOpSegment::checkEnds() {
|
| #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.fCommand = MissingSpan::kAddMissing;
|
| - missing.fT = t;
|
| - missing.fOther = match;
|
| - missing.fOtherT = matchT;
|
| - missing.fPt = peekSpan.fPt;
|
| - }
|
| -nextPeeker:
|
| - ;
|
| + {
|
| + MissingSpan& missing = missingSpans.push_back();
|
| + SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
|
| + missing.fCommand = MissingSpan::kAddMissing;
|
| + missing.fT = t;
|
| + missing.fOther = match;
|
| + missing.fOtherT = matchT;
|
| + missing.fPt = peekSpan.fPt;
|
| + }
|
| + break;
|
| +nextPeekIndex:
|
| + ;
|
| + }
|
| }
|
| if (missingSpans.count() == 0) {
|
| + debugValidate();
|
| return;
|
| }
|
| // if one end is near the other point, look for a coincident span
|
| @@ -1690,6 +1748,11 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart
|
| SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted;
|
| int calcWinding = computeSum(startIndex, end, SkOpAngle::kBinaryOpp, &angles, &sorted);
|
| bool sortable = calcWinding != SK_NaN32;
|
| + if (sortable && sorted.count() == 0) {
|
| + // if no edge has a computed winding sum, we can go no further
|
| + *unsortable = true;
|
| + return NULL;
|
| + }
|
| int angleCount = angles.count();
|
| int firstIndex = findStartingEdge(sorted, startIndex, end);
|
| SkASSERT(!sortable || firstIndex >= 0);
|
| @@ -2204,14 +2267,17 @@ void SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkSc
|
| }
|
| }
|
| (void) markAndChaseWinding(start, end, winding, oppWind);
|
| + // OPTIMIZATION: the reverse mark and chase could skip the first marking
|
| + (void) markAndChaseWinding(end, start, winding, oppWind);
|
| }
|
|
|
| // 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 {
|
| +bool SkOpSegment::isMissing(double startT, const SkPoint& pt) const {
|
| size_t tCount = fTs.count();
|
| for (size_t index = 0; index < tCount; ++index) {
|
| - if (approximately_zero(startT - fTs[index].fT)) {
|
| + const SkOpSpan& span = fTs[index];
|
| + if (approximately_zero(startT - span.fT) && pt == span.fPt) {
|
| return false;
|
| }
|
| }
|
| @@ -2352,9 +2418,10 @@ SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, bool activeAngl
|
| }
|
| #if DEBUG_WINDING
|
| if (last) {
|
| - SkDebugf("%s last id=%d windSum=%d small=%d\n", __FUNCTION__,
|
| - last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
|
| - last->fSmall);
|
| + SkDebugf("%s last id=%d windSum=", __FUNCTION__,
|
| + last->fOther->fTs[last->fOtherIndex].fOther->debugID());
|
| + SkPathOpsDebug::WindingPrintf(last->fWindSum);
|
| + SkDebugf(" small=%d\n", last->fSmall);
|
| }
|
| #endif
|
| return last;
|
| @@ -2377,9 +2444,10 @@ SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWindi
|
| }
|
| #if DEBUG_WINDING
|
| if (last) {
|
| - SkDebugf("%s last id=%d windSum=%d small=%d\n", __FUNCTION__,
|
| - last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
|
| - last->fSmall);
|
| + SkDebugf("%s last id=%d windSum=", __FUNCTION__,
|
| + last->fOther->fTs[last->fOtherIndex].fOther->debugID());
|
| + SkPathOpsDebug::WindingPrintf(last->fWindSum);
|
| + SkDebugf(" small=%d\n", last->fSmall);
|
| }
|
| #endif
|
| return last;
|
| @@ -2491,7 +2559,7 @@ SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int windi
|
| SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding,
|
| int oppWinding) {
|
| SkOpSpan& span = fTs[tIndex];
|
| - if (span.fDone) {
|
| + if (span.fDone && !span.fSmall) {
|
| return NULL;
|
| }
|
| #if DEBUG_MARK_DONE
|
| @@ -3134,6 +3202,9 @@ 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;
|
| @@ -3162,8 +3233,8 @@ void SkOpSegment::debugAddTPair(double t, const SkOpSegment& other, double other
|
| #endif
|
|
|
| #if DEBUG_CONCIDENT
|
| -void SkOpSegment::debugShowTs() const {
|
| - SkDebugf("%s id=%d", __FUNCTION__, fID);
|
| +void SkOpSegment::debugShowTs(const char* prefix) const {
|
| + SkDebugf("%s %s id=%d", __FUNCTION__, prefix, fID);
|
| int lastWind = -1;
|
| int lastOpp = -1;
|
| double lastT = -1;
|
|
|