blob: a88f413b002d49e1a9704ce7568f483b96a383e2 [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
Ben Wagner29380bd2017-10-09 14:43:00 -040066
67#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) {
caryclark26ad22a2015-10-16 09:03:38 -0700103 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));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001397 angles.push(next);
1398 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));
1743 if (coinSeg->collapsed(coinTs, coinTe)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001744 return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, coinSeg);
caryclark8016b262016-09-06 05:59:47 -07001745 }
1746 oppTs = TRange(over2s, tStart, oppSeg SkDEBUGPARAMS(over2e));
1747 oppTe = TRange(over2s, tEnd, oppSeg SkDEBUGPARAMS(over2e));
1748 if (oppSeg->collapsed(oppTs, oppTe)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001749 return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, oppSeg);
caryclark8016b262016-09-06 05:59:47 -07001750 }
1751 if (coinTs > coinTe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001752 using std::swap;
1753 swap(coinTs, coinTe);
1754 swap(oppTs, oppTe);
caryclark26ad22a2015-10-16 09:03:38 -07001755 }
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001756 return this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, added);
caryclark26ad22a2015-10-16 09:03:38 -07001757}
1758
caryclark55888e42016-07-18 10:01:36 -07001759/* Commented-out lines keep this in sync addOrOverlap() */
caryclark30b9fdd2016-08-31 14:36:29 -07001760// If this is called by addEndMovedSpans(), a returned false propogates out to an abort.
1761// If this is called by AddIfMissing(), a returned false indicates there was nothing to add
Cary Clarkab87d7a2016-10-04 10:01:04 -04001762void SkOpCoincidence::debugAddOrOverlap(SkPathOpsDebug::GlitchLog* log,
caryclark30b9fdd2016-08-31 14:36:29 -07001763 const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
caryclark81a478c2016-09-09 09:37:57 -07001764 double coinTs, double coinTe, double oppTs, double oppTe, bool* added) const {
caryclark55888e42016-07-18 10:01:36 -07001765 SkTDArray<SkCoincidentSpans*> overlaps;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001766 SkOPASSERT(!fTop); // this is (correctly) reversed in addifMissing()
caryclark81a478c2016-09-09 09:37:57 -07001767 if (fTop && !this->checkOverlap(fTop, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe,
1768 &overlaps)) {
caryclark55888e42016-07-18 10:01:36 -07001769 return;
1770 }
1771 if (fHead && !this->checkOverlap(fHead, coinSeg, oppSeg, coinTs,
1772 coinTe, oppTs, oppTe, &overlaps)) {
1773 return;
1774 }
1775 const SkCoincidentSpans* overlap = overlaps.count() ? overlaps[0] : nullptr;
1776 for (int index = 1; index < overlaps.count(); ++index) { // combine overlaps before continuing
1777 const SkCoincidentSpans* test = overlaps[index];
1778 if (overlap->coinPtTStart()->fT > test->coinPtTStart()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001779 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTStart());
caryclark55888e42016-07-18 10:01:36 -07001780 }
1781 if (overlap->coinPtTEnd()->fT < test->coinPtTEnd()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001782 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07001783 }
1784 if (overlap->flipped()
1785 ? overlap->oppPtTStart()->fT < test->oppPtTStart()->fT
1786 : overlap->oppPtTStart()->fT > test->oppPtTStart()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001787 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTStart());
caryclark55888e42016-07-18 10:01:36 -07001788 }
1789 if (overlap->flipped()
1790 ? overlap->oppPtTEnd()->fT > test->oppPtTEnd()->fT
1791 : overlap->oppPtTEnd()->fT < test->oppPtTEnd()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001792 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07001793 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001794 if (!fHead) { this->debugRelease(log, fHead, test);
1795 this->debugRelease(log, fTop, test);
caryclark55888e42016-07-18 10:01:36 -07001796 }
1797 }
1798 const SkOpPtT* cs = coinSeg->existing(coinTs, oppSeg);
1799 const SkOpPtT* ce = coinSeg->existing(coinTe, oppSeg);
caryclark30b9fdd2016-08-31 14:36:29 -07001800 RETURN_FALSE_IF(overlap && cs && ce && overlap->contains(cs, ce), coinSeg);
1801 RETURN_FALSE_IF(cs != ce || !cs, coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001802 const SkOpPtT* os = oppSeg->existing(oppTs, coinSeg);
1803 const SkOpPtT* oe = oppSeg->existing(oppTe, coinSeg);
caryclark30b9fdd2016-08-31 14:36:29 -07001804 RETURN_FALSE_IF(overlap && os && oe && overlap->contains(os, oe), oppSeg);
caryclark55888e42016-07-18 10:01:36 -07001805 SkASSERT(true || !cs || !cs->deleted());
1806 SkASSERT(true || !os || !os->deleted());
1807 SkASSERT(true || !ce || !ce->deleted());
1808 SkASSERT(true || !oe || !oe->deleted());
1809 const SkOpPtT* csExisting = !cs ? coinSeg->existing(coinTs, nullptr) : nullptr;
1810 const SkOpPtT* ceExisting = !ce ? coinSeg->existing(coinTe, nullptr) : nullptr;
caryclark30b9fdd2016-08-31 14:36:29 -07001811 RETURN_FALSE_IF(csExisting && csExisting == ceExisting, coinSeg);
1812 RETURN_FALSE_IF(csExisting && (csExisting == ce ||
1813 csExisting->contains(ceExisting ? ceExisting : ce)), coinSeg);
1814 RETURN_FALSE_IF(ceExisting && (ceExisting == cs ||
1815 ceExisting->contains(csExisting ? csExisting : cs)), coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001816 const SkOpPtT* osExisting = !os ? oppSeg->existing(oppTs, nullptr) : nullptr;
1817 const SkOpPtT* oeExisting = !oe ? oppSeg->existing(oppTe, nullptr) : nullptr;
caryclark30b9fdd2016-08-31 14:36:29 -07001818 RETURN_FALSE_IF(osExisting && osExisting == oeExisting, oppSeg);
1819 RETURN_FALSE_IF(osExisting && (osExisting == oe ||
1820 osExisting->contains(oeExisting ? oeExisting : oe)), oppSeg);
1821 RETURN_FALSE_IF(oeExisting && (oeExisting == os ||
1822 oeExisting->contains(osExisting ? osExisting : os)), oppSeg);
caryclark55888e42016-07-18 10:01:36 -07001823 bool csDeleted = false, osDeleted = false, ceDeleted = false, oeDeleted = false;
1824 this->debugValidate();
1825 if (!cs || !os) {
1826 if (!cs)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001827 cs = coinSeg->debugAddT(coinTs, log);
caryclark55888e42016-07-18 10:01:36 -07001828 if (!os)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001829 os = oppSeg->debugAddT(oppTs, log);
caryclark30b9fdd2016-08-31 14:36:29 -07001830// RETURN_FALSE_IF(callerAborts, !csWritable || !osWritable);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001831 if (cs && os) cs->span()->debugAddOpp(log, os->span());
caryclark55888e42016-07-18 10:01:36 -07001832// cs = csWritable;
caryclark30b9fdd2016-08-31 14:36:29 -07001833// os = osWritable->active();
1834 RETURN_FALSE_IF((ce && ce->deleted()) || (oe && oe->deleted()), coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001835 }
1836 if (!ce || !oe) {
1837 if (!ce)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001838 ce = coinSeg->debugAddT(coinTe, log);
caryclark55888e42016-07-18 10:01:36 -07001839 if (!oe)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001840 oe = oppSeg->debugAddT(oppTe, log);
1841 if (ce && oe) ce->span()->debugAddOpp(log, oe->span());
caryclark55888e42016-07-18 10:01:36 -07001842// ce = ceWritable;
1843// oe = oeWritable;
1844 }
1845 this->debugValidate();
caryclark30b9fdd2016-08-31 14:36:29 -07001846 RETURN_FALSE_IF(csDeleted, coinSeg);
Ben Wagner63fd7602017-10-09 15:45:33 -04001847 RETURN_FALSE_IF(osDeleted, oppSeg);
1848 RETURN_FALSE_IF(ceDeleted, coinSeg);
caryclark30b9fdd2016-08-31 14:36:29 -07001849 RETURN_FALSE_IF(oeDeleted, oppSeg);
caryclark8016b262016-09-06 05:59:47 -07001850 RETURN_FALSE_IF(!cs || !ce || cs == ce || cs->contains(ce) || !os || !oe || os == oe || os->contains(oe), coinSeg);
1851 bool result = true;
caryclark55888e42016-07-18 10:01:36 -07001852 if (overlap) {
1853 if (overlap->coinPtTStart()->segment() == coinSeg) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001854 log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
caryclark55888e42016-07-18 10:01:36 -07001855 } else {
1856 if (oppTs > oppTe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001857 using std::swap;
1858 swap(coinTs, coinTe);
1859 swap(oppTs, oppTe);
caryclark55888e42016-07-18 10:01:36 -07001860 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001861 log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, oppSeg, oppTs, oppTe, coinSeg, coinTs, coinTe);
caryclark55888e42016-07-18 10:01:36 -07001862 }
caryclark8016b262016-09-06 05:59:47 -07001863#if 0 && DEBUG_COINCIDENCE_VERBOSE
1864 if (result) {
1865 overlap->debugShow();
1866 }
caryclark55888e42016-07-18 10:01:36 -07001867#endif
1868 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001869 log->record(SkPathOpsDebug::kAddMissingCoin_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
caryclark8016b262016-09-06 05:59:47 -07001870#if 0 && DEBUG_COINCIDENCE_VERBOSE
1871 fHead->debugShow();
caryclark55888e42016-07-18 10:01:36 -07001872#endif
1873 }
1874 this->debugValidate();
caryclark8016b262016-09-06 05:59:47 -07001875 return (void) result;
caryclark55888e42016-07-18 10:01:36 -07001876}
1877
1878// Extra commented-out lines keep this in sync with addMissing()
1879/* detects overlaps of different coincident runs on same segment */
1880/* does not detect overlaps for pairs without any segments in common */
1881// returns true if caller should loop again
Cary Clarkab87d7a2016-10-04 10:01:04 -04001882void SkOpCoincidence::debugAddMissing(SkPathOpsDebug::GlitchLog* log, bool* added) const {
caryclark26ad22a2015-10-16 09:03:38 -07001883 const SkCoincidentSpans* outer = fHead;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001884 *added = false;
caryclark26ad22a2015-10-16 09:03:38 -07001885 if (!outer) {
1886 return;
1887 }
caryclark55888e42016-07-18 10:01:36 -07001888 // fTop = outer;
1889 // fHead = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -07001890 do {
1891 // addifmissing can modify the list that this is walking
1892 // save head so that walker can iterate over old data unperturbed
1893 // addifmissing adds to head freely then add saved head in the end
caryclark8016b262016-09-06 05:59:47 -07001894 const SkOpPtT* ocs = outer->coinPtTStart();
1895 SkASSERT(!ocs->deleted());
1896 const SkOpSegment* outerCoin = ocs->segment();
1897 SkASSERT(!outerCoin->done()); // if it's done, should have already been removed from list
1898 const SkOpPtT* oos = outer->oppPtTStart();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001899 if (oos->deleted()) {
1900 return;
1901 }
caryclark8016b262016-09-06 05:59:47 -07001902 const SkOpSegment* outerOpp = oos->segment();
1903 SkASSERT(!outerOpp->done());
1904// SkOpSegment* outerCoinWritable = const_cast<SkOpSegment*>(outerCoin);
1905// SkOpSegment* outerOppWritable = const_cast<SkOpSegment*>(outerOpp);
caryclark26ad22a2015-10-16 09:03:38 -07001906 const SkCoincidentSpans* inner = outer;
caryclark55888e42016-07-18 10:01:36 -07001907 while ((inner = inner->next())) {
1908 this->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001909 double overS, overE;
caryclark8016b262016-09-06 05:59:47 -07001910 const SkOpPtT* ics = inner->coinPtTStart();
1911 SkASSERT(!ics->deleted());
1912 const SkOpSegment* innerCoin = ics->segment();
1913 SkASSERT(!innerCoin->done());
1914 const SkOpPtT* ios = inner->oppPtTStart();
1915 SkASSERT(!ios->deleted());
1916 const SkOpSegment* innerOpp = ios->segment();
1917 SkASSERT(!innerOpp->done());
1918// SkOpSegment* innerCoinWritable = const_cast<SkOpSegment*>(innerCoin);
1919// SkOpSegment* innerOppWritable = const_cast<SkOpSegment*>(innerOpp);
caryclark55888e42016-07-18 10:01:36 -07001920 if (outerCoin == innerCoin) {
caryclark8016b262016-09-06 05:59:47 -07001921 const SkOpPtT* oce = outer->coinPtTEnd();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001922 if (oce->deleted()) {
1923 return;
1924 }
caryclark8016b262016-09-06 05:59:47 -07001925 const SkOpPtT* ice = inner->coinPtTEnd();
1926 SkASSERT(!ice->deleted());
1927 if (outerOpp != innerOpp && this->overlap(ocs, oce, ics, ice, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001928 this->debugAddIfMissing(log, ocs->starter(oce), ics->starter(ice),
caryclark81a478c2016-09-09 09:37:57 -07001929 overS, overE, outerOpp, innerOpp, added,
caryclark8016b262016-09-06 05:59:47 -07001930 ocs->debugEnder(oce),
1931 ics->debugEnder(ice));
caryclark26ad22a2015-10-16 09:03:38 -07001932 }
caryclark55888e42016-07-18 10:01:36 -07001933 } else if (outerCoin == innerOpp) {
caryclark8016b262016-09-06 05:59:47 -07001934 const SkOpPtT* oce = outer->coinPtTEnd();
1935 SkASSERT(!oce->deleted());
1936 const SkOpPtT* ioe = inner->oppPtTEnd();
1937 SkASSERT(!ioe->deleted());
1938 if (outerOpp != innerCoin && this->overlap(ocs, oce, ios, ioe, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001939 this->debugAddIfMissing(log, ocs->starter(oce), ios->starter(ioe),
caryclark81a478c2016-09-09 09:37:57 -07001940 overS, overE, outerOpp, innerCoin, added,
caryclark8016b262016-09-06 05:59:47 -07001941 ocs->debugEnder(oce),
1942 ios->debugEnder(ioe));
caryclark26ad22a2015-10-16 09:03:38 -07001943 }
caryclark55888e42016-07-18 10:01:36 -07001944 } else if (outerOpp == innerCoin) {
caryclark8016b262016-09-06 05:59:47 -07001945 const SkOpPtT* ooe = outer->oppPtTEnd();
1946 SkASSERT(!ooe->deleted());
1947 const SkOpPtT* ice = inner->coinPtTEnd();
1948 SkASSERT(!ice->deleted());
caryclark55888e42016-07-18 10:01:36 -07001949 SkASSERT(outerCoin != innerOpp);
caryclark8016b262016-09-06 05:59:47 -07001950 if (this->overlap(oos, ooe, ics, ice, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001951 this->debugAddIfMissing(log, oos->starter(ooe), ics->starter(ice),
caryclark81a478c2016-09-09 09:37:57 -07001952 overS, overE, outerCoin, innerOpp, added,
caryclark8016b262016-09-06 05:59:47 -07001953 oos->debugEnder(ooe),
1954 ics->debugEnder(ice));
caryclark26ad22a2015-10-16 09:03:38 -07001955 }
caryclark55888e42016-07-18 10:01:36 -07001956 } else if (outerOpp == innerOpp) {
caryclark8016b262016-09-06 05:59:47 -07001957 const SkOpPtT* ooe = outer->oppPtTEnd();
1958 SkASSERT(!ooe->deleted());
1959 const SkOpPtT* ioe = inner->oppPtTEnd();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001960 if (ioe->deleted()) {
1961 return;
1962 }
caryclark55888e42016-07-18 10:01:36 -07001963 SkASSERT(outerCoin != innerCoin);
caryclark8016b262016-09-06 05:59:47 -07001964 if (this->overlap(oos, ooe, ios, ioe, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001965 this->debugAddIfMissing(log, oos->starter(ooe), ios->starter(ioe),
caryclark81a478c2016-09-09 09:37:57 -07001966 overS, overE, outerCoin, innerCoin, added,
caryclark8016b262016-09-06 05:59:47 -07001967 oos->debugEnder(ooe),
1968 ios->debugEnder(ioe));
caryclark26ad22a2015-10-16 09:03:38 -07001969 }
1970 }
caryclark55888e42016-07-18 10:01:36 -07001971 this->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001972 }
caryclark55888e42016-07-18 10:01:36 -07001973 } while ((outer = outer->next()));
1974 // this->restoreHead();
1975 return;
caryclark26ad22a2015-10-16 09:03:38 -07001976}
1977
caryclark55888e42016-07-18 10:01:36 -07001978// Commented-out lines keep this in sync with release()
Cary Clarkab87d7a2016-10-04 10:01:04 -04001979void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkCoincidentSpans* remove) const {
caryclark30b9fdd2016-08-31 14:36:29 -07001980 const SkCoincidentSpans* head = coin;
1981 const SkCoincidentSpans* prev = nullptr;
1982 const SkCoincidentSpans* next;
1983 do {
1984 next = coin->next();
1985 if (coin == remove) {
1986 if (prev) {
1987// prev->setNext(next);
1988 } else if (head == fHead) {
1989// fHead = next;
1990 } else {
1991// fTop = next;
1992 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001993 log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
caryclark30b9fdd2016-08-31 14:36:29 -07001994 }
1995 prev = coin;
1996 } while ((coin = next));
1997 return;
1998}
1999
Cary Clarkab87d7a2016-10-04 10:01:04 -04002000void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* deleted) const {
caryclark55888e42016-07-18 10:01:36 -07002001 const SkCoincidentSpans* coin = fHead;
2002 if (!coin) {
2003 return;
2004 }
2005 do {
2006 if (coin->coinPtTStart()->segment() == deleted
2007 || coin->coinPtTEnd()->segment() == deleted
2008 || coin->oppPtTStart()->segment() == deleted
2009 || coin->oppPtTEnd()->segment() == deleted) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002010 log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07002011 }
2012 } while ((coin = coin->next()));
2013}
2014
caryclark55888e42016-07-18 10:01:36 -07002015// Commented-out lines keep this in sync with expand()
2016// expand the range by checking adjacent spans for coincidence
Cary Clarkab87d7a2016-10-04 10:01:04 -04002017bool SkOpCoincidence::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07002018 const SkCoincidentSpans* coin = fHead;
2019 if (!coin) {
2020 return false;
2021 }
2022 bool expanded = false;
2023 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002024 if (coin->debugExpand(log)) {
caryclark55888e42016-07-18 10:01:36 -07002025 // check to see if multiple spans expanded so they are now identical
2026 const SkCoincidentSpans* test = fHead;
2027 do {
2028 if (coin == test) {
2029 continue;
2030 }
2031 if (coin->coinPtTStart() == test->coinPtTStart()
2032 && coin->oppPtTStart() == test->oppPtTStart()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002033 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, fHead, test->coinPtTStart());
caryclark55888e42016-07-18 10:01:36 -07002034 break;
2035 }
2036 } while ((test = test->next()));
2037 expanded = true;
caryclark26ad22a2015-10-16 09:03:38 -07002038 }
caryclark55888e42016-07-18 10:01:36 -07002039 } while ((coin = coin->next()));
caryclark26ad22a2015-10-16 09:03:38 -07002040 return expanded;
2041}
2042
caryclark55888e42016-07-18 10:01:36 -07002043// Commented-out lines keep this in sync with mark()
2044/* this sets up the coincidence links in the segments when the coincidence crosses multiple spans */
Cary Clarkab87d7a2016-10-04 10:01:04 -04002045void SkOpCoincidence::debugMark(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07002046 const SkCoincidentSpans* coin = fHead;
2047 if (!coin) {
2048 return;
2049 }
2050 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002051 FAIL_IF(!coin->coinPtTStartWritable()->span()->upCastable(), coin);
caryclark55888e42016-07-18 10:01:36 -07002052 const SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast();
2053// SkASSERT(start->deleted());
2054 const SkOpSpanBase* end = coin->coinPtTEndWritable()->span();
2055// SkASSERT(end->deleted());
2056 const SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span();
2057// SkASSERT(oStart->deleted());
2058 const SkOpSpanBase* oEnd = coin->oppPtTEndWritable()->span();
2059// SkASSERT(oEnd->deleted());
2060 bool flipped = coin->flipped();
caryclark26ad22a2015-10-16 09:03:38 -07002061 if (flipped) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002062 using std::swap;
2063 swap(oStart, oEnd);
caryclark26ad22a2015-10-16 09:03:38 -07002064 }
caryclark55888e42016-07-18 10:01:36 -07002065 /* coin and opp spans may not match up. Mark the ends, and then let the interior
2066 get marked as many times as the spans allow */
Cary Clarkab87d7a2016-10-04 10:01:04 -04002067 start->debugInsertCoincidence(log, oStart->upCast());
2068 end->debugInsertCoinEnd(log, oEnd);
caryclark55888e42016-07-18 10:01:36 -07002069 const SkOpSegment* segment = start->segment();
2070 const SkOpSegment* oSegment = oStart->segment();
caryclark26ad22a2015-10-16 09:03:38 -07002071 const SkOpSpanBase* next = start;
2072 const SkOpSpanBase* oNext = oStart;
caryclarka35ab3e2016-10-20 08:32:18 -07002073 bool ordered;
2074 FAIL_IF(!coin->ordered(&ordered), coin);
caryclark55888e42016-07-18 10:01:36 -07002075 while ((next = next->upCast()->next()) != end) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002076 FAIL_IF(!next->upCastable(), coin);
2077 if (next->upCast()->debugInsertCoincidence(log, oSegment, flipped, ordered), false) {
caryclark55888e42016-07-18 10:01:36 -07002078 return;
caryclark26ad22a2015-10-16 09:03:38 -07002079 }
caryclark55888e42016-07-18 10:01:36 -07002080 }
2081 while ((oNext = oNext->upCast()->next()) != oEnd) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002082 FAIL_IF(!oNext->upCastable(), coin);
2083 if (oNext->upCast()->debugInsertCoincidence(log, segment, flipped, ordered), false) {
caryclark55888e42016-07-18 10:01:36 -07002084 return;
caryclark26ad22a2015-10-16 09:03:38 -07002085 }
caryclark55888e42016-07-18 10:01:36 -07002086 }
2087 } while ((coin = coin->next()));
2088 return;
caryclark26ad22a2015-10-16 09:03:38 -07002089}
2090#endif
2091
Cary Clarkab87d7a2016-10-04 10:01:04 -04002092#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002093// Commented-out lines keep this in sync with markCollapsed()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002094void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkOpPtT* test) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002095 const SkCoincidentSpans* head = coin;
caryclark55888e42016-07-18 10:01:36 -07002096 while (coin) {
2097 if (coin->collapsed(test)) {
2098 if (zero_or_one(coin->coinPtTStart()->fT) && zero_or_one(coin->coinPtTEnd()->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002099 log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07002100 }
2101 if (zero_or_one(coin->oppPtTStart()->fT) && zero_or_one(coin->oppPtTEnd()->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002102 log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07002103 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002104 this->debugRelease(log, head, coin);
caryclark55888e42016-07-18 10:01:36 -07002105 }
2106 coin = coin->next();
caryclark624637c2015-05-11 07:21:27 -07002107 }
2108}
2109
caryclark55888e42016-07-18 10:01:36 -07002110// Commented-out lines keep this in sync with markCollapsed()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002111void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* test) const {
2112 this->debugMarkCollapsed(log, fHead, test);
2113 this->debugMarkCollapsed(log, fTop, test);
caryclark55888e42016-07-18 10:01:36 -07002114}
2115#endif
2116
2117void SkCoincidentSpans::debugShow() const {
caryclark6c3b9cd2016-09-26 05:36:58 -07002118 SkDebugf("coinSpan - id=%d t=%1.9g tEnd=%1.9g\n", coinPtTStart()->segment()->debugID(),
caryclark55888e42016-07-18 10:01:36 -07002119 coinPtTStart()->fT, coinPtTEnd()->fT);
caryclark6c3b9cd2016-09-26 05:36:58 -07002120 SkDebugf("coinSpan + id=%d t=%1.9g tEnd=%1.9g\n", oppPtTStart()->segment()->debugID(),
caryclark55888e42016-07-18 10:01:36 -07002121 oppPtTStart()->fT, oppPtTEnd()->fT);
2122}
2123
2124void SkOpCoincidence::debugShowCoincidence() const {
caryclark26ad22a2015-10-16 09:03:38 -07002125#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -07002126 const SkCoincidentSpans* span = fHead;
2127 while (span) {
2128 span->debugShow();
2129 span = span->next();
2130 }
2131#endif
2132}
2133
Cary Clarkab87d7a2016-10-04 10:01:04 -04002134#if DEBUG_COIN
caryclark6c3b9cd2016-09-26 05:36:58 -07002135static void DebugCheckBetween(const SkOpSpanBase* next, const SkOpSpanBase* end,
caryclark55888e42016-07-18 10:01:36 -07002136 double oStart, double oEnd, const SkOpSegment* oSegment,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002137 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002138 SkASSERT(next != end);
2139 SkASSERT(!next->contains(end) || log);
2140 if (next->t() > end->t()) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002141 using std::swap;
2142 swap(next, end);
caryclark55888e42016-07-18 10:01:36 -07002143 }
2144 do {
2145 const SkOpPtT* ptT = next->ptT();
2146 int index = 0;
caryclark27c015d2016-09-23 05:47:20 -07002147 bool somethingBetween = false;
caryclark55888e42016-07-18 10:01:36 -07002148 do {
2149 ++index;
2150 ptT = ptT->next();
2151 const SkOpPtT* checkPtT = next->ptT();
2152 if (ptT == checkPtT) {
2153 break;
2154 }
2155 bool looped = false;
2156 for (int check = 0; check < index; ++check) {
2157 if ((looped = checkPtT == ptT)) {
2158 break;
2159 }
2160 checkPtT = checkPtT->next();
2161 }
2162 if (looped) {
2163 SkASSERT(0);
2164 break;
2165 }
2166 if (ptT->deleted()) {
2167 continue;
2168 }
2169 if (ptT->segment() != oSegment) {
2170 continue;
2171 }
2172 somethingBetween |= between(oStart, ptT->fT, oEnd);
2173 } while (true);
2174 SkASSERT(somethingBetween);
2175 } while (next != end && (next = next->upCast()->next()));
2176}
2177
2178static void DebugCheckOverlap(const SkCoincidentSpans* test, const SkCoincidentSpans* list,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002179 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002180 if (!list) {
2181 return;
2182 }
2183 const SkOpSegment* coinSeg = test->coinPtTStart()->segment();
2184 SkASSERT(coinSeg == test->coinPtTEnd()->segment());
2185 const SkOpSegment* oppSeg = test->oppPtTStart()->segment();
2186 SkASSERT(oppSeg == test->oppPtTEnd()->segment());
2187 SkASSERT(coinSeg != test->oppPtTStart()->segment());
2188 SkDEBUGCODE(double tcs = test->coinPtTStart()->fT);
2189 SkASSERT(between(0, tcs, 1));
2190 SkDEBUGCODE(double tce = test->coinPtTEnd()->fT);
2191 SkASSERT(between(0, tce, 1));
2192 SkASSERT(tcs < tce);
2193 double tos = test->oppPtTStart()->fT;
2194 SkASSERT(between(0, tos, 1));
2195 double toe = test->oppPtTEnd()->fT;
2196 SkASSERT(between(0, toe, 1));
2197 SkASSERT(tos != toe);
2198 if (tos > toe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002199 using std::swap;
2200 swap(tos, toe);
caryclark55888e42016-07-18 10:01:36 -07002201 }
2202 do {
2203 double lcs, lce, los, loe;
2204 if (coinSeg == list->coinPtTStart()->segment()) {
2205 if (oppSeg != list->oppPtTStart()->segment()) {
2206 continue;
2207 }
2208 lcs = list->coinPtTStart()->fT;
2209 lce = list->coinPtTEnd()->fT;
2210 los = list->oppPtTStart()->fT;
2211 loe = list->oppPtTEnd()->fT;
2212 if (los > loe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002213 using std::swap;
2214 swap(los, loe);
caryclark55888e42016-07-18 10:01:36 -07002215 }
2216 } else if (coinSeg == list->oppPtTStart()->segment()) {
2217 if (oppSeg != list->coinPtTStart()->segment()) {
2218 continue;
2219 }
2220 lcs = list->oppPtTStart()->fT;
2221 lce = list->oppPtTEnd()->fT;
2222 if (lcs > lce) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002223 using std::swap;
2224 swap(lcs, lce);
caryclark55888e42016-07-18 10:01:36 -07002225 }
2226 los = list->coinPtTStart()->fT;
2227 loe = list->coinPtTEnd()->fT;
2228 } else {
2229 continue;
2230 }
2231 SkASSERT(tce < lcs || lce < tcs);
2232 SkASSERT(toe < los || loe < tos);
2233 } while ((list = list->next()));
2234}
2235
2236
2237static void DebugCheckOverlapTop(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002238 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002239 // check for overlapping coincident spans
2240 const SkCoincidentSpans* test = head;
2241 while (test) {
2242 const SkCoincidentSpans* next = test->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04002243 DebugCheckOverlap(test, next, log);
2244 DebugCheckOverlap(test, opt, log);
caryclark55888e42016-07-18 10:01:36 -07002245 test = next;
2246 }
2247}
2248
caryclark55888e42016-07-18 10:01:36 -07002249static void DebugValidate(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002250 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002251 // look for pts inside coincident spans that are not inside the opposite spans
2252 const SkCoincidentSpans* coin = head;
2253 while (coin) {
2254 SkASSERT(SkOpCoincidence::Ordered(coin->coinPtTStart()->segment(),
2255 coin->oppPtTStart()->segment()));
2256 SkASSERT(coin->coinPtTStart()->span()->ptT() == coin->coinPtTStart());
2257 SkASSERT(coin->coinPtTEnd()->span()->ptT() == coin->coinPtTEnd());
2258 SkASSERT(coin->oppPtTStart()->span()->ptT() == coin->oppPtTStart());
2259 SkASSERT(coin->oppPtTEnd()->span()->ptT() == coin->oppPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07002260 coin = coin->next();
2261 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002262 DebugCheckOverlapTop(head, opt, log);
caryclark55888e42016-07-18 10:01:36 -07002263}
2264#endif
2265
2266void SkOpCoincidence::debugValidate() const {
2267#if DEBUG_COINCIDENCE
Cary Clarkab87d7a2016-10-04 10:01:04 -04002268 DebugValidate(fHead, fTop, nullptr);
2269 DebugValidate(fTop, nullptr, nullptr);
caryclark55888e42016-07-18 10:01:36 -07002270#endif
2271}
2272
Cary Clarkab87d7a2016-10-04 10:01:04 -04002273#if DEBUG_COIN
caryclark6c3b9cd2016-09-26 05:36:58 -07002274static void DebugCheckBetween(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002275 SkPathOpsDebug::GlitchLog* log) {
caryclark6c3b9cd2016-09-26 05:36:58 -07002276 // look for pts inside coincident spans that are not inside the opposite spans
2277 const SkCoincidentSpans* coin = head;
2278 while (coin) {
2279 DebugCheckBetween(coin->coinPtTStart()->span(), coin->coinPtTEnd()->span(),
2280 coin->oppPtTStart()->fT, coin->oppPtTEnd()->fT, coin->oppPtTStart()->segment(),
Cary Clarkab87d7a2016-10-04 10:01:04 -04002281 log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002282 DebugCheckBetween(coin->oppPtTStart()->span(), coin->oppPtTEnd()->span(),
2283 coin->coinPtTStart()->fT, coin->coinPtTEnd()->fT, coin->coinPtTStart()->segment(),
Cary Clarkab87d7a2016-10-04 10:01:04 -04002284 log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002285 coin = coin->next();
2286 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002287 DebugCheckOverlapTop(head, opt, log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002288}
2289#endif
2290
2291void SkOpCoincidence::debugCheckBetween() const {
2292#if DEBUG_COINCIDENCE
2293 if (fGlobalState->debugCheckHealth()) {
2294 return;
2295 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002296 DebugCheckBetween(fHead, fTop, nullptr);
2297 DebugCheckBetween(fTop, nullptr, nullptr);
caryclark6c3b9cd2016-09-26 05:36:58 -07002298#endif
2299}
2300
Cary Clarkab87d7a2016-10-04 10:01:04 -04002301#if DEBUG_COIN
2302void SkOpContour::debugCheckHealth(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07002303 const SkOpSegment* segment = &fHead;
2304 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002305 segment->debugCheckHealth(log);
caryclark26ad22a2015-10-16 09:03:38 -07002306 } while ((segment = segment->next()));
2307}
2308
Cary Clarkab87d7a2016-10-04 10:01:04 -04002309void SkOpCoincidence::debugCheckValid(SkPathOpsDebug::GlitchLog* log) const {
2310#if DEBUG_VALIDATE
2311 DebugValidate(fHead, fTop, log);
2312 DebugValidate(fTop, nullptr, log);
2313#endif
2314}
2315
2316void SkOpCoincidence::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
2317 const SkCoincidentSpans* coin = fHead;
2318 if (!coin) {
2319 return;
2320 }
2321 do {
2322 coin->debugCorrectEnds(log);
2323 } while ((coin = coin->next()));
2324}
2325
caryclark55888e42016-07-18 10:01:36 -07002326// commmented-out lines keep this aligned with missingCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002327void SkOpContour::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07002328// SkASSERT(fCount > 0);
caryclark26ad22a2015-10-16 09:03:38 -07002329 const SkOpSegment* segment = &fHead;
caryclark55888e42016-07-18 10:01:36 -07002330// bool result = false;
caryclark26ad22a2015-10-16 09:03:38 -07002331 do {
Cary Clarke47ae292016-10-05 08:51:39 -04002332 if (segment->debugMissingCoincidence(log), false) {
caryclark55888e42016-07-18 10:01:36 -07002333// result = true;
caryclark55888e42016-07-18 10:01:36 -07002334 }
2335 segment = segment->next();
2336 } while (segment);
2337 return;
caryclark26ad22a2015-10-16 09:03:38 -07002338}
Cary Clarkab87d7a2016-10-04 10:01:04 -04002339
2340void SkOpContour::debugMoveMultiples(SkPathOpsDebug::GlitchLog* log) const {
2341 SkASSERT(fCount > 0);
2342 const SkOpSegment* segment = &fHead;
2343 do {
2344 if (segment->debugMoveMultiples(log), false) {
2345 return;
2346 }
2347 } while ((segment = segment->next()));
2348 return;
2349}
2350
2351void SkOpContour::debugMoveNearby(SkPathOpsDebug::GlitchLog* log) const {
2352 SkASSERT(fCount > 0);
2353 const SkOpSegment* segment = &fHead;
2354 do {
2355 segment->debugMoveNearby(log);
2356 } while ((segment = segment->next()));
2357}
caryclark26ad22a2015-10-16 09:03:38 -07002358#endif
2359
caryclark025b11e2016-08-25 05:21:14 -07002360#if DEBUG_COINCIDENCE_ORDER
2361void SkOpSegment::debugResetCoinT() const {
2362 fDebugBaseIndex = -1;
2363 fDebugBaseMin = 1;
2364 fDebugBaseMax = -1;
2365 fDebugLastIndex = -1;
2366 fDebugLastMin = 1;
2367 fDebugLastMax = -1;
2368}
2369#endif
2370
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002371void SkOpSegment::debugValidate() const {
caryclark025b11e2016-08-25 05:21:14 -07002372#if DEBUG_COINCIDENCE_ORDER
2373 {
2374 const SkOpSpanBase* span = &fHead;
2375 do {
2376 span->debugResetCoinT();
2377 } while (!span->final() && (span = span->upCast()->next()));
2378 span = &fHead;
2379 int index = 0;
2380 do {
2381 span->debugSetCoinT(index++);
2382 } while (!span->final() && (span = span->upCast()->next()));
2383 }
2384#endif
caryclark55888e42016-07-18 10:01:36 -07002385#if DEBUG_COINCIDENCE
2386 if (this->globalState()->debugCheckHealth()) {
2387 return;
2388 }
2389#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002390#if DEBUG_VALIDATE
caryclark54359292015-03-26 07:52:43 -07002391 const SkOpSpanBase* span = &fHead;
2392 double lastT = -1;
halcanary96fcdcc2015-08-27 07:41:13 -07002393 const SkOpSpanBase* prev = nullptr;
caryclark54359292015-03-26 07:52:43 -07002394 int count = 0;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002395 int done = 0;
caryclark54359292015-03-26 07:52:43 -07002396 do {
2397 if (!span->final()) {
2398 ++count;
2399 done += span->upCast()->done() ? 1 : 0;
2400 }
2401 SkASSERT(span->segment() == this);
2402 SkASSERT(!prev || prev->upCast()->next() == span);
2403 SkASSERT(!prev || prev == span->prev());
2404 prev = span;
2405 double t = span->ptT()->fT;
2406 SkASSERT(lastT < t);
2407 lastT = t;
2408 span->debugValidate();
2409 } while (!span->final() && (span = span->upCast()->next()));
2410 SkASSERT(count == fCount);
2411 SkASSERT(done == fDoneCount);
caryclark08bc8482015-04-24 09:08:57 -07002412 SkASSERT(count >= fDoneCount);
caryclark54359292015-03-26 07:52:43 -07002413 SkASSERT(span->final());
2414 span->debugValidate();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002415#endif
caryclark54359292015-03-26 07:52:43 -07002416}
2417
Cary Clarkab87d7a2016-10-04 10:01:04 -04002418#if DEBUG_COIN
caryclark30b9fdd2016-08-31 14:36:29 -07002419
2420// Commented-out lines keep this in sync with addOpp()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002421void SkOpSpanBase::debugAddOpp(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002422 const SkOpPtT* oppPrev = this->ptT()->oppPrev(opp->ptT());
2423 if (!oppPrev) {
caryclark55888e42016-07-18 10:01:36 -07002424 return;
caryclark26ad22a2015-10-16 09:03:38 -07002425 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002426 this->debugMergeMatches(log, opp);
caryclark30b9fdd2016-08-31 14:36:29 -07002427 this->ptT()->debugAddOpp(opp->ptT(), oppPrev);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002428 this->debugCheckForCollapsedCoincidence(log);
caryclark26ad22a2015-10-16 09:03:38 -07002429}
2430
caryclark55888e42016-07-18 10:01:36 -07002431// Commented-out lines keep this in sync with checkForCollapsedCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002432void SkOpSpanBase::debugCheckForCollapsedCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07002433 const SkOpCoincidence* coins = this->globalState()->coincidence();
2434 if (coins->isEmpty()) {
2435 return;
2436 }
2437// the insert above may have put both ends of a coincident run in the same span
2438// for each coincident ptT in loop; see if its opposite in is also in the loop
2439// this implementation is the motivation for marking that a ptT is referenced by a coincident span
2440 const SkOpPtT* head = this->ptT();
2441 const SkOpPtT* test = head;
caryclark26ad22a2015-10-16 09:03:38 -07002442 do {
caryclark55888e42016-07-18 10:01:36 -07002443 if (!test->coincident()) {
2444 continue;
caryclark26ad22a2015-10-16 09:03:38 -07002445 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002446 coins->debugMarkCollapsed(log, test);
caryclark55888e42016-07-18 10:01:36 -07002447 } while ((test = test->next()) != head);
caryclark26ad22a2015-10-16 09:03:38 -07002448}
caryclark55888e42016-07-18 10:01:36 -07002449#endif
caryclark26ad22a2015-10-16 09:03:38 -07002450
caryclark54359292015-03-26 07:52:43 -07002451bool SkOpSpanBase::debugCoinEndLoopCheck() const {
2452 int loop = 0;
2453 const SkOpSpanBase* next = this;
2454 SkOpSpanBase* nextCoin;
2455 do {
2456 nextCoin = next->fCoinEnd;
2457 SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin);
2458 for (int check = 1; check < loop - 1; ++check) {
2459 const SkOpSpanBase* checkCoin = this->fCoinEnd;
2460 const SkOpSpanBase* innerCoin = checkCoin;
2461 for (int inner = check + 1; inner < loop; ++inner) {
2462 innerCoin = innerCoin->fCoinEnd;
2463 if (checkCoin == innerCoin) {
2464 SkDebugf("*** bad coincident end loop ***\n");
2465 return false;
2466 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002467 }
2468 }
caryclark54359292015-03-26 07:52:43 -07002469 ++loop;
2470 } while ((next = nextCoin) && next != this);
2471 return true;
2472}
2473
Cary Clarkab87d7a2016-10-04 10:01:04 -04002474#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002475// Commented-out lines keep this in sync with insertCoinEnd()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002476void SkOpSpanBase::debugInsertCoinEnd(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* coin) const {
caryclark55888e42016-07-18 10:01:36 -07002477 if (containsCoinEnd(coin)) {
2478// SkASSERT(coin->containsCoinEnd(this));
2479 return;
2480 }
2481 debugValidate();
2482// SkASSERT(this != coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002483 log->record(SkPathOpsDebug::kMarkCoinEnd_Glitch, this, coin);
caryclark55888e42016-07-18 10:01:36 -07002484// coin->fCoinEnd = this->fCoinEnd;
2485// this->fCoinEnd = coinNext;
2486 debugValidate();
2487}
2488
caryclark30b9fdd2016-08-31 14:36:29 -07002489// Commented-out lines keep this in sync with mergeMatches()
2490// Look to see if pt-t linked list contains same segment more than once
2491// if so, and if each pt-t is directly pointed to by spans in that segment,
2492// merge them
2493// keep the points, but remove spans so that the segment doesn't have 2 or more
2494// spans pointing to the same pt-t loop at different loop elements
Cary Clarkab87d7a2016-10-04 10:01:04 -04002495void SkOpSpanBase::debugMergeMatches(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002496 const SkOpPtT* test = &fPtT;
2497 const SkOpPtT* testNext;
2498 const SkOpPtT* stop = test;
2499 do {
2500 testNext = test->next();
2501 if (test->deleted()) {
2502 continue;
2503 }
2504 const SkOpSpanBase* testBase = test->span();
2505 SkASSERT(testBase->ptT() == test);
2506 const SkOpSegment* segment = test->segment();
2507 if (segment->done()) {
2508 continue;
2509 }
2510 const SkOpPtT* inner = opp->ptT();
2511 const SkOpPtT* innerStop = inner;
2512 do {
2513 if (inner->segment() != segment) {
2514 continue;
2515 }
2516 if (inner->deleted()) {
2517 continue;
2518 }
2519 const SkOpSpanBase* innerBase = inner->span();
2520 SkASSERT(innerBase->ptT() == inner);
Ben Wagner63fd7602017-10-09 15:45:33 -04002521 // when the intersection is first detected, the span base is marked if there are
caryclark30b9fdd2016-08-31 14:36:29 -07002522 // more than one point in the intersection.
2523// if (!innerBase->hasMultipleHint() && !testBase->hasMultipleHint()) {
2524 if (!zero_or_one(inner->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002525 log->record(SkPathOpsDebug::kMergeMatches_Glitch, innerBase, test);
caryclark30b9fdd2016-08-31 14:36:29 -07002526 } else {
2527 SkASSERT(inner->fT != test->fT);
2528 if (!zero_or_one(test->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002529 log->record(SkPathOpsDebug::kMergeMatches_Glitch, testBase, inner);
caryclark30b9fdd2016-08-31 14:36:29 -07002530 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002531 log->record(SkPathOpsDebug::kMergeMatches_Glitch, segment);
caryclark30b9fdd2016-08-31 14:36:29 -07002532// SkDEBUGCODE(testBase->debugSetDeleted());
2533// test->setDeleted();
2534// SkDEBUGCODE(innerBase->debugSetDeleted());
2535// inner->setDeleted();
2536 }
2537 }
2538#ifdef SK_DEBUG // assert if another undeleted entry points to segment
2539 const SkOpPtT* debugInner = inner;
2540 while ((debugInner = debugInner->next()) != innerStop) {
2541 if (debugInner->segment() != segment) {
2542 continue;
2543 }
2544 if (debugInner->deleted()) {
2545 continue;
2546 }
2547 SkOPASSERT(0);
2548 }
2549#endif
Ben Wagner63fd7602017-10-09 15:45:33 -04002550 break;
caryclark30b9fdd2016-08-31 14:36:29 -07002551// }
2552 break;
2553 } while ((inner = inner->next()) != innerStop);
2554 } while ((test = testNext) != stop);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002555 this->debugCheckForCollapsedCoincidence(log);
caryclark30b9fdd2016-08-31 14:36:29 -07002556}
2557
caryclark55888e42016-07-18 10:01:36 -07002558#endif
caryclark26ad22a2015-10-16 09:03:38 -07002559
caryclark025b11e2016-08-25 05:21:14 -07002560void SkOpSpanBase::debugResetCoinT() const {
2561#if DEBUG_COINCIDENCE_ORDER
2562 const SkOpPtT* ptT = &fPtT;
2563 do {
2564 ptT->debugResetCoinT();
2565 ptT = ptT->next();
2566 } while (ptT != &fPtT);
2567#endif
2568}
2569
2570void SkOpSpanBase::debugSetCoinT(int index) const {
2571#if DEBUG_COINCIDENCE_ORDER
2572 const SkOpPtT* ptT = &fPtT;
2573 do {
2574 if (!ptT->deleted()) {
2575 ptT->debugSetCoinT(index);
2576 }
2577 ptT = ptT->next();
2578 } while (ptT != &fPtT);
2579#endif
2580}
2581
caryclark26ad22a2015-10-16 09:03:38 -07002582const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const {
2583 const SkOpSpanBase* end = *endPtr;
2584 SkASSERT(this->segment() == end->segment());
2585 const SkOpSpanBase* result;
2586 if (t() < end->t()) {
2587 result = this;
2588 } else {
2589 result = end;
2590 *endPtr = this;
2591 }
2592 return result->upCast();
2593}
2594
caryclark54359292015-03-26 07:52:43 -07002595void SkOpSpanBase::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07002596#if DEBUG_COINCIDENCE
2597 if (this->globalState()->debugCheckHealth()) {
2598 return;
2599 }
2600#endif
caryclark54359292015-03-26 07:52:43 -07002601#if DEBUG_VALIDATE
2602 const SkOpPtT* ptT = &fPtT;
2603 SkASSERT(ptT->span() == this);
2604 do {
2605// SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt));
2606 ptT->debugValidate();
2607 ptT = ptT->next();
2608 } while (ptT != &fPtT);
2609 SkASSERT(this->debugCoinEndLoopCheck());
2610 if (!this->final()) {
2611 SkASSERT(this->upCast()->debugCoinLoopCheck());
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002612 }
caryclark54359292015-03-26 07:52:43 -07002613 if (fFromAngle) {
2614 fFromAngle->debugValidate();
2615 }
2616 if (!this->final() && this->upCast()->toAngle()) {
2617 this->upCast()->toAngle()->debugValidate();
2618 }
2619#endif
2620}
2621
2622bool SkOpSpan::debugCoinLoopCheck() const {
2623 int loop = 0;
2624 const SkOpSpan* next = this;
2625 SkOpSpan* nextCoin;
2626 do {
2627 nextCoin = next->fCoincident;
2628 SkASSERT(nextCoin == this || nextCoin->fCoincident != nextCoin);
2629 for (int check = 1; check < loop - 1; ++check) {
2630 const SkOpSpan* checkCoin = this->fCoincident;
2631 const SkOpSpan* innerCoin = checkCoin;
2632 for (int inner = check + 1; inner < loop; ++inner) {
2633 innerCoin = innerCoin->fCoincident;
2634 if (checkCoin == innerCoin) {
2635 SkDebugf("*** bad coincident loop ***\n");
2636 return false;
2637 }
2638 }
2639 }
2640 ++loop;
2641 } while ((next = nextCoin) && next != this);
2642 return true;
2643}
2644
Cary Clarkab87d7a2016-10-04 10:01:04 -04002645#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002646// Commented-out lines keep this in sync with insertCoincidence() in header
Cary Clarkab87d7a2016-10-04 10:01:04 -04002647void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* coin) const {
caryclark55888e42016-07-18 10:01:36 -07002648 if (containsCoincidence(coin)) {
2649// SkASSERT(coin->containsCoincidence(this));
2650 return;
2651 }
2652 debugValidate();
2653// SkASSERT(this != coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002654 log->record(SkPathOpsDebug::kMarkCoinStart_Glitch, this, coin);
caryclark55888e42016-07-18 10:01:36 -07002655// coin->fCoincident = this->fCoincident;
2656// this->fCoincident = coinNext;
2657 debugValidate();
2658}
2659
2660// Commented-out lines keep this in sync with insertCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002661void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* segment, bool flipped, bool ordered) const {
caryclark55888e42016-07-18 10:01:36 -07002662 if (this->containsCoincidence(segment)) {
2663 return;
2664 }
2665 const SkOpPtT* next = &fPtT;
2666 while ((next = next->next()) != &fPtT) {
2667 if (next->segment() == segment) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002668 const SkOpSpan* span;
2669 const SkOpSpanBase* base = next->span();
2670 if (!ordered) {
2671 const SkOpSpanBase* spanEnd = fNext->contains(segment)->span();
2672 const SkOpPtT* start = base->ptT()->starter(spanEnd->ptT());
2673 FAIL_IF(!start->span()->upCastable(), this);
2674 span = const_cast<SkOpSpan*>(start->span()->upCast());
2675 }
2676 else if (flipped) {
2677 span = base->prev();
2678 FAIL_IF(!span, this);
2679 }
2680 else {
2681 FAIL_IF(!base->upCastable(), this);
2682 span = base->upCast();
2683 }
2684 log->record(SkPathOpsDebug::kMarkCoinInsert_Glitch, span);
caryclark55888e42016-07-18 10:01:36 -07002685 return;
2686 }
2687 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002688#if DEBUG_COIN
2689 log->record(SkPathOpsDebug::kMarkCoinMissing_Glitch, segment, this);
caryclark55888e42016-07-18 10:01:36 -07002690#endif
Cary Clarkab87d7a2016-10-04 10:01:04 -04002691 return;
caryclark55888e42016-07-18 10:01:36 -07002692}
2693#endif
2694
caryclark624637c2015-05-11 07:21:27 -07002695// called only by test code
2696int SkIntersections::debugCoincidentUsed() const {
2697 if (!fIsCoincident[0]) {
2698 SkASSERT(!fIsCoincident[1]);
2699 return 0;
2700 }
2701 int count = 0;
2702 SkDEBUGCODE(int count2 = 0;)
2703 for (int index = 0; index < fUsed; ++index) {
2704 if (fIsCoincident[0] & (1 << index)) {
2705 ++count;
2706 }
2707#ifdef SK_DEBUG
2708 if (fIsCoincident[1] & (1 << index)) {
2709 ++count2;
2710 }
2711#endif
2712 }
2713 SkASSERT(count == count2);
2714 return count;
2715}
2716
caryclark54359292015-03-26 07:52:43 -07002717#include "SkOpContour.h"
2718
caryclark55888e42016-07-18 10:01:36 -07002719// Commented-out lines keep this in sync with addOpp()
caryclark29b25632016-08-25 11:27:17 -07002720void SkOpPtT::debugAddOpp(const SkOpPtT* opp, const SkOpPtT* oppPrev) const {
2721 SkDEBUGCODE(const SkOpPtT* oldNext = this->fNext);
caryclark55888e42016-07-18 10:01:36 -07002722 SkASSERT(this != opp);
2723// this->fNext = opp;
caryclark29b25632016-08-25 11:27:17 -07002724 SkASSERT(oppPrev != oldNext);
caryclark55888e42016-07-18 10:01:36 -07002725// oppPrev->fNext = oldNext;
caryclark55888e42016-07-18 10:01:36 -07002726}
2727
caryclark26ad22a2015-10-16 09:03:38 -07002728bool SkOpPtT::debugContains(const SkOpPtT* check) const {
2729 SkASSERT(this != check);
2730 const SkOpPtT* ptT = this;
2731 int links = 0;
2732 do {
2733 ptT = ptT->next();
2734 if (ptT == check) {
2735 return true;
2736 }
2737 ++links;
2738 const SkOpPtT* test = this;
2739 for (int index = 0; index < links; ++index) {
2740 if (ptT == test) {
2741 return false;
2742 }
2743 test = test->next();
2744 }
2745 } while (true);
2746}
2747
2748const SkOpPtT* SkOpPtT::debugContains(const SkOpSegment* check) const {
2749 SkASSERT(this->segment() != check);
2750 const SkOpPtT* ptT = this;
2751 int links = 0;
2752 do {
2753 ptT = ptT->next();
2754 if (ptT->segment() == check) {
2755 return ptT;
2756 }
2757 ++links;
2758 const SkOpPtT* test = this;
2759 for (int index = 0; index < links; ++index) {
2760 if (ptT == test) {
2761 return nullptr;
2762 }
2763 test = test->next();
2764 }
2765 } while (true);
2766}
2767
caryclark8016b262016-09-06 05:59:47 -07002768const SkOpPtT* SkOpPtT::debugEnder(const SkOpPtT* end) const {
2769 return fT < end->fT ? end : this;
2770}
2771
caryclark54359292015-03-26 07:52:43 -07002772int SkOpPtT::debugLoopLimit(bool report) const {
2773 int loop = 0;
2774 const SkOpPtT* next = this;
2775 do {
2776 for (int check = 1; check < loop - 1; ++check) {
2777 const SkOpPtT* checkPtT = this->fNext;
2778 const SkOpPtT* innerPtT = checkPtT;
2779 for (int inner = check + 1; inner < loop; ++inner) {
2780 innerPtT = innerPtT->fNext;
2781 if (checkPtT == innerPtT) {
2782 if (report) {
2783 SkDebugf("*** bad ptT loop ***\n");
2784 }
2785 return loop;
2786 }
2787 }
2788 }
caryclark26ad22a2015-10-16 09:03:38 -07002789 // there's nothing wrong with extremely large loop counts -- but this may appear to hang
2790 // by taking a very long time to figure out that no loop entry is a duplicate
2791 // -- and it's likely that a large loop count is indicative of a bug somewhere
2792 if (++loop > 1000) {
2793 SkDebugf("*** loop count exceeds 1000 ***\n");
2794 return 1000;
2795 }
caryclark54359292015-03-26 07:52:43 -07002796 } while ((next = next->fNext) && next != this);
2797 return 0;
2798}
2799
caryclark29b25632016-08-25 11:27:17 -07002800const SkOpPtT* SkOpPtT::debugOppPrev(const SkOpPtT* opp) const {
2801 return this->oppPrev(const_cast<SkOpPtT*>(opp));
2802}
2803
caryclark025b11e2016-08-25 05:21:14 -07002804void SkOpPtT::debugResetCoinT() const {
2805#if DEBUG_COINCIDENCE_ORDER
Ben Wagner63fd7602017-10-09 15:45:33 -04002806 this->segment()->debugResetCoinT();
caryclark025b11e2016-08-25 05:21:14 -07002807#endif
2808}
2809
2810void SkOpPtT::debugSetCoinT(int index) const {
2811#if DEBUG_COINCIDENCE_ORDER
Ben Wagner63fd7602017-10-09 15:45:33 -04002812 this->segment()->debugSetCoinT(index, fT);
caryclark025b11e2016-08-25 05:21:14 -07002813#endif
2814}
2815
caryclark54359292015-03-26 07:52:43 -07002816void SkOpPtT::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07002817#if DEBUG_COINCIDENCE
2818 if (this->globalState()->debugCheckHealth()) {
2819 return;
2820 }
2821#endif
caryclark54359292015-03-26 07:52:43 -07002822#if DEBUG_VALIDATE
Cary Clarkab87d7a2016-10-04 10:01:04 -04002823 SkOpPhase phase = contour()->globalState()->phase();
2824 if (phase == SkOpPhase::kIntersecting || phase == SkOpPhase::kFixWinding) {
caryclark54359292015-03-26 07:52:43 -07002825 return;
2826 }
2827 SkASSERT(fNext);
2828 SkASSERT(fNext != this);
2829 SkASSERT(fNext->fNext);
2830 SkASSERT(debugLoopLimit(false) == 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002831#endif
2832}
caryclark1049f122015-04-20 08:31:59 -07002833
2834static void output_scalar(SkScalar num) {
2835 if (num == (int) num) {
2836 SkDebugf("%d", (int) num);
2837 } else {
2838 SkString str;
2839 str.printf("%1.9g", num);
2840 int width = (int) str.size();
2841 const char* cStr = str.c_str();
2842 while (cStr[width - 1] == '0') {
2843 --width;
2844 }
2845 str.resize(width);
2846 SkDebugf("%sf", str.c_str());
2847 }
2848}
2849
2850static void output_points(const SkPoint* pts, int count) {
2851 for (int index = 0; index < count; ++index) {
2852 output_scalar(pts[index].fX);
2853 SkDebugf(", ");
2854 output_scalar(pts[index].fY);
2855 if (index + 1 < count) {
2856 SkDebugf(", ");
2857 }
2858 }
2859}
2860
2861static void showPathContours(SkPath::RawIter& iter, const char* pathName) {
2862 uint8_t verb;
2863 SkPoint pts[4];
2864 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
2865 switch (verb) {
2866 case SkPath::kMove_Verb:
2867 SkDebugf(" %s.moveTo(", pathName);
2868 output_points(&pts[0], 1);
2869 SkDebugf(");\n");
2870 continue;
2871 case SkPath::kLine_Verb:
2872 SkDebugf(" %s.lineTo(", pathName);
2873 output_points(&pts[1], 1);
2874 SkDebugf(");\n");
2875 break;
2876 case SkPath::kQuad_Verb:
2877 SkDebugf(" %s.quadTo(", pathName);
2878 output_points(&pts[1], 2);
2879 SkDebugf(");\n");
2880 break;
2881 case SkPath::kConic_Verb:
2882 SkDebugf(" %s.conicTo(", pathName);
2883 output_points(&pts[1], 2);
2884 SkDebugf(", %1.9gf);\n", iter.conicWeight());
2885 break;
2886 case SkPath::kCubic_Verb:
2887 SkDebugf(" %s.cubicTo(", pathName);
2888 output_points(&pts[1], 3);
2889 SkDebugf(");\n");
2890 break;
2891 case SkPath::kClose_Verb:
2892 SkDebugf(" %s.close();\n", pathName);
2893 break;
2894 default:
2895 SkDEBUGFAIL("bad verb");
2896 return;
2897 }
2898 }
2899}
2900
2901static const char* gFillTypeStr[] = {
2902 "kWinding_FillType",
2903 "kEvenOdd_FillType",
2904 "kInverseWinding_FillType",
2905 "kInverseEvenOdd_FillType"
2906};
2907
2908void SkPathOpsDebug::ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration) {
2909 SkPath::RawIter iter(path);
2910#define SUPPORT_RECT_CONTOUR_DETECTION 0
2911#if SUPPORT_RECT_CONTOUR_DETECTION
halcanary96fcdcc2015-08-27 07:41:13 -07002912 int rectCount = path.isRectContours() ? path.rectContours(nullptr, nullptr) : 0;
caryclark1049f122015-04-20 08:31:59 -07002913 if (rectCount > 0) {
2914 SkTDArray<SkRect> rects;
2915 SkTDArray<SkPath::Direction> directions;
2916 rects.setCount(rectCount);
2917 directions.setCount(rectCount);
2918 path.rectContours(rects.begin(), directions.begin());
2919 for (int contour = 0; contour < rectCount; ++contour) {
2920 const SkRect& rect = rects[contour];
2921 SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
2922 rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
2923 ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
2924 }
2925 return;
2926 }
2927#endif
2928 SkPath::FillType fillType = path.getFillType();
2929 SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType);
2930 if (includeDeclaration) {
2931 SkDebugf(" SkPath %s;\n", name);
2932 }
2933 SkDebugf(" %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[fillType]);
2934 iter.setPath(path);
2935 showPathContours(iter, name);
2936}
caryclark13260682016-10-24 05:10:14 -07002937
Cary Clark918fb1f2016-11-15 13:22:25 -05002938#if DEBUG_DUMP_VERIFY
caryclark13260682016-10-24 05:10:14 -07002939#include "SkData.h"
2940#include "SkStream.h"
2941
2942static void dump_path(FILE* file, const SkPath& path, bool force, bool dumpAsHex) {
2943 SkDynamicMemoryWStream wStream;
2944 path.dump(&wStream, force, dumpAsHex);
2945 sk_sp<SkData> data(wStream.detachAsData());
2946 fprintf(file, "%.*s\n", (int) data->size(), (char*) data->data());
2947}
2948
2949static int dumpID = 0;
2950
2951void SkPathOpsDebug::DumpOp(const SkPath& one, const SkPath& two, SkPathOp op,
2952 const char* testName) {
2953 FILE* file = sk_fopen("op_dump.txt", kWrite_SkFILE_Flag);
2954 DumpOp(file, one, two, op, testName);
2955}
2956
2957void SkPathOpsDebug::DumpOp(FILE* file, const SkPath& one, const SkPath& two, SkPathOp op,
2958 const char* testName) {
2959 const char* name = testName ? testName : "op";
2960 fprintf(file,
2961 "\nstatic void %s_%d(skiatest::Reporter* reporter, const char* filename) {\n",
2962 name, ++dumpID);
2963 fprintf(file, " SkPath path;\n");
2964 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", one.getFillType());
2965 dump_path(file, one, false, true);
2966 fprintf(file, " SkPath path1(path);\n");
2967 fprintf(file, " path.reset();\n");
2968 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", two.getFillType());
2969 dump_path(file, two, false, true);
2970 fprintf(file, " SkPath path2(path);\n");
2971 fprintf(file, " testPathOp(reporter, path1, path2, (SkPathOp) %d, filename);\n", op);
2972 fprintf(file, "}\n\n");
2973 fclose(file);
2974}
2975
2976void SkPathOpsDebug::DumpSimplify(const SkPath& path, const char* testName) {
2977 FILE* file = sk_fopen("simplify_dump.txt", kWrite_SkFILE_Flag);
2978 DumpSimplify(file, path, testName);
2979}
2980
2981void SkPathOpsDebug::DumpSimplify(FILE* file, const SkPath& path, const char* testName) {
2982 const char* name = testName ? testName : "simplify";
2983 fprintf(file,
2984 "\nstatic void %s_%d(skiatest::Reporter* reporter, const char* filename) {\n",
2985 name, ++dumpID);
2986 fprintf(file, " SkPath path;\n");
2987 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", path.getFillType());
2988 dump_path(file, path, false, true);
2989 fprintf(file, " testSimplify(reporter, path, filename);\n");
2990 fprintf(file, "}\n\n");
2991 fclose(file);
2992}
2993
2994#include "SkBitmap.h"
2995#include "SkCanvas.h"
2996#include "SkPaint.h"
2997
2998const int bitWidth = 64;
2999const int bitHeight = 64;
3000
3001static void debug_scale_matrix(const SkPath& one, const SkPath* two, SkMatrix& scale) {
3002 SkRect larger = one.getBounds();
3003 if (two) {
3004 larger.join(two->getBounds());
3005 }
3006 SkScalar largerWidth = larger.width();
3007 if (largerWidth < 4) {
3008 largerWidth = 4;
3009 }
3010 SkScalar largerHeight = larger.height();
3011 if (largerHeight < 4) {
3012 largerHeight = 4;
3013 }
3014 SkScalar hScale = (bitWidth - 2) / largerWidth;
3015 SkScalar vScale = (bitHeight - 2) / largerHeight;
3016 scale.reset();
3017 scale.preScale(hScale, vScale);
3018 larger.fLeft *= hScale;
3019 larger.fRight *= hScale;
3020 larger.fTop *= vScale;
3021 larger.fBottom *= vScale;
3022 SkScalar dx = -16000 > larger.fLeft ? -16000 - larger.fLeft
3023 : 16000 < larger.fRight ? 16000 - larger.fRight : 0;
3024 SkScalar dy = -16000 > larger.fTop ? -16000 - larger.fTop
3025 : 16000 < larger.fBottom ? 16000 - larger.fBottom : 0;
3026 scale.preTranslate(dx, dy);
3027}
3028
3029static int debug_paths_draw_the_same(const SkPath& one, const SkPath& two, SkBitmap& bits) {
3030 if (bits.width() == 0) {
3031 bits.allocN32Pixels(bitWidth * 2, bitHeight);
3032 }
3033 SkCanvas canvas(bits);
3034 canvas.drawColor(SK_ColorWHITE);
3035 SkPaint paint;
3036 canvas.save();
3037 const SkRect& bounds1 = one.getBounds();
3038 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
3039 canvas.drawPath(one, paint);
3040 canvas.restore();
3041 canvas.save();
3042 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
3043 canvas.drawPath(two, paint);
3044 canvas.restore();
3045 int errors = 0;
3046 for (int y = 0; y < bitHeight - 1; ++y) {
3047 uint32_t* addr1 = bits.getAddr32(0, y);
3048 uint32_t* addr2 = bits.getAddr32(0, y + 1);
3049 uint32_t* addr3 = bits.getAddr32(bitWidth, y);
3050 uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1);
3051 for (int x = 0; x < bitWidth - 1; ++x) {
3052 // count 2x2 blocks
3053 bool err = addr1[x] != addr3[x];
3054 if (err) {
3055 errors += addr1[x + 1] != addr3[x + 1]
3056 && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1];
3057 }
3058 }
3059 }
3060 return errors;
3061}
3062
3063void SkPathOpsDebug::ReportOpFail(const SkPath& one, const SkPath& two, SkPathOp op) {
3064 SkDebugf("// Op did not expect failure\n");
3065 DumpOp(stderr, one, two, op, "opTest");
3066 fflush(stderr);
3067}
3068
3069void SkPathOpsDebug::VerifyOp(const SkPath& one, const SkPath& two, SkPathOp op,
3070 const SkPath& result) {
3071 SkPath pathOut, scaledPathOut;
3072 SkRegion rgnA, rgnB, openClip, rgnOut;
3073 openClip.setRect(-16000, -16000, 16000, 16000);
3074 rgnA.setPath(one, openClip);
3075 rgnB.setPath(two, openClip);
3076 rgnOut.op(rgnA, rgnB, (SkRegion::Op) op);
3077 rgnOut.getBoundaryPath(&pathOut);
3078 SkMatrix scale;
3079 debug_scale_matrix(one, &two, scale);
3080 SkRegion scaledRgnA, scaledRgnB, scaledRgnOut;
3081 SkPath scaledA, scaledB;
3082 scaledA.addPath(one, scale);
3083 scaledA.setFillType(one.getFillType());
3084 scaledB.addPath(two, scale);
3085 scaledB.setFillType(two.getFillType());
3086 scaledRgnA.setPath(scaledA, openClip);
3087 scaledRgnB.setPath(scaledB, openClip);
3088 scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) op);
3089 scaledRgnOut.getBoundaryPath(&scaledPathOut);
3090 SkBitmap bitmap;
3091 SkPath scaledOut;
3092 scaledOut.addPath(result, scale);
3093 scaledOut.setFillType(result.getFillType());
3094 int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
3095 const int MAX_ERRORS = 9;
3096 if (errors > MAX_ERRORS) {
3097 fprintf(stderr, "// Op did not expect errors=%d\n", errors);
3098 DumpOp(stderr, one, two, op, "opTest");
3099 fflush(stderr);
3100 }
3101}
3102
3103void SkPathOpsDebug::ReportSimplifyFail(const SkPath& path) {
3104 SkDebugf("// Simplify did not expect failure\n");
3105 DumpSimplify(stderr, path, "simplifyTest");
3106 fflush(stderr);
3107}
3108
3109void SkPathOpsDebug::VerifySimplify(const SkPath& path, const SkPath& result) {
3110 SkPath pathOut, scaledPathOut;
3111 SkRegion rgnA, openClip, rgnOut;
3112 openClip.setRect(-16000, -16000, 16000, 16000);
3113 rgnA.setPath(path, openClip);
3114 rgnOut.getBoundaryPath(&pathOut);
3115 SkMatrix scale;
3116 debug_scale_matrix(path, nullptr, scale);
3117 SkRegion scaledRgnA;
3118 SkPath scaledA;
3119 scaledA.addPath(path, scale);
3120 scaledA.setFillType(path.getFillType());
3121 scaledRgnA.setPath(scaledA, openClip);
3122 scaledRgnA.getBoundaryPath(&scaledPathOut);
3123 SkBitmap bitmap;
3124 SkPath scaledOut;
3125 scaledOut.addPath(result, scale);
3126 scaledOut.setFillType(result.getFillType());
3127 int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
3128 const int MAX_ERRORS = 9;
3129 if (errors > MAX_ERRORS) {
3130 fprintf(stderr, "// Simplify did not expect errors=%d\n", errors);
3131 DumpSimplify(stderr, path, "simplifyTest");
3132 fflush(stderr);
3133 }
3134}
3135
3136#endif
Cary Clarkb8421ed2018-03-14 15:55:02 -04003137
3138// global path dumps for msvs Visual Studio 17 to use from Immediate Window
3139void Dump(const SkPath& path) {
3140 path.dump();
3141}
3142
3143void DumpHex(const SkPath& path) {
3144 path.dumpHex();
3145}