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 SkSTArenaAlloc<4096> allocator; // FIXME: constant-ize, tune 150 SkOpContour contour; 151 SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour); 152 SkOpGlobalState globalState(contourList, &allocator 153 SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName)); 154 SkOpCoincidence coincidence(&globalState); 155 #if DEBUG_DUMP_VERIFY 156 #ifndef SK_DEBUG 157 const char* testName = "release"; 158 #endif 159 if (SkPathOpsDebug::gDumpOp) { 160 SkPathOpsDebug::DumpSimplify(path, testName); 161 } 162 #endif 163 SkScalar scaleFactor = ScaleFactor(path); 164 SkPath scaledPath; 165 const SkPath* workingPath; 166 if (scaleFactor > SK_Scalar1) { 167 ScalePath(path, 1.f / scaleFactor, &scaledPath); 168 workingPath = &scaledPath; 169 } else { 170 workingPath = &path; 171 } 172 #if DEBUG_SORT 173 SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault; 174 #endif 175 SkOpEdgeBuilder builder(*workingPath, contourList, &globalState); 176 if (!builder.finish()) { 177 return false; 178 } 179 #if DEBUG_DUMP_SEGMENTS 180 contour.dumpSegments(); 181 #endif 182 if (!SortContourList(&contourList, false, false)) { 183 result->reset(); 184 result->setFillType(fillType); 185 return true; 186 } 187 // find all intersections between segments 188 SkOpContour* current = contourList; 189 do { 190 SkOpContour* next = current; 191 while (AddIntersectTs(current, next, &coincidence) 192 && (next = next->next())); 193 } while ((current = current->next())); 194 #if DEBUG_VALIDATE 195 globalState.setPhase(SkOpPhase::kWalking); 196 #endif 197 bool success = HandleCoincidence(contourList, &coincidence); 198 #if DEBUG_COIN 199 globalState.debugAddToGlobalCoinDicts(); 200 #endif 201 if (!success) { 202 return false; 203 } 204 #if DEBUG_DUMP_ALIGNMENT 205 contour.dumpSegments("aligned"); 206 #endif 207 // construct closed contours 208 result->reset(); 209 result->setFillType(fillType); 210 SkPathWriter wrapper(*result); 211 if (builder.xorMask() == kWinding_PathOpsMask ? !bridgeWinding(contourList, &wrapper) 212 : !bridgeXor(contourList, &wrapper)) { 213 return false; 214 } 215 wrapper.assemble(); // if some edges could not be resolved, assemble remaining 216 if (scaleFactor > 1) { 217 ScalePath(*result, scaleFactor, result); 218 } 219 return true; 220 } 221 222 bool Simplify(const SkPath& path, SkPath* result) { 223 #if DEBUG_DUMP_VERIFY 224 if (SkPathOpsDebug::gVerifyOp) { 225 if (!SimplifyDebug(path, result SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr))) { 226 SkPathOpsDebug::ReportSimplifyFail(path); 227 return false; 228 } 229 SkPathOpsDebug::VerifySimplify(path, *result); 230 return true; 231 } 232 #endif 233 return SimplifyDebug(path, result SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr)); 234 } 235