1 /*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "SkMutex.h"
9 #include "SkOpCoincidence.h"
10 #include "SkOpContour.h"
11 #include "SkOSFile.h"
12 #include "SkPath.h"
13 #include "SkPathOpsDebug.h"
14 #include "SkString.h"
15
16 #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
560 SK_DECLARE_STATIC_MUTEX(gTestMutex);
561
ShowPath(const SkPath & a,const SkPath & b,SkPathOp shapeOp,const char * testName)562 void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
563 const char* testName) {
564 SkAutoMutexAcquire ac(gTestMutex);
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 "SkPathOpsTypes.h"
572 #include "SkIntersectionHelper.h"
573 #include "SkIntersections.h"
574
575 #if DEBUG_COIN
576
577 SK_DECLARE_STATIC_MUTEX(gCoinDictMutex);
578
debugAddToGlobalCoinDicts()579 void SkOpGlobalState::debugAddToGlobalCoinDicts() {
580 SkAutoMutexAcquire ac(&gCoinDictMutex);
581 SkPathOpsDebug::gCoinSumChangedDict.add(fCoinChangedDict);
582 SkPathOpsDebug::gCoinSumVisitedDict.add(fCoinVisitedDict);
583 }
584
585 #endif
586
587 #if DEBUG_T_SECT_LOOP_COUNT
debugAddLoopCount(SkIntersections * i,const SkIntersectionHelper & wt,const SkIntersectionHelper & wn)588 void SkOpGlobalState::debugAddLoopCount(SkIntersections* i, const SkIntersectionHelper& wt,
589 const SkIntersectionHelper& wn) {
590 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
591 SkIntersections::DebugLoop looper = (SkIntersections::DebugLoop) index;
592 if (fDebugLoopCount[index] >= i->debugLoopCount(looper)) {
593 continue;
594 }
595 fDebugLoopCount[index] = i->debugLoopCount(looper);
596 fDebugWorstVerb[index * 2] = wt.segment()->verb();
597 fDebugWorstVerb[index * 2 + 1] = wn.segment()->verb();
598 sk_bzero(&fDebugWorstPts[index * 8], sizeof(SkPoint) * 8);
599 memcpy(&fDebugWorstPts[index * 2 * 4], wt.pts(),
600 (SkPathOpsVerbToPoints(wt.segment()->verb()) + 1) * sizeof(SkPoint));
601 memcpy(&fDebugWorstPts[(index * 2 + 1) * 4], wn.pts(),
602 (SkPathOpsVerbToPoints(wn.segment()->verb()) + 1) * sizeof(SkPoint));
603 fDebugWorstWeight[index * 2] = wt.weight();
604 fDebugWorstWeight[index * 2 + 1] = wn.weight();
605 }
606 i->debugResetLoopCount();
607 }
608
debugDoYourWorst(SkOpGlobalState * local)609 void SkOpGlobalState::debugDoYourWorst(SkOpGlobalState* local) {
610 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
611 if (fDebugLoopCount[index] >= local->fDebugLoopCount[index]) {
612 continue;
613 }
614 fDebugLoopCount[index] = local->fDebugLoopCount[index];
615 fDebugWorstVerb[index * 2] = local->fDebugWorstVerb[index * 2];
616 fDebugWorstVerb[index * 2 + 1] = local->fDebugWorstVerb[index * 2 + 1];
617 memcpy(&fDebugWorstPts[index * 2 * 4], &local->fDebugWorstPts[index * 2 * 4],
618 sizeof(SkPoint) * 8);
619 fDebugWorstWeight[index * 2] = local->fDebugWorstWeight[index * 2];
620 fDebugWorstWeight[index * 2 + 1] = local->fDebugWorstWeight[index * 2 + 1];
621 }
622 local->debugResetLoopCounts();
623 }
624
dump_curve(SkPath::Verb verb,const SkPoint & pts,float weight)625 static void dump_curve(SkPath::Verb verb, const SkPoint& pts, float weight) {
626 if (!verb) {
627 return;
628 }
629 const char* verbs[] = { "", "line", "quad", "conic", "cubic" };
630 SkDebugf("%s: {{", verbs[verb]);
631 int ptCount = SkPathOpsVerbToPoints(verb);
632 for (int index = 0; index <= ptCount; ++index) {
633 SkDPoint::Dump((&pts)[index]);
634 if (index < ptCount - 1) {
635 SkDebugf(", ");
636 }
637 }
638 SkDebugf("}");
639 if (weight != 1) {
640 SkDebugf(", ");
641 if (weight == floorf(weight)) {
642 SkDebugf("%.0f", weight);
643 } else {
644 SkDebugf("%1.9gf", weight);
645 }
646 }
647 SkDebugf("}\n");
648 }
649
debugLoopReport()650 void SkOpGlobalState::debugLoopReport() {
651 const char* loops[] = { "iterations", "coinChecks", "perpCalcs" };
652 SkDebugf("\n");
653 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
654 SkDebugf("%s: %d\n", loops[index], fDebugLoopCount[index]);
655 dump_curve(fDebugWorstVerb[index * 2], fDebugWorstPts[index * 2 * 4],
656 fDebugWorstWeight[index * 2]);
657 dump_curve(fDebugWorstVerb[index * 2 + 1], fDebugWorstPts[(index * 2 + 1) * 4],
658 fDebugWorstWeight[index * 2 + 1]);
659 }
660 }
661
debugResetLoopCounts()662 void SkOpGlobalState::debugResetLoopCounts() {
663 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
664 sk_bzero(fDebugWorstVerb, sizeof(fDebugWorstVerb));
665 sk_bzero(fDebugWorstPts, sizeof(fDebugWorstPts));
666 sk_bzero(fDebugWorstWeight, sizeof(fDebugWorstWeight));
667 }
668 #endif
669
DebugRunFail()670 bool SkOpGlobalState::DebugRunFail() {
671 return SkPathOpsDebug::gRunFail;
672 }
673
674 // this is const so it can be called by const methods that overwise don't alter state
675 #if DEBUG_VALIDATE || DEBUG_COIN
debugSetPhase(const char * funcName DEBUG_COIN_DECLARE_PARAMS ()) const676 void SkOpGlobalState::debugSetPhase(const char* funcName DEBUG_COIN_DECLARE_PARAMS()) const {
677 auto writable = const_cast<SkOpGlobalState*>(this);
678 #if DEBUG_VALIDATE
679 writable->setPhase(phase);
680 #endif
681 #if DEBUG_COIN
682 SkPathOpsDebug::CoinDictEntry* entry = &writable->fCoinDictEntry;
683 writable->fPreviousFuncName = entry->fFunctionName;
684 entry->fIteration = iteration;
685 entry->fLineNumber = lineNo;
686 entry->fGlitchType = SkPathOpsDebug::kUninitialized_Glitch;
687 entry->fFunctionName = funcName;
688 writable->fCoinVisitedDict.add(*entry);
689 writable->debugAddToCoinChangedDict();
690 #endif
691 }
692 #endif
693
694 #if DEBUG_T_SECT_LOOP_COUNT
debugBumpLoopCount(DebugLoop index)695 void SkIntersections::debugBumpLoopCount(DebugLoop index) {
696 fDebugLoopCount[index]++;
697 }
698
debugLoopCount(DebugLoop index) const699 int SkIntersections::debugLoopCount(DebugLoop index) const {
700 return fDebugLoopCount[index];
701 }
702
debugResetLoopCount()703 void SkIntersections::debugResetLoopCount() {
704 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
705 }
706 #endif
707
708 #include "SkPathOpsConic.h"
709 #include "SkPathOpsCubic.h"
710
debugToCubic() const711 SkDCubic SkDQuad::debugToCubic() const {
712 SkDCubic cubic;
713 cubic[0] = fPts[0];
714 cubic[2] = fPts[1];
715 cubic[3] = fPts[2];
716 cubic[1].fX = (cubic[0].fX + cubic[2].fX * 2) / 3;
717 cubic[1].fY = (cubic[0].fY + cubic[2].fY * 2) / 3;
718 cubic[2].fX = (cubic[3].fX + cubic[2].fX * 2) / 3;
719 cubic[2].fY = (cubic[3].fY + cubic[2].fY * 2) / 3;
720 return cubic;
721 }
722
debugSet(const SkDPoint * pts)723 void SkDQuad::debugSet(const SkDPoint* pts) {
724 memcpy(fPts, pts, sizeof(fPts));
725 SkDEBUGCODE(fDebugGlobalState = nullptr);
726 }
727
debugSet(const SkDPoint * pts)728 void SkDCubic::debugSet(const SkDPoint* pts) {
729 memcpy(fPts, pts, sizeof(fPts));
730 SkDEBUGCODE(fDebugGlobalState = nullptr);
731 }
732
debugSet(const SkDPoint * pts,SkScalar weight)733 void SkDConic::debugSet(const SkDPoint* pts, SkScalar weight) {
734 fPts.debugSet(pts);
735 fWeight = weight;
736 }
737
debugInit()738 void SkDRect::debugInit() {
739 fLeft = fTop = fRight = fBottom = SK_ScalarNaN;
740 }
741
742 #include "SkOpAngle.h"
743 #include "SkOpSegment.h"
744
745 #if DEBUG_COIN
746 // commented-out lines keep this in sync with addT()
debugAddT(double t,SkPathOpsDebug::GlitchLog * log) const747 const SkOpPtT* SkOpSegment::debugAddT(double t, SkPathOpsDebug::GlitchLog* log) const {
748 debugValidate();
749 SkPoint pt = this->ptAtT(t);
750 const SkOpSpanBase* span = &fHead;
751 do {
752 const SkOpPtT* result = span->ptT();
753 if (t == result->fT || this->match(result, this, t, pt)) {
754 // span->bumpSpanAdds();
755 return result;
756 }
757 if (t < result->fT) {
758 const SkOpSpan* prev = result->span()->prev();
759 FAIL_WITH_NULL_IF(!prev, span);
760 // marks in global state that new op span has been allocated
761 this->globalState()->setAllocatedOpSpan();
762 // span->init(this, prev, t, pt);
763 this->debugValidate();
764 // #if DEBUG_ADD_T
765 // SkDebugf("%s insert t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
766 // span->segment()->debugID(), span->debugID());
767 // #endif
768 // span->bumpSpanAdds();
769 return nullptr;
770 }
771 FAIL_WITH_NULL_IF(span != &fTail, span);
772 } while ((span = span->upCast()->next()));
773 SkASSERT(0);
774 return nullptr; // we never get here, but need this to satisfy compiler
775 }
776 #endif
777
778 #if DEBUG_ANGLE
debugCheckAngleCoin() const779 void SkOpSegment::debugCheckAngleCoin() const {
780 const SkOpSpanBase* base = &fHead;
781 const SkOpSpan* span;
782 do {
783 const SkOpAngle* angle = base->fromAngle();
784 if (angle && angle->debugCheckCoincidence()) {
785 angle->debugCheckNearCoincidence();
786 }
787 if (base->final()) {
788 break;
789 }
790 span = base->upCast();
791 angle = span->toAngle();
792 if (angle && angle->debugCheckCoincidence()) {
793 angle->debugCheckNearCoincidence();
794 }
795 } while ((base = span->next()));
796 }
797 #endif
798
799 #if DEBUG_COIN
800 // this mimics the order of the checks in handle coincidence
debugCheckHealth(SkPathOpsDebug::GlitchLog * glitches) const801 void SkOpSegment::debugCheckHealth(SkPathOpsDebug::GlitchLog* glitches) const {
802 debugMoveMultiples(glitches);
803 debugMoveNearby(glitches);
804 debugMissingCoincidence(glitches);
805 }
806
807 // commented-out lines keep this in sync with clearAll()
debugClearAll(SkPathOpsDebug::GlitchLog * glitches) const808 void SkOpSegment::debugClearAll(SkPathOpsDebug::GlitchLog* glitches) const {
809 const SkOpSpan* span = &fHead;
810 do {
811 this->debugClearOne(span, glitches);
812 } while ((span = span->next()->upCastable()));
813 this->globalState()->coincidence()->debugRelease(glitches, this);
814 }
815
816 // commented-out lines keep this in sync with clearOne()
debugClearOne(const SkOpSpan * span,SkPathOpsDebug::GlitchLog * glitches) const817 void SkOpSegment::debugClearOne(const SkOpSpan* span, SkPathOpsDebug::GlitchLog* glitches) const {
818 if (span->windValue()) glitches->record(SkPathOpsDebug::kCollapsedWindValue_Glitch, span);
819 if (span->oppValue()) glitches->record(SkPathOpsDebug::kCollapsedOppValue_Glitch, span);
820 if (!span->done()) glitches->record(SkPathOpsDebug::kCollapsedDone_Glitch, span);
821 }
822 #endif
823
debugLastAngle()824 SkOpAngle* SkOpSegment::debugLastAngle() {
825 SkOpAngle* result = nullptr;
826 SkOpSpan* span = this->head();
827 do {
828 if (span->toAngle()) {
829 SkASSERT(!result);
830 result = span->toAngle();
831 }
832 } while ((span = span->next()->upCastable()));
833 SkASSERT(result);
834 return result;
835 }
836
837 #if DEBUG_COIN
838 // commented-out lines keep this in sync with ClearVisited
DebugClearVisited(const SkOpSpanBase * span)839 void SkOpSegment::DebugClearVisited(const SkOpSpanBase* span) {
840 // reset visited flag back to false
841 do {
842 const SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
843 while ((ptT = ptT->next()) != stopPtT) {
844 const SkOpSegment* opp = ptT->segment();
845 opp->resetDebugVisited();
846 }
847 } while (!span->final() && (span = span->upCast()->next()));
848 }
849 #endif
850
851 #if DEBUG_COIN
852 // commented-out lines keep this in sync with missingCoincidence()
853 // look for pairs of undetected coincident curves
854 // assumes that segments going in have visited flag clear
855 // Even though pairs of curves correct detect coincident runs, a run may be missed
856 // if the coincidence is a product of multiple intersections. For instance, given
857 // curves A, B, and C:
858 // A-B intersect at a point 1; A-C and B-C intersect at point 2, so near
859 // the end of C that the intersection is replaced with the end of C.
860 // Even though A-B correctly do not detect an intersection at point 2,
861 // the resulting run from point 1 to point 2 is coincident on A and B.
debugMissingCoincidence(SkPathOpsDebug::GlitchLog * log) const862 void SkOpSegment::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const {
863 if (this->done()) {
864 return;
865 }
866 const SkOpSpan* prior = nullptr;
867 const SkOpSpanBase* spanBase = &fHead;
868 // bool result = false;
869 do {
870 const SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT;
871 SkASSERT(ptT->span() == spanBase);
872 while ((ptT = ptT->next()) != spanStopPtT) {
873 if (ptT->deleted()) {
874 continue;
875 }
876 const SkOpSegment* opp = ptT->span()->segment();
877 if (opp->done()) {
878 continue;
879 }
880 // when opp is encounted the 1st time, continue; on 2nd encounter, look for coincidence
881 if (!opp->debugVisited()) {
882 continue;
883 }
884 if (spanBase == &fHead) {
885 continue;
886 }
887 if (ptT->segment() == this) {
888 continue;
889 }
890 const SkOpSpan* span = spanBase->upCastable();
891 // FIXME?: this assumes that if the opposite segment is coincident then no more
892 // coincidence needs to be detected. This may not be true.
893 if (span && span->segment() != opp && span->containsCoincidence(opp)) { // debug has additional condition since it may be called before inner duplicate points have been deleted
894 continue;
895 }
896 if (spanBase->segment() != opp && spanBase->containsCoinEnd(opp)) { // debug has additional condition since it may be called before inner duplicate points have been deleted
897 continue;
898 }
899 const SkOpPtT* priorPtT = nullptr, * priorStopPtT;
900 // find prior span containing opp segment
901 const SkOpSegment* priorOpp = nullptr;
902 const SkOpSpan* priorTest = spanBase->prev();
903 while (!priorOpp && priorTest) {
904 priorStopPtT = priorPtT = priorTest->ptT();
905 while ((priorPtT = priorPtT->next()) != priorStopPtT) {
906 if (priorPtT->deleted()) {
907 continue;
908 }
909 const SkOpSegment* segment = priorPtT->span()->segment();
910 if (segment == opp) {
911 prior = priorTest;
912 priorOpp = opp;
913 break;
914 }
915 }
916 priorTest = priorTest->prev();
917 }
918 if (!priorOpp) {
919 continue;
920 }
921 if (priorPtT == ptT) {
922 continue;
923 }
924 const SkOpPtT* oppStart = prior->ptT();
925 const SkOpPtT* oppEnd = spanBase->ptT();
926 bool swapped = priorPtT->fT > ptT->fT;
927 if (swapped) {
928 using std::swap;
929 swap(priorPtT, ptT);
930 swap(oppStart, oppEnd);
931 }
932 const SkOpCoincidence* coincidence = this->globalState()->coincidence();
933 const SkOpPtT* rootPriorPtT = priorPtT->span()->ptT();
934 const SkOpPtT* rootPtT = ptT->span()->ptT();
935 const SkOpPtT* rootOppStart = oppStart->span()->ptT();
936 const SkOpPtT* rootOppEnd = oppEnd->span()->ptT();
937 if (coincidence->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) {
938 goto swapBack;
939 }
940 if (testForCoincidence(rootPriorPtT, rootPtT, prior, spanBase, opp)) {
941 // mark coincidence
942 #if DEBUG_COINCIDENCE_VERBOSE
943 // SkDebugf("%s coinSpan=%d endSpan=%d oppSpan=%d oppEndSpan=%d\n", __FUNCTION__,
944 // rootPriorPtT->debugID(), rootPtT->debugID(), rootOppStart->debugID(),
945 // rootOppEnd->debugID());
946 #endif
947 log->record(SkPathOpsDebug::kMissingCoin_Glitch, priorPtT, ptT, oppStart, oppEnd);
948 // coincidences->add(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
949 // }
950 #if DEBUG_COINCIDENCE
951 // SkASSERT(coincidences->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
952 #endif
953 // result = true;
954 }
955 swapBack:
956 if (swapped) {
957 using std::swap;
958 swap(priorPtT, ptT);
959 }
960 }
961 } while ((spanBase = spanBase->final() ? nullptr : spanBase->upCast()->next()));
962 DebugClearVisited(&fHead);
963 return;
964 }
965
966 // commented-out lines keep this in sync with moveMultiples()
967 // if a span has more than one intersection, merge the other segments' span as needed
debugMoveMultiples(SkPathOpsDebug::GlitchLog * glitches) const968 void SkOpSegment::debugMoveMultiples(SkPathOpsDebug::GlitchLog* glitches) const {
969 debugValidate();
970 const SkOpSpanBase* test = &fHead;
971 do {
972 int addCount = test->spanAddsCount();
973 // SkASSERT(addCount >= 1);
974 if (addCount <= 1) {
975 continue;
976 }
977 const SkOpPtT* startPtT = test->ptT();
978 const SkOpPtT* testPtT = startPtT;
979 do { // iterate through all spans associated with start
980 const SkOpSpanBase* oppSpan = testPtT->span();
981 if (oppSpan->spanAddsCount() == addCount) {
982 continue;
983 }
984 if (oppSpan->deleted()) {
985 continue;
986 }
987 const SkOpSegment* oppSegment = oppSpan->segment();
988 if (oppSegment == this) {
989 continue;
990 }
991 // find range of spans to consider merging
992 const SkOpSpanBase* oppPrev = oppSpan;
993 const SkOpSpanBase* oppFirst = oppSpan;
994 while ((oppPrev = oppPrev->prev())) {
995 if (!roughly_equal(oppPrev->t(), oppSpan->t())) {
996 break;
997 }
998 if (oppPrev->spanAddsCount() == addCount) {
999 continue;
1000 }
1001 if (oppPrev->deleted()) {
1002 continue;
1003 }
1004 oppFirst = oppPrev;
1005 }
1006 const SkOpSpanBase* oppNext = oppSpan;
1007 const SkOpSpanBase* oppLast = oppSpan;
1008 while ((oppNext = oppNext->final() ? nullptr : oppNext->upCast()->next())) {
1009 if (!roughly_equal(oppNext->t(), oppSpan->t())) {
1010 break;
1011 }
1012 if (oppNext->spanAddsCount() == addCount) {
1013 continue;
1014 }
1015 if (oppNext->deleted()) {
1016 continue;
1017 }
1018 oppLast = oppNext;
1019 }
1020 if (oppFirst == oppLast) {
1021 continue;
1022 }
1023 const SkOpSpanBase* oppTest = oppFirst;
1024 do {
1025 if (oppTest == oppSpan) {
1026 continue;
1027 }
1028 // check to see if the candidate meets specific criteria:
1029 // it contains spans of segments in test's loop but not including 'this'
1030 const SkOpPtT* oppStartPtT = oppTest->ptT();
1031 const SkOpPtT* oppPtT = oppStartPtT;
1032 while ((oppPtT = oppPtT->next()) != oppStartPtT) {
1033 const SkOpSegment* oppPtTSegment = oppPtT->segment();
1034 if (oppPtTSegment == this) {
1035 goto tryNextSpan;
1036 }
1037 const SkOpPtT* matchPtT = startPtT;
1038 do {
1039 if (matchPtT->segment() == oppPtTSegment) {
1040 goto foundMatch;
1041 }
1042 } while ((matchPtT = matchPtT->next()) != startPtT);
1043 goto tryNextSpan;
1044 foundMatch: // merge oppTest and oppSpan
1045 oppSegment->debugValidate();
1046 oppTest->debugMergeMatches(glitches, oppSpan);
1047 oppTest->debugAddOpp(glitches, oppSpan);
1048 oppSegment->debugValidate();
1049 goto checkNextSpan;
1050 }
1051 tryNextSpan:
1052 ;
1053 } while (oppTest != oppLast && (oppTest = oppTest->upCast()->next()));
1054 } while ((testPtT = testPtT->next()) != startPtT);
1055 checkNextSpan:
1056 ;
1057 } while ((test = test->final() ? nullptr : test->upCast()->next()));
1058 debugValidate();
1059 return;
1060 }
1061
1062 // commented-out lines keep this in sync with moveNearby()
1063 // Move nearby t values and pts so they all hang off the same span. Alignment happens later.
debugMoveNearby(SkPathOpsDebug::GlitchLog * glitches) const1064 void SkOpSegment::debugMoveNearby(SkPathOpsDebug::GlitchLog* glitches) const {
1065 debugValidate();
1066 // release undeleted spans pointing to this seg that are linked to the primary span
1067 const SkOpSpanBase* spanBase = &fHead;
1068 do {
1069 const SkOpPtT* ptT = spanBase->ptT();
1070 const SkOpPtT* headPtT = ptT;
1071 while ((ptT = ptT->next()) != headPtT) {
1072 const SkOpSpanBase* test = ptT->span();
1073 if (ptT->segment() == this && !ptT->deleted() && test != spanBase
1074 && test->ptT() == ptT) {
1075 if (test->final()) {
1076 if (spanBase == &fHead) {
1077 glitches->record(SkPathOpsDebug::kMoveNearbyClearAll_Glitch, this);
1078 // return;
1079 }
1080 glitches->record(SkPathOpsDebug::kMoveNearbyReleaseFinal_Glitch, spanBase, ptT);
1081 } else if (test->prev()) {
1082 glitches->record(SkPathOpsDebug::kMoveNearbyRelease_Glitch, test, headPtT);
1083 }
1084 // break;
1085 }
1086 }
1087 spanBase = spanBase->upCast()->next();
1088 } while (!spanBase->final());
1089
1090 // This loop looks for adjacent spans which are near by
1091 spanBase = &fHead;
1092 do { // iterate through all spans associated with start
1093 const SkOpSpanBase* test = spanBase->upCast()->next();
1094 bool found;
1095 if (!this->spansNearby(spanBase, test, &found)) {
1096 glitches->record(SkPathOpsDebug::kMoveNearbyMergeFinal_Glitch, test);
1097 }
1098 if (found) {
1099 if (test->final()) {
1100 if (spanBase->prev()) {
1101 glitches->record(SkPathOpsDebug::kMoveNearbyMergeFinal_Glitch, test);
1102 } else {
1103 glitches->record(SkPathOpsDebug::kMoveNearbyClearAll2_Glitch, this);
1104 // return
1105 }
1106 } else {
1107 glitches->record(SkPathOpsDebug::kMoveNearbyMerge_Glitch, spanBase);
1108 }
1109 }
1110 spanBase = test;
1111 } while (!spanBase->final());
1112 debugValidate();
1113 }
1114 #endif
1115
debugReset()1116 void SkOpSegment::debugReset() {
1117 this->init(this->fPts, this->fWeight, this->contour(), this->verb());
1118 }
1119
1120 #if DEBUG_COINCIDENCE_ORDER
debugSetCoinT(int index,SkScalar t) const1121 void SkOpSegment::debugSetCoinT(int index, SkScalar t) const {
1122 if (fDebugBaseMax < 0 || fDebugBaseIndex == index) {
1123 fDebugBaseIndex = index;
1124 fDebugBaseMin = SkTMin(t, fDebugBaseMin);
1125 fDebugBaseMax = SkTMax(t, fDebugBaseMax);
1126 return;
1127 }
1128 SkASSERT(fDebugBaseMin >= t || t >= fDebugBaseMax);
1129 if (fDebugLastMax < 0 || fDebugLastIndex == index) {
1130 fDebugLastIndex = index;
1131 fDebugLastMin = SkTMin(t, fDebugLastMin);
1132 fDebugLastMax = SkTMax(t, fDebugLastMax);
1133 return;
1134 }
1135 SkASSERT(fDebugLastMin >= t || t >= fDebugLastMax);
1136 SkASSERT((t - fDebugBaseMin > 0) == (fDebugLastMin - fDebugBaseMin > 0));
1137 }
1138 #endif
1139
1140 #if DEBUG_ACTIVE_SPANS
debugShowActiveSpans(SkString * str) const1141 void SkOpSegment::debugShowActiveSpans(SkString* str) const {
1142 debugValidate();
1143 if (done()) {
1144 return;
1145 }
1146 int lastId = -1;
1147 double lastT = -1;
1148 const SkOpSpan* span = &fHead;
1149 do {
1150 if (span->done()) {
1151 continue;
1152 }
1153 if (lastId == this->debugID() && lastT == span->t()) {
1154 continue;
1155 }
1156 lastId = this->debugID();
1157 lastT = span->t();
1158 str->appendf("%s id=%d", __FUNCTION__, this->debugID());
1159 // since endpoints may have be adjusted, show actual computed curves
1160 SkDCurve curvePart;
1161 this->subDivide(span, span->next(), &curvePart);
1162 const SkDPoint* pts = curvePart.fCubic.fPts;
1163 str->appendf(" (%1.9g,%1.9g", pts[0].fX, pts[0].fY);
1164 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1165 str->appendf(" %1.9g,%1.9g", pts[vIndex].fX, pts[vIndex].fY);
1166 }
1167 if (SkPath::kConic_Verb == fVerb) {
1168 str->appendf(" %1.9gf", curvePart.fConic.fWeight);
1169 }
1170 str->appendf(") t=%1.9g tEnd=%1.9g", span->t(), span->next()->t());
1171 if (span->windSum() == SK_MinS32) {
1172 str->appendf(" windSum=?");
1173 } else {
1174 str->appendf(" windSum=%d", span->windSum());
1175 }
1176 if (span->oppValue() && span->oppSum() == SK_MinS32) {
1177 str->appendf(" oppSum=?");
1178 } else if (span->oppValue() || span->oppSum() != SK_MinS32) {
1179 str->appendf(" oppSum=%d", span->oppSum());
1180 }
1181 str->appendf(" windValue=%d", span->windValue());
1182 if (span->oppValue() || span->oppSum() != SK_MinS32) {
1183 str->appendf(" oppValue=%d", span->oppValue());
1184 }
1185 str->appendf("\n");
1186 } while ((span = span->next()->upCastable()));
1187 }
1188 #endif
1189
1190 #if DEBUG_MARK_DONE
debugShowNewWinding(const char * fun,const SkOpSpan * span,int winding)1191 void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding) {
1192 const SkPoint& pt = span->ptT()->fPt;
1193 SkDebugf("%s id=%d", fun, this->debugID());
1194 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1195 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1196 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1197 }
1198 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
1199 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t());
1200 if (winding == SK_MinS32) {
1201 SkDebugf("?");
1202 } else {
1203 SkDebugf("%d", winding);
1204 }
1205 SkDebugf(" windSum=");
1206 if (span->windSum() == SK_MinS32) {
1207 SkDebugf("?");
1208 } else {
1209 SkDebugf("%d", span->windSum());
1210 }
1211 SkDebugf(" windValue=%d\n", span->windValue());
1212 }
1213
debugShowNewWinding(const char * fun,const SkOpSpan * span,int winding,int oppWinding)1214 void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding,
1215 int oppWinding) {
1216 const SkPoint& pt = span->ptT()->fPt;
1217 SkDebugf("%s id=%d", fun, this->debugID());
1218 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1219 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1220 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1221 }
1222 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
1223 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t(), winding, oppWinding);
1224 if (winding == SK_MinS32) {
1225 SkDebugf("?");
1226 } else {
1227 SkDebugf("%d", winding);
1228 }
1229 SkDebugf(" newOppSum=");
1230 if (oppWinding == SK_MinS32) {
1231 SkDebugf("?");
1232 } else {
1233 SkDebugf("%d", oppWinding);
1234 }
1235 SkDebugf(" oppSum=");
1236 if (span->oppSum() == SK_MinS32) {
1237 SkDebugf("?");
1238 } else {
1239 SkDebugf("%d", span->oppSum());
1240 }
1241 SkDebugf(" windSum=");
1242 if (span->windSum() == SK_MinS32) {
1243 SkDebugf("?");
1244 } else {
1245 SkDebugf("%d", span->windSum());
1246 }
1247 SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue());
1248 }
1249
1250 #endif
1251
1252 // loop looking for a pair of angle parts that are too close to be sorted
1253 /* This is called after other more simple intersection and angle sorting tests have been exhausted.
1254 This should be rarely called -- the test below is thorough and time consuming.
1255 This checks the distance between start points; the distance between
1256 */
1257 #if DEBUG_ANGLE
debugCheckNearCoincidence() const1258 void SkOpAngle::debugCheckNearCoincidence() const {
1259 const SkOpAngle* test = this;
1260 do {
1261 const SkOpSegment* testSegment = test->segment();
1262 double testStartT = test->start()->t();
1263 SkDPoint testStartPt = testSegment->dPtAtT(testStartT);
1264 double testEndT = test->end()->t();
1265 SkDPoint testEndPt = testSegment->dPtAtT(testEndT);
1266 double testLenSq = testStartPt.distanceSquared(testEndPt);
1267 SkDebugf("%s testLenSq=%1.9g id=%d\n", __FUNCTION__, testLenSq, testSegment->debugID());
1268 double testMidT = (testStartT + testEndT) / 2;
1269 const SkOpAngle* next = test;
1270 while ((next = next->fNext) != this) {
1271 SkOpSegment* nextSegment = next->segment();
1272 double testMidDistSq = testSegment->distSq(testMidT, next);
1273 double testEndDistSq = testSegment->distSq(testEndT, next);
1274 double nextStartT = next->start()->t();
1275 SkDPoint nextStartPt = nextSegment->dPtAtT(nextStartT);
1276 double distSq = testStartPt.distanceSquared(nextStartPt);
1277 double nextEndT = next->end()->t();
1278 double nextMidT = (nextStartT + nextEndT) / 2;
1279 double nextMidDistSq = nextSegment->distSq(nextMidT, test);
1280 double nextEndDistSq = nextSegment->distSq(nextEndT, test);
1281 SkDebugf("%s distSq=%1.9g testId=%d nextId=%d\n", __FUNCTION__, distSq,
1282 testSegment->debugID(), nextSegment->debugID());
1283 SkDebugf("%s testMidDistSq=%1.9g\n", __FUNCTION__, testMidDistSq);
1284 SkDebugf("%s testEndDistSq=%1.9g\n", __FUNCTION__, testEndDistSq);
1285 SkDebugf("%s nextMidDistSq=%1.9g\n", __FUNCTION__, nextMidDistSq);
1286 SkDebugf("%s nextEndDistSq=%1.9g\n", __FUNCTION__, nextEndDistSq);
1287 SkDPoint nextEndPt = nextSegment->dPtAtT(nextEndT);
1288 double nextLenSq = nextStartPt.distanceSquared(nextEndPt);
1289 SkDebugf("%s nextLenSq=%1.9g\n", __FUNCTION__, nextLenSq);
1290 SkDebugf("\n");
1291 }
1292 test = test->fNext;
1293 } while (test->fNext != this);
1294 }
1295 #endif
1296
1297 #if DEBUG_ANGLE
debugPart() const1298 SkString SkOpAngle::debugPart() const {
1299 SkString result;
1300 switch (this->segment()->verb()) {
1301 case SkPath::kLine_Verb:
1302 result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fPart.fCurve),
1303 this->segment()->debugID());
1304 break;
1305 case SkPath::kQuad_Verb:
1306 result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fPart.fCurve),
1307 this->segment()->debugID());
1308 break;
1309 case SkPath::kConic_Verb:
1310 result.printf(CONIC_DEBUG_STR " id=%d",
1311 CONIC_DEBUG_DATA(fPart.fCurve, fPart.fCurve.fConic.fWeight),
1312 this->segment()->debugID());
1313 break;
1314 case SkPath::kCubic_Verb:
1315 result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fPart.fCurve),
1316 this->segment()->debugID());
1317 break;
1318 default:
1319 SkASSERT(0);
1320 }
1321 return result;
1322 }
1323 #endif
1324
1325 #if DEBUG_SORT
debugLoop() const1326 void SkOpAngle::debugLoop() const {
1327 const SkOpAngle* first = this;
1328 const SkOpAngle* next = this;
1329 do {
1330 next->dumpOne(true);
1331 SkDebugf("\n");
1332 next = next->fNext;
1333 } while (next && next != first);
1334 next = first;
1335 do {
1336 next->debugValidate();
1337 next = next->fNext;
1338 } while (next && next != first);
1339 }
1340 #endif
1341
debugValidate() const1342 void SkOpAngle::debugValidate() const {
1343 #if DEBUG_COINCIDENCE
1344 if (this->globalState()->debugCheckHealth()) {
1345 return;
1346 }
1347 #endif
1348 #if DEBUG_VALIDATE
1349 const SkOpAngle* first = this;
1350 const SkOpAngle* next = this;
1351 int wind = 0;
1352 int opp = 0;
1353 int lastXor = -1;
1354 int lastOppXor = -1;
1355 do {
1356 if (next->unorderable()) {
1357 return;
1358 }
1359 const SkOpSpan* minSpan = next->start()->starter(next->end());
1360 if (minSpan->windValue() == SK_MinS32) {
1361 return;
1362 }
1363 bool op = next->segment()->operand();
1364 bool isXor = next->segment()->isXor();
1365 bool oppXor = next->segment()->oppXor();
1366 SkASSERT(!DEBUG_LIMIT_WIND_SUM || between(0, minSpan->windValue(), DEBUG_LIMIT_WIND_SUM));
1367 SkASSERT(!DEBUG_LIMIT_WIND_SUM
1368 || between(-DEBUG_LIMIT_WIND_SUM, minSpan->oppValue(), DEBUG_LIMIT_WIND_SUM));
1369 bool useXor = op ? oppXor : isXor;
1370 SkASSERT(lastXor == -1 || lastXor == (int) useXor);
1371 lastXor = (int) useXor;
1372 wind += next->debugSign() * (op ? minSpan->oppValue() : minSpan->windValue());
1373 if (useXor) {
1374 wind &= 1;
1375 }
1376 useXor = op ? isXor : oppXor;
1377 SkASSERT(lastOppXor == -1 || lastOppXor == (int) useXor);
1378 lastOppXor = (int) useXor;
1379 opp += next->debugSign() * (op ? minSpan->windValue() : minSpan->oppValue());
1380 if (useXor) {
1381 opp &= 1;
1382 }
1383 next = next->fNext;
1384 } while (next && next != first);
1385 SkASSERT(wind == 0 || !SkPathOpsDebug::gRunFail);
1386 SkASSERT(opp == 0 || !SkPathOpsDebug::gRunFail);
1387 #endif
1388 }
1389
debugValidateNext() const1390 void SkOpAngle::debugValidateNext() const {
1391 #if !FORCE_RELEASE
1392 const SkOpAngle* first = this;
1393 const SkOpAngle* next = first;
1394 SkTDArray<const SkOpAngle*>(angles);
1395 do {
1396 // SkASSERT_RELEASE(next->fSegment->debugContains(next));
1397 angles.push_back(next);
1398 next = next->next();
1399 if (next == first) {
1400 break;
1401 }
1402 SkASSERT_RELEASE(!angles.contains(next));
1403 if (!next) {
1404 return;
1405 }
1406 } while (true);
1407 #endif
1408 }
1409
1410 #ifdef SK_DEBUG
debugStartCheck(const SkOpSpanBase * outer,const SkOpSpanBase * over,const SkOpGlobalState * debugState) const1411 void SkCoincidentSpans::debugStartCheck(const SkOpSpanBase* outer, const SkOpSpanBase* over,
1412 const SkOpGlobalState* debugState) const {
1413 SkASSERT(coinPtTEnd()->span() == over || !SkOpGlobalState::DebugRunFail());
1414 SkASSERT(oppPtTEnd()->span() == outer || !SkOpGlobalState::DebugRunFail());
1415 }
1416 #endif
1417
1418 #if DEBUG_COIN
1419 // 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) const1420 void SkCoincidentSpans::debugCorrectOneEnd(SkPathOpsDebug::GlitchLog* log,
1421 const SkOpPtT* (SkCoincidentSpans::* getEnd)() const,
1422 void (SkCoincidentSpans::*setEnd)(const SkOpPtT* ptT) const ) const {
1423 const SkOpPtT* origPtT = (this->*getEnd)();
1424 const SkOpSpanBase* origSpan = origPtT->span();
1425 const SkOpSpan* prev = origSpan->prev();
1426 const SkOpPtT* testPtT = prev ? prev->next()->ptT()
1427 : origSpan->upCast()->next()->prev()->ptT();
1428 if (origPtT != testPtT) {
1429 log->record(SkPathOpsDebug::kCorrectEnd_Glitch, this, origPtT, testPtT);
1430 }
1431 }
1432
1433
1434 /* Commented-out lines keep this in sync with correctEnds */
1435 // FIXME: member pointers have fallen out of favor and can be replaced with
1436 // an alternative approach.
1437 // makes all span ends agree with the segment's spans that define them
debugCorrectEnds(SkPathOpsDebug::GlitchLog * log) const1438 void SkCoincidentSpans::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
1439 this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTStart, nullptr);
1440 this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTEnd, nullptr);
1441 this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTStart, nullptr);
1442 this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTEnd, nullptr);
1443 }
1444
1445 /* Commented-out lines keep this in sync with expand */
1446 // expand the range by checking adjacent spans for coincidence
debugExpand(SkPathOpsDebug::GlitchLog * log) const1447 bool SkCoincidentSpans::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
1448 bool expanded = false;
1449 const SkOpSegment* segment = coinPtTStart()->segment();
1450 const SkOpSegment* oppSegment = oppPtTStart()->segment();
1451 do {
1452 const SkOpSpan* start = coinPtTStart()->span()->upCast();
1453 const SkOpSpan* prev = start->prev();
1454 const SkOpPtT* oppPtT;
1455 if (!prev || !(oppPtT = prev->contains(oppSegment))) {
1456 break;
1457 }
1458 double midT = (prev->t() + start->t()) / 2;
1459 if (!segment->isClose(midT, oppSegment)) {
1460 break;
1461 }
1462 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, prev->ptT(), oppPtT);
1463 expanded = true;
1464 } while (false); // actual continues while expansion is possible
1465 do {
1466 const SkOpSpanBase* end = coinPtTEnd()->span();
1467 SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
1468 if (next && next->deleted()) {
1469 break;
1470 }
1471 const SkOpPtT* oppPtT;
1472 if (!next || !(oppPtT = next->contains(oppSegment))) {
1473 break;
1474 }
1475 double midT = (end->t() + next->t()) / 2;
1476 if (!segment->isClose(midT, oppSegment)) {
1477 break;
1478 }
1479 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, next->ptT(), oppPtT);
1480 expanded = true;
1481 } while (false); // actual continues while expansion is possible
1482 return expanded;
1483 }
1484
1485 // description below
debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog * log,const SkOpSpan * base,const SkOpSpanBase * testSpan) const1486 void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* base, const SkOpSpanBase* testSpan) const {
1487 const SkOpPtT* testPtT = testSpan->ptT();
1488 const SkOpPtT* stopPtT = testPtT;
1489 const SkOpSegment* baseSeg = base->segment();
1490 while ((testPtT = testPtT->next()) != stopPtT) {
1491 const SkOpSegment* testSeg = testPtT->segment();
1492 if (testPtT->deleted()) {
1493 continue;
1494 }
1495 if (testSeg == baseSeg) {
1496 continue;
1497 }
1498 if (testPtT->span()->ptT() != testPtT) {
1499 continue;
1500 }
1501 if (this->contains(baseSeg, testSeg, testPtT->fT)) {
1502 continue;
1503 }
1504 // intersect perp with base->ptT() with testPtT->segment()
1505 SkDVector dxdy = baseSeg->dSlopeAtT(base->t());
1506 const SkPoint& pt = base->pt();
1507 SkDLine ray = {{{pt.fX, pt.fY}, {pt.fX + dxdy.fY, pt.fY - dxdy.fX}}};
1508 SkIntersections i;
1509 (*CurveIntersectRay[testSeg->verb()])(testSeg->pts(), testSeg->weight(), ray, &i);
1510 for (int index = 0; index < i.used(); ++index) {
1511 double t = i[0][index];
1512 if (!between(0, t, 1)) {
1513 continue;
1514 }
1515 SkDPoint oppPt = i.pt(index);
1516 if (!oppPt.approximatelyEqual(pt)) {
1517 continue;
1518 }
1519 SkOpSegment* writableSeg = const_cast<SkOpSegment*>(testSeg);
1520 SkOpPtT* oppStart = writableSeg->addT(t);
1521 if (oppStart == testPtT) {
1522 continue;
1523 }
1524 SkOpSpan* writableBase = const_cast<SkOpSpan*>(base);
1525 oppStart->span()->addOpp(writableBase);
1526 if (oppStart->deleted()) {
1527 continue;
1528 }
1529 SkOpSegment* coinSeg = base->segment();
1530 SkOpSegment* oppSeg = oppStart->segment();
1531 double coinTs, coinTe, oppTs, oppTe;
1532 if (Ordered(coinSeg, oppSeg)) {
1533 coinTs = base->t();
1534 coinTe = testSpan->t();
1535 oppTs = oppStart->fT;
1536 oppTe = testPtT->fT;
1537 } else {
1538 using std::swap;
1539 swap(coinSeg, oppSeg);
1540 coinTs = oppStart->fT;
1541 coinTe = testPtT->fT;
1542 oppTs = base->t();
1543 oppTe = testSpan->t();
1544 }
1545 if (coinTs > coinTe) {
1546 using std::swap;
1547 swap(coinTs, coinTe);
1548 swap(oppTs, oppTe);
1549 }
1550 bool added;
1551 if (this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &added), false) {
1552 return;
1553 }
1554 }
1555 }
1556 return;
1557 }
1558
1559 // description below
debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog * log,const SkOpPtT * ptT) const1560 void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* ptT) const {
1561 FAIL_IF(!ptT->span()->upCastable(), ptT->span());
1562 const SkOpSpan* base = ptT->span()->upCast();
1563 const SkOpSpan* prev = base->prev();
1564 FAIL_IF(!prev, ptT->span());
1565 if (!prev->isCanceled()) {
1566 if (this->debugAddEndMovedSpans(log, base, base->prev()), false) {
1567 return;
1568 }
1569 }
1570 if (!base->isCanceled()) {
1571 if (this->debugAddEndMovedSpans(log, base, base->next()), false) {
1572 return;
1573 }
1574 }
1575 return;
1576 }
1577
1578 /* If A is coincident with B and B includes an endpoint, and A's matching point
1579 is not the endpoint (i.e., there's an implied line connecting B-end and A)
1580 then assume that the same implied line may intersect another curve close to B.
1581 Since we only care about coincidence that was undetected, look at the
1582 ptT list on B-segment adjacent to the B-end/A ptT loop (not in the loop, but
1583 next door) and see if the A matching point is close enough to form another
1584 coincident pair. If so, check for a new coincident span between B-end/A ptT loop
1585 and the adjacent ptT loop.
1586 */
debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog * log) const1587 void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log) const {
1588 const SkCoincidentSpans* span = fHead;
1589 if (!span) {
1590 return;
1591 }
1592 // fTop = span;
1593 // fHead = nullptr;
1594 do {
1595 if (span->coinPtTStart()->fPt != span->oppPtTStart()->fPt) {
1596 FAIL_IF(1 == span->coinPtTStart()->fT, span);
1597 bool onEnd = span->coinPtTStart()->fT == 0;
1598 bool oOnEnd = zero_or_one(span->oppPtTStart()->fT);
1599 if (onEnd) {
1600 if (!oOnEnd) { // if both are on end, any nearby intersect was already found
1601 if (this->debugAddEndMovedSpans(log, span->oppPtTStart()), false) {
1602 return;
1603 }
1604 }
1605 } else if (oOnEnd) {
1606 if (this->debugAddEndMovedSpans(log, span->coinPtTStart()), false) {
1607 return;
1608 }
1609 }
1610 }
1611 if (span->coinPtTEnd()->fPt != span->oppPtTEnd()->fPt) {
1612 bool onEnd = span->coinPtTEnd()->fT == 1;
1613 bool oOnEnd = zero_or_one(span->oppPtTEnd()->fT);
1614 if (onEnd) {
1615 if (!oOnEnd) {
1616 if (this->debugAddEndMovedSpans(log, span->oppPtTEnd()), false) {
1617 return;
1618 }
1619 }
1620 } else if (oOnEnd) {
1621 if (this->debugAddEndMovedSpans(log, span->coinPtTEnd()), false) {
1622 return;
1623 }
1624 }
1625 }
1626 } while ((span = span->next()));
1627 // this->restoreHead();
1628 return;
1629 }
1630
1631 /* Commented-out lines keep this in sync with addExpanded */
1632 // for each coincident pair, match the spans
1633 // if the spans don't match, add the mssing pt to the segment and loop it in the opposite span
debugAddExpanded(SkPathOpsDebug::GlitchLog * log) const1634 void SkOpCoincidence::debugAddExpanded(SkPathOpsDebug::GlitchLog* log) const {
1635 // DEBUG_SET_PHASE();
1636 const SkCoincidentSpans* coin = this->fHead;
1637 if (!coin) {
1638 return;
1639 }
1640 do {
1641 const SkOpPtT* startPtT = coin->coinPtTStart();
1642 const SkOpPtT* oStartPtT = coin->oppPtTStart();
1643 double priorT = startPtT->fT;
1644 double oPriorT = oStartPtT->fT;
1645 FAIL_IF(!startPtT->contains(oStartPtT), coin);
1646 SkOPASSERT(coin->coinPtTEnd()->contains(coin->oppPtTEnd()));
1647 const SkOpSpanBase* start = startPtT->span();
1648 const SkOpSpanBase* oStart = oStartPtT->span();
1649 const SkOpSpanBase* end = coin->coinPtTEnd()->span();
1650 const SkOpSpanBase* oEnd = coin->oppPtTEnd()->span();
1651 FAIL_IF(oEnd->deleted(), coin);
1652 FAIL_IF(!start->upCastable(), coin);
1653 const SkOpSpanBase* test = start->upCast()->next();
1654 FAIL_IF(!coin->flipped() && !oStart->upCastable(), coin);
1655 const SkOpSpanBase* oTest = coin->flipped() ? oStart->prev() : oStart->upCast()->next();
1656 FAIL_IF(!oTest, coin);
1657 const SkOpSegment* seg = start->segment();
1658 const SkOpSegment* oSeg = oStart->segment();
1659 while (test != end || oTest != oEnd) {
1660 const SkOpPtT* containedOpp = test->ptT()->contains(oSeg);
1661 const SkOpPtT* containedThis = oTest->ptT()->contains(seg);
1662 if (!containedOpp || !containedThis) {
1663 // choose the ends, or the first common pt-t list shared by both
1664 double nextT, oNextT;
1665 if (containedOpp) {
1666 nextT = test->t();
1667 oNextT = containedOpp->fT;
1668 } else if (containedThis) {
1669 nextT = containedThis->fT;
1670 oNextT = oTest->t();
1671 } else {
1672 // iterate through until a pt-t list found that contains the other
1673 const SkOpSpanBase* walk = test;
1674 const SkOpPtT* walkOpp;
1675 do {
1676 FAIL_IF(!walk->upCastable(), coin);
1677 walk = walk->upCast()->next();
1678 } while (!(walkOpp = walk->ptT()->contains(oSeg))
1679 && walk != coin->coinPtTEnd()->span());
1680 FAIL_IF(!walkOpp, coin);
1681 nextT = walk->t();
1682 oNextT = walkOpp->fT;
1683 }
1684 // use t ranges to guess which one is missing
1685 double startRange = nextT - priorT;
1686 FAIL_IF(!startRange, coin);
1687 double startPart = (test->t() - priorT) / startRange;
1688 double oStartRange = oNextT - oPriorT;
1689 FAIL_IF(!oStartRange, coin);
1690 double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange;
1691 FAIL_IF(startPart == oStartPart, coin);
1692 bool addToOpp = !containedOpp && !containedThis ? startPart < oStartPart
1693 : !!containedThis;
1694 bool startOver = false;
1695 addToOpp ? log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch,
1696 oPriorT + oStartRange * startPart, test)
1697 : log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch,
1698 priorT + startRange * oStartPart, oTest);
1699 // FAIL_IF(!success, coin);
1700 if (startOver) {
1701 test = start;
1702 oTest = oStart;
1703 }
1704 end = coin->coinPtTEnd()->span();
1705 oEnd = coin->oppPtTEnd()->span();
1706 }
1707 if (test != end) {
1708 FAIL_IF(!test->upCastable(), coin);
1709 priorT = test->t();
1710 test = test->upCast()->next();
1711 }
1712 if (oTest != oEnd) {
1713 oPriorT = oTest->t();
1714 oTest = coin->flipped() ? oTest->prev() : oTest->upCast()->next();
1715 FAIL_IF(!oTest, coin);
1716 }
1717 }
1718 } while ((coin = coin->next()));
1719 return;
1720 }
1721
1722 /* Commented-out lines keep this in sync addIfMissing() */
1723 // 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) const1724 void SkOpCoincidence::debugAddIfMissing(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* over1s, const SkOpPtT* over2s,
1725 double tStart, double tEnd, const SkOpSegment* coinSeg, const SkOpSegment* oppSeg, bool* added,
1726 const SkOpPtT* over1e, const SkOpPtT* over2e) const {
1727 SkASSERT(tStart < tEnd);
1728 SkASSERT(over1s->fT < over1e->fT);
1729 SkASSERT(between(over1s->fT, tStart, over1e->fT));
1730 SkASSERT(between(over1s->fT, tEnd, over1e->fT));
1731 SkASSERT(over2s->fT < over2e->fT);
1732 SkASSERT(between(over2s->fT, tStart, over2e->fT));
1733 SkASSERT(between(over2s->fT, tEnd, over2e->fT));
1734 SkASSERT(over1s->segment() == over1e->segment());
1735 SkASSERT(over2s->segment() == over2e->segment());
1736 SkASSERT(over1s->segment() == over2s->segment());
1737 SkASSERT(over1s->segment() != coinSeg);
1738 SkASSERT(over1s->segment() != oppSeg);
1739 SkASSERT(coinSeg != oppSeg);
1740 double coinTs, coinTe, oppTs, oppTe;
1741 coinTs = TRange(over1s, tStart, coinSeg SkDEBUGPARAMS(over1e));
1742 coinTe = TRange(over1s, tEnd, coinSeg SkDEBUGPARAMS(over1e));
1743 SkOpSpanBase::Collapsed result = coinSeg->collapsed(coinTs, coinTe);
1744 if (SkOpSpanBase::Collapsed::kNo != result) {
1745 return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, coinSeg);
1746 }
1747 oppTs = TRange(over2s, tStart, oppSeg SkDEBUGPARAMS(over2e));
1748 oppTe = TRange(over2s, tEnd, oppSeg SkDEBUGPARAMS(over2e));
1749 result = oppSeg->collapsed(oppTs, oppTe);
1750 if (SkOpSpanBase::Collapsed::kNo != result) {
1751 return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, oppSeg);
1752 }
1753 if (coinTs > coinTe) {
1754 using std::swap;
1755 swap(coinTs, coinTe);
1756 swap(oppTs, oppTe);
1757 }
1758 this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, added);
1759 return;
1760 }
1761
1762 /* Commented-out lines keep this in sync addOrOverlap() */
1763 // If this is called by addEndMovedSpans(), a returned false propogates out to an abort.
1764 // 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) const1765 void SkOpCoincidence::debugAddOrOverlap(SkPathOpsDebug::GlitchLog* log,
1766 const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
1767 double coinTs, double coinTe, double oppTs, double oppTe, bool* added) const {
1768 SkTDArray<SkCoincidentSpans*> overlaps;
1769 SkOPASSERT(!fTop); // this is (correctly) reversed in addifMissing()
1770 if (fTop && !this->checkOverlap(fTop, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe,
1771 &overlaps)) {
1772 return;
1773 }
1774 if (fHead && !this->checkOverlap(fHead, coinSeg, oppSeg, coinTs,
1775 coinTe, oppTs, oppTe, &overlaps)) {
1776 return;
1777 }
1778 const SkCoincidentSpans* overlap = overlaps.count() ? overlaps[0] : nullptr;
1779 for (int index = 1; index < overlaps.count(); ++index) { // combine overlaps before continuing
1780 const SkCoincidentSpans* test = overlaps[index];
1781 if (overlap->coinPtTStart()->fT > test->coinPtTStart()->fT) {
1782 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTStart());
1783 }
1784 if (overlap->coinPtTEnd()->fT < test->coinPtTEnd()->fT) {
1785 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTEnd());
1786 }
1787 if (overlap->flipped()
1788 ? overlap->oppPtTStart()->fT < test->oppPtTStart()->fT
1789 : overlap->oppPtTStart()->fT > test->oppPtTStart()->fT) {
1790 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTStart());
1791 }
1792 if (overlap->flipped()
1793 ? overlap->oppPtTEnd()->fT > test->oppPtTEnd()->fT
1794 : overlap->oppPtTEnd()->fT < test->oppPtTEnd()->fT) {
1795 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTEnd());
1796 }
1797 if (!fHead) { this->debugRelease(log, fHead, test);
1798 this->debugRelease(log, fTop, test);
1799 }
1800 }
1801 const SkOpPtT* cs = coinSeg->existing(coinTs, oppSeg);
1802 const SkOpPtT* ce = coinSeg->existing(coinTe, oppSeg);
1803 RETURN_FALSE_IF(overlap && cs && ce && overlap->contains(cs, ce), coinSeg);
1804 RETURN_FALSE_IF(cs != ce || !cs, coinSeg);
1805 const SkOpPtT* os = oppSeg->existing(oppTs, coinSeg);
1806 const SkOpPtT* oe = oppSeg->existing(oppTe, coinSeg);
1807 RETURN_FALSE_IF(overlap && os && oe && overlap->contains(os, oe), oppSeg);
1808 SkASSERT(true || !cs || !cs->deleted());
1809 SkASSERT(true || !os || !os->deleted());
1810 SkASSERT(true || !ce || !ce->deleted());
1811 SkASSERT(true || !oe || !oe->deleted());
1812 const SkOpPtT* csExisting = !cs ? coinSeg->existing(coinTs, nullptr) : nullptr;
1813 const SkOpPtT* ceExisting = !ce ? coinSeg->existing(coinTe, nullptr) : nullptr;
1814 RETURN_FALSE_IF(csExisting && csExisting == ceExisting, coinSeg);
1815 RETURN_FALSE_IF(csExisting && (csExisting == ce ||
1816 csExisting->contains(ceExisting ? ceExisting : ce)), coinSeg);
1817 RETURN_FALSE_IF(ceExisting && (ceExisting == cs ||
1818 ceExisting->contains(csExisting ? csExisting : cs)), coinSeg);
1819 const SkOpPtT* osExisting = !os ? oppSeg->existing(oppTs, nullptr) : nullptr;
1820 const SkOpPtT* oeExisting = !oe ? oppSeg->existing(oppTe, nullptr) : nullptr;
1821 RETURN_FALSE_IF(osExisting && osExisting == oeExisting, oppSeg);
1822 RETURN_FALSE_IF(osExisting && (osExisting == oe ||
1823 osExisting->contains(oeExisting ? oeExisting : oe)), oppSeg);
1824 RETURN_FALSE_IF(oeExisting && (oeExisting == os ||
1825 oeExisting->contains(osExisting ? osExisting : os)), oppSeg);
1826 bool csDeleted = false, osDeleted = false, ceDeleted = false, oeDeleted = false;
1827 this->debugValidate();
1828 if (!cs || !os) {
1829 if (!cs)
1830 cs = coinSeg->debugAddT(coinTs, log);
1831 if (!os)
1832 os = oppSeg->debugAddT(oppTs, log);
1833 // RETURN_FALSE_IF(callerAborts, !csWritable || !osWritable);
1834 if (cs && os) cs->span()->debugAddOpp(log, os->span());
1835 // cs = csWritable;
1836 // os = osWritable->active();
1837 RETURN_FALSE_IF((ce && ce->deleted()) || (oe && oe->deleted()), coinSeg);
1838 }
1839 if (!ce || !oe) {
1840 if (!ce)
1841 ce = coinSeg->debugAddT(coinTe, log);
1842 if (!oe)
1843 oe = oppSeg->debugAddT(oppTe, log);
1844 if (ce && oe) ce->span()->debugAddOpp(log, oe->span());
1845 // ce = ceWritable;
1846 // oe = oeWritable;
1847 }
1848 this->debugValidate();
1849 RETURN_FALSE_IF(csDeleted, coinSeg);
1850 RETURN_FALSE_IF(osDeleted, oppSeg);
1851 RETURN_FALSE_IF(ceDeleted, coinSeg);
1852 RETURN_FALSE_IF(oeDeleted, oppSeg);
1853 RETURN_FALSE_IF(!cs || !ce || cs == ce || cs->contains(ce) || !os || !oe || os == oe || os->contains(oe), coinSeg);
1854 bool result = true;
1855 if (overlap) {
1856 if (overlap->coinPtTStart()->segment() == coinSeg) {
1857 log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
1858 } else {
1859 if (oppTs > oppTe) {
1860 using std::swap;
1861 swap(coinTs, coinTe);
1862 swap(oppTs, oppTe);
1863 }
1864 log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, oppSeg, oppTs, oppTe, coinSeg, coinTs, coinTe);
1865 }
1866 #if 0 && DEBUG_COINCIDENCE_VERBOSE
1867 if (result) {
1868 overlap->debugShow();
1869 }
1870 #endif
1871 } else {
1872 log->record(SkPathOpsDebug::kAddMissingCoin_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
1873 #if 0 && DEBUG_COINCIDENCE_VERBOSE
1874 fHead->debugShow();
1875 #endif
1876 }
1877 this->debugValidate();
1878 return (void) result;
1879 }
1880
1881 // Extra commented-out lines keep this in sync with addMissing()
1882 /* detects overlaps of different coincident runs on same segment */
1883 /* does not detect overlaps for pairs without any segments in common */
1884 // returns true if caller should loop again
debugAddMissing(SkPathOpsDebug::GlitchLog * log,bool * added) const1885 void SkOpCoincidence::debugAddMissing(SkPathOpsDebug::GlitchLog* log, bool* added) const {
1886 const SkCoincidentSpans* outer = fHead;
1887 *added = false;
1888 if (!outer) {
1889 return;
1890 }
1891 // fTop = outer;
1892 // fHead = nullptr;
1893 do {
1894 // addifmissing can modify the list that this is walking
1895 // save head so that walker can iterate over old data unperturbed
1896 // addifmissing adds to head freely then add saved head in the end
1897 const SkOpPtT* ocs = outer->coinPtTStart();
1898 SkASSERT(!ocs->deleted());
1899 const SkOpSegment* outerCoin = ocs->segment();
1900 SkASSERT(!outerCoin->done()); // if it's done, should have already been removed from list
1901 const SkOpPtT* oos = outer->oppPtTStart();
1902 if (oos->deleted()) {
1903 return;
1904 }
1905 const SkOpSegment* outerOpp = oos->segment();
1906 SkASSERT(!outerOpp->done());
1907 // SkOpSegment* outerCoinWritable = const_cast<SkOpSegment*>(outerCoin);
1908 // SkOpSegment* outerOppWritable = const_cast<SkOpSegment*>(outerOpp);
1909 const SkCoincidentSpans* inner = outer;
1910 while ((inner = inner->next())) {
1911 this->debugValidate();
1912 double overS, overE;
1913 const SkOpPtT* ics = inner->coinPtTStart();
1914 SkASSERT(!ics->deleted());
1915 const SkOpSegment* innerCoin = ics->segment();
1916 SkASSERT(!innerCoin->done());
1917 const SkOpPtT* ios = inner->oppPtTStart();
1918 SkASSERT(!ios->deleted());
1919 const SkOpSegment* innerOpp = ios->segment();
1920 SkASSERT(!innerOpp->done());
1921 // SkOpSegment* innerCoinWritable = const_cast<SkOpSegment*>(innerCoin);
1922 // SkOpSegment* innerOppWritable = const_cast<SkOpSegment*>(innerOpp);
1923 if (outerCoin == innerCoin) {
1924 const SkOpPtT* oce = outer->coinPtTEnd();
1925 if (oce->deleted()) {
1926 return;
1927 }
1928 const SkOpPtT* ice = inner->coinPtTEnd();
1929 SkASSERT(!ice->deleted());
1930 if (outerOpp != innerOpp && this->overlap(ocs, oce, ics, ice, &overS, &overE)) {
1931 this->debugAddIfMissing(log, ocs->starter(oce), ics->starter(ice),
1932 overS, overE, outerOpp, innerOpp, added,
1933 ocs->debugEnder(oce),
1934 ics->debugEnder(ice));
1935 }
1936 } else if (outerCoin == innerOpp) {
1937 const SkOpPtT* oce = outer->coinPtTEnd();
1938 SkASSERT(!oce->deleted());
1939 const SkOpPtT* ioe = inner->oppPtTEnd();
1940 SkASSERT(!ioe->deleted());
1941 if (outerOpp != innerCoin && this->overlap(ocs, oce, ios, ioe, &overS, &overE)) {
1942 this->debugAddIfMissing(log, ocs->starter(oce), ios->starter(ioe),
1943 overS, overE, outerOpp, innerCoin, added,
1944 ocs->debugEnder(oce),
1945 ios->debugEnder(ioe));
1946 }
1947 } else if (outerOpp == innerCoin) {
1948 const SkOpPtT* ooe = outer->oppPtTEnd();
1949 SkASSERT(!ooe->deleted());
1950 const SkOpPtT* ice = inner->coinPtTEnd();
1951 SkASSERT(!ice->deleted());
1952 SkASSERT(outerCoin != innerOpp);
1953 if (this->overlap(oos, ooe, ics, ice, &overS, &overE)) {
1954 this->debugAddIfMissing(log, oos->starter(ooe), ics->starter(ice),
1955 overS, overE, outerCoin, innerOpp, added,
1956 oos->debugEnder(ooe),
1957 ics->debugEnder(ice));
1958 }
1959 } else if (outerOpp == innerOpp) {
1960 const SkOpPtT* ooe = outer->oppPtTEnd();
1961 SkASSERT(!ooe->deleted());
1962 const SkOpPtT* ioe = inner->oppPtTEnd();
1963 if (ioe->deleted()) {
1964 return;
1965 }
1966 SkASSERT(outerCoin != innerCoin);
1967 if (this->overlap(oos, ooe, ios, ioe, &overS, &overE)) {
1968 this->debugAddIfMissing(log, oos->starter(ooe), ios->starter(ioe),
1969 overS, overE, outerCoin, innerCoin, added,
1970 oos->debugEnder(ooe),
1971 ios->debugEnder(ioe));
1972 }
1973 }
1974 this->debugValidate();
1975 }
1976 } while ((outer = outer->next()));
1977 // this->restoreHead();
1978 return;
1979 }
1980
1981 // Commented-out lines keep this in sync with release()
debugRelease(SkPathOpsDebug::GlitchLog * log,const SkCoincidentSpans * coin,const SkCoincidentSpans * remove) const1982 void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkCoincidentSpans* remove) const {
1983 const SkCoincidentSpans* head = coin;
1984 const SkCoincidentSpans* prev = nullptr;
1985 const SkCoincidentSpans* next;
1986 do {
1987 next = coin->next();
1988 if (coin == remove) {
1989 if (prev) {
1990 // prev->setNext(next);
1991 } else if (head == fHead) {
1992 // fHead = next;
1993 } else {
1994 // fTop = next;
1995 }
1996 log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
1997 }
1998 prev = coin;
1999 } while ((coin = next));
2000 return;
2001 }
2002
debugRelease(SkPathOpsDebug::GlitchLog * log,const SkOpSegment * deleted) const2003 void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* deleted) const {
2004 const SkCoincidentSpans* coin = fHead;
2005 if (!coin) {
2006 return;
2007 }
2008 do {
2009 if (coin->coinPtTStart()->segment() == deleted
2010 || coin->coinPtTEnd()->segment() == deleted
2011 || coin->oppPtTStart()->segment() == deleted
2012 || coin->oppPtTEnd()->segment() == deleted) {
2013 log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
2014 }
2015 } while ((coin = coin->next()));
2016 }
2017
2018 // Commented-out lines keep this in sync with expand()
2019 // expand the range by checking adjacent spans for coincidence
debugExpand(SkPathOpsDebug::GlitchLog * log) const2020 bool SkOpCoincidence::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
2021 const SkCoincidentSpans* coin = fHead;
2022 if (!coin) {
2023 return false;
2024 }
2025 bool expanded = false;
2026 do {
2027 if (coin->debugExpand(log)) {
2028 // check to see if multiple spans expanded so they are now identical
2029 const SkCoincidentSpans* test = fHead;
2030 do {
2031 if (coin == test) {
2032 continue;
2033 }
2034 if (coin->coinPtTStart() == test->coinPtTStart()
2035 && coin->oppPtTStart() == test->oppPtTStart()) {
2036 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, fHead, test->coinPtTStart());
2037 break;
2038 }
2039 } while ((test = test->next()));
2040 expanded = true;
2041 }
2042 } while ((coin = coin->next()));
2043 return expanded;
2044 }
2045
2046 // Commented-out lines keep this in sync with mark()
2047 /* this sets up the coincidence links in the segments when the coincidence crosses multiple spans */
debugMark(SkPathOpsDebug::GlitchLog * log) const2048 void SkOpCoincidence::debugMark(SkPathOpsDebug::GlitchLog* log) const {
2049 const SkCoincidentSpans* coin = fHead;
2050 if (!coin) {
2051 return;
2052 }
2053 do {
2054 FAIL_IF(!coin->coinPtTStartWritable()->span()->upCastable(), coin);
2055 const SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast();
2056 // SkASSERT(start->deleted());
2057 const SkOpSpanBase* end = coin->coinPtTEndWritable()->span();
2058 // SkASSERT(end->deleted());
2059 const SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span();
2060 // SkASSERT(oStart->deleted());
2061 const SkOpSpanBase* oEnd = coin->oppPtTEndWritable()->span();
2062 // SkASSERT(oEnd->deleted());
2063 bool flipped = coin->flipped();
2064 if (flipped) {
2065 using std::swap;
2066 swap(oStart, oEnd);
2067 }
2068 /* coin and opp spans may not match up. Mark the ends, and then let the interior
2069 get marked as many times as the spans allow */
2070 start->debugInsertCoincidence(log, oStart->upCast());
2071 end->debugInsertCoinEnd(log, oEnd);
2072 const SkOpSegment* segment = start->segment();
2073 const SkOpSegment* oSegment = oStart->segment();
2074 const SkOpSpanBase* next = start;
2075 const SkOpSpanBase* oNext = oStart;
2076 bool ordered;
2077 FAIL_IF(!coin->ordered(&ordered), coin);
2078 while ((next = next->upCast()->next()) != end) {
2079 FAIL_IF(!next->upCastable(), coin);
2080 if (next->upCast()->debugInsertCoincidence(log, oSegment, flipped, ordered), false) {
2081 return;
2082 }
2083 }
2084 while ((oNext = oNext->upCast()->next()) != oEnd) {
2085 FAIL_IF(!oNext->upCastable(), coin);
2086 if (oNext->upCast()->debugInsertCoincidence(log, segment, flipped, ordered), false) {
2087 return;
2088 }
2089 }
2090 } while ((coin = coin->next()));
2091 return;
2092 }
2093 #endif
2094
2095 #if DEBUG_COIN
2096 // Commented-out lines keep this in sync with markCollapsed()
debugMarkCollapsed(SkPathOpsDebug::GlitchLog * log,const SkCoincidentSpans * coin,const SkOpPtT * test) const2097 void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkOpPtT* test) const {
2098 const SkCoincidentSpans* head = coin;
2099 while (coin) {
2100 if (coin->collapsed(test)) {
2101 if (zero_or_one(coin->coinPtTStart()->fT) && zero_or_one(coin->coinPtTEnd()->fT)) {
2102 log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
2103 }
2104 if (zero_or_one(coin->oppPtTStart()->fT) && zero_or_one(coin->oppPtTEnd()->fT)) {
2105 log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
2106 }
2107 this->debugRelease(log, head, coin);
2108 }
2109 coin = coin->next();
2110 }
2111 }
2112
2113 // Commented-out lines keep this in sync with markCollapsed()
debugMarkCollapsed(SkPathOpsDebug::GlitchLog * log,const SkOpPtT * test) const2114 void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* test) const {
2115 this->debugMarkCollapsed(log, fHead, test);
2116 this->debugMarkCollapsed(log, fTop, test);
2117 }
2118 #endif
2119
debugShow() const2120 void SkCoincidentSpans::debugShow() const {
2121 SkDebugf("coinSpan - id=%d t=%1.9g tEnd=%1.9g\n", coinPtTStart()->segment()->debugID(),
2122 coinPtTStart()->fT, coinPtTEnd()->fT);
2123 SkDebugf("coinSpan + id=%d t=%1.9g tEnd=%1.9g\n", oppPtTStart()->segment()->debugID(),
2124 oppPtTStart()->fT, oppPtTEnd()->fT);
2125 }
2126
debugShowCoincidence() const2127 void SkOpCoincidence::debugShowCoincidence() const {
2128 #if DEBUG_COINCIDENCE
2129 const SkCoincidentSpans* span = fHead;
2130 while (span) {
2131 span->debugShow();
2132 span = span->next();
2133 }
2134 #endif
2135 }
2136
2137 #if DEBUG_COIN
DebugCheckBetween(const SkOpSpanBase * next,const SkOpSpanBase * end,double oStart,double oEnd,const SkOpSegment * oSegment,SkPathOpsDebug::GlitchLog * log)2138 static void DebugCheckBetween(const SkOpSpanBase* next, const SkOpSpanBase* end,
2139 double oStart, double oEnd, const SkOpSegment* oSegment,
2140 SkPathOpsDebug::GlitchLog* log) {
2141 SkASSERT(next != end);
2142 SkASSERT(!next->contains(end) || log);
2143 if (next->t() > end->t()) {
2144 using std::swap;
2145 swap(next, end);
2146 }
2147 do {
2148 const SkOpPtT* ptT = next->ptT();
2149 int index = 0;
2150 bool somethingBetween = false;
2151 do {
2152 ++index;
2153 ptT = ptT->next();
2154 const SkOpPtT* checkPtT = next->ptT();
2155 if (ptT == checkPtT) {
2156 break;
2157 }
2158 bool looped = false;
2159 for (int check = 0; check < index; ++check) {
2160 if ((looped = checkPtT == ptT)) {
2161 break;
2162 }
2163 checkPtT = checkPtT->next();
2164 }
2165 if (looped) {
2166 SkASSERT(0);
2167 break;
2168 }
2169 if (ptT->deleted()) {
2170 continue;
2171 }
2172 if (ptT->segment() != oSegment) {
2173 continue;
2174 }
2175 somethingBetween |= between(oStart, ptT->fT, oEnd);
2176 } while (true);
2177 SkASSERT(somethingBetween);
2178 } while (next != end && (next = next->upCast()->next()));
2179 }
2180
DebugCheckOverlap(const SkCoincidentSpans * test,const SkCoincidentSpans * list,SkPathOpsDebug::GlitchLog * log)2181 static void DebugCheckOverlap(const SkCoincidentSpans* test, const SkCoincidentSpans* list,
2182 SkPathOpsDebug::GlitchLog* log) {
2183 if (!list) {
2184 return;
2185 }
2186 const SkOpSegment* coinSeg = test->coinPtTStart()->segment();
2187 SkASSERT(coinSeg == test->coinPtTEnd()->segment());
2188 const SkOpSegment* oppSeg = test->oppPtTStart()->segment();
2189 SkASSERT(oppSeg == test->oppPtTEnd()->segment());
2190 SkASSERT(coinSeg != test->oppPtTStart()->segment());
2191 SkDEBUGCODE(double tcs = test->coinPtTStart()->fT);
2192 SkASSERT(between(0, tcs, 1));
2193 SkDEBUGCODE(double tce = test->coinPtTEnd()->fT);
2194 SkASSERT(between(0, tce, 1));
2195 SkASSERT(tcs < tce);
2196 double tos = test->oppPtTStart()->fT;
2197 SkASSERT(between(0, tos, 1));
2198 double toe = test->oppPtTEnd()->fT;
2199 SkASSERT(between(0, toe, 1));
2200 SkASSERT(tos != toe);
2201 if (tos > toe) {
2202 using std::swap;
2203 swap(tos, toe);
2204 }
2205 do {
2206 double lcs, lce, los, loe;
2207 if (coinSeg == list->coinPtTStart()->segment()) {
2208 if (oppSeg != list->oppPtTStart()->segment()) {
2209 continue;
2210 }
2211 lcs = list->coinPtTStart()->fT;
2212 lce = list->coinPtTEnd()->fT;
2213 los = list->oppPtTStart()->fT;
2214 loe = list->oppPtTEnd()->fT;
2215 if (los > loe) {
2216 using std::swap;
2217 swap(los, loe);
2218 }
2219 } else if (coinSeg == list->oppPtTStart()->segment()) {
2220 if (oppSeg != list->coinPtTStart()->segment()) {
2221 continue;
2222 }
2223 lcs = list->oppPtTStart()->fT;
2224 lce = list->oppPtTEnd()->fT;
2225 if (lcs > lce) {
2226 using std::swap;
2227 swap(lcs, lce);
2228 }
2229 los = list->coinPtTStart()->fT;
2230 loe = list->coinPtTEnd()->fT;
2231 } else {
2232 continue;
2233 }
2234 SkASSERT(tce < lcs || lce < tcs);
2235 SkASSERT(toe < los || loe < tos);
2236 } while ((list = list->next()));
2237 }
2238
2239
DebugCheckOverlapTop(const SkCoincidentSpans * head,const SkCoincidentSpans * opt,SkPathOpsDebug::GlitchLog * log)2240 static void DebugCheckOverlapTop(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
2241 SkPathOpsDebug::GlitchLog* log) {
2242 // check for overlapping coincident spans
2243 const SkCoincidentSpans* test = head;
2244 while (test) {
2245 const SkCoincidentSpans* next = test->next();
2246 DebugCheckOverlap(test, next, log);
2247 DebugCheckOverlap(test, opt, log);
2248 test = next;
2249 }
2250 }
2251
DebugValidate(const SkCoincidentSpans * head,const SkCoincidentSpans * opt,SkPathOpsDebug::GlitchLog * log)2252 static void DebugValidate(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
2253 SkPathOpsDebug::GlitchLog* log) {
2254 // look for pts inside coincident spans that are not inside the opposite spans
2255 const SkCoincidentSpans* coin = head;
2256 while (coin) {
2257 SkASSERT(SkOpCoincidence::Ordered(coin->coinPtTStart()->segment(),
2258 coin->oppPtTStart()->segment()));
2259 SkASSERT(coin->coinPtTStart()->span()->ptT() == coin->coinPtTStart());
2260 SkASSERT(coin->coinPtTEnd()->span()->ptT() == coin->coinPtTEnd());
2261 SkASSERT(coin->oppPtTStart()->span()->ptT() == coin->oppPtTStart());
2262 SkASSERT(coin->oppPtTEnd()->span()->ptT() == coin->oppPtTEnd());
2263 coin = coin->next();
2264 }
2265 DebugCheckOverlapTop(head, opt, log);
2266 }
2267 #endif
2268
debugValidate() const2269 void SkOpCoincidence::debugValidate() const {
2270 #if DEBUG_COINCIDENCE
2271 DebugValidate(fHead, fTop, nullptr);
2272 DebugValidate(fTop, nullptr, nullptr);
2273 #endif
2274 }
2275
2276 #if DEBUG_COIN
DebugCheckBetween(const SkCoincidentSpans * head,const SkCoincidentSpans * opt,SkPathOpsDebug::GlitchLog * log)2277 static void DebugCheckBetween(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
2278 SkPathOpsDebug::GlitchLog* log) {
2279 // look for pts inside coincident spans that are not inside the opposite spans
2280 const SkCoincidentSpans* coin = head;
2281 while (coin) {
2282 DebugCheckBetween(coin->coinPtTStart()->span(), coin->coinPtTEnd()->span(),
2283 coin->oppPtTStart()->fT, coin->oppPtTEnd()->fT, coin->oppPtTStart()->segment(),
2284 log);
2285 DebugCheckBetween(coin->oppPtTStart()->span(), coin->oppPtTEnd()->span(),
2286 coin->coinPtTStart()->fT, coin->coinPtTEnd()->fT, coin->coinPtTStart()->segment(),
2287 log);
2288 coin = coin->next();
2289 }
2290 DebugCheckOverlapTop(head, opt, log);
2291 }
2292 #endif
2293
debugCheckBetween() const2294 void SkOpCoincidence::debugCheckBetween() const {
2295 #if DEBUG_COINCIDENCE
2296 if (fGlobalState->debugCheckHealth()) {
2297 return;
2298 }
2299 DebugCheckBetween(fHead, fTop, nullptr);
2300 DebugCheckBetween(fTop, nullptr, nullptr);
2301 #endif
2302 }
2303
2304 #if DEBUG_COIN
debugCheckHealth(SkPathOpsDebug::GlitchLog * log) const2305 void SkOpContour::debugCheckHealth(SkPathOpsDebug::GlitchLog* log) const {
2306 const SkOpSegment* segment = &fHead;
2307 do {
2308 segment->debugCheckHealth(log);
2309 } while ((segment = segment->next()));
2310 }
2311
debugCheckValid(SkPathOpsDebug::GlitchLog * log) const2312 void SkOpCoincidence::debugCheckValid(SkPathOpsDebug::GlitchLog* log) const {
2313 #if DEBUG_VALIDATE
2314 DebugValidate(fHead, fTop, log);
2315 DebugValidate(fTop, nullptr, log);
2316 #endif
2317 }
2318
debugCorrectEnds(SkPathOpsDebug::GlitchLog * log) const2319 void SkOpCoincidence::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
2320 const SkCoincidentSpans* coin = fHead;
2321 if (!coin) {
2322 return;
2323 }
2324 do {
2325 coin->debugCorrectEnds(log);
2326 } while ((coin = coin->next()));
2327 }
2328
2329 // commmented-out lines keep this aligned with missingCoincidence()
debugMissingCoincidence(SkPathOpsDebug::GlitchLog * log) const2330 void SkOpContour::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const {
2331 // SkASSERT(fCount > 0);
2332 const SkOpSegment* segment = &fHead;
2333 // bool result = false;
2334 do {
2335 if (segment->debugMissingCoincidence(log), false) {
2336 // result = true;
2337 }
2338 segment = segment->next();
2339 } while (segment);
2340 return;
2341 }
2342
debugMoveMultiples(SkPathOpsDebug::GlitchLog * log) const2343 void SkOpContour::debugMoveMultiples(SkPathOpsDebug::GlitchLog* log) const {
2344 SkASSERT(fCount > 0);
2345 const SkOpSegment* segment = &fHead;
2346 do {
2347 if (segment->debugMoveMultiples(log), false) {
2348 return;
2349 }
2350 } while ((segment = segment->next()));
2351 return;
2352 }
2353
debugMoveNearby(SkPathOpsDebug::GlitchLog * log) const2354 void SkOpContour::debugMoveNearby(SkPathOpsDebug::GlitchLog* log) const {
2355 SkASSERT(fCount > 0);
2356 const SkOpSegment* segment = &fHead;
2357 do {
2358 segment->debugMoveNearby(log);
2359 } while ((segment = segment->next()));
2360 }
2361 #endif
2362
2363 #if DEBUG_COINCIDENCE_ORDER
debugResetCoinT() const2364 void SkOpSegment::debugResetCoinT() const {
2365 fDebugBaseIndex = -1;
2366 fDebugBaseMin = 1;
2367 fDebugBaseMax = -1;
2368 fDebugLastIndex = -1;
2369 fDebugLastMin = 1;
2370 fDebugLastMax = -1;
2371 }
2372 #endif
2373
debugValidate() const2374 void SkOpSegment::debugValidate() const {
2375 #if DEBUG_COINCIDENCE_ORDER
2376 {
2377 const SkOpSpanBase* span = &fHead;
2378 do {
2379 span->debugResetCoinT();
2380 } while (!span->final() && (span = span->upCast()->next()));
2381 span = &fHead;
2382 int index = 0;
2383 do {
2384 span->debugSetCoinT(index++);
2385 } while (!span->final() && (span = span->upCast()->next()));
2386 }
2387 #endif
2388 #if DEBUG_COINCIDENCE
2389 if (this->globalState()->debugCheckHealth()) {
2390 return;
2391 }
2392 #endif
2393 #if DEBUG_VALIDATE
2394 const SkOpSpanBase* span = &fHead;
2395 double lastT = -1;
2396 const SkOpSpanBase* prev = nullptr;
2397 int count = 0;
2398 int done = 0;
2399 do {
2400 if (!span->final()) {
2401 ++count;
2402 done += span->upCast()->done() ? 1 : 0;
2403 }
2404 SkASSERT(span->segment() == this);
2405 SkASSERT(!prev || prev->upCast()->next() == span);
2406 SkASSERT(!prev || prev == span->prev());
2407 prev = span;
2408 double t = span->ptT()->fT;
2409 SkASSERT(lastT < t);
2410 lastT = t;
2411 span->debugValidate();
2412 } while (!span->final() && (span = span->upCast()->next()));
2413 SkASSERT(count == fCount);
2414 SkASSERT(done == fDoneCount);
2415 SkASSERT(count >= fDoneCount);
2416 SkASSERT(span->final());
2417 span->debugValidate();
2418 #endif
2419 }
2420
2421 #if DEBUG_COIN
2422
2423 // Commented-out lines keep this in sync with addOpp()
debugAddOpp(SkPathOpsDebug::GlitchLog * log,const SkOpSpanBase * opp) const2424 void SkOpSpanBase::debugAddOpp(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
2425 const SkOpPtT* oppPrev = this->ptT()->oppPrev(opp->ptT());
2426 if (!oppPrev) {
2427 return;
2428 }
2429 this->debugMergeMatches(log, opp);
2430 this->ptT()->debugAddOpp(opp->ptT(), oppPrev);
2431 this->debugCheckForCollapsedCoincidence(log);
2432 }
2433
2434 // Commented-out lines keep this in sync with checkForCollapsedCoincidence()
debugCheckForCollapsedCoincidence(SkPathOpsDebug::GlitchLog * log) const2435 void SkOpSpanBase::debugCheckForCollapsedCoincidence(SkPathOpsDebug::GlitchLog* log) const {
2436 const SkOpCoincidence* coins = this->globalState()->coincidence();
2437 if (coins->isEmpty()) {
2438 return;
2439 }
2440 // the insert above may have put both ends of a coincident run in the same span
2441 // for each coincident ptT in loop; see if its opposite in is also in the loop
2442 // this implementation is the motivation for marking that a ptT is referenced by a coincident span
2443 const SkOpPtT* head = this->ptT();
2444 const SkOpPtT* test = head;
2445 do {
2446 if (!test->coincident()) {
2447 continue;
2448 }
2449 coins->debugMarkCollapsed(log, test);
2450 } while ((test = test->next()) != head);
2451 }
2452 #endif
2453
debugCoinEndLoopCheck() const2454 bool SkOpSpanBase::debugCoinEndLoopCheck() const {
2455 int loop = 0;
2456 const SkOpSpanBase* next = this;
2457 SkOpSpanBase* nextCoin;
2458 do {
2459 nextCoin = next->fCoinEnd;
2460 SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin);
2461 for (int check = 1; check < loop - 1; ++check) {
2462 const SkOpSpanBase* checkCoin = this->fCoinEnd;
2463 const SkOpSpanBase* innerCoin = checkCoin;
2464 for (int inner = check + 1; inner < loop; ++inner) {
2465 innerCoin = innerCoin->fCoinEnd;
2466 if (checkCoin == innerCoin) {
2467 SkDebugf("*** bad coincident end loop ***\n");
2468 return false;
2469 }
2470 }
2471 }
2472 ++loop;
2473 } while ((next = nextCoin) && next != this);
2474 return true;
2475 }
2476
2477 #if DEBUG_COIN
2478 // Commented-out lines keep this in sync with insertCoinEnd()
debugInsertCoinEnd(SkPathOpsDebug::GlitchLog * log,const SkOpSpanBase * coin) const2479 void SkOpSpanBase::debugInsertCoinEnd(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* coin) const {
2480 if (containsCoinEnd(coin)) {
2481 // SkASSERT(coin->containsCoinEnd(this));
2482 return;
2483 }
2484 debugValidate();
2485 // SkASSERT(this != coin);
2486 log->record(SkPathOpsDebug::kMarkCoinEnd_Glitch, this, coin);
2487 // coin->fCoinEnd = this->fCoinEnd;
2488 // this->fCoinEnd = coinNext;
2489 debugValidate();
2490 }
2491
2492 // Commented-out lines keep this in sync with mergeMatches()
2493 // Look to see if pt-t linked list contains same segment more than once
2494 // if so, and if each pt-t is directly pointed to by spans in that segment,
2495 // merge them
2496 // keep the points, but remove spans so that the segment doesn't have 2 or more
2497 // spans pointing to the same pt-t loop at different loop elements
debugMergeMatches(SkPathOpsDebug::GlitchLog * log,const SkOpSpanBase * opp) const2498 void SkOpSpanBase::debugMergeMatches(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
2499 const SkOpPtT* test = &fPtT;
2500 const SkOpPtT* testNext;
2501 const SkOpPtT* stop = test;
2502 do {
2503 testNext = test->next();
2504 if (test->deleted()) {
2505 continue;
2506 }
2507 const SkOpSpanBase* testBase = test->span();
2508 SkASSERT(testBase->ptT() == test);
2509 const SkOpSegment* segment = test->segment();
2510 if (segment->done()) {
2511 continue;
2512 }
2513 const SkOpPtT* inner = opp->ptT();
2514 const SkOpPtT* innerStop = inner;
2515 do {
2516 if (inner->segment() != segment) {
2517 continue;
2518 }
2519 if (inner->deleted()) {
2520 continue;
2521 }
2522 const SkOpSpanBase* innerBase = inner->span();
2523 SkASSERT(innerBase->ptT() == inner);
2524 // when the intersection is first detected, the span base is marked if there are
2525 // more than one point in the intersection.
2526 // if (!innerBase->hasMultipleHint() && !testBase->hasMultipleHint()) {
2527 if (!zero_or_one(inner->fT)) {
2528 log->record(SkPathOpsDebug::kMergeMatches_Glitch, innerBase, test);
2529 } else {
2530 SkASSERT(inner->fT != test->fT);
2531 if (!zero_or_one(test->fT)) {
2532 log->record(SkPathOpsDebug::kMergeMatches_Glitch, testBase, inner);
2533 } else {
2534 log->record(SkPathOpsDebug::kMergeMatches_Glitch, segment);
2535 // SkDEBUGCODE(testBase->debugSetDeleted());
2536 // test->setDeleted();
2537 // SkDEBUGCODE(innerBase->debugSetDeleted());
2538 // inner->setDeleted();
2539 }
2540 }
2541 #ifdef SK_DEBUG // assert if another undeleted entry points to segment
2542 const SkOpPtT* debugInner = inner;
2543 while ((debugInner = debugInner->next()) != innerStop) {
2544 if (debugInner->segment() != segment) {
2545 continue;
2546 }
2547 if (debugInner->deleted()) {
2548 continue;
2549 }
2550 SkOPASSERT(0);
2551 }
2552 #endif
2553 break;
2554 // }
2555 break;
2556 } while ((inner = inner->next()) != innerStop);
2557 } while ((test = testNext) != stop);
2558 this->debugCheckForCollapsedCoincidence(log);
2559 }
2560
2561 #endif
2562
debugResetCoinT() const2563 void SkOpSpanBase::debugResetCoinT() const {
2564 #if DEBUG_COINCIDENCE_ORDER
2565 const SkOpPtT* ptT = &fPtT;
2566 do {
2567 ptT->debugResetCoinT();
2568 ptT = ptT->next();
2569 } while (ptT != &fPtT);
2570 #endif
2571 }
2572
debugSetCoinT(int index) const2573 void SkOpSpanBase::debugSetCoinT(int index) const {
2574 #if DEBUG_COINCIDENCE_ORDER
2575 const SkOpPtT* ptT = &fPtT;
2576 do {
2577 if (!ptT->deleted()) {
2578 ptT->debugSetCoinT(index);
2579 }
2580 ptT = ptT->next();
2581 } while (ptT != &fPtT);
2582 #endif
2583 }
2584
debugStarter(SkOpSpanBase const ** endPtr) const2585 const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const {
2586 const SkOpSpanBase* end = *endPtr;
2587 SkASSERT(this->segment() == end->segment());
2588 const SkOpSpanBase* result;
2589 if (t() < end->t()) {
2590 result = this;
2591 } else {
2592 result = end;
2593 *endPtr = this;
2594 }
2595 return result->upCast();
2596 }
2597
debugValidate() const2598 void SkOpSpanBase::debugValidate() const {
2599 #if DEBUG_COINCIDENCE
2600 if (this->globalState()->debugCheckHealth()) {
2601 return;
2602 }
2603 #endif
2604 #if DEBUG_VALIDATE
2605 const SkOpPtT* ptT = &fPtT;
2606 SkASSERT(ptT->span() == this);
2607 do {
2608 // SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt));
2609 ptT->debugValidate();
2610 ptT = ptT->next();
2611 } while (ptT != &fPtT);
2612 SkASSERT(this->debugCoinEndLoopCheck());
2613 if (!this->final()) {
2614 SkASSERT(this->upCast()->debugCoinLoopCheck());
2615 }
2616 if (fFromAngle) {
2617 fFromAngle->debugValidate();
2618 }
2619 if (!this->final() && this->upCast()->toAngle()) {
2620 this->upCast()->toAngle()->debugValidate();
2621 }
2622 #endif
2623 }
2624
debugCoinLoopCheck() const2625 bool SkOpSpan::debugCoinLoopCheck() const {
2626 int loop = 0;
2627 const SkOpSpan* next = this;
2628 SkOpSpan* nextCoin;
2629 do {
2630 nextCoin = next->fCoincident;
2631 SkASSERT(nextCoin == this || nextCoin->fCoincident != nextCoin);
2632 for (int check = 1; check < loop - 1; ++check) {
2633 const SkOpSpan* checkCoin = this->fCoincident;
2634 const SkOpSpan* innerCoin = checkCoin;
2635 for (int inner = check + 1; inner < loop; ++inner) {
2636 innerCoin = innerCoin->fCoincident;
2637 if (checkCoin == innerCoin) {
2638 SkDebugf("*** bad coincident loop ***\n");
2639 return false;
2640 }
2641 }
2642 }
2643 ++loop;
2644 } while ((next = nextCoin) && next != this);
2645 return true;
2646 }
2647
2648 #if DEBUG_COIN
2649 // Commented-out lines keep this in sync with insertCoincidence() in header
debugInsertCoincidence(SkPathOpsDebug::GlitchLog * log,const SkOpSpan * coin) const2650 void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* coin) const {
2651 if (containsCoincidence(coin)) {
2652 // SkASSERT(coin->containsCoincidence(this));
2653 return;
2654 }
2655 debugValidate();
2656 // SkASSERT(this != coin);
2657 log->record(SkPathOpsDebug::kMarkCoinStart_Glitch, this, coin);
2658 // coin->fCoincident = this->fCoincident;
2659 // this->fCoincident = coinNext;
2660 debugValidate();
2661 }
2662
2663 // Commented-out lines keep this in sync with insertCoincidence()
debugInsertCoincidence(SkPathOpsDebug::GlitchLog * log,const SkOpSegment * segment,bool flipped,bool ordered) const2664 void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* segment, bool flipped, bool ordered) const {
2665 if (this->containsCoincidence(segment)) {
2666 return;
2667 }
2668 const SkOpPtT* next = &fPtT;
2669 while ((next = next->next()) != &fPtT) {
2670 if (next->segment() == segment) {
2671 const SkOpSpan* span;
2672 const SkOpSpanBase* base = next->span();
2673 if (!ordered) {
2674 const SkOpSpanBase* spanEnd = fNext->contains(segment)->span();
2675 const SkOpPtT* start = base->ptT()->starter(spanEnd->ptT());
2676 FAIL_IF(!start->span()->upCastable(), this);
2677 span = const_cast<SkOpSpan*>(start->span()->upCast());
2678 }
2679 else if (flipped) {
2680 span = base->prev();
2681 FAIL_IF(!span, this);
2682 }
2683 else {
2684 FAIL_IF(!base->upCastable(), this);
2685 span = base->upCast();
2686 }
2687 log->record(SkPathOpsDebug::kMarkCoinInsert_Glitch, span);
2688 return;
2689 }
2690 }
2691 #if DEBUG_COIN
2692 log->record(SkPathOpsDebug::kMarkCoinMissing_Glitch, segment, this);
2693 #endif
2694 return;
2695 }
2696 #endif
2697
2698 // called only by test code
debugCoincidentUsed() const2699 int SkIntersections::debugCoincidentUsed() const {
2700 if (!fIsCoincident[0]) {
2701 SkASSERT(!fIsCoincident[1]);
2702 return 0;
2703 }
2704 int count = 0;
2705 SkDEBUGCODE(int count2 = 0;)
2706 for (int index = 0; index < fUsed; ++index) {
2707 if (fIsCoincident[0] & (1 << index)) {
2708 ++count;
2709 }
2710 #ifdef SK_DEBUG
2711 if (fIsCoincident[1] & (1 << index)) {
2712 ++count2;
2713 }
2714 #endif
2715 }
2716 SkASSERT(count == count2);
2717 return count;
2718 }
2719
2720 #include "SkOpContour.h"
2721
2722 // Commented-out lines keep this in sync with addOpp()
debugAddOpp(const SkOpPtT * opp,const SkOpPtT * oppPrev) const2723 void SkOpPtT::debugAddOpp(const SkOpPtT* opp, const SkOpPtT* oppPrev) const {
2724 SkDEBUGCODE(const SkOpPtT* oldNext = this->fNext);
2725 SkASSERT(this != opp);
2726 // this->fNext = opp;
2727 SkASSERT(oppPrev != oldNext);
2728 // oppPrev->fNext = oldNext;
2729 }
2730
debugContains(const SkOpPtT * check) const2731 bool SkOpPtT::debugContains(const SkOpPtT* check) const {
2732 SkASSERT(this != check);
2733 const SkOpPtT* ptT = this;
2734 int links = 0;
2735 do {
2736 ptT = ptT->next();
2737 if (ptT == check) {
2738 return true;
2739 }
2740 ++links;
2741 const SkOpPtT* test = this;
2742 for (int index = 0; index < links; ++index) {
2743 if (ptT == test) {
2744 return false;
2745 }
2746 test = test->next();
2747 }
2748 } while (true);
2749 }
2750
debugContains(const SkOpSegment * check) const2751 const SkOpPtT* SkOpPtT::debugContains(const SkOpSegment* check) const {
2752 SkASSERT(this->segment() != check);
2753 const SkOpPtT* ptT = this;
2754 int links = 0;
2755 do {
2756 ptT = ptT->next();
2757 if (ptT->segment() == check) {
2758 return ptT;
2759 }
2760 ++links;
2761 const SkOpPtT* test = this;
2762 for (int index = 0; index < links; ++index) {
2763 if (ptT == test) {
2764 return nullptr;
2765 }
2766 test = test->next();
2767 }
2768 } while (true);
2769 }
2770
debugEnder(const SkOpPtT * end) const2771 const SkOpPtT* SkOpPtT::debugEnder(const SkOpPtT* end) const {
2772 return fT < end->fT ? end : this;
2773 }
2774
debugLoopLimit(bool report) const2775 int SkOpPtT::debugLoopLimit(bool report) const {
2776 int loop = 0;
2777 const SkOpPtT* next = this;
2778 do {
2779 for (int check = 1; check < loop - 1; ++check) {
2780 const SkOpPtT* checkPtT = this->fNext;
2781 const SkOpPtT* innerPtT = checkPtT;
2782 for (int inner = check + 1; inner < loop; ++inner) {
2783 innerPtT = innerPtT->fNext;
2784 if (checkPtT == innerPtT) {
2785 if (report) {
2786 SkDebugf("*** bad ptT loop ***\n");
2787 }
2788 return loop;
2789 }
2790 }
2791 }
2792 // there's nothing wrong with extremely large loop counts -- but this may appear to hang
2793 // by taking a very long time to figure out that no loop entry is a duplicate
2794 // -- and it's likely that a large loop count is indicative of a bug somewhere
2795 if (++loop > 1000) {
2796 SkDebugf("*** loop count exceeds 1000 ***\n");
2797 return 1000;
2798 }
2799 } while ((next = next->fNext) && next != this);
2800 return 0;
2801 }
2802
debugOppPrev(const SkOpPtT * opp) const2803 const SkOpPtT* SkOpPtT::debugOppPrev(const SkOpPtT* opp) const {
2804 return this->oppPrev(const_cast<SkOpPtT*>(opp));
2805 }
2806
debugResetCoinT() const2807 void SkOpPtT::debugResetCoinT() const {
2808 #if DEBUG_COINCIDENCE_ORDER
2809 this->segment()->debugResetCoinT();
2810 #endif
2811 }
2812
debugSetCoinT(int index) const2813 void SkOpPtT::debugSetCoinT(int index) const {
2814 #if DEBUG_COINCIDENCE_ORDER
2815 this->segment()->debugSetCoinT(index, fT);
2816 #endif
2817 }
2818
debugValidate() const2819 void SkOpPtT::debugValidate() const {
2820 #if DEBUG_COINCIDENCE
2821 if (this->globalState()->debugCheckHealth()) {
2822 return;
2823 }
2824 #endif
2825 #if DEBUG_VALIDATE
2826 SkOpPhase phase = contour()->globalState()->phase();
2827 if (phase == SkOpPhase::kIntersecting || phase == SkOpPhase::kFixWinding) {
2828 return;
2829 }
2830 SkASSERT(fNext);
2831 SkASSERT(fNext != this);
2832 SkASSERT(fNext->fNext);
2833 SkASSERT(debugLoopLimit(false) == 0);
2834 #endif
2835 }
2836
output_scalar(SkScalar num)2837 static void output_scalar(SkScalar num) {
2838 if (num == (int) num) {
2839 SkDebugf("%d", (int) num);
2840 } else {
2841 SkString str;
2842 str.printf("%1.9g", num);
2843 int width = (int) str.size();
2844 const char* cStr = str.c_str();
2845 while (cStr[width - 1] == '0') {
2846 --width;
2847 }
2848 str.resize(width);
2849 SkDebugf("%sf", str.c_str());
2850 }
2851 }
2852
output_points(const SkPoint * pts,int count)2853 static void output_points(const SkPoint* pts, int count) {
2854 for (int index = 0; index < count; ++index) {
2855 output_scalar(pts[index].fX);
2856 SkDebugf(", ");
2857 output_scalar(pts[index].fY);
2858 if (index + 1 < count) {
2859 SkDebugf(", ");
2860 }
2861 }
2862 }
2863
showPathContours(SkPath::RawIter & iter,const char * pathName)2864 static void showPathContours(SkPath::RawIter& iter, const char* pathName) {
2865 uint8_t verb;
2866 SkPoint pts[4];
2867 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
2868 switch (verb) {
2869 case SkPath::kMove_Verb:
2870 SkDebugf(" %s.moveTo(", pathName);
2871 output_points(&pts[0], 1);
2872 SkDebugf(");\n");
2873 continue;
2874 case SkPath::kLine_Verb:
2875 SkDebugf(" %s.lineTo(", pathName);
2876 output_points(&pts[1], 1);
2877 SkDebugf(");\n");
2878 break;
2879 case SkPath::kQuad_Verb:
2880 SkDebugf(" %s.quadTo(", pathName);
2881 output_points(&pts[1], 2);
2882 SkDebugf(");\n");
2883 break;
2884 case SkPath::kConic_Verb:
2885 SkDebugf(" %s.conicTo(", pathName);
2886 output_points(&pts[1], 2);
2887 SkDebugf(", %1.9gf);\n", iter.conicWeight());
2888 break;
2889 case SkPath::kCubic_Verb:
2890 SkDebugf(" %s.cubicTo(", pathName);
2891 output_points(&pts[1], 3);
2892 SkDebugf(");\n");
2893 break;
2894 case SkPath::kClose_Verb:
2895 SkDebugf(" %s.close();\n", pathName);
2896 break;
2897 default:
2898 SkDEBUGFAIL("bad verb");
2899 return;
2900 }
2901 }
2902 }
2903
2904 static const char* gFillTypeStr[] = {
2905 "kWinding_FillType",
2906 "kEvenOdd_FillType",
2907 "kInverseWinding_FillType",
2908 "kInverseEvenOdd_FillType"
2909 };
2910
ShowOnePath(const SkPath & path,const char * name,bool includeDeclaration)2911 void SkPathOpsDebug::ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration) {
2912 SkPath::RawIter iter(path);
2913 #define SUPPORT_RECT_CONTOUR_DETECTION 0
2914 #if SUPPORT_RECT_CONTOUR_DETECTION
2915 int rectCount = path.isRectContours() ? path.rectContours(nullptr, nullptr) : 0;
2916 if (rectCount > 0) {
2917 SkTDArray<SkRect> rects;
2918 SkTDArray<SkPath::Direction> directions;
2919 rects.setCount(rectCount);
2920 directions.setCount(rectCount);
2921 path.rectContours(rects.begin(), directions.begin());
2922 for (int contour = 0; contour < rectCount; ++contour) {
2923 const SkRect& rect = rects[contour];
2924 SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
2925 rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
2926 ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
2927 }
2928 return;
2929 }
2930 #endif
2931 SkPath::FillType fillType = path.getFillType();
2932 SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType);
2933 if (includeDeclaration) {
2934 SkDebugf(" SkPath %s;\n", name);
2935 }
2936 SkDebugf(" %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[fillType]);
2937 iter.setPath(path);
2938 showPathContours(iter, name);
2939 }
2940
2941 #if DEBUG_DUMP_VERIFY
2942 #include "SkData.h"
2943 #include "SkStream.h"
2944
dump_path(FILE * file,const SkPath & path,bool force,bool dumpAsHex)2945 static void dump_path(FILE* file, const SkPath& path, bool force, bool dumpAsHex) {
2946 SkDynamicMemoryWStream wStream;
2947 path.dump(&wStream, force, dumpAsHex);
2948 sk_sp<SkData> data(wStream.detachAsData());
2949 fprintf(file, "%.*s\n", (int) data->size(), (char*) data->data());
2950 }
2951
2952 static int dumpID = 0;
2953
DumpOp(const SkPath & one,const SkPath & two,SkPathOp op,const char * testName)2954 void SkPathOpsDebug::DumpOp(const SkPath& one, const SkPath& two, SkPathOp op,
2955 const char* testName) {
2956 FILE* file = sk_fopen("op_dump.txt", kWrite_SkFILE_Flag);
2957 DumpOp(file, one, two, op, testName);
2958 }
2959
DumpOp(FILE * file,const SkPath & one,const SkPath & two,SkPathOp op,const char * testName)2960 void SkPathOpsDebug::DumpOp(FILE* file, const SkPath& one, const SkPath& two, SkPathOp op,
2961 const char* testName) {
2962 const char* name = testName ? testName : "op";
2963 fprintf(file,
2964 "\nstatic void %s_%d(skiatest::Reporter* reporter, const char* filename) {\n",
2965 name, ++dumpID);
2966 fprintf(file, " SkPath path;\n");
2967 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", one.getFillType());
2968 dump_path(file, one, false, true);
2969 fprintf(file, " SkPath path1(path);\n");
2970 fprintf(file, " path.reset();\n");
2971 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", two.getFillType());
2972 dump_path(file, two, false, true);
2973 fprintf(file, " SkPath path2(path);\n");
2974 fprintf(file, " testPathOp(reporter, path1, path2, (SkPathOp) %d, filename);\n", op);
2975 fprintf(file, "}\n\n");
2976 fclose(file);
2977 }
2978
DumpSimplify(const SkPath & path,const char * testName)2979 void SkPathOpsDebug::DumpSimplify(const SkPath& path, const char* testName) {
2980 FILE* file = sk_fopen("simplify_dump.txt", kWrite_SkFILE_Flag);
2981 DumpSimplify(file, path, testName);
2982 }
2983
DumpSimplify(FILE * file,const SkPath & path,const char * testName)2984 void SkPathOpsDebug::DumpSimplify(FILE* file, const SkPath& path, const char* testName) {
2985 const char* name = testName ? testName : "simplify";
2986 fprintf(file,
2987 "\nstatic void %s_%d(skiatest::Reporter* reporter, const char* filename) {\n",
2988 name, ++dumpID);
2989 fprintf(file, " SkPath path;\n");
2990 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", path.getFillType());
2991 dump_path(file, path, false, true);
2992 fprintf(file, " testSimplify(reporter, path, filename);\n");
2993 fprintf(file, "}\n\n");
2994 fclose(file);
2995 }
2996
2997 #include "SkBitmap.h"
2998 #include "SkCanvas.h"
2999 #include "SkPaint.h"
3000
3001 const int bitWidth = 64;
3002 const int bitHeight = 64;
3003
debug_scale_matrix(const SkPath & one,const SkPath * two,SkMatrix & scale)3004 static void debug_scale_matrix(const SkPath& one, const SkPath* two, SkMatrix& scale) {
3005 SkRect larger = one.getBounds();
3006 if (two) {
3007 larger.join(two->getBounds());
3008 }
3009 SkScalar largerWidth = larger.width();
3010 if (largerWidth < 4) {
3011 largerWidth = 4;
3012 }
3013 SkScalar largerHeight = larger.height();
3014 if (largerHeight < 4) {
3015 largerHeight = 4;
3016 }
3017 SkScalar hScale = (bitWidth - 2) / largerWidth;
3018 SkScalar vScale = (bitHeight - 2) / largerHeight;
3019 scale.reset();
3020 scale.preScale(hScale, vScale);
3021 larger.fLeft *= hScale;
3022 larger.fRight *= hScale;
3023 larger.fTop *= vScale;
3024 larger.fBottom *= vScale;
3025 SkScalar dx = -16000 > larger.fLeft ? -16000 - larger.fLeft
3026 : 16000 < larger.fRight ? 16000 - larger.fRight : 0;
3027 SkScalar dy = -16000 > larger.fTop ? -16000 - larger.fTop
3028 : 16000 < larger.fBottom ? 16000 - larger.fBottom : 0;
3029 scale.preTranslate(dx, dy);
3030 }
3031
debug_paths_draw_the_same(const SkPath & one,const SkPath & two,SkBitmap & bits)3032 static int debug_paths_draw_the_same(const SkPath& one, const SkPath& two, SkBitmap& bits) {
3033 if (bits.width() == 0) {
3034 bits.allocN32Pixels(bitWidth * 2, bitHeight);
3035 }
3036 SkCanvas canvas(bits);
3037 canvas.drawColor(SK_ColorWHITE);
3038 SkPaint paint;
3039 canvas.save();
3040 const SkRect& bounds1 = one.getBounds();
3041 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
3042 canvas.drawPath(one, paint);
3043 canvas.restore();
3044 canvas.save();
3045 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
3046 canvas.drawPath(two, paint);
3047 canvas.restore();
3048 int errors = 0;
3049 for (int y = 0; y < bitHeight - 1; ++y) {
3050 uint32_t* addr1 = bits.getAddr32(0, y);
3051 uint32_t* addr2 = bits.getAddr32(0, y + 1);
3052 uint32_t* addr3 = bits.getAddr32(bitWidth, y);
3053 uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1);
3054 for (int x = 0; x < bitWidth - 1; ++x) {
3055 // count 2x2 blocks
3056 bool err = addr1[x] != addr3[x];
3057 if (err) {
3058 errors += addr1[x + 1] != addr3[x + 1]
3059 && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1];
3060 }
3061 }
3062 }
3063 return errors;
3064 }
3065
ReportOpFail(const SkPath & one,const SkPath & two,SkPathOp op)3066 void SkPathOpsDebug::ReportOpFail(const SkPath& one, const SkPath& two, SkPathOp op) {
3067 SkDebugf("// Op did not expect failure\n");
3068 DumpOp(stderr, one, two, op, "opTest");
3069 fflush(stderr);
3070 }
3071
VerifyOp(const SkPath & one,const SkPath & two,SkPathOp op,const SkPath & result)3072 void SkPathOpsDebug::VerifyOp(const SkPath& one, const SkPath& two, SkPathOp op,
3073 const SkPath& result) {
3074 SkPath pathOut, scaledPathOut;
3075 SkRegion rgnA, rgnB, openClip, rgnOut;
3076 openClip.setRect(-16000, -16000, 16000, 16000);
3077 rgnA.setPath(one, openClip);
3078 rgnB.setPath(two, openClip);
3079 rgnOut.op(rgnA, rgnB, (SkRegion::Op) op);
3080 rgnOut.getBoundaryPath(&pathOut);
3081 SkMatrix scale;
3082 debug_scale_matrix(one, &two, scale);
3083 SkRegion scaledRgnA, scaledRgnB, scaledRgnOut;
3084 SkPath scaledA, scaledB;
3085 scaledA.addPath(one, scale);
3086 scaledA.setFillType(one.getFillType());
3087 scaledB.addPath(two, scale);
3088 scaledB.setFillType(two.getFillType());
3089 scaledRgnA.setPath(scaledA, openClip);
3090 scaledRgnB.setPath(scaledB, openClip);
3091 scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) op);
3092 scaledRgnOut.getBoundaryPath(&scaledPathOut);
3093 SkBitmap bitmap;
3094 SkPath scaledOut;
3095 scaledOut.addPath(result, scale);
3096 scaledOut.setFillType(result.getFillType());
3097 int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
3098 const int MAX_ERRORS = 9;
3099 if (errors > MAX_ERRORS) {
3100 fprintf(stderr, "// Op did not expect errors=%d\n", errors);
3101 DumpOp(stderr, one, two, op, "opTest");
3102 fflush(stderr);
3103 }
3104 }
3105
ReportSimplifyFail(const SkPath & path)3106 void SkPathOpsDebug::ReportSimplifyFail(const SkPath& path) {
3107 SkDebugf("// Simplify did not expect failure\n");
3108 DumpSimplify(stderr, path, "simplifyTest");
3109 fflush(stderr);
3110 }
3111
VerifySimplify(const SkPath & path,const SkPath & result)3112 void SkPathOpsDebug::VerifySimplify(const SkPath& path, const SkPath& result) {
3113 SkPath pathOut, scaledPathOut;
3114 SkRegion rgnA, openClip, rgnOut;
3115 openClip.setRect(-16000, -16000, 16000, 16000);
3116 rgnA.setPath(path, openClip);
3117 rgnOut.getBoundaryPath(&pathOut);
3118 SkMatrix scale;
3119 debug_scale_matrix(path, nullptr, scale);
3120 SkRegion scaledRgnA;
3121 SkPath scaledA;
3122 scaledA.addPath(path, scale);
3123 scaledA.setFillType(path.getFillType());
3124 scaledRgnA.setPath(scaledA, openClip);
3125 scaledRgnA.getBoundaryPath(&scaledPathOut);
3126 SkBitmap bitmap;
3127 SkPath scaledOut;
3128 scaledOut.addPath(result, scale);
3129 scaledOut.setFillType(result.getFillType());
3130 int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
3131 const int MAX_ERRORS = 9;
3132 if (errors > MAX_ERRORS) {
3133 fprintf(stderr, "// Simplify did not expect errors=%d\n", errors);
3134 DumpSimplify(stderr, path, "simplifyTest");
3135 fflush(stderr);
3136 }
3137 }
3138
3139 #endif
3140
3141 // global path dumps for msvs Visual Studio 17 to use from Immediate Window
Dump(const SkPath & path)3142 void Dump(const SkPath& path) {
3143 path.dump();
3144 }
3145
DumpHex(const SkPath & path)3146 void DumpHex(const SkPath& path) {
3147 path.dumpHex();
3148 }
3149