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