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