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