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