blob: b7174da86f7845fa42321958442a916e10e0a03b [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
mtklein1b249332015-07-07 12:21:21 -07008#include "SkMutex.h"
caryclark26ad22a2015-10-16 09:03:38 -07009#include "SkOpCoincidence.h"
10#include "SkOpContour.h"
caryclark13260682016-10-24 05:10:14 -070011#include "SkOSFile.h"
jvanverth02802f62015-07-02 06:42:49 -070012#include "SkPath.h"
mtklein1b249332015-07-07 12:21:21 -070013#include "SkPathOpsDebug.h"
caryclark54359292015-03-26 07:52:43 -070014#include "SkString.h"
caryclark54359292015-03-26 07:52:43 -070015
Ben Wagnerf08d1d02018-06-18 15:11:00 -040016#include <utility>
17
Cary Clark918fb1f2016-11-15 13:22:25 -050018#if DEBUG_DUMP_VERIFY
caryclark13260682016-10-24 05:10:14 -070019bool SkPathOpsDebug::gDumpOp; // set to true to write op to file before a crash
20bool SkPathOpsDebug::gVerifyOp; // set to true to compare result against regions
21#endif
22
Cary Clark59d5a0e2017-01-23 14:38:52 +000023bool SkPathOpsDebug::gRunFail; // set to true to check for success on tests known to fail
caryclark13260682016-10-24 05:10:14 -070024bool SkPathOpsDebug::gVeryVerbose; // set to true to run extensive checking tests
25
caryclark30b9fdd2016-08-31 14:36:29 -070026#undef FAIL_IF
27#define FAIL_IF(cond, coin) \
Cary Clarkab87d7a2016-10-04 10:01:04 -040028 do { if (cond) log->record(SkPathOpsDebug::kFail_Glitch, coin); } while (false)
caryclark30b9fdd2016-08-31 14:36:29 -070029
30#undef FAIL_WITH_NULL_IF
31#define FAIL_WITH_NULL_IF(cond, span) \
Cary Clarkab87d7a2016-10-04 10:01:04 -040032 do { if (cond) log->record(SkPathOpsDebug::kFail_Glitch, span); } while (false)
caryclark30b9fdd2016-08-31 14:36:29 -070033
34#undef RETURN_FALSE_IF
35#define RETURN_FALSE_IF(cond, span) \
Cary Clarkab87d7a2016-10-04 10:01:04 -040036 do { if (cond) log->record(SkPathOpsDebug::kReturnFalse_Glitch, span); \
37 } while (false)
caryclark30b9fdd2016-08-31 14:36:29 -070038
caryclark55888e42016-07-18 10:01:36 -070039class SkCoincidentSpans;
caryclark26ad22a2015-10-16 09:03:38 -070040
caryclark624637c2015-05-11 07:21:27 -070041#if DEBUG_SORT
42int SkPathOpsDebug::gSortCountDefault = SK_MaxS32;
43int SkPathOpsDebug::gSortCount;
44#endif
45
46#if DEBUG_ACTIVE_OP
Ben Wagner3f985522017-10-09 10:47:47 -040047const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor", "rdiff"};
caryclark624637c2015-05-11 07:21:27 -070048#endif
49
caryclark@google.com07393ca2013-04-08 11:47:37 +000050#if defined SK_DEBUG || !FORCE_RELEASE
51
commit-bot@chromium.orgfe41b8f2014-04-25 14:03:52 +000052int SkPathOpsDebug::gContourID = 0;
53int SkPathOpsDebug::gSegmentID = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +000054
caryclark54359292015-03-26 07:52:43 -070055bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray,
56 const SkOpSpanBase* span) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000057 for (int index = 0; index < chaseArray.count(); ++index) {
caryclark54359292015-03-26 07:52:43 -070058 const SkOpSpanBase* entry = chaseArray[index];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000059 if (entry == span) {
60 return true;
61 }
62 }
63 return false;
64}
caryclark26ad22a2015-10-16 09:03:38 -070065#endif
Hal Canaryd4998172018-07-11 11:31:34 -040066
Ben Wagner29380bd2017-10-09 14:43:00 -040067#if DEBUG_ACTIVE_SPANS
68SkString SkPathOpsDebug::gActiveSpans;
Cary Clarkff114282016-12-14 11:56:16 -050069#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000070
Cary Clarkab87d7a2016-10-04 10:01:04 -040071#if DEBUG_COIN
caryclark26ad22a2015-10-16 09:03:38 -070072
Cary Clarkab87d7a2016-10-04 10:01:04 -040073SkPathOpsDebug::CoinDict SkPathOpsDebug::gCoinSumChangedDict;
74SkPathOpsDebug::CoinDict SkPathOpsDebug::gCoinSumVisitedDict;
75
76static const int kGlitchType_Count = SkPathOpsDebug::kUnalignedTail_Glitch + 1;
caryclark26ad22a2015-10-16 09:03:38 -070077
78struct SpanGlitch {
caryclark26ad22a2015-10-16 09:03:38 -070079 const SkOpSpanBase* fBase;
80 const SkOpSpanBase* fSuspect;
caryclark26ad22a2015-10-16 09:03:38 -070081 const SkOpSegment* fSegment;
caryclark55888e42016-07-18 10:01:36 -070082 const SkOpSegment* fOppSegment;
caryclark26ad22a2015-10-16 09:03:38 -070083 const SkOpPtT* fCoinSpan;
84 const SkOpPtT* fEndSpan;
85 const SkOpPtT* fOppSpan;
86 const SkOpPtT* fOppEndSpan;
caryclark55888e42016-07-18 10:01:36 -070087 double fStartT;
88 double fEndT;
89 double fOppStartT;
90 double fOppEndT;
caryclark26ad22a2015-10-16 09:03:38 -070091 SkPoint fPt;
Cary Clarkab87d7a2016-10-04 10:01:04 -040092 SkPathOpsDebug::GlitchType fType;
93
94 void dumpType() const;
caryclark26ad22a2015-10-16 09:03:38 -070095};
96
97struct SkPathOpsDebug::GlitchLog {
Cary Clarkab87d7a2016-10-04 10:01:04 -040098 void init(const SkOpGlobalState* state) {
99 fGlobalState = state;
100 }
101
102 SpanGlitch* recordCommon(GlitchType type) {
Cary Clarkea2a6322018-08-27 13:19:09 -0400103 SpanGlitch* glitch = fGlitches.push();
caryclark26ad22a2015-10-16 09:03:38 -0700104 glitch->fBase = nullptr;
105 glitch->fSuspect = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -0700106 glitch->fSegment = nullptr;
caryclark55888e42016-07-18 10:01:36 -0700107 glitch->fOppSegment = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -0700108 glitch->fCoinSpan = nullptr;
109 glitch->fEndSpan = nullptr;
110 glitch->fOppSpan = nullptr;
111 glitch->fOppEndSpan = nullptr;
caryclark55888e42016-07-18 10:01:36 -0700112 glitch->fStartT = SK_ScalarNaN;
113 glitch->fEndT = SK_ScalarNaN;
114 glitch->fOppStartT = SK_ScalarNaN;
115 glitch->fOppEndT = SK_ScalarNaN;
caryclark26ad22a2015-10-16 09:03:38 -0700116 glitch->fPt = { SK_ScalarNaN, SK_ScalarNaN };
117 glitch->fType = type;
118 return glitch;
119 }
120
Cary Clarkab87d7a2016-10-04 10:01:04 -0400121 void record(GlitchType type, const SkOpSpanBase* base,
caryclark26ad22a2015-10-16 09:03:38 -0700122 const SkOpSpanBase* suspect = NULL) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400123 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700124 glitch->fBase = base;
125 glitch->fSuspect = suspect;
126 }
127
Cary Clarkab87d7a2016-10-04 10:01:04 -0400128 void record(GlitchType type, const SkOpSpanBase* base,
caryclark55888e42016-07-18 10:01:36 -0700129 const SkOpPtT* ptT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400130 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700131 glitch->fBase = base;
132 glitch->fCoinSpan = ptT;
133 }
134
Cary Clarkab87d7a2016-10-04 10:01:04 -0400135 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark55888e42016-07-18 10:01:36 -0700136 const SkCoincidentSpans* opp = NULL) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400137 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700138 glitch->fCoinSpan = coin->coinPtTStart();
139 glitch->fEndSpan = coin->coinPtTEnd();
140 if (opp) {
141 glitch->fOppSpan = opp->coinPtTStart();
142 glitch->fOppEndSpan = opp->coinPtTEnd();
143 }
caryclark26ad22a2015-10-16 09:03:38 -0700144 }
145
Cary Clarkab87d7a2016-10-04 10:01:04 -0400146 void record(GlitchType type, const SkOpSpanBase* base,
caryclark26ad22a2015-10-16 09:03:38 -0700147 const SkOpSegment* seg, double t, SkPoint pt) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400148 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700149 glitch->fBase = base;
150 glitch->fSegment = seg;
caryclark55888e42016-07-18 10:01:36 -0700151 glitch->fStartT = t;
caryclark26ad22a2015-10-16 09:03:38 -0700152 glitch->fPt = pt;
153 }
154
Cary Clarkab87d7a2016-10-04 10:01:04 -0400155 void record(GlitchType type, const SkOpSpanBase* base, double t,
caryclark26ad22a2015-10-16 09:03:38 -0700156 SkPoint pt) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400157 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700158 glitch->fBase = base;
caryclark55888e42016-07-18 10:01:36 -0700159 glitch->fStartT = t;
caryclark26ad22a2015-10-16 09:03:38 -0700160 glitch->fPt = pt;
161 }
162
Cary Clarkab87d7a2016-10-04 10:01:04 -0400163 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark26ad22a2015-10-16 09:03:38 -0700164 const SkOpPtT* coinSpan, const SkOpPtT* endSpan) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400165 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700166 glitch->fCoinSpan = coin->coinPtTStart();
167 glitch->fEndSpan = coin->coinPtTEnd();
caryclark26ad22a2015-10-16 09:03:38 -0700168 glitch->fEndSpan = endSpan;
caryclark55888e42016-07-18 10:01:36 -0700169 glitch->fOppSpan = coinSpan;
170 glitch->fOppEndSpan = endSpan;
caryclark26ad22a2015-10-16 09:03:38 -0700171 }
172
Cary Clarkab87d7a2016-10-04 10:01:04 -0400173 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark55888e42016-07-18 10:01:36 -0700174 const SkOpSpanBase* base) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400175 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700176 glitch->fBase = base;
177 glitch->fCoinSpan = coin->coinPtTStart();
178 glitch->fEndSpan = coin->coinPtTEnd();
caryclark26ad22a2015-10-16 09:03:38 -0700179 }
180
Cary Clarkab87d7a2016-10-04 10:01:04 -0400181 void record(GlitchType type, const SkOpPtT* ptTS, const SkOpPtT* ptTE,
caryclark26ad22a2015-10-16 09:03:38 -0700182 const SkOpPtT* oPtTS, const SkOpPtT* oPtTE) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400183 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700184 glitch->fCoinSpan = ptTS;
185 glitch->fEndSpan = ptTE;
186 glitch->fOppSpan = oPtTS;
187 glitch->fOppEndSpan = oPtTE;
188 }
189
Cary Clarkab87d7a2016-10-04 10:01:04 -0400190 void record(GlitchType type, const SkOpSegment* seg, double startT,
caryclark55888e42016-07-18 10:01:36 -0700191 double endT, const SkOpSegment* oppSeg, double oppStartT, double oppEndT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400192 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700193 glitch->fSegment = seg;
194 glitch->fStartT = startT;
195 glitch->fEndT = endT;
196 glitch->fOppSegment = oppSeg;
197 glitch->fOppStartT = oppStartT;
198 glitch->fOppEndT = oppEndT;
199 }
200
Cary Clarkab87d7a2016-10-04 10:01:04 -0400201 void record(GlitchType type, const SkOpSegment* seg,
caryclark55888e42016-07-18 10:01:36 -0700202 const SkOpSpan* span) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400203 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700204 glitch->fSegment = seg;
205 glitch->fBase = span;
206 }
207
Cary Clarkab87d7a2016-10-04 10:01:04 -0400208 void record(GlitchType type, double t, const SkOpSpanBase* span) {
209 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700210 glitch->fStartT = t;
211 glitch->fBase = span;
212 }
213
Cary Clarkab87d7a2016-10-04 10:01:04 -0400214 void record(GlitchType type, const SkOpSegment* seg) {
215 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700216 glitch->fSegment = seg;
217 }
218
Cary Clarkab87d7a2016-10-04 10:01:04 -0400219 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark55888e42016-07-18 10:01:36 -0700220 const SkOpPtT* ptT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400221 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700222 glitch->fCoinSpan = coin->coinPtTStart();
223 glitch->fEndSpan = ptT;
224 }
225
caryclark26ad22a2015-10-16 09:03:38 -0700226 SkTDArray<SpanGlitch> fGlitches;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400227 const SkOpGlobalState* fGlobalState;
caryclark26ad22a2015-10-16 09:03:38 -0700228};
Cary Clarkab87d7a2016-10-04 10:01:04 -0400229
230
231void SkPathOpsDebug::CoinDict::add(const SkPathOpsDebug::CoinDict& dict) {
232 int count = dict.fDict.count();
233 for (int index = 0; index < count; ++index) {
234 this->add(dict.fDict[index]);
235 }
236}
237
238void SkPathOpsDebug::CoinDict::add(const CoinDictEntry& key) {
239 int count = fDict.count();
240 for (int index = 0; index < count; ++index) {
241 CoinDictEntry* entry = &fDict[index];
242 if (entry->fIteration == key.fIteration && entry->fLineNumber == key.fLineNumber) {
243 SkASSERT(!strcmp(entry->fFunctionName, key.fFunctionName));
244 if (entry->fGlitchType == kUninitialized_Glitch) {
245 entry->fGlitchType = key.fGlitchType;
246 }
247 return;
248 }
249 }
250 *fDict.append() = key;
251}
252
253#endif
254
255#if DEBUG_COIN
256static void missing_coincidence(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) {
257 const SkOpContour* contour = contourList;
258 // bool result = false;
259 do {
260 /* result |= */ contour->debugMissingCoincidence(glitches);
261 } while ((contour = contour->next()));
262 return;
263}
264
265static void move_multiples(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) {
266 const SkOpContour* contour = contourList;
267 do {
268 if (contour->debugMoveMultiples(glitches), false) {
269 return;
270 }
271 } while ((contour = contour->next()));
272 return;
273}
274
275static void move_nearby(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) {
276 const SkOpContour* contour = contourList;
277 do {
278 contour->debugMoveNearby(glitches);
279 } while ((contour = contour->next()));
280}
281
282
283#endif
284
285#if DEBUG_COIN
286void SkOpGlobalState::debugAddToCoinChangedDict() {
287
288#if DEBUG_COINCIDENCE
caryclarke6522ea2016-10-17 07:54:33 -0700289 SkPathOpsDebug::CheckHealth(fContourHead);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400290#endif
291 // see if next coincident operation makes a change; if so, record it
292 SkPathOpsDebug::GlitchLog glitches;
293 const char* funcName = fCoinDictEntry.fFunctionName;
294 if (!strcmp("calc_angles", funcName)) {
295 ;
296 } else if (!strcmp("missing_coincidence", funcName)) {
297 missing_coincidence(&glitches, fContourHead);
298 } else if (!strcmp("move_multiples", funcName)) {
299 move_multiples(&glitches, fContourHead);
300 } else if (!strcmp("move_nearby", funcName)) {
301 move_nearby(&glitches, fContourHead);
302 } else if (!strcmp("addExpanded", funcName)) {
303 fCoincidence->debugAddExpanded(&glitches);
304 } else if (!strcmp("addMissing", funcName)) {
305 bool added;
306 fCoincidence->debugAddMissing(&glitches, &added);
307 } else if (!strcmp("addEndMovedSpans", funcName)) {
308 fCoincidence->debugAddEndMovedSpans(&glitches);
309 } else if (!strcmp("correctEnds", funcName)) {
310 fCoincidence->debugCorrectEnds(&glitches);
311 } else if (!strcmp("expand", funcName)) {
312 fCoincidence->debugExpand(&glitches);
313 } else if (!strcmp("findOverlaps", funcName)) {
314 ;
315 } else if (!strcmp("mark", funcName)) {
316 fCoincidence->debugMark(&glitches);
317 } else if (!strcmp("apply", funcName)) {
318 ;
319 } else {
320 SkASSERT(0); // add missing case
321 }
322 if (glitches.fGlitches.count()) {
323 fCoinDictEntry.fGlitchType = glitches.fGlitches[0].fType;
324 }
325 fCoinChangedDict.add(fCoinDictEntry);
326}
caryclark55888e42016-07-18 10:01:36 -0700327#endif
caryclark26ad22a2015-10-16 09:03:38 -0700328
caryclark55888e42016-07-18 10:01:36 -0700329void SkPathOpsDebug::ShowActiveSpans(SkOpContourHead* contourList) {
330#if DEBUG_ACTIVE_SPANS
Cary Clarkff114282016-12-14 11:56:16 -0500331 SkString str;
caryclark55888e42016-07-18 10:01:36 -0700332 SkOpContour* contour = contourList;
333 do {
Cary Clarkff114282016-12-14 11:56:16 -0500334 contour->debugShowActiveSpans(&str);
caryclark55888e42016-07-18 10:01:36 -0700335 } while ((contour = contour->next()));
Ben Wagner29380bd2017-10-09 14:43:00 -0400336 if (!gActiveSpans.equals(str)) {
337 const char* s = str.c_str();
338 const char* end;
339 while ((end = strchr(s, '\n'))) {
340 SkDebugf("%.*s", end - s + 1, s);
341 s = end + 1;
342 }
343 gActiveSpans.set(str);
344 }
caryclark55888e42016-07-18 10:01:36 -0700345#endif
346}
347
Cary Clarkab87d7a2016-10-04 10:01:04 -0400348#if DEBUG_COINCIDENCE || DEBUG_COIN
349void SkPathOpsDebug::CheckHealth(SkOpContourHead* contourList) {
caryclark55888e42016-07-18 10:01:36 -0700350#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -0700351 contourList->globalState()->debugSetCheckHealth(true);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400352#endif
353#if DEBUG_COIN
caryclark26ad22a2015-10-16 09:03:38 -0700354 GlitchLog glitches;
355 const SkOpContour* contour = contourList;
356 const SkOpCoincidence* coincidence = contour->globalState()->coincidence();
Cary Clarkab87d7a2016-10-04 10:01:04 -0400357 coincidence->debugCheckValid(&glitches); // don't call validate; spans may be inconsistent
caryclark26ad22a2015-10-16 09:03:38 -0700358 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400359 contour->debugCheckHealth(&glitches);
360 contour->debugMissingCoincidence(&glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700361 } while ((contour = contour->next()));
caryclark81a478c2016-09-09 09:37:57 -0700362 bool added;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400363 coincidence->debugAddMissing(&glitches, &added);
364 coincidence->debugExpand(&glitches);
365 coincidence->debugAddExpanded(&glitches);
366 coincidence->debugMark(&glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700367 unsigned mask = 0;
368 for (int index = 0; index < glitches.fGlitches.count(); ++index) {
369 const SpanGlitch& glitch = glitches.fGlitches[index];
370 mask |= 1 << glitch.fType;
371 }
372 for (int index = 0; index < kGlitchType_Count; ++index) {
373 SkDebugf(mask & (1 << index) ? "x" : "-");
374 }
Cary Clarkff114282016-12-14 11:56:16 -0500375 SkDebugf(" %s\n", contourList->globalState()->debugCoinDictEntry().fFunctionName);
caryclark55888e42016-07-18 10:01:36 -0700376 for (int index = 0; index < glitches.fGlitches.count(); ++index) {
377 const SpanGlitch& glitch = glitches.fGlitches[index];
378 SkDebugf("%02d: ", index);
379 if (glitch.fBase) {
caryclark8016b262016-09-06 05:59:47 -0700380 SkDebugf(" seg/base=%d/%d", glitch.fBase->segment()->debugID(),
381 glitch.fBase->debugID());
caryclark55888e42016-07-18 10:01:36 -0700382 }
383 if (glitch.fSuspect) {
caryclark8016b262016-09-06 05:59:47 -0700384 SkDebugf(" seg/base=%d/%d", glitch.fSuspect->segment()->debugID(),
385 glitch.fSuspect->debugID());
caryclark55888e42016-07-18 10:01:36 -0700386 }
387 if (glitch.fSegment) {
388 SkDebugf(" segment=%d", glitch.fSegment->debugID());
389 }
390 if (glitch.fCoinSpan) {
caryclark8016b262016-09-06 05:59:47 -0700391 SkDebugf(" coinSeg/Span/PtT=%d/%d/%d", glitch.fCoinSpan->segment()->debugID(),
392 glitch.fCoinSpan->span()->debugID(), glitch.fCoinSpan->debugID());
caryclark55888e42016-07-18 10:01:36 -0700393 }
394 if (glitch.fEndSpan) {
395 SkDebugf(" endSpan=%d", glitch.fEndSpan->debugID());
396 }
397 if (glitch.fOppSpan) {
caryclark8016b262016-09-06 05:59:47 -0700398 SkDebugf(" oppSeg/Span/PtT=%d/%d/%d", glitch.fOppSpan->segment()->debugID(),
399 glitch.fOppSpan->span()->debugID(), glitch.fOppSpan->debugID());
caryclark55888e42016-07-18 10:01:36 -0700400 }
401 if (glitch.fOppEndSpan) {
402 SkDebugf(" oppEndSpan=%d", glitch.fOppEndSpan->debugID());
403 }
404 if (!SkScalarIsNaN(glitch.fStartT)) {
405 SkDebugf(" startT=%g", glitch.fStartT);
406 }
407 if (!SkScalarIsNaN(glitch.fEndT)) {
408 SkDebugf(" endT=%g", glitch.fEndT);
409 }
410 if (glitch.fOppSegment) {
411 SkDebugf(" segment=%d", glitch.fOppSegment->debugID());
412 }
413 if (!SkScalarIsNaN(glitch.fOppStartT)) {
414 SkDebugf(" oppStartT=%g", glitch.fOppStartT);
415 }
416 if (!SkScalarIsNaN(glitch.fOppEndT)) {
417 SkDebugf(" oppEndT=%g", glitch.fOppEndT);
418 }
419 if (!SkScalarIsNaN(glitch.fPt.fX) || !SkScalarIsNaN(glitch.fPt.fY)) {
420 SkDebugf(" pt=%g,%g", glitch.fPt.fX, glitch.fPt.fY);
421 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400422 DumpGlitchType(glitch.fType);
caryclark55888e42016-07-18 10:01:36 -0700423 SkDebugf("\n");
424 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400425#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -0700426 contourList->globalState()->debugSetCheckHealth(false);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400427#endif
caryclark6c3b9cd2016-09-26 05:36:58 -0700428#if 01 && DEBUG_ACTIVE_SPANS
Cary Clarkab87d7a2016-10-04 10:01:04 -0400429// SkDebugf("active after %s:\n", id);
caryclark55888e42016-07-18 10:01:36 -0700430 ShowActiveSpans(contourList);
431#endif
432#endif
caryclark26ad22a2015-10-16 09:03:38 -0700433}
434#endif
435
Cary Clarkab87d7a2016-10-04 10:01:04 -0400436#if DEBUG_COIN
437void SkPathOpsDebug::DumpGlitchType(GlitchType glitchType) {
438 switch (glitchType) {
439 case kAddCorruptCoin_Glitch: SkDebugf(" AddCorruptCoin"); break;
440 case kAddExpandedCoin_Glitch: SkDebugf(" AddExpandedCoin"); break;
441 case kAddExpandedFail_Glitch: SkDebugf(" AddExpandedFail"); break;
442 case kAddIfCollapsed_Glitch: SkDebugf(" AddIfCollapsed"); break;; break;
443 case kAddIfMissingCoin_Glitch: SkDebugf(" AddIfMissingCoin"); break;
444 case kAddMissingCoin_Glitch: SkDebugf(" AddMissingCoin"); break;
445 case kAddMissingExtend_Glitch: SkDebugf(" AddMissingExtend"); break;
446 case kAddOrOverlap_Glitch: SkDebugf(" AAddOrOverlap"); break;
447 case kCollapsedCoin_Glitch: SkDebugf(" CollapsedCoin"); break;
448 case kCollapsedDone_Glitch: SkDebugf(" CollapsedDone"); break;
449 case kCollapsedOppValue_Glitch: SkDebugf(" CollapsedOppValue"); break;
450 case kCollapsedSpan_Glitch: SkDebugf(" CollapsedSpan"); break;
451 case kCollapsedWindValue_Glitch: SkDebugf(" CollapsedWindValue"); break;
452 case kCorrectEnd_Glitch: SkDebugf(" CorrectEnd"); break;
453 case kDeletedCoin_Glitch: SkDebugf(" DeletedCoin"); break;
454 case kExpandCoin_Glitch: SkDebugf(" ExpandCoin"); break;
455 case kFail_Glitch: SkDebugf(" Fail"); break;
456 case kMarkCoinEnd_Glitch: SkDebugf(" MarkCoinEnd"); break;
457 case kMarkCoinInsert_Glitch: SkDebugf(" MarkCoinInsert"); break;
458 case kMarkCoinMissing_Glitch: SkDebugf(" MarkCoinMissing"); break;
459 case kMarkCoinStart_Glitch: SkDebugf(" MarkCoinStart"); break;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400460 case kMergeMatches_Glitch: SkDebugf(" MergeMatches"); break;
461 case kMissingCoin_Glitch: SkDebugf(" MissingCoin"); break;
462 case kMissingDone_Glitch: SkDebugf(" MissingDone"); break;
463 case kMissingIntersection_Glitch: SkDebugf(" MissingIntersection"); break;
464 case kMoveMultiple_Glitch: SkDebugf(" MoveMultiple"); break;
465 case kMoveNearbyClearAll_Glitch: SkDebugf(" MoveNearbyClearAll"); break;
466 case kMoveNearbyClearAll2_Glitch: SkDebugf(" MoveNearbyClearAll2"); break;
467 case kMoveNearbyMerge_Glitch: SkDebugf(" MoveNearbyMerge"); break;
468 case kMoveNearbyMergeFinal_Glitch: SkDebugf(" MoveNearbyMergeFinal"); break;
469 case kMoveNearbyRelease_Glitch: SkDebugf(" MoveNearbyRelease"); break;
470 case kMoveNearbyReleaseFinal_Glitch: SkDebugf(" MoveNearbyReleaseFinal"); break;
471 case kReleasedSpan_Glitch: SkDebugf(" ReleasedSpan"); break;
472 case kReturnFalse_Glitch: SkDebugf(" ReturnFalse"); break;
473 case kUnaligned_Glitch: SkDebugf(" Unaligned"); break;
474 case kUnalignedHead_Glitch: SkDebugf(" UnalignedHead"); break;
475 case kUnalignedTail_Glitch: SkDebugf(" UnalignedTail"); break;
476 case kUninitialized_Glitch: break;
477 default: SkASSERT(0);
478 }
479}
480#endif
481
caryclark26ad22a2015-10-16 09:03:38 -0700482#if defined SK_DEBUG || !FORCE_RELEASE
caryclark@google.com570863f2013-09-16 15:55:01 +0000483void SkPathOpsDebug::MathematicaIze(char* str, size_t bufferLen) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000484 size_t len = strlen(str);
485 bool num = false;
486 for (size_t idx = 0; idx < len; ++idx) {
487 if (num && str[idx] == 'e') {
488 if (len + 2 >= bufferLen) {
489 return;
490 }
491 memmove(&str[idx + 2], &str[idx + 1], len - idx);
492 str[idx] = '*';
493 str[idx + 1] = '^';
494 ++len;
495 }
496 num = str[idx] >= '0' && str[idx] <= '9';
497 }
498}
499
caryclark@google.com570863f2013-09-16 15:55:01 +0000500bool SkPathOpsDebug::ValidWind(int wind) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000501 return wind > SK_MinS32 + 0xFFFF && wind < SK_MaxS32 - 0xFFFF;
502}
503
caryclark@google.com570863f2013-09-16 15:55:01 +0000504void SkPathOpsDebug::WindingPrintf(int wind) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000505 if (wind == SK_MinS32) {
506 SkDebugf("?");
507 } else {
508 SkDebugf("%d", wind);
509 }
510}
caryclark54359292015-03-26 07:52:43 -0700511#endif // defined SK_DEBUG || !FORCE_RELEASE
512
caryclark@google.coma5e55922013-05-07 18:51:31 +0000513
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000514#if DEBUG_SHOW_TEST_NAME
halcanary385fe4d2015-08-26 13:07:48 -0700515void* SkPathOpsDebug::CreateNameStr() { return new char[DEBUG_FILENAME_STRING_LENGTH]; }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000516
halcanary385fe4d2015-08-26 13:07:48 -0700517void SkPathOpsDebug::DeleteNameStr(void* v) { delete[] reinterpret_cast<char*>(v); }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000518
caryclark@google.com570863f2013-09-16 15:55:01 +0000519void SkPathOpsDebug::BumpTestName(char* test) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000520 char* num = test + strlen(test);
521 while (num[-1] >= '0' && num[-1] <= '9') {
522 --num;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000523 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000524 if (num[0] == '\0') {
525 return;
526 }
527 int dec = atoi(num);
528 if (dec == 0) {
529 return;
530 }
531 ++dec;
532 SK_SNPRINTF(num, DEBUG_FILENAME_STRING_LENGTH - (num - test), "%d", dec);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000533}
534#endif
caryclark@google.com570863f2013-09-16 15:55:01 +0000535
caryclark1049f122015-04-20 08:31:59 -0700536static void show_function_header(const char* functionName) {
537 SkDebugf("\nstatic void %s(skiatest::Reporter* reporter, const char* filename) {\n", functionName);
538 if (strcmp("skphealth_com76", functionName) == 0) {
539 SkDebugf("found it\n");
540 }
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000541}
caryclark1049f122015-04-20 08:31:59 -0700542
543static const char* gOpStrs[] = {
544 "kDifference_SkPathOp",
545 "kIntersect_SkPathOp",
546 "kUnion_SkPathOp",
caryclark55888e42016-07-18 10:01:36 -0700547 "kXOR_PathOp",
caryclark1049f122015-04-20 08:31:59 -0700548 "kReverseDifference_SkPathOp",
549};
550
caryclark03b03ca2015-04-23 09:13:37 -0700551const char* SkPathOpsDebug::OpStr(SkPathOp op) {
552 return gOpStrs[op];
553}
554
caryclark1049f122015-04-20 08:31:59 -0700555static void show_op(SkPathOp op, const char* pathOne, const char* pathTwo) {
556 SkDebugf(" testPathOp(reporter, %s, %s, %s, filename);\n", pathOne, pathTwo, gOpStrs[op]);
557 SkDebugf("}\n");
558}
559
reed086eea92016-05-04 17:12:46 -0700560SK_DECLARE_STATIC_MUTEX(gTestMutex);
caryclark1049f122015-04-20 08:31:59 -0700561
562void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
563 const char* testName) {
564 SkAutoMutexAcquire ac(gTestMutex);
565 show_function_header(testName);
566 ShowOnePath(a, "path", true);
567 ShowOnePath(b, "pathB", true);
568 show_op(shapeOp, "path", "pathB");
569}
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000570
caryclark27c8eb82015-07-06 11:38:33 -0700571#include "SkPathOpsTypes.h"
caryclark26ad22a2015-10-16 09:03:38 -0700572#include "SkIntersectionHelper.h"
573#include "SkIntersections.h"
574
Cary Clarkab87d7a2016-10-04 10:01:04 -0400575#if DEBUG_COIN
576
577SK_DECLARE_STATIC_MUTEX(gCoinDictMutex);
578
579void SkOpGlobalState::debugAddToGlobalCoinDicts() {
580 SkAutoMutexAcquire ac(&gCoinDictMutex);
581 SkPathOpsDebug::gCoinSumChangedDict.add(fCoinChangedDict);
582 SkPathOpsDebug::gCoinSumVisitedDict.add(fCoinVisitedDict);
583}
584
585#endif
586
caryclark26ad22a2015-10-16 09:03:38 -0700587#if DEBUG_T_SECT_LOOP_COUNT
588void SkOpGlobalState::debugAddLoopCount(SkIntersections* i, const SkIntersectionHelper& wt,
589 const SkIntersectionHelper& wn) {
590 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
591 SkIntersections::DebugLoop looper = (SkIntersections::DebugLoop) index;
592 if (fDebugLoopCount[index] >= i->debugLoopCount(looper)) {
593 continue;
594 }
595 fDebugLoopCount[index] = i->debugLoopCount(looper);
596 fDebugWorstVerb[index * 2] = wt.segment()->verb();
597 fDebugWorstVerb[index * 2 + 1] = wn.segment()->verb();
598 sk_bzero(&fDebugWorstPts[index * 8], sizeof(SkPoint) * 8);
599 memcpy(&fDebugWorstPts[index * 2 * 4], wt.pts(),
600 (SkPathOpsVerbToPoints(wt.segment()->verb()) + 1) * sizeof(SkPoint));
601 memcpy(&fDebugWorstPts[(index * 2 + 1) * 4], wn.pts(),
602 (SkPathOpsVerbToPoints(wn.segment()->verb()) + 1) * sizeof(SkPoint));
603 fDebugWorstWeight[index * 2] = wt.weight();
604 fDebugWorstWeight[index * 2 + 1] = wn.weight();
605 }
606 i->debugResetLoopCount();
607}
608
609void SkOpGlobalState::debugDoYourWorst(SkOpGlobalState* local) {
610 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
611 if (fDebugLoopCount[index] >= local->fDebugLoopCount[index]) {
612 continue;
613 }
614 fDebugLoopCount[index] = local->fDebugLoopCount[index];
615 fDebugWorstVerb[index * 2] = local->fDebugWorstVerb[index * 2];
616 fDebugWorstVerb[index * 2 + 1] = local->fDebugWorstVerb[index * 2 + 1];
617 memcpy(&fDebugWorstPts[index * 2 * 4], &local->fDebugWorstPts[index * 2 * 4],
618 sizeof(SkPoint) * 8);
619 fDebugWorstWeight[index * 2] = local->fDebugWorstWeight[index * 2];
620 fDebugWorstWeight[index * 2 + 1] = local->fDebugWorstWeight[index * 2 + 1];
621 }
622 local->debugResetLoopCounts();
623}
624
625static void dump_curve(SkPath::Verb verb, const SkPoint& pts, float weight) {
626 if (!verb) {
627 return;
628 }
629 const char* verbs[] = { "", "line", "quad", "conic", "cubic" };
630 SkDebugf("%s: {{", verbs[verb]);
631 int ptCount = SkPathOpsVerbToPoints(verb);
632 for (int index = 0; index <= ptCount; ++index) {
633 SkDPoint::Dump((&pts)[index]);
634 if (index < ptCount - 1) {
635 SkDebugf(", ");
636 }
637 }
638 SkDebugf("}");
639 if (weight != 1) {
640 SkDebugf(", ");
641 if (weight == floorf(weight)) {
642 SkDebugf("%.0f", weight);
643 } else {
644 SkDebugf("%1.9gf", weight);
645 }
646 }
647 SkDebugf("}\n");
648}
649
650void SkOpGlobalState::debugLoopReport() {
651 const char* loops[] = { "iterations", "coinChecks", "perpCalcs" };
652 SkDebugf("\n");
653 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
654 SkDebugf("%s: %d\n", loops[index], fDebugLoopCount[index]);
655 dump_curve(fDebugWorstVerb[index * 2], fDebugWorstPts[index * 2 * 4],
656 fDebugWorstWeight[index * 2]);
657 dump_curve(fDebugWorstVerb[index * 2 + 1], fDebugWorstPts[(index * 2 + 1) * 4],
658 fDebugWorstWeight[index * 2 + 1]);
659 }
660}
661
662void SkOpGlobalState::debugResetLoopCounts() {
663 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
664 sk_bzero(fDebugWorstVerb, sizeof(fDebugWorstVerb));
665 sk_bzero(fDebugWorstPts, sizeof(fDebugWorstPts));
666 sk_bzero(fDebugWorstWeight, sizeof(fDebugWorstWeight));
667}
668#endif
caryclark27c8eb82015-07-06 11:38:33 -0700669
Cary Clark59d5a0e2017-01-23 14:38:52 +0000670bool SkOpGlobalState::DebugRunFail() {
671 return SkPathOpsDebug::gRunFail;
672}
673
Cary Clarkab87d7a2016-10-04 10:01:04 -0400674// this is const so it can be called by const methods that overwise don't alter state
675#if DEBUG_VALIDATE || DEBUG_COIN
676void SkOpGlobalState::debugSetPhase(const char* funcName DEBUG_COIN_DECLARE_PARAMS()) const {
677 auto writable = const_cast<SkOpGlobalState*>(this);
678#if DEBUG_VALIDATE
679 writable->setPhase(phase);
680#endif
681#if DEBUG_COIN
682 SkPathOpsDebug::CoinDictEntry* entry = &writable->fCoinDictEntry;
683 writable->fPreviousFuncName = entry->fFunctionName;
684 entry->fIteration = iteration;
685 entry->fLineNumber = lineNo;
686 entry->fGlitchType = SkPathOpsDebug::kUninitialized_Glitch;
687 entry->fFunctionName = funcName;
688 writable->fCoinVisitedDict.add(*entry);
689 writable->debugAddToCoinChangedDict();
690#endif
691}
692#endif
693
caryclark26ad22a2015-10-16 09:03:38 -0700694#if DEBUG_T_SECT_LOOP_COUNT
695void SkIntersections::debugBumpLoopCount(DebugLoop index) {
696 fDebugLoopCount[index]++;
697}
698
699int SkIntersections::debugLoopCount(DebugLoop index) const {
700 return fDebugLoopCount[index];
701}
702
703void SkIntersections::debugResetLoopCount() {
704 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
705}
706#endif
707
caryclarka35ab3e2016-10-20 08:32:18 -0700708#include "SkPathOpsConic.h"
caryclark624637c2015-05-11 07:21:27 -0700709#include "SkPathOpsCubic.h"
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000710
caryclark624637c2015-05-11 07:21:27 -0700711SkDCubic SkDQuad::debugToCubic() const {
712 SkDCubic cubic;
713 cubic[0] = fPts[0];
714 cubic[2] = fPts[1];
715 cubic[3] = fPts[2];
716 cubic[1].fX = (cubic[0].fX + cubic[2].fX * 2) / 3;
717 cubic[1].fY = (cubic[0].fY + cubic[2].fY * 2) / 3;
718 cubic[2].fX = (cubic[3].fX + cubic[2].fX * 2) / 3;
719 cubic[2].fY = (cubic[3].fY + cubic[2].fY * 2) / 3;
720 return cubic;
caryclark54359292015-03-26 07:52:43 -0700721}
caryclark624637c2015-05-11 07:21:27 -0700722
caryclarka35ab3e2016-10-20 08:32:18 -0700723void SkDQuad::debugSet(const SkDPoint* pts) {
724 memcpy(fPts, pts, sizeof(fPts));
725 SkDEBUGCODE(fDebugGlobalState = nullptr);
726}
727
728void SkDCubic::debugSet(const SkDPoint* pts) {
729 memcpy(fPts, pts, sizeof(fPts));
730 SkDEBUGCODE(fDebugGlobalState = nullptr);
731}
732
733void SkDConic::debugSet(const SkDPoint* pts, SkScalar weight) {
734 fPts.debugSet(pts);
735 fWeight = weight;
736}
737
caryclarked0935a2015-10-22 07:23:52 -0700738void SkDRect::debugInit() {
739 fLeft = fTop = fRight = fBottom = SK_ScalarNaN;
740}
741
caryclark624637c2015-05-11 07:21:27 -0700742#include "SkOpAngle.h"
caryclark624637c2015-05-11 07:21:27 -0700743#include "SkOpSegment.h"
caryclark54359292015-03-26 07:52:43 -0700744
Cary Clarkab87d7a2016-10-04 10:01:04 -0400745#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -0700746// commented-out lines keep this in sync with addT()
Cary Clarkab87d7a2016-10-04 10:01:04 -0400747 const SkOpPtT* SkOpSegment::debugAddT(double t, SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -0700748 debugValidate();
749 SkPoint pt = this->ptAtT(t);
caryclark26ad22a2015-10-16 09:03:38 -0700750 const SkOpSpanBase* span = &fHead;
caryclark55888e42016-07-18 10:01:36 -0700751 do {
752 const SkOpPtT* result = span->ptT();
caryclark29b25632016-08-25 11:27:17 -0700753 if (t == result->fT || this->match(result, this, t, pt)) {
caryclark55888e42016-07-18 10:01:36 -0700754// span->bumpSpanAdds();
caryclarkef4f32a2016-08-24 09:24:18 -0700755 return result;
caryclark26ad22a2015-10-16 09:03:38 -0700756 }
caryclark55888e42016-07-18 10:01:36 -0700757 if (t < result->fT) {
758 const SkOpSpan* prev = result->span()->prev();
caryclark30b9fdd2016-08-31 14:36:29 -0700759 FAIL_WITH_NULL_IF(!prev, span);
caryclark29b25632016-08-25 11:27:17 -0700760 // marks in global state that new op span has been allocated
761 this->globalState()->setAllocatedOpSpan();
caryclark55888e42016-07-18 10:01:36 -0700762// span->init(this, prev, t, pt);
763 this->debugValidate();
764// #if DEBUG_ADD_T
765// SkDebugf("%s insert t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
766// span->segment()->debugID(), span->debugID());
767// #endif
768// span->bumpSpanAdds();
caryclark55888e42016-07-18 10:01:36 -0700769 return nullptr;
caryclark26ad22a2015-10-16 09:03:38 -0700770 }
caryclark30b9fdd2016-08-31 14:36:29 -0700771 FAIL_WITH_NULL_IF(span != &fTail, span);
caryclark55888e42016-07-18 10:01:36 -0700772 } while ((span = span->upCast()->next()));
773 SkASSERT(0);
caryclark29b25632016-08-25 11:27:17 -0700774 return nullptr; // we never get here, but need this to satisfy compiler
caryclark26ad22a2015-10-16 09:03:38 -0700775}
776#endif
777
778#if DEBUG_ANGLE
779void SkOpSegment::debugCheckAngleCoin() const {
780 const SkOpSpanBase* base = &fHead;
781 const SkOpSpan* span;
782 do {
783 const SkOpAngle* angle = base->fromAngle();
caryclark55888e42016-07-18 10:01:36 -0700784 if (angle && angle->debugCheckCoincidence()) {
caryclark26ad22a2015-10-16 09:03:38 -0700785 angle->debugCheckNearCoincidence();
786 }
787 if (base->final()) {
788 break;
789 }
790 span = base->upCast();
791 angle = span->toAngle();
caryclark55888e42016-07-18 10:01:36 -0700792 if (angle && angle->debugCheckCoincidence()) {
caryclark26ad22a2015-10-16 09:03:38 -0700793 angle->debugCheckNearCoincidence();
794 }
795 } while ((base = span->next()));
796}
797#endif
798
Cary Clarkab87d7a2016-10-04 10:01:04 -0400799#if DEBUG_COIN
caryclark26ad22a2015-10-16 09:03:38 -0700800// this mimics the order of the checks in handle coincidence
Cary Clarkab87d7a2016-10-04 10:01:04 -0400801void SkOpSegment::debugCheckHealth(SkPathOpsDebug::GlitchLog* glitches) const {
802 debugMoveMultiples(glitches);
803 debugMoveNearby(glitches);
804 debugMissingCoincidence(glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700805}
806
caryclark55888e42016-07-18 10:01:36 -0700807// commented-out lines keep this in sync with clearAll()
Cary Clarkab87d7a2016-10-04 10:01:04 -0400808void SkOpSegment::debugClearAll(SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -0700809 const SkOpSpan* span = &fHead;
810 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400811 this->debugClearOne(span, glitches);
caryclark55888e42016-07-18 10:01:36 -0700812 } while ((span = span->next()->upCastable()));
Cary Clarkab87d7a2016-10-04 10:01:04 -0400813 this->globalState()->coincidence()->debugRelease(glitches, this);
caryclark55888e42016-07-18 10:01:36 -0700814}
815
816// commented-out lines keep this in sync with clearOne()
Cary Clarkab87d7a2016-10-04 10:01:04 -0400817void SkOpSegment::debugClearOne(const SkOpSpan* span, SkPathOpsDebug::GlitchLog* glitches) const {
818 if (span->windValue()) glitches->record(SkPathOpsDebug::kCollapsedWindValue_Glitch, span);
819 if (span->oppValue()) glitches->record(SkPathOpsDebug::kCollapsedOppValue_Glitch, span);
820 if (!span->done()) glitches->record(SkPathOpsDebug::kCollapsedDone_Glitch, span);
caryclark26ad22a2015-10-16 09:03:38 -0700821}
822#endif
823
caryclark54359292015-03-26 07:52:43 -0700824SkOpAngle* SkOpSegment::debugLastAngle() {
halcanary96fcdcc2015-08-27 07:41:13 -0700825 SkOpAngle* result = nullptr;
caryclark54359292015-03-26 07:52:43 -0700826 SkOpSpan* span = this->head();
827 do {
828 if (span->toAngle()) {
829 SkASSERT(!result);
830 result = span->toAngle();
831 }
832 } while ((span = span->next()->upCastable()));
833 SkASSERT(result);
834 return result;
835}
836
Cary Clarkab87d7a2016-10-04 10:01:04 -0400837#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -0700838// commented-out lines keep this in sync with ClearVisited
839void SkOpSegment::DebugClearVisited(const SkOpSpanBase* span) {
840 // reset visited flag back to false
841 do {
842 const SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
843 while ((ptT = ptT->next()) != stopPtT) {
844 const SkOpSegment* opp = ptT->segment();
845 opp->resetDebugVisited();
846 }
847 } while (!span->final() && (span = span->upCast()->next()));
848}
849#endif
850
Cary Clarkab87d7a2016-10-04 10:01:04 -0400851#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -0700852// commented-out lines keep this in sync with missingCoincidence()
853// look for pairs of undetected coincident curves
854// assumes that segments going in have visited flag clear
855// Even though pairs of curves correct detect coincident runs, a run may be missed
856// if the coincidence is a product of multiple intersections. For instance, given
857// curves A, B, and C:
858// A-B intersect at a point 1; A-C and B-C intersect at point 2, so near
859// the end of C that the intersection is replaced with the end of C.
860// Even though A-B correctly do not detect an intersection at point 2,
861// the resulting run from point 1 to point 2 is coincident on A and B.
Cary Clarkab87d7a2016-10-04 10:01:04 -0400862void SkOpSegment::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -0700863 if (this->done()) {
864 return;
865 }
866 const SkOpSpan* prior = nullptr;
867 const SkOpSpanBase* spanBase = &fHead;
caryclark55888e42016-07-18 10:01:36 -0700868// bool result = false;
caryclark26ad22a2015-10-16 09:03:38 -0700869 do {
870 const SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT;
871 SkASSERT(ptT->span() == spanBase);
872 while ((ptT = ptT->next()) != spanStopPtT) {
873 if (ptT->deleted()) {
874 continue;
875 }
caryclark55888e42016-07-18 10:01:36 -0700876 const SkOpSegment* opp = ptT->span()->segment();
caryclark26ad22a2015-10-16 09:03:38 -0700877 if (opp->done()) {
878 continue;
879 }
880 // when opp is encounted the 1st time, continue; on 2nd encounter, look for coincidence
caryclark55888e42016-07-18 10:01:36 -0700881 if (!opp->debugVisited()) {
caryclark26ad22a2015-10-16 09:03:38 -0700882 continue;
883 }
884 if (spanBase == &fHead) {
885 continue;
886 }
caryclark55888e42016-07-18 10:01:36 -0700887 if (ptT->segment() == this) {
888 continue;
889 }
caryclark26ad22a2015-10-16 09:03:38 -0700890 const SkOpSpan* span = spanBase->upCastable();
891 // FIXME?: this assumes that if the opposite segment is coincident then no more
892 // coincidence needs to be detected. This may not be true.
caryclark55888e42016-07-18 10:01:36 -0700893 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 -0700894 continue;
895 }
caryclark55888e42016-07-18 10:01:36 -0700896 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 -0700897 continue;
caryclark55888e42016-07-18 10:01:36 -0700898 }
caryclark26ad22a2015-10-16 09:03:38 -0700899 const SkOpPtT* priorPtT = nullptr, * priorStopPtT;
900 // find prior span containing opp segment
901 const SkOpSegment* priorOpp = nullptr;
902 const SkOpSpan* priorTest = spanBase->prev();
903 while (!priorOpp && priorTest) {
904 priorStopPtT = priorPtT = priorTest->ptT();
905 while ((priorPtT = priorPtT->next()) != priorStopPtT) {
906 if (priorPtT->deleted()) {
907 continue;
908 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400909 const SkOpSegment* segment = priorPtT->span()->segment();
caryclark26ad22a2015-10-16 09:03:38 -0700910 if (segment == opp) {
911 prior = priorTest;
912 priorOpp = opp;
913 break;
914 }
915 }
916 priorTest = priorTest->prev();
917 }
918 if (!priorOpp) {
919 continue;
920 }
caryclark55888e42016-07-18 10:01:36 -0700921 if (priorPtT == ptT) {
922 continue;
923 }
caryclark26ad22a2015-10-16 09:03:38 -0700924 const SkOpPtT* oppStart = prior->ptT();
925 const SkOpPtT* oppEnd = spanBase->ptT();
926 bool swapped = priorPtT->fT > ptT->fT;
927 if (swapped) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -0400928 using std::swap;
929 swap(priorPtT, ptT);
930 swap(oppStart, oppEnd);
caryclark26ad22a2015-10-16 09:03:38 -0700931 }
caryclark55888e42016-07-18 10:01:36 -0700932 const SkOpCoincidence* coincidence = this->globalState()->coincidence();
933 const SkOpPtT* rootPriorPtT = priorPtT->span()->ptT();
934 const SkOpPtT* rootPtT = ptT->span()->ptT();
935 const SkOpPtT* rootOppStart = oppStart->span()->ptT();
936 const SkOpPtT* rootOppEnd = oppEnd->span()->ptT();
937 if (coincidence->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) {
caryclark26ad22a2015-10-16 09:03:38 -0700938 goto swapBack;
939 }
caryclark55888e42016-07-18 10:01:36 -0700940 if (testForCoincidence(rootPriorPtT, rootPtT, prior, spanBase, opp)) {
941 // mark coincidence
caryclark30b9fdd2016-08-31 14:36:29 -0700942#if DEBUG_COINCIDENCE_VERBOSE
caryclark55888e42016-07-18 10:01:36 -0700943// SkDebugf("%s coinSpan=%d endSpan=%d oppSpan=%d oppEndSpan=%d\n", __FUNCTION__,
944// rootPriorPtT->debugID(), rootPtT->debugID(), rootOppStart->debugID(),
945// rootOppEnd->debugID());
946#endif
Cary Clarkab87d7a2016-10-04 10:01:04 -0400947 log->record(SkPathOpsDebug::kMissingCoin_Glitch, priorPtT, ptT, oppStart, oppEnd);
caryclark55888e42016-07-18 10:01:36 -0700948 // coincidences->add(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
949 // }
950#if DEBUG_COINCIDENCE
caryclarkd6562002016-07-27 12:02:07 -0700951// SkASSERT(coincidences->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
caryclark55888e42016-07-18 10:01:36 -0700952#endif
953 // result = true;
caryclark26ad22a2015-10-16 09:03:38 -0700954 }
955 swapBack:
956 if (swapped) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -0400957 using std::swap;
958 swap(priorPtT, ptT);
caryclark26ad22a2015-10-16 09:03:38 -0700959 }
960 }
961 } while ((spanBase = spanBase->final() ? nullptr : spanBase->upCast()->next()));
caryclark55888e42016-07-18 10:01:36 -0700962 DebugClearVisited(&fHead);
963 return;
caryclark26ad22a2015-10-16 09:03:38 -0700964}
965
caryclark55888e42016-07-18 10:01:36 -0700966// commented-out lines keep this in sync with moveMultiples()
967// if a span has more than one intersection, merge the other segments' span as needed
Cary Clarkab87d7a2016-10-04 10:01:04 -0400968void SkOpSegment::debugMoveMultiples(SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -0700969 debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -0700970 const SkOpSpanBase* test = &fHead;
971 do {
972 int addCount = test->spanAddsCount();
Cary Clarkff114282016-12-14 11:56:16 -0500973// SkASSERT(addCount >= 1);
974 if (addCount <= 1) {
caryclark26ad22a2015-10-16 09:03:38 -0700975 continue;
976 }
977 const SkOpPtT* startPtT = test->ptT();
978 const SkOpPtT* testPtT = startPtT;
979 do { // iterate through all spans associated with start
980 const SkOpSpanBase* oppSpan = testPtT->span();
981 if (oppSpan->spanAddsCount() == addCount) {
982 continue;
983 }
984 if (oppSpan->deleted()) {
985 continue;
986 }
987 const SkOpSegment* oppSegment = oppSpan->segment();
988 if (oppSegment == this) {
989 continue;
990 }
991 // find range of spans to consider merging
992 const SkOpSpanBase* oppPrev = oppSpan;
993 const SkOpSpanBase* oppFirst = oppSpan;
994 while ((oppPrev = oppPrev->prev())) {
995 if (!roughly_equal(oppPrev->t(), oppSpan->t())) {
996 break;
997 }
998 if (oppPrev->spanAddsCount() == addCount) {
999 continue;
1000 }
1001 if (oppPrev->deleted()) {
1002 continue;
1003 }
1004 oppFirst = oppPrev;
1005 }
1006 const SkOpSpanBase* oppNext = oppSpan;
1007 const SkOpSpanBase* oppLast = oppSpan;
1008 while ((oppNext = oppNext->final() ? nullptr : oppNext->upCast()->next())) {
1009 if (!roughly_equal(oppNext->t(), oppSpan->t())) {
1010 break;
1011 }
1012 if (oppNext->spanAddsCount() == addCount) {
1013 continue;
1014 }
1015 if (oppNext->deleted()) {
1016 continue;
1017 }
1018 oppLast = oppNext;
1019 }
1020 if (oppFirst == oppLast) {
1021 continue;
1022 }
1023 const SkOpSpanBase* oppTest = oppFirst;
1024 do {
1025 if (oppTest == oppSpan) {
1026 continue;
1027 }
1028 // check to see if the candidate meets specific criteria:
1029 // it contains spans of segments in test's loop but not including 'this'
1030 const SkOpPtT* oppStartPtT = oppTest->ptT();
1031 const SkOpPtT* oppPtT = oppStartPtT;
1032 while ((oppPtT = oppPtT->next()) != oppStartPtT) {
1033 const SkOpSegment* oppPtTSegment = oppPtT->segment();
1034 if (oppPtTSegment == this) {
1035 goto tryNextSpan;
1036 }
1037 const SkOpPtT* matchPtT = startPtT;
1038 do {
1039 if (matchPtT->segment() == oppPtTSegment) {
1040 goto foundMatch;
1041 }
1042 } while ((matchPtT = matchPtT->next()) != startPtT);
1043 goto tryNextSpan;
1044 foundMatch: // merge oppTest and oppSpan
caryclark55888e42016-07-18 10:01:36 -07001045 oppSegment->debugValidate();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001046 oppTest->debugMergeMatches(glitches, oppSpan);
1047 oppTest->debugAddOpp(glitches, oppSpan);
caryclark55888e42016-07-18 10:01:36 -07001048 oppSegment->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001049 goto checkNextSpan;
1050 }
caryclark55888e42016-07-18 10:01:36 -07001051 tryNextSpan:
caryclark26ad22a2015-10-16 09:03:38 -07001052 ;
1053 } while (oppTest != oppLast && (oppTest = oppTest->upCast()->next()));
1054 } while ((testPtT = testPtT->next()) != startPtT);
caryclark55888e42016-07-18 10:01:36 -07001055checkNextSpan:
caryclark26ad22a2015-10-16 09:03:38 -07001056 ;
1057 } while ((test = test->final() ? nullptr : test->upCast()->next()));
caryclark55888e42016-07-18 10:01:36 -07001058 debugValidate();
1059 return;
caryclark26ad22a2015-10-16 09:03:38 -07001060}
1061
caryclark55888e42016-07-18 10:01:36 -07001062// commented-out lines keep this in sync with moveNearby()
1063// Move nearby t values and pts so they all hang off the same span. Alignment happens later.
Cary Clarkab87d7a2016-10-04 10:01:04 -04001064void SkOpSegment::debugMoveNearby(SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -07001065 debugValidate();
1066 // release undeleted spans pointing to this seg that are linked to the primary span
1067 const SkOpSpanBase* spanBase = &fHead;
caryclark26ad22a2015-10-16 09:03:38 -07001068 do {
caryclark55888e42016-07-18 10:01:36 -07001069 const SkOpPtT* ptT = spanBase->ptT();
1070 const SkOpPtT* headPtT = ptT;
1071 while ((ptT = ptT->next()) != headPtT) {
1072 const SkOpSpanBase* test = ptT->span();
1073 if (ptT->segment() == this && !ptT->deleted() && test != spanBase
1074 && test->ptT() == ptT) {
1075 if (test->final()) {
1076 if (spanBase == &fHead) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001077 glitches->record(SkPathOpsDebug::kMoveNearbyClearAll_Glitch, this);
caryclark55888e42016-07-18 10:01:36 -07001078// return;
1079 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001080 glitches->record(SkPathOpsDebug::kMoveNearbyReleaseFinal_Glitch, spanBase, ptT);
caryclark55888e42016-07-18 10:01:36 -07001081 } else if (test->prev()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001082 glitches->record(SkPathOpsDebug::kMoveNearbyRelease_Glitch, test, headPtT);
caryclark55888e42016-07-18 10:01:36 -07001083 }
1084// break;
caryclark26ad22a2015-10-16 09:03:38 -07001085 }
1086 }
caryclark55888e42016-07-18 10:01:36 -07001087 spanBase = spanBase->upCast()->next();
1088 } while (!spanBase->final());
1089
1090 // This loop looks for adjacent spans which are near by
1091 spanBase = &fHead;
1092 do { // iterate through all spans associated with start
1093 const SkOpSpanBase* test = spanBase->upCast()->next();
Cary Clark73e597d2017-04-18 12:08:58 -04001094 bool found;
1095 if (!this->spansNearby(spanBase, test, &found)) {
1096 glitches->record(SkPathOpsDebug::kMoveNearbyMergeFinal_Glitch, test);
1097 }
1098 if (found) {
caryclark55888e42016-07-18 10:01:36 -07001099 if (test->final()) {
1100 if (spanBase->prev()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001101 glitches->record(SkPathOpsDebug::kMoveNearbyMergeFinal_Glitch, test);
caryclark55888e42016-07-18 10:01:36 -07001102 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001103 glitches->record(SkPathOpsDebug::kMoveNearbyClearAll2_Glitch, this);
caryclark55888e42016-07-18 10:01:36 -07001104 // return
1105 }
1106 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001107 glitches->record(SkPathOpsDebug::kMoveNearbyMerge_Glitch, spanBase);
caryclark55888e42016-07-18 10:01:36 -07001108 }
1109 }
1110 spanBase = test;
1111 } while (!spanBase->final());
1112 debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001113}
1114#endif
1115
caryclark54359292015-03-26 07:52:43 -07001116void SkOpSegment::debugReset() {
caryclark1049f122015-04-20 08:31:59 -07001117 this->init(this->fPts, this->fWeight, this->contour(), this->verb());
caryclark54359292015-03-26 07:52:43 -07001118}
1119
caryclark025b11e2016-08-25 05:21:14 -07001120#if DEBUG_COINCIDENCE_ORDER
1121void SkOpSegment::debugSetCoinT(int index, SkScalar t) const {
1122 if (fDebugBaseMax < 0 || fDebugBaseIndex == index) {
1123 fDebugBaseIndex = index;
1124 fDebugBaseMin = SkTMin(t, fDebugBaseMin);
1125 fDebugBaseMax = SkTMax(t, fDebugBaseMax);
1126 return;
Ben Wagner63fd7602017-10-09 15:45:33 -04001127 }
caryclark025b11e2016-08-25 05:21:14 -07001128 SkASSERT(fDebugBaseMin >= t || t >= fDebugBaseMax);
1129 if (fDebugLastMax < 0 || fDebugLastIndex == index) {
1130 fDebugLastIndex = index;
1131 fDebugLastMin = SkTMin(t, fDebugLastMin);
1132 fDebugLastMax = SkTMax(t, fDebugLastMax);
1133 return;
1134 }
1135 SkASSERT(fDebugLastMin >= t || t >= fDebugLastMax);
1136 SkASSERT((t - fDebugBaseMin > 0) == (fDebugLastMin - fDebugBaseMin > 0));
1137}
1138#endif
1139
caryclark54359292015-03-26 07:52:43 -07001140#if DEBUG_ACTIVE_SPANS
Cary Clarkff114282016-12-14 11:56:16 -05001141void SkOpSegment::debugShowActiveSpans(SkString* str) const {
caryclark54359292015-03-26 07:52:43 -07001142 debugValidate();
1143 if (done()) {
1144 return;
1145 }
1146 int lastId = -1;
1147 double lastT = -1;
1148 const SkOpSpan* span = &fHead;
1149 do {
1150 if (span->done()) {
1151 continue;
1152 }
caryclark1049f122015-04-20 08:31:59 -07001153 if (lastId == this->debugID() && lastT == span->t()) {
caryclark54359292015-03-26 07:52:43 -07001154 continue;
1155 }
caryclark1049f122015-04-20 08:31:59 -07001156 lastId = this->debugID();
caryclark54359292015-03-26 07:52:43 -07001157 lastT = span->t();
Cary Clarkff114282016-12-14 11:56:16 -05001158 str->appendf("%s id=%d", __FUNCTION__, this->debugID());
caryclark55888e42016-07-18 10:01:36 -07001159 // since endpoints may have be adjusted, show actual computed curves
1160 SkDCurve curvePart;
1161 this->subDivide(span, span->next(), &curvePart);
1162 const SkDPoint* pts = curvePart.fCubic.fPts;
Cary Clarkff114282016-12-14 11:56:16 -05001163 str->appendf(" (%1.9g,%1.9g", pts[0].fX, pts[0].fY);
caryclark54359292015-03-26 07:52:43 -07001164 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
Cary Clarkff114282016-12-14 11:56:16 -05001165 str->appendf(" %1.9g,%1.9g", pts[vIndex].fX, pts[vIndex].fY);
caryclark54359292015-03-26 07:52:43 -07001166 }
caryclark1049f122015-04-20 08:31:59 -07001167 if (SkPath::kConic_Verb == fVerb) {
Cary Clarkff114282016-12-14 11:56:16 -05001168 str->appendf(" %1.9gf", curvePart.fConic.fWeight);
caryclark1049f122015-04-20 08:31:59 -07001169 }
Cary Clarkff114282016-12-14 11:56:16 -05001170 str->appendf(") t=%1.9g tEnd=%1.9g", span->t(), span->next()->t());
caryclark54359292015-03-26 07:52:43 -07001171 if (span->windSum() == SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001172 str->appendf(" windSum=?");
caryclark54359292015-03-26 07:52:43 -07001173 } else {
Cary Clarkff114282016-12-14 11:56:16 -05001174 str->appendf(" windSum=%d", span->windSum());
caryclark54359292015-03-26 07:52:43 -07001175 }
caryclark624637c2015-05-11 07:21:27 -07001176 if (span->oppValue() && span->oppSum() == SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001177 str->appendf(" oppSum=?");
caryclark624637c2015-05-11 07:21:27 -07001178 } else if (span->oppValue() || span->oppSum() != SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001179 str->appendf(" oppSum=%d", span->oppSum());
caryclark624637c2015-05-11 07:21:27 -07001180 }
Cary Clarkff114282016-12-14 11:56:16 -05001181 str->appendf(" windValue=%d", span->windValue());
caryclark624637c2015-05-11 07:21:27 -07001182 if (span->oppValue() || span->oppSum() != SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001183 str->appendf(" oppValue=%d", span->oppValue());
caryclark624637c2015-05-11 07:21:27 -07001184 }
Cary Clarkff114282016-12-14 11:56:16 -05001185 str->appendf("\n");
caryclark54359292015-03-26 07:52:43 -07001186 } while ((span = span->next()->upCastable()));
1187}
1188#endif
1189
1190#if DEBUG_MARK_DONE
1191void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding) {
1192 const SkPoint& pt = span->ptT()->fPt;
caryclark1049f122015-04-20 08:31:59 -07001193 SkDebugf("%s id=%d", fun, this->debugID());
caryclark54359292015-03-26 07:52:43 -07001194 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1195 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1196 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1197 }
1198 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
1199 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t());
1200 if (winding == SK_MinS32) {
1201 SkDebugf("?");
1202 } else {
1203 SkDebugf("%d", winding);
1204 }
1205 SkDebugf(" windSum=");
1206 if (span->windSum() == SK_MinS32) {
1207 SkDebugf("?");
1208 } else {
1209 SkDebugf("%d", span->windSum());
1210 }
1211 SkDebugf(" windValue=%d\n", span->windValue());
1212}
1213
1214void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding,
1215 int oppWinding) {
1216 const SkPoint& pt = span->ptT()->fPt;
caryclark1049f122015-04-20 08:31:59 -07001217 SkDebugf("%s id=%d", fun, this->debugID());
caryclark54359292015-03-26 07:52:43 -07001218 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1219 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1220 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1221 }
1222 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
1223 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t(), winding, oppWinding);
1224 if (winding == SK_MinS32) {
1225 SkDebugf("?");
1226 } else {
1227 SkDebugf("%d", winding);
1228 }
1229 SkDebugf(" newOppSum=");
1230 if (oppWinding == SK_MinS32) {
1231 SkDebugf("?");
1232 } else {
1233 SkDebugf("%d", oppWinding);
1234 }
1235 SkDebugf(" oppSum=");
1236 if (span->oppSum() == SK_MinS32) {
1237 SkDebugf("?");
1238 } else {
1239 SkDebugf("%d", span->oppSum());
1240 }
1241 SkDebugf(" windSum=");
1242 if (span->windSum() == SK_MinS32) {
1243 SkDebugf("?");
1244 } else {
1245 SkDebugf("%d", span->windSum());
1246 }
1247 SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue());
1248}
1249
1250#endif
1251
caryclark26ad22a2015-10-16 09:03:38 -07001252// loop looking for a pair of angle parts that are too close to be sorted
1253/* This is called after other more simple intersection and angle sorting tests have been exhausted.
1254 This should be rarely called -- the test below is thorough and time consuming.
caryclark55888e42016-07-18 10:01:36 -07001255 This checks the distance between start points; the distance between
caryclark26ad22a2015-10-16 09:03:38 -07001256*/
1257#if DEBUG_ANGLE
1258void SkOpAngle::debugCheckNearCoincidence() const {
1259 const SkOpAngle* test = this;
1260 do {
1261 const SkOpSegment* testSegment = test->segment();
1262 double testStartT = test->start()->t();
1263 SkDPoint testStartPt = testSegment->dPtAtT(testStartT);
1264 double testEndT = test->end()->t();
1265 SkDPoint testEndPt = testSegment->dPtAtT(testEndT);
1266 double testLenSq = testStartPt.distanceSquared(testEndPt);
1267 SkDebugf("%s testLenSq=%1.9g id=%d\n", __FUNCTION__, testLenSq, testSegment->debugID());
1268 double testMidT = (testStartT + testEndT) / 2;
1269 const SkOpAngle* next = test;
1270 while ((next = next->fNext) != this) {
1271 SkOpSegment* nextSegment = next->segment();
1272 double testMidDistSq = testSegment->distSq(testMidT, next);
1273 double testEndDistSq = testSegment->distSq(testEndT, next);
1274 double nextStartT = next->start()->t();
1275 SkDPoint nextStartPt = nextSegment->dPtAtT(nextStartT);
1276 double distSq = testStartPt.distanceSquared(nextStartPt);
1277 double nextEndT = next->end()->t();
1278 double nextMidT = (nextStartT + nextEndT) / 2;
1279 double nextMidDistSq = nextSegment->distSq(nextMidT, test);
1280 double nextEndDistSq = nextSegment->distSq(nextEndT, test);
1281 SkDebugf("%s distSq=%1.9g testId=%d nextId=%d\n", __FUNCTION__, distSq,
1282 testSegment->debugID(), nextSegment->debugID());
1283 SkDebugf("%s testMidDistSq=%1.9g\n", __FUNCTION__, testMidDistSq);
1284 SkDebugf("%s testEndDistSq=%1.9g\n", __FUNCTION__, testEndDistSq);
1285 SkDebugf("%s nextMidDistSq=%1.9g\n", __FUNCTION__, nextMidDistSq);
1286 SkDebugf("%s nextEndDistSq=%1.9g\n", __FUNCTION__, nextEndDistSq);
1287 SkDPoint nextEndPt = nextSegment->dPtAtT(nextEndT);
1288 double nextLenSq = nextStartPt.distanceSquared(nextEndPt);
1289 SkDebugf("%s nextLenSq=%1.9g\n", __FUNCTION__, nextLenSq);
1290 SkDebugf("\n");
1291 }
1292 test = test->fNext;
caryclark55888e42016-07-18 10:01:36 -07001293 } while (test->fNext != this);
caryclark26ad22a2015-10-16 09:03:38 -07001294}
1295#endif
1296
caryclark54359292015-03-26 07:52:43 -07001297#if DEBUG_ANGLE
1298SkString SkOpAngle::debugPart() const {
1299 SkString result;
1300 switch (this->segment()->verb()) {
1301 case SkPath::kLine_Verb:
caryclarkeed356d2016-09-14 07:18:20 -07001302 result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fPart.fCurve),
caryclark54359292015-03-26 07:52:43 -07001303 this->segment()->debugID());
1304 break;
1305 case SkPath::kQuad_Verb:
caryclarkeed356d2016-09-14 07:18:20 -07001306 result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fPart.fCurve),
caryclark54359292015-03-26 07:52:43 -07001307 this->segment()->debugID());
1308 break;
caryclark1049f122015-04-20 08:31:59 -07001309 case SkPath::kConic_Verb:
1310 result.printf(CONIC_DEBUG_STR " id=%d",
caryclarkeed356d2016-09-14 07:18:20 -07001311 CONIC_DEBUG_DATA(fPart.fCurve, fPart.fCurve.fConic.fWeight),
caryclark1049f122015-04-20 08:31:59 -07001312 this->segment()->debugID());
1313 break;
caryclark54359292015-03-26 07:52:43 -07001314 case SkPath::kCubic_Verb:
caryclarkeed356d2016-09-14 07:18:20 -07001315 result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fPart.fCurve),
caryclark54359292015-03-26 07:52:43 -07001316 this->segment()->debugID());
1317 break;
1318 default:
1319 SkASSERT(0);
mtklein1b249332015-07-07 12:21:21 -07001320 }
caryclark54359292015-03-26 07:52:43 -07001321 return result;
1322}
1323#endif
1324
caryclark624637c2015-05-11 07:21:27 -07001325#if DEBUG_SORT
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001326void SkOpAngle::debugLoop() const {
caryclarke4097e32014-06-18 07:24:19 -07001327 const SkOpAngle* first = this;
1328 const SkOpAngle* next = this;
1329 do {
1330 next->dumpOne(true);
caryclark19eb3b22014-07-18 05:08:14 -07001331 SkDebugf("\n");
caryclarke4097e32014-06-18 07:24:19 -07001332 next = next->fNext;
1333 } while (next && next != first);
caryclark54359292015-03-26 07:52:43 -07001334 next = first;
1335 do {
1336 next->debugValidate();
1337 next = next->fNext;
1338 } while (next && next != first);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001339}
1340#endif
1341
caryclark54359292015-03-26 07:52:43 -07001342void SkOpAngle::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07001343#if DEBUG_COINCIDENCE
1344 if (this->globalState()->debugCheckHealth()) {
1345 return;
1346 }
1347#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001348#if DEBUG_VALIDATE
caryclark54359292015-03-26 07:52:43 -07001349 const SkOpAngle* first = this;
1350 const SkOpAngle* next = this;
1351 int wind = 0;
1352 int opp = 0;
1353 int lastXor = -1;
1354 int lastOppXor = -1;
1355 do {
1356 if (next->unorderable()) {
1357 return;
1358 }
1359 const SkOpSpan* minSpan = next->start()->starter(next->end());
1360 if (minSpan->windValue() == SK_MinS32) {
1361 return;
1362 }
1363 bool op = next->segment()->operand();
1364 bool isXor = next->segment()->isXor();
1365 bool oppXor = next->segment()->oppXor();
1366 SkASSERT(!DEBUG_LIMIT_WIND_SUM || between(0, minSpan->windValue(), DEBUG_LIMIT_WIND_SUM));
1367 SkASSERT(!DEBUG_LIMIT_WIND_SUM
1368 || between(-DEBUG_LIMIT_WIND_SUM, minSpan->oppValue(), DEBUG_LIMIT_WIND_SUM));
1369 bool useXor = op ? oppXor : isXor;
1370 SkASSERT(lastXor == -1 || lastXor == (int) useXor);
1371 lastXor = (int) useXor;
caryclark624637c2015-05-11 07:21:27 -07001372 wind += next->debugSign() * (op ? minSpan->oppValue() : minSpan->windValue());
caryclark54359292015-03-26 07:52:43 -07001373 if (useXor) {
1374 wind &= 1;
1375 }
1376 useXor = op ? isXor : oppXor;
1377 SkASSERT(lastOppXor == -1 || lastOppXor == (int) useXor);
1378 lastOppXor = (int) useXor;
caryclark624637c2015-05-11 07:21:27 -07001379 opp += next->debugSign() * (op ? minSpan->windValue() : minSpan->oppValue());
caryclark54359292015-03-26 07:52:43 -07001380 if (useXor) {
1381 opp &= 1;
1382 }
1383 next = next->fNext;
1384 } while (next && next != first);
Cary Clark59d5a0e2017-01-23 14:38:52 +00001385 SkASSERT(wind == 0 || !SkPathOpsDebug::gRunFail);
1386 SkASSERT(opp == 0 || !SkPathOpsDebug::gRunFail);
caryclark54359292015-03-26 07:52:43 -07001387#endif
1388}
1389
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001390void SkOpAngle::debugValidateNext() const {
caryclark54359292015-03-26 07:52:43 -07001391#if !FORCE_RELEASE
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001392 const SkOpAngle* first = this;
1393 const SkOpAngle* next = first;
1394 SkTDArray<const SkOpAngle*>(angles);
1395 do {
djsollenf2b340f2016-01-29 08:51:04 -08001396// SkASSERT_RELEASE(next->fSegment->debugContains(next));
Mike Reedb5475792018-08-08 16:17:42 -04001397 angles.push_back(next);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001398 next = next->next();
1399 if (next == first) {
1400 break;
1401 }
djsollenf2b340f2016-01-29 08:51:04 -08001402 SkASSERT_RELEASE(!angles.contains(next));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001403 if (!next) {
1404 return;
1405 }
1406 } while (true);
reed0dc4dd62015-03-24 13:55:33 -07001407#endif
reed0dc4dd62015-03-24 13:55:33 -07001408}
reed0dc4dd62015-03-24 13:55:33 -07001409
caryclark55888e42016-07-18 10:01:36 -07001410#ifdef SK_DEBUG
1411void SkCoincidentSpans::debugStartCheck(const SkOpSpanBase* outer, const SkOpSpanBase* over,
1412 const SkOpGlobalState* debugState) const {
Cary Clark59d5a0e2017-01-23 14:38:52 +00001413 SkASSERT(coinPtTEnd()->span() == over || !SkOpGlobalState::DebugRunFail());
1414 SkASSERT(oppPtTEnd()->span() == outer || !SkOpGlobalState::DebugRunFail());
caryclark55888e42016-07-18 10:01:36 -07001415}
1416#endif
caryclark26ad22a2015-10-16 09:03:38 -07001417
Cary Clarkab87d7a2016-10-04 10:01:04 -04001418#if DEBUG_COIN
1419// sets the span's end to the ptT referenced by the previous-next
1420void SkCoincidentSpans::debugCorrectOneEnd(SkPathOpsDebug::GlitchLog* log,
1421 const SkOpPtT* (SkCoincidentSpans::* getEnd)() const,
1422 void (SkCoincidentSpans::*setEnd)(const SkOpPtT* ptT) const ) const {
1423 const SkOpPtT* origPtT = (this->*getEnd)();
1424 const SkOpSpanBase* origSpan = origPtT->span();
1425 const SkOpSpan* prev = origSpan->prev();
1426 const SkOpPtT* testPtT = prev ? prev->next()->ptT()
1427 : origSpan->upCast()->next()->prev()->ptT();
1428 if (origPtT != testPtT) {
1429 log->record(SkPathOpsDebug::kCorrectEnd_Glitch, this, origPtT, testPtT);
1430 }
1431}
1432
1433
1434/* Commented-out lines keep this in sync with correctEnds */
1435// FIXME: member pointers have fallen out of favor and can be replaced with
1436// an alternative approach.
1437// makes all span ends agree with the segment's spans that define them
1438void SkCoincidentSpans::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
1439 this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTStart, nullptr);
1440 this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTEnd, nullptr);
1441 this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTStart, nullptr);
1442 this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTEnd, nullptr);
1443}
1444
caryclark55888e42016-07-18 10:01:36 -07001445/* Commented-out lines keep this in sync with expand */
caryclark30b9fdd2016-08-31 14:36:29 -07001446// expand the range by checking adjacent spans for coincidence
Cary Clarkab87d7a2016-10-04 10:01:04 -04001447bool SkCoincidentSpans::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07001448 bool expanded = false;
1449 const SkOpSegment* segment = coinPtTStart()->segment();
1450 const SkOpSegment* oppSegment = oppPtTStart()->segment();
1451 do {
1452 const SkOpSpan* start = coinPtTStart()->span()->upCast();
1453 const SkOpSpan* prev = start->prev();
1454 const SkOpPtT* oppPtT;
1455 if (!prev || !(oppPtT = prev->contains(oppSegment))) {
1456 break;
1457 }
1458 double midT = (prev->t() + start->t()) / 2;
1459 if (!segment->isClose(midT, oppSegment)) {
1460 break;
1461 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001462 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, prev->ptT(), oppPtT);
caryclark55888e42016-07-18 10:01:36 -07001463 expanded = true;
1464 } while (false); // actual continues while expansion is possible
1465 do {
1466 const SkOpSpanBase* end = coinPtTEnd()->span();
1467 SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
caryclark30b9fdd2016-08-31 14:36:29 -07001468 if (next && next->deleted()) {
1469 break;
1470 }
caryclark55888e42016-07-18 10:01:36 -07001471 const SkOpPtT* oppPtT;
1472 if (!next || !(oppPtT = next->contains(oppSegment))) {
1473 break;
1474 }
1475 double midT = (end->t() + next->t()) / 2;
1476 if (!segment->isClose(midT, oppSegment)) {
1477 break;
1478 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001479 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, next->ptT(), oppPtT);
caryclark55888e42016-07-18 10:01:36 -07001480 expanded = true;
1481 } while (false); // actual continues while expansion is possible
1482 return expanded;
1483}
1484
Cary Clarkab87d7a2016-10-04 10:01:04 -04001485// description below
1486void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* base, const SkOpSpanBase* testSpan) const {
1487 const SkOpPtT* testPtT = testSpan->ptT();
1488 const SkOpPtT* stopPtT = testPtT;
1489 const SkOpSegment* baseSeg = base->segment();
1490 while ((testPtT = testPtT->next()) != stopPtT) {
1491 const SkOpSegment* testSeg = testPtT->segment();
1492 if (testPtT->deleted()) {
1493 continue;
1494 }
1495 if (testSeg == baseSeg) {
1496 continue;
1497 }
1498 if (testPtT->span()->ptT() != testPtT) {
1499 continue;
1500 }
1501 if (this->contains(baseSeg, testSeg, testPtT->fT)) {
1502 continue;
1503 }
1504 // intersect perp with base->ptT() with testPtT->segment()
1505 SkDVector dxdy = baseSeg->dSlopeAtT(base->t());
1506 const SkPoint& pt = base->pt();
1507 SkDLine ray = {{{pt.fX, pt.fY}, {pt.fX + dxdy.fY, pt.fY - dxdy.fX}}};
1508 SkIntersections i;
1509 (*CurveIntersectRay[testSeg->verb()])(testSeg->pts(), testSeg->weight(), ray, &i);
1510 for (int index = 0; index < i.used(); ++index) {
1511 double t = i[0][index];
1512 if (!between(0, t, 1)) {
1513 continue;
1514 }
1515 SkDPoint oppPt = i.pt(index);
1516 if (!oppPt.approximatelyEqual(pt)) {
1517 continue;
1518 }
1519 SkOpSegment* writableSeg = const_cast<SkOpSegment*>(testSeg);
1520 SkOpPtT* oppStart = writableSeg->addT(t);
1521 if (oppStart == testPtT) {
1522 continue;
1523 }
1524 SkOpSpan* writableBase = const_cast<SkOpSpan*>(base);
1525 oppStart->span()->addOpp(writableBase);
1526 if (oppStart->deleted()) {
1527 continue;
1528 }
1529 SkOpSegment* coinSeg = base->segment();
1530 SkOpSegment* oppSeg = oppStart->segment();
1531 double coinTs, coinTe, oppTs, oppTe;
1532 if (Ordered(coinSeg, oppSeg)) {
1533 coinTs = base->t();
1534 coinTe = testSpan->t();
1535 oppTs = oppStart->fT;
1536 oppTe = testPtT->fT;
1537 } else {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001538 using std::swap;
1539 swap(coinSeg, oppSeg);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001540 coinTs = oppStart->fT;
1541 coinTe = testPtT->fT;
1542 oppTs = base->t();
1543 oppTe = testSpan->t();
1544 }
1545 if (coinTs > coinTe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001546 using std::swap;
1547 swap(coinTs, coinTe);
1548 swap(oppTs, oppTe);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001549 }
1550 bool added;
1551 if (this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &added), false) {
1552 return;
1553 }
1554 }
1555 }
1556 return;
1557}
1558
1559// description below
1560void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* ptT) const {
1561 FAIL_IF(!ptT->span()->upCastable(), ptT->span());
1562 const SkOpSpan* base = ptT->span()->upCast();
1563 const SkOpSpan* prev = base->prev();
1564 FAIL_IF(!prev, ptT->span());
1565 if (!prev->isCanceled()) {
1566 if (this->debugAddEndMovedSpans(log, base, base->prev()), false) {
1567 return;
1568 }
1569 }
1570 if (!base->isCanceled()) {
1571 if (this->debugAddEndMovedSpans(log, base, base->next()), false) {
1572 return;
1573 }
1574 }
1575 return;
1576}
1577
1578/* If A is coincident with B and B includes an endpoint, and A's matching point
1579 is not the endpoint (i.e., there's an implied line connecting B-end and A)
1580 then assume that the same implied line may intersect another curve close to B.
1581 Since we only care about coincidence that was undetected, look at the
1582 ptT list on B-segment adjacent to the B-end/A ptT loop (not in the loop, but
1583 next door) and see if the A matching point is close enough to form another
1584 coincident pair. If so, check for a new coincident span between B-end/A ptT loop
1585 and the adjacent ptT loop.
1586*/
1587void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log) const {
1588 const SkCoincidentSpans* span = fHead;
1589 if (!span) {
1590 return;
1591 }
1592// fTop = span;
1593// fHead = nullptr;
1594 do {
1595 if (span->coinPtTStart()->fPt != span->oppPtTStart()->fPt) {
1596 FAIL_IF(1 == span->coinPtTStart()->fT, span);
1597 bool onEnd = span->coinPtTStart()->fT == 0;
1598 bool oOnEnd = zero_or_one(span->oppPtTStart()->fT);
1599 if (onEnd) {
1600 if (!oOnEnd) { // if both are on end, any nearby intersect was already found
1601 if (this->debugAddEndMovedSpans(log, span->oppPtTStart()), false) {
1602 return;
1603 }
1604 }
1605 } else if (oOnEnd) {
1606 if (this->debugAddEndMovedSpans(log, span->coinPtTStart()), false) {
1607 return;
1608 }
1609 }
1610 }
1611 if (span->coinPtTEnd()->fPt != span->oppPtTEnd()->fPt) {
1612 bool onEnd = span->coinPtTEnd()->fT == 1;
1613 bool oOnEnd = zero_or_one(span->oppPtTEnd()->fT);
1614 if (onEnd) {
1615 if (!oOnEnd) {
1616 if (this->debugAddEndMovedSpans(log, span->oppPtTEnd()), false) {
1617 return;
1618 }
1619 }
1620 } else if (oOnEnd) {
1621 if (this->debugAddEndMovedSpans(log, span->coinPtTEnd()), false) {
1622 return;
1623 }
1624 }
1625 }
1626 } while ((span = span->next()));
1627// this->restoreHead();
1628 return;
1629}
1630
caryclark55888e42016-07-18 10:01:36 -07001631/* Commented-out lines keep this in sync with addExpanded */
1632// for each coincident pair, match the spans
1633// if the spans don't match, add the mssing pt to the segment and loop it in the opposite span
Cary Clarkab87d7a2016-10-04 10:01:04 -04001634void SkOpCoincidence::debugAddExpanded(SkPathOpsDebug::GlitchLog* log) const {
caryclarka35ab3e2016-10-20 08:32:18 -07001635// DEBUG_SET_PHASE();
caryclark26ad22a2015-10-16 09:03:38 -07001636 const SkCoincidentSpans* coin = this->fHead;
1637 if (!coin) {
caryclarked0935a2015-10-22 07:23:52 -07001638 return;
1639 }
caryclark26ad22a2015-10-16 09:03:38 -07001640 do {
caryclark55888e42016-07-18 10:01:36 -07001641 const SkOpPtT* startPtT = coin->coinPtTStart();
1642 const SkOpPtT* oStartPtT = coin->oppPtTStart();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001643 double priorT = startPtT->fT;
1644 double oPriorT = oStartPtT->fT;
Cary Clarkff114282016-12-14 11:56:16 -05001645 FAIL_IF(!startPtT->contains(oStartPtT), coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001646 SkOPASSERT(coin->coinPtTEnd()->contains(coin->oppPtTEnd()));
caryclark26ad22a2015-10-16 09:03:38 -07001647 const SkOpSpanBase* start = startPtT->span();
1648 const SkOpSpanBase* oStart = oStartPtT->span();
caryclark55888e42016-07-18 10:01:36 -07001649 const SkOpSpanBase* end = coin->coinPtTEnd()->span();
1650 const SkOpSpanBase* oEnd = coin->oppPtTEnd()->span();
caryclark30b9fdd2016-08-31 14:36:29 -07001651 FAIL_IF(oEnd->deleted(), coin);
1652 FAIL_IF(!start->upCastable(), coin);
caryclark26ad22a2015-10-16 09:03:38 -07001653 const SkOpSpanBase* test = start->upCast()->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001654 FAIL_IF(!coin->flipped() && !oStart->upCastable(), coin);
caryclark55888e42016-07-18 10:01:36 -07001655 const SkOpSpanBase* oTest = coin->flipped() ? oStart->prev() : oStart->upCast()->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001656 FAIL_IF(!oTest, coin);
1657 const SkOpSegment* seg = start->segment();
1658 const SkOpSegment* oSeg = oStart->segment();
caryclark26ad22a2015-10-16 09:03:38 -07001659 while (test != end || oTest != oEnd) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001660 const SkOpPtT* containedOpp = test->ptT()->contains(oSeg);
1661 const SkOpPtT* containedThis = oTest->ptT()->contains(seg);
1662 if (!containedOpp || !containedThis) {
1663 // choose the ends, or the first common pt-t list shared by both
1664 double nextT, oNextT;
1665 if (containedOpp) {
1666 nextT = test->t();
1667 oNextT = containedOpp->fT;
1668 } else if (containedThis) {
1669 nextT = containedThis->fT;
1670 oNextT = oTest->t();
1671 } else {
1672 // iterate through until a pt-t list found that contains the other
1673 const SkOpSpanBase* walk = test;
1674 const SkOpPtT* walkOpp;
1675 do {
1676 FAIL_IF(!walk->upCastable(), coin);
1677 walk = walk->upCast()->next();
1678 } while (!(walkOpp = walk->ptT()->contains(oSeg))
1679 && walk != coin->coinPtTEnd()->span());
caryclarka35ab3e2016-10-20 08:32:18 -07001680 FAIL_IF(!walkOpp, coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001681 nextT = walk->t();
1682 oNextT = walkOpp->fT;
1683 }
caryclark26ad22a2015-10-16 09:03:38 -07001684 // use t ranges to guess which one is missing
caryclarka35ab3e2016-10-20 08:32:18 -07001685 double startRange = nextT - priorT;
caryclark30b9fdd2016-08-31 14:36:29 -07001686 FAIL_IF(!startRange, coin);
caryclarka35ab3e2016-10-20 08:32:18 -07001687 double startPart = (test->t() - priorT) / startRange;
1688 double oStartRange = oNextT - oPriorT;
caryclark30b9fdd2016-08-31 14:36:29 -07001689 FAIL_IF(!oStartRange, coin);
caryclark26ad22a2015-10-16 09:03:38 -07001690 double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange;
caryclark30b9fdd2016-08-31 14:36:29 -07001691 FAIL_IF(startPart == oStartPart, coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001692 bool addToOpp = !containedOpp && !containedThis ? startPart < oStartPart
1693 : !!containedThis;
caryclark55888e42016-07-18 10:01:36 -07001694 bool startOver = false;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001695 addToOpp ? log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch,
1696 oPriorT + oStartRange * startPart, test)
1697 : log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch,
1698 priorT + startRange * oStartPart, oTest);
1699 // FAIL_IF(!success, coin);
caryclark55888e42016-07-18 10:01:36 -07001700 if (startOver) {
1701 test = start;
1702 oTest = oStart;
caryclark26ad22a2015-10-16 09:03:38 -07001703 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001704 end = coin->coinPtTEnd()->span();
1705 oEnd = coin->oppPtTEnd()->span();
caryclark26ad22a2015-10-16 09:03:38 -07001706 }
caryclark55888e42016-07-18 10:01:36 -07001707 if (test != end) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001708 FAIL_IF(!test->upCastable(), coin);
1709 priorT = test->t();
caryclark26ad22a2015-10-16 09:03:38 -07001710 test = test->upCast()->next();
1711 }
caryclark55888e42016-07-18 10:01:36 -07001712 if (oTest != oEnd) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001713 oPriorT = oTest->t();
caryclark55888e42016-07-18 10:01:36 -07001714 oTest = coin->flipped() ? oTest->prev() : oTest->upCast()->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001715 FAIL_IF(!oTest, coin);
caryclark26ad22a2015-10-16 09:03:38 -07001716 }
1717 }
caryclark55888e42016-07-18 10:01:36 -07001718 } while ((coin = coin->next()));
1719 return;
caryclark26ad22a2015-10-16 09:03:38 -07001720}
1721
caryclark55888e42016-07-18 10:01:36 -07001722/* Commented-out lines keep this in sync addIfMissing() */
caryclark8016b262016-09-06 05:59:47 -07001723// note that over1s, over1e, over2s, over2e are ordered
Cary Clarkab87d7a2016-10-04 10:01:04 -04001724void SkOpCoincidence::debugAddIfMissing(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* over1s, const SkOpPtT* over2s,
caryclark81a478c2016-09-09 09:37:57 -07001725 double tStart, double tEnd, const SkOpSegment* coinSeg, const SkOpSegment* oppSeg, bool* added,
caryclark8016b262016-09-06 05:59:47 -07001726 const SkOpPtT* over1e, const SkOpPtT* over2e) const {
1727 SkASSERT(tStart < tEnd);
1728 SkASSERT(over1s->fT < over1e->fT);
1729 SkASSERT(between(over1s->fT, tStart, over1e->fT));
1730 SkASSERT(between(over1s->fT, tEnd, over1e->fT));
1731 SkASSERT(over2s->fT < over2e->fT);
1732 SkASSERT(between(over2s->fT, tStart, over2e->fT));
1733 SkASSERT(between(over2s->fT, tEnd, over2e->fT));
1734 SkASSERT(over1s->segment() == over1e->segment());
1735 SkASSERT(over2s->segment() == over2e->segment());
1736 SkASSERT(over1s->segment() == over2s->segment());
1737 SkASSERT(over1s->segment() != coinSeg);
1738 SkASSERT(over1s->segment() != oppSeg);
1739 SkASSERT(coinSeg != oppSeg);
caryclark26ad22a2015-10-16 09:03:38 -07001740 double coinTs, coinTe, oppTs, oppTe;
caryclark8016b262016-09-06 05:59:47 -07001741 coinTs = TRange(over1s, tStart, coinSeg SkDEBUGPARAMS(over1e));
1742 coinTe = TRange(over1s, tEnd, coinSeg SkDEBUGPARAMS(over1e));
Cary Clarkba610292018-06-19 09:47:15 -04001743 SkOpSpanBase::Collapsed result = coinSeg->collapsed(coinTs, coinTe);
1744 if (SkOpSpanBase::Collapsed::kNo != result) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001745 return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, coinSeg);
caryclark8016b262016-09-06 05:59:47 -07001746 }
1747 oppTs = TRange(over2s, tStart, oppSeg SkDEBUGPARAMS(over2e));
1748 oppTe = TRange(over2s, tEnd, oppSeg SkDEBUGPARAMS(over2e));
Cary Clarkba610292018-06-19 09:47:15 -04001749 result = oppSeg->collapsed(oppTs, oppTe);
1750 if (SkOpSpanBase::Collapsed::kNo != result) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001751 return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, oppSeg);
caryclark8016b262016-09-06 05:59:47 -07001752 }
1753 if (coinTs > coinTe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001754 using std::swap;
1755 swap(coinTs, coinTe);
1756 swap(oppTs, oppTe);
caryclark26ad22a2015-10-16 09:03:38 -07001757 }
Cary Clarkba610292018-06-19 09:47:15 -04001758 this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, added);
1759 return;
caryclark26ad22a2015-10-16 09:03:38 -07001760}
1761
caryclark55888e42016-07-18 10:01:36 -07001762/* Commented-out lines keep this in sync addOrOverlap() */
caryclark30b9fdd2016-08-31 14:36:29 -07001763// If this is called by addEndMovedSpans(), a returned false propogates out to an abort.
1764// If this is called by AddIfMissing(), a returned false indicates there was nothing to add
Cary Clarkab87d7a2016-10-04 10:01:04 -04001765void SkOpCoincidence::debugAddOrOverlap(SkPathOpsDebug::GlitchLog* log,
caryclark30b9fdd2016-08-31 14:36:29 -07001766 const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
caryclark81a478c2016-09-09 09:37:57 -07001767 double coinTs, double coinTe, double oppTs, double oppTe, bool* added) const {
caryclark55888e42016-07-18 10:01:36 -07001768 SkTDArray<SkCoincidentSpans*> overlaps;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001769 SkOPASSERT(!fTop); // this is (correctly) reversed in addifMissing()
caryclark81a478c2016-09-09 09:37:57 -07001770 if (fTop && !this->checkOverlap(fTop, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe,
1771 &overlaps)) {
caryclark55888e42016-07-18 10:01:36 -07001772 return;
1773 }
1774 if (fHead && !this->checkOverlap(fHead, coinSeg, oppSeg, coinTs,
1775 coinTe, oppTs, oppTe, &overlaps)) {
1776 return;
1777 }
1778 const SkCoincidentSpans* overlap = overlaps.count() ? overlaps[0] : nullptr;
1779 for (int index = 1; index < overlaps.count(); ++index) { // combine overlaps before continuing
1780 const SkCoincidentSpans* test = overlaps[index];
1781 if (overlap->coinPtTStart()->fT > test->coinPtTStart()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001782 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTStart());
caryclark55888e42016-07-18 10:01:36 -07001783 }
1784 if (overlap->coinPtTEnd()->fT < test->coinPtTEnd()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001785 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07001786 }
1787 if (overlap->flipped()
1788 ? overlap->oppPtTStart()->fT < test->oppPtTStart()->fT
1789 : overlap->oppPtTStart()->fT > test->oppPtTStart()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001790 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTStart());
caryclark55888e42016-07-18 10:01:36 -07001791 }
1792 if (overlap->flipped()
1793 ? overlap->oppPtTEnd()->fT > test->oppPtTEnd()->fT
1794 : overlap->oppPtTEnd()->fT < test->oppPtTEnd()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001795 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07001796 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001797 if (!fHead) { this->debugRelease(log, fHead, test);
1798 this->debugRelease(log, fTop, test);
caryclark55888e42016-07-18 10:01:36 -07001799 }
1800 }
1801 const SkOpPtT* cs = coinSeg->existing(coinTs, oppSeg);
1802 const SkOpPtT* ce = coinSeg->existing(coinTe, oppSeg);
caryclark30b9fdd2016-08-31 14:36:29 -07001803 RETURN_FALSE_IF(overlap && cs && ce && overlap->contains(cs, ce), coinSeg);
1804 RETURN_FALSE_IF(cs != ce || !cs, coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001805 const SkOpPtT* os = oppSeg->existing(oppTs, coinSeg);
1806 const SkOpPtT* oe = oppSeg->existing(oppTe, coinSeg);
caryclark30b9fdd2016-08-31 14:36:29 -07001807 RETURN_FALSE_IF(overlap && os && oe && overlap->contains(os, oe), oppSeg);
caryclark55888e42016-07-18 10:01:36 -07001808 SkASSERT(true || !cs || !cs->deleted());
1809 SkASSERT(true || !os || !os->deleted());
1810 SkASSERT(true || !ce || !ce->deleted());
1811 SkASSERT(true || !oe || !oe->deleted());
1812 const SkOpPtT* csExisting = !cs ? coinSeg->existing(coinTs, nullptr) : nullptr;
1813 const SkOpPtT* ceExisting = !ce ? coinSeg->existing(coinTe, nullptr) : nullptr;
caryclark30b9fdd2016-08-31 14:36:29 -07001814 RETURN_FALSE_IF(csExisting && csExisting == ceExisting, coinSeg);
1815 RETURN_FALSE_IF(csExisting && (csExisting == ce ||
1816 csExisting->contains(ceExisting ? ceExisting : ce)), coinSeg);
1817 RETURN_FALSE_IF(ceExisting && (ceExisting == cs ||
1818 ceExisting->contains(csExisting ? csExisting : cs)), coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001819 const SkOpPtT* osExisting = !os ? oppSeg->existing(oppTs, nullptr) : nullptr;
1820 const SkOpPtT* oeExisting = !oe ? oppSeg->existing(oppTe, nullptr) : nullptr;
caryclark30b9fdd2016-08-31 14:36:29 -07001821 RETURN_FALSE_IF(osExisting && osExisting == oeExisting, oppSeg);
1822 RETURN_FALSE_IF(osExisting && (osExisting == oe ||
1823 osExisting->contains(oeExisting ? oeExisting : oe)), oppSeg);
1824 RETURN_FALSE_IF(oeExisting && (oeExisting == os ||
1825 oeExisting->contains(osExisting ? osExisting : os)), oppSeg);
caryclark55888e42016-07-18 10:01:36 -07001826 bool csDeleted = false, osDeleted = false, ceDeleted = false, oeDeleted = false;
1827 this->debugValidate();
1828 if (!cs || !os) {
1829 if (!cs)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001830 cs = coinSeg->debugAddT(coinTs, log);
caryclark55888e42016-07-18 10:01:36 -07001831 if (!os)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001832 os = oppSeg->debugAddT(oppTs, log);
caryclark30b9fdd2016-08-31 14:36:29 -07001833// RETURN_FALSE_IF(callerAborts, !csWritable || !osWritable);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001834 if (cs && os) cs->span()->debugAddOpp(log, os->span());
caryclark55888e42016-07-18 10:01:36 -07001835// cs = csWritable;
caryclark30b9fdd2016-08-31 14:36:29 -07001836// os = osWritable->active();
1837 RETURN_FALSE_IF((ce && ce->deleted()) || (oe && oe->deleted()), coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001838 }
1839 if (!ce || !oe) {
1840 if (!ce)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001841 ce = coinSeg->debugAddT(coinTe, log);
caryclark55888e42016-07-18 10:01:36 -07001842 if (!oe)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001843 oe = oppSeg->debugAddT(oppTe, log);
1844 if (ce && oe) ce->span()->debugAddOpp(log, oe->span());
caryclark55888e42016-07-18 10:01:36 -07001845// ce = ceWritable;
1846// oe = oeWritable;
1847 }
1848 this->debugValidate();
caryclark30b9fdd2016-08-31 14:36:29 -07001849 RETURN_FALSE_IF(csDeleted, coinSeg);
Ben Wagner63fd7602017-10-09 15:45:33 -04001850 RETURN_FALSE_IF(osDeleted, oppSeg);
1851 RETURN_FALSE_IF(ceDeleted, coinSeg);
caryclark30b9fdd2016-08-31 14:36:29 -07001852 RETURN_FALSE_IF(oeDeleted, oppSeg);
caryclark8016b262016-09-06 05:59:47 -07001853 RETURN_FALSE_IF(!cs || !ce || cs == ce || cs->contains(ce) || !os || !oe || os == oe || os->contains(oe), coinSeg);
1854 bool result = true;
caryclark55888e42016-07-18 10:01:36 -07001855 if (overlap) {
1856 if (overlap->coinPtTStart()->segment() == coinSeg) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001857 log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
caryclark55888e42016-07-18 10:01:36 -07001858 } else {
1859 if (oppTs > oppTe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001860 using std::swap;
1861 swap(coinTs, coinTe);
1862 swap(oppTs, oppTe);
caryclark55888e42016-07-18 10:01:36 -07001863 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001864 log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, oppSeg, oppTs, oppTe, coinSeg, coinTs, coinTe);
caryclark55888e42016-07-18 10:01:36 -07001865 }
caryclark8016b262016-09-06 05:59:47 -07001866#if 0 && DEBUG_COINCIDENCE_VERBOSE
1867 if (result) {
1868 overlap->debugShow();
1869 }
caryclark55888e42016-07-18 10:01:36 -07001870#endif
1871 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001872 log->record(SkPathOpsDebug::kAddMissingCoin_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
caryclark8016b262016-09-06 05:59:47 -07001873#if 0 && DEBUG_COINCIDENCE_VERBOSE
1874 fHead->debugShow();
caryclark55888e42016-07-18 10:01:36 -07001875#endif
1876 }
1877 this->debugValidate();
caryclark8016b262016-09-06 05:59:47 -07001878 return (void) result;
caryclark55888e42016-07-18 10:01:36 -07001879}
1880
1881// Extra commented-out lines keep this in sync with addMissing()
1882/* detects overlaps of different coincident runs on same segment */
1883/* does not detect overlaps for pairs without any segments in common */
1884// returns true if caller should loop again
Cary Clarkab87d7a2016-10-04 10:01:04 -04001885void SkOpCoincidence::debugAddMissing(SkPathOpsDebug::GlitchLog* log, bool* added) const {
caryclark26ad22a2015-10-16 09:03:38 -07001886 const SkCoincidentSpans* outer = fHead;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001887 *added = false;
caryclark26ad22a2015-10-16 09:03:38 -07001888 if (!outer) {
1889 return;
1890 }
caryclark55888e42016-07-18 10:01:36 -07001891 // fTop = outer;
1892 // fHead = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -07001893 do {
1894 // addifmissing can modify the list that this is walking
1895 // save head so that walker can iterate over old data unperturbed
1896 // addifmissing adds to head freely then add saved head in the end
caryclark8016b262016-09-06 05:59:47 -07001897 const SkOpPtT* ocs = outer->coinPtTStart();
1898 SkASSERT(!ocs->deleted());
1899 const SkOpSegment* outerCoin = ocs->segment();
1900 SkASSERT(!outerCoin->done()); // if it's done, should have already been removed from list
1901 const SkOpPtT* oos = outer->oppPtTStart();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001902 if (oos->deleted()) {
1903 return;
1904 }
caryclark8016b262016-09-06 05:59:47 -07001905 const SkOpSegment* outerOpp = oos->segment();
1906 SkASSERT(!outerOpp->done());
1907// SkOpSegment* outerCoinWritable = const_cast<SkOpSegment*>(outerCoin);
1908// SkOpSegment* outerOppWritable = const_cast<SkOpSegment*>(outerOpp);
caryclark26ad22a2015-10-16 09:03:38 -07001909 const SkCoincidentSpans* inner = outer;
caryclark55888e42016-07-18 10:01:36 -07001910 while ((inner = inner->next())) {
1911 this->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001912 double overS, overE;
caryclark8016b262016-09-06 05:59:47 -07001913 const SkOpPtT* ics = inner->coinPtTStart();
1914 SkASSERT(!ics->deleted());
1915 const SkOpSegment* innerCoin = ics->segment();
1916 SkASSERT(!innerCoin->done());
1917 const SkOpPtT* ios = inner->oppPtTStart();
1918 SkASSERT(!ios->deleted());
1919 const SkOpSegment* innerOpp = ios->segment();
1920 SkASSERT(!innerOpp->done());
1921// SkOpSegment* innerCoinWritable = const_cast<SkOpSegment*>(innerCoin);
1922// SkOpSegment* innerOppWritable = const_cast<SkOpSegment*>(innerOpp);
caryclark55888e42016-07-18 10:01:36 -07001923 if (outerCoin == innerCoin) {
caryclark8016b262016-09-06 05:59:47 -07001924 const SkOpPtT* oce = outer->coinPtTEnd();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001925 if (oce->deleted()) {
1926 return;
1927 }
caryclark8016b262016-09-06 05:59:47 -07001928 const SkOpPtT* ice = inner->coinPtTEnd();
1929 SkASSERT(!ice->deleted());
1930 if (outerOpp != innerOpp && this->overlap(ocs, oce, ics, ice, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001931 this->debugAddIfMissing(log, ocs->starter(oce), ics->starter(ice),
caryclark81a478c2016-09-09 09:37:57 -07001932 overS, overE, outerOpp, innerOpp, added,
caryclark8016b262016-09-06 05:59:47 -07001933 ocs->debugEnder(oce),
1934 ics->debugEnder(ice));
caryclark26ad22a2015-10-16 09:03:38 -07001935 }
caryclark55888e42016-07-18 10:01:36 -07001936 } else if (outerCoin == innerOpp) {
caryclark8016b262016-09-06 05:59:47 -07001937 const SkOpPtT* oce = outer->coinPtTEnd();
1938 SkASSERT(!oce->deleted());
1939 const SkOpPtT* ioe = inner->oppPtTEnd();
1940 SkASSERT(!ioe->deleted());
1941 if (outerOpp != innerCoin && this->overlap(ocs, oce, ios, ioe, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001942 this->debugAddIfMissing(log, ocs->starter(oce), ios->starter(ioe),
caryclark81a478c2016-09-09 09:37:57 -07001943 overS, overE, outerOpp, innerCoin, added,
caryclark8016b262016-09-06 05:59:47 -07001944 ocs->debugEnder(oce),
1945 ios->debugEnder(ioe));
caryclark26ad22a2015-10-16 09:03:38 -07001946 }
caryclark55888e42016-07-18 10:01:36 -07001947 } else if (outerOpp == innerCoin) {
caryclark8016b262016-09-06 05:59:47 -07001948 const SkOpPtT* ooe = outer->oppPtTEnd();
1949 SkASSERT(!ooe->deleted());
1950 const SkOpPtT* ice = inner->coinPtTEnd();
1951 SkASSERT(!ice->deleted());
caryclark55888e42016-07-18 10:01:36 -07001952 SkASSERT(outerCoin != innerOpp);
caryclark8016b262016-09-06 05:59:47 -07001953 if (this->overlap(oos, ooe, ics, ice, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001954 this->debugAddIfMissing(log, oos->starter(ooe), ics->starter(ice),
caryclark81a478c2016-09-09 09:37:57 -07001955 overS, overE, outerCoin, innerOpp, added,
caryclark8016b262016-09-06 05:59:47 -07001956 oos->debugEnder(ooe),
1957 ics->debugEnder(ice));
caryclark26ad22a2015-10-16 09:03:38 -07001958 }
caryclark55888e42016-07-18 10:01:36 -07001959 } else if (outerOpp == innerOpp) {
caryclark8016b262016-09-06 05:59:47 -07001960 const SkOpPtT* ooe = outer->oppPtTEnd();
1961 SkASSERT(!ooe->deleted());
1962 const SkOpPtT* ioe = inner->oppPtTEnd();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001963 if (ioe->deleted()) {
1964 return;
1965 }
caryclark55888e42016-07-18 10:01:36 -07001966 SkASSERT(outerCoin != innerCoin);
caryclark8016b262016-09-06 05:59:47 -07001967 if (this->overlap(oos, ooe, ios, ioe, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001968 this->debugAddIfMissing(log, oos->starter(ooe), ios->starter(ioe),
caryclark81a478c2016-09-09 09:37:57 -07001969 overS, overE, outerCoin, innerCoin, added,
caryclark8016b262016-09-06 05:59:47 -07001970 oos->debugEnder(ooe),
1971 ios->debugEnder(ioe));
caryclark26ad22a2015-10-16 09:03:38 -07001972 }
1973 }
caryclark55888e42016-07-18 10:01:36 -07001974 this->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001975 }
caryclark55888e42016-07-18 10:01:36 -07001976 } while ((outer = outer->next()));
1977 // this->restoreHead();
1978 return;
caryclark26ad22a2015-10-16 09:03:38 -07001979}
1980
caryclark55888e42016-07-18 10:01:36 -07001981// Commented-out lines keep this in sync with release()
Cary Clarkab87d7a2016-10-04 10:01:04 -04001982void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkCoincidentSpans* remove) const {
caryclark30b9fdd2016-08-31 14:36:29 -07001983 const SkCoincidentSpans* head = coin;
1984 const SkCoincidentSpans* prev = nullptr;
1985 const SkCoincidentSpans* next;
1986 do {
1987 next = coin->next();
1988 if (coin == remove) {
1989 if (prev) {
1990// prev->setNext(next);
1991 } else if (head == fHead) {
1992// fHead = next;
1993 } else {
1994// fTop = next;
1995 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001996 log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
caryclark30b9fdd2016-08-31 14:36:29 -07001997 }
1998 prev = coin;
1999 } while ((coin = next));
2000 return;
2001}
2002
Cary Clarkab87d7a2016-10-04 10:01:04 -04002003void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* deleted) const {
caryclark55888e42016-07-18 10:01:36 -07002004 const SkCoincidentSpans* coin = fHead;
2005 if (!coin) {
2006 return;
2007 }
2008 do {
2009 if (coin->coinPtTStart()->segment() == deleted
2010 || coin->coinPtTEnd()->segment() == deleted
2011 || coin->oppPtTStart()->segment() == deleted
2012 || coin->oppPtTEnd()->segment() == deleted) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002013 log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07002014 }
2015 } while ((coin = coin->next()));
2016}
2017
caryclark55888e42016-07-18 10:01:36 -07002018// Commented-out lines keep this in sync with expand()
2019// expand the range by checking adjacent spans for coincidence
Cary Clarkab87d7a2016-10-04 10:01:04 -04002020bool SkOpCoincidence::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07002021 const SkCoincidentSpans* coin = fHead;
2022 if (!coin) {
2023 return false;
2024 }
2025 bool expanded = false;
2026 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002027 if (coin->debugExpand(log)) {
caryclark55888e42016-07-18 10:01:36 -07002028 // check to see if multiple spans expanded so they are now identical
2029 const SkCoincidentSpans* test = fHead;
2030 do {
2031 if (coin == test) {
2032 continue;
2033 }
2034 if (coin->coinPtTStart() == test->coinPtTStart()
2035 && coin->oppPtTStart() == test->oppPtTStart()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002036 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, fHead, test->coinPtTStart());
caryclark55888e42016-07-18 10:01:36 -07002037 break;
2038 }
2039 } while ((test = test->next()));
2040 expanded = true;
caryclark26ad22a2015-10-16 09:03:38 -07002041 }
caryclark55888e42016-07-18 10:01:36 -07002042 } while ((coin = coin->next()));
caryclark26ad22a2015-10-16 09:03:38 -07002043 return expanded;
2044}
2045
caryclark55888e42016-07-18 10:01:36 -07002046// Commented-out lines keep this in sync with mark()
2047/* this sets up the coincidence links in the segments when the coincidence crosses multiple spans */
Cary Clarkab87d7a2016-10-04 10:01:04 -04002048void SkOpCoincidence::debugMark(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07002049 const SkCoincidentSpans* coin = fHead;
2050 if (!coin) {
2051 return;
2052 }
2053 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002054 FAIL_IF(!coin->coinPtTStartWritable()->span()->upCastable(), coin);
caryclark55888e42016-07-18 10:01:36 -07002055 const SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast();
2056// SkASSERT(start->deleted());
2057 const SkOpSpanBase* end = coin->coinPtTEndWritable()->span();
2058// SkASSERT(end->deleted());
2059 const SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span();
2060// SkASSERT(oStart->deleted());
2061 const SkOpSpanBase* oEnd = coin->oppPtTEndWritable()->span();
2062// SkASSERT(oEnd->deleted());
2063 bool flipped = coin->flipped();
caryclark26ad22a2015-10-16 09:03:38 -07002064 if (flipped) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002065 using std::swap;
2066 swap(oStart, oEnd);
caryclark26ad22a2015-10-16 09:03:38 -07002067 }
caryclark55888e42016-07-18 10:01:36 -07002068 /* coin and opp spans may not match up. Mark the ends, and then let the interior
2069 get marked as many times as the spans allow */
Cary Clarkab87d7a2016-10-04 10:01:04 -04002070 start->debugInsertCoincidence(log, oStart->upCast());
2071 end->debugInsertCoinEnd(log, oEnd);
caryclark55888e42016-07-18 10:01:36 -07002072 const SkOpSegment* segment = start->segment();
2073 const SkOpSegment* oSegment = oStart->segment();
caryclark26ad22a2015-10-16 09:03:38 -07002074 const SkOpSpanBase* next = start;
2075 const SkOpSpanBase* oNext = oStart;
caryclarka35ab3e2016-10-20 08:32:18 -07002076 bool ordered;
2077 FAIL_IF(!coin->ordered(&ordered), coin);
caryclark55888e42016-07-18 10:01:36 -07002078 while ((next = next->upCast()->next()) != end) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002079 FAIL_IF(!next->upCastable(), coin);
2080 if (next->upCast()->debugInsertCoincidence(log, oSegment, flipped, ordered), false) {
caryclark55888e42016-07-18 10:01:36 -07002081 return;
caryclark26ad22a2015-10-16 09:03:38 -07002082 }
caryclark55888e42016-07-18 10:01:36 -07002083 }
2084 while ((oNext = oNext->upCast()->next()) != oEnd) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002085 FAIL_IF(!oNext->upCastable(), coin);
2086 if (oNext->upCast()->debugInsertCoincidence(log, segment, flipped, ordered), false) {
caryclark55888e42016-07-18 10:01:36 -07002087 return;
caryclark26ad22a2015-10-16 09:03:38 -07002088 }
caryclark55888e42016-07-18 10:01:36 -07002089 }
2090 } while ((coin = coin->next()));
2091 return;
caryclark26ad22a2015-10-16 09:03:38 -07002092}
2093#endif
2094
Cary Clarkab87d7a2016-10-04 10:01:04 -04002095#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002096// Commented-out lines keep this in sync with markCollapsed()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002097void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkOpPtT* test) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002098 const SkCoincidentSpans* head = coin;
caryclark55888e42016-07-18 10:01:36 -07002099 while (coin) {
2100 if (coin->collapsed(test)) {
2101 if (zero_or_one(coin->coinPtTStart()->fT) && zero_or_one(coin->coinPtTEnd()->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002102 log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07002103 }
2104 if (zero_or_one(coin->oppPtTStart()->fT) && zero_or_one(coin->oppPtTEnd()->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002105 log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07002106 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002107 this->debugRelease(log, head, coin);
caryclark55888e42016-07-18 10:01:36 -07002108 }
2109 coin = coin->next();
caryclark624637c2015-05-11 07:21:27 -07002110 }
2111}
2112
caryclark55888e42016-07-18 10:01:36 -07002113// Commented-out lines keep this in sync with markCollapsed()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002114void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* test) const {
2115 this->debugMarkCollapsed(log, fHead, test);
2116 this->debugMarkCollapsed(log, fTop, test);
caryclark55888e42016-07-18 10:01:36 -07002117}
2118#endif
2119
2120void SkCoincidentSpans::debugShow() const {
caryclark6c3b9cd2016-09-26 05:36:58 -07002121 SkDebugf("coinSpan - id=%d t=%1.9g tEnd=%1.9g\n", coinPtTStart()->segment()->debugID(),
caryclark55888e42016-07-18 10:01:36 -07002122 coinPtTStart()->fT, coinPtTEnd()->fT);
caryclark6c3b9cd2016-09-26 05:36:58 -07002123 SkDebugf("coinSpan + id=%d t=%1.9g tEnd=%1.9g\n", oppPtTStart()->segment()->debugID(),
caryclark55888e42016-07-18 10:01:36 -07002124 oppPtTStart()->fT, oppPtTEnd()->fT);
2125}
2126
2127void SkOpCoincidence::debugShowCoincidence() const {
caryclark26ad22a2015-10-16 09:03:38 -07002128#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -07002129 const SkCoincidentSpans* span = fHead;
2130 while (span) {
2131 span->debugShow();
2132 span = span->next();
2133 }
2134#endif
2135}
2136
Cary Clarkab87d7a2016-10-04 10:01:04 -04002137#if DEBUG_COIN
caryclark6c3b9cd2016-09-26 05:36:58 -07002138static void DebugCheckBetween(const SkOpSpanBase* next, const SkOpSpanBase* end,
caryclark55888e42016-07-18 10:01:36 -07002139 double oStart, double oEnd, const SkOpSegment* oSegment,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002140 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002141 SkASSERT(next != end);
2142 SkASSERT(!next->contains(end) || log);
2143 if (next->t() > end->t()) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002144 using std::swap;
2145 swap(next, end);
caryclark55888e42016-07-18 10:01:36 -07002146 }
2147 do {
2148 const SkOpPtT* ptT = next->ptT();
2149 int index = 0;
caryclark27c015d2016-09-23 05:47:20 -07002150 bool somethingBetween = false;
caryclark55888e42016-07-18 10:01:36 -07002151 do {
2152 ++index;
2153 ptT = ptT->next();
2154 const SkOpPtT* checkPtT = next->ptT();
2155 if (ptT == checkPtT) {
2156 break;
2157 }
2158 bool looped = false;
2159 for (int check = 0; check < index; ++check) {
2160 if ((looped = checkPtT == ptT)) {
2161 break;
2162 }
2163 checkPtT = checkPtT->next();
2164 }
2165 if (looped) {
2166 SkASSERT(0);
2167 break;
2168 }
2169 if (ptT->deleted()) {
2170 continue;
2171 }
2172 if (ptT->segment() != oSegment) {
2173 continue;
2174 }
2175 somethingBetween |= between(oStart, ptT->fT, oEnd);
2176 } while (true);
2177 SkASSERT(somethingBetween);
2178 } while (next != end && (next = next->upCast()->next()));
2179}
2180
2181static void DebugCheckOverlap(const SkCoincidentSpans* test, const SkCoincidentSpans* list,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002182 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002183 if (!list) {
2184 return;
2185 }
2186 const SkOpSegment* coinSeg = test->coinPtTStart()->segment();
2187 SkASSERT(coinSeg == test->coinPtTEnd()->segment());
2188 const SkOpSegment* oppSeg = test->oppPtTStart()->segment();
2189 SkASSERT(oppSeg == test->oppPtTEnd()->segment());
2190 SkASSERT(coinSeg != test->oppPtTStart()->segment());
2191 SkDEBUGCODE(double tcs = test->coinPtTStart()->fT);
2192 SkASSERT(between(0, tcs, 1));
2193 SkDEBUGCODE(double tce = test->coinPtTEnd()->fT);
2194 SkASSERT(between(0, tce, 1));
2195 SkASSERT(tcs < tce);
2196 double tos = test->oppPtTStart()->fT;
2197 SkASSERT(between(0, tos, 1));
2198 double toe = test->oppPtTEnd()->fT;
2199 SkASSERT(between(0, toe, 1));
2200 SkASSERT(tos != toe);
2201 if (tos > toe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002202 using std::swap;
2203 swap(tos, toe);
caryclark55888e42016-07-18 10:01:36 -07002204 }
2205 do {
2206 double lcs, lce, los, loe;
2207 if (coinSeg == list->coinPtTStart()->segment()) {
2208 if (oppSeg != list->oppPtTStart()->segment()) {
2209 continue;
2210 }
2211 lcs = list->coinPtTStart()->fT;
2212 lce = list->coinPtTEnd()->fT;
2213 los = list->oppPtTStart()->fT;
2214 loe = list->oppPtTEnd()->fT;
2215 if (los > loe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002216 using std::swap;
2217 swap(los, loe);
caryclark55888e42016-07-18 10:01:36 -07002218 }
2219 } else if (coinSeg == list->oppPtTStart()->segment()) {
2220 if (oppSeg != list->coinPtTStart()->segment()) {
2221 continue;
2222 }
2223 lcs = list->oppPtTStart()->fT;
2224 lce = list->oppPtTEnd()->fT;
2225 if (lcs > lce) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002226 using std::swap;
2227 swap(lcs, lce);
caryclark55888e42016-07-18 10:01:36 -07002228 }
2229 los = list->coinPtTStart()->fT;
2230 loe = list->coinPtTEnd()->fT;
2231 } else {
2232 continue;
2233 }
2234 SkASSERT(tce < lcs || lce < tcs);
2235 SkASSERT(toe < los || loe < tos);
2236 } while ((list = list->next()));
2237}
2238
2239
2240static void DebugCheckOverlapTop(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002241 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002242 // check for overlapping coincident spans
2243 const SkCoincidentSpans* test = head;
2244 while (test) {
2245 const SkCoincidentSpans* next = test->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04002246 DebugCheckOverlap(test, next, log);
2247 DebugCheckOverlap(test, opt, log);
caryclark55888e42016-07-18 10:01:36 -07002248 test = next;
2249 }
2250}
2251
caryclark55888e42016-07-18 10:01:36 -07002252static void DebugValidate(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002253 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002254 // look for pts inside coincident spans that are not inside the opposite spans
2255 const SkCoincidentSpans* coin = head;
2256 while (coin) {
2257 SkASSERT(SkOpCoincidence::Ordered(coin->coinPtTStart()->segment(),
2258 coin->oppPtTStart()->segment()));
2259 SkASSERT(coin->coinPtTStart()->span()->ptT() == coin->coinPtTStart());
2260 SkASSERT(coin->coinPtTEnd()->span()->ptT() == coin->coinPtTEnd());
2261 SkASSERT(coin->oppPtTStart()->span()->ptT() == coin->oppPtTStart());
2262 SkASSERT(coin->oppPtTEnd()->span()->ptT() == coin->oppPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07002263 coin = coin->next();
2264 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002265 DebugCheckOverlapTop(head, opt, log);
caryclark55888e42016-07-18 10:01:36 -07002266}
2267#endif
2268
2269void SkOpCoincidence::debugValidate() const {
2270#if DEBUG_COINCIDENCE
Cary Clarkab87d7a2016-10-04 10:01:04 -04002271 DebugValidate(fHead, fTop, nullptr);
2272 DebugValidate(fTop, nullptr, nullptr);
caryclark55888e42016-07-18 10:01:36 -07002273#endif
2274}
2275
Cary Clarkab87d7a2016-10-04 10:01:04 -04002276#if DEBUG_COIN
caryclark6c3b9cd2016-09-26 05:36:58 -07002277static void DebugCheckBetween(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002278 SkPathOpsDebug::GlitchLog* log) {
caryclark6c3b9cd2016-09-26 05:36:58 -07002279 // look for pts inside coincident spans that are not inside the opposite spans
2280 const SkCoincidentSpans* coin = head;
2281 while (coin) {
2282 DebugCheckBetween(coin->coinPtTStart()->span(), coin->coinPtTEnd()->span(),
2283 coin->oppPtTStart()->fT, coin->oppPtTEnd()->fT, coin->oppPtTStart()->segment(),
Cary Clarkab87d7a2016-10-04 10:01:04 -04002284 log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002285 DebugCheckBetween(coin->oppPtTStart()->span(), coin->oppPtTEnd()->span(),
2286 coin->coinPtTStart()->fT, coin->coinPtTEnd()->fT, coin->coinPtTStart()->segment(),
Cary Clarkab87d7a2016-10-04 10:01:04 -04002287 log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002288 coin = coin->next();
2289 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002290 DebugCheckOverlapTop(head, opt, log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002291}
2292#endif
2293
2294void SkOpCoincidence::debugCheckBetween() const {
2295#if DEBUG_COINCIDENCE
2296 if (fGlobalState->debugCheckHealth()) {
2297 return;
2298 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002299 DebugCheckBetween(fHead, fTop, nullptr);
2300 DebugCheckBetween(fTop, nullptr, nullptr);
caryclark6c3b9cd2016-09-26 05:36:58 -07002301#endif
2302}
2303
Cary Clarkab87d7a2016-10-04 10:01:04 -04002304#if DEBUG_COIN
2305void SkOpContour::debugCheckHealth(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07002306 const SkOpSegment* segment = &fHead;
2307 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002308 segment->debugCheckHealth(log);
caryclark26ad22a2015-10-16 09:03:38 -07002309 } while ((segment = segment->next()));
2310}
2311
Cary Clarkab87d7a2016-10-04 10:01:04 -04002312void SkOpCoincidence::debugCheckValid(SkPathOpsDebug::GlitchLog* log) const {
2313#if DEBUG_VALIDATE
2314 DebugValidate(fHead, fTop, log);
2315 DebugValidate(fTop, nullptr, log);
2316#endif
2317}
2318
2319void SkOpCoincidence::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
2320 const SkCoincidentSpans* coin = fHead;
2321 if (!coin) {
2322 return;
2323 }
2324 do {
2325 coin->debugCorrectEnds(log);
2326 } while ((coin = coin->next()));
2327}
2328
caryclark55888e42016-07-18 10:01:36 -07002329// commmented-out lines keep this aligned with missingCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002330void SkOpContour::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07002331// SkASSERT(fCount > 0);
caryclark26ad22a2015-10-16 09:03:38 -07002332 const SkOpSegment* segment = &fHead;
caryclark55888e42016-07-18 10:01:36 -07002333// bool result = false;
caryclark26ad22a2015-10-16 09:03:38 -07002334 do {
Cary Clarke47ae292016-10-05 08:51:39 -04002335 if (segment->debugMissingCoincidence(log), false) {
caryclark55888e42016-07-18 10:01:36 -07002336// result = true;
caryclark55888e42016-07-18 10:01:36 -07002337 }
2338 segment = segment->next();
2339 } while (segment);
2340 return;
caryclark26ad22a2015-10-16 09:03:38 -07002341}
Cary Clarkab87d7a2016-10-04 10:01:04 -04002342
2343void SkOpContour::debugMoveMultiples(SkPathOpsDebug::GlitchLog* log) const {
2344 SkASSERT(fCount > 0);
2345 const SkOpSegment* segment = &fHead;
2346 do {
2347 if (segment->debugMoveMultiples(log), false) {
2348 return;
2349 }
2350 } while ((segment = segment->next()));
2351 return;
2352}
2353
2354void SkOpContour::debugMoveNearby(SkPathOpsDebug::GlitchLog* log) const {
2355 SkASSERT(fCount > 0);
2356 const SkOpSegment* segment = &fHead;
2357 do {
2358 segment->debugMoveNearby(log);
2359 } while ((segment = segment->next()));
2360}
caryclark26ad22a2015-10-16 09:03:38 -07002361#endif
2362
caryclark025b11e2016-08-25 05:21:14 -07002363#if DEBUG_COINCIDENCE_ORDER
2364void SkOpSegment::debugResetCoinT() const {
2365 fDebugBaseIndex = -1;
2366 fDebugBaseMin = 1;
2367 fDebugBaseMax = -1;
2368 fDebugLastIndex = -1;
2369 fDebugLastMin = 1;
2370 fDebugLastMax = -1;
2371}
2372#endif
2373
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002374void SkOpSegment::debugValidate() const {
caryclark025b11e2016-08-25 05:21:14 -07002375#if DEBUG_COINCIDENCE_ORDER
2376 {
2377 const SkOpSpanBase* span = &fHead;
2378 do {
2379 span->debugResetCoinT();
2380 } while (!span->final() && (span = span->upCast()->next()));
2381 span = &fHead;
2382 int index = 0;
2383 do {
2384 span->debugSetCoinT(index++);
2385 } while (!span->final() && (span = span->upCast()->next()));
2386 }
2387#endif
caryclark55888e42016-07-18 10:01:36 -07002388#if DEBUG_COINCIDENCE
2389 if (this->globalState()->debugCheckHealth()) {
2390 return;
2391 }
2392#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002393#if DEBUG_VALIDATE
caryclark54359292015-03-26 07:52:43 -07002394 const SkOpSpanBase* span = &fHead;
2395 double lastT = -1;
halcanary96fcdcc2015-08-27 07:41:13 -07002396 const SkOpSpanBase* prev = nullptr;
caryclark54359292015-03-26 07:52:43 -07002397 int count = 0;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002398 int done = 0;
caryclark54359292015-03-26 07:52:43 -07002399 do {
2400 if (!span->final()) {
2401 ++count;
2402 done += span->upCast()->done() ? 1 : 0;
2403 }
2404 SkASSERT(span->segment() == this);
2405 SkASSERT(!prev || prev->upCast()->next() == span);
2406 SkASSERT(!prev || prev == span->prev());
2407 prev = span;
2408 double t = span->ptT()->fT;
2409 SkASSERT(lastT < t);
2410 lastT = t;
2411 span->debugValidate();
2412 } while (!span->final() && (span = span->upCast()->next()));
2413 SkASSERT(count == fCount);
2414 SkASSERT(done == fDoneCount);
caryclark08bc8482015-04-24 09:08:57 -07002415 SkASSERT(count >= fDoneCount);
caryclark54359292015-03-26 07:52:43 -07002416 SkASSERT(span->final());
2417 span->debugValidate();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002418#endif
caryclark54359292015-03-26 07:52:43 -07002419}
2420
Cary Clarkab87d7a2016-10-04 10:01:04 -04002421#if DEBUG_COIN
caryclark30b9fdd2016-08-31 14:36:29 -07002422
2423// Commented-out lines keep this in sync with addOpp()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002424void SkOpSpanBase::debugAddOpp(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002425 const SkOpPtT* oppPrev = this->ptT()->oppPrev(opp->ptT());
2426 if (!oppPrev) {
caryclark55888e42016-07-18 10:01:36 -07002427 return;
caryclark26ad22a2015-10-16 09:03:38 -07002428 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002429 this->debugMergeMatches(log, opp);
caryclark30b9fdd2016-08-31 14:36:29 -07002430 this->ptT()->debugAddOpp(opp->ptT(), oppPrev);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002431 this->debugCheckForCollapsedCoincidence(log);
caryclark26ad22a2015-10-16 09:03:38 -07002432}
2433
caryclark55888e42016-07-18 10:01:36 -07002434// Commented-out lines keep this in sync with checkForCollapsedCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002435void SkOpSpanBase::debugCheckForCollapsedCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07002436 const SkOpCoincidence* coins = this->globalState()->coincidence();
2437 if (coins->isEmpty()) {
2438 return;
2439 }
2440// the insert above may have put both ends of a coincident run in the same span
2441// for each coincident ptT in loop; see if its opposite in is also in the loop
2442// this implementation is the motivation for marking that a ptT is referenced by a coincident span
2443 const SkOpPtT* head = this->ptT();
2444 const SkOpPtT* test = head;
caryclark26ad22a2015-10-16 09:03:38 -07002445 do {
caryclark55888e42016-07-18 10:01:36 -07002446 if (!test->coincident()) {
2447 continue;
caryclark26ad22a2015-10-16 09:03:38 -07002448 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002449 coins->debugMarkCollapsed(log, test);
caryclark55888e42016-07-18 10:01:36 -07002450 } while ((test = test->next()) != head);
caryclark26ad22a2015-10-16 09:03:38 -07002451}
caryclark55888e42016-07-18 10:01:36 -07002452#endif
caryclark26ad22a2015-10-16 09:03:38 -07002453
caryclark54359292015-03-26 07:52:43 -07002454bool SkOpSpanBase::debugCoinEndLoopCheck() const {
2455 int loop = 0;
2456 const SkOpSpanBase* next = this;
2457 SkOpSpanBase* nextCoin;
2458 do {
2459 nextCoin = next->fCoinEnd;
2460 SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin);
2461 for (int check = 1; check < loop - 1; ++check) {
2462 const SkOpSpanBase* checkCoin = this->fCoinEnd;
2463 const SkOpSpanBase* innerCoin = checkCoin;
2464 for (int inner = check + 1; inner < loop; ++inner) {
2465 innerCoin = innerCoin->fCoinEnd;
2466 if (checkCoin == innerCoin) {
2467 SkDebugf("*** bad coincident end loop ***\n");
2468 return false;
2469 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002470 }
2471 }
caryclark54359292015-03-26 07:52:43 -07002472 ++loop;
2473 } while ((next = nextCoin) && next != this);
2474 return true;
2475}
2476
Cary Clarkab87d7a2016-10-04 10:01:04 -04002477#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002478// Commented-out lines keep this in sync with insertCoinEnd()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002479void SkOpSpanBase::debugInsertCoinEnd(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* coin) const {
caryclark55888e42016-07-18 10:01:36 -07002480 if (containsCoinEnd(coin)) {
2481// SkASSERT(coin->containsCoinEnd(this));
2482 return;
2483 }
2484 debugValidate();
2485// SkASSERT(this != coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002486 log->record(SkPathOpsDebug::kMarkCoinEnd_Glitch, this, coin);
caryclark55888e42016-07-18 10:01:36 -07002487// coin->fCoinEnd = this->fCoinEnd;
2488// this->fCoinEnd = coinNext;
2489 debugValidate();
2490}
2491
caryclark30b9fdd2016-08-31 14:36:29 -07002492// Commented-out lines keep this in sync with mergeMatches()
2493// Look to see if pt-t linked list contains same segment more than once
2494// if so, and if each pt-t is directly pointed to by spans in that segment,
2495// merge them
2496// keep the points, but remove spans so that the segment doesn't have 2 or more
2497// spans pointing to the same pt-t loop at different loop elements
Cary Clarkab87d7a2016-10-04 10:01:04 -04002498void SkOpSpanBase::debugMergeMatches(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002499 const SkOpPtT* test = &fPtT;
2500 const SkOpPtT* testNext;
2501 const SkOpPtT* stop = test;
2502 do {
2503 testNext = test->next();
2504 if (test->deleted()) {
2505 continue;
2506 }
2507 const SkOpSpanBase* testBase = test->span();
2508 SkASSERT(testBase->ptT() == test);
2509 const SkOpSegment* segment = test->segment();
2510 if (segment->done()) {
2511 continue;
2512 }
2513 const SkOpPtT* inner = opp->ptT();
2514 const SkOpPtT* innerStop = inner;
2515 do {
2516 if (inner->segment() != segment) {
2517 continue;
2518 }
2519 if (inner->deleted()) {
2520 continue;
2521 }
2522 const SkOpSpanBase* innerBase = inner->span();
2523 SkASSERT(innerBase->ptT() == inner);
Ben Wagner63fd7602017-10-09 15:45:33 -04002524 // when the intersection is first detected, the span base is marked if there are
caryclark30b9fdd2016-08-31 14:36:29 -07002525 // more than one point in the intersection.
2526// if (!innerBase->hasMultipleHint() && !testBase->hasMultipleHint()) {
2527 if (!zero_or_one(inner->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002528 log->record(SkPathOpsDebug::kMergeMatches_Glitch, innerBase, test);
caryclark30b9fdd2016-08-31 14:36:29 -07002529 } else {
2530 SkASSERT(inner->fT != test->fT);
2531 if (!zero_or_one(test->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002532 log->record(SkPathOpsDebug::kMergeMatches_Glitch, testBase, inner);
caryclark30b9fdd2016-08-31 14:36:29 -07002533 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002534 log->record(SkPathOpsDebug::kMergeMatches_Glitch, segment);
caryclark30b9fdd2016-08-31 14:36:29 -07002535// SkDEBUGCODE(testBase->debugSetDeleted());
2536// test->setDeleted();
2537// SkDEBUGCODE(innerBase->debugSetDeleted());
2538// inner->setDeleted();
2539 }
2540 }
2541#ifdef SK_DEBUG // assert if another undeleted entry points to segment
2542 const SkOpPtT* debugInner = inner;
2543 while ((debugInner = debugInner->next()) != innerStop) {
2544 if (debugInner->segment() != segment) {
2545 continue;
2546 }
2547 if (debugInner->deleted()) {
2548 continue;
2549 }
2550 SkOPASSERT(0);
2551 }
2552#endif
Ben Wagner63fd7602017-10-09 15:45:33 -04002553 break;
caryclark30b9fdd2016-08-31 14:36:29 -07002554// }
2555 break;
2556 } while ((inner = inner->next()) != innerStop);
2557 } while ((test = testNext) != stop);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002558 this->debugCheckForCollapsedCoincidence(log);
caryclark30b9fdd2016-08-31 14:36:29 -07002559}
2560
caryclark55888e42016-07-18 10:01:36 -07002561#endif
caryclark26ad22a2015-10-16 09:03:38 -07002562
caryclark025b11e2016-08-25 05:21:14 -07002563void SkOpSpanBase::debugResetCoinT() const {
2564#if DEBUG_COINCIDENCE_ORDER
2565 const SkOpPtT* ptT = &fPtT;
2566 do {
2567 ptT->debugResetCoinT();
2568 ptT = ptT->next();
2569 } while (ptT != &fPtT);
2570#endif
2571}
2572
2573void SkOpSpanBase::debugSetCoinT(int index) const {
2574#if DEBUG_COINCIDENCE_ORDER
2575 const SkOpPtT* ptT = &fPtT;
2576 do {
2577 if (!ptT->deleted()) {
2578 ptT->debugSetCoinT(index);
2579 }
2580 ptT = ptT->next();
2581 } while (ptT != &fPtT);
2582#endif
2583}
2584
caryclark26ad22a2015-10-16 09:03:38 -07002585const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const {
2586 const SkOpSpanBase* end = *endPtr;
2587 SkASSERT(this->segment() == end->segment());
2588 const SkOpSpanBase* result;
2589 if (t() < end->t()) {
2590 result = this;
2591 } else {
2592 result = end;
2593 *endPtr = this;
2594 }
2595 return result->upCast();
2596}
2597
caryclark54359292015-03-26 07:52:43 -07002598void SkOpSpanBase::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07002599#if DEBUG_COINCIDENCE
2600 if (this->globalState()->debugCheckHealth()) {
2601 return;
2602 }
2603#endif
caryclark54359292015-03-26 07:52:43 -07002604#if DEBUG_VALIDATE
2605 const SkOpPtT* ptT = &fPtT;
2606 SkASSERT(ptT->span() == this);
2607 do {
2608// SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt));
2609 ptT->debugValidate();
2610 ptT = ptT->next();
2611 } while (ptT != &fPtT);
2612 SkASSERT(this->debugCoinEndLoopCheck());
2613 if (!this->final()) {
2614 SkASSERT(this->upCast()->debugCoinLoopCheck());
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002615 }
caryclark54359292015-03-26 07:52:43 -07002616 if (fFromAngle) {
2617 fFromAngle->debugValidate();
2618 }
2619 if (!this->final() && this->upCast()->toAngle()) {
2620 this->upCast()->toAngle()->debugValidate();
2621 }
2622#endif
2623}
2624
2625bool SkOpSpan::debugCoinLoopCheck() const {
2626 int loop = 0;
2627 const SkOpSpan* next = this;
2628 SkOpSpan* nextCoin;
2629 do {
2630 nextCoin = next->fCoincident;
2631 SkASSERT(nextCoin == this || nextCoin->fCoincident != nextCoin);
2632 for (int check = 1; check < loop - 1; ++check) {
2633 const SkOpSpan* checkCoin = this->fCoincident;
2634 const SkOpSpan* innerCoin = checkCoin;
2635 for (int inner = check + 1; inner < loop; ++inner) {
2636 innerCoin = innerCoin->fCoincident;
2637 if (checkCoin == innerCoin) {
2638 SkDebugf("*** bad coincident loop ***\n");
2639 return false;
2640 }
2641 }
2642 }
2643 ++loop;
2644 } while ((next = nextCoin) && next != this);
2645 return true;
2646}
2647
Cary Clarkab87d7a2016-10-04 10:01:04 -04002648#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002649// Commented-out lines keep this in sync with insertCoincidence() in header
Cary Clarkab87d7a2016-10-04 10:01:04 -04002650void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* coin) const {
caryclark55888e42016-07-18 10:01:36 -07002651 if (containsCoincidence(coin)) {
2652// SkASSERT(coin->containsCoincidence(this));
2653 return;
2654 }
2655 debugValidate();
2656// SkASSERT(this != coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002657 log->record(SkPathOpsDebug::kMarkCoinStart_Glitch, this, coin);
caryclark55888e42016-07-18 10:01:36 -07002658// coin->fCoincident = this->fCoincident;
2659// this->fCoincident = coinNext;
2660 debugValidate();
2661}
2662
2663// Commented-out lines keep this in sync with insertCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002664void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* segment, bool flipped, bool ordered) const {
caryclark55888e42016-07-18 10:01:36 -07002665 if (this->containsCoincidence(segment)) {
2666 return;
2667 }
2668 const SkOpPtT* next = &fPtT;
2669 while ((next = next->next()) != &fPtT) {
2670 if (next->segment() == segment) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002671 const SkOpSpan* span;
2672 const SkOpSpanBase* base = next->span();
2673 if (!ordered) {
2674 const SkOpSpanBase* spanEnd = fNext->contains(segment)->span();
2675 const SkOpPtT* start = base->ptT()->starter(spanEnd->ptT());
2676 FAIL_IF(!start->span()->upCastable(), this);
2677 span = const_cast<SkOpSpan*>(start->span()->upCast());
2678 }
2679 else if (flipped) {
2680 span = base->prev();
2681 FAIL_IF(!span, this);
2682 }
2683 else {
2684 FAIL_IF(!base->upCastable(), this);
2685 span = base->upCast();
2686 }
2687 log->record(SkPathOpsDebug::kMarkCoinInsert_Glitch, span);
caryclark55888e42016-07-18 10:01:36 -07002688 return;
2689 }
2690 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002691#if DEBUG_COIN
2692 log->record(SkPathOpsDebug::kMarkCoinMissing_Glitch, segment, this);
caryclark55888e42016-07-18 10:01:36 -07002693#endif
Cary Clarkab87d7a2016-10-04 10:01:04 -04002694 return;
caryclark55888e42016-07-18 10:01:36 -07002695}
2696#endif
2697
caryclark624637c2015-05-11 07:21:27 -07002698// called only by test code
2699int SkIntersections::debugCoincidentUsed() const {
2700 if (!fIsCoincident[0]) {
2701 SkASSERT(!fIsCoincident[1]);
2702 return 0;
2703 }
2704 int count = 0;
2705 SkDEBUGCODE(int count2 = 0;)
2706 for (int index = 0; index < fUsed; ++index) {
2707 if (fIsCoincident[0] & (1 << index)) {
2708 ++count;
2709 }
2710#ifdef SK_DEBUG
2711 if (fIsCoincident[1] & (1 << index)) {
2712 ++count2;
2713 }
2714#endif
2715 }
2716 SkASSERT(count == count2);
2717 return count;
2718}
2719
caryclark54359292015-03-26 07:52:43 -07002720#include "SkOpContour.h"
2721
caryclark55888e42016-07-18 10:01:36 -07002722// Commented-out lines keep this in sync with addOpp()
caryclark29b25632016-08-25 11:27:17 -07002723void SkOpPtT::debugAddOpp(const SkOpPtT* opp, const SkOpPtT* oppPrev) const {
2724 SkDEBUGCODE(const SkOpPtT* oldNext = this->fNext);
caryclark55888e42016-07-18 10:01:36 -07002725 SkASSERT(this != opp);
2726// this->fNext = opp;
caryclark29b25632016-08-25 11:27:17 -07002727 SkASSERT(oppPrev != oldNext);
caryclark55888e42016-07-18 10:01:36 -07002728// oppPrev->fNext = oldNext;
caryclark55888e42016-07-18 10:01:36 -07002729}
2730
caryclark26ad22a2015-10-16 09:03:38 -07002731bool SkOpPtT::debugContains(const SkOpPtT* check) const {
2732 SkASSERT(this != check);
2733 const SkOpPtT* ptT = this;
2734 int links = 0;
2735 do {
2736 ptT = ptT->next();
2737 if (ptT == check) {
2738 return true;
2739 }
2740 ++links;
2741 const SkOpPtT* test = this;
2742 for (int index = 0; index < links; ++index) {
2743 if (ptT == test) {
2744 return false;
2745 }
2746 test = test->next();
2747 }
2748 } while (true);
2749}
2750
2751const SkOpPtT* SkOpPtT::debugContains(const SkOpSegment* check) const {
2752 SkASSERT(this->segment() != check);
2753 const SkOpPtT* ptT = this;
2754 int links = 0;
2755 do {
2756 ptT = ptT->next();
2757 if (ptT->segment() == check) {
2758 return ptT;
2759 }
2760 ++links;
2761 const SkOpPtT* test = this;
2762 for (int index = 0; index < links; ++index) {
2763 if (ptT == test) {
2764 return nullptr;
2765 }
2766 test = test->next();
2767 }
2768 } while (true);
2769}
2770
caryclark8016b262016-09-06 05:59:47 -07002771const SkOpPtT* SkOpPtT::debugEnder(const SkOpPtT* end) const {
2772 return fT < end->fT ? end : this;
2773}
2774
caryclark54359292015-03-26 07:52:43 -07002775int SkOpPtT::debugLoopLimit(bool report) const {
2776 int loop = 0;
2777 const SkOpPtT* next = this;
2778 do {
2779 for (int check = 1; check < loop - 1; ++check) {
2780 const SkOpPtT* checkPtT = this->fNext;
2781 const SkOpPtT* innerPtT = checkPtT;
2782 for (int inner = check + 1; inner < loop; ++inner) {
2783 innerPtT = innerPtT->fNext;
2784 if (checkPtT == innerPtT) {
2785 if (report) {
2786 SkDebugf("*** bad ptT loop ***\n");
2787 }
2788 return loop;
2789 }
2790 }
2791 }
caryclark26ad22a2015-10-16 09:03:38 -07002792 // there's nothing wrong with extremely large loop counts -- but this may appear to hang
2793 // by taking a very long time to figure out that no loop entry is a duplicate
2794 // -- and it's likely that a large loop count is indicative of a bug somewhere
2795 if (++loop > 1000) {
2796 SkDebugf("*** loop count exceeds 1000 ***\n");
2797 return 1000;
2798 }
caryclark54359292015-03-26 07:52:43 -07002799 } while ((next = next->fNext) && next != this);
2800 return 0;
2801}
2802
caryclark29b25632016-08-25 11:27:17 -07002803const SkOpPtT* SkOpPtT::debugOppPrev(const SkOpPtT* opp) const {
2804 return this->oppPrev(const_cast<SkOpPtT*>(opp));
2805}
2806
caryclark025b11e2016-08-25 05:21:14 -07002807void SkOpPtT::debugResetCoinT() const {
2808#if DEBUG_COINCIDENCE_ORDER
Ben Wagner63fd7602017-10-09 15:45:33 -04002809 this->segment()->debugResetCoinT();
caryclark025b11e2016-08-25 05:21:14 -07002810#endif
2811}
2812
2813void SkOpPtT::debugSetCoinT(int index) const {
2814#if DEBUG_COINCIDENCE_ORDER
Ben Wagner63fd7602017-10-09 15:45:33 -04002815 this->segment()->debugSetCoinT(index, fT);
caryclark025b11e2016-08-25 05:21:14 -07002816#endif
2817}
2818
caryclark54359292015-03-26 07:52:43 -07002819void SkOpPtT::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07002820#if DEBUG_COINCIDENCE
2821 if (this->globalState()->debugCheckHealth()) {
2822 return;
2823 }
2824#endif
caryclark54359292015-03-26 07:52:43 -07002825#if DEBUG_VALIDATE
Cary Clarkab87d7a2016-10-04 10:01:04 -04002826 SkOpPhase phase = contour()->globalState()->phase();
2827 if (phase == SkOpPhase::kIntersecting || phase == SkOpPhase::kFixWinding) {
caryclark54359292015-03-26 07:52:43 -07002828 return;
2829 }
2830 SkASSERT(fNext);
2831 SkASSERT(fNext != this);
2832 SkASSERT(fNext->fNext);
2833 SkASSERT(debugLoopLimit(false) == 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002834#endif
2835}
caryclark1049f122015-04-20 08:31:59 -07002836
2837static void output_scalar(SkScalar num) {
2838 if (num == (int) num) {
2839 SkDebugf("%d", (int) num);
2840 } else {
2841 SkString str;
2842 str.printf("%1.9g", num);
2843 int width = (int) str.size();
2844 const char* cStr = str.c_str();
2845 while (cStr[width - 1] == '0') {
2846 --width;
2847 }
2848 str.resize(width);
2849 SkDebugf("%sf", str.c_str());
2850 }
2851}
2852
2853static void output_points(const SkPoint* pts, int count) {
2854 for (int index = 0; index < count; ++index) {
2855 output_scalar(pts[index].fX);
2856 SkDebugf(", ");
2857 output_scalar(pts[index].fY);
2858 if (index + 1 < count) {
2859 SkDebugf(", ");
2860 }
2861 }
2862}
2863
2864static void showPathContours(SkPath::RawIter& iter, const char* pathName) {
2865 uint8_t verb;
2866 SkPoint pts[4];
2867 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
2868 switch (verb) {
2869 case SkPath::kMove_Verb:
2870 SkDebugf(" %s.moveTo(", pathName);
2871 output_points(&pts[0], 1);
2872 SkDebugf(");\n");
2873 continue;
2874 case SkPath::kLine_Verb:
2875 SkDebugf(" %s.lineTo(", pathName);
2876 output_points(&pts[1], 1);
2877 SkDebugf(");\n");
2878 break;
2879 case SkPath::kQuad_Verb:
2880 SkDebugf(" %s.quadTo(", pathName);
2881 output_points(&pts[1], 2);
2882 SkDebugf(");\n");
2883 break;
2884 case SkPath::kConic_Verb:
2885 SkDebugf(" %s.conicTo(", pathName);
2886 output_points(&pts[1], 2);
2887 SkDebugf(", %1.9gf);\n", iter.conicWeight());
2888 break;
2889 case SkPath::kCubic_Verb:
2890 SkDebugf(" %s.cubicTo(", pathName);
2891 output_points(&pts[1], 3);
2892 SkDebugf(");\n");
2893 break;
2894 case SkPath::kClose_Verb:
2895 SkDebugf(" %s.close();\n", pathName);
2896 break;
2897 default:
2898 SkDEBUGFAIL("bad verb");
2899 return;
2900 }
2901 }
2902}
2903
2904static const char* gFillTypeStr[] = {
2905 "kWinding_FillType",
2906 "kEvenOdd_FillType",
2907 "kInverseWinding_FillType",
2908 "kInverseEvenOdd_FillType"
2909};
2910
2911void SkPathOpsDebug::ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration) {
2912 SkPath::RawIter iter(path);
2913#define SUPPORT_RECT_CONTOUR_DETECTION 0
2914#if SUPPORT_RECT_CONTOUR_DETECTION
halcanary96fcdcc2015-08-27 07:41:13 -07002915 int rectCount = path.isRectContours() ? path.rectContours(nullptr, nullptr) : 0;
caryclark1049f122015-04-20 08:31:59 -07002916 if (rectCount > 0) {
2917 SkTDArray<SkRect> rects;
2918 SkTDArray<SkPath::Direction> directions;
2919 rects.setCount(rectCount);
2920 directions.setCount(rectCount);
2921 path.rectContours(rects.begin(), directions.begin());
2922 for (int contour = 0; contour < rectCount; ++contour) {
2923 const SkRect& rect = rects[contour];
2924 SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
2925 rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
2926 ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
2927 }
2928 return;
2929 }
2930#endif
2931 SkPath::FillType fillType = path.getFillType();
2932 SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType);
2933 if (includeDeclaration) {
2934 SkDebugf(" SkPath %s;\n", name);
2935 }
2936 SkDebugf(" %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[fillType]);
2937 iter.setPath(path);
2938 showPathContours(iter, name);
2939}
caryclark13260682016-10-24 05:10:14 -07002940
Cary Clark918fb1f2016-11-15 13:22:25 -05002941#if DEBUG_DUMP_VERIFY
caryclark13260682016-10-24 05:10:14 -07002942#include "SkData.h"
2943#include "SkStream.h"
2944
2945static void dump_path(FILE* file, const SkPath& path, bool force, bool dumpAsHex) {
2946 SkDynamicMemoryWStream wStream;
2947 path.dump(&wStream, force, dumpAsHex);
2948 sk_sp<SkData> data(wStream.detachAsData());
2949 fprintf(file, "%.*s\n", (int) data->size(), (char*) data->data());
2950}
2951
2952static int dumpID = 0;
2953
2954void SkPathOpsDebug::DumpOp(const SkPath& one, const SkPath& two, SkPathOp op,
2955 const char* testName) {
2956 FILE* file = sk_fopen("op_dump.txt", kWrite_SkFILE_Flag);
2957 DumpOp(file, one, two, op, testName);
2958}
2959
2960void SkPathOpsDebug::DumpOp(FILE* file, const SkPath& one, const SkPath& two, SkPathOp op,
2961 const char* testName) {
2962 const char* name = testName ? testName : "op";
2963 fprintf(file,
2964 "\nstatic void %s_%d(skiatest::Reporter* reporter, const char* filename) {\n",
2965 name, ++dumpID);
2966 fprintf(file, " SkPath path;\n");
2967 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", one.getFillType());
2968 dump_path(file, one, false, true);
2969 fprintf(file, " SkPath path1(path);\n");
2970 fprintf(file, " path.reset();\n");
2971 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", two.getFillType());
2972 dump_path(file, two, false, true);
2973 fprintf(file, " SkPath path2(path);\n");
2974 fprintf(file, " testPathOp(reporter, path1, path2, (SkPathOp) %d, filename);\n", op);
2975 fprintf(file, "}\n\n");
2976 fclose(file);
2977}
2978
2979void SkPathOpsDebug::DumpSimplify(const SkPath& path, const char* testName) {
2980 FILE* file = sk_fopen("simplify_dump.txt", kWrite_SkFILE_Flag);
2981 DumpSimplify(file, path, testName);
2982}
2983
2984void SkPathOpsDebug::DumpSimplify(FILE* file, const SkPath& path, const char* testName) {
2985 const char* name = testName ? testName : "simplify";
2986 fprintf(file,
2987 "\nstatic void %s_%d(skiatest::Reporter* reporter, const char* filename) {\n",
2988 name, ++dumpID);
2989 fprintf(file, " SkPath path;\n");
2990 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", path.getFillType());
2991 dump_path(file, path, false, true);
2992 fprintf(file, " testSimplify(reporter, path, filename);\n");
2993 fprintf(file, "}\n\n");
2994 fclose(file);
2995}
2996
2997#include "SkBitmap.h"
2998#include "SkCanvas.h"
2999#include "SkPaint.h"
3000
3001const int bitWidth = 64;
3002const int bitHeight = 64;
3003
3004static void debug_scale_matrix(const SkPath& one, const SkPath* two, SkMatrix& scale) {
3005 SkRect larger = one.getBounds();
3006 if (two) {
3007 larger.join(two->getBounds());
3008 }
3009 SkScalar largerWidth = larger.width();
3010 if (largerWidth < 4) {
3011 largerWidth = 4;
3012 }
3013 SkScalar largerHeight = larger.height();
3014 if (largerHeight < 4) {
3015 largerHeight = 4;
3016 }
3017 SkScalar hScale = (bitWidth - 2) / largerWidth;
3018 SkScalar vScale = (bitHeight - 2) / largerHeight;
3019 scale.reset();
3020 scale.preScale(hScale, vScale);
3021 larger.fLeft *= hScale;
3022 larger.fRight *= hScale;
3023 larger.fTop *= vScale;
3024 larger.fBottom *= vScale;
3025 SkScalar dx = -16000 > larger.fLeft ? -16000 - larger.fLeft
3026 : 16000 < larger.fRight ? 16000 - larger.fRight : 0;
3027 SkScalar dy = -16000 > larger.fTop ? -16000 - larger.fTop
3028 : 16000 < larger.fBottom ? 16000 - larger.fBottom : 0;
3029 scale.preTranslate(dx, dy);
3030}
3031
3032static int debug_paths_draw_the_same(const SkPath& one, const SkPath& two, SkBitmap& bits) {
3033 if (bits.width() == 0) {
3034 bits.allocN32Pixels(bitWidth * 2, bitHeight);
3035 }
3036 SkCanvas canvas(bits);
3037 canvas.drawColor(SK_ColorWHITE);
3038 SkPaint paint;
3039 canvas.save();
3040 const SkRect& bounds1 = one.getBounds();
3041 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
3042 canvas.drawPath(one, paint);
3043 canvas.restore();
3044 canvas.save();
3045 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
3046 canvas.drawPath(two, paint);
3047 canvas.restore();
3048 int errors = 0;
3049 for (int y = 0; y < bitHeight - 1; ++y) {
3050 uint32_t* addr1 = bits.getAddr32(0, y);
3051 uint32_t* addr2 = bits.getAddr32(0, y + 1);
3052 uint32_t* addr3 = bits.getAddr32(bitWidth, y);
3053 uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1);
3054 for (int x = 0; x < bitWidth - 1; ++x) {
3055 // count 2x2 blocks
3056 bool err = addr1[x] != addr3[x];
3057 if (err) {
3058 errors += addr1[x + 1] != addr3[x + 1]
3059 && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1];
3060 }
3061 }
3062 }
3063 return errors;
3064}
3065
3066void SkPathOpsDebug::ReportOpFail(const SkPath& one, const SkPath& two, SkPathOp op) {
3067 SkDebugf("// Op did not expect failure\n");
3068 DumpOp(stderr, one, two, op, "opTest");
3069 fflush(stderr);
3070}
3071
3072void SkPathOpsDebug::VerifyOp(const SkPath& one, const SkPath& two, SkPathOp op,
3073 const SkPath& result) {
3074 SkPath pathOut, scaledPathOut;
3075 SkRegion rgnA, rgnB, openClip, rgnOut;
3076 openClip.setRect(-16000, -16000, 16000, 16000);
3077 rgnA.setPath(one, openClip);
3078 rgnB.setPath(two, openClip);
3079 rgnOut.op(rgnA, rgnB, (SkRegion::Op) op);
3080 rgnOut.getBoundaryPath(&pathOut);
3081 SkMatrix scale;
3082 debug_scale_matrix(one, &two, scale);
3083 SkRegion scaledRgnA, scaledRgnB, scaledRgnOut;
3084 SkPath scaledA, scaledB;
3085 scaledA.addPath(one, scale);
3086 scaledA.setFillType(one.getFillType());
3087 scaledB.addPath(two, scale);
3088 scaledB.setFillType(two.getFillType());
3089 scaledRgnA.setPath(scaledA, openClip);
3090 scaledRgnB.setPath(scaledB, openClip);
3091 scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) op);
3092 scaledRgnOut.getBoundaryPath(&scaledPathOut);
3093 SkBitmap bitmap;
3094 SkPath scaledOut;
3095 scaledOut.addPath(result, scale);
3096 scaledOut.setFillType(result.getFillType());
3097 int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
3098 const int MAX_ERRORS = 9;
3099 if (errors > MAX_ERRORS) {
3100 fprintf(stderr, "// Op did not expect errors=%d\n", errors);
3101 DumpOp(stderr, one, two, op, "opTest");
3102 fflush(stderr);
3103 }
3104}
3105
3106void SkPathOpsDebug::ReportSimplifyFail(const SkPath& path) {
3107 SkDebugf("// Simplify did not expect failure\n");
3108 DumpSimplify(stderr, path, "simplifyTest");
3109 fflush(stderr);
3110}
3111
3112void SkPathOpsDebug::VerifySimplify(const SkPath& path, const SkPath& result) {
3113 SkPath pathOut, scaledPathOut;
3114 SkRegion rgnA, openClip, rgnOut;
3115 openClip.setRect(-16000, -16000, 16000, 16000);
3116 rgnA.setPath(path, openClip);
3117 rgnOut.getBoundaryPath(&pathOut);
3118 SkMatrix scale;
3119 debug_scale_matrix(path, nullptr, scale);
3120 SkRegion scaledRgnA;
3121 SkPath scaledA;
3122 scaledA.addPath(path, scale);
3123 scaledA.setFillType(path.getFillType());
3124 scaledRgnA.setPath(scaledA, openClip);
3125 scaledRgnA.getBoundaryPath(&scaledPathOut);
3126 SkBitmap bitmap;
3127 SkPath scaledOut;
3128 scaledOut.addPath(result, scale);
3129 scaledOut.setFillType(result.getFillType());
3130 int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
3131 const int MAX_ERRORS = 9;
3132 if (errors > MAX_ERRORS) {
3133 fprintf(stderr, "// Simplify did not expect errors=%d\n", errors);
3134 DumpSimplify(stderr, path, "simplifyTest");
3135 fflush(stderr);
3136 }
3137}
3138
3139#endif
Cary Clarkb8421ed2018-03-14 15:55:02 -04003140
3141// global path dumps for msvs Visual Studio 17 to use from Immediate Window
3142void Dump(const SkPath& path) {
3143 path.dump();
3144}
3145
3146void DumpHex(const SkPath& path) {
3147 path.dumpHex();
3148}