| Index: src/pathops/SkOpSegment.cpp | 
| diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp | 
| index 41d62369b644dfe12363173375bba0ab47ae0d9c..e4e00bbfab6fb46585aef89bb3845b24baa512b2 100644 | 
| --- a/src/pathops/SkOpSegment.cpp | 
| +++ b/src/pathops/SkOpSegment.cpp | 
| @@ -9,6 +9,8 @@ | 
| #include "SkOpSegment.h" | 
| #include "SkPathWriter.h" | 
|  | 
| +#define FAIL_IF(cond) do { if (cond) return false; } while (false) | 
| + | 
| /* | 
| After computing raw intersections, post process all segments to: | 
| - find small collections of points that can be collapsed to a single point | 
| @@ -159,90 +161,10 @@ bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sum | 
| return result; | 
| } | 
|  | 
| -void SkOpSegment::addAlignIntersection(SkOpPtT& endPtT, SkPoint& oldPt, | 
| -        SkOpContourHead* contourList, SkChunkAlloc* allocator) { | 
| -    const SkPoint& newPt = endPtT.fPt; | 
| -    if (newPt == oldPt) { | 
| -        return; | 
| -    } | 
| -    SkPoint line[2] = { newPt, oldPt }; | 
| -    SkPathOpsBounds lineBounds; | 
| -    lineBounds.setBounds(line, 2); | 
| -    SkDLine aLine; | 
| -    aLine.set(line); | 
| -    SkOpContour* current = contourList; | 
| -    do { | 
| -        if (!SkPathOpsBounds::Intersects(current->bounds(), lineBounds)) { | 
| -            continue; | 
| -        } | 
| -        SkOpSegment* segment = current->first(); | 
| -        do { | 
| -            if (!SkPathOpsBounds::Intersects(segment->bounds(), lineBounds)) { | 
| -                continue; | 
| -            } | 
| -            if (newPt == segment->fPts[0]) { | 
| -                continue; | 
| -            } | 
| -            if (newPt == segment->fPts[SkPathOpsVerbToPoints(segment->fVerb)]) { | 
| -                continue; | 
| -            } | 
| -            if (oldPt == segment->fPts[0]) { | 
| -                continue; | 
| -            } | 
| -            if (oldPt == segment->fPts[SkPathOpsVerbToPoints(segment->fVerb)]) { | 
| -                continue; | 
| -            } | 
| -            if (endPtT.contains(segment)) { | 
| -                continue; | 
| -            } | 
| -            SkIntersections i; | 
| -            switch (segment->fVerb) { | 
| -                case SkPath::kLine_Verb: { | 
| -                    SkDLine bLine; | 
| -                    bLine.set(segment->fPts); | 
| -                    i.intersect(bLine, aLine); | 
| -                    } break; | 
| -                case SkPath::kQuad_Verb: { | 
| -                    SkDQuad bQuad; | 
| -                    bQuad.set(segment->fPts); | 
| -                    i.intersect(bQuad, aLine); | 
| -                    } break; | 
| -                case SkPath::kConic_Verb: { | 
| -                    SkDConic bConic; | 
| -                    bConic.set(segment->fPts, segment->fWeight); | 
| -                    i.intersect(bConic, aLine); | 
| -                    } break; | 
| -                case SkPath::kCubic_Verb: { | 
| -                    SkDCubic bCubic; | 
| -                    bCubic.set(segment->fPts); | 
| -                    i.intersect(bCubic, aLine); | 
| -                    } break; | 
| -                default: | 
| -                    SkASSERT(0); | 
| -            } | 
| -            if (i.used()) { | 
| -                SkASSERT(i.used() == 1); | 
| -                SkASSERT(!zero_or_one(i[0][0])); | 
| -                SkOpSpanBase* checkSpan = fHead.next(); | 
| -                while (!checkSpan->final()) { | 
| -                    if (checkSpan->contains(segment)) { | 
| -                        goto nextSegment; | 
| -                    } | 
| -                    checkSpan = checkSpan->upCast()->next(); | 
| -                } | 
| -                SkOpPtT* ptT = segment->addT(i[0][0], SkOpSegment::kAllowAlias, allocator); | 
| -                ptT->fPt = newPt; | 
| -                endPtT.addOpp(ptT); | 
| -            } | 
| -    nextSegment: | 
| -            ; | 
| -        } while ((segment = segment->next())); | 
| -    } while ((current = current->next())); | 
| -} | 
| - | 
| bool SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end, | 
| SkPathWriter* path) const { | 
| if (start->starter(end)->alreadyAdded()) { | 
| +        SkDEBUGF(("same curve added twice aborted pathops\n")); | 
| return false; | 
| } | 
| SkOpCurve edge; | 
| @@ -298,29 +220,57 @@ bool SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end, | 
| return true; | 
| } | 
|  | 
| -SkOpPtT* SkOpSegment::addMissing(double t, SkOpSegment* opp, SkChunkAlloc* allocator) { | 
| -    SkOpSpanBase* existing = nullptr; | 
| -    SkOpSpanBase* test = &fHead; | 
| -    double testT; | 
| +const SkOpPtT* SkOpSegment::existing(double t, const SkOpSegment* opp) const { | 
| +    const SkOpSpanBase* test = &fHead; | 
| +    const SkOpPtT* testPtT; | 
| +    SkPoint pt = this->ptAtT(t); | 
| do { | 
| -        if ((testT = test->ptT()->fT) >= t) { | 
| -            if (testT == t) { | 
| -                existing = test; | 
| -            } | 
| +        testPtT = test->ptT(); | 
| +        if (testPtT->fT == t) { | 
| break; | 
| } | 
| +        if (!this->match(testPtT, this, t, pt, opp ? kAllowAliasMatch : kNoAliasMatch)) { | 
| +            if (t < testPtT->fT) { | 
| +                return nullptr; | 
| +            } | 
| +            continue; | 
| +        } | 
| +        if (!opp) { | 
| +            return testPtT; | 
| +        } | 
| +        const SkOpPtT* loop = testPtT->next(); | 
| +        while (loop != testPtT) { | 
| +            if (loop->segment() == this && loop->fT == t && loop->fPt == pt) { | 
| +                goto foundMatch; | 
| +            } | 
| +            loop = loop->next(); | 
| +        } | 
| +        return nullptr; | 
| } while ((test = test->upCast()->next())); | 
| -    SkOpPtT* result; | 
| -    if (existing && existing->contains(opp)) { | 
| -        result = existing->ptT(); | 
| -    } else { | 
| -        result = this->addT(t, SkOpSegment::kNoAlias, allocator); | 
| +foundMatch: | 
| +    return opp && !test->contains(opp) ? nullptr : testPtT; | 
| +} | 
| + | 
| +// break the span so that the coincident part does not change the angle of the remainder | 
| +bool SkOpSegment::addExpanded(double newT, const SkOpSpanBase* test, bool* startOver) { | 
| +    if (this->contains(newT)) { | 
| +        return true; | 
| } | 
| -    SkASSERT(result); | 
| -    return result; | 
| +    SkOpPtT* newPtT = this->addT(newT, kAllowAliasMatch, startOver); | 
| +    if (!newPtT) { | 
| +        return false; | 
| +    } | 
| +    newPtT->fPt = this->ptAtT(newT); | 
| +    // const cast away to change linked list; pt/t values stays unchanged | 
| +    SkOpSpanBase* writableTest = const_cast<SkOpSpanBase*>(test); | 
| +    if (writableTest->ptT()->addOpp(newPtT)) { | 
| +        writableTest->checkForCollapsedCoincidence(); | 
| +    } | 
| +    return true; | 
| } | 
|  | 
| -SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* allocator) { | 
| +// Please keep this in sync with debugAddT() | 
| +SkOpPtT* SkOpSegment::addT(double t, AliasMatch allowAlias, bool* allocated) { | 
| debugValidate(); | 
| SkPoint pt = this->ptAtT(t); | 
| SkOpSpanBase* span = &fHead; | 
| @@ -331,9 +281,9 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca | 
| if (t == result->fT) { | 
| goto bumpSpan; | 
| } | 
| -        if (this->match(result, this, t, pt)) { | 
| +        if (this->match(result, this, t, pt, allowAlias)) { | 
| // see if any existing alias matches segment, pt, and t | 
| -           loop = result->next(); | 
| +            loop = result->next(); | 
| duplicatePt = false; | 
| while (loop != result) { | 
| bool ptMatch = loop->fPt == pt; | 
| @@ -343,12 +293,12 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca | 
| duplicatePt |= ptMatch; | 
| loop = loop->next(); | 
| } | 
| -            if (kNoAlias == allowAlias) { | 
| +            if (kNoAliasMatch == allowAlias) { | 
| bumpSpan: | 
| span->bumpSpanAdds(); | 
| return result; | 
| } | 
| -            SkOpPtT* alias = SkOpTAllocator<SkOpPtT>::Allocate(allocator); | 
| +            SkOpPtT* alias = SkOpTAllocator<SkOpPtT>::Allocate(this->globalState()->allocator()); | 
| alias->init(result->span(), t, pt, duplicatePt); | 
| result->insert(alias); | 
| result->span()->unaligned(); | 
| @@ -358,6 +308,9 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca | 
| alias->segment()->debugID(), alias->span()->debugID()); | 
| #endif | 
| span->bumpSpanAdds(); | 
| +            if (allocated) { | 
| +                *allocated = true; | 
| +            } | 
| return alias; | 
| } | 
| if (t < result->fT) { | 
| @@ -365,7 +318,7 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca | 
| if (!prev) { | 
| return nullptr; | 
| } | 
| -            SkOpSpan* span = insert(prev, allocator); | 
| +            SkOpSpan* span = insert(prev); | 
| span->init(this, prev, t, pt); | 
| this->debugValidate(); | 
| #if DEBUG_ADD_T | 
| @@ -373,6 +326,9 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca | 
| span->segment()->debugID(), span->debugID()); | 
| #endif | 
| span->bumpSpanAdds(); | 
| +            if (allocated) { | 
| +                *allocated = true; | 
| +            } | 
| return span->ptT(); | 
| } | 
| SkASSERT(span != &fTail); | 
| @@ -381,43 +337,17 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca | 
| return nullptr; | 
| } | 
|  | 
| -// choose a solitary t and pt value; remove aliases; align the opposite ends | 
| -void SkOpSegment::align() { | 
| -    debugValidate(); | 
| -    SkOpSpanBase* span = &fHead; | 
| -    if (!span->aligned()) { | 
| -        span->alignEnd(0, fPts[0]); | 
| -    } | 
| -    while ((span = span->upCast()->next())) { | 
| -        if (span == &fTail) { | 
| -            break; | 
| -        } | 
| -        span->align(); | 
| -    } | 
| -    if (!span->aligned()) { | 
| -        span->alignEnd(1, fPts[SkPathOpsVerbToPoints(fVerb)]); | 
| -    } | 
| -    if (this->collapsed()) { | 
| -        SkOpSpan* span = &fHead; | 
| -        do { | 
| -            span->setWindValue(0); | 
| -            span->setOppValue(0); | 
| -            this->markDone(span); | 
| -        } while ((span = span->next()->upCastable())); | 
| -    } | 
| -    debugValidate(); | 
| -} | 
| - | 
| -void SkOpSegment::calcAngles(SkChunkAlloc* allocator) { | 
| +void SkOpSegment::calcAngles() { | 
| bool activePrior = !fHead.isCanceled(); | 
| if (activePrior && !fHead.simple()) { | 
| -        addStartSpan(allocator); | 
| +        addStartSpan(); | 
| } | 
| SkOpSpan* prior = &fHead; | 
| SkOpSpanBase* spanBase = fHead.next(); | 
| while (spanBase != &fTail) { | 
| if (activePrior) { | 
| -            SkOpAngle* priorAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator); | 
| +            SkOpAngle* priorAngle = SkOpTAllocator<SkOpAngle>::Allocate( | 
| +                    this->globalState()->allocator()); | 
| priorAngle->set(spanBase, prior); | 
| spanBase->setFromAngle(priorAngle); | 
| } | 
| @@ -425,7 +355,8 @@ void SkOpSegment::calcAngles(SkChunkAlloc* allocator) { | 
| bool active = !span->isCanceled(); | 
| SkOpSpanBase* next = span->next(); | 
| if (active) { | 
| -            SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator); | 
| +            SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate( | 
| +                    this->globalState()->allocator()); | 
| angle->set(span, next); | 
| span->setToAngle(angle); | 
| } | 
| @@ -434,12 +365,32 @@ void SkOpSegment::calcAngles(SkChunkAlloc* allocator) { | 
| spanBase = next; | 
| } | 
| if (activePrior && !fTail.simple()) { | 
| -        addEndSpan(allocator); | 
| +        addEndSpan(); | 
| } | 
| } | 
|  | 
| +// Please keep this in sync with debugClearAll() | 
| +void SkOpSegment::clearAll() { | 
| +    SkOpSpan* span = &fHead; | 
| +    do { | 
| +        this->clearOne(span); | 
| +    } while ((span = span->next()->upCastable())); | 
| +    this->globalState()->coincidence()->release(this); | 
| +} | 
| + | 
| +// Please keep this in sync with debugClearOne() | 
| +void SkOpSegment::clearOne(SkOpSpan* span) { | 
| +    span->setWindValue(0); | 
| +    span->setOppValue(0); | 
| +    this->markDone(span); | 
| +} | 
| + | 
| +// Quads and conics collapse if the end points are the same, because | 
| +// the curve doesn't enclose an area. | 
| bool SkOpSegment::collapsed() const { | 
| -    return fVerb == SkPath::kLine_Verb && fHead.pt() == fTail.pt(); | 
| +    // FIXME: cubics can have also collapsed -- need to check if the | 
| +    // control points are on a line with the end points | 
| +    return fVerb < SkPath::kCubic_Verb && fHead.pt() == fTail.pt(); | 
| } | 
|  | 
| void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle, | 
| @@ -571,12 +522,26 @@ int SkOpSegment::computeSum(SkOpSpanBase* start, SkOpSpanBase* end, | 
| return start->starter(end)->windSum(); | 
| } | 
|  | 
| +bool SkOpSegment::contains(double newT) const { | 
| +    const SkOpSpanBase* spanBase = &fHead; | 
| +    do { | 
| +        if (spanBase->ptT()->contains(this, newT)) { | 
| +            return true; | 
| +        } | 
| +        if (spanBase == &fTail) { | 
| +            break; | 
| +        } | 
| +        spanBase = spanBase->upCast()->next(); | 
| +    } while (true); | 
| +    return false; | 
| +} | 
| + | 
| void SkOpSegment::release(const SkOpSpan* span) { | 
| if (span->done()) { | 
| --fDoneCount; | 
| } | 
| --fCount; | 
| -    SkASSERT(fCount >= fDoneCount); | 
| +    SkASSERT(this->globalState()->debugSkipAssert() || fCount >= fDoneCount); | 
| } | 
|  | 
| double SkOpSegment::distSq(double t, const SkOpAngle* oppAngle) const { | 
| @@ -601,15 +566,6 @@ double SkOpSegment::distSq(double t, const SkOpAngle* oppAngle) const { | 
| return closestDistSq; | 
| } | 
|  | 
| -void SkOpSegment::findCollapsed() { | 
| -    if (fHead.contains(&fTail)) { | 
| -        markAllDone(); | 
| -        // move start and end to the same point | 
| -        fHead.alignEnd(0, fHead.pt()); | 
| -        fTail.setAligned(); | 
| -    } | 
| -} | 
| - | 
| /* | 
| The M and S variable name parts stand for the operators. | 
| Mi stands for Minuend (see wiki subtraction, analogous to difference) | 
| @@ -887,14 +843,12 @@ SkOpSegment* SkOpSegment::findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** n | 
| } | 
|  | 
| SkOpGlobalState* SkOpSegment::globalState() const { | 
| -    return contour()->globalState(); | 
| +    return contour()->globalState(); | 
| } | 
|  | 
| void SkOpSegment::init(SkPoint pts[], SkScalar weight, SkOpContour* contour, SkPath::Verb verb) { | 
| fContour = contour; | 
| fNext = nullptr; | 
| -    fOriginal[0] = pts[0]; | 
| -    fOriginal[1] = pts[SkPathOpsVerbToPoints(verb)]; | 
| fPts = pts; | 
| fWeight = weight; | 
| fVerb = verb; | 
| @@ -1095,15 +1049,17 @@ bool SkOpSegment::markWinding(SkOpSpan* span, int winding, int oppWinding) { | 
| } | 
|  | 
| bool SkOpSegment::match(const SkOpPtT* base, const SkOpSegment* testParent, double testT, | 
| -        const SkPoint& testPt) const { | 
| -    const SkOpSegment* baseParent = base->segment(); | 
| -    if (this == baseParent && this == testParent && precisely_equal(base->fT, testT)) { | 
| -        return true; | 
| +        const SkPoint& testPt, AliasMatch aliasMatch) const { | 
| +    SkASSERT(this == base->segment()); | 
| +    if (this == testParent) { | 
| +        if (precisely_equal(base->fT, testT)) { | 
| +            return true; | 
| +        } | 
| } | 
| if (!SkDPoint::ApproximatelyEqual(testPt, base->fPt)) { | 
| return false; | 
| } | 
| -    return !this->ptsDisjoint(base->fT, base->fPt, testT, testPt); | 
| +    return this != testParent || !this->ptsDisjoint(base->fT, base->fPt, testT, testPt); | 
| } | 
|  | 
| static SkOpSegment* set_last(SkOpSpanBase** last, SkOpSpanBase* endSpan) { | 
| @@ -1178,7 +1134,8 @@ SkOpSegment* SkOpSegment::nextChase(SkOpSpanBase** startPtr, int* stepPtr, SkOpS | 
| return other; | 
| } | 
|  | 
| -static void clear_visited(SkOpSpanBase* span) { | 
| +// Please keep this in sync with DebugClearVisited() | 
| +void SkOpSegment::ClearVisited(SkOpSpanBase* span) { | 
| // reset visited flag back to false | 
| do { | 
| SkOpPtT* ptT = span->ptT(), * stopPtT = ptT; | 
| @@ -1189,20 +1146,23 @@ static void clear_visited(SkOpSpanBase* span) { | 
| } while (!span->final() && (span = span->upCast()->next())); | 
| } | 
|  | 
| +// Please keep this in sync with debugMissingCoincidence() | 
| // look for pairs of undetected coincident curves | 
| // assumes that segments going in have visited flag clear | 
| -// curve/curve intersection should now do a pretty good job of finding coincident runs so | 
| -// this may be only be necessary for line/curve pairs -- so skip unless this is a line and the | 
| -// the opp is not a line | 
| -bool SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) { | 
| -    if (this->verb() != SkPath::kLine_Verb) { | 
| -        return false; | 
| -    } | 
| +// Even though pairs of curves correct detect coincident runs, a run may be missed | 
| +// if the coincidence is a product of multiple intersections. For instance, given | 
| +// curves A, B, and C: | 
| +// A-B intersect at a point 1; A-C and B-C intersect at point 2, so near | 
| +// the end of C that the intersection is replaced with the end of C. | 
| +// Even though A-B correctly do not detect an intersection at point 2, | 
| +// the resulting run from point 1 to point 2 is coincident on A and B. | 
| +bool SkOpSegment::missingCoincidence() { | 
| if (this->done()) { | 
| return false; | 
| } | 
| SkOpSpan* prior = nullptr; | 
| SkOpSpanBase* spanBase = &fHead; | 
| +    bool result = false; | 
| do { | 
| SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT; | 
| SkASSERT(ptT->span() == spanBase); | 
| @@ -1211,9 +1171,6 @@ bool SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc | 
| continue; | 
| } | 
| SkOpSegment* opp = ptT->span()->segment(); | 
| -//            if (opp->verb() == SkPath::kLine_Verb) { | 
| -//                continue; | 
| -//            } | 
| if (opp->done()) { | 
| continue; | 
| } | 
| @@ -1224,18 +1181,18 @@ bool SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc | 
| if (spanBase == &fHead) { | 
| continue; | 
| } | 
| +            if (ptT->segment() == this) { | 
| +                continue; | 
| +            } | 
| SkOpSpan* span = spanBase->upCastable(); | 
| // FIXME?: this assumes that if the opposite segment is coincident then no more | 
| // coincidence needs to be detected. This may not be true. | 
| -            if (span && span->containsCoincidence(opp)) { | 
| -                continue; | 
| -            } | 
| -            if (spanBase->segment() == opp) { | 
| +            if (span && span->containsCoincidence(opp)) { | 
| continue; | 
| } | 
| if (spanBase->containsCoinEnd(opp)) { | 
| continue; | 
| -            } | 
| +            } | 
| SkOpPtT* priorPtT = nullptr, * priorStopPtT; | 
| // find prior span containing opp segment | 
| SkOpSegment* priorOpp = nullptr; | 
| @@ -1268,28 +1225,28 @@ bool SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc | 
| SkTSwap(priorPtT, ptT); | 
| SkTSwap(oppStart, oppEnd); | 
| } | 
| -            bool flipped = oppStart->fT > oppEnd->fT; | 
| -            bool coincident = false; | 
| -            if (coincidences->contains(priorPtT, ptT, oppStart, oppEnd, flipped)) { | 
| +            SkOpCoincidence* coincidences = this->globalState()->coincidence(); | 
| +            SkOpPtT* rootPriorPtT = priorPtT->span()->ptT(); | 
| +            SkOpPtT* rootPtT = ptT->span()->ptT(); | 
| +            SkOpPtT* rootOppStart = oppStart->span()->ptT(); | 
| +            SkOpPtT* rootOppEnd = oppEnd->span()->ptT(); | 
| +            if (coincidences->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) { | 
| goto swapBack; | 
| } | 
| -            if (opp->verb() == SkPath::kLine_Verb) { | 
| -                coincident = (SkDPoint::ApproximatelyEqual(priorPtT->fPt, oppStart->fPt) || | 
| -                        SkDPoint::ApproximatelyEqual(priorPtT->fPt, oppEnd->fPt)) && | 
| -                        (SkDPoint::ApproximatelyEqual(ptT->fPt, oppStart->fPt) || | 
| -                        SkDPoint::ApproximatelyEqual(ptT->fPt, oppEnd->fPt)); | 
| -            } | 
| -            if (!coincident) { | 
| -                coincident = testForCoincidence(priorPtT, ptT, prior, spanBase, opp, 5000); | 
| -            } | 
| -            if (coincident) { | 
| +            if (this->testForCoincidence(rootPriorPtT, rootPtT, prior, spanBase, opp)) { | 
| // mark coincidence | 
| -                if (!coincidences->extend(priorPtT, ptT, oppStart, oppEnd) | 
| -                        && !coincidences->extend(oppStart, oppEnd, priorPtT, ptT)) { | 
| -                    coincidences->add(priorPtT, ptT, oppStart, oppEnd, allocator); | 
| +#if DEBUG_COINCIDENCE_VERBOSE | 
| +                SkDebugf("%s coinSpan=%d endSpan=%d oppSpan=%d oppEndSpan=%d\n", __FUNCTION__, | 
| +                        rootPriorPtT->debugID(), rootPtT->debugID(), rootOppStart->debugID(), | 
| +                        rootOppEnd->debugID()); | 
| +#endif | 
| +                if (!coincidences->extend(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) { | 
| +                    coincidences->add(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd); | 
| } | 
| -                clear_visited(&fHead); | 
| -                return true; | 
| +#if DEBUG_COINCIDENCE | 
| +                SkASSERT(coincidences->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)); | 
| +#endif | 
| +                result = true; | 
| } | 
| swapBack: | 
| if (swapped) { | 
| @@ -1297,19 +1254,18 @@ bool SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc | 
| } | 
| } | 
| } while ((spanBase = spanBase->final() ? nullptr : spanBase->upCast()->next())); | 
| -    clear_visited(&fHead); | 
| -    return false; | 
| +    ClearVisited(&fHead); | 
| +    return result; | 
| } | 
|  | 
| +// please keep this in sync with debugMoveMultiples() | 
| // if a span has more than one intersection, merge the other segments' span as needed | 
| bool SkOpSegment::moveMultiples() { | 
| debugValidate(); | 
| SkOpSpanBase* test = &fHead; | 
| do { | 
| int addCount = test->spanAddsCount(); | 
| -        if (addCount < 1) { | 
| -            return false; | 
| -        } | 
| +        FAIL_IF(addCount < 1); | 
| if (addCount == 1) { | 
| continue; | 
| } | 
| @@ -1392,66 +1348,126 @@ bool SkOpSegment::moveMultiples() { | 
| oppSegment->debugValidate(); | 
| goto checkNextSpan; | 
| } | 
| -        tryNextSpan: | 
| +        tryNextSpan: | 
| ; | 
| } while (oppTest != oppLast && (oppTest = oppTest->upCast()->next())); | 
| } while ((testPtT = testPtT->next()) != startPtT); | 
| -checkNextSpan: | 
| +checkNextSpan: | 
| ; | 
| } while ((test = test->final() ? nullptr : test->upCast()->next())); | 
| debugValidate(); | 
| return true; | 
| } | 
|  | 
| +// adjacent spans may have points close by | 
| +bool SkOpSegment::spansNearby(const SkOpSpanBase* refSpan, const SkOpSpanBase* checkSpan) const { | 
| +    const SkOpPtT* refHead = refSpan->ptT(); | 
| +    const SkOpPtT* checkHead = checkSpan->ptT(); | 
| +// if the first pt pair from adjacent spans are far apart, assume that all are far enough apart | 
| +    if (!SkDPoint::RoughlyEqual(refHead->fPt, checkHead->fPt)) { | 
| +#if DEBUG_COINCIDENCE | 
| +        // verify that no combination of points are close | 
| +        const SkOpPtT* dBugRef = refHead; | 
| +        do { | 
| +            const SkOpPtT* dBugCheck = checkHead; | 
| +            do { | 
| +                SkASSERT(!SkDPoint::ApproximatelyEqual(dBugRef->fPt, dBugCheck->fPt)); | 
| +                dBugCheck = dBugCheck->next(); | 
| +            } while (dBugCheck != checkHead); | 
| +            dBugRef = dBugRef->next(); | 
| +        } while (dBugRef != refHead); | 
| +#endif | 
| +        return false; | 
| +    } | 
| +    // check only unique points | 
| +    SkScalar distSqBest = SK_ScalarMax; | 
| +    const SkOpPtT* refBest = nullptr; | 
| +    const SkOpPtT* checkBest = nullptr; | 
| +    const SkOpPtT* ref = refHead; | 
| +    do { | 
| +        if (ref->deleted()) { | 
| +            continue; | 
| +        } | 
| +        while (ref->ptAlreadySeen(refHead)) { | 
| +            ref = ref->next(); | 
| +            if (ref == refHead) { | 
| +                goto doneCheckingDistance; | 
| +            } | 
| +        } | 
| +        const SkOpPtT* check = checkHead; | 
| +        const SkOpSegment* refSeg = ref->segment(); | 
| +        do { | 
| +            if (check->deleted()) { | 
| +                continue; | 
| +            } | 
| +            while (check->ptAlreadySeen(checkHead)) { | 
| +                check = check->next(); | 
| +                if (check == checkHead) { | 
| +                    goto nextRef; | 
| +                } | 
| +            } | 
| +            SkScalar distSq = ref->fPt.distanceToSqd(check->fPt); | 
| +            if (distSqBest > distSq && (refSeg != check->segment() | 
| +                    || !refSeg->ptsDisjoint(*ref, *check))) { | 
| +                distSqBest = distSq; | 
| +                refBest = ref; | 
| +                checkBest = check; | 
| +            } | 
| +        } while ((check = check->next()) != checkHead); | 
| +nextRef: | 
| +        ; | 
| +   } while ((ref = ref->next()) != refHead); | 
| +doneCheckingDistance: | 
| +    return checkBest && refBest->segment()->match(refBest, checkBest->segment(), checkBest->fT, | 
| +            checkBest->fPt, kAllowAliasMatch); | 
| +} | 
| + | 
| +// Please keep this function in sync with debugMoveNearby() | 
| // Move nearby t values and pts so they all hang off the same span. Alignment happens later. | 
| void SkOpSegment::moveNearby() { | 
| debugValidate(); | 
| -    SkOpSpanBase* spanS = &fHead; | 
| +    // release undeleted spans pointing to this seg that are linked to the primary span | 
| +    SkOpSpanBase* spanBase = &fHead; | 
| do { | 
| -        SkOpSpanBase* test = spanS->upCast()->next(); | 
| -        SkOpSpanBase* next; | 
| -        if (spanS->contains(test)) { | 
| -            if (!test->final()) { | 
| -                test->upCast()->release(spanS->ptT()); | 
| -                continue; | 
| -            } else if (spanS != &fHead) { | 
| -                spanS->upCast()->release(test->ptT()); | 
| -                spanS = test; | 
| -                continue; | 
| +        SkOpPtT* ptT = spanBase->ptT(); | 
| +        const SkOpPtT* headPtT = ptT; | 
| +        while ((ptT = ptT->next()) != headPtT) { | 
| +            SkOpSpanBase* test = ptT->span(); | 
| +            if (ptT->segment() == this && !ptT->deleted() && test != spanBase | 
| +                    && test->ptT() == ptT) { | 
| +                if (test->final()) { | 
| +                    if (spanBase == &fHead) { | 
| +                        this->clearAll(); | 
| +                        return; | 
| +                    } | 
| +                    spanBase->upCast()->release(ptT); | 
| +                } else if (test->prev()) { | 
| +                    test->upCast()->release(headPtT); | 
| +                } | 
| +                break; | 
| } | 
| } | 
| -        do {  // iterate through all spans associated with start | 
| -            SkOpPtT* startBase = spanS->ptT(); | 
| -            next = test->final() ? nullptr : test->upCast()->next(); | 
| -            do { | 
| -                SkOpPtT* testBase = test->ptT(); | 
| -                do { | 
| -                    if (startBase == testBase) { | 
| -                        goto checkNextSpan; | 
| -                    } | 
| -                    if (testBase->duplicate()) { | 
| -                        continue; | 
| -                    } | 
| -                    if (this->match(startBase, testBase->segment(), testBase->fT, testBase->fPt)) { | 
| -                        if (test == &this->fTail) { | 
| -                            if (spanS == &fHead) { | 
| -                                debugValidate(); | 
| -                                return;  // if this span has collapsed, remove it from parent | 
| -                            } | 
| -                            this->fTail.merge(spanS->upCast()); | 
| -                            debugValidate(); | 
| -                            return; | 
| -                        } | 
| -                        spanS->merge(test->upCast()); | 
| -                        goto checkNextSpan; | 
| -                    } | 
| -                } while ((testBase = testBase->next()) != test->ptT()); | 
| -            } while ((startBase = startBase->next()) != spanS->ptT()); | 
| -    checkNextSpan: | 
| -            ; | 
| -        } while ((test = next)); | 
| -        spanS = spanS->upCast()->next(); | 
| -    } while (!spanS->final()); | 
| +        spanBase = spanBase->upCast()->next(); | 
| +    } while (!spanBase->final()); | 
| + | 
| +    // This loop looks for adjacent spans which are near by | 
| +    spanBase = &fHead; | 
| +    do {  // iterate through all spans associated with start | 
| +        SkOpSpanBase* test = spanBase->upCast()->next(); | 
| +        if (this->spansNearby(spanBase, test)) { | 
| +            if (test->final()) { | 
| +                if (spanBase->prev()) { | 
| +                    test->merge(spanBase->upCast()); | 
| +                } else { | 
| +                    this->clearAll(); | 
| +                    return; | 
| +                } | 
| +            } else { | 
| +                spanBase->merge(test->upCast()); | 
| +            } | 
| +        } | 
| +        spanBase = test; | 
| +    } while (!spanBase->final()); | 
| debugValidate(); | 
| } | 
|  | 
| @@ -1679,8 +1695,7 @@ bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end, | 
| } | 
|  | 
| bool SkOpSegment::testForCoincidence(const SkOpPtT* priorPtT, const SkOpPtT* ptT, | 
| -        const SkOpSpanBase* prior, const SkOpSpanBase* spanBase, const SkOpSegment* opp, | 
| -        SkScalar flatnessLimit) const { | 
| +        const SkOpSpanBase* prior, const SkOpSpanBase* spanBase, const SkOpSegment* opp) const { | 
| // average t, find mid pt | 
| double midT = (prior->t() + spanBase->t()) / 2; | 
| SkPoint midPt = this->ptAtT(midT); | 
| @@ -1688,22 +1703,28 @@ bool SkOpSegment::testForCoincidence(const SkOpPtT* priorPtT, const SkOpPtT* ptT | 
| // if the mid pt is not near either end pt, project perpendicular through opp seg | 
| if (!SkDPoint::ApproximatelyEqual(priorPtT->fPt, midPt) | 
| && !SkDPoint::ApproximatelyEqual(ptT->fPt, midPt)) { | 
| +        if (priorPtT->span() == ptT->span()) { | 
| +          return false; | 
| +        } | 
| coincident = false; | 
| SkIntersections i; | 
| -        SkVector dxdy = (*CurveSlopeAtT[fVerb])(this->pts(), this->weight(), midT); | 
| -        SkDLine ray = {{{midPt.fX, midPt.fY}, | 
| -                {(double) midPt.fX + dxdy.fY, (double) midPt.fY - dxdy.fX}}}; | 
| -        (*CurveIntersectRay[opp->verb()])(opp->pts(), opp->weight(), ray, &i); | 
| +        SkDCurve curvePart; | 
| +        this->subDivide(prior, spanBase, &curvePart); | 
| +        SkDVector dxdy = (*CurveDDSlopeAtT[fVerb])(curvePart, 0.5f); | 
| +        SkDPoint partMidPt = (*CurveDDPointAtT[fVerb])(curvePart, 0.5f); | 
| +        SkDLine ray = {{{midPt.fX, midPt.fY}, {partMidPt.fX + dxdy.fY, partMidPt.fY - dxdy.fX}}}; | 
| +        SkDCurve oppPart; | 
| +        opp->subDivide(priorPtT->span(), ptT->span(), &oppPart); | 
| +        (*CurveDIntersectRay[opp->verb()])(oppPart, ray, &i); | 
| // measure distance and see if it's small enough to denote coincidence | 
| for (int index = 0; index < i.used(); ++index) { | 
| +            if (!between(0, i[0][index], 1)) { | 
| +                continue; | 
| +            } | 
| SkDPoint oppPt = i.pt(index); | 
| if (oppPt.approximatelyEqual(midPt)) { | 
| -                SkVector oppDxdy = (*CurveSlopeAtT[opp->verb()])(opp->pts(), | 
| -                        opp->weight(), i[index][0]); | 
| -                oppDxdy.normalize(); | 
| -                dxdy.normalize(); | 
| -                SkScalar flatness = SkScalarAbs(dxdy.cross(oppDxdy) / FLT_EPSILON); | 
| -                coincident |= flatness < flatnessLimit; | 
| +                // the coincidence can occur at almost any angle | 
| +                coincident = true; | 
| } | 
| } | 
| } | 
|  |