Home | History | Annotate | Download | only in pathops
      1 /*
      2  * Copyright 2012 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 "SkAddIntersections.h"
      8 #include "SkOpCoincidence.h"
      9 #include "SkOpEdgeBuilder.h"
     10 #include "SkPathOpsCommon.h"
     11 #include "SkPathWriter.h"
     12 #include "SkTSort.h"
     13 
     14 SkScalar ScaleFactor(const SkPath& path) {
     15     static const SkScalar twoTo10 = 1024.f;
     16     SkScalar largest = 0;
     17     const SkScalar* oneBounds = &path.getBounds().fLeft;
     18     for (int index = 0; index < 4; ++index) {
     19         largest = SkTMax(largest, SkScalarAbs(oneBounds[index]));
     20     }
     21     SkScalar scale = twoTo10;
     22     SkScalar next;
     23     while ((next = scale * twoTo10) < largest) {
     24         scale = next;
     25     }
     26     return scale == twoTo10 ? SK_Scalar1 : scale;
     27 }
     28 
     29 void ScalePath(const SkPath& path, SkScalar scale, SkPath* scaled) {
     30     SkMatrix matrix;
     31     matrix.setScale(scale, scale);
     32     *scaled = path;
     33     scaled->transform(matrix);
     34 }
     35 
     36 const SkOpAngle* AngleWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* windingPtr,
     37         bool* sortablePtr) {
     38     // find first angle, initialize winding to computed fWindSum
     39     SkOpSegment* segment = start->segment();
     40     const SkOpAngle* angle = segment->spanToAngle(start, end);
     41     if (!angle) {
     42         *windingPtr = SK_MinS32;
     43         return nullptr;
     44     }
     45     bool computeWinding = false;
     46     const SkOpAngle* firstAngle = angle;
     47     bool loop = false;
     48     bool unorderable = false;
     49     int winding = SK_MinS32;
     50     do {
     51         angle = angle->next();
     52         if (!angle) {
     53             return nullptr;
     54         }
     55         unorderable |= angle->unorderable();
     56         if ((computeWinding = unorderable || (angle == firstAngle && loop))) {
     57             break;    // if we get here, there's no winding, loop is unorderable
     58         }
     59         loop |= angle == firstAngle;
     60         segment = angle->segment();
     61         winding = segment->windSum(angle);
     62     } while (winding == SK_MinS32);
     63     // if the angle loop contains an unorderable span, the angle order may be useless
     64     // directly compute the winding in this case for each span
     65     if (computeWinding) {
     66         firstAngle = angle;
     67         winding = SK_MinS32;
     68         do {
     69             SkOpSpanBase* startSpan = angle->start();
     70             SkOpSpanBase* endSpan = angle->end();
     71             SkOpSpan* lesser = startSpan->starter(endSpan);
     72             int testWinding = lesser->windSum();
     73             if (testWinding == SK_MinS32) {
     74                 testWinding = lesser->computeWindSum();
     75             }
     76             if (testWinding != SK_MinS32) {
     77                 segment = angle->segment();
     78                 winding = testWinding;
     79             }
     80             angle = angle->next();
     81         } while (angle != firstAngle);
     82     }
     83     *sortablePtr = !unorderable;
     84     *windingPtr = winding;
     85     return angle;
     86 }
     87 
     88 SkOpSpan* FindUndone(SkOpContourHead* contourHead) {
     89     SkOpContour* contour = contourHead;
     90     do {
     91         if (contour->done()) {
     92             continue;
     93         }
     94         SkOpSpan* result = contour->undoneSpan();
     95         if (result) {
     96             return result;
     97         }
     98     } while ((contour = contour->next()));
     99     return nullptr;
    100 }
    101 
    102 SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
    103         SkOpSpanBase** endPtr) {
    104     while (chase->count()) {
    105         SkOpSpanBase* span;
    106         chase->pop(&span);
    107         SkOpSegment* segment = span->segment();
    108         *startPtr = span->ptT()->next()->span();
    109         bool done = true;
    110         *endPtr = nullptr;
    111         if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) {
    112             *startPtr = last->start();
    113             *endPtr = last->end();
    114     #if TRY_ROTATE
    115             *chase->insert(0) = span;
    116     #else
    117             *chase->append() = span;
    118     #endif
    119             return last->segment();
    120         }
    121         if (done) {
    122             continue;
    123         }
    124         // find first angle, initialize winding to computed wind sum
    125         int winding;
    126         bool sortable;
    127         const SkOpAngle* angle = AngleWinding(*startPtr, *endPtr, &winding, &sortable);
    128         if (!angle) {
    129             return nullptr;
    130         }
    131         if (winding == SK_MinS32) {
    132             continue;
    133         }
    134         int sumWinding SK_INIT_TO_AVOID_WARNING;
    135         if (sortable) {
    136             segment = angle->segment();
    137             sumWinding = segment->updateWindingReverse(angle);
    138         }
    139         SkOpSegment* first = nullptr;
    140         const SkOpAngle* firstAngle = angle;
    141         while ((angle = angle->next()) != firstAngle) {
    142             segment = angle->segment();
    143             SkOpSpanBase* start = angle->start();
    144             SkOpSpanBase* end = angle->end();
    145             int maxWinding SK_INIT_TO_AVOID_WARNING;
    146             if (sortable) {
    147                 segment->setUpWinding(start, end, &maxWinding, &sumWinding);
    148             }
    149             if (!segment->done(angle)) {
    150                 if (!first && (sortable || start->starter(end)->windSum() != SK_MinS32)) {
    151                     first = segment;
    152                     *startPtr = start;
    153                     *endPtr = end;
    154                 }
    155                 // OPTIMIZATION: should this also add to the chase?
    156                 if (sortable) {
    157                     (void) segment->markAngle(maxWinding, sumWinding, angle);
    158                 }
    159             }
    160         }
    161         if (first) {
    162        #if TRY_ROTATE
    163             *chase->insert(0) = span;
    164        #else
    165             *chase->append() = span;
    166        #endif
    167             return first;
    168         }
    169     }
    170     return nullptr;
    171 }
    172 
    173 bool SortContourList(SkOpContourHead** contourList, bool evenOdd, bool oppEvenOdd) {
    174     SkTDArray<SkOpContour* > list;
    175     SkOpContour* contour = *contourList;
    176     do {
    177         if (contour->count()) {
    178             contour->setOppXor(contour->operand() ? evenOdd : oppEvenOdd);
    179             *list.append() = contour;
    180         }
    181     } while ((contour = contour->next()));
    182     int count = list.count();
    183     if (!count) {
    184         return false;
    185     }
    186     if (count > 1) {
    187         SkTQSort<SkOpContour>(list.begin(), list.end() - 1);
    188     }
    189     contour = list[0];
    190     SkOpContourHead* contourHead = static_cast<SkOpContourHead*>(contour);
    191     contour->globalState()->setContourHead(contourHead);
    192     *contourList = contourHead;
    193     for (int index = 1; index < count; ++index) {
    194         SkOpContour* next = list[index];
    195         contour->setNext(next);
    196         contour = next;
    197     }
    198     contour->setNext(nullptr);
    199     return true;
    200 }
    201 
    202 static void calc_angles(SkOpContourHead* contourList  DEBUG_COIN_DECLARE_PARAMS()) {
    203     DEBUG_STATIC_SET_PHASE(contourList);
    204     SkOpContour* contour = contourList;
    205     do {
    206         contour->calcAngles();
    207     } while ((contour = contour->next()));
    208 }
    209 
    210 static bool missing_coincidence(SkOpContourHead* contourList  DEBUG_COIN_DECLARE_PARAMS()) {
    211     DEBUG_STATIC_SET_PHASE(contourList);
    212     SkOpContour* contour = contourList;
    213     bool result = false;
    214     do {
    215         result |= contour->missingCoincidence();
    216     } while ((contour = contour->next()));
    217     return result;
    218 }
    219 
    220 static bool move_multiples(SkOpContourHead* contourList  DEBUG_COIN_DECLARE_PARAMS()) {
    221     DEBUG_STATIC_SET_PHASE(contourList);
    222     SkOpContour* contour = contourList;
    223     do {
    224         if (!contour->moveMultiples()) {
    225             return false;
    226         }
    227     } while ((contour = contour->next()));
    228     return true;
    229 }
    230 
    231 static bool move_nearby(SkOpContourHead* contourList  DEBUG_COIN_DECLARE_PARAMS()) {
    232     DEBUG_STATIC_SET_PHASE(contourList);
    233     SkOpContour* contour = contourList;
    234     do {
    235         if (!contour->moveNearby()) {
    236             return false;
    237         }
    238     } while ((contour = contour->next()));
    239     return true;
    240 }
    241 
    242 static bool sort_angles(SkOpContourHead* contourList) {
    243     SkOpContour* contour = contourList;
    244     do {
    245         if (!contour->sortAngles()) {
    246             return false;
    247         }
    248     } while ((contour = contour->next()));
    249     return true;
    250 }
    251 
    252 bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidence) {
    253     SkOpGlobalState* globalState = contourList->globalState();
    254     // match up points within the coincident runs
    255     if (!coincidence->addExpanded(DEBUG_PHASE_ONLY_PARAMS(kIntersecting))) {
    256         return false;
    257     }
    258     // combine t values when multiple intersections occur on some segments but not others
    259     if (!move_multiples(contourList  DEBUG_PHASE_PARAMS(kWalking))) {
    260         return false;
    261     }
    262     // move t values and points together to eliminate small/tiny gaps
    263     if (!move_nearby(contourList  DEBUG_COIN_PARAMS())) {
    264         return false;
    265     }
    266     // add coincidence formed by pairing on curve points and endpoints
    267     coincidence->correctEnds(DEBUG_PHASE_ONLY_PARAMS(kIntersecting));
    268     if (!coincidence->addEndMovedSpans(DEBUG_COIN_ONLY_PARAMS())) {
    269         return false;
    270     }
    271     const int SAFETY_COUNT = 3;
    272     int safetyHatch = SAFETY_COUNT;
    273     // look for coincidence present in A-B and A-C but missing in B-C
    274     do {
    275         bool added;
    276         if (!coincidence->addMissing(&added  DEBUG_ITER_PARAMS(SAFETY_COUNT - safetyHatch))) {
    277             return false;
    278         }
    279         if (!added) {
    280             break;
    281         }
    282         if (!--safetyHatch) {
    283             SkASSERT(globalState->debugSkipAssert());
    284             return false;
    285         }
    286         move_nearby(contourList  DEBUG_ITER_PARAMS(SAFETY_COUNT - safetyHatch - 1));
    287     } while (true);
    288     // check to see if, loosely, coincident ranges may be expanded
    289     if (coincidence->expand(DEBUG_COIN_ONLY_PARAMS())) {
    290         bool added;
    291         if (!coincidence->addMissing(&added  DEBUG_COIN_PARAMS())) {
    292             return false;
    293         }
    294         if (!coincidence->addExpanded(DEBUG_COIN_ONLY_PARAMS())) {
    295             return false;
    296         }
    297         if (!move_multiples(contourList  DEBUG_COIN_PARAMS())) {
    298             return false;
    299         }
    300         move_nearby(contourList  DEBUG_COIN_PARAMS());
    301     }
    302     // the expanded ranges may not align -- add the missing spans
    303     if (!coincidence->addExpanded(DEBUG_PHASE_ONLY_PARAMS(kWalking))) {
    304         return false;
    305     }
    306     // mark spans of coincident segments as coincident
    307     coincidence->mark(DEBUG_COIN_ONLY_PARAMS());
    308     // look for coincidence lines and curves undetected by intersection
    309     if (missing_coincidence(contourList  DEBUG_COIN_PARAMS())) {
    310         (void) coincidence->expand(DEBUG_PHASE_ONLY_PARAMS(kIntersecting));
    311         if (!coincidence->addExpanded(DEBUG_COIN_ONLY_PARAMS())) {
    312             return false;
    313         }
    314         if (!coincidence->mark(DEBUG_PHASE_ONLY_PARAMS(kWalking))) {
    315             return false;
    316         }
    317     } else {
    318         (void) coincidence->expand(DEBUG_COIN_ONLY_PARAMS());
    319     }
    320     (void) coincidence->expand(DEBUG_COIN_ONLY_PARAMS());
    321 
    322     SkOpCoincidence overlaps(globalState);
    323     safetyHatch = SAFETY_COUNT;
    324     do {
    325         SkOpCoincidence* pairs = overlaps.isEmpty() ? coincidence : &overlaps;
    326         // adjust the winding value to account for coincident edges
    327         if (!pairs->apply(DEBUG_ITER_ONLY_PARAMS(SAFETY_COUNT - safetyHatch))) {
    328             return false;
    329         }
    330         // For each coincident pair that overlaps another, when the receivers (the 1st of the pair)
    331         // are different, construct a new pair to resolve their mutual span
    332         if (!pairs->findOverlaps(&overlaps  DEBUG_ITER_PARAMS(SAFETY_COUNT - safetyHatch))) {
    333             return false;
    334         }
    335         if (!--safetyHatch) {
    336             SkASSERT(globalState->debugSkipAssert());
    337             return false;
    338         }
    339     } while (!overlaps.isEmpty());
    340     calc_angles(contourList  DEBUG_COIN_PARAMS());
    341     if (!sort_angles(contourList)) {
    342         return false;
    343     }
    344 #if DEBUG_COINCIDENCE_VERBOSE
    345     coincidence->debugShowCoincidence();
    346 #endif
    347 #if DEBUG_COINCIDENCE
    348     coincidence->debugValidate();
    349 #endif
    350     SkPathOpsDebug::ShowActiveSpans(contourList);
    351     return true;
    352 }
    353