Index: src/pathops/SkPathOpsCommon.cpp |
diff --git a/src/pathops/SkPathOpsCommon.cpp b/src/pathops/SkPathOpsCommon.cpp |
index b0fd822a9d359d08ca8f378b71e8551a466f011c..1a5bfc18896d11375766b5971aff9065483f26d5 100644 |
--- a/src/pathops/SkPathOpsCommon.cpp |
+++ b/src/pathops/SkPathOpsCommon.cpp |
@@ -5,25 +5,47 @@ |
* found in the LICENSE file. |
*/ |
#include "SkAddIntersections.h" |
-#include "SkOpCoincidence.h" |
#include "SkOpEdgeBuilder.h" |
#include "SkPathOpsCommon.h" |
#include "SkPathWriter.h" |
#include "SkTSort.h" |
-static int contourRangeCheckY(const SkTDArray<SkOpContour* >& contourList, |
- SkOpSegment** currentPtr, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr, |
- double* bestHit, SkScalar* bestDx, bool* tryAgain, double* midPtr, bool opp) { |
- SkOpSpanBase* start = *startPtr; |
- SkOpSpanBase* end = *endPtr; |
+static void alignMultiples(SkTArray<SkOpContour*, true>* contourList, |
+ SkTDArray<SkOpSegment::AlignedSpan>* aligned) { |
+ int contourCount = (*contourList).count(); |
+ for (int cTest = 0; cTest < contourCount; ++cTest) { |
+ SkOpContour* contour = (*contourList)[cTest]; |
+ if (contour->hasMultiples()) { |
+ contour->alignMultiples(aligned); |
+ } |
+ } |
+} |
+ |
+static void alignCoincidence(SkTArray<SkOpContour*, true>* contourList, |
+ const SkTDArray<SkOpSegment::AlignedSpan>& aligned) { |
+ int contourCount = (*contourList).count(); |
+ for (int cTest = 0; cTest < contourCount; ++cTest) { |
+ SkOpContour* contour = (*contourList)[cTest]; |
+ int count = aligned.count(); |
+ for (int index = 0; index < count; ++index) { |
+ contour->alignCoincidence(aligned[index]); |
+ } |
+ } |
+} |
+ |
+static int contourRangeCheckY(const SkTArray<SkOpContour*, true>& contourList, SkOpSegment** currentPtr, |
+ int* indexPtr, int* endIndexPtr, double* bestHit, SkScalar* bestDx, |
+ bool* tryAgain, double* midPtr, bool opp) { |
+ const int index = *indexPtr; |
+ const int endIndex = *endIndexPtr; |
const double mid = *midPtr; |
const SkOpSegment* current = *currentPtr; |
- double tAtMid = SkOpSegment::TAtMid(start, end, mid); |
+ double tAtMid = current->tAtMid(index, endIndex, mid); |
SkPoint basePt = current->ptAtT(tAtMid); |
int contourCount = contourList.count(); |
SkScalar bestY = SK_ScalarMin; |
SkOpSegment* bestSeg = NULL; |
- SkOpSpan* bestTSpan = NULL; |
+ int bestTIndex = 0; |
bool bestOpp; |
bool hitSomething = false; |
for (int cTest = 0; cTest < contourCount; ++cTest) { |
@@ -35,38 +57,37 @@ |
if (bestY > contour->bounds().fBottom) { |
continue; |
} |
- SkOpSegment* testSeg = contour->first(); |
- SkASSERT(testSeg); |
- do { |
+ int segmentCount = contour->segments().count(); |
+ for (int test = 0; test < segmentCount; ++test) { |
+ SkOpSegment* testSeg = &contour->segments()[test]; |
SkScalar testY = bestY; |
double testHit; |
- bool vertical; |
- SkOpSpan* testTSpan = testSeg->crossedSpanY(basePt, tAtMid, testOpp, |
- testSeg == current, &testY, &testHit, &hitSomething, &vertical); |
- if (!testTSpan) { |
- if (vertical) { |
+ int testTIndex = testSeg->crossedSpanY(basePt, &testY, &testHit, &hitSomething, tAtMid, |
+ testOpp, testSeg == current); |
+ if (testTIndex < 0) { |
+ if (testTIndex == SK_MinS32) { |
hitSomething = true; |
bestSeg = NULL; |
goto abortContours; // vertical encountered, return and try different point |
} |
continue; |
} |
- if (testSeg == current && SkOpSegment::BetweenTs(start, testHit, end)) { |
- double baseT = start->t(); |
- double endT = end->t(); |
+ if (testSeg == current && current->betweenTs(index, testHit, endIndex)) { |
+ double baseT = current->t(index); |
+ double endT = current->t(endIndex); |
double newMid = (testHit - baseT) / (endT - baseT); |
#if DEBUG_WINDING |
- double midT = SkOpSegment::TAtMid(start, end, mid); |
- SkPoint midXY = current->ptAtT(midT); |
- double newMidT = SkOpSegment::TAtMid(start, end, newMid); |
- SkPoint newXY = current->ptAtT(newMidT); |
+ double midT = current->tAtMid(index, endIndex, mid); |
+ SkPoint midXY = current->xyAtT(midT); |
+ double newMidT = current->tAtMid(index, endIndex, newMid); |
+ SkPoint newXY = current->xyAtT(newMidT); |
SkDebugf("%s [%d] mid=%1.9g->%1.9g s=%1.9g (%1.9g,%1.9g) m=%1.9g (%1.9g,%1.9g)" |
" n=%1.9g (%1.9g,%1.9g) e=%1.9g (%1.9g,%1.9g)\n", __FUNCTION__, |
current->debugID(), mid, newMid, |
- baseT, start->pt().fX, start->pt().fY, |
+ baseT, current->xAtT(index), current->yAtT(index), |
baseT + mid * (endT - baseT), midXY.fX, midXY.fY, |
baseT + newMid * (endT - baseT), newXY.fX, newXY.fY, |
- endT, end->pt().fX, end->pt().fY); |
+ endT, current->xAtT(endIndex), current->yAtT(endIndex)); |
#endif |
*midPtr = newMid * 2; // calling loop with divide by 2 before continuing |
return SK_MinS32; |
@@ -74,39 +95,38 @@ |
bestSeg = testSeg; |
*bestHit = testHit; |
bestOpp = testOpp; |
- bestTSpan = testTSpan; |
+ bestTIndex = testTIndex; |
bestY = testY; |
- } while ((testSeg = testSeg->next())); |
+ } |
} |
abortContours: |
int result; |
if (!bestSeg) { |
result = hitSomething ? SK_MinS32 : 0; |
} else { |
- if (bestTSpan->windSum() == SK_MinS32) { |
+ if (bestSeg->windSum(bestTIndex) == SK_MinS32) { |
*currentPtr = bestSeg; |
- *startPtr = bestTSpan; |
- *endPtr = bestTSpan->next(); |
- SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr); |
+ *indexPtr = bestTIndex; |
+ *endIndexPtr = bestSeg->nextSpan(bestTIndex, 1); |
+ SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0); |
*tryAgain = true; |
return 0; |
} |
- result = bestSeg->windingAtT(*bestHit, bestTSpan, bestOpp, bestDx); |
+ result = bestSeg->windingAtT(*bestHit, bestTIndex, bestOpp, bestDx); |
SkASSERT(result == SK_MinS32 || *bestDx); |
} |
- double baseT = (*startPtr)->t(); |
- double endT = (*endPtr)->t(); |
+ double baseT = current->t(index); |
+ double endT = current->t(endIndex); |
*bestHit = baseT + mid * (endT - baseT); |
return result; |
} |
-SkOpSegment* FindUndone(SkTDArray<SkOpContour* >& contourList, SkOpSpanBase** startPtr, |
- SkOpSpanBase** endPtr) { |
+SkOpSegment* FindUndone(SkTArray<SkOpContour*, true>& contourList, int* start, int* end) { |
int contourCount = contourList.count(); |
SkOpSegment* result; |
for (int cIndex = 0; cIndex < contourCount; ++cIndex) { |
SkOpContour* contour = contourList[cIndex]; |
- result = contour->undoneSegment(startPtr, endPtr); |
+ result = contour->undoneSegment(start, end); |
if (result) { |
return result; |
} |
@@ -114,23 +134,20 @@ |
return NULL; |
} |
-SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr, |
- SkOpSpanBase** endPtr) { |
+SkOpSegment* FindChase(SkTDArray<SkOpSpan*>* chase, int* tIndex, int* endIndex) { |
while (chase->count()) { |
- SkOpSpanBase* span; |
+ SkOpSpan* span; |
chase->pop(&span); |
- SkOpSegment* segment = span->segment(); |
- *startPtr = span->ptT()->next()->span(); |
+ const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex); |
+ SkOpSegment* segment = backPtr.fOther; |
+ *tIndex = backPtr.fOtherIndex; |
bool sortable = true; |
bool done = true; |
- *endPtr = NULL; |
- if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done, |
+ *endIndex = -1; |
+ if (const SkOpAngle* last = segment->activeAngle(*tIndex, tIndex, endIndex, &done, |
&sortable)) { |
- if (last->unorderable()) { |
- continue; |
- } |
- *startPtr = last->start(); |
- *endPtr = last->end(); |
+ *tIndex = last->start(); |
+ *endIndex = last->end(); |
#if TRY_ROTATE |
*chase->insert(0) = span; |
#else |
@@ -145,58 +162,65 @@ |
continue; |
} |
// find first angle, initialize winding to computed wind sum |
- const SkOpAngle* angle = segment->spanToAngle(*startPtr, *endPtr); |
- if (!angle) { |
- continue; |
- } |
- const SkOpAngle* firstAngle = angle; |
- bool loop = false; |
- int winding = SK_MinS32; |
+ const SkOpAngle* angle = segment->spanToAngle(*tIndex, *endIndex); |
+ const SkOpAngle* firstAngle; |
+ SkDEBUGCODE(firstAngle = angle); |
+ SkDEBUGCODE(bool loop = false); |
+ int winding; |
do { |
angle = angle->next(); |
- if (angle == firstAngle && loop) { |
- break; // if we get here, there's no winding, loop is unorderable |
- } |
- loop |= angle == firstAngle; |
+ SkASSERT(angle != firstAngle || !loop); |
+ SkDEBUGCODE(loop |= angle == firstAngle); |
segment = angle->segment(); |
winding = segment->windSum(angle); |
} while (winding == SK_MinS32); |
- if (winding == SK_MinS32) { |
- continue; |
- } |
- int sumWinding = segment->updateWindingReverse(angle); |
- SkOpSegment* first = NULL; |
+ int spanWinding = segment->spanSign(angle->start(), angle->end()); |
+ #if DEBUG_WINDING |
+ SkDebugf("%s winding=%d spanWinding=%d\n", __FUNCTION__, winding, spanWinding); |
+ #endif |
+ // turn span winding into contour winding |
+ if (spanWinding * winding < 0) { |
+ winding += spanWinding; |
+ } |
+ // we care about first sign and whether wind sum indicates this |
+ // edge is inside or outside. Maybe need to pass span winding |
+ // or first winding or something into this function? |
+ // advance to first undone angle, then return it and winding |
+ // (to set whether edges are active or not) |
firstAngle = angle; |
+ winding -= firstAngle->segment()->spanSign(firstAngle); |
while ((angle = angle->next()) != firstAngle) { |
segment = angle->segment(); |
- SkOpSpanBase* start = angle->start(); |
- SkOpSpanBase* end = angle->end(); |
- int maxWinding; |
- segment->setUpWinding(start, end, &maxWinding, &sumWinding); |
- if (!segment->done(angle)) { |
- if (!first) { |
- first = segment; |
- *startPtr = start; |
- *endPtr = end; |
+ int maxWinding = winding; |
+ winding -= segment->spanSign(angle); |
+ #if DEBUG_SORT |
+ SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__, |
+ segment->debugID(), maxWinding, winding, angle->sign()); |
+ #endif |
+ *tIndex = angle->start(); |
+ *endIndex = angle->end(); |
+ int lesser = SkMin32(*tIndex, *endIndex); |
+ const SkOpSpan& nextSpan = segment->span(lesser); |
+ if (!nextSpan.fDone) { |
+ // FIXME: this be wrong? assign startWinding if edge is in |
+ // same direction. If the direction is opposite, winding to |
+ // assign is flipped sign or +/- 1? |
+ if (SkOpSegment::UseInnerWinding(maxWinding, winding)) { |
+ maxWinding = winding; |
} |
- // OPTIMIZATION: should this also add to the chase? |
- (void) segment->markAngle(maxWinding, sumWinding, angle); |
- } |
- } |
- if (first) { |
- #if TRY_ROTATE |
- *chase->insert(0) = span; |
- #else |
- *chase->append() = span; |
- #endif |
- return first; |
- } |
+ // allowed to do nothing |
+ (void) segment->markAndChaseWinding(angle, maxWinding, 0, NULL); |
+ break; |
+ } |
+ } |
+ *chase->insert(0) = span; |
+ return segment; |
} |
return NULL; |
} |
-#if DEBUG_ACTIVE_SPANS |
-void DebugShowActiveSpans(SkTDArray<SkOpContour* >& contourList) { |
+#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY |
+void DebugShowActiveSpans(SkTArray<SkOpContour*, true>& contourList) { |
int index; |
for (index = 0; index < contourList.count(); ++ index) { |
contourList[index]->debugShowActiveSpans(); |
@@ -204,12 +228,11 @@ |
} |
#endif |
-static SkOpSegment* findTopSegment(const SkTDArray<SkOpContour* >& contourList, |
- bool firstPass, SkOpSpanBase** start, SkOpSpanBase** end, SkPoint* topLeft, |
- bool* unsortable, bool* done, SkChunkAlloc* allocator) { |
+static SkOpSegment* findTopSegment(const SkTArray<SkOpContour*, true>& contourList, int* index, |
+ int* endIndex, SkPoint* topLeft, bool* unsortable, bool* done, bool firstPass) { |
SkOpSegment* result; |
const SkOpSegment* lastTopStart = NULL; |
- SkOpSpanBase* lastStart = NULL, * lastEnd = NULL; |
+ int lastIndex = -1, lastEndIndex = -1; |
do { |
SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax}; |
int contourCount = contourList.count(); |
@@ -238,27 +261,27 @@ |
return NULL; |
} |
*topLeft = bestXY; |
- result = topStart->findTop(firstPass, start, end, unsortable, allocator); |
+ result = topStart->findTop(index, endIndex, unsortable, firstPass); |
if (!result) { |
- if (lastTopStart == topStart && lastStart == *start && lastEnd == *end) { |
+ if (lastTopStart == topStart && lastIndex == *index && lastEndIndex == *endIndex) { |
*done = true; |
return NULL; |
} |
lastTopStart = topStart; |
- lastStart = *start; |
- lastEnd = *end; |
+ lastIndex = *index; |
+ lastEndIndex = *endIndex; |
} |
} while (!result); |
return result; |
} |
-static int rightAngleWinding(const SkTDArray<SkOpContour* >& contourList, |
- SkOpSegment** currentPtr, SkOpSpanBase** start, SkOpSpanBase** end, double* tHit, |
+static int rightAngleWinding(const SkTArray<SkOpContour*, true>& contourList, |
+ SkOpSegment** currentPtr, int* indexPtr, int* endIndexPtr, double* tHit, |
SkScalar* hitDx, bool* tryAgain, bool* onlyVertical, bool opp) { |
double test = 0.9; |
int contourWinding; |
do { |
- contourWinding = contourRangeCheckY(contourList, currentPtr, start, end, |
+ contourWinding = contourRangeCheckY(contourList, currentPtr, indexPtr, endIndexPtr, |
tHit, hitDx, tryAgain, &test, opp); |
if (contourWinding != SK_MinS32 || *tryAgain) { |
return contourWinding; |
@@ -273,9 +296,9 @@ |
return contourWinding; |
} |
-static void skipVertical(const SkTDArray<SkOpContour* >& contourList, |
- SkOpSegment** current, SkOpSpanBase** start, SkOpSpanBase** end) { |
- if (!(*current)->isVertical(*start, *end)) { |
+static void skipVertical(const SkTArray<SkOpContour*, true>& contourList, |
+ SkOpSegment** current, int* index, int* endIndex) { |
+ if (!(*current)->isVertical(*index, *endIndex)) { |
return; |
} |
int contourCount = contourList.count(); |
@@ -284,7 +307,7 @@ |
if (contour->done()) { |
continue; |
} |
- SkOpSegment* nonVertical = contour->nonVerticalSegment(start, end); |
+ SkOpSegment* nonVertical = contour->nonVerticalSegment(index, endIndex); |
if (nonVertical) { |
*current = nonVertical; |
return; |
@@ -293,41 +316,41 @@ |
return; |
} |
-struct SortableTop2 { // error if local in pre-C++11 |
- SkOpSpanBase* fStart; |
- SkOpSpanBase* fEnd; |
+struct SortableTop { // error if local in pre-C++11 |
+ SkOpSegment* fSegment; |
+ int fIndex; |
+ int fEndIndex; |
}; |
-SkOpSegment* FindSortableTop(const SkTDArray<SkOpContour* >& contourList, bool firstPass, |
- SkOpAngle::IncludeType angleIncludeType, bool* firstContour, SkOpSpanBase** startPtr, |
- SkOpSpanBase** endPtr, SkPoint* topLeft, bool* unsortable, bool* done, bool* onlyVertical, |
- SkChunkAlloc* allocator) { |
- SkOpSegment* current = findTopSegment(contourList, firstPass, startPtr, endPtr, topLeft, |
- unsortable, done, allocator); |
+SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList, |
+ SkOpAngle::IncludeType angleIncludeType, bool* firstContour, int* indexPtr, |
+ int* endIndexPtr, SkPoint* topLeft, bool* unsortable, bool* done, bool* onlyVertical, |
+ bool firstPass) { |
+ SkOpSegment* current = findTopSegment(contourList, indexPtr, endIndexPtr, topLeft, unsortable, |
+ done, firstPass); |
if (!current) { |
return NULL; |
} |
- SkOpSpanBase* start = *startPtr; |
- SkOpSpanBase* end = *endPtr; |
- SkASSERT(current == start->segment()); |
+ const int startIndex = *indexPtr; |
+ const int endIndex = *endIndexPtr; |
if (*firstContour) { |
- current->initWinding(start, end, angleIncludeType); |
+ current->initWinding(startIndex, endIndex, angleIncludeType); |
*firstContour = false; |
return current; |
} |
- SkOpSpan* minSpan = start->starter(end); |
- int sumWinding = minSpan->windSum(); |
+ int minIndex = SkMin32(startIndex, endIndex); |
+ int sumWinding = current->windSum(minIndex); |
if (sumWinding == SK_MinS32) { |
- SkOpSpanBase* iSpan = end; |
- SkOpSpanBase* oSpan = start; |
- do { |
- bool checkFrom = oSpan->t() < iSpan->t(); |
- if ((checkFrom ? iSpan->fromAngle() : iSpan->upCast()->toAngle()) == NULL) { |
- iSpan->addSimpleAngle(checkFrom, allocator); |
- } |
- sumWinding = current->computeSum(oSpan, iSpan, angleIncludeType); |
- SkTSwap(iSpan, oSpan); |
- } while (sumWinding == SK_MinS32 && iSpan == start); |
+ int index = endIndex; |
+ int oIndex = startIndex; |
+ do { |
+ const SkOpSpan& span = current->span(index); |
+ if ((oIndex < index ? span.fFromAngle : span.fToAngle) == NULL) { |
+ current->addSimpleAngle(index); |
+ } |
+ sumWinding = current->computeSum(oIndex, index, angleIncludeType); |
+ SkTSwap(index, oIndex); |
+ } while (sumWinding == SK_MinS32 && index == startIndex); |
} |
if (sumWinding != SK_MinS32 && sumWinding != SK_NaN32) { |
return current; |
@@ -341,28 +364,26 @@ |
SkScalar hitDx = 0; |
SkScalar hitOppDx = 0; |
// keep track of subsequent returns to detect infinite loops |
- SkTDArray<SortableTop2> sortableTops; |
+ SkTDArray<SortableTop> sortableTops; |
do { |
// if current is vertical, find another candidate which is not |
// if only remaining candidates are vertical, then they can be marked done |
- SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr); |
- SkASSERT(current == (*startPtr)->segment()); |
- skipVertical(contourList, ¤t, startPtr, endPtr); |
+ SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0); |
+ skipVertical(contourList, ¤t, indexPtr, endIndexPtr); |
SkASSERT(current); // FIXME: if null, all remaining are vertical |
- SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr); |
- SkASSERT(current == (*startPtr)->segment()); |
+ SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0); |
tryAgain = false; |
- contourWinding = rightAngleWinding(contourList, ¤t, startPtr, endPtr, &tHit, |
+ contourWinding = rightAngleWinding(contourList, ¤t, indexPtr, endIndexPtr, &tHit, |
&hitDx, &tryAgain, onlyVertical, false); |
- SkASSERT(current == (*startPtr)->segment()); |
if (tryAgain) { |
bool giveUp = false; |
int count = sortableTops.count(); |
for (int index = 0; index < count; ++index) { |
- const SortableTop2& prev = sortableTops[index]; |
+ const SortableTop& prev = sortableTops[index]; |
if (giveUp) { |
- prev.fStart->segment()->markDone(prev.fStart->starter(prev.fEnd)); |
- } else if (prev.fStart == *startPtr || prev.fEnd == *endPtr) { |
+ prev.fSegment->markDoneFinal(prev.fIndex); |
+ } else if (prev.fSegment == current |
+ && (prev.fIndex == *indexPtr || prev.fEndIndex == *endIndexPtr)) { |
// remaining edges are non-vertical and cannot have their winding computed |
// mark them as done and return, and hope that assembly can fill the holes |
giveUp = true; |
@@ -374,13 +395,14 @@ |
return NULL; |
} |
} |
- SortableTop2* sortableTop = sortableTops.append(); |
- sortableTop->fStart = *startPtr; |
- sortableTop->fEnd = *endPtr; |
+ SortableTop* sortableTop = sortableTops.append(); |
+ sortableTop->fSegment = current; |
+ sortableTop->fIndex = *indexPtr; |
+ sortableTop->fEndIndex = *endIndexPtr; |
#if DEBUG_SORT |
SkDebugf("%s current=%d index=%d endIndex=%d tHit=%1.9g hitDx=%1.9g try=%d vert=%d\n", |
- __FUNCTION__, current->debugID(), (*startPtr)->debugID(), (*endPtr)->debugID(), |
- tHit, hitDx, tryAgain, *onlyVertical); |
+ __FUNCTION__, current->debugID(), *indexPtr, *endIndexPtr, tHit, hitDx, tryAgain, |
+ *onlyVertical); |
#endif |
if (*onlyVertical) { |
return current; |
@@ -391,34 +413,126 @@ |
if (angleIncludeType < SkOpAngle::kBinarySingle) { |
break; |
} |
- oppContourWinding = rightAngleWinding(contourList, ¤t, startPtr, endPtr, &tHit, |
+ oppContourWinding = rightAngleWinding(contourList, ¤t, indexPtr, endIndexPtr, &tHit, |
&hitOppDx, &tryAgain, NULL, true); |
- SkASSERT(current == (*startPtr)->segment()); |
} while (tryAgain); |
- bool success = current->initWinding(*startPtr, *endPtr, tHit, contourWinding, hitDx, |
+ bool success = current->initWinding(*indexPtr, *endIndexPtr, tHit, contourWinding, hitDx, |
oppContourWinding, hitOppDx); |
if (current->done()) { |
return NULL; |
} else if (!success) { // check if the span has a valid winding |
- SkOpSpan* minSpan = (*startPtr)->t() < (*endPtr)->t() ? (*startPtr)->upCast() |
- : (*endPtr)->upCast(); |
- if (minSpan->windSum() == SK_MinS32) { |
+ int min = SkTMin(*indexPtr, *endIndexPtr); |
+ const SkOpSpan& span = current->span(min); |
+ if (span.fWindSum == SK_MinS32) { |
return NULL; |
} |
} |
return current; |
} |
-void MakeContourList(SkOpContour* contour, SkTDArray<SkOpContour* >& list, |
+static bool calcAngles(SkTArray<SkOpContour*, true>* contourList) { |
+ int contourCount = (*contourList).count(); |
+ for (int cTest = 0; cTest < contourCount; ++cTest) { |
+ SkOpContour* contour = (*contourList)[cTest]; |
+ if (!contour->calcAngles()) { |
+ return false; |
+ } |
+ } |
+ return true; |
+} |
+ |
+static void checkDuplicates(SkTArray<SkOpContour*, true>* contourList) { |
+ int contourCount = (*contourList).count(); |
+ for (int cTest = 0; cTest < contourCount; ++cTest) { |
+ SkOpContour* contour = (*contourList)[cTest]; |
+ contour->checkDuplicates(); |
+ } |
+} |
+ |
+static bool checkEnds(SkTArray<SkOpContour*, true>* contourList) { |
+ // it's hard to determine if the end of a cubic or conic nearly intersects another curve. |
+ // instead, look to see if the connecting curve intersected at that same end. |
+ int contourCount = (*contourList).count(); |
+ for (int cTest = 0; cTest < contourCount; ++cTest) { |
+ SkOpContour* contour = (*contourList)[cTest]; |
+ if (!contour->checkEnds()) { |
+ return false; |
+ } |
+ } |
+ return true; |
+} |
+ |
+static bool checkMultiples(SkTArray<SkOpContour*, true>* contourList) { |
+ bool hasMultiples = false; |
+ int contourCount = (*contourList).count(); |
+ for (int cTest = 0; cTest < contourCount; ++cTest) { |
+ SkOpContour* contour = (*contourList)[cTest]; |
+ contour->checkMultiples(); |
+ hasMultiples |= contour->hasMultiples(); |
+ } |
+ return hasMultiples; |
+} |
+ |
+// A small interval of a pair of curves may collapse to lines for each, triggering coincidence |
+static void checkSmall(SkTArray<SkOpContour*, true>* contourList) { |
+ int contourCount = (*contourList).count(); |
+ for (int cTest = 0; cTest < contourCount; ++cTest) { |
+ SkOpContour* contour = (*contourList)[cTest]; |
+ contour->checkSmall(); |
+ } |
+} |
+ |
+// A tiny interval may indicate an undiscovered coincidence. Find and fix. |
+static void checkTiny(SkTArray<SkOpContour*, true>* contourList) { |
+ int contourCount = (*contourList).count(); |
+ for (int cTest = 0; cTest < contourCount; ++cTest) { |
+ SkOpContour* contour = (*contourList)[cTest]; |
+ contour->checkTiny(); |
+ } |
+} |
+ |
+static void fixOtherTIndex(SkTArray<SkOpContour*, true>* contourList) { |
+ int contourCount = (*contourList).count(); |
+ for (int cTest = 0; cTest < contourCount; ++cTest) { |
+ SkOpContour* contour = (*contourList)[cTest]; |
+ contour->fixOtherTIndex(); |
+ } |
+} |
+ |
+static void joinCoincidence(SkTArray<SkOpContour*, true>* contourList) { |
+ int contourCount = (*contourList).count(); |
+ for (int cTest = 0; cTest < contourCount; ++cTest) { |
+ SkOpContour* contour = (*contourList)[cTest]; |
+ contour->joinCoincidence(); |
+ } |
+} |
+ |
+static void sortAngles(SkTArray<SkOpContour*, true>* contourList) { |
+ int contourCount = (*contourList).count(); |
+ for (int cTest = 0; cTest < contourCount; ++cTest) { |
+ SkOpContour* contour = (*contourList)[cTest]; |
+ contour->sortAngles(); |
+ } |
+} |
+ |
+static void sortSegments(SkTArray<SkOpContour*, true>* contourList) { |
+ int contourCount = (*contourList).count(); |
+ for (int cTest = 0; cTest < contourCount; ++cTest) { |
+ SkOpContour* contour = (*contourList)[cTest]; |
+ contour->sortSegments(); |
+ } |
+} |
+ |
+void MakeContourList(SkTArray<SkOpContour>& contours, SkTArray<SkOpContour*, true>& list, |
bool evenOdd, bool oppEvenOdd) { |
- do { |
- if (contour->count()) { |
- contour->setOppXor(contour->operand() ? evenOdd : oppEvenOdd); |
- *list.append() = contour; |
- } |
- } while ((contour = contour->next())); |
- if (list.count() < 2) { |
+ int count = contours.count(); |
+ if (count == 0) { |
return; |
+ } |
+ for (int index = 0; index < count; ++index) { |
+ SkOpContour& contour = contours[index]; |
+ contour.setOppXor(contour.operand() ? evenOdd : oppEvenOdd); |
+ list.push_back(&contour); |
} |
SkTQSort<SkOpContour>(list.begin(), list.end() - 1); |
} |
@@ -440,22 +554,19 @@ |
reassemble contour pieces into new path |
*/ |
void Assemble(const SkPathWriter& path, SkPathWriter* simple) { |
- SkOpContour contour; |
- SkOpGlobalState globalState(NULL PATH_OPS_DEBUG_PARAMS(&contour)); |
#if DEBUG_PATH_CONSTRUCTION |
SkDebugf("%s\n", __FUNCTION__); |
#endif |
- SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune |
- SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState); |
- builder.finish(&allocator); |
- SkTDArray<const SkOpContour* > runs; // indices of partial contours |
- const SkOpContour* eContour = builder.head(); |
- do { |
- if (!eContour->count()) { |
- continue; |
- } |
- const SkPoint& eStart = eContour->start(); |
- const SkPoint& eEnd = eContour->end(); |
+ SkTArray<SkOpContour> contours; |
+ SkOpEdgeBuilder builder(path, contours); |
+ builder.finish(); |
+ int count = contours.count(); |
+ int outer; |
+ SkTArray<int, true> runs(count); // indices of partial contours |
+ for (outer = 0; outer < count; ++outer) { |
+ const SkOpContour& eContour = contours[outer]; |
+ const SkPoint& eStart = eContour.start(); |
+ const SkPoint& eEnd = eContour.end(); |
#if DEBUG_ASSEMBLE |
SkDebugf("%s contour", __FUNCTION__); |
if (!SkDPoint::ApproximatelyEqual(eStart, eEnd)) { |
@@ -467,42 +578,44 @@ |
eStart.fX, eStart.fY, eEnd.fX, eEnd.fY); |
#endif |
if (SkDPoint::ApproximatelyEqual(eStart, eEnd)) { |
- eContour->toPath(simple); |
- continue; |
- } |
- *runs.append() = eContour; |
- } while ((eContour = eContour->next())); |
- int count = runs.count(); |
+ eContour.toPath(simple); |
+ continue; |
+ } |
+ runs.push_back(outer); |
+ } |
+ count = runs.count(); |
if (count == 0) { |
return; |
} |
- SkTDArray<int> sLink, eLink; |
- sLink.append(count); |
- eLink.append(count); |
+ SkTArray<int, true> sLink, eLink; |
+ sLink.push_back_n(count); |
+ eLink.push_back_n(count); |
int rIndex, iIndex; |
for (rIndex = 0; rIndex < count; ++rIndex) { |
sLink[rIndex] = eLink[rIndex] = SK_MaxS32; |
} |
const int ends = count * 2; // all starts and ends |
const int entries = (ends - 1) * count; // folded triangle : n * (n - 1) / 2 |
- SkTDArray<double> distances; |
- distances.append(entries); |
+ SkTArray<double, true> distances; |
+ distances.push_back_n(entries); |
for (rIndex = 0; rIndex < ends - 1; ++rIndex) { |
- const SkOpContour* oContour = runs[rIndex >> 1]; |
- const SkPoint& oPt = rIndex & 1 ? oContour->end() : oContour->start(); |
+ outer = runs[rIndex >> 1]; |
+ const SkOpContour& oContour = contours[outer]; |
+ const SkPoint& oPt = rIndex & 1 ? oContour.end() : oContour.start(); |
const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2) |
* ends - rIndex - 1; |
for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) { |
- const SkOpContour* iContour = runs[iIndex >> 1]; |
- const SkPoint& iPt = iIndex & 1 ? iContour->end() : iContour->start(); |
+ int inner = runs[iIndex >> 1]; |
+ const SkOpContour& iContour = contours[inner]; |
+ const SkPoint& iPt = iIndex & 1 ? iContour.end() : iContour.start(); |
double dx = iPt.fX - oPt.fX; |
double dy = iPt.fY - oPt.fY; |
double dist = dx * dx + dy * dy; |
distances[row + iIndex] = dist; // oStart distance from iStart |
} |
} |
- SkTDArray<int> sortedDist; |
- sortedDist.append(entries); |
+ SkTArray<int, true> sortedDist; |
+ sortedDist.push_back_n(entries); |
for (rIndex = 0; rIndex < entries; ++rIndex) { |
sortedDist[rIndex] = rIndex; |
} |
@@ -565,16 +678,17 @@ |
eIndex < 0 ? ~eIndex : eIndex); |
#endif |
do { |
- const SkOpContour* contour = runs[rIndex]; |
+ outer = runs[rIndex]; |
+ const SkOpContour& contour = contours[outer]; |
if (first) { |
first = false; |
- const SkPoint* startPtr = &contour->start(); |
+ const SkPoint* startPtr = &contour.start(); |
simple->deferredMove(startPtr[0]); |
} |
if (forward) { |
- contour->toPartialForward(simple); |
+ contour.toPartialForward(simple); |
} else { |
- contour->toPartialBackward(simple); |
+ contour.toPartialBackward(simple); |
} |
#if DEBUG_ASSEMBLE |
SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex, |
@@ -628,89 +742,37 @@ |
#endif |
} |
-static void align(SkTDArray<SkOpContour* >* contourList) { |
- int contourCount = (*contourList).count(); |
- for (int cTest = 0; cTest < contourCount; ++cTest) { |
- SkOpContour* contour = (*contourList)[cTest]; |
- contour->align(); |
- } |
-} |
- |
-static void calcAngles(SkTDArray<SkOpContour* >* contourList, SkChunkAlloc* allocator) { |
- int contourCount = (*contourList).count(); |
- for (int cTest = 0; cTest < contourCount; ++cTest) { |
- SkOpContour* contour = (*contourList)[cTest]; |
- contour->calcAngles(allocator); |
- } |
-} |
- |
-static void missingCoincidence(SkTDArray<SkOpContour* >* contourList, |
- SkOpCoincidence* coincidence, SkChunkAlloc* allocator) { |
- int contourCount = (*contourList).count(); |
- for (int cTest = 0; cTest < contourCount; ++cTest) { |
- SkOpContour* contour = (*contourList)[cTest]; |
- contour->missingCoincidence(coincidence, allocator); |
- } |
-} |
- |
-static bool moveNearby(SkTDArray<SkOpContour* >* contourList) { |
- int contourCount = (*contourList).count(); |
- for (int cTest = 0; cTest < contourCount; ++cTest) { |
- SkOpContour* contour = (*contourList)[cTest]; |
- if (!contour->moveNearby()) { |
- return false; |
- } |
- } |
+bool HandleCoincidence(SkTArray<SkOpContour*, true>* contourList, int total) { |
+#if DEBUG_SHOW_WINDING |
+ SkOpContour::debugShowWindingValues(contourList); |
+#endif |
+ if (!CoincidenceCheck(contourList, total)) { |
+ return false; |
+ } |
+#if DEBUG_SHOW_WINDING |
+ SkOpContour::debugShowWindingValues(contourList); |
+#endif |
+ fixOtherTIndex(contourList); |
+ if (!checkEnds(contourList)) { // check if connecting curve intersected at the same end |
+ return false; |
+ } |
+ bool hasM = checkMultiples(contourList); // check if intersections agree on t and point values |
+ SkTDArray<SkOpSegment::AlignedSpan> aligned; |
+ if (hasM) { |
+ alignMultiples(contourList, &aligned); // align pairs of identical points |
+ alignCoincidence(contourList, aligned); |
+ } |
+ checkDuplicates(contourList); // check if spans have the same number on the other end |
+ checkTiny(contourList); // if pair have the same end points, mark them as parallel |
+ checkSmall(contourList); // a pair of curves with a small span may turn into coincident lines |
+ joinCoincidence(contourList); // join curves that connect to a coincident pair |
+ sortSegments(contourList); |
+ if (!calcAngles(contourList)) { |
+ return false; |
+ } |
+ sortAngles(contourList); |
+#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY |
+ DebugShowActiveSpans(*contourList); |
+#endif |
return true; |
} |
- |
-static void sortAngles(SkTDArray<SkOpContour* >* contourList) { |
- int contourCount = (*contourList).count(); |
- for (int cTest = 0; cTest < contourCount; ++cTest) { |
- SkOpContour* contour = (*contourList)[cTest]; |
- contour->sortAngles(); |
- } |
-} |
- |
-static void sortSegments(SkTDArray<SkOpContour* >* contourList) { |
- int contourCount = (*contourList).count(); |
- for (int cTest = 0; cTest < contourCount; ++cTest) { |
- SkOpContour* contour = (*contourList)[cTest]; |
- contour->sortSegments(); |
- } |
-} |
- |
-bool HandleCoincidence(SkTDArray<SkOpContour* >* contourList, SkOpCoincidence* coincidence, |
- SkChunkAlloc* allocator, SkOpGlobalState* globalState) { |
- // move t values and points together to eliminate small/tiny gaps |
- if (!moveNearby(contourList)) { |
- return false; |
- } |
- align(contourList); // give all span members common values |
-#if DEBUG_VALIDATE |
- globalState->setPhase(SkOpGlobalState::kIntersecting); |
-#endif |
- coincidence->addMissing(allocator); |
-#if DEBUG_VALIDATE |
- globalState->setPhase(SkOpGlobalState::kWalking); |
-#endif |
- coincidence->expand(); // check to see if, loosely, coincident ranges may be expanded |
- coincidence->mark(); // mark spans of coincident segments as coincident |
- missingCoincidence(contourList, coincidence, allocator); // look for coincidence missed earlier |
- if (!coincidence->apply()) { // adjust the winding value to account for coincident edges |
- return false; |
- } |
- sortSegments(contourList); |
- calcAngles(contourList, allocator); |
- sortAngles(contourList); |
- if (globalState->angleCoincidence()) { |
- missingCoincidence(contourList, coincidence, allocator); |
- if (!coincidence->apply()) { |
- return false; |
- } |
- } |
-#if DEBUG_ACTIVE_SPANS |
- DebugShowActiveSpans(*contourList); |
-#endif |
- return true; |
-} |