Esempio n. 1
0
void SkOpContour::topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY,
        SkOpSegment** topStart) {
    int segmentCount = fSortedSegments.count();
    SkASSERT(segmentCount > 0);
    int sortedIndex = fFirstSorted;
    fDone = true;  // may be cleared below
    for ( ; sortedIndex < segmentCount; ++sortedIndex) {
        SkOpSegment* testSegment = fSortedSegments[sortedIndex];
        if (testSegment->done()) {
            if (sortedIndex == fFirstSorted) {
                ++fFirstSorted;
            }
            continue;
        }
        fDone = false;
        SkPoint testXY = testSegment->activeLeftTop(true, NULL);
        if (*topStart) {
            if (testXY.fY < topLeft.fY) {
                continue;
            }
            if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
                continue;
            }
            if (bestXY->fY < testXY.fY) {
                continue;
            }
            if (bestXY->fY == testXY.fY && bestXY->fX < testXY.fX) {
                continue;
            }
        }
        *topStart = testSegment;
        *bestXY = testXY;
    }
}
Esempio n. 2
0
void FixWinding(SkPath* path) {
    SkPath::FillType fillType = path->getFillType();
    if (fillType == SkPath::kInverseEvenOdd_FillType) {
        fillType = SkPath::kInverseWinding_FillType;
    } else if (fillType == SkPath::kEvenOdd_FillType) {
        fillType = SkPath::kWinding_FillType;
    }
    SkPathPriv::FirstDirection dir;
    if (one_contour(*path) && SkPathPriv::CheapComputeFirstDirection(*path, &dir)) {
        if (dir != SkPathPriv::kCCW_FirstDirection) {
            SkPath temp;
            temp.reverseAddPath(*path);
            *path = temp;
        }
        path->setFillType(fillType);
        return;
    }
    SkChunkAlloc allocator(4096);
    SkOpContourHead contourHead;
    SkOpGlobalState globalState(nullptr, &contourHead  SkDEBUGPARAMS(nullptr));
    SkOpEdgeBuilder builder(*path, &contourHead, &allocator, &globalState);
    builder.finish(&allocator);
    SkASSERT(contourHead.next());
    contourHead.resetReverse();
    bool writePath = false;
    SkOpSpan* topSpan;
    globalState.setPhase(SkOpGlobalState::kFixWinding);
    while ((topSpan = FindSortableTop(&contourHead))) {
        SkOpSegment* topSegment = topSpan->segment();
        SkOpContour* topContour = topSegment->contour();
        SkASSERT(topContour->isCcw() >= 0);
#if DEBUG_WINDING
        SkDebugf("%s id=%d nested=%d ccw=%d\n",  __FUNCTION__,
                topSegment->debugID(), globalState.nested(), topContour->isCcw());
#endif
        if ((globalState.nested() & 1) != SkToBool(topContour->isCcw())) {
            topContour->setReverse();
            writePath = true;
        }
        topContour->markDone();
        globalState.clearNested();
    }
    if (!writePath) {
        path->setFillType(fillType);
        return;
    }
    SkPath empty;
    SkPathWriter woundPath(empty);
    SkOpContour* test = &contourHead;
    do {
        if (test->reversed()) {
            test->toReversePath(&woundPath);
        } else {
            test->toPath(&woundPath);
        }
    } while ((test = test->next()));
    *path = *woundPath.nativePath();
    path->setFillType(fillType);
}
Esempio n. 3
0
// please keep in sync with debugMergeMatches()
// Look to see if pt-t linked list contains same segment more than once
// if so, and if each pt-t is directly pointed to by spans in that segment,
// merge them
// keep the points, but remove spans so that the segment doesn't have 2 or more
// spans pointing to the same pt-t loop at different loop elements
void SkOpSpanBase::mergeMatches(SkOpSpanBase* opp) {
    SkOpPtT* test = &fPtT;
    SkOpPtT* testNext;
    const SkOpPtT* stop = test;
    do {
        testNext = test->next();
        if (test->deleted()) {
            continue;
        }
        SkOpSpanBase* testBase = test->span();
        SkASSERT(testBase->ptT() == test);
        SkOpSegment* segment = test->segment();
        if (segment->done()) {
            continue;
        }
        SkOpPtT* inner = opp->ptT();
        const SkOpPtT* innerStop = inner;
        do {
            if (inner->segment() != segment) {
                continue;
            }
            if (inner->deleted()) {
                continue;
            }
            SkOpSpanBase* innerBase = inner->span();
            SkASSERT(innerBase->ptT() == inner);
            // when the intersection is first detected, the span base is marked if there are 
            // more than one point in the intersection.
            if (!zero_or_one(inner->fT)) {
                innerBase->upCast()->release(test);
            } else {
                SkOPASSERT(inner->fT != test->fT);
                if (!zero_or_one(test->fT)) {
                    testBase->upCast()->release(inner);
                } else {
                    segment->markAllDone();  // mark segment as collapsed
                    SkDEBUGCODE(testBase->debugSetDeleted());
                    test->setDeleted();
                    SkDEBUGCODE(innerBase->debugSetDeleted());
                    inner->setDeleted();
                }
            }
#ifdef SK_DEBUG   // assert if another undeleted entry points to segment
            const SkOpPtT* debugInner = inner;
            while ((debugInner = debugInner->next()) != innerStop) {
                if (debugInner->segment() != segment) {
                    continue;
                }
                if (debugInner->deleted()) {
                    continue;
                }
                SkOPASSERT(0);
            }
#endif
            break;
        } while ((inner = inner->next()) != innerStop);
    } while ((test = testNext) != stop);
    this->checkForCollapsedCoincidence();
}
Esempio n. 4
0
SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList, bool* firstContour,
                             int* indexPtr, int* endIndexPtr, SkPoint* topLeft, bool* unsortable,
                             bool* done,  bool binary) {
    SkOpSegment* current = findSortableTop(contourList, indexPtr, endIndexPtr, topLeft, unsortable,
            done, true);
    if (!current) {
        return NULL;
    }
    const int index = *indexPtr;
    const int endIndex = *endIndexPtr;
    if (*firstContour) {
        current->initWinding(index, endIndex);
        *firstContour = false;
        return current;
    }
    int minIndex = SkMin32(index, endIndex);
    int sumWinding = current->windSum(minIndex);
    if (sumWinding != SK_MinS32) {
        return current;
    }
    sumWinding = current->computeSum(index, endIndex, binary);
    if (sumWinding != SK_MinS32) {
        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;
    do {
        // if current is vertical, find another candidate which is not
        // if only remaining candidates are vertical, then they can be marked done
        SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
        skipVertical(contourList, &current, indexPtr, endIndexPtr);

        SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
        tryAgain = false;
        contourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
                &hitDx, &tryAgain, false);
        if (tryAgain) {
            continue;
        }
        if (!binary) {
            break;
        }
        oppContourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
                &hitOppDx, &tryAgain, true);
    } while (tryAgain);
    current->initWinding(*indexPtr, *endIndexPtr, tHit, contourWinding, hitDx, oppContourWinding,
            hitOppDx);
    return current;
}
Esempio n. 5
0
SkOpSegment* SkOpContour::undoneSegment(SkOpSpanBase** startPtr, SkOpSpanBase** endPtr) {
    SkOpSegment* segment = &fHead;
    do {
        if (segment->done()) {
            continue;
        }
        segment->undoneSpan(startPtr, endPtr);
        return segment;
    } while ((segment = segment->next()));
    return nullptr;
}
Esempio n. 6
0
SkOpSpan* SkOpContour::undoneSpan() {
    SkOpSegment* testSegment = &fHead;
    do {
        if (testSegment->done()) {
            continue;
        }
        return testSegment->undoneSpan();
    } while ((testSegment = testSegment->next()));
    fDone = true;
    return nullptr;
}
Esempio n. 7
0
SkOpSegment* SkOpContour::undoneSegment(int* start, int* end) {
    int segmentCount = fSegments.count();
    for (int test = 0; test < segmentCount; ++test) {
        SkOpSegment* testSegment = &fSegments[test];
        if (testSegment->done()) {
            continue;
        }
        testSegment->undoneSpan(start, end);
        return testSegment;
    }
    return NULL;
}
Esempio n. 8
0
const SkOpAngle* AngleWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* windingPtr,
        bool* sortablePtr) {
    // find first angle, initialize winding to computed fWindSum
    SkOpSegment* segment = start->segment();
    const SkOpAngle* angle = segment->spanToAngle(start, end);
    if (!angle) {
        *windingPtr = SK_MinS32;
        return nullptr;
    }
    bool computeWinding = false;
    const SkOpAngle* firstAngle = angle;
    bool loop = false;
    bool unorderable = false;
    int winding = SK_MinS32;
    do {
        angle = angle->next();
        if (!angle) {
            return nullptr;
        }
        unorderable |= angle->unorderable();
        if ((computeWinding = unorderable || (angle == firstAngle && loop))) {
            break;    // if we get here, there's no winding, loop is unorderable
        }
        loop |= angle == firstAngle;
        segment = angle->segment();
        winding = segment->windSum(angle);
    } while (winding == SK_MinS32);
    // if the angle loop contains an unorderable span, the angle order may be useless
    // directly compute the winding in this case for each span
    if (computeWinding) {
        firstAngle = angle;
        winding = SK_MinS32;
        do {
            SkOpSpanBase* startSpan = angle->start();
            SkOpSpanBase* endSpan = angle->end();
            SkOpSpan* lesser = startSpan->starter(endSpan);
            int testWinding = lesser->windSum();
            if (testWinding == SK_MinS32) {
                testWinding = lesser->computeWindSum();
            }
            if (testWinding != SK_MinS32) {
                segment = angle->segment();
                winding = testWinding;
            }
            angle = angle->next();
        } while (angle != firstAngle);
    }
    *sortablePtr = !unorderable;
    *windingPtr = winding;
    return angle;
}
Esempio n. 9
0
SkOpSegment* SkOpContour::nonVerticalSegment(int* start, int* end) {
    int segmentCount = fSortedSegments.count();
    SkASSERT(segmentCount > 0);
    for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedIndex) {
        SkOpSegment* testSegment = fSortedSegments[sortedIndex];
        if (testSegment->done()) {
            continue;
        }
        *start = *end = 0;
        while (testSegment->nextCandidate(start, end)) {
            if (!testSegment->isVertical(*start, *end)) {
                return testSegment;
            }
        }
    }
    return NULL;
}
Esempio n. 10
0
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;
    int lastIndex = -1, lastEndIndex = -1;
    do {
        SkPoint 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(index, endIndex, unsortable, firstPass);
        if (!result) {
            if (lastTopStart == topStart && lastIndex == *index && lastEndIndex == *endIndex) {
                *done = true;
                return NULL;
            }
            lastTopStart = topStart;
            lastIndex = *index;
            lastEndIndex = *endIndex;
        }
    } while (!result);
    return result;
}
Esempio n. 11
0
DEF_TEST(PathOpsAngleCircle, reporter) {
    SkSTArenaAlloc<4096> allocator;
    SkOpContourHead contour;
    SkOpGlobalState state(&contour, &allocator  SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
    contour.init(&state, false, false);
    for (int index = 0; index < circleDataSetSize; ++index) {
        CircleData& data = circleDataSet[index];
        for (int idx2 = 0; idx2 < data.fPtCount; ++idx2) {
            data.fShortPts[idx2] = data.fPts.fPts[idx2].asSkPoint();
        }
        switch (data.fPtCount) {
            case 2:
                contour.addLine(data.fShortPts);
                break;
            case 3:
                contour.addQuad(data.fShortPts);
                break;
            case 4:
                contour.addCubic(data.fShortPts);
                break;
        }
    }
    SkOpSegment* first = contour.first();
    first->debugAddAngle(0, 1);
    SkOpSegment* next = first->next();
    next->debugAddAngle(0, 1);
    PathOpsAngleTester::Orderable(*first->debugLastAngle(), *next->debugLastAngle());
}
Esempio n. 12
0
SkOpSegment* SkOpContour::nonVerticalSegment(SkOpSpanBase** start, SkOpSpanBase** end) {
    int segmentCount = fSortedSegments.count();
    SkASSERT(segmentCount > 0);
    for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedIndex) {
        SkOpSegment* testSegment = fSortedSegments[sortedIndex];
        if (testSegment->done()) {
            continue;
        }
        SkOpSpanBase* span = testSegment->head();
        SkOpSpanBase* testS, * testE;
        while (SkOpSegment::NextCandidate(span, &testS, &testE)) {
            if (!testSegment->isVertical(testS, testE)) {
                *start = testS;
                *end = testE;
                return testSegment;
            }
            span = span->upCast()->next();
        }
    }
    return NULL;
}
Esempio n. 13
0
// returns true if all edges were processed
static bool bridgeXor(SkTDArray<SkOpContour*>& contourList, SkPathWriter* simple) {
    SkOpSegment* current;
    int start, end;
    bool unsortable = false;
    bool closable = true;
    while ((current = FindUndone(contourList, &start, &end))) {
        do {
    #if DEBUG_ACTIVE_SPANS
            if (!unsortable && current->done()) {
                DebugShowActiveSpans(contourList);
            }
    #endif
            SkASSERT(unsortable || !current->done());
            int nextStart = start;
            int nextEnd = end;
            SkOpSegment* next = current->findNextXor(&nextStart, &nextEnd, &unsortable);
            if (!next) {
                if (!unsortable && simple->hasMove()
                        && current->verb() != SkPath::kLine_Verb
                        && !simple->isClosed()) {
                    current->addCurveTo(start, end, simple, true);
                    SkASSERT(simple->isClosed());
                }
                break;
            }
        #if DEBUG_FLOW
            SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
                    current->debugID(), current->xyAtT(start).fX, current->xyAtT(start).fY,
                    current->xyAtT(end).fX, current->xyAtT(end).fY);
        #endif
            current->addCurveTo(start, end, simple, true);
            current = next;
            start = nextStart;
            end = nextEnd;
        } while (!simple->isClosed() && (!unsortable || !current->done(SkMin32(start, end))));
        if (!simple->isClosed()) {
            SkASSERT(unsortable);
            int min = SkMin32(start, end);
            if (!current->done(min)) {
                current->addCurveTo(start, end, simple, true);
                current->markDone(min, 1);
            }
            closable = false;
        }
        simple->close();
    #if DEBUG_ACTIVE_SPANS
        DebugShowActiveSpans(contourList);
    #endif
    }
    return closable;
}
Esempio n. 14
0
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 = current->tAtMid(index, endIndex, mid);
    SkPoint basePt = current->ptAtT(tAtMid);
    int contourCount = contourList.count();
    SkScalar bestY = SK_ScalarMin;
    SkOpSegment* bestSeg = NULL;
    int bestTIndex = 0;
    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;
        }
        int segmentCount = contour->segments().count();
        for (int test = 0; test < segmentCount; ++test) {
            SkOpSegment* testSeg = &contour->segments()[test];
            SkScalar testY = bestY;
            double testHit;
            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 && 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 = 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, current->xAtT(index), current->yAtT(index),
                        baseT + mid * (endT - baseT), midXY.fX, midXY.fY,
                        baseT + newMid * (endT - baseT), newXY.fX, newXY.fY,
                        endT, current->xAtT(endIndex), current->yAtT(endIndex));
