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 bool bridgeWinding(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* simple) {
     13     bool firstContour = true;
     14     bool unsortable = false;
     15     bool topUnsortable = false;
     16     SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
     17     do {
     18         int index, endIndex;
     19         bool topDone;
     20         SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kUnaryWinding, &firstContour,
     21                 &index, &endIndex, &topLeft, &topUnsortable, &topDone);
     22         if (!current) {
     23             if (topUnsortable || !topDone) {
     24                 topUnsortable = false;
     25                 SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
     26                 topLeft.fX = topLeft.fY = SK_ScalarMin;
     27                 continue;
     28             }
     29             break;
     30         }
     31         SkTDArray<SkOpSpan*> chaseArray;
     32         do {
     33             if (current->activeWinding(index, endIndex)) {
     34                 do {
     35                     if (!unsortable && current->done()) {
     36             #if DEBUG_ACTIVE_SPANS
     37                         DebugShowActiveSpans(contourList);
     38             #endif
     39                         if (simple->isEmpty()) {
     40                             simple->init();
     41                             break;
     42                         }
     43                     }
     44                     SkASSERT(unsortable || !current->done());
     45                     int nextStart = index;
     46                     int nextEnd = endIndex;
     47                     SkOpSegment* next = current->findNextWinding(&chaseArray, &nextStart, &nextEnd,
     48                             &unsortable);
     49                     if (!next) {
     50                         if (!unsortable && simple->hasMove()
     51                                 && current->verb() != SkPath::kLine_Verb
     52                                 && !simple->isClosed()) {
     53                             current->addCurveTo(index, endIndex, simple, true);
     54                             SkASSERT(simple->isClosed());
     55                         }
     56                         break;
     57                     }
     58         #if DEBUG_FLOW
     59             SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
     60                     current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
     61                     current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
     62         #endif
     63                     current->addCurveTo(index, endIndex, simple, true);
     64                     current = next;
     65                     index = nextStart;
     66                     endIndex = nextEnd;
     67                 } while (!simple->isClosed() && (!unsortable
     68                         || !current->done(SkMin32(index, endIndex))));
     69                 if (current->activeWinding(index, endIndex) && !simple->isClosed()) {
     70                     SkASSERT(unsortable || simple->isEmpty());
     71                     int min = SkMin32(index, endIndex);
     72                     if (!current->done(min)) {
     73                         current->addCurveTo(index, endIndex, simple, true);
     74                         current->markDoneUnary(min);
     75                     }
     76                 }
     77                 simple->close();
     78             } else {
     79                 SkOpSpan* last = current->markAndChaseDoneUnary(index, endIndex);
     80                 if (last && !last->fLoop) {
     81                     *chaseArray.append() = last;
     82                 }
     83             }
     84             current = FindChase(chaseArray, index, endIndex);
     85         #if DEBUG_ACTIVE_SPANS
     86             DebugShowActiveSpans(contourList);
     87         #endif
     88             if (!current) {
     89                 break;
     90             }
     91         } while (true);
     92     } while (true);
     93     return simple->someAssemblyRequired();
     94 }
     95 
     96 // returns true if all edges were processed
     97 static bool bridgeXor(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* simple) {
     98     SkOpSegment* current;
     99     int start, end;
    100     bool unsortable = false;
    101     bool closable = true;
    102     while ((current = FindUndone(contourList, &start, &end))) {
    103         do {
    104     #if DEBUG_ACTIVE_SPANS
    105             if (!unsortable && current->done()) {
    106                 DebugShowActiveSpans(contourList);
    107             }
    108     #endif
    109             SkASSERT(unsortable || !current->done());
    110             int nextStart = start;
    111             int nextEnd = end;
    112             SkOpSegment* next = current->findNextXor(&nextStart, &nextEnd, &unsortable);
    113             if (!next) {
    114                 if (!unsortable && simple->hasMove()
    115                         && current->verb() != SkPath::kLine_Verb
    116                         && !simple->isClosed()) {
    117                     current->addCurveTo(start, end, simple, true);
    118                     SkASSERT(simple->isClosed());
    119                 }
    120                 break;
    121             }
    122         #if DEBUG_FLOW
    123             SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
    124                     current->debugID(), current->xyAtT(start).fX, current->xyAtT(start).fY,
    125                     current->xyAtT(end).fX, current->xyAtT(end).fY);
    126         #endif
    127             current->addCurveTo(start, end, simple, true);
    128             current = next;
    129             start = nextStart;
    130             end = nextEnd;
    131         } while (!simple->isClosed() && (!unsortable || !current->done(SkMin32(start, end))));
    132         if (!simple->isClosed()) {
    133             SkASSERT(unsortable);
    134             int min = SkMin32(start, end);
    135             if (!current->done(min)) {
    136                 current->addCurveTo(start, end, simple, true);
    137                 current->markDone(min, 1);
    138             }
    139             closable = false;
    140         }
    141         simple->close();
    142     #if DEBUG_ACTIVE_SPANS
    143         DebugShowActiveSpans(contourList);
    144     #endif
    145     }
    146     return closable;
    147 }
    148 
    149 // FIXME : add this as a member of SkPath
    150 bool Simplify(const SkPath& path, SkPath* result) {
    151 #if DEBUG_SORT || DEBUG_SWAP_TOP
    152     SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
    153 #endif
    154     // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
    155     SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
    156             : SkPath::kEvenOdd_FillType;
    157 
    158     // turn path into list of segments
    159     SkTArray<SkOpContour> contours;
    160     SkOpEdgeBuilder builder(path, contours);
    161     if (!builder.finish()) {
    162         return false;
    163     }
    164     SkTArray<SkOpContour*, true> contourList;
    165     MakeContourList(contours, contourList, false, false);
    166     SkOpContour** currentPtr = contourList.begin();
    167     result->reset();
    168     result->setFillType(fillType);
    169     if (!currentPtr) {
    170         return true;
    171     }
    172     SkOpContour** listEnd = contourList.end();
    173     // find all intersections between segments
    174     do {
    175         SkOpContour** nextPtr = currentPtr;
    176         SkOpContour* current = *currentPtr++;
    177         if (current->containsCubics()) {
    178             AddSelfIntersectTs(current);
    179         }
    180         SkOpContour* next;
    181         do {
    182             next = *nextPtr++;
    183         } while (AddIntersectTs(current, next) && nextPtr != listEnd);
    184     } while (currentPtr != listEnd);
    185     HandleCoincidence(&contourList, 0);
    186     // construct closed contours
    187     SkPathWriter simple(*result);
    188     if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, &simple)
    189                 : !bridgeXor(contourList, &simple))
    190     {  // if some edges could not be resolved, assemble remaining fragments
    191         SkPath temp;
    192         temp.setFillType(fillType);
    193         SkPathWriter assembled(temp);
    194         Assemble(simple, &assembled);
    195         *result = *assembled.nativePath();
    196         result->setFillType(fillType);
    197     }
    198     return true;
    199 }
    200