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