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