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