diff options
Diffstat (limited to 'src/pathops/SkPathOpsCommon.cpp')
-rw-r--r-- | src/pathops/SkPathOpsCommon.cpp | 414 |
1 files changed, 59 insertions, 355 deletions
diff --git a/src/pathops/SkPathOpsCommon.cpp b/src/pathops/SkPathOpsCommon.cpp index 05b370a1df..734b5f0819 100644 --- a/src/pathops/SkPathOpsCommon.cpp +++ b/src/pathops/SkPathOpsCommon.cpp @@ -11,106 +11,16 @@ #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; - const double mid = *midPtr; - const SkOpSegment* current = *currentPtr; - double tAtMid = SkOpSegment::TAtMid(start, end, mid); - SkPoint basePt = current->ptAtT(tAtMid); - int contourCount = contourList.count(); - SkScalar bestY = SK_ScalarMin; - SkOpSegment* bestSeg = NULL; - SkOpSpan* bestTSpan = NULL; - bool bestOpp; - bool hitSomething = false; - for (int cTest = 0; cTest < contourCount; ++cTest) { - SkOpContour* contour = contourList[cTest]; - bool testOpp = contour->operand() ^ current->operand() ^ opp; - if (basePt.fY < contour->bounds().fTop) { - continue; - } - if (bestY > contour->bounds().fBottom) { - continue; - } - SkOpSegment* testSeg = contour->first(); - SkASSERT(testSeg); - do { - SkScalar testY = bestY; - double testHit; - bool vertical; - SkOpSpan* testTSpan = testSeg->crossedSpanY(basePt, tAtMid, testOpp, - testSeg == current, &testY, &testHit, &hitSomething, &vertical); - if (!testTSpan) { - if (vertical) { - 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(); - 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); - 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 + mid * (endT - baseT), midXY.fX, midXY.fY, - baseT + newMid * (endT - baseT), newXY.fX, newXY.fY, - endT, end->pt().fX, end->pt().fY); -#endif - *midPtr = newMid * 2; // calling loop with divide by 2 before continuing - return SK_MinS32; - } - bestSeg = testSeg; - *bestHit = testHit; - bestOpp = testOpp; - bestTSpan = testTSpan; - bestY = testY; - } while ((testSeg = testSeg->next())); - } -abortContours: - int result; - if (!bestSeg) { - result = hitSomething ? SK_MinS32 : 0; - } else { - if (bestTSpan->windSum() == SK_MinS32) { - *currentPtr = bestSeg; - *startPtr = bestTSpan; - *endPtr = bestTSpan->next(); - SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr); - *tryAgain = true; - return 0; - } - result = bestSeg->windingAtT(*bestHit, bestTSpan, bestOpp, bestDx); - SkASSERT(result == SK_MinS32 || *bestDx); - } - double baseT = (*startPtr)->t(); - double endT = (*endPtr)->t(); - *bestHit = baseT + mid * (endT - baseT); - return result; -} - -SkOpSegment* FindUndone(SkTDArray<SkOpContour* >& contourList, SkOpSpanBase** startPtr, +SkOpSegment* FindUndone(SkOpContourHead* contourList, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr) { - int contourCount = contourList.count(); SkOpSegment* result; - for (int cIndex = 0; cIndex < contourCount; ++cIndex) { - SkOpContour* contour = contourList[cIndex]; + SkOpContour* contour = contourList; + do { result = contour->undoneSegment(startPtr, endPtr); if (result) { return result; } - } + } while ((contour = contour->next())); return NULL; } @@ -196,234 +106,41 @@ SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr, } #if DEBUG_ACTIVE_SPANS -void DebugShowActiveSpans(SkTDArray<SkOpContour* >& contourList) { - int index; - for (index = 0; index < contourList.count(); ++ index) { - contourList[index]->debugShowActiveSpans(); - } -} -#endif - -static SkOpSegment* findTopSegment(const SkTDArray<SkOpContour* >& contourList, - bool firstPass, SkOpSpanBase** start, SkOpSpanBase** end, SkDPoint* topLeft, - bool* unsortable, bool* done, SkChunkAlloc* allocator) { - SkOpSegment* result; - const SkOpSegment* lastTopStart = NULL; - SkOpSpanBase* lastStart = NULL, * lastEnd = NULL; - do { - SkDPoint bestXY = {SK_ScalarMax, SK_ScalarMax}; - int contourCount = contourList.count(); - SkOpSegment* topStart = NULL; - *done = true; - for (int cIndex = 0; cIndex < contourCount; ++cIndex) { - SkOpContour* contour = contourList[cIndex]; - if (contour->done()) { - continue; - } - const SkPathOpsBounds& bounds = contour->bounds(); - if (bounds.fBottom < topLeft->fY) { - *done = false; - continue; - } - if (bounds.fBottom == topLeft->fY && bounds.fRight < topLeft->fX) { - *done = false; - continue; - } - contour->topSortableSegment(*topLeft, &bestXY, &topStart); - if (!contour->done()) { - *done = false; - } - } - if (!topStart) { - return NULL; - } - *topLeft = bestXY; - result = topStart->findTop(firstPass, start, end, unsortable, allocator); - if (!result) { - if (lastTopStart == topStart && lastStart == *start && lastEnd == *end) { - *done = true; - return NULL; - } - lastTopStart = topStart; - lastStart = *start; - lastEnd = *end; - } - } while (!result); - return result; -} - -static int rightAngleWinding(const SkTDArray<SkOpContour* >& contourList, - SkOpSegment** currentPtr, SkOpSpanBase** start, SkOpSpanBase** end, double* tHit, - SkScalar* hitDx, bool* tryAgain, bool* onlyVertical, bool opp) { - double test = 0.9; - int contourWinding; +void DebugShowActiveSpans(SkOpContourHead* contourList) { + SkOpContour* contour = contourList; do { - contourWinding = contourRangeCheckY(contourList, currentPtr, start, end, - tHit, hitDx, tryAgain, &test, opp); - if (contourWinding != SK_MinS32 || *tryAgain) { - return contourWinding; - } - if (*currentPtr && (*currentPtr)->isVertical()) { - *onlyVertical = true; - return contourWinding; - } - test /= 2; - } while (!approximately_negative(test)); - SkASSERT(0); // FIXME: incomplete functionality - return contourWinding; -} - -static void skipVertical(const SkTDArray<SkOpContour* >& contourList, - SkOpSegment** current, SkOpSpanBase** start, SkOpSpanBase** end) { - if (!(*current)->isVertical(*start, *end)) { - return; - } - int contourCount = contourList.count(); - for (int cIndex = 0; cIndex < contourCount; ++cIndex) { - SkOpContour* contour = contourList[cIndex]; - if (contour->done()) { - continue; - } - SkOpSegment* nonVertical = contour->nonVerticalSegment(start, end); - if (nonVertical) { - *current = nonVertical; - return; - } - } - return; + contour->debugShowActiveSpans(); + } while ((contour = contour->next())); } - -struct SortableTop2 { // error if local in pre-C++11 - SkOpSpanBase* fStart; - SkOpSpanBase* fEnd; -}; - -SkOpSegment* FindSortableTop(const SkTDArray<SkOpContour* >& contourList, bool firstPass, - SkOpAngle::IncludeType angleIncludeType, bool* firstContour, SkOpSpanBase** startPtr, - SkOpSpanBase** endPtr, SkDPoint* topLeft, bool* unsortable, bool* done, bool* onlyVertical, - SkChunkAlloc* allocator) { - SkOpSegment* current = findTopSegment(contourList, firstPass, startPtr, endPtr, topLeft, - unsortable, done, allocator); - if (!current) { - return NULL; - } - SkOpSpanBase* start = *startPtr; - SkOpSpanBase* end = *endPtr; - SkASSERT(current == start->segment()); - if (*firstContour) { - current->initWinding(start, end, angleIncludeType); - *firstContour = false; - return current; - } - SkOpSpan* minSpan = start->starter(end); - int sumWinding = minSpan->windSum(); - 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) { - if (!iSpan->addSimpleAngle(checkFrom, allocator)) { - *unsortable = true; - return NULL; - } - } - sumWinding = current->computeSum(oSpan, iSpan, angleIncludeType); - SkTSwap(iSpan, oSpan); - } while (sumWinding == SK_MinS32 && iSpan == start); - } - if (sumWinding != SK_MinS32 && sumWinding != SK_NaN32) { - return current; - } - int contourWinding; - int oppContourWinding = 0; - // the simple upward projection of the unresolved points hit unsortable angles - // shoot rays at right angles to the segment to find its winding, ignoring angle cases - bool tryAgain; - double tHit; - SkScalar hitDx = 0; - SkScalar hitOppDx = 0; - // keep track of subsequent returns to detect infinite loops - SkTDArray<SortableTop2> 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(current); // FIXME: if null, all remaining are vertical - SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr); - SkASSERT(current == (*startPtr)->segment()); - tryAgain = false; - contourWinding = rightAngleWinding(contourList, ¤t, startPtr, endPtr, &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]; - if (giveUp) { - prev.fStart->segment()->markDone(prev.fStart->starter(prev.fEnd)); - } else if (prev.fStart == *startPtr || prev.fEnd == *endPtr) { - // 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; - index = -1; - } - } - if (giveUp) { - *done = true; - return NULL; - } - } - SortableTop2* sortableTop = sortableTops.append(); - sortableTop->fStart = *startPtr; - sortableTop->fEnd = *endPtr; -#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); #endif - if (*onlyVertical) { - return current; - } - if (tryAgain) { - continue; - } - if (angleIncludeType < SkOpAngle::kBinarySingle) { - break; - } - oppContourWinding = rightAngleWinding(contourList, ¤t, startPtr, endPtr, &tHit, - &hitOppDx, &tryAgain, NULL, true); - SkASSERT(current == (*startPtr)->segment()); - } while (tryAgain); - bool success = current->initWinding(*startPtr, *endPtr, 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) { - return NULL; - } - } - return current; -} -void MakeContourList(SkOpContour* contour, SkTDArray<SkOpContour* >& list, - bool evenOdd, bool oppEvenOdd) { +bool SortContourList(SkOpContourHead** contourList, bool evenOdd, bool oppEvenOdd) { + SkTDArray<SkOpContour* > list; + SkOpContour* contour = *contourList; do { if (contour->count()) { contour->setOppXor(contour->operand() ? evenOdd : oppEvenOdd); *list.append() = contour; } } while ((contour = contour->next())); - if (list.count() < 2) { - return; + int count = list.count(); + if (!count) { + return false; + } + if (count > 1) { + SkTQSort<SkOpContour>(list.begin(), list.end() - 1); } - SkTQSort<SkOpContour>(list.begin(), list.end() - 1); + contour = list[0]; + SkOpContourHead* contourHead = static_cast<SkOpContourHead*>(contour); + contour->globalState()->setContourHead(contourHead); + *contourList = contourHead; + for (int index = 1; index < count; ++index) { + SkOpContour* next = list[index]; + contour->setNext(next); + contour = next; + } + contour->setNext(NULL); + return true; } class DistanceLessThan { @@ -444,8 +161,8 @@ public: */ void Assemble(const SkPathWriter& path, SkPathWriter* simple) { SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune - SkOpContour contour; - SkOpGlobalState globalState(NULL SkDEBUGPARAMS(&contour)); + SkOpContourHead contour; + SkOpGlobalState globalState(NULL, &contour); #if DEBUG_SHOW_TEST_NAME SkDebugf("</div>\n"); #endif @@ -634,65 +351,52 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple) { #endif } -static void align(SkTDArray<SkOpContour* >* contourList) { - int contourCount = (*contourList).count(); - for (int cTest = 0; cTest < contourCount; ++cTest) { - SkOpContour* contour = (*contourList)[cTest]; +static void align(SkOpContourHead* contourList) { + SkOpContour* contour = contourList; + do { contour->align(); - } + } while ((contour = contour->next())); } -static void calcAngles(SkTDArray<SkOpContour* >* contourList, SkChunkAlloc* allocator) { - int contourCount = (*contourList).count(); - for (int cTest = 0; cTest < contourCount; ++cTest) { - SkOpContour* contour = (*contourList)[cTest]; +static void calcAngles(SkOpContourHead* contourList, SkChunkAlloc* allocator) { + SkOpContour* contour = contourList; + do { contour->calcAngles(allocator); - } + } while ((contour = contour->next())); } -static void missingCoincidence(SkTDArray<SkOpContour* >* contourList, +static void missingCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidence, SkChunkAlloc* allocator) { - int contourCount = (*contourList).count(); - for (int cTest = 0; cTest < contourCount; ++cTest) { - SkOpContour* contour = (*contourList)[cTest]; + SkOpContour* contour = contourList; + do { contour->missingCoincidence(coincidence, allocator); - } + } while ((contour = contour->next())); } -static void moveMultiples(SkTDArray<SkOpContour* >* contourList) { - int contourCount = (*contourList).count(); - for (int cTest = 0; cTest < contourCount; ++cTest) { - SkOpContour* contour = (*contourList)[cTest]; +static void moveMultiples(SkOpContourHead* contourList) { + SkOpContour* contour = contourList; + do { contour->moveMultiples(); - } + } while ((contour = contour->next())); } -static void moveNearby(SkTDArray<SkOpContour* >* contourList) { - int contourCount = (*contourList).count(); - for (int cTest = 0; cTest < contourCount; ++cTest) { - SkOpContour* contour = (*contourList)[cTest]; +static void moveNearby(SkOpContourHead* contourList) { + SkOpContour* contour = contourList; + do { contour->moveNearby(); - } + } while ((contour = contour->next())); } -static void sortAngles(SkTDArray<SkOpContour* >* contourList) { - int contourCount = (*contourList).count(); - for (int cTest = 0; cTest < contourCount; ++cTest) { - SkOpContour* contour = (*contourList)[cTest]; +static void sortAngles(SkOpContourHead* contourList) { + SkOpContour* contour = contourList; + do { 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(); - } + } while ((contour = contour->next())); } -bool HandleCoincidence(SkTDArray<SkOpContour* >* contourList, SkOpCoincidence* coincidence, - SkChunkAlloc* allocator, SkOpGlobalState* globalState) { +bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidence, + SkChunkAlloc* allocator) { + SkOpGlobalState* globalState = contourList->globalState(); // combine t values when multiple intersections occur on some segments but not others moveMultiples(contourList); // move t values and points together to eliminate small/tiny gaps @@ -711,7 +415,6 @@ bool HandleCoincidence(SkTDArray<SkOpContour* >* contourList, SkOpCoincidence* c if (!coincidence->apply()) { // adjust the winding value to account for coincident edges return false; } - sortSegments(contourList); calcAngles(contourList, allocator); sortAngles(contourList); if (globalState->angleCoincidence()) { @@ -721,7 +424,8 @@ bool HandleCoincidence(SkTDArray<SkOpContour* >* contourList, SkOpCoincidence* c } } #if DEBUG_ACTIVE_SPANS - DebugShowActiveSpans(*contourList); + coincidence->debugShowCoincidence(); + DebugShowActiveSpans(contourList); #endif return true; } |