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