Home | History | Annotate | Download | only in pathops
      1 /*
      2  * Copyright 2013 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkMutex.h"
      9 #include "SkOpCoincidence.h"
     10 #include "SkOpContour.h"
     11 #include "SkPath.h"
     12 #include "SkPathOpsDebug.h"
     13 #include "SkString.h"
     14 
     15 struct SkCoincidentSpans;
     16 
     17 #if DEBUG_VALIDATE
     18 extern bool FLAGS_runFail;
     19 #endif
     20 
     21 #if DEBUG_SORT
     22 int SkPathOpsDebug::gSortCountDefault = SK_MaxS32;
     23 int SkPathOpsDebug::gSortCount;
     24 #endif
     25 
     26 #if DEBUG_ACTIVE_OP
     27 const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor"};
     28 #endif
     29 
     30 #if defined SK_DEBUG || !FORCE_RELEASE
     31 
     32 const char* SkPathOpsDebug::kLVerbStr[] = {"", "line", "quad", "cubic"};
     33 
     34 int SkPathOpsDebug::gContourID = 0;
     35 int SkPathOpsDebug::gSegmentID = 0;
     36 
     37 bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray,
     38         const SkOpSpanBase* span) {
     39     for (int index = 0; index < chaseArray.count(); ++index) {
     40         const SkOpSpanBase* entry = chaseArray[index];
     41         if (entry == span) {
     42             return true;
     43         }
     44     }
     45     return false;
     46 }
     47 #endif
     48 
     49 #if DEBUG_COINCIDENCE
     50 enum GlitchType {
     51     kAddCorruptCoin_Glitch,
     52     kAddExpandedCoin_Glitch,
     53     kAddMissingCoin_Glitch,
     54     kCollapsedCoin_Glitch,
     55     kCollapsedDone_Glitch,
     56     kCollapsedOppValue_Glitch,
     57     kCollapsedSpan_Glitch,
     58     kCollapsedWindValue_Glitch,
     59     kDeletedCoin_Glitch,
     60     kExpandCoin_Glitch,
     61     kMarkCoinEnd_Glitch,
     62     kMarkCoinInsert_Glitch,
     63     kMissingCoin_Glitch,
     64     kMissingDone_Glitch,
     65     kMissingIntersection_Glitch,
     66     kMoveMultiple_Glitch,
     67     kUnaligned_Glitch,
     68     kUnalignedHead_Glitch,
     69     kUnalignedTail_Glitch,
     70     kUndetachedSpan_Glitch,
     71     kUnmergedSpan_Glitch,
     72 };
     73 
     74 static const int kGlitchType_Count = kUnmergedSpan_Glitch + 1;
     75 
     76 struct SpanGlitch {
     77     const char* fStage;
     78     const SkOpSpanBase* fBase;
     79     const SkOpSpanBase* fSuspect;
     80     const SkCoincidentSpans* fCoin;
     81     const SkOpSegment* fSegment;
     82     const SkOpPtT* fCoinSpan;
     83     const SkOpPtT* fEndSpan;
     84     const SkOpPtT* fOppSpan;
     85     const SkOpPtT* fOppEndSpan;
     86     double fT;
     87     SkPoint fPt;
     88     GlitchType fType;
     89 };
     90 
     91 struct SkPathOpsDebug::GlitchLog {
     92     SpanGlitch* recordCommon(GlitchType type, const char* stage) {
     93         SpanGlitch* glitch = fGlitches.push();
     94         glitch->fStage = stage;
     95         glitch->fBase = nullptr;
     96         glitch->fSuspect = nullptr;
     97         glitch->fCoin = nullptr;
     98         glitch->fSegment = nullptr;
     99         glitch->fCoinSpan = nullptr;
    100         glitch->fEndSpan = nullptr;
    101         glitch->fOppSpan = nullptr;
    102         glitch->fOppEndSpan = nullptr;
    103         glitch->fT = SK_ScalarNaN;
    104         glitch->fPt = { SK_ScalarNaN, SK_ScalarNaN };
    105         glitch->fType = type;
    106         return glitch;
    107     }
    108 
    109     void record(GlitchType type, const char* stage, const SkOpSpanBase* base,
    110             const SkOpSpanBase* suspect = NULL) {
    111         SpanGlitch* glitch = recordCommon(type, stage);
    112         glitch->fBase = base;
    113         glitch->fSuspect = suspect;
    114     }
    115 
    116     void record(GlitchType type, const char* stage, const SkCoincidentSpans* coin,
    117             const SkOpPtT* coinSpan) {
    118         SpanGlitch* glitch = recordCommon(type, stage);
    119         glitch->fCoin = coin;
    120         glitch->fCoinSpan = coinSpan;
    121     }
    122 
    123     void record(GlitchType type, const char* stage, const SkOpSpanBase* base,
    124             const SkOpSegment* seg, double t, SkPoint pt) {
    125         SpanGlitch* glitch = recordCommon(type, stage);
    126         glitch->fBase = base;
    127         glitch->fSegment = seg;
    128         glitch->fT = t;
    129         glitch->fPt = pt;
    130     }
    131 
    132     void record(GlitchType type, const char* stage, const SkOpSpanBase* base, double t,
    133             SkPoint pt) {
    134         SpanGlitch* glitch = recordCommon(type, stage);
    135         glitch->fBase = base;
    136         glitch->fT = t;
    137         glitch->fPt = pt;
    138     }
    139 
    140     void record(GlitchType type, const char* stage, const SkCoincidentSpans* coin,
    141             const SkOpPtT* coinSpan, const SkOpPtT* endSpan) {
    142         SpanGlitch* glitch = recordCommon(type, stage);
    143         glitch->fCoin = coin;
    144         glitch->fCoinSpan = coinSpan;
    145         glitch->fEndSpan = endSpan;
    146     }
    147 
    148     void record(GlitchType type, const char* stage, const SkCoincidentSpans* coin,
    149             const SkOpSpanBase* suspect) {
    150         SpanGlitch* glitch = recordCommon(type, stage);
    151         glitch->fSuspect = suspect;
    152         glitch->fCoin = coin;
    153     }
    154 
    155     void record(GlitchType type, const char* stage, const SkOpPtT* ptTS, const SkOpPtT* ptTE,
    156             const SkOpPtT* oPtTS, const SkOpPtT* oPtTE) {
    157         SpanGlitch* glitch = recordCommon(type, stage);
    158         glitch->fCoinSpan = ptTS;
    159         glitch->fEndSpan = ptTE;
    160         glitch->fOppSpan = oPtTS;
    161         glitch->fOppEndSpan = oPtTE;
    162     }
    163 
    164     SkTDArray<SpanGlitch> fGlitches;
    165 };
    166 
    167 void SkPathOpsDebug::CheckHealth(SkOpContourHead* contourList, const char* id) {
    168     GlitchLog glitches;
    169     const SkOpContour* contour = contourList;
    170     const SkOpCoincidence* coincidence = contour->globalState()->coincidence();
    171     do {
    172         contour->debugCheckHealth(id, &glitches);
    173         contour->debugMissingCoincidence(id, &glitches, coincidence);
    174     } while ((contour = contour->next()));
    175     coincidence->debugFixAligned(id, &glitches);
    176     coincidence->debugAddMissing(id, &glitches);
    177     coincidence->debugExpand(id, &glitches);
    178     coincidence->debugAddExpanded(id, &glitches);
    179     coincidence->debugMark(id, &glitches);
    180     unsigned mask = 0;
    181     for (int index = 0; index < glitches.fGlitches.count(); ++index) {
    182         const SpanGlitch& glitch = glitches.fGlitches[index];
    183         mask |= 1 << glitch.fType;
    184     }
    185     for (int index = 0; index < kGlitchType_Count; ++index) {
    186         SkDebugf(mask & (1 << index) ? "x" : "-");
    187     }
    188     SkDebugf("  %s\n", id);
    189 }
    190 #endif
    191 
    192 #if defined SK_DEBUG || !FORCE_RELEASE
    193 void SkPathOpsDebug::MathematicaIze(char* str, size_t bufferLen) {
    194     size_t len = strlen(str);
    195     bool num = false;
    196     for (size_t idx = 0; idx < len; ++idx) {
    197         if (num && str[idx] == 'e') {
    198             if (len + 2 >= bufferLen) {
    199                 return;
    200             }
    201             memmove(&str[idx + 2], &str[idx + 1], len - idx);
    202             str[idx] = '*';
    203             str[idx + 1] = '^';
    204             ++len;
    205         }
    206         num = str[idx] >= '0' && str[idx] <= '9';
    207     }
    208 }
    209 
    210 bool SkPathOpsDebug::ValidWind(int wind) {
    211     return wind > SK_MinS32 + 0xFFFF && wind < SK_MaxS32 - 0xFFFF;
    212 }
    213 
    214 void SkPathOpsDebug::WindingPrintf(int wind) {
    215     if (wind == SK_MinS32) {
    216         SkDebugf("?");
    217     } else {
    218         SkDebugf("%d", wind);
    219     }
    220 }
    221 #endif //  defined SK_DEBUG || !FORCE_RELEASE
    222 
    223 
    224 #if DEBUG_SHOW_TEST_NAME
    225 void* SkPathOpsDebug::CreateNameStr() { return new char[DEBUG_FILENAME_STRING_LENGTH]; }
    226 
    227 void SkPathOpsDebug::DeleteNameStr(void* v) { delete[] reinterpret_cast<char*>(v); }
    228 
    229 void SkPathOpsDebug::BumpTestName(char* test) {
    230     char* num = test + strlen(test);
    231     while (num[-1] >= '0' && num[-1] <= '9') {
    232         --num;
    233     }
    234     if (num[0] == '\0') {
    235         return;
    236     }
    237     int dec = atoi(num);
    238     if (dec == 0) {
    239         return;
    240     }
    241     ++dec;
    242     SK_SNPRINTF(num, DEBUG_FILENAME_STRING_LENGTH - (num - test), "%d", dec);
    243 }
    244 #endif
    245 
    246 static void show_function_header(const char* functionName) {
    247     SkDebugf("\nstatic void %s(skiatest::Reporter* reporter, const char* filename) {\n", functionName);
    248     if (strcmp("skphealth_com76", functionName) == 0) {
    249         SkDebugf("found it\n");
    250     }
    251 }
    252 
    253 static const char* gOpStrs[] = {
    254     "kDifference_SkPathOp",
    255     "kIntersect_SkPathOp",
    256     "kUnion_SkPathOp",
    257     "kXor_PathOp",
    258     "kReverseDifference_SkPathOp",
    259 };
    260 
    261 const char* SkPathOpsDebug::OpStr(SkPathOp op) {
    262     return gOpStrs[op];
    263 }
    264 
    265 static void show_op(SkPathOp op, const char* pathOne, const char* pathTwo) {
    266     SkDebugf("    testPathOp(reporter, %s, %s, %s, filename);\n", pathOne, pathTwo, gOpStrs[op]);
    267     SkDebugf("}\n");
    268 }
    269 
    270 SK_DECLARE_STATIC_MUTEX(gTestMutex);
    271 
    272 void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
    273         const char* testName) {
    274     SkAutoMutexAcquire ac(gTestMutex);
    275     show_function_header(testName);
    276     ShowOnePath(a, "path", true);
    277     ShowOnePath(b, "pathB", true);
    278     show_op(shapeOp, "path", "pathB");
    279 }
    280 
    281 #include "SkPathOpsTypes.h"
    282 #include "SkIntersectionHelper.h"
    283 #include "SkIntersections.h"
    284 
    285 #if DEBUG_T_SECT_LOOP_COUNT
    286 void SkOpGlobalState::debugAddLoopCount(SkIntersections* i, const SkIntersectionHelper& wt,
    287         const SkIntersectionHelper& wn) {
    288     for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
    289         SkIntersections::DebugLoop looper = (SkIntersections::DebugLoop) index;
    290         if (fDebugLoopCount[index] >= i->debugLoopCount(looper)) {
    291             continue;
    292         }
    293         fDebugLoopCount[index] = i->debugLoopCount(looper);
    294         fDebugWorstVerb[index * 2] = wt.segment()->verb();
    295         fDebugWorstVerb[index * 2 + 1] = wn.segment()->verb();
    296         sk_bzero(&fDebugWorstPts[index * 8], sizeof(SkPoint) * 8);
    297         memcpy(&fDebugWorstPts[index * 2 * 4], wt.pts(),
    298                 (SkPathOpsVerbToPoints(wt.segment()->verb()) + 1) * sizeof(SkPoint));
    299         memcpy(&fDebugWorstPts[(index * 2 + 1) * 4], wn.pts(),
    300                 (SkPathOpsVerbToPoints(wn.segment()->verb()) + 1) * sizeof(SkPoint));
    301         fDebugWorstWeight[index * 2] = wt.weight();
    302         fDebugWorstWeight[index * 2 + 1] = wn.weight();
    303     }
    304     i->debugResetLoopCount();
    305 }
    306 
    307 void SkOpGlobalState::debugDoYourWorst(SkOpGlobalState* local) {
    308     for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
    309         if (fDebugLoopCount[index] >= local->fDebugLoopCount[index]) {
    310             continue;
    311         }
    312         fDebugLoopCount[index] = local->fDebugLoopCount[index];
    313         fDebugWorstVerb[index * 2] = local->fDebugWorstVerb[index * 2];
    314         fDebugWorstVerb[index * 2 + 1] = local->fDebugWorstVerb[index * 2 + 1];
    315         memcpy(&fDebugWorstPts[index * 2 * 4], &local->fDebugWorstPts[index * 2 * 4],
    316                 sizeof(SkPoint) * 8);
    317         fDebugWorstWeight[index * 2] = local->fDebugWorstWeight[index * 2];
    318         fDebugWorstWeight[index * 2 + 1] = local->fDebugWorstWeight[index * 2 + 1];
    319     }
    320     local->debugResetLoopCounts();
    321 }
    322 
    323 static void dump_curve(SkPath::Verb verb, const SkPoint& pts, float weight) {
    324     if (!verb) {
    325         return;
    326     }
    327     const char* verbs[] = { "", "line", "quad", "conic", "cubic" };
    328     SkDebugf("%s: {{", verbs[verb]);
    329     int ptCount = SkPathOpsVerbToPoints(verb);
    330     for (int index = 0; index <= ptCount; ++index) {
    331         SkDPoint::Dump((&pts)[index]);
    332         if (index < ptCount - 1) {
    333             SkDebugf(", ");
    334         }
    335     }
    336     SkDebugf("}");
    337     if (weight != 1) {
    338         SkDebugf(", ");
    339         if (weight == floorf(weight)) {
    340             SkDebugf("%.0f", weight);
    341         } else {
    342             SkDebugf("%1.9gf", weight);
    343         }
    344     }
    345     SkDebugf("}\n");
    346 }
    347 
    348 void SkOpGlobalState::debugLoopReport() {
    349     const char* loops[] = { "iterations", "coinChecks", "perpCalcs" };
    350     SkDebugf("\n");
    351     for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
    352         SkDebugf("%s: %d\n", loops[index], fDebugLoopCount[index]);
    353         dump_curve(fDebugWorstVerb[index * 2], fDebugWorstPts[index * 2 * 4],
    354                 fDebugWorstWeight[index * 2]);
    355         dump_curve(fDebugWorstVerb[index * 2 + 1], fDebugWorstPts[(index * 2 + 1) * 4],
    356                 fDebugWorstWeight[index * 2 + 1]);
    357     }
    358 }
    359 
    360 void SkOpGlobalState::debugResetLoopCounts() {
    361     sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
    362     sk_bzero(fDebugWorstVerb, sizeof(fDebugWorstVerb));
    363     sk_bzero(fDebugWorstPts, sizeof(fDebugWorstPts));
    364     sk_bzero(fDebugWorstWeight, sizeof(fDebugWorstWeight));
    365 }
    366 #endif
    367 
    368 #ifdef SK_DEBUG
    369 bool SkOpGlobalState::debugRunFail() const {
    370 #if DEBUG_VALIDATE
    371     return FLAGS_runFail;
    372 #else
    373     return false;
    374 #endif
    375 }
    376 #endif
    377 
    378 #if DEBUG_T_SECT_LOOP_COUNT
    379 void SkIntersections::debugBumpLoopCount(DebugLoop index) {
    380     fDebugLoopCount[index]++;
    381 }
    382 
    383 int SkIntersections::debugLoopCount(DebugLoop index) const {
    384     return fDebugLoopCount[index];
    385 }
    386 
    387 void SkIntersections::debugResetLoopCount() {
    388     sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
    389 }
    390 #endif
    391 
    392 #include "SkPathOpsCubic.h"
    393 #include "SkPathOpsQuad.h"
    394 
    395 SkDCubic SkDQuad::debugToCubic() const {
    396     SkDCubic cubic;
    397     cubic[0] = fPts[0];
    398     cubic[2] = fPts[1];
    399     cubic[3] = fPts[2];
    400     cubic[1].fX = (cubic[0].fX + cubic[2].fX * 2) / 3;
    401     cubic[1].fY = (cubic[0].fY + cubic[2].fY * 2) / 3;
    402     cubic[2].fX = (cubic[3].fX + cubic[2].fX * 2) / 3;
    403     cubic[2].fY = (cubic[3].fY + cubic[2].fY * 2) / 3;
    404     return cubic;
    405 }
    406 
    407 void SkDRect::debugInit() {
    408     fLeft = fTop = fRight = fBottom = SK_ScalarNaN;
    409 }
    410 
    411 #include "SkOpAngle.h"
    412 #include "SkOpSegment.h"
    413 
    414 #if DEBUG_COINCIDENCE
    415 void SkOpSegment::debugAddAlignIntersection(const char* id, SkPathOpsDebug::GlitchLog* log,
    416         const SkOpPtT& endPtT, const SkPoint& oldPt,  const SkOpContourHead* contourList) const {
    417     const SkPoint& newPt = endPtT.fPt;
    418     if (newPt == oldPt) {
    419         return;
    420     }
    421     SkPoint line[2] = { newPt, oldPt };
    422     SkPathOpsBounds lineBounds;
    423     lineBounds.setBounds(line, 2);
    424     SkDLine aLine;
    425     aLine.set(line);
    426     const SkOpContour* current = contourList;
    427     do {
    428         if (!SkPathOpsBounds::Intersects(current->bounds(), lineBounds)) {
    429             continue;
    430         }
    431         const SkOpSegment* segment = current->first();
    432         do {
    433             if (!SkPathOpsBounds::Intersects(segment->bounds(), lineBounds)) {
    434                 continue;
    435             }
    436             if (newPt == segment->fPts[0]) {
    437                 continue;
    438             }
    439             if (newPt == segment->fPts[SkPathOpsVerbToPoints(segment->fVerb)]) {
    440                 continue;
    441             }
    442             if (oldPt == segment->fPts[0]) {
    443                 continue;
    444             }
    445             if (oldPt == segment->fPts[SkPathOpsVerbToPoints(segment->fVerb)]) {
    446                 continue;
    447             }
    448             if (endPtT.debugContains(segment)) {
    449                 continue;
    450             }
    451             SkIntersections i;
    452             switch (segment->fVerb) {
    453                 case SkPath::kLine_Verb: {
    454                     SkDLine bLine;
    455                     bLine.set(segment->fPts);
    456                     i.intersect(bLine, aLine);
    457                     } break;
    458                 case SkPath::kQuad_Verb: {
    459                     SkDQuad bQuad;
    460                     bQuad.set(segment->fPts);
    461                     i.intersect(bQuad, aLine);
    462                     } break;
    463                 case SkPath::kConic_Verb: {
    464                     SkDConic bConic;
    465                     bConic.set(segment->fPts, segment->fWeight);
    466                     i.intersect(bConic, aLine);
    467                     } break;
    468                 case SkPath::kCubic_Verb: {
    469                     SkDCubic bCubic;
    470                     bCubic.set(segment->fPts);
    471                     i.intersect(bCubic, aLine);
    472                     } break;
    473                 default:
    474                     SkASSERT(0);
    475             }
    476             if (i.used()) {
    477                 SkASSERT(i.used() == 1);
    478                 SkASSERT(!zero_or_one(i[0][0]));
    479                 SkOpSpanBase* checkSpan = fHead.next();
    480                 while (!checkSpan->final()) {
    481                     if (checkSpan->contains(segment)) {
    482                         goto nextSegment;
    483                     }
    484                     checkSpan = checkSpan->upCast()->next();
    485                 }
    486                 log->record(kMissingIntersection_Glitch, id, checkSpan, segment, i[0][0], newPt);
    487             }
    488     nextSegment:
    489             ;
    490         } while ((segment = segment->next()));
    491     } while ((current = current->next()));
    492 }
    493 
    494 bool SkOpSegment::debugAddMissing(double t, const SkOpSegment* opp) const {
    495     const SkOpSpanBase* existing = nullptr;
    496     const SkOpSpanBase* test = &fHead;
    497     double testT;
    498     do {
    499         if ((testT = test->ptT()->fT) >= t) {
    500             if (testT == t) {
    501                 existing = test;
    502             }
    503             break;
    504         }
    505     } while ((test = test->upCast()->next()));
    506     return !existing || !existing->debugContains(opp);
    507 }
    508 
    509 void SkOpSegment::debugAlign(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
    510     const SkOpSpanBase* span = &fHead;
    511     if (!span->aligned()) {
    512         if (!span->debugAlignedEnd(0, fPts[0])) {
    513             glitches->record(kUnalignedHead_Glitch, id, span);
    514         }
    515     }
    516     while ((span = span->upCast()->next())) {
    517         if (span == &fTail) {
    518             break;
    519         }
    520         if (!span->aligned()) {
    521             glitches->record(kUnaligned_Glitch, id, span);
    522         }
    523     }
    524     if (!span->aligned()) {
    525         span->debugAlignedEnd(1, fPts[SkPathOpsVerbToPoints(fVerb)]);
    526     }
    527     if (this->collapsed()) {
    528         const SkOpSpan* span = &fHead;
    529         do {
    530             if (span->windValue()) {
    531                 glitches->record(kCollapsedWindValue_Glitch, id, span);
    532             }
    533             if (span->oppValue()) {
    534                 glitches->record(kCollapsedOppValue_Glitch, id, span);
    535             }
    536             if (!span->done()) {
    537                 glitches->record(kCollapsedDone_Glitch, id, span);
    538             }
    539         } while ((span = span->next()->upCastable()));
    540     }
    541 }
    542 #endif
    543 
    544 #if DEBUG_ANGLE
    545 void SkOpSegment::debugCheckAngleCoin() const {
    546     const SkOpSpanBase* base = &fHead;
    547     const SkOpSpan* span;
    548     do {
    549         const SkOpAngle* angle = base->fromAngle();
    550         if (angle && angle->fCheckCoincidence) {
    551             angle->debugCheckNearCoincidence();
    552         }
    553         if (base->final()) {
    554              break;
    555         }
    556         span = base->upCast();
    557         angle = span->toAngle();
    558         if (angle && angle->fCheckCoincidence) {
    559             angle->debugCheckNearCoincidence();
    560         }
    561     } while ((base = span->next()));
    562 }
    563 #endif
    564 
    565 #if DEBUG_COINCIDENCE
    566 // this mimics the order of the checks in handle coincidence
    567 void SkOpSegment::debugCheckHealth(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
    568     debugMoveMultiples(id, glitches);
    569     debugFindCollapsed(id, glitches);
    570     debugMoveNearby(id, glitches);
    571     debugAlign(id, glitches);
    572     debugAddAlignIntersections(id, glitches, this->globalState()->contourHead());
    573 
    574 }
    575 
    576 void SkOpSegment::debugFindCollapsed(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
    577     if (fHead.contains(&fTail)) {
    578         const SkOpSpan* span = this->head();
    579         bool missingDone = false;
    580         do {
    581             missingDone |= !span->done();
    582         } while ((span = span->next()->upCastable()));
    583         if (missingDone) {
    584             glitches->record(kMissingDone_Glitch, id, &fHead);
    585         }
    586         if (!fHead.debugAlignedEnd(0, fHead.pt())) {
    587             glitches->record(kUnalignedHead_Glitch, id, &fHead);
    588         }
    589         if (!fTail.aligned()) {
    590             glitches->record(kUnalignedTail_Glitch, id, &fTail);
    591         }
    592     }
    593 }
    594 #endif
    595 
    596 SkOpAngle* SkOpSegment::debugLastAngle() {
    597     SkOpAngle* result = nullptr;
    598     SkOpSpan* span = this->head();
    599     do {
    600         if (span->toAngle()) {
    601             SkASSERT(!result);
    602             result = span->toAngle();
    603         }
    604     } while ((span = span->next()->upCastable()));
    605     SkASSERT(result);
    606     return result;
    607 }
    608 
    609 #if DEBUG_COINCIDENCE
    610 void SkOpSegment::debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log,
    611         const SkOpCoincidence* coincidences) const {
    612     if (this->verb() != SkPath::kLine_Verb) {
    613         return;
    614     }
    615     if (this->done()) {
    616         return;
    617     }
    618     const SkOpSpan* prior = nullptr;
    619     const SkOpSpanBase* spanBase = &fHead;
    620     do {
    621         const SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT;
    622         SkASSERT(ptT->span() == spanBase);
    623         while ((ptT = ptT->next()) != spanStopPtT) {
    624             if (ptT->deleted()) {
    625                 continue;
    626             }
    627             SkOpSegment* opp = ptT->span()->segment();
    628 //            if (opp->verb() == SkPath::kLine_Verb) {
    629 //                continue;
    630 //            }
    631             if (opp->done()) {
    632                 continue;
    633             }
    634             // when opp is encounted the 1st time, continue; on 2nd encounter, look for coincidence
    635             if (!opp->visited()) {
    636                 continue;
    637             }
    638             if (spanBase == &fHead) {
    639                 continue;
    640             }
    641             const SkOpSpan* span = spanBase->upCastable();
    642             // FIXME?: this assumes that if the opposite segment is coincident then no more
    643             // coincidence needs to be detected. This may not be true.
    644             if (span && span->segment() != opp && span->containsCoincidence(opp)) {
    645                 continue;
    646             }
    647             if (spanBase->segment() != opp && spanBase->containsCoinEnd(opp)) {
    648                 continue;
    649             }
    650             const SkOpPtT* priorPtT = nullptr, * priorStopPtT;
    651             // find prior span containing opp segment
    652             const SkOpSegment* priorOpp = nullptr;
    653             const SkOpSpan* priorTest = spanBase->prev();
    654             while (!priorOpp && priorTest) {
    655                 priorStopPtT = priorPtT = priorTest->ptT();
    656                 while ((priorPtT = priorPtT->next()) != priorStopPtT) {
    657                     if (priorPtT->deleted()) {
    658                         continue;
    659                     }
    660                     SkOpSegment* segment = priorPtT->span()->segment();
    661                     if (segment == opp) {
    662                         prior = priorTest;
    663                         priorOpp = opp;
    664                         break;
    665                     }
    666                 }
    667                 priorTest = priorTest->prev();
    668             }
    669             if (!priorOpp) {
    670                 continue;
    671             }
    672             const SkOpPtT* oppStart = prior->ptT();
    673             const SkOpPtT* oppEnd = spanBase->ptT();
    674             bool swapped = priorPtT->fT > ptT->fT;
    675             if (swapped) {
    676                 SkTSwap(priorPtT, ptT);
    677                 SkTSwap(oppStart, oppEnd);
    678             }
    679             bool flipped = oppStart->fT > oppEnd->fT;
    680             bool coincident = false;
    681             if (coincidences->contains(priorPtT, ptT, oppStart, oppEnd, flipped)) {
    682                 goto swapBack;
    683             }
    684             if (opp->verb() == SkPath::kLine_Verb) {
    685                 coincident = (SkDPoint::ApproximatelyEqual(priorPtT->fPt, oppStart->fPt) ||
    686                         SkDPoint::ApproximatelyEqual(priorPtT->fPt, oppEnd->fPt)) &&
    687                         (SkDPoint::ApproximatelyEqual(ptT->fPt, oppStart->fPt) ||
    688                         SkDPoint::ApproximatelyEqual(ptT->fPt, oppEnd->fPt));
    689             }
    690             if (!coincident) {
    691                 coincident = testForCoincidence(priorPtT, ptT, prior, spanBase, opp, 5000);
    692             }
    693             if (coincident) {
    694                 log->record(kMissingCoin_Glitch, id, priorPtT, ptT, oppStart, oppEnd);
    695             }
    696     swapBack:
    697             if (swapped) {
    698                 SkTSwap(priorPtT, ptT);
    699             }
    700         }
    701     } while ((spanBase = spanBase->final() ? nullptr : spanBase->upCast()->next()));
    702 }
    703 
    704 void SkOpSegment::debugMoveMultiples(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
    705     const SkOpSpanBase* test = &fHead;
    706     do {
    707         int addCount = test->spanAddsCount();
    708         SkASSERT(addCount >= 1);
    709         if (addCount == 1) {
    710             continue;
    711         }
    712         const SkOpPtT* startPtT = test->ptT();
    713         const SkOpPtT* testPtT = startPtT;
    714         do {  // iterate through all spans associated with start
    715             const SkOpSpanBase* oppSpan = testPtT->span();
    716             if (oppSpan->spanAddsCount() == addCount) {
    717                 continue;
    718             }
    719             if (oppSpan->deleted()) {
    720                 continue;
    721             }
    722             const SkOpSegment* oppSegment = oppSpan->segment();
    723             if (oppSegment == this) {
    724                 continue;
    725             }
    726             // find range of spans to consider merging
    727             const SkOpSpanBase* oppPrev = oppSpan;
    728             const SkOpSpanBase* oppFirst = oppSpan;
    729             while ((oppPrev = oppPrev->prev())) {
    730                 if (!roughly_equal(oppPrev->t(), oppSpan->t())) {
    731                     break;
    732                 }
    733                 if (oppPrev->spanAddsCount() == addCount) {
    734                     continue;
    735                 }
    736                 if (oppPrev->deleted()) {
    737                     continue;
    738                 }
    739                 oppFirst = oppPrev;
    740             }
    741             const SkOpSpanBase* oppNext = oppSpan;
    742             const SkOpSpanBase* oppLast = oppSpan;
    743             while ((oppNext = oppNext->final() ? nullptr : oppNext->upCast()->next())) {
    744                 if (!roughly_equal(oppNext->t(), oppSpan->t())) {
    745                     break;
    746                 }
    747                 if (oppNext->spanAddsCount() == addCount) {
    748                     continue;
    749                 }
    750                 if (oppNext->deleted()) {
    751                     continue;
    752                 }
    753                 oppLast = oppNext;
    754             }
    755             if (oppFirst == oppLast) {
    756                 continue;
    757             }
    758             const SkOpSpanBase* oppTest = oppFirst;
    759             do {
    760                 if (oppTest == oppSpan) {
    761                     continue;
    762                 }
    763                 // check to see if the candidate meets specific criteria:
    764                 // it contains spans of segments in test's loop but not including 'this'
    765                 const SkOpPtT* oppStartPtT = oppTest->ptT();
    766                 const SkOpPtT* oppPtT = oppStartPtT;
    767                 while ((oppPtT = oppPtT->next()) != oppStartPtT) {
    768                     const SkOpSegment* oppPtTSegment = oppPtT->segment();
    769                     if (oppPtTSegment == this) {
    770                         goto tryNextSpan;
    771                     }
    772                     const SkOpPtT* matchPtT = startPtT;
    773                     do {
    774                         if (matchPtT->segment() == oppPtTSegment) {
    775                             goto foundMatch;
    776                         }
    777                     } while ((matchPtT = matchPtT->next()) != startPtT);
    778                     goto tryNextSpan;
    779             foundMatch:  // merge oppTest and oppSpan
    780                     if (oppTest == &oppSegment->fTail || oppTest == &oppSegment->fHead) {
    781                         SkASSERT(oppSpan != &oppSegment->fHead); // don't expect collapse
    782                         SkASSERT(oppSpan != &oppSegment->fTail);
    783                         glitches->record(kMoveMultiple_Glitch, id, oppTest, oppSpan);
    784                     } else {
    785                         glitches->record(kMoveMultiple_Glitch, id, oppSpan, oppTest);
    786                     }
    787                     goto checkNextSpan;
    788                 }
    789         tryNextSpan:
    790                 ;
    791             } while (oppTest != oppLast && (oppTest = oppTest->upCast()->next()));
    792         } while ((testPtT = testPtT->next()) != startPtT);
    793 checkNextSpan:
    794         ;
    795     } while ((test = test->final() ? nullptr : test->upCast()->next()));
    796 }
    797 
    798 void SkOpSegment::debugMoveNearby(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
    799         const SkOpSpanBase* spanS = &fHead;
    800     do {
    801         const SkOpSpanBase* test = spanS->upCast()->next();
    802         const SkOpSpanBase* next;
    803         if (spanS->contains(test)) {
    804             if (!test->final()) {
    805                 glitches->record(kUndetachedSpan_Glitch, id, test, spanS);
    806             } else if (spanS != &fHead) {
    807                 glitches->record(kUndetachedSpan_Glitch, id, spanS, test);
    808             }
    809         }
    810         do {  // iterate through all spans associated with start
    811             const SkOpPtT* startBase = spanS->ptT();
    812             next = test->final() ? nullptr : test->upCast()->next();
    813             do {
    814                 const SkOpPtT* testBase = test->ptT();
    815                 do {
    816                     if (startBase == testBase) {
    817                         goto checkNextSpan;
    818                     }
    819                     if (testBase->duplicate()) {
    820                         continue;
    821                     }
    822                     if (this->match(startBase, testBase->segment(), testBase->fT, testBase->fPt)) {
    823                         if (test == &this->fTail) {
    824                             if (spanS == &fHead) {
    825                                 glitches->record(kCollapsedSpan_Glitch, id, spanS);
    826                             } else {
    827                                 glitches->record(kUnmergedSpan_Glitch, id, &this->fTail, spanS);
    828                             }
    829                         } else {
    830                             glitches->record(kUnmergedSpan_Glitch, id, spanS, test);
    831                             goto checkNextSpan;
    832                         }
    833                     }
    834                 } while ((testBase = testBase->next()) != test->ptT());
    835             } while ((startBase = startBase->next()) != spanS->ptT());
    836     checkNextSpan:
    837             ;
    838         } while ((test = next));
    839         spanS = spanS->upCast()->next();
    840     } while (!spanS->final());
    841 }
    842 #endif
    843 
    844 void SkOpSegment::debugReset() {
    845     this->init(this->fPts, this->fWeight, this->contour(), this->verb());
    846 }
    847 
    848 #if DEBUG_ACTIVE_SPANS
    849 void SkOpSegment::debugShowActiveSpans() const {
    850     debugValidate();
    851     if (done()) {
    852         return;
    853     }
    854     int lastId = -1;
    855     double lastT = -1;
    856     const SkOpSpan* span = &fHead;
    857     do {
    858         if (span->done()) {
    859             continue;
    860         }
    861         if (lastId == this->debugID() && lastT == span->t()) {
    862             continue;
    863         }
    864         lastId = this->debugID();
    865         lastT = span->t();
    866         SkDebugf("%s id=%d", __FUNCTION__, this->debugID());
    867         SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
    868         for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
    869             SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
    870         }
    871         if (SkPath::kConic_Verb == fVerb) {
    872             SkDebugf(" %1.9gf", fWeight);
    873         }
    874         const SkOpPtT* ptT = span->ptT();
    875         SkDebugf(") t=%1.9g (%1.9g,%1.9g)", ptT->fT, ptT->fPt.fX, ptT->fPt.fY);
    876         SkDebugf(" tEnd=%1.9g", span->next()->t());
    877         if (span->windSum() == SK_MinS32) {
    878             SkDebugf(" windSum=?");
    879         } else {
    880             SkDebugf(" windSum=%d", span->windSum());
    881         }
    882         if (span->oppValue() && span->oppSum() == SK_MinS32) {
    883             SkDebugf(" oppSum=?");
    884         } else if (span->oppValue() || span->oppSum() != SK_MinS32) {
    885             SkDebugf(" oppSum=%d", span->oppSum());
    886         }
    887         SkDebugf(" windValue=%d", span->windValue());
    888         if (span->oppValue() || span->oppSum() != SK_MinS32) {
    889             SkDebugf(" oppValue=%d", span->oppValue());
    890         }
    891         SkDebugf("\n");
    892    } while ((span = span->next()->upCastable()));
    893 }
    894 #endif
    895 
    896 #if DEBUG_MARK_DONE
    897 void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding) {
    898     const SkPoint& pt = span->ptT()->fPt;
    899     SkDebugf("%s id=%d", fun, this->debugID());
    900     SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
    901     for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
    902         SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
    903     }
    904     SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
    905             span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t());
    906     if (winding == SK_MinS32) {
    907         SkDebugf("?");
    908     } else {
    909         SkDebugf("%d", winding);
    910     }
    911     SkDebugf(" windSum=");
    912     if (span->windSum() == SK_MinS32) {
    913         SkDebugf("?");
    914     } else {
    915         SkDebugf("%d", span->windSum());
    916     }
    917     SkDebugf(" windValue=%d\n", span->windValue());
    918 }
    919 
    920 void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding,
    921                                       int oppWinding) {
    922     const SkPoint& pt = span->ptT()->fPt;
    923     SkDebugf("%s id=%d", fun, this->debugID());
    924     SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
    925     for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
    926         SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
    927     }
    928     SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
    929             span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t(), winding, oppWinding);
    930     if (winding == SK_MinS32) {
    931         SkDebugf("?");
    932     } else {
    933         SkDebugf("%d", winding);
    934     }
    935     SkDebugf(" newOppSum=");
    936     if (oppWinding == SK_MinS32) {
    937         SkDebugf("?");
    938     } else {
    939         SkDebugf("%d", oppWinding);
    940     }
    941     SkDebugf(" oppSum=");
    942     if (span->oppSum() == SK_MinS32) {
    943         SkDebugf("?");
    944     } else {
    945         SkDebugf("%d", span->oppSum());
    946     }
    947     SkDebugf(" windSum=");
    948     if (span->windSum() == SK_MinS32) {
    949         SkDebugf("?");
    950     } else {
    951         SkDebugf("%d", span->windSum());
    952     }
    953     SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue());
    954 }
    955 
    956 #endif
    957 
    958 // loop looking for a pair of angle parts that are too close to be sorted
    959 /* This is called after other more simple intersection and angle sorting tests have been exhausted.
    960    This should be rarely called -- the test below is thorough and time consuming.
    961    This checks the distance between start points; the distance between
    962 */
    963 #if DEBUG_ANGLE
    964 void SkOpAngle::debugCheckNearCoincidence() const {
    965     const SkOpAngle* test = this;
    966     do {
    967         const SkOpSegment* testSegment = test->segment();
    968         double testStartT = test->start()->t();
    969         SkDPoint testStartPt = testSegment->dPtAtT(testStartT);
    970         double testEndT = test->end()->t();
    971         SkDPoint testEndPt = testSegment->dPtAtT(testEndT);
    972         double testLenSq = testStartPt.distanceSquared(testEndPt);
    973         SkDebugf("%s testLenSq=%1.9g id=%d\n", __FUNCTION__, testLenSq, testSegment->debugID());
    974         double testMidT = (testStartT + testEndT) / 2;
    975         const SkOpAngle* next = test;
    976         while ((next = next->fNext) != this) {
    977             SkOpSegment* nextSegment = next->segment();
    978             double testMidDistSq = testSegment->distSq(testMidT, next);
    979             double testEndDistSq = testSegment->distSq(testEndT, next);
    980             double nextStartT = next->start()->t();
    981             SkDPoint nextStartPt = nextSegment->dPtAtT(nextStartT);
    982             double distSq = testStartPt.distanceSquared(nextStartPt);
    983             double nextEndT = next->end()->t();
    984             double nextMidT = (nextStartT + nextEndT) / 2;
    985             double nextMidDistSq = nextSegment->distSq(nextMidT, test);
    986             double nextEndDistSq = nextSegment->distSq(nextEndT, test);
    987             SkDebugf("%s distSq=%1.9g testId=%d nextId=%d\n", __FUNCTION__, distSq,
    988                     testSegment->debugID(), nextSegment->debugID());
    989             SkDebugf("%s testMidDistSq=%1.9g\n", __FUNCTION__, testMidDistSq);
    990             SkDebugf("%s testEndDistSq=%1.9g\n", __FUNCTION__, testEndDistSq);
    991             SkDebugf("%s nextMidDistSq=%1.9g\n", __FUNCTION__, nextMidDistSq);
    992             SkDebugf("%s nextEndDistSq=%1.9g\n", __FUNCTION__, nextEndDistSq);
    993             SkDPoint nextEndPt = nextSegment->dPtAtT(nextEndT);
    994             double nextLenSq = nextStartPt.distanceSquared(nextEndPt);
    995             SkDebugf("%s nextLenSq=%1.9g\n", __FUNCTION__, nextLenSq);
    996             SkDebugf("\n");
    997         }
    998         test = test->fNext;
    999     } while (test->fNext != this);
   1000 }
   1001 #endif
   1002 
   1003 #if DEBUG_ANGLE
   1004 SkString SkOpAngle::debugPart() const {
   1005     SkString result;
   1006     switch (this->segment()->verb()) {
   1007         case SkPath::kLine_Verb:
   1008             result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fCurvePart),
   1009                     this->segment()->debugID());
   1010             break;
   1011         case SkPath::kQuad_Verb:
   1012             result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fCurvePart),
   1013                     this->segment()->debugID());
   1014             break;
   1015         case SkPath::kConic_Verb:
   1016             result.printf(CONIC_DEBUG_STR " id=%d",
   1017                     CONIC_DEBUG_DATA(fCurvePart, fCurvePart.fConic.fWeight),
   1018                     this->segment()->debugID());
   1019             break;
   1020         case SkPath::kCubic_Verb:
   1021             result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fCurvePart),
   1022                     this->segment()->debugID());
   1023             break;
   1024         default:
   1025             SkASSERT(0);
   1026     }
   1027     return result;
   1028 }
   1029 #endif
   1030 
   1031 #if DEBUG_SORT
   1032 void SkOpAngle::debugLoop() const {
   1033     const SkOpAngle* first = this;
   1034     const SkOpAngle* next = this;
   1035     do {
   1036         next->dumpOne(true);
   1037         SkDebugf("\n");
   1038         next = next->fNext;
   1039     } while (next && next != first);
   1040     next = first;
   1041     do {
   1042         next->debugValidate();
   1043         next = next->fNext;
   1044     } while (next && next != first);
   1045 }
   1046 #endif
   1047 
   1048 void SkOpAngle::debugValidate() const {
   1049 #if DEBUG_VALIDATE
   1050     const SkOpAngle* first = this;
   1051     const SkOpAngle* next = this;
   1052     int wind = 0;
   1053     int opp = 0;
   1054     int lastXor = -1;
   1055     int lastOppXor = -1;
   1056     do {
   1057         if (next->unorderable()) {
   1058             return;
   1059         }
   1060         const SkOpSpan* minSpan = next->start()->starter(next->end());
   1061         if (minSpan->windValue() == SK_MinS32) {
   1062             return;
   1063         }
   1064         bool op = next->segment()->operand();
   1065         bool isXor = next->segment()->isXor();
   1066         bool oppXor = next->segment()->oppXor();
   1067         SkASSERT(!DEBUG_LIMIT_WIND_SUM || between(0, minSpan->windValue(), DEBUG_LIMIT_WIND_SUM));
   1068         SkASSERT(!DEBUG_LIMIT_WIND_SUM
   1069                 || between(-DEBUG_LIMIT_WIND_SUM, minSpan->oppValue(), DEBUG_LIMIT_WIND_SUM));
   1070         bool useXor = op ? oppXor : isXor;
   1071         SkASSERT(lastXor == -1 || lastXor == (int) useXor);
   1072         lastXor = (int) useXor;
   1073         wind += next->debugSign() * (op ? minSpan->oppValue() : minSpan->windValue());
   1074         if (useXor) {
   1075             wind &= 1;
   1076         }
   1077         useXor = op ? isXor : oppXor;
   1078         SkASSERT(lastOppXor == -1 || lastOppXor == (int) useXor);
   1079         lastOppXor = (int) useXor;
   1080         opp += next->debugSign() * (op ? minSpan->windValue() : minSpan->oppValue());
   1081         if (useXor) {
   1082             opp &= 1;
   1083         }
   1084         next = next->fNext;
   1085     } while (next && next != first);
   1086     SkASSERT(wind == 0 || !FLAGS_runFail);
   1087     SkASSERT(opp == 0 || !FLAGS_runFail);
   1088 #endif
   1089 }
   1090 
   1091 void SkOpAngle::debugValidateNext() const {
   1092 #if !FORCE_RELEASE
   1093     const SkOpAngle* first = this;
   1094     const SkOpAngle* next = first;
   1095     SkTDArray<const SkOpAngle*>(angles);
   1096     do {
   1097 //        SkASSERT_RELEASE(next->fSegment->debugContains(next));
   1098         angles.push(next);
   1099         next = next->next();
   1100         if (next == first) {
   1101             break;
   1102         }
   1103         SkASSERT_RELEASE(!angles.contains(next));
   1104         if (!next) {
   1105             return;
   1106         }
   1107     } while (true);
   1108 #endif
   1109 }
   1110 
   1111 
   1112 #if DEBUG_COINCIDENCE
   1113 void SkOpCoincidence::debugAddExpanded(const char* id, SkPathOpsDebug::GlitchLog* log) const {
   1114     // for each coincident pair, match the spans
   1115     // if the spans don't match, add the mssing pt to the segment and loop it in the opposite span
   1116     const SkCoincidentSpans* coin = this->fHead;
   1117     if (!coin) {
   1118         coin = this->fTop;
   1119     }
   1120     if (!coin) {
   1121         return;
   1122     }
   1123     do {
   1124         const SkOpPtT* startPtT = coin->fCoinPtTStart;
   1125         const SkOpPtT* oStartPtT = coin->fOppPtTStart;
   1126         SkASSERT(startPtT->contains(oStartPtT));
   1127         SkASSERT(coin->fCoinPtTEnd->contains(coin->fOppPtTEnd));
   1128         const SkOpSpanBase* start = startPtT->span();
   1129         const SkOpSpanBase* oStart = oStartPtT->span();
   1130         const SkOpSpanBase* end = coin->fCoinPtTEnd->span();
   1131         const SkOpSpanBase* oEnd = coin->fOppPtTEnd->span();
   1132         const SkOpSpanBase* test = start->upCast()->next();
   1133         const SkOpSpanBase* oTest = coin->fFlipped ? oStart->prev() : oStart->upCast()->next();
   1134         while (test != end || oTest != oEnd) {
   1135             bool bumpTest = true;
   1136             bool bumpOTest = true;
   1137             if (!test->ptT()->contains(oTest->ptT())) {
   1138                 // use t ranges to guess which one is missing
   1139                 double startRange = coin->fCoinPtTEnd->fT - startPtT->fT;
   1140                 double startPart = (test->t() - startPtT->fT) / startRange;
   1141                 double oStartRange = coin->fOppPtTEnd->fT - oStartPtT->fT;
   1142                 double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange;
   1143                 if (startPart == oStartPart) {
   1144                     // data is corrupt
   1145                     log->record(kAddCorruptCoin_Glitch, id, start, oStart);
   1146                     break;
   1147                 }
   1148                 if (startPart < oStartPart) {
   1149                     double newT = oStartPtT->fT + oStartRange * startPart;
   1150                     log->record(kAddExpandedCoin_Glitch, id, oStart, newT, test->pt());
   1151                     bumpOTest = false;
   1152                 } else {
   1153                     double newT = startPtT->fT + startRange * oStartPart;
   1154                     log->record(kAddExpandedCoin_Glitch, id, start, newT, oTest->pt());
   1155                     bumpTest = false;
   1156                 }
   1157             }
   1158             if (bumpTest && test != end) {
   1159                 test = test->upCast()->next();
   1160             }
   1161             if (bumpOTest && oTest != oEnd) {
   1162                 oTest = coin->fFlipped ? oTest->prev() : oTest->upCast()->next();
   1163             }
   1164         }
   1165     } while ((coin = coin->fNext));
   1166 }
   1167 
   1168 static void t_range(const SkOpPtT* overS, const SkOpPtT* overE, double tStart, double tEnd,
   1169         const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, double* coinTs, double* coinTe) {
   1170     double denom = overE->fT - overS->fT;
   1171     double start = 0 < denom ? tStart : tEnd;
   1172     double end = 0 < denom ? tEnd : tStart;
   1173     double sRatio = (start - overS->fT) / denom;
   1174     double eRatio = (end - overS->fT) / denom;
   1175     *coinTs = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * sRatio;
   1176     *coinTe = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * eRatio;
   1177 }
   1178 
   1179 bool SkOpCoincidence::debugAddIfMissing(const SkCoincidentSpans* outer, const SkOpPtT* over1s,
   1180             const SkOpPtT* over1e) const {
   1181     const SkCoincidentSpans* check = this->fTop;
   1182     while (check) {
   1183         if (check->fCoinPtTStart->span() == over1s->span()
   1184                 && check->fOppPtTStart->span() == outer->fOppPtTStart->span()) {
   1185             SkASSERT(check->fCoinPtTEnd->span() == over1e->span()
   1186                     || !fDebugState->debugRunFail());
   1187             SkASSERT(check->fOppPtTEnd->span() == outer->fOppPtTEnd->span()
   1188                     || !fDebugState->debugRunFail());
   1189             return false;
   1190         }
   1191         if (check->fCoinPtTStart->span() == outer->fCoinPtTStart->span()
   1192                 && check->fOppPtTStart->span() == over1s->span()) {
   1193             SkASSERT(check->fCoinPtTEnd->span() == outer->fCoinPtTEnd->span()
   1194                     || !fDebugState->debugRunFail());
   1195             SkASSERT(check->fOppPtTEnd->span() == over1e->span()
   1196                     || !fDebugState->debugRunFail());
   1197             return false;
   1198         }
   1199         check = check->fNext;
   1200     }
   1201     return true;
   1202 }
   1203 
   1204 bool SkOpCoincidence::debugAddIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
   1205                       const SkOpPtT* over2s, const SkOpPtT* over2e, double tStart, double tEnd,
   1206         SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
   1207         SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) const {
   1208     double coinTs, coinTe, oppTs, oppTe;
   1209     t_range(over1s, over1e, tStart, tEnd, coinPtTStart, coinPtTEnd, &coinTs, &coinTe);
   1210     t_range(over2s, over2e, tStart, tEnd, oppPtTStart, oppPtTEnd, &oppTs, &oppTe);
   1211     const SkOpSegment* coinSeg = coinPtTStart->segment();
   1212     const SkOpSegment* oppSeg = oppPtTStart->segment();
   1213     SkASSERT(coinSeg != oppSeg);
   1214     const SkCoincidentSpans* check = this->fTop;
   1215     ;
   1216     while (check) {
   1217         const SkOpSegment* checkCoinSeg = check->fCoinPtTStart->segment();
   1218         const SkOpSegment* checkOppSeg;
   1219         if (checkCoinSeg != coinSeg && checkCoinSeg != oppSeg) {
   1220             goto next;
   1221         }
   1222         checkOppSeg = check->fOppPtTStart->segment();
   1223         if (checkOppSeg != coinSeg && checkOppSeg != oppSeg) {
   1224             goto next;
   1225         }
   1226         {
   1227             int cTs = coinTs;
   1228             int cTe = coinTe;
   1229             int oTs = oppTs;
   1230             int oTe = oppTe;
   1231             if (checkCoinSeg != coinSeg) {
   1232                 SkASSERT(checkOppSeg != oppSeg);
   1233                 SkTSwap(cTs, oTs);
   1234                 SkTSwap(cTe, oTe);
   1235             }
   1236             int tweenCount = (int) between(check->fCoinPtTStart->fT, cTs, check->fCoinPtTEnd->fT)
   1237                            + (int) between(check->fCoinPtTStart->fT, cTe, check->fCoinPtTEnd->fT)
   1238                            + (int) between(check->fOppPtTStart->fT, oTs, check->fOppPtTEnd->fT)
   1239                            + (int) between(check->fOppPtTStart->fT, oTe, check->fOppPtTEnd->fT);
   1240     //        SkASSERT(tweenCount == 0 || tweenCount == 4);
   1241             if (tweenCount) {
   1242                 return true;
   1243             }
   1244         }
   1245 next:
   1246         check = check->fNext;
   1247     }
   1248     if ((over1s->fT < over1e->fT) != (over2s->fT < over2e->fT)) {
   1249         SkTSwap(oppTs, oppTe);
   1250     }
   1251     if (coinTs > coinTe) {
   1252         SkTSwap(coinTs, coinTe);
   1253         SkTSwap(oppTs, oppTe);
   1254     }
   1255     bool cs = coinSeg->debugAddMissing(coinTs, oppSeg);
   1256     bool ce = coinSeg->debugAddMissing(coinTe, oppSeg);
   1257     if (cs == ce) {
   1258         return false;
   1259     }
   1260     return true;
   1261 }
   1262 
   1263 void SkOpCoincidence::debugAddMissing(const char* id, SkPathOpsDebug::GlitchLog* log) const {
   1264     const SkCoincidentSpans* outer = fHead;
   1265     if (!outer) {
   1266         return;
   1267     }
   1268     do {
   1269     // addifmissing can modify the list that this is walking
   1270     // save head so that walker can iterate over old data unperturbed
   1271     // addifmissing adds to head freely then add saved head in the end
   1272         const SkOpSegment* outerCoin = outer->fCoinPtTStart->segment();
   1273         SkASSERT(outerCoin == outer->fCoinPtTEnd->segment());
   1274         const SkOpSegment* outerOpp = outer->fOppPtTStart->segment();
   1275         SkASSERT(outerOpp == outer->fOppPtTEnd->segment());
   1276         const SkCoincidentSpans* inner = outer;
   1277         while ((inner = inner->fNext)) {
   1278             double overS, overE;
   1279             const SkOpSegment* innerCoin = inner->fCoinPtTStart->segment();
   1280             SkASSERT(innerCoin == inner->fCoinPtTEnd->segment());
   1281             const SkOpSegment* innerOpp = inner->fOppPtTStart->segment();
   1282             SkASSERT(innerOpp == inner->fOppPtTEnd->segment());
   1283             if (outerCoin == innerCoin
   1284                     && this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
   1285                     inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) {
   1286                 if (this->debugAddIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd,
   1287                         inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE,
   1288                         outer->fOppPtTStart, outer->fOppPtTEnd,
   1289                         inner->fOppPtTStart, inner->fOppPtTEnd)) {
   1290                     log->record(kAddMissingCoin_Glitch, id, outer, inner->fCoinPtTStart);
   1291                 }
   1292             } else if (outerCoin == innerOpp
   1293                     && this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
   1294                     inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) {
   1295                 if (this->debugAddIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd,
   1296                         inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE,
   1297                         outer->fOppPtTStart, outer->fOppPtTEnd,
   1298                         inner->fCoinPtTStart, inner->fCoinPtTEnd)) {
   1299                     log->record(kAddMissingCoin_Glitch, id, outer, inner->fOppPtTStart);
   1300                 }
   1301             } else if (outerOpp == innerCoin
   1302                     && this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
   1303                     inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) {
   1304                 if (this->debugAddIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd,
   1305                         inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE,
   1306                         outer->fCoinPtTStart, outer->fCoinPtTEnd,
   1307                         inner->fOppPtTStart, inner->fOppPtTEnd)) {
   1308                     log->record(kAddMissingCoin_Glitch, id, outer, inner->fCoinPtTStart);
   1309                 }
   1310             } else if (outerOpp == innerOpp
   1311                     && this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
   1312                     inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) {
   1313                 if (this->debugAddIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd,
   1314                         inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE,
   1315                         outer->fCoinPtTStart, outer->fCoinPtTEnd,
   1316                         inner->fCoinPtTStart, inner->fCoinPtTEnd)) {
   1317                     log->record(kAddMissingCoin_Glitch, id, outer, inner->fOppPtTStart);
   1318                 }
   1319             } else if (outerCoin != innerCoin) {
   1320                 // check to see if outer span overlaps the inner span
   1321                     // look for inner segment in pt-t list
   1322                     // if present, and if t values are in coincident range
   1323                     // add two pairs of new coincidence
   1324                 const SkOpPtT* testS = outer->fCoinPtTStart->debugContains(innerCoin);
   1325                 const SkOpPtT* testE = outer->fCoinPtTEnd->debugContains(innerCoin);
   1326                 if (testS && testS->fT >= inner->fCoinPtTStart->fT
   1327                         && testE && testE->fT <= inner->fCoinPtTEnd->fT
   1328                         && this->testForCoincidence(outer, testS, testE)) {
   1329                     if (this->debugAddIfMissing(outer, testS, testE)) {
   1330                         log->record(kAddMissingCoin_Glitch, id, outer, testS, testE);
   1331                     }
   1332                 } else {
   1333                     testS = inner->fCoinPtTStart->debugContains(outerCoin);
   1334                     testE = inner->fCoinPtTEnd->debugContains(outerCoin);
   1335                     if (testS && testS->fT >= outer->fCoinPtTStart->fT
   1336                             && testE && testE->fT <= outer->fCoinPtTEnd->fT
   1337                             && this->testForCoincidence(inner, testS, testE)) {
   1338                         if (this->debugAddIfMissing(inner, testS, testE)) {
   1339                             log->record(kAddMissingCoin_Glitch, id, inner, testS, testE);
   1340                         }
   1341                     }
   1342                 }
   1343             }
   1344         }
   1345     } while ((outer = outer->fNext));
   1346 }
   1347 
   1348 bool SkOpCoincidence::debugExpand(const char* id, SkPathOpsDebug::GlitchLog* log) const {
   1349     const SkCoincidentSpans* coin = fHead;
   1350     if (!coin) {
   1351         return false;
   1352     }
   1353     bool expanded = false;
   1354     do {
   1355         const SkOpSpan* start = coin->fCoinPtTStart->span()->upCast();
   1356         const SkOpSpanBase* end = coin->fCoinPtTEnd->span();
   1357         const SkOpSegment* segment = coin->fCoinPtTStart->segment();
   1358         const SkOpSegment* oppSegment = coin->fOppPtTStart->segment();
   1359         const SkOpSpan* prev = start->prev();
   1360         if (prev && prev->debugContains(oppSegment)) {
   1361             double midT = (prev->t() + start->t()) / 2;
   1362             if (segment->isClose(midT, oppSegment)) {
   1363                 log->record(kExpandCoin_Glitch, id, coin, prev);
   1364             }
   1365         }
   1366         SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
   1367         if (next && next->debugContains(oppSegment)) {
   1368             double midT = (end->t() + next->t()) / 2;
   1369             if (segment->isClose(midT, oppSegment)) {
   1370                 log->record(kExpandCoin_Glitch, id, coin, next);
   1371             }
   1372         }
   1373     } while ((coin = coin->fNext));
   1374     return expanded;
   1375 }
   1376 
   1377 void SkOpCoincidence::debugFixAligned(const char* id, SkPathOpsDebug::GlitchLog* log) const {
   1378     const SkCoincidentSpans* coin = fHead;
   1379     if (!coin) {
   1380         return;
   1381     }
   1382     do {
   1383         if (coin->fCoinPtTStart->deleted()) {
   1384             log->record(kDeletedCoin_Glitch, id, coin, coin->fCoinPtTStart);
   1385         }
   1386         if (coin->fCoinPtTEnd->deleted()) {
   1387             log->record(kDeletedCoin_Glitch, id, coin, coin->fCoinPtTEnd);
   1388         }
   1389         if (coin->fOppPtTStart->deleted()) {
   1390             log->record(kDeletedCoin_Glitch, id, coin, coin->fOppPtTStart);
   1391         }
   1392         if (coin->fOppPtTEnd->deleted()) {
   1393             log->record(kDeletedCoin_Glitch, id, coin, coin->fOppPtTEnd);
   1394         }
   1395     } while ((coin = coin->fNext));
   1396     coin = fHead;
   1397     do {
   1398         if (coin->fCoinPtTStart->collapsed(coin->fCoinPtTEnd)) {
   1399             log->record(kCollapsedCoin_Glitch, id, coin, coin->fCoinPtTStart);
   1400         }
   1401         if (coin->fOppPtTStart->collapsed(coin->fOppPtTEnd)) {
   1402             log->record(kCollapsedCoin_Glitch, id, coin, coin->fOppPtTStart);
   1403         }
   1404     } while ((coin = coin->fNext));
   1405 }
   1406 
   1407 void SkOpCoincidence::debugMark(const char* id, SkPathOpsDebug::GlitchLog* log) const {
   1408     const SkCoincidentSpans* coin = fHead;
   1409     if (!coin) {
   1410         return;
   1411     }
   1412     do {
   1413         const SkOpSpanBase* end = coin->fCoinPtTEnd->span();
   1414         const SkOpSpanBase* oldEnd = end;
   1415         const SkOpSpan* start = coin->fCoinPtTStart->span()->debugStarter(&end);
   1416         const SkOpSpanBase* oEnd = coin->fOppPtTEnd->span();
   1417         const SkOpSpanBase* oOldEnd = oEnd;
   1418         const SkOpSpanBase* oStart = coin->fOppPtTStart->span()->debugStarter(&oEnd);
   1419         bool flipped = (end == oldEnd) != (oEnd == oOldEnd);
   1420         if (flipped) {
   1421             SkTSwap(oStart, oEnd);
   1422         }
   1423         const SkOpSpanBase* next = start;
   1424         const SkOpSpanBase* oNext = oStart;
   1425         do {
   1426             next = next->upCast()->next();
   1427             oNext = flipped ? oNext->prev() : oNext->upCast()->next();
   1428             if (next == end || oNext == oEnd) {
   1429                 break;
   1430             }
   1431             if (!next->containsCoinEnd(oNext)) {
   1432                 log->record(kMarkCoinEnd_Glitch, id, next, oNext);
   1433             }
   1434             const SkOpSpan* nextSpan = next->upCast();
   1435             const SkOpSpan* oNextSpan = oNext->upCast();
   1436             if (!nextSpan->containsCoincidence(oNextSpan)) {
   1437                 log->record(kMarkCoinInsert_Glitch, id, nextSpan, oNextSpan);
   1438             }
   1439         } while (true);
   1440     } while ((coin = coin->fNext));
   1441 }
   1442 #endif
   1443 
   1444 void SkOpCoincidence::debugShowCoincidence() const {
   1445     SkCoincidentSpans* span = fHead;
   1446     while (span) {
   1447         SkDebugf("%s - id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__,
   1448                 span->fCoinPtTStart->segment()->debugID(),
   1449                 span->fCoinPtTStart->fT, span->fCoinPtTEnd->fT);
   1450         SkDebugf("%s + id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__,
   1451                 span->fOppPtTStart->segment()->debugID(),
   1452                 span->fOppPtTStart->fT, span->fOppPtTEnd->fT);
   1453         span = span->fNext;
   1454     }
   1455 }
   1456 
   1457 #if DEBUG_COINCIDENCE
   1458 void SkOpContour::debugCheckHealth(const char* id, SkPathOpsDebug::GlitchLog* log) const {
   1459     const SkOpSegment* segment = &fHead;
   1460     do {
   1461         segment->debugCheckHealth(id, log);
   1462     } while ((segment = segment->next()));
   1463 }
   1464 
   1465 void SkOpContour::debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log,
   1466         const SkOpCoincidence* coincidence) const {
   1467     const SkOpSegment* segment = &fHead;
   1468     do {
   1469         segment->debugMissingCoincidence(id, log, coincidence);
   1470     } while ((segment = segment->next()));
   1471 }
   1472 #endif
   1473 
   1474 void SkOpSegment::debugValidate() const {
   1475 #if DEBUG_VALIDATE
   1476     const SkOpSpanBase* span = &fHead;
   1477     double lastT = -1;
   1478     const SkOpSpanBase* prev = nullptr;
   1479     int count = 0;
   1480     int done = 0;
   1481     do {
   1482         if (!span->final()) {
   1483             ++count;
   1484             done += span->upCast()->done() ? 1 : 0;
   1485         }
   1486         SkASSERT(span->segment() == this);
   1487         SkASSERT(!prev || prev->upCast()->next() == span);
   1488         SkASSERT(!prev || prev == span->prev());
   1489         prev = span;
   1490         double t = span->ptT()->fT;
   1491         SkASSERT(lastT < t);
   1492         lastT = t;
   1493         span->debugValidate();
   1494     } while (!span->final() && (span = span->upCast()->next()));
   1495     SkASSERT(count == fCount);
   1496     SkASSERT(done == fDoneCount);
   1497     SkASSERT(count >= fDoneCount);
   1498     SkASSERT(span->final());
   1499     span->debugValidate();
   1500 #endif
   1501 }
   1502 
   1503 bool SkOpSpanBase::debugAlignedEnd(double t, const SkPoint& pt) const {
   1504     SkASSERT(zero_or_one(t));
   1505     const SkOpSegment* segment = this->segment();
   1506     SkASSERT(t ? segment->lastPt() == pt : segment->pts()[0] == pt);
   1507     if (!debugAlignedInner()) {
   1508           return false;
   1509     }
   1510     if ((t ? segment->lastPt() : segment->pts()[0]) != pt) {
   1511         return false;
   1512     }
   1513     const SkOpPtT* ptT = &this->fPtT;
   1514     SkASSERT(t == ptT->fT);
   1515     SkASSERT(pt == ptT->fPt);
   1516     const SkOpPtT* test = ptT, * stopPtT = ptT;
   1517     while ((test = test->next()) != stopPtT) {
   1518         const SkOpSegment* other = test->segment();
   1519         if (other == this->segment()) {
   1520             continue;
   1521         }
   1522         if (!zero_or_one(test->fT)) {
   1523             continue;
   1524         }
   1525         if ((test->fT ? other->lastPt() : other->pts()[0]) != pt) {
   1526             return false;
   1527         }
   1528     }
   1529     return this->fAligned;
   1530 }
   1531 
   1532 bool SkOpSpanBase::debugAlignedInner() const {
   1533     // force the spans to share points and t values
   1534     const SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT;
   1535     const SkPoint& pt = ptT->fPt;
   1536     do {
   1537         if (ptT->fPt != pt) {
   1538             return false;
   1539         }
   1540         const SkOpSpanBase* span = ptT->span();
   1541         const SkOpPtT* test = ptT;
   1542         do {
   1543             if ((test = test->next()) == stopPtT) {
   1544                 break;
   1545             }
   1546             if (span == test->span() && !span->segment()->ptsDisjoint(*ptT, *test)) {
   1547                 return false;
   1548             }
   1549         } while (true);
   1550     } while ((ptT = ptT->next()) != stopPtT);
   1551     return true;
   1552 }
   1553 
   1554 bool SkOpSpanBase::debugCoinEndLoopCheck() const {
   1555     int loop = 0;
   1556     const SkOpSpanBase* next = this;
   1557     SkOpSpanBase* nextCoin;
   1558     do {
   1559         nextCoin = next->fCoinEnd;
   1560         SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin);
   1561         for (int check = 1; check < loop - 1; ++check) {
   1562             const SkOpSpanBase* checkCoin = this->fCoinEnd;
   1563             const SkOpSpanBase* innerCoin = checkCoin;
   1564             for (int inner = check + 1; inner < loop; ++inner) {
   1565                 innerCoin = innerCoin->fCoinEnd;
   1566                 if (checkCoin == innerCoin) {
   1567                     SkDebugf("*** bad coincident end loop ***\n");
   1568                     return false;
   1569                 }
   1570             }
   1571         }
   1572         ++loop;
   1573     } while ((next = nextCoin) && next != this);
   1574     return true;
   1575 }
   1576 
   1577 bool SkOpSpanBase::debugContains(const SkOpSegment* segment) const {
   1578     const SkOpPtT* start = &fPtT;
   1579     const SkOpPtT* walk = start;
   1580     while ((walk = walk->next()) != start) {
   1581         if (walk->segment() == segment) {
   1582             return true;
   1583         }
   1584     }
   1585     return false;
   1586 }
   1587 
   1588 const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const {
   1589     const SkOpSpanBase* end = *endPtr;
   1590     SkASSERT(this->segment() == end->segment());
   1591     const SkOpSpanBase* result;
   1592     if (t() < end->t()) {
   1593         result = this;
   1594     } else {
   1595         result = end;
   1596         *endPtr = this;
   1597     }
   1598     return result->upCast();
   1599 }
   1600 
   1601 void SkOpSpanBase::debugValidate() const {
   1602 #if DEBUG_VALIDATE
   1603     const SkOpPtT* ptT = &fPtT;
   1604     SkASSERT(ptT->span() == this);
   1605     do {
   1606 //        SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt));
   1607         ptT->debugValidate();
   1608         ptT = ptT->next();
   1609     } while (ptT != &fPtT);
   1610     SkASSERT(this->debugCoinEndLoopCheck());
   1611     if (!this->final()) {
   1612         SkASSERT(this->upCast()->debugCoinLoopCheck());
   1613     }
   1614     if (fFromAngle) {
   1615         fFromAngle->debugValidate();
   1616     }
   1617     if (!this->final() && this->upCast()->toAngle()) {
   1618         this->upCast()->toAngle()->debugValidate();
   1619     }
   1620 #endif
   1621 }
   1622 
   1623 bool SkOpSpan::debugCoinLoopCheck() const {
   1624     int loop = 0;
   1625     const SkOpSpan* next = this;
   1626     SkOpSpan* nextCoin;
   1627     do {
   1628         nextCoin = next->fCoincident;
   1629         SkASSERT(nextCoin == this || nextCoin->fCoincident != nextCoin);
   1630         for (int check = 1; check < loop - 1; ++check) {
   1631             const SkOpSpan* checkCoin = this->fCoincident;
   1632             const SkOpSpan* innerCoin = checkCoin;
   1633             for (int inner = check + 1; inner < loop; ++inner) {
   1634                 innerCoin = innerCoin->fCoincident;
   1635                 if (checkCoin == innerCoin) {
   1636                     SkDebugf("*** bad coincident loop ***\n");
   1637                     return false;
   1638                 }
   1639             }
   1640         }
   1641         ++loop;
   1642     } while ((next = nextCoin) && next != this);
   1643     return true;
   1644 }
   1645 
   1646 // called only by test code
   1647 int SkIntersections::debugCoincidentUsed() const {
   1648     if (!fIsCoincident[0]) {
   1649         SkASSERT(!fIsCoincident[1]);
   1650         return 0;
   1651     }
   1652     int count = 0;
   1653     SkDEBUGCODE(int count2 = 0;)
   1654     for (int index = 0; index < fUsed; ++index) {
   1655         if (fIsCoincident[0] & (1 << index)) {
   1656             ++count;
   1657         }
   1658 #ifdef SK_DEBUG
   1659         if (fIsCoincident[1] & (1 << index)) {
   1660             ++count2;
   1661         }
   1662 #endif
   1663     }
   1664     SkASSERT(count == count2);
   1665     return count;
   1666 }
   1667 
   1668 #include "SkOpContour.h"
   1669 
   1670 bool SkOpPtT::debugContains(const SkOpPtT* check) const {
   1671     SkASSERT(this != check);
   1672     const SkOpPtT* ptT = this;
   1673     int links = 0;
   1674     do {
   1675         ptT = ptT->next();
   1676         if (ptT == check) {
   1677             return true;
   1678         }
   1679         ++links;
   1680         const SkOpPtT* test = this;
   1681         for (int index = 0; index < links; ++index) {
   1682             if (ptT == test) {
   1683                 return false;
   1684             }
   1685             test = test->next();
   1686         }
   1687     } while (true);
   1688 }
   1689 
   1690 const SkOpPtT* SkOpPtT::debugContains(const SkOpSegment* check) const {
   1691     SkASSERT(this->segment() != check);
   1692     const SkOpPtT* ptT = this;
   1693     int links = 0;
   1694     do {
   1695         ptT = ptT->next();
   1696         if (ptT->segment() == check) {
   1697             return ptT;
   1698         }
   1699         ++links;
   1700         const SkOpPtT* test = this;
   1701         for (int index = 0; index < links; ++index) {
   1702             if (ptT == test) {
   1703                 return nullptr;
   1704             }
   1705             test = test->next();
   1706         }
   1707     } while (true);
   1708 }
   1709 
   1710 int SkOpPtT::debugLoopLimit(bool report) const {
   1711     int loop = 0;
   1712     const SkOpPtT* next = this;
   1713     do {
   1714         for (int check = 1; check < loop - 1; ++check) {
   1715             const SkOpPtT* checkPtT = this->fNext;
   1716             const SkOpPtT* innerPtT = checkPtT;
   1717             for (int inner = check + 1; inner < loop; ++inner) {
   1718                 innerPtT = innerPtT->fNext;
   1719                 if (checkPtT == innerPtT) {
   1720                     if (report) {
   1721                         SkDebugf("*** bad ptT loop ***\n");
   1722                     }
   1723                     return loop;
   1724                 }
   1725             }
   1726         }
   1727         // there's nothing wrong with extremely large loop counts -- but this may appear to hang
   1728         // by taking a very long time to figure out that no loop entry is a duplicate
   1729         // -- and it's likely that a large loop count is indicative of a bug somewhere
   1730         if (++loop > 1000) {
   1731             SkDebugf("*** loop count exceeds 1000 ***\n");
   1732             return 1000;
   1733         }
   1734     } while ((next = next->fNext) && next != this);
   1735     return 0;
   1736 }
   1737 
   1738 void SkOpPtT::debugValidate() const {
   1739 #if DEBUG_VALIDATE
   1740     SkOpGlobalState::Phase phase = contour()->globalState()->phase();
   1741     if (phase == SkOpGlobalState::kIntersecting
   1742             || phase == SkOpGlobalState::kFixWinding) {
   1743         return;
   1744     }
   1745     SkASSERT(fNext);
   1746     SkASSERT(fNext != this);
   1747     SkASSERT(fNext->fNext);
   1748     SkASSERT(debugLoopLimit(false) == 0);
   1749 #endif
   1750 }
   1751 
   1752 static void output_scalar(SkScalar num) {
   1753     if (num == (int) num) {
   1754         SkDebugf("%d", (int) num);
   1755     } else {
   1756         SkString str;
   1757         str.printf("%1.9g", num);
   1758         int width = (int) str.size();
   1759         const char* cStr = str.c_str();
   1760         while (cStr[width - 1] == '0') {
   1761             --width;
   1762         }
   1763         str.resize(width);
   1764         SkDebugf("%sf", str.c_str());
   1765     }
   1766 }
   1767 
   1768 static void output_points(const SkPoint* pts, int count) {
   1769     for (int index = 0; index < count; ++index) {
   1770         output_scalar(pts[index].fX);
   1771         SkDebugf(", ");
   1772         output_scalar(pts[index].fY);
   1773         if (index + 1 < count) {
   1774             SkDebugf(", ");
   1775         }
   1776     }
   1777 }
   1778 
   1779 static void showPathContours(SkPath::RawIter& iter, const char* pathName) {
   1780     uint8_t verb;
   1781     SkPoint pts[4];
   1782     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
   1783         switch (verb) {
   1784             case SkPath::kMove_Verb:
   1785                 SkDebugf("    %s.moveTo(", pathName);
   1786                 output_points(&pts[0], 1);
   1787                 SkDebugf(");\n");
   1788                 continue;
   1789             case SkPath::kLine_Verb:
   1790                 SkDebugf("    %s.lineTo(", pathName);
   1791                 output_points(&pts[1], 1);
   1792                 SkDebugf(");\n");
   1793                 break;
   1794             case SkPath::kQuad_Verb:
   1795                 SkDebugf("    %s.quadTo(", pathName);
   1796                 output_points(&pts[1], 2);
   1797                 SkDebugf(");\n");
   1798                 break;
   1799             case SkPath::kConic_Verb:
   1800                 SkDebugf("    %s.conicTo(", pathName);
   1801                 output_points(&pts[1], 2);
   1802                 SkDebugf(", %1.9gf);\n", iter.conicWeight());
   1803                 break;
   1804             case SkPath::kCubic_Verb:
   1805                 SkDebugf("    %s.cubicTo(", pathName);
   1806                 output_points(&pts[1], 3);
   1807                 SkDebugf(");\n");
   1808                 break;
   1809             case SkPath::kClose_Verb:
   1810                 SkDebugf("    %s.close();\n", pathName);
   1811                 break;
   1812             default:
   1813                 SkDEBUGFAIL("bad verb");
   1814                 return;
   1815         }
   1816     }
   1817 }
   1818 
   1819 static const char* gFillTypeStr[] = {
   1820     "kWinding_FillType",
   1821     "kEvenOdd_FillType",
   1822     "kInverseWinding_FillType",
   1823     "kInverseEvenOdd_FillType"
   1824 };
   1825 
   1826 void SkPathOpsDebug::ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration) {
   1827     SkPath::RawIter iter(path);
   1828 #define SUPPORT_RECT_CONTOUR_DETECTION 0
   1829 #if SUPPORT_RECT_CONTOUR_DETECTION
   1830     int rectCount = path.isRectContours() ? path.rectContours(nullptr, nullptr) : 0;
   1831     if (rectCount > 0) {
   1832         SkTDArray<SkRect> rects;
   1833         SkTDArray<SkPath::Direction> directions;
   1834         rects.setCount(rectCount);
   1835         directions.setCount(rectCount);
   1836         path.rectContours(rects.begin(), directions.begin());
   1837         for (int contour = 0; contour < rectCount; ++contour) {
   1838             const SkRect& rect = rects[contour];
   1839             SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
   1840                     rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
   1841                     ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
   1842         }
   1843         return;
   1844     }
   1845 #endif
   1846     SkPath::FillType fillType = path.getFillType();
   1847     SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType);
   1848     if (includeDeclaration) {
   1849         SkDebugf("    SkPath %s;\n", name);
   1850     }
   1851     SkDebugf("    %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[fillType]);
   1852     iter.setPath(path);
   1853     showPathContours(iter, name);
   1854 }
   1855