blob: da4e76b750e61777ba105ec714ff25edd707e248 [file] [log] [blame]
caryclark@google.com07393ca2013-04-08 11:47:37 +00001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkPath.h"
9#include "include/core/SkString.h"
10#include "include/private/SkMutex.h"
11#include "src/core/SkOSFile.h"
12#include "src/pathops/SkOpCoincidence.h"
13#include "src/pathops/SkOpContour.h"
14#include "src/pathops/SkPathOpsDebug.h"
caryclark54359292015-03-26 07:52:43 -070015
Ben Wagnerf08d1d02018-06-18 15:11:00 -040016#include <utility>
17
Cary Clark918fb1f2016-11-15 13:22:25 -050018#if DEBUG_DUMP_VERIFY
caryclark13260682016-10-24 05:10:14 -070019bool SkPathOpsDebug::gDumpOp; // set to true to write op to file before a crash
20bool SkPathOpsDebug::gVerifyOp; // set to true to compare result against regions
21#endif
22
Cary Clark59d5a0e2017-01-23 14:38:52 +000023bool SkPathOpsDebug::gRunFail; // set to true to check for success on tests known to fail
caryclark13260682016-10-24 05:10:14 -070024bool SkPathOpsDebug::gVeryVerbose; // set to true to run extensive checking tests
25
caryclark30b9fdd2016-08-31 14:36:29 -070026#undef FAIL_IF
27#define FAIL_IF(cond, coin) \
Cary Clarkab87d7a2016-10-04 10:01:04 -040028 do { if (cond) log->record(SkPathOpsDebug::kFail_Glitch, coin); } while (false)
caryclark30b9fdd2016-08-31 14:36:29 -070029
30#undef FAIL_WITH_NULL_IF
31#define FAIL_WITH_NULL_IF(cond, span) \
Cary Clarkab87d7a2016-10-04 10:01:04 -040032 do { if (cond) log->record(SkPathOpsDebug::kFail_Glitch, span); } while (false)
caryclark30b9fdd2016-08-31 14:36:29 -070033
34#undef RETURN_FALSE_IF
35#define RETURN_FALSE_IF(cond, span) \
Cary Clarkab87d7a2016-10-04 10:01:04 -040036 do { if (cond) log->record(SkPathOpsDebug::kReturnFalse_Glitch, span); \
37 } while (false)
caryclark30b9fdd2016-08-31 14:36:29 -070038
caryclark55888e42016-07-18 10:01:36 -070039class SkCoincidentSpans;
caryclark26ad22a2015-10-16 09:03:38 -070040
caryclark624637c2015-05-11 07:21:27 -070041#if DEBUG_SORT
42int SkPathOpsDebug::gSortCountDefault = SK_MaxS32;
43int SkPathOpsDebug::gSortCount;
44#endif
45
46#if DEBUG_ACTIVE_OP
Ben Wagner3f985522017-10-09 10:47:47 -040047const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor", "rdiff"};
caryclark624637c2015-05-11 07:21:27 -070048#endif
49
caryclark@google.com07393ca2013-04-08 11:47:37 +000050#if defined SK_DEBUG || !FORCE_RELEASE
51
commit-bot@chromium.orgfe41b8f2014-04-25 14:03:52 +000052int SkPathOpsDebug::gContourID = 0;
53int SkPathOpsDebug::gSegmentID = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +000054
caryclark54359292015-03-26 07:52:43 -070055bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray,
56 const SkOpSpanBase* span) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000057 for (int index = 0; index < chaseArray.count(); ++index) {
caryclark54359292015-03-26 07:52:43 -070058 const SkOpSpanBase* entry = chaseArray[index];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000059 if (entry == span) {
60 return true;
61 }
62 }
63 return false;
64}
caryclark26ad22a2015-10-16 09:03:38 -070065#endif
Hal Canaryd4998172018-07-11 11:31:34 -040066
Ben Wagner29380bd2017-10-09 14:43:00 -040067#if DEBUG_ACTIVE_SPANS
68SkString SkPathOpsDebug::gActiveSpans;
Cary Clarkff114282016-12-14 11:56:16 -050069#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000070
Cary Clarkab87d7a2016-10-04 10:01:04 -040071#if DEBUG_COIN
caryclark26ad22a2015-10-16 09:03:38 -070072
Cary Clarkab87d7a2016-10-04 10:01:04 -040073SkPathOpsDebug::CoinDict SkPathOpsDebug::gCoinSumChangedDict;
74SkPathOpsDebug::CoinDict SkPathOpsDebug::gCoinSumVisitedDict;
75
76static const int kGlitchType_Count = SkPathOpsDebug::kUnalignedTail_Glitch + 1;
caryclark26ad22a2015-10-16 09:03:38 -070077
78struct SpanGlitch {
caryclark26ad22a2015-10-16 09:03:38 -070079 const SkOpSpanBase* fBase;
80 const SkOpSpanBase* fSuspect;
caryclark26ad22a2015-10-16 09:03:38 -070081 const SkOpSegment* fSegment;
caryclark55888e42016-07-18 10:01:36 -070082 const SkOpSegment* fOppSegment;
caryclark26ad22a2015-10-16 09:03:38 -070083 const SkOpPtT* fCoinSpan;
84 const SkOpPtT* fEndSpan;
85 const SkOpPtT* fOppSpan;
86 const SkOpPtT* fOppEndSpan;
caryclark55888e42016-07-18 10:01:36 -070087 double fStartT;
88 double fEndT;
89 double fOppStartT;
90 double fOppEndT;
caryclark26ad22a2015-10-16 09:03:38 -070091 SkPoint fPt;
Cary Clarkab87d7a2016-10-04 10:01:04 -040092 SkPathOpsDebug::GlitchType fType;
93
94 void dumpType() const;
caryclark26ad22a2015-10-16 09:03:38 -070095};
96
97struct SkPathOpsDebug::GlitchLog {
Cary Clarkab87d7a2016-10-04 10:01:04 -040098 void init(const SkOpGlobalState* state) {
99 fGlobalState = state;
100 }
101
102 SpanGlitch* recordCommon(GlitchType type) {
Cary Clarkea2a6322018-08-27 13:19:09 -0400103 SpanGlitch* glitch = fGlitches.push();
caryclark26ad22a2015-10-16 09:03:38 -0700104 glitch->fBase = nullptr;
105 glitch->fSuspect = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -0700106 glitch->fSegment = nullptr;
caryclark55888e42016-07-18 10:01:36 -0700107 glitch->fOppSegment = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -0700108 glitch->fCoinSpan = nullptr;
109 glitch->fEndSpan = nullptr;
110 glitch->fOppSpan = nullptr;
111 glitch->fOppEndSpan = nullptr;
caryclark55888e42016-07-18 10:01:36 -0700112 glitch->fStartT = SK_ScalarNaN;
113 glitch->fEndT = SK_ScalarNaN;
114 glitch->fOppStartT = SK_ScalarNaN;
115 glitch->fOppEndT = SK_ScalarNaN;
caryclark26ad22a2015-10-16 09:03:38 -0700116 glitch->fPt = { SK_ScalarNaN, SK_ScalarNaN };
117 glitch->fType = type;
118 return glitch;
119 }
120
Cary Clarkab87d7a2016-10-04 10:01:04 -0400121 void record(GlitchType type, const SkOpSpanBase* base,
caryclark26ad22a2015-10-16 09:03:38 -0700122 const SkOpSpanBase* suspect = NULL) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400123 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700124 glitch->fBase = base;
125 glitch->fSuspect = suspect;
126 }
127
Cary Clarkab87d7a2016-10-04 10:01:04 -0400128 void record(GlitchType type, const SkOpSpanBase* base,
caryclark55888e42016-07-18 10:01:36 -0700129 const SkOpPtT* ptT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400130 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700131 glitch->fBase = base;
132 glitch->fCoinSpan = ptT;
133 }
134
Cary Clarkab87d7a2016-10-04 10:01:04 -0400135 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark55888e42016-07-18 10:01:36 -0700136 const SkCoincidentSpans* opp = NULL) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400137 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700138 glitch->fCoinSpan = coin->coinPtTStart();
139 glitch->fEndSpan = coin->coinPtTEnd();
140 if (opp) {
141 glitch->fOppSpan = opp->coinPtTStart();
142 glitch->fOppEndSpan = opp->coinPtTEnd();
143 }
caryclark26ad22a2015-10-16 09:03:38 -0700144 }
145
Cary Clarkab87d7a2016-10-04 10:01:04 -0400146 void record(GlitchType type, const SkOpSpanBase* base,
caryclark26ad22a2015-10-16 09:03:38 -0700147 const SkOpSegment* seg, double t, SkPoint pt) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400148 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700149 glitch->fBase = base;
150 glitch->fSegment = seg;
caryclark55888e42016-07-18 10:01:36 -0700151 glitch->fStartT = t;
caryclark26ad22a2015-10-16 09:03:38 -0700152 glitch->fPt = pt;
153 }
154
Cary Clarkab87d7a2016-10-04 10:01:04 -0400155 void record(GlitchType type, const SkOpSpanBase* base, double t,
caryclark26ad22a2015-10-16 09:03:38 -0700156 SkPoint pt) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400157 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700158 glitch->fBase = base;
caryclark55888e42016-07-18 10:01:36 -0700159 glitch->fStartT = t;
caryclark26ad22a2015-10-16 09:03:38 -0700160 glitch->fPt = pt;
161 }
162
Cary Clarkab87d7a2016-10-04 10:01:04 -0400163 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark26ad22a2015-10-16 09:03:38 -0700164 const SkOpPtT* coinSpan, const SkOpPtT* endSpan) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400165 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700166 glitch->fCoinSpan = coin->coinPtTStart();
167 glitch->fEndSpan = coin->coinPtTEnd();
caryclark26ad22a2015-10-16 09:03:38 -0700168 glitch->fEndSpan = endSpan;
caryclark55888e42016-07-18 10:01:36 -0700169 glitch->fOppSpan = coinSpan;
170 glitch->fOppEndSpan = endSpan;
caryclark26ad22a2015-10-16 09:03:38 -0700171 }
172
Cary Clarkab87d7a2016-10-04 10:01:04 -0400173 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark55888e42016-07-18 10:01:36 -0700174 const SkOpSpanBase* base) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400175 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700176 glitch->fBase = base;
177 glitch->fCoinSpan = coin->coinPtTStart();
178 glitch->fEndSpan = coin->coinPtTEnd();
caryclark26ad22a2015-10-16 09:03:38 -0700179 }
180
Cary Clarkab87d7a2016-10-04 10:01:04 -0400181 void record(GlitchType type, const SkOpPtT* ptTS, const SkOpPtT* ptTE,
caryclark26ad22a2015-10-16 09:03:38 -0700182 const SkOpPtT* oPtTS, const SkOpPtT* oPtTE) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400183 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700184 glitch->fCoinSpan = ptTS;
185 glitch->fEndSpan = ptTE;
186 glitch->fOppSpan = oPtTS;
187 glitch->fOppEndSpan = oPtTE;
188 }
189
Cary Clarkab87d7a2016-10-04 10:01:04 -0400190 void record(GlitchType type, const SkOpSegment* seg, double startT,
caryclark55888e42016-07-18 10:01:36 -0700191 double endT, const SkOpSegment* oppSeg, double oppStartT, double oppEndT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400192 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700193 glitch->fSegment = seg;
194 glitch->fStartT = startT;
195 glitch->fEndT = endT;
196 glitch->fOppSegment = oppSeg;
197 glitch->fOppStartT = oppStartT;
198 glitch->fOppEndT = oppEndT;
199 }
200
Cary Clarkab87d7a2016-10-04 10:01:04 -0400201 void record(GlitchType type, const SkOpSegment* seg,
caryclark55888e42016-07-18 10:01:36 -0700202 const SkOpSpan* span) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400203 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700204 glitch->fSegment = seg;
205 glitch->fBase = span;
206 }
207
Cary Clarkab87d7a2016-10-04 10:01:04 -0400208 void record(GlitchType type, double t, const SkOpSpanBase* span) {
209 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700210 glitch->fStartT = t;
211 glitch->fBase = span;
212 }
213
Cary Clarkab87d7a2016-10-04 10:01:04 -0400214 void record(GlitchType type, const SkOpSegment* seg) {
215 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700216 glitch->fSegment = seg;
217 }
218
Cary Clarkab87d7a2016-10-04 10:01:04 -0400219 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark55888e42016-07-18 10:01:36 -0700220 const SkOpPtT* ptT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400221 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700222 glitch->fCoinSpan = coin->coinPtTStart();
223 glitch->fEndSpan = ptT;
224 }
225
caryclark26ad22a2015-10-16 09:03:38 -0700226 SkTDArray<SpanGlitch> fGlitches;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400227 const SkOpGlobalState* fGlobalState;
caryclark26ad22a2015-10-16 09:03:38 -0700228};
Cary Clarkab87d7a2016-10-04 10:01:04 -0400229
230
231void SkPathOpsDebug::CoinDict::add(const SkPathOpsDebug::CoinDict& dict) {
232 int count = dict.fDict.count();
233 for (int index = 0; index < count; ++index) {
234 this->add(dict.fDict[index]);
235 }
236}
237
238void SkPathOpsDebug::CoinDict::add(const CoinDictEntry& key) {
239 int count = fDict.count();
240 for (int index = 0; index < count; ++index) {
241 CoinDictEntry* entry = &fDict[index];
242 if (entry->fIteration == key.fIteration && entry->fLineNumber == key.fLineNumber) {
243 SkASSERT(!strcmp(entry->fFunctionName, key.fFunctionName));
244 if (entry->fGlitchType == kUninitialized_Glitch) {
245 entry->fGlitchType = key.fGlitchType;
246 }
247 return;
248 }
249 }
250 *fDict.append() = key;
251}
252
253#endif
254
255#if DEBUG_COIN
256static void missing_coincidence(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) {
257 const SkOpContour* contour = contourList;
258 // bool result = false;
259 do {
260 /* result |= */ contour->debugMissingCoincidence(glitches);
261 } while ((contour = contour->next()));
262 return;
263}
264
265static void move_multiples(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) {
266 const SkOpContour* contour = contourList;
267 do {
268 if (contour->debugMoveMultiples(glitches), false) {
269 return;
270 }
271 } while ((contour = contour->next()));
272 return;
273}
274
275static void move_nearby(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) {
276 const SkOpContour* contour = contourList;
277 do {
278 contour->debugMoveNearby(glitches);
279 } while ((contour = contour->next()));
280}
281
282
283#endif
284
285#if DEBUG_COIN
286void SkOpGlobalState::debugAddToCoinChangedDict() {
287
288#if DEBUG_COINCIDENCE
caryclarke6522ea2016-10-17 07:54:33 -0700289 SkPathOpsDebug::CheckHealth(fContourHead);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400290#endif
291 // see if next coincident operation makes a change; if so, record it
292 SkPathOpsDebug::GlitchLog glitches;
293 const char* funcName = fCoinDictEntry.fFunctionName;
294 if (!strcmp("calc_angles", funcName)) {
295 ;
296 } else if (!strcmp("missing_coincidence", funcName)) {
297 missing_coincidence(&glitches, fContourHead);
298 } else if (!strcmp("move_multiples", funcName)) {
299 move_multiples(&glitches, fContourHead);
300 } else if (!strcmp("move_nearby", funcName)) {
301 move_nearby(&glitches, fContourHead);
302 } else if (!strcmp("addExpanded", funcName)) {
303 fCoincidence->debugAddExpanded(&glitches);
304 } else if (!strcmp("addMissing", funcName)) {
305 bool added;
306 fCoincidence->debugAddMissing(&glitches, &added);
307 } else if (!strcmp("addEndMovedSpans", funcName)) {
308 fCoincidence->debugAddEndMovedSpans(&glitches);
309 } else if (!strcmp("correctEnds", funcName)) {
310 fCoincidence->debugCorrectEnds(&glitches);
311 } else if (!strcmp("expand", funcName)) {
312 fCoincidence->debugExpand(&glitches);
313 } else if (!strcmp("findOverlaps", funcName)) {
314 ;
315 } else if (!strcmp("mark", funcName)) {
316 fCoincidence->debugMark(&glitches);
317 } else if (!strcmp("apply", funcName)) {
318 ;
319 } else {
320 SkASSERT(0); // add missing case
321 }
322 if (glitches.fGlitches.count()) {
323 fCoinDictEntry.fGlitchType = glitches.fGlitches[0].fType;
324 }
325 fCoinChangedDict.add(fCoinDictEntry);
326}
caryclark55888e42016-07-18 10:01:36 -0700327#endif
caryclark26ad22a2015-10-16 09:03:38 -0700328
caryclark55888e42016-07-18 10:01:36 -0700329void SkPathOpsDebug::ShowActiveSpans(SkOpContourHead* contourList) {
330#if DEBUG_ACTIVE_SPANS
Cary Clarkff114282016-12-14 11:56:16 -0500331 SkString str;
caryclark55888e42016-07-18 10:01:36 -0700332 SkOpContour* contour = contourList;
333 do {
Cary Clarkff114282016-12-14 11:56:16 -0500334 contour->debugShowActiveSpans(&str);
caryclark55888e42016-07-18 10:01:36 -0700335 } while ((contour = contour->next()));
Ben Wagner29380bd2017-10-09 14:43:00 -0400336 if (!gActiveSpans.equals(str)) {
337 const char* s = str.c_str();
338 const char* end;
339 while ((end = strchr(s, '\n'))) {
340 SkDebugf("%.*s", end - s + 1, s);
341 s = end + 1;
342 }
343 gActiveSpans.set(str);
344 }
caryclark55888e42016-07-18 10:01:36 -0700345#endif
346}
347
Cary Clarkab87d7a2016-10-04 10:01:04 -0400348#if DEBUG_COINCIDENCE || DEBUG_COIN
349void SkPathOpsDebug::CheckHealth(SkOpContourHead* contourList) {
caryclark55888e42016-07-18 10:01:36 -0700350#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -0700351 contourList->globalState()->debugSetCheckHealth(true);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400352#endif
353#if DEBUG_COIN
caryclark26ad22a2015-10-16 09:03:38 -0700354 GlitchLog glitches;
355 const SkOpContour* contour = contourList;
356 const SkOpCoincidence* coincidence = contour->globalState()->coincidence();
Cary Clarkab87d7a2016-10-04 10:01:04 -0400357 coincidence->debugCheckValid(&glitches); // don't call validate; spans may be inconsistent
caryclark26ad22a2015-10-16 09:03:38 -0700358 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400359 contour->debugCheckHealth(&glitches);
360 contour->debugMissingCoincidence(&glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700361 } while ((contour = contour->next()));
caryclark81a478c2016-09-09 09:37:57 -0700362 bool added;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400363 coincidence->debugAddMissing(&glitches, &added);
364 coincidence->debugExpand(&glitches);
365 coincidence->debugAddExpanded(&glitches);
366 coincidence->debugMark(&glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700367 unsigned mask = 0;
368 for (int index = 0; index < glitches.fGlitches.count(); ++index) {
369 const SpanGlitch& glitch = glitches.fGlitches[index];
370 mask |= 1 << glitch.fType;
371 }
372 for (int index = 0; index < kGlitchType_Count; ++index) {
373 SkDebugf(mask & (1 << index) ? "x" : "-");
374 }
Cary Clarkff114282016-12-14 11:56:16 -0500375 SkDebugf(" %s\n", contourList->globalState()->debugCoinDictEntry().fFunctionName);
caryclark55888e42016-07-18 10:01:36 -0700376 for (int index = 0; index < glitches.fGlitches.count(); ++index) {
377 const SpanGlitch& glitch = glitches.fGlitches[index];
378 SkDebugf("%02d: ", index);
379 if (glitch.fBase) {
caryclark8016b262016-09-06 05:59:47 -0700380 SkDebugf(" seg/base=%d/%d", glitch.fBase->segment()->debugID(),
381 glitch.fBase->debugID());
caryclark55888e42016-07-18 10:01:36 -0700382 }
383 if (glitch.fSuspect) {
caryclark8016b262016-09-06 05:59:47 -0700384 SkDebugf(" seg/base=%d/%d", glitch.fSuspect->segment()->debugID(),
385 glitch.fSuspect->debugID());
caryclark55888e42016-07-18 10:01:36 -0700386 }
387 if (glitch.fSegment) {
388 SkDebugf(" segment=%d", glitch.fSegment->debugID());
389 }
390 if (glitch.fCoinSpan) {
caryclark8016b262016-09-06 05:59:47 -0700391 SkDebugf(" coinSeg/Span/PtT=%d/%d/%d", glitch.fCoinSpan->segment()->debugID(),
392 glitch.fCoinSpan->span()->debugID(), glitch.fCoinSpan->debugID());
caryclark55888e42016-07-18 10:01:36 -0700393 }
394 if (glitch.fEndSpan) {
395 SkDebugf(" endSpan=%d", glitch.fEndSpan->debugID());
396 }
397 if (glitch.fOppSpan) {
caryclark8016b262016-09-06 05:59:47 -0700398 SkDebugf(" oppSeg/Span/PtT=%d/%d/%d", glitch.fOppSpan->segment()->debugID(),
399 glitch.fOppSpan->span()->debugID(), glitch.fOppSpan->debugID());
caryclark55888e42016-07-18 10:01:36 -0700400 }
401 if (glitch.fOppEndSpan) {
402 SkDebugf(" oppEndSpan=%d", glitch.fOppEndSpan->debugID());
403 }
404 if (!SkScalarIsNaN(glitch.fStartT)) {
405 SkDebugf(" startT=%g", glitch.fStartT);
406 }
407 if (!SkScalarIsNaN(glitch.fEndT)) {
408 SkDebugf(" endT=%g", glitch.fEndT);
409 }
410 if (glitch.fOppSegment) {
411 SkDebugf(" segment=%d", glitch.fOppSegment->debugID());
412 }
413 if (!SkScalarIsNaN(glitch.fOppStartT)) {
414 SkDebugf(" oppStartT=%g", glitch.fOppStartT);
415 }
416 if (!SkScalarIsNaN(glitch.fOppEndT)) {
417 SkDebugf(" oppEndT=%g", glitch.fOppEndT);
418 }
419 if (!SkScalarIsNaN(glitch.fPt.fX) || !SkScalarIsNaN(glitch.fPt.fY)) {
420 SkDebugf(" pt=%g,%g", glitch.fPt.fX, glitch.fPt.fY);
421 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400422 DumpGlitchType(glitch.fType);
caryclark55888e42016-07-18 10:01:36 -0700423 SkDebugf("\n");
424 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400425#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -0700426 contourList->globalState()->debugSetCheckHealth(false);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400427#endif
caryclark6c3b9cd2016-09-26 05:36:58 -0700428#if 01 && DEBUG_ACTIVE_SPANS
Cary Clarkab87d7a2016-10-04 10:01:04 -0400429// SkDebugf("active after %s:\n", id);
caryclark55888e42016-07-18 10:01:36 -0700430 ShowActiveSpans(contourList);
431#endif
432#endif
caryclark26ad22a2015-10-16 09:03:38 -0700433}
434#endif
435
Cary Clarkab87d7a2016-10-04 10:01:04 -0400436#if DEBUG_COIN
437void SkPathOpsDebug::DumpGlitchType(GlitchType glitchType) {
438 switch (glitchType) {
439 case kAddCorruptCoin_Glitch: SkDebugf(" AddCorruptCoin"); break;
440 case kAddExpandedCoin_Glitch: SkDebugf(" AddExpandedCoin"); break;
441 case kAddExpandedFail_Glitch: SkDebugf(" AddExpandedFail"); break;
Kevin Lubick48ce5432019-01-04 08:54:32 -0500442 case kAddIfCollapsed_Glitch: SkDebugf(" AddIfCollapsed"); break;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400443 case kAddIfMissingCoin_Glitch: SkDebugf(" AddIfMissingCoin"); break;
444 case kAddMissingCoin_Glitch: SkDebugf(" AddMissingCoin"); break;
445 case kAddMissingExtend_Glitch: SkDebugf(" AddMissingExtend"); break;
446 case kAddOrOverlap_Glitch: SkDebugf(" AAddOrOverlap"); break;
447 case kCollapsedCoin_Glitch: SkDebugf(" CollapsedCoin"); break;
448 case kCollapsedDone_Glitch: SkDebugf(" CollapsedDone"); break;
449 case kCollapsedOppValue_Glitch: SkDebugf(" CollapsedOppValue"); break;
450 case kCollapsedSpan_Glitch: SkDebugf(" CollapsedSpan"); break;
451 case kCollapsedWindValue_Glitch: SkDebugf(" CollapsedWindValue"); break;
452 case kCorrectEnd_Glitch: SkDebugf(" CorrectEnd"); break;
453 case kDeletedCoin_Glitch: SkDebugf(" DeletedCoin"); break;
454 case kExpandCoin_Glitch: SkDebugf(" ExpandCoin"); break;
455 case kFail_Glitch: SkDebugf(" Fail"); break;
456 case kMarkCoinEnd_Glitch: SkDebugf(" MarkCoinEnd"); break;
457 case kMarkCoinInsert_Glitch: SkDebugf(" MarkCoinInsert"); break;
458 case kMarkCoinMissing_Glitch: SkDebugf(" MarkCoinMissing"); break;
459 case kMarkCoinStart_Glitch: SkDebugf(" MarkCoinStart"); break;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400460 case kMergeMatches_Glitch: SkDebugf(" MergeMatches"); break;
461 case kMissingCoin_Glitch: SkDebugf(" MissingCoin"); break;
462 case kMissingDone_Glitch: SkDebugf(" MissingDone"); break;
463 case kMissingIntersection_Glitch: SkDebugf(" MissingIntersection"); break;
464 case kMoveMultiple_Glitch: SkDebugf(" MoveMultiple"); break;
465 case kMoveNearbyClearAll_Glitch: SkDebugf(" MoveNearbyClearAll"); break;
466 case kMoveNearbyClearAll2_Glitch: SkDebugf(" MoveNearbyClearAll2"); break;
467 case kMoveNearbyMerge_Glitch: SkDebugf(" MoveNearbyMerge"); break;
468 case kMoveNearbyMergeFinal_Glitch: SkDebugf(" MoveNearbyMergeFinal"); break;
469 case kMoveNearbyRelease_Glitch: SkDebugf(" MoveNearbyRelease"); break;
470 case kMoveNearbyReleaseFinal_Glitch: SkDebugf(" MoveNearbyReleaseFinal"); break;
471 case kReleasedSpan_Glitch: SkDebugf(" ReleasedSpan"); break;
472 case kReturnFalse_Glitch: SkDebugf(" ReturnFalse"); break;
473 case kUnaligned_Glitch: SkDebugf(" Unaligned"); break;
474 case kUnalignedHead_Glitch: SkDebugf(" UnalignedHead"); break;
475 case kUnalignedTail_Glitch: SkDebugf(" UnalignedTail"); break;
476 case kUninitialized_Glitch: break;
477 default: SkASSERT(0);
478 }
479}
480#endif
481
caryclark26ad22a2015-10-16 09:03:38 -0700482#if defined SK_DEBUG || !FORCE_RELEASE
caryclark@google.com570863f2013-09-16 15:55:01 +0000483void SkPathOpsDebug::MathematicaIze(char* str, size_t bufferLen) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000484 size_t len = strlen(str);
485 bool num = false;
486 for (size_t idx = 0; idx < len; ++idx) {
487 if (num && str[idx] == 'e') {
488 if (len + 2 >= bufferLen) {
489 return;
490 }
491 memmove(&str[idx + 2], &str[idx + 1], len - idx);
492 str[idx] = '*';
493 str[idx + 1] = '^';
494 ++len;
495 }
496 num = str[idx] >= '0' && str[idx] <= '9';
497 }
498}
499
caryclark@google.com570863f2013-09-16 15:55:01 +0000500bool SkPathOpsDebug::ValidWind(int wind) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000501 return wind > SK_MinS32 + 0xFFFF && wind < SK_MaxS32 - 0xFFFF;
502}
503
caryclark@google.com570863f2013-09-16 15:55:01 +0000504void SkPathOpsDebug::WindingPrintf(int wind) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000505 if (wind == SK_MinS32) {
506 SkDebugf("?");
507 } else {
508 SkDebugf("%d", wind);
509 }
510}
caryclark54359292015-03-26 07:52:43 -0700511#endif // defined SK_DEBUG || !FORCE_RELEASE
512
caryclark@google.coma5e55922013-05-07 18:51:31 +0000513
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000514#if DEBUG_SHOW_TEST_NAME
halcanary385fe4d2015-08-26 13:07:48 -0700515void* SkPathOpsDebug::CreateNameStr() { return new char[DEBUG_FILENAME_STRING_LENGTH]; }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000516
halcanary385fe4d2015-08-26 13:07:48 -0700517void SkPathOpsDebug::DeleteNameStr(void* v) { delete[] reinterpret_cast<char*>(v); }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000518
caryclark@google.com570863f2013-09-16 15:55:01 +0000519void SkPathOpsDebug::BumpTestName(char* test) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000520 char* num = test + strlen(test);
521 while (num[-1] >= '0' && num[-1] <= '9') {
522 --num;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000523 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000524 if (num[0] == '\0') {
525 return;
526 }
527 int dec = atoi(num);
528 if (dec == 0) {
529 return;
530 }
531 ++dec;
532 SK_SNPRINTF(num, DEBUG_FILENAME_STRING_LENGTH - (num - test), "%d", dec);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000533}
534#endif
caryclark@google.com570863f2013-09-16 15:55:01 +0000535
caryclark1049f122015-04-20 08:31:59 -0700536static void show_function_header(const char* functionName) {
537 SkDebugf("\nstatic void %s(skiatest::Reporter* reporter, const char* filename) {\n", functionName);
538 if (strcmp("skphealth_com76", functionName) == 0) {
539 SkDebugf("found it\n");
540 }
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000541}
caryclark1049f122015-04-20 08:31:59 -0700542
543static const char* gOpStrs[] = {
544 "kDifference_SkPathOp",
545 "kIntersect_SkPathOp",
546 "kUnion_SkPathOp",
caryclark55888e42016-07-18 10:01:36 -0700547 "kXOR_PathOp",
caryclark1049f122015-04-20 08:31:59 -0700548 "kReverseDifference_SkPathOp",
549};
550
caryclark03b03ca2015-04-23 09:13:37 -0700551const char* SkPathOpsDebug::OpStr(SkPathOp op) {
552 return gOpStrs[op];
553}
554
caryclark1049f122015-04-20 08:31:59 -0700555static void show_op(SkPathOp op, const char* pathOne, const char* pathTwo) {
556 SkDebugf(" testPathOp(reporter, %s, %s, %s, filename);\n", pathOne, pathTwo, gOpStrs[op]);
557 SkDebugf("}\n");
558}
559
caryclark1049f122015-04-20 08:31:59 -0700560void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
561 const char* testName) {
Herb Derby9c71e7b2019-06-17 14:40:42 -0400562 static SkMutex& mutex = *(new SkMutex);
563
564 SkAutoMutexExclusive ac(mutex);
caryclark1049f122015-04-20 08:31:59 -0700565 show_function_header(testName);
566 ShowOnePath(a, "path", true);
567 ShowOnePath(b, "pathB", true);
568 show_op(shapeOp, "path", "pathB");
569}
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000570
Mike Kleinc0bd9f92019-04-23 12:05:21 -0500571#include "src/pathops/SkIntersectionHelper.h"
572#include "src/pathops/SkIntersections.h"
573#include "src/pathops/SkPathOpsTypes.h"
caryclark26ad22a2015-10-16 09:03:38 -0700574
Cary Clarkab87d7a2016-10-04 10:01:04 -0400575#if DEBUG_COIN
576
Cary Clarkab87d7a2016-10-04 10:01:04 -0400577void SkOpGlobalState::debugAddToGlobalCoinDicts() {
Herb Derby9c71e7b2019-06-17 14:40:42 -0400578 static SkMutex& mutex = *(new SkMutex);
579 SkAutoMutexExclusive ac(mutex);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400580 SkPathOpsDebug::gCoinSumChangedDict.add(fCoinChangedDict);
581 SkPathOpsDebug::gCoinSumVisitedDict.add(fCoinVisitedDict);
582}
583
584#endif
585
caryclark26ad22a2015-10-16 09:03:38 -0700586#if DEBUG_T_SECT_LOOP_COUNT
587void SkOpGlobalState::debugAddLoopCount(SkIntersections* i, const SkIntersectionHelper& wt,
588 const SkIntersectionHelper& wn) {
589 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
590 SkIntersections::DebugLoop looper = (SkIntersections::DebugLoop) index;
591 if (fDebugLoopCount[index] >= i->debugLoopCount(looper)) {
592 continue;
593 }
594 fDebugLoopCount[index] = i->debugLoopCount(looper);
595 fDebugWorstVerb[index * 2] = wt.segment()->verb();
596 fDebugWorstVerb[index * 2 + 1] = wn.segment()->verb();
597 sk_bzero(&fDebugWorstPts[index * 8], sizeof(SkPoint) * 8);
598 memcpy(&fDebugWorstPts[index * 2 * 4], wt.pts(),
599 (SkPathOpsVerbToPoints(wt.segment()->verb()) + 1) * sizeof(SkPoint));
600 memcpy(&fDebugWorstPts[(index * 2 + 1) * 4], wn.pts(),
601 (SkPathOpsVerbToPoints(wn.segment()->verb()) + 1) * sizeof(SkPoint));
602 fDebugWorstWeight[index * 2] = wt.weight();
603 fDebugWorstWeight[index * 2 + 1] = wn.weight();
604 }
605 i->debugResetLoopCount();
606}
607
608void SkOpGlobalState::debugDoYourWorst(SkOpGlobalState* local) {
609 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
610 if (fDebugLoopCount[index] >= local->fDebugLoopCount[index]) {
611 continue;
612 }
613 fDebugLoopCount[index] = local->fDebugLoopCount[index];
614 fDebugWorstVerb[index * 2] = local->fDebugWorstVerb[index * 2];
615 fDebugWorstVerb[index * 2 + 1] = local->fDebugWorstVerb[index * 2 + 1];
616 memcpy(&fDebugWorstPts[index * 2 * 4], &local->fDebugWorstPts[index * 2 * 4],
617 sizeof(SkPoint) * 8);
618 fDebugWorstWeight[index * 2] = local->fDebugWorstWeight[index * 2];
619 fDebugWorstWeight[index * 2 + 1] = local->fDebugWorstWeight[index * 2 + 1];
620 }
621 local->debugResetLoopCounts();
622}
623
624static void dump_curve(SkPath::Verb verb, const SkPoint& pts, float weight) {
625 if (!verb) {
626 return;
627 }
628 const char* verbs[] = { "", "line", "quad", "conic", "cubic" };
629 SkDebugf("%s: {{", verbs[verb]);
630 int ptCount = SkPathOpsVerbToPoints(verb);
631 for (int index = 0; index <= ptCount; ++index) {
632 SkDPoint::Dump((&pts)[index]);
633 if (index < ptCount - 1) {
634 SkDebugf(", ");
635 }
636 }
637 SkDebugf("}");
638 if (weight != 1) {
639 SkDebugf(", ");
640 if (weight == floorf(weight)) {
641 SkDebugf("%.0f", weight);
642 } else {
643 SkDebugf("%1.9gf", weight);
644 }
645 }
646 SkDebugf("}\n");
647}
648
649void SkOpGlobalState::debugLoopReport() {
650 const char* loops[] = { "iterations", "coinChecks", "perpCalcs" };
651 SkDebugf("\n");
652 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
653 SkDebugf("%s: %d\n", loops[index], fDebugLoopCount[index]);
654 dump_curve(fDebugWorstVerb[index * 2], fDebugWorstPts[index * 2 * 4],
655 fDebugWorstWeight[index * 2]);
656 dump_curve(fDebugWorstVerb[index * 2 + 1], fDebugWorstPts[(index * 2 + 1) * 4],
657 fDebugWorstWeight[index * 2 + 1]);
658 }
659}
660
661void SkOpGlobalState::debugResetLoopCounts() {
662 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
663 sk_bzero(fDebugWorstVerb, sizeof(fDebugWorstVerb));
664 sk_bzero(fDebugWorstPts, sizeof(fDebugWorstPts));
665 sk_bzero(fDebugWorstWeight, sizeof(fDebugWorstWeight));
666}
667#endif
caryclark27c8eb82015-07-06 11:38:33 -0700668
Cary Clark59d5a0e2017-01-23 14:38:52 +0000669bool SkOpGlobalState::DebugRunFail() {
670 return SkPathOpsDebug::gRunFail;
671}
672
Cary Clarkab87d7a2016-10-04 10:01:04 -0400673// this is const so it can be called by const methods that overwise don't alter state
674#if DEBUG_VALIDATE || DEBUG_COIN
675void SkOpGlobalState::debugSetPhase(const char* funcName DEBUG_COIN_DECLARE_PARAMS()) const {
676 auto writable = const_cast<SkOpGlobalState*>(this);
677#if DEBUG_VALIDATE
678 writable->setPhase(phase);
679#endif
680#if DEBUG_COIN
681 SkPathOpsDebug::CoinDictEntry* entry = &writable->fCoinDictEntry;
682 writable->fPreviousFuncName = entry->fFunctionName;
683 entry->fIteration = iteration;
684 entry->fLineNumber = lineNo;
685 entry->fGlitchType = SkPathOpsDebug::kUninitialized_Glitch;
686 entry->fFunctionName = funcName;
687 writable->fCoinVisitedDict.add(*entry);
688 writable->debugAddToCoinChangedDict();
689#endif
690}
691#endif
692
caryclark26ad22a2015-10-16 09:03:38 -0700693#if DEBUG_T_SECT_LOOP_COUNT
694void SkIntersections::debugBumpLoopCount(DebugLoop index) {
695 fDebugLoopCount[index]++;
696}
697
698int SkIntersections::debugLoopCount(DebugLoop index) const {
699 return fDebugLoopCount[index];
700}
701
702void SkIntersections::debugResetLoopCount() {
703 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
704}
705#endif
706
Mike Kleinc0bd9f92019-04-23 12:05:21 -0500707#include "src/pathops/SkPathOpsConic.h"
708#include "src/pathops/SkPathOpsCubic.h"
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000709
caryclark624637c2015-05-11 07:21:27 -0700710SkDCubic SkDQuad::debugToCubic() const {
711 SkDCubic cubic;
712 cubic[0] = fPts[0];
713 cubic[2] = fPts[1];
714 cubic[3] = fPts[2];
715 cubic[1].fX = (cubic[0].fX + cubic[2].fX * 2) / 3;
716 cubic[1].fY = (cubic[0].fY + cubic[2].fY * 2) / 3;
717 cubic[2].fX = (cubic[3].fX + cubic[2].fX * 2) / 3;
718 cubic[2].fY = (cubic[3].fY + cubic[2].fY * 2) / 3;
719 return cubic;
caryclark54359292015-03-26 07:52:43 -0700720}
caryclark624637c2015-05-11 07:21:27 -0700721
caryclarka35ab3e2016-10-20 08:32:18 -0700722void SkDQuad::debugSet(const SkDPoint* pts) {
723 memcpy(fPts, pts, sizeof(fPts));
724 SkDEBUGCODE(fDebugGlobalState = nullptr);
725}
726
727void SkDCubic::debugSet(const SkDPoint* pts) {
728 memcpy(fPts, pts, sizeof(fPts));
729 SkDEBUGCODE(fDebugGlobalState = nullptr);
730}
731
732void SkDConic::debugSet(const SkDPoint* pts, SkScalar weight) {
733 fPts.debugSet(pts);
734 fWeight = weight;
735}
736
caryclarked0935a2015-10-22 07:23:52 -0700737void SkDRect::debugInit() {
738 fLeft = fTop = fRight = fBottom = SK_ScalarNaN;
739}
740
Mike Kleinc0bd9f92019-04-23 12:05:21 -0500741#include "src/pathops/SkOpAngle.h"
742#include "src/pathops/SkOpSegment.h"
caryclark54359292015-03-26 07:52:43 -0700743
Cary Clarkab87d7a2016-10-04 10:01:04 -0400744#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -0700745// commented-out lines keep this in sync with addT()
Cary Clarkab87d7a2016-10-04 10:01:04 -0400746 const SkOpPtT* SkOpSegment::debugAddT(double t, SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -0700747 debugValidate();
748 SkPoint pt = this->ptAtT(t);
caryclark26ad22a2015-10-16 09:03:38 -0700749 const SkOpSpanBase* span = &fHead;
caryclark55888e42016-07-18 10:01:36 -0700750 do {
751 const SkOpPtT* result = span->ptT();
caryclark29b25632016-08-25 11:27:17 -0700752 if (t == result->fT || this->match(result, this, t, pt)) {
caryclark55888e42016-07-18 10:01:36 -0700753// span->bumpSpanAdds();
caryclarkef4f32a2016-08-24 09:24:18 -0700754 return result;
caryclark26ad22a2015-10-16 09:03:38 -0700755 }
caryclark55888e42016-07-18 10:01:36 -0700756 if (t < result->fT) {
757 const SkOpSpan* prev = result->span()->prev();
caryclark30b9fdd2016-08-31 14:36:29 -0700758 FAIL_WITH_NULL_IF(!prev, span);
caryclark29b25632016-08-25 11:27:17 -0700759 // marks in global state that new op span has been allocated
760 this->globalState()->setAllocatedOpSpan();
caryclark55888e42016-07-18 10:01:36 -0700761// span->init(this, prev, t, pt);
762 this->debugValidate();
763// #if DEBUG_ADD_T
764// SkDebugf("%s insert t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
765// span->segment()->debugID(), span->debugID());
766// #endif
767// span->bumpSpanAdds();
caryclark55888e42016-07-18 10:01:36 -0700768 return nullptr;
caryclark26ad22a2015-10-16 09:03:38 -0700769 }
caryclark30b9fdd2016-08-31 14:36:29 -0700770 FAIL_WITH_NULL_IF(span != &fTail, span);
caryclark55888e42016-07-18 10:01:36 -0700771 } while ((span = span->upCast()->next()));
772 SkASSERT(0);
caryclark29b25632016-08-25 11:27:17 -0700773 return nullptr; // we never get here, but need this to satisfy compiler
caryclark26ad22a2015-10-16 09:03:38 -0700774}
775#endif
776
777#if DEBUG_ANGLE
778void SkOpSegment::debugCheckAngleCoin() const {
779 const SkOpSpanBase* base = &fHead;
780 const SkOpSpan* span;
781 do {
782 const SkOpAngle* angle = base->fromAngle();
caryclark55888e42016-07-18 10:01:36 -0700783 if (angle && angle->debugCheckCoincidence()) {
caryclark26ad22a2015-10-16 09:03:38 -0700784 angle->debugCheckNearCoincidence();
785 }
786 if (base->final()) {
787 break;
788 }
789 span = base->upCast();
790 angle = span->toAngle();
caryclark55888e42016-07-18 10:01:36 -0700791 if (angle && angle->debugCheckCoincidence()) {
caryclark26ad22a2015-10-16 09:03:38 -0700792 angle->debugCheckNearCoincidence();
793 }
794 } while ((base = span->next()));
795}
796#endif
797
Cary Clarkab87d7a2016-10-04 10:01:04 -0400798#if DEBUG_COIN
caryclark26ad22a2015-10-16 09:03:38 -0700799// this mimics the order of the checks in handle coincidence
Cary Clarkab87d7a2016-10-04 10:01:04 -0400800void SkOpSegment::debugCheckHealth(SkPathOpsDebug::GlitchLog* glitches) const {
801 debugMoveMultiples(glitches);
802 debugMoveNearby(glitches);
803 debugMissingCoincidence(glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700804}
805
caryclark55888e42016-07-18 10:01:36 -0700806// commented-out lines keep this in sync with clearAll()
Cary Clarkab87d7a2016-10-04 10:01:04 -0400807void SkOpSegment::debugClearAll(SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -0700808 const SkOpSpan* span = &fHead;
809 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400810 this->debugClearOne(span, glitches);
caryclark55888e42016-07-18 10:01:36 -0700811 } while ((span = span->next()->upCastable()));
Cary Clarkab87d7a2016-10-04 10:01:04 -0400812 this->globalState()->coincidence()->debugRelease(glitches, this);
caryclark55888e42016-07-18 10:01:36 -0700813}
814
815// commented-out lines keep this in sync with clearOne()
Cary Clarkab87d7a2016-10-04 10:01:04 -0400816void SkOpSegment::debugClearOne(const SkOpSpan* span, SkPathOpsDebug::GlitchLog* glitches) const {
817 if (span->windValue()) glitches->record(SkPathOpsDebug::kCollapsedWindValue_Glitch, span);
818 if (span->oppValue()) glitches->record(SkPathOpsDebug::kCollapsedOppValue_Glitch, span);
819 if (!span->done()) glitches->record(SkPathOpsDebug::kCollapsedDone_Glitch, span);
caryclark26ad22a2015-10-16 09:03:38 -0700820}
821#endif
822
caryclark54359292015-03-26 07:52:43 -0700823SkOpAngle* SkOpSegment::debugLastAngle() {
halcanary96fcdcc2015-08-27 07:41:13 -0700824 SkOpAngle* result = nullptr;
caryclark54359292015-03-26 07:52:43 -0700825 SkOpSpan* span = this->head();
826 do {
827 if (span->toAngle()) {
828 SkASSERT(!result);
829 result = span->toAngle();
830 }
831 } while ((span = span->next()->upCastable()));
832 SkASSERT(result);
833 return result;
834}
835
Cary Clarkab87d7a2016-10-04 10:01:04 -0400836#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -0700837// commented-out lines keep this in sync with ClearVisited
838void SkOpSegment::DebugClearVisited(const SkOpSpanBase* span) {
839 // reset visited flag back to false
840 do {
841 const SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
842 while ((ptT = ptT->next()) != stopPtT) {
843 const SkOpSegment* opp = ptT->segment();
844 opp->resetDebugVisited();
845 }
846 } while (!span->final() && (span = span->upCast()->next()));
847}
848#endif
849
Cary Clarkab87d7a2016-10-04 10:01:04 -0400850#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -0700851// commented-out lines keep this in sync with missingCoincidence()
852// look for pairs of undetected coincident curves
853// assumes that segments going in have visited flag clear
854// Even though pairs of curves correct detect coincident runs, a run may be missed
855// if the coincidence is a product of multiple intersections. For instance, given
856// curves A, B, and C:
857// A-B intersect at a point 1; A-C and B-C intersect at point 2, so near
858// the end of C that the intersection is replaced with the end of C.
859// Even though A-B correctly do not detect an intersection at point 2,
860// the resulting run from point 1 to point 2 is coincident on A and B.
Cary Clarkab87d7a2016-10-04 10:01:04 -0400861void SkOpSegment::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -0700862 if (this->done()) {
863 return;
864 }
865 const SkOpSpan* prior = nullptr;
866 const SkOpSpanBase* spanBase = &fHead;
caryclark55888e42016-07-18 10:01:36 -0700867// bool result = false;
caryclark26ad22a2015-10-16 09:03:38 -0700868 do {
869 const SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT;
870 SkASSERT(ptT->span() == spanBase);
871 while ((ptT = ptT->next()) != spanStopPtT) {
872 if (ptT->deleted()) {
873 continue;
874 }
caryclark55888e42016-07-18 10:01:36 -0700875 const SkOpSegment* opp = ptT->span()->segment();
caryclark26ad22a2015-10-16 09:03:38 -0700876 if (opp->done()) {
877 continue;
878 }
879 // when opp is encounted the 1st time, continue; on 2nd encounter, look for coincidence
caryclark55888e42016-07-18 10:01:36 -0700880 if (!opp->debugVisited()) {
caryclark26ad22a2015-10-16 09:03:38 -0700881 continue;
882 }
883 if (spanBase == &fHead) {
884 continue;
885 }
caryclark55888e42016-07-18 10:01:36 -0700886 if (ptT->segment() == this) {
887 continue;
888 }
caryclark26ad22a2015-10-16 09:03:38 -0700889 const SkOpSpan* span = spanBase->upCastable();
890 // FIXME?: this assumes that if the opposite segment is coincident then no more
891 // coincidence needs to be detected. This may not be true.
caryclark55888e42016-07-18 10:01:36 -0700892 if (span && span->segment() != opp && span->containsCoincidence(opp)) { // debug has additional condition since it may be called before inner duplicate points have been deleted
caryclark26ad22a2015-10-16 09:03:38 -0700893 continue;
894 }
caryclark55888e42016-07-18 10:01:36 -0700895 if (spanBase->segment() != opp && spanBase->containsCoinEnd(opp)) { // debug has additional condition since it may be called before inner duplicate points have been deleted
caryclark26ad22a2015-10-16 09:03:38 -0700896 continue;
caryclark55888e42016-07-18 10:01:36 -0700897 }
caryclark26ad22a2015-10-16 09:03:38 -0700898 const SkOpPtT* priorPtT = nullptr, * priorStopPtT;
899 // find prior span containing opp segment
900 const SkOpSegment* priorOpp = nullptr;
901 const SkOpSpan* priorTest = spanBase->prev();
902 while (!priorOpp && priorTest) {
903 priorStopPtT = priorPtT = priorTest->ptT();
904 while ((priorPtT = priorPtT->next()) != priorStopPtT) {
905 if (priorPtT->deleted()) {
906 continue;
907 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400908 const SkOpSegment* segment = priorPtT->span()->segment();
caryclark26ad22a2015-10-16 09:03:38 -0700909 if (segment == opp) {
910 prior = priorTest;
911 priorOpp = opp;
912 break;
913 }
914 }
915 priorTest = priorTest->prev();
916 }
917 if (!priorOpp) {
918 continue;
919 }
caryclark55888e42016-07-18 10:01:36 -0700920 if (priorPtT == ptT) {
921 continue;
922 }
caryclark26ad22a2015-10-16 09:03:38 -0700923 const SkOpPtT* oppStart = prior->ptT();
924 const SkOpPtT* oppEnd = spanBase->ptT();
925 bool swapped = priorPtT->fT > ptT->fT;
926 if (swapped) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -0400927 using std::swap;
928 swap(priorPtT, ptT);
929 swap(oppStart, oppEnd);
caryclark26ad22a2015-10-16 09:03:38 -0700930 }
caryclark55888e42016-07-18 10:01:36 -0700931 const SkOpCoincidence* coincidence = this->globalState()->coincidence();
932 const SkOpPtT* rootPriorPtT = priorPtT->span()->ptT();
933 const SkOpPtT* rootPtT = ptT->span()->ptT();
934 const SkOpPtT* rootOppStart = oppStart->span()->ptT();
935 const SkOpPtT* rootOppEnd = oppEnd->span()->ptT();
936 if (coincidence->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) {
caryclark26ad22a2015-10-16 09:03:38 -0700937 goto swapBack;
938 }
caryclark55888e42016-07-18 10:01:36 -0700939 if (testForCoincidence(rootPriorPtT, rootPtT, prior, spanBase, opp)) {
940 // mark coincidence
caryclark30b9fdd2016-08-31 14:36:29 -0700941#if DEBUG_COINCIDENCE_VERBOSE
caryclark55888e42016-07-18 10:01:36 -0700942// SkDebugf("%s coinSpan=%d endSpan=%d oppSpan=%d oppEndSpan=%d\n", __FUNCTION__,
943// rootPriorPtT->debugID(), rootPtT->debugID(), rootOppStart->debugID(),
944// rootOppEnd->debugID());
945#endif
Cary Clarkab87d7a2016-10-04 10:01:04 -0400946 log->record(SkPathOpsDebug::kMissingCoin_Glitch, priorPtT, ptT, oppStart, oppEnd);
caryclark55888e42016-07-18 10:01:36 -0700947 // coincidences->add(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
948 // }
949#if DEBUG_COINCIDENCE
caryclarkd6562002016-07-27 12:02:07 -0700950// SkASSERT(coincidences->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
caryclark55888e42016-07-18 10:01:36 -0700951#endif
952 // result = true;
caryclark26ad22a2015-10-16 09:03:38 -0700953 }
954 swapBack:
955 if (swapped) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -0400956 using std::swap;
957 swap(priorPtT, ptT);
caryclark26ad22a2015-10-16 09:03:38 -0700958 }
959 }
960 } while ((spanBase = spanBase->final() ? nullptr : spanBase->upCast()->next()));
caryclark55888e42016-07-18 10:01:36 -0700961 DebugClearVisited(&fHead);
962 return;
caryclark26ad22a2015-10-16 09:03:38 -0700963}
964
caryclark55888e42016-07-18 10:01:36 -0700965// commented-out lines keep this in sync with moveMultiples()
966// if a span has more than one intersection, merge the other segments' span as needed
Cary Clarkab87d7a2016-10-04 10:01:04 -0400967void SkOpSegment::debugMoveMultiples(SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -0700968 debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -0700969 const SkOpSpanBase* test = &fHead;
970 do {
971 int addCount = test->spanAddsCount();
Cary Clarkff114282016-12-14 11:56:16 -0500972// SkASSERT(addCount >= 1);
973 if (addCount <= 1) {
caryclark26ad22a2015-10-16 09:03:38 -0700974 continue;
975 }
976 const SkOpPtT* startPtT = test->ptT();
977 const SkOpPtT* testPtT = startPtT;
978 do { // iterate through all spans associated with start
979 const SkOpSpanBase* oppSpan = testPtT->span();
980 if (oppSpan->spanAddsCount() == addCount) {
981 continue;
982 }
983 if (oppSpan->deleted()) {
984 continue;
985 }
986 const SkOpSegment* oppSegment = oppSpan->segment();
987 if (oppSegment == this) {
988 continue;
989 }
990 // find range of spans to consider merging
991 const SkOpSpanBase* oppPrev = oppSpan;
992 const SkOpSpanBase* oppFirst = oppSpan;
993 while ((oppPrev = oppPrev->prev())) {
994 if (!roughly_equal(oppPrev->t(), oppSpan->t())) {
995 break;
996 }
997 if (oppPrev->spanAddsCount() == addCount) {
998 continue;
999 }
1000 if (oppPrev->deleted()) {
1001 continue;
1002 }
1003 oppFirst = oppPrev;
1004 }
1005 const SkOpSpanBase* oppNext = oppSpan;
1006 const SkOpSpanBase* oppLast = oppSpan;
1007 while ((oppNext = oppNext->final() ? nullptr : oppNext->upCast()->next())) {
1008 if (!roughly_equal(oppNext->t(), oppSpan->t())) {
1009 break;
1010 }
1011 if (oppNext->spanAddsCount() == addCount) {
1012 continue;
1013 }
1014 if (oppNext->deleted()) {
1015 continue;
1016 }
1017 oppLast = oppNext;
1018 }
1019 if (oppFirst == oppLast) {
1020 continue;
1021 }
1022 const SkOpSpanBase* oppTest = oppFirst;
1023 do {
1024 if (oppTest == oppSpan) {
1025 continue;
1026 }
1027 // check to see if the candidate meets specific criteria:
1028 // it contains spans of segments in test's loop but not including 'this'
1029 const SkOpPtT* oppStartPtT = oppTest->ptT();
1030 const SkOpPtT* oppPtT = oppStartPtT;
1031 while ((oppPtT = oppPtT->next()) != oppStartPtT) {
1032 const SkOpSegment* oppPtTSegment = oppPtT->segment();
1033 if (oppPtTSegment == this) {
1034 goto tryNextSpan;
1035 }
1036 const SkOpPtT* matchPtT = startPtT;
1037 do {
1038 if (matchPtT->segment() == oppPtTSegment) {
1039 goto foundMatch;
1040 }
1041 } while ((matchPtT = matchPtT->next()) != startPtT);
1042 goto tryNextSpan;
1043 foundMatch: // merge oppTest and oppSpan
caryclark55888e42016-07-18 10:01:36 -07001044 oppSegment->debugValidate();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001045 oppTest->debugMergeMatches(glitches, oppSpan);
1046 oppTest->debugAddOpp(glitches, oppSpan);
caryclark55888e42016-07-18 10:01:36 -07001047 oppSegment->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001048 goto checkNextSpan;
1049 }
caryclark55888e42016-07-18 10:01:36 -07001050 tryNextSpan:
caryclark26ad22a2015-10-16 09:03:38 -07001051 ;
1052 } while (oppTest != oppLast && (oppTest = oppTest->upCast()->next()));
1053 } while ((testPtT = testPtT->next()) != startPtT);
caryclark55888e42016-07-18 10:01:36 -07001054checkNextSpan:
caryclark26ad22a2015-10-16 09:03:38 -07001055 ;
1056 } while ((test = test->final() ? nullptr : test->upCast()->next()));
caryclark55888e42016-07-18 10:01:36 -07001057 debugValidate();
1058 return;
caryclark26ad22a2015-10-16 09:03:38 -07001059}
1060
caryclark55888e42016-07-18 10:01:36 -07001061// commented-out lines keep this in sync with moveNearby()
1062// Move nearby t values and pts so they all hang off the same span. Alignment happens later.
Cary Clarkab87d7a2016-10-04 10:01:04 -04001063void SkOpSegment::debugMoveNearby(SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -07001064 debugValidate();
1065 // release undeleted spans pointing to this seg that are linked to the primary span
1066 const SkOpSpanBase* spanBase = &fHead;
caryclark26ad22a2015-10-16 09:03:38 -07001067 do {
caryclark55888e42016-07-18 10:01:36 -07001068 const SkOpPtT* ptT = spanBase->ptT();
1069 const SkOpPtT* headPtT = ptT;
1070 while ((ptT = ptT->next()) != headPtT) {
1071 const SkOpSpanBase* test = ptT->span();
1072 if (ptT->segment() == this && !ptT->deleted() && test != spanBase
1073 && test->ptT() == ptT) {
1074 if (test->final()) {
1075 if (spanBase == &fHead) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001076 glitches->record(SkPathOpsDebug::kMoveNearbyClearAll_Glitch, this);
caryclark55888e42016-07-18 10:01:36 -07001077// return;
1078 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001079 glitches->record(SkPathOpsDebug::kMoveNearbyReleaseFinal_Glitch, spanBase, ptT);
caryclark55888e42016-07-18 10:01:36 -07001080 } else if (test->prev()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001081 glitches->record(SkPathOpsDebug::kMoveNearbyRelease_Glitch, test, headPtT);
caryclark55888e42016-07-18 10:01:36 -07001082 }
1083// break;
caryclark26ad22a2015-10-16 09:03:38 -07001084 }
1085 }
caryclark55888e42016-07-18 10:01:36 -07001086 spanBase = spanBase->upCast()->next();
1087 } while (!spanBase->final());
1088
1089 // This loop looks for adjacent spans which are near by
1090 spanBase = &fHead;
1091 do { // iterate through all spans associated with start
1092 const SkOpSpanBase* test = spanBase->upCast()->next();
Cary Clark73e597d2017-04-18 12:08:58 -04001093 bool found;
1094 if (!this->spansNearby(spanBase, test, &found)) {
1095 glitches->record(SkPathOpsDebug::kMoveNearbyMergeFinal_Glitch, test);
1096 }
1097 if (found) {
caryclark55888e42016-07-18 10:01:36 -07001098 if (test->final()) {
1099 if (spanBase->prev()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001100 glitches->record(SkPathOpsDebug::kMoveNearbyMergeFinal_Glitch, test);
caryclark55888e42016-07-18 10:01:36 -07001101 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001102 glitches->record(SkPathOpsDebug::kMoveNearbyClearAll2_Glitch, this);
caryclark55888e42016-07-18 10:01:36 -07001103 // return
1104 }
1105 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001106 glitches->record(SkPathOpsDebug::kMoveNearbyMerge_Glitch, spanBase);
caryclark55888e42016-07-18 10:01:36 -07001107 }
1108 }
1109 spanBase = test;
1110 } while (!spanBase->final());
1111 debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001112}
1113#endif
1114
caryclark54359292015-03-26 07:52:43 -07001115void SkOpSegment::debugReset() {
caryclark1049f122015-04-20 08:31:59 -07001116 this->init(this->fPts, this->fWeight, this->contour(), this->verb());
caryclark54359292015-03-26 07:52:43 -07001117}
1118
caryclark025b11e2016-08-25 05:21:14 -07001119#if DEBUG_COINCIDENCE_ORDER
1120void SkOpSegment::debugSetCoinT(int index, SkScalar t) const {
1121 if (fDebugBaseMax < 0 || fDebugBaseIndex == index) {
1122 fDebugBaseIndex = index;
1123 fDebugBaseMin = SkTMin(t, fDebugBaseMin);
1124 fDebugBaseMax = SkTMax(t, fDebugBaseMax);
1125 return;
Ben Wagner63fd7602017-10-09 15:45:33 -04001126 }
caryclark025b11e2016-08-25 05:21:14 -07001127 SkASSERT(fDebugBaseMin >= t || t >= fDebugBaseMax);
1128 if (fDebugLastMax < 0 || fDebugLastIndex == index) {
1129 fDebugLastIndex = index;
1130 fDebugLastMin = SkTMin(t, fDebugLastMin);
1131 fDebugLastMax = SkTMax(t, fDebugLastMax);
1132 return;
1133 }
1134 SkASSERT(fDebugLastMin >= t || t >= fDebugLastMax);
1135 SkASSERT((t - fDebugBaseMin > 0) == (fDebugLastMin - fDebugBaseMin > 0));
1136}
1137#endif
1138
caryclark54359292015-03-26 07:52:43 -07001139#if DEBUG_ACTIVE_SPANS
Cary Clarkff114282016-12-14 11:56:16 -05001140void SkOpSegment::debugShowActiveSpans(SkString* str) const {
caryclark54359292015-03-26 07:52:43 -07001141 debugValidate();
1142 if (done()) {
1143 return;
1144 }
1145 int lastId = -1;
1146 double lastT = -1;
1147 const SkOpSpan* span = &fHead;
1148 do {
1149 if (span->done()) {
1150 continue;
1151 }
caryclark1049f122015-04-20 08:31:59 -07001152 if (lastId == this->debugID() && lastT == span->t()) {
caryclark54359292015-03-26 07:52:43 -07001153 continue;
1154 }
caryclark1049f122015-04-20 08:31:59 -07001155 lastId = this->debugID();
caryclark54359292015-03-26 07:52:43 -07001156 lastT = span->t();
Cary Clarkff114282016-12-14 11:56:16 -05001157 str->appendf("%s id=%d", __FUNCTION__, this->debugID());
caryclark55888e42016-07-18 10:01:36 -07001158 // since endpoints may have be adjusted, show actual computed curves
1159 SkDCurve curvePart;
1160 this->subDivide(span, span->next(), &curvePart);
1161 const SkDPoint* pts = curvePart.fCubic.fPts;
Cary Clarkff114282016-12-14 11:56:16 -05001162 str->appendf(" (%1.9g,%1.9g", pts[0].fX, pts[0].fY);
caryclark54359292015-03-26 07:52:43 -07001163 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
Cary Clarkff114282016-12-14 11:56:16 -05001164 str->appendf(" %1.9g,%1.9g", pts[vIndex].fX, pts[vIndex].fY);
caryclark54359292015-03-26 07:52:43 -07001165 }
caryclark1049f122015-04-20 08:31:59 -07001166 if (SkPath::kConic_Verb == fVerb) {
Cary Clarkff114282016-12-14 11:56:16 -05001167 str->appendf(" %1.9gf", curvePart.fConic.fWeight);
caryclark1049f122015-04-20 08:31:59 -07001168 }
Cary Clarkff114282016-12-14 11:56:16 -05001169 str->appendf(") t=%1.9g tEnd=%1.9g", span->t(), span->next()->t());
caryclark54359292015-03-26 07:52:43 -07001170 if (span->windSum() == SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001171 str->appendf(" windSum=?");
caryclark54359292015-03-26 07:52:43 -07001172 } else {
Cary Clarkff114282016-12-14 11:56:16 -05001173 str->appendf(" windSum=%d", span->windSum());
caryclark54359292015-03-26 07:52:43 -07001174 }
caryclark624637c2015-05-11 07:21:27 -07001175 if (span->oppValue() && span->oppSum() == SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001176 str->appendf(" oppSum=?");
caryclark624637c2015-05-11 07:21:27 -07001177 } else if (span->oppValue() || span->oppSum() != SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001178 str->appendf(" oppSum=%d", span->oppSum());
caryclark624637c2015-05-11 07:21:27 -07001179 }
Cary Clarkff114282016-12-14 11:56:16 -05001180 str->appendf(" windValue=%d", span->windValue());
caryclark624637c2015-05-11 07:21:27 -07001181 if (span->oppValue() || span->oppSum() != SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001182 str->appendf(" oppValue=%d", span->oppValue());
caryclark624637c2015-05-11 07:21:27 -07001183 }
Cary Clarkff114282016-12-14 11:56:16 -05001184 str->appendf("\n");
caryclark54359292015-03-26 07:52:43 -07001185 } while ((span = span->next()->upCastable()));
1186}
1187#endif
1188
1189#if DEBUG_MARK_DONE
1190void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding) {
1191 const SkPoint& pt = span->ptT()->fPt;
caryclark1049f122015-04-20 08:31:59 -07001192 SkDebugf("%s id=%d", fun, this->debugID());
caryclark54359292015-03-26 07:52:43 -07001193 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1194 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1195 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1196 }
1197 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
1198 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t());
1199 if (winding == SK_MinS32) {
1200 SkDebugf("?");
1201 } else {
1202 SkDebugf("%d", winding);
1203 }
1204 SkDebugf(" windSum=");
1205 if (span->windSum() == SK_MinS32) {
1206 SkDebugf("?");
1207 } else {
1208 SkDebugf("%d", span->windSum());
1209 }
1210 SkDebugf(" windValue=%d\n", span->windValue());
1211}
1212
1213void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding,
1214 int oppWinding) {
1215 const SkPoint& pt = span->ptT()->fPt;
caryclark1049f122015-04-20 08:31:59 -07001216 SkDebugf("%s id=%d", fun, this->debugID());
caryclark54359292015-03-26 07:52:43 -07001217 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1218 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1219 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1220 }
1221 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
1222 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t(), winding, oppWinding);
1223 if (winding == SK_MinS32) {
1224 SkDebugf("?");
1225 } else {
1226 SkDebugf("%d", winding);
1227 }
1228 SkDebugf(" newOppSum=");
1229 if (oppWinding == SK_MinS32) {
1230 SkDebugf("?");
1231 } else {
1232 SkDebugf("%d", oppWinding);
1233 }
1234 SkDebugf(" oppSum=");
1235 if (span->oppSum() == SK_MinS32) {
1236 SkDebugf("?");
1237 } else {
1238 SkDebugf("%d", span->oppSum());
1239 }
1240 SkDebugf(" windSum=");
1241 if (span->windSum() == SK_MinS32) {
1242 SkDebugf("?");
1243 } else {
1244 SkDebugf("%d", span->windSum());
1245 }
1246 SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue());
1247}
1248
1249#endif
1250
caryclark26ad22a2015-10-16 09:03:38 -07001251// loop looking for a pair of angle parts that are too close to be sorted
1252/* This is called after other more simple intersection and angle sorting tests have been exhausted.
1253 This should be rarely called -- the test below is thorough and time consuming.
caryclark55888e42016-07-18 10:01:36 -07001254 This checks the distance between start points; the distance between
caryclark26ad22a2015-10-16 09:03:38 -07001255*/
1256#if DEBUG_ANGLE
1257void SkOpAngle::debugCheckNearCoincidence() const {
1258 const SkOpAngle* test = this;
1259 do {
1260 const SkOpSegment* testSegment = test->segment();
1261 double testStartT = test->start()->t();
1262 SkDPoint testStartPt = testSegment->dPtAtT(testStartT);
1263 double testEndT = test->end()->t();
1264 SkDPoint testEndPt = testSegment->dPtAtT(testEndT);
1265 double testLenSq = testStartPt.distanceSquared(testEndPt);
1266 SkDebugf("%s testLenSq=%1.9g id=%d\n", __FUNCTION__, testLenSq, testSegment->debugID());
1267 double testMidT = (testStartT + testEndT) / 2;
1268 const SkOpAngle* next = test;
1269 while ((next = next->fNext) != this) {
1270 SkOpSegment* nextSegment = next->segment();
1271 double testMidDistSq = testSegment->distSq(testMidT, next);
1272 double testEndDistSq = testSegment->distSq(testEndT, next);
1273 double nextStartT = next->start()->t();
1274 SkDPoint nextStartPt = nextSegment->dPtAtT(nextStartT);
1275 double distSq = testStartPt.distanceSquared(nextStartPt);
1276 double nextEndT = next->end()->t();
1277 double nextMidT = (nextStartT + nextEndT) / 2;
1278 double nextMidDistSq = nextSegment->distSq(nextMidT, test);
1279 double nextEndDistSq = nextSegment->distSq(nextEndT, test);
1280 SkDebugf("%s distSq=%1.9g testId=%d nextId=%d\n", __FUNCTION__, distSq,
1281 testSegment->debugID(), nextSegment->debugID());
1282 SkDebugf("%s testMidDistSq=%1.9g\n", __FUNCTION__, testMidDistSq);
1283 SkDebugf("%s testEndDistSq=%1.9g\n", __FUNCTION__, testEndDistSq);
1284 SkDebugf("%s nextMidDistSq=%1.9g\n", __FUNCTION__, nextMidDistSq);
1285 SkDebugf("%s nextEndDistSq=%1.9g\n", __FUNCTION__, nextEndDistSq);
1286 SkDPoint nextEndPt = nextSegment->dPtAtT(nextEndT);
1287 double nextLenSq = nextStartPt.distanceSquared(nextEndPt);
1288 SkDebugf("%s nextLenSq=%1.9g\n", __FUNCTION__, nextLenSq);
1289 SkDebugf("\n");
1290 }
1291 test = test->fNext;
caryclark55888e42016-07-18 10:01:36 -07001292 } while (test->fNext != this);
caryclark26ad22a2015-10-16 09:03:38 -07001293}
1294#endif
1295
caryclark54359292015-03-26 07:52:43 -07001296#if DEBUG_ANGLE
1297SkString SkOpAngle::debugPart() const {
1298 SkString result;
1299 switch (this->segment()->verb()) {
1300 case SkPath::kLine_Verb:
caryclarkeed356d2016-09-14 07:18:20 -07001301 result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fPart.fCurve),
caryclark54359292015-03-26 07:52:43 -07001302 this->segment()->debugID());
1303 break;
1304 case SkPath::kQuad_Verb:
caryclarkeed356d2016-09-14 07:18:20 -07001305 result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fPart.fCurve),
caryclark54359292015-03-26 07:52:43 -07001306 this->segment()->debugID());
1307 break;
caryclark1049f122015-04-20 08:31:59 -07001308 case SkPath::kConic_Verb:
1309 result.printf(CONIC_DEBUG_STR " id=%d",
caryclarkeed356d2016-09-14 07:18:20 -07001310 CONIC_DEBUG_DATA(fPart.fCurve, fPart.fCurve.fConic.fWeight),
caryclark1049f122015-04-20 08:31:59 -07001311 this->segment()->debugID());
1312 break;
caryclark54359292015-03-26 07:52:43 -07001313 case SkPath::kCubic_Verb:
caryclarkeed356d2016-09-14 07:18:20 -07001314 result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fPart.fCurve),
caryclark54359292015-03-26 07:52:43 -07001315 this->segment()->debugID());
1316 break;
1317 default:
1318 SkASSERT(0);
mtklein1b249332015-07-07 12:21:21 -07001319 }
caryclark54359292015-03-26 07:52:43 -07001320 return result;
1321}
1322#endif
1323
caryclark624637c2015-05-11 07:21:27 -07001324#if DEBUG_SORT
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001325void SkOpAngle::debugLoop() const {
caryclarke4097e32014-06-18 07:24:19 -07001326 const SkOpAngle* first = this;
1327 const SkOpAngle* next = this;
1328 do {
1329 next->dumpOne(true);
caryclark19eb3b22014-07-18 05:08:14 -07001330 SkDebugf("\n");
caryclarke4097e32014-06-18 07:24:19 -07001331 next = next->fNext;
1332 } while (next && next != first);
caryclark54359292015-03-26 07:52:43 -07001333 next = first;
1334 do {
1335 next->debugValidate();
1336 next = next->fNext;
1337 } while (next && next != first);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001338}
1339#endif
1340
caryclark54359292015-03-26 07:52:43 -07001341void SkOpAngle::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07001342#if DEBUG_COINCIDENCE
1343 if (this->globalState()->debugCheckHealth()) {
1344 return;
1345 }
1346#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001347#if DEBUG_VALIDATE
caryclark54359292015-03-26 07:52:43 -07001348 const SkOpAngle* first = this;
1349 const SkOpAngle* next = this;
1350 int wind = 0;
1351 int opp = 0;
1352 int lastXor = -1;
1353 int lastOppXor = -1;
1354 do {
1355 if (next->unorderable()) {
1356 return;
1357 }
1358 const SkOpSpan* minSpan = next->start()->starter(next->end());
1359 if (minSpan->windValue() == SK_MinS32) {
1360 return;
1361 }
1362 bool op = next->segment()->operand();
1363 bool isXor = next->segment()->isXor();
1364 bool oppXor = next->segment()->oppXor();
1365 SkASSERT(!DEBUG_LIMIT_WIND_SUM || between(0, minSpan->windValue(), DEBUG_LIMIT_WIND_SUM));
1366 SkASSERT(!DEBUG_LIMIT_WIND_SUM
1367 || between(-DEBUG_LIMIT_WIND_SUM, minSpan->oppValue(), DEBUG_LIMIT_WIND_SUM));
1368 bool useXor = op ? oppXor : isXor;
1369 SkASSERT(lastXor == -1 || lastXor == (int) useXor);
1370 lastXor = (int) useXor;
caryclark624637c2015-05-11 07:21:27 -07001371 wind += next->debugSign() * (op ? minSpan->oppValue() : minSpan->windValue());
caryclark54359292015-03-26 07:52:43 -07001372 if (useXor) {
1373 wind &= 1;
1374 }
1375 useXor = op ? isXor : oppXor;
1376 SkASSERT(lastOppXor == -1 || lastOppXor == (int) useXor);
1377 lastOppXor = (int) useXor;
caryclark624637c2015-05-11 07:21:27 -07001378 opp += next->debugSign() * (op ? minSpan->windValue() : minSpan->oppValue());
caryclark54359292015-03-26 07:52:43 -07001379 if (useXor) {
1380 opp &= 1;
1381 }
1382 next = next->fNext;
1383 } while (next && next != first);
Cary Clark59d5a0e2017-01-23 14:38:52 +00001384 SkASSERT(wind == 0 || !SkPathOpsDebug::gRunFail);
1385 SkASSERT(opp == 0 || !SkPathOpsDebug::gRunFail);
caryclark54359292015-03-26 07:52:43 -07001386#endif
1387}
1388
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001389void SkOpAngle::debugValidateNext() const {
caryclark54359292015-03-26 07:52:43 -07001390#if !FORCE_RELEASE
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001391 const SkOpAngle* first = this;
1392 const SkOpAngle* next = first;
1393 SkTDArray<const SkOpAngle*>(angles);
1394 do {
djsollenf2b340f2016-01-29 08:51:04 -08001395// SkASSERT_RELEASE(next->fSegment->debugContains(next));
Mike Reedb5475792018-08-08 16:17:42 -04001396 angles.push_back(next);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001397 next = next->next();
1398 if (next == first) {
1399 break;
1400 }
djsollenf2b340f2016-01-29 08:51:04 -08001401 SkASSERT_RELEASE(!angles.contains(next));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001402 if (!next) {
1403 return;
1404 }
1405 } while (true);
reed0dc4dd62015-03-24 13:55:33 -07001406#endif
reed0dc4dd62015-03-24 13:55:33 -07001407}
reed0dc4dd62015-03-24 13:55:33 -07001408
caryclark55888e42016-07-18 10:01:36 -07001409#ifdef SK_DEBUG
1410void SkCoincidentSpans::debugStartCheck(const SkOpSpanBase* outer, const SkOpSpanBase* over,
1411 const SkOpGlobalState* debugState) const {
Cary Clark59d5a0e2017-01-23 14:38:52 +00001412 SkASSERT(coinPtTEnd()->span() == over || !SkOpGlobalState::DebugRunFail());
1413 SkASSERT(oppPtTEnd()->span() == outer || !SkOpGlobalState::DebugRunFail());
caryclark55888e42016-07-18 10:01:36 -07001414}
1415#endif
caryclark26ad22a2015-10-16 09:03:38 -07001416
Cary Clarkab87d7a2016-10-04 10:01:04 -04001417#if DEBUG_COIN
1418// sets the span's end to the ptT referenced by the previous-next
1419void SkCoincidentSpans::debugCorrectOneEnd(SkPathOpsDebug::GlitchLog* log,
1420 const SkOpPtT* (SkCoincidentSpans::* getEnd)() const,
1421 void (SkCoincidentSpans::*setEnd)(const SkOpPtT* ptT) const ) const {
1422 const SkOpPtT* origPtT = (this->*getEnd)();
1423 const SkOpSpanBase* origSpan = origPtT->span();
1424 const SkOpSpan* prev = origSpan->prev();
1425 const SkOpPtT* testPtT = prev ? prev->next()->ptT()
1426 : origSpan->upCast()->next()->prev()->ptT();
1427 if (origPtT != testPtT) {
1428 log->record(SkPathOpsDebug::kCorrectEnd_Glitch, this, origPtT, testPtT);
1429 }
1430}
1431
1432
1433/* Commented-out lines keep this in sync with correctEnds */
1434// FIXME: member pointers have fallen out of favor and can be replaced with
1435// an alternative approach.
1436// makes all span ends agree with the segment's spans that define them
1437void SkCoincidentSpans::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
1438 this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTStart, nullptr);
1439 this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTEnd, nullptr);
1440 this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTStart, nullptr);
1441 this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTEnd, nullptr);
1442}
1443
caryclark55888e42016-07-18 10:01:36 -07001444/* Commented-out lines keep this in sync with expand */
caryclark30b9fdd2016-08-31 14:36:29 -07001445// expand the range by checking adjacent spans for coincidence
Cary Clarkab87d7a2016-10-04 10:01:04 -04001446bool SkCoincidentSpans::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07001447 bool expanded = false;
1448 const SkOpSegment* segment = coinPtTStart()->segment();
1449 const SkOpSegment* oppSegment = oppPtTStart()->segment();
1450 do {
1451 const SkOpSpan* start = coinPtTStart()->span()->upCast();
1452 const SkOpSpan* prev = start->prev();
1453 const SkOpPtT* oppPtT;
1454 if (!prev || !(oppPtT = prev->contains(oppSegment))) {
1455 break;
1456 }
1457 double midT = (prev->t() + start->t()) / 2;
1458 if (!segment->isClose(midT, oppSegment)) {
1459 break;
1460 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001461 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, prev->ptT(), oppPtT);
caryclark55888e42016-07-18 10:01:36 -07001462 expanded = true;
1463 } while (false); // actual continues while expansion is possible
1464 do {
1465 const SkOpSpanBase* end = coinPtTEnd()->span();
1466 SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
caryclark30b9fdd2016-08-31 14:36:29 -07001467 if (next && next->deleted()) {
1468 break;
1469 }
caryclark55888e42016-07-18 10:01:36 -07001470 const SkOpPtT* oppPtT;
1471 if (!next || !(oppPtT = next->contains(oppSegment))) {
1472 break;
1473 }
1474 double midT = (end->t() + next->t()) / 2;
1475 if (!segment->isClose(midT, oppSegment)) {
1476 break;
1477 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001478 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, next->ptT(), oppPtT);
caryclark55888e42016-07-18 10:01:36 -07001479 expanded = true;
1480 } while (false); // actual continues while expansion is possible
1481 return expanded;
1482}
1483
Cary Clarkab87d7a2016-10-04 10:01:04 -04001484// description below
1485void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* base, const SkOpSpanBase* testSpan) const {
1486 const SkOpPtT* testPtT = testSpan->ptT();
1487 const SkOpPtT* stopPtT = testPtT;
1488 const SkOpSegment* baseSeg = base->segment();
1489 while ((testPtT = testPtT->next()) != stopPtT) {
1490 const SkOpSegment* testSeg = testPtT->segment();
1491 if (testPtT->deleted()) {
1492 continue;
1493 }
1494 if (testSeg == baseSeg) {
1495 continue;
1496 }
1497 if (testPtT->span()->ptT() != testPtT) {
1498 continue;
1499 }
1500 if (this->contains(baseSeg, testSeg, testPtT->fT)) {
1501 continue;
1502 }
1503 // intersect perp with base->ptT() with testPtT->segment()
1504 SkDVector dxdy = baseSeg->dSlopeAtT(base->t());
1505 const SkPoint& pt = base->pt();
1506 SkDLine ray = {{{pt.fX, pt.fY}, {pt.fX + dxdy.fY, pt.fY - dxdy.fX}}};
1507 SkIntersections i;
1508 (*CurveIntersectRay[testSeg->verb()])(testSeg->pts(), testSeg->weight(), ray, &i);
1509 for (int index = 0; index < i.used(); ++index) {
1510 double t = i[0][index];
1511 if (!between(0, t, 1)) {
1512 continue;
1513 }
1514 SkDPoint oppPt = i.pt(index);
1515 if (!oppPt.approximatelyEqual(pt)) {
1516 continue;
1517 }
1518 SkOpSegment* writableSeg = const_cast<SkOpSegment*>(testSeg);
1519 SkOpPtT* oppStart = writableSeg->addT(t);
1520 if (oppStart == testPtT) {
1521 continue;
1522 }
1523 SkOpSpan* writableBase = const_cast<SkOpSpan*>(base);
1524 oppStart->span()->addOpp(writableBase);
1525 if (oppStart->deleted()) {
1526 continue;
1527 }
1528 SkOpSegment* coinSeg = base->segment();
1529 SkOpSegment* oppSeg = oppStart->segment();
1530 double coinTs, coinTe, oppTs, oppTe;
1531 if (Ordered(coinSeg, oppSeg)) {
1532 coinTs = base->t();
1533 coinTe = testSpan->t();
1534 oppTs = oppStart->fT;
1535 oppTe = testPtT->fT;
1536 } else {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001537 using std::swap;
1538 swap(coinSeg, oppSeg);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001539 coinTs = oppStart->fT;
1540 coinTe = testPtT->fT;
1541 oppTs = base->t();
1542 oppTe = testSpan->t();
1543 }
1544 if (coinTs > coinTe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001545 using std::swap;
1546 swap(coinTs, coinTe);
1547 swap(oppTs, oppTe);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001548 }
1549 bool added;
1550 if (this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &added), false) {
1551 return;
1552 }
1553 }
1554 }
1555 return;
1556}
1557
1558// description below
1559void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* ptT) const {
1560 FAIL_IF(!ptT->span()->upCastable(), ptT->span());
1561 const SkOpSpan* base = ptT->span()->upCast();
1562 const SkOpSpan* prev = base->prev();
1563 FAIL_IF(!prev, ptT->span());
1564 if (!prev->isCanceled()) {
1565 if (this->debugAddEndMovedSpans(log, base, base->prev()), false) {
1566 return;
1567 }
1568 }
1569 if (!base->isCanceled()) {
1570 if (this->debugAddEndMovedSpans(log, base, base->next()), false) {
1571 return;
1572 }
1573 }
1574 return;
1575}
1576
1577/* If A is coincident with B and B includes an endpoint, and A's matching point
1578 is not the endpoint (i.e., there's an implied line connecting B-end and A)
1579 then assume that the same implied line may intersect another curve close to B.
1580 Since we only care about coincidence that was undetected, look at the
1581 ptT list on B-segment adjacent to the B-end/A ptT loop (not in the loop, but
1582 next door) and see if the A matching point is close enough to form another
1583 coincident pair. If so, check for a new coincident span between B-end/A ptT loop
1584 and the adjacent ptT loop.
1585*/
1586void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log) const {
1587 const SkCoincidentSpans* span = fHead;
1588 if (!span) {
1589 return;
1590 }
1591// fTop = span;
1592// fHead = nullptr;
1593 do {
1594 if (span->coinPtTStart()->fPt != span->oppPtTStart()->fPt) {
1595 FAIL_IF(1 == span->coinPtTStart()->fT, span);
1596 bool onEnd = span->coinPtTStart()->fT == 0;
1597 bool oOnEnd = zero_or_one(span->oppPtTStart()->fT);
1598 if (onEnd) {
1599 if (!oOnEnd) { // if both are on end, any nearby intersect was already found
1600 if (this->debugAddEndMovedSpans(log, span->oppPtTStart()), false) {
1601 return;
1602 }
1603 }
1604 } else if (oOnEnd) {
1605 if (this->debugAddEndMovedSpans(log, span->coinPtTStart()), false) {
1606 return;
1607 }
1608 }
1609 }
1610 if (span->coinPtTEnd()->fPt != span->oppPtTEnd()->fPt) {
1611 bool onEnd = span->coinPtTEnd()->fT == 1;
1612 bool oOnEnd = zero_or_one(span->oppPtTEnd()->fT);
1613 if (onEnd) {
1614 if (!oOnEnd) {
1615 if (this->debugAddEndMovedSpans(log, span->oppPtTEnd()), false) {
1616 return;
1617 }
1618 }
1619 } else if (oOnEnd) {
1620 if (this->debugAddEndMovedSpans(log, span->coinPtTEnd()), false) {
1621 return;
1622 }
1623 }
1624 }
1625 } while ((span = span->next()));
1626// this->restoreHead();
1627 return;
1628}
1629
caryclark55888e42016-07-18 10:01:36 -07001630/* Commented-out lines keep this in sync with addExpanded */
1631// for each coincident pair, match the spans
1632// if the spans don't match, add the mssing pt to the segment and loop it in the opposite span
Cary Clarkab87d7a2016-10-04 10:01:04 -04001633void SkOpCoincidence::debugAddExpanded(SkPathOpsDebug::GlitchLog* log) const {
caryclarka35ab3e2016-10-20 08:32:18 -07001634// DEBUG_SET_PHASE();
caryclark26ad22a2015-10-16 09:03:38 -07001635 const SkCoincidentSpans* coin = this->fHead;
1636 if (!coin) {
caryclarked0935a2015-10-22 07:23:52 -07001637 return;
1638 }
caryclark26ad22a2015-10-16 09:03:38 -07001639 do {
caryclark55888e42016-07-18 10:01:36 -07001640 const SkOpPtT* startPtT = coin->coinPtTStart();
1641 const SkOpPtT* oStartPtT = coin->oppPtTStart();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001642 double priorT = startPtT->fT;
1643 double oPriorT = oStartPtT->fT;
Cary Clarkff114282016-12-14 11:56:16 -05001644 FAIL_IF(!startPtT->contains(oStartPtT), coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001645 SkOPASSERT(coin->coinPtTEnd()->contains(coin->oppPtTEnd()));
caryclark26ad22a2015-10-16 09:03:38 -07001646 const SkOpSpanBase* start = startPtT->span();
1647 const SkOpSpanBase* oStart = oStartPtT->span();
caryclark55888e42016-07-18 10:01:36 -07001648 const SkOpSpanBase* end = coin->coinPtTEnd()->span();
1649 const SkOpSpanBase* oEnd = coin->oppPtTEnd()->span();
caryclark30b9fdd2016-08-31 14:36:29 -07001650 FAIL_IF(oEnd->deleted(), coin);
1651 FAIL_IF(!start->upCastable(), coin);
caryclark26ad22a2015-10-16 09:03:38 -07001652 const SkOpSpanBase* test = start->upCast()->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001653 FAIL_IF(!coin->flipped() && !oStart->upCastable(), coin);
caryclark55888e42016-07-18 10:01:36 -07001654 const SkOpSpanBase* oTest = coin->flipped() ? oStart->prev() : oStart->upCast()->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001655 FAIL_IF(!oTest, coin);
1656 const SkOpSegment* seg = start->segment();
1657 const SkOpSegment* oSeg = oStart->segment();
caryclark26ad22a2015-10-16 09:03:38 -07001658 while (test != end || oTest != oEnd) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001659 const SkOpPtT* containedOpp = test->ptT()->contains(oSeg);
1660 const SkOpPtT* containedThis = oTest->ptT()->contains(seg);
1661 if (!containedOpp || !containedThis) {
1662 // choose the ends, or the first common pt-t list shared by both
1663 double nextT, oNextT;
1664 if (containedOpp) {
1665 nextT = test->t();
1666 oNextT = containedOpp->fT;
1667 } else if (containedThis) {
1668 nextT = containedThis->fT;
1669 oNextT = oTest->t();
1670 } else {
1671 // iterate through until a pt-t list found that contains the other
1672 const SkOpSpanBase* walk = test;
1673 const SkOpPtT* walkOpp;
1674 do {
1675 FAIL_IF(!walk->upCastable(), coin);
1676 walk = walk->upCast()->next();
1677 } while (!(walkOpp = walk->ptT()->contains(oSeg))
1678 && walk != coin->coinPtTEnd()->span());
caryclarka35ab3e2016-10-20 08:32:18 -07001679 FAIL_IF(!walkOpp, coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001680 nextT = walk->t();
1681 oNextT = walkOpp->fT;
1682 }
caryclark26ad22a2015-10-16 09:03:38 -07001683 // use t ranges to guess which one is missing
caryclarka35ab3e2016-10-20 08:32:18 -07001684 double startRange = nextT - priorT;
caryclark30b9fdd2016-08-31 14:36:29 -07001685 FAIL_IF(!startRange, coin);
caryclarka35ab3e2016-10-20 08:32:18 -07001686 double startPart = (test->t() - priorT) / startRange;
1687 double oStartRange = oNextT - oPriorT;
caryclark30b9fdd2016-08-31 14:36:29 -07001688 FAIL_IF(!oStartRange, coin);
caryclark26ad22a2015-10-16 09:03:38 -07001689 double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange;
caryclark30b9fdd2016-08-31 14:36:29 -07001690 FAIL_IF(startPart == oStartPart, coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001691 bool addToOpp = !containedOpp && !containedThis ? startPart < oStartPart
1692 : !!containedThis;
caryclark55888e42016-07-18 10:01:36 -07001693 bool startOver = false;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001694 addToOpp ? log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch,
1695 oPriorT + oStartRange * startPart, test)
1696 : log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch,
1697 priorT + startRange * oStartPart, oTest);
1698 // FAIL_IF(!success, coin);
caryclark55888e42016-07-18 10:01:36 -07001699 if (startOver) {
1700 test = start;
1701 oTest = oStart;
caryclark26ad22a2015-10-16 09:03:38 -07001702 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001703 end = coin->coinPtTEnd()->span();
1704 oEnd = coin->oppPtTEnd()->span();
caryclark26ad22a2015-10-16 09:03:38 -07001705 }
caryclark55888e42016-07-18 10:01:36 -07001706 if (test != end) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001707 FAIL_IF(!test->upCastable(), coin);
1708 priorT = test->t();
caryclark26ad22a2015-10-16 09:03:38 -07001709 test = test->upCast()->next();
1710 }
caryclark55888e42016-07-18 10:01:36 -07001711 if (oTest != oEnd) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001712 oPriorT = oTest->t();
caryclark55888e42016-07-18 10:01:36 -07001713 oTest = coin->flipped() ? oTest->prev() : oTest->upCast()->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001714 FAIL_IF(!oTest, coin);
caryclark26ad22a2015-10-16 09:03:38 -07001715 }
1716 }
caryclark55888e42016-07-18 10:01:36 -07001717 } while ((coin = coin->next()));
1718 return;
caryclark26ad22a2015-10-16 09:03:38 -07001719}
1720
caryclark55888e42016-07-18 10:01:36 -07001721/* Commented-out lines keep this in sync addIfMissing() */
caryclark8016b262016-09-06 05:59:47 -07001722// note that over1s, over1e, over2s, over2e are ordered
Cary Clarkab87d7a2016-10-04 10:01:04 -04001723void SkOpCoincidence::debugAddIfMissing(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* over1s, const SkOpPtT* over2s,
caryclark81a478c2016-09-09 09:37:57 -07001724 double tStart, double tEnd, const SkOpSegment* coinSeg, const SkOpSegment* oppSeg, bool* added,
caryclark8016b262016-09-06 05:59:47 -07001725 const SkOpPtT* over1e, const SkOpPtT* over2e) const {
1726 SkASSERT(tStart < tEnd);
1727 SkASSERT(over1s->fT < over1e->fT);
1728 SkASSERT(between(over1s->fT, tStart, over1e->fT));
1729 SkASSERT(between(over1s->fT, tEnd, over1e->fT));
1730 SkASSERT(over2s->fT < over2e->fT);
1731 SkASSERT(between(over2s->fT, tStart, over2e->fT));
1732 SkASSERT(between(over2s->fT, tEnd, over2e->fT));
1733 SkASSERT(over1s->segment() == over1e->segment());
1734 SkASSERT(over2s->segment() == over2e->segment());
1735 SkASSERT(over1s->segment() == over2s->segment());
1736 SkASSERT(over1s->segment() != coinSeg);
1737 SkASSERT(over1s->segment() != oppSeg);
1738 SkASSERT(coinSeg != oppSeg);
caryclark26ad22a2015-10-16 09:03:38 -07001739 double coinTs, coinTe, oppTs, oppTe;
caryclark8016b262016-09-06 05:59:47 -07001740 coinTs = TRange(over1s, tStart, coinSeg SkDEBUGPARAMS(over1e));
1741 coinTe = TRange(over1s, tEnd, coinSeg SkDEBUGPARAMS(over1e));
Cary Clarkba610292018-06-19 09:47:15 -04001742 SkOpSpanBase::Collapsed result = coinSeg->collapsed(coinTs, coinTe);
1743 if (SkOpSpanBase::Collapsed::kNo != result) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001744 return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, coinSeg);
caryclark8016b262016-09-06 05:59:47 -07001745 }
1746 oppTs = TRange(over2s, tStart, oppSeg SkDEBUGPARAMS(over2e));
1747 oppTe = TRange(over2s, tEnd, oppSeg SkDEBUGPARAMS(over2e));
Cary Clarkba610292018-06-19 09:47:15 -04001748 result = oppSeg->collapsed(oppTs, oppTe);
1749 if (SkOpSpanBase::Collapsed::kNo != result) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001750 return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, oppSeg);
caryclark8016b262016-09-06 05:59:47 -07001751 }
1752 if (coinTs > coinTe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001753 using std::swap;
1754 swap(coinTs, coinTe);
1755 swap(oppTs, oppTe);
caryclark26ad22a2015-10-16 09:03:38 -07001756 }
Cary Clarkba610292018-06-19 09:47:15 -04001757 this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, added);
1758 return;
caryclark26ad22a2015-10-16 09:03:38 -07001759}
1760
caryclark55888e42016-07-18 10:01:36 -07001761/* Commented-out lines keep this in sync addOrOverlap() */
caryclark30b9fdd2016-08-31 14:36:29 -07001762// If this is called by addEndMovedSpans(), a returned false propogates out to an abort.
1763// If this is called by AddIfMissing(), a returned false indicates there was nothing to add
Cary Clarkab87d7a2016-10-04 10:01:04 -04001764void SkOpCoincidence::debugAddOrOverlap(SkPathOpsDebug::GlitchLog* log,
caryclark30b9fdd2016-08-31 14:36:29 -07001765 const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
caryclark81a478c2016-09-09 09:37:57 -07001766 double coinTs, double coinTe, double oppTs, double oppTe, bool* added) const {
caryclark55888e42016-07-18 10:01:36 -07001767 SkTDArray<SkCoincidentSpans*> overlaps;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001768 SkOPASSERT(!fTop); // this is (correctly) reversed in addifMissing()
caryclark81a478c2016-09-09 09:37:57 -07001769 if (fTop && !this->checkOverlap(fTop, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe,
1770 &overlaps)) {
caryclark55888e42016-07-18 10:01:36 -07001771 return;
1772 }
1773 if (fHead && !this->checkOverlap(fHead, coinSeg, oppSeg, coinTs,
1774 coinTe, oppTs, oppTe, &overlaps)) {
1775 return;
1776 }
1777 const SkCoincidentSpans* overlap = overlaps.count() ? overlaps[0] : nullptr;
1778 for (int index = 1; index < overlaps.count(); ++index) { // combine overlaps before continuing
1779 const SkCoincidentSpans* test = overlaps[index];
1780 if (overlap->coinPtTStart()->fT > test->coinPtTStart()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001781 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTStart());
caryclark55888e42016-07-18 10:01:36 -07001782 }
1783 if (overlap->coinPtTEnd()->fT < test->coinPtTEnd()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001784 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07001785 }
1786 if (overlap->flipped()
1787 ? overlap->oppPtTStart()->fT < test->oppPtTStart()->fT
1788 : overlap->oppPtTStart()->fT > test->oppPtTStart()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001789 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTStart());
caryclark55888e42016-07-18 10:01:36 -07001790 }
1791 if (overlap->flipped()
1792 ? overlap->oppPtTEnd()->fT > test->oppPtTEnd()->fT
1793 : overlap->oppPtTEnd()->fT < test->oppPtTEnd()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001794 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07001795 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001796 if (!fHead) { this->debugRelease(log, fHead, test);
1797 this->debugRelease(log, fTop, test);
caryclark55888e42016-07-18 10:01:36 -07001798 }
1799 }
1800 const SkOpPtT* cs = coinSeg->existing(coinTs, oppSeg);
1801 const SkOpPtT* ce = coinSeg->existing(coinTe, oppSeg);
caryclark30b9fdd2016-08-31 14:36:29 -07001802 RETURN_FALSE_IF(overlap && cs && ce && overlap->contains(cs, ce), coinSeg);
1803 RETURN_FALSE_IF(cs != ce || !cs, coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001804 const SkOpPtT* os = oppSeg->existing(oppTs, coinSeg);
1805 const SkOpPtT* oe = oppSeg->existing(oppTe, coinSeg);
caryclark30b9fdd2016-08-31 14:36:29 -07001806 RETURN_FALSE_IF(overlap && os && oe && overlap->contains(os, oe), oppSeg);
caryclark55888e42016-07-18 10:01:36 -07001807 SkASSERT(true || !cs || !cs->deleted());
1808 SkASSERT(true || !os || !os->deleted());
1809 SkASSERT(true || !ce || !ce->deleted());
1810 SkASSERT(true || !oe || !oe->deleted());
1811 const SkOpPtT* csExisting = !cs ? coinSeg->existing(coinTs, nullptr) : nullptr;
1812 const SkOpPtT* ceExisting = !ce ? coinSeg->existing(coinTe, nullptr) : nullptr;
caryclark30b9fdd2016-08-31 14:36:29 -07001813 RETURN_FALSE_IF(csExisting && csExisting == ceExisting, coinSeg);
1814 RETURN_FALSE_IF(csExisting && (csExisting == ce ||
1815 csExisting->contains(ceExisting ? ceExisting : ce)), coinSeg);
1816 RETURN_FALSE_IF(ceExisting && (ceExisting == cs ||
1817 ceExisting->contains(csExisting ? csExisting : cs)), coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001818 const SkOpPtT* osExisting = !os ? oppSeg->existing(oppTs, nullptr) : nullptr;
1819 const SkOpPtT* oeExisting = !oe ? oppSeg->existing(oppTe, nullptr) : nullptr;
caryclark30b9fdd2016-08-31 14:36:29 -07001820 RETURN_FALSE_IF(osExisting && osExisting == oeExisting, oppSeg);
1821 RETURN_FALSE_IF(osExisting && (osExisting == oe ||
1822 osExisting->contains(oeExisting ? oeExisting : oe)), oppSeg);
1823 RETURN_FALSE_IF(oeExisting && (oeExisting == os ||
1824 oeExisting->contains(osExisting ? osExisting : os)), oppSeg);
caryclark55888e42016-07-18 10:01:36 -07001825 bool csDeleted = false, osDeleted = false, ceDeleted = false, oeDeleted = false;
1826 this->debugValidate();
1827 if (!cs || !os) {
1828 if (!cs)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001829 cs = coinSeg->debugAddT(coinTs, log);
caryclark55888e42016-07-18 10:01:36 -07001830 if (!os)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001831 os = oppSeg->debugAddT(oppTs, log);
caryclark30b9fdd2016-08-31 14:36:29 -07001832// RETURN_FALSE_IF(callerAborts, !csWritable || !osWritable);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001833 if (cs && os) cs->span()->debugAddOpp(log, os->span());
caryclark55888e42016-07-18 10:01:36 -07001834// cs = csWritable;
caryclark30b9fdd2016-08-31 14:36:29 -07001835// os = osWritable->active();
1836 RETURN_FALSE_IF((ce && ce->deleted()) || (oe && oe->deleted()), coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001837 }
1838 if (!ce || !oe) {
1839 if (!ce)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001840 ce = coinSeg->debugAddT(coinTe, log);
caryclark55888e42016-07-18 10:01:36 -07001841 if (!oe)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001842 oe = oppSeg->debugAddT(oppTe, log);
1843 if (ce && oe) ce->span()->debugAddOpp(log, oe->span());
caryclark55888e42016-07-18 10:01:36 -07001844// ce = ceWritable;
1845// oe = oeWritable;
1846 }
1847 this->debugValidate();
caryclark30b9fdd2016-08-31 14:36:29 -07001848 RETURN_FALSE_IF(csDeleted, coinSeg);
Ben Wagner63fd7602017-10-09 15:45:33 -04001849 RETURN_FALSE_IF(osDeleted, oppSeg);
1850 RETURN_FALSE_IF(ceDeleted, coinSeg);
caryclark30b9fdd2016-08-31 14:36:29 -07001851 RETURN_FALSE_IF(oeDeleted, oppSeg);
caryclark8016b262016-09-06 05:59:47 -07001852 RETURN_FALSE_IF(!cs || !ce || cs == ce || cs->contains(ce) || !os || !oe || os == oe || os->contains(oe), coinSeg);
1853 bool result = true;
caryclark55888e42016-07-18 10:01:36 -07001854 if (overlap) {
1855 if (overlap->coinPtTStart()->segment() == coinSeg) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001856 log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
caryclark55888e42016-07-18 10:01:36 -07001857 } else {
1858 if (oppTs > oppTe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001859 using std::swap;
1860 swap(coinTs, coinTe);
1861 swap(oppTs, oppTe);
caryclark55888e42016-07-18 10:01:36 -07001862 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001863 log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, oppSeg, oppTs, oppTe, coinSeg, coinTs, coinTe);
caryclark55888e42016-07-18 10:01:36 -07001864 }
caryclark8016b262016-09-06 05:59:47 -07001865#if 0 && DEBUG_COINCIDENCE_VERBOSE
1866 if (result) {
1867 overlap->debugShow();
1868 }
caryclark55888e42016-07-18 10:01:36 -07001869#endif
1870 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001871 log->record(SkPathOpsDebug::kAddMissingCoin_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
caryclark8016b262016-09-06 05:59:47 -07001872#if 0 && DEBUG_COINCIDENCE_VERBOSE
1873 fHead->debugShow();
caryclark55888e42016-07-18 10:01:36 -07001874#endif
1875 }
1876 this->debugValidate();
caryclark8016b262016-09-06 05:59:47 -07001877 return (void) result;
caryclark55888e42016-07-18 10:01:36 -07001878}
1879
1880// Extra commented-out lines keep this in sync with addMissing()
1881/* detects overlaps of different coincident runs on same segment */
1882/* does not detect overlaps for pairs without any segments in common */
1883// returns true if caller should loop again
Cary Clarkab87d7a2016-10-04 10:01:04 -04001884void SkOpCoincidence::debugAddMissing(SkPathOpsDebug::GlitchLog* log, bool* added) const {
caryclark26ad22a2015-10-16 09:03:38 -07001885 const SkCoincidentSpans* outer = fHead;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001886 *added = false;
caryclark26ad22a2015-10-16 09:03:38 -07001887 if (!outer) {
1888 return;
1889 }
caryclark55888e42016-07-18 10:01:36 -07001890 // fTop = outer;
1891 // fHead = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -07001892 do {
1893 // addifmissing can modify the list that this is walking
1894 // save head so that walker can iterate over old data unperturbed
1895 // addifmissing adds to head freely then add saved head in the end
caryclark8016b262016-09-06 05:59:47 -07001896 const SkOpPtT* ocs = outer->coinPtTStart();
1897 SkASSERT(!ocs->deleted());
1898 const SkOpSegment* outerCoin = ocs->segment();
1899 SkASSERT(!outerCoin->done()); // if it's done, should have already been removed from list
1900 const SkOpPtT* oos = outer->oppPtTStart();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001901 if (oos->deleted()) {
1902 return;
1903 }
caryclark8016b262016-09-06 05:59:47 -07001904 const SkOpSegment* outerOpp = oos->segment();
1905 SkASSERT(!outerOpp->done());
1906// SkOpSegment* outerCoinWritable = const_cast<SkOpSegment*>(outerCoin);
1907// SkOpSegment* outerOppWritable = const_cast<SkOpSegment*>(outerOpp);
caryclark26ad22a2015-10-16 09:03:38 -07001908 const SkCoincidentSpans* inner = outer;
caryclark55888e42016-07-18 10:01:36 -07001909 while ((inner = inner->next())) {
1910 this->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001911 double overS, overE;
caryclark8016b262016-09-06 05:59:47 -07001912 const SkOpPtT* ics = inner->coinPtTStart();
1913 SkASSERT(!ics->deleted());
1914 const SkOpSegment* innerCoin = ics->segment();
1915 SkASSERT(!innerCoin->done());
1916 const SkOpPtT* ios = inner->oppPtTStart();
1917 SkASSERT(!ios->deleted());
1918 const SkOpSegment* innerOpp = ios->segment();
1919 SkASSERT(!innerOpp->done());
1920// SkOpSegment* innerCoinWritable = const_cast<SkOpSegment*>(innerCoin);
1921// SkOpSegment* innerOppWritable = const_cast<SkOpSegment*>(innerOpp);
caryclark55888e42016-07-18 10:01:36 -07001922 if (outerCoin == innerCoin) {
caryclark8016b262016-09-06 05:59:47 -07001923 const SkOpPtT* oce = outer->coinPtTEnd();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001924 if (oce->deleted()) {
1925 return;
1926 }
caryclark8016b262016-09-06 05:59:47 -07001927 const SkOpPtT* ice = inner->coinPtTEnd();
1928 SkASSERT(!ice->deleted());
1929 if (outerOpp != innerOpp && this->overlap(ocs, oce, ics, ice, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001930 this->debugAddIfMissing(log, ocs->starter(oce), ics->starter(ice),
caryclark81a478c2016-09-09 09:37:57 -07001931 overS, overE, outerOpp, innerOpp, added,
caryclark8016b262016-09-06 05:59:47 -07001932 ocs->debugEnder(oce),
1933 ics->debugEnder(ice));
caryclark26ad22a2015-10-16 09:03:38 -07001934 }
caryclark55888e42016-07-18 10:01:36 -07001935 } else if (outerCoin == innerOpp) {
caryclark8016b262016-09-06 05:59:47 -07001936 const SkOpPtT* oce = outer->coinPtTEnd();
1937 SkASSERT(!oce->deleted());
1938 const SkOpPtT* ioe = inner->oppPtTEnd();
1939 SkASSERT(!ioe->deleted());
1940 if (outerOpp != innerCoin && this->overlap(ocs, oce, ios, ioe, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001941 this->debugAddIfMissing(log, ocs->starter(oce), ios->starter(ioe),
caryclark81a478c2016-09-09 09:37:57 -07001942 overS, overE, outerOpp, innerCoin, added,
caryclark8016b262016-09-06 05:59:47 -07001943 ocs->debugEnder(oce),
1944 ios->debugEnder(ioe));
caryclark26ad22a2015-10-16 09:03:38 -07001945 }
caryclark55888e42016-07-18 10:01:36 -07001946 } else if (outerOpp == innerCoin) {
caryclark8016b262016-09-06 05:59:47 -07001947 const SkOpPtT* ooe = outer->oppPtTEnd();
1948 SkASSERT(!ooe->deleted());
1949 const SkOpPtT* ice = inner->coinPtTEnd();
1950 SkASSERT(!ice->deleted());
caryclark55888e42016-07-18 10:01:36 -07001951 SkASSERT(outerCoin != innerOpp);
caryclark8016b262016-09-06 05:59:47 -07001952 if (this->overlap(oos, ooe, ics, ice, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001953 this->debugAddIfMissing(log, oos->starter(ooe), ics->starter(ice),
caryclark81a478c2016-09-09 09:37:57 -07001954 overS, overE, outerCoin, innerOpp, added,
caryclark8016b262016-09-06 05:59:47 -07001955 oos->debugEnder(ooe),
1956 ics->debugEnder(ice));
caryclark26ad22a2015-10-16 09:03:38 -07001957 }
caryclark55888e42016-07-18 10:01:36 -07001958 } else if (outerOpp == innerOpp) {
caryclark8016b262016-09-06 05:59:47 -07001959 const SkOpPtT* ooe = outer->oppPtTEnd();
1960 SkASSERT(!ooe->deleted());
1961 const SkOpPtT* ioe = inner->oppPtTEnd();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001962 if (ioe->deleted()) {
1963 return;
1964 }
caryclark55888e42016-07-18 10:01:36 -07001965 SkASSERT(outerCoin != innerCoin);
caryclark8016b262016-09-06 05:59:47 -07001966 if (this->overlap(oos, ooe, ios, ioe, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001967 this->debugAddIfMissing(log, oos->starter(ooe), ios->starter(ioe),
caryclark81a478c2016-09-09 09:37:57 -07001968 overS, overE, outerCoin, innerCoin, added,
caryclark8016b262016-09-06 05:59:47 -07001969 oos->debugEnder(ooe),
1970 ios->debugEnder(ioe));
caryclark26ad22a2015-10-16 09:03:38 -07001971 }
1972 }
caryclark55888e42016-07-18 10:01:36 -07001973 this->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001974 }
caryclark55888e42016-07-18 10:01:36 -07001975 } while ((outer = outer->next()));
1976 // this->restoreHead();
1977 return;
caryclark26ad22a2015-10-16 09:03:38 -07001978}
1979
caryclark55888e42016-07-18 10:01:36 -07001980// Commented-out lines keep this in sync with release()
Cary Clarkab87d7a2016-10-04 10:01:04 -04001981void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkCoincidentSpans* remove) const {
caryclark30b9fdd2016-08-31 14:36:29 -07001982 const SkCoincidentSpans* head = coin;
1983 const SkCoincidentSpans* prev = nullptr;
1984 const SkCoincidentSpans* next;
1985 do {
1986 next = coin->next();
1987 if (coin == remove) {
1988 if (prev) {
1989// prev->setNext(next);
1990 } else if (head == fHead) {
1991// fHead = next;
1992 } else {
1993// fTop = next;
1994 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001995 log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
caryclark30b9fdd2016-08-31 14:36:29 -07001996 }
1997 prev = coin;
1998 } while ((coin = next));
1999 return;
2000}
2001
Cary Clarkab87d7a2016-10-04 10:01:04 -04002002void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* deleted) const {
caryclark55888e42016-07-18 10:01:36 -07002003 const SkCoincidentSpans* coin = fHead;
2004 if (!coin) {
2005 return;
2006 }
2007 do {
2008 if (coin->coinPtTStart()->segment() == deleted
2009 || coin->coinPtTEnd()->segment() == deleted
2010 || coin->oppPtTStart()->segment() == deleted
2011 || coin->oppPtTEnd()->segment() == deleted) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002012 log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07002013 }
2014 } while ((coin = coin->next()));
2015}
2016
caryclark55888e42016-07-18 10:01:36 -07002017// Commented-out lines keep this in sync with expand()
2018// expand the range by checking adjacent spans for coincidence
Cary Clarkab87d7a2016-10-04 10:01:04 -04002019bool SkOpCoincidence::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07002020 const SkCoincidentSpans* coin = fHead;
2021 if (!coin) {
2022 return false;
2023 }
2024 bool expanded = false;
2025 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002026 if (coin->debugExpand(log)) {
caryclark55888e42016-07-18 10:01:36 -07002027 // check to see if multiple spans expanded so they are now identical
2028 const SkCoincidentSpans* test = fHead;
2029 do {
2030 if (coin == test) {
2031 continue;
2032 }
2033 if (coin->coinPtTStart() == test->coinPtTStart()
2034 && coin->oppPtTStart() == test->oppPtTStart()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002035 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, fHead, test->coinPtTStart());
caryclark55888e42016-07-18 10:01:36 -07002036 break;
2037 }
2038 } while ((test = test->next()));
2039 expanded = true;
caryclark26ad22a2015-10-16 09:03:38 -07002040 }
caryclark55888e42016-07-18 10:01:36 -07002041 } while ((coin = coin->next()));
caryclark26ad22a2015-10-16 09:03:38 -07002042 return expanded;
2043}
2044
caryclark55888e42016-07-18 10:01:36 -07002045// Commented-out lines keep this in sync with mark()
2046/* this sets up the coincidence links in the segments when the coincidence crosses multiple spans */
Cary Clarkab87d7a2016-10-04 10:01:04 -04002047void SkOpCoincidence::debugMark(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07002048 const SkCoincidentSpans* coin = fHead;
2049 if (!coin) {
2050 return;
2051 }
2052 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002053 FAIL_IF(!coin->coinPtTStartWritable()->span()->upCastable(), coin);
caryclark55888e42016-07-18 10:01:36 -07002054 const SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast();
2055// SkASSERT(start->deleted());
2056 const SkOpSpanBase* end = coin->coinPtTEndWritable()->span();
2057// SkASSERT(end->deleted());
2058 const SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span();
2059// SkASSERT(oStart->deleted());
2060 const SkOpSpanBase* oEnd = coin->oppPtTEndWritable()->span();
2061// SkASSERT(oEnd->deleted());
2062 bool flipped = coin->flipped();
caryclark26ad22a2015-10-16 09:03:38 -07002063 if (flipped) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002064 using std::swap;
2065 swap(oStart, oEnd);
caryclark26ad22a2015-10-16 09:03:38 -07002066 }
caryclark55888e42016-07-18 10:01:36 -07002067 /* coin and opp spans may not match up. Mark the ends, and then let the interior
2068 get marked as many times as the spans allow */
Cary Clarkab87d7a2016-10-04 10:01:04 -04002069 start->debugInsertCoincidence(log, oStart->upCast());
2070 end->debugInsertCoinEnd(log, oEnd);
caryclark55888e42016-07-18 10:01:36 -07002071 const SkOpSegment* segment = start->segment();
2072 const SkOpSegment* oSegment = oStart->segment();
caryclark26ad22a2015-10-16 09:03:38 -07002073 const SkOpSpanBase* next = start;
2074 const SkOpSpanBase* oNext = oStart;
caryclarka35ab3e2016-10-20 08:32:18 -07002075 bool ordered;
2076 FAIL_IF(!coin->ordered(&ordered), coin);
caryclark55888e42016-07-18 10:01:36 -07002077 while ((next = next->upCast()->next()) != end) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002078 FAIL_IF(!next->upCastable(), coin);
2079 if (next->upCast()->debugInsertCoincidence(log, oSegment, flipped, ordered), false) {
caryclark55888e42016-07-18 10:01:36 -07002080 return;
caryclark26ad22a2015-10-16 09:03:38 -07002081 }
caryclark55888e42016-07-18 10:01:36 -07002082 }
2083 while ((oNext = oNext->upCast()->next()) != oEnd) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002084 FAIL_IF(!oNext->upCastable(), coin);
2085 if (oNext->upCast()->debugInsertCoincidence(log, segment, flipped, ordered), false) {
caryclark55888e42016-07-18 10:01:36 -07002086 return;
caryclark26ad22a2015-10-16 09:03:38 -07002087 }
caryclark55888e42016-07-18 10:01:36 -07002088 }
2089 } while ((coin = coin->next()));
2090 return;
caryclark26ad22a2015-10-16 09:03:38 -07002091}
2092#endif
2093
Cary Clarkab87d7a2016-10-04 10:01:04 -04002094#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002095// Commented-out lines keep this in sync with markCollapsed()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002096void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkOpPtT* test) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002097 const SkCoincidentSpans* head = coin;
caryclark55888e42016-07-18 10:01:36 -07002098 while (coin) {
2099 if (coin->collapsed(test)) {
2100 if (zero_or_one(coin->coinPtTStart()->fT) && zero_or_one(coin->coinPtTEnd()->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002101 log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07002102 }
2103 if (zero_or_one(coin->oppPtTStart()->fT) && zero_or_one(coin->oppPtTEnd()->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002104 log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07002105 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002106 this->debugRelease(log, head, coin);
caryclark55888e42016-07-18 10:01:36 -07002107 }
2108 coin = coin->next();
caryclark624637c2015-05-11 07:21:27 -07002109 }
2110}
2111
caryclark55888e42016-07-18 10:01:36 -07002112// Commented-out lines keep this in sync with markCollapsed()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002113void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* test) const {
2114 this->debugMarkCollapsed(log, fHead, test);
2115 this->debugMarkCollapsed(log, fTop, test);
caryclark55888e42016-07-18 10:01:36 -07002116}
2117#endif
2118
2119void SkCoincidentSpans::debugShow() const {
caryclark6c3b9cd2016-09-26 05:36:58 -07002120 SkDebugf("coinSpan - id=%d t=%1.9g tEnd=%1.9g\n", coinPtTStart()->segment()->debugID(),
caryclark55888e42016-07-18 10:01:36 -07002121 coinPtTStart()->fT, coinPtTEnd()->fT);
caryclark6c3b9cd2016-09-26 05:36:58 -07002122 SkDebugf("coinSpan + id=%d t=%1.9g tEnd=%1.9g\n", oppPtTStart()->segment()->debugID(),
caryclark55888e42016-07-18 10:01:36 -07002123 oppPtTStart()->fT, oppPtTEnd()->fT);
2124}
2125
2126void SkOpCoincidence::debugShowCoincidence() const {
caryclark26ad22a2015-10-16 09:03:38 -07002127#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -07002128 const SkCoincidentSpans* span = fHead;
2129 while (span) {
2130 span->debugShow();
2131 span = span->next();
2132 }
2133#endif
2134}
2135
Cary Clarkab87d7a2016-10-04 10:01:04 -04002136#if DEBUG_COIN
caryclark6c3b9cd2016-09-26 05:36:58 -07002137static void DebugCheckBetween(const SkOpSpanBase* next, const SkOpSpanBase* end,
caryclark55888e42016-07-18 10:01:36 -07002138 double oStart, double oEnd, const SkOpSegment* oSegment,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002139 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002140 SkASSERT(next != end);
2141 SkASSERT(!next->contains(end) || log);
2142 if (next->t() > end->t()) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002143 using std::swap;
2144 swap(next, end);
caryclark55888e42016-07-18 10:01:36 -07002145 }
2146 do {
2147 const SkOpPtT* ptT = next->ptT();
2148 int index = 0;
caryclark27c015d2016-09-23 05:47:20 -07002149 bool somethingBetween = false;
caryclark55888e42016-07-18 10:01:36 -07002150 do {
2151 ++index;
2152 ptT = ptT->next();
2153 const SkOpPtT* checkPtT = next->ptT();
2154 if (ptT == checkPtT) {
2155 break;
2156 }
2157 bool looped = false;
2158 for (int check = 0; check < index; ++check) {
2159 if ((looped = checkPtT == ptT)) {
2160 break;
2161 }
2162 checkPtT = checkPtT->next();
2163 }
2164 if (looped) {
2165 SkASSERT(0);
2166 break;
2167 }
2168 if (ptT->deleted()) {
2169 continue;
2170 }
2171 if (ptT->segment() != oSegment) {
2172 continue;
2173 }
2174 somethingBetween |= between(oStart, ptT->fT, oEnd);
2175 } while (true);
2176 SkASSERT(somethingBetween);
2177 } while (next != end && (next = next->upCast()->next()));
2178}
2179
2180static void DebugCheckOverlap(const SkCoincidentSpans* test, const SkCoincidentSpans* list,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002181 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002182 if (!list) {
2183 return;
2184 }
2185 const SkOpSegment* coinSeg = test->coinPtTStart()->segment();
2186 SkASSERT(coinSeg == test->coinPtTEnd()->segment());
2187 const SkOpSegment* oppSeg = test->oppPtTStart()->segment();
2188 SkASSERT(oppSeg == test->oppPtTEnd()->segment());
2189 SkASSERT(coinSeg != test->oppPtTStart()->segment());
2190 SkDEBUGCODE(double tcs = test->coinPtTStart()->fT);
2191 SkASSERT(between(0, tcs, 1));
2192 SkDEBUGCODE(double tce = test->coinPtTEnd()->fT);
2193 SkASSERT(between(0, tce, 1));
2194 SkASSERT(tcs < tce);
2195 double tos = test->oppPtTStart()->fT;
2196 SkASSERT(between(0, tos, 1));
2197 double toe = test->oppPtTEnd()->fT;
2198 SkASSERT(between(0, toe, 1));
2199 SkASSERT(tos != toe);
2200 if (tos > toe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002201 using std::swap;
2202 swap(tos, toe);
caryclark55888e42016-07-18 10:01:36 -07002203 }
2204 do {
2205 double lcs, lce, los, loe;
2206 if (coinSeg == list->coinPtTStart()->segment()) {
2207 if (oppSeg != list->oppPtTStart()->segment()) {
2208 continue;
2209 }
2210 lcs = list->coinPtTStart()->fT;
2211 lce = list->coinPtTEnd()->fT;
2212 los = list->oppPtTStart()->fT;
2213 loe = list->oppPtTEnd()->fT;
2214 if (los > loe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002215 using std::swap;
2216 swap(los, loe);
caryclark55888e42016-07-18 10:01:36 -07002217 }
2218 } else if (coinSeg == list->oppPtTStart()->segment()) {
2219 if (oppSeg != list->coinPtTStart()->segment()) {
2220 continue;
2221 }
2222 lcs = list->oppPtTStart()->fT;
2223 lce = list->oppPtTEnd()->fT;
2224 if (lcs > lce) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002225 using std::swap;
2226 swap(lcs, lce);
caryclark55888e42016-07-18 10:01:36 -07002227 }
2228 los = list->coinPtTStart()->fT;
2229 loe = list->coinPtTEnd()->fT;
2230 } else {
2231 continue;
2232 }
2233 SkASSERT(tce < lcs || lce < tcs);
2234 SkASSERT(toe < los || loe < tos);
2235 } while ((list = list->next()));
2236}
2237
2238
2239static void DebugCheckOverlapTop(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002240 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002241 // check for overlapping coincident spans
2242 const SkCoincidentSpans* test = head;
2243 while (test) {
2244 const SkCoincidentSpans* next = test->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04002245 DebugCheckOverlap(test, next, log);
2246 DebugCheckOverlap(test, opt, log);
caryclark55888e42016-07-18 10:01:36 -07002247 test = next;
2248 }
2249}
2250
caryclark55888e42016-07-18 10:01:36 -07002251static void DebugValidate(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002252 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002253 // look for pts inside coincident spans that are not inside the opposite spans
2254 const SkCoincidentSpans* coin = head;
2255 while (coin) {
2256 SkASSERT(SkOpCoincidence::Ordered(coin->coinPtTStart()->segment(),
2257 coin->oppPtTStart()->segment()));
2258 SkASSERT(coin->coinPtTStart()->span()->ptT() == coin->coinPtTStart());
2259 SkASSERT(coin->coinPtTEnd()->span()->ptT() == coin->coinPtTEnd());
2260 SkASSERT(coin->oppPtTStart()->span()->ptT() == coin->oppPtTStart());
2261 SkASSERT(coin->oppPtTEnd()->span()->ptT() == coin->oppPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07002262 coin = coin->next();
2263 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002264 DebugCheckOverlapTop(head, opt, log);
caryclark55888e42016-07-18 10:01:36 -07002265}
2266#endif
2267
2268void SkOpCoincidence::debugValidate() const {
2269#if DEBUG_COINCIDENCE
Cary Clarkab87d7a2016-10-04 10:01:04 -04002270 DebugValidate(fHead, fTop, nullptr);
2271 DebugValidate(fTop, nullptr, nullptr);
caryclark55888e42016-07-18 10:01:36 -07002272#endif
2273}
2274
Cary Clarkab87d7a2016-10-04 10:01:04 -04002275#if DEBUG_COIN
caryclark6c3b9cd2016-09-26 05:36:58 -07002276static void DebugCheckBetween(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002277 SkPathOpsDebug::GlitchLog* log) {
caryclark6c3b9cd2016-09-26 05:36:58 -07002278 // look for pts inside coincident spans that are not inside the opposite spans
2279 const SkCoincidentSpans* coin = head;
2280 while (coin) {
2281 DebugCheckBetween(coin->coinPtTStart()->span(), coin->coinPtTEnd()->span(),
2282 coin->oppPtTStart()->fT, coin->oppPtTEnd()->fT, coin->oppPtTStart()->segment(),
Cary Clarkab87d7a2016-10-04 10:01:04 -04002283 log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002284 DebugCheckBetween(coin->oppPtTStart()->span(), coin->oppPtTEnd()->span(),
2285 coin->coinPtTStart()->fT, coin->coinPtTEnd()->fT, coin->coinPtTStart()->segment(),
Cary Clarkab87d7a2016-10-04 10:01:04 -04002286 log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002287 coin = coin->next();
2288 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002289 DebugCheckOverlapTop(head, opt, log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002290}
2291#endif
2292
2293void SkOpCoincidence::debugCheckBetween() const {
2294#if DEBUG_COINCIDENCE
2295 if (fGlobalState->debugCheckHealth()) {
2296 return;
2297 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002298 DebugCheckBetween(fHead, fTop, nullptr);
2299 DebugCheckBetween(fTop, nullptr, nullptr);
caryclark6c3b9cd2016-09-26 05:36:58 -07002300#endif
2301}
2302
Cary Clarkab87d7a2016-10-04 10:01:04 -04002303#if DEBUG_COIN
2304void SkOpContour::debugCheckHealth(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07002305 const SkOpSegment* segment = &fHead;
2306 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002307 segment->debugCheckHealth(log);
caryclark26ad22a2015-10-16 09:03:38 -07002308 } while ((segment = segment->next()));
2309}
2310
Cary Clarkab87d7a2016-10-04 10:01:04 -04002311void SkOpCoincidence::debugCheckValid(SkPathOpsDebug::GlitchLog* log) const {
2312#if DEBUG_VALIDATE
2313 DebugValidate(fHead, fTop, log);
2314 DebugValidate(fTop, nullptr, log);
2315#endif
2316}
2317
2318void SkOpCoincidence::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
2319 const SkCoincidentSpans* coin = fHead;
2320 if (!coin) {
2321 return;
2322 }
2323 do {
2324 coin->debugCorrectEnds(log);
2325 } while ((coin = coin->next()));
2326}
2327
caryclark55888e42016-07-18 10:01:36 -07002328// commmented-out lines keep this aligned with missingCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002329void SkOpContour::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07002330// SkASSERT(fCount > 0);
caryclark26ad22a2015-10-16 09:03:38 -07002331 const SkOpSegment* segment = &fHead;
caryclark55888e42016-07-18 10:01:36 -07002332// bool result = false;
caryclark26ad22a2015-10-16 09:03:38 -07002333 do {
Cary Clarke47ae292016-10-05 08:51:39 -04002334 if (segment->debugMissingCoincidence(log), false) {
caryclark55888e42016-07-18 10:01:36 -07002335// result = true;
caryclark55888e42016-07-18 10:01:36 -07002336 }
2337 segment = segment->next();
2338 } while (segment);
2339 return;
caryclark26ad22a2015-10-16 09:03:38 -07002340}
Cary Clarkab87d7a2016-10-04 10:01:04 -04002341
2342void SkOpContour::debugMoveMultiples(SkPathOpsDebug::GlitchLog* log) const {
2343 SkASSERT(fCount > 0);
2344 const SkOpSegment* segment = &fHead;
2345 do {
2346 if (segment->debugMoveMultiples(log), false) {
2347 return;
2348 }
2349 } while ((segment = segment->next()));
2350 return;
2351}
2352
2353void SkOpContour::debugMoveNearby(SkPathOpsDebug::GlitchLog* log) const {
2354 SkASSERT(fCount > 0);
2355 const SkOpSegment* segment = &fHead;
2356 do {
2357 segment->debugMoveNearby(log);
2358 } while ((segment = segment->next()));
2359}
caryclark26ad22a2015-10-16 09:03:38 -07002360#endif
2361
caryclark025b11e2016-08-25 05:21:14 -07002362#if DEBUG_COINCIDENCE_ORDER
2363void SkOpSegment::debugResetCoinT() const {
2364 fDebugBaseIndex = -1;
2365 fDebugBaseMin = 1;
2366 fDebugBaseMax = -1;
2367 fDebugLastIndex = -1;
2368 fDebugLastMin = 1;
2369 fDebugLastMax = -1;
2370}
2371#endif
2372
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002373void SkOpSegment::debugValidate() const {
caryclark025b11e2016-08-25 05:21:14 -07002374#if DEBUG_COINCIDENCE_ORDER
2375 {
2376 const SkOpSpanBase* span = &fHead;
2377 do {
2378 span->debugResetCoinT();
2379 } while (!span->final() && (span = span->upCast()->next()));
2380 span = &fHead;
2381 int index = 0;
2382 do {
2383 span->debugSetCoinT(index++);
2384 } while (!span->final() && (span = span->upCast()->next()));
2385 }
2386#endif
caryclark55888e42016-07-18 10:01:36 -07002387#if DEBUG_COINCIDENCE
2388 if (this->globalState()->debugCheckHealth()) {
2389 return;
2390 }
2391#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002392#if DEBUG_VALIDATE
caryclark54359292015-03-26 07:52:43 -07002393 const SkOpSpanBase* span = &fHead;
2394 double lastT = -1;
halcanary96fcdcc2015-08-27 07:41:13 -07002395 const SkOpSpanBase* prev = nullptr;
caryclark54359292015-03-26 07:52:43 -07002396 int count = 0;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002397 int done = 0;
caryclark54359292015-03-26 07:52:43 -07002398 do {
2399 if (!span->final()) {
2400 ++count;
2401 done += span->upCast()->done() ? 1 : 0;
2402 }
2403 SkASSERT(span->segment() == this);
2404 SkASSERT(!prev || prev->upCast()->next() == span);
2405 SkASSERT(!prev || prev == span->prev());
2406 prev = span;
2407 double t = span->ptT()->fT;
2408 SkASSERT(lastT < t);
2409 lastT = t;
2410 span->debugValidate();
2411 } while (!span->final() && (span = span->upCast()->next()));
2412 SkASSERT(count == fCount);
2413 SkASSERT(done == fDoneCount);
caryclark08bc8482015-04-24 09:08:57 -07002414 SkASSERT(count >= fDoneCount);
caryclark54359292015-03-26 07:52:43 -07002415 SkASSERT(span->final());
2416 span->debugValidate();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002417#endif
caryclark54359292015-03-26 07:52:43 -07002418}
2419
Cary Clarkab87d7a2016-10-04 10:01:04 -04002420#if DEBUG_COIN
caryclark30b9fdd2016-08-31 14:36:29 -07002421
2422// Commented-out lines keep this in sync with addOpp()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002423void SkOpSpanBase::debugAddOpp(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002424 const SkOpPtT* oppPrev = this->ptT()->oppPrev(opp->ptT());
2425 if (!oppPrev) {
caryclark55888e42016-07-18 10:01:36 -07002426 return;
caryclark26ad22a2015-10-16 09:03:38 -07002427 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002428 this->debugMergeMatches(log, opp);
caryclark30b9fdd2016-08-31 14:36:29 -07002429 this->ptT()->debugAddOpp(opp->ptT(), oppPrev);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002430 this->debugCheckForCollapsedCoincidence(log);
caryclark26ad22a2015-10-16 09:03:38 -07002431}
2432
caryclark55888e42016-07-18 10:01:36 -07002433// Commented-out lines keep this in sync with checkForCollapsedCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002434void SkOpSpanBase::debugCheckForCollapsedCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07002435 const SkOpCoincidence* coins = this->globalState()->coincidence();
2436 if (coins->isEmpty()) {
2437 return;
2438 }
2439// the insert above may have put both ends of a coincident run in the same span
2440// for each coincident ptT in loop; see if its opposite in is also in the loop
2441// this implementation is the motivation for marking that a ptT is referenced by a coincident span
2442 const SkOpPtT* head = this->ptT();
2443 const SkOpPtT* test = head;
caryclark26ad22a2015-10-16 09:03:38 -07002444 do {
caryclark55888e42016-07-18 10:01:36 -07002445 if (!test->coincident()) {
2446 continue;
caryclark26ad22a2015-10-16 09:03:38 -07002447 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002448 coins->debugMarkCollapsed(log, test);
caryclark55888e42016-07-18 10:01:36 -07002449 } while ((test = test->next()) != head);
caryclark26ad22a2015-10-16 09:03:38 -07002450}
caryclark55888e42016-07-18 10:01:36 -07002451#endif
caryclark26ad22a2015-10-16 09:03:38 -07002452
caryclark54359292015-03-26 07:52:43 -07002453bool SkOpSpanBase::debugCoinEndLoopCheck() const {
2454 int loop = 0;
2455 const SkOpSpanBase* next = this;
2456 SkOpSpanBase* nextCoin;
2457 do {
2458 nextCoin = next->fCoinEnd;
2459 SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin);
2460 for (int check = 1; check < loop - 1; ++check) {
2461 const SkOpSpanBase* checkCoin = this->fCoinEnd;
2462 const SkOpSpanBase* innerCoin = checkCoin;
2463 for (int inner = check + 1; inner < loop; ++inner) {
2464 innerCoin = innerCoin->fCoinEnd;
2465 if (checkCoin == innerCoin) {
2466 SkDebugf("*** bad coincident end loop ***\n");
2467 return false;
2468 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002469 }
2470 }
caryclark54359292015-03-26 07:52:43 -07002471 ++loop;
2472 } while ((next = nextCoin) && next != this);
2473 return true;
2474}
2475
Cary Clarkab87d7a2016-10-04 10:01:04 -04002476#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002477// Commented-out lines keep this in sync with insertCoinEnd()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002478void SkOpSpanBase::debugInsertCoinEnd(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* coin) const {
caryclark55888e42016-07-18 10:01:36 -07002479 if (containsCoinEnd(coin)) {
2480// SkASSERT(coin->containsCoinEnd(this));
2481 return;
2482 }
2483 debugValidate();
2484// SkASSERT(this != coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002485 log->record(SkPathOpsDebug::kMarkCoinEnd_Glitch, this, coin);
caryclark55888e42016-07-18 10:01:36 -07002486// coin->fCoinEnd = this->fCoinEnd;
2487// this->fCoinEnd = coinNext;
2488 debugValidate();
2489}
2490
caryclark30b9fdd2016-08-31 14:36:29 -07002491// Commented-out lines keep this in sync with mergeMatches()
2492// Look to see if pt-t linked list contains same segment more than once
2493// if so, and if each pt-t is directly pointed to by spans in that segment,
2494// merge them
2495// keep the points, but remove spans so that the segment doesn't have 2 or more
2496// spans pointing to the same pt-t loop at different loop elements
Cary Clarkab87d7a2016-10-04 10:01:04 -04002497void SkOpSpanBase::debugMergeMatches(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002498 const SkOpPtT* test = &fPtT;
2499 const SkOpPtT* testNext;
2500 const SkOpPtT* stop = test;
2501 do {
2502 testNext = test->next();
2503 if (test->deleted()) {
2504 continue;
2505 }
2506 const SkOpSpanBase* testBase = test->span();
2507 SkASSERT(testBase->ptT() == test);
2508 const SkOpSegment* segment = test->segment();
2509 if (segment->done()) {
2510 continue;
2511 }
2512 const SkOpPtT* inner = opp->ptT();
2513 const SkOpPtT* innerStop = inner;
2514 do {
2515 if (inner->segment() != segment) {
2516 continue;
2517 }
2518 if (inner->deleted()) {
2519 continue;
2520 }
2521 const SkOpSpanBase* innerBase = inner->span();
2522 SkASSERT(innerBase->ptT() == inner);
Ben Wagner63fd7602017-10-09 15:45:33 -04002523 // when the intersection is first detected, the span base is marked if there are
caryclark30b9fdd2016-08-31 14:36:29 -07002524 // more than one point in the intersection.
2525// if (!innerBase->hasMultipleHint() && !testBase->hasMultipleHint()) {
2526 if (!zero_or_one(inner->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002527 log->record(SkPathOpsDebug::kMergeMatches_Glitch, innerBase, test);
caryclark30b9fdd2016-08-31 14:36:29 -07002528 } else {
2529 SkASSERT(inner->fT != test->fT);
2530 if (!zero_or_one(test->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002531 log->record(SkPathOpsDebug::kMergeMatches_Glitch, testBase, inner);
caryclark30b9fdd2016-08-31 14:36:29 -07002532 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002533 log->record(SkPathOpsDebug::kMergeMatches_Glitch, segment);
caryclark30b9fdd2016-08-31 14:36:29 -07002534// SkDEBUGCODE(testBase->debugSetDeleted());
2535// test->setDeleted();
2536// SkDEBUGCODE(innerBase->debugSetDeleted());
2537// inner->setDeleted();
2538 }
2539 }
2540#ifdef SK_DEBUG // assert if another undeleted entry points to segment
2541 const SkOpPtT* debugInner = inner;
2542 while ((debugInner = debugInner->next()) != innerStop) {
2543 if (debugInner->segment() != segment) {
2544 continue;
2545 }
2546 if (debugInner->deleted()) {
2547 continue;
2548 }
2549 SkOPASSERT(0);
2550 }
2551#endif
Ben Wagner63fd7602017-10-09 15:45:33 -04002552 break;
caryclark30b9fdd2016-08-31 14:36:29 -07002553// }
2554 break;
2555 } while ((inner = inner->next()) != innerStop);
2556 } while ((test = testNext) != stop);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002557 this->debugCheckForCollapsedCoincidence(log);
caryclark30b9fdd2016-08-31 14:36:29 -07002558}
2559
caryclark55888e42016-07-18 10:01:36 -07002560#endif
caryclark26ad22a2015-10-16 09:03:38 -07002561
caryclark025b11e2016-08-25 05:21:14 -07002562void SkOpSpanBase::debugResetCoinT() const {
2563#if DEBUG_COINCIDENCE_ORDER
2564 const SkOpPtT* ptT = &fPtT;
2565 do {
2566 ptT->debugResetCoinT();
2567 ptT = ptT->next();
2568 } while (ptT != &fPtT);
2569#endif
2570}
2571
2572void SkOpSpanBase::debugSetCoinT(int index) const {
2573#if DEBUG_COINCIDENCE_ORDER
2574 const SkOpPtT* ptT = &fPtT;
2575 do {
2576 if (!ptT->deleted()) {
2577 ptT->debugSetCoinT(index);
2578 }
2579 ptT = ptT->next();
2580 } while (ptT != &fPtT);
2581#endif
2582}
2583
caryclark26ad22a2015-10-16 09:03:38 -07002584const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const {
2585 const SkOpSpanBase* end = *endPtr;
2586 SkASSERT(this->segment() == end->segment());
2587 const SkOpSpanBase* result;
2588 if (t() < end->t()) {
2589 result = this;
2590 } else {
2591 result = end;
2592 *endPtr = this;
2593 }
2594 return result->upCast();
2595}
2596
caryclark54359292015-03-26 07:52:43 -07002597void SkOpSpanBase::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07002598#if DEBUG_COINCIDENCE
2599 if (this->globalState()->debugCheckHealth()) {
2600 return;
2601 }
2602#endif
caryclark54359292015-03-26 07:52:43 -07002603#if DEBUG_VALIDATE
2604 const SkOpPtT* ptT = &fPtT;
2605 SkASSERT(ptT->span() == this);
2606 do {
2607// SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt));
2608 ptT->debugValidate();
2609 ptT = ptT->next();
2610 } while (ptT != &fPtT);
2611 SkASSERT(this->debugCoinEndLoopCheck());
2612 if (!this->final()) {
2613 SkASSERT(this->upCast()->debugCoinLoopCheck());
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002614 }
caryclark54359292015-03-26 07:52:43 -07002615 if (fFromAngle) {
2616 fFromAngle->debugValidate();
2617 }
2618 if (!this->final() && this->upCast()->toAngle()) {
2619 this->upCast()->toAngle()->debugValidate();
2620 }
2621#endif
2622}
2623
2624bool SkOpSpan::debugCoinLoopCheck() const {
2625 int loop = 0;
2626 const SkOpSpan* next = this;
2627 SkOpSpan* nextCoin;
2628 do {
2629 nextCoin = next->fCoincident;
2630 SkASSERT(nextCoin == this || nextCoin->fCoincident != nextCoin);
2631 for (int check = 1; check < loop - 1; ++check) {
2632 const SkOpSpan* checkCoin = this->fCoincident;
2633 const SkOpSpan* innerCoin = checkCoin;
2634 for (int inner = check + 1; inner < loop; ++inner) {
2635 innerCoin = innerCoin->fCoincident;
2636 if (checkCoin == innerCoin) {
2637 SkDebugf("*** bad coincident loop ***\n");
2638 return false;
2639 }
2640 }
2641 }
2642 ++loop;
2643 } while ((next = nextCoin) && next != this);
2644 return true;
2645}
2646
Cary Clarkab87d7a2016-10-04 10:01:04 -04002647#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002648// Commented-out lines keep this in sync with insertCoincidence() in header
Cary Clarkab87d7a2016-10-04 10:01:04 -04002649void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* coin) const {
caryclark55888e42016-07-18 10:01:36 -07002650 if (containsCoincidence(coin)) {
2651// SkASSERT(coin->containsCoincidence(this));
2652 return;
2653 }
2654 debugValidate();
2655// SkASSERT(this != coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002656 log->record(SkPathOpsDebug::kMarkCoinStart_Glitch, this, coin);
caryclark55888e42016-07-18 10:01:36 -07002657// coin->fCoincident = this->fCoincident;
2658// this->fCoincident = coinNext;
2659 debugValidate();
2660}
2661
2662// Commented-out lines keep this in sync with insertCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002663void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* segment, bool flipped, bool ordered) const {
caryclark55888e42016-07-18 10:01:36 -07002664 if (this->containsCoincidence(segment)) {
2665 return;
2666 }
2667 const SkOpPtT* next = &fPtT;
2668 while ((next = next->next()) != &fPtT) {
2669 if (next->segment() == segment) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002670 const SkOpSpan* span;
2671 const SkOpSpanBase* base = next->span();
2672 if (!ordered) {
2673 const SkOpSpanBase* spanEnd = fNext->contains(segment)->span();
2674 const SkOpPtT* start = base->ptT()->starter(spanEnd->ptT());
2675 FAIL_IF(!start->span()->upCastable(), this);
2676 span = const_cast<SkOpSpan*>(start->span()->upCast());
2677 }
2678 else if (flipped) {
2679 span = base->prev();
2680 FAIL_IF(!span, this);
2681 }
2682 else {
2683 FAIL_IF(!base->upCastable(), this);
2684 span = base->upCast();
2685 }
2686 log->record(SkPathOpsDebug::kMarkCoinInsert_Glitch, span);
caryclark55888e42016-07-18 10:01:36 -07002687 return;
2688 }
2689 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002690#if DEBUG_COIN
2691 log->record(SkPathOpsDebug::kMarkCoinMissing_Glitch, segment, this);
caryclark55888e42016-07-18 10:01:36 -07002692#endif
Cary Clarkab87d7a2016-10-04 10:01:04 -04002693 return;
caryclark55888e42016-07-18 10:01:36 -07002694}
2695#endif
2696
caryclark624637c2015-05-11 07:21:27 -07002697// called only by test code
2698int SkIntersections::debugCoincidentUsed() const {
2699 if (!fIsCoincident[0]) {
2700 SkASSERT(!fIsCoincident[1]);
2701 return 0;
2702 }
2703 int count = 0;
2704 SkDEBUGCODE(int count2 = 0;)
2705 for (int index = 0; index < fUsed; ++index) {
2706 if (fIsCoincident[0] & (1 << index)) {
2707 ++count;
2708 }
2709#ifdef SK_DEBUG
2710 if (fIsCoincident[1] & (1 << index)) {
2711 ++count2;
2712 }
2713#endif
2714 }
2715 SkASSERT(count == count2);
2716 return count;
2717}
2718
Mike Kleinc0bd9f92019-04-23 12:05:21 -05002719#include "src/pathops/SkOpContour.h"
caryclark54359292015-03-26 07:52:43 -07002720
caryclark55888e42016-07-18 10:01:36 -07002721// Commented-out lines keep this in sync with addOpp()
caryclark29b25632016-08-25 11:27:17 -07002722void SkOpPtT::debugAddOpp(const SkOpPtT* opp, const SkOpPtT* oppPrev) const {
2723 SkDEBUGCODE(const SkOpPtT* oldNext = this->fNext);
caryclark55888e42016-07-18 10:01:36 -07002724 SkASSERT(this != opp);
2725// this->fNext = opp;
caryclark29b25632016-08-25 11:27:17 -07002726 SkASSERT(oppPrev != oldNext);
caryclark55888e42016-07-18 10:01:36 -07002727// oppPrev->fNext = oldNext;
caryclark55888e42016-07-18 10:01:36 -07002728}
2729
caryclark26ad22a2015-10-16 09:03:38 -07002730bool SkOpPtT::debugContains(const SkOpPtT* check) const {
2731 SkASSERT(this != check);
2732 const SkOpPtT* ptT = this;
2733 int links = 0;
2734 do {
2735 ptT = ptT->next();
2736 if (ptT == check) {
2737 return true;
2738 }
2739 ++links;
2740 const SkOpPtT* test = this;
2741 for (int index = 0; index < links; ++index) {
2742 if (ptT == test) {
2743 return false;
2744 }
2745 test = test->next();
2746 }
2747 } while (true);
2748}
2749
2750const SkOpPtT* SkOpPtT::debugContains(const SkOpSegment* check) const {
2751 SkASSERT(this->segment() != check);
2752 const SkOpPtT* ptT = this;
2753 int links = 0;
2754 do {
2755 ptT = ptT->next();
2756 if (ptT->segment() == check) {
2757 return ptT;
2758 }
2759 ++links;
2760 const SkOpPtT* test = this;
2761 for (int index = 0; index < links; ++index) {
2762 if (ptT == test) {
2763 return nullptr;
2764 }
2765 test = test->next();
2766 }
2767 } while (true);
2768}
2769
caryclark8016b262016-09-06 05:59:47 -07002770const SkOpPtT* SkOpPtT::debugEnder(const SkOpPtT* end) const {
2771 return fT < end->fT ? end : this;
2772}
2773
caryclark54359292015-03-26 07:52:43 -07002774int SkOpPtT::debugLoopLimit(bool report) const {
2775 int loop = 0;
2776 const SkOpPtT* next = this;
2777 do {
2778 for (int check = 1; check < loop - 1; ++check) {
2779 const SkOpPtT* checkPtT = this->fNext;
2780 const SkOpPtT* innerPtT = checkPtT;
2781 for (int inner = check + 1; inner < loop; ++inner) {
2782 innerPtT = innerPtT->fNext;
2783 if (checkPtT == innerPtT) {
2784 if (report) {
2785 SkDebugf("*** bad ptT loop ***\n");
2786 }
2787 return loop;
2788 }
2789 }
2790 }
caryclark26ad22a2015-10-16 09:03:38 -07002791 // there's nothing wrong with extremely large loop counts -- but this may appear to hang
2792 // by taking a very long time to figure out that no loop entry is a duplicate
2793 // -- and it's likely that a large loop count is indicative of a bug somewhere
2794 if (++loop > 1000) {
2795 SkDebugf("*** loop count exceeds 1000 ***\n");
2796 return 1000;
2797 }
caryclark54359292015-03-26 07:52:43 -07002798 } while ((next = next->fNext) && next != this);
2799 return 0;
2800}
2801
caryclark29b25632016-08-25 11:27:17 -07002802const SkOpPtT* SkOpPtT::debugOppPrev(const SkOpPtT* opp) const {
2803 return this->oppPrev(const_cast<SkOpPtT*>(opp));
2804}
2805
caryclark025b11e2016-08-25 05:21:14 -07002806void SkOpPtT::debugResetCoinT() const {
2807#if DEBUG_COINCIDENCE_ORDER
Ben Wagner63fd7602017-10-09 15:45:33 -04002808 this->segment()->debugResetCoinT();
caryclark025b11e2016-08-25 05:21:14 -07002809#endif
2810}
2811
2812void SkOpPtT::debugSetCoinT(int index) const {
2813#if DEBUG_COINCIDENCE_ORDER
Ben Wagner63fd7602017-10-09 15:45:33 -04002814 this->segment()->debugSetCoinT(index, fT);
caryclark025b11e2016-08-25 05:21:14 -07002815#endif
2816}
2817
caryclark54359292015-03-26 07:52:43 -07002818void SkOpPtT::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07002819#if DEBUG_COINCIDENCE
2820 if (this->globalState()->debugCheckHealth()) {
2821 return;
2822 }
2823#endif
caryclark54359292015-03-26 07:52:43 -07002824#if DEBUG_VALIDATE
Cary Clarkab87d7a2016-10-04 10:01:04 -04002825 SkOpPhase phase = contour()->globalState()->phase();
2826 if (phase == SkOpPhase::kIntersecting || phase == SkOpPhase::kFixWinding) {
caryclark54359292015-03-26 07:52:43 -07002827 return;
2828 }
2829 SkASSERT(fNext);
2830 SkASSERT(fNext != this);
2831 SkASSERT(fNext->fNext);
2832 SkASSERT(debugLoopLimit(false) == 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002833#endif
2834}
caryclark1049f122015-04-20 08:31:59 -07002835
2836static void output_scalar(SkScalar num) {
2837 if (num == (int) num) {
2838 SkDebugf("%d", (int) num);
2839 } else {
2840 SkString str;
2841 str.printf("%1.9g", num);
2842 int width = (int) str.size();
2843 const char* cStr = str.c_str();
2844 while (cStr[width - 1] == '0') {
2845 --width;
2846 }
2847 str.resize(width);
2848 SkDebugf("%sf", str.c_str());
2849 }
2850}
2851
2852static void output_points(const SkPoint* pts, int count) {
2853 for (int index = 0; index < count; ++index) {
2854 output_scalar(pts[index].fX);
2855 SkDebugf(", ");
2856 output_scalar(pts[index].fY);
2857 if (index + 1 < count) {
2858 SkDebugf(", ");
2859 }
2860 }
2861}
2862
2863static void showPathContours(SkPath::RawIter& iter, const char* pathName) {
2864 uint8_t verb;
2865 SkPoint pts[4];
2866 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
2867 switch (verb) {
2868 case SkPath::kMove_Verb:
2869 SkDebugf(" %s.moveTo(", pathName);
2870 output_points(&pts[0], 1);
2871 SkDebugf(");\n");
2872 continue;
2873 case SkPath::kLine_Verb:
2874 SkDebugf(" %s.lineTo(", pathName);
2875 output_points(&pts[1], 1);
2876 SkDebugf(");\n");
2877 break;
2878 case SkPath::kQuad_Verb:
2879 SkDebugf(" %s.quadTo(", pathName);
2880 output_points(&pts[1], 2);
2881 SkDebugf(");\n");
2882 break;
2883 case SkPath::kConic_Verb:
2884 SkDebugf(" %s.conicTo(", pathName);
2885 output_points(&pts[1], 2);
2886 SkDebugf(", %1.9gf);\n", iter.conicWeight());
2887 break;
2888 case SkPath::kCubic_Verb:
2889 SkDebugf(" %s.cubicTo(", pathName);
2890 output_points(&pts[1], 3);
2891 SkDebugf(");\n");
2892 break;
2893 case SkPath::kClose_Verb:
2894 SkDebugf(" %s.close();\n", pathName);
2895 break;
2896 default:
2897 SkDEBUGFAIL("bad verb");
2898 return;
2899 }
2900 }
2901}
2902
2903static const char* gFillTypeStr[] = {
Mike Reed3e7af412019-11-26 03:34:16 +00002904 "kWinding_FillType",
2905 "kEvenOdd_FillType",
2906 "kInverseWinding_FillType",
2907 "kInverseEvenOdd_FillType"
caryclark1049f122015-04-20 08:31:59 -07002908};
2909
2910void SkPathOpsDebug::ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration) {
2911 SkPath::RawIter iter(path);
2912#define SUPPORT_RECT_CONTOUR_DETECTION 0
2913#if SUPPORT_RECT_CONTOUR_DETECTION
halcanary96fcdcc2015-08-27 07:41:13 -07002914 int rectCount = path.isRectContours() ? path.rectContours(nullptr, nullptr) : 0;
caryclark1049f122015-04-20 08:31:59 -07002915 if (rectCount > 0) {
2916 SkTDArray<SkRect> rects;
Mike Reed30bc5272019-11-22 18:34:02 +00002917 SkTDArray<SkPathDirection> directions;
caryclark1049f122015-04-20 08:31:59 -07002918 rects.setCount(rectCount);
2919 directions.setCount(rectCount);
2920 path.rectContours(rects.begin(), directions.begin());
2921 for (int contour = 0; contour < rectCount; ++contour) {
2922 const SkRect& rect = rects[contour];
2923 SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
Mike Reed30bc5272019-11-22 18:34:02 +00002924 rect.fRight, rect.fBottom, directions[contour] == SkPathDirection::kCCW
2925 ? "SkPathDirection::kCCW" : "SkPathDirection::kCW");
caryclark1049f122015-04-20 08:31:59 -07002926 }
2927 return;
2928 }
2929#endif
Mike Reed3e7af412019-11-26 03:34:16 +00002930 SkPath::FillType fillType = path.getFillType();
2931 SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType);
caryclark1049f122015-04-20 08:31:59 -07002932 if (includeDeclaration) {
2933 SkDebugf(" SkPath %s;\n", name);
2934 }
Mike Reed3e7af412019-11-26 03:34:16 +00002935 SkDebugf(" %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[fillType]);
caryclark1049f122015-04-20 08:31:59 -07002936 iter.setPath(path);
2937 showPathContours(iter, name);
2938}
caryclark13260682016-10-24 05:10:14 -07002939
Cary Clark918fb1f2016-11-15 13:22:25 -05002940#if DEBUG_DUMP_VERIFY
Mike Kleinc0bd9f92019-04-23 12:05:21 -05002941#include "include/core/SkData.h"
2942#include "include/core/SkStream.h"
caryclark13260682016-10-24 05:10:14 -07002943
2944static void dump_path(FILE* file, const SkPath& path, bool force, bool dumpAsHex) {
2945 SkDynamicMemoryWStream wStream;
2946 path.dump(&wStream, force, dumpAsHex);
2947 sk_sp<SkData> data(wStream.detachAsData());
2948 fprintf(file, "%.*s\n", (int) data->size(), (char*) data->data());
2949}
2950
2951static int dumpID = 0;
2952
2953void SkPathOpsDebug::DumpOp(const SkPath& one, const SkPath& two, SkPathOp op,
2954 const char* testName) {
2955 FILE* file = sk_fopen("op_dump.txt", kWrite_SkFILE_Flag);
2956 DumpOp(file, one, two, op, testName);
2957}
2958
2959void SkPathOpsDebug::DumpOp(FILE* file, const SkPath& one, const SkPath& two, SkPathOp op,
2960 const char* testName) {
2961 const char* name = testName ? testName : "op";
2962 fprintf(file,
2963 "\nstatic void %s_%d(skiatest::Reporter* reporter, const char* filename) {\n",
2964 name, ++dumpID);
2965 fprintf(file, " SkPath path;\n");
2966 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", one.getFillType());
2967 dump_path(file, one, false, true);
2968 fprintf(file, " SkPath path1(path);\n");
2969 fprintf(file, " path.reset();\n");
2970 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", two.getFillType());
2971 dump_path(file, two, false, true);
2972 fprintf(file, " SkPath path2(path);\n");
2973 fprintf(file, " testPathOp(reporter, path1, path2, (SkPathOp) %d, filename);\n", op);
2974 fprintf(file, "}\n\n");
2975 fclose(file);
2976}
2977
2978void SkPathOpsDebug::DumpSimplify(const SkPath& path, const char* testName) {
2979 FILE* file = sk_fopen("simplify_dump.txt", kWrite_SkFILE_Flag);
2980 DumpSimplify(file, path, testName);
2981}
2982
2983void SkPathOpsDebug::DumpSimplify(FILE* file, const SkPath& path, const char* testName) {
2984 const char* name = testName ? testName : "simplify";
2985 fprintf(file,
2986 "\nstatic void %s_%d(skiatest::Reporter* reporter, const char* filename) {\n",
2987 name, ++dumpID);
2988 fprintf(file, " SkPath path;\n");
2989 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", path.getFillType());
2990 dump_path(file, path, false, true);
2991 fprintf(file, " testSimplify(reporter, path, filename);\n");
2992 fprintf(file, "}\n\n");
2993 fclose(file);
2994}
2995
Mike Kleinc0bd9f92019-04-23 12:05:21 -05002996#include "include/core/SkBitmap.h"
2997#include "include/core/SkCanvas.h"
2998#include "include/core/SkPaint.h"
caryclark13260682016-10-24 05:10:14 -07002999
3000const int bitWidth = 64;
3001const int bitHeight = 64;
3002
3003static void debug_scale_matrix(const SkPath& one, const SkPath* two, SkMatrix& scale) {
3004 SkRect larger = one.getBounds();
3005 if (two) {
3006 larger.join(two->getBounds());
3007 }
3008 SkScalar largerWidth = larger.width();
3009 if (largerWidth < 4) {
3010 largerWidth = 4;
3011 }
3012 SkScalar largerHeight = larger.height();
3013 if (largerHeight < 4) {
3014 largerHeight = 4;
3015 }
3016 SkScalar hScale = (bitWidth - 2) / largerWidth;
3017 SkScalar vScale = (bitHeight - 2) / largerHeight;
3018 scale.reset();
3019 scale.preScale(hScale, vScale);
3020 larger.fLeft *= hScale;
3021 larger.fRight *= hScale;
3022 larger.fTop *= vScale;
3023 larger.fBottom *= vScale;
3024 SkScalar dx = -16000 > larger.fLeft ? -16000 - larger.fLeft
3025 : 16000 < larger.fRight ? 16000 - larger.fRight : 0;
3026 SkScalar dy = -16000 > larger.fTop ? -16000 - larger.fTop
3027 : 16000 < larger.fBottom ? 16000 - larger.fBottom : 0;
3028 scale.preTranslate(dx, dy);
3029}
3030
3031static int debug_paths_draw_the_same(const SkPath& one, const SkPath& two, SkBitmap& bits) {
3032 if (bits.width() == 0) {
3033 bits.allocN32Pixels(bitWidth * 2, bitHeight);
3034 }
3035 SkCanvas canvas(bits);
3036 canvas.drawColor(SK_ColorWHITE);
3037 SkPaint paint;
3038 canvas.save();
3039 const SkRect& bounds1 = one.getBounds();
3040 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
3041 canvas.drawPath(one, paint);
3042 canvas.restore();
3043 canvas.save();
3044 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
3045 canvas.drawPath(two, paint);
3046 canvas.restore();
3047 int errors = 0;
3048 for (int y = 0; y < bitHeight - 1; ++y) {
3049 uint32_t* addr1 = bits.getAddr32(0, y);
3050 uint32_t* addr2 = bits.getAddr32(0, y + 1);
3051 uint32_t* addr3 = bits.getAddr32(bitWidth, y);
3052 uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1);
3053 for (int x = 0; x < bitWidth - 1; ++x) {
3054 // count 2x2 blocks
3055 bool err = addr1[x] != addr3[x];
3056 if (err) {
3057 errors += addr1[x + 1] != addr3[x + 1]
3058 && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1];
3059 }
3060 }
3061 }
3062 return errors;
3063}
3064
3065void SkPathOpsDebug::ReportOpFail(const SkPath& one, const SkPath& two, SkPathOp op) {
3066 SkDebugf("// Op did not expect failure\n");
3067 DumpOp(stderr, one, two, op, "opTest");
3068 fflush(stderr);
3069}
3070
3071void SkPathOpsDebug::VerifyOp(const SkPath& one, const SkPath& two, SkPathOp op,
3072 const SkPath& result) {
3073 SkPath pathOut, scaledPathOut;
3074 SkRegion rgnA, rgnB, openClip, rgnOut;
3075 openClip.setRect(-16000, -16000, 16000, 16000);
3076 rgnA.setPath(one, openClip);
3077 rgnB.setPath(two, openClip);
3078 rgnOut.op(rgnA, rgnB, (SkRegion::Op) op);
3079 rgnOut.getBoundaryPath(&pathOut);
3080 SkMatrix scale;
3081 debug_scale_matrix(one, &two, scale);
3082 SkRegion scaledRgnA, scaledRgnB, scaledRgnOut;
3083 SkPath scaledA, scaledB;
3084 scaledA.addPath(one, scale);
3085 scaledA.setFillType(one.getFillType());
3086 scaledB.addPath(two, scale);
3087 scaledB.setFillType(two.getFillType());
3088 scaledRgnA.setPath(scaledA, openClip);
3089 scaledRgnB.setPath(scaledB, openClip);
3090 scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) op);
3091 scaledRgnOut.getBoundaryPath(&scaledPathOut);
3092 SkBitmap bitmap;
3093 SkPath scaledOut;
3094 scaledOut.addPath(result, scale);
3095 scaledOut.setFillType(result.getFillType());
3096 int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
3097 const int MAX_ERRORS = 9;
3098 if (errors > MAX_ERRORS) {
3099 fprintf(stderr, "// Op did not expect errors=%d\n", errors);
3100 DumpOp(stderr, one, two, op, "opTest");
3101 fflush(stderr);
3102 }
3103}
3104
3105void SkPathOpsDebug::ReportSimplifyFail(const SkPath& path) {
3106 SkDebugf("// Simplify did not expect failure\n");
3107 DumpSimplify(stderr, path, "simplifyTest");
3108 fflush(stderr);
3109}
3110
3111void SkPathOpsDebug::VerifySimplify(const SkPath& path, const SkPath& result) {
3112 SkPath pathOut, scaledPathOut;
3113 SkRegion rgnA, openClip, rgnOut;
3114 openClip.setRect(-16000, -16000, 16000, 16000);
3115 rgnA.setPath(path, openClip);
3116 rgnOut.getBoundaryPath(&pathOut);
3117 SkMatrix scale;
3118 debug_scale_matrix(path, nullptr, scale);
3119 SkRegion scaledRgnA;
3120 SkPath scaledA;
3121 scaledA.addPath(path, scale);
3122 scaledA.setFillType(path.getFillType());
3123 scaledRgnA.setPath(scaledA, openClip);
3124 scaledRgnA.getBoundaryPath(&scaledPathOut);
3125 SkBitmap bitmap;
3126 SkPath scaledOut;
3127 scaledOut.addPath(result, scale);
3128 scaledOut.setFillType(result.getFillType());
3129 int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
3130 const int MAX_ERRORS = 9;
3131 if (errors > MAX_ERRORS) {
3132 fprintf(stderr, "// Simplify did not expect errors=%d\n", errors);
3133 DumpSimplify(stderr, path, "simplifyTest");
3134 fflush(stderr);
3135 }
3136}
3137
3138#endif
Cary Clarkb8421ed2018-03-14 15:55:02 -04003139
3140// global path dumps for msvs Visual Studio 17 to use from Immediate Window
3141void Dump(const SkPath& path) {
3142 path.dump();
3143}
3144
3145void DumpHex(const SkPath& path) {
3146 path.dumpHex();
3147}