| Index: src/pathops/SkOpSegment.cpp | 
| diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp | 
| index 4d11eb39e8259b73c79e313c109e1aa314f94402..6fe1fbb49d46d893c08da9fa9cfac2cf35c30425 100644 | 
| --- a/src/pathops/SkOpSegment.cpp | 
| +++ b/src/pathops/SkOpSegment.cpp | 
| @@ -1298,6 +1298,7 @@ int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hi | 
| SkIntersections intersections; | 
| // OPTIMIZE: use specialty function that intersects ray with curve, | 
| // returning t values only for curve (we don't care about t on ray) | 
| +    intersections.allowNear(false); | 
| int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)]) | 
| (fPts, top, bottom, basePt.fX, false); | 
| if (pts == 0 || (current && pts == 1)) { | 
| @@ -1420,15 +1421,29 @@ void SkOpSegment::checkEnds() { | 
| } | 
| // t start/last describe the range of spans that match the t of this span | 
| double t = span.fT; | 
| -        int tStart = index; | 
| -        while (--tStart >= 0 && (t == fTs[tStart].fT || fTs[tStart].fTiny)) | 
| -            ; | 
| -        int tLast = index; | 
| -        while (fTs[tLast].fTiny) { | 
| -            ++tLast; | 
| +        double tBottom = -1; | 
| +        int tStart = -1; | 
| +        int tLast = count; | 
| +        bool lastSmall = false; | 
| +        double afterT = t; | 
| +        for (int inner = 0; inner < count; ++inner) { | 
| +            double innerT = fTs[inner].fT; | 
| +            if (innerT <= t && innerT > tBottom) { | 
| +                if (innerT < t || !lastSmall) { | 
| +                    tStart = inner - 1; | 
| +                } | 
| +                tBottom = innerT; | 
| +            } | 
| +            if (innerT > afterT) { | 
| +                if (t == afterT && lastSmall) { | 
| +                    afterT = innerT; | 
| +                } else { | 
| +                    tLast = inner; | 
| +                    break; | 
| +                } | 
| +            } | 
| +            lastSmall = innerT <= t ? fTs[inner].fSmall : false; | 
| } | 
| -        while (++tLast < count && t == fTs[tLast].fT) | 
| -            ; | 
| for (int peekIndex = peekStart; peekIndex <= peekLast; ++peekIndex) { | 
| if (peekIndex == span.fOtherIndex) {  // skip the other span pointed to by this span | 
| continue; | 
| @@ -1696,6 +1711,70 @@ void SkOpSegment::checkTiny() { | 
| } | 
| } | 
|  | 
| +bool SkOpSegment::findCoincidentMatch(const SkOpSpan* span, const SkOpSegment* other, int oStart, | 
| +        int oEnd, int step, SkPoint* startPt, SkPoint* endPt, double* endT) const { | 
| +    SkASSERT(span->fT == 0 || span->fT == 1); | 
| +    SkASSERT(span->fOtherT == 0 || span->fOtherT == 1); | 
| +    const SkOpSpan* otherSpan = &other->span(oEnd); | 
| +    double refT = otherSpan->fT; | 
| +    const SkPoint& refPt = otherSpan->fPt; | 
| +    const SkOpSpan* lastSpan = &other->span(step > 0 ? other->count() - 1 : 0); | 
| +    do { | 
| +        const SkOpSegment* match = span->fOther; | 
| +        if (match == otherSpan->fOther) { | 
| +            // find start of respective spans and see if both have winding | 
| +            int startIndex, endIndex; | 
| +            if (span->fOtherT == 1) { | 
| +                endIndex = span->fOtherIndex; | 
| +                startIndex = match->nextExactSpan(endIndex, -1); | 
| +            } else { | 
| +                startIndex = span->fOtherIndex; | 
| +                endIndex = match->nextExactSpan(startIndex, 1); | 
| +            } | 
| +            const SkOpSpan& startSpan = match->span(startIndex); | 
| +            if (startSpan.fWindValue != 0) { | 
| +                // draw ray from endSpan.fPt perpendicular to end tangent and measure distance | 
| +                // to other segment. | 
| +                const SkOpSpan& endSpan = match->span(endIndex); | 
| +                SkDLine ray; | 
| +                SkVector dxdy; | 
| +                if (span->fOtherT == 1) { | 
| +                    ray.fPts[0].set(startSpan.fPt); | 
| +                    dxdy = match->dxdy(startIndex); | 
| +                } else { | 
| +                    ray.fPts[0].set(endSpan.fPt); | 
| +                    dxdy = match->dxdy(endIndex); | 
| +                } | 
| +                ray.fPts[1].fX = ray.fPts[0].fX + dxdy.fY; | 
| +                ray.fPts[1].fY = ray.fPts[0].fY - dxdy.fX; | 
| +                SkIntersections i; | 
| +                int roots = (i.*CurveRay[SkPathOpsVerbToPoints(other->verb())])(other->pts(), ray); | 
| +                for (int index = 0; index < roots; ++index) { | 
| +                    if (ray.fPts[0].approximatelyEqual(i.pt(index))) { | 
| +                        double matchMidT = (match->span(startIndex).fT | 
| +                                + match->span(endIndex).fT) / 2; | 
| +                        SkPoint matchMidPt = match->ptAtT(matchMidT); | 
| +                        double otherMidT = (i[0][index] + other->span(oStart).fT) / 2; | 
| +                        SkPoint otherMidPt = other->ptAtT(otherMidT); | 
| +                        if (SkDPoint::ApproximatelyEqual(matchMidPt, otherMidPt)) { | 
| +                            *startPt = startSpan.fPt; | 
| +                            *endPt = endSpan.fPt; | 
| +                            *endT = endSpan.fT; | 
| +                            return true; | 
| +                        } | 
| +                    } | 
| +                } | 
| +            } | 
| +            return false; | 
| +        } | 
| +        if (otherSpan == lastSpan) { | 
| +            break; | 
| +        } | 
| +        otherSpan += step; | 
| +    } while (otherSpan->fT == refT || otherSpan->fPt == refPt); | 
| +    return false; | 
| +} | 
| + | 
| /* | 
| The M and S variable name parts stand for the operators. | 
| Mi stands for Minuend (see wiki subtraction, analogous to difference) | 
| @@ -2076,6 +2155,18 @@ int SkOpSegment::findStartingEdge(const SkTArray<SkOpAngle*, true>& sorted, int | 
| return firstIndex; | 
| } | 
|  | 
| +int SkOpSegment::findT(double t, const SkOpSegment* match) const { | 
| +    int count = this->count(); | 
| +    for (int index = 0; index < count; ++index) { | 
| +        const SkOpSpan& span = fTs[index]; | 
| +        if (span.fT == t && span.fOther == match) { | 
| +            return index; | 
| +        } | 
| +    } | 
| +    SkASSERT(0); | 
| +    return -1; | 
| +} | 
| + | 
| // FIXME: either: | 
| // a) mark spans with either end unsortable as done, or | 
| // b) rewrite findTop / findTopSegment / findTopContour to iterate further | 
| @@ -2299,6 +2390,76 @@ bool SkOpSegment::isSimple(int end) const { | 
| return false; | 
| } | 
|  | 
| +bool SkOpSegment::isTiny(const SkOpAngle* angle) const { | 
| +    int start = angle->start(); | 
| +    int end = angle->end(); | 
| +    const SkOpSpan& mSpan = fTs[SkMin32(start, end)]; | 
| +    return mSpan.fTiny; | 
| +} | 
| + | 
| +bool SkOpSegment::isTiny(int index) const { | 
| +    return fTs[index].fTiny; | 
| +} | 
| + | 
| +// look pair of active edges going away from coincident edge | 
| +// one of them should be the continuation of other | 
| +// if both are active, look to see if they both the connect to another coincident pair | 
| +// if one at least one is a line, then make the pair coincident | 
| +// if neither is a line, test for coincidence | 
| +bool SkOpSegment::joinCoincidence(bool end, SkOpSegment* other, double otherT, int step, | 
| +        bool cancel) { | 
| +    int otherTIndex = other->findT(otherT, this); | 
| +    int next = other->nextExactSpan(otherTIndex, step); | 
| +    int otherMin = SkTMin(otherTIndex, next); | 
| +    int otherWind = other->span(otherMin).fWindValue; | 
| +    if (otherWind == 0) { | 
| +        return false; | 
| +    } | 
| +    SkASSERT(next >= 0); | 
| +    if (end) { | 
| +        int tIndex = count() - 1; | 
| +        do { | 
| +            SkOpSpan* test = &fTs[tIndex]; | 
| +            SkASSERT(test->fT == 1); | 
| +            if (test->fOther == other || test->fOtherT != 0) { | 
| +                continue; | 
| +            } | 
| +            SkPoint startPt, endPt; | 
| +            double endT; | 
| +            if (findCoincidentMatch(test, other, otherTIndex, next, step, &startPt, &endPt, &endT)) { | 
| +                SkOpSegment* match = test->fOther; | 
| +                if (cancel) { | 
| +                    match->addTCancel(startPt, endPt, other); | 
| +                } else { | 
| +                    match->addTCoincident(startPt, endPt, endT, other); | 
| +                } | 
| +                return true; | 
| +            } | 
| +        } while (fTs[--tIndex].fT == 1); | 
| +    } else { | 
| +        int tIndex = 0; | 
| +        do { | 
| +            SkOpSpan* test = &fTs[tIndex]; | 
| +            SkASSERT(test->fT == 0); | 
| +            if (test->fOther == other || test->fOtherT != 1) { | 
| +                continue; | 
| +            } | 
| +            SkPoint startPt, endPt; | 
| +            double endT; | 
| +            if (findCoincidentMatch(test, other, otherTIndex, next, step, &startPt, &endPt, &endT)) { | 
| +                SkOpSegment* match = test->fOther; | 
| +                if (cancel) { | 
| +                    match->addTCancel(startPt, endPt, other); | 
| +                } else { | 
| +                    match->addTCoincident(startPt, endPt, endT, other); | 
| +                } | 
| +                return true; | 
| +            } | 
| +        } while (fTs[++tIndex].fT == 0); | 
| +    } | 
| +    return false; | 
| +} | 
| + | 
| // this span is excluded by the winding rule -- chase the ends | 
| // as long as they are unambiguous to mark connections as done | 
| // and give them the same winding value | 
| @@ -3018,17 +3179,6 @@ void SkOpSegment::subDivideBounds(int start, int end, SkPathOpsBounds* bounds) c | 
| (bounds->*SetCurveBounds[SkPathOpsVerbToPoints(fVerb)])(edge); | 
| } | 
|  | 
| -bool SkOpSegment::isTiny(const SkOpAngle* angle) const { | 
| -    int start = angle->start(); | 
| -    int end = angle->end(); | 
| -    const SkOpSpan& mSpan = fTs[SkMin32(start, end)]; | 
| -    return mSpan.fTiny; | 
| -} | 
| - | 
| -bool SkOpSegment::isTiny(int index) const { | 
| -    return fTs[index].fTiny; | 
| -} | 
| - | 
| void SkOpSegment::TrackOutsidePair(SkTArray<SkPoint, true>* outsidePts, const SkPoint& endPt, | 
| const SkPoint& startPt) { | 
| int outCount = outsidePts->count(); | 
| @@ -3558,10 +3708,10 @@ void SkOpSegment::dumpPts() const { | 
| SkDebugf("{{"); | 
| int index = 0; | 
| do { | 
| -        SkDPoint::DumpSkPoint(fPts[index]); | 
| +        SkDPoint::dump(fPts[index]); | 
| SkDebugf(", "); | 
| } while (++index < last); | 
| -    SkDPoint::DumpSkPoint(fPts[index]); | 
| +    SkDPoint::dump(fPts[index]); | 
| SkDebugf("}}\n"); | 
| } | 
|  | 
|  |