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 |