blob: a6555d1b20a8bceb156884643c27ab1b856e4397 [file] [log] [blame]
caryclark@google.com07393ca2013-04-08 11:47:37 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkPath.h"
9#include "include/core/SkString.h"
10#include "include/private/SkMutex.h"
11#include "src/core/SkOSFile.h"
Chris Dalton957189b2020-05-07 12:47:26 -060012#include "src/core/SkPathPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "src/pathops/SkOpCoincidence.h"
14#include "src/pathops/SkOpContour.h"
15#include "src/pathops/SkPathOpsDebug.h"
caryclark54359292015-03-26 07:52:43 -070016
Ben Wagnerf08d1d02018-06-18 15:11:00 -040017#include <utility>
18
Cary Clark918fb1f2016-11-15 13:22:25 -050019#if DEBUG_DUMP_VERIFY
caryclark13260682016-10-24 05:10:14 -070020bool SkPathOpsDebug::gDumpOp; // set to true to write op to file before a crash
21bool SkPathOpsDebug::gVerifyOp; // set to true to compare result against regions
22#endif
23
Cary Clark59d5a0e2017-01-23 14:38:52 +000024bool SkPathOpsDebug::gRunFail; // set to true to check for success on tests known to fail
caryclark13260682016-10-24 05:10:14 -070025bool SkPathOpsDebug::gVeryVerbose; // set to true to run extensive checking tests
26
caryclark30b9fdd2016-08-31 14:36:29 -070027#undef FAIL_IF
28#define FAIL_IF(cond, coin) \
Cary Clarkab87d7a2016-10-04 10:01:04 -040029 do { if (cond) log->record(SkPathOpsDebug::kFail_Glitch, coin); } while (false)
caryclark30b9fdd2016-08-31 14:36:29 -070030
31#undef FAIL_WITH_NULL_IF
32#define FAIL_WITH_NULL_IF(cond, span) \
Cary Clarkab87d7a2016-10-04 10:01:04 -040033 do { if (cond) log->record(SkPathOpsDebug::kFail_Glitch, span); } while (false)
caryclark30b9fdd2016-08-31 14:36:29 -070034
35#undef RETURN_FALSE_IF
36#define RETURN_FALSE_IF(cond, span) \
Cary Clarkab87d7a2016-10-04 10:01:04 -040037 do { if (cond) log->record(SkPathOpsDebug::kReturnFalse_Glitch, span); \
38 } while (false)
caryclark30b9fdd2016-08-31 14:36:29 -070039
caryclark55888e42016-07-18 10:01:36 -070040class SkCoincidentSpans;
caryclark26ad22a2015-10-16 09:03:38 -070041
caryclark624637c2015-05-11 07:21:27 -070042#if DEBUG_SORT
43int SkPathOpsDebug::gSortCountDefault = SK_MaxS32;
44int SkPathOpsDebug::gSortCount;
45#endif
46
47#if DEBUG_ACTIVE_OP
Ben Wagner3f985522017-10-09 10:47:47 -040048const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor", "rdiff"};
caryclark624637c2015-05-11 07:21:27 -070049#endif
50
caryclark@google.com07393ca2013-04-08 11:47:37 +000051#if defined SK_DEBUG || !FORCE_RELEASE
52
commit-bot@chromium.orgfe41b8f2014-04-25 14:03:52 +000053int SkPathOpsDebug::gContourID = 0;
54int SkPathOpsDebug::gSegmentID = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +000055
caryclark54359292015-03-26 07:52:43 -070056bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray,
57 const SkOpSpanBase* span) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000058 for (int index = 0; index < chaseArray.count(); ++index) {
caryclark54359292015-03-26 07:52:43 -070059 const SkOpSpanBase* entry = chaseArray[index];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000060 if (entry == span) {
61 return true;
62 }
63 }
64 return false;
65}
caryclark26ad22a2015-10-16 09:03:38 -070066#endif
Hal Canaryd4998172018-07-11 11:31:34 -040067
Ben Wagner29380bd2017-10-09 14:43:00 -040068#if DEBUG_ACTIVE_SPANS
69SkString SkPathOpsDebug::gActiveSpans;
Cary Clarkff114282016-12-14 11:56:16 -050070#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000071
Cary Clarkab87d7a2016-10-04 10:01:04 -040072#if DEBUG_COIN
caryclark26ad22a2015-10-16 09:03:38 -070073
Cary Clarkab87d7a2016-10-04 10:01:04 -040074SkPathOpsDebug::CoinDict SkPathOpsDebug::gCoinSumChangedDict;
75SkPathOpsDebug::CoinDict SkPathOpsDebug::gCoinSumVisitedDict;
76
77static const int kGlitchType_Count = SkPathOpsDebug::kUnalignedTail_Glitch + 1;
caryclark26ad22a2015-10-16 09:03:38 -070078
79struct SpanGlitch {
caryclark26ad22a2015-10-16 09:03:38 -070080 const SkOpSpanBase* fBase;
81 const SkOpSpanBase* fSuspect;
caryclark26ad22a2015-10-16 09:03:38 -070082 const SkOpSegment* fSegment;
caryclark55888e42016-07-18 10:01:36 -070083 const SkOpSegment* fOppSegment;
caryclark26ad22a2015-10-16 09:03:38 -070084 const SkOpPtT* fCoinSpan;
85 const SkOpPtT* fEndSpan;
86 const SkOpPtT* fOppSpan;
87 const SkOpPtT* fOppEndSpan;
caryclark55888e42016-07-18 10:01:36 -070088 double fStartT;
89 double fEndT;
90 double fOppStartT;
91 double fOppEndT;
caryclark26ad22a2015-10-16 09:03:38 -070092 SkPoint fPt;
Cary Clarkab87d7a2016-10-04 10:01:04 -040093 SkPathOpsDebug::GlitchType fType;
94
95 void dumpType() const;
caryclark26ad22a2015-10-16 09:03:38 -070096};
97
98struct SkPathOpsDebug::GlitchLog {
Cary Clarkab87d7a2016-10-04 10:01:04 -040099 void init(const SkOpGlobalState* state) {
100 fGlobalState = state;
101 }
102
103 SpanGlitch* recordCommon(GlitchType type) {
Cary Clarkea2a6322018-08-27 13:19:09 -0400104 SpanGlitch* glitch = fGlitches.push();
caryclark26ad22a2015-10-16 09:03:38 -0700105 glitch->fBase = nullptr;
106 glitch->fSuspect = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -0700107 glitch->fSegment = nullptr;
caryclark55888e42016-07-18 10:01:36 -0700108 glitch->fOppSegment = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -0700109 glitch->fCoinSpan = nullptr;
110 glitch->fEndSpan = nullptr;
111 glitch->fOppSpan = nullptr;
112 glitch->fOppEndSpan = nullptr;
caryclark55888e42016-07-18 10:01:36 -0700113 glitch->fStartT = SK_ScalarNaN;
114 glitch->fEndT = SK_ScalarNaN;
115 glitch->fOppStartT = SK_ScalarNaN;
116 glitch->fOppEndT = SK_ScalarNaN;
caryclark26ad22a2015-10-16 09:03:38 -0700117 glitch->fPt = { SK_ScalarNaN, SK_ScalarNaN };
118 glitch->fType = type;
119 return glitch;
120 }
121
Cary Clarkab87d7a2016-10-04 10:01:04 -0400122 void record(GlitchType type, const SkOpSpanBase* base,
caryclark26ad22a2015-10-16 09:03:38 -0700123 const SkOpSpanBase* suspect = NULL) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400124 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700125 glitch->fBase = base;
126 glitch->fSuspect = suspect;
127 }
128
Cary Clarkab87d7a2016-10-04 10:01:04 -0400129 void record(GlitchType type, const SkOpSpanBase* base,
caryclark55888e42016-07-18 10:01:36 -0700130 const SkOpPtT* ptT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400131 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700132 glitch->fBase = base;
133 glitch->fCoinSpan = ptT;
134 }
135
Cary Clarkab87d7a2016-10-04 10:01:04 -0400136 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark55888e42016-07-18 10:01:36 -0700137 const SkCoincidentSpans* opp = NULL) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400138 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700139 glitch->fCoinSpan = coin->coinPtTStart();
140 glitch->fEndSpan = coin->coinPtTEnd();
141 if (opp) {
142 glitch->fOppSpan = opp->coinPtTStart();
143 glitch->fOppEndSpan = opp->coinPtTEnd();
144 }
caryclark26ad22a2015-10-16 09:03:38 -0700145 }
146
Cary Clarkab87d7a2016-10-04 10:01:04 -0400147 void record(GlitchType type, const SkOpSpanBase* base,
caryclark26ad22a2015-10-16 09:03:38 -0700148 const SkOpSegment* seg, double t, SkPoint pt) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400149 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700150 glitch->fBase = base;
151 glitch->fSegment = seg;
caryclark55888e42016-07-18 10:01:36 -0700152 glitch->fStartT = t;
caryclark26ad22a2015-10-16 09:03:38 -0700153 glitch->fPt = pt;
154 }
155
Cary Clarkab87d7a2016-10-04 10:01:04 -0400156 void record(GlitchType type, const SkOpSpanBase* base, double t,
caryclark26ad22a2015-10-16 09:03:38 -0700157 SkPoint pt) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400158 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700159 glitch->fBase = base;
caryclark55888e42016-07-18 10:01:36 -0700160 glitch->fStartT = t;
caryclark26ad22a2015-10-16 09:03:38 -0700161 glitch->fPt = pt;
162 }
163
Cary Clarkab87d7a2016-10-04 10:01:04 -0400164 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark26ad22a2015-10-16 09:03:38 -0700165 const SkOpPtT* coinSpan, const SkOpPtT* endSpan) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400166 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700167 glitch->fCoinSpan = coin->coinPtTStart();
168 glitch->fEndSpan = coin->coinPtTEnd();
caryclark26ad22a2015-10-16 09:03:38 -0700169 glitch->fEndSpan = endSpan;
caryclark55888e42016-07-18 10:01:36 -0700170 glitch->fOppSpan = coinSpan;
171 glitch->fOppEndSpan = endSpan;
caryclark26ad22a2015-10-16 09:03:38 -0700172 }
173
Cary Clarkab87d7a2016-10-04 10:01:04 -0400174 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark55888e42016-07-18 10:01:36 -0700175 const SkOpSpanBase* base) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400176 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700177 glitch->fBase = base;
178 glitch->fCoinSpan = coin->coinPtTStart();
179 glitch->fEndSpan = coin->coinPtTEnd();
caryclark26ad22a2015-10-16 09:03:38 -0700180 }
181
Cary Clarkab87d7a2016-10-04 10:01:04 -0400182 void record(GlitchType type, const SkOpPtT* ptTS, const SkOpPtT* ptTE,
caryclark26ad22a2015-10-16 09:03:38 -0700183 const SkOpPtT* oPtTS, const SkOpPtT* oPtTE) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400184 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700185 glitch->fCoinSpan = ptTS;
186 glitch->fEndSpan = ptTE;
187 glitch->fOppSpan = oPtTS;
188 glitch->fOppEndSpan = oPtTE;
189 }
190
Cary Clarkab87d7a2016-10-04 10:01:04 -0400191 void record(GlitchType type, const SkOpSegment* seg, double startT,
caryclark55888e42016-07-18 10:01:36 -0700192 double endT, const SkOpSegment* oppSeg, double oppStartT, double oppEndT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400193 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700194 glitch->fSegment = seg;
195 glitch->fStartT = startT;
196 glitch->fEndT = endT;
197 glitch->fOppSegment = oppSeg;
198 glitch->fOppStartT = oppStartT;
199 glitch->fOppEndT = oppEndT;
200 }
201
Cary Clarkab87d7a2016-10-04 10:01:04 -0400202 void record(GlitchType type, const SkOpSegment* seg,
caryclark55888e42016-07-18 10:01:36 -0700203 const SkOpSpan* span) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400204 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700205 glitch->fSegment = seg;
206 glitch->fBase = span;
207 }
208
Cary Clarkab87d7a2016-10-04 10:01:04 -0400209 void record(GlitchType type, double t, const SkOpSpanBase* span) {
210 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700211 glitch->fStartT = t;
212 glitch->fBase = span;
213 }
214
Cary Clarkab87d7a2016-10-04 10:01:04 -0400215 void record(GlitchType type, const SkOpSegment* seg) {
216 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700217 glitch->fSegment = seg;
218 }
219
Cary Clarkab87d7a2016-10-04 10:01:04 -0400220 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark55888e42016-07-18 10:01:36 -0700221 const SkOpPtT* ptT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400222 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700223 glitch->fCoinSpan = coin->coinPtTStart();
224 glitch->fEndSpan = ptT;
225 }
226
caryclark26ad22a2015-10-16 09:03:38 -0700227 SkTDArray<SpanGlitch> fGlitches;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400228 const SkOpGlobalState* fGlobalState;
caryclark26ad22a2015-10-16 09:03:38 -0700229};
Cary Clarkab87d7a2016-10-04 10:01:04 -0400230
231
232void SkPathOpsDebug::CoinDict::add(const SkPathOpsDebug::CoinDict& dict) {
233 int count = dict.fDict.count();
234 for (int index = 0; index < count; ++index) {
235 this->add(dict.fDict[index]);
236 }
237}
238
239void SkPathOpsDebug::CoinDict::add(const CoinDictEntry& key) {
240 int count = fDict.count();
241 for (int index = 0; index < count; ++index) {
242 CoinDictEntry* entry = &fDict[index];
243 if (entry->fIteration == key.fIteration && entry->fLineNumber == key.fLineNumber) {
244 SkASSERT(!strcmp(entry->fFunctionName, key.fFunctionName));
245 if (entry->fGlitchType == kUninitialized_Glitch) {
246 entry->fGlitchType = key.fGlitchType;
247 }
248 return;
249 }
250 }
251 *fDict.append() = key;
252}
253
254#endif
255
256#if DEBUG_COIN
257static void missing_coincidence(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) {
258 const SkOpContour* contour = contourList;
259 // bool result = false;
260 do {
261 /* result |= */ contour->debugMissingCoincidence(glitches);
262 } while ((contour = contour->next()));
263 return;
264}
265
266static void move_multiples(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) {
267 const SkOpContour* contour = contourList;
268 do {
269 if (contour->debugMoveMultiples(glitches), false) {
270 return;
271 }
272 } while ((contour = contour->next()));
273 return;
274}
275
276static void move_nearby(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) {
277 const SkOpContour* contour = contourList;
278 do {
279 contour->debugMoveNearby(glitches);
280 } while ((contour = contour->next()));
281}
282
283
284#endif
285
286#if DEBUG_COIN
287void SkOpGlobalState::debugAddToCoinChangedDict() {
288
289#if DEBUG_COINCIDENCE
caryclarke6522ea2016-10-17 07:54:33 -0700290 SkPathOpsDebug::CheckHealth(fContourHead);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400291#endif
292 // see if next coincident operation makes a change; if so, record it
293 SkPathOpsDebug::GlitchLog glitches;
294 const char* funcName = fCoinDictEntry.fFunctionName;
295 if (!strcmp("calc_angles", funcName)) {
296 ;
297 } else if (!strcmp("missing_coincidence", funcName)) {
298 missing_coincidence(&glitches, fContourHead);
299 } else if (!strcmp("move_multiples", funcName)) {
300 move_multiples(&glitches, fContourHead);
301 } else if (!strcmp("move_nearby", funcName)) {
302 move_nearby(&glitches, fContourHead);
303 } else if (!strcmp("addExpanded", funcName)) {
304 fCoincidence->debugAddExpanded(&glitches);
305 } else if (!strcmp("addMissing", funcName)) {
306 bool added;
307 fCoincidence->debugAddMissing(&glitches, &added);
308 } else if (!strcmp("addEndMovedSpans", funcName)) {
309 fCoincidence->debugAddEndMovedSpans(&glitches);
310 } else if (!strcmp("correctEnds", funcName)) {
311 fCoincidence->debugCorrectEnds(&glitches);
312 } else if (!strcmp("expand", funcName)) {
313 fCoincidence->debugExpand(&glitches);
314 } else if (!strcmp("findOverlaps", funcName)) {
315 ;
316 } else if (!strcmp("mark", funcName)) {
317 fCoincidence->debugMark(&glitches);
318 } else if (!strcmp("apply", funcName)) {
319 ;
320 } else {
321 SkASSERT(0); // add missing case
322 }
323 if (glitches.fGlitches.count()) {
324 fCoinDictEntry.fGlitchType = glitches.fGlitches[0].fType;
325 }
326 fCoinChangedDict.add(fCoinDictEntry);
327}
caryclark55888e42016-07-18 10:01:36 -0700328#endif
caryclark26ad22a2015-10-16 09:03:38 -0700329
caryclark55888e42016-07-18 10:01:36 -0700330void SkPathOpsDebug::ShowActiveSpans(SkOpContourHead* contourList) {
331#if DEBUG_ACTIVE_SPANS
Cary Clarkff114282016-12-14 11:56:16 -0500332 SkString str;
caryclark55888e42016-07-18 10:01:36 -0700333 SkOpContour* contour = contourList;
334 do {
Cary Clarkff114282016-12-14 11:56:16 -0500335 contour->debugShowActiveSpans(&str);
caryclark55888e42016-07-18 10:01:36 -0700336 } while ((contour = contour->next()));
Ben Wagner29380bd2017-10-09 14:43:00 -0400337 if (!gActiveSpans.equals(str)) {
338 const char* s = str.c_str();
339 const char* end;
340 while ((end = strchr(s, '\n'))) {
341 SkDebugf("%.*s", end - s + 1, s);
342 s = end + 1;
343 }
344 gActiveSpans.set(str);
345 }
caryclark55888e42016-07-18 10:01:36 -0700346#endif
347}
348
Cary Clarkab87d7a2016-10-04 10:01:04 -0400349#if DEBUG_COINCIDENCE || DEBUG_COIN
350void SkPathOpsDebug::CheckHealth(SkOpContourHead* contourList) {
caryclark55888e42016-07-18 10:01:36 -0700351#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -0700352 contourList->globalState()->debugSetCheckHealth(true);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400353#endif
354#if DEBUG_COIN
caryclark26ad22a2015-10-16 09:03:38 -0700355 GlitchLog glitches;
356 const SkOpContour* contour = contourList;
357 const SkOpCoincidence* coincidence = contour->globalState()->coincidence();
Cary Clarkab87d7a2016-10-04 10:01:04 -0400358 coincidence->debugCheckValid(&glitches); // don't call validate; spans may be inconsistent
caryclark26ad22a2015-10-16 09:03:38 -0700359 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400360 contour->debugCheckHealth(&glitches);
361 contour->debugMissingCoincidence(&glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700362 } while ((contour = contour->next()));
caryclark81a478c2016-09-09 09:37:57 -0700363 bool added;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400364 coincidence->debugAddMissing(&glitches, &added);
365 coincidence->debugExpand(&glitches);
366 coincidence->debugAddExpanded(&glitches);
367 coincidence->debugMark(&glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700368 unsigned mask = 0;
369 for (int index = 0; index < glitches.fGlitches.count(); ++index) {
370 const SpanGlitch& glitch = glitches.fGlitches[index];
371 mask |= 1 << glitch.fType;
372 }
373 for (int index = 0; index < kGlitchType_Count; ++index) {
374 SkDebugf(mask & (1 << index) ? "x" : "-");
375 }
Cary Clarkff114282016-12-14 11:56:16 -0500376 SkDebugf(" %s\n", contourList->globalState()->debugCoinDictEntry().fFunctionName);
caryclark55888e42016-07-18 10:01:36 -0700377 for (int index = 0; index < glitches.fGlitches.count(); ++index) {
378 const SpanGlitch& glitch = glitches.fGlitches[index];
379 SkDebugf("%02d: ", index);
380 if (glitch.fBase) {
caryclark8016b262016-09-06 05:59:47 -0700381 SkDebugf(" seg/base=%d/%d", glitch.fBase->segment()->debugID(),
382 glitch.fBase->debugID());
caryclark55888e42016-07-18 10:01:36 -0700383 }
384 if (glitch.fSuspect) {
caryclark8016b262016-09-06 05:59:47 -0700385 SkDebugf(" seg/base=%d/%d", glitch.fSuspect->segment()->debugID(),
386 glitch.fSuspect->debugID());
caryclark55888e42016-07-18 10:01:36 -0700387 }
388 if (glitch.fSegment) {
389 SkDebugf(" segment=%d", glitch.fSegment->debugID());
390 }
391 if (glitch.fCoinSpan) {
caryclark8016b262016-09-06 05:59:47 -0700392 SkDebugf(" coinSeg/Span/PtT=%d/%d/%d", glitch.fCoinSpan->segment()->debugID(),
393 glitch.fCoinSpan->span()->debugID(), glitch.fCoinSpan->debugID());
caryclark55888e42016-07-18 10:01:36 -0700394 }
395 if (glitch.fEndSpan) {
396 SkDebugf(" endSpan=%d", glitch.fEndSpan->debugID());
397 }
398 if (glitch.fOppSpan) {
caryclark8016b262016-09-06 05:59:47 -0700399 SkDebugf(" oppSeg/Span/PtT=%d/%d/%d", glitch.fOppSpan->segment()->debugID(),
400 glitch.fOppSpan->span()->debugID(), glitch.fOppSpan->debugID());
caryclark55888e42016-07-18 10:01:36 -0700401 }
402 if (glitch.fOppEndSpan) {
403 SkDebugf(" oppEndSpan=%d", glitch.fOppEndSpan->debugID());
404 }
405 if (!SkScalarIsNaN(glitch.fStartT)) {
406 SkDebugf(" startT=%g", glitch.fStartT);
407 }
408 if (!SkScalarIsNaN(glitch.fEndT)) {
409 SkDebugf(" endT=%g", glitch.fEndT);
410 }
411 if (glitch.fOppSegment) {
412 SkDebugf(" segment=%d", glitch.fOppSegment->debugID());
413 }
414 if (!SkScalarIsNaN(glitch.fOppStartT)) {
415 SkDebugf(" oppStartT=%g", glitch.fOppStartT);
416 }
417 if (!SkScalarIsNaN(glitch.fOppEndT)) {
418 SkDebugf(" oppEndT=%g", glitch.fOppEndT);
419 }
420 if (!SkScalarIsNaN(glitch.fPt.fX) || !SkScalarIsNaN(glitch.fPt.fY)) {
421 SkDebugf(" pt=%g,%g", glitch.fPt.fX, glitch.fPt.fY);
422 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400423 DumpGlitchType(glitch.fType);
caryclark55888e42016-07-18 10:01:36 -0700424 SkDebugf("\n");
425 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400426#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -0700427 contourList->globalState()->debugSetCheckHealth(false);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400428#endif
caryclark6c3b9cd2016-09-26 05:36:58 -0700429#if 01 && DEBUG_ACTIVE_SPANS
Cary Clarkab87d7a2016-10-04 10:01:04 -0400430// SkDebugf("active after %s:\n", id);
caryclark55888e42016-07-18 10:01:36 -0700431 ShowActiveSpans(contourList);
432#endif
433#endif
caryclark26ad22a2015-10-16 09:03:38 -0700434}
435#endif
436
Cary Clarkab87d7a2016-10-04 10:01:04 -0400437#if DEBUG_COIN
438void SkPathOpsDebug::DumpGlitchType(GlitchType glitchType) {
439 switch (glitchType) {
440 case kAddCorruptCoin_Glitch: SkDebugf(" AddCorruptCoin"); break;
441 case kAddExpandedCoin_Glitch: SkDebugf(" AddExpandedCoin"); break;
442 case kAddExpandedFail_Glitch: SkDebugf(" AddExpandedFail"); break;
Kevin Lubick48ce5432019-01-04 08:54:32 -0500443 case kAddIfCollapsed_Glitch: SkDebugf(" AddIfCollapsed"); break;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400444 case kAddIfMissingCoin_Glitch: SkDebugf(" AddIfMissingCoin"); break;
445 case kAddMissingCoin_Glitch: SkDebugf(" AddMissingCoin"); break;
446 case kAddMissingExtend_Glitch: SkDebugf(" AddMissingExtend"); break;
447 case kAddOrOverlap_Glitch: SkDebugf(" AAddOrOverlap"); break;
448 case kCollapsedCoin_Glitch: SkDebugf(" CollapsedCoin"); break;
449 case kCollapsedDone_Glitch: SkDebugf(" CollapsedDone"); break;
450 case kCollapsedOppValue_Glitch: SkDebugf(" CollapsedOppValue"); break;
451 case kCollapsedSpan_Glitch: SkDebugf(" CollapsedSpan"); break;
452 case kCollapsedWindValue_Glitch: SkDebugf(" CollapsedWindValue"); break;
453 case kCorrectEnd_Glitch: SkDebugf(" CorrectEnd"); break;
454 case kDeletedCoin_Glitch: SkDebugf(" DeletedCoin"); break;
455 case kExpandCoin_Glitch: SkDebugf(" ExpandCoin"); break;
456 case kFail_Glitch: SkDebugf(" Fail"); break;
457 case kMarkCoinEnd_Glitch: SkDebugf(" MarkCoinEnd"); break;
458 case kMarkCoinInsert_Glitch: SkDebugf(" MarkCoinInsert"); break;
459 case kMarkCoinMissing_Glitch: SkDebugf(" MarkCoinMissing"); break;
460 case kMarkCoinStart_Glitch: SkDebugf(" MarkCoinStart"); break;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400461 case kMergeMatches_Glitch: SkDebugf(" MergeMatches"); break;
462 case kMissingCoin_Glitch: SkDebugf(" MissingCoin"); break;
463 case kMissingDone_Glitch: SkDebugf(" MissingDone"); break;
464 case kMissingIntersection_Glitch: SkDebugf(" MissingIntersection"); break;
465 case kMoveMultiple_Glitch: SkDebugf(" MoveMultiple"); break;
466 case kMoveNearbyClearAll_Glitch: SkDebugf(" MoveNearbyClearAll"); break;
467 case kMoveNearbyClearAll2_Glitch: SkDebugf(" MoveNearbyClearAll2"); break;
468 case kMoveNearbyMerge_Glitch: SkDebugf(" MoveNearbyMerge"); break;
469 case kMoveNearbyMergeFinal_Glitch: SkDebugf(" MoveNearbyMergeFinal"); break;
470 case kMoveNearbyRelease_Glitch: SkDebugf(" MoveNearbyRelease"); break;
471 case kMoveNearbyReleaseFinal_Glitch: SkDebugf(" MoveNearbyReleaseFinal"); break;
472 case kReleasedSpan_Glitch: SkDebugf(" ReleasedSpan"); break;
473 case kReturnFalse_Glitch: SkDebugf(" ReturnFalse"); break;
474 case kUnaligned_Glitch: SkDebugf(" Unaligned"); break;
475 case kUnalignedHead_Glitch: SkDebugf(" UnalignedHead"); break;
476 case kUnalignedTail_Glitch: SkDebugf(" UnalignedTail"); break;
477 case kUninitialized_Glitch: break;
478 default: SkASSERT(0);
479 }
480}
481#endif
482
caryclark26ad22a2015-10-16 09:03:38 -0700483#if defined SK_DEBUG || !FORCE_RELEASE
caryclark@google.com570863f2013-09-16 15:55:01 +0000484void SkPathOpsDebug::MathematicaIze(char* str, size_t bufferLen) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000485 size_t len = strlen(str);
486 bool num = false;
487 for (size_t idx = 0; idx < len; ++idx) {
488 if (num && str[idx] == 'e') {
489 if (len + 2 >= bufferLen) {
490 return;
491 }
492 memmove(&str[idx + 2], &str[idx + 1], len - idx);
493 str[idx] = '*';
494 str[idx + 1] = '^';
495 ++len;
496 }
497 num = str[idx] >= '0' && str[idx] <= '9';
498 }
499}
500
caryclark@google.com570863f2013-09-16 15:55:01 +0000501bool SkPathOpsDebug::ValidWind(int wind) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000502 return wind > SK_MinS32 + 0xFFFF && wind < SK_MaxS32 - 0xFFFF;
503}
504
caryclark@google.com570863f2013-09-16 15:55:01 +0000505void SkPathOpsDebug::WindingPrintf(int wind) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000506 if (wind == SK_MinS32) {
507 SkDebugf("?");
508 } else {
509 SkDebugf("%d", wind);
510 }
511}
caryclark54359292015-03-26 07:52:43 -0700512#endif // defined SK_DEBUG || !FORCE_RELEASE
513
caryclark@google.coma5e55922013-05-07 18:51:31 +0000514
caryclark1049f122015-04-20 08:31:59 -0700515static void show_function_header(const char* functionName) {
516 SkDebugf("\nstatic void %s(skiatest::Reporter* reporter, const char* filename) {\n", functionName);
517 if (strcmp("skphealth_com76", functionName) == 0) {
518 SkDebugf("found it\n");
519 }
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000520}
caryclark1049f122015-04-20 08:31:59 -0700521
522static const char* gOpStrs[] = {
523 "kDifference_SkPathOp",
524 "kIntersect_SkPathOp",
525 "kUnion_SkPathOp",
caryclark55888e42016-07-18 10:01:36 -0700526 "kXOR_PathOp",
caryclark1049f122015-04-20 08:31:59 -0700527 "kReverseDifference_SkPathOp",
528};
529
caryclark03b03ca2015-04-23 09:13:37 -0700530const char* SkPathOpsDebug::OpStr(SkPathOp op) {
531 return gOpStrs[op];
532}
533
caryclark1049f122015-04-20 08:31:59 -0700534static void show_op(SkPathOp op, const char* pathOne, const char* pathTwo) {
535 SkDebugf(" testPathOp(reporter, %s, %s, %s, filename);\n", pathOne, pathTwo, gOpStrs[op]);
536 SkDebugf("}\n");
537}
538
caryclark1049f122015-04-20 08:31:59 -0700539void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
540 const char* testName) {
Herb Derby9c71e7b2019-06-17 14:40:42 -0400541 static SkMutex& mutex = *(new SkMutex);
542
543 SkAutoMutexExclusive ac(mutex);
caryclark1049f122015-04-20 08:31:59 -0700544 show_function_header(testName);
545 ShowOnePath(a, "path", true);
546 ShowOnePath(b, "pathB", true);
547 show_op(shapeOp, "path", "pathB");
548}
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000549
Mike Kleinc0bd9f92019-04-23 12:05:21 -0500550#include "src/pathops/SkIntersectionHelper.h"
551#include "src/pathops/SkIntersections.h"
552#include "src/pathops/SkPathOpsTypes.h"
caryclark26ad22a2015-10-16 09:03:38 -0700553
Cary Clarkab87d7a2016-10-04 10:01:04 -0400554#if DEBUG_COIN
555
Cary Clarkab87d7a2016-10-04 10:01:04 -0400556void SkOpGlobalState::debugAddToGlobalCoinDicts() {
Herb Derby9c71e7b2019-06-17 14:40:42 -0400557 static SkMutex& mutex = *(new SkMutex);
558 SkAutoMutexExclusive ac(mutex);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400559 SkPathOpsDebug::gCoinSumChangedDict.add(fCoinChangedDict);
560 SkPathOpsDebug::gCoinSumVisitedDict.add(fCoinVisitedDict);
561}
562
563#endif
564
caryclark26ad22a2015-10-16 09:03:38 -0700565#if DEBUG_T_SECT_LOOP_COUNT
566void SkOpGlobalState::debugAddLoopCount(SkIntersections* i, const SkIntersectionHelper& wt,
567 const SkIntersectionHelper& wn) {
568 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
569 SkIntersections::DebugLoop looper = (SkIntersections::DebugLoop) index;
570 if (fDebugLoopCount[index] >= i->debugLoopCount(looper)) {
571 continue;
572 }
573 fDebugLoopCount[index] = i->debugLoopCount(looper);
574 fDebugWorstVerb[index * 2] = wt.segment()->verb();
575 fDebugWorstVerb[index * 2 + 1] = wn.segment()->verb();
576 sk_bzero(&fDebugWorstPts[index * 8], sizeof(SkPoint) * 8);
577 memcpy(&fDebugWorstPts[index * 2 * 4], wt.pts(),
578 (SkPathOpsVerbToPoints(wt.segment()->verb()) + 1) * sizeof(SkPoint));
579 memcpy(&fDebugWorstPts[(index * 2 + 1) * 4], wn.pts(),
580 (SkPathOpsVerbToPoints(wn.segment()->verb()) + 1) * sizeof(SkPoint));
581 fDebugWorstWeight[index * 2] = wt.weight();
582 fDebugWorstWeight[index * 2 + 1] = wn.weight();
583 }
584 i->debugResetLoopCount();
585}
586
587void SkOpGlobalState::debugDoYourWorst(SkOpGlobalState* local) {
588 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
589 if (fDebugLoopCount[index] >= local->fDebugLoopCount[index]) {
590 continue;
591 }
592 fDebugLoopCount[index] = local->fDebugLoopCount[index];
593 fDebugWorstVerb[index * 2] = local->fDebugWorstVerb[index * 2];
594 fDebugWorstVerb[index * 2 + 1] = local->fDebugWorstVerb[index * 2 + 1];
595 memcpy(&fDebugWorstPts[index * 2 * 4], &local->fDebugWorstPts[index * 2 * 4],
596 sizeof(SkPoint) * 8);
597 fDebugWorstWeight[index * 2] = local->fDebugWorstWeight[index * 2];
598 fDebugWorstWeight[index * 2 + 1] = local->fDebugWorstWeight[index * 2 + 1];
599 }
600 local->debugResetLoopCounts();
601}
602
603static void dump_curve(SkPath::Verb verb, const SkPoint& pts, float weight) {
604 if (!verb) {
605 return;
606 }
607 const char* verbs[] = { "", "line", "quad", "conic", "cubic" };
608 SkDebugf("%s: {{", verbs[verb]);
609 int ptCount = SkPathOpsVerbToPoints(verb);
610 for (int index = 0; index <= ptCount; ++index) {
611 SkDPoint::Dump((&pts)[index]);
612 if (index < ptCount - 1) {
613 SkDebugf(", ");
614 }
615 }
616 SkDebugf("}");
617 if (weight != 1) {
618 SkDebugf(", ");
619 if (weight == floorf(weight)) {
620 SkDebugf("%.0f", weight);
621 } else {
622 SkDebugf("%1.9gf", weight);
623 }
624 }
625 SkDebugf("}\n");
626}
627
628void SkOpGlobalState::debugLoopReport() {
629 const char* loops[] = { "iterations", "coinChecks", "perpCalcs" };
630 SkDebugf("\n");
631 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
632 SkDebugf("%s: %d\n", loops[index], fDebugLoopCount[index]);
633 dump_curve(fDebugWorstVerb[index * 2], fDebugWorstPts[index * 2 * 4],
634 fDebugWorstWeight[index * 2]);
635 dump_curve(fDebugWorstVerb[index * 2 + 1], fDebugWorstPts[(index * 2 + 1) * 4],
636 fDebugWorstWeight[index * 2 + 1]);
637 }
638}
639
640void SkOpGlobalState::debugResetLoopCounts() {
641 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
642 sk_bzero(fDebugWorstVerb, sizeof(fDebugWorstVerb));
643 sk_bzero(fDebugWorstPts, sizeof(fDebugWorstPts));
644 sk_bzero(fDebugWorstWeight, sizeof(fDebugWorstWeight));
645}
646#endif
caryclark27c8eb82015-07-06 11:38:33 -0700647
Cary Clark59d5a0e2017-01-23 14:38:52 +0000648bool SkOpGlobalState::DebugRunFail() {
649 return SkPathOpsDebug::gRunFail;
650}
651
Cary Clarkab87d7a2016-10-04 10:01:04 -0400652// this is const so it can be called by const methods that overwise don't alter state
653#if DEBUG_VALIDATE || DEBUG_COIN
654void SkOpGlobalState::debugSetPhase(const char* funcName DEBUG_COIN_DECLARE_PARAMS()) const {
655 auto writable = const_cast<SkOpGlobalState*>(this);
656#if DEBUG_VALIDATE
657 writable->setPhase(phase);
658#endif
659#if DEBUG_COIN
660 SkPathOpsDebug::CoinDictEntry* entry = &writable->fCoinDictEntry;
661 writable->fPreviousFuncName = entry->fFunctionName;
662 entry->fIteration = iteration;
663 entry->fLineNumber = lineNo;
664 entry->fGlitchType = SkPathOpsDebug::kUninitialized_Glitch;
665 entry->fFunctionName = funcName;
666 writable->fCoinVisitedDict.add(*entry);
667 writable->debugAddToCoinChangedDict();
668#endif
669}
670#endif
671
caryclark26ad22a2015-10-16 09:03:38 -0700672#if DEBUG_T_SECT_LOOP_COUNT
673void SkIntersections::debugBumpLoopCount(DebugLoop index) {
674 fDebugLoopCount[index]++;
675}
676
677int SkIntersections::debugLoopCount(DebugLoop index) const {
678 return fDebugLoopCount[index];
679}
680
681void SkIntersections::debugResetLoopCount() {
682 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
683}
684#endif
685
Mike Kleinc0bd9f92019-04-23 12:05:21 -0500686#include "src/pathops/SkPathOpsConic.h"
687#include "src/pathops/SkPathOpsCubic.h"
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000688
caryclark624637c2015-05-11 07:21:27 -0700689SkDCubic SkDQuad::debugToCubic() const {
690 SkDCubic cubic;
691 cubic[0] = fPts[0];
692 cubic[2] = fPts[1];
693 cubic[3] = fPts[2];
694 cubic[1].fX = (cubic[0].fX + cubic[2].fX * 2) / 3;
695 cubic[1].fY = (cubic[0].fY + cubic[2].fY * 2) / 3;
696 cubic[2].fX = (cubic[3].fX + cubic[2].fX * 2) / 3;
697 cubic[2].fY = (cubic[3].fY + cubic[2].fY * 2) / 3;
698 return cubic;
caryclark54359292015-03-26 07:52:43 -0700699}
caryclark624637c2015-05-11 07:21:27 -0700700
caryclarka35ab3e2016-10-20 08:32:18 -0700701void SkDQuad::debugSet(const SkDPoint* pts) {
702 memcpy(fPts, pts, sizeof(fPts));
703 SkDEBUGCODE(fDebugGlobalState = nullptr);
704}
705
706void SkDCubic::debugSet(const SkDPoint* pts) {
707 memcpy(fPts, pts, sizeof(fPts));
708 SkDEBUGCODE(fDebugGlobalState = nullptr);
709}
710
711void SkDConic::debugSet(const SkDPoint* pts, SkScalar weight) {
712 fPts.debugSet(pts);
713 fWeight = weight;
714}
715
caryclarked0935a2015-10-22 07:23:52 -0700716void SkDRect::debugInit() {
717 fLeft = fTop = fRight = fBottom = SK_ScalarNaN;
718}
719
Mike Kleinc0bd9f92019-04-23 12:05:21 -0500720#include "src/pathops/SkOpAngle.h"
721#include "src/pathops/SkOpSegment.h"
caryclark54359292015-03-26 07:52:43 -0700722
Cary Clarkab87d7a2016-10-04 10:01:04 -0400723#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -0700724// commented-out lines keep this in sync with addT()
Cary Clarkab87d7a2016-10-04 10:01:04 -0400725 const SkOpPtT* SkOpSegment::debugAddT(double t, SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -0700726 debugValidate();
727 SkPoint pt = this->ptAtT(t);
caryclark26ad22a2015-10-16 09:03:38 -0700728 const SkOpSpanBase* span = &fHead;
caryclark55888e42016-07-18 10:01:36 -0700729 do {
730 const SkOpPtT* result = span->ptT();
caryclark29b25632016-08-25 11:27:17 -0700731 if (t == result->fT || this->match(result, this, t, pt)) {
caryclark55888e42016-07-18 10:01:36 -0700732// span->bumpSpanAdds();
caryclarkef4f32a2016-08-24 09:24:18 -0700733 return result;
caryclark26ad22a2015-10-16 09:03:38 -0700734 }
caryclark55888e42016-07-18 10:01:36 -0700735 if (t < result->fT) {
736 const SkOpSpan* prev = result->span()->prev();
caryclark30b9fdd2016-08-31 14:36:29 -0700737 FAIL_WITH_NULL_IF(!prev, span);
caryclark29b25632016-08-25 11:27:17 -0700738 // marks in global state that new op span has been allocated
739 this->globalState()->setAllocatedOpSpan();
caryclark55888e42016-07-18 10:01:36 -0700740// span->init(this, prev, t, pt);
741 this->debugValidate();
742// #if DEBUG_ADD_T
743// SkDebugf("%s insert t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
744// span->segment()->debugID(), span->debugID());
745// #endif
746// span->bumpSpanAdds();
caryclark55888e42016-07-18 10:01:36 -0700747 return nullptr;
caryclark26ad22a2015-10-16 09:03:38 -0700748 }
caryclark30b9fdd2016-08-31 14:36:29 -0700749 FAIL_WITH_NULL_IF(span != &fTail, span);
caryclark55888e42016-07-18 10:01:36 -0700750 } while ((span = span->upCast()->next()));
751 SkASSERT(0);
caryclark29b25632016-08-25 11:27:17 -0700752 return nullptr; // we never get here, but need this to satisfy compiler
caryclark26ad22a2015-10-16 09:03:38 -0700753}
754#endif
755
756#if DEBUG_ANGLE
757void SkOpSegment::debugCheckAngleCoin() const {
758 const SkOpSpanBase* base = &fHead;
759 const SkOpSpan* span;
760 do {
761 const SkOpAngle* angle = base->fromAngle();
caryclark55888e42016-07-18 10:01:36 -0700762 if (angle && angle->debugCheckCoincidence()) {
caryclark26ad22a2015-10-16 09:03:38 -0700763 angle->debugCheckNearCoincidence();
764 }
765 if (base->final()) {
766 break;
767 }
768 span = base->upCast();
769 angle = span->toAngle();
caryclark55888e42016-07-18 10:01:36 -0700770 if (angle && angle->debugCheckCoincidence()) {
caryclark26ad22a2015-10-16 09:03:38 -0700771 angle->debugCheckNearCoincidence();
772 }
773 } while ((base = span->next()));
774}
775#endif
776
Cary Clarkab87d7a2016-10-04 10:01:04 -0400777#if DEBUG_COIN
caryclark26ad22a2015-10-16 09:03:38 -0700778// this mimics the order of the checks in handle coincidence
Cary Clarkab87d7a2016-10-04 10:01:04 -0400779void SkOpSegment::debugCheckHealth(SkPathOpsDebug::GlitchLog* glitches) const {
780 debugMoveMultiples(glitches);
781 debugMoveNearby(glitches);
782 debugMissingCoincidence(glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700783}
784
caryclark55888e42016-07-18 10:01:36 -0700785// commented-out lines keep this in sync with clearAll()
Cary Clarkab87d7a2016-10-04 10:01:04 -0400786void SkOpSegment::debugClearAll(SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -0700787 const SkOpSpan* span = &fHead;
788 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400789 this->debugClearOne(span, glitches);
caryclark55888e42016-07-18 10:01:36 -0700790 } while ((span = span->next()->upCastable()));
Cary Clarkab87d7a2016-10-04 10:01:04 -0400791 this->globalState()->coincidence()->debugRelease(glitches, this);
caryclark55888e42016-07-18 10:01:36 -0700792}
793
794// commented-out lines keep this in sync with clearOne()
Cary Clarkab87d7a2016-10-04 10:01:04 -0400795void SkOpSegment::debugClearOne(const SkOpSpan* span, SkPathOpsDebug::GlitchLog* glitches) const {
796 if (span->windValue()) glitches->record(SkPathOpsDebug::kCollapsedWindValue_Glitch, span);
797 if (span->oppValue()) glitches->record(SkPathOpsDebug::kCollapsedOppValue_Glitch, span);
798 if (!span->done()) glitches->record(SkPathOpsDebug::kCollapsedDone_Glitch, span);
caryclark26ad22a2015-10-16 09:03:38 -0700799}
800#endif
801
caryclark54359292015-03-26 07:52:43 -0700802SkOpAngle* SkOpSegment::debugLastAngle() {
halcanary96fcdcc2015-08-27 07:41:13 -0700803 SkOpAngle* result = nullptr;
caryclark54359292015-03-26 07:52:43 -0700804 SkOpSpan* span = this->head();
805 do {
806 if (span->toAngle()) {
807 SkASSERT(!result);
808 result = span->toAngle();
809 }
810 } while ((span = span->next()->upCastable()));
811 SkASSERT(result);
812 return result;
813}
814
Cary Clarkab87d7a2016-10-04 10:01:04 -0400815#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -0700816// commented-out lines keep this in sync with ClearVisited
817void SkOpSegment::DebugClearVisited(const SkOpSpanBase* span) {
818 // reset visited flag back to false
819 do {
820 const SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
821 while ((ptT = ptT->next()) != stopPtT) {
822 const SkOpSegment* opp = ptT->segment();
823 opp->resetDebugVisited();
824 }
825 } while (!span->final() && (span = span->upCast()->next()));
826}
827#endif
828
Cary Clarkab87d7a2016-10-04 10:01:04 -0400829#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -0700830// commented-out lines keep this in sync with missingCoincidence()
831// look for pairs of undetected coincident curves
832// assumes that segments going in have visited flag clear
833// Even though pairs of curves correct detect coincident runs, a run may be missed
834// if the coincidence is a product of multiple intersections. For instance, given
835// curves A, B, and C:
836// A-B intersect at a point 1; A-C and B-C intersect at point 2, so near
837// the end of C that the intersection is replaced with the end of C.
838// Even though A-B correctly do not detect an intersection at point 2,
839// the resulting run from point 1 to point 2 is coincident on A and B.
Cary Clarkab87d7a2016-10-04 10:01:04 -0400840void SkOpSegment::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -0700841 if (this->done()) {
842 return;
843 }
844 const SkOpSpan* prior = nullptr;
845 const SkOpSpanBase* spanBase = &fHead;
caryclark55888e42016-07-18 10:01:36 -0700846// bool result = false;
caryclark26ad22a2015-10-16 09:03:38 -0700847 do {
848 const SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT;
849 SkASSERT(ptT->span() == spanBase);
850 while ((ptT = ptT->next()) != spanStopPtT) {
851 if (ptT->deleted()) {
852 continue;
853 }
caryclark55888e42016-07-18 10:01:36 -0700854 const SkOpSegment* opp = ptT->span()->segment();
caryclark26ad22a2015-10-16 09:03:38 -0700855 if (opp->done()) {
856 continue;
857 }
858 // when opp is encounted the 1st time, continue; on 2nd encounter, look for coincidence
caryclark55888e42016-07-18 10:01:36 -0700859 if (!opp->debugVisited()) {
caryclark26ad22a2015-10-16 09:03:38 -0700860 continue;
861 }
862 if (spanBase == &fHead) {
863 continue;
864 }
caryclark55888e42016-07-18 10:01:36 -0700865 if (ptT->segment() == this) {
866 continue;
867 }
caryclark26ad22a2015-10-16 09:03:38 -0700868 const SkOpSpan* span = spanBase->upCastable();
869 // FIXME?: this assumes that if the opposite segment is coincident then no more
870 // coincidence needs to be detected. This may not be true.
caryclark55888e42016-07-18 10:01:36 -0700871 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 -0700872 continue;
873 }
caryclark55888e42016-07-18 10:01:36 -0700874 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 -0700875 continue;
caryclark55888e42016-07-18 10:01:36 -0700876 }
caryclark26ad22a2015-10-16 09:03:38 -0700877 const SkOpPtT* priorPtT = nullptr, * priorStopPtT;
878 // find prior span containing opp segment
879 const SkOpSegment* priorOpp = nullptr;
880 const SkOpSpan* priorTest = spanBase->prev();
881 while (!priorOpp && priorTest) {
882 priorStopPtT = priorPtT = priorTest->ptT();
883 while ((priorPtT = priorPtT->next()) != priorStopPtT) {
884 if (priorPtT->deleted()) {
885 continue;
886 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400887 const SkOpSegment* segment = priorPtT->span()->segment();
caryclark26ad22a2015-10-16 09:03:38 -0700888 if (segment == opp) {
889 prior = priorTest;
890 priorOpp = opp;
891 break;
892 }
893 }
894 priorTest = priorTest->prev();
895 }
896 if (!priorOpp) {
897 continue;
898 }
caryclark55888e42016-07-18 10:01:36 -0700899 if (priorPtT == ptT) {
900 continue;
901 }
caryclark26ad22a2015-10-16 09:03:38 -0700902 const SkOpPtT* oppStart = prior->ptT();
903 const SkOpPtT* oppEnd = spanBase->ptT();
904 bool swapped = priorPtT->fT > ptT->fT;
905 if (swapped) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -0400906 using std::swap;
907 swap(priorPtT, ptT);
908 swap(oppStart, oppEnd);
caryclark26ad22a2015-10-16 09:03:38 -0700909 }
caryclark55888e42016-07-18 10:01:36 -0700910 const SkOpCoincidence* coincidence = this->globalState()->coincidence();
911 const SkOpPtT* rootPriorPtT = priorPtT->span()->ptT();
912 const SkOpPtT* rootPtT = ptT->span()->ptT();
913 const SkOpPtT* rootOppStart = oppStart->span()->ptT();
914 const SkOpPtT* rootOppEnd = oppEnd->span()->ptT();
915 if (coincidence->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) {
caryclark26ad22a2015-10-16 09:03:38 -0700916 goto swapBack;
917 }
caryclark55888e42016-07-18 10:01:36 -0700918 if (testForCoincidence(rootPriorPtT, rootPtT, prior, spanBase, opp)) {
919 // mark coincidence
caryclark30b9fdd2016-08-31 14:36:29 -0700920#if DEBUG_COINCIDENCE_VERBOSE
caryclark55888e42016-07-18 10:01:36 -0700921// SkDebugf("%s coinSpan=%d endSpan=%d oppSpan=%d oppEndSpan=%d\n", __FUNCTION__,
922// rootPriorPtT->debugID(), rootPtT->debugID(), rootOppStart->debugID(),
923// rootOppEnd->debugID());
924#endif
Cary Clarkab87d7a2016-10-04 10:01:04 -0400925 log->record(SkPathOpsDebug::kMissingCoin_Glitch, priorPtT, ptT, oppStart, oppEnd);
caryclark55888e42016-07-18 10:01:36 -0700926 // coincidences->add(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
927 // }
928#if DEBUG_COINCIDENCE
caryclarkd6562002016-07-27 12:02:07 -0700929// SkASSERT(coincidences->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
caryclark55888e42016-07-18 10:01:36 -0700930#endif
931 // result = true;
caryclark26ad22a2015-10-16 09:03:38 -0700932 }
933 swapBack:
934 if (swapped) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -0400935 using std::swap;
936 swap(priorPtT, ptT);
caryclark26ad22a2015-10-16 09:03:38 -0700937 }
938 }
939 } while ((spanBase = spanBase->final() ? nullptr : spanBase->upCast()->next()));
caryclark55888e42016-07-18 10:01:36 -0700940 DebugClearVisited(&fHead);
941 return;
caryclark26ad22a2015-10-16 09:03:38 -0700942}
943
caryclark55888e42016-07-18 10:01:36 -0700944// commented-out lines keep this in sync with moveMultiples()
945// if a span has more than one intersection, merge the other segments' span as needed
Cary Clarkab87d7a2016-10-04 10:01:04 -0400946void SkOpSegment::debugMoveMultiples(SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -0700947 debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -0700948 const SkOpSpanBase* test = &fHead;
949 do {
950 int addCount = test->spanAddsCount();
Cary Clarkff114282016-12-14 11:56:16 -0500951// SkASSERT(addCount >= 1);
952 if (addCount <= 1) {
caryclark26ad22a2015-10-16 09:03:38 -0700953 continue;
954 }
955 const SkOpPtT* startPtT = test->ptT();
956 const SkOpPtT* testPtT = startPtT;
957 do { // iterate through all spans associated with start
958 const SkOpSpanBase* oppSpan = testPtT->span();
959 if (oppSpan->spanAddsCount() == addCount) {
960 continue;
961 }
962 if (oppSpan->deleted()) {
963 continue;
964 }
965 const SkOpSegment* oppSegment = oppSpan->segment();
966 if (oppSegment == this) {
967 continue;
968 }
969 // find range of spans to consider merging
970 const SkOpSpanBase* oppPrev = oppSpan;
971 const SkOpSpanBase* oppFirst = oppSpan;
972 while ((oppPrev = oppPrev->prev())) {
973 if (!roughly_equal(oppPrev->t(), oppSpan->t())) {
974 break;
975 }
976 if (oppPrev->spanAddsCount() == addCount) {
977 continue;
978 }
979 if (oppPrev->deleted()) {
980 continue;
981 }
982 oppFirst = oppPrev;
983 }
984 const SkOpSpanBase* oppNext = oppSpan;
985 const SkOpSpanBase* oppLast = oppSpan;
986 while ((oppNext = oppNext->final() ? nullptr : oppNext->upCast()->next())) {
987 if (!roughly_equal(oppNext->t(), oppSpan->t())) {
988 break;
989 }
990 if (oppNext->spanAddsCount() == addCount) {
991 continue;
992 }
993 if (oppNext->deleted()) {
994 continue;
995 }
996 oppLast = oppNext;
997 }
998 if (oppFirst == oppLast) {
999 continue;
1000 }
1001 const SkOpSpanBase* oppTest = oppFirst;
1002 do {
1003 if (oppTest == oppSpan) {
1004 continue;
1005 }
1006 // check to see if the candidate meets specific criteria:
1007 // it contains spans of segments in test's loop but not including 'this'
1008 const SkOpPtT* oppStartPtT = oppTest->ptT();
1009 const SkOpPtT* oppPtT = oppStartPtT;
1010 while ((oppPtT = oppPtT->next()) != oppStartPtT) {
1011 const SkOpSegment* oppPtTSegment = oppPtT->segment();
1012 if (oppPtTSegment == this) {
1013 goto tryNextSpan;
1014 }
1015 const SkOpPtT* matchPtT = startPtT;
1016 do {
1017 if (matchPtT->segment() == oppPtTSegment) {
1018 goto foundMatch;
1019 }
1020 } while ((matchPtT = matchPtT->next()) != startPtT);
1021 goto tryNextSpan;
1022 foundMatch: // merge oppTest and oppSpan
caryclark55888e42016-07-18 10:01:36 -07001023 oppSegment->debugValidate();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001024 oppTest->debugMergeMatches(glitches, oppSpan);
1025 oppTest->debugAddOpp(glitches, oppSpan);
caryclark55888e42016-07-18 10:01:36 -07001026 oppSegment->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001027 goto checkNextSpan;
1028 }
caryclark55888e42016-07-18 10:01:36 -07001029 tryNextSpan:
caryclark26ad22a2015-10-16 09:03:38 -07001030 ;
1031 } while (oppTest != oppLast && (oppTest = oppTest->upCast()->next()));
1032 } while ((testPtT = testPtT->next()) != startPtT);
caryclark55888e42016-07-18 10:01:36 -07001033checkNextSpan:
caryclark26ad22a2015-10-16 09:03:38 -07001034 ;
1035 } while ((test = test->final() ? nullptr : test->upCast()->next()));
caryclark55888e42016-07-18 10:01:36 -07001036 debugValidate();
1037 return;
caryclark26ad22a2015-10-16 09:03:38 -07001038}
1039
caryclark55888e42016-07-18 10:01:36 -07001040// commented-out lines keep this in sync with moveNearby()
1041// Move nearby t values and pts so they all hang off the same span. Alignment happens later.
Cary Clarkab87d7a2016-10-04 10:01:04 -04001042void SkOpSegment::debugMoveNearby(SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -07001043 debugValidate();
1044 // release undeleted spans pointing to this seg that are linked to the primary span
1045 const SkOpSpanBase* spanBase = &fHead;
caryclark26ad22a2015-10-16 09:03:38 -07001046 do {
caryclark55888e42016-07-18 10:01:36 -07001047 const SkOpPtT* ptT = spanBase->ptT();
1048 const SkOpPtT* headPtT = ptT;
1049 while ((ptT = ptT->next()) != headPtT) {
1050 const SkOpSpanBase* test = ptT->span();
1051 if (ptT->segment() == this && !ptT->deleted() && test != spanBase
1052 && test->ptT() == ptT) {
1053 if (test->final()) {
1054 if (spanBase == &fHead) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001055 glitches->record(SkPathOpsDebug::kMoveNearbyClearAll_Glitch, this);
caryclark55888e42016-07-18 10:01:36 -07001056// return;
1057 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001058 glitches->record(SkPathOpsDebug::kMoveNearbyReleaseFinal_Glitch, spanBase, ptT);
caryclark55888e42016-07-18 10:01:36 -07001059 } else if (test->prev()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001060 glitches->record(SkPathOpsDebug::kMoveNearbyRelease_Glitch, test, headPtT);
caryclark55888e42016-07-18 10:01:36 -07001061 }
1062// break;
caryclark26ad22a2015-10-16 09:03:38 -07001063 }
1064 }
caryclark55888e42016-07-18 10:01:36 -07001065 spanBase = spanBase->upCast()->next();
1066 } while (!spanBase->final());
1067
1068 // This loop looks for adjacent spans which are near by
1069 spanBase = &fHead;
1070 do { // iterate through all spans associated with start
1071 const SkOpSpanBase* test = spanBase->upCast()->next();
Cary Clark73e597d2017-04-18 12:08:58 -04001072 bool found;
1073 if (!this->spansNearby(spanBase, test, &found)) {
1074 glitches->record(SkPathOpsDebug::kMoveNearbyMergeFinal_Glitch, test);
1075 }
1076 if (found) {
caryclark55888e42016-07-18 10:01:36 -07001077 if (test->final()) {
1078 if (spanBase->prev()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001079 glitches->record(SkPathOpsDebug::kMoveNearbyMergeFinal_Glitch, test);
caryclark55888e42016-07-18 10:01:36 -07001080 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001081 glitches->record(SkPathOpsDebug::kMoveNearbyClearAll2_Glitch, this);
caryclark55888e42016-07-18 10:01:36 -07001082 // return
1083 }
1084 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001085 glitches->record(SkPathOpsDebug::kMoveNearbyMerge_Glitch, spanBase);
caryclark55888e42016-07-18 10:01:36 -07001086 }
1087 }
1088 spanBase = test;
1089 } while (!spanBase->final());
1090 debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001091}
1092#endif
1093
caryclark54359292015-03-26 07:52:43 -07001094void SkOpSegment::debugReset() {
caryclark1049f122015-04-20 08:31:59 -07001095 this->init(this->fPts, this->fWeight, this->contour(), this->verb());
caryclark54359292015-03-26 07:52:43 -07001096}
1097
caryclark025b11e2016-08-25 05:21:14 -07001098#if DEBUG_COINCIDENCE_ORDER
1099void SkOpSegment::debugSetCoinT(int index, SkScalar t) const {
1100 if (fDebugBaseMax < 0 || fDebugBaseIndex == index) {
1101 fDebugBaseIndex = index;
Brian Osman788b9162020-02-07 10:36:46 -05001102 fDebugBaseMin = std::min(t, fDebugBaseMin);
1103 fDebugBaseMax = std::max(t, fDebugBaseMax);
caryclark025b11e2016-08-25 05:21:14 -07001104 return;
Ben Wagner63fd7602017-10-09 15:45:33 -04001105 }
caryclark025b11e2016-08-25 05:21:14 -07001106 SkASSERT(fDebugBaseMin >= t || t >= fDebugBaseMax);
1107 if (fDebugLastMax < 0 || fDebugLastIndex == index) {
1108 fDebugLastIndex = index;
Brian Osman788b9162020-02-07 10:36:46 -05001109 fDebugLastMin = std::min(t, fDebugLastMin);
1110 fDebugLastMax = std::max(t, fDebugLastMax);
caryclark025b11e2016-08-25 05:21:14 -07001111 return;
1112 }
1113 SkASSERT(fDebugLastMin >= t || t >= fDebugLastMax);
1114 SkASSERT((t - fDebugBaseMin > 0) == (fDebugLastMin - fDebugBaseMin > 0));
1115}
1116#endif
1117
caryclark54359292015-03-26 07:52:43 -07001118#if DEBUG_ACTIVE_SPANS
Cary Clarkff114282016-12-14 11:56:16 -05001119void SkOpSegment::debugShowActiveSpans(SkString* str) const {
caryclark54359292015-03-26 07:52:43 -07001120 debugValidate();
1121 if (done()) {
1122 return;
1123 }
1124 int lastId = -1;
1125 double lastT = -1;
1126 const SkOpSpan* span = &fHead;
1127 do {
1128 if (span->done()) {
1129 continue;
1130 }
caryclark1049f122015-04-20 08:31:59 -07001131 if (lastId == this->debugID() && lastT == span->t()) {
caryclark54359292015-03-26 07:52:43 -07001132 continue;
1133 }
caryclark1049f122015-04-20 08:31:59 -07001134 lastId = this->debugID();
caryclark54359292015-03-26 07:52:43 -07001135 lastT = span->t();
Cary Clarkff114282016-12-14 11:56:16 -05001136 str->appendf("%s id=%d", __FUNCTION__, this->debugID());
caryclark55888e42016-07-18 10:01:36 -07001137 // since endpoints may have be adjusted, show actual computed curves
1138 SkDCurve curvePart;
1139 this->subDivide(span, span->next(), &curvePart);
1140 const SkDPoint* pts = curvePart.fCubic.fPts;
Cary Clarkff114282016-12-14 11:56:16 -05001141 str->appendf(" (%1.9g,%1.9g", pts[0].fX, pts[0].fY);
caryclark54359292015-03-26 07:52:43 -07001142 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
Cary Clarkff114282016-12-14 11:56:16 -05001143 str->appendf(" %1.9g,%1.9g", pts[vIndex].fX, pts[vIndex].fY);
caryclark54359292015-03-26 07:52:43 -07001144 }
caryclark1049f122015-04-20 08:31:59 -07001145 if (SkPath::kConic_Verb == fVerb) {
Cary Clarkff114282016-12-14 11:56:16 -05001146 str->appendf(" %1.9gf", curvePart.fConic.fWeight);
caryclark1049f122015-04-20 08:31:59 -07001147 }
Cary Clarkff114282016-12-14 11:56:16 -05001148 str->appendf(") t=%1.9g tEnd=%1.9g", span->t(), span->next()->t());
caryclark54359292015-03-26 07:52:43 -07001149 if (span->windSum() == SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001150 str->appendf(" windSum=?");
caryclark54359292015-03-26 07:52:43 -07001151 } else {
Cary Clarkff114282016-12-14 11:56:16 -05001152 str->appendf(" windSum=%d", span->windSum());
caryclark54359292015-03-26 07:52:43 -07001153 }
caryclark624637c2015-05-11 07:21:27 -07001154 if (span->oppValue() && span->oppSum() == SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001155 str->appendf(" oppSum=?");
caryclark624637c2015-05-11 07:21:27 -07001156 } else if (span->oppValue() || span->oppSum() != SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001157 str->appendf(" oppSum=%d", span->oppSum());
caryclark624637c2015-05-11 07:21:27 -07001158 }
Cary Clarkff114282016-12-14 11:56:16 -05001159 str->appendf(" windValue=%d", span->windValue());
caryclark624637c2015-05-11 07:21:27 -07001160 if (span->oppValue() || span->oppSum() != SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001161 str->appendf(" oppValue=%d", span->oppValue());
caryclark624637c2015-05-11 07:21:27 -07001162 }
Cary Clarkff114282016-12-14 11:56:16 -05001163 str->appendf("\n");
caryclark54359292015-03-26 07:52:43 -07001164 } while ((span = span->next()->upCastable()));
1165}
1166#endif
1167
1168#if DEBUG_MARK_DONE
1169void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding) {
1170 const SkPoint& pt = span->ptT()->fPt;
caryclark1049f122015-04-20 08:31:59 -07001171 SkDebugf("%s id=%d", fun, this->debugID());
caryclark54359292015-03-26 07:52:43 -07001172 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1173 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1174 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1175 }
1176 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
1177 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t());
1178 if (winding == SK_MinS32) {
1179 SkDebugf("?");
1180 } else {
1181 SkDebugf("%d", winding);
1182 }
1183 SkDebugf(" windSum=");
1184 if (span->windSum() == SK_MinS32) {
1185 SkDebugf("?");
1186 } else {
1187 SkDebugf("%d", span->windSum());
1188 }
1189 SkDebugf(" windValue=%d\n", span->windValue());
1190}
1191
1192void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding,
1193 int oppWinding) {
1194 const SkPoint& pt = span->ptT()->fPt;
caryclark1049f122015-04-20 08:31:59 -07001195 SkDebugf("%s id=%d", fun, this->debugID());
caryclark54359292015-03-26 07:52:43 -07001196 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1197 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1198 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1199 }
1200 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
1201 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t(), winding, oppWinding);
1202 if (winding == SK_MinS32) {
1203 SkDebugf("?");
1204 } else {
1205 SkDebugf("%d", winding);
1206 }
1207 SkDebugf(" newOppSum=");
1208 if (oppWinding == SK_MinS32) {
1209 SkDebugf("?");
1210 } else {
1211 SkDebugf("%d", oppWinding);
1212 }
1213 SkDebugf(" oppSum=");
1214 if (span->oppSum() == SK_MinS32) {
1215 SkDebugf("?");
1216 } else {
1217 SkDebugf("%d", span->oppSum());
1218 }
1219 SkDebugf(" windSum=");
1220 if (span->windSum() == SK_MinS32) {
1221 SkDebugf("?");
1222 } else {
1223 SkDebugf("%d", span->windSum());
1224 }
1225 SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue());
1226}
1227
1228#endif
1229
caryclark26ad22a2015-10-16 09:03:38 -07001230// loop looking for a pair of angle parts that are too close to be sorted
1231/* This is called after other more simple intersection and angle sorting tests have been exhausted.
1232 This should be rarely called -- the test below is thorough and time consuming.
caryclark55888e42016-07-18 10:01:36 -07001233 This checks the distance between start points; the distance between
caryclark26ad22a2015-10-16 09:03:38 -07001234*/
1235#if DEBUG_ANGLE
1236void SkOpAngle::debugCheckNearCoincidence() const {
1237 const SkOpAngle* test = this;
1238 do {
1239 const SkOpSegment* testSegment = test->segment();
1240 double testStartT = test->start()->t();
1241 SkDPoint testStartPt = testSegment->dPtAtT(testStartT);
1242 double testEndT = test->end()->t();
1243 SkDPoint testEndPt = testSegment->dPtAtT(testEndT);
1244 double testLenSq = testStartPt.distanceSquared(testEndPt);
1245 SkDebugf("%s testLenSq=%1.9g id=%d\n", __FUNCTION__, testLenSq, testSegment->debugID());
1246 double testMidT = (testStartT + testEndT) / 2;
1247 const SkOpAngle* next = test;
1248 while ((next = next->fNext) != this) {
1249 SkOpSegment* nextSegment = next->segment();
1250 double testMidDistSq = testSegment->distSq(testMidT, next);
1251 double testEndDistSq = testSegment->distSq(testEndT, next);
1252 double nextStartT = next->start()->t();
1253 SkDPoint nextStartPt = nextSegment->dPtAtT(nextStartT);
1254 double distSq = testStartPt.distanceSquared(nextStartPt);
1255 double nextEndT = next->end()->t();
1256 double nextMidT = (nextStartT + nextEndT) / 2;
1257 double nextMidDistSq = nextSegment->distSq(nextMidT, test);
1258 double nextEndDistSq = nextSegment->distSq(nextEndT, test);
1259 SkDebugf("%s distSq=%1.9g testId=%d nextId=%d\n", __FUNCTION__, distSq,
1260 testSegment->debugID(), nextSegment->debugID());
1261 SkDebugf("%s testMidDistSq=%1.9g\n", __FUNCTION__, testMidDistSq);
1262 SkDebugf("%s testEndDistSq=%1.9g\n", __FUNCTION__, testEndDistSq);
1263 SkDebugf("%s nextMidDistSq=%1.9g\n", __FUNCTION__, nextMidDistSq);
1264 SkDebugf("%s nextEndDistSq=%1.9g\n", __FUNCTION__, nextEndDistSq);
1265 SkDPoint nextEndPt = nextSegment->dPtAtT(nextEndT);
1266 double nextLenSq = nextStartPt.distanceSquared(nextEndPt);
1267 SkDebugf("%s nextLenSq=%1.9g\n", __FUNCTION__, nextLenSq);
1268 SkDebugf("\n");
1269 }
1270 test = test->fNext;
caryclark55888e42016-07-18 10:01:36 -07001271 } while (test->fNext != this);
caryclark26ad22a2015-10-16 09:03:38 -07001272}
1273#endif
1274
caryclark54359292015-03-26 07:52:43 -07001275#if DEBUG_ANGLE
1276SkString SkOpAngle::debugPart() const {
1277 SkString result;
1278 switch (this->segment()->verb()) {
1279 case SkPath::kLine_Verb:
caryclarkeed356d2016-09-14 07:18:20 -07001280 result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fPart.fCurve),
caryclark54359292015-03-26 07:52:43 -07001281 this->segment()->debugID());
1282 break;
1283 case SkPath::kQuad_Verb:
caryclarkeed356d2016-09-14 07:18:20 -07001284 result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fPart.fCurve),
caryclark54359292015-03-26 07:52:43 -07001285 this->segment()->debugID());
1286 break;
caryclark1049f122015-04-20 08:31:59 -07001287 case SkPath::kConic_Verb:
1288 result.printf(CONIC_DEBUG_STR " id=%d",
caryclarkeed356d2016-09-14 07:18:20 -07001289 CONIC_DEBUG_DATA(fPart.fCurve, fPart.fCurve.fConic.fWeight),
caryclark1049f122015-04-20 08:31:59 -07001290 this->segment()->debugID());
1291 break;
caryclark54359292015-03-26 07:52:43 -07001292 case SkPath::kCubic_Verb:
caryclarkeed356d2016-09-14 07:18:20 -07001293 result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fPart.fCurve),
caryclark54359292015-03-26 07:52:43 -07001294 this->segment()->debugID());
1295 break;
1296 default:
1297 SkASSERT(0);
mtklein1b249332015-07-07 12:21:21 -07001298 }
caryclark54359292015-03-26 07:52:43 -07001299 return result;
1300}
1301#endif
1302
caryclark624637c2015-05-11 07:21:27 -07001303#if DEBUG_SORT
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001304void SkOpAngle::debugLoop() const {
caryclarke4097e32014-06-18 07:24:19 -07001305 const SkOpAngle* first = this;
1306 const SkOpAngle* next = this;
1307 do {
1308 next->dumpOne(true);
caryclark19eb3b22014-07-18 05:08:14 -07001309 SkDebugf("\n");
caryclarke4097e32014-06-18 07:24:19 -07001310 next = next->fNext;
1311 } while (next && next != first);
caryclark54359292015-03-26 07:52:43 -07001312 next = first;
1313 do {
1314 next->debugValidate();
1315 next = next->fNext;
1316 } while (next && next != first);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001317}
1318#endif
1319
caryclark54359292015-03-26 07:52:43 -07001320void SkOpAngle::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07001321#if DEBUG_COINCIDENCE
1322 if (this->globalState()->debugCheckHealth()) {
1323 return;
1324 }
1325#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001326#if DEBUG_VALIDATE
caryclark54359292015-03-26 07:52:43 -07001327 const SkOpAngle* first = this;
1328 const SkOpAngle* next = this;
1329 int wind = 0;
1330 int opp = 0;
1331 int lastXor = -1;
1332 int lastOppXor = -1;
1333 do {
1334 if (next->unorderable()) {
1335 return;
1336 }
1337 const SkOpSpan* minSpan = next->start()->starter(next->end());
1338 if (minSpan->windValue() == SK_MinS32) {
1339 return;
1340 }
1341 bool op = next->segment()->operand();
1342 bool isXor = next->segment()->isXor();
1343 bool oppXor = next->segment()->oppXor();
1344 SkASSERT(!DEBUG_LIMIT_WIND_SUM || between(0, minSpan->windValue(), DEBUG_LIMIT_WIND_SUM));
1345 SkASSERT(!DEBUG_LIMIT_WIND_SUM
1346 || between(-DEBUG_LIMIT_WIND_SUM, minSpan->oppValue(), DEBUG_LIMIT_WIND_SUM));
1347 bool useXor = op ? oppXor : isXor;
1348 SkASSERT(lastXor == -1 || lastXor == (int) useXor);
1349 lastXor = (int) useXor;
caryclark624637c2015-05-11 07:21:27 -07001350 wind += next->debugSign() * (op ? minSpan->oppValue() : minSpan->windValue());
caryclark54359292015-03-26 07:52:43 -07001351 if (useXor) {
1352 wind &= 1;
1353 }
1354 useXor = op ? isXor : oppXor;
1355 SkASSERT(lastOppXor == -1 || lastOppXor == (int) useXor);
1356 lastOppXor = (int) useXor;
caryclark624637c2015-05-11 07:21:27 -07001357 opp += next->debugSign() * (op ? minSpan->windValue() : minSpan->oppValue());
caryclark54359292015-03-26 07:52:43 -07001358 if (useXor) {
1359 opp &= 1;
1360 }
1361 next = next->fNext;
1362 } while (next && next != first);
Cary Clark59d5a0e2017-01-23 14:38:52 +00001363 SkASSERT(wind == 0 || !SkPathOpsDebug::gRunFail);
1364 SkASSERT(opp == 0 || !SkPathOpsDebug::gRunFail);
caryclark54359292015-03-26 07:52:43 -07001365#endif
1366}
1367
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001368void SkOpAngle::debugValidateNext() const {
caryclark54359292015-03-26 07:52:43 -07001369#if !FORCE_RELEASE
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001370 const SkOpAngle* first = this;
1371 const SkOpAngle* next = first;
1372 SkTDArray<const SkOpAngle*>(angles);
1373 do {
djsollenf2b340f2016-01-29 08:51:04 -08001374// SkASSERT_RELEASE(next->fSegment->debugContains(next));
Mike Reedb5475792018-08-08 16:17:42 -04001375 angles.push_back(next);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001376 next = next->next();
1377 if (next == first) {
1378 break;
1379 }
djsollenf2b340f2016-01-29 08:51:04 -08001380 SkASSERT_RELEASE(!angles.contains(next));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001381 if (!next) {
1382 return;
1383 }
1384 } while (true);
reed0dc4dd62015-03-24 13:55:33 -07001385#endif
reed0dc4dd62015-03-24 13:55:33 -07001386}
reed0dc4dd62015-03-24 13:55:33 -07001387
caryclark55888e42016-07-18 10:01:36 -07001388#ifdef SK_DEBUG
1389void SkCoincidentSpans::debugStartCheck(const SkOpSpanBase* outer, const SkOpSpanBase* over,
1390 const SkOpGlobalState* debugState) const {
Cary Clark59d5a0e2017-01-23 14:38:52 +00001391 SkASSERT(coinPtTEnd()->span() == over || !SkOpGlobalState::DebugRunFail());
1392 SkASSERT(oppPtTEnd()->span() == outer || !SkOpGlobalState::DebugRunFail());
caryclark55888e42016-07-18 10:01:36 -07001393}
1394#endif
caryclark26ad22a2015-10-16 09:03:38 -07001395
Cary Clarkab87d7a2016-10-04 10:01:04 -04001396#if DEBUG_COIN
1397// sets the span's end to the ptT referenced by the previous-next
1398void SkCoincidentSpans::debugCorrectOneEnd(SkPathOpsDebug::GlitchLog* log,
1399 const SkOpPtT* (SkCoincidentSpans::* getEnd)() const,
1400 void (SkCoincidentSpans::*setEnd)(const SkOpPtT* ptT) const ) const {
1401 const SkOpPtT* origPtT = (this->*getEnd)();
1402 const SkOpSpanBase* origSpan = origPtT->span();
1403 const SkOpSpan* prev = origSpan->prev();
1404 const SkOpPtT* testPtT = prev ? prev->next()->ptT()
1405 : origSpan->upCast()->next()->prev()->ptT();
1406 if (origPtT != testPtT) {
1407 log->record(SkPathOpsDebug::kCorrectEnd_Glitch, this, origPtT, testPtT);
1408 }
1409}
1410
1411
1412/* Commented-out lines keep this in sync with correctEnds */
1413// FIXME: member pointers have fallen out of favor and can be replaced with
1414// an alternative approach.
1415// makes all span ends agree with the segment's spans that define them
1416void SkCoincidentSpans::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
1417 this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTStart, nullptr);
1418 this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTEnd, nullptr);
1419 this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTStart, nullptr);
1420 this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTEnd, nullptr);
1421}
1422
caryclark55888e42016-07-18 10:01:36 -07001423/* Commented-out lines keep this in sync with expand */
caryclark30b9fdd2016-08-31 14:36:29 -07001424// expand the range by checking adjacent spans for coincidence
Cary Clarkab87d7a2016-10-04 10:01:04 -04001425bool SkCoincidentSpans::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07001426 bool expanded = false;
1427 const SkOpSegment* segment = coinPtTStart()->segment();
1428 const SkOpSegment* oppSegment = oppPtTStart()->segment();
1429 do {
1430 const SkOpSpan* start = coinPtTStart()->span()->upCast();
1431 const SkOpSpan* prev = start->prev();
1432 const SkOpPtT* oppPtT;
1433 if (!prev || !(oppPtT = prev->contains(oppSegment))) {
1434 break;
1435 }
1436 double midT = (prev->t() + start->t()) / 2;
1437 if (!segment->isClose(midT, oppSegment)) {
1438 break;
1439 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001440 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, prev->ptT(), oppPtT);
caryclark55888e42016-07-18 10:01:36 -07001441 expanded = true;
1442 } while (false); // actual continues while expansion is possible
1443 do {
1444 const SkOpSpanBase* end = coinPtTEnd()->span();
1445 SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
caryclark30b9fdd2016-08-31 14:36:29 -07001446 if (next && next->deleted()) {
1447 break;
1448 }
caryclark55888e42016-07-18 10:01:36 -07001449 const SkOpPtT* oppPtT;
1450 if (!next || !(oppPtT = next->contains(oppSegment))) {
1451 break;
1452 }
1453 double midT = (end->t() + next->t()) / 2;
1454 if (!segment->isClose(midT, oppSegment)) {
1455 break;
1456 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001457 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, next->ptT(), oppPtT);
caryclark55888e42016-07-18 10:01:36 -07001458 expanded = true;
1459 } while (false); // actual continues while expansion is possible
1460 return expanded;
1461}
1462
Cary Clarkab87d7a2016-10-04 10:01:04 -04001463// description below
1464void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* base, const SkOpSpanBase* testSpan) const {
1465 const SkOpPtT* testPtT = testSpan->ptT();
1466 const SkOpPtT* stopPtT = testPtT;
1467 const SkOpSegment* baseSeg = base->segment();
1468 while ((testPtT = testPtT->next()) != stopPtT) {
1469 const SkOpSegment* testSeg = testPtT->segment();
1470 if (testPtT->deleted()) {
1471 continue;
1472 }
1473 if (testSeg == baseSeg) {
1474 continue;
1475 }
1476 if (testPtT->span()->ptT() != testPtT) {
1477 continue;
1478 }
1479 if (this->contains(baseSeg, testSeg, testPtT->fT)) {
1480 continue;
1481 }
1482 // intersect perp with base->ptT() with testPtT->segment()
1483 SkDVector dxdy = baseSeg->dSlopeAtT(base->t());
1484 const SkPoint& pt = base->pt();
1485 SkDLine ray = {{{pt.fX, pt.fY}, {pt.fX + dxdy.fY, pt.fY - dxdy.fX}}};
1486 SkIntersections i;
1487 (*CurveIntersectRay[testSeg->verb()])(testSeg->pts(), testSeg->weight(), ray, &i);
1488 for (int index = 0; index < i.used(); ++index) {
1489 double t = i[0][index];
1490 if (!between(0, t, 1)) {
1491 continue;
1492 }
1493 SkDPoint oppPt = i.pt(index);
1494 if (!oppPt.approximatelyEqual(pt)) {
1495 continue;
1496 }
1497 SkOpSegment* writableSeg = const_cast<SkOpSegment*>(testSeg);
1498 SkOpPtT* oppStart = writableSeg->addT(t);
1499 if (oppStart == testPtT) {
1500 continue;
1501 }
1502 SkOpSpan* writableBase = const_cast<SkOpSpan*>(base);
1503 oppStart->span()->addOpp(writableBase);
1504 if (oppStart->deleted()) {
1505 continue;
1506 }
1507 SkOpSegment* coinSeg = base->segment();
1508 SkOpSegment* oppSeg = oppStart->segment();
1509 double coinTs, coinTe, oppTs, oppTe;
1510 if (Ordered(coinSeg, oppSeg)) {
1511 coinTs = base->t();
1512 coinTe = testSpan->t();
1513 oppTs = oppStart->fT;
1514 oppTe = testPtT->fT;
1515 } else {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001516 using std::swap;
1517 swap(coinSeg, oppSeg);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001518 coinTs = oppStart->fT;
1519 coinTe = testPtT->fT;
1520 oppTs = base->t();
1521 oppTe = testSpan->t();
1522 }
1523 if (coinTs > coinTe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001524 using std::swap;
1525 swap(coinTs, coinTe);
1526 swap(oppTs, oppTe);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001527 }
1528 bool added;
1529 if (this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &added), false) {
1530 return;
1531 }
1532 }
1533 }
1534 return;
1535}
1536
1537// description below
1538void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* ptT) const {
1539 FAIL_IF(!ptT->span()->upCastable(), ptT->span());
1540 const SkOpSpan* base = ptT->span()->upCast();
1541 const SkOpSpan* prev = base->prev();
1542 FAIL_IF(!prev, ptT->span());
1543 if (!prev->isCanceled()) {
1544 if (this->debugAddEndMovedSpans(log, base, base->prev()), false) {
1545 return;
1546 }
1547 }
1548 if (!base->isCanceled()) {
1549 if (this->debugAddEndMovedSpans(log, base, base->next()), false) {
1550 return;
1551 }
1552 }
1553 return;
1554}
1555
1556/* If A is coincident with B and B includes an endpoint, and A's matching point
1557 is not the endpoint (i.e., there's an implied line connecting B-end and A)
1558 then assume that the same implied line may intersect another curve close to B.
1559 Since we only care about coincidence that was undetected, look at the
1560 ptT list on B-segment adjacent to the B-end/A ptT loop (not in the loop, but
1561 next door) and see if the A matching point is close enough to form another
1562 coincident pair. If so, check for a new coincident span between B-end/A ptT loop
1563 and the adjacent ptT loop.
1564*/
1565void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log) const {
1566 const SkCoincidentSpans* span = fHead;
1567 if (!span) {
1568 return;
1569 }
1570// fTop = span;
1571// fHead = nullptr;
1572 do {
1573 if (span->coinPtTStart()->fPt != span->oppPtTStart()->fPt) {
1574 FAIL_IF(1 == span->coinPtTStart()->fT, span);
1575 bool onEnd = span->coinPtTStart()->fT == 0;
1576 bool oOnEnd = zero_or_one(span->oppPtTStart()->fT);
1577 if (onEnd) {
1578 if (!oOnEnd) { // if both are on end, any nearby intersect was already found
1579 if (this->debugAddEndMovedSpans(log, span->oppPtTStart()), false) {
1580 return;
1581 }
1582 }
1583 } else if (oOnEnd) {
1584 if (this->debugAddEndMovedSpans(log, span->coinPtTStart()), false) {
1585 return;
1586 }
1587 }
1588 }
1589 if (span->coinPtTEnd()->fPt != span->oppPtTEnd()->fPt) {
1590 bool onEnd = span->coinPtTEnd()->fT == 1;
1591 bool oOnEnd = zero_or_one(span->oppPtTEnd()->fT);
1592 if (onEnd) {
1593 if (!oOnEnd) {
1594 if (this->debugAddEndMovedSpans(log, span->oppPtTEnd()), false) {
1595 return;
1596 }
1597 }
1598 } else if (oOnEnd) {
1599 if (this->debugAddEndMovedSpans(log, span->coinPtTEnd()), false) {
1600 return;
1601 }
1602 }
1603 }
1604 } while ((span = span->next()));
1605// this->restoreHead();
1606 return;
1607}
1608
caryclark55888e42016-07-18 10:01:36 -07001609/* Commented-out lines keep this in sync with addExpanded */
1610// for each coincident pair, match the spans
1611// 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 -04001612void SkOpCoincidence::debugAddExpanded(SkPathOpsDebug::GlitchLog* log) const {
caryclarka35ab3e2016-10-20 08:32:18 -07001613// DEBUG_SET_PHASE();
caryclark26ad22a2015-10-16 09:03:38 -07001614 const SkCoincidentSpans* coin = this->fHead;
1615 if (!coin) {
caryclarked0935a2015-10-22 07:23:52 -07001616 return;
1617 }
caryclark26ad22a2015-10-16 09:03:38 -07001618 do {
caryclark55888e42016-07-18 10:01:36 -07001619 const SkOpPtT* startPtT = coin->coinPtTStart();
1620 const SkOpPtT* oStartPtT = coin->oppPtTStart();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001621 double priorT = startPtT->fT;
1622 double oPriorT = oStartPtT->fT;
Cary Clarkff114282016-12-14 11:56:16 -05001623 FAIL_IF(!startPtT->contains(oStartPtT), coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001624 SkOPASSERT(coin->coinPtTEnd()->contains(coin->oppPtTEnd()));
caryclark26ad22a2015-10-16 09:03:38 -07001625 const SkOpSpanBase* start = startPtT->span();
1626 const SkOpSpanBase* oStart = oStartPtT->span();
caryclark55888e42016-07-18 10:01:36 -07001627 const SkOpSpanBase* end = coin->coinPtTEnd()->span();
1628 const SkOpSpanBase* oEnd = coin->oppPtTEnd()->span();
caryclark30b9fdd2016-08-31 14:36:29 -07001629 FAIL_IF(oEnd->deleted(), coin);
1630 FAIL_IF(!start->upCastable(), coin);
caryclark26ad22a2015-10-16 09:03:38 -07001631 const SkOpSpanBase* test = start->upCast()->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001632 FAIL_IF(!coin->flipped() && !oStart->upCastable(), coin);
caryclark55888e42016-07-18 10:01:36 -07001633 const SkOpSpanBase* oTest = coin->flipped() ? oStart->prev() : oStart->upCast()->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001634 FAIL_IF(!oTest, coin);
1635 const SkOpSegment* seg = start->segment();
1636 const SkOpSegment* oSeg = oStart->segment();
caryclark26ad22a2015-10-16 09:03:38 -07001637 while (test != end || oTest != oEnd) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001638 const SkOpPtT* containedOpp = test->ptT()->contains(oSeg);
1639 const SkOpPtT* containedThis = oTest->ptT()->contains(seg);
1640 if (!containedOpp || !containedThis) {
1641 // choose the ends, or the first common pt-t list shared by both
1642 double nextT, oNextT;
1643 if (containedOpp) {
1644 nextT = test->t();
1645 oNextT = containedOpp->fT;
1646 } else if (containedThis) {
1647 nextT = containedThis->fT;
1648 oNextT = oTest->t();
1649 } else {
1650 // iterate through until a pt-t list found that contains the other
1651 const SkOpSpanBase* walk = test;
1652 const SkOpPtT* walkOpp;
1653 do {
1654 FAIL_IF(!walk->upCastable(), coin);
1655 walk = walk->upCast()->next();
1656 } while (!(walkOpp = walk->ptT()->contains(oSeg))
1657 && walk != coin->coinPtTEnd()->span());
caryclarka35ab3e2016-10-20 08:32:18 -07001658 FAIL_IF(!walkOpp, coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001659 nextT = walk->t();
1660 oNextT = walkOpp->fT;
1661 }
caryclark26ad22a2015-10-16 09:03:38 -07001662 // use t ranges to guess which one is missing
caryclarka35ab3e2016-10-20 08:32:18 -07001663 double startRange = nextT - priorT;
caryclark30b9fdd2016-08-31 14:36:29 -07001664 FAIL_IF(!startRange, coin);
caryclarka35ab3e2016-10-20 08:32:18 -07001665 double startPart = (test->t() - priorT) / startRange;
1666 double oStartRange = oNextT - oPriorT;
caryclark30b9fdd2016-08-31 14:36:29 -07001667 FAIL_IF(!oStartRange, coin);
caryclark26ad22a2015-10-16 09:03:38 -07001668 double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange;
caryclark30b9fdd2016-08-31 14:36:29 -07001669 FAIL_IF(startPart == oStartPart, coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001670 bool addToOpp = !containedOpp && !containedThis ? startPart < oStartPart
1671 : !!containedThis;
caryclark55888e42016-07-18 10:01:36 -07001672 bool startOver = false;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001673 addToOpp ? log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch,
1674 oPriorT + oStartRange * startPart, test)
1675 : log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch,
1676 priorT + startRange * oStartPart, oTest);
1677 // FAIL_IF(!success, coin);
caryclark55888e42016-07-18 10:01:36 -07001678 if (startOver) {
1679 test = start;
1680 oTest = oStart;
caryclark26ad22a2015-10-16 09:03:38 -07001681 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001682 end = coin->coinPtTEnd()->span();
1683 oEnd = coin->oppPtTEnd()->span();
caryclark26ad22a2015-10-16 09:03:38 -07001684 }
caryclark55888e42016-07-18 10:01:36 -07001685 if (test != end) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001686 FAIL_IF(!test->upCastable(), coin);
1687 priorT = test->t();
caryclark26ad22a2015-10-16 09:03:38 -07001688 test = test->upCast()->next();
1689 }
caryclark55888e42016-07-18 10:01:36 -07001690 if (oTest != oEnd) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001691 oPriorT = oTest->t();
caryclark55888e42016-07-18 10:01:36 -07001692 oTest = coin->flipped() ? oTest->prev() : oTest->upCast()->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001693 FAIL_IF(!oTest, coin);
caryclark26ad22a2015-10-16 09:03:38 -07001694 }
1695 }
caryclark55888e42016-07-18 10:01:36 -07001696 } while ((coin = coin->next()));
1697 return;
caryclark26ad22a2015-10-16 09:03:38 -07001698}
1699
caryclark55888e42016-07-18 10:01:36 -07001700/* Commented-out lines keep this in sync addIfMissing() */
caryclark8016b262016-09-06 05:59:47 -07001701// note that over1s, over1e, over2s, over2e are ordered
Cary Clarkab87d7a2016-10-04 10:01:04 -04001702void SkOpCoincidence::debugAddIfMissing(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* over1s, const SkOpPtT* over2s,
caryclark81a478c2016-09-09 09:37:57 -07001703 double tStart, double tEnd, const SkOpSegment* coinSeg, const SkOpSegment* oppSeg, bool* added,
caryclark8016b262016-09-06 05:59:47 -07001704 const SkOpPtT* over1e, const SkOpPtT* over2e) const {
1705 SkASSERT(tStart < tEnd);
1706 SkASSERT(over1s->fT < over1e->fT);
1707 SkASSERT(between(over1s->fT, tStart, over1e->fT));
1708 SkASSERT(between(over1s->fT, tEnd, over1e->fT));
1709 SkASSERT(over2s->fT < over2e->fT);
1710 SkASSERT(between(over2s->fT, tStart, over2e->fT));
1711 SkASSERT(between(over2s->fT, tEnd, over2e->fT));
1712 SkASSERT(over1s->segment() == over1e->segment());
1713 SkASSERT(over2s->segment() == over2e->segment());
1714 SkASSERT(over1s->segment() == over2s->segment());
1715 SkASSERT(over1s->segment() != coinSeg);
1716 SkASSERT(over1s->segment() != oppSeg);
1717 SkASSERT(coinSeg != oppSeg);
caryclark26ad22a2015-10-16 09:03:38 -07001718 double coinTs, coinTe, oppTs, oppTe;
caryclark8016b262016-09-06 05:59:47 -07001719 coinTs = TRange(over1s, tStart, coinSeg SkDEBUGPARAMS(over1e));
1720 coinTe = TRange(over1s, tEnd, coinSeg SkDEBUGPARAMS(over1e));
Cary Clarkba610292018-06-19 09:47:15 -04001721 SkOpSpanBase::Collapsed result = coinSeg->collapsed(coinTs, coinTe);
1722 if (SkOpSpanBase::Collapsed::kNo != result) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001723 return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, coinSeg);
caryclark8016b262016-09-06 05:59:47 -07001724 }
1725 oppTs = TRange(over2s, tStart, oppSeg SkDEBUGPARAMS(over2e));
1726 oppTe = TRange(over2s, tEnd, oppSeg SkDEBUGPARAMS(over2e));
Cary Clarkba610292018-06-19 09:47:15 -04001727 result = oppSeg->collapsed(oppTs, oppTe);
1728 if (SkOpSpanBase::Collapsed::kNo != result) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001729 return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, oppSeg);
caryclark8016b262016-09-06 05:59:47 -07001730 }
1731 if (coinTs > coinTe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001732 using std::swap;
1733 swap(coinTs, coinTe);
1734 swap(oppTs, oppTe);
caryclark26ad22a2015-10-16 09:03:38 -07001735 }
Cary Clarkba610292018-06-19 09:47:15 -04001736 this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, added);
1737 return;
caryclark26ad22a2015-10-16 09:03:38 -07001738}
1739
caryclark55888e42016-07-18 10:01:36 -07001740/* Commented-out lines keep this in sync addOrOverlap() */
caryclark30b9fdd2016-08-31 14:36:29 -07001741// If this is called by addEndMovedSpans(), a returned false propogates out to an abort.
1742// If this is called by AddIfMissing(), a returned false indicates there was nothing to add
Cary Clarkab87d7a2016-10-04 10:01:04 -04001743void SkOpCoincidence::debugAddOrOverlap(SkPathOpsDebug::GlitchLog* log,
caryclark30b9fdd2016-08-31 14:36:29 -07001744 const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
caryclark81a478c2016-09-09 09:37:57 -07001745 double coinTs, double coinTe, double oppTs, double oppTe, bool* added) const {
caryclark55888e42016-07-18 10:01:36 -07001746 SkTDArray<SkCoincidentSpans*> overlaps;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001747 SkOPASSERT(!fTop); // this is (correctly) reversed in addifMissing()
caryclark81a478c2016-09-09 09:37:57 -07001748 if (fTop && !this->checkOverlap(fTop, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe,
1749 &overlaps)) {
caryclark55888e42016-07-18 10:01:36 -07001750 return;
1751 }
1752 if (fHead && !this->checkOverlap(fHead, coinSeg, oppSeg, coinTs,
1753 coinTe, oppTs, oppTe, &overlaps)) {
1754 return;
1755 }
1756 const SkCoincidentSpans* overlap = overlaps.count() ? overlaps[0] : nullptr;
1757 for (int index = 1; index < overlaps.count(); ++index) { // combine overlaps before continuing
1758 const SkCoincidentSpans* test = overlaps[index];
1759 if (overlap->coinPtTStart()->fT > test->coinPtTStart()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001760 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTStart());
caryclark55888e42016-07-18 10:01:36 -07001761 }
1762 if (overlap->coinPtTEnd()->fT < test->coinPtTEnd()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001763 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07001764 }
1765 if (overlap->flipped()
1766 ? overlap->oppPtTStart()->fT < test->oppPtTStart()->fT
1767 : overlap->oppPtTStart()->fT > test->oppPtTStart()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001768 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTStart());
caryclark55888e42016-07-18 10:01:36 -07001769 }
1770 if (overlap->flipped()
1771 ? overlap->oppPtTEnd()->fT > test->oppPtTEnd()->fT
1772 : overlap->oppPtTEnd()->fT < test->oppPtTEnd()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001773 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07001774 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001775 if (!fHead) { this->debugRelease(log, fHead, test);
1776 this->debugRelease(log, fTop, test);
caryclark55888e42016-07-18 10:01:36 -07001777 }
1778 }
1779 const SkOpPtT* cs = coinSeg->existing(coinTs, oppSeg);
1780 const SkOpPtT* ce = coinSeg->existing(coinTe, oppSeg);
caryclark30b9fdd2016-08-31 14:36:29 -07001781 RETURN_FALSE_IF(overlap && cs && ce && overlap->contains(cs, ce), coinSeg);
1782 RETURN_FALSE_IF(cs != ce || !cs, coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001783 const SkOpPtT* os = oppSeg->existing(oppTs, coinSeg);
1784 const SkOpPtT* oe = oppSeg->existing(oppTe, coinSeg);
caryclark30b9fdd2016-08-31 14:36:29 -07001785 RETURN_FALSE_IF(overlap && os && oe && overlap->contains(os, oe), oppSeg);
caryclark55888e42016-07-18 10:01:36 -07001786 SkASSERT(true || !cs || !cs->deleted());
1787 SkASSERT(true || !os || !os->deleted());
1788 SkASSERT(true || !ce || !ce->deleted());
1789 SkASSERT(true || !oe || !oe->deleted());
1790 const SkOpPtT* csExisting = !cs ? coinSeg->existing(coinTs, nullptr) : nullptr;
1791 const SkOpPtT* ceExisting = !ce ? coinSeg->existing(coinTe, nullptr) : nullptr;
caryclark30b9fdd2016-08-31 14:36:29 -07001792 RETURN_FALSE_IF(csExisting && csExisting == ceExisting, coinSeg);
1793 RETURN_FALSE_IF(csExisting && (csExisting == ce ||
1794 csExisting->contains(ceExisting ? ceExisting : ce)), coinSeg);
1795 RETURN_FALSE_IF(ceExisting && (ceExisting == cs ||
1796 ceExisting->contains(csExisting ? csExisting : cs)), coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001797 const SkOpPtT* osExisting = !os ? oppSeg->existing(oppTs, nullptr) : nullptr;
1798 const SkOpPtT* oeExisting = !oe ? oppSeg->existing(oppTe, nullptr) : nullptr;
caryclark30b9fdd2016-08-31 14:36:29 -07001799 RETURN_FALSE_IF(osExisting && osExisting == oeExisting, oppSeg);
1800 RETURN_FALSE_IF(osExisting && (osExisting == oe ||
1801 osExisting->contains(oeExisting ? oeExisting : oe)), oppSeg);
1802 RETURN_FALSE_IF(oeExisting && (oeExisting == os ||
1803 oeExisting->contains(osExisting ? osExisting : os)), oppSeg);
caryclark55888e42016-07-18 10:01:36 -07001804 bool csDeleted = false, osDeleted = false, ceDeleted = false, oeDeleted = false;
1805 this->debugValidate();
1806 if (!cs || !os) {
1807 if (!cs)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001808 cs = coinSeg->debugAddT(coinTs, log);
caryclark55888e42016-07-18 10:01:36 -07001809 if (!os)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001810 os = oppSeg->debugAddT(oppTs, log);
caryclark30b9fdd2016-08-31 14:36:29 -07001811// RETURN_FALSE_IF(callerAborts, !csWritable || !osWritable);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001812 if (cs && os) cs->span()->debugAddOpp(log, os->span());
caryclark55888e42016-07-18 10:01:36 -07001813// cs = csWritable;
caryclark30b9fdd2016-08-31 14:36:29 -07001814// os = osWritable->active();
1815 RETURN_FALSE_IF((ce && ce->deleted()) || (oe && oe->deleted()), coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001816 }
1817 if (!ce || !oe) {
1818 if (!ce)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001819 ce = coinSeg->debugAddT(coinTe, log);
caryclark55888e42016-07-18 10:01:36 -07001820 if (!oe)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001821 oe = oppSeg->debugAddT(oppTe, log);
1822 if (ce && oe) ce->span()->debugAddOpp(log, oe->span());
caryclark55888e42016-07-18 10:01:36 -07001823// ce = ceWritable;
1824// oe = oeWritable;
1825 }
1826 this->debugValidate();
caryclark30b9fdd2016-08-31 14:36:29 -07001827 RETURN_FALSE_IF(csDeleted, coinSeg);
Ben Wagner63fd7602017-10-09 15:45:33 -04001828 RETURN_FALSE_IF(osDeleted, oppSeg);
1829 RETURN_FALSE_IF(ceDeleted, coinSeg);
caryclark30b9fdd2016-08-31 14:36:29 -07001830 RETURN_FALSE_IF(oeDeleted, oppSeg);
caryclark8016b262016-09-06 05:59:47 -07001831 RETURN_FALSE_IF(!cs || !ce || cs == ce || cs->contains(ce) || !os || !oe || os == oe || os->contains(oe), coinSeg);
1832 bool result = true;
caryclark55888e42016-07-18 10:01:36 -07001833 if (overlap) {
1834 if (overlap->coinPtTStart()->segment() == coinSeg) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001835 log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
caryclark55888e42016-07-18 10:01:36 -07001836 } else {
1837 if (oppTs > oppTe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001838 using std::swap;
1839 swap(coinTs, coinTe);
1840 swap(oppTs, oppTe);
caryclark55888e42016-07-18 10:01:36 -07001841 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001842 log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, oppSeg, oppTs, oppTe, coinSeg, coinTs, coinTe);
caryclark55888e42016-07-18 10:01:36 -07001843 }
caryclark8016b262016-09-06 05:59:47 -07001844#if 0 && DEBUG_COINCIDENCE_VERBOSE
1845 if (result) {
1846 overlap->debugShow();
1847 }
caryclark55888e42016-07-18 10:01:36 -07001848#endif
1849 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001850 log->record(SkPathOpsDebug::kAddMissingCoin_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
caryclark8016b262016-09-06 05:59:47 -07001851#if 0 && DEBUG_COINCIDENCE_VERBOSE
1852 fHead->debugShow();
caryclark55888e42016-07-18 10:01:36 -07001853#endif
1854 }
1855 this->debugValidate();
caryclark8016b262016-09-06 05:59:47 -07001856 return (void) result;
caryclark55888e42016-07-18 10:01:36 -07001857}
1858
1859// Extra commented-out lines keep this in sync with addMissing()
1860/* detects overlaps of different coincident runs on same segment */
1861/* does not detect overlaps for pairs without any segments in common */
1862// returns true if caller should loop again
Cary Clarkab87d7a2016-10-04 10:01:04 -04001863void SkOpCoincidence::debugAddMissing(SkPathOpsDebug::GlitchLog* log, bool* added) const {
caryclark26ad22a2015-10-16 09:03:38 -07001864 const SkCoincidentSpans* outer = fHead;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001865 *added = false;
caryclark26ad22a2015-10-16 09:03:38 -07001866 if (!outer) {
1867 return;
1868 }
caryclark55888e42016-07-18 10:01:36 -07001869 // fTop = outer;
1870 // fHead = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -07001871 do {
1872 // addifmissing can modify the list that this is walking
1873 // save head so that walker can iterate over old data unperturbed
1874 // addifmissing adds to head freely then add saved head in the end
caryclark8016b262016-09-06 05:59:47 -07001875 const SkOpPtT* ocs = outer->coinPtTStart();
1876 SkASSERT(!ocs->deleted());
1877 const SkOpSegment* outerCoin = ocs->segment();
1878 SkASSERT(!outerCoin->done()); // if it's done, should have already been removed from list
1879 const SkOpPtT* oos = outer->oppPtTStart();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001880 if (oos->deleted()) {
1881 return;
1882 }
caryclark8016b262016-09-06 05:59:47 -07001883 const SkOpSegment* outerOpp = oos->segment();
1884 SkASSERT(!outerOpp->done());
1885// SkOpSegment* outerCoinWritable = const_cast<SkOpSegment*>(outerCoin);
1886// SkOpSegment* outerOppWritable = const_cast<SkOpSegment*>(outerOpp);
caryclark26ad22a2015-10-16 09:03:38 -07001887 const SkCoincidentSpans* inner = outer;
caryclark55888e42016-07-18 10:01:36 -07001888 while ((inner = inner->next())) {
1889 this->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001890 double overS, overE;
caryclark8016b262016-09-06 05:59:47 -07001891 const SkOpPtT* ics = inner->coinPtTStart();
1892 SkASSERT(!ics->deleted());
1893 const SkOpSegment* innerCoin = ics->segment();
1894 SkASSERT(!innerCoin->done());
1895 const SkOpPtT* ios = inner->oppPtTStart();
1896 SkASSERT(!ios->deleted());
1897 const SkOpSegment* innerOpp = ios->segment();
1898 SkASSERT(!innerOpp->done());
1899// SkOpSegment* innerCoinWritable = const_cast<SkOpSegment*>(innerCoin);
1900// SkOpSegment* innerOppWritable = const_cast<SkOpSegment*>(innerOpp);
caryclark55888e42016-07-18 10:01:36 -07001901 if (outerCoin == innerCoin) {
caryclark8016b262016-09-06 05:59:47 -07001902 const SkOpPtT* oce = outer->coinPtTEnd();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001903 if (oce->deleted()) {
1904 return;
1905 }
caryclark8016b262016-09-06 05:59:47 -07001906 const SkOpPtT* ice = inner->coinPtTEnd();
1907 SkASSERT(!ice->deleted());
1908 if (outerOpp != innerOpp && this->overlap(ocs, oce, ics, ice, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001909 this->debugAddIfMissing(log, ocs->starter(oce), ics->starter(ice),
caryclark81a478c2016-09-09 09:37:57 -07001910 overS, overE, outerOpp, innerOpp, added,
caryclark8016b262016-09-06 05:59:47 -07001911 ocs->debugEnder(oce),
1912 ics->debugEnder(ice));
caryclark26ad22a2015-10-16 09:03:38 -07001913 }
caryclark55888e42016-07-18 10:01:36 -07001914 } else if (outerCoin == innerOpp) {
caryclark8016b262016-09-06 05:59:47 -07001915 const SkOpPtT* oce = outer->coinPtTEnd();
1916 SkASSERT(!oce->deleted());
1917 const SkOpPtT* ioe = inner->oppPtTEnd();
1918 SkASSERT(!ioe->deleted());
1919 if (outerOpp != innerCoin && this->overlap(ocs, oce, ios, ioe, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001920 this->debugAddIfMissing(log, ocs->starter(oce), ios->starter(ioe),
caryclark81a478c2016-09-09 09:37:57 -07001921 overS, overE, outerOpp, innerCoin, added,
caryclark8016b262016-09-06 05:59:47 -07001922 ocs->debugEnder(oce),
1923 ios->debugEnder(ioe));
caryclark26ad22a2015-10-16 09:03:38 -07001924 }
caryclark55888e42016-07-18 10:01:36 -07001925 } else if (outerOpp == innerCoin) {
caryclark8016b262016-09-06 05:59:47 -07001926 const SkOpPtT* ooe = outer->oppPtTEnd();
1927 SkASSERT(!ooe->deleted());
1928 const SkOpPtT* ice = inner->coinPtTEnd();
1929 SkASSERT(!ice->deleted());
caryclark55888e42016-07-18 10:01:36 -07001930 SkASSERT(outerCoin != innerOpp);
caryclark8016b262016-09-06 05:59:47 -07001931 if (this->overlap(oos, ooe, ics, ice, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001932 this->debugAddIfMissing(log, oos->starter(ooe), ics->starter(ice),
caryclark81a478c2016-09-09 09:37:57 -07001933 overS, overE, outerCoin, innerOpp, added,
caryclark8016b262016-09-06 05:59:47 -07001934 oos->debugEnder(ooe),
1935 ics->debugEnder(ice));
caryclark26ad22a2015-10-16 09:03:38 -07001936 }
caryclark55888e42016-07-18 10:01:36 -07001937 } else if (outerOpp == innerOpp) {
caryclark8016b262016-09-06 05:59:47 -07001938 const SkOpPtT* ooe = outer->oppPtTEnd();
1939 SkASSERT(!ooe->deleted());
1940 const SkOpPtT* ioe = inner->oppPtTEnd();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001941 if (ioe->deleted()) {
1942 return;
1943 }
caryclark55888e42016-07-18 10:01:36 -07001944 SkASSERT(outerCoin != innerCoin);
caryclark8016b262016-09-06 05:59:47 -07001945 if (this->overlap(oos, ooe, ios, ioe, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001946 this->debugAddIfMissing(log, oos->starter(ooe), ios->starter(ioe),
caryclark81a478c2016-09-09 09:37:57 -07001947 overS, overE, outerCoin, innerCoin, added,
caryclark8016b262016-09-06 05:59:47 -07001948 oos->debugEnder(ooe),
1949 ios->debugEnder(ioe));
caryclark26ad22a2015-10-16 09:03:38 -07001950 }
1951 }
caryclark55888e42016-07-18 10:01:36 -07001952 this->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001953 }
caryclark55888e42016-07-18 10:01:36 -07001954 } while ((outer = outer->next()));
1955 // this->restoreHead();
1956 return;
caryclark26ad22a2015-10-16 09:03:38 -07001957}
1958
caryclark55888e42016-07-18 10:01:36 -07001959// Commented-out lines keep this in sync with release()
Cary Clarkab87d7a2016-10-04 10:01:04 -04001960void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkCoincidentSpans* remove) const {
caryclark30b9fdd2016-08-31 14:36:29 -07001961 const SkCoincidentSpans* head = coin;
1962 const SkCoincidentSpans* prev = nullptr;
1963 const SkCoincidentSpans* next;
1964 do {
1965 next = coin->next();
1966 if (coin == remove) {
1967 if (prev) {
1968// prev->setNext(next);
1969 } else if (head == fHead) {
1970// fHead = next;
1971 } else {
1972// fTop = next;
1973 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001974 log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
caryclark30b9fdd2016-08-31 14:36:29 -07001975 }
1976 prev = coin;
1977 } while ((coin = next));
1978 return;
1979}
1980
Cary Clarkab87d7a2016-10-04 10:01:04 -04001981void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* deleted) const {
caryclark55888e42016-07-18 10:01:36 -07001982 const SkCoincidentSpans* coin = fHead;
1983 if (!coin) {
1984 return;
1985 }
1986 do {
1987 if (coin->coinPtTStart()->segment() == deleted
1988 || coin->coinPtTEnd()->segment() == deleted
1989 || coin->oppPtTStart()->segment() == deleted
1990 || coin->oppPtTEnd()->segment() == deleted) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001991 log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07001992 }
1993 } while ((coin = coin->next()));
1994}
1995
caryclark55888e42016-07-18 10:01:36 -07001996// Commented-out lines keep this in sync with expand()
1997// expand the range by checking adjacent spans for coincidence
Cary Clarkab87d7a2016-10-04 10:01:04 -04001998bool SkOpCoincidence::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07001999 const SkCoincidentSpans* coin = fHead;
2000 if (!coin) {
2001 return false;
2002 }
2003 bool expanded = false;
2004 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002005 if (coin->debugExpand(log)) {
caryclark55888e42016-07-18 10:01:36 -07002006 // check to see if multiple spans expanded so they are now identical
2007 const SkCoincidentSpans* test = fHead;
2008 do {
2009 if (coin == test) {
2010 continue;
2011 }
2012 if (coin->coinPtTStart() == test->coinPtTStart()
2013 && coin->oppPtTStart() == test->oppPtTStart()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002014 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, fHead, test->coinPtTStart());
caryclark55888e42016-07-18 10:01:36 -07002015 break;
2016 }
2017 } while ((test = test->next()));
2018 expanded = true;
caryclark26ad22a2015-10-16 09:03:38 -07002019 }
caryclark55888e42016-07-18 10:01:36 -07002020 } while ((coin = coin->next()));
caryclark26ad22a2015-10-16 09:03:38 -07002021 return expanded;
2022}
2023
caryclark55888e42016-07-18 10:01:36 -07002024// Commented-out lines keep this in sync with mark()
2025/* this sets up the coincidence links in the segments when the coincidence crosses multiple spans */
Cary Clarkab87d7a2016-10-04 10:01:04 -04002026void SkOpCoincidence::debugMark(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07002027 const SkCoincidentSpans* coin = fHead;
2028 if (!coin) {
2029 return;
2030 }
2031 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002032 FAIL_IF(!coin->coinPtTStartWritable()->span()->upCastable(), coin);
caryclark55888e42016-07-18 10:01:36 -07002033 const SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast();
2034// SkASSERT(start->deleted());
2035 const SkOpSpanBase* end = coin->coinPtTEndWritable()->span();
2036// SkASSERT(end->deleted());
2037 const SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span();
2038// SkASSERT(oStart->deleted());
2039 const SkOpSpanBase* oEnd = coin->oppPtTEndWritable()->span();
2040// SkASSERT(oEnd->deleted());
2041 bool flipped = coin->flipped();
caryclark26ad22a2015-10-16 09:03:38 -07002042 if (flipped) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002043 using std::swap;
2044 swap(oStart, oEnd);
caryclark26ad22a2015-10-16 09:03:38 -07002045 }
caryclark55888e42016-07-18 10:01:36 -07002046 /* coin and opp spans may not match up. Mark the ends, and then let the interior
2047 get marked as many times as the spans allow */
Cary Clarkab87d7a2016-10-04 10:01:04 -04002048 start->debugInsertCoincidence(log, oStart->upCast());
2049 end->debugInsertCoinEnd(log, oEnd);
caryclark55888e42016-07-18 10:01:36 -07002050 const SkOpSegment* segment = start->segment();
2051 const SkOpSegment* oSegment = oStart->segment();
caryclark26ad22a2015-10-16 09:03:38 -07002052 const SkOpSpanBase* next = start;
2053 const SkOpSpanBase* oNext = oStart;
caryclarka35ab3e2016-10-20 08:32:18 -07002054 bool ordered;
2055 FAIL_IF(!coin->ordered(&ordered), coin);
caryclark55888e42016-07-18 10:01:36 -07002056 while ((next = next->upCast()->next()) != end) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002057 FAIL_IF(!next->upCastable(), coin);
2058 if (next->upCast()->debugInsertCoincidence(log, oSegment, flipped, ordered), false) {
caryclark55888e42016-07-18 10:01:36 -07002059 return;
caryclark26ad22a2015-10-16 09:03:38 -07002060 }
caryclark55888e42016-07-18 10:01:36 -07002061 }
2062 while ((oNext = oNext->upCast()->next()) != oEnd) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002063 FAIL_IF(!oNext->upCastable(), coin);
2064 if (oNext->upCast()->debugInsertCoincidence(log, segment, flipped, ordered), false) {
caryclark55888e42016-07-18 10:01:36 -07002065 return;
caryclark26ad22a2015-10-16 09:03:38 -07002066 }
caryclark55888e42016-07-18 10:01:36 -07002067 }
2068 } while ((coin = coin->next()));
2069 return;
caryclark26ad22a2015-10-16 09:03:38 -07002070}
2071#endif
2072
Cary Clarkab87d7a2016-10-04 10:01:04 -04002073#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002074// Commented-out lines keep this in sync with markCollapsed()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002075void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkOpPtT* test) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002076 const SkCoincidentSpans* head = coin;
caryclark55888e42016-07-18 10:01:36 -07002077 while (coin) {
2078 if (coin->collapsed(test)) {
2079 if (zero_or_one(coin->coinPtTStart()->fT) && zero_or_one(coin->coinPtTEnd()->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002080 log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07002081 }
2082 if (zero_or_one(coin->oppPtTStart()->fT) && zero_or_one(coin->oppPtTEnd()->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002083 log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07002084 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002085 this->debugRelease(log, head, coin);
caryclark55888e42016-07-18 10:01:36 -07002086 }
2087 coin = coin->next();
caryclark624637c2015-05-11 07:21:27 -07002088 }
2089}
2090
caryclark55888e42016-07-18 10:01:36 -07002091// Commented-out lines keep this in sync with markCollapsed()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002092void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* test) const {
2093 this->debugMarkCollapsed(log, fHead, test);
2094 this->debugMarkCollapsed(log, fTop, test);
caryclark55888e42016-07-18 10:01:36 -07002095}
2096#endif
2097
2098void SkCoincidentSpans::debugShow() const {
caryclark6c3b9cd2016-09-26 05:36:58 -07002099 SkDebugf("coinSpan - id=%d t=%1.9g tEnd=%1.9g\n", coinPtTStart()->segment()->debugID(),
caryclark55888e42016-07-18 10:01:36 -07002100 coinPtTStart()->fT, coinPtTEnd()->fT);
caryclark6c3b9cd2016-09-26 05:36:58 -07002101 SkDebugf("coinSpan + id=%d t=%1.9g tEnd=%1.9g\n", oppPtTStart()->segment()->debugID(),
caryclark55888e42016-07-18 10:01:36 -07002102 oppPtTStart()->fT, oppPtTEnd()->fT);
2103}
2104
2105void SkOpCoincidence::debugShowCoincidence() const {
caryclark26ad22a2015-10-16 09:03:38 -07002106#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -07002107 const SkCoincidentSpans* span = fHead;
2108 while (span) {
2109 span->debugShow();
2110 span = span->next();
2111 }
2112#endif
2113}
2114
Cary Clarkab87d7a2016-10-04 10:01:04 -04002115#if DEBUG_COIN
caryclark6c3b9cd2016-09-26 05:36:58 -07002116static void DebugCheckBetween(const SkOpSpanBase* next, const SkOpSpanBase* end,
caryclark55888e42016-07-18 10:01:36 -07002117 double oStart, double oEnd, const SkOpSegment* oSegment,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002118 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002119 SkASSERT(next != end);
2120 SkASSERT(!next->contains(end) || log);
2121 if (next->t() > end->t()) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002122 using std::swap;
2123 swap(next, end);
caryclark55888e42016-07-18 10:01:36 -07002124 }
2125 do {
2126 const SkOpPtT* ptT = next->ptT();
2127 int index = 0;
caryclark27c015d2016-09-23 05:47:20 -07002128 bool somethingBetween = false;
caryclark55888e42016-07-18 10:01:36 -07002129 do {
2130 ++index;
2131 ptT = ptT->next();
2132 const SkOpPtT* checkPtT = next->ptT();
2133 if (ptT == checkPtT) {
2134 break;
2135 }
2136 bool looped = false;
2137 for (int check = 0; check < index; ++check) {
2138 if ((looped = checkPtT == ptT)) {
2139 break;
2140 }
2141 checkPtT = checkPtT->next();
2142 }
2143 if (looped) {
2144 SkASSERT(0);
2145 break;
2146 }
2147 if (ptT->deleted()) {
2148 continue;
2149 }
2150 if (ptT->segment() != oSegment) {
2151 continue;
2152 }
2153 somethingBetween |= between(oStart, ptT->fT, oEnd);
2154 } while (true);
2155 SkASSERT(somethingBetween);
2156 } while (next != end && (next = next->upCast()->next()));
2157}
2158
2159static void DebugCheckOverlap(const SkCoincidentSpans* test, const SkCoincidentSpans* list,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002160 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002161 if (!list) {
2162 return;
2163 }
2164 const SkOpSegment* coinSeg = test->coinPtTStart()->segment();
2165 SkASSERT(coinSeg == test->coinPtTEnd()->segment());
2166 const SkOpSegment* oppSeg = test->oppPtTStart()->segment();
2167 SkASSERT(oppSeg == test->oppPtTEnd()->segment());
2168 SkASSERT(coinSeg != test->oppPtTStart()->segment());
2169 SkDEBUGCODE(double tcs = test->coinPtTStart()->fT);
2170 SkASSERT(between(0, tcs, 1));
2171 SkDEBUGCODE(double tce = test->coinPtTEnd()->fT);
2172 SkASSERT(between(0, tce, 1));
2173 SkASSERT(tcs < tce);
2174 double tos = test->oppPtTStart()->fT;
2175 SkASSERT(between(0, tos, 1));
2176 double toe = test->oppPtTEnd()->fT;
2177 SkASSERT(between(0, toe, 1));
2178 SkASSERT(tos != toe);
2179 if (tos > toe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002180 using std::swap;
2181 swap(tos, toe);
caryclark55888e42016-07-18 10:01:36 -07002182 }
2183 do {
2184 double lcs, lce, los, loe;
2185 if (coinSeg == list->coinPtTStart()->segment()) {
2186 if (oppSeg != list->oppPtTStart()->segment()) {
2187 continue;
2188 }
2189 lcs = list->coinPtTStart()->fT;
2190 lce = list->coinPtTEnd()->fT;
2191 los = list->oppPtTStart()->fT;
2192 loe = list->oppPtTEnd()->fT;
2193 if (los > loe) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002194 using std::swap;
2195 swap(los, loe);
caryclark55888e42016-07-18 10:01:36 -07002196 }
2197 } else if (coinSeg == list->oppPtTStart()->segment()) {
2198 if (oppSeg != list->coinPtTStart()->segment()) {
2199 continue;
2200 }
2201 lcs = list->oppPtTStart()->fT;
2202 lce = list->oppPtTEnd()->fT;
2203 if (lcs > lce) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002204 using std::swap;
2205 swap(lcs, lce);
caryclark55888e42016-07-18 10:01:36 -07002206 }
2207 los = list->coinPtTStart()->fT;
2208 loe = list->coinPtTEnd()->fT;
2209 } else {
2210 continue;
2211 }
2212 SkASSERT(tce < lcs || lce < tcs);
2213 SkASSERT(toe < los || loe < tos);
2214 } while ((list = list->next()));
2215}
2216
2217
2218static void DebugCheckOverlapTop(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002219 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002220 // check for overlapping coincident spans
2221 const SkCoincidentSpans* test = head;
2222 while (test) {
2223 const SkCoincidentSpans* next = test->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04002224 DebugCheckOverlap(test, next, log);
2225 DebugCheckOverlap(test, opt, log);
caryclark55888e42016-07-18 10:01:36 -07002226 test = next;
2227 }
2228}
2229
caryclark55888e42016-07-18 10:01:36 -07002230static void DebugValidate(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002231 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002232 // look for pts inside coincident spans that are not inside the opposite spans
2233 const SkCoincidentSpans* coin = head;
2234 while (coin) {
2235 SkASSERT(SkOpCoincidence::Ordered(coin->coinPtTStart()->segment(),
2236 coin->oppPtTStart()->segment()));
2237 SkASSERT(coin->coinPtTStart()->span()->ptT() == coin->coinPtTStart());
2238 SkASSERT(coin->coinPtTEnd()->span()->ptT() == coin->coinPtTEnd());
2239 SkASSERT(coin->oppPtTStart()->span()->ptT() == coin->oppPtTStart());
2240 SkASSERT(coin->oppPtTEnd()->span()->ptT() == coin->oppPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07002241 coin = coin->next();
2242 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002243 DebugCheckOverlapTop(head, opt, log);
caryclark55888e42016-07-18 10:01:36 -07002244}
2245#endif
2246
2247void SkOpCoincidence::debugValidate() const {
2248#if DEBUG_COINCIDENCE
Cary Clarkab87d7a2016-10-04 10:01:04 -04002249 DebugValidate(fHead, fTop, nullptr);
2250 DebugValidate(fTop, nullptr, nullptr);
caryclark55888e42016-07-18 10:01:36 -07002251#endif
2252}
2253
Cary Clarkab87d7a2016-10-04 10:01:04 -04002254#if DEBUG_COIN
caryclark6c3b9cd2016-09-26 05:36:58 -07002255static void DebugCheckBetween(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002256 SkPathOpsDebug::GlitchLog* log) {
caryclark6c3b9cd2016-09-26 05:36:58 -07002257 // look for pts inside coincident spans that are not inside the opposite spans
2258 const SkCoincidentSpans* coin = head;
2259 while (coin) {
2260 DebugCheckBetween(coin->coinPtTStart()->span(), coin->coinPtTEnd()->span(),
2261 coin->oppPtTStart()->fT, coin->oppPtTEnd()->fT, coin->oppPtTStart()->segment(),
Cary Clarkab87d7a2016-10-04 10:01:04 -04002262 log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002263 DebugCheckBetween(coin->oppPtTStart()->span(), coin->oppPtTEnd()->span(),
2264 coin->coinPtTStart()->fT, coin->coinPtTEnd()->fT, coin->coinPtTStart()->segment(),
Cary Clarkab87d7a2016-10-04 10:01:04 -04002265 log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002266 coin = coin->next();
2267 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002268 DebugCheckOverlapTop(head, opt, log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002269}
2270#endif
2271
2272void SkOpCoincidence::debugCheckBetween() const {
2273#if DEBUG_COINCIDENCE
2274 if (fGlobalState->debugCheckHealth()) {
2275 return;
2276 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002277 DebugCheckBetween(fHead, fTop, nullptr);
2278 DebugCheckBetween(fTop, nullptr, nullptr);
caryclark6c3b9cd2016-09-26 05:36:58 -07002279#endif
2280}
2281
Cary Clarkab87d7a2016-10-04 10:01:04 -04002282#if DEBUG_COIN
2283void SkOpContour::debugCheckHealth(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07002284 const SkOpSegment* segment = &fHead;
2285 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002286 segment->debugCheckHealth(log);
caryclark26ad22a2015-10-16 09:03:38 -07002287 } while ((segment = segment->next()));
2288}
2289
Cary Clarkab87d7a2016-10-04 10:01:04 -04002290void SkOpCoincidence::debugCheckValid(SkPathOpsDebug::GlitchLog* log) const {
2291#if DEBUG_VALIDATE
2292 DebugValidate(fHead, fTop, log);
2293 DebugValidate(fTop, nullptr, log);
2294#endif
2295}
2296
2297void SkOpCoincidence::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
2298 const SkCoincidentSpans* coin = fHead;
2299 if (!coin) {
2300 return;
2301 }
2302 do {
2303 coin->debugCorrectEnds(log);
2304 } while ((coin = coin->next()));
2305}
2306
caryclark55888e42016-07-18 10:01:36 -07002307// commmented-out lines keep this aligned with missingCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002308void SkOpContour::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07002309// SkASSERT(fCount > 0);
caryclark26ad22a2015-10-16 09:03:38 -07002310 const SkOpSegment* segment = &fHead;
caryclark55888e42016-07-18 10:01:36 -07002311// bool result = false;
caryclark26ad22a2015-10-16 09:03:38 -07002312 do {
Cary Clarke47ae292016-10-05 08:51:39 -04002313 if (segment->debugMissingCoincidence(log), false) {
caryclark55888e42016-07-18 10:01:36 -07002314// result = true;
caryclark55888e42016-07-18 10:01:36 -07002315 }
2316 segment = segment->next();
2317 } while (segment);
2318 return;
caryclark26ad22a2015-10-16 09:03:38 -07002319}
Cary Clarkab87d7a2016-10-04 10:01:04 -04002320
2321void SkOpContour::debugMoveMultiples(SkPathOpsDebug::GlitchLog* log) const {
2322 SkASSERT(fCount > 0);
2323 const SkOpSegment* segment = &fHead;
2324 do {
2325 if (segment->debugMoveMultiples(log), false) {
2326 return;
2327 }
2328 } while ((segment = segment->next()));
2329 return;
2330}
2331
2332void SkOpContour::debugMoveNearby(SkPathOpsDebug::GlitchLog* log) const {
2333 SkASSERT(fCount > 0);
2334 const SkOpSegment* segment = &fHead;
2335 do {
2336 segment->debugMoveNearby(log);
2337 } while ((segment = segment->next()));
2338}
caryclark26ad22a2015-10-16 09:03:38 -07002339#endif
2340
caryclark025b11e2016-08-25 05:21:14 -07002341#if DEBUG_COINCIDENCE_ORDER
2342void SkOpSegment::debugResetCoinT() const {
2343 fDebugBaseIndex = -1;
2344 fDebugBaseMin = 1;
2345 fDebugBaseMax = -1;
2346 fDebugLastIndex = -1;
2347 fDebugLastMin = 1;
2348 fDebugLastMax = -1;
2349}
2350#endif
2351
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002352void SkOpSegment::debugValidate() const {
caryclark025b11e2016-08-25 05:21:14 -07002353#if DEBUG_COINCIDENCE_ORDER
2354 {
2355 const SkOpSpanBase* span = &fHead;
2356 do {
2357 span->debugResetCoinT();
2358 } while (!span->final() && (span = span->upCast()->next()));
2359 span = &fHead;
2360 int index = 0;
2361 do {
2362 span->debugSetCoinT(index++);
2363 } while (!span->final() && (span = span->upCast()->next()));
2364 }
2365#endif
caryclark55888e42016-07-18 10:01:36 -07002366#if DEBUG_COINCIDENCE
2367 if (this->globalState()->debugCheckHealth()) {
2368 return;
2369 }
2370#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002371#if DEBUG_VALIDATE
caryclark54359292015-03-26 07:52:43 -07002372 const SkOpSpanBase* span = &fHead;
2373 double lastT = -1;
halcanary96fcdcc2015-08-27 07:41:13 -07002374 const SkOpSpanBase* prev = nullptr;
caryclark54359292015-03-26 07:52:43 -07002375 int count = 0;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002376 int done = 0;
caryclark54359292015-03-26 07:52:43 -07002377 do {
2378 if (!span->final()) {
2379 ++count;
2380 done += span->upCast()->done() ? 1 : 0;
2381 }
2382 SkASSERT(span->segment() == this);
2383 SkASSERT(!prev || prev->upCast()->next() == span);
2384 SkASSERT(!prev || prev == span->prev());
2385 prev = span;
2386 double t = span->ptT()->fT;
2387 SkASSERT(lastT < t);
2388 lastT = t;
2389 span->debugValidate();
2390 } while (!span->final() && (span = span->upCast()->next()));
2391 SkASSERT(count == fCount);
2392 SkASSERT(done == fDoneCount);
caryclark08bc8482015-04-24 09:08:57 -07002393 SkASSERT(count >= fDoneCount);
caryclark54359292015-03-26 07:52:43 -07002394 SkASSERT(span->final());
2395 span->debugValidate();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002396#endif
caryclark54359292015-03-26 07:52:43 -07002397}
2398
Cary Clarkab87d7a2016-10-04 10:01:04 -04002399#if DEBUG_COIN
caryclark30b9fdd2016-08-31 14:36:29 -07002400
2401// Commented-out lines keep this in sync with addOpp()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002402void SkOpSpanBase::debugAddOpp(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002403 const SkOpPtT* oppPrev = this->ptT()->oppPrev(opp->ptT());
2404 if (!oppPrev) {
caryclark55888e42016-07-18 10:01:36 -07002405 return;
caryclark26ad22a2015-10-16 09:03:38 -07002406 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002407 this->debugMergeMatches(log, opp);
caryclark30b9fdd2016-08-31 14:36:29 -07002408 this->ptT()->debugAddOpp(opp->ptT(), oppPrev);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002409 this->debugCheckForCollapsedCoincidence(log);
caryclark26ad22a2015-10-16 09:03:38 -07002410}
2411
caryclark55888e42016-07-18 10:01:36 -07002412// Commented-out lines keep this in sync with checkForCollapsedCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002413void SkOpSpanBase::debugCheckForCollapsedCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07002414 const SkOpCoincidence* coins = this->globalState()->coincidence();
2415 if (coins->isEmpty()) {
2416 return;
2417 }
2418// the insert above may have put both ends of a coincident run in the same span
2419// for each coincident ptT in loop; see if its opposite in is also in the loop
2420// this implementation is the motivation for marking that a ptT is referenced by a coincident span
2421 const SkOpPtT* head = this->ptT();
2422 const SkOpPtT* test = head;
caryclark26ad22a2015-10-16 09:03:38 -07002423 do {
caryclark55888e42016-07-18 10:01:36 -07002424 if (!test->coincident()) {
2425 continue;
caryclark26ad22a2015-10-16 09:03:38 -07002426 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002427 coins->debugMarkCollapsed(log, test);
caryclark55888e42016-07-18 10:01:36 -07002428 } while ((test = test->next()) != head);
caryclark26ad22a2015-10-16 09:03:38 -07002429}
caryclark55888e42016-07-18 10:01:36 -07002430#endif
caryclark26ad22a2015-10-16 09:03:38 -07002431
caryclark54359292015-03-26 07:52:43 -07002432bool SkOpSpanBase::debugCoinEndLoopCheck() const {
2433 int loop = 0;
2434 const SkOpSpanBase* next = this;
2435 SkOpSpanBase* nextCoin;
2436 do {
2437 nextCoin = next->fCoinEnd;
2438 SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin);
2439 for (int check = 1; check < loop - 1; ++check) {
2440 const SkOpSpanBase* checkCoin = this->fCoinEnd;
2441 const SkOpSpanBase* innerCoin = checkCoin;
2442 for (int inner = check + 1; inner < loop; ++inner) {
2443 innerCoin = innerCoin->fCoinEnd;
2444 if (checkCoin == innerCoin) {
2445 SkDebugf("*** bad coincident end loop ***\n");
2446 return false;
2447 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002448 }
2449 }
caryclark54359292015-03-26 07:52:43 -07002450 ++loop;
2451 } while ((next = nextCoin) && next != this);
2452 return true;
2453}
2454
Cary Clarkab87d7a2016-10-04 10:01:04 -04002455#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002456// Commented-out lines keep this in sync with insertCoinEnd()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002457void SkOpSpanBase::debugInsertCoinEnd(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* coin) const {
caryclark55888e42016-07-18 10:01:36 -07002458 if (containsCoinEnd(coin)) {
2459// SkASSERT(coin->containsCoinEnd(this));
2460 return;
2461 }
2462 debugValidate();
2463// SkASSERT(this != coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002464 log->record(SkPathOpsDebug::kMarkCoinEnd_Glitch, this, coin);
caryclark55888e42016-07-18 10:01:36 -07002465// coin->fCoinEnd = this->fCoinEnd;
2466// this->fCoinEnd = coinNext;
2467 debugValidate();
2468}
2469
caryclark30b9fdd2016-08-31 14:36:29 -07002470// Commented-out lines keep this in sync with mergeMatches()
2471// Look to see if pt-t linked list contains same segment more than once
2472// if so, and if each pt-t is directly pointed to by spans in that segment,
2473// merge them
2474// keep the points, but remove spans so that the segment doesn't have 2 or more
2475// spans pointing to the same pt-t loop at different loop elements
Cary Clarkab87d7a2016-10-04 10:01:04 -04002476void SkOpSpanBase::debugMergeMatches(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002477 const SkOpPtT* test = &fPtT;
2478 const SkOpPtT* testNext;
2479 const SkOpPtT* stop = test;
2480 do {
2481 testNext = test->next();
2482 if (test->deleted()) {
2483 continue;
2484 }
2485 const SkOpSpanBase* testBase = test->span();
2486 SkASSERT(testBase->ptT() == test);
2487 const SkOpSegment* segment = test->segment();
2488 if (segment->done()) {
2489 continue;
2490 }
2491 const SkOpPtT* inner = opp->ptT();
2492 const SkOpPtT* innerStop = inner;
2493 do {
2494 if (inner->segment() != segment) {
2495 continue;
2496 }
2497 if (inner->deleted()) {
2498 continue;
2499 }
2500 const SkOpSpanBase* innerBase = inner->span();
2501 SkASSERT(innerBase->ptT() == inner);
Ben Wagner63fd7602017-10-09 15:45:33 -04002502 // when the intersection is first detected, the span base is marked if there are
caryclark30b9fdd2016-08-31 14:36:29 -07002503 // more than one point in the intersection.
2504// if (!innerBase->hasMultipleHint() && !testBase->hasMultipleHint()) {
2505 if (!zero_or_one(inner->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002506 log->record(SkPathOpsDebug::kMergeMatches_Glitch, innerBase, test);
caryclark30b9fdd2016-08-31 14:36:29 -07002507 } else {
2508 SkASSERT(inner->fT != test->fT);
2509 if (!zero_or_one(test->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002510 log->record(SkPathOpsDebug::kMergeMatches_Glitch, testBase, inner);
caryclark30b9fdd2016-08-31 14:36:29 -07002511 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002512 log->record(SkPathOpsDebug::kMergeMatches_Glitch, segment);
caryclark30b9fdd2016-08-31 14:36:29 -07002513// SkDEBUGCODE(testBase->debugSetDeleted());
2514// test->setDeleted();
2515// SkDEBUGCODE(innerBase->debugSetDeleted());
2516// inner->setDeleted();
2517 }
2518 }
2519#ifdef SK_DEBUG // assert if another undeleted entry points to segment
2520 const SkOpPtT* debugInner = inner;
2521 while ((debugInner = debugInner->next()) != innerStop) {
2522 if (debugInner->segment() != segment) {
2523 continue;
2524 }
2525 if (debugInner->deleted()) {
2526 continue;
2527 }
2528 SkOPASSERT(0);
2529 }
2530#endif
Ben Wagner63fd7602017-10-09 15:45:33 -04002531 break;
caryclark30b9fdd2016-08-31 14:36:29 -07002532// }
2533 break;
2534 } while ((inner = inner->next()) != innerStop);
2535 } while ((test = testNext) != stop);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002536 this->debugCheckForCollapsedCoincidence(log);
caryclark30b9fdd2016-08-31 14:36:29 -07002537}
2538
caryclark55888e42016-07-18 10:01:36 -07002539#endif
caryclark26ad22a2015-10-16 09:03:38 -07002540
caryclark025b11e2016-08-25 05:21:14 -07002541void SkOpSpanBase::debugResetCoinT() const {
2542#if DEBUG_COINCIDENCE_ORDER
2543 const SkOpPtT* ptT = &fPtT;
2544 do {
2545 ptT->debugResetCoinT();
2546 ptT = ptT->next();
2547 } while (ptT != &fPtT);
2548#endif
2549}
2550
2551void SkOpSpanBase::debugSetCoinT(int index) const {
2552#if DEBUG_COINCIDENCE_ORDER
2553 const SkOpPtT* ptT = &fPtT;
2554 do {
2555 if (!ptT->deleted()) {
2556 ptT->debugSetCoinT(index);
2557 }
2558 ptT = ptT->next();
2559 } while (ptT != &fPtT);
2560#endif
2561}
2562
caryclark26ad22a2015-10-16 09:03:38 -07002563const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const {
2564 const SkOpSpanBase* end = *endPtr;
2565 SkASSERT(this->segment() == end->segment());
2566 const SkOpSpanBase* result;
2567 if (t() < end->t()) {
2568 result = this;
2569 } else {
2570 result = end;
2571 *endPtr = this;
2572 }
2573 return result->upCast();
2574}
2575
caryclark54359292015-03-26 07:52:43 -07002576void SkOpSpanBase::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07002577#if DEBUG_COINCIDENCE
2578 if (this->globalState()->debugCheckHealth()) {
2579 return;
2580 }
2581#endif
caryclark54359292015-03-26 07:52:43 -07002582#if DEBUG_VALIDATE
2583 const SkOpPtT* ptT = &fPtT;
2584 SkASSERT(ptT->span() == this);
2585 do {
2586// SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt));
2587 ptT->debugValidate();
2588 ptT = ptT->next();
2589 } while (ptT != &fPtT);
2590 SkASSERT(this->debugCoinEndLoopCheck());
2591 if (!this->final()) {
2592 SkASSERT(this->upCast()->debugCoinLoopCheck());
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002593 }
caryclark54359292015-03-26 07:52:43 -07002594 if (fFromAngle) {
2595 fFromAngle->debugValidate();
2596 }
2597 if (!this->final() && this->upCast()->toAngle()) {
2598 this->upCast()->toAngle()->debugValidate();
2599 }
2600#endif
2601}
2602
2603bool SkOpSpan::debugCoinLoopCheck() const {
2604 int loop = 0;
2605 const SkOpSpan* next = this;
2606 SkOpSpan* nextCoin;
2607 do {
2608 nextCoin = next->fCoincident;
2609 SkASSERT(nextCoin == this || nextCoin->fCoincident != nextCoin);
2610 for (int check = 1; check < loop - 1; ++check) {
2611 const SkOpSpan* checkCoin = this->fCoincident;
2612 const SkOpSpan* innerCoin = checkCoin;
2613 for (int inner = check + 1; inner < loop; ++inner) {
2614 innerCoin = innerCoin->fCoincident;
2615 if (checkCoin == innerCoin) {
2616 SkDebugf("*** bad coincident loop ***\n");
2617 return false;
2618 }
2619 }
2620 }
2621 ++loop;
2622 } while ((next = nextCoin) && next != this);
2623 return true;
2624}
2625
Cary Clarkab87d7a2016-10-04 10:01:04 -04002626#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002627// Commented-out lines keep this in sync with insertCoincidence() in header
Cary Clarkab87d7a2016-10-04 10:01:04 -04002628void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* coin) const {
caryclark55888e42016-07-18 10:01:36 -07002629 if (containsCoincidence(coin)) {
2630// SkASSERT(coin->containsCoincidence(this));
2631 return;
2632 }
2633 debugValidate();
2634// SkASSERT(this != coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002635 log->record(SkPathOpsDebug::kMarkCoinStart_Glitch, this, coin);
caryclark55888e42016-07-18 10:01:36 -07002636// coin->fCoincident = this->fCoincident;
2637// this->fCoincident = coinNext;
2638 debugValidate();
2639}
2640
2641// Commented-out lines keep this in sync with insertCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002642void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* segment, bool flipped, bool ordered) const {
caryclark55888e42016-07-18 10:01:36 -07002643 if (this->containsCoincidence(segment)) {
2644 return;
2645 }
2646 const SkOpPtT* next = &fPtT;
2647 while ((next = next->next()) != &fPtT) {
2648 if (next->segment() == segment) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002649 const SkOpSpan* span;
2650 const SkOpSpanBase* base = next->span();
2651 if (!ordered) {
2652 const SkOpSpanBase* spanEnd = fNext->contains(segment)->span();
2653 const SkOpPtT* start = base->ptT()->starter(spanEnd->ptT());
2654 FAIL_IF(!start->span()->upCastable(), this);
2655 span = const_cast<SkOpSpan*>(start->span()->upCast());
2656 }
2657 else if (flipped) {
2658 span = base->prev();
2659 FAIL_IF(!span, this);
2660 }
2661 else {
2662 FAIL_IF(!base->upCastable(), this);
2663 span = base->upCast();
2664 }
2665 log->record(SkPathOpsDebug::kMarkCoinInsert_Glitch, span);
caryclark55888e42016-07-18 10:01:36 -07002666 return;
2667 }
2668 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002669#if DEBUG_COIN
2670 log->record(SkPathOpsDebug::kMarkCoinMissing_Glitch, segment, this);
caryclark55888e42016-07-18 10:01:36 -07002671#endif
Cary Clarkab87d7a2016-10-04 10:01:04 -04002672 return;
caryclark55888e42016-07-18 10:01:36 -07002673}
2674#endif
2675
caryclark624637c2015-05-11 07:21:27 -07002676// called only by test code
2677int SkIntersections::debugCoincidentUsed() const {
2678 if (!fIsCoincident[0]) {
2679 SkASSERT(!fIsCoincident[1]);
2680 return 0;
2681 }
2682 int count = 0;
2683 SkDEBUGCODE(int count2 = 0;)
2684 for (int index = 0; index < fUsed; ++index) {
2685 if (fIsCoincident[0] & (1 << index)) {
2686 ++count;
2687 }
2688#ifdef SK_DEBUG
2689 if (fIsCoincident[1] & (1 << index)) {
2690 ++count2;
2691 }
2692#endif
2693 }
2694 SkASSERT(count == count2);
2695 return count;
2696}
2697
Mike Kleinc0bd9f92019-04-23 12:05:21 -05002698#include "src/pathops/SkOpContour.h"
caryclark54359292015-03-26 07:52:43 -07002699
caryclark55888e42016-07-18 10:01:36 -07002700// Commented-out lines keep this in sync with addOpp()
caryclark29b25632016-08-25 11:27:17 -07002701void SkOpPtT::debugAddOpp(const SkOpPtT* opp, const SkOpPtT* oppPrev) const {
2702 SkDEBUGCODE(const SkOpPtT* oldNext = this->fNext);
caryclark55888e42016-07-18 10:01:36 -07002703 SkASSERT(this != opp);
2704// this->fNext = opp;
caryclark29b25632016-08-25 11:27:17 -07002705 SkASSERT(oppPrev != oldNext);
caryclark55888e42016-07-18 10:01:36 -07002706// oppPrev->fNext = oldNext;
caryclark55888e42016-07-18 10:01:36 -07002707}
2708
caryclark26ad22a2015-10-16 09:03:38 -07002709bool SkOpPtT::debugContains(const SkOpPtT* check) const {
2710 SkASSERT(this != check);
2711 const SkOpPtT* ptT = this;
2712 int links = 0;
2713 do {
2714 ptT = ptT->next();
2715 if (ptT == check) {
2716 return true;
2717 }
2718 ++links;
2719 const SkOpPtT* test = this;
2720 for (int index = 0; index < links; ++index) {
2721 if (ptT == test) {
2722 return false;
2723 }
2724 test = test->next();
2725 }
2726 } while (true);
2727}
2728
2729const SkOpPtT* SkOpPtT::debugContains(const SkOpSegment* check) const {
2730 SkASSERT(this->segment() != check);
2731 const SkOpPtT* ptT = this;
2732 int links = 0;
2733 do {
2734 ptT = ptT->next();
2735 if (ptT->segment() == check) {
2736 return ptT;
2737 }
2738 ++links;
2739 const SkOpPtT* test = this;
2740 for (int index = 0; index < links; ++index) {
2741 if (ptT == test) {
2742 return nullptr;
2743 }
2744 test = test->next();
2745 }
2746 } while (true);
2747}
2748
caryclark8016b262016-09-06 05:59:47 -07002749const SkOpPtT* SkOpPtT::debugEnder(const SkOpPtT* end) const {
2750 return fT < end->fT ? end : this;
2751}
2752
caryclark54359292015-03-26 07:52:43 -07002753int SkOpPtT::debugLoopLimit(bool report) const {
2754 int loop = 0;
2755 const SkOpPtT* next = this;
2756 do {
2757 for (int check = 1; check < loop - 1; ++check) {
2758 const SkOpPtT* checkPtT = this->fNext;
2759 const SkOpPtT* innerPtT = checkPtT;
2760 for (int inner = check + 1; inner < loop; ++inner) {
2761 innerPtT = innerPtT->fNext;
2762 if (checkPtT == innerPtT) {
2763 if (report) {
2764 SkDebugf("*** bad ptT loop ***\n");
2765 }
2766 return loop;
2767 }
2768 }
2769 }
caryclark26ad22a2015-10-16 09:03:38 -07002770 // there's nothing wrong with extremely large loop counts -- but this may appear to hang
2771 // by taking a very long time to figure out that no loop entry is a duplicate
2772 // -- and it's likely that a large loop count is indicative of a bug somewhere
2773 if (++loop > 1000) {
2774 SkDebugf("*** loop count exceeds 1000 ***\n");
2775 return 1000;
2776 }
caryclark54359292015-03-26 07:52:43 -07002777 } while ((next = next->fNext) && next != this);
2778 return 0;
2779}
2780
caryclark29b25632016-08-25 11:27:17 -07002781const SkOpPtT* SkOpPtT::debugOppPrev(const SkOpPtT* opp) const {
2782 return this->oppPrev(const_cast<SkOpPtT*>(opp));
2783}
2784
caryclark025b11e2016-08-25 05:21:14 -07002785void SkOpPtT::debugResetCoinT() const {
2786#if DEBUG_COINCIDENCE_ORDER
Ben Wagner63fd7602017-10-09 15:45:33 -04002787 this->segment()->debugResetCoinT();
caryclark025b11e2016-08-25 05:21:14 -07002788#endif
2789}
2790
2791void SkOpPtT::debugSetCoinT(int index) const {
2792#if DEBUG_COINCIDENCE_ORDER
Ben Wagner63fd7602017-10-09 15:45:33 -04002793 this->segment()->debugSetCoinT(index, fT);
caryclark025b11e2016-08-25 05:21:14 -07002794#endif
2795}
2796
caryclark54359292015-03-26 07:52:43 -07002797void SkOpPtT::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07002798#if DEBUG_COINCIDENCE
2799 if (this->globalState()->debugCheckHealth()) {
2800 return;
2801 }
2802#endif
caryclark54359292015-03-26 07:52:43 -07002803#if DEBUG_VALIDATE
Cary Clarkab87d7a2016-10-04 10:01:04 -04002804 SkOpPhase phase = contour()->globalState()->phase();
2805 if (phase == SkOpPhase::kIntersecting || phase == SkOpPhase::kFixWinding) {
caryclark54359292015-03-26 07:52:43 -07002806 return;
2807 }
2808 SkASSERT(fNext);
2809 SkASSERT(fNext != this);
2810 SkASSERT(fNext->fNext);
2811 SkASSERT(debugLoopLimit(false) == 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002812#endif
2813}
caryclark1049f122015-04-20 08:31:59 -07002814
2815static void output_scalar(SkScalar num) {
2816 if (num == (int) num) {
2817 SkDebugf("%d", (int) num);
2818 } else {
2819 SkString str;
2820 str.printf("%1.9g", num);
2821 int width = (int) str.size();
2822 const char* cStr = str.c_str();
2823 while (cStr[width - 1] == '0') {
2824 --width;
2825 }
2826 str.resize(width);
2827 SkDebugf("%sf", str.c_str());
2828 }
2829}
2830
2831static void output_points(const SkPoint* pts, int count) {
2832 for (int index = 0; index < count; ++index) {
2833 output_scalar(pts[index].fX);
2834 SkDebugf(", ");
2835 output_scalar(pts[index].fY);
2836 if (index + 1 < count) {
2837 SkDebugf(", ");
2838 }
2839 }
2840}
2841
Chris Dalton957189b2020-05-07 12:47:26 -06002842static void showPathContours(const SkPath& path, const char* pathName) {
2843 for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
caryclark1049f122015-04-20 08:31:59 -07002844 switch (verb) {
Chris Dalton957189b2020-05-07 12:47:26 -06002845 case SkPathVerb::kMove:
caryclark1049f122015-04-20 08:31:59 -07002846 SkDebugf(" %s.moveTo(", pathName);
2847 output_points(&pts[0], 1);
2848 SkDebugf(");\n");
2849 continue;
Chris Dalton957189b2020-05-07 12:47:26 -06002850 case SkPathVerb::kLine:
caryclark1049f122015-04-20 08:31:59 -07002851 SkDebugf(" %s.lineTo(", pathName);
2852 output_points(&pts[1], 1);
2853 SkDebugf(");\n");
2854 break;
Chris Dalton957189b2020-05-07 12:47:26 -06002855 case SkPathVerb::kQuad:
caryclark1049f122015-04-20 08:31:59 -07002856 SkDebugf(" %s.quadTo(", pathName);
2857 output_points(&pts[1], 2);
2858 SkDebugf(");\n");
2859 break;
Chris Dalton957189b2020-05-07 12:47:26 -06002860 case SkPathVerb::kConic:
caryclark1049f122015-04-20 08:31:59 -07002861 SkDebugf(" %s.conicTo(", pathName);
2862 output_points(&pts[1], 2);
Chris Dalton957189b2020-05-07 12:47:26 -06002863 SkDebugf(", %1.9gf);\n", *w);
caryclark1049f122015-04-20 08:31:59 -07002864 break;
Chris Dalton957189b2020-05-07 12:47:26 -06002865 case SkPathVerb::kCubic:
caryclark1049f122015-04-20 08:31:59 -07002866 SkDebugf(" %s.cubicTo(", pathName);
2867 output_points(&pts[1], 3);
2868 SkDebugf(");\n");
2869 break;
Chris Dalton957189b2020-05-07 12:47:26 -06002870 case SkPathVerb::kClose:
caryclark1049f122015-04-20 08:31:59 -07002871 SkDebugf(" %s.close();\n", pathName);
2872 break;
2873 default:
2874 SkDEBUGFAIL("bad verb");
2875 return;
2876 }
2877 }
2878}
2879
2880static const char* gFillTypeStr[] = {
Mike Reed7d34dc72019-11-26 12:17:17 -05002881 "kWinding",
2882 "kEvenOdd",
2883 "kInverseWinding",
2884 "kInverseEvenOdd"
caryclark1049f122015-04-20 08:31:59 -07002885};
2886
2887void SkPathOpsDebug::ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration) {
caryclark1049f122015-04-20 08:31:59 -07002888#define SUPPORT_RECT_CONTOUR_DETECTION 0
2889#if SUPPORT_RECT_CONTOUR_DETECTION
halcanary96fcdcc2015-08-27 07:41:13 -07002890 int rectCount = path.isRectContours() ? path.rectContours(nullptr, nullptr) : 0;
caryclark1049f122015-04-20 08:31:59 -07002891 if (rectCount > 0) {
2892 SkTDArray<SkRect> rects;
Mike Reed30bc5272019-11-22 18:34:02 +00002893 SkTDArray<SkPathDirection> directions;
caryclark1049f122015-04-20 08:31:59 -07002894 rects.setCount(rectCount);
2895 directions.setCount(rectCount);
2896 path.rectContours(rects.begin(), directions.begin());
2897 for (int contour = 0; contour < rectCount; ++contour) {
2898 const SkRect& rect = rects[contour];
2899 SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
Mike Reed30bc5272019-11-22 18:34:02 +00002900 rect.fRight, rect.fBottom, directions[contour] == SkPathDirection::kCCW
2901 ? "SkPathDirection::kCCW" : "SkPathDirection::kCW");
caryclark1049f122015-04-20 08:31:59 -07002902 }
2903 return;
2904 }
2905#endif
Mike Reedcf0e3c62019-12-03 16:26:15 -05002906 SkPathFillType fillType = path.getFillType();
Mike Reed7d34dc72019-11-26 12:17:17 -05002907 SkASSERT(fillType >= SkPathFillType::kWinding && fillType <= SkPathFillType::kInverseEvenOdd);
caryclark1049f122015-04-20 08:31:59 -07002908 if (includeDeclaration) {
2909 SkDebugf(" SkPath %s;\n", name);
2910 }
Mike Reed7d34dc72019-11-26 12:17:17 -05002911 SkDebugf(" %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[(int)fillType]);
Chris Dalton957189b2020-05-07 12:47:26 -06002912 showPathContours(path, name);
caryclark1049f122015-04-20 08:31:59 -07002913}
caryclark13260682016-10-24 05:10:14 -07002914
Cary Clark918fb1f2016-11-15 13:22:25 -05002915#if DEBUG_DUMP_VERIFY
Mike Kleinc0bd9f92019-04-23 12:05:21 -05002916#include "include/core/SkData.h"
2917#include "include/core/SkStream.h"
caryclark13260682016-10-24 05:10:14 -07002918
2919static void dump_path(FILE* file, const SkPath& path, bool force, bool dumpAsHex) {
2920 SkDynamicMemoryWStream wStream;
2921 path.dump(&wStream, force, dumpAsHex);
2922 sk_sp<SkData> data(wStream.detachAsData());
2923 fprintf(file, "%.*s\n", (int) data->size(), (char*) data->data());
2924}
2925
2926static int dumpID = 0;
2927
2928void SkPathOpsDebug::DumpOp(const SkPath& one, const SkPath& two, SkPathOp op,
2929 const char* testName) {
2930 FILE* file = sk_fopen("op_dump.txt", kWrite_SkFILE_Flag);
2931 DumpOp(file, one, two, op, testName);
2932}
2933
2934void SkPathOpsDebug::DumpOp(FILE* file, const SkPath& one, const SkPath& two, SkPathOp op,
2935 const char* testName) {
2936 const char* name = testName ? testName : "op";
2937 fprintf(file,
2938 "\nstatic void %s_%d(skiatest::Reporter* reporter, const char* filename) {\n",
2939 name, ++dumpID);
2940 fprintf(file, " SkPath path;\n");
2941 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", one.getFillType());
2942 dump_path(file, one, false, true);
2943 fprintf(file, " SkPath path1(path);\n");
2944 fprintf(file, " path.reset();\n");
2945 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", two.getFillType());
2946 dump_path(file, two, false, true);
2947 fprintf(file, " SkPath path2(path);\n");
2948 fprintf(file, " testPathOp(reporter, path1, path2, (SkPathOp) %d, filename);\n", op);
2949 fprintf(file, "}\n\n");
2950 fclose(file);
2951}
2952
2953void SkPathOpsDebug::DumpSimplify(const SkPath& path, const char* testName) {
2954 FILE* file = sk_fopen("simplify_dump.txt", kWrite_SkFILE_Flag);
2955 DumpSimplify(file, path, testName);
2956}
2957
2958void SkPathOpsDebug::DumpSimplify(FILE* file, const SkPath& path, const char* testName) {
2959 const char* name = testName ? testName : "simplify";
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", path.getFillType());
2965 dump_path(file, path, false, true);
2966 fprintf(file, " testSimplify(reporter, path, filename);\n");
2967 fprintf(file, "}\n\n");
2968 fclose(file);
2969}
2970
Mike Kleinc0bd9f92019-04-23 12:05:21 -05002971#include "include/core/SkBitmap.h"
2972#include "include/core/SkCanvas.h"
2973#include "include/core/SkPaint.h"
caryclark13260682016-10-24 05:10:14 -07002974
2975const int bitWidth = 64;
2976const int bitHeight = 64;
2977
2978static void debug_scale_matrix(const SkPath& one, const SkPath* two, SkMatrix& scale) {
2979 SkRect larger = one.getBounds();
2980 if (two) {
2981 larger.join(two->getBounds());
2982 }
2983 SkScalar largerWidth = larger.width();
2984 if (largerWidth < 4) {
2985 largerWidth = 4;
2986 }
2987 SkScalar largerHeight = larger.height();
2988 if (largerHeight < 4) {
2989 largerHeight = 4;
2990 }
2991 SkScalar hScale = (bitWidth - 2) / largerWidth;
2992 SkScalar vScale = (bitHeight - 2) / largerHeight;
2993 scale.reset();
2994 scale.preScale(hScale, vScale);
2995 larger.fLeft *= hScale;
2996 larger.fRight *= hScale;
2997 larger.fTop *= vScale;
2998 larger.fBottom *= vScale;
2999 SkScalar dx = -16000 > larger.fLeft ? -16000 - larger.fLeft
3000 : 16000 < larger.fRight ? 16000 - larger.fRight : 0;
3001 SkScalar dy = -16000 > larger.fTop ? -16000 - larger.fTop
3002 : 16000 < larger.fBottom ? 16000 - larger.fBottom : 0;
3003 scale.preTranslate(dx, dy);
3004}
3005
3006static int debug_paths_draw_the_same(const SkPath& one, const SkPath& two, SkBitmap& bits) {
3007 if (bits.width() == 0) {
3008 bits.allocN32Pixels(bitWidth * 2, bitHeight);
3009 }
3010 SkCanvas canvas(bits);
3011 canvas.drawColor(SK_ColorWHITE);
3012 SkPaint paint;
3013 canvas.save();
3014 const SkRect& bounds1 = one.getBounds();
3015 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
3016 canvas.drawPath(one, paint);
3017 canvas.restore();
3018 canvas.save();
3019 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
3020 canvas.drawPath(two, paint);
3021 canvas.restore();
3022 int errors = 0;
3023 for (int y = 0; y < bitHeight - 1; ++y) {
3024 uint32_t* addr1 = bits.getAddr32(0, y);
3025 uint32_t* addr2 = bits.getAddr32(0, y + 1);
3026 uint32_t* addr3 = bits.getAddr32(bitWidth, y);
3027 uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1);
3028 for (int x = 0; x < bitWidth - 1; ++x) {
3029 // count 2x2 blocks
3030 bool err = addr1[x] != addr3[x];
3031 if (err) {
3032 errors += addr1[x + 1] != addr3[x + 1]
3033 && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1];
3034 }
3035 }
3036 }
3037 return errors;
3038}
3039
3040void SkPathOpsDebug::ReportOpFail(const SkPath& one, const SkPath& two, SkPathOp op) {
3041 SkDebugf("// Op did not expect failure\n");
3042 DumpOp(stderr, one, two, op, "opTest");
3043 fflush(stderr);
3044}
3045
3046void SkPathOpsDebug::VerifyOp(const SkPath& one, const SkPath& two, SkPathOp op,
3047 const SkPath& result) {
3048 SkPath pathOut, scaledPathOut;
3049 SkRegion rgnA, rgnB, openClip, rgnOut;
3050 openClip.setRect(-16000, -16000, 16000, 16000);
3051 rgnA.setPath(one, openClip);
3052 rgnB.setPath(two, openClip);
3053 rgnOut.op(rgnA, rgnB, (SkRegion::Op) op);
3054 rgnOut.getBoundaryPath(&pathOut);
3055 SkMatrix scale;
3056 debug_scale_matrix(one, &two, scale);
3057 SkRegion scaledRgnA, scaledRgnB, scaledRgnOut;
3058 SkPath scaledA, scaledB;
3059 scaledA.addPath(one, scale);
3060 scaledA.setFillType(one.getFillType());
3061 scaledB.addPath(two, scale);
3062 scaledB.setFillType(two.getFillType());
3063 scaledRgnA.setPath(scaledA, openClip);
3064 scaledRgnB.setPath(scaledB, openClip);
3065 scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) op);
3066 scaledRgnOut.getBoundaryPath(&scaledPathOut);
3067 SkBitmap bitmap;
3068 SkPath scaledOut;
3069 scaledOut.addPath(result, scale);
3070 scaledOut.setFillType(result.getFillType());
3071 int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
3072 const int MAX_ERRORS = 9;
3073 if (errors > MAX_ERRORS) {
3074 fprintf(stderr, "// Op did not expect errors=%d\n", errors);
3075 DumpOp(stderr, one, two, op, "opTest");
3076 fflush(stderr);
3077 }
3078}
3079
3080void SkPathOpsDebug::ReportSimplifyFail(const SkPath& path) {
3081 SkDebugf("// Simplify did not expect failure\n");
3082 DumpSimplify(stderr, path, "simplifyTest");
3083 fflush(stderr);
3084}
3085
3086void SkPathOpsDebug::VerifySimplify(const SkPath& path, const SkPath& result) {
3087 SkPath pathOut, scaledPathOut;
3088 SkRegion rgnA, openClip, rgnOut;
3089 openClip.setRect(-16000, -16000, 16000, 16000);
3090 rgnA.setPath(path, openClip);
3091 rgnOut.getBoundaryPath(&pathOut);
3092 SkMatrix scale;
3093 debug_scale_matrix(path, nullptr, scale);
3094 SkRegion scaledRgnA;
3095 SkPath scaledA;
3096 scaledA.addPath(path, scale);
3097 scaledA.setFillType(path.getFillType());
3098 scaledRgnA.setPath(scaledA, openClip);
3099 scaledRgnA.getBoundaryPath(&scaledPathOut);
3100 SkBitmap bitmap;
3101 SkPath scaledOut;
3102 scaledOut.addPath(result, scale);
3103 scaledOut.setFillType(result.getFillType());
3104 int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
3105 const int MAX_ERRORS = 9;
3106 if (errors > MAX_ERRORS) {
3107 fprintf(stderr, "// Simplify did not expect errors=%d\n", errors);
3108 DumpSimplify(stderr, path, "simplifyTest");
3109 fflush(stderr);
3110 }
3111}
3112
3113#endif
Cary Clarkb8421ed2018-03-14 15:55:02 -04003114
3115// global path dumps for msvs Visual Studio 17 to use from Immediate Window
3116void Dump(const SkPath& path) {
3117 path.dump();
3118}
3119
3120void DumpHex(const SkPath& path) {
3121 path.dumpHex();
3122}