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 
     13 static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple) {
     14     bool unsortable = false;
     15     do {
     16         SkOpSpan* span = FindSortableTop(contourList);
     17         if (!span) {
     18             break;
     19         }
     20         SkOpSegment* current = span->segment();
     21         SkOpSpanBase* start = span->next();
     22         SkOpSpanBase* end = span;
     23         SkTDArray<SkOpSpanBase*> chase;
     24         do {
     25             if (current->activeWinding(start, end)) {
     26                 do {
     27                     if (!unsortable && current->done()) {
     28                           break;
     29                     }
     30                     SkASSERT(unsortable || !current->done());
     31                     SkOpSpanBase* nextStart = start;
     32                     SkOpSpanBase* nextEnd = end;
     33                     SkOpSegment* next = current->findNextWinding(&chase, &nextStart, &nextEnd,
     34                             &unsortable);
     35                     if (!next) {
     36                         break;
     37                     }
     38         #if DEBUG_FLOW
     39             SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
     40                     current->debugID(), start->pt().fX, start->pt().fY,
     41                     end->pt().fX, end->pt().fY);
     42         #endif
     43                     if (!current->addCurveTo(start, end, simple)) {
     44                         return false;
     45                     }
     46                     current = next;
     47                     start = nextStart;
     48                     end = nextEnd;
     49                 } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
     50                 if (current->activeWinding(start, end) && !simple->isClosed()) {
     51                     SkOpSpan* spanStart = start->starter(end);
     52                     if (!spanStart->done()) {
     53                         if (!current->addCurveTo(start, end, simple)) {
     54                             return false;
     55                         }
     56                         current->markDone(spanStart);
     57                     }
     58                 }
     59                 simple->finishContour();
     60             } else {
     61                 SkOpSpanBase* last = current->markAndChaseDone(start, end);
     62                 if (last && !last->chased()) {
     63                     last->setChased(true);
     64                     SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
     65                     *chase.append() = last;
     66 #if DEBUG_WINDING
     67                     SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
     68                     if (!last->final()) {
     69                          SkDebugf(" windSum=%d", last->upCast()->windSum());
     70                     }
     71                     SkDebugf("\n");
     72 #endif
     73                 }
     74             }
     75             current = FindChase(&chase, &start, &end);
     76             SkPathOpsDebug::ShowActiveSpans(contourList);
     77             if (!current) {
     78                 break;
     79             }
     80         } while (true);
     81     } while (true);
     82     return true;
     83 }
     84 
     85 // returns true if all edges were processed
     86 static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple) {
     87     bool unsortable = false;
     88     do {
     89         SkOpSpan* span = FindUndone(contourList);
     90         if (!span) {
     91             break;
     92         }
     93         SkOpSegment* current = span->segment();
     94         SkOpSpanBase* start = span->next();
     95         SkOpSpanBase* end = span;
     96         do {
     97             if (!unsortable && current->done()) {
     98                 break;
     99             }
    100             SkASSERT(unsortable || !current->done());
    101             SkOpSpanBase* nextStart = start;
    102             SkOpSpanBase* nextEnd = end;
    103             SkOpSegment* next = current->findNextXor(&nextStart, &nextEnd,
    104                     &unsortable);
    105             if (!next) {
    106                 break;
    107             }
    108         #if DEBUG_FLOW
    109             SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
    110                     current->debugID(), start->pt().fX, start->pt().fY,
    111                     end->pt().fX, end->pt().fY);
    112         #endif
    113             if (!current->addCurveTo(start, end, simple)) {
    114                 return false;
    115             }
    116             current = next;
    117             start = nextStart;
    118             end = nextEnd;
    119         } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
    120         if (!simple->isClosed()) {
    121             SkOpSpan* spanStart = start->starter(end);
    122             if (!spanStart->done()) {
    123                 if (!current->addCurveTo(start, end, simple)) {
    124                     return false;
    125                 }
    126                 current->markDone(spanStart);
    127             }
    128         }
    129         simple->finishContour();
    130         SkPathOpsDebug::ShowActiveSpans(contourList);
    131     } while (true);
    132     return true;
    133 }
    134 
    135 // FIXME : add this as a member of SkPath
    136 bool SimplifyDebug(const SkPath& path, SkPath* result
    137         SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)) {
    138     // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
    139     SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
    140             : SkPath::kEvenOdd_FillType;
    141     if (path.isConvex()) {
    142         if (result != &path) {
    143             *result = path;
    144         }
    145         result->setFillType(fillType);
    146         return true;
    147     }
    148     // turn path into list of segments
    149     char storage[4096];
    150     SkArenaAlloc allocator(storage);  // FIXME: constant-ize, tune
    151     SkOpContour contour;
    152     SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
    153     SkOpGlobalState globalState(contourList, &allocator
    154             SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName));
    155     SkOpCoincidence coincidence(&globalState);
    156 #if DEBUG_DUMP_VERIFY
    157 #ifndef SK_DEBUG
    158     const char* testName = "release";
    159 #endif
    160     if (SkPathOpsDebug::gDumpOp) {
    161         SkPathOpsDebug::DumpSimplify(path, testName);
    162     }
    163 #endif
    164     SkScalar scaleFactor = ScaleFactor(path);
    165     SkPath scaledPath;
    166     const SkPath* workingPath;
    167     if (scaleFactor > SK_Scalar1) {
    168         ScalePath(path, 1.f / scaleFactor, &scaledPath);
    169         workingPath = &scaledPath;
    170     } else {
    171         workingPath = &path;
    172     }
    173 #if DEBUG_SORT
    174     SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
    175 #endif
    176     SkOpEdgeBuilder builder(*workingPath, contourList, &globalState);
    177     if (!builder.finish()) {
    178         return false;
    179     }
    180 #if DEBUG_DUMP_SEGMENTS
    181     contour.dumpSegments();
    182 #endif
    183     if (!SortContourList(&contourList, false, false)) {
    184         result->reset();
    185         result->setFillType(fillType);
    186         return true;
    187     }
    188     // find all intersections between segments
    189     SkOpContour* current = contourList;
    190     do {
    191         SkOpContour* next = current;
    192         while (AddIntersectTs(current, next, &coincidence)
    193                 && (next = next->next()));
    194     } while ((current = current->next()));
    195 #if DEBUG_VALIDATE
    196     globalState.setPhase(SkOpPhase::kWalking);
    197 #endif
    198     bool success = HandleCoincidence(contourList, &coincidence);
    199 #if DEBUG_COIN
    200     globalState.debugAddToGlobalCoinDicts();
    201 #endif
    202     if (!success) {
    203         return false;
    204     }
    205 #if DEBUG_DUMP_ALIGNMENT
    206     contour.dumpSegments("aligned");
    207 #endif
    208     // construct closed contours
    209     result->reset();
    210     result->setFillType(fillType);
    211     SkPathWriter wrapper(*result);
    212     if (builder.xorMask() == kWinding_PathOpsMask ? !bridgeWinding(contourList, &wrapper)
    213             : !bridgeXor(contourList, &wrapper)) {
    214         return false;
    215     }
    216     wrapper.assemble();  // if some edges could not be resolved, assemble remaining
    217     if (scaleFactor > 1) {
    218         ScalePath(*result, scaleFactor, result);
    219     }
    220     return true;
    221 }
    222 
    223 bool Simplify(const SkPath& path, SkPath* result) {
    224 #if DEBUG_DUMP_VERIFY
    225     if (SkPathOpsDebug::gVerifyOp) {
    226         if (!SimplifyDebug(path, result  SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr))) {
    227             SkPathOpsDebug::ReportSimplifyFail(path);
    228             return false;
    229         }
    230         SkPathOpsDebug::VerifySimplify(path, *result);
    231         return true;
    232     }
    233 #endif
    234     return SimplifyDebug(path, result  SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr));
    235 }
    236