#endif
                *midPtr = newMid * 2;  // calling loop with divide by 2 before continuing
                return SK_MinS32;
            }
            bestSeg = testSeg;
            *bestHit = testHit;
            bestOpp = testOpp;
            bestTIndex = testTIndex;
            bestY = testY;
        }
    }
abortContours:
    int result;
    if (!bestSeg) {
        result = hitSomething ? SK_MinS32 : 0;
    } else {
        if (bestSeg->windSum(bestTIndex) == SK_MinS32) {
            *currentPtr = bestSeg;
            *indexPtr = bestTIndex;
            *endIndexPtr = bestSeg->nextSpan(bestTIndex, 1);
            SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
            *tryAgain = true;
            return 0;
        }
        result = bestSeg->windingAtT(*bestHit, bestTIndex, bestOpp, bestDx);
        SkASSERT(result == SK_MinS32 || *bestDx);
    }
    double baseT = current->t(index);
    double endT = current->t(endIndex);
    *bestHit = baseT + mid * (endT - baseT);
    return result;
}
Esempio n. 15
0
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;
    }
    const int startIndex = *indexPtr;
    const int endIndex = *endIndexPtr;
    if (*firstContour) {
        current->initWinding(startIndex, endIndex, angleIncludeType);
        *firstContour = false;
        return current;
    }
    int minIndex = SkMin32(startIndex, endIndex);
    int sumWinding = current->windSum(minIndex);
    if (sumWinding == SK_MinS32) {
        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;
    }
    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;
    do {
        // if current is vertical, find another candidate which is not
        // if only remaining candidates are vertical, then they can be marked done
        SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
        skipVertical(contourList, &current, indexPtr, endIndexPtr);
        SkASSERT(current);  // FIXME: if null, all remaining are vertical
        SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
        tryAgain = false;
        contourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
                &hitDx, &tryAgain, onlyVertical, false);
        if (*onlyVertical) {
            return current;
        }
        if (tryAgain) {
            continue;
        }
        if (angleIncludeType < SkOpAngle::kBinarySingle) {
            break;
        }
        oppContourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
                &hitOppDx, &tryAgain, NULL, true);
    } while (tryAgain);
    current->initWinding(*indexPtr, *endIndexPtr, tHit, contourWinding, hitDx, oppContourWinding,
            hitOppDx);
    if (current->done()) {
        return NULL;
    }
    return current;
}
Esempio n. 16
0
SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
        SkOpSpanBase** endPtr) {
    while (chase->count()) {
        SkOpSpanBase* span;
        chase->pop(&span);
        SkOpSegment* segment = span->segment();
        *startPtr = span->ptT()->next()->span();
        bool done = true;
        *endPtr = nullptr;
        if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) {
            *startPtr = last->start();
            *endPtr = last->end();
    #if TRY_ROTATE
            *chase->insert(0) = span;
    #else
            *chase->append() = span;
    #endif
            return last->segment();
        }
        if (done) {
            continue;
        }
        // find first angle, initialize winding to computed wind sum
        int winding;
        bool sortable;
        const SkOpAngle* angle = AngleWinding(*startPtr, *endPtr, &winding, &sortable);
        if (winding == SK_MinS32) {
            continue;
        }
        int sumWinding SK_INIT_TO_AVOID_WARNING;
        if (sortable) {
            segment = angle->segment();
            sumWinding = segment->updateWindingReverse(angle);
        }
        SkOpSegment* first = nullptr;
        const SkOpAngle* firstAngle = angle;
        while ((angle = angle->next()) != firstAngle) {
            segment = angle->segment();
            SkOpSpanBase* start = angle->start();
            SkOpSpanBase* end = angle->end();
            int maxWinding;
            if (sortable) {
                segment->setUpWinding(start, end, &maxWinding, &sumWinding);
            }
            if (!segment->done(angle)) {
                if (!first && (sortable || start->starter(end)->windSum() != SK_MinS32)) {
                    first = segment;
                    *startPtr = start;
                    *endPtr = end;
                }
                // OPTIMIZATION: should this also add to the chase?
                if (sortable) {
                    (void) segment->markAngle(maxWinding, sumWinding, angle);
                }
            }
        }
        if (first) {
       #if TRY_ROTATE
            *chase->insert(0) = span;
       #else
            *chase->append() = span;
       #endif
            return first;
        }
    }
    return nullptr;
}
Esempio n. 17
0
SkOpSegment* FindChase(SkTDArray<SkOpSpan*>* chase, int* tIndex, int* endIndex) {
    while (chase->count()) {
        SkOpSpan* span;
        chase->pop(&span);
        const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
        SkOpSegment* segment = backPtr.fOther;
        *tIndex = backPtr.fOtherIndex;
        bool sortable = true;
        bool done = true;
        *endIndex = -1;
        if (const SkOpAngle* last = segment->activeAngle(*tIndex, tIndex, endIndex, &done,
                &sortable)) {
            *tIndex = last->start();
            *endIndex = last->end();
    #if TRY_ROTATE
            *chase->insert(0) = span;
    #else
            *chase->append() = span;
    #endif
            return last->segment();
        }
        if (done) {
            continue;
        }
        if (!sortable) {
            continue;
        }
        // find first angle, initialize winding to computed fWindSum
        const SkOpAngle* angle = segment->spanToAngle(*tIndex, *endIndex);
        const SkOpAngle* firstAngle;
        SkDEBUGCODE(firstAngle = angle);
        SkDEBUGCODE(bool loop = false);
        int winding;
        do {
            angle = angle->next();
            SkASSERT(angle != firstAngle || !loop);
            SkDEBUGCODE(loop |= angle == firstAngle);
            segment = angle->segment();
            winding = segment->windSum(angle);
        } while (winding == SK_MinS32);
        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();
            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;
                }
                (void) segment->markAndChaseWinding(angle, maxWinding, 0);
                break;
            }
        }
        *chase->insert(0) = span;
        return segment;
    }
    return NULL;
}
Esempio n. 18
0
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;
    }
    const int startIndex = *indexPtr;
    const int endIndex = *endIndexPtr;
    if (*firstContour) {
        current->initWinding(startIndex, endIndex, angleIncludeType);
        *firstContour = false;
        return current;
    }
    int minIndex = SkMin32(startIndex, endIndex);
    int sumWinding = current->windSum(minIndex);
    if (sumWinding == SK_MinS32) {
        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;
    }
    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<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(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
        skipVertical(contourList, &current, indexPtr, endIndexPtr);
        SkASSERT(current);  // FIXME: if null, all remaining are vertical
        SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
        tryAgain = false;
        contourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
                &hitDx, &tryAgain, onlyVertical, false);
        if (tryAgain) {
            bool giveUp = false;
            int count = sortableTops.count();
            for (int index = 0; index < count; ++index) {
                const SortableTop& prev = sortableTops[index];
                if (giveUp) {
                    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;
                    index = -1;
                }
            }
            if (giveUp) {
                *done = true;
                return NULL;
            }
        }
        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(), *indexPtr, *endIndexPtr, tHit, hitDx, tryAgain,
                *onlyVertical);
