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