LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/pathops - SkPathOpsDebug.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 11 285 3.9 %
Date: 2017-07-14 16:53:18 Functions: 1 45 2.2 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13