#endif
        if (*onlyVertical) {
            return current;
        }
        if (tryAgain) {
            continue;
        }
        if (angleIncludeType < SkOpAngle::kBinarySingle) {
            break;
        }
        oppContourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
                &hitOppDx, &tryAgain, NULL, true);
    } while (tryAgain);
    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
        int min = SkTMin(*indexPtr, *endIndexPtr);
        const SkOpSpan& span = current->span(min);
        if (span.fWindSum == SK_MinS32) {
            return NULL;
        }
    }
    return current;
}
Esempio n. 19
0
// FIXME: this and find chase should be merge together, along with
// other code that walks winding in angles
// OPTIMIZATION: Probably, the walked winding should be rolled into the angle structure
// so it isn't duplicated by walkers like this one
static SkOpSegment* findChaseOp(SkTDArray<SkOpSpan*>& chase, int& nextStart, int& nextEnd) {
    while (chase.count()) {
        SkOpSpan* span;
        chase.pop(&span);
        const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
        SkOpSegment* segment = backPtr.fOther;
        nextStart = backPtr.fOtherIndex;
        SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles;
        int done = 0;
        if (segment->activeAngle(nextStart, &done, &angles)) {
            SkOpAngle* last = angles.end() - 1;
            nextStart = last->start();
            nextEnd = last->end();
   #if TRY_ROTATE
            *chase.insert(0) = span;
   #else
            *chase.append() = span;
   #endif
            return last->segment();
        }
        if (done == angles.count()) {
            continue;
        }
        SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted;
        bool sortable = SkOpSegment::SortAngles(angles, &sorted,
                SkOpSegment::kMayBeUnordered_SortAngleKind);
        int angleCount = sorted.count();
#if DEBUG_SORT
        sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, sortable);
#endif
        if (!sortable) {
            continue;
        }
        // find first angle, initialize winding to computed fWindSum
        int firstIndex = -1;
        const SkOpAngle* angle;
        bool foundAngle = true;
        do {
            ++firstIndex;
            if (firstIndex >= angleCount) {
                foundAngle = false;
                break;
            }
            angle = sorted[firstIndex];
            segment = angle->segment();
        } while (segment->windSum(angle) == SK_MinS32);
        if (!foundAngle) {
            continue;
        }
    #if DEBUG_SORT
        segment->debugShowSort(__FUNCTION__, sorted, firstIndex, sortable);
    #endif
        int sumMiWinding = segment->updateWindingReverse(angle);
        int sumSuWinding = segment->updateOppWindingReverse(angle);
        if (segment->operand()) {
            SkTSwap<int>(sumMiWinding, sumSuWinding);
        }
        int nextIndex = firstIndex + 1;
        int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
        SkOpSegment* first = NULL;
        do {
            SkASSERT(nextIndex != firstIndex);
            if (nextIndex == angleCount) {
                nextIndex = 0;
            }
            angle = sorted[nextIndex];
            segment = angle->segment();
            int start = angle->start();
            int end = angle->end();
            int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
            segment->setUpWindings(start, end, &sumMiWinding, &sumSuWinding,
                    &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
            if (!segment->done(angle)) {
                if (!first) {
                    first = segment;
                    nextStart = start;
                    nextEnd = end;
                }
                (void) segment->markAngle(maxWinding, sumWinding, oppMaxWinding,
                    oppSumWinding, angle);
            }
        } while (++nextIndex != lastIndex);
        if (first) {
       #if TRY_ROTATE
            *chase.insert(0) = span;
       #else
            *chase.append() = span;
       #endif
            return first;
        }
    }
    return NULL;
}
Esempio n. 20
0
static bool bridgeOp(SkTArray<SkOpContour*, true>& contourList, const SkPathOp op,
        const int xorMask, const int xorOpMask, SkPathWriter* simple) {
    bool firstContour = true;
    bool unsortable = false;
    bool topUnsortable = false;
    SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
    do {
        int index, endIndex;
        bool done;
        SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kBinarySingle, &firstContour,
                &index, &endIndex, &topLeft, &topUnsortable, &done);
        if (!current) {
            if (topUnsortable || !done) {
                topUnsortable = false;
                SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
                topLeft.fX = topLeft.fY = SK_ScalarMin;
                continue;
            }
            break;
        }
        SkTDArray<SkOpSpan*> chaseArray;
        do {
            if (current->activeOp(index, endIndex, xorMask, xorOpMask, op)) {
                do {
                    if (!unsortable && current->done()) {
            #if DEBUG_ACTIVE_SPANS
                        DebugShowActiveSpans(contourList);
            #endif
                        if (simple->isEmpty()) {
                            simple->init();
                        }
                        break;
                    }
                    SkASSERT(unsortable || !current->done());
                    int nextStart = index;
                    int nextEnd = endIndex;
                    SkOpSegment* next = current->findNextOp(&chaseArray, &nextStart, &nextEnd,
                            &unsortable, op, xorMask, xorOpMask);
                    if (!next) {
                        if (!unsortable && simple->hasMove()
                                && current->verb() != SkPath::kLine_Verb
                                && !simple->isClosed()) {
                            current->addCurveTo(index, endIndex, simple, true);
                            SkASSERT(simple->isClosed());
                        }
                        break;
                    }
        #if DEBUG_FLOW
            SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
                    current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
                    current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
        #endif
                    current->addCurveTo(index, endIndex, simple, true);
                    current = next;
                    index = nextStart;
                    endIndex = nextEnd;
                } while (!simple->isClosed() && (!unsortable
                        || !current->done(SkMin32(index, endIndex))));
                if (current->activeWinding(index, endIndex) && !simple->isClosed()) {
                    // FIXME : add to simplify, xor cpaths
                    int min = SkMin32(index, endIndex);
                    if (!unsortable && !simple->isEmpty()) {
                        unsortable = current->checkSmall(min);
                    }
                    SkASSERT(unsortable || simple->isEmpty());
                    if (!current->done(min)) {
                        current->addCurveTo(index, endIndex, simple, true);
                        current->markDoneBinary(min);
                    }
                }
                simple->close();
            } else {
                SkOpSpan* last = current->markAndChaseDoneBinary(index, endIndex);
                if (last && !last->fLoop) {
                    *chaseArray.append() = last;
                }
            }
            current = findChaseOp(chaseArray, index, endIndex);
        #if DEBUG_ACTIVE_SPANS
            DebugShowActiveSpans(contourList);
        #endif
            if (!current) {
                break;
            }
        } while (true);
    } while (true);
    return simple->someAssemblyRequired();
}
Esempio n. 21
0
SkOpSegment* FindChase(SkTDArray<SkOpSpan*>& chase, int& tIndex, int& endIndex) {
    while (chase.count()) {
        SkOpSpan* span;
        chase.pop(&span);
        const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
        SkOpSegment* segment = backPtr.fOther;
        tIndex = backPtr.fOtherIndex;
        SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles;
        int done = 0;
        if (segment->activeAngle(tIndex, &done, &angles)) {
            SkOpAngle* last = angles.end() - 1;
            tIndex = last->start();
            endIndex = last->end();
   #if TRY_ROTATE
            *chase.insert(0) = span;
   #else
            *chase.append() = span;
   #endif
            return last->segment();
        }
        if (done == angles.count()) {
            continue;
        }
        SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted;
        bool sortable = SkOpSegment::SortAngles(angles, &sorted,
                SkOpSegment::kMayBeUnordered_SortAngleKind);
        int angleCount = sorted.count();
#if DEBUG_SORT
        sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
#endif
        if (!sortable) {
            continue;
        }
        // find first angle, initialize winding to computed fWindSum
        int firstIndex = -1;
        const SkOpAngle* angle;
        int winding;
        do {
            angle = sorted[++firstIndex];
            segment = angle->segment();
            winding = segment->windSum(angle);
        } while (winding == SK_MinS32);
        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;
        }
    #if DEBUG_SORT
        segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
    #endif
        // 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)
        int nextIndex = firstIndex + 1;
        int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
        angle = sorted[firstIndex];
        winding -= angle->segment()->spanSign(angle);
        do {
            SkASSERT(nextIndex != firstIndex);
            if (nextIndex == angleCount) {
                nextIndex = 0;
            }
            angle = sorted[nextIndex];
            segment = angle->segment();
            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;
                }
                segment->markAndChaseWinding(angle, maxWinding, 0);
                break;
            }
        } while (++nextIndex != lastIndex);
        *chase.insert(0) = span;
        return segment;
    }
    return NULL;
}
Esempio n. 22
0
static SkOpSegment* findChaseOp(SkTDArray<SkOpSpan*>& chase, int* tIndex, int* endIndex) {
    while (chase.count()) {
        SkOpSpan* span;
        chase.pop(&span);
        const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
        SkOpSegment* segment = backPtr.fOther;
        *tIndex = backPtr.fOtherIndex;
        bool sortable = true;
        bool done = true;
        *endIndex = -1;
        if (const SkOpAngle* last = segment->activeAngle(*tIndex, tIndex, endIndex, &done,
                &sortable)) {
            if (last->unorderable()) {
                continue;
            }
            *tIndex = last->start();
            *endIndex = last->end();
   #if TRY_ROTATE
            *chase.insert(0) = span;
   #else
            *chase.append() = span;
   #endif
            return last->segment();
        }
        if (done) {
            continue;
        }
        if (!sortable) {
            continue;
        }
        // find first angle, initialize winding to computed fWindSum
        const SkOpAngle* angle = segment->spanToAngle(*tIndex, *endIndex);
        if (!angle) {
            continue;
        }
        const SkOpAngle* firstAngle = angle;
        SkDEBUGCODE(bool loop = false);
        int winding;
        do {
            angle = angle->next();
            SkASSERT(angle != firstAngle || !loop);
            SkDEBUGCODE(loop |= angle == firstAngle);
            segment = angle->segment();
            winding = segment->windSum(angle);
        } while (winding == SK_MinS32);
        int sumMiWinding = segment->updateWindingReverse(angle);
        int sumSuWinding = segment->updateOppWindingReverse(angle);
        if (segment->operand()) {
            SkTSwap<int>(sumMiWinding, sumSuWinding);
        }
        SkOpSegment* first = NULL;
        while ((angle = angle->next()) != firstAngle) {
            segment = angle->segment();
            int start = angle->start();
            int end = angle->end();
            int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
            segment->setUpWindings(start, end, &sumMiWinding, &sumSuWinding,
                    &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
            if (!segment->done(angle)) {
                if (!first) {
                    first = segment;
                    *tIndex = start;
                    *endIndex = end;
                }
                // OPTIMIZATION: should this also add to the chase?
                (void) segment->markAngle(maxWinding, sumWinding, oppMaxWinding,
                    oppSumWinding, angle);
            }
        }
        if (first) {
       #if TRY_ROTATE
            *chase.insert(0) = span;
       #else
            *chase.append() = span;
       #endif
            return first;
        }
    }
    return NULL;
}
Esempio n. 23
0
static bool bridgeOp(SkTArray<SkOpContour*, true>& contourList, const SkPathOp op,
        const int xorMask, const int xorOpMask, SkPathWriter* simple) {
    bool firstContour = true;
    bool unsortable = false;
    bool topUnsortable = false;
    bool firstPass = true;
    SkPoint lastTopLeft;
    SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
    do {
        int index, endIndex;
        bool topDone;
        bool onlyVertical = false;
        lastTopLeft = topLeft;
        SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kBinarySingle, &firstContour,
                &index, &endIndex, &topLeft, &topUnsortable, &topDone, &onlyVertical, firstPass);
        if (!current) {
            if ((!topUnsortable || firstPass) && !topDone) {
                SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
                if (lastTopLeft.fX == SK_ScalarMin && lastTopLeft.fY == SK_ScalarMin) {
                    if (firstPass) {
                        firstPass = false;
                    } else {
                        break;
                    }
                }
                topLeft.fX = topLeft.fY = SK_ScalarMin;
                continue;
            }
            break;
        } else if (onlyVertical) {
            break;
        }
        firstPass = !topUnsortable || lastTopLeft != topLeft;
        SkTDArray<SkOpSpan*> chase;
        do {
            if (current->activeOp(index, endIndex, xorMask, xorOpMask, op)) {
                do {
                    if (!unsortable && current->done()) {
                        break;
                    }
                    SkASSERT(unsortable || !current->done());
                    int nextStart = index;
                    int nextEnd = endIndex;
                    SkOpSegment* next = current->findNextOp(&chase, &nextStart, &nextEnd,
                            &unsortable, op, xorMask, xorOpMask);
                    if (!next) {
                        if (!unsortable && simple->hasMove()
                                && current->verb() != SkPath::kLine_Verb
                                && !simple->isClosed()) {
                            current->addCurveTo(index, endIndex, simple, true);
                    #if DEBUG_ACTIVE_SPANS
                            if (!simple->isClosed()) {
                                DebugShowActiveSpans(contourList);
                            }
                    #endif
//                            SkASSERT(simple->isClosed());
                        }
                        break;
                    }
        #if DEBUG_FLOW
            SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
                    current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
                    current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
        #endif
                    current->addCurveTo(index, endIndex, simple, true);
                    current = next;
                    index = nextStart;
                    endIndex = nextEnd;
                } while (!simple->isClosed() && (!unsortable
                        || !current->done(SkMin32(index, endIndex))));
                if (current->activeWinding(index, endIndex) && !simple->isClosed()) {
                    // FIXME : add to simplify, xor cpaths
                    int min = SkMin32(index, endIndex);
                    if (!unsortable && !simple->isEmpty()) {
                        unsortable = current->checkSmall(min);
                    }
                    if (!current->done(min)) {
                        current->addCurveTo(index, endIndex, simple, true);
                        current->markDoneBinary(min);
                    }
                }
                simple->close();
            } else {
                SkOpSpan* last = current->markAndChaseDoneBinary(index, endIndex);
                if (last && !last->fChased && !last->fLoop) {
                    last->fChased = true;
                    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->span(last->fOtherIndex).fOther->debugID(), last->fWindSum,
                            last->fSmall);
#endif
                }
            }
            current = findChaseOp(chase, &index, &endIndex);
        #if DEBUG_ACTIVE_SPANS
            DebugShowActiveSpans(contourList);
        #endif
            if (!current) {
                break;
            }
        } while (true);
    } while (true);
    return simple->someAssemblyRequired();
}