| Index: src/pathops/SkOpSegment.cpp | 
| =================================================================== | 
| --- src/pathops/SkOpSegment.cpp	(revision 9425) | 
| +++ src/pathops/SkOpSegment.cpp	(working copy) | 
| @@ -209,19 +209,22 @@ | 
| SkOpAngle* angle = anglesPtr->append(); | 
| #if DEBUG_ANGLE | 
| SkTDArray<SkOpAngle>& angles = *anglesPtr; | 
| -    if (angles.count() > 1 && !fTs[start].fTiny) { | 
| -        SkPoint angle0Pt = (*CurvePointAtT[SkPathOpsVerbToPoints(angles[0].verb())])(angles[0].pts(), | 
| -                (*angles[0].spans())[angles[0].start()].fT); | 
| -        SkPoint newPt = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, fTs[start].fT); | 
| -        bool match = AlmostEqualUlps(angle0Pt.fX, newPt.fX); | 
| -        match &= AlmostEqualUlps(angle0Pt.fY, newPt.fY); | 
| -        if (!match) { | 
| -            SkDebugf("%s no match\n", __FUNCTION__); | 
| -            SkASSERT(0); | 
| +    if (angles.count() > 1) { | 
| +        const SkOpSegment* aSeg = angles[0].segment(); | 
| +        int aStart = angles[0].start(); | 
| +        if (!aSeg->fTs[aStart].fTiny) { | 
| +            const SkPoint& angle0Pt = aSeg->xyAtT(aStart); | 
| +            SkDPoint newPt = (*CurveDPointAtT[SkPathOpsVerbToPoints(aSeg->fVerb)])(aSeg->fPts, | 
| +                    aSeg->fTs[aStart].fT); | 
| +#if ONE_OFF_DEBUG | 
| +            SkDebugf("%s t1=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g)\n", __FUNCTION__, | 
| +                    aSeg->fTs[aStart].fT, newPt.fX, newPt.fY, angle0Pt.fX, angle0Pt.fY); | 
| +#endif | 
| +            SkASSERT(newPt.approximatelyEqual(angle0Pt)); | 
| } | 
| } | 
| #endif | 
| -    angle->set(fPts, fVerb, this, start, end, fTs); | 
| +    angle->set(this, start, end); | 
| } | 
|  | 
| void SkOpSegment::addCancelOutsides(double tStart, double oStart, SkOpSegment* other, double oEnd) { | 
| @@ -853,7 +856,8 @@ | 
| // OPTIMIZATION: check all angles to see if any have computed wind sum | 
| // before sorting (early exit if none) | 
| SkTDArray<SkOpAngle*> sorted; | 
| -    bool sortable = SortAngles(angles, &sorted); | 
| +    // FIXME?: Not sure if this sort must be ordered or if the relaxed ordering is OK ... | 
| +    bool sortable = SortAngles(angles, &sorted, SkOpSegment::kMustBeOrdered_SortAngleKind); | 
| #if DEBUG_SORT | 
| sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0); | 
| #endif | 
| @@ -979,7 +983,8 @@ | 
| 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) | 
| -    int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)])(fPts, top, bottom, basePt.fX, false); | 
| +    int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)]) | 
| +            (fPts, top, bottom, basePt.fX, false); | 
| if (pts == 0 || (current && pts == 1)) { | 
| return bestTIndex; | 
| } | 
| @@ -1126,6 +1131,9 @@ | 
| } | 
| while (precisely_zero(startT - other->fTs[*nextEnd].fT)); | 
| SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count()); | 
| +        if (other->isTiny(SkMin32(*nextStart, *nextEnd))) { | 
| +            return NULL; | 
| +        } | 
| return other; | 
| } | 
| // more than one viable candidate -- measure angles to find best | 
| @@ -1135,7 +1143,7 @@ | 
| addTwoAngles(startIndex, end, &angles); | 
| buildAngles(end, &angles, true); | 
| SkTDArray<SkOpAngle*> sorted; | 
| -    bool sortable = SortAngles(angles, &sorted); | 
| +    bool sortable = SortAngles(angles, &sorted, SkOpSegment::kMustBeOrdered_SortAngleKind); | 
| int angleCount = angles.count(); | 
| int firstIndex = findStartingEdge(sorted, startIndex, end); | 
| SkASSERT(firstIndex >= 0); | 
| @@ -1177,12 +1185,12 @@ | 
| if (activeAngle) { | 
| ++activeCount; | 
| if (!foundAngle || (foundDone && activeCount & 1)) { | 
| -                if (nextSegment->tiny(nextAngle)) { | 
| +                if (nextSegment->isTiny(nextAngle)) { | 
| *unsortable = true; | 
| return NULL; | 
| } | 
| foundAngle = nextAngle; | 
| -                foundDone = nextSegment->done(nextAngle) && !nextSegment->tiny(nextAngle); | 
| +                foundDone = nextSegment->done(nextAngle) && !nextSegment->isTiny(nextAngle); | 
| } | 
| } | 
| if (nextSegment->done()) { | 
| @@ -1257,7 +1265,7 @@ | 
| addTwoAngles(startIndex, end, &angles); | 
| buildAngles(end, &angles, true); | 
| SkTDArray<SkOpAngle*> sorted; | 
| -    bool sortable = SortAngles(angles, &sorted); | 
| +    bool sortable = SortAngles(angles, &sorted, SkOpSegment::kMustBeOrdered_SortAngleKind); | 
| int angleCount = angles.count(); | 
| int firstIndex = findStartingEdge(sorted, startIndex, end); | 
| SkASSERT(firstIndex >= 0); | 
| @@ -1294,7 +1302,7 @@ | 
| if (activeAngle) { | 
| ++activeCount; | 
| if (!foundAngle || (foundDone && activeCount & 1)) { | 
| -                if (nextSegment->tiny(nextAngle)) { | 
| +                if (nextSegment->isTiny(nextAngle)) { | 
| *unsortable = true; | 
| return NULL; | 
| } | 
| @@ -1386,7 +1394,7 @@ | 
| addTwoAngles(startIndex, end, &angles); | 
| buildAngles(end, &angles, false); | 
| SkTDArray<SkOpAngle*> sorted; | 
| -    bool sortable = SortAngles(angles, &sorted); | 
| +    bool sortable = SortAngles(angles, &sorted, SkOpSegment::kMustBeOrdered_SortAngleKind); | 
| if (!sortable) { | 
| *unsortable = true; | 
| #if DEBUG_SORT | 
| @@ -1416,7 +1424,7 @@ | 
| nextSegment = nextAngle->segment(); | 
| ++activeCount; | 
| if (!foundAngle || (foundDone && activeCount & 1)) { | 
| -            if (nextSegment->tiny(nextAngle)) { | 
| +            if (nextSegment->isTiny(nextAngle)) { | 
| *unsortable = true; | 
| return NULL; | 
| } | 
| @@ -1628,7 +1636,7 @@ | 
| addTwoAngles(end, firstT, &angles); | 
| buildAngles(firstT, &angles, true); | 
| SkTDArray<SkOpAngle*> sorted; | 
| -    bool sortable = SortAngles(angles, &sorted); | 
| +    bool sortable = SortAngles(angles, &sorted, SkOpSegment::kMayBeUnordered_SortAngleKind); | 
| int first = SK_MaxS32; | 
| SkScalar top = SK_ScalarMax; | 
| int count = sorted.count(); | 
| @@ -2081,7 +2089,8 @@ | 
| SkASSERT(fVerb != SkPath::kLine_Verb); | 
| SkPoint edge[4]; | 
| subDivide(tStart, tEnd, edge); | 
| -    double sum = (edge[0].fX - edge[SkPathOpsVerbToPoints(fVerb)].fX) * (edge[0].fY + edge[SkPathOpsVerbToPoints(fVerb)].fY); | 
| +    int points = SkPathOpsVerbToPoints(fVerb); | 
| +    double sum = (edge[0].fX - edge[points].fX) * (edge[0].fY + edge[points].fY); | 
| if (fVerb == SkPath::kCubic_Verb) { | 
| SkScalar lesser = SkTMin<SkScalar>(edge[0].fY, edge[3].fY); | 
| if (edge[1].fY < lesser && edge[2].fY < lesser) { | 
| @@ -2095,7 +2104,7 @@ | 
| } | 
| } | 
| } | 
| -    for (int idx = 0; idx < SkPathOpsVerbToPoints(fVerb); ++idx){ | 
| +    for (int idx = 0; idx < points; ++idx){ | 
| sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY); | 
| } | 
| return sum <= 0; | 
| @@ -2334,8 +2343,8 @@ | 
| // exclusion in find top and others. This could be optimized to only mark | 
| // adjacent spans that unsortable. However, this makes it difficult to later | 
| // determine starting points for edge detection in find top and the like. | 
| -bool SkOpSegment::SortAngles(const SkTDArray<SkOpAngle>& angles, | 
| -                             SkTDArray<SkOpAngle*>* angleList) { | 
| +bool SkOpSegment::SortAngles(const SkTDArray<SkOpAngle>& angles, SkTDArray<SkOpAngle*>* angleList, | 
| +                             SortAngleKind orderKind) { | 
| bool sortable = true; | 
| int angleCount = angles.count(); | 
| int angleIndex; | 
| @@ -2343,12 +2352,17 @@ | 
| for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) { | 
| const SkOpAngle& angle = angles[angleIndex]; | 
| *angleList->append() = const_cast<SkOpAngle*>(&angle); | 
| -        sortable &= !angle.unsortable(); | 
| +#if DEBUG_ANGLE | 
| +        (*(angleList->end() - 1))->setID(angleIndex); | 
| +#endif | 
| +        sortable &= !(angle.unsortable() || (orderKind == kMustBeOrdered_SortAngleKind | 
| +                    && angle.unorderable())); | 
| } | 
| if (sortable) { | 
| SkTQSort<SkOpAngle>(angleList->begin(), angleList->end() - 1); | 
| for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) { | 
| -            if (angles[angleIndex].unsortable()) { | 
| +            if (angles[angleIndex].unsortable() || (orderKind == kMustBeOrdered_SortAngleKind | 
| +                        && angles[angleIndex].unorderable())) { | 
| sortable = false; | 
| break; | 
| } | 
| @@ -2363,35 +2377,99 @@ | 
| return sortable; | 
| } | 
|  | 
| -void SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const { | 
| +// return true if midpoints were computed | 
| +bool SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const { | 
| +    SkASSERT(start != end); | 
| edge[0] = fTs[start].fPt; | 
| -    edge[SkPathOpsVerbToPoints(fVerb)] = fTs[end].fPt; | 
| -    if (fVerb == SkPath::kQuad_Verb || fVerb == SkPath::kCubic_Verb) { | 
| -        SkDPoint sub[2] = {{ edge[0].fX, edge[0].fY}, {edge[SkPathOpsVerbToPoints(fVerb)].fX, edge[SkPathOpsVerbToPoints(fVerb)].fY }}; | 
| +    int points = SkPathOpsVerbToPoints(fVerb); | 
| +    edge[points] = fTs[end].fPt; | 
| +    if (fVerb == SkPath::kLine_Verb) { | 
| +        return false; | 
| +    } | 
| +    double startT = fTs[start].fT; | 
| +    double endT = fTs[end].fT; | 
| +    if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) { | 
| +        // don't compute midpoints if we already have them | 
| if (fVerb == SkPath::kQuad_Verb) { | 
| -            edge[1] = SkDQuad::SubDivide(fPts, sub[0], sub[1], fTs[start].fT, | 
| -                    fTs[end].fT).asSkPoint(); | 
| -        } else { | 
| -            SkDCubic::SubDivide(fPts, sub[0], sub[1], fTs[start].fT, fTs[end].fT, sub); | 
| -            edge[1] = sub[0].asSkPoint(); | 
| -            edge[2] = sub[1].asSkPoint(); | 
| +            edge[1] = fPts[1]; | 
| +            return false; | 
| } | 
| +        SkASSERT(fVerb == SkPath::kCubic_Verb); | 
| +        if (start < end) { | 
| +            edge[1] = fPts[1]; | 
| +            edge[2] = fPts[2]; | 
| +            return false; | 
| +        } | 
| +        edge[1] = fPts[2]; | 
| +        edge[2] = fPts[1]; | 
| +        return false; | 
| } | 
| +    const SkDPoint sub[2] = {{ edge[0].fX, edge[0].fY}, {edge[points].fX, edge[points].fY }}; | 
| +    if (fVerb == SkPath::kQuad_Verb) { | 
| +        edge[1] = SkDQuad::SubDivide(fPts, sub[0], sub[1], startT, endT).asSkPoint(); | 
| +    } else { | 
| +        SkASSERT(fVerb == SkPath::kCubic_Verb); | 
| +        SkDPoint ctrl[2]; | 
| +        SkDCubic::SubDivide(fPts, sub[0], sub[1], startT, endT, ctrl); | 
| +        edge[1] = ctrl[0].asSkPoint(); | 
| +        edge[2] = ctrl[1].asSkPoint(); | 
| +    } | 
| +    return true; | 
| } | 
|  | 
| +// return true if midpoints were computed | 
| +bool SkOpSegment::subDivide(int start, int end, SkDCubic* result) const { | 
| +    SkASSERT(start != end); | 
| +    (*result)[0].set(fTs[start].fPt); | 
| +    int points = SkPathOpsVerbToPoints(fVerb); | 
| +    (*result)[points].set(fTs[end].fPt); | 
| +    if (fVerb == SkPath::kLine_Verb) { | 
| +        return false; | 
| +    } | 
| +    double startT = fTs[start].fT; | 
| +    double endT = fTs[end].fT; | 
| +    if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) { | 
| +        // don't compute midpoints if we already have them | 
| +        if (fVerb == SkPath::kQuad_Verb) { | 
| +            (*result)[1].set(fPts[1]); | 
| +            return false; | 
| +        } | 
| +        SkASSERT(fVerb == SkPath::kCubic_Verb); | 
| +        if (start < end) { | 
| +            (*result)[1].set(fPts[1]); | 
| +            (*result)[2].set(fPts[2]); | 
| +            return false; | 
| +        } | 
| +        (*result)[1].set(fPts[2]); | 
| +        (*result)[2].set(fPts[1]); | 
| +        return false; | 
| +    } | 
| +    if (fVerb == SkPath::kQuad_Verb) { | 
| +        (*result)[1] = SkDQuad::SubDivide(fPts, (*result)[0], (*result)[2], startT, endT); | 
| +    } else { | 
| +        SkASSERT(fVerb == SkPath::kCubic_Verb); | 
| +        SkDCubic::SubDivide(fPts, (*result)[0], (*result)[3], startT, endT, &(*result)[1]); | 
| +    } | 
| +    return true; | 
| +} | 
| + | 
| void SkOpSegment::subDivideBounds(int start, int end, SkPathOpsBounds* bounds) const { | 
| SkPoint edge[4]; | 
| subDivide(start, end, edge); | 
| (bounds->*SetCurveBounds[SkPathOpsVerbToPoints(fVerb)])(edge); | 
| } | 
|  | 
| -bool SkOpSegment::tiny(const SkOpAngle* angle) const { | 
| +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::TrackOutside(SkTDArray<double>* outsideTs, double end, double start) { | 
| int outCount = outsideTs->count(); | 
| if (outCount == 0 || !approximately_negative(end - (*outsideTs)[outCount - 2])) { | 
| @@ -2735,8 +2813,8 @@ | 
| } | 
| SkDebugf("%s [%d] %s", __FUNCTION__, index, | 
| angle.unsortable() ? "*** UNSORTABLE *** " : ""); | 
| -    #if COMPACT_DEBUG_SORT | 
| -        SkDebugf("id=%d %s start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)", | 
| +    #if DEBUG_SORT_COMPACT | 
| +        SkDebugf("id=%d %s start=%d (%1.9g,%1.9g) end=%d (%1.9g,%1.9g)", | 
| segment.fID, kLVerbStr[SkPathOpsVerbToPoints(segment.fVerb)], | 
| start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end, | 
| segment.xAtT(&eSpan), segment.yAtT(&eSpan)); | 
|  |