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