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 "src/pathops/SkAddIntersections.h" |
8 | #include "src/pathops/SkOpCoincidence.h" |
9 | #include "src/pathops/SkOpEdgeBuilder.h" |
10 | #include "src/pathops/SkPathOpsCommon.h" |
11 | #include "src/pathops/SkPathWriter.h" |
12 | |
13 | #include <utility> |
14 | |
15 | static bool findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase** startPtr, |
16 | SkOpSpanBase** endPtr, SkOpSegment** result) { |
17 | while (chase.count()) { |
18 | SkOpSpanBase* span; |
19 | chase.pop(&span); |
20 | // OPTIMIZE: prev makes this compatible with old code -- but is it necessary? |
21 | *startPtr = span->ptT()->prev()->span(); |
22 | SkOpSegment* segment = (*startPtr)->segment(); |
23 | bool done = true; |
24 | *endPtr = nullptr; |
25 | if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) { |
26 | *startPtr = last->start(); |
27 | *endPtr = last->end(); |
28 | #if TRY_ROTATE |
29 | *chase.insert(0) = span; |
30 | #else |
31 | *chase.append() = span; |
32 | #endif |
33 | *result = last->segment(); |
34 | return true; |
35 | } |
36 | if (done) { |
37 | continue; |
38 | } |
39 | int winding; |
40 | bool sortable; |
41 | const SkOpAngle* angle = AngleWinding(*startPtr, *endPtr, &winding, &sortable); |
42 | if (!angle) { |
43 | *result = nullptr; |
44 | return true; |
45 | } |
46 | if (winding == SK_MinS32) { |
47 | continue; |
48 | } |
49 | int sumMiWinding, sumSuWinding; |
50 | if (sortable) { |
51 | segment = angle->segment(); |
52 | sumMiWinding = segment->updateWindingReverse(angle); |
53 | if (sumMiWinding == SK_MinS32) { |
54 | SkASSERT(segment->globalState()->debugSkipAssert()); |
55 | *result = nullptr; |
56 | return true; |
57 | } |
58 | sumSuWinding = segment->updateOppWindingReverse(angle); |
59 | if (sumSuWinding == SK_MinS32) { |
60 | SkASSERT(segment->globalState()->debugSkipAssert()); |
61 | *result = nullptr; |
62 | return true; |
63 | } |
64 | if (segment->operand()) { |
65 | using std::swap; |
66 | swap(sumMiWinding, sumSuWinding); |
67 | } |
68 | } |
69 | SkOpSegment* first = nullptr; |
70 | const SkOpAngle* firstAngle = angle; |
71 | while ((angle = angle->next()) != firstAngle) { |
72 | segment = angle->segment(); |
73 | SkOpSpanBase* start = angle->start(); |
74 | SkOpSpanBase* end = angle->end(); |
75 | int maxWinding = 0, sumWinding = 0, oppMaxWinding = 0, oppSumWinding = 0; |
76 | if (sortable) { |
77 | segment->setUpWindings(start, end, &sumMiWinding, &sumSuWinding, |
78 | &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding); |
79 | } |
80 | if (!segment->done(angle)) { |
81 | if (!first && (sortable || start->starter(end)->windSum() != SK_MinS32)) { |
82 | first = segment; |
83 | *startPtr = start; |
84 | *endPtr = end; |
85 | } |
86 | // OPTIMIZATION: should this also add to the chase? |
87 | if (sortable) { |
88 | if (!segment->markAngle(maxWinding, sumWinding, oppMaxWinding, |
89 | oppSumWinding, angle, nullptr)) { |
90 | return false; |
91 | } |
92 | } |
93 | } |
94 | } |
95 | if (first) { |
96 | #if TRY_ROTATE |
97 | *chase.insert(0) = span; |
98 | #else |
99 | *chase.append() = span; |
100 | #endif |
101 | *result = first; |
102 | return true; |
103 | } |
104 | } |
105 | *result = nullptr; |
106 | return true; |
107 | } |
108 | |
109 | static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, |
110 | const int xorMask, const int xorOpMask, SkPathWriter* writer) { |
111 | bool unsortable = false; |
112 | bool lastSimple = false; |
113 | bool simple = false; |
114 | do { |
115 | SkOpSpan* span = FindSortableTop(contourList); |
116 | if (!span) { |
117 | break; |
118 | } |
119 | SkOpSegment* current = span->segment(); |
120 | SkOpSpanBase* start = span->next(); |
121 | SkOpSpanBase* end = span; |
122 | SkTDArray<SkOpSpanBase*> chase; |
123 | do { |
124 | if (current->activeOp(start, end, xorMask, xorOpMask, op)) { |
125 | do { |
126 | if (!unsortable && current->done()) { |
127 | break; |
128 | } |
129 | SkASSERT(unsortable || !current->done()); |
130 | SkOpSpanBase* nextStart = start; |
131 | SkOpSpanBase* nextEnd = end; |
132 | lastSimple = simple; |
133 | SkOpSegment* next = current->findNextOp(&chase, &nextStart, &nextEnd, |
134 | &unsortable, &simple, op, xorMask, xorOpMask); |
135 | if (!next) { |
136 | if (!unsortable && writer->hasMove() |
137 | && current->verb() != SkPath::kLine_Verb |
138 | && !writer->isClosed()) { |
139 | if (!current->addCurveTo(start, end, writer)) { |
140 | return false; |
141 | } |
142 | if (!writer->isClosed()) { |
143 | SkPathOpsDebug::ShowActiveSpans(contourList); |
144 | } |
145 | } else if (lastSimple) { |
146 | if (!current->addCurveTo(start, end, writer)) { |
147 | return false; |
148 | } |
149 | } |
150 | break; |
151 | } |
152 | #if DEBUG_FLOW |
153 | SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n" , __FUNCTION__, |
154 | current->debugID(), start->pt().fX, start->pt().fY, |
155 | end->pt().fX, end->pt().fY); |
156 | #endif |
157 | if (!current->addCurveTo(start, end, writer)) { |
158 | return false; |
159 | } |
160 | current = next; |
161 | start = nextStart; |
162 | end = nextEnd; |
163 | } while (!writer->isClosed() && (!unsortable || !start->starter(end)->done())); |
164 | if (current->activeWinding(start, end) && !writer->isClosed()) { |
165 | SkOpSpan* spanStart = start->starter(end); |
166 | if (!spanStart->done()) { |
167 | if (!current->addCurveTo(start, end, writer)) { |
168 | return false; |
169 | } |
170 | current->markDone(spanStart); |
171 | } |
172 | } |
173 | writer->finishContour(); |
174 | } else { |
175 | SkOpSpanBase* last; |
176 | if (!current->markAndChaseDone(start, end, &last)) { |
177 | return false; |
178 | } |
179 | if (last && !last->chased()) { |
180 | last->setChased(true); |
181 | SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last)); |
182 | *chase.append() = last; |
183 | #if DEBUG_WINDING |
184 | SkDebugf("%s chase.append id=%d" , __FUNCTION__, last->segment()->debugID()); |
185 | if (!last->final()) { |
186 | SkDebugf(" windSum=%d" , last->upCast()->windSum()); |
187 | } |
188 | SkDebugf("\n" ); |
189 | #endif |
190 | } |
191 | } |
192 | if (!findChaseOp(chase, &start, &end, ¤t)) { |
193 | return false; |
194 | } |
195 | SkPathOpsDebug::ShowActiveSpans(contourList); |
196 | if (!current) { |
197 | break; |
198 | } |
199 | } while (true); |
200 | } while (true); |
201 | return true; |
202 | } |
203 | |
204 | // diagram of why this simplifcation is possible is here: |
205 | // https://skia.org/dev/present/pathops link at bottom of the page |
206 | // https://drive.google.com/file/d/0BwoLUwz9PYkHLWpsaXd0UDdaN00/view?usp=sharing |
207 | static const SkPathOp gOpInverse[kReverseDifference_SkPathOp + 1][2][2] = { |
208 | // inside minuend outside minuend |
209 | // inside subtrahend outside subtrahend inside subtrahend outside subtrahend |
210 | {{ kDifference_SkPathOp, kIntersect_SkPathOp }, { kUnion_SkPathOp, kReverseDifference_SkPathOp }}, |
211 | {{ kIntersect_SkPathOp, kDifference_SkPathOp }, { kReverseDifference_SkPathOp, kUnion_SkPathOp }}, |
212 | {{ kUnion_SkPathOp, kReverseDifference_SkPathOp }, { kDifference_SkPathOp, kIntersect_SkPathOp }}, |
213 | {{ kXOR_SkPathOp, kXOR_SkPathOp }, { kXOR_SkPathOp, kXOR_SkPathOp }}, |
214 | {{ kReverseDifference_SkPathOp, kUnion_SkPathOp }, { kIntersect_SkPathOp, kDifference_SkPathOp }}, |
215 | }; |
216 | |
217 | static const bool gOutInverse[kReverseDifference_SkPathOp + 1][2][2] = { |
218 | {{ false, false }, { true, false }}, // diff |
219 | {{ false, false }, { false, true }}, // sect |
220 | {{ false, true }, { true, true }}, // union |
221 | {{ false, true }, { true, false }}, // xor |
222 | {{ false, true }, { false, false }}, // rev diff |
223 | }; |
224 | |
225 | #if DEBUG_T_SECT_LOOP_COUNT |
226 | |
227 | #include "include/private/SkMutex.h" |
228 | |
229 | SkOpGlobalState debugWorstState(nullptr, nullptr SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr)); |
230 | |
231 | void ReportPathOpsDebugging() { |
232 | debugWorstState.debugLoopReport(); |
233 | } |
234 | |
235 | extern void (*gVerboseFinalize)(); |
236 | |
237 | #endif |
238 | |
239 | bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result |
240 | SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)) { |
241 | #if DEBUG_DUMP_VERIFY |
242 | #ifndef SK_DEBUG |
243 | const char* testName = "release" ; |
244 | #endif |
245 | if (SkPathOpsDebug::gDumpOp) { |
246 | SkPathOpsDebug::DumpOp(one, two, op, testName); |
247 | } |
248 | #endif |
249 | op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()]; |
250 | bool inverseFill = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()]; |
251 | SkPathFillType fillType = inverseFill ? SkPathFillType::kInverseEvenOdd : |
252 | SkPathFillType::kEvenOdd; |
253 | SkRect rect1, rect2; |
254 | if (kIntersect_SkPathOp == op && one.isRect(&rect1) && two.isRect(&rect2)) { |
255 | result->reset(); |
256 | result->setFillType(fillType); |
257 | if (rect1.intersect(rect2)) { |
258 | result->addRect(rect1); |
259 | } |
260 | return true; |
261 | } |
262 | if (one.isEmpty() || two.isEmpty()) { |
263 | SkPath work; |
264 | switch (op) { |
265 | case kIntersect_SkPathOp: |
266 | break; |
267 | case kUnion_SkPathOp: |
268 | case kXOR_SkPathOp: |
269 | work = one.isEmpty() ? two : one; |
270 | break; |
271 | case kDifference_SkPathOp: |
272 | if (!one.isEmpty()) { |
273 | work = one; |
274 | } |
275 | break; |
276 | case kReverseDifference_SkPathOp: |
277 | if (!two.isEmpty()) { |
278 | work = two; |
279 | } |
280 | break; |
281 | default: |
282 | SkASSERT(0); // unhandled case |
283 | } |
284 | if (inverseFill != work.isInverseFillType()) { |
285 | work.toggleInverseFillType(); |
286 | } |
287 | return Simplify(work, result); |
288 | } |
289 | SkSTArenaAlloc<4096> allocator; // FIXME: add a constant expression here, tune |
290 | SkOpContour contour; |
291 | SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour); |
292 | SkOpGlobalState globalState(contourList, &allocator |
293 | SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName)); |
294 | SkOpCoincidence coincidence(&globalState); |
295 | const SkPath* minuend = &one; |
296 | const SkPath* subtrahend = &two; |
297 | if (op == kReverseDifference_SkPathOp) { |
298 | using std::swap; |
299 | swap(minuend, subtrahend); |
300 | op = kDifference_SkPathOp; |
301 | } |
302 | #if DEBUG_SORT |
303 | SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault; |
304 | #endif |
305 | // turn path into list of segments |
306 | SkOpEdgeBuilder builder(*minuend, contourList, &globalState); |
307 | if (builder.unparseable()) { |
308 | return false; |
309 | } |
310 | const int xorMask = builder.xorMask(); |
311 | builder.addOperand(*subtrahend); |
312 | if (!builder.finish()) { |
313 | return false; |
314 | } |
315 | #if DEBUG_DUMP_SEGMENTS |
316 | contourList->dumpSegments("seg" , op); |
317 | #endif |
318 | |
319 | const int xorOpMask = builder.xorMask(); |
320 | if (!SortContourList(&contourList, xorMask == kEvenOdd_PathOpsMask, |
321 | xorOpMask == kEvenOdd_PathOpsMask)) { |
322 | result->reset(); |
323 | result->setFillType(fillType); |
324 | return true; |
325 | } |
326 | // find all intersections between segments |
327 | SkOpContour* current = contourList; |
328 | do { |
329 | SkOpContour* next = current; |
330 | while (AddIntersectTs(current, next, &coincidence) |
331 | && (next = next->next())) |
332 | ; |
333 | } while ((current = current->next())); |
334 | #if DEBUG_VALIDATE |
335 | globalState.setPhase(SkOpPhase::kWalking); |
336 | #endif |
337 | bool success = HandleCoincidence(contourList, &coincidence); |
338 | #if DEBUG_COIN |
339 | globalState.debugAddToGlobalCoinDicts(); |
340 | #endif |
341 | if (!success) { |
342 | return false; |
343 | } |
344 | #if DEBUG_ALIGNMENT |
345 | contourList->dumpSegments("aligned" ); |
346 | #endif |
347 | // construct closed contours |
348 | SkPath original = *result; |
349 | result->reset(); |
350 | result->setFillType(fillType); |
351 | SkPathWriter wrapper(*result); |
352 | if (!bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper)) { |
353 | *result = original; |
354 | return false; |
355 | } |
356 | wrapper.assemble(); // if some edges could not be resolved, assemble remaining |
357 | #if DEBUG_T_SECT_LOOP_COUNT |
358 | static SkMutex& debugWorstLoop = *(new SkMutex); |
359 | { |
360 | SkAutoMutexExclusive autoM(debugWorstLoop); |
361 | if (!gVerboseFinalize) { |
362 | gVerboseFinalize = &ReportPathOpsDebugging; |
363 | } |
364 | debugWorstState.debugDoYourWorst(&globalState); |
365 | } |
366 | #endif |
367 | return true; |
368 | } |
369 | |
370 | bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) { |
371 | #if DEBUG_DUMP_VERIFY |
372 | if (SkPathOpsDebug::gVerifyOp) { |
373 | if (!OpDebug(one, two, op, result SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr))) { |
374 | SkPathOpsDebug::ReportOpFail(one, two, op); |
375 | return false; |
376 | } |
377 | SkPathOpsDebug::VerifyOp(one, two, op, *result); |
378 | return true; |
379 | } |
380 | #endif |
381 | return OpDebug(one, two, op, result SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr)); |
382 | } |
383 | |