Line data Source code
1 : /*
2 : * Copyright 2013 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 :
8 : #include "SkMutex.h"
9 : #include "SkOpCoincidence.h"
10 : #include "SkOpContour.h"
11 : #include "SkOSFile.h"
12 : #include "SkPath.h"
13 : #include "SkPathOpsDebug.h"
14 : #include "SkString.h"
15 :
16 : #if DEBUG_DUMP_VERIFY
17 : bool SkPathOpsDebug::gDumpOp; // set to true to write op to file before a crash
18 : bool SkPathOpsDebug::gVerifyOp; // set to true to compare result against regions
19 : #endif
20 :
21 : bool SkPathOpsDebug::gRunFail; // set to true to check for success on tests known to fail
22 : bool SkPathOpsDebug::gVeryVerbose; // set to true to run extensive checking tests
23 :
24 : #undef FAIL_IF
25 : #define FAIL_IF(cond, coin) \
26 : do { if (cond) log->record(SkPathOpsDebug::kFail_Glitch, coin); } while (false)
27 :
28 : #undef FAIL_WITH_NULL_IF
29 : #define FAIL_WITH_NULL_IF(cond, span) \
30 : do { if (cond) log->record(SkPathOpsDebug::kFail_Glitch, span); } while (false)
31 :
32 : #undef RETURN_FALSE_IF
33 : #define RETURN_FALSE_IF(cond, span) \
34 : do { if (cond) log->record(SkPathOpsDebug::kReturnFalse_Glitch, span); \
35 : } while (false)
36 :
37 : class SkCoincidentSpans;
38 :
39 : #if DEBUG_SORT
40 : int SkPathOpsDebug::gSortCountDefault = SK_MaxS32;
41 : int SkPathOpsDebug::gSortCount;
42 : #endif
43 :
44 : #if DEBUG_ACTIVE_OP
45 : const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor"};
46 : #endif
47 :
48 : #if defined SK_DEBUG || !FORCE_RELEASE
49 :
50 : const char* SkPathOpsDebug::kLVerbStr[] = {"", "line", "quad", "cubic"};
51 :
52 : int SkPathOpsDebug::gContourID = 0;
53 : int SkPathOpsDebug::gSegmentID = 0;
54 :
55 0 : bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray,
56 : const SkOpSpanBase* span) {
57 0 : for (int index = 0; index < chaseArray.count(); ++index) {
58 0 : const SkOpSpanBase* entry = chaseArray[index];
59 0 : if (entry == span) {
60 0 : return true;
61 : }
62 : }
63 0 : return false;
64 : }
65 : #endif
66 :
67 : #if DEBUG_ACTIVE_SPANS
68 : SkString SkPathOpsDebug::gActiveSpans;
69 : #endif
70 :
71 : #if DEBUG_COIN
72 :
73 : SkPathOpsDebug::CoinDict SkPathOpsDebug::gCoinSumChangedDict;
74 : SkPathOpsDebug::CoinDict SkPathOpsDebug::gCoinSumVisitedDict;
75 :
76 : static const int kGlitchType_Count = SkPathOpsDebug::kUnalignedTail_Glitch + 1;
77 :
78 : struct SpanGlitch {
79 : const SkOpSpanBase* fBase;
80 : const SkOpSpanBase* fSuspect;
81 : const SkOpSegment* fSegment;
82 : const SkOpSegment* fOppSegment;
83 : const SkOpPtT* fCoinSpan;
84 : const SkOpPtT* fEndSpan;
85 : const SkOpPtT* fOppSpan;
86 : const SkOpPtT* fOppEndSpan;
87 : double fStartT;
88 : double fEndT;
89 : double fOppStartT;
90 : double fOppEndT;
91 : SkPoint fPt;
92 : SkPathOpsDebug::GlitchType fType;
93 :
94 : void dumpType() const;
95 : };
96 :
97 : struct SkPathOpsDebug::GlitchLog {
98 : void init(const SkOpGlobalState* state) {
99 : fGlobalState = state;
100 : }
101 :
102 : SpanGlitch* recordCommon(GlitchType type) {
103 : SpanGlitch* glitch = fGlitches.push();
104 : glitch->fBase = nullptr;
105 : glitch->fSuspect = nullptr;
106 : glitch->fSegment = nullptr;
107 : glitch->fOppSegment = nullptr;
108 : glitch->fCoinSpan = nullptr;
109 : glitch->fEndSpan = nullptr;
110 : glitch->fOppSpan = nullptr;
111 : glitch->fOppEndSpan = nullptr;
112 : glitch->fStartT = SK_ScalarNaN;
113 : glitch->fEndT = SK_ScalarNaN;
114 : glitch->fOppStartT = SK_ScalarNaN;
115 : glitch->fOppEndT = SK_ScalarNaN;
116 : glitch->fPt = { SK_ScalarNaN, SK_ScalarNaN };
117 : glitch->fType = type;
118 : return glitch;
119 : }
120 :
121 : void record(GlitchType type, const SkOpSpanBase* base,
122 : const SkOpSpanBase* suspect = NULL) {
123 : SpanGlitch* glitch = recordCommon(type);
124 : glitch->fBase = base;
125 : glitch->fSuspect = suspect;
126 : }
127 :
128 : void record(GlitchType type, const SkOpSpanBase* base,
129 : const SkOpPtT* ptT) {
130 : SpanGlitch* glitch = recordCommon(type);
131 : glitch->fBase = base;
132 : glitch->fCoinSpan = ptT;
133 : }
134 :
135 : void record(GlitchType type, const SkCoincidentSpans* coin,
136 : const SkCoincidentSpans* opp = NULL) {
137 : SpanGlitch* glitch = recordCommon(type);
138 : glitch->fCoinSpan = coin->coinPtTStart();
139 : glitch->fEndSpan = coin->coinPtTEnd();
140 : if (opp) {
141 : glitch->fOppSpan = opp->coinPtTStart();
142 : glitch->fOppEndSpan = opp->coinPtTEnd();
143 : }
144 : }
145 :
146 : void record(GlitchType type, const SkOpSpanBase* base,
147 : const SkOpSegment* seg, double t, SkPoint pt) {
148 : SpanGlitch* glitch = recordCommon(type);
149 : glitch->fBase = base;
150 : glitch->fSegment = seg;
151 : glitch->fStartT = t;
152 : glitch->fPt = pt;
153 : }
154 :
155 : void record(GlitchType type, const SkOpSpanBase* base, double t,
156 : SkPoint pt) {
157 : SpanGlitch* glitch = recordCommon(type);
158 : glitch->fBase = base;
159 : glitch->fStartT = t;
160 : glitch->fPt = pt;
161 : }
162 :
163 : void record(GlitchType type, const SkCoincidentSpans* coin,
164 : const SkOpPtT* coinSpan, const SkOpPtT* endSpan) {
165 : SpanGlitch* glitch = recordCommon(type);
166 : glitch->fCoinSpan = coin->coinPtTStart();
167 : glitch->fEndSpan = coin->coinPtTEnd();
168 : glitch->fEndSpan = endSpan;
169 : glitch->fOppSpan = coinSpan;
170 : glitch->fOppEndSpan = endSpan;
171 : }
172 :
173 : void record(GlitchType type, const SkCoincidentSpans* coin,
174 : const SkOpSpanBase* base) {
175 : SpanGlitch* glitch = recordCommon(type);
176 : glitch->fBase = base;
177 : glitch->fCoinSpan = coin->coinPtTStart();
178 : glitch->fEndSpan = coin->coinPtTEnd();
179 : }
180 :
181 : void record(GlitchType type, const SkOpPtT* ptTS, const SkOpPtT* ptTE,
182 : const SkOpPtT* oPtTS, const SkOpPtT* oPtTE) {
183 : SpanGlitch* glitch = recordCommon(type);
184 : glitch->fCoinSpan = ptTS;
185 : glitch->fEndSpan = ptTE;
186 : glitch->fOppSpan = oPtTS;
187 : glitch->fOppEndSpan = oPtTE;
188 : }
189 :
190 : void record(GlitchType type, const SkOpSegment* seg, double startT,
191 : double endT, const SkOpSegment* oppSeg, double oppStartT, double oppEndT) {
192 : SpanGlitch* glitch = recordCommon(type);
193 : glitch->fSegment = seg;
194 : glitch->fStartT = startT;
195 : glitch->fEndT = endT;
196 : glitch->fOppSegment = oppSeg;
197 : glitch->fOppStartT = oppStartT;
198 : glitch->fOppEndT = oppEndT;
199 : }
200 :
201 : void record(GlitchType type, const SkOpSegment* seg,
202 : const SkOpSpan* span) {
203 : SpanGlitch* glitch = recordCommon(type);
204 : glitch->fSegment = seg;
205 : glitch->fBase = span;
206 : }
207 :
208 : void record(GlitchType type, double t, const SkOpSpanBase* span) {
209 : SpanGlitch* glitch = recordCommon(type);
210 : glitch->fStartT = t;
211 : glitch->fBase = span;
212 : }
213 :
214 : void record(GlitchType type, const SkOpSegment* seg) {
215 : SpanGlitch* glitch = recordCommon(type);
216 : glitch->fSegment = seg;
217 : }
218 :
219 : void record(GlitchType type, const SkCoincidentSpans* coin,
220 : const SkOpPtT* ptT) {
221 : SpanGlitch* glitch = recordCommon(type);
222 : glitch->fCoinSpan = coin->coinPtTStart();
223 : glitch->fEndSpan = ptT;
224 : }
225 :
226 : SkTDArray<SpanGlitch> fGlitches;
227 : const SkOpGlobalState* fGlobalState;
228 : };
229 :
230 :
231 : void SkPathOpsDebug::CoinDict::add(const SkPathOpsDebug::CoinDict& dict) {
232 : int count = dict.fDict.count();
233 : for (int index = 0; index < count; ++index) {
234 : this->add(dict.fDict[index]);
235 : }
236 : }
237 :
238 : void SkPathOpsDebug::CoinDict::add(const CoinDictEntry& key) {
239 : int count = fDict.count();
240 : for (int index = 0; index < count; ++index) {
241 : CoinDictEntry* entry = &fDict[index];
242 : if (entry->fIteration == key.fIteration && entry->fLineNumber == key.fLineNumber) {
243 : SkASSERT(!strcmp(entry->fFunctionName, key.fFunctionName));
244 : if (entry->fGlitchType == kUninitialized_Glitch) {
245 : entry->fGlitchType = key.fGlitchType;
246 : }
247 : return;
248 : }
249 : }
250 : *fDict.append() = key;
251 : }
252 :
253 : #endif
254 :
255 : #if DEBUG_COIN
256 : static void missing_coincidence(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) {
257 : const SkOpContour* contour = contourList;
258 : // bool result = false;
259 : do {
260 : /* result |= */ contour->debugMissingCoincidence(glitches);
261 : } while ((contour = contour->next()));
262 : return;
263 : }
264 :
265 : static void move_multiples(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) {
266 : const SkOpContour* contour = contourList;
267 : do {
268 : if (contour->debugMoveMultiples(glitches), false) {
269 : return;
270 : }
271 : } while ((contour = contour->next()));
272 : return;
273 : }
274 :
275 : static void move_nearby(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) {
276 : const SkOpContour* contour = contourList;
277 : do {
278 : contour->debugMoveNearby(glitches);
279 : } while ((contour = contour->next()));
280 : }
281 :
282 :
283 : #endif
284 :
285 : #if DEBUG_COIN
286 : void SkOpGlobalState::debugAddToCoinChangedDict() {
287 :
288 : #if DEBUG_COINCIDENCE
289 : SkPathOpsDebug::CheckHealth(fContourHead);
290 : #endif
291 : // see if next coincident operation makes a change; if so, record it
292 : SkPathOpsDebug::GlitchLog glitches;
293 : const char* funcName = fCoinDictEntry.fFunctionName;
294 : if (!strcmp("calc_angles", funcName)) {
295 : ;
296 : } else if (!strcmp("missing_coincidence", funcName)) {
297 : missing_coincidence(&glitches, fContourHead);
298 : } else if (!strcmp("move_multiples", funcName)) {
299 : move_multiples(&glitches, fContourHead);
300 : } else if (!strcmp("move_nearby", funcName)) {
301 : move_nearby(&glitches, fContourHead);
302 : } else if (!strcmp("addExpanded", funcName)) {
303 : fCoincidence->debugAddExpanded(&glitches);
304 : } else if (!strcmp("addMissing", funcName)) {
305 : bool added;
306 : fCoincidence->debugAddMissing(&glitches, &added);
307 : } else if (!strcmp("addEndMovedSpans", funcName)) {
308 : fCoincidence->debugAddEndMovedSpans(&glitches);
309 : } else if (!strcmp("correctEnds", funcName)) {
310 : fCoincidence->debugCorrectEnds(&glitches);
311 : } else if (!strcmp("expand", funcName)) {
312 : fCoincidence->debugExpand(&glitches);
313 : } else if (!strcmp("findOverlaps", funcName)) {
314 : ;
315 : } else if (!strcmp("mark", funcName)) {
316 : fCoincidence->debugMark(&glitches);
317 : } else if (!strcmp("apply", funcName)) {
318 : ;
319 : } else {
320 : SkASSERT(0); // add missing case
321 : }
322 : if (glitches.fGlitches.count()) {
323 : fCoinDictEntry.fGlitchType = glitches.fGlitches[0].fType;
324 : }
325 : fCoinChangedDict.add(fCoinDictEntry);
326 : }
327 : #endif
328 :
329 0 : void SkPathOpsDebug::ShowActiveSpans(SkOpContourHead* contourList) {
330 : #if DEBUG_ACTIVE_SPANS
331 : SkString str;
332 : SkOpContour* contour = contourList;
333 : do {
334 : contour->debugShowActiveSpans(&str);
335 : } while ((contour = contour->next()));
336 : if (!gActiveSpans.equals(str)) {
337 : const char* s = str.c_str();
338 : const char* end;
339 : while ((end = strchr(s, '\n'))) {
340 : SkDebugf("%.*s", end - s + 1, s);
341 : s = end + 1;
342 : }
343 : gActiveSpans.set(str);
344 : }
345 : #endif
346 0 : }
347 :
348 : #if DEBUG_COINCIDENCE || DEBUG_COIN
349 : void SkPathOpsDebug::CheckHealth(SkOpContourHead* contourList) {
350 : #if DEBUG_COINCIDENCE
351 : contourList->globalState()->debugSetCheckHealth(true);
352 : #endif
353 : #if DEBUG_COIN
354 : GlitchLog glitches;
355 : const SkOpContour* contour = contourList;
356 : const SkOpCoincidence* coincidence = contour->globalState()->coincidence();
357 : coincidence->debugCheckValid(&glitches); // don't call validate; spans may be inconsistent
358 : do {
359 : contour->debugCheckHealth(&glitches);
360 : contour->debugMissingCoincidence(&glitches);
361 : } while ((contour = contour->next()));
362 : bool added;
363 : coincidence->debugAddMissing(&glitches, &added);
364 : coincidence->debugExpand(&glitches);
365 : coincidence->debugAddExpanded(&glitches);
366 : coincidence->debugMark(&glitches);
367 : unsigned mask = 0;
368 : for (int index = 0; index < glitches.fGlitches.count(); ++index) {
369 : const SpanGlitch& glitch = glitches.fGlitches[index];
370 : mask |= 1 << glitch.fType;
371 : }
372 : for (int index = 0; index < kGlitchType_Count; ++index) {
373 : SkDebugf(mask & (1 << index) ? "x" : "-");
374 : }
375 : SkDebugf(" %s\n", contourList->globalState()->debugCoinDictEntry().fFunctionName);
376 : for (int index = 0; index < glitches.fGlitches.count(); ++index) {
377 : const SpanGlitch& glitch = glitches.fGlitches[index];
378 : SkDebugf("%02d: ", index);
379 : if (glitch.fBase) {
380 : SkDebugf(" seg/base=%d/%d", glitch.fBase->segment()->debugID(),
381 : glitch.fBase->debugID());
382 : }
383 : if (glitch.fSuspect) {
384 : SkDebugf(" seg/base=%d/%d", glitch.fSuspect->segment()->debugID(),
385 : glitch.fSuspect->debugID());
386 : }
387 : if (glitch.fSegment) {
388 : SkDebugf(" segment=%d", glitch.fSegment->debugID());
389 : }
390 : if (glitch.fCoinSpan) {
391 : SkDebugf(" coinSeg/Span/PtT=%d/%d/%d", glitch.fCoinSpan->segment()->debugID(),
392 : glitch.fCoinSpan->span()->debugID(), glitch.fCoinSpan->debugID());
393 : }
394 : if (glitch.fEndSpan) {
395 : SkDebugf(" endSpan=%d", glitch.fEndSpan->debugID());
396 : }
397 : if (glitch.fOppSpan) {
398 : SkDebugf(" oppSeg/Span/PtT=%d/%d/%d", glitch.fOppSpan->segment()->debugID(),
399 : glitch.fOppSpan->span()->debugID(), glitch.fOppSpan->debugID());
400 : }
401 : if (glitch.fOppEndSpan) {
402 : SkDebugf(" oppEndSpan=%d", glitch.fOppEndSpan->debugID());
403 : }
404 : if (!SkScalarIsNaN(glitch.fStartT)) {
405 : SkDebugf(" startT=%g", glitch.fStartT);
406 : }
407 : if (!SkScalarIsNaN(glitch.fEndT)) {
408 : SkDebugf(" endT=%g", glitch.fEndT);
409 : }
410 : if (glitch.fOppSegment) {
411 : SkDebugf(" segment=%d", glitch.fOppSegment->debugID());
412 : }
413 : if (!SkScalarIsNaN(glitch.fOppStartT)) {
414 : SkDebugf(" oppStartT=%g", glitch.fOppStartT);
415 : }
416 : if (!SkScalarIsNaN(glitch.fOppEndT)) {
417 : SkDebugf(" oppEndT=%g", glitch.fOppEndT);
418 : }
419 : if (!SkScalarIsNaN(glitch.fPt.fX) || !SkScalarIsNaN(glitch.fPt.fY)) {
420 : SkDebugf(" pt=%g,%g", glitch.fPt.fX, glitch.fPt.fY);
421 : }
422 : DumpGlitchType(glitch.fType);
423 : SkDebugf("\n");
424 : }
425 : #if DEBUG_COINCIDENCE
426 : contourList->globalState()->debugSetCheckHealth(false);
427 : #endif
428 : #if 01 && DEBUG_ACTIVE_SPANS
429 : // SkDebugf("active after %s:\n", id);
430 : ShowActiveSpans(contourList);
431 : #endif
432 : #endif
433 : }
434 : #endif
435 :
436 : #if DEBUG_COIN
437 : void SkPathOpsDebug::DumpGlitchType(GlitchType glitchType) {
438 : switch (glitchType) {
439 : case kAddCorruptCoin_Glitch: SkDebugf(" AddCorruptCoin"); break;
440 : case kAddExpandedCoin_Glitch: SkDebugf(" AddExpandedCoin"); break;
441 : case kAddExpandedFail_Glitch: SkDebugf(" AddExpandedFail"); break;
442 : case kAddIfCollapsed_Glitch: SkDebugf(" AddIfCollapsed"); break;; break;
443 : case kAddIfMissingCoin_Glitch: SkDebugf(" AddIfMissingCoin"); break;
444 : case kAddMissingCoin_Glitch: SkDebugf(" AddMissingCoin"); break;
445 : case kAddMissingExtend_Glitch: SkDebugf(" AddMissingExtend"); break;
446 : case kAddOrOverlap_Glitch: SkDebugf(" AAddOrOverlap"); break;
447 : case kCollapsedCoin_Glitch: SkDebugf(" CollapsedCoin"); break;
448 : case kCollapsedDone_Glitch: SkDebugf(" CollapsedDone"); break;
449 : case kCollapsedOppValue_Glitch: SkDebugf(" CollapsedOppValue"); break;
450 : case kCollapsedSpan_Glitch: SkDebugf(" CollapsedSpan"); break;
451 : case kCollapsedWindValue_Glitch: SkDebugf(" CollapsedWindValue"); break;
452 : case kCorrectEnd_Glitch: SkDebugf(" CorrectEnd"); break;
453 : case kDeletedCoin_Glitch: SkDebugf(" DeletedCoin"); break;
454 : case kExpandCoin_Glitch: SkDebugf(" ExpandCoin"); break;
455 : case kFail_Glitch: SkDebugf(" Fail"); break;
456 : case kMarkCoinEnd_Glitch: SkDebugf(" MarkCoinEnd"); break;
457 : case kMarkCoinInsert_Glitch: SkDebugf(" MarkCoinInsert"); break;
458 : case kMarkCoinMissing_Glitch: SkDebugf(" MarkCoinMissing"); break;
459 : case kMarkCoinStart_Glitch: SkDebugf(" MarkCoinStart"); break;
460 : case kMergeMatches_Glitch: SkDebugf(" MergeMatches"); break;
461 : case kMissingCoin_Glitch: SkDebugf(" MissingCoin"); break;
462 : case kMissingDone_Glitch: SkDebugf(" MissingDone"); break;
463 : case kMissingIntersection_Glitch: SkDebugf(" MissingIntersection"); break;
464 : case kMoveMultiple_Glitch: SkDebugf(" MoveMultiple"); break;
465 : case kMoveNearbyClearAll_Glitch: SkDebugf(" MoveNearbyClearAll"); break;
466 : case kMoveNearbyClearAll2_Glitch: SkDebugf(" MoveNearbyClearAll2"); break;
467 : case kMoveNearbyMerge_Glitch: SkDebugf(" MoveNearbyMerge"); break;
468 : case kMoveNearbyMergeFinal_Glitch: SkDebugf(" MoveNearbyMergeFinal"); break;
469 : case kMoveNearbyRelease_Glitch: SkDebugf(" MoveNearbyRelease"); break;
470 : case kMoveNearbyReleaseFinal_Glitch: SkDebugf(" MoveNearbyReleaseFinal"); break;
471 : case kReleasedSpan_Glitch: SkDebugf(" ReleasedSpan"); break;
472 : case kReturnFalse_Glitch: SkDebugf(" ReturnFalse"); break;
473 : case kUnaligned_Glitch: SkDebugf(" Unaligned"); break;
474 : case kUnalignedHead_Glitch: SkDebugf(" UnalignedHead"); break;
475 : case kUnalignedTail_Glitch: SkDebugf(" UnalignedTail"); break;
476 : case kUninitialized_Glitch: break;
477 : default: SkASSERT(0);
478 : }
479 : }
480 : #endif
481 :
482 : #if defined SK_DEBUG || !FORCE_RELEASE
483 33 : void SkPathOpsDebug::MathematicaIze(char* str, size_t bufferLen) {
484 33 : size_t len = strlen(str);
485 33 : bool num = false;
486 3180 : for (size_t idx = 0; idx < len; ++idx) {
487 3147 : if (num && str[idx] == 'e') {
488 3 : if (len + 2 >= bufferLen) {
489 0 : return;
490 : }
491 3 : memmove(&str[idx + 2], &str[idx + 1], len - idx);
492 3 : str[idx] = '*';
493 3 : str[idx + 1] = '^';
494 3 : ++len;
495 : }
496 3147 : num = str[idx] >= '0' && str[idx] <= '9';
497 : }
498 : }
499 :
500 0 : bool SkPathOpsDebug::ValidWind(int wind) {
501 0 : return wind > SK_MinS32 + 0xFFFF && wind < SK_MaxS32 - 0xFFFF;
502 : }
503 :
504 0 : void SkPathOpsDebug::WindingPrintf(int wind) {
505 0 : if (wind == SK_MinS32) {
506 0 : SkDebugf("?");
507 : } else {
508 0 : SkDebugf("%d", wind);
509 : }
510 0 : }
511 : #endif // defined SK_DEBUG || !FORCE_RELEASE
512 :
513 :
514 : #if DEBUG_SHOW_TEST_NAME
515 : void* SkPathOpsDebug::CreateNameStr() { return new char[DEBUG_FILENAME_STRING_LENGTH]; }
516 :
517 : void SkPathOpsDebug::DeleteNameStr(void* v) { delete[] reinterpret_cast<char*>(v); }
518 :
519 : void SkPathOpsDebug::BumpTestName(char* test) {
520 : char* num = test + strlen(test);
521 : while (num[-1] >= '0' && num[-1] <= '9') {
522 : --num;
523 : }
524 : if (num[0] == '\0') {
525 : return;
526 : }
527 : int dec = atoi(num);
528 : if (dec == 0) {
529 : return;
530 : }
531 : ++dec;
532 : SK_SNPRINTF(num, DEBUG_FILENAME_STRING_LENGTH - (num - test), "%d", dec);
533 : }
534 : #endif
535 :
536 0 : static void show_function_header(const char* functionName) {
537 0 : SkDebugf("\nstatic void %s(skiatest::Reporter* reporter, const char* filename) {\n", functionName);
538 0 : if (strcmp("skphealth_com76", functionName) == 0) {
539 0 : SkDebugf("found it\n");
540 : }
541 0 : }
542 :
543 : static const char* gOpStrs[] = {
544 : "kDifference_SkPathOp",
545 : "kIntersect_SkPathOp",
546 : "kUnion_SkPathOp",
547 : "kXOR_PathOp",
548 : "kReverseDifference_SkPathOp",
549 : };
550 :
551 0 : const char* SkPathOpsDebug::OpStr(SkPathOp op) {
552 0 : return gOpStrs[op];
553 : }
554 :
555 0 : static void show_op(SkPathOp op, const char* pathOne, const char* pathTwo) {
556 0 : SkDebugf(" testPathOp(reporter, %s, %s, %s, filename);\n", pathOne, pathTwo, gOpStrs[op]);
557 0 : SkDebugf("}\n");
558 0 : }
559 :
560 : SK_DECLARE_STATIC_MUTEX(gTestMutex);
561 :
562 0 : void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
563 : const char* testName) {
564 0 : SkAutoMutexAcquire ac(gTestMutex);
565 0 : show_function_header(testName);
566 0 : ShowOnePath(a, "path", true);
567 0 : ShowOnePath(b, "pathB", true);
568 0 : show_op(shapeOp, "path", "pathB");
569 0 : }
570 :
571 : #include "SkPathOpsTypes.h"
572 : #include "SkIntersectionHelper.h"
573 : #include "SkIntersections.h"
574 :
575 : #if DEBUG_COIN
576 :
577 : SK_DECLARE_STATIC_MUTEX(gCoinDictMutex);
578 :
579 : void SkOpGlobalState::debugAddToGlobalCoinDicts() {
580 : SkAutoMutexAcquire ac(&gCoinDictMutex);
581 : SkPathOpsDebug::gCoinSumChangedDict.add(fCoinChangedDict);
582 : SkPathOpsDebug::gCoinSumVisitedDict.add(fCoinVisitedDict);
583 : }
584 :
585 : #endif
586 :
587 : #if DEBUG_T_SECT_LOOP_COUNT
588 : void SkOpGlobalState::debugAddLoopCount(SkIntersections* i, const SkIntersectionHelper& wt,
589 : const SkIntersectionHelper& wn) {
590 : for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
591 : SkIntersections::DebugLoop looper = (SkIntersections::DebugLoop) index;
592 : if (fDebugLoopCount[index] >= i->debugLoopCount(looper)) {
593 : continue;
594 : }
595 : fDebugLoopCount[index] = i->debugLoopCount(looper);
596 : fDebugWorstVerb[index * 2] = wt.segment()->verb();
597 : fDebugWorstVerb[index * 2 + 1] = wn.segment()->verb();
598 : sk_bzero(&fDebugWorstPts[index * 8], sizeof(SkPoint) * 8);
599 : memcpy(&fDebugWorstPts[index * 2 * 4], wt.pts(),
600 : (SkPathOpsVerbToPoints(wt.segment()->verb()) + 1) * sizeof(SkPoint));
601 : memcpy(&fDebugWorstPts[(index * 2 + 1) * 4], wn.pts(),
602 : (SkPathOpsVerbToPoints(wn.segment()->verb()) + 1) * sizeof(SkPoint));
603 : fDebugWorstWeight[index * 2] = wt.weight();
604 : fDebugWorstWeight[index * 2 + 1] = wn.weight();
605 : }
606 : i->debugResetLoopCount();
607 : }
608 :
609 : void SkOpGlobalState::debugDoYourWorst(SkOpGlobalState* local) {
610 : for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
611 : if (fDebugLoopCount[index] >= local->fDebugLoopCount[index]) {
612 : continue;
613 : }
614 : fDebugLoopCount[index] = local->fDebugLoopCount[index];
615 : fDebugWorstVerb[index * 2] = local->fDebugWorstVerb[index * 2];
616 : fDebugWorstVerb[index * 2 + 1] = local->fDebugWorstVerb[index * 2 + 1];
617 : memcpy(&fDebugWorstPts[index * 2 * 4], &local->fDebugWorstPts[index * 2 * 4],
618 : sizeof(SkPoint) * 8);
619 : fDebugWorstWeight[index * 2] = local->fDebugWorstWeight[index * 2];
620 : fDebugWorstWeight[index * 2 + 1] = local->fDebugWorstWeight[index * 2 + 1];
621 : }
622 : local->debugResetLoopCounts();
623 : }
624 :
625 : static void dump_curve(SkPath::Verb verb, const SkPoint& pts, float weight) {
626 : if (!verb) {
627 : return;
628 : }
629 : const char* verbs[] = { "", "line", "quad", "conic", "cubic" };
630 : SkDebugf("%s: {{", verbs[verb]);
631 : int ptCount = SkPathOpsVerbToPoints(verb);
632 : for (int index = 0; index <= ptCount; ++index) {
633 : SkDPoint::Dump((&pts)[index]);
634 : if (index < ptCount - 1) {
635 : SkDebugf(", ");
636 : }
637 : }
638 : SkDebugf("}");
639 : if (weight != 1) {
640 : SkDebugf(", ");
641 : if (weight == floorf(weight)) {
642 : SkDebugf("%.0f", weight);
643 : } else {
644 : SkDebugf("%1.9gf", weight);
645 : }
646 : }
647 : SkDebugf("}\n");
648 : }
649 :
650 : void SkOpGlobalState::debugLoopReport() {
651 : const char* loops[] = { "iterations", "coinChecks", "perpCalcs" };
652 : SkDebugf("\n");
653 : for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
654 : SkDebugf("%s: %d\n", loops[index], fDebugLoopCount[index]);
655 : dump_curve(fDebugWorstVerb[index * 2], fDebugWorstPts[index * 2 * 4],
656 : fDebugWorstWeight[index * 2]);
657 : dump_curve(fDebugWorstVerb[index * 2 + 1], fDebugWorstPts[(index * 2 + 1) * 4],
658 : fDebugWorstWeight[index * 2 + 1]);
659 : }
660 : }
661 :
662 : void SkOpGlobalState::debugResetLoopCounts() {
663 : sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
664 : sk_bzero(fDebugWorstVerb, sizeof(fDebugWorstVerb));
665 : sk_bzero(fDebugWorstPts, sizeof(fDebugWorstPts));
666 : sk_bzero(fDebugWorstWeight, sizeof(fDebugWorstWeight));
667 : }
668 : #endif
669 :
670 0 : bool SkOpGlobalState::DebugRunFail() {
671 0 : return SkPathOpsDebug::gRunFail;
672 : }
673 :
674 : // this is const so it can be called by const methods that overwise don't alter state
675 : #if DEBUG_VALIDATE || DEBUG_COIN
676 : void SkOpGlobalState::debugSetPhase(const char* funcName DEBUG_COIN_DECLARE_PARAMS()) const {
677 : auto writable = const_cast<SkOpGlobalState*>(this);
678 : #if DEBUG_VALIDATE
679 : writable->setPhase(phase);
680 : #endif
681 : #if DEBUG_COIN
682 : SkPathOpsDebug::CoinDictEntry* entry = &writable->fCoinDictEntry;
683 : writable->fPreviousFuncName = entry->fFunctionName;
684 : entry->fIteration = iteration;
685 : entry->fLineNumber = lineNo;
686 : entry->fGlitchType = SkPathOpsDebug::kUninitialized_Glitch;
687 : entry->fFunctionName = funcName;
688 : writable->fCoinVisitedDict.add(*entry);
689 : writable->debugAddToCoinChangedDict();
690 : #endif
691 : }
692 : #endif
693 :
694 : #if DEBUG_T_SECT_LOOP_COUNT
695 : void SkIntersections::debugBumpLoopCount(DebugLoop index) {
696 : fDebugLoopCount[index]++;
697 : }
698 :
699 : int SkIntersections::debugLoopCount(DebugLoop index) const {
700 : return fDebugLoopCount[index];
701 : }
702 :
703 : void SkIntersections::debugResetLoopCount() {
704 : sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
705 : }
706 : #endif
707 :
708 : #include "SkPathOpsConic.h"
709 : #include "SkPathOpsCubic.h"
710 :
711 0 : SkDCubic SkDQuad::debugToCubic() const {
712 : SkDCubic cubic;
713 0 : cubic[0] = fPts[0];
714 0 : cubic[2] = fPts[1];
715 0 : cubic[3] = fPts[2];
716 0 : cubic[1].fX = (cubic[0].fX + cubic[2].fX * 2) / 3;
717 0 : cubic[1].fY = (cubic[0].fY + cubic[2].fY * 2) / 3;
718 0 : cubic[2].fX = (cubic[3].fX + cubic[2].fX * 2) / 3;
719 0 : cubic[2].fY = (cubic[3].fY + cubic[2].fY * 2) / 3;
720 0 : return cubic;
721 : }
722 :
723 0 : void SkDQuad::debugSet(const SkDPoint* pts) {
724 0 : memcpy(fPts, pts, sizeof(fPts));
725 0 : SkDEBUGCODE(fDebugGlobalState = nullptr);
726 0 : }
727 :
728 0 : void SkDCubic::debugSet(const SkDPoint* pts) {
729 0 : memcpy(fPts, pts, sizeof(fPts));
730 0 : SkDEBUGCODE(fDebugGlobalState = nullptr);
731 0 : }
732 :
733 0 : void SkDConic::debugSet(const SkDPoint* pts, SkScalar weight) {
734 0 : fPts.debugSet(pts);
735 0 : fWeight = weight;
736 0 : }
737 :
738 0 : void SkDRect::debugInit() {
739 0 : fLeft = fTop = fRight = fBottom = SK_ScalarNaN;
740 0 : }
741 :
742 : #include "SkOpAngle.h"
743 : #include "SkOpSegment.h"
744 :
745 : #if DEBUG_COIN
746 : // commented-out lines keep this in sync with addT()
747 : const SkOpPtT* SkOpSegment::debugAddT(double t, SkPathOpsDebug::GlitchLog* log) const {
748 : debugValidate();
749 : SkPoint pt = this->ptAtT(t);
750 : const SkOpSpanBase* span = &fHead;
751 : do {
752 : const SkOpPtT* result = span->ptT();
753 : if (t == result->fT || this->match(result, this, t, pt)) {
754 : // span->bumpSpanAdds();
755 : return result;
756 : }
757 : if (t < result->fT) {
758 : const SkOpSpan* prev = result->span()->prev();
759 : FAIL_WITH_NULL_IF(!prev, span);
760 : // marks in global state that new op span has been allocated
761 : this->globalState()->setAllocatedOpSpan();
762 : // span->init(this, prev, t, pt);
763 : this->debugValidate();
764 : // #if DEBUG_ADD_T
765 : // SkDebugf("%s insert t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
766 : // span->segment()->debugID(), span->debugID());
767 : // #endif
768 : // span->bumpSpanAdds();
769 : return nullptr;
770 : }
771 : FAIL_WITH_NULL_IF(span != &fTail, span);
772 : } while ((span = span->upCast()->next()));
773 : SkASSERT(0);
774 : return nullptr; // we never get here, but need this to satisfy compiler
775 : }
776 : #endif
777 :
778 : #if DEBUG_ANGLE
779 : void SkOpSegment::debugCheckAngleCoin() const {
780 : const SkOpSpanBase* base = &fHead;
781 : const SkOpSpan* span;
782 : do {
783 : const SkOpAngle* angle = base->fromAngle();
784 : if (angle && angle->debugCheckCoincidence()) {
785 : angle->debugCheckNearCoincidence();
786 : }
787 : if (base->final()) {
788 : break;
789 : }
790 : span = base->upCast();
791 : angle = span->toAngle();
792 : if (angle && angle->debugCheckCoincidence()) {
793 : angle->debugCheckNearCoincidence();
794 : }
795 : } while ((base = span->next()));
796 : }
797 : #endif
798 :
799 : #if DEBUG_COIN
800 : // this mimics the order of the checks in handle coincidence
801 : void SkOpSegment::debugCheckHealth(SkPathOpsDebug::GlitchLog* glitches) const {
802 : debugMoveMultiples(glitches);
803 : debugMoveNearby(glitches);
804 : debugMissingCoincidence(glitches);
805 : }
806 :
807 : // commented-out lines keep this in sync with clearAll()
808 : void SkOpSegment::debugClearAll(SkPathOpsDebug::GlitchLog* glitches) const {
809 : const SkOpSpan* span = &fHead;
810 : do {
811 : this->debugClearOne(span, glitches);
812 : } while ((span = span->next()->upCastable()));
813 : this->globalState()->coincidence()->debugRelease(glitches, this);
814 : }
815 :
816 : // commented-out lines keep this in sync with clearOne()
817 : void SkOpSegment::debugClearOne(const SkOpSpan* span, SkPathOpsDebug::GlitchLog* glitches) const {
818 : if (span->windValue()) glitches->record(SkPathOpsDebug::kCollapsedWindValue_Glitch, span);
819 : if (span->oppValue()) glitches->record(SkPathOpsDebug::kCollapsedOppValue_Glitch, span);
820 : if (!span->done()) glitches->record(SkPathOpsDebug::kCollapsedDone_Glitch, span);
821 : }
822 : #endif
823 :
824 0 : SkOpAngle* SkOpSegment::debugLastAngle() {
825 0 : SkOpAngle* result = nullptr;
826 0 : SkOpSpan* span = this->head();
827 0 : do {
828 0 : if (span->toAngle()) {
829 0 : SkASSERT(!result);
830 0 : result = span->toAngle();
831 : }
832 0 : } while ((span = span->next()->upCastable()));
833 0 : SkASSERT(result);
834 0 : return result;
835 : }
836 :
837 : #if DEBUG_COIN
838 : // commented-out lines keep this in sync with ClearVisited
839 : void SkOpSegment::DebugClearVisited(const SkOpSpanBase* span) {
840 : // reset visited flag back to false
841 : do {
842 : const SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
843 : while ((ptT = ptT->next()) != stopPtT) {
844 : const SkOpSegment* opp = ptT->segment();
845 : opp->resetDebugVisited();
846 : }
847 : } while (!span->final() && (span = span->upCast()->next()));
848 : }
849 : #endif
850 :
851 : #if DEBUG_COIN
852 : // commented-out lines keep this in sync with missingCoincidence()
853 : // look for pairs of undetected coincident curves
854 : // assumes that segments going in have visited flag clear
855 : // Even though pairs of curves correct detect coincident runs, a run may be missed
856 : // if the coincidence is a product of multiple intersections. For instance, given
857 : // curves A, B, and C:
858 : // A-B intersect at a point 1; A-C and B-C intersect at point 2, so near
859 : // the end of C that the intersection is replaced with the end of C.
860 : // Even though A-B correctly do not detect an intersection at point 2,
861 : // the resulting run from point 1 to point 2 is coincident on A and B.
862 : void SkOpSegment::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const {
863 : if (this->done()) {
864 : return;
865 : }
866 : const SkOpSpan* prior = nullptr;
867 : const SkOpSpanBase* spanBase = &fHead;
868 : // bool result = false;
869 : do {
870 : const SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT;
871 : SkASSERT(ptT->span() == spanBase);
872 : while ((ptT = ptT->next()) != spanStopPtT) {
873 : if (ptT->deleted()) {
874 : continue;
875 : }
876 : const SkOpSegment* opp = ptT->span()->segment();
877 : if (opp->done()) {
878 : continue;
879 : }
880 : // when opp is encounted the 1st time, continue; on 2nd encounter, look for coincidence
881 : if (!opp->debugVisited()) {
882 : continue;
883 : }
884 : if (spanBase == &fHead) {
885 : continue;
886 : }
887 : if (ptT->segment() == this) {
888 : continue;
889 : }
890 : const SkOpSpan* span = spanBase->upCastable();
891 : // FIXME?: this assumes that if the opposite segment is coincident then no more
892 : // coincidence needs to be detected. This may not be true.
893 : if (span && span->segment() != opp && span->containsCoincidence(opp)) { // debug has additional condition since it may be called before inner duplicate points have been deleted
894 : continue;
895 : }
896 : if (spanBase->segment() != opp && spanBase->containsCoinEnd(opp)) { // debug has additional condition since it may be called before inner duplicate points have been deleted
897 : continue;
898 : }
899 : const SkOpPtT* priorPtT = nullptr, * priorStopPtT;
900 : // find prior span containing opp segment
901 : const SkOpSegment* priorOpp = nullptr;
902 : const SkOpSpan* priorTest = spanBase->prev();
903 : while (!priorOpp && priorTest) {
904 : priorStopPtT = priorPtT = priorTest->ptT();
905 : while ((priorPtT = priorPtT->next()) != priorStopPtT) {
906 : if (priorPtT->deleted()) {
907 : continue;
908 : }
909 : const SkOpSegment* segment = priorPtT->span()->segment();
910 : if (segment == opp) {
911 : prior = priorTest;
912 : priorOpp = opp;
913 : break;
914 : }
915 : }
916 : priorTest = priorTest->prev();
917 : }
918 : if (!priorOpp) {
919 : continue;
920 : }
921 : if (priorPtT == ptT) {
922 : continue;
923 : }
924 : const SkOpPtT* oppStart = prior->ptT();
925 : const SkOpPtT* oppEnd = spanBase->ptT();
926 : bool swapped = priorPtT->fT > ptT->fT;
927 : if (swapped) {
928 : SkTSwap(priorPtT, ptT);
929 : SkTSwap(oppStart, oppEnd);
930 : }
931 : const SkOpCoincidence* coincidence = this->globalState()->coincidence();
932 : const SkOpPtT* rootPriorPtT = priorPtT->span()->ptT();
933 : const SkOpPtT* rootPtT = ptT->span()->ptT();
934 : const SkOpPtT* rootOppStart = oppStart->span()->ptT();
935 : const SkOpPtT* rootOppEnd = oppEnd->span()->ptT();
936 : if (coincidence->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) {
937 : goto swapBack;
938 : }
939 : if (testForCoincidence(rootPriorPtT, rootPtT, prior, spanBase, opp)) {
940 : // mark coincidence
941 : #if DEBUG_COINCIDENCE_VERBOSE
942 : // SkDebugf("%s coinSpan=%d endSpan=%d oppSpan=%d oppEndSpan=%d\n", __FUNCTION__,
943 : // rootPriorPtT->debugID(), rootPtT->debugID(), rootOppStart->debugID(),
944 : // rootOppEnd->debugID());
945 : #endif
946 : log->record(SkPathOpsDebug::kMissingCoin_Glitch, priorPtT, ptT, oppStart, oppEnd);
947 : // coincidences->add(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
948 : // }
949 : #if DEBUG_COINCIDENCE
950 : // SkASSERT(coincidences->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
951 : #endif
952 : // result = true;
953 : }
954 : swapBack:
955 : if (swapped) {
956 : SkTSwap(priorPtT, ptT);
957 : }
958 : }
959 : } while ((spanBase = spanBase->final() ? nullptr : spanBase->upCast()->next()));
960 : DebugClearVisited(&fHead);
961 : return;
962 : }
963 :
964 : // commented-out lines keep this in sync with moveMultiples()
965 : // if a span has more than one intersection, merge the other segments' span as needed
966 : void SkOpSegment::debugMoveMultiples(SkPathOpsDebug::GlitchLog* glitches) const {
967 : debugValidate();
968 : const SkOpSpanBase* test = &fHead;
969 : do {
970 : int addCount = test->spanAddsCount();
971 : // SkASSERT(addCount >= 1);
972 : if (addCount <= 1) {
973 : continue;
974 : }
975 : const SkOpPtT* startPtT = test->ptT();
976 : const SkOpPtT* testPtT = startPtT;
977 : do { // iterate through all spans associated with start
978 : const SkOpSpanBase* oppSpan = testPtT->span();
979 : if (oppSpan->spanAddsCount() == addCount) {
980 : continue;
981 : }
982 : if (oppSpan->deleted()) {
983 : continue;
984 : }
985 : const SkOpSegment* oppSegment = oppSpan->segment();
986 : if (oppSegment == this) {
987 : continue;
988 : }
989 : // find range of spans to consider merging
990 : const SkOpSpanBase* oppPrev = oppSpan;
991 : const SkOpSpanBase* oppFirst = oppSpan;
992 : while ((oppPrev = oppPrev->prev())) {
993 : if (!roughly_equal(oppPrev->t(), oppSpan->t())) {
994 : break;
995 : }
996 : if (oppPrev->spanAddsCount() == addCount) {
997 : continue;
998 : }
999 : if (oppPrev->deleted()) {
1000 : continue;
1001 : }
1002 : oppFirst = oppPrev;
1003 : }
1004 : const SkOpSpanBase* oppNext = oppSpan;
1005 : const SkOpSpanBase* oppLast = oppSpan;
1006 : while ((oppNext = oppNext->final() ? nullptr : oppNext->upCast()->next())) {
1007 : if (!roughly_equal(oppNext->t(), oppSpan->t())) {
1008 : break;
1009 : }
1010 : if (oppNext->spanAddsCount() == addCount) {
1011 : continue;
1012 : }
1013 : if (oppNext->deleted()) {
1014 : continue;
1015 : }
1016 : oppLast = oppNext;
1017 : }
1018 : if (oppFirst == oppLast) {
1019 : continue;
1020 : }
1021 : const SkOpSpanBase* oppTest = oppFirst;
1022 : do {
1023 : if (oppTest == oppSpan) {
1024 : continue;
1025 : }
1026 : // check to see if the candidate meets specific criteria:
1027 : // it contains spans of segments in test's loop but not including 'this'
1028 : const SkOpPtT* oppStartPtT = oppTest->ptT();
1029 : const SkOpPtT* oppPtT = oppStartPtT;
1030 : while ((oppPtT = oppPtT->next()) != oppStartPtT) {
1031 : const SkOpSegment* oppPtTSegment = oppPtT->segment();
1032 : if (oppPtTSegment == this) {
1033 : goto tryNextSpan;
1034 : }
1035 : const SkOpPtT* matchPtT = startPtT;
1036 : do {
1037 : if (matchPtT->segment() == oppPtTSegment) {
1038 : goto foundMatch;
1039 : }
1040 : } while ((matchPtT = matchPtT->next()) != startPtT);
1041 : goto tryNextSpan;
1042 : foundMatch: // merge oppTest and oppSpan
1043 : oppSegment->debugValidate();
1044 : oppTest->debugMergeMatches(glitches, oppSpan);
1045 : oppTest->debugAddOpp(glitches, oppSpan);
1046 : oppSegment->debugValidate();
1047 : goto checkNextSpan;
1048 : }
1049 : tryNextSpan:
1050 : ;
1051 : } while (oppTest != oppLast && (oppTest = oppTest->upCast()->next()));
1052 : } while ((testPtT = testPtT->next()) != startPtT);
1053 : checkNextSpan:
1054 : ;
1055 : } while ((test = test->final() ? nullptr : test->upCast()->next()));
1056 : debugValidate();
1057 : return;
1058 : }
1059 :
1060 : // commented-out lines keep this in sync with moveNearby()
1061 : // Move nearby t values and pts so they all hang off the same span. Alignment happens later.
1062 : void SkOpSegment::debugMoveNearby(SkPathOpsDebug::GlitchLog* glitches) const {
1063 : debugValidate();
1064 : // release undeleted spans pointing to this seg that are linked to the primary span
1065 : const SkOpSpanBase* spanBase = &fHead;
1066 : do {
1067 : const SkOpPtT* ptT = spanBase->ptT();
1068 : const SkOpPtT* headPtT = ptT;
1069 : while ((ptT = ptT->next()) != headPtT) {
1070 : const SkOpSpanBase* test = ptT->span();
1071 : if (ptT->segment() == this && !ptT->deleted() && test != spanBase
1072 : && test->ptT() == ptT) {
1073 : if (test->final()) {
1074 : if (spanBase == &fHead) {
1075 : glitches->record(SkPathOpsDebug::kMoveNearbyClearAll_Glitch, this);
1076 : // return;
1077 : }
1078 : glitches->record(SkPathOpsDebug::kMoveNearbyReleaseFinal_Glitch, spanBase, ptT);
1079 : } else if (test->prev()) {
1080 : glitches->record(SkPathOpsDebug::kMoveNearbyRelease_Glitch, test, headPtT);
1081 : }
1082 : // break;
1083 : }
1084 : }
1085 : spanBase = spanBase->upCast()->next();
1086 : } while (!spanBase->final());
1087 :
1088 : // This loop looks for adjacent spans which are near by
1089 : spanBase = &fHead;
1090 : do { // iterate through all spans associated with start
1091 : const SkOpSpanBase* test = spanBase->upCast()->next();
1092 : if (this->spansNearby(spanBase, test)) {
1093 : if (test->final()) {
1094 : if (spanBase->prev()) {
1095 : glitches->record(SkPathOpsDebug::kMoveNearbyMergeFinal_Glitch, test);
1096 : } else {
1097 : glitches->record(SkPathOpsDebug::kMoveNearbyClearAll2_Glitch, this);
1098 : // return
1099 : }
1100 : } else {
1101 : glitches->record(SkPathOpsDebug::kMoveNearbyMerge_Glitch, spanBase);
1102 : }
1103 : }
1104 : spanBase = test;
1105 : } while (!spanBase->final());
1106 : debugValidate();
1107 : }
1108 : #endif
1109 :
1110 0 : void SkOpSegment::debugReset() {
1111 0 : this->init(this->fPts, this->fWeight, this->contour(), this->verb());
1112 0 : }
1113 :
1114 : #if DEBUG_COINCIDENCE_ORDER
1115 : void SkOpSegment::debugSetCoinT(int index, SkScalar t) const {
1116 : if (fDebugBaseMax < 0 || fDebugBaseIndex == index) {
1117 : fDebugBaseIndex = index;
1118 : fDebugBaseMin = SkTMin(t, fDebugBaseMin);
1119 : fDebugBaseMax = SkTMax(t, fDebugBaseMax);
1120 : return;
1121 : }
1122 : SkASSERT(fDebugBaseMin >= t || t >= fDebugBaseMax);
1123 : if (fDebugLastMax < 0 || fDebugLastIndex == index) {
1124 : fDebugLastIndex = index;
1125 : fDebugLastMin = SkTMin(t, fDebugLastMin);
1126 : fDebugLastMax = SkTMax(t, fDebugLastMax);
1127 : return;
1128 : }
1129 : SkASSERT(fDebugLastMin >= t || t >= fDebugLastMax);
1130 : SkASSERT((t - fDebugBaseMin > 0) == (fDebugLastMin - fDebugBaseMin > 0));
1131 : }
1132 : #endif
1133 :
1134 : #if DEBUG_ACTIVE_SPANS
1135 : void SkOpSegment::debugShowActiveSpans(SkString* str) const {
1136 : debugValidate();
1137 : if (done()) {
1138 : return;
1139 : }
1140 : int lastId = -1;
1141 : double lastT = -1;
1142 : const SkOpSpan* span = &fHead;
1143 : do {
1144 : if (span->done()) {
1145 : continue;
1146 : }
1147 : if (lastId == this->debugID() && lastT == span->t()) {
1148 : continue;
1149 : }
1150 : lastId = this->debugID();
1151 : lastT = span->t();
1152 : str->appendf("%s id=%d", __FUNCTION__, this->debugID());
1153 : // since endpoints may have be adjusted, show actual computed curves
1154 : SkDCurve curvePart;
1155 : this->subDivide(span, span->next(), &curvePart);
1156 : const SkDPoint* pts = curvePart.fCubic.fPts;
1157 : str->appendf(" (%1.9g,%1.9g", pts[0].fX, pts[0].fY);
1158 : for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1159 : str->appendf(" %1.9g,%1.9g", pts[vIndex].fX, pts[vIndex].fY);
1160 : }
1161 : if (SkPath::kConic_Verb == fVerb) {
1162 : str->appendf(" %1.9gf", curvePart.fConic.fWeight);
1163 : }
1164 : str->appendf(") t=%1.9g tEnd=%1.9g", span->t(), span->next()->t());
1165 : if (span->windSum() == SK_MinS32) {
1166 : str->appendf(" windSum=?");
1167 : } else {
1168 : str->appendf(" windSum=%d", span->windSum());
1169 : }
1170 : if (span->oppValue() && span->oppSum() == SK_MinS32) {
1171 : str->appendf(" oppSum=?");
1172 : } else if (span->oppValue() || span->oppSum() != SK_MinS32) {
1173 : str->appendf(" oppSum=%d", span->oppSum());
1174 : }
1175 : str->appendf(" windValue=%d", span->windValue());
1176 : if (span->oppValue() || span->oppSum() != SK_MinS32) {
1177 : str->appendf(" oppValue=%d", span->oppValue());
1178 : }
1179 : str->appendf("\n");
1180 : } while ((span = span->next()->upCastable()));
1181 : }
1182 : #endif
1183 :
1184 : #if DEBUG_MARK_DONE
1185 : void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding) {
1186 : const SkPoint& pt = span->ptT()->fPt;
1187 : SkDebugf("%s id=%d", fun, this->debugID());
1188 : SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1189 : for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1190 : SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1191 : }
1192 : SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
1193 : span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t());
1194 : if (winding == SK_MinS32) {
1195 : SkDebugf("?");
1196 : } else {
1197 : SkDebugf("%d", winding);
1198 : }
1199 : SkDebugf(" windSum=");
1200 : if (span->windSum() == SK_MinS32) {
1201 : SkDebugf("?");
1202 : } else {
1203 : SkDebugf("%d", span->windSum());
1204 : }
1205 : SkDebugf(" windValue=%d\n", span->windValue());
1206 : }
1207 :
1208 : void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding,
1209 : int oppWinding) {
1210 : const SkPoint& pt = span->ptT()->fPt;
1211 : SkDebugf("%s id=%d", fun, this->debugID());
1212 : SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1213 : for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1214 : SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1215 : }
1216 : SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
1217 : span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t(), winding, oppWinding);
1218 : if (winding == SK_MinS32) {
1219 : SkDebugf("?");
1220 : } else {
1221 : SkDebugf("%d", winding);
1222 : }
1223 : SkDebugf(" newOppSum=");
1224 : if (oppWinding == SK_MinS32) {
1225 : SkDebugf("?");
1226 : } else {
1227 : SkDebugf("%d", oppWinding);
1228 : }
1229 : SkDebugf(" oppSum=");
1230 : if (span->oppSum() == SK_MinS32) {
1231 : SkDebugf("?");
1232 : } else {
1233 : SkDebugf("%d", span->oppSum());
1234 : }
1235 : SkDebugf(" windSum=");
1236 : if (span->windSum() == SK_MinS32) {
1237 : SkDebugf("?");
1238 : } else {
1239 : SkDebugf("%d", span->windSum());
1240 : }
1241 : SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue());
1242 : }
1243 :
1244 : #endif
1245 :
1246 : // loop looking for a pair of angle parts that are too close to be sorted
1247 : /* This is called after other more simple intersection and angle sorting tests have been exhausted.
1248 : This should be rarely called -- the test below is thorough and time consuming.
1249 : This checks the distance between start points; the distance between
1250 : */
1251 : #if DEBUG_ANGLE
1252 : void SkOpAngle::debugCheckNearCoincidence() const {
1253 : const SkOpAngle* test = this;
1254 : do {
1255 : const SkOpSegment* testSegment = test->segment();
1256 : double testStartT = test->start()->t();
1257 : SkDPoint testStartPt = testSegment->dPtAtT(testStartT);
1258 : double testEndT = test->end()->t();
1259 : SkDPoint testEndPt = testSegment->dPtAtT(testEndT);
1260 : double testLenSq = testStartPt.distanceSquared(testEndPt);
1261 : SkDebugf("%s testLenSq=%1.9g id=%d\n", __FUNCTION__, testLenSq, testSegment->debugID());
1262 : double testMidT = (testStartT + testEndT) / 2;
1263 : const SkOpAngle* next = test;
1264 : while ((next = next->fNext) != this) {
1265 : SkOpSegment* nextSegment = next->segment();
1266 : double testMidDistSq = testSegment->distSq(testMidT, next);
1267 : double testEndDistSq = testSegment->distSq(testEndT, next);
1268 : double nextStartT = next->start()->t();
1269 : SkDPoint nextStartPt = nextSegment->dPtAtT(nextStartT);
1270 : double distSq = testStartPt.distanceSquared(nextStartPt);
1271 : double nextEndT = next->end()->t();
1272 : double nextMidT = (nextStartT + nextEndT) / 2;
1273 : double nextMidDistSq = nextSegment->distSq(nextMidT, test);
1274 : double nextEndDistSq = nextSegment->distSq(nextEndT, test);
1275 : SkDebugf("%s distSq=%1.9g testId=%d nextId=%d\n", __FUNCTION__, distSq,
1276 : testSegment->debugID(), nextSegment->debugID());
1277 : SkDebugf("%s testMidDistSq=%1.9g\n", __FUNCTION__, testMidDistSq);
1278 : SkDebugf("%s testEndDistSq=%1.9g\n", __FUNCTION__, testEndDistSq);
1279 : SkDebugf("%s nextMidDistSq=%1.9g\n", __FUNCTION__, nextMidDistSq);
1280 : SkDebugf("%s nextEndDistSq=%1.9g\n", __FUNCTION__, nextEndDistSq);
1281 : SkDPoint nextEndPt = nextSegment->dPtAtT(nextEndT);
1282 : double nextLenSq = nextStartPt.distanceSquared(nextEndPt);
1283 : SkDebugf("%s nextLenSq=%1.9g\n", __FUNCTION__, nextLenSq);
1284 : SkDebugf("\n");
1285 : }
1286 : test = test->fNext;
1287 : } while (test->fNext != this);
1288 : }
1289 : #endif
1290 :
1291 : #if DEBUG_ANGLE
1292 : SkString SkOpAngle::debugPart() const {
1293 : SkString result;
1294 : switch (this->segment()->verb()) {
1295 : case SkPath::kLine_Verb:
1296 : result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fPart.fCurve),
1297 : this->segment()->debugID());
1298 : break;
1299 : case SkPath::kQuad_Verb:
1300 : result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fPart.fCurve),
1301 : this->segment()->debugID());
1302 : break;
1303 : case SkPath::kConic_Verb:
1304 : result.printf(CONIC_DEBUG_STR " id=%d",
1305 : CONIC_DEBUG_DATA(fPart.fCurve, fPart.fCurve.fConic.fWeight),
1306 : this->segment()->debugID());
1307 : break;
1308 : case SkPath::kCubic_Verb:
1309 : result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fPart.fCurve),
1310 : this->segment()->debugID());
1311 : break;
1312 : default:
1313 : SkASSERT(0);
1314 : }
1315 : return result;
1316 : }
1317 : #endif
1318 :
1319 : #if DEBUG_SORT
1320 : void SkOpAngle::debugLoop() const {
1321 : const SkOpAngle* first = this;
1322 : const SkOpAngle* next = this;
1323 : do {
1324 : next->dumpOne(true);
1325 : SkDebugf("\n");
1326 : next = next->fNext;
1327 : } while (next && next != first);
1328 : next = first;
1329 : do {
1330 : next->debugValidate();
1331 : next = next->fNext;
1332 : } while (next && next != first);
1333 : }
1334 : #endif
1335 :
1336 0 : void SkOpAngle::debugValidate() const {
1337 : #if DEBUG_COINCIDENCE
1338 : if (this->globalState()->debugCheckHealth()) {
1339 : return;
1340 : }
1341 : #endif
1342 : #if DEBUG_VALIDATE
1343 : const SkOpAngle* first = this;
1344 : const SkOpAngle* next = this;
1345 : int wind = 0;
1346 : int opp = 0;
1347 : int lastXor = -1;
1348 : int lastOppXor = -1;
1349 : do {
1350 : if (next->unorderable()) {
1351 : return;
1352 : }
1353 : const SkOpSpan* minSpan = next->start()->starter(next->end());
1354 : if (minSpan->windValue() == SK_MinS32) {
1355 : return;
1356 : }
1357 : bool op = next->segment()->operand();
1358 : bool isXor = next->segment()->isXor();
1359 : bool oppXor = next->segment()->oppXor();
1360 : SkASSERT(!DEBUG_LIMIT_WIND_SUM || between(0, minSpan->windValue(), DEBUG_LIMIT_WIND_SUM));
1361 : SkASSERT(!DEBUG_LIMIT_WIND_SUM
1362 : || between(-DEBUG_LIMIT_WIND_SUM, minSpan->oppValue(), DEBUG_LIMIT_WIND_SUM));
1363 : bool useXor = op ? oppXor : isXor;
1364 : SkASSERT(lastXor == -1 || lastXor == (int) useXor);
1365 : lastXor = (int) useXor;
1366 : wind += next->debugSign() * (op ? minSpan->oppValue() : minSpan->windValue());
1367 : if (useXor) {
1368 : wind &= 1;
1369 : }
1370 : useXor = op ? isXor : oppXor;
1371 : SkASSERT(lastOppXor == -1 || lastOppXor == (int) useXor);
1372 : lastOppXor = (int) useXor;
1373 : opp += next->debugSign() * (op ? minSpan->windValue() : minSpan->oppValue());
1374 : if (useXor) {
1375 : opp &= 1;
1376 : }
1377 : next = next->fNext;
1378 : } while (next && next != first);
1379 : SkASSERT(wind == 0 || !SkPathOpsDebug::gRunFail);
1380 : SkASSERT(opp == 0 || !SkPathOpsDebug::gRunFail);
1381 : #endif
1382 0 : }
1383 :
1384 0 : void SkOpAngle::debugValidateNext() const {
1385 : #if !FORCE_RELEASE
1386 : const SkOpAngle* first = this;
1387 : const SkOpAngle* next = first;
1388 : SkTDArray<const SkOpAngle*>(angles);
1389 : do {
1390 : // SkASSERT_RELEASE(next->fSegment->debugContains(next));
1391 : angles.push(next);
1392 : next = next->next();
1393 : if (next == first) {
1394 : break;
1395 : }
1396 : SkASSERT_RELEASE(!angles.contains(next));
1397 : if (!next) {
1398 : return;
1399 : }
1400 : } while (true);
1401 : #endif
1402 0 : }
1403 :
1404 : #ifdef SK_DEBUG
1405 0 : void SkCoincidentSpans::debugStartCheck(const SkOpSpanBase* outer, const SkOpSpanBase* over,
1406 : const SkOpGlobalState* debugState) const {
1407 0 : SkASSERT(coinPtTEnd()->span() == over || !SkOpGlobalState::DebugRunFail());
1408 0 : SkASSERT(oppPtTEnd()->span() == outer || !SkOpGlobalState::DebugRunFail());
1409 0 : }
1410 : #endif
1411 :
1412 : #if DEBUG_COIN
1413 : // sets the span's end to the ptT referenced by the previous-next
1414 : void SkCoincidentSpans::debugCorrectOneEnd(SkPathOpsDebug::GlitchLog* log,
1415 : const SkOpPtT* (SkCoincidentSpans::* getEnd)() const,
1416 : void (SkCoincidentSpans::*setEnd)(const SkOpPtT* ptT) const ) const {
1417 : const SkOpPtT* origPtT = (this->*getEnd)();
1418 : const SkOpSpanBase* origSpan = origPtT->span();
1419 : const SkOpSpan* prev = origSpan->prev();
1420 : const SkOpPtT* testPtT = prev ? prev->next()->ptT()
1421 : : origSpan->upCast()->next()->prev()->ptT();
1422 : if (origPtT != testPtT) {
1423 : log->record(SkPathOpsDebug::kCorrectEnd_Glitch, this, origPtT, testPtT);
1424 : }
1425 : }
1426 :
1427 :
1428 : /* Commented-out lines keep this in sync with correctEnds */
1429 : // FIXME: member pointers have fallen out of favor and can be replaced with
1430 : // an alternative approach.
1431 : // makes all span ends agree with the segment's spans that define them
1432 : void SkCoincidentSpans::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
1433 : this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTStart, nullptr);
1434 : this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTEnd, nullptr);
1435 : this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTStart, nullptr);
1436 : this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTEnd, nullptr);
1437 : }
1438 :
1439 : /* Commented-out lines keep this in sync with expand */
1440 : // expand the range by checking adjacent spans for coincidence
1441 : bool SkCoincidentSpans::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
1442 : bool expanded = false;
1443 : const SkOpSegment* segment = coinPtTStart()->segment();
1444 : const SkOpSegment* oppSegment = oppPtTStart()->segment();
1445 : do {
1446 : const SkOpSpan* start = coinPtTStart()->span()->upCast();
1447 : const SkOpSpan* prev = start->prev();
1448 : const SkOpPtT* oppPtT;
1449 : if (!prev || !(oppPtT = prev->contains(oppSegment))) {
1450 : break;
1451 : }
1452 : double midT = (prev->t() + start->t()) / 2;
1453 : if (!segment->isClose(midT, oppSegment)) {
1454 : break;
1455 : }
1456 : if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, prev->ptT(), oppPtT);
1457 : expanded = true;
1458 : } while (false); // actual continues while expansion is possible
1459 : do {
1460 : const SkOpSpanBase* end = coinPtTEnd()->span();
1461 : SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
1462 : if (next && next->deleted()) {
1463 : break;
1464 : }
1465 : const SkOpPtT* oppPtT;
1466 : if (!next || !(oppPtT = next->contains(oppSegment))) {
1467 : break;
1468 : }
1469 : double midT = (end->t() + next->t()) / 2;
1470 : if (!segment->isClose(midT, oppSegment)) {
1471 : break;
1472 : }
1473 : if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, next->ptT(), oppPtT);
1474 : expanded = true;
1475 : } while (false); // actual continues while expansion is possible
1476 : return expanded;
1477 : }
1478 :
1479 : // description below
1480 : void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* base, const SkOpSpanBase* testSpan) const {
1481 : const SkOpPtT* testPtT = testSpan->ptT();
1482 : const SkOpPtT* stopPtT = testPtT;
1483 : const SkOpSegment* baseSeg = base->segment();
1484 : while ((testPtT = testPtT->next()) != stopPtT) {
1485 : const SkOpSegment* testSeg = testPtT->segment();
1486 : if (testPtT->deleted()) {
1487 : continue;
1488 : }
1489 : if (testSeg == baseSeg) {
1490 : continue;
1491 : }
1492 : if (testPtT->span()->ptT() != testPtT) {
1493 : continue;
1494 : }
1495 : if (this->contains(baseSeg, testSeg, testPtT->fT)) {
1496 : continue;
1497 : }
1498 : // intersect perp with base->ptT() with testPtT->segment()
1499 : SkDVector dxdy = baseSeg->dSlopeAtT(base->t());
1500 : const SkPoint& pt = base->pt();
1501 : SkDLine ray = {{{pt.fX, pt.fY}, {pt.fX + dxdy.fY, pt.fY - dxdy.fX}}};
1502 : SkIntersections i;
1503 : (*CurveIntersectRay[testSeg->verb()])(testSeg->pts(), testSeg->weight(), ray, &i);
1504 : for (int index = 0; index < i.used(); ++index) {
1505 : double t = i[0][index];
1506 : if (!between(0, t, 1)) {
1507 : continue;
1508 : }
1509 : SkDPoint oppPt = i.pt(index);
1510 : if (!oppPt.approximatelyEqual(pt)) {
1511 : continue;
1512 : }
1513 : SkOpSegment* writableSeg = const_cast<SkOpSegment*>(testSeg);
1514 : SkOpPtT* oppStart = writableSeg->addT(t);
1515 : if (oppStart == testPtT) {
1516 : continue;
1517 : }
1518 : SkOpSpan* writableBase = const_cast<SkOpSpan*>(base);
1519 : oppStart->span()->addOpp(writableBase);
1520 : if (oppStart->deleted()) {
1521 : continue;
1522 : }
1523 : SkOpSegment* coinSeg = base->segment();
1524 : SkOpSegment* oppSeg = oppStart->segment();
1525 : double coinTs, coinTe, oppTs, oppTe;
1526 : if (Ordered(coinSeg, oppSeg)) {
1527 : coinTs = base->t();
1528 : coinTe = testSpan->t();
1529 : oppTs = oppStart->fT;
1530 : oppTe = testPtT->fT;
1531 : } else {
1532 : SkTSwap(coinSeg, oppSeg);
1533 : coinTs = oppStart->fT;
1534 : coinTe = testPtT->fT;
1535 : oppTs = base->t();
1536 : oppTe = testSpan->t();
1537 : }
1538 : if (coinTs > coinTe) {
1539 : SkTSwap(coinTs, coinTe);
1540 : SkTSwap(oppTs, oppTe);
1541 : }
1542 : bool added;
1543 : if (this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &added), false) {
1544 : return;
1545 : }
1546 : }
1547 : }
1548 : return;
1549 : }
1550 :
1551 : // description below
1552 : void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* ptT) const {
1553 : FAIL_IF(!ptT->span()->upCastable(), ptT->span());
1554 : const SkOpSpan* base = ptT->span()->upCast();
1555 : const SkOpSpan* prev = base->prev();
1556 : FAIL_IF(!prev, ptT->span());
1557 : if (!prev->isCanceled()) {
1558 : if (this->debugAddEndMovedSpans(log, base, base->prev()), false) {
1559 : return;
1560 : }
1561 : }
1562 : if (!base->isCanceled()) {
1563 : if (this->debugAddEndMovedSpans(log, base, base->next()), false) {
1564 : return;
1565 : }
1566 : }
1567 : return;
1568 : }
1569 :
1570 : /* If A is coincident with B and B includes an endpoint, and A's matching point
1571 : is not the endpoint (i.e., there's an implied line connecting B-end and A)
1572 : then assume that the same implied line may intersect another curve close to B.
1573 : Since we only care about coincidence that was undetected, look at the
1574 : ptT list on B-segment adjacent to the B-end/A ptT loop (not in the loop, but
1575 : next door) and see if the A matching point is close enough to form another
1576 : coincident pair. If so, check for a new coincident span between B-end/A ptT loop
1577 : and the adjacent ptT loop.
1578 : */
1579 : void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log) const {
1580 : const SkCoincidentSpans* span = fHead;
1581 : if (!span) {
1582 : return;
1583 : }
1584 : // fTop = span;
1585 : // fHead = nullptr;
1586 : do {
1587 : if (span->coinPtTStart()->fPt != span->oppPtTStart()->fPt) {
1588 : FAIL_IF(1 == span->coinPtTStart()->fT, span);
1589 : bool onEnd = span->coinPtTStart()->fT == 0;
1590 : bool oOnEnd = zero_or_one(span->oppPtTStart()->fT);
1591 : if (onEnd) {
1592 : if (!oOnEnd) { // if both are on end, any nearby intersect was already found
1593 : if (this->debugAddEndMovedSpans(log, span->oppPtTStart()), false) {
1594 : return;
1595 : }
1596 : }
1597 : } else if (oOnEnd) {
1598 : if (this->debugAddEndMovedSpans(log, span->coinPtTStart()), false) {
1599 : return;
1600 : }
1601 : }
1602 : }
1603 : if (span->coinPtTEnd()->fPt != span->oppPtTEnd()->fPt) {
1604 : bool onEnd = span->coinPtTEnd()->fT == 1;
1605 : bool oOnEnd = zero_or_one(span->oppPtTEnd()->fT);
1606 : if (onEnd) {
1607 : if (!oOnEnd) {
1608 : if (this->debugAddEndMovedSpans(log, span->oppPtTEnd()), false) {
1609 : return;
1610 : }
1611 : }
1612 : } else if (oOnEnd) {
1613 : if (this->debugAddEndMovedSpans(log, span->coinPtTEnd()), false) {
1614 : return;
1615 : }
1616 : }
1617 : }
1618 : } while ((span = span->next()));
1619 : // this->restoreHead();
1620 : return;
1621 : }
1622 :
1623 : /* Commented-out lines keep this in sync with addExpanded */
1624 : // for each coincident pair, match the spans
1625 : // if the spans don't match, add the mssing pt to the segment and loop it in the opposite span
1626 : void SkOpCoincidence::debugAddExpanded(SkPathOpsDebug::GlitchLog* log) const {
1627 : // DEBUG_SET_PHASE();
1628 : const SkCoincidentSpans* coin = this->fHead;
1629 : if (!coin) {
1630 : return;
1631 : }
1632 : do {
1633 : const SkOpPtT* startPtT = coin->coinPtTStart();
1634 : const SkOpPtT* oStartPtT = coin->oppPtTStart();
1635 : double priorT = startPtT->fT;
1636 : double oPriorT = oStartPtT->fT;
1637 : FAIL_IF(!startPtT->contains(oStartPtT), coin);
1638 : SkOPASSERT(coin->coinPtTEnd()->contains(coin->oppPtTEnd()));
1639 : const SkOpSpanBase* start = startPtT->span();
1640 : const SkOpSpanBase* oStart = oStartPtT->span();
1641 : const SkOpSpanBase* end = coin->coinPtTEnd()->span();
1642 : const SkOpSpanBase* oEnd = coin->oppPtTEnd()->span();
1643 : FAIL_IF(oEnd->deleted(), coin);
1644 : FAIL_IF(!start->upCastable(), coin);
1645 : const SkOpSpanBase* test = start->upCast()->next();
1646 : FAIL_IF(!coin->flipped() && !oStart->upCastable(), coin);
1647 : const SkOpSpanBase* oTest = coin->flipped() ? oStart->prev() : oStart->upCast()->next();
1648 : FAIL_IF(!oTest, coin);
1649 : const SkOpSegment* seg = start->segment();
1650 : const SkOpSegment* oSeg = oStart->segment();
1651 : while (test != end || oTest != oEnd) {
1652 : const SkOpPtT* containedOpp = test->ptT()->contains(oSeg);
1653 : const SkOpPtT* containedThis = oTest->ptT()->contains(seg);
1654 : if (!containedOpp || !containedThis) {
1655 : // choose the ends, or the first common pt-t list shared by both
1656 : double nextT, oNextT;
1657 : if (containedOpp) {
1658 : nextT = test->t();
1659 : oNextT = containedOpp->fT;
1660 : } else if (containedThis) {
1661 : nextT = containedThis->fT;
1662 : oNextT = oTest->t();
1663 : } else {
1664 : // iterate through until a pt-t list found that contains the other
1665 : const SkOpSpanBase* walk = test;
1666 : const SkOpPtT* walkOpp;
1667 : do {
1668 : FAIL_IF(!walk->upCastable(), coin);
1669 : walk = walk->upCast()->next();
1670 : } while (!(walkOpp = walk->ptT()->contains(oSeg))
1671 : && walk != coin->coinPtTEnd()->span());
1672 : FAIL_IF(!walkOpp, coin);
1673 : nextT = walk->t();
1674 : oNextT = walkOpp->fT;
1675 : }
1676 : // use t ranges to guess which one is missing
1677 : double startRange = nextT - priorT;
1678 : FAIL_IF(!startRange, coin);
1679 : double startPart = (test->t() - priorT) / startRange;
1680 : double oStartRange = oNextT - oPriorT;
1681 : FAIL_IF(!oStartRange, coin);
1682 : double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange;
1683 : FAIL_IF(startPart == oStartPart, coin);
1684 : bool addToOpp = !containedOpp && !containedThis ? startPart < oStartPart
1685 : : !!containedThis;
1686 : bool startOver = false;
1687 : addToOpp ? log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch,
1688 : oPriorT + oStartRange * startPart, test)
1689 : : log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch,
1690 : priorT + startRange * oStartPart, oTest);
1691 : // FAIL_IF(!success, coin);
1692 : if (startOver) {
1693 : test = start;
1694 : oTest = oStart;
1695 : }
1696 : end = coin->coinPtTEnd()->span();
1697 : oEnd = coin->oppPtTEnd()->span();
1698 : }
1699 : if (test != end) {
1700 : FAIL_IF(!test->upCastable(), coin);
1701 : priorT = test->t();
1702 : test = test->upCast()->next();
1703 : }
1704 : if (oTest != oEnd) {
1705 : oPriorT = oTest->t();
1706 : oTest = coin->flipped() ? oTest->prev() : oTest->upCast()->next();
1707 : FAIL_IF(!oTest, coin);
1708 : }
1709 : }
1710 : } while ((coin = coin->next()));
1711 : return;
1712 : }
1713 :
1714 : /* Commented-out lines keep this in sync addIfMissing() */
1715 : // note that over1s, over1e, over2s, over2e are ordered
1716 : void SkOpCoincidence::debugAddIfMissing(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* over1s, const SkOpPtT* over2s,
1717 : double tStart, double tEnd, const SkOpSegment* coinSeg, const SkOpSegment* oppSeg, bool* added,
1718 : const SkOpPtT* over1e, const SkOpPtT* over2e) const {
1719 : SkASSERT(tStart < tEnd);
1720 : SkASSERT(over1s->fT < over1e->fT);
1721 : SkASSERT(between(over1s->fT, tStart, over1e->fT));
1722 : SkASSERT(between(over1s->fT, tEnd, over1e->fT));
1723 : SkASSERT(over2s->fT < over2e->fT);
1724 : SkASSERT(between(over2s->fT, tStart, over2e->fT));
1725 : SkASSERT(between(over2s->fT, tEnd, over2e->fT));
1726 : SkASSERT(over1s->segment() == over1e->segment());
1727 : SkASSERT(over2s->segment() == over2e->segment());
1728 : SkASSERT(over1s->segment() == over2s->segment());
1729 : SkASSERT(over1s->segment() != coinSeg);
1730 : SkASSERT(over1s->segment() != oppSeg);
1731 : SkASSERT(coinSeg != oppSeg);
1732 : double coinTs, coinTe, oppTs, oppTe;
1733 : coinTs = TRange(over1s, tStart, coinSeg SkDEBUGPARAMS(over1e));
1734 : coinTe = TRange(over1s, tEnd, coinSeg SkDEBUGPARAMS(over1e));
1735 : if (coinSeg->collapsed(coinTs, coinTe)) {
1736 : return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, coinSeg);
1737 : }
1738 : oppTs = TRange(over2s, tStart, oppSeg SkDEBUGPARAMS(over2e));
1739 : oppTe = TRange(over2s, tEnd, oppSeg SkDEBUGPARAMS(over2e));
1740 : if (oppSeg->collapsed(oppTs, oppTe)) {
1741 : return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, oppSeg);
1742 : }
1743 : if (coinTs > coinTe) {
1744 : SkTSwap(coinTs, coinTe);
1745 : SkTSwap(oppTs, oppTe);
1746 : }
1747 : return this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, added
1748 : );
1749 : }
1750 :
1751 : /* Commented-out lines keep this in sync addOrOverlap() */
1752 : // If this is called by addEndMovedSpans(), a returned false propogates out to an abort.
1753 : // If this is called by AddIfMissing(), a returned false indicates there was nothing to add
1754 : void SkOpCoincidence::debugAddOrOverlap(SkPathOpsDebug::GlitchLog* log,
1755 : const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
1756 : double coinTs, double coinTe, double oppTs, double oppTe, bool* added) const {
1757 : SkTDArray<SkCoincidentSpans*> overlaps;
1758 : SkOPASSERT(!fTop); // this is (correctly) reversed in addifMissing()
1759 : if (fTop && !this->checkOverlap(fTop, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe,
1760 : &overlaps)) {
1761 : return;
1762 : }
1763 : if (fHead && !this->checkOverlap(fHead, coinSeg, oppSeg, coinTs,
1764 : coinTe, oppTs, oppTe, &overlaps)) {
1765 : return;
1766 : }
1767 : const SkCoincidentSpans* overlap = overlaps.count() ? overlaps[0] : nullptr;
1768 : for (int index = 1; index < overlaps.count(); ++index) { // combine overlaps before continuing
1769 : const SkCoincidentSpans* test = overlaps[index];
1770 : if (overlap->coinPtTStart()->fT > test->coinPtTStart()->fT) {
1771 : log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTStart());
1772 : }
1773 : if (overlap->coinPtTEnd()->fT < test->coinPtTEnd()->fT) {
1774 : log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTEnd());
1775 : }
1776 : if (overlap->flipped()
1777 : ? overlap->oppPtTStart()->fT < test->oppPtTStart()->fT
1778 : : overlap->oppPtTStart()->fT > test->oppPtTStart()->fT) {
1779 : log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTStart());
1780 : }
1781 : if (overlap->flipped()
1782 : ? overlap->oppPtTEnd()->fT > test->oppPtTEnd()->fT
1783 : : overlap->oppPtTEnd()->fT < test->oppPtTEnd()->fT) {
1784 : log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTEnd());
1785 : }
1786 : if (!fHead) { this->debugRelease(log, fHead, test);
1787 : this->debugRelease(log, fTop, test);
1788 : }
1789 : }
1790 : const SkOpPtT* cs = coinSeg->existing(coinTs, oppSeg);
1791 : const SkOpPtT* ce = coinSeg->existing(coinTe, oppSeg);
1792 : RETURN_FALSE_IF(overlap && cs && ce && overlap->contains(cs, ce), coinSeg);
1793 : RETURN_FALSE_IF(cs != ce || !cs, coinSeg);
1794 : const SkOpPtT* os = oppSeg->existing(oppTs, coinSeg);
1795 : const SkOpPtT* oe = oppSeg->existing(oppTe, coinSeg);
1796 : RETURN_FALSE_IF(overlap && os && oe && overlap->contains(os, oe), oppSeg);
1797 : SkASSERT(true || !cs || !cs->deleted());
1798 : SkASSERT(true || !os || !os->deleted());
1799 : SkASSERT(true || !ce || !ce->deleted());
1800 : SkASSERT(true || !oe || !oe->deleted());
1801 : const SkOpPtT* csExisting = !cs ? coinSeg->existing(coinTs, nullptr) : nullptr;
1802 : const SkOpPtT* ceExisting = !ce ? coinSeg->existing(coinTe, nullptr) : nullptr;
1803 : RETURN_FALSE_IF(csExisting && csExisting == ceExisting, coinSeg);
1804 : RETURN_FALSE_IF(csExisting && (csExisting == ce ||
1805 : csExisting->contains(ceExisting ? ceExisting : ce)), coinSeg);
1806 : RETURN_FALSE_IF(ceExisting && (ceExisting == cs ||
1807 : ceExisting->contains(csExisting ? csExisting : cs)), coinSeg);
1808 : const SkOpPtT* osExisting = !os ? oppSeg->existing(oppTs, nullptr) : nullptr;
1809 : const SkOpPtT* oeExisting = !oe ? oppSeg->existing(oppTe, nullptr) : nullptr;
1810 : RETURN_FALSE_IF(osExisting && osExisting == oeExisting, oppSeg);
1811 : RETURN_FALSE_IF(osExisting && (osExisting == oe ||
1812 : osExisting->contains(oeExisting ? oeExisting : oe)), oppSeg);
1813 : RETURN_FALSE_IF(oeExisting && (oeExisting == os ||
1814 : oeExisting->contains(osExisting ? osExisting : os)), oppSeg);
1815 : bool csDeleted = false, osDeleted = false, ceDeleted = false, oeDeleted = false;
1816 : this->debugValidate();
1817 : if (!cs || !os) {
1818 : if (!cs)
1819 : cs = coinSeg->debugAddT(coinTs, log);
1820 : if (!os)
1821 : os = oppSeg->debugAddT(oppTs, log);
1822 : // RETURN_FALSE_IF(callerAborts, !csWritable || !osWritable);
1823 : if (cs && os) cs->span()->debugAddOpp(log, os->span());
1824 : // cs = csWritable;
1825 : // os = osWritable->active();
1826 : RETURN_FALSE_IF((ce && ce->deleted()) || (oe && oe->deleted()), coinSeg);
1827 : }
1828 : if (!ce || !oe) {
1829 : if (!ce)
1830 : ce = coinSeg->debugAddT(coinTe, log);
1831 : if (!oe)
1832 : oe = oppSeg->debugAddT(oppTe, log);
1833 : if (ce && oe) ce->span()->debugAddOpp(log, oe->span());
1834 : // ce = ceWritable;
1835 : // oe = oeWritable;
1836 : }
1837 : this->debugValidate();
1838 : RETURN_FALSE_IF(csDeleted, coinSeg);
1839 : RETURN_FALSE_IF(osDeleted, oppSeg);
1840 : RETURN_FALSE_IF(ceDeleted, coinSeg);
1841 : RETURN_FALSE_IF(oeDeleted, oppSeg);
1842 : RETURN_FALSE_IF(!cs || !ce || cs == ce || cs->contains(ce) || !os || !oe || os == oe || os->contains(oe), coinSeg);
1843 : bool result = true;
1844 : if (overlap) {
1845 : if (overlap->coinPtTStart()->segment() == coinSeg) {
1846 : log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
1847 : } else {
1848 : if (oppTs > oppTe) {
1849 : SkTSwap(coinTs, coinTe);
1850 : SkTSwap(oppTs, oppTe);
1851 : }
1852 : log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, oppSeg, oppTs, oppTe, coinSeg, coinTs, coinTe);
1853 : }
1854 : #if 0 && DEBUG_COINCIDENCE_VERBOSE
1855 : if (result) {
1856 : overlap->debugShow();
1857 : }
1858 : #endif
1859 : } else {
1860 : log->record(SkPathOpsDebug::kAddMissingCoin_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
1861 : #if 0 && DEBUG_COINCIDENCE_VERBOSE
1862 : fHead->debugShow();
1863 : #endif
1864 : }
1865 : this->debugValidate();
1866 : return (void) result;
1867 : }
1868 :
1869 : // Extra commented-out lines keep this in sync with addMissing()
1870 : /* detects overlaps of different coincident runs on same segment */
1871 : /* does not detect overlaps for pairs without any segments in common */
1872 : // returns true if caller should loop again
1873 : void SkOpCoincidence::debugAddMissing(SkPathOpsDebug::GlitchLog* log, bool* added) const {
1874 : const SkCoincidentSpans* outer = fHead;
1875 : *added = false;
1876 : if (!outer) {
1877 : return;
1878 : }
1879 : // fTop = outer;
1880 : // fHead = nullptr;
1881 : do {
1882 : // addifmissing can modify the list that this is walking
1883 : // save head so that walker can iterate over old data unperturbed
1884 : // addifmissing adds to head freely then add saved head in the end
1885 : const SkOpPtT* ocs = outer->coinPtTStart();
1886 : SkASSERT(!ocs->deleted());
1887 : const SkOpSegment* outerCoin = ocs->segment();
1888 : SkASSERT(!outerCoin->done()); // if it's done, should have already been removed from list
1889 : const SkOpPtT* oos = outer->oppPtTStart();
1890 : if (oos->deleted()) {
1891 : return;
1892 : }
1893 : const SkOpSegment* outerOpp = oos->segment();
1894 : SkASSERT(!outerOpp->done());
1895 : // SkOpSegment* outerCoinWritable = const_cast<SkOpSegment*>(outerCoin);
1896 : // SkOpSegment* outerOppWritable = const_cast<SkOpSegment*>(outerOpp);
1897 : const SkCoincidentSpans* inner = outer;
1898 : while ((inner = inner->next())) {
1899 : this->debugValidate();
1900 : double overS, overE;
1901 : const SkOpPtT* ics = inner->coinPtTStart();
1902 : SkASSERT(!ics->deleted());
1903 : const SkOpSegment* innerCoin = ics->segment();
1904 : SkASSERT(!innerCoin->done());
1905 : const SkOpPtT* ios = inner->oppPtTStart();
1906 : SkASSERT(!ios->deleted());
1907 : const SkOpSegment* innerOpp = ios->segment();
1908 : SkASSERT(!innerOpp->done());
1909 : // SkOpSegment* innerCoinWritable = const_cast<SkOpSegment*>(innerCoin);
1910 : // SkOpSegment* innerOppWritable = const_cast<SkOpSegment*>(innerOpp);
1911 : if (outerCoin == innerCoin) {
1912 : const SkOpPtT* oce = outer->coinPtTEnd();
1913 : if (oce->deleted()) {
1914 : return;
1915 : }
1916 : const SkOpPtT* ice = inner->coinPtTEnd();
1917 : SkASSERT(!ice->deleted());
1918 : if (outerOpp != innerOpp && this->overlap(ocs, oce, ics, ice, &overS, &overE)) {
1919 : this->debugAddIfMissing(log, ocs->starter(oce), ics->starter(ice),
1920 : overS, overE, outerOpp, innerOpp, added,
1921 : ocs->debugEnder(oce),
1922 : ics->debugEnder(ice));
1923 : }
1924 : } else if (outerCoin == innerOpp) {
1925 : const SkOpPtT* oce = outer->coinPtTEnd();
1926 : SkASSERT(!oce->deleted());
1927 : const SkOpPtT* ioe = inner->oppPtTEnd();
1928 : SkASSERT(!ioe->deleted());
1929 : if (outerOpp != innerCoin && this->overlap(ocs, oce, ios, ioe, &overS, &overE)) {
1930 : this->debugAddIfMissing(log, ocs->starter(oce), ios->starter(ioe),
1931 : overS, overE, outerOpp, innerCoin, added,
1932 : ocs->debugEnder(oce),
1933 : ios->debugEnder(ioe));
1934 : }
1935 : } else if (outerOpp == innerCoin) {
1936 : const SkOpPtT* ooe = outer->oppPtTEnd();
1937 : SkASSERT(!ooe->deleted());
1938 : const SkOpPtT* ice = inner->coinPtTEnd();
1939 : SkASSERT(!ice->deleted());
1940 : SkASSERT(outerCoin != innerOpp);
1941 : if (this->overlap(oos, ooe, ics, ice, &overS, &overE)) {
1942 : this->debugAddIfMissing(log, oos->starter(ooe), ics->starter(ice),
1943 : overS, overE, outerCoin, innerOpp, added,
1944 : oos->debugEnder(ooe),
1945 : ics->debugEnder(ice));
1946 : }
1947 : } else if (outerOpp == innerOpp) {
1948 : const SkOpPtT* ooe = outer->oppPtTEnd();
1949 : SkASSERT(!ooe->deleted());
1950 : const SkOpPtT* ioe = inner->oppPtTEnd();
1951 : if (ioe->deleted()) {
1952 : return;
1953 : }
1954 : SkASSERT(outerCoin != innerCoin);
1955 : if (this->overlap(oos, ooe, ios, ioe, &overS, &overE)) {
1956 : this->debugAddIfMissing(log, oos->starter(ooe), ios->starter(ioe),
1957 : overS, overE, outerCoin, innerCoin, added,
1958 : oos->debugEnder(ooe),
1959 : ios->debugEnder(ioe));
1960 : }
1961 : }
1962 : this->debugValidate();
1963 : }
1964 : } while ((outer = outer->next()));
1965 : // this->restoreHead();
1966 : return;
1967 : }
1968 :
1969 : // Commented-out lines keep this in sync with release()
1970 : void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkCoincidentSpans* remove) const {
1971 : const SkCoincidentSpans* head = coin;
1972 : const SkCoincidentSpans* prev = nullptr;
1973 : const SkCoincidentSpans* next;
1974 : do {
1975 : next = coin->next();
1976 : if (coin == remove) {
1977 : if (prev) {
1978 : // prev->setNext(next);
1979 : } else if (head == fHead) {
1980 : // fHead = next;
1981 : } else {
1982 : // fTop = next;
1983 : }
1984 : log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
1985 : }
1986 : prev = coin;
1987 : } while ((coin = next));
1988 : return;
1989 : }
1990 :
1991 : void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* deleted) const {
1992 : const SkCoincidentSpans* coin = fHead;
1993 : if (!coin) {
1994 : return;
1995 : }
1996 : do {
1997 : if (coin->coinPtTStart()->segment() == deleted
1998 : || coin->coinPtTEnd()->segment() == deleted
1999 : || coin->oppPtTStart()->segment() == deleted
2000 : || coin->oppPtTEnd()->segment() == deleted) {
2001 : log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
2002 : }
2003 : } while ((coin = coin->next()));
2004 : }
2005 :
2006 : // Commented-out lines keep this in sync with expand()
2007 : // expand the range by checking adjacent spans for coincidence
2008 : bool SkOpCoincidence::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
2009 : const SkCoincidentSpans* coin = fHead;
2010 : if (!coin) {
2011 : return false;
2012 : }
2013 : bool expanded = false;
2014 : do {
2015 : if (coin->debugExpand(log)) {
2016 : // check to see if multiple spans expanded so they are now identical
2017 : const SkCoincidentSpans* test = fHead;
2018 : do {
2019 : if (coin == test) {
2020 : continue;
2021 : }
2022 : if (coin->coinPtTStart() == test->coinPtTStart()
2023 : && coin->oppPtTStart() == test->oppPtTStart()) {
2024 : if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, fHead, test->coinPtTStart());
2025 : break;
2026 : }
2027 : } while ((test = test->next()));
2028 : expanded = true;
2029 : }
2030 : } while ((coin = coin->next()));
2031 : return expanded;
2032 : }
2033 :
2034 : // Commented-out lines keep this in sync with mark()
2035 : /* this sets up the coincidence links in the segments when the coincidence crosses multiple spans */
2036 : void SkOpCoincidence::debugMark(SkPathOpsDebug::GlitchLog* log) const {
2037 : const SkCoincidentSpans* coin = fHead;
2038 : if (!coin) {
2039 : return;
2040 : }
2041 : do {
2042 : FAIL_IF(!coin->coinPtTStartWritable()->span()->upCastable(), coin);
2043 : const SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast();
2044 : // SkASSERT(start->deleted());
2045 : const SkOpSpanBase* end = coin->coinPtTEndWritable()->span();
2046 : // SkASSERT(end->deleted());
2047 : const SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span();
2048 : // SkASSERT(oStart->deleted());
2049 : const SkOpSpanBase* oEnd = coin->oppPtTEndWritable()->span();
2050 : // SkASSERT(oEnd->deleted());
2051 : bool flipped = coin->flipped();
2052 : if (flipped) {
2053 : SkTSwap(oStart, oEnd);
2054 : }
2055 : /* coin and opp spans may not match up. Mark the ends, and then let the interior
2056 : get marked as many times as the spans allow */
2057 : start->debugInsertCoincidence(log, oStart->upCast());
2058 : end->debugInsertCoinEnd(log, oEnd);
2059 : const SkOpSegment* segment = start->segment();
2060 : const SkOpSegment* oSegment = oStart->segment();
2061 : const SkOpSpanBase* next = start;
2062 : const SkOpSpanBase* oNext = oStart;
2063 : bool ordered;
2064 : FAIL_IF(!coin->ordered(&ordered), coin);
2065 : while ((next = next->upCast()->next()) != end) {
2066 : FAIL_IF(!next->upCastable(), coin);
2067 : if (next->upCast()->debugInsertCoincidence(log, oSegment, flipped, ordered), false) {
2068 : return;
2069 : }
2070 : }
2071 : while ((oNext = oNext->upCast()->next()) != oEnd) {
2072 : FAIL_IF(!oNext->upCastable(), coin);
2073 : if (oNext->upCast()->debugInsertCoincidence(log, segment, flipped, ordered), false) {
2074 : return;
2075 : }
2076 : }
2077 : } while ((coin = coin->next()));
2078 : return;
2079 : }
2080 : #endif
2081 :
2082 : #if DEBUG_COIN
2083 : // Commented-out lines keep this in sync with markCollapsed()
2084 : void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkOpPtT* test) const {
2085 : const SkCoincidentSpans* head = coin;
2086 : while (coin) {
2087 : if (coin->collapsed(test)) {
2088 : if (zero_or_one(coin->coinPtTStart()->fT) && zero_or_one(coin->coinPtTEnd()->fT)) {
2089 : log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
2090 : }
2091 : if (zero_or_one(coin->oppPtTStart()->fT) && zero_or_one(coin->oppPtTEnd()->fT)) {
2092 : log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
2093 : }
2094 : this->debugRelease(log, head, coin);
2095 : }
2096 : coin = coin->next();
2097 : }
2098 : }
2099 :
2100 : // Commented-out lines keep this in sync with markCollapsed()
2101 : void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* test) const {
2102 : this->debugMarkCollapsed(log, fHead, test);
2103 : this->debugMarkCollapsed(log, fTop, test);
2104 : }
2105 : #endif
2106 :
2107 0 : void SkCoincidentSpans::debugShow() const {
2108 0 : SkDebugf("coinSpan - id=%d t=%1.9g tEnd=%1.9g\n", coinPtTStart()->segment()->debugID(),
2109 0 : coinPtTStart()->fT, coinPtTEnd()->fT);
2110 0 : SkDebugf("coinSpan + id=%d t=%1.9g tEnd=%1.9g\n", oppPtTStart()->segment()->debugID(),
2111 0 : oppPtTStart()->fT, oppPtTEnd()->fT);
2112 0 : }
2113 :
2114 0 : void SkOpCoincidence::debugShowCoincidence() const {
2115 : #if DEBUG_COINCIDENCE
2116 : const SkCoincidentSpans* span = fHead;
2117 : while (span) {
2118 : span->debugShow();
2119 : span = span->next();
2120 : }
2121 : #endif
2122 0 : }
2123 :
2124 : #if DEBUG_COIN
2125 : static void DebugCheckBetween(const SkOpSpanBase* next, const SkOpSpanBase* end,
2126 : double oStart, double oEnd, const SkOpSegment* oSegment,
2127 : SkPathOpsDebug::GlitchLog* log) {
2128 : SkASSERT(next != end);
2129 : SkASSERT(!next->contains(end) || log);
2130 : if (next->t() > end->t()) {
2131 : SkTSwap(next, end);
2132 : }
2133 : do {
2134 : const SkOpPtT* ptT = next->ptT();
2135 : int index = 0;
2136 : bool somethingBetween = false;
2137 : do {
2138 : ++index;
2139 : ptT = ptT->next();
2140 : const SkOpPtT* checkPtT = next->ptT();
2141 : if (ptT == checkPtT) {
2142 : break;
2143 : }
2144 : bool looped = false;
2145 : for (int check = 0; check < index; ++check) {
2146 : if ((looped = checkPtT == ptT)) {
2147 : break;
2148 : }
2149 : checkPtT = checkPtT->next();
2150 : }
2151 : if (looped) {
2152 : SkASSERT(0);
2153 : break;
2154 : }
2155 : if (ptT->deleted()) {
2156 : continue;
2157 : }
2158 : if (ptT->segment() != oSegment) {
2159 : continue;
2160 : }
2161 : somethingBetween |= between(oStart, ptT->fT, oEnd);
2162 : } while (true);
2163 : SkASSERT(somethingBetween);
2164 : } while (next != end && (next = next->upCast()->next()));
2165 : }
2166 :
2167 : static void DebugCheckOverlap(const SkCoincidentSpans* test, const SkCoincidentSpans* list,
2168 : SkPathOpsDebug::GlitchLog* log) {
2169 : if (!list) {
2170 : return;
2171 : }
2172 : const SkOpSegment* coinSeg = test->coinPtTStart()->segment();
2173 : SkASSERT(coinSeg == test->coinPtTEnd()->segment());
2174 : const SkOpSegment* oppSeg = test->oppPtTStart()->segment();
2175 : SkASSERT(oppSeg == test->oppPtTEnd()->segment());
2176 : SkASSERT(coinSeg != test->oppPtTStart()->segment());
2177 : SkDEBUGCODE(double tcs = test->coinPtTStart()->fT);
2178 : SkASSERT(between(0, tcs, 1));
2179 : SkDEBUGCODE(double tce = test->coinPtTEnd()->fT);
2180 : SkASSERT(between(0, tce, 1));
2181 : SkASSERT(tcs < tce);
2182 : double tos = test->oppPtTStart()->fT;
2183 : SkASSERT(between(0, tos, 1));
2184 : double toe = test->oppPtTEnd()->fT;
2185 : SkASSERT(between(0, toe, 1));
2186 : SkASSERT(tos != toe);
2187 : if (tos > toe) {
2188 : SkTSwap(tos, toe);
2189 : }
2190 : do {
2191 : double lcs, lce, los, loe;
2192 : if (coinSeg == list->coinPtTStart()->segment()) {
2193 : if (oppSeg != list->oppPtTStart()->segment()) {
2194 : continue;
2195 : }
2196 : lcs = list->coinPtTStart()->fT;
2197 : lce = list->coinPtTEnd()->fT;
2198 : los = list->oppPtTStart()->fT;
2199 : loe = list->oppPtTEnd()->fT;
2200 : if (los > loe) {
2201 : SkTSwap(los, loe);
2202 : }
2203 : } else if (coinSeg == list->oppPtTStart()->segment()) {
2204 : if (oppSeg != list->coinPtTStart()->segment()) {
2205 : continue;
2206 : }
2207 : lcs = list->oppPtTStart()->fT;
2208 : lce = list->oppPtTEnd()->fT;
2209 : if (lcs > lce) {
2210 : SkTSwap(lcs, lce);
2211 : }
2212 : los = list->coinPtTStart()->fT;
2213 : loe = list->coinPtTEnd()->fT;
2214 : } else {
2215 : continue;
2216 : }
2217 : SkASSERT(tce < lcs || lce < tcs);
2218 : SkASSERT(toe < los || loe < tos);
2219 : } while ((list = list->next()));
2220 : }
2221 :
2222 :
2223 : static void DebugCheckOverlapTop(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
2224 : SkPathOpsDebug::GlitchLog* log) {
2225 : // check for overlapping coincident spans
2226 : const SkCoincidentSpans* test = head;
2227 : while (test) {
2228 : const SkCoincidentSpans* next = test->next();
2229 : DebugCheckOverlap(test, next, log);
2230 : DebugCheckOverlap(test, opt, log);
2231 : test = next;
2232 : }
2233 : }
2234 :
2235 : static void DebugValidate(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
2236 : SkPathOpsDebug::GlitchLog* log) {
2237 : // look for pts inside coincident spans that are not inside the opposite spans
2238 : const SkCoincidentSpans* coin = head;
2239 : while (coin) {
2240 : SkASSERT(SkOpCoincidence::Ordered(coin->coinPtTStart()->segment(),
2241 : coin->oppPtTStart()->segment()));
2242 : SkASSERT(coin->coinPtTStart()->span()->ptT() == coin->coinPtTStart());
2243 : SkASSERT(coin->coinPtTEnd()->span()->ptT() == coin->coinPtTEnd());
2244 : SkASSERT(coin->oppPtTStart()->span()->ptT() == coin->oppPtTStart());
2245 : SkASSERT(coin->oppPtTEnd()->span()->ptT() == coin->oppPtTEnd());
2246 : coin = coin->next();
2247 : }
2248 : DebugCheckOverlapTop(head, opt, log);
2249 : }
2250 : #endif
2251 :
2252 0 : void SkOpCoincidence::debugValidate() const {
2253 : #if DEBUG_COINCIDENCE
2254 : DebugValidate(fHead, fTop, nullptr);
2255 : DebugValidate(fTop, nullptr, nullptr);
2256 : #endif
2257 0 : }
2258 :
2259 : #if DEBUG_COIN
2260 : static void DebugCheckBetween(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
2261 : SkPathOpsDebug::GlitchLog* log) {
2262 : // look for pts inside coincident spans that are not inside the opposite spans
2263 : const SkCoincidentSpans* coin = head;
2264 : while (coin) {
2265 : DebugCheckBetween(coin->coinPtTStart()->span(), coin->coinPtTEnd()->span(),
2266 : coin->oppPtTStart()->fT, coin->oppPtTEnd()->fT, coin->oppPtTStart()->segment(),
2267 : log);
2268 : DebugCheckBetween(coin->oppPtTStart()->span(), coin->oppPtTEnd()->span(),
2269 : coin->coinPtTStart()->fT, coin->coinPtTEnd()->fT, coin->coinPtTStart()->segment(),
2270 : log);
2271 : coin = coin->next();
2272 : }
2273 : DebugCheckOverlapTop(head, opt, log);
2274 : }
2275 : #endif
2276 :
2277 0 : void SkOpCoincidence::debugCheckBetween() const {
2278 : #if DEBUG_COINCIDENCE
2279 : if (fGlobalState->debugCheckHealth()) {
2280 : return;
2281 : }
2282 : DebugCheckBetween(fHead, fTop, nullptr);
2283 : DebugCheckBetween(fTop, nullptr, nullptr);
2284 : #endif
2285 0 : }
2286 :
2287 : #if DEBUG_COIN
2288 : void SkOpContour::debugCheckHealth(SkPathOpsDebug::GlitchLog* log) const {
2289 : const SkOpSegment* segment = &fHead;
2290 : do {
2291 : segment->debugCheckHealth(log);
2292 : } while ((segment = segment->next()));
2293 : }
2294 :
2295 : void SkOpCoincidence::debugCheckValid(SkPathOpsDebug::GlitchLog* log) const {
2296 : #if DEBUG_VALIDATE
2297 : DebugValidate(fHead, fTop, log);
2298 : DebugValidate(fTop, nullptr, log);
2299 : #endif
2300 : }
2301 :
2302 : void SkOpCoincidence::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
2303 : const SkCoincidentSpans* coin = fHead;
2304 : if (!coin) {
2305 : return;
2306 : }
2307 : do {
2308 : coin->debugCorrectEnds(log);
2309 : } while ((coin = coin->next()));
2310 : }
2311 :
2312 : // commmented-out lines keep this aligned with missingCoincidence()
2313 : void SkOpContour::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const {
2314 : // SkASSERT(fCount > 0);
2315 : const SkOpSegment* segment = &fHead;
2316 : // bool result = false;
2317 : do {
2318 : if (segment->debugMissingCoincidence(log), false) {
2319 : // result = true;
2320 : }
2321 : segment = segment->next();
2322 : } while (segment);
2323 : return;
2324 : }
2325 :
2326 : void SkOpContour::debugMoveMultiples(SkPathOpsDebug::GlitchLog* log) const {
2327 : SkASSERT(fCount > 0);
2328 : const SkOpSegment* segment = &fHead;
2329 : do {
2330 : if (segment->debugMoveMultiples(log), false) {
2331 : return;
2332 : }
2333 : } while ((segment = segment->next()));
2334 : return;
2335 : }
2336 :
2337 : void SkOpContour::debugMoveNearby(SkPathOpsDebug::GlitchLog* log) const {
2338 : SkASSERT(fCount > 0);
2339 : const SkOpSegment* segment = &fHead;
2340 : do {
2341 : segment->debugMoveNearby(log);
2342 : } while ((segment = segment->next()));
2343 : }
2344 : #endif
2345 :
2346 : #if DEBUG_COINCIDENCE_ORDER
2347 : void SkOpSegment::debugResetCoinT() const {
2348 : fDebugBaseIndex = -1;
2349 : fDebugBaseMin = 1;
2350 : fDebugBaseMax = -1;
2351 : fDebugLastIndex = -1;
2352 : fDebugLastMin = 1;
2353 : fDebugLastMax = -1;
2354 : }
2355 : #endif
2356 :
2357 0 : void SkOpSegment::debugValidate() const {
2358 : #if DEBUG_COINCIDENCE_ORDER
2359 : {
2360 : const SkOpSpanBase* span = &fHead;
2361 : do {
2362 : span->debugResetCoinT();
2363 : } while (!span->final() && (span = span->upCast()->next()));
2364 : span = &fHead;
2365 : int index = 0;
2366 : do {
2367 : span->debugSetCoinT(index++);
2368 : } while (!span->final() && (span = span->upCast()->next()));
2369 : }
2370 : #endif
2371 : #if DEBUG_COINCIDENCE
2372 : if (this->globalState()->debugCheckHealth()) {
2373 : return;
2374 : }
2375 : #endif
2376 : #if DEBUG_VALIDATE
2377 : const SkOpSpanBase* span = &fHead;
2378 : double lastT = -1;
2379 : const SkOpSpanBase* prev = nullptr;
2380 : int count = 0;
2381 : int done = 0;
2382 : do {
2383 : if (!span->final()) {
2384 : ++count;
2385 : done += span->upCast()->done() ? 1 : 0;
2386 : }
2387 : SkASSERT(span->segment() == this);
2388 : SkASSERT(!prev || prev->upCast()->next() == span);
2389 : SkASSERT(!prev || prev == span->prev());
2390 : prev = span;
2391 : double t = span->ptT()->fT;
2392 : SkASSERT(lastT < t);
2393 : lastT = t;
2394 : span->debugValidate();
2395 : } while (!span->final() && (span = span->upCast()->next()));
2396 : SkASSERT(count == fCount);
2397 : SkASSERT(done == fDoneCount);
2398 : SkASSERT(count >= fDoneCount);
2399 : SkASSERT(span->final());
2400 : span->debugValidate();
2401 : #endif
2402 0 : }
2403 :
2404 : #if DEBUG_COIN
2405 :
2406 : // Commented-out lines keep this in sync with addOpp()
2407 : void SkOpSpanBase::debugAddOpp(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
2408 : const SkOpPtT* oppPrev = this->ptT()->oppPrev(opp->ptT());
2409 : if (!oppPrev) {
2410 : return;
2411 : }
2412 : this->debugMergeMatches(log, opp);
2413 : this->ptT()->debugAddOpp(opp->ptT(), oppPrev);
2414 : this->debugCheckForCollapsedCoincidence(log);
2415 : }
2416 :
2417 : // Commented-out lines keep this in sync with checkForCollapsedCoincidence()
2418 : void SkOpSpanBase::debugCheckForCollapsedCoincidence(SkPathOpsDebug::GlitchLog* log) const {
2419 : const SkOpCoincidence* coins = this->globalState()->coincidence();
2420 : if (coins->isEmpty()) {
2421 : return;
2422 : }
2423 : // the insert above may have put both ends of a coincident run in the same span
2424 : // for each coincident ptT in loop; see if its opposite in is also in the loop
2425 : // this implementation is the motivation for marking that a ptT is referenced by a coincident span
2426 : const SkOpPtT* head = this->ptT();
2427 : const SkOpPtT* test = head;
2428 : do {
2429 : if (!test->coincident()) {
2430 : continue;
2431 : }
2432 : coins->debugMarkCollapsed(log, test);
2433 : } while ((test = test->next()) != head);
2434 : }
2435 : #endif
2436 :
2437 0 : bool SkOpSpanBase::debugCoinEndLoopCheck() const {
2438 0 : int loop = 0;
2439 0 : const SkOpSpanBase* next = this;
2440 : SkOpSpanBase* nextCoin;
2441 0 : do {
2442 0 : nextCoin = next->fCoinEnd;
2443 0 : SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin);
2444 0 : for (int check = 1; check < loop - 1; ++check) {
2445 0 : const SkOpSpanBase* checkCoin = this->fCoinEnd;
2446 0 : const SkOpSpanBase* innerCoin = checkCoin;
2447 0 : for (int inner = check + 1; inner < loop; ++inner) {
2448 0 : innerCoin = innerCoin->fCoinEnd;
2449 0 : if (checkCoin == innerCoin) {
2450 0 : SkDebugf("*** bad coincident end loop ***\n");
2451 0 : return false;
2452 : }
2453 : }
2454 : }
2455 0 : ++loop;
2456 0 : } while ((next = nextCoin) && next != this);
2457 0 : return true;
2458 : }
2459 :
2460 : #if DEBUG_COIN
2461 : // Commented-out lines keep this in sync with insertCoinEnd()
2462 : void SkOpSpanBase::debugInsertCoinEnd(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* coin) const {
2463 : if (containsCoinEnd(coin)) {
2464 : // SkASSERT(coin->containsCoinEnd(this));
2465 : return;
2466 : }
2467 : debugValidate();
2468 : // SkASSERT(this != coin);
2469 : log->record(SkPathOpsDebug::kMarkCoinEnd_Glitch, this, coin);
2470 : // coin->fCoinEnd = this->fCoinEnd;
2471 : // this->fCoinEnd = coinNext;
2472 : debugValidate();
2473 : }
2474 :
2475 : // Commented-out lines keep this in sync with mergeMatches()
2476 : // Look to see if pt-t linked list contains same segment more than once
2477 : // if so, and if each pt-t is directly pointed to by spans in that segment,
2478 : // merge them
2479 : // keep the points, but remove spans so that the segment doesn't have 2 or more
2480 : // spans pointing to the same pt-t loop at different loop elements
2481 : void SkOpSpanBase::debugMergeMatches(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
2482 : const SkOpPtT* test = &fPtT;
2483 : const SkOpPtT* testNext;
2484 : const SkOpPtT* stop = test;
2485 : do {
2486 : testNext = test->next();
2487 : if (test->deleted()) {
2488 : continue;
2489 : }
2490 : const SkOpSpanBase* testBase = test->span();
2491 : SkASSERT(testBase->ptT() == test);
2492 : const SkOpSegment* segment = test->segment();
2493 : if (segment->done()) {
2494 : continue;
2495 : }
2496 : const SkOpPtT* inner = opp->ptT();
2497 : const SkOpPtT* innerStop = inner;
2498 : do {
2499 : if (inner->segment() != segment) {
2500 : continue;
2501 : }
2502 : if (inner->deleted()) {
2503 : continue;
2504 : }
2505 : const SkOpSpanBase* innerBase = inner->span();
2506 : SkASSERT(innerBase->ptT() == inner);
2507 : // when the intersection is first detected, the span base is marked if there are
2508 : // more than one point in the intersection.
2509 : // if (!innerBase->hasMultipleHint() && !testBase->hasMultipleHint()) {
2510 : if (!zero_or_one(inner->fT)) {
2511 : log->record(SkPathOpsDebug::kMergeMatches_Glitch, innerBase, test);
2512 : } else {
2513 : SkASSERT(inner->fT != test->fT);
2514 : if (!zero_or_one(test->fT)) {
2515 : log->record(SkPathOpsDebug::kMergeMatches_Glitch, testBase, inner);
2516 : } else {
2517 : log->record(SkPathOpsDebug::kMergeMatches_Glitch, segment);
2518 : // SkDEBUGCODE(testBase->debugSetDeleted());
2519 : // test->setDeleted();
2520 : // SkDEBUGCODE(innerBase->debugSetDeleted());
2521 : // inner->setDeleted();
2522 : }
2523 : }
2524 : #ifdef SK_DEBUG // assert if another undeleted entry points to segment
2525 : const SkOpPtT* debugInner = inner;
2526 : while ((debugInner = debugInner->next()) != innerStop) {
2527 : if (debugInner->segment() != segment) {
2528 : continue;
2529 : }
2530 : if (debugInner->deleted()) {
2531 : continue;
2532 : }
2533 : SkOPASSERT(0);
2534 : }
2535 : #endif
2536 : break;
2537 : // }
2538 : break;
2539 : } while ((inner = inner->next()) != innerStop);
2540 : } while ((test = testNext) != stop);
2541 : this->debugCheckForCollapsedCoincidence(log);
2542 : }
2543 :
2544 : #endif
2545 :
2546 0 : void SkOpSpanBase::debugResetCoinT() const {
2547 : #if DEBUG_COINCIDENCE_ORDER
2548 : const SkOpPtT* ptT = &fPtT;
2549 : do {
2550 : ptT->debugResetCoinT();
2551 : ptT = ptT->next();
2552 : } while (ptT != &fPtT);
2553 : #endif
2554 0 : }
2555 :
2556 0 : void SkOpSpanBase::debugSetCoinT(int index) const {
2557 : #if DEBUG_COINCIDENCE_ORDER
2558 : const SkOpPtT* ptT = &fPtT;
2559 : do {
2560 : if (!ptT->deleted()) {
2561 : ptT->debugSetCoinT(index);
2562 : }
2563 : ptT = ptT->next();
2564 : } while (ptT != &fPtT);
2565 : #endif
2566 0 : }
2567 :
2568 0 : const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const {
2569 0 : const SkOpSpanBase* end = *endPtr;
2570 0 : SkASSERT(this->segment() == end->segment());
2571 : const SkOpSpanBase* result;
2572 0 : if (t() < end->t()) {
2573 0 : result = this;
2574 : } else {
2575 0 : result = end;
2576 0 : *endPtr = this;
2577 : }
2578 0 : return result->upCast();
2579 : }
2580 :
2581 0 : void SkOpSpanBase::debugValidate() const {
2582 : #if DEBUG_COINCIDENCE
2583 : if (this->globalState()->debugCheckHealth()) {
2584 : return;
2585 : }
2586 : #endif
2587 : #if DEBUG_VALIDATE
2588 : const SkOpPtT* ptT = &fPtT;
2589 : SkASSERT(ptT->span() == this);
2590 : do {
2591 : // SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt));
2592 : ptT->debugValidate();
2593 : ptT = ptT->next();
2594 : } while (ptT != &fPtT);
2595 : SkASSERT(this->debugCoinEndLoopCheck());
2596 : if (!this->final()) {
2597 : SkASSERT(this->upCast()->debugCoinLoopCheck());
2598 : }
2599 : if (fFromAngle) {
2600 : fFromAngle->debugValidate();
2601 : }
2602 : if (!this->final() && this->upCast()->toAngle()) {
2603 : this->upCast()->toAngle()->debugValidate();
2604 : }
2605 : #endif
2606 0 : }
2607 :
2608 0 : bool SkOpSpan::debugCoinLoopCheck() const {
2609 0 : int loop = 0;
2610 0 : const SkOpSpan* next = this;
2611 : SkOpSpan* nextCoin;
2612 0 : do {
2613 0 : nextCoin = next->fCoincident;
2614 0 : SkASSERT(nextCoin == this || nextCoin->fCoincident != nextCoin);
2615 0 : for (int check = 1; check < loop - 1; ++check) {
2616 0 : const SkOpSpan* checkCoin = this->fCoincident;
2617 0 : const SkOpSpan* innerCoin = checkCoin;
2618 0 : for (int inner = check + 1; inner < loop; ++inner) {
2619 0 : innerCoin = innerCoin->fCoincident;
2620 0 : if (checkCoin == innerCoin) {
2621 0 : SkDebugf("*** bad coincident loop ***\n");
2622 0 : return false;
2623 : }
2624 : }
2625 : }
2626 0 : ++loop;
2627 0 : } while ((next = nextCoin) && next != this);
2628 0 : return true;
2629 : }
2630 :
2631 : #if DEBUG_COIN
2632 : // Commented-out lines keep this in sync with insertCoincidence() in header
2633 : void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* coin) const {
2634 : if (containsCoincidence(coin)) {
2635 : // SkASSERT(coin->containsCoincidence(this));
2636 : return;
2637 : }
2638 : debugValidate();
2639 : // SkASSERT(this != coin);
2640 : log->record(SkPathOpsDebug::kMarkCoinStart_Glitch, this, coin);
2641 : // coin->fCoincident = this->fCoincident;
2642 : // this->fCoincident = coinNext;
2643 : debugValidate();
2644 : }
2645 :
2646 : // Commented-out lines keep this in sync with insertCoincidence()
2647 : void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* segment, bool flipped, bool ordered) const {
2648 : if (this->containsCoincidence(segment)) {
2649 : return;
2650 : }
2651 : const SkOpPtT* next = &fPtT;
2652 : while ((next = next->next()) != &fPtT) {
2653 : if (next->segment() == segment) {
2654 : const SkOpSpan* span;
2655 : const SkOpSpanBase* base = next->span();
2656 : if (!ordered) {
2657 : const SkOpSpanBase* spanEnd = fNext->contains(segment)->span();
2658 : const SkOpPtT* start = base->ptT()->starter(spanEnd->ptT());
2659 : FAIL_IF(!start->span()->upCastable(), this);
2660 : span = const_cast<SkOpSpan*>(start->span()->upCast());
2661 : }
2662 : else if (flipped) {
2663 : span = base->prev();
2664 : FAIL_IF(!span, this);
2665 : }
2666 : else {
2667 : FAIL_IF(!base->upCastable(), this);
2668 : span = base->upCast();
2669 : }
2670 : log->record(SkPathOpsDebug::kMarkCoinInsert_Glitch, span);
2671 : return;
2672 : }
2673 : }
2674 : #if DEBUG_COIN
2675 : log->record(SkPathOpsDebug::kMarkCoinMissing_Glitch, segment, this);
2676 : #endif
2677 : return;
2678 : }
2679 : #endif
2680 :
2681 : // called only by test code
2682 0 : int SkIntersections::debugCoincidentUsed() const {
2683 0 : if (!fIsCoincident[0]) {
2684 0 : SkASSERT(!fIsCoincident[1]);
2685 0 : return 0;
2686 : }
2687 0 : int count = 0;
2688 0 : SkDEBUGCODE(int count2 = 0;)
2689 0 : for (int index = 0; index < fUsed; ++index) {
2690 0 : if (fIsCoincident[0] & (1 << index)) {
2691 0 : ++count;
2692 : }
2693 : #ifdef SK_DEBUG
2694 0 : if (fIsCoincident[1] & (1 << index)) {
2695 0 : ++count2;
2696 : }
2697 : #endif
2698 : }
2699 0 : SkASSERT(count == count2);
2700 0 : return count;
2701 : }
2702 :
2703 : #include "SkOpContour.h"
2704 :
2705 : // Commented-out lines keep this in sync with addOpp()
2706 0 : void SkOpPtT::debugAddOpp(const SkOpPtT* opp, const SkOpPtT* oppPrev) const {
2707 0 : SkDEBUGCODE(const SkOpPtT* oldNext = this->fNext);
2708 0 : SkASSERT(this != opp);
2709 : // this->fNext = opp;
2710 0 : SkASSERT(oppPrev != oldNext);
2711 : // oppPrev->fNext = oldNext;
2712 0 : }
2713 :
2714 0 : bool SkOpPtT::debugContains(const SkOpPtT* check) const {
2715 0 : SkASSERT(this != check);
2716 0 : const SkOpPtT* ptT = this;
2717 0 : int links = 0;
2718 : do {
2719 0 : ptT = ptT->next();
2720 0 : if (ptT == check) {
2721 0 : return true;
2722 : }
2723 0 : ++links;
2724 0 : const SkOpPtT* test = this;
2725 0 : for (int index = 0; index < links; ++index) {
2726 0 : if (ptT == test) {
2727 0 : return false;
2728 : }
2729 0 : test = test->next();
2730 0 : }
2731 : } while (true);
2732 : }
2733 :
2734 0 : const SkOpPtT* SkOpPtT::debugContains(const SkOpSegment* check) const {
2735 0 : SkASSERT(this->segment() != check);
2736 0 : const SkOpPtT* ptT = this;
2737 0 : int links = 0;
2738 : do {
2739 0 : ptT = ptT->next();
2740 0 : if (ptT->segment() == check) {
2741 0 : return ptT;
2742 : }
2743 0 : ++links;
2744 0 : const SkOpPtT* test = this;
2745 0 : for (int index = 0; index < links; ++index) {
2746 0 : if (ptT == test) {
2747 0 : return nullptr;
2748 : }
2749 0 : test = test->next();
2750 0 : }
2751 : } while (true);
2752 : }
2753 :
2754 0 : const SkOpPtT* SkOpPtT::debugEnder(const SkOpPtT* end) const {
2755 0 : return fT < end->fT ? end : this;
2756 : }
2757 :
2758 0 : int SkOpPtT::debugLoopLimit(bool report) const {
2759 0 : int loop = 0;
2760 0 : const SkOpPtT* next = this;
2761 0 : do {
2762 0 : for (int check = 1; check < loop - 1; ++check) {
2763 0 : const SkOpPtT* checkPtT = this->fNext;
2764 0 : const SkOpPtT* innerPtT = checkPtT;
2765 0 : for (int inner = check + 1; inner < loop; ++inner) {
2766 0 : innerPtT = innerPtT->fNext;
2767 0 : if (checkPtT == innerPtT) {
2768 0 : if (report) {
2769 0 : SkDebugf("*** bad ptT loop ***\n");
2770 : }
2771 0 : return loop;
2772 : }
2773 : }
2774 : }
2775 : // there's nothing wrong with extremely large loop counts -- but this may appear to hang
2776 : // by taking a very long time to figure out that no loop entry is a duplicate
2777 : // -- and it's likely that a large loop count is indicative of a bug somewhere
2778 0 : if (++loop > 1000) {
2779 0 : SkDebugf("*** loop count exceeds 1000 ***\n");
2780 0 : return 1000;
2781 : }
2782 0 : } while ((next = next->fNext) && next != this);
2783 0 : return 0;
2784 : }
2785 :
2786 0 : const SkOpPtT* SkOpPtT::debugOppPrev(const SkOpPtT* opp) const {
2787 0 : return this->oppPrev(const_cast<SkOpPtT*>(opp));
2788 : }
2789 :
2790 0 : void SkOpPtT::debugResetCoinT() const {
2791 : #if DEBUG_COINCIDENCE_ORDER
2792 : this->segment()->debugResetCoinT();
2793 : #endif
2794 0 : }
2795 :
2796 0 : void SkOpPtT::debugSetCoinT(int index) const {
2797 : #if DEBUG_COINCIDENCE_ORDER
2798 : this->segment()->debugSetCoinT(index, fT);
2799 : #endif
2800 0 : }
2801 :
2802 0 : void SkOpPtT::debugValidate() const {
2803 : #if DEBUG_COINCIDENCE
2804 : if (this->globalState()->debugCheckHealth()) {
2805 : return;
2806 : }
2807 : #endif
2808 : #if DEBUG_VALIDATE
2809 : SkOpPhase phase = contour()->globalState()->phase();
2810 : if (phase == SkOpPhase::kIntersecting || phase == SkOpPhase::kFixWinding) {
2811 : return;
2812 : }
2813 : SkASSERT(fNext);
2814 : SkASSERT(fNext != this);
2815 : SkASSERT(fNext->fNext);
2816 : SkASSERT(debugLoopLimit(false) == 0);
2817 : #endif
2818 0 : }
2819 :
2820 0 : static void output_scalar(SkScalar num) {
2821 0 : if (num == (int) num) {
2822 0 : SkDebugf("%d", (int) num);
2823 : } else {
2824 0 : SkString str;
2825 0 : str.printf("%1.9g", num);
2826 0 : int width = (int) str.size();
2827 0 : const char* cStr = str.c_str();
2828 0 : while (cStr[width - 1] == '0') {
2829 0 : --width;
2830 : }
2831 0 : str.resize(width);
2832 0 : SkDebugf("%sf", str.c_str());
2833 : }
2834 0 : }
2835 :
2836 0 : static void output_points(const SkPoint* pts, int count) {
2837 0 : for (int index = 0; index < count; ++index) {
2838 0 : output_scalar(pts[index].fX);
2839 0 : SkDebugf(", ");
2840 0 : output_scalar(pts[index].fY);
2841 0 : if (index + 1 < count) {
2842 0 : SkDebugf(", ");
2843 : }
2844 : }
2845 0 : }
2846 :
2847 0 : static void showPathContours(SkPath::RawIter& iter, const char* pathName) {
2848 : uint8_t verb;
2849 : SkPoint pts[4];
2850 0 : while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
2851 0 : switch (verb) {
2852 : case SkPath::kMove_Verb:
2853 0 : SkDebugf(" %s.moveTo(", pathName);
2854 0 : output_points(&pts[0], 1);
2855 0 : SkDebugf(");\n");
2856 0 : continue;
2857 : case SkPath::kLine_Verb:
2858 0 : SkDebugf(" %s.lineTo(", pathName);
2859 0 : output_points(&pts[1], 1);
2860 0 : SkDebugf(");\n");
2861 0 : break;
2862 : case SkPath::kQuad_Verb:
2863 0 : SkDebugf(" %s.quadTo(", pathName);
2864 0 : output_points(&pts[1], 2);
2865 0 : SkDebugf(");\n");
2866 0 : break;
2867 : case SkPath::kConic_Verb:
2868 0 : SkDebugf(" %s.conicTo(", pathName);
2869 0 : output_points(&pts[1], 2);
2870 0 : SkDebugf(", %1.9gf);\n", iter.conicWeight());
2871 0 : break;
2872 : case SkPath::kCubic_Verb:
2873 0 : SkDebugf(" %s.cubicTo(", pathName);
2874 0 : output_points(&pts[1], 3);
2875 0 : SkDebugf(");\n");
2876 0 : break;
2877 : case SkPath::kClose_Verb:
2878 0 : SkDebugf(" %s.close();\n", pathName);
2879 0 : break;
2880 : default:
2881 0 : SkDEBUGFAIL("bad verb");
2882 0 : return;
2883 : }
2884 : }
2885 : }
2886 :
2887 : static const char* gFillTypeStr[] = {
2888 : "kWinding_FillType",
2889 : "kEvenOdd_FillType",
2890 : "kInverseWinding_FillType",
2891 : "kInverseEvenOdd_FillType"
2892 : };
2893 :
2894 0 : void SkPathOpsDebug::ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration) {
2895 0 : SkPath::RawIter iter(path);
2896 : #define SUPPORT_RECT_CONTOUR_DETECTION 0
2897 : #if SUPPORT_RECT_CONTOUR_DETECTION
2898 : int rectCount = path.isRectContours() ? path.rectContours(nullptr, nullptr) : 0;
2899 : if (rectCount > 0) {
2900 : SkTDArray<SkRect> rects;
2901 : SkTDArray<SkPath::Direction> directions;
2902 : rects.setCount(rectCount);
2903 : directions.setCount(rectCount);
2904 : path.rectContours(rects.begin(), directions.begin());
2905 : for (int contour = 0; contour < rectCount; ++contour) {
2906 : const SkRect& rect = rects[contour];
2907 : SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
2908 : rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
2909 : ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
2910 : }
2911 : return;
2912 : }
2913 : #endif
2914 0 : SkPath::FillType fillType = path.getFillType();
2915 0 : SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType);
2916 0 : if (includeDeclaration) {
2917 0 : SkDebugf(" SkPath %s;\n", name);
2918 : }
2919 0 : SkDebugf(" %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[fillType]);
2920 0 : iter.setPath(path);
2921 0 : showPathContours(iter, name);
2922 0 : }
2923 :
2924 : #if DEBUG_DUMP_VERIFY
2925 : #include "SkData.h"
2926 : #include "SkStream.h"
2927 :
2928 : static void dump_path(FILE* file, const SkPath& path, bool force, bool dumpAsHex) {
2929 : SkDynamicMemoryWStream wStream;
2930 : path.dump(&wStream, force, dumpAsHex);
2931 : sk_sp<SkData> data(wStream.detachAsData());
2932 : fprintf(file, "%.*s\n", (int) data->size(), (char*) data->data());
2933 : }
2934 :
2935 : static int dumpID = 0;
2936 :
2937 : void SkPathOpsDebug::DumpOp(const SkPath& one, const SkPath& two, SkPathOp op,
2938 : const char* testName) {
2939 : FILE* file = sk_fopen("op_dump.txt", kWrite_SkFILE_Flag);
2940 : DumpOp(file, one, two, op, testName);
2941 : }
2942 :
2943 : void SkPathOpsDebug::DumpOp(FILE* file, const SkPath& one, const SkPath& two, SkPathOp op,
2944 : const char* testName) {
2945 : const char* name = testName ? testName : "op";
2946 : fprintf(file,
2947 : "\nstatic void %s_%d(skiatest::Reporter* reporter, const char* filename) {\n",
2948 : name, ++dumpID);
2949 : fprintf(file, " SkPath path;\n");
2950 : fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", one.getFillType());
2951 : dump_path(file, one, false, true);
2952 : fprintf(file, " SkPath path1(path);\n");
2953 : fprintf(file, " path.reset();\n");
2954 : fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", two.getFillType());
2955 : dump_path(file, two, false, true);
2956 : fprintf(file, " SkPath path2(path);\n");
2957 : fprintf(file, " testPathOp(reporter, path1, path2, (SkPathOp) %d, filename);\n", op);
2958 : fprintf(file, "}\n\n");
2959 : fclose(file);
2960 : }
2961 :
2962 : void SkPathOpsDebug::DumpSimplify(const SkPath& path, const char* testName) {
2963 : FILE* file = sk_fopen("simplify_dump.txt", kWrite_SkFILE_Flag);
2964 : DumpSimplify(file, path, testName);
2965 : }
2966 :
2967 : void SkPathOpsDebug::DumpSimplify(FILE* file, const SkPath& path, const char* testName) {
2968 : const char* name = testName ? testName : "simplify";
2969 : fprintf(file,
2970 : "\nstatic void %s_%d(skiatest::Reporter* reporter, const char* filename) {\n",
2971 : name, ++dumpID);
2972 : fprintf(file, " SkPath path;\n");
2973 : fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", path.getFillType());
2974 : dump_path(file, path, false, true);
2975 : fprintf(file, " testSimplify(reporter, path, filename);\n");
2976 : fprintf(file, "}\n\n");
2977 : fclose(file);
2978 : }
2979 :
2980 : #include "SkBitmap.h"
2981 : #include "SkCanvas.h"
2982 : #include "SkPaint.h"
2983 :
2984 : const int bitWidth = 64;
2985 : const int bitHeight = 64;
2986 :
2987 : static void debug_scale_matrix(const SkPath& one, const SkPath* two, SkMatrix& scale) {
2988 : SkRect larger = one.getBounds();
2989 : if (two) {
2990 : larger.join(two->getBounds());
2991 : }
2992 : SkScalar largerWidth = larger.width();
2993 : if (largerWidth < 4) {
2994 : largerWidth = 4;
2995 : }
2996 : SkScalar largerHeight = larger.height();
2997 : if (largerHeight < 4) {
2998 : largerHeight = 4;
2999 : }
3000 : SkScalar hScale = (bitWidth - 2) / largerWidth;
3001 : SkScalar vScale = (bitHeight - 2) / largerHeight;
3002 : scale.reset();
3003 : scale.preScale(hScale, vScale);
3004 : larger.fLeft *= hScale;
3005 : larger.fRight *= hScale;
3006 : larger.fTop *= vScale;
3007 : larger.fBottom *= vScale;
3008 : SkScalar dx = -16000 > larger.fLeft ? -16000 - larger.fLeft
3009 : : 16000 < larger.fRight ? 16000 - larger.fRight : 0;
3010 : SkScalar dy = -16000 > larger.fTop ? -16000 - larger.fTop
3011 : : 16000 < larger.fBottom ? 16000 - larger.fBottom : 0;
3012 : scale.preTranslate(dx, dy);
3013 : }
3014 :
3015 : static int debug_paths_draw_the_same(const SkPath& one, const SkPath& two, SkBitmap& bits) {
3016 : if (bits.width() == 0) {
3017 : bits.allocN32Pixels(bitWidth * 2, bitHeight);
3018 : }
3019 : SkCanvas canvas(bits);
3020 : canvas.drawColor(SK_ColorWHITE);
3021 : SkPaint paint;
3022 : canvas.save();
3023 : const SkRect& bounds1 = one.getBounds();
3024 : canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
3025 : canvas.drawPath(one, paint);
3026 : canvas.restore();
3027 : canvas.save();
3028 : canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
3029 : canvas.drawPath(two, paint);
3030 : canvas.restore();
3031 : int errors = 0;
3032 : for (int y = 0; y < bitHeight - 1; ++y) {
3033 : uint32_t* addr1 = bits.getAddr32(0, y);
3034 : uint32_t* addr2 = bits.getAddr32(0, y + 1);
3035 : uint32_t* addr3 = bits.getAddr32(bitWidth, y);
3036 : uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1);
3037 : for (int x = 0; x < bitWidth - 1; ++x) {
3038 : // count 2x2 blocks
3039 : bool err = addr1[x] != addr3[x];
3040 : if (err) {
3041 : errors += addr1[x + 1] != addr3[x + 1]
3042 : && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1];
3043 : }
3044 : }
3045 : }
3046 : return errors;
3047 : }
3048 :
3049 : void SkPathOpsDebug::ReportOpFail(const SkPath& one, const SkPath& two, SkPathOp op) {
3050 : SkDebugf("// Op did not expect failure\n");
3051 : DumpOp(stderr, one, two, op, "opTest");
3052 : fflush(stderr);
3053 : }
3054 :
3055 : void SkPathOpsDebug::VerifyOp(const SkPath& one, const SkPath& two, SkPathOp op,
3056 : const SkPath& result) {
3057 : SkPath pathOut, scaledPathOut;
3058 : SkRegion rgnA, rgnB, openClip, rgnOut;
3059 : openClip.setRect(-16000, -16000, 16000, 16000);
3060 : rgnA.setPath(one, openClip);
3061 : rgnB.setPath(two, openClip);
3062 : rgnOut.op(rgnA, rgnB, (SkRegion::Op) op);
3063 : rgnOut.getBoundaryPath(&pathOut);
3064 : SkMatrix scale;
3065 : debug_scale_matrix(one, &two, scale);
3066 : SkRegion scaledRgnA, scaledRgnB, scaledRgnOut;
3067 : SkPath scaledA, scaledB;
3068 : scaledA.addPath(one, scale);
3069 : scaledA.setFillType(one.getFillType());
3070 : scaledB.addPath(two, scale);
3071 : scaledB.setFillType(two.getFillType());
3072 : scaledRgnA.setPath(scaledA, openClip);
3073 : scaledRgnB.setPath(scaledB, openClip);
3074 : scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) op);
3075 : scaledRgnOut.getBoundaryPath(&scaledPathOut);
3076 : SkBitmap bitmap;
3077 : SkPath scaledOut;
3078 : scaledOut.addPath(result, scale);
3079 : scaledOut.setFillType(result.getFillType());
3080 : int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
3081 : const int MAX_ERRORS = 9;
3082 : if (errors > MAX_ERRORS) {
3083 : fprintf(stderr, "// Op did not expect errors=%d\n", errors);
3084 : DumpOp(stderr, one, two, op, "opTest");
3085 : fflush(stderr);
3086 : }
3087 : }
3088 :
3089 : void SkPathOpsDebug::ReportSimplifyFail(const SkPath& path) {
3090 : SkDebugf("// Simplify did not expect failure\n");
3091 : DumpSimplify(stderr, path, "simplifyTest");
3092 : fflush(stderr);
3093 : }
3094 :
3095 : void SkPathOpsDebug::VerifySimplify(const SkPath& path, const SkPath& result) {
3096 : SkPath pathOut, scaledPathOut;
3097 : SkRegion rgnA, openClip, rgnOut;
3098 : openClip.setRect(-16000, -16000, 16000, 16000);
3099 : rgnA.setPath(path, openClip);
3100 : rgnOut.getBoundaryPath(&pathOut);
3101 : SkMatrix scale;
3102 : debug_scale_matrix(path, nullptr, scale);
3103 : SkRegion scaledRgnA;
3104 : SkPath scaledA;
3105 : scaledA.addPath(path, scale);
3106 : scaledA.setFillType(path.getFillType());
3107 : scaledRgnA.setPath(scaledA, openClip);
3108 : scaledRgnA.getBoundaryPath(&scaledPathOut);
3109 : SkBitmap bitmap;
3110 : SkPath scaledOut;
3111 : scaledOut.addPath(result, scale);
3112 : scaledOut.setFillType(result.getFillType());
3113 : int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
3114 : const int MAX_ERRORS = 9;
3115 : if (errors > MAX_ERRORS) {
3116 : fprintf(stderr, "// Simplify did not expect errors=%d\n", errors);
3117 : DumpSimplify(stderr, path, "simplifyTest");
3118 : fflush(stderr);
3119 : }
3120 : }
3121 :
3122 : #endif
|