blob: 7c8979253945e3922a8ef9d902a9084b0b6b157f [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"
Chris Dalton957189b2020-05-07 12:47:26 -060012#include "src/core/SkPathPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "src/pathops/SkOpCoincidence.h"
14#include "src/pathops/SkOpContour.h"
15#include "src/pathops/SkPathOpsDebug.h"
caryclark54359292015-03-26 07:52:43 -070016
Ben Wagnerf08d1d02018-06-18 15:11:00 -040017#include <utility>
18
Cary Clark918fb1f2016-11-15 13:22:25 -050019#if DEBUG_DUMP_VERIFY
caryclark13260682016-10-24 05:10:14 -070020bool SkPathOpsDebug::gDumpOp; // set to true to write op to file before a crash
21bool SkPathOpsDebug::gVerifyOp; // set to true to compare result against regions
22#endif
23
Cary Clark59d5a0e2017-01-23 14:38:52 +000024bool SkPathOpsDebug::gRunFail; // set to true to check for success on tests known to fail
caryclark13260682016-10-24 05:10:14 -070025bool SkPathOpsDebug::gVeryVerbose; // set to true to run extensive checking tests
26
caryclark30b9fdd2016-08-31 14:36:29 -070027#undef FAIL_IF
28#define FAIL_IF(cond, coin) \
Cary Clarkab87d7a2016-10-04 10:01:04 -040029 do { if (cond) log->record(SkPathOpsDebug::kFail_Glitch, coin); } while (false)
caryclark30b9fdd2016-08-31 14:36:29 -070030
31#undef FAIL_WITH_NULL_IF
32#define FAIL_WITH_NULL_IF(cond, span) \
Cary Clarkab87d7a2016-10-04 10:01:04 -040033 do { if (cond) log->record(SkPathOpsDebug::kFail_Glitch, span); } while (false)
caryclark30b9fdd2016-08-31 14:36:29 -070034
caryclark30b9fdd2016-08-31 14:36:29 -070035#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
caryclark1049f122015-04-20 08:31:59 -0700514static void show_function_header(const char* functionName) {
515 SkDebugf("\nstatic void %s(skiatest::Reporter* reporter, const char* filename) {\n", functionName);
516 if (strcmp("skphealth_com76", functionName) == 0) {
517 SkDebugf("found it\n");
518 }
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000519}
caryclark1049f122015-04-20 08:31:59 -0700520
521static const char* gOpStrs[] = {
522 "kDifference_SkPathOp",
523 "kIntersect_SkPathOp",
524 "kUnion_SkPathOp",
caryclark55888e42016-07-18 10:01:36 -0700525 "kXOR_PathOp",
caryclark1049f122015-04-20 08:31:59 -0700526 "kReverseDifference_SkPathOp",
527};
528
caryclark03b03ca2015-04-23 09:13:37 -0700529const char* SkPathOpsDebug::OpStr(SkPathOp op) {
530 return gOpStrs[op];
531}
532
caryclark1049f122015-04-20 08:31:59 -0700533static void show_op(SkPathOp op, const char* pathOne, const char* pathTwo) {
534 SkDebugf(" testPathOp(reporter, %s, %s, %s, filename);\n", pathOne, pathTwo, gOpStrs[op]);
535 SkDebugf("}\n");
536}
537
caryclark1049f122015-04-20 08:31:59 -0700538void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
539 const char* testName) {
Herb Derby9c71e7b2019-06-17 14:40:42 -0400540 static SkMutex& mutex = *(new SkMutex);
541
542 SkAutoMutexExclusive ac(mutex);
caryclark1049f122015-04-20 08:31:59 -0700543 show_function_header(testName);
544 ShowOnePath(a, "path", true);
545 ShowOnePath(b, "pathB", true);
546 show_op(shapeOp, "path", "pathB");
547}
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000548
Mike Kleinc0bd9f92019-04-23 12:05:21 -0500549#include "src/pathops/SkIntersectionHelper.h"
550#include "src/pathops/SkIntersections.h"
551#include "src/pathops/SkPathOpsTypes.h"
caryclark26ad22a2015-10-16 09:03:38 -0700552
Cary Clarkab87d7a2016-10-04 10:01:04 -0400553#if DEBUG_COIN
554
Cary Clarkab87d7a2016-10-04 10:01:04 -0400555void SkOpGlobalState::debugAddToGlobalCoinDicts() {
Herb Derby9c71e7b2019-06-17 14:40:42 -0400556 static SkMutex& mutex = *(new SkMutex);
557 SkAutoMutexExclusive ac(mutex);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400558 SkPathOpsDebug::gCoinSumChangedDict.add(fCoinChangedDict);
559 SkPathOpsDebug::gCoinSumVisitedDict.add(fCoinVisitedDict);
560}
561
562#endif
563
caryclark26ad22a2015-10-16 09:03:38 -0700564#if DEBUG_T_SECT_LOOP_COUNT
565void SkOpGlobalState::debugAddLoopCount(SkIntersections* i, const SkIntersectionHelper& wt,
566 const SkIntersectionHelper& wn) {
567 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
568 SkIntersections::DebugLoop looper = (SkIntersections::DebugLoop) index;
569 if (fDebugLoopCount[index] >= i->debugLoopCount(looper)) {
570 continue;
571 }
572 fDebugLoopCount[index] = i->debugLoopCount(looper);
573 fDebugWorstVerb[index * 2] = wt.segment()->verb();
574 fDebugWorstVerb[index * 2 + 1] = wn.segment()->verb();
575 sk_bzero(&fDebugWorstPts[index * 8], sizeof(SkPoint) * 8);
576 memcpy(&fDebugWorstPts[index * 2 * 4], wt.pts(),
577 (SkPathOpsVerbToPoints(wt.segment()->verb()) + 1) * sizeof(SkPoint));
578 memcpy(&fDebugWorstPts[(index * 2 + 1) * 4], wn.pts(),
579 (SkPathOpsVerbToPoints(wn.segment()->verb()) + 1) * sizeof(SkPoint));
580 fDebugWorstWeight[index * 2] = wt.weight();
581 fDebugWorstWeight[index * 2 + 1] = wn.weight();
582 }
583 i->debugResetLoopCount();
584}
585
586void SkOpGlobalState::debugDoYourWorst(SkOpGlobalState* local) {
587 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
588 if (fDebugLoopCount[index] >= local->fDebugLoopCount[index]) {
589 continue;
590 }
591 fDebugLoopCount[index] = local->fDebugLoopCount[index];
592 fDebugWorstVerb[index * 2] = local->fDebugWorstVerb[index * 2];
593 fDebugWorstVerb[index * 2 + 1] = local->fDebugWorstVerb[index * 2 + 1];
594 memcpy(&fDebugWorstPts[index * 2 * 4], &local->fDebugWorstPts[index * 2 * 4],
595 sizeof(SkPoint) * 8);
596 fDebugWorstWeight[index * 2] = local->fDebugWorstWeight[index * 2];
597 fDebugWorstWeight[index * 2 + 1] = local->fDebugWorstWeight[index * 2 + 1];
598 }
599 local->debugResetLoopCounts();
600}
601
602static void dump_curve(SkPath::Verb verb, const SkPoint& pts, float weight) {
603 if (!verb) {
604 return;
605 }
606 const char* verbs[] = { "", "line", "quad", "conic", "cubic" };
607 SkDebugf("%s: {{", verbs[verb]);
608 int ptCount = SkPathOpsVerbToPoints(verb);
609 for (int index = 0; index <= ptCount; ++index) {
610 SkDPoint::Dump((&pts)[index]);
611 if (index < ptCount - 1) {
612 SkDebugf(", ");
613 }
614 }
615 SkDebugf("}");
616 if (weight != 1) {
617 SkDebugf(", ");
618 if (weight == floorf(weight)) {
619 SkDebugf("%.0f", weight);
620 } else {
621 SkDebugf("%1.9gf", weight);
622 }
623 }
624 SkDebugf("}\n");
625}
626
627void SkOpGlobalState::debugLoopReport() {
628 const char* loops[] = { "iterations", "coinChecks", "perpCalcs" };
629 SkDebugf("\n");
630 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
631 SkDebugf("%s: %d\n", loops[index], fDebugLoopCount[index]);
632 dump_curve(fDebugWorstVerb[index * 2], fDebugWorstPts[index * 2 * 4],
633 fDebugWorstWeight[index * 2]);
634 dump_curve(fDebugWorstVerb[index * 2 + 1], fDebugWorstPts[(index * 2 + 1) * 4],
635 fDebugWorstWeight[index * 2 + 1]);
636 }
637}
638
639void SkOpGlobalState::debugResetLoopCounts() {
640 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
641 sk_bzero(fDebugWorstVerb, sizeof(fDebugWorstVerb));
642 sk_bzero(fDebugWorstPts, sizeof(fDebugWorstPts));
643 sk_bzero(fDebugWorstWeight, sizeof(fDebugWorstWeight));
644}
645#endif
caryclark27c8eb82015-07-06 11:38:33 -0700646
Cary Clark59d5a0e2017-01-23 14:38:52 +0000647bool SkOpGlobalState::DebugRunFail() {
648 return SkPathOpsDebug::gRunFail;
649}
650
Cary Clarkab87d7a2016-10-04 10:01:04 -0400651// this is const so it can be called by const methods that overwise don't alter state
652#if DEBUG_VALIDATE || DEBUG_COIN
653void SkOpGlobalState::debugSetPhase(const char* funcName DEBUG_COIN_DECLARE_PARAMS()) const {
654 auto writable = const_cast<SkOpGlobalState*>(this);
655#if DEBUG_VALIDATE
656 writable->setPhase(phase);
657#endif
658#if DEBUG_COIN
659 SkPathOpsDebug::CoinDictEntry* entry = &writable->fCoinDictEntry;
660 writable->fPreviousFuncName = entry->fFunctionName;
661 entry->fIteration = iteration;
662 entry->fLineNumber = lineNo;
663 entry->fGlitchType = SkPathOpsDebug::kUninitialized_Glitch;
664 entry->fFunctionName = funcName;
665 writable->fCoinVisitedDict.add(*entry);
666 writable->debugAddToCoinChangedDict();
667#endif
668}
669#endif
670
caryclark26ad22a2015-10-16 09:03:38 -0700671#if DEBUG_T_SECT_LOOP_COUNT
672void SkIntersections::debugBumpLoopCount(DebugLoop index) {
673 fDebugLoopCount[index]++;
674}
675
676int SkIntersections::debugLoopCount(DebugLoop index) const {
677 return fDebugLoopCount[index];
678}
679
680void SkIntersections::debugResetLoopCount() {
681 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
682}
683#endif
684
Mike Kleinc0bd9f92019-04-23 12:05:21 -0500685#include "src/pathops/SkPathOpsConic.h"
686#include "src/pathops/SkPathOpsCubic.h"
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000687
caryclark624637c2015-05-11 07:21:27 -0700688SkDCubic SkDQuad::debugToCubic() const {
689 SkDCubic cubic;
690 cubic[0] = fPts[0];
691 cubic[2] = fPts[1];
692 cubic[3] = fPts[2];
693 cubic[1].fX = (cubic[0].fX + cubic[2].fX * 2) / 3;
694 cubic[1].fY = (cubic[0].fY + cubic[2].fY * 2) / 3;
695 cubic[2].fX = (cubic[3].fX + cubic[2].fX * 2) / 3;
696 cubic[2].fY = (cubic[3].fY + cubic[2].fY * 2) / 3;
697 return cubic;
caryclark54359292015-03-26 07:52:43 -0700698}
caryclark624637c2015-05-11 07:21:27 -0700699
caryclarka35ab3e2016-10-20 08:32:18 -0700700void SkDQuad::debugSet(const SkDPoint* pts) {
701 memcpy(fPts, pts, sizeof(fPts));
702 SkDEBUGCODE(fDebugGlobalState = nullptr);
703}
704
705void SkDCubic::debugSet(const SkDPoint* pts) {
706 memcpy(fPts, pts, sizeof(fPts));
707 SkDEBUGCODE(fDebugGlobalState = nullptr);
708}
709
710void SkDConic::debugSet(const SkDPoint* pts, SkScalar weight) {
711 fPts.debugSet(pts);
712 fWeight = weight;
713}
714
caryclarked0935a2015-10-22 07:23:52 -0700715void SkDRect::debugInit() {
716 fLeft = fTop = fRight = fBottom = SK_ScalarNaN;
717}
718
Mike Kleinc0bd9f92019-04-23 12:05:21 -0500719#include "src/pathops/SkOpAngle.h"
720#include "src/pathops/SkOpSegment.h"
caryclark54359292015-03-26 07:52:43 -0700721
Cary Clarkab87d7a2016-10-04 10:01:04 -0400722#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -0700723// commented-out lines keep this in sync with addT()
Cary Clarkab87d7a2016-10-04 10:01:04 -0400724 const SkOpPtT* SkOpSegment::debugAddT(double t, SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -0700725 debugValidate();
726 SkPoint pt = this->ptAtT(t);
caryclark26ad22a2015-10-16 09:03:38 -0700727 const SkOpSpanBase* span = &fHead;
caryclark55888e42016-07-18 10:01:36 -0700728 do {
729 const SkOpPtT* result = span->ptT();
caryclark29b25632016-08-25 11:27:17 -0700730 if (t == result->fT || this->match(result, this, t, pt)) {
caryclark55888e42016-07-18 10:01:36 -0700731// span->bumpSpanAdds();
caryclarkef4f32a2016-08-24 09:24:18 -0700732 return result;
caryclark26ad22a2015-10-16 09:03:38 -0700733 }
caryclark55888e42016-07-18 10:01:36 -0700734 if (t < result->fT) {
735 const SkOpSpan* prev = result->span()->prev();
caryclark30b9fdd2016-08-31 14:36:29 -0700736 FAIL_WITH_NULL_IF(!prev, span);
caryclark29b25632016-08-25 11:27:17 -0700737 // marks in global state that new op span has been allocated
738 this->globalState()->setAllocatedOpSpan();
caryclark55888e42016-07-18 10:01:36 -0700739// span->init(this, prev, t, pt);
740 this->debugValidate();
741// #if DEBUG_ADD_T
742// SkDebugf("%s insert t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
743// span->segment()->debugID(), span->debugID());
744// #endif
745// span->bumpSpanAdds();
caryclark55888e42016-07-18 10:01:36 -0700746 return nullptr;
caryclark26ad22a2015-10-16 09:03:38 -0700747 }
caryclark30b9fdd2016-08-31 14:36:29 -0700748 FAIL_WITH_NULL_IF(span != &fTail, span);
caryclark55888e42016-07-18 10:01:36 -0700749 } while ((span = span->upCast()->next()));
750 SkASSERT(0);
caryclark29b25632016-08-25 11:27:17 -0700751 return nullptr; // we never get here, but need this to satisfy compiler
caryclark26ad22a2015-10-16 09:03:38 -0700752}
753#endif
754
755#if DEBUG_ANGLE
756void SkOpSegment::debugCheckAngleCoin() const {
757 const SkOpSpanBase* base = &fHead;
758 const SkOpSpan* span;
759 do {
760 const SkOpAngle* angle = base->fromAngle();
caryclark55888e42016-07-18 10:01:36 -0700761 if (angle && angle->debugCheckCoincidence()) {
caryclark26ad22a2015-10-16 09:03:38 -0700762 angle->debugCheckNearCoincidence();
763 }
764 if (base->final()) {
765 break;
766 }
767 span = base->upCast();
768 angle = span->toAngle();
caryclark55888e42016-07-18 10:01:36 -0700769 if (angle && angle->debugCheckCoincidence()) {
caryclark26ad22a2015-10-16 09:03:38 -0700770 angle->debugCheckNearCoincidence();
771 }
772 } while ((base = span->next()));
773}
774#endif
775
Cary Clarkab87d7a2016-10-04 10:01:04 -0400776#if DEBUG_COIN
caryclark26ad22a2015-10-16 09:03:38 -0700777// this mimics the order of the checks in handle coincidence
Cary Clarkab87d7a2016-10-04 10:01:04 -0400778void SkOpSegment::debugCheckHealth(SkPathOpsDebug::GlitchLog* glitches) const {
779 debugMoveMultiples(glitches);
780 debugMoveNearby(glitches);
781 debugMissingCoincidence(glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700782}
783
caryclark55888e42016-07-18 10:01:36 -0700784// commented-out lines keep this in sync with clearAll()
Cary Clarkab87d7a2016-10-04 10:01:04 -0400785void SkOpSegment::debugClearAll(SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -0700786 const SkOpSpan* span = &fHead;
787 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400788 this->debugClearOne(span, glitches);
caryclark55888e42016-07-18 10:01:36 -0700789 } while ((span = span->next()->upCastable()));
Cary Clarkab87d7a2016-10-04 10:01:04 -0400790 this->globalState()->coincidence()->debugRelease(glitches, this);
caryclark55888e42016-07-18 10:01:36 -0700791}
792
793// commented-out lines keep this in sync with clearOne()
Cary Clarkab87d7a2016-10-04 10:01:04 -0400794void SkOpSegment::debugClearOne(const SkOpSpan* span, SkPathOpsDebug::GlitchLog* glitches) const {
795 if (span->windValue()) glitches->record(SkPathOpsDebug::kCollapsedWindValue_Glitch, span);
796 if (span->oppValue()) glitches->record(SkPathOpsDebug::kCollapsedOppValue_Glitch, span);
797 if (!span->done()) glitches->record(SkPathOpsDebug::kCollapsedDone_Glitch, span);
caryclark26ad22a2015-10-16 09:03:38 -0700798}
799#endif
800
caryclark54359292015-03-26 07:52:43 -0700801SkOpAngle* SkOpSegment::debugLastAngle() {
halcanary96fcdcc2015-08-27 07:41:13 -0700802 SkOpAngle* result = nullptr;
caryclark54359292015-03-26 07:52:43 -0700803 SkOpSpan* span = this->head();
804 do {
805 if (span->toAngle()) {
806 SkASSERT(!result);
807 result = span->toAngle();
808 }
809 } while ((span = span->next()->upCastable()));
810 SkASSERT(result);
811 return result;
812}
813
Cary Clarkab87d7a2016-10-04 10:01:04 -0400814#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -0700815// commented-out lines keep this in sync with ClearVisited
816void SkOpSegment::DebugClearVisited(const SkOpSpanBase* span) {
817 // reset visited flag back to false
818 do {
819 const SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
820 while ((ptT = ptT->next()) != stopPtT) {
821 const SkOpSegment* opp = ptT->segment();
822 opp->resetDebugVisited();
823 }
824 } while (!span->final() && (span = span->upCast()->next()));
825}
826#endif
827
Cary Clarkab87d7a2016-10-04 10:01:04 -0400828#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -0700829// commented-out lines keep this in sync with missingCoincidence()
830// look for pairs of undetected coincident curves
831// assumes that segments going in have visited flag clear
832// Even though pairs of curves correct detect coincident runs, a run may be missed
833// if the coincidence is a product of multiple intersections. For instance, given
834// curves A, B, and C:
835// A-B intersect at a point 1; A-C and B-C intersect at point 2, so near
836// the end of C that the intersection is replaced with the end of C.
837// Even though A-B correctly do not detect an intersection at point 2,
838// the resulting run from point 1 to point 2 is coincident on A and B.
Cary Clarkab87d7a2016-10-04 10:01:04 -0400839void SkOpSegment::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -0700840 if (this->done()) {
841 return;
842 }
843 const SkOpSpan* prior = nullptr;
844 const SkOpSpanBase* spanBase = &fHead;
caryclark55888e42016-07-18 10:01:36 -0700845// bool result = false;
caryclark26ad22a2015-10-16 09:03:38 -0700846 do {
847 const SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT;
848 SkASSERT(ptT->span() == spanBase);
849 while ((ptT = ptT->next()) != spanStopPtT) {
850 if (ptT->deleted()) {
851 continue;
852 }
caryclark55888e42016-07-18 10:01:36 -0700853 const SkOpSegment* opp = ptT->span()->segment();
caryclark26ad22a2015-10-16 09:03:38 -0700854 if (opp->done()) {
855 continue;
856 }
857 // when opp is encounted the 1st time, continue; on 2nd encounter, look for coincidence
caryclark55888e42016-07-18 10:01:36 -0700858 if (!opp->debugVisited()) {
caryclark26ad22a2015-10-16 09:03:38 -0700859 continue;
860 }
861 if (spanBase == &fHead) {
862 continue;
863 }
caryclark55888e42016-07-18 10:01:36 -0700864 if (ptT->segment() == this) {
865 continue;
866 }
caryclark26ad22a2015-10-16 09:03:38 -0700867 const SkOpSpan* span = spanBase->upCastable();
868 // FIXME?: this assumes that if the opposite segment is coincident then no more
869 // coincidence needs to be detected. This may not be true.
caryclark55888e42016-07-18 10:01:36 -0700870 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 -0700871 continue;
872 }
caryclark55888e42016-07-18 10:01:36 -0700873 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 -0700874 continue;
caryclark55888e42016-07-18 10:01:36 -0700875 }
caryclark26ad22a2015-10-16 09:03:38 -0700876 const SkOpPtT* priorPtT = nullptr, * priorStopPtT;
877 // find prior span containing opp segment
878 const SkOpSegment* priorOpp = nullptr;
879 const SkOpSpan* priorTest = spanBase->prev();
880 while (!priorOpp && priorTest) {
881 priorStopPtT = priorPtT = priorTest->ptT();
882 while ((priorPtT = priorPtT->next()) != priorStopPtT) {
883 if (priorPtT->deleted()) {
884 continue;
885 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400886 const SkOpSegment* segment = priorPtT->span()->segment();
caryclark26ad22a2015-10-16 09:03:38 -0700887 if (segment == opp) {
888 prior = priorTest;
889 priorOpp = opp;
890 break;
891 }
892 }
893 priorTest = priorTest->prev();
894 }
895 if (!priorOpp) {
896 continue;
897 }
caryclark55888e42016-07-18 10:01:36 -0700898 if (priorPtT == ptT) {
899 continue;
900 }
caryclark26ad22a2015-10-16 09:03:38 -0700901 const SkOpPtT* oppStart = prior->ptT();
902 const SkOpPtT* oppEnd = spanBase->ptT();
903 bool swapped = priorPtT->fT > ptT->fT;
904 if (swapped) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -0400905 using std::swap;
906 swap(priorPtT, ptT);
907 swap(oppStart, oppEnd);
caryclark26ad22a2015-10-16 09:03:38 -0700908 }
caryclark55888e42016-07-18 10:01:36 -0700909 const SkOpCoincidence* coincidence = this->globalState()->coincidence();
910 const SkOpPtT* rootPriorPtT = priorPtT->span()->ptT();
911 const SkOpPtT* rootPtT = ptT->span()->ptT();
912 const SkOpPtT* rootOppStart = oppStart->span()->ptT();
913 const SkOpPtT* rootOppEnd = oppEnd->span()->ptT();
914 if (coincidence->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) {
caryclark26ad22a2015-10-16 09:03:38 -0700915 goto swapBack;
916 }
caryclark55888e42016-07-18 10:01:36 -0700917 if (testForCoincidence(rootPriorPtT, rootPtT, prior, spanBase, opp)) {
918 // mark coincidence
caryclark30b9fdd2016-08-31 14:36:29 -0700919#if DEBUG_COINCIDENCE_VERBOSE
caryclark55888e42016-07-18 10:01:36 -0700920// SkDebugf("%s coinSpan=%d endSpan=%d oppSpan=%d oppEndSpan=%d\n", __FUNCTION__,
921// rootPriorPtT->debugID(), rootPtT->debugID(), rootOppStart->debugID(),
922// rootOppEnd->debugID());
923#endif
Cary Clarkab87d7a2016-10-04 10:01:04 -0400924 log->record(SkPathOpsDebug::kMissingCoin_Glitch, priorPtT, ptT, oppStart, oppEnd);
caryclark55888e42016-07-18 10:01:36 -0700925 // coincidences->add(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
926 // }
927#if DEBUG_COINCIDENCE
caryclarkd6562002016-07-27 12:02:07 -0700928// SkASSERT(coincidences->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
caryclark55888e42016-07-18 10:01:36 -0700929#endif
930 // result = true;
caryclark26ad22a2015-10-16 09:03:38 -0700931 }
932 swapBack:
933 if (swapped) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -0400934 using std::swap;
935 swap(priorPtT, ptT);
caryclark26ad22a2015-10-16 09:03:38 -0700936 }
937 }
938 } while ((spanBase = spanBase->final() ? nullptr : spanBase->upCast()->next()));
caryclark55888e42016-07-18 10:01:36 -0700939 DebugClearVisited(&fHead);
940 return;
caryclark26ad22a2015-10-16 09:03:38 -0700941}
942
caryclark55888e42016-07-18 10:01:36 -0700943// commented-out lines keep this in sync with moveMultiples()
944// if a span has more than one intersection, merge the other segments' span as needed
Cary Clarkab87d7a2016-10-04 10:01:04 -0400945void SkOpSegment::debugMoveMultiples(SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -0700946 debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -0700947 const SkOpSpanBase* test = &fHead;
948 do {
949 int addCount = test->spanAddsCount();
Cary Clarkff114282016-12-14 11:56:16 -0500950// SkASSERT(addCount >= 1);
951 if (addCount <= 1) {
caryclark26ad22a2015-10-16 09:03:38 -0700952 continue;
953 }
954 const SkOpPtT* startPtT = test->ptT();
955 const SkOpPtT* testPtT = startPtT;
956 do { // iterate through all spans associated with start
957 const SkOpSpanBase* oppSpan = testPtT->span();
958 if (oppSpan->spanAddsCount() == addCount) {
959 continue;
960 }
961 if (oppSpan->deleted()) {
962 continue;
963 }
964 const SkOpSegment* oppSegment = oppSpan->segment();
965 if (oppSegment == this) {
966 continue;
967 }
968 // find range of spans to consider merging
969 const SkOpSpanBase* oppPrev = oppSpan;
970 const SkOpSpanBase* oppFirst = oppSpan;
971 while ((oppPrev = oppPrev->prev())) {
972 if (!roughly_equal(oppPrev->t(), oppSpan->t())) {
973 break;
974 }
975 if (oppPrev->spanAddsCount() == addCount) {
976 continue;
977 }
978 if (oppPrev->deleted()) {
979 continue;
980 }
981 oppFirst = oppPrev;
982 }
983 const SkOpSpanBase* oppNext = oppSpan;
984 const SkOpSpanBase* oppLast = oppSpan;
985 while ((oppNext = oppNext->final() ? nullptr : oppNext->upCast()->next())) {
986 if (!roughly_equal(oppNext->t(), oppSpan->t())) {
987 break;
988 }
989 if (oppNext->spanAddsCount() == addCount) {
990 continue;
991 }
992 if (oppNext->deleted()) {
993 continue;
994 }
995 oppLast = oppNext;
996 }
997 if (oppFirst == oppLast) {
998 continue;
999 }
1000 const SkOpSpanBase* oppTest = oppFirst;
1001 do {
1002 if (oppTest == oppSpan) {
1003 continue;
1004 }
1005 // check to see if the candidate meets specific criteria:
1006 // it contains spans of segments in test's loop but not including 'this'
1007 const SkOpPtT* oppStartPtT = oppTest->ptT();
1008 const SkOpPtT* oppPtT = oppStartPtT;
1009 while ((oppPtT = oppPtT->next()) != oppStartPtT) {
1010 const SkOpSegment* oppPtTSegment = oppPtT->segment();
1011 if (oppPtTSegment == this) {
1012 goto tryNextSpan;
1013 }
1014 const SkOpPtT* matchPtT = startPtT;
1015 do {
1016 if (matchPtT->segment() == oppPtTSegment) {
1017 goto foundMatch;
1018 }
1019 } while ((matchPtT = matchPtT->next()) != startPtT);
1020 goto tryNextSpan;
1021 foundMatch: // merge oppTest and oppSpan
caryclark55888e42016-07-18 10:01:36 -07001022 oppSegment->debugValidate();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001023 oppTest->debugMergeMatches(glitches, oppSpan);
1024 oppTest->debugAddOpp(glitches, oppSpan);
caryclark55888e42016-07-18 10:01:36 -07001025 oppSegment->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001026 goto checkNextSpan;
1027 }
caryclark55888e42016-07-18 10:01:36 -07001028 tryNextSpan:
caryclark26ad22a2015-10-16 09:03:38 -07001029 ;
1030 } while (oppTest != oppLast && (oppTest = oppTest->upCast()->next()));
1031 } while ((testPtT = testPtT->next()) != startPtT);
caryclark55888e42016-07-18 10:01:36 -07001032checkNextSpan:
caryclark26ad22a2015-10-16 09:03:38 -07001033 ;
1034 } while ((test = test->final() ? nullptr : test->upCast()->next()));
caryclark55888e42016-07-18 10:01:36 -07001035 debugValidate();
1036 return;
caryclark26ad22a2015-10-16 09:03:38 -07001037}
1038
caryclark55888e42016-07-18 10:01:36 -07001039// commented-out lines keep this in sync with moveNearby()
1040// Move nearby t values and pts so they all hang off the same span. Alignment happens later.
Cary Clarkab87d7a2016-10-04 10:01:04 -04001041void SkOpSegment::debugMoveNearby(SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -07001042 debugValidate();
1043 // release undeleted spans pointing to this seg that are linked to the primary span
1044 const SkOpSpanBase* spanBase = &fHead;
caryclark26ad22a2015-10-16 09:03:38 -07001045 do {
caryclark55888e42016-07-18 10:01:36 -07001046 const SkOpPtT* ptT = spanBase->ptT();
1047 const SkOpPtT* headPtT = ptT;
1048 while ((ptT = ptT->next()) != headPtT) {
1049 const SkOpSpanBase* test = ptT->span();
1050 if (ptT->segment() == this && !ptT->deleted() && test != spanBase
1051 && test->ptT() == ptT) {
1052 if (test->final()) {
1053 if (spanBase == &fHead) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001054 glitches->record(SkPathOpsDebug::kMoveNearbyClearAll_Glitch, this);
caryclark55888e42016-07-18 10:01:36 -07001055// return;
1056 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001057 glitches->record(SkPathOpsDebug::kMoveNearbyReleaseFinal_Glitch, spanBase, ptT);
caryclark55888e42016-07-18 10:01:36 -07001058 } else if (test->prev()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001059 glitches->record(SkPathOpsDebug::kMoveNearbyRelease_Glitch, test, headPtT);
caryclark55888e42016-07-18 10:01:36 -07001060 }
1061// break;
caryclark26ad22a2015-10-16 09:03:38 -07001062 }
1063 }
caryclark55888e42016-07-18 10:01:36 -07001064 spanBase = spanBase->upCast()->next();
1065 } while (!spanBase->final());
1066
1067 // This loop looks for adjacent spans which are near by
1068 spanBase = &fHead;
1069 do { // iterate through all spans associated with start
1070 const SkOpSpanBase* test = spanBase->upCast()->next();
Cary Clark73e597d2017-04-18 12:08:58 -04001071 bool found;
1072 if (!this->spansNearby(spanBase, test, &found)) {
1073 glitches->record(SkPathOpsDebug::kMoveNearbyMergeFinal_Glitch, test);
1074 }
1075 if (found) {
caryclark55888e42016-07-18 10:01:36 -07001076 if (test->final()) {
1077 if (spanBase->prev()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001078 glitches->record(SkPathOpsDebug::kMoveNearbyMergeFinal_Glitch, test);
caryclark55888e42016-07-18 10:01:36 -07001079 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001080 glitches->record(SkPathOpsDebug::kMoveNearbyClearAll2_Glitch, this);
caryclark55888e42016-07-18 10:01:36 -07001081 // return
1082 }
1083 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001084 glitches->record(SkPathOpsDebug::kMoveNearbyMerge_Glitch, spanBase);
caryclark55888e42016-07-18 10:01:36 -07001085 }
1086 }
1087 spanBase = test;
1088 } while (!spanBase->final());
1089 debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001090}
1091#endif
1092
caryclark54359292015-03-26 07:52:43 -07001093void SkOpSegment::debugReset() {
caryclark1049f122015-04-20 08:31:59 -07001094 this->init(this->fPts, this->fWeight, this->contour(), this->verb());
caryclark54359292015-03-26 07:52:43 -07001095}
1096
caryclark025b11e2016-08-25 05:21:14 -07001097#if DEBUG_COINCIDENCE_ORDER
1098void SkOpSegment::debugSetCoinT(int index, SkScalar t) const {
1099 if (fDebugBaseMax < 0 || fDebugBaseIndex == index) {
1100 fDebugBaseIndex = index;
Brian Osman788b9162020-02-07 10:36:46 -05001101 fDebugBaseMin = std::min(t, fDebugBaseMin);
1102 fDebugBaseMax = std::max(t, fDebugBaseMax);
caryclark025b11e2016-08-25 05:21:14 -07001103 return;
Ben Wagner63fd7602017-10-09 15:45:33 -04001104 }
caryclark025b11e2016-08-25 05:21:14 -07001105 SkASSERT(fDebugBaseMin >= t || t >= fDebugBaseMax);
1106 if (fDebugLastMax < 0 || fDebugLastIndex == index) {
1107 fDebugLastIndex = index;
Brian Osman788b9162020-02-07 10:36:46 -05001108 fDebugLastMin = std::min(t, fDebugLastMin);
1109 fDebugLastMax = std::max(t, fDebugLastMax);
caryclark025b11e2016-08-25 05:21:14 -07001110 return;
1111 }
1112 SkASSERT(fDebugLastMin >= t || t >= fDebugLastMax);
1113 SkASSERT((t - fDebugBaseMin > 0) == (fDebugLastMin - fDebugBaseMin > 0));
1114}
1115#endif
1116
caryclark54359292015-03-26 07:52:43 -07001117#if DEBUG_ACTIVE_SPANS
Cary Clarkff114282016-12-14 11:56:16 -05001118void SkOpSegment::debugShowActiveSpans(SkString* str) const {
caryclark54359292015-03-26 07:52:43 -07001119 debugValidate();
1120 if (done()) {
1121 return;
1122 }
1123 int lastId = -1;
1124 double lastT = -1;
1125 const SkOpSpan* span = &fHead;
1126 do {
1127 if (span->done()) {
1128 continue;
1129 }
caryclark1049f122015-04-20 08:31:59 -07001130 if (lastId == this->debugID() && lastT == span->t()) {
caryclark54359292015-03-26 07:52:43 -07001131 continue;
1132 }
caryclark1049f122015-04-20 08:31:59 -07001133 lastId = this->debugID();
caryclark54359292015-03-26 07:52:43 -07001134 lastT = span->t();
Cary Clarkff114282016-12-14 11:56:16 -05001135 str->appendf("%s id=%d", __FUNCTION__, this->debugID());
caryclark55888e42016-07-18 10:01:36 -07001136 // since endpoints may have be adjusted, show actual computed curves
1137 SkDCurve curvePart;
1138 this->subDivide(span, span->next(), &curvePart);
1139 const SkDPoint* pts = curvePart.fCubic.fPts;
Cary Clarkff114282016-12-14 11:56:16 -05001140 str->appendf(" (%1.9g,%1.9g", pts[0].fX, pts[0].fY);
caryclark54359292015-03-26 07:52:43 -07001141 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
Cary Clarkff114282016-12-14 11:56:16 -05001142 str->appendf(" %1.9g,%1.9g", pts[vIndex].fX, pts[vIndex].fY);
caryclark54359292015-03-26 07:52:43 -07001143 }
caryclark1049f122015-04-20 08:31:59 -07001144 if (SkPath::kConic_Verb == fVerb) {
Cary Clarkff114282016-12-14 11:56:16 -05001145 str->appendf(" %1.9gf", curvePart.fConic.fWeight);
caryclark1049f122015-04-20 08:31:59 -07001146 }
Cary Clarkff114282016-12-14 11:56:16 -05001147 str->appendf(") t=%1.9g tEnd=%1.9g", span->t(), span->next()->t());
caryclark54359292015-03-26 07:52:43 -07001148 if (span->windSum() == SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001149 str->appendf(" windSum=?");
caryclark54359292015-03-26 07:52:43 -07001150 } else {
Cary Clarkff114282016-12-14 11:56:16 -05001151 str->appendf(" windSum=%d", span->windSum());
caryclark54359292015-03-26 07:52:43 -07001152 }
caryclark624637c2015-05-11 07:21:27 -07001153 if (span->oppValue() && span->oppSum() == SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001154 str->appendf(" oppSum=?");
caryclark624637c2015-05-11 07:21:27 -07001155 } else if (span->oppValue() || span->oppSum() != SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001156 str->appendf(" oppSum=%d", span->oppSum());
caryclark624637c2015-05-11 07:21:27 -07001157 }
Cary Clarkff114282016-12-14 11:56:16 -05001158 str->appendf(" windValue=%d", span->windValue());
caryclark624637c2015-05-11 07:21:27 -07001159 if (span->oppValue() || span->oppSum() != SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001160 str->appendf(" oppValue=%d", span->oppValue());
caryclark624637c2015-05-11 07:21:27 -07001161 }
Cary Clarkff114282016-12-14 11:56:16 -05001162 str->appendf("\n");
caryclark54359292015-03-26 07:52:43 -07001163 } while ((span = span->next()->upCastable()));
1164}
1165#endif
1166
1167#if DEBUG_MARK_DONE
1168void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding) {
1169 const SkPoint& pt = span->ptT()->fPt;
caryclark1049f122015-04-20 08:31:59 -07001170 SkDebugf("%s id=%d", fun, this->debugID());
caryclark54359292015-03-26 07:52:43 -07001171 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1172 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1173 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1174 }
1175 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
1176 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t());
1177 if (winding == SK_MinS32) {
1178 SkDebugf("?");
1179 } else {
1180 SkDebugf("%d", winding);
1181 }
1182 SkDebugf(" windSum=");
1183 if (span->windSum() == SK_MinS32) {
1184 SkDebugf("?");
1185 } else {
1186 SkDebugf("%d", span->windSum());
1187 }
1188 SkDebugf(" windValue=%d\n", span->windValue());
1189}
1190
1191void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding,
1192 int oppWinding) {
1193 const SkPoint& pt = span->ptT()->fPt;
caryclark1049f122015-04-20 08:31:59 -07001194 SkDebugf("%s id=%d", fun, this->debugID());
caryclark54359292015-03-26 07:52:43 -07001195 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1196 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1197 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1198 }
1199 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
1200 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t(), winding, oppWinding);
1201 if (winding == SK_MinS32) {
1202 SkDebugf("?");
1203 } else {
1204 SkDebugf("%d", winding);
1205 }
1206 SkDebugf(" newOppSum=");
1207 if (oppWinding == SK_MinS32) {
1208 SkDebugf("?");
1209 } else {
1210 SkDebugf("%d", oppWinding);
1211 }
1212 SkDebugf(" oppSum=");
1213 if (span->oppSum() == SK_MinS32) {
1214 SkDebugf("?");
1215 } else {
1216 SkDebugf("%d", span->oppSum());
1217 }
1218 SkDebugf(" windSum=");
1219 if (span->windSum() == SK_MinS32) {
1220 SkDebugf("?");
1221 } else {
1222 SkDebugf("%d", span->windSum());
1223 }
1224 SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue());
1225}
1226
1227#endif
1228
caryclark26ad22a2015-10-16 09:03:38 -07001229// loop looking for a pair of angle parts that are too close to be sorted
1230/* This is called after other more simple intersection and angle sorting tests have been exhausted.
1231 This should be rarely called -- the test below is thorough and time consuming.
caryclark55888e42016-07-18 10:01:36 -07001232 This checks the distance between start points; the distance between
caryclark26ad22a2015-10-16 09:03:38 -07001233*/
1234#if DEBUG_ANGLE
1235void SkOpAngle::debugCheckNearCoincidence() const {
1236 const SkOpAngle* test = this;
1237 do {
1238 const SkOpSegment* testSegment = test->segment();
1239 double testStartT = test->start()->t();
1240 SkDPoint testStartPt = testSegment->dPtAtT(testStartT);
1241 double testEndT = test->end()->t();
1242 SkDPoint testEndPt = testSegment->dPtAtT(testEndT);
1243 double testLenSq = testStartPt.distanceSquared(testEndPt);
1244 SkDebugf("%s testLenSq=%1.9g id=%d\n", __FUNCTION__, testLenSq, testSegment->debugID());
1245 double testMidT = (testStartT + testEndT) / 2;
1246 const SkOpAngle* next = test;
1247 while ((next = next->fNext) != this) {
1248 SkOpSegment* nextSegment = next->segment();
1249 double testMidDistSq = testSegment->distSq(testMidT, next);
1250 double testEndDistSq = testSegment->distSq(testEndT, next);
1251 double nextStartT = next->start()->t();
1252 SkDPoint nextStartPt = nextSegment->dPtAtT(nextStartT);
1253 double distSq = testStartPt.distanceSquared(nextStartPt);
1254 double nextEndT = next->end()->t();
1255 double nextMidT = (nextStartT + nextEndT) / 2;
1256 double nextMidDistSq = nextSegment->distSq(nextMidT, test);
1257 double nextEndDistSq = nextSegment->distSq(nextEndT, test);
1258 SkDebugf("%s distSq=%1.9g testId=%d nextId=%d\n", __FUNCTION__, distSq,
1259 testSegment->debugID(), nextSegment->debugID());
1260 SkDebugf("%s testMidDistSq=%1.9g\n", __FUNCTION__, testMidDistSq);
1261 SkDebugf("%s testEndDistSq=%1.9g\n", __FUNCTION__, testEndDistSq);
1262 SkDebugf("%s nextMidDistSq=%1.9g\n", __FUNCTION__, nextMidDistSq);
1263 SkDebugf("%s nextEndDistSq=%1.9g\n", __FUNCTION__, nextEndDistSq);
1264 SkDPoint nextEndPt = nextSegment->dPtAtT(nextEndT);
1265 double nextLenSq = nextStartPt.distanceSquared(nextEndPt);
1266 SkDebugf("%s nextLenSq=%1.9g\n", __FUNCTION__, nextLenSq);
1267 SkDebugf("\n");
1268 }
1269 test = test->fNext;
caryclark55888e42016-07-18 10:01:36 -07001270 } while (test->fNext != this);
caryclark26ad22a2015-10-16 09:03:38 -07001271}
1272#endif
1273
caryclark54359292015-03-26 07:52:43 -07001274#if DEBUG_ANGLE
1275SkString SkOpAngle::debugPart() const {
1276 SkString result;
1277 switch (this->segment()->verb()) {
1278 case SkPath::kLine_Verb:
caryclarkeed356d2016-09-14 07:18:20 -07001279 result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fPart.fCurve),
caryclark54359292015-03-26 07:52:43 -07001280 this->segment()->debugID());
1281 break;
1282 case SkPath::kQuad_Verb:
caryclarkeed356d2016-09-14 07:18:20 -07001283 result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fPart.fCurve),
caryclark54359292015-03-26 07:52:43 -07001284 this->segment()->debugID());
1285 break;
caryclark1049f122015-04-20 08:31:59 -07001286 case SkPath::kConic_Verb:
1287 result.printf(CONIC_DEBUG_STR " id=%d",
caryclarkeed356d2016-09-14 07:18:20 -07001288 CONIC_DEBUG_DATA(fPart.fCurve, fPart.fCurve.fConic.fWeight),
caryclark1049f122015-04-20 08:31:59 -07001289 this->segment()->debugID());
1290 break;
caryclark54359292015-03-26 07:52:43 -07001291 case SkPath::kCubic_Verb:
caryclarkeed356d2016-09-14 07:18:20 -07001292 result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fPart.fCurve),
caryclark54359292015-03-26 07:52:43 -07001293 this->segment()->debugID());
1294 break;
1295 default:
1296 SkASSERT(0);
mtklein1b249332015-07-07 12:21:21 -07001297 }
caryclark54359292015-03-26 07:52:43 -07001298 return result;
1299}
1300#endif
1301
caryclark624637c2015-05-11 07:21:27 -07001302#if DEBUG_SORT
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001303void SkOpAngle::debugLoop() const {
caryclarke4097e32014-06-18 07:24:19 -07001304 const SkOpAngle* first = this;
1305 const SkOpAngle* next = this;
1306 do {
1307 next->dumpOne(true);
caryclark19eb3b22014-07-18 05:08:14 -07001308 SkDebugf("\n");
caryclarke4097e32014-06-18 07:24:19 -07001309 next = next->fNext;
1310 } while (next && next != first);
caryclark54359292015-03-26 07:52:43 -07001311 next = first;
1312 do {
1313 next->debugValidate();
1314 next = next->fNext;
1315 } while (next && next != first);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001316}
1317#endif
1318
caryclark54359292015-03-26 07:52:43 -07001319void SkOpAngle::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07001320#if DEBUG_COINCIDENCE
1321 if (this->globalState()->debugCheckHealth()) {
1322 return;
1323 }
1324#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001325#if DEBUG_VALIDATE
caryclark54359292015-03-26 07:52:43 -07001326 const SkOpAngle* first = this;
1327 const SkOpAngle* next = this;
1328 int wind = 0;
1329 int opp = 0;
1330 int lastXor = -1;
1331 int lastOppXor = -1;
1332 do {
1333 if (next->unorderable()) {
1334 return;
1335 }
1336 const SkOpSpan* minSpan = next->start()->starter(next->end());
1337 if (minSpan->windValue() == SK_MinS32) {
1338 return;
1339 }
1340 bool op = next->segment()->operand();
1341 bool isXor = next->segment()->isXor();
1342 bool oppXor = next->segment()->oppXor();
1343 SkASSERT(!DEBUG_LIMIT_WIND_SUM || between(0, minSpan->windValue(), DEBUG_LIMIT_WIND_SUM));
1344 SkASSERT(!DEBUG_LIMIT_WIND_SUM
1345 || between(-DEBUG_LIMIT_WIND_SUM, minSpan->oppValue(), DEBUG_LIMIT_WIND_SUM));
1346 bool useXor = op ? oppXor : isXor;
1347 SkASSERT(lastXor == -1 || lastXor == (int) useXor);
1348 lastXor = (int) useXor;
caryclark624637c2015-05-11 07:21:27 -07001349 wind += next->debugSign() * (op ? minSpan->oppValue() : minSpan->windValue());
caryclark54359292015-03-26 07:52:43 -07001350 if (useXor) {
1351 wind &= 1;
1352 }
1353 useXor = op ? isXor : oppXor;
1354 SkASSERT(lastOppXor == -1 || lastOppXor == (int) useXor);
1355 lastOppXor = (int) useXor;
caryclark624637c2015-05-11 07:21:27 -07001356 opp += next->debugSign() * (op ? minSpan->windValue() : minSpan->oppValue());
caryclark54359292015-03-26 07:52:43 -07001357 if (useXor) {
1358 opp &= 1;
1359 }
1360 next = next->fNext;
1361 } while (next && next != first);
Cary Clark59d5a0e2017-01-23 14:38:52 +00001362 SkASSERT(wind == 0 || !SkPathOpsDebug::gRunFail);
1363 SkASSERT(opp == 0 || !SkPathOpsDebug::gRunFail);
caryclark54359292015-03-26 07:52:43 -07001364#endif
1365}
1366
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001367void SkOpAngle::debugValidateNext() const {
caryclark54359292015-03-26 07:52:43 -07001368#if !FORCE_RELEASE
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001369 const SkOpAngle* first = this;
1370 const SkOpAngle* next = first;
1371 SkTDArray<const SkOpAngle*>(angles);
1372 do {
djsollenf2b340f2016-01-29 08:51:04 -08001373// SkASSERT_RELEASE(next->fSegment->debugContains(next));
Mike Reedb5475792018-08-08 16:17:42 -04001374 angles.push_back(next);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001375 next = next->next();
1376 if (next == first) {
1377 break;
1378 }
djsollenf2b340f2016-01-29 08:51:04 -08001379 SkASSERT_RELEASE(!angles.contains(next));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001380 if (!next) {
1381 return;
1382 }
1383 } while (true);
reed0dc4dd62015-03-24 13:55:33 -07001384#endif
reed0dc4dd62015-03-24 13:55:33 -07001385}
reed0dc4dd62015-03-24 13:55:33 -07001386
caryclark55888e42016-07-18 10:01:36 -07001387#ifdef SK_DEBUG
1388void SkCoincidentSpans::debugStartCheck(const SkOpSpanBase* outer, const SkOpSpanBase* over,
1389 const SkOpGlobalState* debugState) const {
Cary Clark59d5a0e2017-01-23 14:38:52 +00001390 SkASSERT(coinPtTEnd()->span() == over || !SkOpGlobalState::DebugRunFail());
1391 SkASSERT(oppPtTEnd()->span() == outer || !SkOpGlobalState::DebugRunFail());
caryclark55888e42016-07-18 10:01:36 -07001392}
1393#endif
caryclark26ad22a2015-10-16 09:03:38 -07001394
Cary Clarkab87d7a2016-10-04 10:01:04 -04001395#if DEBUG_COIN
1396// sets the span's end to the ptT referenced by the previous-next
1397void SkCoincidentSpans::debugCorrectOneEnd(SkPathOpsDebug::GlitchLog* log,
1398 const SkOpPtT* (SkCoincidentSpans::* getEnd)() const,
1399 void (SkCoincidentSpans::*setEnd)(const SkOpPtT* ptT) const ) const {
1400 const SkOpPtT* origPtT = (this->*getEnd)();
1401 const SkOpSpanBase* origSpan = origPtT->span();
1402 const SkOpSpan* prev = origSpan->prev();
1403 const SkOpPtT* testPtT = prev ? prev->next()->ptT()
1404 : origSpan->upCast()->next()->prev()->ptT();
1405 if (origPtT != testPtT) {
1406 log->record(SkPathOpsDebug::kCorrectEnd_Glitch, this, origPtT, testPtT);
1407 }
1408}
1409
1410
1411/* Commented-out lines keep this in sync with correctEnds */
1412// FIXME: member pointers have fallen out of favor and can be replaced with
1413// an alternative approach.
1414// makes all span ends agree with the segment's spans that define them
1415void SkCoincidentSpans::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
1416 this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTStart, nullptr);
1417 this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTEnd, nullptr);
1418 this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTStart, nullptr);
1419 this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTEnd, nullptr);
1420}
1421
caryclark55888e42016-07-18 10:01:36 -07001422/* Commented-out lines keep this in sync with expand */
caryclark30b9fdd2016-08-31 14:36:29 -07001423// expand the range by checking adjacent spans for coincidence
Cary Clarkab87d7a2016-10-04 10:01:04 -04001424bool SkCoincidentSpans::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07001425 bool expanded = false;
1426 const SkOpSegment* segment = coinPtTStart()->segment();
1427 const SkOpSegment* oppSegment = oppPtTStart()->segment();
1428 do {
1429 const SkOpSpan* start = coinPtTStart()->span()->upCast();
1430 const SkOpSpan* prev = start->prev();
1431 const SkOpPtT* oppPtT;
1432 if (!prev || !(oppPtT = prev->contains(oppSegment))) {
1433 break;
1434 }
1435 double midT = (prev->t() + start->t()) / 2;
1436 if (!segment->isClose(midT, oppSegment)) {
1437 break;
1438 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001439 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, prev->ptT(), oppPtT);
caryclark55888e42016-07-18 10:01:36 -07001440 expanded = true;
1441 } while (false); // actual continues while expansion is possible
1442 do {
1443 const SkOpSpanBase* end = coinPtTEnd()->span();
1444 SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
caryclark30b9fdd2016-08-31 14:36:29 -07001445 if (next && next->deleted()) {
1446 break;
1447 }
caryclark55888e42016-07-18 10:01:36 -07001448 const SkOpPtT* oppPtT;
1449 if (!next || !(oppPtT = next->contains(oppSegment))) {
1450 break;
1451 }
1452 double midT = (end->t() + next->t()) / 2;
1453 if (!segment->isClose(midT, oppSegment)) {
1454 break;
1455 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001456 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, next->ptT(), oppPtT);
caryclark55888e42016-07-18 10:01:36 -07001457 expanded = true;
1458 } while (false); // actual continues while expansion is possible
1459 return expanded;
1460}
1461
Cary Clarkab87d7a2016-10-04 10:01:04 -04001462// description below
1463void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* base, const SkOpSpanBase* testSpan) const {
1464 const SkOpPtT* testPtT = testSpan->ptT();
1465 const SkOpPtT* stopPtT = testPtT;
1466 const SkOpSegment* baseSeg = base->segment();
1467 while ((testPtT = testPtT->next()) != stopPtT) {
1468 const SkOpSegment* testSeg = testPtT->segment();
1469 if (testPtT->deleted()) {
1470 continue;
1471 }
1472 if (testSeg == baseSeg) {
1473 continue;
1474 }
1475 if (testPtT->span()->ptT() != testPtT) {
1476 continue;
1477 }
1478 if (this->contains(baseSeg, testSeg, testPtT->fT)) {
1479 continue;
1480 }
1481 // intersect perp with base->ptT() with testPtT->segment()
1482 SkDVector dxdy = baseSeg->dSlopeAtT(base->t());
1483 const SkPoint& pt = base->pt();
1484 SkDLine ray = {{{pt.fX, pt.fY}, {pt.fX + dxdy.fY, pt.fY - dxdy.fX}}};
1485 SkIntersections i;
1486 (*CurveIntersectRay[testSeg->verb()])(testSeg->pts(), testSeg->weight(), ray, &i);
1487 for (int index = 0; index < i.used(); ++index) {
1488 double t = i[0][index];
1489 if (!between(0, t, 1)) {
1490 continue;
1491 }
1492 SkDPoint oppPt = i.pt(index);
1493 if (!oppPt.approximatelyEqual(pt)) {
1494 continue;
1495 }
1496 SkOpSegment* writableSeg = const_cast<SkOpSegment*>(testSeg);
1497 SkOpPtT* oppStart = writableSeg->addT(t);
1498 if (oppStart == testPtT) {
1499 continue;
1500 }
1501 SkOpSpan* writableBase = const_cast<SkOpSpan*>(base);
1502 oppStart->span()->addOpp(writableBase);
1503 if (oppStart->deleted()) {
1504 continue;
1505 }
1506 SkOpSegment* coinSeg = base->segment();
1507 SkOpSegment* oppSeg = oppStart->segment();
1508 double coinTs, coinTe, oppTs, oppTe;
1509 if (Ordered(coinSeg, oppSeg)) {
1510 coinTs = base->t();
1511 coinTe = testSpan->t();
1512 oppTs = oppStart->fT;
1513 oppTe = testPtT->fT;
1514 } else {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001515 using std::swap;
1516 swap(coinSeg, oppSeg);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001517 coinTs = oppStart->fT;
1518 coinTe = testPtT->fT;
1519 oppTs = base->t();
1520 oppTe = testSpan->t();
1521 }
1522 if (coinTs > coinTe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001523 using std::swap;
1524 swap(coinTs, coinTe);
1525 swap(oppTs, oppTe);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001526 }
1527 bool added;
1528 if (this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &added), false) {
1529 return;
1530 }
1531 }
1532 }
1533 return;
1534}
1535
1536// description below
1537void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* ptT) const {
1538 FAIL_IF(!ptT->span()->upCastable(), ptT->span());
1539 const SkOpSpan* base = ptT->span()->upCast();
1540 const SkOpSpan* prev = base->prev();
1541 FAIL_IF(!prev, ptT->span());
1542 if (!prev->isCanceled()) {
1543 if (this->debugAddEndMovedSpans(log, base, base->prev()), false) {
1544 return;
1545 }
1546 }
1547 if (!base->isCanceled()) {
1548 if (this->debugAddEndMovedSpans(log, base, base->next()), false) {
1549 return;
1550 }
1551 }
1552 return;
1553}
1554
1555/* If A is coincident with B and B includes an endpoint, and A's matching point
1556 is not the endpoint (i.e., there's an implied line connecting B-end and A)
1557 then assume that the same implied line may intersect another curve close to B.
1558 Since we only care about coincidence that was undetected, look at the
1559 ptT list on B-segment adjacent to the B-end/A ptT loop (not in the loop, but
1560 next door) and see if the A matching point is close enough to form another
1561 coincident pair. If so, check for a new coincident span between B-end/A ptT loop
1562 and the adjacent ptT loop.
1563*/
1564void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log) const {
1565 const SkCoincidentSpans* span = fHead;
1566 if (!span) {
1567 return;
1568 }
1569// fTop = span;
1570// fHead = nullptr;
1571 do {
1572 if (span->coinPtTStart()->fPt != span->oppPtTStart()->fPt) {
1573 FAIL_IF(1 == span->coinPtTStart()->fT, span);
1574 bool onEnd = span->coinPtTStart()->fT == 0;
1575 bool oOnEnd = zero_or_one(span->oppPtTStart()->fT);
1576 if (onEnd) {
1577 if (!oOnEnd) { // if both are on end, any nearby intersect was already found
1578 if (this->debugAddEndMovedSpans(log, span->oppPtTStart()), false) {
1579 return;
1580 }
1581 }
1582 } else if (oOnEnd) {
1583 if (this->debugAddEndMovedSpans(log, span->coinPtTStart()), false) {
1584 return;
1585 }
1586 }
1587 }
1588 if (span->coinPtTEnd()->fPt != span->oppPtTEnd()->fPt) {
1589 bool onEnd = span->coinPtTEnd()->fT == 1;
1590 bool oOnEnd = zero_or_one(span->oppPtTEnd()->fT);
1591 if (onEnd) {
1592 if (!oOnEnd) {
1593 if (this->debugAddEndMovedSpans(log, span->oppPtTEnd()), false) {
1594 return;
1595 }
1596 }
1597 } else if (oOnEnd) {
1598 if (this->debugAddEndMovedSpans(log, span->coinPtTEnd()), false) {
1599 return;
1600 }
1601 }
1602 }
1603 } while ((span = span->next()));
1604// this->restoreHead();
1605 return;
1606}
1607
caryclark55888e42016-07-18 10:01:36 -07001608/* Commented-out lines keep this in sync with addExpanded */
1609// for each coincident pair, match the spans
1610// 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 -04001611void SkOpCoincidence::debugAddExpanded(SkPathOpsDebug::GlitchLog* log) const {
caryclarka35ab3e2016-10-20 08:32:18 -07001612// DEBUG_SET_PHASE();
caryclark26ad22a2015-10-16 09:03:38 -07001613 const SkCoincidentSpans* coin = this->fHead;
1614 if (!coin) {
caryclarked0935a2015-10-22 07:23:52 -07001615 return;
1616 }
caryclark26ad22a2015-10-16 09:03:38 -07001617 do {
caryclark55888e42016-07-18 10:01:36 -07001618 const SkOpPtT* startPtT = coin->coinPtTStart();
1619 const SkOpPtT* oStartPtT = coin->oppPtTStart();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001620 double priorT = startPtT->fT;
1621 double oPriorT = oStartPtT->fT;
Cary Clarkff114282016-12-14 11:56:16 -05001622 FAIL_IF(!startPtT->contains(oStartPtT), coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001623 SkOPASSERT(coin->coinPtTEnd()->contains(coin->oppPtTEnd()));
caryclark26ad22a2015-10-16 09:03:38 -07001624 const SkOpSpanBase* start = startPtT->span();
1625 const SkOpSpanBase* oStart = oStartPtT->span();
caryclark55888e42016-07-18 10:01:36 -07001626 const SkOpSpanBase* end = coin->coinPtTEnd()->span();
1627 const SkOpSpanBase* oEnd = coin->oppPtTEnd()->span();
caryclark30b9fdd2016-08-31 14:36:29 -07001628 FAIL_IF(oEnd->deleted(), coin);
1629 FAIL_IF(!start->upCastable(), coin);
caryclark26ad22a2015-10-16 09:03:38 -07001630 const SkOpSpanBase* test = start->upCast()->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001631 FAIL_IF(!coin->flipped() && !oStart->upCastable(), coin);
caryclark55888e42016-07-18 10:01:36 -07001632 const SkOpSpanBase* oTest = coin->flipped() ? oStart->prev() : oStart->upCast()->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001633 FAIL_IF(!oTest, coin);
1634 const SkOpSegment* seg = start->segment();
1635 const SkOpSegment* oSeg = oStart->segment();
caryclark26ad22a2015-10-16 09:03:38 -07001636 while (test != end || oTest != oEnd) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001637 const SkOpPtT* containedOpp = test->ptT()->contains(oSeg);
1638 const SkOpPtT* containedThis = oTest->ptT()->contains(seg);
1639 if (!containedOpp || !containedThis) {
1640 // choose the ends, or the first common pt-t list shared by both
1641 double nextT, oNextT;
1642 if (containedOpp) {
1643 nextT = test->t();
1644 oNextT = containedOpp->fT;
1645 } else if (containedThis) {
1646 nextT = containedThis->fT;
1647 oNextT = oTest->t();
1648 } else {
1649 // iterate through until a pt-t list found that contains the other
1650 const SkOpSpanBase* walk = test;
1651 const SkOpPtT* walkOpp;
1652 do {
1653 FAIL_IF(!walk->upCastable(), coin);
1654 walk = walk->upCast()->next();
1655 } while (!(walkOpp = walk->ptT()->contains(oSeg))
1656 && walk != coin->coinPtTEnd()->span());
caryclarka35ab3e2016-10-20 08:32:18 -07001657 FAIL_IF(!walkOpp, coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001658 nextT = walk->t();
1659 oNextT = walkOpp->fT;
1660 }
caryclark26ad22a2015-10-16 09:03:38 -07001661 // use t ranges to guess which one is missing
caryclarka35ab3e2016-10-20 08:32:18 -07001662 double startRange = nextT - priorT;
caryclark30b9fdd2016-08-31 14:36:29 -07001663 FAIL_IF(!startRange, coin);
caryclarka35ab3e2016-10-20 08:32:18 -07001664 double startPart = (test->t() - priorT) / startRange;
1665 double oStartRange = oNextT - oPriorT;
caryclark30b9fdd2016-08-31 14:36:29 -07001666 FAIL_IF(!oStartRange, coin);
caryclark26ad22a2015-10-16 09:03:38 -07001667 double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange;
caryclark30b9fdd2016-08-31 14:36:29 -07001668 FAIL_IF(startPart == oStartPart, coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001669 bool addToOpp = !containedOpp && !containedThis ? startPart < oStartPart
1670 : !!containedThis;
caryclark55888e42016-07-18 10:01:36 -07001671 bool startOver = false;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001672 addToOpp ? log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch,
1673 oPriorT + oStartRange * startPart, test)
1674 : log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch,
1675 priorT + startRange * oStartPart, oTest);
1676 // FAIL_IF(!success, coin);
caryclark55888e42016-07-18 10:01:36 -07001677 if (startOver) {
1678 test = start;
1679 oTest = oStart;
caryclark26ad22a2015-10-16 09:03:38 -07001680 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001681 end = coin->coinPtTEnd()->span();
1682 oEnd = coin->oppPtTEnd()->span();
caryclark26ad22a2015-10-16 09:03:38 -07001683 }
caryclark55888e42016-07-18 10:01:36 -07001684 if (test != end) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001685 FAIL_IF(!test->upCastable(), coin);
1686 priorT = test->t();
caryclark26ad22a2015-10-16 09:03:38 -07001687 test = test->upCast()->next();
1688 }
caryclark55888e42016-07-18 10:01:36 -07001689 if (oTest != oEnd) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001690 oPriorT = oTest->t();
caryclark55888e42016-07-18 10:01:36 -07001691 oTest = coin->flipped() ? oTest->prev() : oTest->upCast()->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001692 FAIL_IF(!oTest, coin);
caryclark26ad22a2015-10-16 09:03:38 -07001693 }
1694 }
caryclark55888e42016-07-18 10:01:36 -07001695 } while ((coin = coin->next()));
1696 return;
caryclark26ad22a2015-10-16 09:03:38 -07001697}
1698
caryclark55888e42016-07-18 10:01:36 -07001699/* Commented-out lines keep this in sync addIfMissing() */
caryclark8016b262016-09-06 05:59:47 -07001700// note that over1s, over1e, over2s, over2e are ordered
Cary Clarkab87d7a2016-10-04 10:01:04 -04001701void SkOpCoincidence::debugAddIfMissing(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* over1s, const SkOpPtT* over2s,
caryclark81a478c2016-09-09 09:37:57 -07001702 double tStart, double tEnd, const SkOpSegment* coinSeg, const SkOpSegment* oppSeg, bool* added,
caryclark8016b262016-09-06 05:59:47 -07001703 const SkOpPtT* over1e, const SkOpPtT* over2e) const {
1704 SkASSERT(tStart < tEnd);
1705 SkASSERT(over1s->fT < over1e->fT);
1706 SkASSERT(between(over1s->fT, tStart, over1e->fT));
1707 SkASSERT(between(over1s->fT, tEnd, over1e->fT));
1708 SkASSERT(over2s->fT < over2e->fT);
1709 SkASSERT(between(over2s->fT, tStart, over2e->fT));
1710 SkASSERT(between(over2s->fT, tEnd, over2e->fT));
1711 SkASSERT(over1s->segment() == over1e->segment());
1712 SkASSERT(over2s->segment() == over2e->segment());
1713 SkASSERT(over1s->segment() == over2s->segment());
1714 SkASSERT(over1s->segment() != coinSeg);
1715 SkASSERT(over1s->segment() != oppSeg);
1716 SkASSERT(coinSeg != oppSeg);
caryclark26ad22a2015-10-16 09:03:38 -07001717 double coinTs, coinTe, oppTs, oppTe;
caryclark8016b262016-09-06 05:59:47 -07001718 coinTs = TRange(over1s, tStart, coinSeg SkDEBUGPARAMS(over1e));
1719 coinTe = TRange(over1s, tEnd, coinSeg SkDEBUGPARAMS(over1e));
Cary Clarkba610292018-06-19 09:47:15 -04001720 SkOpSpanBase::Collapsed result = coinSeg->collapsed(coinTs, coinTe);
1721 if (SkOpSpanBase::Collapsed::kNo != result) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001722 return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, coinSeg);
caryclark8016b262016-09-06 05:59:47 -07001723 }
1724 oppTs = TRange(over2s, tStart, oppSeg SkDEBUGPARAMS(over2e));
1725 oppTe = TRange(over2s, tEnd, oppSeg SkDEBUGPARAMS(over2e));
Cary Clarkba610292018-06-19 09:47:15 -04001726 result = oppSeg->collapsed(oppTs, oppTe);
1727 if (SkOpSpanBase::Collapsed::kNo != result) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001728 return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, oppSeg);
caryclark8016b262016-09-06 05:59:47 -07001729 }
1730 if (coinTs > coinTe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001731 using std::swap;
1732 swap(coinTs, coinTe);
1733 swap(oppTs, oppTe);
caryclark26ad22a2015-10-16 09:03:38 -07001734 }
Cary Clarkba610292018-06-19 09:47:15 -04001735 this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, added);
1736 return;
caryclark26ad22a2015-10-16 09:03:38 -07001737}
1738
caryclark55888e42016-07-18 10:01:36 -07001739/* Commented-out lines keep this in sync addOrOverlap() */
caryclark30b9fdd2016-08-31 14:36:29 -07001740// If this is called by addEndMovedSpans(), a returned false propogates out to an abort.
1741// If this is called by AddIfMissing(), a returned false indicates there was nothing to add
Cary Clarkab87d7a2016-10-04 10:01:04 -04001742void SkOpCoincidence::debugAddOrOverlap(SkPathOpsDebug::GlitchLog* log,
caryclark30b9fdd2016-08-31 14:36:29 -07001743 const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
caryclark81a478c2016-09-09 09:37:57 -07001744 double coinTs, double coinTe, double oppTs, double oppTe, bool* added) const {
caryclark55888e42016-07-18 10:01:36 -07001745 SkTDArray<SkCoincidentSpans*> overlaps;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001746 SkOPASSERT(!fTop); // this is (correctly) reversed in addifMissing()
caryclark81a478c2016-09-09 09:37:57 -07001747 if (fTop && !this->checkOverlap(fTop, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe,
1748 &overlaps)) {
caryclark55888e42016-07-18 10:01:36 -07001749 return;
1750 }
1751 if (fHead && !this->checkOverlap(fHead, coinSeg, oppSeg, coinTs,
1752 coinTe, oppTs, oppTe, &overlaps)) {
1753 return;
1754 }
1755 const SkCoincidentSpans* overlap = overlaps.count() ? overlaps[0] : nullptr;
1756 for (int index = 1; index < overlaps.count(); ++index) { // combine overlaps before continuing
1757 const SkCoincidentSpans* test = overlaps[index];
1758 if (overlap->coinPtTStart()->fT > test->coinPtTStart()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001759 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTStart());
caryclark55888e42016-07-18 10:01:36 -07001760 }
1761 if (overlap->coinPtTEnd()->fT < test->coinPtTEnd()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001762 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07001763 }
1764 if (overlap->flipped()
1765 ? overlap->oppPtTStart()->fT < test->oppPtTStart()->fT
1766 : overlap->oppPtTStart()->fT > test->oppPtTStart()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001767 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTStart());
caryclark55888e42016-07-18 10:01:36 -07001768 }
1769 if (overlap->flipped()
1770 ? overlap->oppPtTEnd()->fT > test->oppPtTEnd()->fT
1771 : overlap->oppPtTEnd()->fT < test->oppPtTEnd()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001772 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07001773 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001774 if (!fHead) { this->debugRelease(log, fHead, test);
1775 this->debugRelease(log, fTop, test);
caryclark55888e42016-07-18 10:01:36 -07001776 }
1777 }
1778 const SkOpPtT* cs = coinSeg->existing(coinTs, oppSeg);
1779 const SkOpPtT* ce = coinSeg->existing(coinTe, oppSeg);
caryclark30b9fdd2016-08-31 14:36:29 -07001780 RETURN_FALSE_IF(overlap && cs && ce && overlap->contains(cs, ce), coinSeg);
1781 RETURN_FALSE_IF(cs != ce || !cs, coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001782 const SkOpPtT* os = oppSeg->existing(oppTs, coinSeg);
1783 const SkOpPtT* oe = oppSeg->existing(oppTe, coinSeg);
caryclark30b9fdd2016-08-31 14:36:29 -07001784 RETURN_FALSE_IF(overlap && os && oe && overlap->contains(os, oe), oppSeg);
caryclark55888e42016-07-18 10:01:36 -07001785 SkASSERT(true || !cs || !cs->deleted());
1786 SkASSERT(true || !os || !os->deleted());
1787 SkASSERT(true || !ce || !ce->deleted());
1788 SkASSERT(true || !oe || !oe->deleted());
1789 const SkOpPtT* csExisting = !cs ? coinSeg->existing(coinTs, nullptr) : nullptr;
1790 const SkOpPtT* ceExisting = !ce ? coinSeg->existing(coinTe, nullptr) : nullptr;
caryclark30b9fdd2016-08-31 14:36:29 -07001791 RETURN_FALSE_IF(csExisting && csExisting == ceExisting, coinSeg);
1792 RETURN_FALSE_IF(csExisting && (csExisting == ce ||
1793 csExisting->contains(ceExisting ? ceExisting : ce)), coinSeg);
1794 RETURN_FALSE_IF(ceExisting && (ceExisting == cs ||
1795 ceExisting->contains(csExisting ? csExisting : cs)), coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001796 const SkOpPtT* osExisting = !os ? oppSeg->existing(oppTs, nullptr) : nullptr;
1797 const SkOpPtT* oeExisting = !oe ? oppSeg->existing(oppTe, nullptr) : nullptr;
caryclark30b9fdd2016-08-31 14:36:29 -07001798 RETURN_FALSE_IF(osExisting && osExisting == oeExisting, oppSeg);
1799 RETURN_FALSE_IF(osExisting && (osExisting == oe ||
1800 osExisting->contains(oeExisting ? oeExisting : oe)), oppSeg);
1801 RETURN_FALSE_IF(oeExisting && (oeExisting == os ||
1802 oeExisting->contains(osExisting ? osExisting : os)), oppSeg);
caryclark55888e42016-07-18 10:01:36 -07001803 bool csDeleted = false, osDeleted = false, ceDeleted = false, oeDeleted = false;
1804 this->debugValidate();
1805 if (!cs || !os) {
1806 if (!cs)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001807 cs = coinSeg->debugAddT(coinTs, log);
caryclark55888e42016-07-18 10:01:36 -07001808 if (!os)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001809 os = oppSeg->debugAddT(oppTs, log);
caryclark30b9fdd2016-08-31 14:36:29 -07001810// RETURN_FALSE_IF(callerAborts, !csWritable || !osWritable);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001811 if (cs && os) cs->span()->debugAddOpp(log, os->span());
caryclark55888e42016-07-18 10:01:36 -07001812// cs = csWritable;
caryclark30b9fdd2016-08-31 14:36:29 -07001813// os = osWritable->active();
1814 RETURN_FALSE_IF((ce && ce->deleted()) || (oe && oe->deleted()), coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001815 }
1816 if (!ce || !oe) {
1817 if (!ce)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001818 ce = coinSeg->debugAddT(coinTe, log);
caryclark55888e42016-07-18 10:01:36 -07001819 if (!oe)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001820 oe = oppSeg->debugAddT(oppTe, log);
1821 if (ce && oe) ce->span()->debugAddOpp(log, oe->span());
caryclark55888e42016-07-18 10:01:36 -07001822// ce = ceWritable;
1823// oe = oeWritable;
1824 }
1825 this->debugValidate();
caryclark30b9fdd2016-08-31 14:36:29 -07001826 RETURN_FALSE_IF(csDeleted, coinSeg);
Ben Wagner63fd7602017-10-09 15:45:33 -04001827 RETURN_FALSE_IF(osDeleted, oppSeg);
1828 RETURN_FALSE_IF(ceDeleted, coinSeg);
caryclark30b9fdd2016-08-31 14:36:29 -07001829 RETURN_FALSE_IF(oeDeleted, oppSeg);
caryclark8016b262016-09-06 05:59:47 -07001830 RETURN_FALSE_IF(!cs || !ce || cs == ce || cs->contains(ce) || !os || !oe || os == oe || os->contains(oe), coinSeg);
1831 bool result = true;
caryclark55888e42016-07-18 10:01:36 -07001832 if (overlap) {
1833 if (overlap->coinPtTStart()->segment() == coinSeg) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001834 log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
caryclark55888e42016-07-18 10:01:36 -07001835 } else {
1836 if (oppTs > oppTe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001837 using std::swap;
1838 swap(coinTs, coinTe);
1839 swap(oppTs, oppTe);
caryclark55888e42016-07-18 10:01:36 -07001840 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001841 log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, oppSeg, oppTs, oppTe, coinSeg, coinTs, coinTe);
caryclark55888e42016-07-18 10:01:36 -07001842 }
caryclark8016b262016-09-06 05:59:47 -07001843#if 0 && DEBUG_COINCIDENCE_VERBOSE
1844 if (result) {
1845 overlap->debugShow();
1846 }
caryclark55888e42016-07-18 10:01:36 -07001847#endif
1848 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001849 log->record(SkPathOpsDebug::kAddMissingCoin_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
caryclark8016b262016-09-06 05:59:47 -07001850#if 0 && DEBUG_COINCIDENCE_VERBOSE
1851 fHead->debugShow();
caryclark55888e42016-07-18 10:01:36 -07001852#endif
1853 }
1854 this->debugValidate();
caryclark8016b262016-09-06 05:59:47 -07001855 return (void) result;
caryclark55888e42016-07-18 10:01:36 -07001856}
1857
1858// Extra commented-out lines keep this in sync with addMissing()
1859/* detects overlaps of different coincident runs on same segment */
1860/* does not detect overlaps for pairs without any segments in common */
1861// returns true if caller should loop again
Cary Clarkab87d7a2016-10-04 10:01:04 -04001862void SkOpCoincidence::debugAddMissing(SkPathOpsDebug::GlitchLog* log, bool* added) const {
caryclark26ad22a2015-10-16 09:03:38 -07001863 const SkCoincidentSpans* outer = fHead;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001864 *added = false;
caryclark26ad22a2015-10-16 09:03:38 -07001865 if (!outer) {
1866 return;
1867 }
caryclark55888e42016-07-18 10:01:36 -07001868 // fTop = outer;
1869 // fHead = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -07001870 do {
1871 // addifmissing can modify the list that this is walking
1872 // save head so that walker can iterate over old data unperturbed
1873 // addifmissing adds to head freely then add saved head in the end
caryclark8016b262016-09-06 05:59:47 -07001874 const SkOpPtT* ocs = outer->coinPtTStart();
1875 SkASSERT(!ocs->deleted());
1876 const SkOpSegment* outerCoin = ocs->segment();
1877 SkASSERT(!outerCoin->done()); // if it's done, should have already been removed from list
1878 const SkOpPtT* oos = outer->oppPtTStart();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001879 if (oos->deleted()) {
1880 return;
1881 }
caryclark8016b262016-09-06 05:59:47 -07001882 const SkOpSegment* outerOpp = oos->segment();
1883 SkASSERT(!outerOpp->done());
1884// SkOpSegment* outerCoinWritable = const_cast<SkOpSegment*>(outerCoin);
1885// SkOpSegment* outerOppWritable = const_cast<SkOpSegment*>(outerOpp);
caryclark26ad22a2015-10-16 09:03:38 -07001886 const SkCoincidentSpans* inner = outer;
caryclark55888e42016-07-18 10:01:36 -07001887 while ((inner = inner->next())) {
1888 this->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001889 double overS, overE;
caryclark8016b262016-09-06 05:59:47 -07001890 const SkOpPtT* ics = inner->coinPtTStart();
1891 SkASSERT(!ics->deleted());
1892 const SkOpSegment* innerCoin = ics->segment();
1893 SkASSERT(!innerCoin->done());
1894 const SkOpPtT* ios = inner->oppPtTStart();
1895 SkASSERT(!ios->deleted());
1896 const SkOpSegment* innerOpp = ios->segment();
1897 SkASSERT(!innerOpp->done());
1898// SkOpSegment* innerCoinWritable = const_cast<SkOpSegment*>(innerCoin);
1899// SkOpSegment* innerOppWritable = const_cast<SkOpSegment*>(innerOpp);
caryclark55888e42016-07-18 10:01:36 -07001900 if (outerCoin == innerCoin) {
caryclark8016b262016-09-06 05:59:47 -07001901 const SkOpPtT* oce = outer->coinPtTEnd();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001902 if (oce->deleted()) {
1903 return;
1904 }
caryclark8016b262016-09-06 05:59:47 -07001905 const SkOpPtT* ice = inner->coinPtTEnd();
1906 SkASSERT(!ice->deleted());
1907 if (outerOpp != innerOpp && this->overlap(ocs, oce, ics, ice, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001908 this->debugAddIfMissing(log, ocs->starter(oce), ics->starter(ice),
caryclark81a478c2016-09-09 09:37:57 -07001909 overS, overE, outerOpp, innerOpp, added,
caryclark8016b262016-09-06 05:59:47 -07001910 ocs->debugEnder(oce),
1911 ics->debugEnder(ice));
caryclark26ad22a2015-10-16 09:03:38 -07001912 }
caryclark55888e42016-07-18 10:01:36 -07001913 } else if (outerCoin == innerOpp) {
caryclark8016b262016-09-06 05:59:47 -07001914 const SkOpPtT* oce = outer->coinPtTEnd();
1915 SkASSERT(!oce->deleted());
1916 const SkOpPtT* ioe = inner->oppPtTEnd();
1917 SkASSERT(!ioe->deleted());
1918 if (outerOpp != innerCoin && this->overlap(ocs, oce, ios, ioe, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001919 this->debugAddIfMissing(log, ocs->starter(oce), ios->starter(ioe),
caryclark81a478c2016-09-09 09:37:57 -07001920 overS, overE, outerOpp, innerCoin, added,
caryclark8016b262016-09-06 05:59:47 -07001921 ocs->debugEnder(oce),
1922 ios->debugEnder(ioe));
caryclark26ad22a2015-10-16 09:03:38 -07001923 }
caryclark55888e42016-07-18 10:01:36 -07001924 } else if (outerOpp == innerCoin) {
caryclark8016b262016-09-06 05:59:47 -07001925 const SkOpPtT* ooe = outer->oppPtTEnd();
1926 SkASSERT(!ooe->deleted());
1927 const SkOpPtT* ice = inner->coinPtTEnd();
1928 SkASSERT(!ice->deleted());
caryclark55888e42016-07-18 10:01:36 -07001929 SkASSERT(outerCoin != innerOpp);
caryclark8016b262016-09-06 05:59:47 -07001930 if (this->overlap(oos, ooe, ics, ice, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001931 this->debugAddIfMissing(log, oos->starter(ooe), ics->starter(ice),
caryclark81a478c2016-09-09 09:37:57 -07001932 overS, overE, outerCoin, innerOpp, added,
caryclark8016b262016-09-06 05:59:47 -07001933 oos->debugEnder(ooe),
1934 ics->debugEnder(ice));
caryclark26ad22a2015-10-16 09:03:38 -07001935 }
caryclark55888e42016-07-18 10:01:36 -07001936 } else if (outerOpp == innerOpp) {
caryclark8016b262016-09-06 05:59:47 -07001937 const SkOpPtT* ooe = outer->oppPtTEnd();
1938 SkASSERT(!ooe->deleted());
1939 const SkOpPtT* ioe = inner->oppPtTEnd();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001940 if (ioe->deleted()) {
1941 return;
1942 }
caryclark55888e42016-07-18 10:01:36 -07001943 SkASSERT(outerCoin != innerCoin);
caryclark8016b262016-09-06 05:59:47 -07001944 if (this->overlap(oos, ooe, ios, ioe, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001945 this->debugAddIfMissing(log, oos->starter(ooe), ios->starter(ioe),
caryclark81a478c2016-09-09 09:37:57 -07001946 overS, overE, outerCoin, innerCoin, added,
caryclark8016b262016-09-06 05:59:47 -07001947 oos->debugEnder(ooe),
1948 ios->debugEnder(ioe));
caryclark26ad22a2015-10-16 09:03:38 -07001949 }
1950 }
caryclark55888e42016-07-18 10:01:36 -07001951 this->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001952 }
caryclark55888e42016-07-18 10:01:36 -07001953 } while ((outer = outer->next()));
1954 // this->restoreHead();
1955 return;
caryclark26ad22a2015-10-16 09:03:38 -07001956}
1957
caryclark55888e42016-07-18 10:01:36 -07001958// Commented-out lines keep this in sync with release()
Cary Clarkab87d7a2016-10-04 10:01:04 -04001959void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkCoincidentSpans* remove) const {
caryclark30b9fdd2016-08-31 14:36:29 -07001960 const SkCoincidentSpans* head = coin;
1961 const SkCoincidentSpans* prev = nullptr;
1962 const SkCoincidentSpans* next;
1963 do {
1964 next = coin->next();
1965 if (coin == remove) {
1966 if (prev) {
1967// prev->setNext(next);
1968 } else if (head == fHead) {
1969// fHead = next;
1970 } else {
1971// fTop = next;
1972 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001973 log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
caryclark30b9fdd2016-08-31 14:36:29 -07001974 }
1975 prev = coin;
1976 } while ((coin = next));
1977 return;
1978}
1979
Cary Clarkab87d7a2016-10-04 10:01:04 -04001980void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* deleted) const {
caryclark55888e42016-07-18 10:01:36 -07001981 const SkCoincidentSpans* coin = fHead;
1982 if (!coin) {
1983 return;
1984 }
1985 do {
1986 if (coin->coinPtTStart()->segment() == deleted
1987 || coin->coinPtTEnd()->segment() == deleted
1988 || coin->oppPtTStart()->segment() == deleted
1989 || coin->oppPtTEnd()->segment() == deleted) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001990 log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07001991 }
1992 } while ((coin = coin->next()));
1993}
1994
caryclark55888e42016-07-18 10:01:36 -07001995// Commented-out lines keep this in sync with expand()
1996// expand the range by checking adjacent spans for coincidence
Cary Clarkab87d7a2016-10-04 10:01:04 -04001997bool SkOpCoincidence::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07001998 const SkCoincidentSpans* coin = fHead;
1999 if (!coin) {
2000 return false;
2001 }
2002 bool expanded = false;
2003 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002004 if (coin->debugExpand(log)) {
caryclark55888e42016-07-18 10:01:36 -07002005 // check to see if multiple spans expanded so they are now identical
2006 const SkCoincidentSpans* test = fHead;
2007 do {
2008 if (coin == test) {
2009 continue;
2010 }
2011 if (coin->coinPtTStart() == test->coinPtTStart()
2012 && coin->oppPtTStart() == test->oppPtTStart()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002013 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, fHead, test->coinPtTStart());
caryclark55888e42016-07-18 10:01:36 -07002014 break;
2015 }
2016 } while ((test = test->next()));
2017 expanded = true;
caryclark26ad22a2015-10-16 09:03:38 -07002018 }
caryclark55888e42016-07-18 10:01:36 -07002019 } while ((coin = coin->next()));
caryclark26ad22a2015-10-16 09:03:38 -07002020 return expanded;
2021}
2022
caryclark55888e42016-07-18 10:01:36 -07002023// Commented-out lines keep this in sync with mark()
2024/* this sets up the coincidence links in the segments when the coincidence crosses multiple spans */
Cary Clarkab87d7a2016-10-04 10:01:04 -04002025void SkOpCoincidence::debugMark(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07002026 const SkCoincidentSpans* coin = fHead;
2027 if (!coin) {
2028 return;
2029 }
2030 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002031 FAIL_IF(!coin->coinPtTStartWritable()->span()->upCastable(), coin);
caryclark55888e42016-07-18 10:01:36 -07002032 const SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast();
2033// SkASSERT(start->deleted());
2034 const SkOpSpanBase* end = coin->coinPtTEndWritable()->span();
2035// SkASSERT(end->deleted());
2036 const SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span();
2037// SkASSERT(oStart->deleted());
2038 const SkOpSpanBase* oEnd = coin->oppPtTEndWritable()->span();
2039// SkASSERT(oEnd->deleted());
2040 bool flipped = coin->flipped();
caryclark26ad22a2015-10-16 09:03:38 -07002041 if (flipped) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002042 using std::swap;
2043 swap(oStart, oEnd);
caryclark26ad22a2015-10-16 09:03:38 -07002044 }
caryclark55888e42016-07-18 10:01:36 -07002045 /* coin and opp spans may not match up. Mark the ends, and then let the interior
2046 get marked as many times as the spans allow */
Cary Clarkab87d7a2016-10-04 10:01:04 -04002047 start->debugInsertCoincidence(log, oStart->upCast());
2048 end->debugInsertCoinEnd(log, oEnd);
caryclark55888e42016-07-18 10:01:36 -07002049 const SkOpSegment* segment = start->segment();
2050 const SkOpSegment* oSegment = oStart->segment();
caryclark26ad22a2015-10-16 09:03:38 -07002051 const SkOpSpanBase* next = start;
2052 const SkOpSpanBase* oNext = oStart;
caryclarka35ab3e2016-10-20 08:32:18 -07002053 bool ordered;
2054 FAIL_IF(!coin->ordered(&ordered), coin);
caryclark55888e42016-07-18 10:01:36 -07002055 while ((next = next->upCast()->next()) != end) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002056 FAIL_IF(!next->upCastable(), coin);
2057 if (next->upCast()->debugInsertCoincidence(log, oSegment, flipped, ordered), false) {
caryclark55888e42016-07-18 10:01:36 -07002058 return;
caryclark26ad22a2015-10-16 09:03:38 -07002059 }
caryclark55888e42016-07-18 10:01:36 -07002060 }
2061 while ((oNext = oNext->upCast()->next()) != oEnd) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002062 FAIL_IF(!oNext->upCastable(), coin);
2063 if (oNext->upCast()->debugInsertCoincidence(log, segment, flipped, ordered), false) {
caryclark55888e42016-07-18 10:01:36 -07002064 return;
caryclark26ad22a2015-10-16 09:03:38 -07002065 }
caryclark55888e42016-07-18 10:01:36 -07002066 }
2067 } while ((coin = coin->next()));
2068 return;
caryclark26ad22a2015-10-16 09:03:38 -07002069}
2070#endif
2071
Cary Clarkab87d7a2016-10-04 10:01:04 -04002072#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002073// Commented-out lines keep this in sync with markCollapsed()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002074void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkOpPtT* test) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002075 const SkCoincidentSpans* head = coin;
caryclark55888e42016-07-18 10:01:36 -07002076 while (coin) {
2077 if (coin->collapsed(test)) {
2078 if (zero_or_one(coin->coinPtTStart()->fT) && zero_or_one(coin->coinPtTEnd()->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002079 log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07002080 }
2081 if (zero_or_one(coin->oppPtTStart()->fT) && zero_or_one(coin->oppPtTEnd()->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002082 log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07002083 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002084 this->debugRelease(log, head, coin);
caryclark55888e42016-07-18 10:01:36 -07002085 }
2086 coin = coin->next();
caryclark624637c2015-05-11 07:21:27 -07002087 }
2088}
2089
caryclark55888e42016-07-18 10:01:36 -07002090// Commented-out lines keep this in sync with markCollapsed()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002091void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* test) const {
2092 this->debugMarkCollapsed(log, fHead, test);
2093 this->debugMarkCollapsed(log, fTop, test);
caryclark55888e42016-07-18 10:01:36 -07002094}
2095#endif
2096
2097void SkCoincidentSpans::debugShow() const {
caryclark6c3b9cd2016-09-26 05:36:58 -07002098 SkDebugf("coinSpan - id=%d t=%1.9g tEnd=%1.9g\n", coinPtTStart()->segment()->debugID(),
caryclark55888e42016-07-18 10:01:36 -07002099 coinPtTStart()->fT, coinPtTEnd()->fT);
caryclark6c3b9cd2016-09-26 05:36:58 -07002100 SkDebugf("coinSpan + id=%d t=%1.9g tEnd=%1.9g\n", oppPtTStart()->segment()->debugID(),
caryclark55888e42016-07-18 10:01:36 -07002101 oppPtTStart()->fT, oppPtTEnd()->fT);
2102}
2103
2104void SkOpCoincidence::debugShowCoincidence() const {
caryclark26ad22a2015-10-16 09:03:38 -07002105#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -07002106 const SkCoincidentSpans* span = fHead;
2107 while (span) {
2108 span->debugShow();
2109 span = span->next();
2110 }
2111#endif
2112}
2113
Cary Clarkab87d7a2016-10-04 10:01:04 -04002114#if DEBUG_COIN
caryclark6c3b9cd2016-09-26 05:36:58 -07002115static void DebugCheckBetween(const SkOpSpanBase* next, const SkOpSpanBase* end,
caryclark55888e42016-07-18 10:01:36 -07002116 double oStart, double oEnd, const SkOpSegment* oSegment,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002117 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002118 SkASSERT(next != end);
2119 SkASSERT(!next->contains(end) || log);
2120 if (next->t() > end->t()) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002121 using std::swap;
2122 swap(next, end);
caryclark55888e42016-07-18 10:01:36 -07002123 }
2124 do {
2125 const SkOpPtT* ptT = next->ptT();
2126 int index = 0;
caryclark27c015d2016-09-23 05:47:20 -07002127 bool somethingBetween = false;
caryclark55888e42016-07-18 10:01:36 -07002128 do {
2129 ++index;
2130 ptT = ptT->next();
2131 const SkOpPtT* checkPtT = next->ptT();
2132 if (ptT == checkPtT) {
2133 break;
2134 }
2135 bool looped = false;
2136 for (int check = 0; check < index; ++check) {
2137 if ((looped = checkPtT == ptT)) {
2138 break;
2139 }
2140 checkPtT = checkPtT->next();
2141 }
2142 if (looped) {
2143 SkASSERT(0);
2144 break;
2145 }
2146 if (ptT->deleted()) {
2147 continue;
2148 }
2149 if (ptT->segment() != oSegment) {
2150 continue;
2151 }
2152 somethingBetween |= between(oStart, ptT->fT, oEnd);
2153 } while (true);
2154 SkASSERT(somethingBetween);
2155 } while (next != end && (next = next->upCast()->next()));
2156}
2157
2158static void DebugCheckOverlap(const SkCoincidentSpans* test, const SkCoincidentSpans* list,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002159 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002160 if (!list) {
2161 return;
2162 }
2163 const SkOpSegment* coinSeg = test->coinPtTStart()->segment();
2164 SkASSERT(coinSeg == test->coinPtTEnd()->segment());
2165 const SkOpSegment* oppSeg = test->oppPtTStart()->segment();
2166 SkASSERT(oppSeg == test->oppPtTEnd()->segment());
2167 SkASSERT(coinSeg != test->oppPtTStart()->segment());
2168 SkDEBUGCODE(double tcs = test->coinPtTStart()->fT);
2169 SkASSERT(between(0, tcs, 1));
2170 SkDEBUGCODE(double tce = test->coinPtTEnd()->fT);
2171 SkASSERT(between(0, tce, 1));
2172 SkASSERT(tcs < tce);
2173 double tos = test->oppPtTStart()->fT;
2174 SkASSERT(between(0, tos, 1));
2175 double toe = test->oppPtTEnd()->fT;
2176 SkASSERT(between(0, toe, 1));
2177 SkASSERT(tos != toe);
2178 if (tos > toe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002179 using std::swap;
2180 swap(tos, toe);
caryclark55888e42016-07-18 10:01:36 -07002181 }
2182 do {
2183 double lcs, lce, los, loe;
2184 if (coinSeg == list->coinPtTStart()->segment()) {
2185 if (oppSeg != list->oppPtTStart()->segment()) {
2186 continue;
2187 }
2188 lcs = list->coinPtTStart()->fT;
2189 lce = list->coinPtTEnd()->fT;
2190 los = list->oppPtTStart()->fT;
2191 loe = list->oppPtTEnd()->fT;
2192 if (los > loe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002193 using std::swap;
2194 swap(los, loe);
caryclark55888e42016-07-18 10:01:36 -07002195 }
2196 } else if (coinSeg == list->oppPtTStart()->segment()) {
2197 if (oppSeg != list->coinPtTStart()->segment()) {
2198 continue;
2199 }
2200 lcs = list->oppPtTStart()->fT;
2201 lce = list->oppPtTEnd()->fT;
2202 if (lcs > lce) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002203 using std::swap;
2204 swap(lcs, lce);
caryclark55888e42016-07-18 10:01:36 -07002205 }
2206 los = list->coinPtTStart()->fT;
2207 loe = list->coinPtTEnd()->fT;
2208 } else {
2209 continue;
2210 }
2211 SkASSERT(tce < lcs || lce < tcs);
2212 SkASSERT(toe < los || loe < tos);
2213 } while ((list = list->next()));
2214}
2215
2216
2217static void DebugCheckOverlapTop(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002218 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002219 // check for overlapping coincident spans
2220 const SkCoincidentSpans* test = head;
2221 while (test) {
2222 const SkCoincidentSpans* next = test->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04002223 DebugCheckOverlap(test, next, log);
2224 DebugCheckOverlap(test, opt, log);
caryclark55888e42016-07-18 10:01:36 -07002225 test = next;
2226 }
2227}
2228
caryclark55888e42016-07-18 10:01:36 -07002229static void DebugValidate(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002230 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002231 // look for pts inside coincident spans that are not inside the opposite spans
2232 const SkCoincidentSpans* coin = head;
2233 while (coin) {
2234 SkASSERT(SkOpCoincidence::Ordered(coin->coinPtTStart()->segment(),
2235 coin->oppPtTStart()->segment()));
2236 SkASSERT(coin->coinPtTStart()->span()->ptT() == coin->coinPtTStart());
2237 SkASSERT(coin->coinPtTEnd()->span()->ptT() == coin->coinPtTEnd());
2238 SkASSERT(coin->oppPtTStart()->span()->ptT() == coin->oppPtTStart());
2239 SkASSERT(coin->oppPtTEnd()->span()->ptT() == coin->oppPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07002240 coin = coin->next();
2241 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002242 DebugCheckOverlapTop(head, opt, log);
caryclark55888e42016-07-18 10:01:36 -07002243}
2244#endif
2245
2246void SkOpCoincidence::debugValidate() const {
2247#if DEBUG_COINCIDENCE
Cary Clarkab87d7a2016-10-04 10:01:04 -04002248 DebugValidate(fHead, fTop, nullptr);
2249 DebugValidate(fTop, nullptr, nullptr);
caryclark55888e42016-07-18 10:01:36 -07002250#endif
2251}
2252
Cary Clarkab87d7a2016-10-04 10:01:04 -04002253#if DEBUG_COIN
caryclark6c3b9cd2016-09-26 05:36:58 -07002254static void DebugCheckBetween(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002255 SkPathOpsDebug::GlitchLog* log) {
caryclark6c3b9cd2016-09-26 05:36:58 -07002256 // look for pts inside coincident spans that are not inside the opposite spans
2257 const SkCoincidentSpans* coin = head;
2258 while (coin) {
2259 DebugCheckBetween(coin->coinPtTStart()->span(), coin->coinPtTEnd()->span(),
2260 coin->oppPtTStart()->fT, coin->oppPtTEnd()->fT, coin->oppPtTStart()->segment(),
Cary Clarkab87d7a2016-10-04 10:01:04 -04002261 log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002262 DebugCheckBetween(coin->oppPtTStart()->span(), coin->oppPtTEnd()->span(),
2263 coin->coinPtTStart()->fT, coin->coinPtTEnd()->fT, coin->coinPtTStart()->segment(),
Cary Clarkab87d7a2016-10-04 10:01:04 -04002264 log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002265 coin = coin->next();
2266 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002267 DebugCheckOverlapTop(head, opt, log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002268}
2269#endif
2270
2271void SkOpCoincidence::debugCheckBetween() const {
2272#if DEBUG_COINCIDENCE
2273 if (fGlobalState->debugCheckHealth()) {
2274 return;
2275 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002276 DebugCheckBetween(fHead, fTop, nullptr);
2277 DebugCheckBetween(fTop, nullptr, nullptr);
caryclark6c3b9cd2016-09-26 05:36:58 -07002278#endif
2279}
2280
Cary Clarkab87d7a2016-10-04 10:01:04 -04002281#if DEBUG_COIN
2282void SkOpContour::debugCheckHealth(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07002283 const SkOpSegment* segment = &fHead;
2284 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002285 segment->debugCheckHealth(log);
caryclark26ad22a2015-10-16 09:03:38 -07002286 } while ((segment = segment->next()));
2287}
2288
Cary Clarkab87d7a2016-10-04 10:01:04 -04002289void SkOpCoincidence::debugCheckValid(SkPathOpsDebug::GlitchLog* log) const {
2290#if DEBUG_VALIDATE
2291 DebugValidate(fHead, fTop, log);
2292 DebugValidate(fTop, nullptr, log);
2293#endif
2294}
2295
2296void SkOpCoincidence::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
2297 const SkCoincidentSpans* coin = fHead;
2298 if (!coin) {
2299 return;
2300 }
2301 do {
2302 coin->debugCorrectEnds(log);
2303 } while ((coin = coin->next()));
2304}
2305
caryclark55888e42016-07-18 10:01:36 -07002306// commmented-out lines keep this aligned with missingCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002307void SkOpContour::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07002308// SkASSERT(fCount > 0);
caryclark26ad22a2015-10-16 09:03:38 -07002309 const SkOpSegment* segment = &fHead;
caryclark55888e42016-07-18 10:01:36 -07002310// bool result = false;
caryclark26ad22a2015-10-16 09:03:38 -07002311 do {
Cary Clarke47ae292016-10-05 08:51:39 -04002312 if (segment->debugMissingCoincidence(log), false) {
caryclark55888e42016-07-18 10:01:36 -07002313// result = true;
caryclark55888e42016-07-18 10:01:36 -07002314 }
2315 segment = segment->next();
2316 } while (segment);
2317 return;
caryclark26ad22a2015-10-16 09:03:38 -07002318}
Cary Clarkab87d7a2016-10-04 10:01:04 -04002319
2320void SkOpContour::debugMoveMultiples(SkPathOpsDebug::GlitchLog* log) const {
2321 SkASSERT(fCount > 0);
2322 const SkOpSegment* segment = &fHead;
2323 do {
2324 if (segment->debugMoveMultiples(log), false) {
2325 return;
2326 }
2327 } while ((segment = segment->next()));
2328 return;
2329}
2330
2331void SkOpContour::debugMoveNearby(SkPathOpsDebug::GlitchLog* log) const {
2332 SkASSERT(fCount > 0);
2333 const SkOpSegment* segment = &fHead;
2334 do {
2335 segment->debugMoveNearby(log);
2336 } while ((segment = segment->next()));
2337}
caryclark26ad22a2015-10-16 09:03:38 -07002338#endif
2339
caryclark025b11e2016-08-25 05:21:14 -07002340#if DEBUG_COINCIDENCE_ORDER
2341void SkOpSegment::debugResetCoinT() const {
2342 fDebugBaseIndex = -1;
2343 fDebugBaseMin = 1;
2344 fDebugBaseMax = -1;
2345 fDebugLastIndex = -1;
2346 fDebugLastMin = 1;
2347 fDebugLastMax = -1;
2348}
2349#endif
2350
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002351void SkOpSegment::debugValidate() const {
caryclark025b11e2016-08-25 05:21:14 -07002352#if DEBUG_COINCIDENCE_ORDER
2353 {
2354 const SkOpSpanBase* span = &fHead;
2355 do {
2356 span->debugResetCoinT();
2357 } while (!span->final() && (span = span->upCast()->next()));
2358 span = &fHead;
2359 int index = 0;
2360 do {
2361 span->debugSetCoinT(index++);
2362 } while (!span->final() && (span = span->upCast()->next()));
2363 }
2364#endif
caryclark55888e42016-07-18 10:01:36 -07002365#if DEBUG_COINCIDENCE
2366 if (this->globalState()->debugCheckHealth()) {
2367 return;
2368 }
2369#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002370#if DEBUG_VALIDATE
caryclark54359292015-03-26 07:52:43 -07002371 const SkOpSpanBase* span = &fHead;
2372 double lastT = -1;
halcanary96fcdcc2015-08-27 07:41:13 -07002373 const SkOpSpanBase* prev = nullptr;
caryclark54359292015-03-26 07:52:43 -07002374 int count = 0;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002375 int done = 0;
caryclark54359292015-03-26 07:52:43 -07002376 do {
2377 if (!span->final()) {
2378 ++count;
2379 done += span->upCast()->done() ? 1 : 0;
2380 }
2381 SkASSERT(span->segment() == this);
2382 SkASSERT(!prev || prev->upCast()->next() == span);
2383 SkASSERT(!prev || prev == span->prev());
2384 prev = span;
2385 double t = span->ptT()->fT;
2386 SkASSERT(lastT < t);
2387 lastT = t;
2388 span->debugValidate();
2389 } while (!span->final() && (span = span->upCast()->next()));
2390 SkASSERT(count == fCount);
2391 SkASSERT(done == fDoneCount);
caryclark08bc8482015-04-24 09:08:57 -07002392 SkASSERT(count >= fDoneCount);
caryclark54359292015-03-26 07:52:43 -07002393 SkASSERT(span->final());
2394 span->debugValidate();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002395#endif
caryclark54359292015-03-26 07:52:43 -07002396}
2397
Cary Clarkab87d7a2016-10-04 10:01:04 -04002398#if DEBUG_COIN
caryclark30b9fdd2016-08-31 14:36:29 -07002399
2400// Commented-out lines keep this in sync with addOpp()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002401void SkOpSpanBase::debugAddOpp(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002402 const SkOpPtT* oppPrev = this->ptT()->oppPrev(opp->ptT());
2403 if (!oppPrev) {
caryclark55888e42016-07-18 10:01:36 -07002404 return;
caryclark26ad22a2015-10-16 09:03:38 -07002405 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002406 this->debugMergeMatches(log, opp);
caryclark30b9fdd2016-08-31 14:36:29 -07002407 this->ptT()->debugAddOpp(opp->ptT(), oppPrev);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002408 this->debugCheckForCollapsedCoincidence(log);
caryclark26ad22a2015-10-16 09:03:38 -07002409}
2410
caryclark55888e42016-07-18 10:01:36 -07002411// Commented-out lines keep this in sync with checkForCollapsedCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002412void SkOpSpanBase::debugCheckForCollapsedCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07002413 const SkOpCoincidence* coins = this->globalState()->coincidence();
2414 if (coins->isEmpty()) {
2415 return;
2416 }
2417// the insert above may have put both ends of a coincident run in the same span
2418// for each coincident ptT in loop; see if its opposite in is also in the loop
2419// this implementation is the motivation for marking that a ptT is referenced by a coincident span
2420 const SkOpPtT* head = this->ptT();
2421 const SkOpPtT* test = head;
caryclark26ad22a2015-10-16 09:03:38 -07002422 do {
caryclark55888e42016-07-18 10:01:36 -07002423 if (!test->coincident()) {
2424 continue;
caryclark26ad22a2015-10-16 09:03:38 -07002425 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002426 coins->debugMarkCollapsed(log, test);
caryclark55888e42016-07-18 10:01:36 -07002427 } while ((test = test->next()) != head);
caryclark26ad22a2015-10-16 09:03:38 -07002428}
caryclark55888e42016-07-18 10:01:36 -07002429#endif
caryclark26ad22a2015-10-16 09:03:38 -07002430
caryclark54359292015-03-26 07:52:43 -07002431bool SkOpSpanBase::debugCoinEndLoopCheck() const {
2432 int loop = 0;
2433 const SkOpSpanBase* next = this;
2434 SkOpSpanBase* nextCoin;
2435 do {
2436 nextCoin = next->fCoinEnd;
2437 SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin);
2438 for (int check = 1; check < loop - 1; ++check) {
2439 const SkOpSpanBase* checkCoin = this->fCoinEnd;
2440 const SkOpSpanBase* innerCoin = checkCoin;
2441 for (int inner = check + 1; inner < loop; ++inner) {
2442 innerCoin = innerCoin->fCoinEnd;
2443 if (checkCoin == innerCoin) {
2444 SkDebugf("*** bad coincident end loop ***\n");
2445 return false;
2446 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002447 }
2448 }
caryclark54359292015-03-26 07:52:43 -07002449 ++loop;
2450 } while ((next = nextCoin) && next != this);
2451 return true;
2452}
2453
Cary Clarkab87d7a2016-10-04 10:01:04 -04002454#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002455// Commented-out lines keep this in sync with insertCoinEnd()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002456void SkOpSpanBase::debugInsertCoinEnd(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* coin) const {
caryclark55888e42016-07-18 10:01:36 -07002457 if (containsCoinEnd(coin)) {
2458// SkASSERT(coin->containsCoinEnd(this));
2459 return;
2460 }
2461 debugValidate();
2462// SkASSERT(this != coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002463 log->record(SkPathOpsDebug::kMarkCoinEnd_Glitch, this, coin);
caryclark55888e42016-07-18 10:01:36 -07002464// coin->fCoinEnd = this->fCoinEnd;
2465// this->fCoinEnd = coinNext;
2466 debugValidate();
2467}
2468
caryclark30b9fdd2016-08-31 14:36:29 -07002469// Commented-out lines keep this in sync with mergeMatches()
2470// Look to see if pt-t linked list contains same segment more than once
2471// if so, and if each pt-t is directly pointed to by spans in that segment,
2472// merge them
2473// keep the points, but remove spans so that the segment doesn't have 2 or more
2474// spans pointing to the same pt-t loop at different loop elements
Cary Clarkab87d7a2016-10-04 10:01:04 -04002475void SkOpSpanBase::debugMergeMatches(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002476 const SkOpPtT* test = &fPtT;
2477 const SkOpPtT* testNext;
2478 const SkOpPtT* stop = test;
2479 do {
2480 testNext = test->next();
2481 if (test->deleted()) {
2482 continue;
2483 }
2484 const SkOpSpanBase* testBase = test->span();
2485 SkASSERT(testBase->ptT() == test);
2486 const SkOpSegment* segment = test->segment();
2487 if (segment->done()) {
2488 continue;
2489 }
2490 const SkOpPtT* inner = opp->ptT();
2491 const SkOpPtT* innerStop = inner;
2492 do {
2493 if (inner->segment() != segment) {
2494 continue;
2495 }
2496 if (inner->deleted()) {
2497 continue;
2498 }
2499 const SkOpSpanBase* innerBase = inner->span();
2500 SkASSERT(innerBase->ptT() == inner);
Ben Wagner63fd7602017-10-09 15:45:33 -04002501 // when the intersection is first detected, the span base is marked if there are
caryclark30b9fdd2016-08-31 14:36:29 -07002502 // more than one point in the intersection.
2503// if (!innerBase->hasMultipleHint() && !testBase->hasMultipleHint()) {
2504 if (!zero_or_one(inner->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002505 log->record(SkPathOpsDebug::kMergeMatches_Glitch, innerBase, test);
caryclark30b9fdd2016-08-31 14:36:29 -07002506 } else {
2507 SkASSERT(inner->fT != test->fT);
2508 if (!zero_or_one(test->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002509 log->record(SkPathOpsDebug::kMergeMatches_Glitch, testBase, inner);
caryclark30b9fdd2016-08-31 14:36:29 -07002510 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002511 log->record(SkPathOpsDebug::kMergeMatches_Glitch, segment);
caryclark30b9fdd2016-08-31 14:36:29 -07002512// SkDEBUGCODE(testBase->debugSetDeleted());
2513// test->setDeleted();
2514// SkDEBUGCODE(innerBase->debugSetDeleted());
2515// inner->setDeleted();
2516 }
2517 }
2518#ifdef SK_DEBUG // assert if another undeleted entry points to segment
2519 const SkOpPtT* debugInner = inner;
2520 while ((debugInner = debugInner->next()) != innerStop) {
2521 if (debugInner->segment() != segment) {
2522 continue;
2523 }
2524 if (debugInner->deleted()) {
2525 continue;
2526 }
2527 SkOPASSERT(0);
2528 }
2529#endif
Ben Wagner63fd7602017-10-09 15:45:33 -04002530 break;
caryclark30b9fdd2016-08-31 14:36:29 -07002531// }
2532 break;
2533 } while ((inner = inner->next()) != innerStop);
2534 } while ((test = testNext) != stop);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002535 this->debugCheckForCollapsedCoincidence(log);
caryclark30b9fdd2016-08-31 14:36:29 -07002536}
2537
caryclark55888e42016-07-18 10:01:36 -07002538#endif
caryclark26ad22a2015-10-16 09:03:38 -07002539
caryclark025b11e2016-08-25 05:21:14 -07002540void SkOpSpanBase::debugResetCoinT() const {
2541#if DEBUG_COINCIDENCE_ORDER
2542 const SkOpPtT* ptT = &fPtT;
2543 do {
2544 ptT->debugResetCoinT();
2545 ptT = ptT->next();
2546 } while (ptT != &fPtT);
2547#endif
2548}
2549
2550void SkOpSpanBase::debugSetCoinT(int index) const {
2551#if DEBUG_COINCIDENCE_ORDER
2552 const SkOpPtT* ptT = &fPtT;
2553 do {
2554 if (!ptT->deleted()) {
2555 ptT->debugSetCoinT(index);
2556 }
2557 ptT = ptT->next();
2558 } while (ptT != &fPtT);
2559#endif
2560}
2561
caryclark26ad22a2015-10-16 09:03:38 -07002562const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const {
2563 const SkOpSpanBase* end = *endPtr;
2564 SkASSERT(this->segment() == end->segment());
2565 const SkOpSpanBase* result;
2566 if (t() < end->t()) {
2567 result = this;
2568 } else {
2569 result = end;
2570 *endPtr = this;
2571 }
2572 return result->upCast();
2573}
2574
caryclark54359292015-03-26 07:52:43 -07002575void SkOpSpanBase::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07002576#if DEBUG_COINCIDENCE
2577 if (this->globalState()->debugCheckHealth()) {
2578 return;
2579 }
2580#endif
caryclark54359292015-03-26 07:52:43 -07002581#if DEBUG_VALIDATE
2582 const SkOpPtT* ptT = &fPtT;
2583 SkASSERT(ptT->span() == this);
2584 do {
2585// SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt));
2586 ptT->debugValidate();
2587 ptT = ptT->next();
2588 } while (ptT != &fPtT);
2589 SkASSERT(this->debugCoinEndLoopCheck());
2590 if (!this->final()) {
2591 SkASSERT(this->upCast()->debugCoinLoopCheck());
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002592 }
caryclark54359292015-03-26 07:52:43 -07002593 if (fFromAngle) {
2594 fFromAngle->debugValidate();
2595 }
2596 if (!this->final() && this->upCast()->toAngle()) {
2597 this->upCast()->toAngle()->debugValidate();
2598 }
2599#endif
2600}
2601
2602bool SkOpSpan::debugCoinLoopCheck() const {
2603 int loop = 0;
2604 const SkOpSpan* next = this;
2605 SkOpSpan* nextCoin;
2606 do {
2607 nextCoin = next->fCoincident;
2608 SkASSERT(nextCoin == this || nextCoin->fCoincident != nextCoin);
2609 for (int check = 1; check < loop - 1; ++check) {
2610 const SkOpSpan* checkCoin = this->fCoincident;
2611 const SkOpSpan* innerCoin = checkCoin;
2612 for (int inner = check + 1; inner < loop; ++inner) {
2613 innerCoin = innerCoin->fCoincident;
2614 if (checkCoin == innerCoin) {
2615 SkDebugf("*** bad coincident loop ***\n");
2616 return false;
2617 }
2618 }
2619 }
2620 ++loop;
2621 } while ((next = nextCoin) && next != this);
2622 return true;
2623}
2624
Cary Clarkab87d7a2016-10-04 10:01:04 -04002625#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002626// Commented-out lines keep this in sync with insertCoincidence() in header
Cary Clarkab87d7a2016-10-04 10:01:04 -04002627void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* coin) const {
caryclark55888e42016-07-18 10:01:36 -07002628 if (containsCoincidence(coin)) {
2629// SkASSERT(coin->containsCoincidence(this));
2630 return;
2631 }
2632 debugValidate();
2633// SkASSERT(this != coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002634 log->record(SkPathOpsDebug::kMarkCoinStart_Glitch, this, coin);
caryclark55888e42016-07-18 10:01:36 -07002635// coin->fCoincident = this->fCoincident;
2636// this->fCoincident = coinNext;
2637 debugValidate();
2638}
2639
2640// Commented-out lines keep this in sync with insertCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002641void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* segment, bool flipped, bool ordered) const {
caryclark55888e42016-07-18 10:01:36 -07002642 if (this->containsCoincidence(segment)) {
2643 return;
2644 }
2645 const SkOpPtT* next = &fPtT;
2646 while ((next = next->next()) != &fPtT) {
2647 if (next->segment() == segment) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002648 const SkOpSpan* span;
2649 const SkOpSpanBase* base = next->span();
2650 if (!ordered) {
2651 const SkOpSpanBase* spanEnd = fNext->contains(segment)->span();
2652 const SkOpPtT* start = base->ptT()->starter(spanEnd->ptT());
2653 FAIL_IF(!start->span()->upCastable(), this);
2654 span = const_cast<SkOpSpan*>(start->span()->upCast());
2655 }
2656 else if (flipped) {
2657 span = base->prev();
2658 FAIL_IF(!span, this);
2659 }
2660 else {
2661 FAIL_IF(!base->upCastable(), this);
2662 span = base->upCast();
2663 }
2664 log->record(SkPathOpsDebug::kMarkCoinInsert_Glitch, span);
caryclark55888e42016-07-18 10:01:36 -07002665 return;
2666 }
2667 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002668#if DEBUG_COIN
2669 log->record(SkPathOpsDebug::kMarkCoinMissing_Glitch, segment, this);
caryclark55888e42016-07-18 10:01:36 -07002670#endif
Cary Clarkab87d7a2016-10-04 10:01:04 -04002671 return;
caryclark55888e42016-07-18 10:01:36 -07002672}
2673#endif
2674
caryclark624637c2015-05-11 07:21:27 -07002675// called only by test code
2676int SkIntersections::debugCoincidentUsed() const {
2677 if (!fIsCoincident[0]) {
2678 SkASSERT(!fIsCoincident[1]);
2679 return 0;
2680 }
2681 int count = 0;
2682 SkDEBUGCODE(int count2 = 0;)
2683 for (int index = 0; index < fUsed; ++index) {
2684 if (fIsCoincident[0] & (1 << index)) {
2685 ++count;
2686 }
2687#ifdef SK_DEBUG
2688 if (fIsCoincident[1] & (1 << index)) {
2689 ++count2;
2690 }
2691#endif
2692 }
2693 SkASSERT(count == count2);
2694 return count;
2695}
2696
Mike Kleinc0bd9f92019-04-23 12:05:21 -05002697#include "src/pathops/SkOpContour.h"
caryclark54359292015-03-26 07:52:43 -07002698
caryclark55888e42016-07-18 10:01:36 -07002699// Commented-out lines keep this in sync with addOpp()
caryclark29b25632016-08-25 11:27:17 -07002700void SkOpPtT::debugAddOpp(const SkOpPtT* opp, const SkOpPtT* oppPrev) const {
2701 SkDEBUGCODE(const SkOpPtT* oldNext = this->fNext);
caryclark55888e42016-07-18 10:01:36 -07002702 SkASSERT(this != opp);
2703// this->fNext = opp;
caryclark29b25632016-08-25 11:27:17 -07002704 SkASSERT(oppPrev != oldNext);
caryclark55888e42016-07-18 10:01:36 -07002705// oppPrev->fNext = oldNext;
caryclark55888e42016-07-18 10:01:36 -07002706}
2707
caryclark26ad22a2015-10-16 09:03:38 -07002708bool SkOpPtT::debugContains(const SkOpPtT* check) const {
2709 SkASSERT(this != check);
2710 const SkOpPtT* ptT = this;
2711 int links = 0;
2712 do {
2713 ptT = ptT->next();
2714 if (ptT == check) {
2715 return true;
2716 }
2717 ++links;
2718 const SkOpPtT* test = this;
2719 for (int index = 0; index < links; ++index) {
2720 if (ptT == test) {
2721 return false;
2722 }
2723 test = test->next();
2724 }
2725 } while (true);
2726}
2727
2728const SkOpPtT* SkOpPtT::debugContains(const SkOpSegment* check) const {
2729 SkASSERT(this->segment() != check);
2730 const SkOpPtT* ptT = this;
2731 int links = 0;
2732 do {
2733 ptT = ptT->next();
2734 if (ptT->segment() == check) {
2735 return ptT;
2736 }
2737 ++links;
2738 const SkOpPtT* test = this;
2739 for (int index = 0; index < links; ++index) {
2740 if (ptT == test) {
2741 return nullptr;
2742 }
2743 test = test->next();
2744 }
2745 } while (true);
2746}
2747
caryclark8016b262016-09-06 05:59:47 -07002748const SkOpPtT* SkOpPtT::debugEnder(const SkOpPtT* end) const {
2749 return fT < end->fT ? end : this;
2750}
2751
caryclark54359292015-03-26 07:52:43 -07002752int SkOpPtT::debugLoopLimit(bool report) const {
2753 int loop = 0;
2754 const SkOpPtT* next = this;
2755 do {
2756 for (int check = 1; check < loop - 1; ++check) {
2757 const SkOpPtT* checkPtT = this->fNext;
2758 const SkOpPtT* innerPtT = checkPtT;
2759 for (int inner = check + 1; inner < loop; ++inner) {
2760 innerPtT = innerPtT->fNext;
2761 if (checkPtT == innerPtT) {
2762 if (report) {
2763 SkDebugf("*** bad ptT loop ***\n");
2764 }
2765 return loop;
2766 }
2767 }
2768 }
caryclark26ad22a2015-10-16 09:03:38 -07002769 // there's nothing wrong with extremely large loop counts -- but this may appear to hang
2770 // by taking a very long time to figure out that no loop entry is a duplicate
2771 // -- and it's likely that a large loop count is indicative of a bug somewhere
2772 if (++loop > 1000) {
2773 SkDebugf("*** loop count exceeds 1000 ***\n");
2774 return 1000;
2775 }
caryclark54359292015-03-26 07:52:43 -07002776 } while ((next = next->fNext) && next != this);
2777 return 0;
2778}
2779
caryclark29b25632016-08-25 11:27:17 -07002780const SkOpPtT* SkOpPtT::debugOppPrev(const SkOpPtT* opp) const {
2781 return this->oppPrev(const_cast<SkOpPtT*>(opp));
2782}
2783
caryclark025b11e2016-08-25 05:21:14 -07002784void SkOpPtT::debugResetCoinT() const {
2785#if DEBUG_COINCIDENCE_ORDER
Ben Wagner63fd7602017-10-09 15:45:33 -04002786 this->segment()->debugResetCoinT();
caryclark025b11e2016-08-25 05:21:14 -07002787#endif
2788}
2789
2790void SkOpPtT::debugSetCoinT(int index) const {
2791#if DEBUG_COINCIDENCE_ORDER
Ben Wagner63fd7602017-10-09 15:45:33 -04002792 this->segment()->debugSetCoinT(index, fT);
caryclark025b11e2016-08-25 05:21:14 -07002793#endif
2794}
2795
caryclark54359292015-03-26 07:52:43 -07002796void SkOpPtT::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07002797#if DEBUG_COINCIDENCE
2798 if (this->globalState()->debugCheckHealth()) {
2799 return;
2800 }
2801#endif
caryclark54359292015-03-26 07:52:43 -07002802#if DEBUG_VALIDATE
Cary Clarkab87d7a2016-10-04 10:01:04 -04002803 SkOpPhase phase = contour()->globalState()->phase();
2804 if (phase == SkOpPhase::kIntersecting || phase == SkOpPhase::kFixWinding) {
caryclark54359292015-03-26 07:52:43 -07002805 return;
2806 }
2807 SkASSERT(fNext);
2808 SkASSERT(fNext != this);
2809 SkASSERT(fNext->fNext);
2810 SkASSERT(debugLoopLimit(false) == 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002811#endif
2812}
caryclark1049f122015-04-20 08:31:59 -07002813
2814static void output_scalar(SkScalar num) {
2815 if (num == (int) num) {
2816 SkDebugf("%d", (int) num);
2817 } else {
2818 SkString str;
2819 str.printf("%1.9g", num);
2820 int width = (int) str.size();
2821 const char* cStr = str.c_str();
2822 while (cStr[width - 1] == '0') {
2823 --width;
2824 }
2825 str.resize(width);
2826 SkDebugf("%sf", str.c_str());
2827 }
2828}
2829
2830static void output_points(const SkPoint* pts, int count) {
2831 for (int index = 0; index < count; ++index) {
2832 output_scalar(pts[index].fX);
2833 SkDebugf(", ");
2834 output_scalar(pts[index].fY);
2835 if (index + 1 < count) {
2836 SkDebugf(", ");
2837 }
2838 }
2839}
2840
Chris Dalton957189b2020-05-07 12:47:26 -06002841static void showPathContours(const SkPath& path, const char* pathName) {
2842 for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
caryclark1049f122015-04-20 08:31:59 -07002843 switch (verb) {
Chris Dalton957189b2020-05-07 12:47:26 -06002844 case SkPathVerb::kMove:
caryclark1049f122015-04-20 08:31:59 -07002845 SkDebugf(" %s.moveTo(", pathName);
2846 output_points(&pts[0], 1);
2847 SkDebugf(");\n");
2848 continue;
Chris Dalton957189b2020-05-07 12:47:26 -06002849 case SkPathVerb::kLine:
caryclark1049f122015-04-20 08:31:59 -07002850 SkDebugf(" %s.lineTo(", pathName);
2851 output_points(&pts[1], 1);
2852 SkDebugf(");\n");
2853 break;
Chris Dalton957189b2020-05-07 12:47:26 -06002854 case SkPathVerb::kQuad:
caryclark1049f122015-04-20 08:31:59 -07002855 SkDebugf(" %s.quadTo(", pathName);
2856 output_points(&pts[1], 2);
2857 SkDebugf(");\n");
2858 break;
Chris Dalton957189b2020-05-07 12:47:26 -06002859 case SkPathVerb::kConic:
caryclark1049f122015-04-20 08:31:59 -07002860 SkDebugf(" %s.conicTo(", pathName);
2861 output_points(&pts[1], 2);
Chris Dalton957189b2020-05-07 12:47:26 -06002862 SkDebugf(", %1.9gf);\n", *w);
caryclark1049f122015-04-20 08:31:59 -07002863 break;
Chris Dalton957189b2020-05-07 12:47:26 -06002864 case SkPathVerb::kCubic:
caryclark1049f122015-04-20 08:31:59 -07002865 SkDebugf(" %s.cubicTo(", pathName);
2866 output_points(&pts[1], 3);
2867 SkDebugf(");\n");
2868 break;
Chris Dalton957189b2020-05-07 12:47:26 -06002869 case SkPathVerb::kClose:
caryclark1049f122015-04-20 08:31:59 -07002870 SkDebugf(" %s.close();\n", pathName);
2871 break;
2872 default:
2873 SkDEBUGFAIL("bad verb");
2874 return;
2875 }
2876 }
2877}
2878
2879static const char* gFillTypeStr[] = {
Mike Reed7d34dc72019-11-26 12:17:17 -05002880 "kWinding",
2881 "kEvenOdd",
2882 "kInverseWinding",
2883 "kInverseEvenOdd"
caryclark1049f122015-04-20 08:31:59 -07002884};
2885
2886void SkPathOpsDebug::ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration) {
caryclark1049f122015-04-20 08:31:59 -07002887#define SUPPORT_RECT_CONTOUR_DETECTION 0
2888#if SUPPORT_RECT_CONTOUR_DETECTION
halcanary96fcdcc2015-08-27 07:41:13 -07002889 int rectCount = path.isRectContours() ? path.rectContours(nullptr, nullptr) : 0;
caryclark1049f122015-04-20 08:31:59 -07002890 if (rectCount > 0) {
2891 SkTDArray<SkRect> rects;
Mike Reed30bc5272019-11-22 18:34:02 +00002892 SkTDArray<SkPathDirection> directions;
caryclark1049f122015-04-20 08:31:59 -07002893 rects.setCount(rectCount);
2894 directions.setCount(rectCount);
2895 path.rectContours(rects.begin(), directions.begin());
2896 for (int contour = 0; contour < rectCount; ++contour) {
2897 const SkRect& rect = rects[contour];
2898 SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
Mike Reed30bc5272019-11-22 18:34:02 +00002899 rect.fRight, rect.fBottom, directions[contour] == SkPathDirection::kCCW
2900 ? "SkPathDirection::kCCW" : "SkPathDirection::kCW");
caryclark1049f122015-04-20 08:31:59 -07002901 }
2902 return;
2903 }
2904#endif
Mike Reedcf0e3c62019-12-03 16:26:15 -05002905 SkPathFillType fillType = path.getFillType();
Mike Reed7d34dc72019-11-26 12:17:17 -05002906 SkASSERT(fillType >= SkPathFillType::kWinding && fillType <= SkPathFillType::kInverseEvenOdd);
caryclark1049f122015-04-20 08:31:59 -07002907 if (includeDeclaration) {
2908 SkDebugf(" SkPath %s;\n", name);
2909 }
Mike Reed7d34dc72019-11-26 12:17:17 -05002910 SkDebugf(" %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[(int)fillType]);
Chris Dalton957189b2020-05-07 12:47:26 -06002911 showPathContours(path, name);
caryclark1049f122015-04-20 08:31:59 -07002912}
caryclark13260682016-10-24 05:10:14 -07002913
Cary Clark918fb1f2016-11-15 13:22:25 -05002914#if DEBUG_DUMP_VERIFY
Mike Kleinc0bd9f92019-04-23 12:05:21 -05002915#include "include/core/SkData.h"
2916#include "include/core/SkStream.h"
caryclark13260682016-10-24 05:10:14 -07002917
2918static void dump_path(FILE* file, const SkPath& path, bool force, bool dumpAsHex) {
2919 SkDynamicMemoryWStream wStream;
2920 path.dump(&wStream, force, dumpAsHex);
2921 sk_sp<SkData> data(wStream.detachAsData());
2922 fprintf(file, "%.*s\n", (int) data->size(), (char*) data->data());
2923}
2924
2925static int dumpID = 0;
2926
2927void SkPathOpsDebug::DumpOp(const SkPath& one, const SkPath& two, SkPathOp op,
2928 const char* testName) {
2929 FILE* file = sk_fopen("op_dump.txt", kWrite_SkFILE_Flag);
2930 DumpOp(file, one, two, op, testName);
2931}
2932
2933void SkPathOpsDebug::DumpOp(FILE* file, const SkPath& one, const SkPath& two, SkPathOp op,
2934 const char* testName) {
2935 const char* name = testName ? testName : "op";
2936 fprintf(file,
2937 "\nstatic void %s_%d(skiatest::Reporter* reporter, const char* filename) {\n",
2938 name, ++dumpID);
2939 fprintf(file, " SkPath path;\n");
2940 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", one.getFillType());
2941 dump_path(file, one, false, true);
2942 fprintf(file, " SkPath path1(path);\n");
2943 fprintf(file, " path.reset();\n");
2944 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", two.getFillType());
2945 dump_path(file, two, false, true);
2946 fprintf(file, " SkPath path2(path);\n");
2947 fprintf(file, " testPathOp(reporter, path1, path2, (SkPathOp) %d, filename);\n", op);
2948 fprintf(file, "}\n\n");
2949 fclose(file);
2950}
2951
2952void SkPathOpsDebug::DumpSimplify(const SkPath& path, const char* testName) {
2953 FILE* file = sk_fopen("simplify_dump.txt", kWrite_SkFILE_Flag);
2954 DumpSimplify(file, path, testName);
2955}
2956
2957void SkPathOpsDebug::DumpSimplify(FILE* file, const SkPath& path, const char* testName) {
2958 const char* name = testName ? testName : "simplify";
2959 fprintf(file,
2960 "\nstatic void %s_%d(skiatest::Reporter* reporter, const char* filename) {\n",
2961 name, ++dumpID);
2962 fprintf(file, " SkPath path;\n");
2963 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", path.getFillType());
2964 dump_path(file, path, false, true);
2965 fprintf(file, " testSimplify(reporter, path, filename);\n");
2966 fprintf(file, "}\n\n");
2967 fclose(file);
2968}
2969
Mike Kleinc0bd9f92019-04-23 12:05:21 -05002970#include "include/core/SkBitmap.h"
2971#include "include/core/SkCanvas.h"
2972#include "include/core/SkPaint.h"
caryclark13260682016-10-24 05:10:14 -07002973
2974const int bitWidth = 64;
2975const int bitHeight = 64;
2976
2977static void debug_scale_matrix(const SkPath& one, const SkPath* two, SkMatrix& scale) {
2978 SkRect larger = one.getBounds();
2979 if (two) {
2980 larger.join(two->getBounds());
2981 }
2982 SkScalar largerWidth = larger.width();
2983 if (largerWidth < 4) {
2984 largerWidth = 4;
2985 }
2986 SkScalar largerHeight = larger.height();
2987 if (largerHeight < 4) {
2988 largerHeight = 4;
2989 }
2990 SkScalar hScale = (bitWidth - 2) / largerWidth;
2991 SkScalar vScale = (bitHeight - 2) / largerHeight;
2992 scale.reset();
2993 scale.preScale(hScale, vScale);
2994 larger.fLeft *= hScale;
2995 larger.fRight *= hScale;
2996 larger.fTop *= vScale;
2997 larger.fBottom *= vScale;
2998 SkScalar dx = -16000 > larger.fLeft ? -16000 - larger.fLeft
2999 : 16000 < larger.fRight ? 16000 - larger.fRight : 0;
3000 SkScalar dy = -16000 > larger.fTop ? -16000 - larger.fTop
3001 : 16000 < larger.fBottom ? 16000 - larger.fBottom : 0;
3002 scale.preTranslate(dx, dy);
3003}
3004
3005static int debug_paths_draw_the_same(const SkPath& one, const SkPath& two, SkBitmap& bits) {
3006 if (bits.width() == 0) {
3007 bits.allocN32Pixels(bitWidth * 2, bitHeight);
3008 }
3009 SkCanvas canvas(bits);
3010 canvas.drawColor(SK_ColorWHITE);
3011 SkPaint paint;
3012 canvas.save();
3013 const SkRect& bounds1 = one.getBounds();
3014 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
3015 canvas.drawPath(one, paint);
3016 canvas.restore();
3017 canvas.save();
3018 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
3019 canvas.drawPath(two, paint);
3020 canvas.restore();
3021 int errors = 0;
3022 for (int y = 0; y < bitHeight - 1; ++y) {
3023 uint32_t* addr1 = bits.getAddr32(0, y);
3024 uint32_t* addr2 = bits.getAddr32(0, y + 1);
3025 uint32_t* addr3 = bits.getAddr32(bitWidth, y);
3026 uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1);
3027 for (int x = 0; x < bitWidth - 1; ++x) {
3028 // count 2x2 blocks
3029 bool err = addr1[x] != addr3[x];
3030 if (err) {
3031 errors += addr1[x + 1] != addr3[x + 1]
3032 && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1];
3033 }
3034 }
3035 }
3036 return errors;
3037}
3038
3039void SkPathOpsDebug::ReportOpFail(const SkPath& one, const SkPath& two, SkPathOp op) {
3040 SkDebugf("// Op did not expect failure\n");
3041 DumpOp(stderr, one, two, op, "opTest");
3042 fflush(stderr);
3043}
3044
3045void SkPathOpsDebug::VerifyOp(const SkPath& one, const SkPath& two, SkPathOp op,
3046 const SkPath& result) {
3047 SkPath pathOut, scaledPathOut;
3048 SkRegion rgnA, rgnB, openClip, rgnOut;
3049 openClip.setRect(-16000, -16000, 16000, 16000);
3050 rgnA.setPath(one, openClip);
3051 rgnB.setPath(two, openClip);
3052 rgnOut.op(rgnA, rgnB, (SkRegion::Op) op);
3053 rgnOut.getBoundaryPath(&pathOut);
3054 SkMatrix scale;
3055 debug_scale_matrix(one, &two, scale);
3056 SkRegion scaledRgnA, scaledRgnB, scaledRgnOut;
3057 SkPath scaledA, scaledB;
3058 scaledA.addPath(one, scale);
3059 scaledA.setFillType(one.getFillType());
3060 scaledB.addPath(two, scale);
3061 scaledB.setFillType(two.getFillType());
3062 scaledRgnA.setPath(scaledA, openClip);
3063 scaledRgnB.setPath(scaledB, openClip);
3064 scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) op);
3065 scaledRgnOut.getBoundaryPath(&scaledPathOut);
3066 SkBitmap bitmap;
3067 SkPath scaledOut;
3068 scaledOut.addPath(result, scale);
3069 scaledOut.setFillType(result.getFillType());
3070 int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
3071 const int MAX_ERRORS = 9;
3072 if (errors > MAX_ERRORS) {
3073 fprintf(stderr, "// Op did not expect errors=%d\n", errors);
3074 DumpOp(stderr, one, two, op, "opTest");
3075 fflush(stderr);
3076 }
3077}
3078
3079void SkPathOpsDebug::ReportSimplifyFail(const SkPath& path) {
3080 SkDebugf("// Simplify did not expect failure\n");
3081 DumpSimplify(stderr, path, "simplifyTest");
3082 fflush(stderr);
3083}
3084
3085void SkPathOpsDebug::VerifySimplify(const SkPath& path, const SkPath& result) {
3086 SkPath pathOut, scaledPathOut;
3087 SkRegion rgnA, openClip, rgnOut;
3088 openClip.setRect(-16000, -16000, 16000, 16000);
3089 rgnA.setPath(path, openClip);
3090 rgnOut.getBoundaryPath(&pathOut);
3091 SkMatrix scale;
3092 debug_scale_matrix(path, nullptr, scale);
3093 SkRegion scaledRgnA;
3094 SkPath scaledA;
3095 scaledA.addPath(path, scale);
3096 scaledA.setFillType(path.getFillType());
3097 scaledRgnA.setPath(scaledA, openClip);
3098 scaledRgnA.getBoundaryPath(&scaledPathOut);
3099 SkBitmap bitmap;
3100 SkPath scaledOut;
3101 scaledOut.addPath(result, scale);
3102 scaledOut.setFillType(result.getFillType());
3103 int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
3104 const int MAX_ERRORS = 9;
3105 if (errors > MAX_ERRORS) {
3106 fprintf(stderr, "// Simplify did not expect errors=%d\n", errors);
3107 DumpSimplify(stderr, path, "simplifyTest");
3108 fflush(stderr);
3109 }
3110}
3111
3112#endif
Cary Clarkb8421ed2018-03-14 15:55:02 -04003113
3114// global path dumps for msvs Visual Studio 17 to use from Immediate Window
3115void Dump(const SkPath& path) {
3116 path.dump();
3117}
3118
3119void DumpHex(const SkPath& path) {
3120 path.dumpHex();
3121}