Home | History | Annotate | Download | only in pathops
      1 /*
      2 * Copyright 2013 Google Inc.
      3 *
      4 * Use of this source code is governed by a BSD-style license that can be
      5 * found in the LICENSE file.
      6 */
      7 #include "SkIntersections.h"
      8 #include "SkOpContour.h"
      9 #include "SkPathWriter.h"
     10 #include "SkTSort.h"
     11 
     12 bool SkOpContour::addCoincident(int index, SkOpContour* other, int otherIndex,
     13         const SkIntersections& ts, bool swap) {
     14     SkPoint pt0 = ts.pt(0).asSkPoint();
     15     SkPoint pt1 = ts.pt(1).asSkPoint();
     16     if (pt0 == pt1) {
     17         // FIXME: one could imagine a case where it would be incorrect to ignore this
     18         // suppose two self-intersecting cubics overlap to be coincident --
     19         // this needs to check that by some measure the t values are far enough apart
     20         // or needs to check to see if the self-intersection bit was set on the cubic segment
     21         return false;
     22     }
     23     SkCoincidence& coincidence = fCoincidences.push_back();
     24     coincidence.fOther = other;
     25     coincidence.fSegments[0] = index;
     26     coincidence.fSegments[1] = otherIndex;
     27     coincidence.fTs[swap][0] = ts[0][0];
     28     coincidence.fTs[swap][1] = ts[0][1];
     29     coincidence.fTs[!swap][0] = ts[1][0];
     30     coincidence.fTs[!swap][1] = ts[1][1];
     31     coincidence.fPts[swap][0] = pt0;
     32     coincidence.fPts[swap][1] = pt1;
     33     bool nearStart = ts.nearlySame(0);
     34     bool nearEnd = ts.nearlySame(1);
     35     coincidence.fPts[!swap][0] = nearStart ? ts.pt2(0).asSkPoint() : pt0;
     36     coincidence.fPts[!swap][1] = nearEnd ? ts.pt2(1).asSkPoint() : pt1;
     37     coincidence.fNearly[0] = nearStart;
     38     coincidence.fNearly[1] = nearEnd;
     39     return true;
     40 }
     41 
     42 SkOpSegment* SkOpContour::nonVerticalSegment(int* start, int* end) {
     43     int segmentCount = fSortedSegments.count();
     44     SkASSERT(segmentCount > 0);
     45     for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedIndex) {
     46         SkOpSegment* testSegment = fSortedSegments[sortedIndex];
     47         if (testSegment->done()) {
     48             continue;
     49         }
     50         *start = *end = 0;
     51         while (testSegment->nextCandidate(start, end)) {
     52             if (!testSegment->isVertical(*start, *end)) {
     53                 return testSegment;
     54             }
     55         }
     56     }
     57     return NULL;
     58 }
     59 
     60 // first pass, add missing T values
     61 // second pass, determine winding values of overlaps
     62 void SkOpContour::addCoincidentPoints() {
     63     int count = fCoincidences.count();
     64     for (int index = 0; index < count; ++index) {
     65         SkCoincidence& coincidence = fCoincidences[index];
     66         int thisIndex = coincidence.fSegments[0];
     67         SkOpSegment& thisOne = fSegments[thisIndex];
     68         SkOpContour* otherContour = coincidence.fOther;
     69         int otherIndex = coincidence.fSegments[1];
     70         SkOpSegment& other = otherContour->fSegments[otherIndex];
     71         if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
     72             // OPTIMIZATION: remove from array
     73             continue;
     74         }
     75     #if DEBUG_CONCIDENT
     76         thisOne.debugShowTs("-");
     77         other.debugShowTs("o");
     78     #endif
     79         double startT = coincidence.fTs[0][0];
     80         double endT = coincidence.fTs[0][1];
     81         bool startSwapped, oStartSwapped, cancelers;
     82         if ((cancelers = startSwapped = startT > endT)) {
     83             SkTSwap(startT, endT);
     84         }
     85         if (startT == endT) { // if one is very large the smaller may have collapsed to nothing
     86             if (endT <= 1 - FLT_EPSILON) {
     87                 endT += FLT_EPSILON;
     88                 SkASSERT(endT <= 1);
     89             } else {
     90                 startT -= FLT_EPSILON;
     91                 SkASSERT(startT >= 0);
     92             }
     93         }
     94         SkASSERT(!approximately_negative(endT - startT));
     95         double oStartT = coincidence.fTs[1][0];
     96         double oEndT = coincidence.fTs[1][1];
     97         if ((oStartSwapped = oStartT > oEndT)) {
     98             SkTSwap(oStartT, oEndT);
     99             cancelers ^= true;
    100         }
    101         SkASSERT(!approximately_negative(oEndT - oStartT));
    102         const SkPoint& startPt = coincidence.fPts[0][startSwapped];
    103         if (cancelers) {
    104             // make sure startT and endT have t entries
    105             if (startT > 0 || oEndT < 1
    106                     || thisOne.isMissing(startT, startPt) || other.isMissing(oEndT, startPt)) {
    107                 thisOne.addTPair(startT, &other, oEndT, true, startPt,
    108                         coincidence.fPts[1][startSwapped]);
    109             }
    110             const SkPoint& oStartPt = coincidence.fPts[1][oStartSwapped];
    111             if (oStartT > 0 || endT < 1
    112                     || thisOne.isMissing(endT, oStartPt) || other.isMissing(oStartT, oStartPt)) {
    113                 other.addTPair(oStartT, &thisOne, endT, true, oStartPt,
    114                         coincidence.fPts[0][oStartSwapped]);
    115             }
    116         } else {
    117             if (startT > 0 || oStartT > 0
    118                     || thisOne.isMissing(startT, startPt) || other.isMissing(oStartT, startPt)) {
    119                 thisOne.addTPair(startT, &other, oStartT, true, startPt,
    120                         coincidence.fPts[1][startSwapped]);
    121             }
    122             const SkPoint& oEndPt = coincidence.fPts[1][!oStartSwapped];
    123             if (endT < 1 || oEndT < 1
    124                     || thisOne.isMissing(endT, oEndPt) || other.isMissing(oEndT, oEndPt)) {
    125                 other.addTPair(oEndT, &thisOne, endT, true, oEndPt,
    126                         coincidence.fPts[0][!oStartSwapped]);
    127             }
    128         }
    129     #if DEBUG_CONCIDENT
    130         thisOne.debugShowTs("+");
    131         other.debugShowTs("o");
    132     #endif
    133     }
    134     // if there are multiple pairs of coincidence that share an edge, see if the opposite
    135     // are also coincident
    136     for (int index = 0; index < count - 1; ++index) {
    137         const SkCoincidence& coincidence = fCoincidences[index];
    138         int thisIndex = coincidence.fSegments[0];
    139         SkOpContour* otherContour = coincidence.fOther;
    140         int otherIndex = coincidence.fSegments[1];
    141         for (int idx2 = 1; idx2 < count; ++idx2) {
    142             const SkCoincidence& innerCoin = fCoincidences[idx2];
    143             int innerThisIndex = innerCoin.fSegments[0];
    144             if (thisIndex == innerThisIndex) {
    145                 checkCoincidentPair(coincidence, 1, innerCoin, 1, false);
    146             }
    147             if (this == otherContour && otherIndex == innerThisIndex) {
    148                 checkCoincidentPair(coincidence, 0, innerCoin, 1, false);
    149             }
    150             SkOpContour* innerOtherContour = innerCoin.fOther;
    151             innerThisIndex = innerCoin.fSegments[1];
    152             if (this == innerOtherContour && thisIndex == innerThisIndex) {
    153                 checkCoincidentPair(coincidence, 1, innerCoin, 0, false);
    154             }
    155             if (otherContour == innerOtherContour && otherIndex == innerThisIndex) {
    156                 checkCoincidentPair(coincidence, 0, innerCoin, 0, false);
    157             }
    158         }
    159     }
    160 }
    161 
    162 bool SkOpContour::addPartialCoincident(int index, SkOpContour* other, int otherIndex,
    163         const SkIntersections& ts, int ptIndex, bool swap) {
    164     SkPoint pt0 = ts.pt(ptIndex).asSkPoint();
    165     SkPoint pt1 = ts.pt(ptIndex + 1).asSkPoint();
    166     if (SkDPoint::ApproximatelyEqual(pt0, pt1)) {
    167         // FIXME: one could imagine a case where it would be incorrect to ignore this
    168         // suppose two self-intersecting cubics overlap to form a partial coincidence --
    169         // although it isn't clear why the regular coincidence could wouldn't pick this up
    170         // this is exceptional enough to ignore for now
    171         return false;
    172     }
    173     SkCoincidence& coincidence = fPartialCoincidences.push_back();
    174     coincidence.fOther = other;
    175     coincidence.fSegments[0] = index;
    176     coincidence.fSegments[1] = otherIndex;
    177     coincidence.fTs[swap][0] = ts[0][ptIndex];
    178     coincidence.fTs[swap][1] = ts[0][ptIndex + 1];
    179     coincidence.fTs[!swap][0] = ts[1][ptIndex];
    180     coincidence.fTs[!swap][1] = ts[1][ptIndex + 1];
    181     coincidence.fPts[0][0] = coincidence.fPts[1][0] = pt0;
    182     coincidence.fPts[0][1] = coincidence.fPts[1][1] = pt1;
    183     coincidence.fNearly[0] = 0;
    184     coincidence.fNearly[1] = 0;
    185     return true;
    186 }
    187 
    188 void SkOpContour::align(const SkOpSegment::AlignedSpan& aligned, bool swap,
    189         SkCoincidence* coincidence) {
    190     for (int idx2 = 0; idx2 < 2; ++idx2) {
    191         if (coincidence->fPts[0][idx2] == aligned.fOldPt
    192                 && coincidence->fTs[swap][idx2] == aligned.fOldT) {
    193             SkASSERT(SkDPoint::RoughlyEqual(coincidence->fPts[0][idx2], aligned.fPt));
    194             coincidence->fPts[0][idx2] = aligned.fPt;
    195             SkASSERT(way_roughly_equal(coincidence->fTs[swap][idx2], aligned.fT));
    196             coincidence->fTs[swap][idx2] = aligned.fT;
    197         }
    198     }
    199 }
    200 
    201 void SkOpContour::alignCoincidence(const SkOpSegment::AlignedSpan& aligned,
    202         SkTArray<SkCoincidence, true>* coincidences) {
    203     int count = coincidences->count();
    204     for (int index = 0; index < count; ++index) {
    205         SkCoincidence& coincidence = (*coincidences)[index];
    206         int thisIndex = coincidence.fSegments[0];
    207         const SkOpSegment* thisOne = &fSegments[thisIndex];
    208         const SkOpContour* otherContour = coincidence.fOther;
    209         int otherIndex = coincidence.fSegments[1];
    210         const SkOpSegment* other = &otherContour->fSegments[otherIndex];
    211         if (thisOne == aligned.fOther1 && other == aligned.fOther2) {
    212             align(aligned, false, &coincidence);
    213         } else if (thisOne == aligned.fOther2 && other == aligned.fOther1) {
    214             align(aligned, true, &coincidence);
    215         }
    216     }
    217 }
    218 
    219 void SkOpContour::alignTPt(int segmentIndex, const SkOpContour* other, int otherIndex,
    220         bool swap, int tIndex, SkIntersections* ts, SkPoint* point) const {
    221     int zeroPt;
    222     if ((zeroPt = alignT(swap, tIndex, ts)) >= 0) {
    223         alignPt(segmentIndex, point, zeroPt);
    224     }
    225     if ((zeroPt = other->alignT(!swap, tIndex, ts)) >= 0) {
    226         other->alignPt(otherIndex, point, zeroPt);
    227     }
    228 }
    229 
    230 void SkOpContour::alignPt(int index, SkPoint* point, int zeroPt) const {
    231     const SkOpSegment& segment = fSegments[index];
    232     if (0 == zeroPt) {
    233         *point = segment.pts()[0];
    234     } else {
    235         *point = segment.pts()[SkPathOpsVerbToPoints(segment.verb())];
    236     }
    237 }
    238 
    239 int SkOpContour::alignT(bool swap, int tIndex, SkIntersections* ts) const {
    240     double tVal = (*ts)[swap][tIndex];
    241     if (tVal != 0 && precisely_zero(tVal)) {
    242         ts->set(swap, tIndex, 0);
    243         return 0;
    244     }
    245      if (tVal != 1 && precisely_equal(tVal, 1)) {
    246         ts->set(swap, tIndex, 1);
    247         return 1;
    248     }
    249     return -1;
    250 }
    251 
    252 bool SkOpContour::calcAngles() {
    253     int segmentCount = fSegments.count();
    254     for (int test = 0; test < segmentCount; ++test) {
    255         if (!fSegments[test].calcAngles()) {
    256             return false;
    257         }
    258     }
    259     return true;
    260 }
    261 
    262 void SkOpContour::calcCoincidentWinding() {
    263     int count = fCoincidences.count();
    264 #if DEBUG_CONCIDENT
    265     if (count > 0) {
    266         SkDebugf("%s count=%d\n", __FUNCTION__, count);
    267     }
    268 #endif
    269     for (int index = 0; index < count; ++index) {
    270         SkCoincidence& coincidence = fCoincidences[index];
    271          calcCommonCoincidentWinding(coincidence);
    272     }
    273 }
    274 
    275 void SkOpContour::calcPartialCoincidentWinding() {
    276     int count = fPartialCoincidences.count();
    277 #if DEBUG_CONCIDENT
    278     if (count > 0) {
    279         SkDebugf("%s count=%d\n", __FUNCTION__, count);
    280     }
    281 #endif
    282     for (int index = 0; index < count; ++index) {
    283         SkCoincidence& coincidence = fPartialCoincidences[index];
    284         calcCommonCoincidentWinding(coincidence);
    285     }
    286     // if there are multiple pairs of partial coincidence that share an edge, see if the opposite
    287     // are also coincident
    288     for (int index = 0; index < count - 1; ++index) {
    289         const SkCoincidence& coincidence = fPartialCoincidences[index];
    290         int thisIndex = coincidence.fSegments[0];
    291         SkOpContour* otherContour = coincidence.fOther;
    292         int otherIndex = coincidence.fSegments[1];
    293         for (int idx2 = 1; idx2 < count; ++idx2) {
    294             const SkCoincidence& innerCoin = fPartialCoincidences[idx2];
    295             int innerThisIndex = innerCoin.fSegments[0];
    296             if (thisIndex == innerThisIndex) {
    297                 checkCoincidentPair(coincidence, 1, innerCoin, 1, true);
    298             }
    299             if (this == otherContour && otherIndex == innerThisIndex) {
    300                 checkCoincidentPair(coincidence, 0, innerCoin, 1, true);
    301             }
    302             SkOpContour* innerOtherContour = innerCoin.fOther;
    303             innerThisIndex = innerCoin.fSegments[1];
    304             if (this == innerOtherContour && thisIndex == innerThisIndex) {
    305                 checkCoincidentPair(coincidence, 1, innerCoin, 0, true);
    306             }
    307             if (otherContour == innerOtherContour && otherIndex == innerThisIndex) {
    308                 checkCoincidentPair(coincidence, 0, innerCoin, 0, true);
    309             }
    310         }
    311     }
    312 }
    313 
    314 void SkOpContour::checkCoincidentPair(const SkCoincidence& oneCoin, int oneIdx,
    315         const SkCoincidence& twoCoin, int twoIdx, bool partial) {
    316     SkASSERT((oneIdx ? this : oneCoin.fOther) == (twoIdx ? this : twoCoin.fOther));
    317     SkASSERT(oneCoin.fSegments[!oneIdx] == twoCoin.fSegments[!twoIdx]);
    318     // look for common overlap
    319     double min = SK_ScalarMax;
    320     double max = SK_ScalarMin;
    321     double min1 = oneCoin.fTs[!oneIdx][0];
    322     double max1 = oneCoin.fTs[!oneIdx][1];
    323     double min2 = twoCoin.fTs[!twoIdx][0];
    324     double max2 = twoCoin.fTs[!twoIdx][1];
    325     bool cancelers = (min1 < max1) != (min2 < max2);
    326     if (min1 > max1) {
    327         SkTSwap(min1, max1);
    328     }
    329     if (min2 > max2) {
    330         SkTSwap(min2, max2);
    331     }
    332     if (between(min1, min2, max1)) {
    333         min = min2;
    334     }
    335     if (between(min1, max2, max1)) {
    336         max = max2;
    337     }
    338     if (between(min2, min1, max2)) {
    339         min = SkTMin(min, min1);
    340     }
    341     if (between(min2, max1, max2)) {
    342         max = SkTMax(max, max1);
    343     }
    344     if (min >= max) {
    345         return;  // no overlap
    346     }
    347     // look to see if opposite are different segments
    348     int seg1Index = oneCoin.fSegments[oneIdx];
    349     int seg2Index = twoCoin.fSegments[twoIdx];
    350     if (seg1Index == seg2Index) {
    351         return;
    352     }
    353     SkOpContour* contour1 = oneIdx ? oneCoin.fOther : this;
    354     SkOpContour* contour2 = twoIdx ? twoCoin.fOther : this;
    355     SkOpSegment* segment1 = &contour1->fSegments[seg1Index];
    356     SkOpSegment* segment2 = &contour2->fSegments[seg2Index];
    357     // find opposite t value ranges corresponding to reference min/max range
    358     const SkOpContour* refContour = oneIdx ? this : oneCoin.fOther;
    359     const int refSegIndex = oneCoin.fSegments[!oneIdx];
    360     const SkOpSegment* refSegment = &refContour->fSegments[refSegIndex];
    361     int seg1Start = segment1->findOtherT(min, refSegment);
    362     int seg1End = segment1->findOtherT(max, refSegment);
    363     int seg2Start = segment2->findOtherT(min, refSegment);
    364     int seg2End = segment2->findOtherT(max, refSegment);
    365     // if the opposite pairs already contain min/max, we're done
    366     if (seg1Start >= 0 && seg1End >= 0 && seg2Start >= 0 && seg2End >= 0) {
    367         return;
    368     }
    369     double loEnd = SkTMin(min1, min2);
    370     double hiEnd = SkTMax(max1, max2);
    371     // insert the missing coincident point(s)
    372     double missingT1 = -1;
    373     double otherT1 = -1;
    374     if (seg1Start < 0) {
    375         if (seg2Start < 0) {
    376             return;
    377         }
    378         missingT1 = segment1->calcMissingTStart(refSegment, loEnd, min, max, hiEnd,
    379                 segment2, seg1End);
    380         if (missingT1 < 0) {
    381             return;
    382         }
    383         const SkOpSpan* missingSpan = &segment2->span(seg2Start);
    384         otherT1 = missingSpan->fT;
    385     } else if (seg2Start < 0) {
    386         SkASSERT(seg1Start >= 0);
    387         missingT1 = segment2->calcMissingTStart(refSegment, loEnd, min, max, hiEnd,
    388                 segment1, seg2End);
    389         if (missingT1 < 0) {
    390             return;
    391         }
    392         const SkOpSpan* missingSpan = &segment1->span(seg1Start);
    393         otherT1 = missingSpan->fT;
    394     }
    395     SkPoint missingPt1;
    396     SkOpSegment* addTo1 = NULL;
    397     SkOpSegment* addOther1 = seg1Start < 0 ? segment2 : segment1;
    398     int minTIndex = refSegment->findExactT(min, addOther1);
    399     SkASSERT(minTIndex >= 0);
    400     if (missingT1 >= 0) {
    401         missingPt1 = refSegment->span(minTIndex).fPt;
    402         addTo1 = seg1Start < 0 ? segment1 : segment2;
    403     }
    404     double missingT2 = -1;
    405     double otherT2 = -1;
    406     if (seg1End < 0) {
    407         if (seg2End < 0) {
    408             return;
    409         }
    410         missingT2 = segment1->calcMissingTEnd(refSegment, loEnd, min, max, hiEnd,
    411                 segment2, seg1Start);
    412         if (missingT2 < 0) {
    413             return;
    414         }
    415         const SkOpSpan* missingSpan = &segment2->span(seg2End);
    416         otherT2 = missingSpan->fT;
    417     } else if (seg2End < 0) {
    418         SkASSERT(seg1End >= 0);
    419         missingT2 = segment2->calcMissingTEnd(refSegment, loEnd, min, max, hiEnd,
    420                 segment1, seg2Start);
    421         if (missingT2 < 0) {
    422             return;
    423         }
    424         const SkOpSpan* missingSpan = &segment1->span(seg1End);
    425         otherT2 = missingSpan->fT;
    426     }
    427     SkPoint missingPt2;
    428     SkOpSegment* addTo2 = NULL;
    429     SkOpSegment* addOther2 = seg1End < 0 ? segment2 : segment1;
    430     int maxTIndex = refSegment->findExactT(max, addOther2);
    431     SkASSERT(maxTIndex >= 0);
    432     if (missingT2 >= 0) {
    433         missingPt2 = refSegment->span(maxTIndex).fPt;
    434         addTo2 = seg1End < 0 ? segment1 : segment2;
    435     }
    436     if (missingT1 >= 0) {
    437         addTo1->pinT(missingPt1, &missingT1);
    438         addTo1->addTPair(missingT1, addOther1, otherT1, false, missingPt1);
    439     } else {
    440         SkASSERT(minTIndex >= 0);
    441         missingPt1 = refSegment->span(minTIndex).fPt;
    442     }
    443     if (missingT2 >= 0) {
    444         addTo2->pinT(missingPt2, &missingT2);
    445         addTo2->addTPair(missingT2, addOther2, otherT2, false, missingPt2);
    446     } else {
    447         SkASSERT(minTIndex >= 0);
    448         missingPt2 = refSegment->span(maxTIndex).fPt;
    449     }
    450     if (!partial) {
    451         return;
    452     }
    453     if (cancelers) {
    454         if (missingT1 >= 0) {
    455             addTo1->addTCancel(missingPt1, missingPt2, addOther1);
    456         } else {
    457             addTo2->addTCancel(missingPt1, missingPt2, addOther2);
    458         }
    459     } else if (missingT1 >= 0) {
    460         addTo1->addTCoincident(missingPt1, missingPt2, addTo1 == addTo2 ? missingT2 : otherT2,
    461                 addOther1);
    462     } else {
    463         addTo2->addTCoincident(missingPt2, missingPt1, addTo2 == addTo1 ? missingT1 : otherT1,
    464                 addOther2);
    465     }
    466 }
    467 
    468 void SkOpContour::joinCoincidence(const SkTArray<SkCoincidence, true>& coincidences, bool partial) {
    469     int count = coincidences.count();
    470 #if DEBUG_CONCIDENT
    471     if (count > 0) {
    472         SkDebugf("%s count=%d\n", __FUNCTION__, count);
    473     }
    474 #endif
    475     // look for a lineup where the partial implies another adjoining coincidence
    476     for (int index = 0; index < count; ++index) {
    477         const SkCoincidence& coincidence = coincidences[index];
    478         int thisIndex = coincidence.fSegments[0];
    479         SkOpSegment& thisOne = fSegments[thisIndex];
    480         if (thisOne.done()) {
    481             continue;
    482         }
    483         SkOpContour* otherContour = coincidence.fOther;
    484         int otherIndex = coincidence.fSegments[1];
    485         SkOpSegment& other = otherContour->fSegments[otherIndex];
    486         if (other.done()) {
    487             continue;
    488         }
    489         double startT = coincidence.fTs[0][0];
    490         double endT = coincidence.fTs[0][1];
    491         if (startT == endT) {  // this can happen in very large compares
    492             continue;
    493         }
    494         double oStartT = coincidence.fTs[1][0];
    495         double oEndT = coincidence.fTs[1][1];
    496         if (oStartT == oEndT) {
    497             continue;
    498         }
    499         bool swapStart = startT > endT;
    500         bool swapOther = oStartT > oEndT;
    501         const SkPoint* startPt = &coincidence.fPts[0][0];
    502         const SkPoint* endPt = &coincidence.fPts[0][1];
    503         if (swapStart) {
    504             SkTSwap(startT, endT);
    505             SkTSwap(oStartT, oEndT);
    506             SkTSwap(startPt, endPt);
    507         }
    508         bool cancel = swapOther != swapStart;
    509         int step = swapStart ? -1 : 1;
    510         int oStep = swapOther ? -1 : 1;
    511         double oMatchStart = cancel ? oEndT : oStartT;
    512         if (partial ? startT != 0 || oMatchStart != 0 : (startT == 0) != (oMatchStart == 0)) {
    513             bool added = false;
    514             if (oMatchStart != 0) {
    515                 const SkPoint& oMatchStartPt = cancel ? *endPt : *startPt;
    516                 added = thisOne.joinCoincidence(&other, oMatchStart, oMatchStartPt, oStep, cancel);
    517             }
    518             if (!cancel && startT != 0 && !added) {
    519                 (void) other.joinCoincidence(&thisOne, startT, *startPt, step, cancel);
    520             }
    521         }
    522         double oMatchEnd = cancel ? oStartT : oEndT;
    523         if (partial ? endT != 1 || oMatchEnd != 1 : (endT == 1) != (oMatchEnd == 1)) {
    524             bool added = false;
    525             if (cancel && endT != 1 && !added) {
    526                 (void) other.joinCoincidence(&thisOne, endT, *endPt, -step, cancel);
    527             }
    528         }
    529     }
    530 }
    531 
    532 void SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence) {
    533     if (coincidence.fNearly[0] && coincidence.fNearly[1]) {
    534         return;
    535     }
    536     int thisIndex = coincidence.fSegments[0];
    537     SkOpSegment& thisOne = fSegments[thisIndex];
    538     if (thisOne.done()) {
    539         return;
    540     }
    541     SkOpContour* otherContour = coincidence.fOther;
    542     int otherIndex = coincidence.fSegments[1];
    543     SkOpSegment& other = otherContour->fSegments[otherIndex];
    544     if (other.done()) {
    545         return;
    546     }
    547     double startT = coincidence.fTs[0][0];
    548     double endT = coincidence.fTs[0][1];
    549     const SkPoint* startPt = &coincidence.fPts[0][0];
    550     const SkPoint* endPt = &coincidence.fPts[0][1];
    551     bool cancelers;
    552     if ((cancelers = startT > endT)) {
    553         SkTSwap<double>(startT, endT);
    554         SkTSwap<const SkPoint*>(startPt, endPt);
    555     }
    556     if (startT == endT) { // if span is very large, the smaller may have collapsed to nothing
    557         if (endT <= 1 - FLT_EPSILON) {
    558             endT += FLT_EPSILON;
    559             SkASSERT(endT <= 1);
    560         } else {
    561             startT -= FLT_EPSILON;
    562             SkASSERT(startT >= 0);
    563         }
    564     }
    565     SkASSERT(!approximately_negative(endT - startT));
    566     double oStartT = coincidence.fTs[1][0];
    567     double oEndT = coincidence.fTs[1][1];
    568     if (oStartT > oEndT) {
    569         SkTSwap<double>(oStartT, oEndT);
    570         cancelers ^= true;
    571     }
    572     SkASSERT(!approximately_negative(oEndT - oStartT));
    573     if (cancelers) {
    574         thisOne.addTCancel(*startPt, *endPt, &other);
    575     } else {
    576         thisOne.addTCoincident(*startPt, *endPt, endT, &other);
    577     }
    578 #if DEBUG_CONCIDENT
    579     thisOne.debugShowTs("p");
    580     other.debugShowTs("o");
    581 #endif
    582 }
    583 
    584 void SkOpContour::resolveNearCoincidence() {
    585     int count = fCoincidences.count();
    586     for (int index = 0; index < count; ++index) {
    587         SkCoincidence& coincidence = fCoincidences[index];
    588         if (!coincidence.fNearly[0] || !coincidence.fNearly[1]) {
    589             continue;
    590         }
    591         int thisIndex = coincidence.fSegments[0];
    592         SkOpSegment& thisOne = fSegments[thisIndex];
    593         SkOpContour* otherContour = coincidence.fOther;
    594         int otherIndex = coincidence.fSegments[1];
    595         SkOpSegment& other = otherContour->fSegments[otherIndex];
    596         if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
    597             // OPTIMIZATION: remove from coincidence array
    598             continue;
    599         }
    600     #if DEBUG_CONCIDENT
    601         thisOne.debugShowTs("-");
    602         other.debugShowTs("o");
    603     #endif
    604         double startT = coincidence.fTs[0][0];
    605         double endT = coincidence.fTs[0][1];
    606         bool cancelers;
    607         if ((cancelers = startT > endT)) {
    608             SkTSwap<double>(startT, endT);
    609         }
    610         if (startT == endT) { // if span is very large, the smaller may have collapsed to nothing
    611             if (endT <= 1 - FLT_EPSILON) {
    612                 endT += FLT_EPSILON;
    613                 SkASSERT(endT <= 1);
    614             } else {
    615                 startT -= FLT_EPSILON;
    616                 SkASSERT(startT >= 0);
    617             }
    618         }
    619         SkASSERT(!approximately_negative(endT - startT));
    620         double oStartT = coincidence.fTs[1][0];
    621         double oEndT = coincidence.fTs[1][1];
    622         if (oStartT > oEndT) {
    623             SkTSwap<double>(oStartT, oEndT);
    624             cancelers ^= true;
    625         }
    626         SkASSERT(!approximately_negative(oEndT - oStartT));
    627         if (cancelers) {
    628             thisOne.blindCancel(coincidence, &other);
    629         } else {
    630             thisOne.blindCoincident(coincidence, &other);
    631         }
    632     }
    633 }
    634 
    635 void SkOpContour::sortAngles() {
    636     int segmentCount = fSegments.count();
    637     for (int test = 0; test < segmentCount; ++test) {
    638         fSegments[test].sortAngles();
    639     }
    640 }
    641 
    642 void SkOpContour::sortSegments() {
    643     int segmentCount = fSegments.count();
    644     fSortedSegments.push_back_n(segmentCount);
    645     for (int test = 0; test < segmentCount; ++test) {
    646         fSortedSegments[test] = &fSegments[test];
    647     }
    648     SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
    649     fFirstSorted = 0;
    650 }
    651 
    652 void SkOpContour::toPath(SkPathWriter* path) const {
    653     int segmentCount = fSegments.count();
    654     const SkPoint& pt = fSegments.front().pts()[0];
    655     path->deferredMove(pt);
    656     for (int test = 0; test < segmentCount; ++test) {
    657         fSegments[test].addCurveTo(0, 1, path, true);
    658     }
    659     path->close();
    660 }
    661 
    662 void SkOpContour::topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY,
    663         SkOpSegment** topStart) {
    664     int segmentCount = fSortedSegments.count();
    665     SkASSERT(segmentCount > 0);
    666     int sortedIndex = fFirstSorted;
    667     fDone = true;  // may be cleared below
    668     for ( ; sortedIndex < segmentCount; ++sortedIndex) {
    669         SkOpSegment* testSegment = fSortedSegments[sortedIndex];
    670         if (testSegment->done()) {
    671             if (sortedIndex == fFirstSorted) {
    672                 ++fFirstSorted;
    673             }
    674             continue;
    675         }
    676         fDone = false;
    677         SkPoint testXY = testSegment->activeLeftTop(NULL);
    678         if (*topStart) {
    679             if (testXY.fY < topLeft.fY) {
    680                 continue;
    681             }
    682             if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
    683                 continue;
    684             }
    685             if (bestXY->fY < testXY.fY) {
    686                 continue;
    687             }
    688             if (bestXY->fY == testXY.fY && bestXY->fX < testXY.fX) {
    689                 continue;
    690             }
    691         }
    692         *topStart = testSegment;
    693         *bestXY = testXY;
    694     }
    695 }
    696 
    697 SkOpSegment* SkOpContour::undoneSegment(int* start, int* end) {
    698     int segmentCount = fSegments.count();
    699     for (int test = 0; test < segmentCount; ++test) {
    700         SkOpSegment* testSegment = &fSegments[test];
    701         if (testSegment->done()) {
    702             continue;
    703         }
    704         testSegment->undoneSpan(start, end);
    705         return testSegment;
    706     }
    707     return NULL;
    708 }
    709 
    710 #if DEBUG_SHOW_WINDING
    711 int SkOpContour::debugShowWindingValues(int totalSegments, int ofInterest) {
    712     int count = fSegments.count();
    713     int sum = 0;
    714     for (int index = 0; index < count; ++index) {
    715         sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
    716     }
    717 //      SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
    718     return sum;
    719 }
    720 
    721 void SkOpContour::debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList) {
    722 //     int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
    723 //    int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
    724     int ofInterest = 1 << 5 | 1 << 8;
    725     int total = 0;
    726     int index;
    727     for (index = 0; index < contourList.count(); ++index) {
    728         total += contourList[index]->segments().count();
    729     }
    730     int sum = 0;
    731     for (index = 0; index < contourList.count(); ++index) {
    732         sum += contourList[index]->debugShowWindingValues(total, ofInterest);
    733     }
    734 //       SkDebugf("%s total=%d\n", __FUNCTION__, sum);
    735 }
    736 #endif
    737 
    738 void SkOpContour::setBounds() {
    739     int count = fSegments.count();
    740     if (count == 0) {
    741         SkDebugf("%s empty contour\n", __FUNCTION__);
    742         SkASSERT(0);
    743         // FIXME: delete empty contour?
    744         return;
    745     }
    746     fBounds = fSegments.front().bounds();
    747     for (int index = 1; index < count; ++index) {
    748         fBounds.add(fSegments[index].bounds());
    749     }
    750 }
    751