/src/skia/src/pathops/SkPathOpsOp.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 2.49M | SkOpSpanBase** endPtr, SkOpSegment** result) { |
17 | 6.57M | while (chase.count()) { |
18 | 6.17M | SkOpSpanBase* span; |
19 | 6.17M | chase.pop(&span); |
20 | | // OPTIMIZE: prev makes this compatible with old code -- but is it necessary? |
21 | 6.17M | *startPtr = span->ptT()->prev()->span(); |
22 | 6.17M | SkOpSegment* segment = (*startPtr)->segment(); |
23 | 6.17M | bool done = true; |
24 | 6.17M | *endPtr = nullptr; |
25 | 6.17M | if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) { |
26 | 1.21M | *startPtr = last->start(); |
27 | 1.21M | *endPtr = last->end(); |
28 | | #if TRY_ROTATE |
29 | | *chase.insert(0) = span; |
30 | | #else |
31 | 1.21M | *chase.append() = span; |
32 | 1.21M | #endif |
33 | 1.21M | *result = last->segment(); |
34 | 1.21M | return true; |
35 | 1.21M | } |
36 | 4.96M | if (done) { |
37 | 4.07M | continue; |
38 | 4.07M | } |
39 | 893k | int winding; |
40 | 893k | bool sortable; |
41 | 893k | const SkOpAngle* angle = AngleWinding(*startPtr, *endPtr, &winding, &sortable); |
42 | 893k | if (!angle) { |
43 | 953 | *result = nullptr; |
44 | 953 | return true; |
45 | 953 | } |
46 | 892k | if (winding == SK_MinS32) { |
47 | 116 | continue; |
48 | 116 | } |
49 | 892k | int sumMiWinding, sumSuWinding; |
50 | 892k | if (sortable) { |
51 | 880k | segment = angle->segment(); |
52 | 880k | sumMiWinding = segment->updateWindingReverse(angle); |
53 | 880k | if (sumMiWinding == SK_MinS32) { |
54 | 17 | SkASSERT(segment->globalState()->debugSkipAssert()); |
55 | 17 | *result = nullptr; |
56 | 17 | return true; |
57 | 17 | } |
58 | 880k | sumSuWinding = segment->updateOppWindingReverse(angle); |
59 | 880k | if (sumSuWinding == SK_MinS32) { |
60 | 0 | SkASSERT(segment->globalState()->debugSkipAssert()); |
61 | 0 | *result = nullptr; |
62 | 0 | return true; |
63 | 0 | } |
64 | 880k | if (segment->operand()) { |
65 | 607k | using std::swap; |
66 | 607k | swap(sumMiWinding, sumSuWinding); |
67 | 607k | } |
68 | 880k | } |
69 | 892k | SkOpSegment* first = nullptr; |
70 | 892k | const SkOpAngle* firstAngle = angle; |
71 | 3.68M | while ((angle = angle->next()) != firstAngle) { |
72 | 2.79M | segment = angle->segment(); |
73 | 2.79M | SkOpSpanBase* start = angle->start(); |
74 | 2.79M | SkOpSpanBase* end = angle->end(); |
75 | 2.79M | int maxWinding = 0, sumWinding = 0, oppMaxWinding = 0, oppSumWinding = 0; |
76 | 2.79M | if (sortable) { |
77 | 2.69M | segment->setUpWindings(start, end, &sumMiWinding, &sumSuWinding, |
78 | 2.69M | &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding); |
79 | 2.69M | } |
80 | 2.79M | if (!segment->done(angle)) { |
81 | 1.98M | if (!first && (sortable || start->starter(end)->windSum() != SK_MinS32)) { |
82 | 888k | first = segment; |
83 | 888k | *startPtr = start; |
84 | 888k | *endPtr = end; |
85 | 888k | } |
86 | | // OPTIMIZATION: should this also add to the chase? |
87 | 1.98M | if (sortable) { |
88 | 1.95M | if (!segment->markAngle(maxWinding, sumWinding, oppMaxWinding, |
89 | 1.05k | oppSumWinding, angle, nullptr)) { |
90 | 1.05k | return false; |
91 | 1.05k | } |
92 | 1.95M | } |
93 | 1.98M | } |
94 | 2.79M | } |
95 | 891k | if (first) { |
96 | | #if TRY_ROTATE |
97 | | *chase.insert(0) = span; |
98 | | #else |
99 | 887k | *chase.append() = span; |
100 | 887k | #endif |
101 | 887k | *result = first; |
102 | 887k | return true; |
103 | 887k | } |
104 | 891k | } |
105 | 397k | *result = nullptr; |
106 | 397k | return true; |
107 | 2.49M | } |
108 | | |
109 | | static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, |
110 | 172k | const int xorMask, const int xorOpMask, SkPathWriter* writer) { |
111 | 172k | bool unsortable = false; |
112 | 172k | bool lastSimple = false; |
113 | 172k | bool simple = false; |
114 | 571k | do { |
115 | 571k | SkOpSpan* span = FindSortableTop(contourList); |
116 | 571k | if (!span) { |
117 | 157k | break; |
118 | 157k | } |
119 | 413k | SkOpSegment* current = span->segment(); |
120 | 413k | SkOpSpanBase* start = span->next(); |
121 | 413k | SkOpSpanBase* end = span; |
122 | 413k | SkTDArray<SkOpSpanBase*> chase; |
123 | 2.51M | do { |
124 | 2.51M | if (current->activeOp(start, end, xorMask, xorOpMask, op)) { |
125 | 5.86M | do { |
126 | 5.86M | if (!unsortable && current->done()) { |
127 | 163k | break; |
128 | 163k | } |
129 | 5.70M | SkASSERT(unsortable || !current->done()); |
130 | 5.70M | SkOpSpanBase* nextStart = start; |
131 | 5.70M | SkOpSpanBase* nextEnd = end; |
132 | 5.70M | lastSimple = simple; |
133 | 5.70M | SkOpSegment* next = current->findNextOp(&chase, &nextStart, &nextEnd, |
134 | 5.70M | &unsortable, &simple, op, xorMask, xorOpMask); |
135 | 5.70M | if (!next) { |
136 | 65.4k | if (!unsortable && writer->hasMove() |
137 | 3.47k | && current->verb() != SkPath::kLine_Verb |
138 | 199 | && !writer->isClosed()) { |
139 | 0 | if (!current->addCurveTo(start, end, writer)) { |
140 | 0 | return false; |
141 | 0 | } |
142 | 0 | if (!writer->isClosed()) { |
143 | 0 | SkPathOpsDebug::ShowActiveSpans(contourList); |
144 | 0 | } |
145 | 65.4k | } else if (lastSimple) { |
146 | 10.4k | if (!current->addCurveTo(start, end, writer)) { |
147 | 1.20k | return false; |
148 | 1.20k | } |
149 | 64.2k | } |
150 | 64.2k | break; |
151 | 64.2k | } |
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 | 5.64M | if (!current->addCurveTo(start, end, writer)) { |
158 | 13.0k | return false; |
159 | 13.0k | } |
160 | 5.62M | current = next; |
161 | 5.62M | start = nextStart; |
162 | 5.62M | end = nextEnd; |
163 | 5.62M | } while (!writer->isClosed() && (!unsortable || !start->starter(end)->done())); |
164 | 717k | if (current->activeWinding(start, end) && !writer->isClosed()) { |
165 | 229k | SkOpSpan* spanStart = start->starter(end); |
166 | 229k | if (!spanStart->done()) { |
167 | 197 | if (!current->addCurveTo(start, end, writer)) { |
168 | 121 | return false; |
169 | 121 | } |
170 | 76 | current->markDone(spanStart); |
171 | 76 | } |
172 | 229k | } |
173 | 717k | writer->finishContour(); |
174 | 1.78M | } else { |
175 | 1.78M | SkOpSpanBase* last; |
176 | 1.78M | if (!current->markAndChaseDone(start, end, &last)) { |
177 | 10 | return false; |
178 | 10 | } |
179 | 1.78M | if (last && !last->chased()) { |
180 | 1.48M | last->setChased(true); |
181 | 1.48M | SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last)); |
182 | 1.48M | *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 | 1.48M | } |
191 | 1.78M | } |
192 | 2.49M | if (!findChaseOp(chase, &start, &end, ¤t)) { |
193 | 1.05k | return false; |
194 | 1.05k | } |
195 | 2.49M | SkPathOpsDebug::ShowActiveSpans(contourList); |
196 | 2.49M | if (!current) { |
197 | 398k | break; |
198 | 398k | } |
199 | 2.10M | } while (true); |
200 | 398k | } while (true); |
201 | 157k | return true; |
202 | 172k | } |
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 | 382k | 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 | 382k | op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()]; |
250 | 382k | bool inverseFill = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()]; |
251 | 6.59k | SkPathFillType fillType = inverseFill ? SkPathFillType::kInverseEvenOdd : |
252 | 376k | SkPathFillType::kEvenOdd; |
253 | 382k | SkRect rect1, rect2; |
254 | 382k | if (kIntersect_SkPathOp == op && one.isRect(&rect1) && two.isRect(&rect2)) { |
255 | 288 | result->reset(); |
256 | 288 | result->setFillType(fillType); |
257 | 288 | if (rect1.intersect(rect2)) { |
258 | 288 | result->addRect(rect1); |
259 | 288 | } |
260 | 288 | return true; |
261 | 288 | } |
262 | 382k | if (one.isEmpty() || two.isEmpty()) { |
263 | 185k | SkPath work; |
264 | 185k | switch (op) { |
265 | 1.36k | case kIntersect_SkPathOp: |
266 | 1.36k | break; |
267 | 169k | case kUnion_SkPathOp: |
268 | 174k | case kXOR_SkPathOp: |
269 | 130k | work = one.isEmpty() ? two : one; |
270 | 174k | break; |
271 | 9.30k | case kDifference_SkPathOp: |
272 | 9.30k | if (!one.isEmpty()) { |
273 | 8.50k | work = one; |
274 | 8.50k | } |
275 | 9.30k | break; |
276 | 609 | case kReverseDifference_SkPathOp: |
277 | 609 | if (!two.isEmpty()) { |
278 | 392 | work = two; |
279 | 392 | } |
280 | 609 | break; |
281 | 0 | default: |
282 | 0 | SkASSERT(0); // unhandled case |
283 | 185k | } |
284 | 185k | if (inverseFill != work.isInverseFillType()) { |
285 | 7.12k | work.toggleInverseFillType(); |
286 | 7.12k | } |
287 | 185k | return Simplify(work, result); |
288 | 196k | } |
289 | 196k | SkSTArenaAlloc<4096> allocator; // FIXME: add a constant expression here, tune |
290 | 196k | SkOpContour contour; |
291 | 196k | SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour); |
292 | 196k | SkOpGlobalState globalState(contourList, &allocator |
293 | 0 | SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName)); |
294 | 196k | SkOpCoincidence coincidence(&globalState); |
295 | 196k | const SkPath* minuend = &one; |
296 | 196k | const SkPath* subtrahend = &two; |
297 | 196k | if (op == kReverseDifference_SkPathOp) { |
298 | 197 | using std::swap; |
299 | 197 | swap(minuend, subtrahend); |
300 | 197 | op = kDifference_SkPathOp; |
301 | 197 | } |
302 | | #if DEBUG_SORT |
303 | | SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault; |
304 | | #endif |
305 | | // turn path into list of segments |
306 | 196k | SkOpEdgeBuilder builder(*minuend, contourList, &globalState); |
307 | 196k | if (builder.unparseable()) { |
308 | 195 | return false; |
309 | 195 | } |
310 | 196k | const int xorMask = builder.xorMask(); |
311 | 196k | builder.addOperand(*subtrahend); |
312 | 196k | if (!builder.finish()) { |
313 | 1.70k | return false; |
314 | 1.70k | } |
315 | | #if DEBUG_DUMP_SEGMENTS |
316 | | contourList->dumpSegments("seg", op); |
317 | | #endif |
318 | | |
319 | 194k | const int xorOpMask = builder.xorMask(); |
320 | 194k | if (!SortContourList(&contourList, xorMask == kEvenOdd_PathOpsMask, |
321 | 3.28k | xorOpMask == kEvenOdd_PathOpsMask)) { |
322 | 3.28k | result->reset(); |
323 | 3.28k | result->setFillType(fillType); |
324 | 3.28k | return true; |
325 | 3.28k | } |
326 | | // find all intersections between segments |
327 | 191k | SkOpContour* current = contourList; |
328 | 843k | do { |
329 | 843k | SkOpContour* next = current; |
330 | 3.77M | while (AddIntersectTs(current, next, &coincidence) |
331 | 3.49M | && (next = next->next())) |
332 | 2.92M | ; |
333 | 843k | } while ((current = current->next())); |
334 | | #if DEBUG_VALIDATE |
335 | | globalState.setPhase(SkOpPhase::kWalking); |
336 | | #endif |
337 | 191k | bool success = HandleCoincidence(contourList, &coincidence); |
338 | | #if DEBUG_COIN |
339 | | globalState.debugAddToGlobalCoinDicts(); |
340 | | #endif |
341 | 191k | if (!success) { |
342 | 18.5k | return false; |
343 | 18.5k | } |
344 | | #if DEBUG_ALIGNMENT |
345 | | contourList->dumpSegments("aligned"); |
346 | | #endif |
347 | | // construct closed contours |
348 | 172k | SkPath original = *result; |
349 | 172k | result->reset(); |
350 | 172k | result->setFillType(fillType); |
351 | 172k | SkPathWriter wrapper(*result); |
352 | 172k | if (!bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper)) { |
353 | 15.4k | *result = original; |
354 | 15.4k | return false; |
355 | 15.4k | } |
356 | 157k | 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 | 157k | return true; |
368 | 157k | } OpDebug(SkPath const&, SkPath const&, SkPathOp, SkPath*) Line | Count | Source | 240 | 382k | 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 | 382k | op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()]; | 250 | 382k | bool inverseFill = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()]; | 251 | 6.59k | SkPathFillType fillType = inverseFill ? SkPathFillType::kInverseEvenOdd : | 252 | 376k | SkPathFillType::kEvenOdd; | 253 | 382k | SkRect rect1, rect2; | 254 | 382k | if (kIntersect_SkPathOp == op && one.isRect(&rect1) && two.isRect(&rect2)) { | 255 | 288 | result->reset(); | 256 | 288 | result->setFillType(fillType); | 257 | 288 | if (rect1.intersect(rect2)) { | 258 | 288 | result->addRect(rect1); | 259 | 288 | } | 260 | 288 | return true; | 261 | 288 | } | 262 | 382k | if (one.isEmpty() || two.isEmpty()) { | 263 | 185k | SkPath work; | 264 | 185k | switch (op) { | 265 | 1.36k | case kIntersect_SkPathOp: | 266 | 1.36k | break; | 267 | 169k | case kUnion_SkPathOp: | 268 | 174k | case kXOR_SkPathOp: | 269 | 130k | work = one.isEmpty() ? two : one; | 270 | 174k | break; | 271 | 9.30k | case kDifference_SkPathOp: | 272 | 9.30k | if (!one.isEmpty()) { | 273 | 8.50k | work = one; | 274 | 8.50k | } | 275 | 9.30k | break; | 276 | 609 | case kReverseDifference_SkPathOp: | 277 | 609 | if (!two.isEmpty()) { | 278 | 392 | work = two; | 279 | 392 | } | 280 | 609 | break; | 281 | 0 | default: | 282 | 0 | SkASSERT(0); // unhandled case | 283 | 185k | } | 284 | 185k | if (inverseFill != work.isInverseFillType()) { | 285 | 7.12k | work.toggleInverseFillType(); | 286 | 7.12k | } | 287 | 185k | return Simplify(work, result); | 288 | 196k | } | 289 | 196k | SkSTArenaAlloc<4096> allocator; // FIXME: add a constant expression here, tune | 290 | 196k | SkOpContour contour; | 291 | 196k | SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour); | 292 | 196k | SkOpGlobalState globalState(contourList, &allocator | 293 | 196k | SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName)); | 294 | 196k | SkOpCoincidence coincidence(&globalState); | 295 | 196k | const SkPath* minuend = &one; | 296 | 196k | const SkPath* subtrahend = &two; | 297 | 196k | if (op == kReverseDifference_SkPathOp) { | 298 | 197 | using std::swap; | 299 | 197 | swap(minuend, subtrahend); | 300 | 197 | op = kDifference_SkPathOp; | 301 | 197 | } | 302 | | #if DEBUG_SORT | 303 | | SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault; | 304 | | #endif | 305 | | // turn path into list of segments | 306 | 196k | SkOpEdgeBuilder builder(*minuend, contourList, &globalState); | 307 | 196k | if (builder.unparseable()) { | 308 | 195 | return false; | 309 | 195 | } | 310 | 196k | const int xorMask = builder.xorMask(); | 311 | 196k | builder.addOperand(*subtrahend); | 312 | 196k | if (!builder.finish()) { | 313 | 1.70k | return false; | 314 | 1.70k | } | 315 | | #if DEBUG_DUMP_SEGMENTS | 316 | | contourList->dumpSegments("seg", op); | 317 | | #endif | 318 | | | 319 | 194k | const int xorOpMask = builder.xorMask(); | 320 | 194k | if (!SortContourList(&contourList, xorMask == kEvenOdd_PathOpsMask, | 321 | 3.28k | xorOpMask == kEvenOdd_PathOpsMask)) { | 322 | 3.28k | result->reset(); | 323 | 3.28k | result->setFillType(fillType); | 324 | 3.28k | return true; | 325 | 3.28k | } | 326 | | // find all intersections between segments | 327 | 191k | SkOpContour* current = contourList; | 328 | 843k | do { | 329 | 843k | SkOpContour* next = current; | 330 | 3.77M | while (AddIntersectTs(current, next, &coincidence) | 331 | 3.49M | && (next = next->next())) | 332 | 2.92M | ; | 333 | 843k | } while ((current = current->next())); | 334 | | #if DEBUG_VALIDATE | 335 | | globalState.setPhase(SkOpPhase::kWalking); | 336 | | #endif | 337 | 191k | bool success = HandleCoincidence(contourList, &coincidence); | 338 | | #if DEBUG_COIN | 339 | | globalState.debugAddToGlobalCoinDicts(); | 340 | | #endif | 341 | 191k | if (!success) { | 342 | 18.5k | return false; | 343 | 18.5k | } | 344 | | #if DEBUG_ALIGNMENT | 345 | | contourList->dumpSegments("aligned"); | 346 | | #endif | 347 | | // construct closed contours | 348 | 172k | SkPath original = *result; | 349 | 172k | result->reset(); | 350 | 172k | result->setFillType(fillType); | 351 | 172k | SkPathWriter wrapper(*result); | 352 | 172k | if (!bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper)) { | 353 | 15.4k | *result = original; | 354 | 15.4k | return false; | 355 | 15.4k | } | 356 | 157k | 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 | 157k | return true; | 368 | 157k | } |
Unexecuted instantiation: OpDebug(SkPath const&, SkPath const&, SkPathOp, SkPath*, bool, char const*) |
369 | | |
370 | 765k | 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 | 382k | return OpDebug(one, two, op, result SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr)); |
382 | 765k | } Op(SkPath const&, SkPath const&, SkPathOp, SkPath*) Line | Count | Source | 370 | 382k | 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 | 382k | return OpDebug(one, two, op, result SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr)); | 382 | 382k | } |
Op(SkPath const&, SkPath const&, SkPathOp, SkPath*) Line | Count | Source | 370 | 382k | 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 | 382k | return OpDebug(one, two, op, result SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr)); | 382 | 382k | } |
|