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[0] = pt0;
     32     coincidence.fPts[1] = pt1;
     33     return true;
     34 }
     35 
     36 SkOpSegment* SkOpContour::nonVerticalSegment(int* start, int* end) {
     37     int segmentCount = fSortedSegments.count();
     38     SkASSERT(segmentCount > 0);
     39     for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedIndex) {
     40         SkOpSegment* testSegment = fSortedSegments[sortedIndex];
     41         if (testSegment->done()) {
     42             continue;
     43         }
     44         *start = *end = 0;
     45         while (testSegment->nextCandidate(start, end)) {
     46             if (!testSegment->isVertical(*start, *end)) {
     47                 return testSegment;
     48             }
     49         }
     50     }
     51     return NULL;
     52 }
     53 
     54 // first pass, add missing T values
     55 // second pass, determine winding values of overlaps
     56 void SkOpContour::addCoincidentPoints() {
     57     int count = fCoincidences.count();
     58     for (int index = 0; index < count; ++index) {
     59         SkCoincidence& coincidence = fCoincidences[index];
     60         int thisIndex = coincidence.fSegments[0];
     61         SkOpSegment& thisOne = fSegments[thisIndex];
     62         SkOpContour* otherContour = coincidence.fOther;
     63         int otherIndex = coincidence.fSegments[1];
     64         SkOpSegment& other = otherContour->fSegments[otherIndex];
     65         if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
     66             // OPTIMIZATION: remove from array
     67             continue;
     68         }
     69     #if DEBUG_CONCIDENT
     70         thisOne.debugShowTs("-");
     71         other.debugShowTs("o");
     72     #endif
     73         double startT = coincidence.fTs[0][0];
     74         double endT = coincidence.fTs[0][1];
     75         bool startSwapped, oStartSwapped, cancelers;
     76         if ((cancelers = startSwapped = startT > endT)) {
     77             SkTSwap(startT, endT);
     78         }
     79         if (startT == endT) { // if one is very large the smaller may have collapsed to nothing
     80             if (endT <= 1 - FLT_EPSILON) {
     81                 endT += FLT_EPSILON;
     82                 SkASSERT(endT <= 1);
     83             } else {
     84                 startT -= FLT_EPSILON;
     85                 SkASSERT(startT >= 0);
     86             }
     87         }
     88         SkASSERT(!approximately_negative(endT - startT));
     89         double oStartT = coincidence.fTs[1][0];
     90         double oEndT = coincidence.fTs[1][1];
     91         if ((oStartSwapped = oStartT > oEndT)) {
     92             SkTSwap(oStartT, oEndT);
     93             cancelers ^= true;
     94         }
     95         SkASSERT(!approximately_negative(oEndT - oStartT));
     96         if (cancelers) {
     97             // make sure startT and endT have t entries
     98             const SkPoint& startPt = coincidence.fPts[startSwapped];
     99             if (startT > 0 || oEndT < 1
    100                     || thisOne.isMissing(startT, startPt) || other.isMissing(oEndT, startPt)) {
    101                 thisOne.addTPair(startT, &other, oEndT, true, startPt);
    102             }
    103             const SkPoint& oStartPt = coincidence.fPts[oStartSwapped];
    104             if (oStartT > 0 || endT < 1
    105                     || thisOne.isMissing(endT, oStartPt) || other.isMissing(oStartT, oStartPt)) {
    106                 other.addTPair(oStartT, &thisOne, endT, true, oStartPt);
    107             }
    108         } else {
    109             const SkPoint& startPt = coincidence.fPts[startSwapped];
    110             if (startT > 0 || oStartT > 0
    111                     || thisOne.isMissing(startT, startPt) || other.isMissing(oStartT, startPt)) {
    112                 thisOne.addTPair(startT, &other, oStartT, true, startPt);
    113             }
    114             const SkPoint& oEndPt = coincidence.fPts[!oStartSwapped];
    115             if (endT < 1 || oEndT < 1
    116                     || thisOne.isMissing(endT, oEndPt) || other.isMissing(oEndT, oEndPt)) {
    117                 other.addTPair(oEndT, &thisOne, endT, true, oEndPt);
    118             }
    119         }
    120     #if DEBUG_CONCIDENT
    121         thisOne.debugShowTs("+");
    122         other.debugShowTs("o");
    123     #endif
    124     }
    125 }
    126 
    127 bool SkOpContour::addPartialCoincident(int index, SkOpContour* other, int otherIndex,
    128         const SkIntersections& ts, int ptIndex, bool swap) {
    129     SkPoint pt0 = ts.pt(ptIndex).asSkPoint();
    130     SkPoint pt1 = ts.pt(ptIndex + 1).asSkPoint();
    131     if (SkDPoint::ApproximatelyEqual(pt0, pt1)) {
    132         // FIXME: one could imagine a case where it would be incorrect to ignore this
    133         // suppose two self-intersecting cubics overlap to form a partial coincidence --
    134         // although it isn't clear why the regular coincidence could wouldn't pick this up
    135         // this is exceptional enough to ignore for now
    136         return false;
    137     }
    138     SkCoincidence& coincidence = fPartialCoincidences.push_back();
    139     coincidence.fOther = other;
    140     coincidence.fSegments[0] = index;
    141     coincidence.fSegments[1] = otherIndex;
    142     coincidence.fTs[swap][0] = ts[0][ptIndex];
    143     coincidence.fTs[swap][1] = ts[0][ptIndex + 1];
    144     coincidence.fTs[!swap][0] = ts[1][ptIndex];
    145     coincidence.fTs[!swap][1] = ts[1][ptIndex + 1];
    146     coincidence.fPts[0] = pt0;
    147     coincidence.fPts[1] = pt1;
    148     return true;
    149 }
    150 
    151 void SkOpContour::calcCoincidentWinding() {
    152     int count = fCoincidences.count();
    153 #if DEBUG_CONCIDENT
    154     if (count > 0) {
    155         SkDebugf("%s count=%d\n", __FUNCTION__, count);
    156     }
    157 #endif
    158     for (int index = 0; index < count; ++index) {
    159         SkCoincidence& coincidence = fCoincidences[index];
    160          calcCommonCoincidentWinding(coincidence);
    161     }
    162 }
    163 
    164 void SkOpContour::calcPartialCoincidentWinding() {
    165     int count = fPartialCoincidences.count();
    166 #if DEBUG_CONCIDENT
    167     if (count > 0) {
    168         SkDebugf("%s count=%d\n", __FUNCTION__, count);
    169     }
    170 #endif
    171     for (int index = 0; index < count; ++index) {
    172         SkCoincidence& coincidence = fPartialCoincidences[index];
    173          calcCommonCoincidentWinding(coincidence);
    174     }
    175 }
    176 
    177 void SkOpContour::joinCoincidence(const SkTArray<SkCoincidence, true>& coincidences, bool partial) {
    178     int count = coincidences.count();
    179 #if DEBUG_CONCIDENT
    180     if (count > 0) {
    181         SkDebugf("%s count=%d\n", __FUNCTION__, count);
    182     }
    183 #endif
    184     // look for a lineup where the partial implies another adjoining coincidence
    185     for (int index = 0; index < count; ++index) {
    186         const SkCoincidence& coincidence = coincidences[index];
    187         int thisIndex = coincidence.fSegments[0];
    188         SkOpSegment& thisOne = fSegments[thisIndex];
    189         SkOpContour* otherContour = coincidence.fOther;
    190         int otherIndex = coincidence.fSegments[1];
    191         SkOpSegment& other = otherContour->fSegments[otherIndex];
    192         double startT = coincidence.fTs[0][0];
    193         double endT = coincidence.fTs[0][1];
    194         if (startT == endT) {  // this can happen in very large compares
    195             continue;
    196         }
    197         double oStartT = coincidence.fTs[1][0];
    198         double oEndT = coincidence.fTs[1][1];
    199         if (oStartT == oEndT) {
    200             continue;
    201         }
    202         bool swapStart = startT > endT;
    203         bool swapOther = oStartT > oEndT;
    204         if (swapStart) {
    205             SkTSwap<double>(startT, endT);
    206             SkTSwap<double>(oStartT, oEndT);
    207         }
    208         bool cancel = swapOther != swapStart;
    209         int step = swapStart ? -1 : 1;
    210         int oStep = swapOther ? -1 : 1;
    211         double oMatchStart = cancel ? oEndT : oStartT;
    212         if (partial ? startT != 0 || oMatchStart != 0 : (startT == 0) != (oMatchStart == 0)) {
    213             bool added = false;
    214             if (oMatchStart != 0) {
    215                 added = thisOne.joinCoincidence(&other, oMatchStart, oStep, cancel);
    216             }
    217             if (!cancel && startT != 0 && !added) {
    218                 (void) other.joinCoincidence(&thisOne, startT, step, cancel);
    219             }
    220         }
    221         double oMatchEnd = cancel ? oStartT : oEndT;
    222         if (partial ? endT != 1 || oMatchEnd != 1 : (endT == 1) != (oMatchEnd == 1)) {
    223             bool added = false;
    224             if (cancel && endT != 1 && !added) {
    225                 (void) other.joinCoincidence(&thisOne, endT, -step, cancel);
    226             }
    227         }
    228     }
    229 }
    230 
    231 void SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence) {
    232     int thisIndex = coincidence.fSegments[0];
    233     SkOpSegment& thisOne = fSegments[thisIndex];
    234     if (thisOne.done()) {
    235         return;
    236     }
    237     SkOpContour* otherContour = coincidence.fOther;
    238     int otherIndex = coincidence.fSegments[1];
    239     SkOpSegment& other = otherContour->fSegments[otherIndex];
    240     if (other.done()) {
    241         return;
    242     }
    243     double startT = coincidence.fTs[0][0];
    244     double endT = coincidence.fTs[0][1];
    245     const SkPoint* startPt = &coincidence.fPts[0];
    246     const SkPoint* endPt = &coincidence.fPts[1];
    247     bool cancelers;
    248     if ((cancelers = startT > endT)) {
    249         SkTSwap<double>(startT, endT);
    250         SkTSwap<const SkPoint*>(startPt, endPt);
    251     }
    252     if (startT == endT) { // if span is very large, the smaller may have collapsed to nothing
    253         if (endT <= 1 - FLT_EPSILON) {
    254             endT += FLT_EPSILON;
    255             SkASSERT(endT <= 1);
    256         } else {
    257             startT -= FLT_EPSILON;
    258             SkASSERT(startT >= 0);
    259         }
    260     }
    261     SkASSERT(!approximately_negative(endT - startT));
    262     double oStartT = coincidence.fTs[1][0];
    263     double oEndT = coincidence.fTs[1][1];
    264     if (oStartT > oEndT) {
    265         SkTSwap<double>(oStartT, oEndT);
    266         cancelers ^= true;
    267     }
    268     SkASSERT(!approximately_negative(oEndT - oStartT));
    269     if (cancelers) {
    270         thisOne.addTCancel(*startPt, *endPt, &other);
    271     } else {
    272         thisOne.addTCoincident(*startPt, *endPt, endT, &other);
    273     }
    274 #if DEBUG_CONCIDENT
    275     thisOne.debugShowTs("p");
    276     other.debugShowTs("o");
    277 #endif
    278 }
    279 
    280 void SkOpContour::sortSegments() {
    281     int segmentCount = fSegments.count();
    282     fSortedSegments.push_back_n(segmentCount);
    283     for (int test = 0; test < segmentCount; ++test) {
    284         fSortedSegments[test] = &fSegments[test];
    285     }
    286     SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
    287     fFirstSorted = 0;
    288 }
    289 
    290 void SkOpContour::toPath(SkPathWriter* path) const {
    291     int segmentCount = fSegments.count();
    292     const SkPoint& pt = fSegments.front().pts()[0];
    293     path->deferredMove(pt);
    294     for (int test = 0; test < segmentCount; ++test) {
    295         fSegments[test].addCurveTo(0, 1, path, true);
    296     }
    297     path->close();
    298 }
    299 
    300 void SkOpContour::topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY,
    301         SkOpSegment** topStart) {
    302     int segmentCount = fSortedSegments.count();
    303     SkASSERT(segmentCount > 0);
    304     int sortedIndex = fFirstSorted;
    305     fDone = true;  // may be cleared below
    306     for ( ; sortedIndex < segmentCount; ++sortedIndex) {
    307         SkOpSegment* testSegment = fSortedSegments[sortedIndex];
    308         if (testSegment->done()) {
    309             if (sortedIndex == fFirstSorted) {
    310                 ++fFirstSorted;
    311             }
    312             continue;
    313         }
    314         fDone = false;
    315         SkPoint testXY = testSegment->activeLeftTop(true, NULL);
    316         if (*topStart) {
    317             if (testXY.fY < topLeft.fY) {
    318                 continue;
    319             }
    320             if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
    321                 continue;
    322             }
    323             if (bestXY->fY < testXY.fY) {
    324                 continue;
    325             }
    326             if (bestXY->fY == testXY.fY && bestXY->fX < testXY.fX) {
    327                 continue;
    328             }
    329         }
    330         *topStart = testSegment;
    331         *bestXY = testXY;
    332     }
    333 }
    334 
    335 SkOpSegment* SkOpContour::undoneSegment(int* start, int* end) {
    336     int segmentCount = fSegments.count();
    337     for (int test = 0; test < segmentCount; ++test) {
    338         SkOpSegment* testSegment = &fSegments[test];
    339         if (testSegment->done()) {
    340             continue;
    341         }
    342         testSegment->undoneSpan(start, end);
    343         return testSegment;
    344     }
    345     return NULL;
    346 }
    347 
    348 #if DEBUG_SHOW_WINDING
    349 int SkOpContour::debugShowWindingValues(int totalSegments, int ofInterest) {
    350     int count = fSegments.count();
    351     int sum = 0;
    352     for (int index = 0; index < count; ++index) {
    353         sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
    354     }
    355 //      SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
    356     return sum;
    357 }
    358 
    359 void SkOpContour::debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList) {
    360 //     int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
    361 //    int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
    362     int ofInterest = 1 << 5 | 1 << 8;
    363     int total = 0;
    364     int index;
    365     for (index = 0; index < contourList.count(); ++index) {
    366         total += contourList[index]->segments().count();
    367     }
    368     int sum = 0;
    369     for (index = 0; index < contourList.count(); ++index) {
    370         sum += contourList[index]->debugShowWindingValues(total, ofInterest);
    371     }
    372 //       SkDebugf("%s total=%d\n", __FUNCTION__, sum);
    373 }
    374 #endif
    375 
    376 void SkOpContour::setBounds() {
    377     int count = fSegments.count();
    378     if (count == 0) {
    379         SkDebugf("%s empty contour\n", __FUNCTION__);
    380         SkASSERT(0);
    381         // FIXME: delete empty contour?
    382         return;
    383     }
    384     fBounds = fSegments.front().bounds();
    385     for (int index = 1; index < count; ++index) {
    386         fBounds.add(fSegments[index].bounds());
    387     }
    388 }
    389