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
ChaseContains(const SkTDArray<SkOpSpanBase * > & chaseArray,const SkOpSpanBase * span)55 bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray,
56 const SkOpSpanBase* span) {
57 for (int index = 0; index < chaseArray.count(); ++index) {
58 const SkOpSpanBase* entry = chaseArray[index];
59 if (entry == span) {
60 return true;
61 }
62 }
63 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 {
initSkPathOpsDebug::GlitchLog98 void init(const SkOpGlobalState* state) {
99 fGlobalState = state;
100 }
101
recordCommonSkPathOpsDebug::GlitchLog102 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
recordSkPathOpsDebug::GlitchLog121 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
recordSkPathOpsDebug::GlitchLog128 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
recordSkPathOpsDebug::GlitchLog135 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
recordSkPathOpsDebug::GlitchLog146 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
recordSkPathOpsDebug::GlitchLog155 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
recordSkPathOpsDebug::GlitchLog163 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
recordSkPathOpsDebug::GlitchLog173 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
recordSkPathOpsDebug::GlitchLog181 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
recordSkPathOpsDebug::GlitchLog190 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
recordSkPathOpsDebug::GlitchLog201 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
recordSkPathOpsDebug::GlitchLog208 void record(GlitchType type, double t, const SkOpSpanBase* span) {
209 SpanGlitch* glitch = recordCommon(type);
210 glitch->fStartT = t;
211 glitch->fBase = span;
212 }
213
recordSkPathOpsDebug::GlitchLog214 void record(GlitchType type, const SkOpSegment* seg) {
215 SpanGlitch* glitch = recordCommon(type);
216 glitch->fSegment = seg;
217 }
218
recordSkPathOpsDebug::GlitchLog219 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
add(const SkPathOpsDebug::CoinDict & dict)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
add(const CoinDictEntry & key)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
missing_coincidence(SkPathOpsDebug::GlitchLog * glitches,const SkOpContourHead * contourList)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
move_multiples(SkPathOpsDebug::GlitchLog * glitches,const SkOpContourHead * contourList)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
move_nearby(SkPathOpsDebug::GlitchLog * glitches,const SkOpContourHead * contourList)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
debugAddToCoinChangedDict()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
ShowActiveSpans(SkOpContourHead * contourList)329 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 }
347
348 #if DEBUG_COINCIDENCE || DEBUG_COIN
CheckHealth(SkOpContourHead * contourList)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
DumpGlitchType(GlitchType glitchType)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
MathematicaIze(char * str,size_t bufferLen)483 void SkPathOpsDebug::MathematicaIze(char* str, size_t bufferLen) {
484 size_t len = strlen(str);
485 bool num = false;
486 for (size_t idx = 0; idx < len; ++idx) {
487 if (num && str[idx] == 'e') {
488 if (len + 2 >= bufferLen) {
489 return;
490 }
491 memmove(&str[idx + 2], &str[idx + 1], len - idx);
492 str[idx] = '*';
493 str[idx + 1] = '^';
494 ++len;
495 }
496 num = str[idx] >= '0' && str[idx] <= '9';
497 }
498 }
499
ValidWind(int wind)500 bool SkPathOpsDebug::ValidWind(int wind) {
501 return wind > SK_MinS32 + 0xFFFF && wind < SK_MaxS32 - 0xFFFF;
502 }
503
WindingPrintf(int wind)504 void SkPathOpsDebug::WindingPrintf(int wind) {
505 if (wind == SK_MinS32) {
506 SkDebugf("?");
507 } else {
508 SkDebugf("%d", wind);
509 }
510 }
511 #endif // defined SK_DEBUG || !FORCE_RELEASE
512
513
514 #if DEBUG_SHOW_TEST_NAME
CreateNameStr()515 void* SkPathOpsDebug::CreateNameStr() { return new char[DEBUG_FILENAME_STRING_LENGTH]; }
516
DeleteNameStr(void * v)517 void SkPathOpsDebug::DeleteNameStr(void* v) { delete[] reinterpret_cast<char*>(v); }
518
BumpTestName(char * test)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
show_function_header(const char * functionName)536 static void show_function_header(const char* functionName) {
537 SkDebugf("\nstatic void %s(skiatest::Reporter* reporter, const char* filename) {\n", functionName);
538 if (strcmp("skphealth_com76", functionName) == 0) {
539 SkDebugf("found it\n");
540 }
541 }
542
543 static const char* gOpStrs[] = {
544 "kDifference_SkPathOp",
545 "kIntersect_SkPathOp",
546 "kUnion_SkPathOp",
547 "kXOR_PathOp",
548 "kReverseDifference_SkPathOp",
549 };
550
OpStr(SkPathOp op)551 const char* SkPathOpsDebug::OpStr(SkPathOp op) {
552 return gOpStrs[op];
553 }
554
show_op(SkPathOp op,const char * pathOne,const char * pathTwo)555 static void show_op(SkPathOp op, const char* pathOne, const char* pathTwo) {
556 SkDebugf(" testPathOp(reporter, %s, %s, %s, filename);\n", pathOne, pathTwo, gOpStrs[op]);
557 SkDebugf("}\n");
558 }
559
560 SK_DECLARE_STATIC_MUTEX(gTestMutex);
561
ShowPath(const SkPath & a,const SkPath & b,SkPathOp shapeOp,const char * testName)562 void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
563 const char* testName) {
564 SkAutoMutexAcquire ac(gTestMutex);
565 show_function_header(testName);
566 ShowOnePath(a, "path", true);
567 ShowOnePath(b, "pathB", true);
568 show_op(shapeOp, "path", "pathB");
569 }
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
debugAddToGlobalCoinDicts()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
debugAddLoopCount(SkIntersections * i,const SkIntersectionHelper & wt,const SkIntersectionHelper & wn)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
debugDoYourWorst(SkOpGlobalState * local)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
dump_curve(SkPath::Verb verb,const SkPoint & pts,float weight)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
debugLoopReport()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
debugResetLoopCounts()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
DebugRunFail()670 bool SkOpGlobalState::DebugRunFail() {
671 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
debugSetPhase(const char * funcName DEBUG_COIN_DECLARE_PARAMS ()) const676 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
debugBumpLoopCount(DebugLoop index)695 void SkIntersections::debugBumpLoopCount(DebugLoop index) {
696 fDebugLoopCount[index]++;
697 }
698
debugLoopCount(DebugLoop index) const699 int SkIntersections::debugLoopCount(DebugLoop index) const {
700 return fDebugLoopCount[index];
701 }
702
debugResetLoopCount()703 void SkIntersections::debugResetLoopCount() {
704 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
705 }
706 #endif
707
708 #include "SkPathOpsConic.h"
709 #include "SkPathOpsCubic.h"
710
debugToCubic() const711 SkDCubic SkDQuad::debugToCubic() const {
712 SkDCubic cubic;
713 cubic[0] = fPts[0];
714 cubic[2] = fPts[1];
715 cubic[3] = fPts[2];
716 cubic[1].fX = (cubic[0].fX + cubic[2].fX * 2) / 3;
717 cubic[1].fY = (cubic[0].fY + cubic[2].fY * 2) / 3;
718 cubic[2].fX = (cubic[3].fX + cubic[2].fX * 2) / 3;
719 cubic[2].fY = (cubic[3].fY + cubic[2].fY * 2) / 3;
720 return cubic;
721 }
722
debugSet(const SkDPoint * pts)723 void SkDQuad::debugSet(const SkDPoint* pts) {
724 memcpy(fPts, pts, sizeof(fPts));
725 SkDEBUGCODE(fDebugGlobalState = nullptr);
726 }
727
debugSet(const SkDPoint * pts)728 void SkDCubic::debugSet(const SkDPoint* pts) {
729 memcpy(fPts, pts, sizeof(fPts));
730 SkDEBUGCODE(fDebugGlobalState = nullptr);
731 }
732
debugSet(const SkDPoint * pts,SkScalar weight)733 void SkDConic::debugSet(const SkDPoint* pts, SkScalar weight) {
734 fPts.debugSet(pts);
735 fWeight = weight;
736 }
737
debugInit()738 void SkDRect::debugInit() {
739 fLeft = fTop = fRight = fBottom = SK_ScalarNaN;
740 }
741
742 #include "SkOpAngle.h"
743 #include "SkOpSegment.h"
744
745 #if DEBUG_COIN
746 // commented-out lines keep this in sync with addT()
debugAddT(double t,SkPathOpsDebug::GlitchLog * log) const747 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
debugCheckAngleCoin() const779 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
debugCheckHealth(SkPathOpsDebug::GlitchLog * glitches) const801 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()
debugClearAll(SkPathOpsDebug::GlitchLog * glitches) const808 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()
debugClearOne(const SkOpSpan * span,SkPathOpsDebug::GlitchLog * glitches) const817 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
debugLastAngle()824 SkOpAngle* SkOpSegment::debugLastAngle() {
825 SkOpAngle* result = nullptr;
826 SkOpSpan* span = this->head();
827 do {
828 if (span->toAngle()) {
829 SkASSERT(!result);
830 result = span->toAngle();
831 }
832 } while ((span = span->next()->upCastable()));
833 SkASSERT(result);
834 return result;
835 }
836
837 #if DEBUG_COIN
838 // commented-out lines keep this in sync with ClearVisited
DebugClearVisited(const SkOpSpanBase * span)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.
debugMissingCoincidence(SkPathOpsDebug::GlitchLog * log) const862 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
debugMoveMultiples(SkPathOpsDebug::GlitchLog * glitches) const966 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.
debugMoveNearby(SkPathOpsDebug::GlitchLog * glitches) const1062 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 bool found;
1093 if (!this->spansNearby(spanBase, test, &found)) {
1094 glitches->record(SkPathOpsDebug::kMoveNearbyMergeFinal_Glitch, test);
1095 }
1096 if (found) {
1097 if (test->final()) {
1098 if (spanBase->prev()) {
1099 glitches->record(SkPathOpsDebug::kMoveNearbyMergeFinal_Glitch, test);
1100 } else {
1101 glitches->record(SkPathOpsDebug::kMoveNearbyClearAll2_Glitch, this);
1102 // return
1103 }
1104 } else {
1105 glitches->record(SkPathOpsDebug::kMoveNearbyMerge_Glitch, spanBase);
1106 }
1107 }
1108 spanBase = test;
1109 } while (!spanBase->final());
1110 debugValidate();
1111 }
1112 #endif
1113
debugReset()1114 void SkOpSegment::debugReset() {
1115 this->init(this->fPts, this->fWeight, this->contour(), this->verb());
1116 }
1117
1118 #if DEBUG_COINCIDENCE_ORDER
debugSetCoinT(int index,SkScalar t) const1119 void SkOpSegment::debugSetCoinT(int index, SkScalar t) const {
1120 if (fDebugBaseMax < 0 || fDebugBaseIndex == index) {
1121 fDebugBaseIndex = index;
1122 fDebugBaseMin = SkTMin(t, fDebugBaseMin);
1123 fDebugBaseMax = SkTMax(t, fDebugBaseMax);
1124 return;
1125 }
1126 SkASSERT(fDebugBaseMin >= t || t >= fDebugBaseMax);
1127 if (fDebugLastMax < 0 || fDebugLastIndex == index) {
1128 fDebugLastIndex = index;
1129 fDebugLastMin = SkTMin(t, fDebugLastMin);
1130 fDebugLastMax = SkTMax(t, fDebugLastMax);
1131 return;
1132 }
1133 SkASSERT(fDebugLastMin >= t || t >= fDebugLastMax);
1134 SkASSERT((t - fDebugBaseMin > 0) == (fDebugLastMin - fDebugBaseMin > 0));
1135 }
1136 #endif
1137
1138 #if DEBUG_ACTIVE_SPANS
debugShowActiveSpans(SkString * str) const1139 void SkOpSegment::debugShowActiveSpans(SkString* str) const {
1140 debugValidate();
1141 if (done()) {
1142 return;
1143 }
1144 int lastId = -1;
1145 double lastT = -1;
1146 const SkOpSpan* span = &fHead;
1147 do {
1148 if (span->done()) {
1149 continue;
1150 }
1151 if (lastId == this->debugID() && lastT == span->t()) {
1152 continue;
1153 }
1154 lastId = this->debugID();
1155 lastT = span->t();
1156 str->appendf("%s id=%d", __FUNCTION__, this->debugID());
1157 // since endpoints may have be adjusted, show actual computed curves
1158 SkDCurve curvePart;
1159 this->subDivide(span, span->next(), &curvePart);
1160 const SkDPoint* pts = curvePart.fCubic.fPts;
1161 str->appendf(" (%1.9g,%1.9g", pts[0].fX, pts[0].fY);
1162 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1163 str->appendf(" %1.9g,%1.9g", pts[vIndex].fX, pts[vIndex].fY);
1164 }
1165 if (SkPath::kConic_Verb == fVerb) {
1166 str->appendf(" %1.9gf", curvePart.fConic.fWeight);
1167 }
1168 str->appendf(") t=%1.9g tEnd=%1.9g", span->t(), span->next()->t());
1169 if (span->windSum() == SK_MinS32) {
1170 str->appendf(" windSum=?");
1171 } else {
1172 str->appendf(" windSum=%d", span->windSum());
1173 }
1174 if (span->oppValue() && span->oppSum() == SK_MinS32) {
1175 str->appendf(" oppSum=?");
1176 } else if (span->oppValue() || span->oppSum() != SK_MinS32) {
1177 str->appendf(" oppSum=%d", span->oppSum());
1178 }
1179 str->appendf(" windValue=%d", span->windValue());
1180 if (span->oppValue() || span->oppSum() != SK_MinS32) {
1181 str->appendf(" oppValue=%d", span->oppValue());
1182 }
1183 str->appendf("\n");
1184 } while ((span = span->next()->upCastable()));
1185 }
1186 #endif
1187
1188 #if DEBUG_MARK_DONE
debugShowNewWinding(const char * fun,const SkOpSpan * span,int winding)1189 void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding) {
1190 const SkPoint& pt = span->ptT()->fPt;
1191 SkDebugf("%s id=%d", fun, this->debugID());
1192 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1193 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1194 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1195 }
1196 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
1197 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t());
1198 if (winding == SK_MinS32) {
1199 SkDebugf("?");
1200 } else {
1201 SkDebugf("%d", winding);
1202 }
1203 SkDebugf(" windSum=");
1204 if (span->windSum() == SK_MinS32) {
1205 SkDebugf("?");
1206 } else {
1207 SkDebugf("%d", span->windSum());
1208 }
1209 SkDebugf(" windValue=%d\n", span->windValue());
1210 }
1211
debugShowNewWinding(const char * fun,const SkOpSpan * span,int winding,int oppWinding)1212 void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding,
1213 int oppWinding) {
1214 const SkPoint& pt = span->ptT()->fPt;
1215 SkDebugf("%s id=%d", fun, this->debugID());
1216 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1217 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1218 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1219 }
1220 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
1221 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t(), winding, oppWinding);
1222 if (winding == SK_MinS32) {
1223 SkDebugf("?");
1224 } else {
1225 SkDebugf("%d", winding);
1226 }
1227 SkDebugf(" newOppSum=");
1228 if (oppWinding == SK_MinS32) {
1229 SkDebugf("?");
1230 } else {
1231 SkDebugf("%d", oppWinding);
1232 }
1233 SkDebugf(" oppSum=");
1234 if (span->oppSum() == SK_MinS32) {
1235 SkDebugf("?");
1236 } else {
1237 SkDebugf("%d", span->oppSum());
1238 }
1239 SkDebugf(" windSum=");
1240 if (span->windSum() == SK_MinS32) {
1241 SkDebugf("?");
1242 } else {
1243 SkDebugf("%d", span->windSum());
1244 }
1245 SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue());
1246 }
1247
1248 #endif
1249
1250 // loop looking for a pair of angle parts that are too close to be sorted
1251 /* This is called after other more simple intersection and angle sorting tests have been exhausted.
1252 This should be rarely called -- the test below is thorough and time consuming.
1253 This checks the distance between start points; the distance between
1254 */
1255 #if DEBUG_ANGLE
debugCheckNearCoincidence() const1256 void SkOpAngle::debugCheckNearCoincidence() const {
1257 const SkOpAngle* test = this;
1258 do {
1259 const SkOpSegment* testSegment = test->segment();
1260 double testStartT = test->start()->t();
1261 SkDPoint testStartPt = testSegment->dPtAtT(testStartT);
1262 double testEndT = test->end()->t();
1263 SkDPoint testEndPt = testSegment->dPtAtT(testEndT);
1264 double testLenSq = testStartPt.distanceSquared(testEndPt);
1265 SkDebugf("%s testLenSq=%1.9g id=%d\n", __FUNCTION__, testLenSq, testSegment->debugID());
1266 double testMidT = (testStartT + testEndT) / 2;
1267 const SkOpAngle* next = test;
1268 while ((next = next->fNext) != this) {
1269 SkOpSegment* nextSegment = next->segment();
1270 double testMidDistSq = testSegment->distSq(testMidT, next);
1271 double testEndDistSq = testSegment->distSq(testEndT, next);
1272 double nextStartT = next->start()->t();
1273 SkDPoint nextStartPt = nextSegment->dPtAtT(nextStartT);
1274 double distSq = testStartPt.distanceSquared(nextStartPt);
1275 double nextEndT = next->end()->t();
1276 double nextMidT = (nextStartT + nextEndT) / 2;
1277 double nextMidDistSq = nextSegment->distSq(nextMidT, test);
1278 double nextEndDistSq = nextSegment->distSq(nextEndT, test);
1279 SkDebugf("%s distSq=%1.9g testId=%d nextId=%d\n", __FUNCTION__, distSq,
1280 testSegment->debugID(), nextSegment->debugID());
1281 SkDebugf("%s testMidDistSq=%1.9g\n", __FUNCTION__, testMidDistSq);
1282 SkDebugf("%s testEndDistSq=%1.9g\n", __FUNCTION__, testEndDistSq);
1283 SkDebugf("%s nextMidDistSq=%1.9g\n", __FUNCTION__, nextMidDistSq);
1284 SkDebugf("%s nextEndDistSq=%1.9g\n", __FUNCTION__, nextEndDistSq);
1285 SkDPoint nextEndPt = nextSegment->dPtAtT(nextEndT);
1286 double nextLenSq = nextStartPt.distanceSquared(nextEndPt);
1287 SkDebugf("%s nextLenSq=%1.9g\n", __FUNCTION__, nextLenSq);
1288 SkDebugf("\n");
1289 }
1290 test = test->fNext;
1291 } while (test->fNext != this);
1292 }
1293 #endif
1294
1295 #if DEBUG_ANGLE
debugPart() const1296 SkString SkOpAngle::debugPart() const {
1297 SkString result;
1298 switch (this->segment()->verb()) {
1299 case SkPath::kLine_Verb:
1300 result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fPart.fCurve),
1301 this->segment()->debugID());
1302 break;
1303 case SkPath::kQuad_Verb:
1304 result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fPart.fCurve),
1305 this->segment()->debugID());
1306 break;
1307 case SkPath::kConic_Verb:
1308 result.printf(CONIC_DEBUG_STR " id=%d",
1309 CONIC_DEBUG_DATA(fPart.fCurve, fPart.fCurve.fConic.fWeight),
1310 this->segment()->debugID());
1311 break;
1312 case SkPath::kCubic_Verb:
1313 result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fPart.fCurve),
1314 this->segment()->debugID());
1315 break;
1316 default:
1317 SkASSERT(0);
1318 }
1319 return result;
1320 }
1321 #endif
1322
1323 #if DEBUG_SORT
debugLoop() const1324 void SkOpAngle::debugLoop() const {
1325 const SkOpAngle* first = this;
1326 const SkOpAngle* next = this;
1327 do {
1328 next->dumpOne(true);
1329 SkDebugf("\n");
1330 next = next->fNext;
1331 } while (next && next != first);
1332 next = first;
1333 do {
1334 next->debugValidate();
1335 next = next->fNext;
1336 } while (next && next != first);
1337 }
1338 #endif
1339
debugValidate() const1340 void SkOpAngle::debugValidate() const {
1341 #if DEBUG_COINCIDENCE
1342 if (this->globalState()->debugCheckHealth()) {
1343 return;
1344 }
1345 #endif
1346 #if DEBUG_VALIDATE
1347 const SkOpAngle* first = this;
1348 const SkOpAngle* next = this;
1349 int wind = 0;
1350 int opp = 0;
1351 int lastXor = -1;
1352 int lastOppXor = -1;
1353 do {
1354 if (next->unorderable()) {
1355 return;
1356 }
1357 const SkOpSpan* minSpan = next->start()->starter(next->end());
1358 if (minSpan->windValue() == SK_MinS32) {
1359 return;
1360 }
1361 bool op = next->segment()->operand();
1362 bool isXor = next->segment()->isXor();
1363 bool oppXor = next->segment()->oppXor();
1364 SkASSERT(!DEBUG_LIMIT_WIND_SUM || between(0, minSpan->windValue(), DEBUG_LIMIT_WIND_SUM));
1365 SkASSERT(!DEBUG_LIMIT_WIND_SUM
1366 || between(-DEBUG_LIMIT_WIND_SUM, minSpan->oppValue(), DEBUG_LIMIT_WIND_SUM));
1367 bool useXor = op ? oppXor : isXor;
1368 SkASSERT(lastXor == -1 || lastXor == (int) useXor);
1369 lastXor = (int) useXor;
1370 wind += next->debugSign() * (op ? minSpan->oppValue() : minSpan->windValue());
1371 if (useXor) {
1372 wind &= 1;
1373 }
1374 useXor = op ? isXor : oppXor;
1375 SkASSERT(lastOppXor == -1 || lastOppXor == (int) useXor);
1376 lastOppXor = (int) useXor;
1377 opp += next->debugSign() * (op ? minSpan->windValue() : minSpan->oppValue());
1378 if (useXor) {
1379 opp &= 1;
1380 }
1381 next = next->fNext;
1382 } while (next && next != first);
1383 SkASSERT(wind == 0 || !SkPathOpsDebug::gRunFail);
1384 SkASSERT(opp == 0 || !SkPathOpsDebug::gRunFail);
1385 #endif
1386 }
1387
debugValidateNext() const1388 void SkOpAngle::debugValidateNext() const {
1389 #if !FORCE_RELEASE
1390 const SkOpAngle* first = this;
1391 const SkOpAngle* next = first;
1392 SkTDArray<const SkOpAngle*>(angles);
1393 do {
1394 // SkASSERT_RELEASE(next->fSegment->debugContains(next));
1395 angles.push(next);
1396 next = next->next();
1397 if (next == first) {
1398 break;
1399 }
1400 SkASSERT_RELEASE(!angles.contains(next));
1401 if (!next) {
1402 return;
1403 }
1404 } while (true);
1405 #endif
1406 }
1407
1408 #ifdef SK_DEBUG
debugStartCheck(const SkOpSpanBase * outer,const SkOpSpanBase * over,const SkOpGlobalState * debugState) const1409 void SkCoincidentSpans::debugStartCheck(const SkOpSpanBase* outer, const SkOpSpanBase* over,
1410 const SkOpGlobalState* debugState) const {
1411 SkASSERT(coinPtTEnd()->span() == over || !SkOpGlobalState::DebugRunFail());
1412 SkASSERT(oppPtTEnd()->span() == outer || !SkOpGlobalState::DebugRunFail());
1413 }
1414 #endif
1415
1416 #if DEBUG_COIN
1417 // sets the span's end to the ptT referenced by the previous-next
debugCorrectOneEnd(SkPathOpsDebug::GlitchLog * log,const SkOpPtT * (SkCoincidentSpans::* getEnd)()const,void (SkCoincidentSpans::* setEnd)(const SkOpPtT * ptT)const) const1418 void SkCoincidentSpans::debugCorrectOneEnd(SkPathOpsDebug::GlitchLog* log,
1419 const SkOpPtT* (SkCoincidentSpans::* getEnd)() const,
1420 void (SkCoincidentSpans::*setEnd)(const SkOpPtT* ptT) const ) const {
1421 const SkOpPtT* origPtT = (this->*getEnd)();
1422 const SkOpSpanBase* origSpan = origPtT->span();
1423 const SkOpSpan* prev = origSpan->prev();
1424 const SkOpPtT* testPtT = prev ? prev->next()->ptT()
1425 : origSpan->upCast()->next()->prev()->ptT();
1426 if (origPtT != testPtT) {
1427 log->record(SkPathOpsDebug::kCorrectEnd_Glitch, this, origPtT, testPtT);
1428 }
1429 }
1430
1431
1432 /* Commented-out lines keep this in sync with correctEnds */
1433 // FIXME: member pointers have fallen out of favor and can be replaced with
1434 // an alternative approach.
1435 // makes all span ends agree with the segment's spans that define them
debugCorrectEnds(SkPathOpsDebug::GlitchLog * log) const1436 void SkCoincidentSpans::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
1437 this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTStart, nullptr);
1438 this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTEnd, nullptr);
1439 this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTStart, nullptr);
1440 this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTEnd, nullptr);
1441 }
1442
1443 /* Commented-out lines keep this in sync with expand */
1444 // expand the range by checking adjacent spans for coincidence
debugExpand(SkPathOpsDebug::GlitchLog * log) const1445 bool SkCoincidentSpans::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
1446 bool expanded = false;
1447 const SkOpSegment* segment = coinPtTStart()->segment();
1448 const SkOpSegment* oppSegment = oppPtTStart()->segment();
1449 do {
1450 const SkOpSpan* start = coinPtTStart()->span()->upCast();
1451 const SkOpSpan* prev = start->prev();
1452 const SkOpPtT* oppPtT;
1453 if (!prev || !(oppPtT = prev->contains(oppSegment))) {
1454 break;
1455 }
1456 double midT = (prev->t() + start->t()) / 2;
1457 if (!segment->isClose(midT, oppSegment)) {
1458 break;
1459 }
1460 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, prev->ptT(), oppPtT);
1461 expanded = true;
1462 } while (false); // actual continues while expansion is possible
1463 do {
1464 const SkOpSpanBase* end = coinPtTEnd()->span();
1465 SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
1466 if (next && next->deleted()) {
1467 break;
1468 }
1469 const SkOpPtT* oppPtT;
1470 if (!next || !(oppPtT = next->contains(oppSegment))) {
1471 break;
1472 }
1473 double midT = (end->t() + next->t()) / 2;
1474 if (!segment->isClose(midT, oppSegment)) {
1475 break;
1476 }
1477 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, next->ptT(), oppPtT);
1478 expanded = true;
1479 } while (false); // actual continues while expansion is possible
1480 return expanded;
1481 }
1482
1483 // description below
debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog * log,const SkOpSpan * base,const SkOpSpanBase * testSpan) const1484 void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* base, const SkOpSpanBase* testSpan) const {
1485 const SkOpPtT* testPtT = testSpan->ptT();
1486 const SkOpPtT* stopPtT = testPtT;
1487 const SkOpSegment* baseSeg = base->segment();
1488 while ((testPtT = testPtT->next()) != stopPtT) {
1489 const SkOpSegment* testSeg = testPtT->segment();
1490 if (testPtT->deleted()) {
1491 continue;
1492 }
1493 if (testSeg == baseSeg) {
1494 continue;
1495 }
1496 if (testPtT->span()->ptT() != testPtT) {
1497 continue;
1498 }
1499 if (this->contains(baseSeg, testSeg, testPtT->fT)) {
1500 continue;
1501 }
1502 // intersect perp with base->ptT() with testPtT->segment()
1503 SkDVector dxdy = baseSeg->dSlopeAtT(base->t());
1504 const SkPoint& pt = base->pt();
1505 SkDLine ray = {{{pt.fX, pt.fY}, {pt.fX + dxdy.fY, pt.fY - dxdy.fX}}};
1506 SkIntersections i;
1507 (*CurveIntersectRay[testSeg->verb()])(testSeg->pts(), testSeg->weight(), ray, &i);
1508 for (int index = 0; index < i.used(); ++index) {
1509 double t = i[0][index];
1510 if (!between(0, t, 1)) {
1511 continue;
1512 }
1513 SkDPoint oppPt = i.pt(index);
1514 if (!oppPt.approximatelyEqual(pt)) {
1515 continue;
1516 }
1517 SkOpSegment* writableSeg = const_cast<SkOpSegment*>(testSeg);
1518 SkOpPtT* oppStart = writableSeg->addT(t);
1519 if (oppStart == testPtT) {
1520 continue;
1521 }
1522 SkOpSpan* writableBase = const_cast<SkOpSpan*>(base);
1523 oppStart->span()->addOpp(writableBase);
1524 if (oppStart->deleted()) {
1525 continue;
1526 }
1527 SkOpSegment* coinSeg = base->segment();
1528 SkOpSegment* oppSeg = oppStart->segment();
1529 double coinTs, coinTe, oppTs, oppTe;
1530 if (Ordered(coinSeg, oppSeg)) {
1531 coinTs = base->t();
1532 coinTe = testSpan->t();
1533 oppTs = oppStart->fT;
1534 oppTe = testPtT->fT;
1535 } else {
1536 SkTSwap(coinSeg, oppSeg);
1537 coinTs = oppStart->fT;
1538 coinTe = testPtT->fT;
1539 oppTs = base->t();
1540 oppTe = testSpan->t();
1541 }
1542 if (coinTs > coinTe) {
1543 SkTSwap(coinTs, coinTe);
1544 SkTSwap(oppTs, oppTe);
1545 }
1546 bool added;
1547 if (this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &added), false) {
1548 return;
1549 }
1550 }
1551 }
1552 return;
1553 }
1554
1555 // description below
debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog * log,const SkOpPtT * ptT) const1556 void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* ptT) const {
1557 FAIL_IF(!ptT->span()->upCastable(), ptT->span());
1558 const SkOpSpan* base = ptT->span()->upCast();
1559 const SkOpSpan* prev = base->prev();
1560 FAIL_IF(!prev, ptT->span());
1561 if (!prev->isCanceled()) {
1562 if (this->debugAddEndMovedSpans(log, base, base->prev()), false) {
1563 return;
1564 }
1565 }
1566 if (!base->isCanceled()) {
1567 if (this->debugAddEndMovedSpans(log, base, base->next()), false) {
1568 return;
1569 }
1570 }
1571 return;
1572 }
1573
1574 /* If A is coincident with B and B includes an endpoint, and A's matching point
1575 is not the endpoint (i.e., there's an implied line connecting B-end and A)
1576 then assume that the same implied line may intersect another curve close to B.
1577 Since we only care about coincidence that was undetected, look at the
1578 ptT list on B-segment adjacent to the B-end/A ptT loop (not in the loop, but
1579 next door) and see if the A matching point is close enough to form another
1580 coincident pair. If so, check for a new coincident span between B-end/A ptT loop
1581 and the adjacent ptT loop.
1582 */
debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog * log) const1583 void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log) const {
1584 const SkCoincidentSpans* span = fHead;
1585 if (!span) {
1586 return;
1587 }
1588 // fTop = span;
1589 // fHead = nullptr;
1590 do {
1591 if (span->coinPtTStart()->fPt != span->oppPtTStart()->fPt) {
1592 FAIL_IF(1 == span->coinPtTStart()->fT, span);
1593 bool onEnd = span->coinPtTStart()->fT == 0;
1594 bool oOnEnd = zero_or_one(span->oppPtTStart()->fT);
1595 if (onEnd) {
1596 if (!oOnEnd) { // if both are on end, any nearby intersect was already found
1597 if (this->debugAddEndMovedSpans(log, span->oppPtTStart()), false) {
1598 return;
1599 }
1600 }
1601 } else if (oOnEnd) {
1602 if (this->debugAddEndMovedSpans(log, span->coinPtTStart()), false) {
1603 return;
1604 }
1605 }
1606 }
1607 if (span->coinPtTEnd()->fPt != span->oppPtTEnd()->fPt) {
1608 bool onEnd = span->coinPtTEnd()->fT == 1;
1609 bool oOnEnd = zero_or_one(span->oppPtTEnd()->fT);
1610 if (onEnd) {
1611 if (!oOnEnd) {
1612 if (this->debugAddEndMovedSpans(log, span->oppPtTEnd()), false) {
1613 return;
1614 }
1615 }
1616 } else if (oOnEnd) {
1617 if (this->debugAddEndMovedSpans(log, span->coinPtTEnd()), false) {
1618 return;
1619 }
1620 }
1621 }
1622 } while ((span = span->next()));
1623 // this->restoreHead();
1624 return;
1625 }
1626
1627 /* Commented-out lines keep this in sync with addExpanded */
1628 // for each coincident pair, match the spans
1629 // if the spans don't match, add the mssing pt to the segment and loop it in the opposite span
debugAddExpanded(SkPathOpsDebug::GlitchLog * log) const1630 void SkOpCoincidence::debugAddExpanded(SkPathOpsDebug::GlitchLog* log) const {
1631 // DEBUG_SET_PHASE();
1632 const SkCoincidentSpans* coin = this->fHead;
1633 if (!coin) {
1634 return;
1635 }
1636 do {
1637 const SkOpPtT* startPtT = coin->coinPtTStart();
1638 const SkOpPtT* oStartPtT = coin->oppPtTStart();
1639 double priorT = startPtT->fT;
1640 double oPriorT = oStartPtT->fT;
1641 FAIL_IF(!startPtT->contains(oStartPtT), coin);
1642 SkOPASSERT(coin->coinPtTEnd()->contains(coin->oppPtTEnd()));
1643 const SkOpSpanBase* start = startPtT->span();
1644 const SkOpSpanBase* oStart = oStartPtT->span();
1645 const SkOpSpanBase* end = coin->coinPtTEnd()->span();
1646 const SkOpSpanBase* oEnd = coin->oppPtTEnd()->span();
1647 FAIL_IF(oEnd->deleted(), coin);
1648 FAIL_IF(!start->upCastable(), coin);
1649 const SkOpSpanBase* test = start->upCast()->next();
1650 FAIL_IF(!coin->flipped() && !oStart->upCastable(), coin);
1651 const SkOpSpanBase* oTest = coin->flipped() ? oStart->prev() : oStart->upCast()->next();
1652 FAIL_IF(!oTest, coin);
1653 const SkOpSegment* seg = start->segment();
1654 const SkOpSegment* oSeg = oStart->segment();
1655 while (test != end || oTest != oEnd) {
1656 const SkOpPtT* containedOpp = test->ptT()->contains(oSeg);
1657 const SkOpPtT* containedThis = oTest->ptT()->contains(seg);
1658 if (!containedOpp || !containedThis) {
1659 // choose the ends, or the first common pt-t list shared by both
1660 double nextT, oNextT;
1661 if (containedOpp) {
1662 nextT = test->t();
1663 oNextT = containedOpp->fT;
1664 } else if (containedThis) {
1665 nextT = containedThis->fT;
1666 oNextT = oTest->t();
1667 } else {
1668 // iterate through until a pt-t list found that contains the other
1669 const SkOpSpanBase* walk = test;
1670 const SkOpPtT* walkOpp;
1671 do {
1672 FAIL_IF(!walk->upCastable(), coin);
1673 walk = walk->upCast()->next();
1674 } while (!(walkOpp = walk->ptT()->contains(oSeg))
1675 && walk != coin->coinPtTEnd()->span());
1676 FAIL_IF(!walkOpp, coin);
1677 nextT = walk->t();
1678 oNextT = walkOpp->fT;
1679 }
1680 // use t ranges to guess which one is missing
1681 double startRange = nextT - priorT;
1682 FAIL_IF(!startRange, coin);
1683 double startPart = (test->t() - priorT) / startRange;
1684 double oStartRange = oNextT - oPriorT;
1685 FAIL_IF(!oStartRange, coin);
1686 double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange;
1687 FAIL_IF(startPart == oStartPart, coin);
1688 bool addToOpp = !containedOpp && !containedThis ? startPart < oStartPart
1689 : !!containedThis;
1690 bool startOver = false;
1691 addToOpp ? log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch,
1692 oPriorT + oStartRange * startPart, test)
1693 : log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch,
1694 priorT + startRange * oStartPart, oTest);
1695 // FAIL_IF(!success, coin);
1696 if (startOver) {
1697 test = start;
1698 oTest = oStart;
1699 }
1700 end = coin->coinPtTEnd()->span();
1701 oEnd = coin->oppPtTEnd()->span();
1702 }
1703 if (test != end) {
1704 FAIL_IF(!test->upCastable(), coin);
1705 priorT = test->t();
1706 test = test->upCast()->next();
1707 }
1708 if (oTest != oEnd) {
1709 oPriorT = oTest->t();
1710 oTest = coin->flipped() ? oTest->prev() : oTest->upCast()->next();
1711 FAIL_IF(!oTest, coin);
1712 }
1713 }
1714 } while ((coin = coin->next()));
1715 return;
1716 }
1717
1718 /* Commented-out lines keep this in sync addIfMissing() */
1719 // note that over1s, over1e, over2s, over2e are ordered
debugAddIfMissing(SkPathOpsDebug::GlitchLog * log,const SkOpPtT * over1s,const SkOpPtT * over2s,double tStart,double tEnd,const SkOpSegment * coinSeg,const SkOpSegment * oppSeg,bool * added,const SkOpPtT * over1e,const SkOpPtT * over2e) const1720 void SkOpCoincidence::debugAddIfMissing(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* over1s, const SkOpPtT* over2s,
1721 double tStart, double tEnd, const SkOpSegment* coinSeg, const SkOpSegment* oppSeg, bool* added,
1722 const SkOpPtT* over1e, const SkOpPtT* over2e) const {
1723 SkASSERT(tStart < tEnd);
1724 SkASSERT(over1s->fT < over1e->fT);
1725 SkASSERT(between(over1s->fT, tStart, over1e->fT));
1726 SkASSERT(between(over1s->fT, tEnd, over1e->fT));
1727 SkASSERT(over2s->fT < over2e->fT);
1728 SkASSERT(between(over2s->fT, tStart, over2e->fT));
1729 SkASSERT(between(over2s->fT, tEnd, over2e->fT));
1730 SkASSERT(over1s->segment() == over1e->segment());
1731 SkASSERT(over2s->segment() == over2e->segment());
1732 SkASSERT(over1s->segment() == over2s->segment());
1733 SkASSERT(over1s->segment() != coinSeg);
1734 SkASSERT(over1s->segment() != oppSeg);
1735 SkASSERT(coinSeg != oppSeg);
1736 double coinTs, coinTe, oppTs, oppTe;
1737 coinTs = TRange(over1s, tStart, coinSeg SkDEBUGPARAMS(over1e));
1738 coinTe = TRange(over1s, tEnd, coinSeg SkDEBUGPARAMS(over1e));
1739 if (coinSeg->collapsed(coinTs, coinTe)) {
1740 return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, coinSeg);
1741 }
1742 oppTs = TRange(over2s, tStart, oppSeg SkDEBUGPARAMS(over2e));
1743 oppTe = TRange(over2s, tEnd, oppSeg SkDEBUGPARAMS(over2e));
1744 if (oppSeg->collapsed(oppTs, oppTe)) {
1745 return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, oppSeg);
1746 }
1747 if (coinTs > coinTe) {
1748 SkTSwap(coinTs, coinTe);
1749 SkTSwap(oppTs, oppTe);
1750 }
1751 return this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, added
1752 );
1753 }
1754
1755 /* Commented-out lines keep this in sync addOrOverlap() */
1756 // If this is called by addEndMovedSpans(), a returned false propogates out to an abort.
1757 // If this is called by AddIfMissing(), a returned false indicates there was nothing to add
debugAddOrOverlap(SkPathOpsDebug::GlitchLog * log,const SkOpSegment * coinSeg,const SkOpSegment * oppSeg,double coinTs,double coinTe,double oppTs,double oppTe,bool * added) const1758 void SkOpCoincidence::debugAddOrOverlap(SkPathOpsDebug::GlitchLog* log,
1759 const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
1760 double coinTs, double coinTe, double oppTs, double oppTe, bool* added) const {
1761 SkTDArray<SkCoincidentSpans*> overlaps;
1762 SkOPASSERT(!fTop); // this is (correctly) reversed in addifMissing()
1763 if (fTop && !this->checkOverlap(fTop, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe,
1764 &overlaps)) {
1765 return;
1766 }
1767 if (fHead && !this->checkOverlap(fHead, coinSeg, oppSeg, coinTs,
1768 coinTe, oppTs, oppTe, &overlaps)) {
1769 return;
1770 }
1771 const SkCoincidentSpans* overlap = overlaps.count() ? overlaps[0] : nullptr;
1772 for (int index = 1; index < overlaps.count(); ++index) { // combine overlaps before continuing
1773 const SkCoincidentSpans* test = overlaps[index];
1774 if (overlap->coinPtTStart()->fT > test->coinPtTStart()->fT) {
1775 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTStart());
1776 }
1777 if (overlap->coinPtTEnd()->fT < test->coinPtTEnd()->fT) {
1778 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTEnd());
1779 }
1780 if (overlap->flipped()
1781 ? overlap->oppPtTStart()->fT < test->oppPtTStart()->fT
1782 : overlap->oppPtTStart()->fT > test->oppPtTStart()->fT) {
1783 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTStart());
1784 }
1785 if (overlap->flipped()
1786 ? overlap->oppPtTEnd()->fT > test->oppPtTEnd()->fT
1787 : overlap->oppPtTEnd()->fT < test->oppPtTEnd()->fT) {
1788 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTEnd());
1789 }
1790 if (!fHead) { this->debugRelease(log, fHead, test);
1791 this->debugRelease(log, fTop, test);
1792 }
1793 }
1794 const SkOpPtT* cs = coinSeg->existing(coinTs, oppSeg);
1795 const SkOpPtT* ce = coinSeg->existing(coinTe, oppSeg);
1796 RETURN_FALSE_IF(overlap && cs && ce && overlap->contains(cs, ce), coinSeg);
1797 RETURN_FALSE_IF(cs != ce || !cs, coinSeg);
1798 const SkOpPtT* os = oppSeg->existing(oppTs, coinSeg);
1799 const SkOpPtT* oe = oppSeg->existing(oppTe, coinSeg);
1800 RETURN_FALSE_IF(overlap && os && oe && overlap->contains(os, oe), oppSeg);
1801 SkASSERT(true || !cs || !cs->deleted());
1802 SkASSERT(true || !os || !os->deleted());
1803 SkASSERT(true || !ce || !ce->deleted());
1804 SkASSERT(true || !oe || !oe->deleted());
1805 const SkOpPtT* csExisting = !cs ? coinSeg->existing(coinTs, nullptr) : nullptr;
1806 const SkOpPtT* ceExisting = !ce ? coinSeg->existing(coinTe, nullptr) : nullptr;
1807 RETURN_FALSE_IF(csExisting && csExisting == ceExisting, coinSeg);
1808 RETURN_FALSE_IF(csExisting && (csExisting == ce ||
1809 csExisting->contains(ceExisting ? ceExisting : ce)), coinSeg);
1810 RETURN_FALSE_IF(ceExisting && (ceExisting == cs ||
1811 ceExisting->contains(csExisting ? csExisting : cs)), coinSeg);
1812 const SkOpPtT* osExisting = !os ? oppSeg->existing(oppTs, nullptr) : nullptr;
1813 const SkOpPtT* oeExisting = !oe ? oppSeg->existing(oppTe, nullptr) : nullptr;
1814 RETURN_FALSE_IF(osExisting && osExisting == oeExisting, oppSeg);
1815 RETURN_FALSE_IF(osExisting && (osExisting == oe ||
1816 osExisting->contains(oeExisting ? oeExisting : oe)), oppSeg);
1817 RETURN_FALSE_IF(oeExisting && (oeExisting == os ||
1818 oeExisting->contains(osExisting ? osExisting : os)), oppSeg);
1819 bool csDeleted = false, osDeleted = false, ceDeleted = false, oeDeleted = false;
1820 this->debugValidate();
1821 if (!cs || !os) {
1822 if (!cs)
1823 cs = coinSeg->debugAddT(coinTs, log);
1824 if (!os)
1825 os = oppSeg->debugAddT(oppTs, log);
1826 // RETURN_FALSE_IF(callerAborts, !csWritable || !osWritable);
1827 if (cs && os) cs->span()->debugAddOpp(log, os->span());
1828 // cs = csWritable;
1829 // os = osWritable->active();
1830 RETURN_FALSE_IF((ce && ce->deleted()) || (oe && oe->deleted()), coinSeg);
1831 }
1832 if (!ce || !oe) {
1833 if (!ce)
1834 ce = coinSeg->debugAddT(coinTe, log);
1835 if (!oe)
1836 oe = oppSeg->debugAddT(oppTe, log);
1837 if (ce && oe) ce->span()->debugAddOpp(log, oe->span());
1838 // ce = ceWritable;
1839 // oe = oeWritable;
1840 }
1841 this->debugValidate();
1842 RETURN_FALSE_IF(csDeleted, coinSeg);
1843 RETURN_FALSE_IF(osDeleted, oppSeg);
1844 RETURN_FALSE_IF(ceDeleted, coinSeg);
1845 RETURN_FALSE_IF(oeDeleted, oppSeg);
1846 RETURN_FALSE_IF(!cs || !ce || cs == ce || cs->contains(ce) || !os || !oe || os == oe || os->contains(oe), coinSeg);
1847 bool result = true;
1848 if (overlap) {
1849 if (overlap->coinPtTStart()->segment() == coinSeg) {
1850 log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
1851 } else {
1852 if (oppTs > oppTe) {
1853 SkTSwap(coinTs, coinTe);
1854 SkTSwap(oppTs, oppTe);
1855 }
1856 log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, oppSeg, oppTs, oppTe, coinSeg, coinTs, coinTe);
1857 }
1858 #if 0 && DEBUG_COINCIDENCE_VERBOSE
1859 if (result) {
1860 overlap->debugShow();
1861 }
1862 #endif
1863 } else {
1864 log->record(SkPathOpsDebug::kAddMissingCoin_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
1865 #if 0 && DEBUG_COINCIDENCE_VERBOSE
1866 fHead->debugShow();
1867 #endif
1868 }
1869 this->debugValidate();
1870 return (void) result;
1871 }
1872
1873 // Extra commented-out lines keep this in sync with addMissing()
1874 /* detects overlaps of different coincident runs on same segment */
1875 /* does not detect overlaps for pairs without any segments in common */
1876 // returns true if caller should loop again
debugAddMissing(SkPathOpsDebug::GlitchLog * log,bool * added) const1877 void SkOpCoincidence::debugAddMissing(SkPathOpsDebug::GlitchLog* log, bool* added) const {
1878 const SkCoincidentSpans* outer = fHead;
1879 *added = false;
1880 if (!outer) {
1881 return;
1882 }
1883 // fTop = outer;
1884 // fHead = nullptr;
1885 do {
1886 // addifmissing can modify the list that this is walking
1887 // save head so that walker can iterate over old data unperturbed
1888 // addifmissing adds to head freely then add saved head in the end
1889 const SkOpPtT* ocs = outer->coinPtTStart();
1890 SkASSERT(!ocs->deleted());
1891 const SkOpSegment* outerCoin = ocs->segment();
1892 SkASSERT(!outerCoin->done()); // if it's done, should have already been removed from list
1893 const SkOpPtT* oos = outer->oppPtTStart();
1894 if (oos->deleted()) {
1895 return;
1896 }
1897 const SkOpSegment* outerOpp = oos->segment();
1898 SkASSERT(!outerOpp->done());
1899 // SkOpSegment* outerCoinWritable = const_cast<SkOpSegment*>(outerCoin);
1900 // SkOpSegment* outerOppWritable = const_cast<SkOpSegment*>(outerOpp);
1901 const SkCoincidentSpans* inner = outer;
1902 while ((inner = inner->next())) {
1903 this->debugValidate();
1904 double overS, overE;
1905 const SkOpPtT* ics = inner->coinPtTStart();
1906 SkASSERT(!ics->deleted());
1907 const SkOpSegment* innerCoin = ics->segment();
1908 SkASSERT(!innerCoin->done());
1909 const SkOpPtT* ios = inner->oppPtTStart();
1910 SkASSERT(!ios->deleted());
1911 const SkOpSegment* innerOpp = ios->segment();
1912 SkASSERT(!innerOpp->done());
1913 // SkOpSegment* innerCoinWritable = const_cast<SkOpSegment*>(innerCoin);
1914 // SkOpSegment* innerOppWritable = const_cast<SkOpSegment*>(innerOpp);
1915 if (outerCoin == innerCoin) {
1916 const SkOpPtT* oce = outer->coinPtTEnd();
1917 if (oce->deleted()) {
1918 return;
1919 }
1920 const SkOpPtT* ice = inner->coinPtTEnd();
1921 SkASSERT(!ice->deleted());
1922 if (outerOpp != innerOpp && this->overlap(ocs, oce, ics, ice, &overS, &overE)) {
1923 this->debugAddIfMissing(log, ocs->starter(oce), ics->starter(ice),
1924 overS, overE, outerOpp, innerOpp, added,
1925 ocs->debugEnder(oce),
1926 ics->debugEnder(ice));
1927 }
1928 } else if (outerCoin == innerOpp) {
1929 const SkOpPtT* oce = outer->coinPtTEnd();
1930 SkASSERT(!oce->deleted());
1931 const SkOpPtT* ioe = inner->oppPtTEnd();
1932 SkASSERT(!ioe->deleted());
1933 if (outerOpp != innerCoin && this->overlap(ocs, oce, ios, ioe, &overS, &overE)) {
1934 this->debugAddIfMissing(log, ocs->starter(oce), ios->starter(ioe),
1935 overS, overE, outerOpp, innerCoin, added,
1936 ocs->debugEnder(oce),
1937 ios->debugEnder(ioe));
1938 }
1939 } else if (outerOpp == innerCoin) {
1940 const SkOpPtT* ooe = outer->oppPtTEnd();
1941 SkASSERT(!ooe->deleted());
1942 const SkOpPtT* ice = inner->coinPtTEnd();
1943 SkASSERT(!ice->deleted());
1944 SkASSERT(outerCoin != innerOpp);
1945 if (this->overlap(oos, ooe, ics, ice, &overS, &overE)) {
1946 this->debugAddIfMissing(log, oos->starter(ooe), ics->starter(ice),
1947 overS, overE, outerCoin, innerOpp, added,
1948 oos->debugEnder(ooe),
1949 ics->debugEnder(ice));
1950 }
1951 } else if (outerOpp == innerOpp) {
1952 const SkOpPtT* ooe = outer->oppPtTEnd();
1953 SkASSERT(!ooe->deleted());
1954 const SkOpPtT* ioe = inner->oppPtTEnd();
1955 if (ioe->deleted()) {
1956 return;
1957 }
1958 SkASSERT(outerCoin != innerCoin);
1959 if (this->overlap(oos, ooe, ios, ioe, &overS, &overE)) {
1960 this->debugAddIfMissing(log, oos->starter(ooe), ios->starter(ioe),
1961 overS, overE, outerCoin, innerCoin, added,
1962 oos->debugEnder(ooe),
1963 ios->debugEnder(ioe));
1964 }
1965 }
1966 this->debugValidate();
1967 }
1968 } while ((outer = outer->next()));
1969 // this->restoreHead();
1970 return;
1971 }
1972
1973 // Commented-out lines keep this in sync with release()
debugRelease(SkPathOpsDebug::GlitchLog * log,const SkCoincidentSpans * coin,const SkCoincidentSpans * remove) const1974 void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkCoincidentSpans* remove) const {
1975 const SkCoincidentSpans* head = coin;
1976 const SkCoincidentSpans* prev = nullptr;
1977 const SkCoincidentSpans* next;
1978 do {
1979 next = coin->next();
1980 if (coin == remove) {
1981 if (prev) {
1982 // prev->setNext(next);
1983 } else if (head == fHead) {
1984 // fHead = next;
1985 } else {
1986 // fTop = next;
1987 }
1988 log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
1989 }
1990 prev = coin;
1991 } while ((coin = next));
1992 return;
1993 }
1994
debugRelease(SkPathOpsDebug::GlitchLog * log,const SkOpSegment * deleted) const1995 void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* deleted) const {
1996 const SkCoincidentSpans* coin = fHead;
1997 if (!coin) {
1998 return;
1999 }
2000 do {
2001 if (coin->coinPtTStart()->segment() == deleted
2002 || coin->coinPtTEnd()->segment() == deleted
2003 || coin->oppPtTStart()->segment() == deleted
2004 || coin->oppPtTEnd()->segment() == deleted) {
2005 log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
2006 }
2007 } while ((coin = coin->next()));
2008 }
2009
2010 // Commented-out lines keep this in sync with expand()
2011 // expand the range by checking adjacent spans for coincidence
debugExpand(SkPathOpsDebug::GlitchLog * log) const2012 bool SkOpCoincidence::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
2013 const SkCoincidentSpans* coin = fHead;
2014 if (!coin) {
2015 return false;
2016 }
2017 bool expanded = false;
2018 do {
2019 if (coin->debugExpand(log)) {
2020 // check to see if multiple spans expanded so they are now identical
2021 const SkCoincidentSpans* test = fHead;
2022 do {
2023 if (coin == test) {
2024 continue;
2025 }
2026 if (coin->coinPtTStart() == test->coinPtTStart()
2027 && coin->oppPtTStart() == test->oppPtTStart()) {
2028 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, fHead, test->coinPtTStart());
2029 break;
2030 }
2031 } while ((test = test->next()));
2032 expanded = true;
2033 }
2034 } while ((coin = coin->next()));
2035 return expanded;
2036 }
2037
2038 // Commented-out lines keep this in sync with mark()
2039 /* this sets up the coincidence links in the segments when the coincidence crosses multiple spans */
debugMark(SkPathOpsDebug::GlitchLog * log) const2040 void SkOpCoincidence::debugMark(SkPathOpsDebug::GlitchLog* log) const {
2041 const SkCoincidentSpans* coin = fHead;
2042 if (!coin) {
2043 return;
2044 }
2045 do {
2046 FAIL_IF(!coin->coinPtTStartWritable()->span()->upCastable(), coin);
2047 const SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast();
2048 // SkASSERT(start->deleted());
2049 const SkOpSpanBase* end = coin->coinPtTEndWritable()->span();
2050 // SkASSERT(end->deleted());
2051 const SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span();
2052 // SkASSERT(oStart->deleted());
2053 const SkOpSpanBase* oEnd = coin->oppPtTEndWritable()->span();
2054 // SkASSERT(oEnd->deleted());
2055 bool flipped = coin->flipped();
2056 if (flipped) {
2057 SkTSwap(oStart, oEnd);
2058 }
2059 /* coin and opp spans may not match up. Mark the ends, and then let the interior
2060 get marked as many times as the spans allow */
2061 start->debugInsertCoincidence(log, oStart->upCast());
2062 end->debugInsertCoinEnd(log, oEnd);
2063 const SkOpSegment* segment = start->segment();
2064 const SkOpSegment* oSegment = oStart->segment();
2065 const SkOpSpanBase* next = start;
2066 const SkOpSpanBase* oNext = oStart;
2067 bool ordered;
2068 FAIL_IF(!coin->ordered(&ordered), coin);
2069 while ((next = next->upCast()->next()) != end) {
2070 FAIL_IF(!next->upCastable(), coin);
2071 if (next->upCast()->debugInsertCoincidence(log, oSegment, flipped, ordered), false) {
2072 return;
2073 }
2074 }
2075 while ((oNext = oNext->upCast()->next()) != oEnd) {
2076 FAIL_IF(!oNext->upCastable(), coin);
2077 if (oNext->upCast()->debugInsertCoincidence(log, segment, flipped, ordered), false) {
2078 return;
2079 }
2080 }
2081 } while ((coin = coin->next()));
2082 return;
2083 }
2084 #endif
2085
2086 #if DEBUG_COIN
2087 // Commented-out lines keep this in sync with markCollapsed()
debugMarkCollapsed(SkPathOpsDebug::GlitchLog * log,const SkCoincidentSpans * coin,const SkOpPtT * test) const2088 void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkOpPtT* test) const {
2089 const SkCoincidentSpans* head = coin;
2090 while (coin) {
2091 if (coin->collapsed(test)) {
2092 if (zero_or_one(coin->coinPtTStart()->fT) && zero_or_one(coin->coinPtTEnd()->fT)) {
2093 log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
2094 }
2095 if (zero_or_one(coin->oppPtTStart()->fT) && zero_or_one(coin->oppPtTEnd()->fT)) {
2096 log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
2097 }
2098 this->debugRelease(log, head, coin);
2099 }
2100 coin = coin->next();
2101 }
2102 }
2103
2104 // Commented-out lines keep this in sync with markCollapsed()
debugMarkCollapsed(SkPathOpsDebug::GlitchLog * log,const SkOpPtT * test) const2105 void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* test) const {
2106 this->debugMarkCollapsed(log, fHead, test);
2107 this->debugMarkCollapsed(log, fTop, test);
2108 }
2109 #endif
2110
debugShow() const2111 void SkCoincidentSpans::debugShow() const {
2112 SkDebugf("coinSpan - id=%d t=%1.9g tEnd=%1.9g\n", coinPtTStart()->segment()->debugID(),
2113 coinPtTStart()->fT, coinPtTEnd()->fT);
2114 SkDebugf("coinSpan + id=%d t=%1.9g tEnd=%1.9g\n", oppPtTStart()->segment()->debugID(),
2115 oppPtTStart()->fT, oppPtTEnd()->fT);
2116 }
2117
debugShowCoincidence() const2118 void SkOpCoincidence::debugShowCoincidence() const {
2119 #if DEBUG_COINCIDENCE
2120 const SkCoincidentSpans* span = fHead;
2121 while (span) {
2122 span->debugShow();
2123 span = span->next();
2124 }
2125 #endif
2126 }
2127
2128 #if DEBUG_COIN
DebugCheckBetween(const SkOpSpanBase * next,const SkOpSpanBase * end,double oStart,double oEnd,const SkOpSegment * oSegment,SkPathOpsDebug::GlitchLog * log)2129 static void DebugCheckBetween(const SkOpSpanBase* next, const SkOpSpanBase* end,
2130 double oStart, double oEnd, const SkOpSegment* oSegment,
2131 SkPathOpsDebug::GlitchLog* log) {
2132 SkASSERT(next != end);
2133 SkASSERT(!next->contains(end) || log);
2134 if (next->t() > end->t()) {
2135 SkTSwap(next, end);
2136 }
2137 do {
2138 const SkOpPtT* ptT = next->ptT();
2139 int index = 0;
2140 bool somethingBetween = false;
2141 do {
2142 ++index;
2143 ptT = ptT->next();
2144 const SkOpPtT* checkPtT = next->ptT();
2145 if (ptT == checkPtT) {
2146 break;
2147 }
2148 bool looped = false;
2149 for (int check = 0; check < index; ++check) {
2150 if ((looped = checkPtT == ptT)) {
2151 break;
2152 }
2153 checkPtT = checkPtT->next();
2154 }
2155 if (looped) {
2156 SkASSERT(0);
2157 break;
2158 }
2159 if (ptT->deleted()) {
2160 continue;
2161 }
2162 if (ptT->segment() != oSegment) {
2163 continue;
2164 }
2165 somethingBetween |= between(oStart, ptT->fT, oEnd);
2166 } while (true);
2167 SkASSERT(somethingBetween);
2168 } while (next != end && (next = next->upCast()->next()));
2169 }
2170
DebugCheckOverlap(const SkCoincidentSpans * test,const SkCoincidentSpans * list,SkPathOpsDebug::GlitchLog * log)2171 static void DebugCheckOverlap(const SkCoincidentSpans* test, const SkCoincidentSpans* list,
2172 SkPathOpsDebug::GlitchLog* log) {
2173 if (!list) {
2174 return;
2175 }
2176 const SkOpSegment* coinSeg = test->coinPtTStart()->segment();
2177 SkASSERT(coinSeg == test->coinPtTEnd()->segment());
2178 const SkOpSegment* oppSeg = test->oppPtTStart()->segment();
2179 SkASSERT(oppSeg == test->oppPtTEnd()->segment());
2180 SkASSERT(coinSeg != test->oppPtTStart()->segment());
2181 SkDEBUGCODE(double tcs = test->coinPtTStart()->fT);
2182 SkASSERT(between(0, tcs, 1));
2183 SkDEBUGCODE(double tce = test->coinPtTEnd()->fT);
2184 SkASSERT(between(0, tce, 1));
2185 SkASSERT(tcs < tce);
2186 double tos = test->oppPtTStart()->fT;
2187 SkASSERT(between(0, tos, 1));
2188 double toe = test->oppPtTEnd()->fT;
2189 SkASSERT(between(0, toe, 1));
2190 SkASSERT(tos != toe);
2191 if (tos > toe) {
2192 SkTSwap(tos, toe);
2193 }
2194 do {
2195 double lcs, lce, los, loe;
2196 if (coinSeg == list->coinPtTStart()->segment()) {
2197 if (oppSeg != list->oppPtTStart()->segment()) {
2198 continue;
2199 }
2200 lcs = list->coinPtTStart()->fT;
2201 lce = list->coinPtTEnd()->fT;
2202 los = list->oppPtTStart()->fT;
2203 loe = list->oppPtTEnd()->fT;
2204 if (los > loe) {
2205 SkTSwap(los, loe);
2206 }
2207 } else if (coinSeg == list->oppPtTStart()->segment()) {
2208 if (oppSeg != list->coinPtTStart()->segment()) {
2209 continue;
2210 }
2211 lcs = list->oppPtTStart()->fT;
2212 lce = list->oppPtTEnd()->fT;
2213 if (lcs > lce) {
2214 SkTSwap(lcs, lce);
2215 }
2216 los = list->coinPtTStart()->fT;
2217 loe = list->coinPtTEnd()->fT;
2218 } else {
2219 continue;
2220 }
2221 SkASSERT(tce < lcs || lce < tcs);
2222 SkASSERT(toe < los || loe < tos);
2223 } while ((list = list->next()));
2224 }
2225
2226
DebugCheckOverlapTop(const SkCoincidentSpans * head,const SkCoincidentSpans * opt,SkPathOpsDebug::GlitchLog * log)2227 static void DebugCheckOverlapTop(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
2228 SkPathOpsDebug::GlitchLog* log) {
2229 // check for overlapping coincident spans
2230 const SkCoincidentSpans* test = head;
2231 while (test) {
2232 const SkCoincidentSpans* next = test->next();
2233 DebugCheckOverlap(test, next, log);
2234 DebugCheckOverlap(test, opt, log);
2235 test = next;
2236 }
2237 }
2238
DebugValidate(const SkCoincidentSpans * head,const SkCoincidentSpans * opt,SkPathOpsDebug::GlitchLog * log)2239 static void DebugValidate(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
2240 SkPathOpsDebug::GlitchLog* log) {
2241 // look for pts inside coincident spans that are not inside the opposite spans
2242 const SkCoincidentSpans* coin = head;
2243 while (coin) {
2244 SkASSERT(SkOpCoincidence::Ordered(coin->coinPtTStart()->segment(),
2245 coin->oppPtTStart()->segment()));
2246 SkASSERT(coin->coinPtTStart()->span()->ptT() == coin->coinPtTStart());
2247 SkASSERT(coin->coinPtTEnd()->span()->ptT() == coin->coinPtTEnd());
2248 SkASSERT(coin->oppPtTStart()->span()->ptT() == coin->oppPtTStart());
2249 SkASSERT(coin->oppPtTEnd()->span()->ptT() == coin->oppPtTEnd());
2250 coin = coin->next();
2251 }
2252 DebugCheckOverlapTop(head, opt, log);
2253 }
2254 #endif
2255
debugValidate() const2256 void SkOpCoincidence::debugValidate() const {
2257 #if DEBUG_COINCIDENCE
2258 DebugValidate(fHead, fTop, nullptr);
2259 DebugValidate(fTop, nullptr, nullptr);
2260 #endif
2261 }
2262
2263 #if DEBUG_COIN
DebugCheckBetween(const SkCoincidentSpans * head,const SkCoincidentSpans * opt,SkPathOpsDebug::GlitchLog * log)2264 static void DebugCheckBetween(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
2265 SkPathOpsDebug::GlitchLog* log) {
2266 // look for pts inside coincident spans that are not inside the opposite spans
2267 const SkCoincidentSpans* coin = head;
2268 while (coin) {
2269 DebugCheckBetween(coin->coinPtTStart()->span(), coin->coinPtTEnd()->span(),
2270 coin->oppPtTStart()->fT, coin->oppPtTEnd()->fT, coin->oppPtTStart()->segment(),
2271 log);
2272 DebugCheckBetween(coin->oppPtTStart()->span(), coin->oppPtTEnd()->span(),
2273 coin->coinPtTStart()->fT, coin->coinPtTEnd()->fT, coin->coinPtTStart()->segment(),
2274 log);
2275 coin = coin->next();
2276 }
2277 DebugCheckOverlapTop(head, opt, log);
2278 }
2279 #endif
2280
debugCheckBetween() const2281 void SkOpCoincidence::debugCheckBetween() const {
2282 #if DEBUG_COINCIDENCE
2283 if (fGlobalState->debugCheckHealth()) {
2284 return;
2285 }
2286 DebugCheckBetween(fHead, fTop, nullptr);
2287 DebugCheckBetween(fTop, nullptr, nullptr);
2288 #endif
2289 }
2290
2291 #if DEBUG_COIN
debugCheckHealth(SkPathOpsDebug::GlitchLog * log) const2292 void SkOpContour::debugCheckHealth(SkPathOpsDebug::GlitchLog* log) const {
2293 const SkOpSegment* segment = &fHead;
2294 do {
2295 segment->debugCheckHealth(log);
2296 } while ((segment = segment->next()));
2297 }
2298
debugCheckValid(SkPathOpsDebug::GlitchLog * log) const2299 void SkOpCoincidence::debugCheckValid(SkPathOpsDebug::GlitchLog* log) const {
2300 #if DEBUG_VALIDATE
2301 DebugValidate(fHead, fTop, log);
2302 DebugValidate(fTop, nullptr, log);
2303 #endif
2304 }
2305
debugCorrectEnds(SkPathOpsDebug::GlitchLog * log) const2306 void SkOpCoincidence::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
2307 const SkCoincidentSpans* coin = fHead;
2308 if (!coin) {
2309 return;
2310 }
2311 do {
2312 coin->debugCorrectEnds(log);
2313 } while ((coin = coin->next()));
2314 }
2315
2316 // commmented-out lines keep this aligned with missingCoincidence()
debugMissingCoincidence(SkPathOpsDebug::GlitchLog * log) const2317 void SkOpContour::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const {
2318 // SkASSERT(fCount > 0);
2319 const SkOpSegment* segment = &fHead;
2320 // bool result = false;
2321 do {
2322 if (segment->debugMissingCoincidence(log), false) {
2323 // result = true;
2324 }
2325 segment = segment->next();
2326 } while (segment);
2327 return;
2328 }
2329
debugMoveMultiples(SkPathOpsDebug::GlitchLog * log) const2330 void SkOpContour::debugMoveMultiples(SkPathOpsDebug::GlitchLog* log) const {
2331 SkASSERT(fCount > 0);
2332 const SkOpSegment* segment = &fHead;
2333 do {
2334 if (segment->debugMoveMultiples(log), false) {
2335 return;
2336 }
2337 } while ((segment = segment->next()));
2338 return;
2339 }
2340
debugMoveNearby(SkPathOpsDebug::GlitchLog * log) const2341 void SkOpContour::debugMoveNearby(SkPathOpsDebug::GlitchLog* log) const {
2342 SkASSERT(fCount > 0);
2343 const SkOpSegment* segment = &fHead;
2344 do {
2345 segment->debugMoveNearby(log);
2346 } while ((segment = segment->next()));
2347 }
2348 #endif
2349
2350 #if DEBUG_COINCIDENCE_ORDER
debugResetCoinT() const2351 void SkOpSegment::debugResetCoinT() const {
2352 fDebugBaseIndex = -1;
2353 fDebugBaseMin = 1;
2354 fDebugBaseMax = -1;
2355 fDebugLastIndex = -1;
2356 fDebugLastMin = 1;
2357 fDebugLastMax = -1;
2358 }
2359 #endif
2360
debugValidate() const2361 void SkOpSegment::debugValidate() const {
2362 #if DEBUG_COINCIDENCE_ORDER
2363 {
2364 const SkOpSpanBase* span = &fHead;
2365 do {
2366 span->debugResetCoinT();
2367 } while (!span->final() && (span = span->upCast()->next()));
2368 span = &fHead;
2369 int index = 0;
2370 do {
2371 span->debugSetCoinT(index++);
2372 } while (!span->final() && (span = span->upCast()->next()));
2373 }
2374 #endif
2375 #if DEBUG_COINCIDENCE
2376 if (this->globalState()->debugCheckHealth()) {
2377 return;
2378 }
2379 #endif
2380 #if DEBUG_VALIDATE
2381 const SkOpSpanBase* span = &fHead;
2382 double lastT = -1;
2383 const SkOpSpanBase* prev = nullptr;
2384 int count = 0;
2385 int done = 0;
2386 do {
2387 if (!span->final()) {
2388 ++count;
2389 done += span->upCast()->done() ? 1 : 0;
2390 }
2391 SkASSERT(span->segment() == this);
2392 SkASSERT(!prev || prev->upCast()->next() == span);
2393 SkASSERT(!prev || prev == span->prev());
2394 prev = span;
2395 double t = span->ptT()->fT;
2396 SkASSERT(lastT < t);
2397 lastT = t;
2398 span->debugValidate();
2399 } while (!span->final() && (span = span->upCast()->next()));
2400 SkASSERT(count == fCount);
2401 SkASSERT(done == fDoneCount);
2402 SkASSERT(count >= fDoneCount);
2403 SkASSERT(span->final());
2404 span->debugValidate();
2405 #endif
2406 }
2407
2408 #if DEBUG_COIN
2409
2410 // Commented-out lines keep this in sync with addOpp()
debugAddOpp(SkPathOpsDebug::GlitchLog * log,const SkOpSpanBase * opp) const2411 void SkOpSpanBase::debugAddOpp(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
2412 const SkOpPtT* oppPrev = this->ptT()->oppPrev(opp->ptT());
2413 if (!oppPrev) {
2414 return;
2415 }
2416 this->debugMergeMatches(log, opp);
2417 this->ptT()->debugAddOpp(opp->ptT(), oppPrev);
2418 this->debugCheckForCollapsedCoincidence(log);
2419 }
2420
2421 // Commented-out lines keep this in sync with checkForCollapsedCoincidence()
debugCheckForCollapsedCoincidence(SkPathOpsDebug::GlitchLog * log) const2422 void SkOpSpanBase::debugCheckForCollapsedCoincidence(SkPathOpsDebug::GlitchLog* log) const {
2423 const SkOpCoincidence* coins = this->globalState()->coincidence();
2424 if (coins->isEmpty()) {
2425 return;
2426 }
2427 // the insert above may have put both ends of a coincident run in the same span
2428 // for each coincident ptT in loop; see if its opposite in is also in the loop
2429 // this implementation is the motivation for marking that a ptT is referenced by a coincident span
2430 const SkOpPtT* head = this->ptT();
2431 const SkOpPtT* test = head;
2432 do {
2433 if (!test->coincident()) {
2434 continue;
2435 }
2436 coins->debugMarkCollapsed(log, test);
2437 } while ((test = test->next()) != head);
2438 }
2439 #endif
2440
debugCoinEndLoopCheck() const2441 bool SkOpSpanBase::debugCoinEndLoopCheck() const {
2442 int loop = 0;
2443 const SkOpSpanBase* next = this;
2444 SkOpSpanBase* nextCoin;
2445 do {
2446 nextCoin = next->fCoinEnd;
2447 SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin);
2448 for (int check = 1; check < loop - 1; ++check) {
2449 const SkOpSpanBase* checkCoin = this->fCoinEnd;
2450 const SkOpSpanBase* innerCoin = checkCoin;
2451 for (int inner = check + 1; inner < loop; ++inner) {
2452 innerCoin = innerCoin->fCoinEnd;
2453 if (checkCoin == innerCoin) {
2454 SkDebugf("*** bad coincident end loop ***\n");
2455 return false;
2456 }
2457 }
2458 }
2459 ++loop;
2460 } while ((next = nextCoin) && next != this);
2461 return true;
2462 }
2463
2464 #if DEBUG_COIN
2465 // Commented-out lines keep this in sync with insertCoinEnd()
debugInsertCoinEnd(SkPathOpsDebug::GlitchLog * log,const SkOpSpanBase * coin) const2466 void SkOpSpanBase::debugInsertCoinEnd(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* coin) const {
2467 if (containsCoinEnd(coin)) {
2468 // SkASSERT(coin->containsCoinEnd(this));
2469 return;
2470 }
2471 debugValidate();
2472 // SkASSERT(this != coin);
2473 log->record(SkPathOpsDebug::kMarkCoinEnd_Glitch, this, coin);
2474 // coin->fCoinEnd = this->fCoinEnd;
2475 // this->fCoinEnd = coinNext;
2476 debugValidate();
2477 }
2478
2479 // Commented-out lines keep this in sync with mergeMatches()
2480 // Look to see if pt-t linked list contains same segment more than once
2481 // if so, and if each pt-t is directly pointed to by spans in that segment,
2482 // merge them
2483 // keep the points, but remove spans so that the segment doesn't have 2 or more
2484 // spans pointing to the same pt-t loop at different loop elements
debugMergeMatches(SkPathOpsDebug::GlitchLog * log,const SkOpSpanBase * opp) const2485 void SkOpSpanBase::debugMergeMatches(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
2486 const SkOpPtT* test = &fPtT;
2487 const SkOpPtT* testNext;
2488 const SkOpPtT* stop = test;
2489 do {
2490 testNext = test->next();
2491 if (test->deleted()) {
2492 continue;
2493 }
2494 const SkOpSpanBase* testBase = test->span();
2495 SkASSERT(testBase->ptT() == test);
2496 const SkOpSegment* segment = test->segment();
2497 if (segment->done()) {
2498 continue;
2499 }
2500 const SkOpPtT* inner = opp->ptT();
2501 const SkOpPtT* innerStop = inner;
2502 do {
2503 if (inner->segment() != segment) {
2504 continue;
2505 }
2506 if (inner->deleted()) {
2507 continue;
2508 }
2509 const SkOpSpanBase* innerBase = inner->span();
2510 SkASSERT(innerBase->ptT() == inner);
2511 // when the intersection is first detected, the span base is marked if there are
2512 // more than one point in the intersection.
2513 // if (!innerBase->hasMultipleHint() && !testBase->hasMultipleHint()) {
2514 if (!zero_or_one(inner->fT)) {
2515 log->record(SkPathOpsDebug::kMergeMatches_Glitch, innerBase, test);
2516 } else {
2517 SkASSERT(inner->fT != test->fT);
2518 if (!zero_or_one(test->fT)) {
2519 log->record(SkPathOpsDebug::kMergeMatches_Glitch, testBase, inner);
2520 } else {
2521 log->record(SkPathOpsDebug::kMergeMatches_Glitch, segment);
2522 // SkDEBUGCODE(testBase->debugSetDeleted());
2523 // test->setDeleted();
2524 // SkDEBUGCODE(innerBase->debugSetDeleted());
2525 // inner->setDeleted();
2526 }
2527 }
2528 #ifdef SK_DEBUG // assert if another undeleted entry points to segment
2529 const SkOpPtT* debugInner = inner;
2530 while ((debugInner = debugInner->next()) != innerStop) {
2531 if (debugInner->segment() != segment) {
2532 continue;
2533 }
2534 if (debugInner->deleted()) {
2535 continue;
2536 }
2537 SkOPASSERT(0);
2538 }
2539 #endif
2540 break;
2541 // }
2542 break;
2543 } while ((inner = inner->next()) != innerStop);
2544 } while ((test = testNext) != stop);
2545 this->debugCheckForCollapsedCoincidence(log);
2546 }
2547
2548 #endif
2549
debugResetCoinT() const2550 void SkOpSpanBase::debugResetCoinT() const {
2551 #if DEBUG_COINCIDENCE_ORDER
2552 const SkOpPtT* ptT = &fPtT;
2553 do {
2554 ptT->debugResetCoinT();
2555 ptT = ptT->next();
2556 } while (ptT != &fPtT);
2557 #endif
2558 }
2559
debugSetCoinT(int index) const2560 void SkOpSpanBase::debugSetCoinT(int index) const {
2561 #if DEBUG_COINCIDENCE_ORDER
2562 const SkOpPtT* ptT = &fPtT;
2563 do {
2564 if (!ptT->deleted()) {
2565 ptT->debugSetCoinT(index);
2566 }
2567 ptT = ptT->next();
2568 } while (ptT != &fPtT);
2569 #endif
2570 }
2571
debugStarter(SkOpSpanBase const ** endPtr) const2572 const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const {
2573 const SkOpSpanBase* end = *endPtr;
2574 SkASSERT(this->segment() == end->segment());
2575 const SkOpSpanBase* result;
2576 if (t() < end->t()) {
2577 result = this;
2578 } else {
2579 result = end;
2580 *endPtr = this;
2581 }
2582 return result->upCast();
2583 }
2584
debugValidate() const2585 void SkOpSpanBase::debugValidate() const {
2586 #if DEBUG_COINCIDENCE
2587 if (this->globalState()->debugCheckHealth()) {
2588 return;
2589 }
2590 #endif
2591 #if DEBUG_VALIDATE
2592 const SkOpPtT* ptT = &fPtT;
2593 SkASSERT(ptT->span() == this);
2594 do {
2595 // SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt));
2596 ptT->debugValidate();
2597 ptT = ptT->next();
2598 } while (ptT != &fPtT);
2599 SkASSERT(this->debugCoinEndLoopCheck());
2600 if (!this->final()) {
2601 SkASSERT(this->upCast()->debugCoinLoopCheck());
2602 }
2603 if (fFromAngle) {
2604 fFromAngle->debugValidate();
2605 }
2606 if (!this->final() && this->upCast()->toAngle()) {
2607 this->upCast()->toAngle()->debugValidate();
2608 }
2609 #endif
2610 }
2611
debugCoinLoopCheck() const2612 bool SkOpSpan::debugCoinLoopCheck() const {
2613 int loop = 0;
2614 const SkOpSpan* next = this;
2615 SkOpSpan* nextCoin;
2616 do {
2617 nextCoin = next->fCoincident;
2618 SkASSERT(nextCoin == this || nextCoin->fCoincident != nextCoin);
2619 for (int check = 1; check < loop - 1; ++check) {
2620 const SkOpSpan* checkCoin = this->fCoincident;
2621 const SkOpSpan* innerCoin = checkCoin;
2622 for (int inner = check + 1; inner < loop; ++inner) {
2623 innerCoin = innerCoin->fCoincident;
2624 if (checkCoin == innerCoin) {
2625 SkDebugf("*** bad coincident loop ***\n");
2626 return false;
2627 }
2628 }
2629 }
2630 ++loop;
2631 } while ((next = nextCoin) && next != this);
2632 return true;
2633 }
2634
2635 #if DEBUG_COIN
2636 // Commented-out lines keep this in sync with insertCoincidence() in header
debugInsertCoincidence(SkPathOpsDebug::GlitchLog * log,const SkOpSpan * coin) const2637 void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* coin) const {
2638 if (containsCoincidence(coin)) {
2639 // SkASSERT(coin->containsCoincidence(this));
2640 return;
2641 }
2642 debugValidate();
2643 // SkASSERT(this != coin);
2644 log->record(SkPathOpsDebug::kMarkCoinStart_Glitch, this, coin);
2645 // coin->fCoincident = this->fCoincident;
2646 // this->fCoincident = coinNext;
2647 debugValidate();
2648 }
2649
2650 // Commented-out lines keep this in sync with insertCoincidence()
debugInsertCoincidence(SkPathOpsDebug::GlitchLog * log,const SkOpSegment * segment,bool flipped,bool ordered) const2651 void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* segment, bool flipped, bool ordered) const {
2652 if (this->containsCoincidence(segment)) {
2653 return;
2654 }
2655 const SkOpPtT* next = &fPtT;
2656 while ((next = next->next()) != &fPtT) {
2657 if (next->segment() == segment) {
2658 const SkOpSpan* span;
2659 const SkOpSpanBase* base = next->span();
2660 if (!ordered) {
2661 const SkOpSpanBase* spanEnd = fNext->contains(segment)->span();
2662 const SkOpPtT* start = base->ptT()->starter(spanEnd->ptT());
2663 FAIL_IF(!start->span()->upCastable(), this);
2664 span = const_cast<SkOpSpan*>(start->span()->upCast());
2665 }
2666 else if (flipped) {
2667 span = base->prev();
2668 FAIL_IF(!span, this);
2669 }
2670 else {
2671 FAIL_IF(!base->upCastable(), this);
2672 span = base->upCast();
2673 }
2674 log->record(SkPathOpsDebug::kMarkCoinInsert_Glitch, span);
2675 return;
2676 }
2677 }
2678 #if DEBUG_COIN
2679 log->record(SkPathOpsDebug::kMarkCoinMissing_Glitch, segment, this);
2680 #endif
2681 return;
2682 }
2683 #endif
2684
2685 // called only by test code
debugCoincidentUsed() const2686 int SkIntersections::debugCoincidentUsed() const {
2687 if (!fIsCoincident[0]) {
2688 SkASSERT(!fIsCoincident[1]);
2689 return 0;
2690 }
2691 int count = 0;
2692 SkDEBUGCODE(int count2 = 0;)
2693 for (int index = 0; index < fUsed; ++index) {
2694 if (fIsCoincident[0] & (1 << index)) {
2695 ++count;
2696 }
2697 #ifdef SK_DEBUG
2698 if (fIsCoincident[1] & (1 << index)) {
2699 ++count2;
2700 }
2701 #endif
2702 }
2703 SkASSERT(count == count2);
2704 return count;
2705 }
2706
2707 #include "SkOpContour.h"
2708
2709 // Commented-out lines keep this in sync with addOpp()
debugAddOpp(const SkOpPtT * opp,const SkOpPtT * oppPrev) const2710 void SkOpPtT::debugAddOpp(const SkOpPtT* opp, const SkOpPtT* oppPrev) const {
2711 SkDEBUGCODE(const SkOpPtT* oldNext = this->fNext);
2712 SkASSERT(this != opp);
2713 // this->fNext = opp;
2714 SkASSERT(oppPrev != oldNext);
2715 // oppPrev->fNext = oldNext;
2716 }
2717
debugContains(const SkOpPtT * check) const2718 bool SkOpPtT::debugContains(const SkOpPtT* check) const {
2719 SkASSERT(this != check);
2720 const SkOpPtT* ptT = this;
2721 int links = 0;
2722 do {
2723 ptT = ptT->next();
2724 if (ptT == check) {
2725 return true;
2726 }
2727 ++links;
2728 const SkOpPtT* test = this;
2729 for (int index = 0; index < links; ++index) {
2730 if (ptT == test) {
2731 return false;
2732 }
2733 test = test->next();
2734 }
2735 } while (true);
2736 }
2737
debugContains(const SkOpSegment * check) const2738 const SkOpPtT* SkOpPtT::debugContains(const SkOpSegment* check) const {
2739 SkASSERT(this->segment() != check);
2740 const SkOpPtT* ptT = this;
2741 int links = 0;
2742 do {
2743 ptT = ptT->next();
2744 if (ptT->segment() == check) {
2745 return ptT;
2746 }
2747 ++links;
2748 const SkOpPtT* test = this;
2749 for (int index = 0; index < links; ++index) {
2750 if (ptT == test) {
2751 return nullptr;
2752 }
2753 test = test->next();
2754 }
2755 } while (true);
2756 }
2757
debugEnder(const SkOpPtT * end) const2758 const SkOpPtT* SkOpPtT::debugEnder(const SkOpPtT* end) const {
2759 return fT < end->fT ? end : this;
2760 }
2761
debugLoopLimit(bool report) const2762 int SkOpPtT::debugLoopLimit(bool report) const {
2763 int loop = 0;
2764 const SkOpPtT* next = this;
2765 do {
2766 for (int check = 1; check < loop - 1; ++check) {
2767 const SkOpPtT* checkPtT = this->fNext;
2768 const SkOpPtT* innerPtT = checkPtT;
2769 for (int inner = check + 1; inner < loop; ++inner) {
2770 innerPtT = innerPtT->fNext;
2771 if (checkPtT == innerPtT) {
2772 if (report) {
2773 SkDebugf("*** bad ptT loop ***\n");
2774 }
2775 return loop;
2776 }
2777 }
2778 }
2779 // there's nothing wrong with extremely large loop counts -- but this may appear to hang
2780 // by taking a very long time to figure out that no loop entry is a duplicate
2781 // -- and it's likely that a large loop count is indicative of a bug somewhere
2782 if (++loop > 1000) {
2783 SkDebugf("*** loop count exceeds 1000 ***\n");
2784 return 1000;
2785 }
2786 } while ((next = next->fNext) && next != this);
2787 return 0;
2788 }
2789
debugOppPrev(const SkOpPtT * opp) const2790 const SkOpPtT* SkOpPtT::debugOppPrev(const SkOpPtT* opp) const {
2791 return this->oppPrev(const_cast<SkOpPtT*>(opp));
2792 }
2793
debugResetCoinT() const2794 void SkOpPtT::debugResetCoinT() const {
2795 #if DEBUG_COINCIDENCE_ORDER
2796 this->segment()->debugResetCoinT();
2797 #endif
2798 }
2799
debugSetCoinT(int index) const2800 void SkOpPtT::debugSetCoinT(int index) const {
2801 #if DEBUG_COINCIDENCE_ORDER
2802 this->segment()->debugSetCoinT(index, fT);
2803 #endif
2804 }
2805
debugValidate() const2806 void SkOpPtT::debugValidate() const {
2807 #if DEBUG_COINCIDENCE
2808 if (this->globalState()->debugCheckHealth()) {
2809 return;
2810 }
2811 #endif
2812 #if DEBUG_VALIDATE
2813 SkOpPhase phase = contour()->globalState()->phase();
2814 if (phase == SkOpPhase::kIntersecting || phase == SkOpPhase::kFixWinding) {
2815 return;
2816 }
2817 SkASSERT(fNext);
2818 SkASSERT(fNext != this);
2819 SkASSERT(fNext->fNext);
2820 SkASSERT(debugLoopLimit(false) == 0);
2821 #endif
2822 }
2823
output_scalar(SkScalar num)2824 static void output_scalar(SkScalar num) {
2825 if (num == (int) num) {
2826 SkDebugf("%d", (int) num);
2827 } else {
2828 SkString str;
2829 str.printf("%1.9g", num);
2830 int width = (int) str.size();
2831 const char* cStr = str.c_str();
2832 while (cStr[width - 1] == '0') {
2833 --width;
2834 }
2835 str.resize(width);
2836 SkDebugf("%sf", str.c_str());
2837 }
2838 }
2839
output_points(const SkPoint * pts,int count)2840 static void output_points(const SkPoint* pts, int count) {
2841 for (int index = 0; index < count; ++index) {
2842 output_scalar(pts[index].fX);
2843 SkDebugf(", ");
2844 output_scalar(pts[index].fY);
2845 if (index + 1 < count) {
2846 SkDebugf(", ");
2847 }
2848 }
2849 }
2850
showPathContours(SkPath::RawIter & iter,const char * pathName)2851 static void showPathContours(SkPath::RawIter& iter, const char* pathName) {
2852 uint8_t verb;
2853 SkPoint pts[4];
2854 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
2855 switch (verb) {
2856 case SkPath::kMove_Verb:
2857 SkDebugf(" %s.moveTo(", pathName);
2858 output_points(&pts[0], 1);
2859 SkDebugf(");\n");
2860 continue;
2861 case SkPath::kLine_Verb:
2862 SkDebugf(" %s.lineTo(", pathName);
2863 output_points(&pts[1], 1);
2864 SkDebugf(");\n");
2865 break;
2866 case SkPath::kQuad_Verb:
2867 SkDebugf(" %s.quadTo(", pathName);
2868 output_points(&pts[1], 2);
2869 SkDebugf(");\n");
2870 break;
2871 case SkPath::kConic_Verb:
2872 SkDebugf(" %s.conicTo(", pathName);
2873 output_points(&pts[1], 2);
2874 SkDebugf(", %1.9gf);\n", iter.conicWeight());
2875 break;
2876 case SkPath::kCubic_Verb:
2877 SkDebugf(" %s.cubicTo(", pathName);
2878 output_points(&pts[1], 3);
2879 SkDebugf(");\n");
2880 break;
2881 case SkPath::kClose_Verb:
2882 SkDebugf(" %s.close();\n", pathName);
2883 break;
2884 default:
2885 SkDEBUGFAIL("bad verb");
2886 return;
2887 }
2888 }
2889 }
2890
2891 static const char* gFillTypeStr[] = {
2892 "kWinding_FillType",
2893 "kEvenOdd_FillType",
2894 "kInverseWinding_FillType",
2895 "kInverseEvenOdd_FillType"
2896 };
2897
ShowOnePath(const SkPath & path,const char * name,bool includeDeclaration)2898 void SkPathOpsDebug::ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration) {
2899 SkPath::RawIter iter(path);
2900 #define SUPPORT_RECT_CONTOUR_DETECTION 0
2901 #if SUPPORT_RECT_CONTOUR_DETECTION
2902 int rectCount = path.isRectContours() ? path.rectContours(nullptr, nullptr) : 0;
2903 if (rectCount > 0) {
2904 SkTDArray<SkRect> rects;
2905 SkTDArray<SkPath::Direction> directions;
2906 rects.setCount(rectCount);
2907 directions.setCount(rectCount);
2908 path.rectContours(rects.begin(), directions.begin());
2909 for (int contour = 0; contour < rectCount; ++contour) {
2910 const SkRect& rect = rects[contour];
2911 SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
2912 rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
2913 ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
2914 }
2915 return;
2916 }
2917 #endif
2918 SkPath::FillType fillType = path.getFillType();
2919 SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType);
2920 if (includeDeclaration) {
2921 SkDebugf(" SkPath %s;\n", name);
2922 }
2923 SkDebugf(" %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[fillType]);
2924 iter.setPath(path);
2925 showPathContours(iter, name);
2926 }
2927
2928 #if DEBUG_DUMP_VERIFY
2929 #include "SkData.h"
2930 #include "SkStream.h"
2931
dump_path(FILE * file,const SkPath & path,bool force,bool dumpAsHex)2932 static void dump_path(FILE* file, const SkPath& path, bool force, bool dumpAsHex) {
2933 SkDynamicMemoryWStream wStream;
2934 path.dump(&wStream, force, dumpAsHex);
2935 sk_sp<SkData> data(wStream.detachAsData());
2936 fprintf(file, "%.*s\n", (int) data->size(), (char*) data->data());
2937 }
2938
2939 static int dumpID = 0;
2940
DumpOp(const SkPath & one,const SkPath & two,SkPathOp op,const char * testName)2941 void SkPathOpsDebug::DumpOp(const SkPath& one, const SkPath& two, SkPathOp op,
2942 const char* testName) {
2943 FILE* file = sk_fopen("op_dump.txt", kWrite_SkFILE_Flag);
2944 DumpOp(file, one, two, op, testName);
2945 }
2946
DumpOp(FILE * file,const SkPath & one,const SkPath & two,SkPathOp op,const char * testName)2947 void SkPathOpsDebug::DumpOp(FILE* file, const SkPath& one, const SkPath& two, SkPathOp op,
2948 const char* testName) {
2949 const char* name = testName ? testName : "op";
2950 fprintf(file,
2951 "\nstatic void %s_%d(skiatest::Reporter* reporter, const char* filename) {\n",
2952 name, ++dumpID);
2953 fprintf(file, " SkPath path;\n");
2954 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", one.getFillType());
2955 dump_path(file, one, false, true);
2956 fprintf(file, " SkPath path1(path);\n");
2957 fprintf(file, " path.reset();\n");
2958 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", two.getFillType());
2959 dump_path(file, two, false, true);
2960 fprintf(file, " SkPath path2(path);\n");
2961 fprintf(file, " testPathOp(reporter, path1, path2, (SkPathOp) %d, filename);\n", op);
2962 fprintf(file, "}\n\n");
2963 fclose(file);
2964 }
2965
DumpSimplify(const SkPath & path,const char * testName)2966 void SkPathOpsDebug::DumpSimplify(const SkPath& path, const char* testName) {
2967 FILE* file = sk_fopen("simplify_dump.txt", kWrite_SkFILE_Flag);
2968 DumpSimplify(file, path, testName);
2969 }
2970
DumpSimplify(FILE * file,const SkPath & path,const char * testName)2971 void SkPathOpsDebug::DumpSimplify(FILE* file, const SkPath& path, const char* testName) {
2972 const char* name = testName ? testName : "simplify";
2973 fprintf(file,
2974 "\nstatic void %s_%d(skiatest::Reporter* reporter, const char* filename) {\n",
2975 name, ++dumpID);
2976 fprintf(file, " SkPath path;\n");
2977 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", path.getFillType());
2978 dump_path(file, path, false, true);
2979 fprintf(file, " testSimplify(reporter, path, filename);\n");
2980 fprintf(file, "}\n\n");
2981 fclose(file);
2982 }
2983
2984 #include "SkBitmap.h"
2985 #include "SkCanvas.h"
2986 #include "SkPaint.h"
2987
2988 const int bitWidth = 64;
2989 const int bitHeight = 64;
2990
debug_scale_matrix(const SkPath & one,const SkPath * two,SkMatrix & scale)2991 static void debug_scale_matrix(const SkPath& one, const SkPath* two, SkMatrix& scale) {
2992 SkRect larger = one.getBounds();
2993 if (two) {
2994 larger.join(two->getBounds());
2995 }
2996 SkScalar largerWidth = larger.width();
2997 if (largerWidth < 4) {
2998 largerWidth = 4;
2999 }
3000 SkScalar largerHeight = larger.height();
3001 if (largerHeight < 4) {
3002 largerHeight = 4;
3003 }
3004 SkScalar hScale = (bitWidth - 2) / largerWidth;
3005 SkScalar vScale = (bitHeight - 2) / largerHeight;
3006 scale.reset();
3007 scale.preScale(hScale, vScale);
3008 larger.fLeft *= hScale;
3009 larger.fRight *= hScale;
3010 larger.fTop *= vScale;
3011 larger.fBottom *= vScale;
3012 SkScalar dx = -16000 > larger.fLeft ? -16000 - larger.fLeft
3013 : 16000 < larger.fRight ? 16000 - larger.fRight : 0;
3014 SkScalar dy = -16000 > larger.fTop ? -16000 - larger.fTop
3015 : 16000 < larger.fBottom ? 16000 - larger.fBottom : 0;
3016 scale.preTranslate(dx, dy);
3017 }
3018
debug_paths_draw_the_same(const SkPath & one,const SkPath & two,SkBitmap & bits)3019 static int debug_paths_draw_the_same(const SkPath& one, const SkPath& two, SkBitmap& bits) {
3020 if (bits.width() == 0) {
3021 bits.allocN32Pixels(bitWidth * 2, bitHeight);
3022 }
3023 SkCanvas canvas(bits);
3024 canvas.drawColor(SK_ColorWHITE);
3025 SkPaint paint;
3026 canvas.save();
3027 const SkRect& bounds1 = one.getBounds();
3028 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
3029 canvas.drawPath(one, paint);
3030 canvas.restore();
3031 canvas.save();
3032 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
3033 canvas.drawPath(two, paint);
3034 canvas.restore();
3035 int errors = 0;
3036 for (int y = 0; y < bitHeight - 1; ++y) {
3037 uint32_t* addr1 = bits.getAddr32(0, y);
3038 uint32_t* addr2 = bits.getAddr32(0, y + 1);
3039 uint32_t* addr3 = bits.getAddr32(bitWidth, y);
3040 uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1);
3041 for (int x = 0; x < bitWidth - 1; ++x) {
3042 // count 2x2 blocks
3043 bool err = addr1[x] != addr3[x];
3044 if (err) {
3045 errors += addr1[x + 1] != addr3[x + 1]
3046 && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1];
3047 }
3048 }
3049 }
3050 return errors;
3051 }
3052
ReportOpFail(const SkPath & one,const SkPath & two,SkPathOp op)3053 void SkPathOpsDebug::ReportOpFail(const SkPath& one, const SkPath& two, SkPathOp op) {
3054 SkDebugf("// Op did not expect failure\n");
3055 DumpOp(stderr, one, two, op, "opTest");
3056 fflush(stderr);
3057 }
3058
VerifyOp(const SkPath & one,const SkPath & two,SkPathOp op,const SkPath & result)3059 void SkPathOpsDebug::VerifyOp(const SkPath& one, const SkPath& two, SkPathOp op,
3060 const SkPath& result) {
3061 SkPath pathOut, scaledPathOut;
3062 SkRegion rgnA, rgnB, openClip, rgnOut;
3063 openClip.setRect(-16000, -16000, 16000, 16000);
3064 rgnA.setPath(one, openClip);
3065 rgnB.setPath(two, openClip);
3066 rgnOut.op(rgnA, rgnB, (SkRegion::Op) op);
3067 rgnOut.getBoundaryPath(&pathOut);
3068 SkMatrix scale;
3069 debug_scale_matrix(one, &two, scale);
3070 SkRegion scaledRgnA, scaledRgnB, scaledRgnOut;
3071 SkPath scaledA, scaledB;
3072 scaledA.addPath(one, scale);
3073 scaledA.setFillType(one.getFillType());
3074 scaledB.addPath(two, scale);
3075 scaledB.setFillType(two.getFillType());
3076 scaledRgnA.setPath(scaledA, openClip);
3077 scaledRgnB.setPath(scaledB, openClip);
3078 scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) op);
3079 scaledRgnOut.getBoundaryPath(&scaledPathOut);
3080 SkBitmap bitmap;
3081 SkPath scaledOut;
3082 scaledOut.addPath(result, scale);
3083 scaledOut.setFillType(result.getFillType());
3084 int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
3085 const int MAX_ERRORS = 9;
3086 if (errors > MAX_ERRORS) {
3087 fprintf(stderr, "// Op did not expect errors=%d\n", errors);
3088 DumpOp(stderr, one, two, op, "opTest");
3089 fflush(stderr);
3090 }
3091 }
3092
ReportSimplifyFail(const SkPath & path)3093 void SkPathOpsDebug::ReportSimplifyFail(const SkPath& path) {
3094 SkDebugf("// Simplify did not expect failure\n");
3095 DumpSimplify(stderr, path, "simplifyTest");
3096 fflush(stderr);
3097 }
3098
VerifySimplify(const SkPath & path,const SkPath & result)3099 void SkPathOpsDebug::VerifySimplify(const SkPath& path, const SkPath& result) {
3100 SkPath pathOut, scaledPathOut;
3101 SkRegion rgnA, openClip, rgnOut;
3102 openClip.setRect(-16000, -16000, 16000, 16000);
3103 rgnA.setPath(path, openClip);
3104 rgnOut.getBoundaryPath(&pathOut);
3105 SkMatrix scale;
3106 debug_scale_matrix(path, nullptr, scale);
3107 SkRegion scaledRgnA;
3108 SkPath scaledA;
3109 scaledA.addPath(path, scale);
3110 scaledA.setFillType(path.getFillType());
3111 scaledRgnA.setPath(scaledA, openClip);
3112 scaledRgnA.getBoundaryPath(&scaledPathOut);
3113 SkBitmap bitmap;
3114 SkPath scaledOut;
3115 scaledOut.addPath(result, scale);
3116 scaledOut.setFillType(result.getFillType());
3117 int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
3118 const int MAX_ERRORS = 9;
3119 if (errors > MAX_ERRORS) {
3120 fprintf(stderr, "// Simplify did not expect errors=%d\n", errors);
3121 DumpSimplify(stderr, path, "simplifyTest");
3122 fflush(stderr);
3123 }
3124 }
3125
3126 #endif
3127