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 "SkOpEdgeBuilder.h"
      9 #include "SkPathOpsCommon.h"
     10 #include "SkPathWriter.h"
     11 
     12 // FIXME: this and find chase should be merge together, along with
     13 // other code that walks winding in angles
     14 // OPTIMIZATION: Probably, the walked winding should be rolled into the angle structure
     15 // so it isn't duplicated by walkers like this one
     16 static SkOpSegment* findChaseOp(SkTDArray<SkOpSpan*>& chase, int& nextStart, int& nextEnd) {
     17     while (chase.count()) {
     18         SkOpSpan* span;
     19         chase.pop(&span);
     20         const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
     21         SkOpSegment* segment = backPtr.fOther;
     22         nextStart = backPtr.fOtherIndex;
     23         SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles;
     24         int done = 0;
     25         if (segment->activeAngle(nextStart, &done, &angles)) {
     26             SkOpAngle* last = angles.end() - 1;
     27             nextStart = last->start();
     28             nextEnd = last->end();
     29    #if TRY_ROTATE
     30             *chase.insert(0) = span;
     31    #else
     32             *chase.append() = span;
     33    #endif
     34             return last->segment();
     35         }
     36         if (done == angles.count()) {
     37             continue;
     38         }
     39         SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted;
     40         bool sortable = SkOpSegment::SortAngles(angles, &sorted,
     41                 SkOpSegment::kMayBeUnordered_SortAngleKind);
     42         int angleCount = sorted.count();
     43 #if DEBUG_SORT
     44         sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, sortable);
     45 #endif
     46         if (!sortable) {
     47             continue;
     48         }
     49         // find first angle, initialize winding to computed fWindSum
     50         int firstIndex = -1;
     51         const SkOpAngle* angle;
     52         do {
     53             angle = sorted[++firstIndex];
     54             segment = angle->segment();
     55         } while (segment->windSum(angle) == SK_MinS32);
     56     #if DEBUG_SORT
     57         segment->debugShowSort(__FUNCTION__, sorted, firstIndex, sortable);
     58     #endif
     59         int sumMiWinding = segment->updateWindingReverse(angle);
     60         int sumSuWinding = segment->updateOppWindingReverse(angle);
     61         if (segment->operand()) {
     62             SkTSwap<int>(sumMiWinding, sumSuWinding);
     63         }
     64         int nextIndex = firstIndex + 1;
     65         int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
     66         SkOpSegment* first = NULL;
     67         do {
     68             SkASSERT(nextIndex != firstIndex);
     69             if (nextIndex == angleCount) {
     70                 nextIndex = 0;
     71             }
     72             angle = sorted[nextIndex];
     73             segment = angle->segment();
     74             int start = angle->start();
     75             int end = angle->end();
     76             int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
     77             segment->setUpWindings(start, end, &sumMiWinding, &sumSuWinding,
     78                     &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
     79             if (!segment->done(angle)) {
     80                 if (!first) {
     81                     first = segment;
     82                     nextStart = start;
     83                     nextEnd = end;
     84                 }
     85                 (void) segment->markAngle(maxWinding, sumWinding, oppMaxWinding,
     86                     oppSumWinding, true, angle);
     87             }
     88         } while (++nextIndex != lastIndex);
     89         if (first) {
     90        #if TRY_ROTATE
     91             *chase.insert(0) = span;
     92        #else
     93             *chase.append() = span;
     94        #endif
     95             return first;
     96         }
     97     }
     98     return NULL;
     99 }
    100 
    101 /*
    102 static bool windingIsActive(int winding, int oppWinding, int spanWinding, int oppSpanWinding,
    103         bool windingIsOp, PathOp op) {
    104     bool active = windingIsActive(winding, spanWinding);
    105     if (!active) {
    106         return false;
    107     }
    108     if (oppSpanWinding && windingIsActive(oppWinding, oppSpanWinding)) {
    109         switch (op) {
    110             case kIntersect_Op:
    111             case kUnion_Op:
    112                 return true;
    113             case kDifference_Op: {
    114                 int absSpan = abs(spanWinding);
    115                 int absOpp = abs(oppSpanWinding);
    116                 return windingIsOp ? absSpan < absOpp : absSpan > absOpp;
    117             }
    118             case kXor_Op:
    119                 return spanWinding != oppSpanWinding;
    120             default:
    121                 SkASSERT(0);
    122         }
    123     }
    124     bool opActive = oppWinding != 0;
    125     return gOpLookup[op][opActive][windingIsOp];
    126 }
    127 */
    128 
    129 static bool bridgeOp(SkTArray<SkOpContour*, true>& contourList, const SkPathOp op,
    130         const int xorMask, const int xorOpMask, SkPathWriter* simple) {
    131     bool firstContour = true;
    132     bool unsortable = false;
    133     bool topUnsortable = false;
    134     SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
    135     do {
    136         int index, endIndex;
    137         bool done;
    138         SkOpSegment* current = FindSortableTop(contourList, &firstContour, &index, &endIndex,
    139                 &topLeft, &topUnsortable, &done, true);
    140         if (!current) {
    141             if (topUnsortable || !done) {
    142                 topUnsortable = false;
    143                 SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
    144                 topLeft.fX = topLeft.fY = SK_ScalarMin;
    145                 continue;
    146             }
    147             break;
    148         }
    149         SkTDArray<SkOpSpan*> chaseArray;
    150         do {
    151             if (current->activeOp(index, endIndex, xorMask, xorOpMask, op)) {
    152                 do {
    153                     if (!unsortable && current->done()) {
    154             #if DEBUG_ACTIVE_SPANS
    155                         DebugShowActiveSpans(contourList);
    156             #endif
    157                         if (simple->isEmpty()) {
    158                             simple->init();
    159                             break;
    160                         }
    161                     }
    162                     SkASSERT(unsortable || !current->done());
    163                     int nextStart = index;
    164                     int nextEnd = endIndex;
    165                     SkOpSegment* next = current->findNextOp(&chaseArray, &nextStart, &nextEnd,
    166                             &unsortable, op, xorMask, xorOpMask);
    167                     if (!next) {
    168                         if (!unsortable && simple->hasMove()
    169                                 && current->verb() != SkPath::kLine_Verb
    170                                 && !simple->isClosed()) {
    171                             current->addCurveTo(index, endIndex, simple, true);
    172                             SkASSERT(simple->isClosed());
    173                         }
    174                         break;
    175                     }
    176         #if DEBUG_FLOW
    177             SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
    178                     current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
    179                     current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
    180         #endif
    181                     current->addCurveTo(index, endIndex, simple, true);
    182                     current = next;
    183                     index = nextStart;
    184                     endIndex = nextEnd;
    185                 } while (!simple->isClosed() && (!unsortable
    186                         || !current->done(SkMin32(index, endIndex))));
    187                 if (current->activeWinding(index, endIndex) && !simple->isClosed()) {
    188                     SkASSERT(unsortable || simple->isEmpty());
    189                     int min = SkMin32(index, endIndex);
    190                     if (!current->done(min)) {
    191                         current->addCurveTo(index, endIndex, simple, true);
    192                         current->markDoneBinary(min);
    193                     }
    194                 }
    195                 simple->close();
    196             } else {
    197                 SkOpSpan* last = current->markAndChaseDoneBinary(index, endIndex);
    198                 if (last && !last->fLoop) {
    199                     *chaseArray.append() = last;
    200                 }
    201             }
    202             current = findChaseOp(chaseArray, index, endIndex);
    203         #if DEBUG_ACTIVE_SPANS
    204             DebugShowActiveSpans(contourList);
    205         #endif
    206             if (!current) {
    207                 break;
    208             }
    209         } while (true);
    210     } while (true);
    211     return simple->someAssemblyRequired();
    212 }
    213 
    214 // pretty picture:
    215 // https://docs.google.com/a/google.com/drawings/d/1sPV8rPfpEFXymBp3iSbDRWAycp1b-7vD9JP2V-kn9Ss/edit?usp=sharing
    216 static const SkPathOp gOpInverse[kReverseDifference_PathOp + 1][2][2] = {
    217 //                  inside minuend                               outside minuend
    218 //     inside subtrahend     outside subtrahend      inside subtrahend     outside subtrahend
    219     {{ kDifference_PathOp,    kIntersect_PathOp }, { kUnion_PathOp, kReverseDifference_PathOp }},
    220     {{ kIntersect_PathOp,    kDifference_PathOp }, { kReverseDifference_PathOp, kUnion_PathOp }},
    221     {{ kUnion_PathOp, kReverseDifference_PathOp }, { kDifference_PathOp,    kIntersect_PathOp }},
    222     {{ kXOR_PathOp,                 kXOR_PathOp }, { kXOR_PathOp,                 kXOR_PathOp }},
    223     {{ kReverseDifference_PathOp, kUnion_PathOp }, { kIntersect_PathOp,    kDifference_PathOp }},
    224 };
    225 
    226 static const bool gOutInverse[kReverseDifference_PathOp + 1][2][2] = {
    227     {{ false, false }, { true, false }},  // diff
    228     {{ false, false }, { false, true }},  // sect
    229     {{ false, true }, { true, true }},    // union
    230     {{ false, true }, { true, false }},   // xor
    231     {{ false, true }, { false, false }},  // rev diff
    232 };
    233 
    234 bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
    235 #if DEBUG_SHOW_TEST_NAME
    236     char* debugName = DEBUG_FILENAME_STRING;
    237     if (debugName && debugName[0]) {
    238         DebugBumpTestName(debugName);
    239         DebugShowPath(one, two, op, debugName);
    240     }
    241 #endif
    242     op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()];
    243     SkPath::FillType fillType = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()]
    244             ? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType;
    245     const SkPath* minuend = &one;
    246     const SkPath* subtrahend = &two;
    247     if (op == kReverseDifference_PathOp) {
    248         minuend = &two;
    249         subtrahend = &one;
    250         op = kDifference_PathOp;
    251     }
    252 #if DEBUG_SORT || DEBUG_SWAP_TOP
    253     gDebugSortCount = gDebugSortCountDefault;
    254 #endif
    255     // turn path into list of segments
    256     SkTArray<SkOpContour> contours;
    257     // FIXME: add self-intersecting cubics' T values to segment
    258     SkOpEdgeBuilder builder(*minuend, contours);
    259     const int xorMask = builder.xorMask();
    260     builder.addOperand(*subtrahend);
    261     if (!builder.finish()) {
    262         return false;
    263     }
    264     result->reset();
    265     result->setFillType(fillType);
    266     const int xorOpMask = builder.xorMask();
    267     SkTArray<SkOpContour*, true> contourList;
    268     MakeContourList(contours, contourList, xorMask == kEvenOdd_PathOpsMask,
    269             xorOpMask == kEvenOdd_PathOpsMask);
    270     SkOpContour** currentPtr = contourList.begin();
    271     if (!currentPtr) {
    272         return true;
    273     }
    274     SkOpContour** listEnd = contourList.end();
    275     // find all intersections between segments
    276     do {
    277         SkOpContour** nextPtr = currentPtr;
    278         SkOpContour* current = *currentPtr++;
    279         if (current->containsCubics()) {
    280             AddSelfIntersectTs(current);
    281         }
    282         SkOpContour* next;
    283         do {
    284             next = *nextPtr++;
    285         } while (AddIntersectTs(current, next) && nextPtr != listEnd);
    286     } while (currentPtr != listEnd);
    287     // eat through coincident edges
    288 
    289     int total = 0;
    290     int index;
    291     for (index = 0; index < contourList.count(); ++index) {
    292         total += contourList[index]->segments().count();
    293     }
    294 #if DEBUG_SHOW_WINDING
    295     SkOpContour::debugShowWindingValues(contourList);
    296 #endif
    297     CoincidenceCheck(&contourList, total);
    298 #if DEBUG_SHOW_WINDING
    299     SkOpContour::debugShowWindingValues(contourList);
    300 #endif
    301     FixOtherTIndex(&contourList);
    302     CheckEnds(&contourList);
    303     SortSegments(&contourList);
    304 #if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
    305     DebugShowActiveSpans(contourList);
    306 #endif
    307     // construct closed contours
    308     SkPathWriter wrapper(*result);
    309     bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper);
    310     {  // if some edges could not be resolved, assemble remaining fragments
    311         SkPath temp;
    312         temp.setFillType(fillType);
    313         SkPathWriter assembled(temp);
    314         Assemble(wrapper, &assembled);
    315         *result = *assembled.nativePath();
    316         result->setFillType(fillType);
    317     }
    318     return true;
    319 }
    320