blob: 346db4b340f7072717d8fc1bf02ecbb7f9a4568a [file] [log] [blame]
caryclark@google.com07393ca2013-04-08 11:47:37 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
mtklein1b249332015-07-07 12:21:21 -07008#include "SkMutex.h"
caryclark26ad22a2015-10-16 09:03:38 -07009#include "SkOpCoincidence.h"
10#include "SkOpContour.h"
caryclark13260682016-10-24 05:10:14 -070011#include "SkOSFile.h"
jvanverth02802f62015-07-02 06:42:49 -070012#include "SkPath.h"
mtklein1b249332015-07-07 12:21:21 -070013#include "SkPathOpsDebug.h"
caryclark54359292015-03-26 07:52:43 -070014#include "SkString.h"
caryclark54359292015-03-26 07:52:43 -070015
Cary Clark918fb1f2016-11-15 13:22:25 -050016#if DEBUG_DUMP_VERIFY
caryclark13260682016-10-24 05:10:14 -070017bool SkPathOpsDebug::gDumpOp; // set to true to write op to file before a crash
18bool SkPathOpsDebug::gVerifyOp; // set to true to compare result against regions
19#endif
20
Cary Clark59d5a0e2017-01-23 14:38:52 +000021bool SkPathOpsDebug::gRunFail; // set to true to check for success on tests known to fail
caryclark13260682016-10-24 05:10:14 -070022bool SkPathOpsDebug::gVeryVerbose; // set to true to run extensive checking tests
23
caryclark30b9fdd2016-08-31 14:36:29 -070024#undef FAIL_IF
25#define FAIL_IF(cond, coin) \
Cary Clarkab87d7a2016-10-04 10:01:04 -040026 do { if (cond) log->record(SkPathOpsDebug::kFail_Glitch, coin); } while (false)
caryclark30b9fdd2016-08-31 14:36:29 -070027
28#undef FAIL_WITH_NULL_IF
29#define FAIL_WITH_NULL_IF(cond, span) \
Cary Clarkab87d7a2016-10-04 10:01:04 -040030 do { if (cond) log->record(SkPathOpsDebug::kFail_Glitch, span); } while (false)
caryclark30b9fdd2016-08-31 14:36:29 -070031
32#undef RETURN_FALSE_IF
33#define RETURN_FALSE_IF(cond, span) \
Cary Clarkab87d7a2016-10-04 10:01:04 -040034 do { if (cond) log->record(SkPathOpsDebug::kReturnFalse_Glitch, span); \
35 } while (false)
caryclark30b9fdd2016-08-31 14:36:29 -070036
caryclark55888e42016-07-18 10:01:36 -070037class SkCoincidentSpans;
caryclark26ad22a2015-10-16 09:03:38 -070038
caryclark624637c2015-05-11 07:21:27 -070039#if DEBUG_SORT
40int SkPathOpsDebug::gSortCountDefault = SK_MaxS32;
41int SkPathOpsDebug::gSortCount;
42#endif
43
44#if DEBUG_ACTIVE_OP
Ben Wagner3f985522017-10-09 10:47:47 -040045const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor", "rdiff"};
caryclark624637c2015-05-11 07:21:27 -070046#endif
47
caryclark@google.com07393ca2013-04-08 11:47:37 +000048#if defined SK_DEBUG || !FORCE_RELEASE
49
commit-bot@chromium.orgfe41b8f2014-04-25 14:03:52 +000050int SkPathOpsDebug::gContourID = 0;
51int SkPathOpsDebug::gSegmentID = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +000052
caryclark54359292015-03-26 07:52:43 -070053bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray,
54 const SkOpSpanBase* span) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000055 for (int index = 0; index < chaseArray.count(); ++index) {
caryclark54359292015-03-26 07:52:43 -070056 const SkOpSpanBase* entry = chaseArray[index];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000057 if (entry == span) {
58 return true;
59 }
60 }
61 return false;
62}
caryclark26ad22a2015-10-16 09:03:38 -070063#endif
Ben Wagner29380bd2017-10-09 14:43:00 -040064
65#if DEBUG_ACTIVE_SPANS
66SkString SkPathOpsDebug::gActiveSpans;
Cary Clarkff114282016-12-14 11:56:16 -050067#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000068
Cary Clarkab87d7a2016-10-04 10:01:04 -040069#if DEBUG_COIN
caryclark26ad22a2015-10-16 09:03:38 -070070
Cary Clarkab87d7a2016-10-04 10:01:04 -040071SkPathOpsDebug::CoinDict SkPathOpsDebug::gCoinSumChangedDict;
72SkPathOpsDebug::CoinDict SkPathOpsDebug::gCoinSumVisitedDict;
73
74static const int kGlitchType_Count = SkPathOpsDebug::kUnalignedTail_Glitch + 1;
caryclark26ad22a2015-10-16 09:03:38 -070075
76struct SpanGlitch {
caryclark26ad22a2015-10-16 09:03:38 -070077 const SkOpSpanBase* fBase;
78 const SkOpSpanBase* fSuspect;
caryclark26ad22a2015-10-16 09:03:38 -070079 const SkOpSegment* fSegment;
caryclark55888e42016-07-18 10:01:36 -070080 const SkOpSegment* fOppSegment;
caryclark26ad22a2015-10-16 09:03:38 -070081 const SkOpPtT* fCoinSpan;
82 const SkOpPtT* fEndSpan;
83 const SkOpPtT* fOppSpan;
84 const SkOpPtT* fOppEndSpan;
caryclark55888e42016-07-18 10:01:36 -070085 double fStartT;
86 double fEndT;
87 double fOppStartT;
88 double fOppEndT;
caryclark26ad22a2015-10-16 09:03:38 -070089 SkPoint fPt;
Cary Clarkab87d7a2016-10-04 10:01:04 -040090 SkPathOpsDebug::GlitchType fType;
91
92 void dumpType() const;
caryclark26ad22a2015-10-16 09:03:38 -070093};
94
95struct SkPathOpsDebug::GlitchLog {
Cary Clarkab87d7a2016-10-04 10:01:04 -040096 void init(const SkOpGlobalState* state) {
97 fGlobalState = state;
98 }
99
100 SpanGlitch* recordCommon(GlitchType type) {
caryclark26ad22a2015-10-16 09:03:38 -0700101 SpanGlitch* glitch = fGlitches.push();
caryclark26ad22a2015-10-16 09:03:38 -0700102 glitch->fBase = nullptr;
103 glitch->fSuspect = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -0700104 glitch->fSegment = nullptr;
caryclark55888e42016-07-18 10:01:36 -0700105 glitch->fOppSegment = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -0700106 glitch->fCoinSpan = nullptr;
107 glitch->fEndSpan = nullptr;
108 glitch->fOppSpan = nullptr;
109 glitch->fOppEndSpan = nullptr;
caryclark55888e42016-07-18 10:01:36 -0700110 glitch->fStartT = SK_ScalarNaN;
111 glitch->fEndT = SK_ScalarNaN;
112 glitch->fOppStartT = SK_ScalarNaN;
113 glitch->fOppEndT = SK_ScalarNaN;
caryclark26ad22a2015-10-16 09:03:38 -0700114 glitch->fPt = { SK_ScalarNaN, SK_ScalarNaN };
115 glitch->fType = type;
116 return glitch;
117 }
118
Cary Clarkab87d7a2016-10-04 10:01:04 -0400119 void record(GlitchType type, const SkOpSpanBase* base,
caryclark26ad22a2015-10-16 09:03:38 -0700120 const SkOpSpanBase* suspect = NULL) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400121 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700122 glitch->fBase = base;
123 glitch->fSuspect = suspect;
124 }
125
Cary Clarkab87d7a2016-10-04 10:01:04 -0400126 void record(GlitchType type, const SkOpSpanBase* base,
caryclark55888e42016-07-18 10:01:36 -0700127 const SkOpPtT* ptT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400128 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700129 glitch->fBase = base;
130 glitch->fCoinSpan = ptT;
131 }
132
Cary Clarkab87d7a2016-10-04 10:01:04 -0400133 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark55888e42016-07-18 10:01:36 -0700134 const SkCoincidentSpans* opp = NULL) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400135 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700136 glitch->fCoinSpan = coin->coinPtTStart();
137 glitch->fEndSpan = coin->coinPtTEnd();
138 if (opp) {
139 glitch->fOppSpan = opp->coinPtTStart();
140 glitch->fOppEndSpan = opp->coinPtTEnd();
141 }
caryclark26ad22a2015-10-16 09:03:38 -0700142 }
143
Cary Clarkab87d7a2016-10-04 10:01:04 -0400144 void record(GlitchType type, const SkOpSpanBase* base,
caryclark26ad22a2015-10-16 09:03:38 -0700145 const SkOpSegment* seg, double t, SkPoint pt) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400146 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700147 glitch->fBase = base;
148 glitch->fSegment = seg;
caryclark55888e42016-07-18 10:01:36 -0700149 glitch->fStartT = t;
caryclark26ad22a2015-10-16 09:03:38 -0700150 glitch->fPt = pt;
151 }
152
Cary Clarkab87d7a2016-10-04 10:01:04 -0400153 void record(GlitchType type, const SkOpSpanBase* base, double t,
caryclark26ad22a2015-10-16 09:03:38 -0700154 SkPoint pt) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400155 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700156 glitch->fBase = base;
caryclark55888e42016-07-18 10:01:36 -0700157 glitch->fStartT = t;
caryclark26ad22a2015-10-16 09:03:38 -0700158 glitch->fPt = pt;
159 }
160
Cary Clarkab87d7a2016-10-04 10:01:04 -0400161 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark26ad22a2015-10-16 09:03:38 -0700162 const SkOpPtT* coinSpan, const SkOpPtT* endSpan) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400163 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700164 glitch->fCoinSpan = coin->coinPtTStart();
165 glitch->fEndSpan = coin->coinPtTEnd();
caryclark26ad22a2015-10-16 09:03:38 -0700166 glitch->fEndSpan = endSpan;
caryclark55888e42016-07-18 10:01:36 -0700167 glitch->fOppSpan = coinSpan;
168 glitch->fOppEndSpan = endSpan;
caryclark26ad22a2015-10-16 09:03:38 -0700169 }
170
Cary Clarkab87d7a2016-10-04 10:01:04 -0400171 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark55888e42016-07-18 10:01:36 -0700172 const SkOpSpanBase* base) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400173 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700174 glitch->fBase = base;
175 glitch->fCoinSpan = coin->coinPtTStart();
176 glitch->fEndSpan = coin->coinPtTEnd();
caryclark26ad22a2015-10-16 09:03:38 -0700177 }
178
Cary Clarkab87d7a2016-10-04 10:01:04 -0400179 void record(GlitchType type, const SkOpPtT* ptTS, const SkOpPtT* ptTE,
caryclark26ad22a2015-10-16 09:03:38 -0700180 const SkOpPtT* oPtTS, const SkOpPtT* oPtTE) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400181 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700182 glitch->fCoinSpan = ptTS;
183 glitch->fEndSpan = ptTE;
184 glitch->fOppSpan = oPtTS;
185 glitch->fOppEndSpan = oPtTE;
186 }
187
Cary Clarkab87d7a2016-10-04 10:01:04 -0400188 void record(GlitchType type, const SkOpSegment* seg, double startT,
caryclark55888e42016-07-18 10:01:36 -0700189 double endT, const SkOpSegment* oppSeg, double oppStartT, double oppEndT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400190 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700191 glitch->fSegment = seg;
192 glitch->fStartT = startT;
193 glitch->fEndT = endT;
194 glitch->fOppSegment = oppSeg;
195 glitch->fOppStartT = oppStartT;
196 glitch->fOppEndT = oppEndT;
197 }
198
Cary Clarkab87d7a2016-10-04 10:01:04 -0400199 void record(GlitchType type, const SkOpSegment* seg,
caryclark55888e42016-07-18 10:01:36 -0700200 const SkOpSpan* span) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400201 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700202 glitch->fSegment = seg;
203 glitch->fBase = span;
204 }
205
Cary Clarkab87d7a2016-10-04 10:01:04 -0400206 void record(GlitchType type, double t, const SkOpSpanBase* span) {
207 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700208 glitch->fStartT = t;
209 glitch->fBase = span;
210 }
211
Cary Clarkab87d7a2016-10-04 10:01:04 -0400212 void record(GlitchType type, const SkOpSegment* seg) {
213 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700214 glitch->fSegment = seg;
215 }
216
Cary Clarkab87d7a2016-10-04 10:01:04 -0400217 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark55888e42016-07-18 10:01:36 -0700218 const SkOpPtT* ptT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400219 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700220 glitch->fCoinSpan = coin->coinPtTStart();
221 glitch->fEndSpan = ptT;
222 }
223
caryclark26ad22a2015-10-16 09:03:38 -0700224 SkTDArray<SpanGlitch> fGlitches;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400225 const SkOpGlobalState* fGlobalState;
caryclark26ad22a2015-10-16 09:03:38 -0700226};
Cary Clarkab87d7a2016-10-04 10:01:04 -0400227
228
229void SkPathOpsDebug::CoinDict::add(const SkPathOpsDebug::CoinDict& dict) {
230 int count = dict.fDict.count();
231 for (int index = 0; index < count; ++index) {
232 this->add(dict.fDict[index]);
233 }
234}
235
236void SkPathOpsDebug::CoinDict::add(const CoinDictEntry& key) {
237 int count = fDict.count();
238 for (int index = 0; index < count; ++index) {
239 CoinDictEntry* entry = &fDict[index];
240 if (entry->fIteration == key.fIteration && entry->fLineNumber == key.fLineNumber) {
241 SkASSERT(!strcmp(entry->fFunctionName, key.fFunctionName));
242 if (entry->fGlitchType == kUninitialized_Glitch) {
243 entry->fGlitchType = key.fGlitchType;
244 }
245 return;
246 }
247 }
248 *fDict.append() = key;
249}
250
251#endif
252
253#if DEBUG_COIN
254static void missing_coincidence(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) {
255 const SkOpContour* contour = contourList;
256 // bool result = false;
257 do {
258 /* result |= */ contour->debugMissingCoincidence(glitches);
259 } while ((contour = contour->next()));
260 return;
261}
262
263static void move_multiples(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) {
264 const SkOpContour* contour = contourList;
265 do {
266 if (contour->debugMoveMultiples(glitches), false) {
267 return;
268 }
269 } while ((contour = contour->next()));
270 return;
271}
272
273static void move_nearby(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) {
274 const SkOpContour* contour = contourList;
275 do {
276 contour->debugMoveNearby(glitches);
277 } while ((contour = contour->next()));
278}
279
280
281#endif
282
283#if DEBUG_COIN
284void SkOpGlobalState::debugAddToCoinChangedDict() {
285
286#if DEBUG_COINCIDENCE
caryclarke6522ea2016-10-17 07:54:33 -0700287 SkPathOpsDebug::CheckHealth(fContourHead);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400288#endif
289 // see if next coincident operation makes a change; if so, record it
290 SkPathOpsDebug::GlitchLog glitches;
291 const char* funcName = fCoinDictEntry.fFunctionName;
292 if (!strcmp("calc_angles", funcName)) {
293 ;
294 } else if (!strcmp("missing_coincidence", funcName)) {
295 missing_coincidence(&glitches, fContourHead);
296 } else if (!strcmp("move_multiples", funcName)) {
297 move_multiples(&glitches, fContourHead);
298 } else if (!strcmp("move_nearby", funcName)) {
299 move_nearby(&glitches, fContourHead);
300 } else if (!strcmp("addExpanded", funcName)) {
301 fCoincidence->debugAddExpanded(&glitches);
302 } else if (!strcmp("addMissing", funcName)) {
303 bool added;
304 fCoincidence->debugAddMissing(&glitches, &added);
305 } else if (!strcmp("addEndMovedSpans", funcName)) {
306 fCoincidence->debugAddEndMovedSpans(&glitches);
307 } else if (!strcmp("correctEnds", funcName)) {
308 fCoincidence->debugCorrectEnds(&glitches);
309 } else if (!strcmp("expand", funcName)) {
310 fCoincidence->debugExpand(&glitches);
311 } else if (!strcmp("findOverlaps", funcName)) {
312 ;
313 } else if (!strcmp("mark", funcName)) {
314 fCoincidence->debugMark(&glitches);
315 } else if (!strcmp("apply", funcName)) {
316 ;
317 } else {
318 SkASSERT(0); // add missing case
319 }
320 if (glitches.fGlitches.count()) {
321 fCoinDictEntry.fGlitchType = glitches.fGlitches[0].fType;
322 }
323 fCoinChangedDict.add(fCoinDictEntry);
324}
caryclark55888e42016-07-18 10:01:36 -0700325#endif
caryclark26ad22a2015-10-16 09:03:38 -0700326
caryclark55888e42016-07-18 10:01:36 -0700327void SkPathOpsDebug::ShowActiveSpans(SkOpContourHead* contourList) {
328#if DEBUG_ACTIVE_SPANS
Cary Clarkff114282016-12-14 11:56:16 -0500329 SkString str;
caryclark55888e42016-07-18 10:01:36 -0700330 SkOpContour* contour = contourList;
331 do {
Cary Clarkff114282016-12-14 11:56:16 -0500332 contour->debugShowActiveSpans(&str);
caryclark55888e42016-07-18 10:01:36 -0700333 } while ((contour = contour->next()));
Ben Wagner29380bd2017-10-09 14:43:00 -0400334 if (!gActiveSpans.equals(str)) {
335 const char* s = str.c_str();
336 const char* end;
337 while ((end = strchr(s, '\n'))) {
338 SkDebugf("%.*s", end - s + 1, s);
339 s = end + 1;
340 }
341 gActiveSpans.set(str);
342 }
caryclark55888e42016-07-18 10:01:36 -0700343#endif
344}
345
Cary Clarkab87d7a2016-10-04 10:01:04 -0400346#if DEBUG_COINCIDENCE || DEBUG_COIN
347void SkPathOpsDebug::CheckHealth(SkOpContourHead* contourList) {
caryclark55888e42016-07-18 10:01:36 -0700348#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -0700349 contourList->globalState()->debugSetCheckHealth(true);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400350#endif
351#if DEBUG_COIN
caryclark26ad22a2015-10-16 09:03:38 -0700352 GlitchLog glitches;
353 const SkOpContour* contour = contourList;
354 const SkOpCoincidence* coincidence = contour->globalState()->coincidence();
Cary Clarkab87d7a2016-10-04 10:01:04 -0400355 coincidence->debugCheckValid(&glitches); // don't call validate; spans may be inconsistent
caryclark26ad22a2015-10-16 09:03:38 -0700356 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400357 contour->debugCheckHealth(&glitches);
358 contour->debugMissingCoincidence(&glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700359 } while ((contour = contour->next()));
caryclark81a478c2016-09-09 09:37:57 -0700360 bool added;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400361 coincidence->debugAddMissing(&glitches, &added);
362 coincidence->debugExpand(&glitches);
363 coincidence->debugAddExpanded(&glitches);
364 coincidence->debugMark(&glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700365 unsigned mask = 0;
366 for (int index = 0; index < glitches.fGlitches.count(); ++index) {
367 const SpanGlitch& glitch = glitches.fGlitches[index];
368 mask |= 1 << glitch.fType;
369 }
370 for (int index = 0; index < kGlitchType_Count; ++index) {
371 SkDebugf(mask & (1 << index) ? "x" : "-");
372 }
Cary Clarkff114282016-12-14 11:56:16 -0500373 SkDebugf(" %s\n", contourList->globalState()->debugCoinDictEntry().fFunctionName);
caryclark55888e42016-07-18 10:01:36 -0700374 for (int index = 0; index < glitches.fGlitches.count(); ++index) {
375 const SpanGlitch& glitch = glitches.fGlitches[index];
376 SkDebugf("%02d: ", index);
377 if (glitch.fBase) {
caryclark8016b262016-09-06 05:59:47 -0700378 SkDebugf(" seg/base=%d/%d", glitch.fBase->segment()->debugID(),
379 glitch.fBase->debugID());
caryclark55888e42016-07-18 10:01:36 -0700380 }
381 if (glitch.fSuspect) {
caryclark8016b262016-09-06 05:59:47 -0700382 SkDebugf(" seg/base=%d/%d", glitch.fSuspect->segment()->debugID(),
383 glitch.fSuspect->debugID());
caryclark55888e42016-07-18 10:01:36 -0700384 }
385 if (glitch.fSegment) {
386 SkDebugf(" segment=%d", glitch.fSegment->debugID());
387 }
388 if (glitch.fCoinSpan) {
caryclark8016b262016-09-06 05:59:47 -0700389 SkDebugf(" coinSeg/Span/PtT=%d/%d/%d", glitch.fCoinSpan->segment()->debugID(),
390 glitch.fCoinSpan->span()->debugID(), glitch.fCoinSpan->debugID());
caryclark55888e42016-07-18 10:01:36 -0700391 }
392 if (glitch.fEndSpan) {
393 SkDebugf(" endSpan=%d", glitch.fEndSpan->debugID());
394 }
395 if (glitch.fOppSpan) {
caryclark8016b262016-09-06 05:59:47 -0700396 SkDebugf(" oppSeg/Span/PtT=%d/%d/%d", glitch.fOppSpan->segment()->debugID(),
397 glitch.fOppSpan->span()->debugID(), glitch.fOppSpan->debugID());
caryclark55888e42016-07-18 10:01:36 -0700398 }
399 if (glitch.fOppEndSpan) {
400 SkDebugf(" oppEndSpan=%d", glitch.fOppEndSpan->debugID());
401 }
402 if (!SkScalarIsNaN(glitch.fStartT)) {
403 SkDebugf(" startT=%g", glitch.fStartT);
404 }
405 if (!SkScalarIsNaN(glitch.fEndT)) {
406 SkDebugf(" endT=%g", glitch.fEndT);
407 }
408 if (glitch.fOppSegment) {
409 SkDebugf(" segment=%d", glitch.fOppSegment->debugID());
410 }
411 if (!SkScalarIsNaN(glitch.fOppStartT)) {
412 SkDebugf(" oppStartT=%g", glitch.fOppStartT);
413 }
414 if (!SkScalarIsNaN(glitch.fOppEndT)) {
415 SkDebugf(" oppEndT=%g", glitch.fOppEndT);
416 }
417 if (!SkScalarIsNaN(glitch.fPt.fX) || !SkScalarIsNaN(glitch.fPt.fY)) {
418 SkDebugf(" pt=%g,%g", glitch.fPt.fX, glitch.fPt.fY);
419 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400420 DumpGlitchType(glitch.fType);
caryclark55888e42016-07-18 10:01:36 -0700421 SkDebugf("\n");
422 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400423#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -0700424 contourList->globalState()->debugSetCheckHealth(false);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400425#endif
caryclark6c3b9cd2016-09-26 05:36:58 -0700426#if 01 && DEBUG_ACTIVE_SPANS
Cary Clarkab87d7a2016-10-04 10:01:04 -0400427// SkDebugf("active after %s:\n", id);
caryclark55888e42016-07-18 10:01:36 -0700428 ShowActiveSpans(contourList);
429#endif
430#endif
caryclark26ad22a2015-10-16 09:03:38 -0700431}
432#endif
433
Cary Clarkab87d7a2016-10-04 10:01:04 -0400434#if DEBUG_COIN
435void SkPathOpsDebug::DumpGlitchType(GlitchType glitchType) {
436 switch (glitchType) {
437 case kAddCorruptCoin_Glitch: SkDebugf(" AddCorruptCoin"); break;
438 case kAddExpandedCoin_Glitch: SkDebugf(" AddExpandedCoin"); break;
439 case kAddExpandedFail_Glitch: SkDebugf(" AddExpandedFail"); break;
440 case kAddIfCollapsed_Glitch: SkDebugf(" AddIfCollapsed"); break;; break;
441 case kAddIfMissingCoin_Glitch: SkDebugf(" AddIfMissingCoin"); break;
442 case kAddMissingCoin_Glitch: SkDebugf(" AddMissingCoin"); break;
443 case kAddMissingExtend_Glitch: SkDebugf(" AddMissingExtend"); break;
444 case kAddOrOverlap_Glitch: SkDebugf(" AAddOrOverlap"); break;
445 case kCollapsedCoin_Glitch: SkDebugf(" CollapsedCoin"); break;
446 case kCollapsedDone_Glitch: SkDebugf(" CollapsedDone"); break;
447 case kCollapsedOppValue_Glitch: SkDebugf(" CollapsedOppValue"); break;
448 case kCollapsedSpan_Glitch: SkDebugf(" CollapsedSpan"); break;
449 case kCollapsedWindValue_Glitch: SkDebugf(" CollapsedWindValue"); break;
450 case kCorrectEnd_Glitch: SkDebugf(" CorrectEnd"); break;
451 case kDeletedCoin_Glitch: SkDebugf(" DeletedCoin"); break;
452 case kExpandCoin_Glitch: SkDebugf(" ExpandCoin"); break;
453 case kFail_Glitch: SkDebugf(" Fail"); break;
454 case kMarkCoinEnd_Glitch: SkDebugf(" MarkCoinEnd"); break;
455 case kMarkCoinInsert_Glitch: SkDebugf(" MarkCoinInsert"); break;
456 case kMarkCoinMissing_Glitch: SkDebugf(" MarkCoinMissing"); break;
457 case kMarkCoinStart_Glitch: SkDebugf(" MarkCoinStart"); break;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400458 case kMergeMatches_Glitch: SkDebugf(" MergeMatches"); break;
459 case kMissingCoin_Glitch: SkDebugf(" MissingCoin"); break;
460 case kMissingDone_Glitch: SkDebugf(" MissingDone"); break;
461 case kMissingIntersection_Glitch: SkDebugf(" MissingIntersection"); break;
462 case kMoveMultiple_Glitch: SkDebugf(" MoveMultiple"); break;
463 case kMoveNearbyClearAll_Glitch: SkDebugf(" MoveNearbyClearAll"); break;
464 case kMoveNearbyClearAll2_Glitch: SkDebugf(" MoveNearbyClearAll2"); break;
465 case kMoveNearbyMerge_Glitch: SkDebugf(" MoveNearbyMerge"); break;
466 case kMoveNearbyMergeFinal_Glitch: SkDebugf(" MoveNearbyMergeFinal"); break;
467 case kMoveNearbyRelease_Glitch: SkDebugf(" MoveNearbyRelease"); break;
468 case kMoveNearbyReleaseFinal_Glitch: SkDebugf(" MoveNearbyReleaseFinal"); break;
469 case kReleasedSpan_Glitch: SkDebugf(" ReleasedSpan"); break;
470 case kReturnFalse_Glitch: SkDebugf(" ReturnFalse"); break;
471 case kUnaligned_Glitch: SkDebugf(" Unaligned"); break;
472 case kUnalignedHead_Glitch: SkDebugf(" UnalignedHead"); break;
473 case kUnalignedTail_Glitch: SkDebugf(" UnalignedTail"); break;
474 case kUninitialized_Glitch: break;
475 default: SkASSERT(0);
476 }
477}
478#endif
479
caryclark26ad22a2015-10-16 09:03:38 -0700480#if defined SK_DEBUG || !FORCE_RELEASE
caryclark@google.com570863f2013-09-16 15:55:01 +0000481void SkPathOpsDebug::MathematicaIze(char* str, size_t bufferLen) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000482 size_t len = strlen(str);
483 bool num = false;
484 for (size_t idx = 0; idx < len; ++idx) {
485 if (num && str[idx] == 'e') {
486 if (len + 2 >= bufferLen) {
487 return;
488 }
489 memmove(&str[idx + 2], &str[idx + 1], len - idx);
490 str[idx] = '*';
491 str[idx + 1] = '^';
492 ++len;
493 }
494 num = str[idx] >= '0' && str[idx] <= '9';
495 }
496}
497
caryclark@google.com570863f2013-09-16 15:55:01 +0000498bool SkPathOpsDebug::ValidWind(int wind) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000499 return wind > SK_MinS32 + 0xFFFF && wind < SK_MaxS32 - 0xFFFF;
500}
501
caryclark@google.com570863f2013-09-16 15:55:01 +0000502void SkPathOpsDebug::WindingPrintf(int wind) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000503 if (wind == SK_MinS32) {
504 SkDebugf("?");
505 } else {
506 SkDebugf("%d", wind);
507 }
508}
caryclark54359292015-03-26 07:52:43 -0700509#endif // defined SK_DEBUG || !FORCE_RELEASE
510
caryclark@google.coma5e55922013-05-07 18:51:31 +0000511
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000512#if DEBUG_SHOW_TEST_NAME
halcanary385fe4d2015-08-26 13:07:48 -0700513void* SkPathOpsDebug::CreateNameStr() { return new char[DEBUG_FILENAME_STRING_LENGTH]; }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000514
halcanary385fe4d2015-08-26 13:07:48 -0700515void SkPathOpsDebug::DeleteNameStr(void* v) { delete[] reinterpret_cast<char*>(v); }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000516
caryclark@google.com570863f2013-09-16 15:55:01 +0000517void SkPathOpsDebug::BumpTestName(char* test) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000518 char* num = test + strlen(test);
519 while (num[-1] >= '0' && num[-1] <= '9') {
520 --num;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000521 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000522 if (num[0] == '\0') {
523 return;
524 }
525 int dec = atoi(num);
526 if (dec == 0) {
527 return;
528 }
529 ++dec;
530 SK_SNPRINTF(num, DEBUG_FILENAME_STRING_LENGTH - (num - test), "%d", dec);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000531}
532#endif
caryclark@google.com570863f2013-09-16 15:55:01 +0000533
caryclark1049f122015-04-20 08:31:59 -0700534static void show_function_header(const char* functionName) {
535 SkDebugf("\nstatic void %s(skiatest::Reporter* reporter, const char* filename) {\n", functionName);
536 if (strcmp("skphealth_com76", functionName) == 0) {
537 SkDebugf("found it\n");
538 }
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000539}
caryclark1049f122015-04-20 08:31:59 -0700540
541static const char* gOpStrs[] = {
542 "kDifference_SkPathOp",
543 "kIntersect_SkPathOp",
544 "kUnion_SkPathOp",
caryclark55888e42016-07-18 10:01:36 -0700545 "kXOR_PathOp",
caryclark1049f122015-04-20 08:31:59 -0700546 "kReverseDifference_SkPathOp",
547};
548
caryclark03b03ca2015-04-23 09:13:37 -0700549const char* SkPathOpsDebug::OpStr(SkPathOp op) {
550 return gOpStrs[op];
551}
552
caryclark1049f122015-04-20 08:31:59 -0700553static void show_op(SkPathOp op, const char* pathOne, const char* pathTwo) {
554 SkDebugf(" testPathOp(reporter, %s, %s, %s, filename);\n", pathOne, pathTwo, gOpStrs[op]);
555 SkDebugf("}\n");
556}
557
reed086eea92016-05-04 17:12:46 -0700558SK_DECLARE_STATIC_MUTEX(gTestMutex);
caryclark1049f122015-04-20 08:31:59 -0700559
560void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
561 const char* testName) {
562 SkAutoMutexAcquire ac(gTestMutex);
563 show_function_header(testName);
564 ShowOnePath(a, "path", true);
565 ShowOnePath(b, "pathB", true);
566 show_op(shapeOp, "path", "pathB");
567}
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000568
caryclark27c8eb82015-07-06 11:38:33 -0700569#include "SkPathOpsTypes.h"
caryclark26ad22a2015-10-16 09:03:38 -0700570#include "SkIntersectionHelper.h"
571#include "SkIntersections.h"
572
Cary Clarkab87d7a2016-10-04 10:01:04 -0400573#if DEBUG_COIN
574
575SK_DECLARE_STATIC_MUTEX(gCoinDictMutex);
576
577void SkOpGlobalState::debugAddToGlobalCoinDicts() {
578 SkAutoMutexAcquire ac(&gCoinDictMutex);
579 SkPathOpsDebug::gCoinSumChangedDict.add(fCoinChangedDict);
580 SkPathOpsDebug::gCoinSumVisitedDict.add(fCoinVisitedDict);
581}
582
583#endif
584
caryclark26ad22a2015-10-16 09:03:38 -0700585#if DEBUG_T_SECT_LOOP_COUNT
586void SkOpGlobalState::debugAddLoopCount(SkIntersections* i, const SkIntersectionHelper& wt,
587 const SkIntersectionHelper& wn) {
588 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
589 SkIntersections::DebugLoop looper = (SkIntersections::DebugLoop) index;
590 if (fDebugLoopCount[index] >= i->debugLoopCount(looper)) {
591 continue;
592 }
593 fDebugLoopCount[index] = i->debugLoopCount(looper);
594 fDebugWorstVerb[index * 2] = wt.segment()->verb();
595 fDebugWorstVerb[index * 2 + 1] = wn.segment()->verb();
596 sk_bzero(&fDebugWorstPts[index * 8], sizeof(SkPoint) * 8);
597 memcpy(&fDebugWorstPts[index * 2 * 4], wt.pts(),
598 (SkPathOpsVerbToPoints(wt.segment()->verb()) + 1) * sizeof(SkPoint));
599 memcpy(&fDebugWorstPts[(index * 2 + 1) * 4], wn.pts(),
600 (SkPathOpsVerbToPoints(wn.segment()->verb()) + 1) * sizeof(SkPoint));
601 fDebugWorstWeight[index * 2] = wt.weight();
602 fDebugWorstWeight[index * 2 + 1] = wn.weight();
603 }
604 i->debugResetLoopCount();
605}
606
607void SkOpGlobalState::debugDoYourWorst(SkOpGlobalState* local) {
608 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
609 if (fDebugLoopCount[index] >= local->fDebugLoopCount[index]) {
610 continue;
611 }
612 fDebugLoopCount[index] = local->fDebugLoopCount[index];
613 fDebugWorstVerb[index * 2] = local->fDebugWorstVerb[index * 2];
614 fDebugWorstVerb[index * 2 + 1] = local->fDebugWorstVerb[index * 2 + 1];
615 memcpy(&fDebugWorstPts[index * 2 * 4], &local->fDebugWorstPts[index * 2 * 4],
616 sizeof(SkPoint) * 8);
617 fDebugWorstWeight[index * 2] = local->fDebugWorstWeight[index * 2];
618 fDebugWorstWeight[index * 2 + 1] = local->fDebugWorstWeight[index * 2 + 1];
619 }
620 local->debugResetLoopCounts();
621}
622
623static void dump_curve(SkPath::Verb verb, const SkPoint& pts, float weight) {
624 if (!verb) {
625 return;
626 }
627 const char* verbs[] = { "", "line", "quad", "conic", "cubic" };
628 SkDebugf("%s: {{", verbs[verb]);
629 int ptCount = SkPathOpsVerbToPoints(verb);
630 for (int index = 0; index <= ptCount; ++index) {
631 SkDPoint::Dump((&pts)[index]);
632 if (index < ptCount - 1) {
633 SkDebugf(", ");
634 }
635 }
636 SkDebugf("}");
637 if (weight != 1) {
638 SkDebugf(", ");
639 if (weight == floorf(weight)) {
640 SkDebugf("%.0f", weight);
641 } else {
642 SkDebugf("%1.9gf", weight);
643 }
644 }
645 SkDebugf("}\n");
646}
647
648void SkOpGlobalState::debugLoopReport() {
649 const char* loops[] = { "iterations", "coinChecks", "perpCalcs" };
650 SkDebugf("\n");
651 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
652 SkDebugf("%s: %d\n", loops[index], fDebugLoopCount[index]);
653 dump_curve(fDebugWorstVerb[index * 2], fDebugWorstPts[index * 2 * 4],
654 fDebugWorstWeight[index * 2]);
655 dump_curve(fDebugWorstVerb[index * 2 + 1], fDebugWorstPts[(index * 2 + 1) * 4],
656 fDebugWorstWeight[index * 2 + 1]);
657 }
658}
659
660void SkOpGlobalState::debugResetLoopCounts() {
661 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
662 sk_bzero(fDebugWorstVerb, sizeof(fDebugWorstVerb));
663 sk_bzero(fDebugWorstPts, sizeof(fDebugWorstPts));
664 sk_bzero(fDebugWorstWeight, sizeof(fDebugWorstWeight));
665}
666#endif
caryclark27c8eb82015-07-06 11:38:33 -0700667
Cary Clark59d5a0e2017-01-23 14:38:52 +0000668bool SkOpGlobalState::DebugRunFail() {
669 return SkPathOpsDebug::gRunFail;
670}
671
Cary Clarkab87d7a2016-10-04 10:01:04 -0400672// this is const so it can be called by const methods that overwise don't alter state
673#if DEBUG_VALIDATE || DEBUG_COIN
674void SkOpGlobalState::debugSetPhase(const char* funcName DEBUG_COIN_DECLARE_PARAMS()) const {
675 auto writable = const_cast<SkOpGlobalState*>(this);
676#if DEBUG_VALIDATE
677 writable->setPhase(phase);
678#endif
679#if DEBUG_COIN
680 SkPathOpsDebug::CoinDictEntry* entry = &writable->fCoinDictEntry;
681 writable->fPreviousFuncName = entry->fFunctionName;
682 entry->fIteration = iteration;
683 entry->fLineNumber = lineNo;
684 entry->fGlitchType = SkPathOpsDebug::kUninitialized_Glitch;
685 entry->fFunctionName = funcName;
686 writable->fCoinVisitedDict.add(*entry);
687 writable->debugAddToCoinChangedDict();
688#endif
689}
690#endif
691
caryclark26ad22a2015-10-16 09:03:38 -0700692#if DEBUG_T_SECT_LOOP_COUNT
693void SkIntersections::debugBumpLoopCount(DebugLoop index) {
694 fDebugLoopCount[index]++;
695}
696
697int SkIntersections::debugLoopCount(DebugLoop index) const {
698 return fDebugLoopCount[index];
699}
700
701void SkIntersections::debugResetLoopCount() {
702 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
703}
704#endif
705
caryclarka35ab3e2016-10-20 08:32:18 -0700706#include "SkPathOpsConic.h"
caryclark624637c2015-05-11 07:21:27 -0700707#include "SkPathOpsCubic.h"
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000708
caryclark624637c2015-05-11 07:21:27 -0700709SkDCubic SkDQuad::debugToCubic() const {
710 SkDCubic cubic;
711 cubic[0] = fPts[0];
712 cubic[2] = fPts[1];
713 cubic[3] = fPts[2];
714 cubic[1].fX = (cubic[0].fX + cubic[2].fX * 2) / 3;
715 cubic[1].fY = (cubic[0].fY + cubic[2].fY * 2) / 3;
716 cubic[2].fX = (cubic[3].fX + cubic[2].fX * 2) / 3;
717 cubic[2].fY = (cubic[3].fY + cubic[2].fY * 2) / 3;
718 return cubic;
caryclark54359292015-03-26 07:52:43 -0700719}
caryclark624637c2015-05-11 07:21:27 -0700720
caryclarka35ab3e2016-10-20 08:32:18 -0700721void SkDQuad::debugSet(const SkDPoint* pts) {
722 memcpy(fPts, pts, sizeof(fPts));
723 SkDEBUGCODE(fDebugGlobalState = nullptr);
724}
725
726void SkDCubic::debugSet(const SkDPoint* pts) {
727 memcpy(fPts, pts, sizeof(fPts));
728 SkDEBUGCODE(fDebugGlobalState = nullptr);
729}
730
731void SkDConic::debugSet(const SkDPoint* pts, SkScalar weight) {
732 fPts.debugSet(pts);
733 fWeight = weight;
734}
735
caryclarked0935a2015-10-22 07:23:52 -0700736void SkDRect::debugInit() {
737 fLeft = fTop = fRight = fBottom = SK_ScalarNaN;
738}
739
caryclark624637c2015-05-11 07:21:27 -0700740#include "SkOpAngle.h"
caryclark624637c2015-05-11 07:21:27 -0700741#include "SkOpSegment.h"
caryclark54359292015-03-26 07:52:43 -0700742
Cary Clarkab87d7a2016-10-04 10:01:04 -0400743#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -0700744// commented-out lines keep this in sync with addT()
Cary Clarkab87d7a2016-10-04 10:01:04 -0400745 const SkOpPtT* SkOpSegment::debugAddT(double t, SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -0700746 debugValidate();
747 SkPoint pt = this->ptAtT(t);
caryclark26ad22a2015-10-16 09:03:38 -0700748 const SkOpSpanBase* span = &fHead;
caryclark55888e42016-07-18 10:01:36 -0700749 do {
750 const SkOpPtT* result = span->ptT();
caryclark29b25632016-08-25 11:27:17 -0700751 if (t == result->fT || this->match(result, this, t, pt)) {
caryclark55888e42016-07-18 10:01:36 -0700752// span->bumpSpanAdds();
caryclarkef4f32a2016-08-24 09:24:18 -0700753 return result;
caryclark26ad22a2015-10-16 09:03:38 -0700754 }
caryclark55888e42016-07-18 10:01:36 -0700755 if (t < result->fT) {
756 const SkOpSpan* prev = result->span()->prev();
caryclark30b9fdd2016-08-31 14:36:29 -0700757 FAIL_WITH_NULL_IF(!prev, span);
caryclark29b25632016-08-25 11:27:17 -0700758 // marks in global state that new op span has been allocated
759 this->globalState()->setAllocatedOpSpan();
caryclark55888e42016-07-18 10:01:36 -0700760// span->init(this, prev, t, pt);
761 this->debugValidate();
762// #if DEBUG_ADD_T
763// SkDebugf("%s insert t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
764// span->segment()->debugID(), span->debugID());
765// #endif
766// span->bumpSpanAdds();
caryclark55888e42016-07-18 10:01:36 -0700767 return nullptr;
caryclark26ad22a2015-10-16 09:03:38 -0700768 }
caryclark30b9fdd2016-08-31 14:36:29 -0700769 FAIL_WITH_NULL_IF(span != &fTail, span);
caryclark55888e42016-07-18 10:01:36 -0700770 } while ((span = span->upCast()->next()));
771 SkASSERT(0);
caryclark29b25632016-08-25 11:27:17 -0700772 return nullptr; // we never get here, but need this to satisfy compiler
caryclark26ad22a2015-10-16 09:03:38 -0700773}
774#endif
775
776#if DEBUG_ANGLE
777void SkOpSegment::debugCheckAngleCoin() const {
778 const SkOpSpanBase* base = &fHead;
779 const SkOpSpan* span;
780 do {
781 const SkOpAngle* angle = base->fromAngle();
caryclark55888e42016-07-18 10:01:36 -0700782 if (angle && angle->debugCheckCoincidence()) {
caryclark26ad22a2015-10-16 09:03:38 -0700783 angle->debugCheckNearCoincidence();
784 }
785 if (base->final()) {
786 break;
787 }
788 span = base->upCast();
789 angle = span->toAngle();
caryclark55888e42016-07-18 10:01:36 -0700790 if (angle && angle->debugCheckCoincidence()) {
caryclark26ad22a2015-10-16 09:03:38 -0700791 angle->debugCheckNearCoincidence();
792 }
793 } while ((base = span->next()));
794}
795#endif
796
Cary Clarkab87d7a2016-10-04 10:01:04 -0400797#if DEBUG_COIN
caryclark26ad22a2015-10-16 09:03:38 -0700798// this mimics the order of the checks in handle coincidence
Cary Clarkab87d7a2016-10-04 10:01:04 -0400799void SkOpSegment::debugCheckHealth(SkPathOpsDebug::GlitchLog* glitches) const {
800 debugMoveMultiples(glitches);
801 debugMoveNearby(glitches);
802 debugMissingCoincidence(glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700803}
804
caryclark55888e42016-07-18 10:01:36 -0700805// commented-out lines keep this in sync with clearAll()
Cary Clarkab87d7a2016-10-04 10:01:04 -0400806void SkOpSegment::debugClearAll(SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -0700807 const SkOpSpan* span = &fHead;
808 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400809 this->debugClearOne(span, glitches);
caryclark55888e42016-07-18 10:01:36 -0700810 } while ((span = span->next()->upCastable()));
Cary Clarkab87d7a2016-10-04 10:01:04 -0400811 this->globalState()->coincidence()->debugRelease(glitches, this);
caryclark55888e42016-07-18 10:01:36 -0700812}
813
814// commented-out lines keep this in sync with clearOne()
Cary Clarkab87d7a2016-10-04 10:01:04 -0400815void SkOpSegment::debugClearOne(const SkOpSpan* span, SkPathOpsDebug::GlitchLog* glitches) const {
816 if (span->windValue()) glitches->record(SkPathOpsDebug::kCollapsedWindValue_Glitch, span);
817 if (span->oppValue()) glitches->record(SkPathOpsDebug::kCollapsedOppValue_Glitch, span);
818 if (!span->done()) glitches->record(SkPathOpsDebug::kCollapsedDone_Glitch, span);
caryclark26ad22a2015-10-16 09:03:38 -0700819}
820#endif
821
caryclark54359292015-03-26 07:52:43 -0700822SkOpAngle* SkOpSegment::debugLastAngle() {
halcanary96fcdcc2015-08-27 07:41:13 -0700823 SkOpAngle* result = nullptr;
caryclark54359292015-03-26 07:52:43 -0700824 SkOpSpan* span = this->head();
825 do {
826 if (span->toAngle()) {
827 SkASSERT(!result);
828 result = span->toAngle();
829 }
830 } while ((span = span->next()->upCastable()));
831 SkASSERT(result);
832 return result;
833}
834
Cary Clarkab87d7a2016-10-04 10:01:04 -0400835#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -0700836// commented-out lines keep this in sync with ClearVisited
837void SkOpSegment::DebugClearVisited(const SkOpSpanBase* span) {
838 // reset visited flag back to false
839 do {
840 const SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
841 while ((ptT = ptT->next()) != stopPtT) {
842 const SkOpSegment* opp = ptT->segment();
843 opp->resetDebugVisited();
844 }
845 } while (!span->final() && (span = span->upCast()->next()));
846}
847#endif
848
Cary Clarkab87d7a2016-10-04 10:01:04 -0400849#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -0700850// commented-out lines keep this in sync with missingCoincidence()
851// look for pairs of undetected coincident curves
852// assumes that segments going in have visited flag clear
853// Even though pairs of curves correct detect coincident runs, a run may be missed
854// if the coincidence is a product of multiple intersections. For instance, given
855// curves A, B, and C:
856// A-B intersect at a point 1; A-C and B-C intersect at point 2, so near
857// the end of C that the intersection is replaced with the end of C.
858// Even though A-B correctly do not detect an intersection at point 2,
859// the resulting run from point 1 to point 2 is coincident on A and B.
Cary Clarkab87d7a2016-10-04 10:01:04 -0400860void SkOpSegment::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -0700861 if (this->done()) {
862 return;
863 }
864 const SkOpSpan* prior = nullptr;
865 const SkOpSpanBase* spanBase = &fHead;
caryclark55888e42016-07-18 10:01:36 -0700866// bool result = false;
caryclark26ad22a2015-10-16 09:03:38 -0700867 do {
868 const SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT;
869 SkASSERT(ptT->span() == spanBase);
870 while ((ptT = ptT->next()) != spanStopPtT) {
871 if (ptT->deleted()) {
872 continue;
873 }
caryclark55888e42016-07-18 10:01:36 -0700874 const SkOpSegment* opp = ptT->span()->segment();
caryclark26ad22a2015-10-16 09:03:38 -0700875 if (opp->done()) {
876 continue;
877 }
878 // when opp is encounted the 1st time, continue; on 2nd encounter, look for coincidence
caryclark55888e42016-07-18 10:01:36 -0700879 if (!opp->debugVisited()) {
caryclark26ad22a2015-10-16 09:03:38 -0700880 continue;
881 }
882 if (spanBase == &fHead) {
883 continue;
884 }
caryclark55888e42016-07-18 10:01:36 -0700885 if (ptT->segment() == this) {
886 continue;
887 }
caryclark26ad22a2015-10-16 09:03:38 -0700888 const SkOpSpan* span = spanBase->upCastable();
889 // FIXME?: this assumes that if the opposite segment is coincident then no more
890 // coincidence needs to be detected. This may not be true.
caryclark55888e42016-07-18 10:01:36 -0700891 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 -0700892 continue;
893 }
caryclark55888e42016-07-18 10:01:36 -0700894 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 -0700895 continue;
caryclark55888e42016-07-18 10:01:36 -0700896 }
caryclark26ad22a2015-10-16 09:03:38 -0700897 const SkOpPtT* priorPtT = nullptr, * priorStopPtT;
898 // find prior span containing opp segment
899 const SkOpSegment* priorOpp = nullptr;
900 const SkOpSpan* priorTest = spanBase->prev();
901 while (!priorOpp && priorTest) {
902 priorStopPtT = priorPtT = priorTest->ptT();
903 while ((priorPtT = priorPtT->next()) != priorStopPtT) {
904 if (priorPtT->deleted()) {
905 continue;
906 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400907 const SkOpSegment* segment = priorPtT->span()->segment();
caryclark26ad22a2015-10-16 09:03:38 -0700908 if (segment == opp) {
909 prior = priorTest;
910 priorOpp = opp;
911 break;
912 }
913 }
914 priorTest = priorTest->prev();
915 }
916 if (!priorOpp) {
917 continue;
918 }
caryclark55888e42016-07-18 10:01:36 -0700919 if (priorPtT == ptT) {
920 continue;
921 }
caryclark26ad22a2015-10-16 09:03:38 -0700922 const SkOpPtT* oppStart = prior->ptT();
923 const SkOpPtT* oppEnd = spanBase->ptT();
924 bool swapped = priorPtT->fT > ptT->fT;
925 if (swapped) {
926 SkTSwap(priorPtT, ptT);
927 SkTSwap(oppStart, oppEnd);
928 }
caryclark55888e42016-07-18 10:01:36 -0700929 const SkOpCoincidence* coincidence = this->globalState()->coincidence();
930 const SkOpPtT* rootPriorPtT = priorPtT->span()->ptT();
931 const SkOpPtT* rootPtT = ptT->span()->ptT();
932 const SkOpPtT* rootOppStart = oppStart->span()->ptT();
933 const SkOpPtT* rootOppEnd = oppEnd->span()->ptT();
934 if (coincidence->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) {
caryclark26ad22a2015-10-16 09:03:38 -0700935 goto swapBack;
936 }
caryclark55888e42016-07-18 10:01:36 -0700937 if (testForCoincidence(rootPriorPtT, rootPtT, prior, spanBase, opp)) {
938 // mark coincidence
caryclark30b9fdd2016-08-31 14:36:29 -0700939#if DEBUG_COINCIDENCE_VERBOSE
caryclark55888e42016-07-18 10:01:36 -0700940// SkDebugf("%s coinSpan=%d endSpan=%d oppSpan=%d oppEndSpan=%d\n", __FUNCTION__,
941// rootPriorPtT->debugID(), rootPtT->debugID(), rootOppStart->debugID(),
942// rootOppEnd->debugID());
943#endif
Cary Clarkab87d7a2016-10-04 10:01:04 -0400944 log->record(SkPathOpsDebug::kMissingCoin_Glitch, priorPtT, ptT, oppStart, oppEnd);
caryclark55888e42016-07-18 10:01:36 -0700945 // coincidences->add(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
946 // }
947#if DEBUG_COINCIDENCE
caryclarkd6562002016-07-27 12:02:07 -0700948// SkASSERT(coincidences->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
caryclark55888e42016-07-18 10:01:36 -0700949#endif
950 // result = true;
caryclark26ad22a2015-10-16 09:03:38 -0700951 }
952 swapBack:
953 if (swapped) {
954 SkTSwap(priorPtT, ptT);
955 }
956 }
957 } while ((spanBase = spanBase->final() ? nullptr : spanBase->upCast()->next()));
caryclark55888e42016-07-18 10:01:36 -0700958 DebugClearVisited(&fHead);
959 return;
caryclark26ad22a2015-10-16 09:03:38 -0700960}
961
caryclark55888e42016-07-18 10:01:36 -0700962// commented-out lines keep this in sync with moveMultiples()
963// if a span has more than one intersection, merge the other segments' span as needed
Cary Clarkab87d7a2016-10-04 10:01:04 -0400964void SkOpSegment::debugMoveMultiples(SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -0700965 debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -0700966 const SkOpSpanBase* test = &fHead;
967 do {
968 int addCount = test->spanAddsCount();
Cary Clarkff114282016-12-14 11:56:16 -0500969// SkASSERT(addCount >= 1);
970 if (addCount <= 1) {
caryclark26ad22a2015-10-16 09:03:38 -0700971 continue;
972 }
973 const SkOpPtT* startPtT = test->ptT();
974 const SkOpPtT* testPtT = startPtT;
975 do { // iterate through all spans associated with start
976 const SkOpSpanBase* oppSpan = testPtT->span();
977 if (oppSpan->spanAddsCount() == addCount) {
978 continue;
979 }
980 if (oppSpan->deleted()) {
981 continue;
982 }
983 const SkOpSegment* oppSegment = oppSpan->segment();
984 if (oppSegment == this) {
985 continue;
986 }
987 // find range of spans to consider merging
988 const SkOpSpanBase* oppPrev = oppSpan;
989 const SkOpSpanBase* oppFirst = oppSpan;
990 while ((oppPrev = oppPrev->prev())) {
991 if (!roughly_equal(oppPrev->t(), oppSpan->t())) {
992 break;
993 }
994 if (oppPrev->spanAddsCount() == addCount) {
995 continue;
996 }
997 if (oppPrev->deleted()) {
998 continue;
999 }
1000 oppFirst = oppPrev;
1001 }
1002 const SkOpSpanBase* oppNext = oppSpan;
1003 const SkOpSpanBase* oppLast = oppSpan;
1004 while ((oppNext = oppNext->final() ? nullptr : oppNext->upCast()->next())) {
1005 if (!roughly_equal(oppNext->t(), oppSpan->t())) {
1006 break;
1007 }
1008 if (oppNext->spanAddsCount() == addCount) {
1009 continue;
1010 }
1011 if (oppNext->deleted()) {
1012 continue;
1013 }
1014 oppLast = oppNext;
1015 }
1016 if (oppFirst == oppLast) {
1017 continue;
1018 }
1019 const SkOpSpanBase* oppTest = oppFirst;
1020 do {
1021 if (oppTest == oppSpan) {
1022 continue;
1023 }
1024 // check to see if the candidate meets specific criteria:
1025 // it contains spans of segments in test's loop but not including 'this'
1026 const SkOpPtT* oppStartPtT = oppTest->ptT();
1027 const SkOpPtT* oppPtT = oppStartPtT;
1028 while ((oppPtT = oppPtT->next()) != oppStartPtT) {
1029 const SkOpSegment* oppPtTSegment = oppPtT->segment();
1030 if (oppPtTSegment == this) {
1031 goto tryNextSpan;
1032 }
1033 const SkOpPtT* matchPtT = startPtT;
1034 do {
1035 if (matchPtT->segment() == oppPtTSegment) {
1036 goto foundMatch;
1037 }
1038 } while ((matchPtT = matchPtT->next()) != startPtT);
1039 goto tryNextSpan;
1040 foundMatch: // merge oppTest and oppSpan
caryclark55888e42016-07-18 10:01:36 -07001041 oppSegment->debugValidate();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001042 oppTest->debugMergeMatches(glitches, oppSpan);
1043 oppTest->debugAddOpp(glitches, oppSpan);
caryclark55888e42016-07-18 10:01:36 -07001044 oppSegment->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001045 goto checkNextSpan;
1046 }
caryclark55888e42016-07-18 10:01:36 -07001047 tryNextSpan:
caryclark26ad22a2015-10-16 09:03:38 -07001048 ;
1049 } while (oppTest != oppLast && (oppTest = oppTest->upCast()->next()));
1050 } while ((testPtT = testPtT->next()) != startPtT);
caryclark55888e42016-07-18 10:01:36 -07001051checkNextSpan:
caryclark26ad22a2015-10-16 09:03:38 -07001052 ;
1053 } while ((test = test->final() ? nullptr : test->upCast()->next()));
caryclark55888e42016-07-18 10:01:36 -07001054 debugValidate();
1055 return;
caryclark26ad22a2015-10-16 09:03:38 -07001056}
1057
caryclark55888e42016-07-18 10:01:36 -07001058// commented-out lines keep this in sync with moveNearby()
1059// Move nearby t values and pts so they all hang off the same span. Alignment happens later.
Cary Clarkab87d7a2016-10-04 10:01:04 -04001060void SkOpSegment::debugMoveNearby(SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -07001061 debugValidate();
1062 // release undeleted spans pointing to this seg that are linked to the primary span
1063 const SkOpSpanBase* spanBase = &fHead;
caryclark26ad22a2015-10-16 09:03:38 -07001064 do {
caryclark55888e42016-07-18 10:01:36 -07001065 const SkOpPtT* ptT = spanBase->ptT();
1066 const SkOpPtT* headPtT = ptT;
1067 while ((ptT = ptT->next()) != headPtT) {
1068 const SkOpSpanBase* test = ptT->span();
1069 if (ptT->segment() == this && !ptT->deleted() && test != spanBase
1070 && test->ptT() == ptT) {
1071 if (test->final()) {
1072 if (spanBase == &fHead) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001073 glitches->record(SkPathOpsDebug::kMoveNearbyClearAll_Glitch, this);
caryclark55888e42016-07-18 10:01:36 -07001074// return;
1075 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001076 glitches->record(SkPathOpsDebug::kMoveNearbyReleaseFinal_Glitch, spanBase, ptT);
caryclark55888e42016-07-18 10:01:36 -07001077 } else if (test->prev()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001078 glitches->record(SkPathOpsDebug::kMoveNearbyRelease_Glitch, test, headPtT);
caryclark55888e42016-07-18 10:01:36 -07001079 }
1080// break;
caryclark26ad22a2015-10-16 09:03:38 -07001081 }
1082 }
caryclark55888e42016-07-18 10:01:36 -07001083 spanBase = spanBase->upCast()->next();
1084 } while (!spanBase->final());
1085
1086 // This loop looks for adjacent spans which are near by
1087 spanBase = &fHead;
1088 do { // iterate through all spans associated with start
1089 const SkOpSpanBase* test = spanBase->upCast()->next();
Cary Clark73e597d2017-04-18 12:08:58 -04001090 bool found;
1091 if (!this->spansNearby(spanBase, test, &found)) {
1092 glitches->record(SkPathOpsDebug::kMoveNearbyMergeFinal_Glitch, test);
1093 }
1094 if (found) {
caryclark55888e42016-07-18 10:01:36 -07001095 if (test->final()) {
1096 if (spanBase->prev()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001097 glitches->record(SkPathOpsDebug::kMoveNearbyMergeFinal_Glitch, test);
caryclark55888e42016-07-18 10:01:36 -07001098 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001099 glitches->record(SkPathOpsDebug::kMoveNearbyClearAll2_Glitch, this);
caryclark55888e42016-07-18 10:01:36 -07001100 // return
1101 }
1102 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001103 glitches->record(SkPathOpsDebug::kMoveNearbyMerge_Glitch, spanBase);
caryclark55888e42016-07-18 10:01:36 -07001104 }
1105 }
1106 spanBase = test;
1107 } while (!spanBase->final());
1108 debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001109}
1110#endif
1111
caryclark54359292015-03-26 07:52:43 -07001112void SkOpSegment::debugReset() {
caryclark1049f122015-04-20 08:31:59 -07001113 this->init(this->fPts, this->fWeight, this->contour(), this->verb());
caryclark54359292015-03-26 07:52:43 -07001114}
1115
caryclark025b11e2016-08-25 05:21:14 -07001116#if DEBUG_COINCIDENCE_ORDER
1117void SkOpSegment::debugSetCoinT(int index, SkScalar t) const {
1118 if (fDebugBaseMax < 0 || fDebugBaseIndex == index) {
1119 fDebugBaseIndex = index;
1120 fDebugBaseMin = SkTMin(t, fDebugBaseMin);
1121 fDebugBaseMax = SkTMax(t, fDebugBaseMax);
1122 return;
Ben Wagner63fd7602017-10-09 15:45:33 -04001123 }
caryclark025b11e2016-08-25 05:21:14 -07001124 SkASSERT(fDebugBaseMin >= t || t >= fDebugBaseMax);
1125 if (fDebugLastMax < 0 || fDebugLastIndex == index) {
1126 fDebugLastIndex = index;
1127 fDebugLastMin = SkTMin(t, fDebugLastMin);
1128 fDebugLastMax = SkTMax(t, fDebugLastMax);
1129 return;
1130 }
1131 SkASSERT(fDebugLastMin >= t || t >= fDebugLastMax);
1132 SkASSERT((t - fDebugBaseMin > 0) == (fDebugLastMin - fDebugBaseMin > 0));
1133}
1134#endif
1135
caryclark54359292015-03-26 07:52:43 -07001136#if DEBUG_ACTIVE_SPANS
Cary Clarkff114282016-12-14 11:56:16 -05001137void SkOpSegment::debugShowActiveSpans(SkString* str) const {
caryclark54359292015-03-26 07:52:43 -07001138 debugValidate();
1139 if (done()) {
1140 return;
1141 }
1142 int lastId = -1;
1143 double lastT = -1;
1144 const SkOpSpan* span = &fHead;
1145 do {
1146 if (span->done()) {
1147 continue;
1148 }
caryclark1049f122015-04-20 08:31:59 -07001149 if (lastId == this->debugID() && lastT == span->t()) {
caryclark54359292015-03-26 07:52:43 -07001150 continue;
1151 }
caryclark1049f122015-04-20 08:31:59 -07001152 lastId = this->debugID();
caryclark54359292015-03-26 07:52:43 -07001153 lastT = span->t();
Cary Clarkff114282016-12-14 11:56:16 -05001154 str->appendf("%s id=%d", __FUNCTION__, this->debugID());
caryclark55888e42016-07-18 10:01:36 -07001155 // since endpoints may have be adjusted, show actual computed curves
1156 SkDCurve curvePart;
1157 this->subDivide(span, span->next(), &curvePart);
1158 const SkDPoint* pts = curvePart.fCubic.fPts;
Cary Clarkff114282016-12-14 11:56:16 -05001159 str->appendf(" (%1.9g,%1.9g", pts[0].fX, pts[0].fY);
caryclark54359292015-03-26 07:52:43 -07001160 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
Cary Clarkff114282016-12-14 11:56:16 -05001161 str->appendf(" %1.9g,%1.9g", pts[vIndex].fX, pts[vIndex].fY);
caryclark54359292015-03-26 07:52:43 -07001162 }
caryclark1049f122015-04-20 08:31:59 -07001163 if (SkPath::kConic_Verb == fVerb) {
Cary Clarkff114282016-12-14 11:56:16 -05001164 str->appendf(" %1.9gf", curvePart.fConic.fWeight);
caryclark1049f122015-04-20 08:31:59 -07001165 }
Cary Clarkff114282016-12-14 11:56:16 -05001166 str->appendf(") t=%1.9g tEnd=%1.9g", span->t(), span->next()->t());
caryclark54359292015-03-26 07:52:43 -07001167 if (span->windSum() == SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001168 str->appendf(" windSum=?");
caryclark54359292015-03-26 07:52:43 -07001169 } else {
Cary Clarkff114282016-12-14 11:56:16 -05001170 str->appendf(" windSum=%d", span->windSum());
caryclark54359292015-03-26 07:52:43 -07001171 }
caryclark624637c2015-05-11 07:21:27 -07001172 if (span->oppValue() && span->oppSum() == SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001173 str->appendf(" oppSum=?");
caryclark624637c2015-05-11 07:21:27 -07001174 } else if (span->oppValue() || span->oppSum() != SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001175 str->appendf(" oppSum=%d", span->oppSum());
caryclark624637c2015-05-11 07:21:27 -07001176 }
Cary Clarkff114282016-12-14 11:56:16 -05001177 str->appendf(" windValue=%d", span->windValue());
caryclark624637c2015-05-11 07:21:27 -07001178 if (span->oppValue() || span->oppSum() != SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001179 str->appendf(" oppValue=%d", span->oppValue());
caryclark624637c2015-05-11 07:21:27 -07001180 }
Cary Clarkff114282016-12-14 11:56:16 -05001181 str->appendf("\n");
caryclark54359292015-03-26 07:52:43 -07001182 } while ((span = span->next()->upCastable()));
1183}
1184#endif
1185
1186#if DEBUG_MARK_DONE
1187void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding) {
1188 const SkPoint& pt = span->ptT()->fPt;
caryclark1049f122015-04-20 08:31:59 -07001189 SkDebugf("%s id=%d", fun, this->debugID());
caryclark54359292015-03-26 07:52:43 -07001190 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1191 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1192 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1193 }
1194 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
1195 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t());
1196 if (winding == SK_MinS32) {
1197 SkDebugf("?");
1198 } else {
1199 SkDebugf("%d", winding);
1200 }
1201 SkDebugf(" windSum=");
1202 if (span->windSum() == SK_MinS32) {
1203 SkDebugf("?");
1204 } else {
1205 SkDebugf("%d", span->windSum());
1206 }
1207 SkDebugf(" windValue=%d\n", span->windValue());
1208}
1209
1210void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding,
1211 int oppWinding) {
1212 const SkPoint& pt = span->ptT()->fPt;
caryclark1049f122015-04-20 08:31:59 -07001213 SkDebugf("%s id=%d", fun, this->debugID());
caryclark54359292015-03-26 07:52:43 -07001214 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1215 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1216 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1217 }
1218 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
1219 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t(), winding, oppWinding);
1220 if (winding == SK_MinS32) {
1221 SkDebugf("?");
1222 } else {
1223 SkDebugf("%d", winding);
1224 }
1225 SkDebugf(" newOppSum=");
1226 if (oppWinding == SK_MinS32) {
1227 SkDebugf("?");
1228 } else {
1229 SkDebugf("%d", oppWinding);
1230 }
1231 SkDebugf(" oppSum=");
1232 if (span->oppSum() == SK_MinS32) {
1233 SkDebugf("?");
1234 } else {
1235 SkDebugf("%d", span->oppSum());
1236 }
1237 SkDebugf(" windSum=");
1238 if (span->windSum() == SK_MinS32) {
1239 SkDebugf("?");
1240 } else {
1241 SkDebugf("%d", span->windSum());
1242 }
1243 SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue());
1244}
1245
1246#endif
1247
caryclark26ad22a2015-10-16 09:03:38 -07001248// loop looking for a pair of angle parts that are too close to be sorted
1249/* This is called after other more simple intersection and angle sorting tests have been exhausted.
1250 This should be rarely called -- the test below is thorough and time consuming.
caryclark55888e42016-07-18 10:01:36 -07001251 This checks the distance between start points; the distance between
caryclark26ad22a2015-10-16 09:03:38 -07001252*/
1253#if DEBUG_ANGLE
1254void SkOpAngle::debugCheckNearCoincidence() const {
1255 const SkOpAngle* test = this;
1256 do {
1257 const SkOpSegment* testSegment = test->segment();
1258 double testStartT = test->start()->t();
1259 SkDPoint testStartPt = testSegment->dPtAtT(testStartT);
1260 double testEndT = test->end()->t();
1261 SkDPoint testEndPt = testSegment->dPtAtT(testEndT);
1262 double testLenSq = testStartPt.distanceSquared(testEndPt);
1263 SkDebugf("%s testLenSq=%1.9g id=%d\n", __FUNCTION__, testLenSq, testSegment->debugID());
1264 double testMidT = (testStartT + testEndT) / 2;
1265 const SkOpAngle* next = test;
1266 while ((next = next->fNext) != this) {
1267 SkOpSegment* nextSegment = next->segment();
1268 double testMidDistSq = testSegment->distSq(testMidT, next);
1269 double testEndDistSq = testSegment->distSq(testEndT, next);
1270 double nextStartT = next->start()->t();
1271 SkDPoint nextStartPt = nextSegment->dPtAtT(nextStartT);
1272 double distSq = testStartPt.distanceSquared(nextStartPt);
1273 double nextEndT = next->end()->t();
1274 double nextMidT = (nextStartT + nextEndT) / 2;
1275 double nextMidDistSq = nextSegment->distSq(nextMidT, test);
1276 double nextEndDistSq = nextSegment->distSq(nextEndT, test);
1277 SkDebugf("%s distSq=%1.9g testId=%d nextId=%d\n", __FUNCTION__, distSq,
1278 testSegment->debugID(), nextSegment->debugID());
1279 SkDebugf("%s testMidDistSq=%1.9g\n", __FUNCTION__, testMidDistSq);
1280 SkDebugf("%s testEndDistSq=%1.9g\n", __FUNCTION__, testEndDistSq);
1281 SkDebugf("%s nextMidDistSq=%1.9g\n", __FUNCTION__, nextMidDistSq);
1282 SkDebugf("%s nextEndDistSq=%1.9g\n", __FUNCTION__, nextEndDistSq);
1283 SkDPoint nextEndPt = nextSegment->dPtAtT(nextEndT);
1284 double nextLenSq = nextStartPt.distanceSquared(nextEndPt);
1285 SkDebugf("%s nextLenSq=%1.9g\n", __FUNCTION__, nextLenSq);
1286 SkDebugf("\n");
1287 }
1288 test = test->fNext;
caryclark55888e42016-07-18 10:01:36 -07001289 } while (test->fNext != this);
caryclark26ad22a2015-10-16 09:03:38 -07001290}
1291#endif
1292
caryclark54359292015-03-26 07:52:43 -07001293#if DEBUG_ANGLE
1294SkString SkOpAngle::debugPart() const {
1295 SkString result;
1296 switch (this->segment()->verb()) {
1297 case SkPath::kLine_Verb:
caryclarkeed356d2016-09-14 07:18:20 -07001298 result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fPart.fCurve),
caryclark54359292015-03-26 07:52:43 -07001299 this->segment()->debugID());
1300 break;
1301 case SkPath::kQuad_Verb:
caryclarkeed356d2016-09-14 07:18:20 -07001302 result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fPart.fCurve),
caryclark54359292015-03-26 07:52:43 -07001303 this->segment()->debugID());
1304 break;
caryclark1049f122015-04-20 08:31:59 -07001305 case SkPath::kConic_Verb:
1306 result.printf(CONIC_DEBUG_STR " id=%d",
caryclarkeed356d2016-09-14 07:18:20 -07001307 CONIC_DEBUG_DATA(fPart.fCurve, fPart.fCurve.fConic.fWeight),
caryclark1049f122015-04-20 08:31:59 -07001308 this->segment()->debugID());
1309 break;
caryclark54359292015-03-26 07:52:43 -07001310 case SkPath::kCubic_Verb:
caryclarkeed356d2016-09-14 07:18:20 -07001311 result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fPart.fCurve),
caryclark54359292015-03-26 07:52:43 -07001312 this->segment()->debugID());
1313 break;
1314 default:
1315 SkASSERT(0);
mtklein1b249332015-07-07 12:21:21 -07001316 }
caryclark54359292015-03-26 07:52:43 -07001317 return result;
1318}
1319#endif
1320
caryclark624637c2015-05-11 07:21:27 -07001321#if DEBUG_SORT
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001322void SkOpAngle::debugLoop() const {
caryclarke4097e32014-06-18 07:24:19 -07001323 const SkOpAngle* first = this;
1324 const SkOpAngle* next = this;
1325 do {
1326 next->dumpOne(true);
caryclark19eb3b22014-07-18 05:08:14 -07001327 SkDebugf("\n");
caryclarke4097e32014-06-18 07:24:19 -07001328 next = next->fNext;
1329 } while (next && next != first);
caryclark54359292015-03-26 07:52:43 -07001330 next = first;
1331 do {
1332 next->debugValidate();
1333 next = next->fNext;
1334 } while (next && next != first);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001335}
1336#endif
1337
caryclark54359292015-03-26 07:52:43 -07001338void SkOpAngle::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07001339#if DEBUG_COINCIDENCE
1340 if (this->globalState()->debugCheckHealth()) {
1341 return;
1342 }
1343#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001344#if DEBUG_VALIDATE
caryclark54359292015-03-26 07:52:43 -07001345 const SkOpAngle* first = this;
1346 const SkOpAngle* next = this;
1347 int wind = 0;
1348 int opp = 0;
1349 int lastXor = -1;
1350 int lastOppXor = -1;
1351 do {
1352 if (next->unorderable()) {
1353 return;
1354 }
1355 const SkOpSpan* minSpan = next->start()->starter(next->end());
1356 if (minSpan->windValue() == SK_MinS32) {
1357 return;
1358 }
1359 bool op = next->segment()->operand();
1360 bool isXor = next->segment()->isXor();
1361 bool oppXor = next->segment()->oppXor();
1362 SkASSERT(!DEBUG_LIMIT_WIND_SUM || between(0, minSpan->windValue(), DEBUG_LIMIT_WIND_SUM));
1363 SkASSERT(!DEBUG_LIMIT_WIND_SUM
1364 || between(-DEBUG_LIMIT_WIND_SUM, minSpan->oppValue(), DEBUG_LIMIT_WIND_SUM));
1365 bool useXor = op ? oppXor : isXor;
1366 SkASSERT(lastXor == -1 || lastXor == (int) useXor);
1367 lastXor = (int) useXor;
caryclark624637c2015-05-11 07:21:27 -07001368 wind += next->debugSign() * (op ? minSpan->oppValue() : minSpan->windValue());
caryclark54359292015-03-26 07:52:43 -07001369 if (useXor) {
1370 wind &= 1;
1371 }
1372 useXor = op ? isXor : oppXor;
1373 SkASSERT(lastOppXor == -1 || lastOppXor == (int) useXor);
1374 lastOppXor = (int) useXor;
caryclark624637c2015-05-11 07:21:27 -07001375 opp += next->debugSign() * (op ? minSpan->windValue() : minSpan->oppValue());
caryclark54359292015-03-26 07:52:43 -07001376 if (useXor) {
1377 opp &= 1;
1378 }
1379 next = next->fNext;
1380 } while (next && next != first);
Cary Clark59d5a0e2017-01-23 14:38:52 +00001381 SkASSERT(wind == 0 || !SkPathOpsDebug::gRunFail);
1382 SkASSERT(opp == 0 || !SkPathOpsDebug::gRunFail);
caryclark54359292015-03-26 07:52:43 -07001383#endif
1384}
1385
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001386void SkOpAngle::debugValidateNext() const {
caryclark54359292015-03-26 07:52:43 -07001387#if !FORCE_RELEASE
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001388 const SkOpAngle* first = this;
1389 const SkOpAngle* next = first;
1390 SkTDArray<const SkOpAngle*>(angles);
1391 do {
djsollenf2b340f2016-01-29 08:51:04 -08001392// SkASSERT_RELEASE(next->fSegment->debugContains(next));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001393 angles.push(next);
1394 next = next->next();
1395 if (next == first) {
1396 break;
1397 }
djsollenf2b340f2016-01-29 08:51:04 -08001398 SkASSERT_RELEASE(!angles.contains(next));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001399 if (!next) {
1400 return;
1401 }
1402 } while (true);
reed0dc4dd62015-03-24 13:55:33 -07001403#endif
reed0dc4dd62015-03-24 13:55:33 -07001404}
reed0dc4dd62015-03-24 13:55:33 -07001405
caryclark55888e42016-07-18 10:01:36 -07001406#ifdef SK_DEBUG
1407void SkCoincidentSpans::debugStartCheck(const SkOpSpanBase* outer, const SkOpSpanBase* over,
1408 const SkOpGlobalState* debugState) const {
Cary Clark59d5a0e2017-01-23 14:38:52 +00001409 SkASSERT(coinPtTEnd()->span() == over || !SkOpGlobalState::DebugRunFail());
1410 SkASSERT(oppPtTEnd()->span() == outer || !SkOpGlobalState::DebugRunFail());
caryclark55888e42016-07-18 10:01:36 -07001411}
1412#endif
caryclark26ad22a2015-10-16 09:03:38 -07001413
Cary Clarkab87d7a2016-10-04 10:01:04 -04001414#if DEBUG_COIN
1415// sets the span's end to the ptT referenced by the previous-next
1416void SkCoincidentSpans::debugCorrectOneEnd(SkPathOpsDebug::GlitchLog* log,
1417 const SkOpPtT* (SkCoincidentSpans::* getEnd)() const,
1418 void (SkCoincidentSpans::*setEnd)(const SkOpPtT* ptT) const ) const {
1419 const SkOpPtT* origPtT = (this->*getEnd)();
1420 const SkOpSpanBase* origSpan = origPtT->span();
1421 const SkOpSpan* prev = origSpan->prev();
1422 const SkOpPtT* testPtT = prev ? prev->next()->ptT()
1423 : origSpan->upCast()->next()->prev()->ptT();
1424 if (origPtT != testPtT) {
1425 log->record(SkPathOpsDebug::kCorrectEnd_Glitch, this, origPtT, testPtT);
1426 }
1427}
1428
1429
1430/* Commented-out lines keep this in sync with correctEnds */
1431// FIXME: member pointers have fallen out of favor and can be replaced with
1432// an alternative approach.
1433// makes all span ends agree with the segment's spans that define them
1434void SkCoincidentSpans::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
1435 this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTStart, nullptr);
1436 this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTEnd, nullptr);
1437 this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTStart, nullptr);
1438 this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTEnd, nullptr);
1439}
1440
caryclark55888e42016-07-18 10:01:36 -07001441/* Commented-out lines keep this in sync with expand */
caryclark30b9fdd2016-08-31 14:36:29 -07001442// expand the range by checking adjacent spans for coincidence
Cary Clarkab87d7a2016-10-04 10:01:04 -04001443bool SkCoincidentSpans::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07001444 bool expanded = false;
1445 const SkOpSegment* segment = coinPtTStart()->segment();
1446 const SkOpSegment* oppSegment = oppPtTStart()->segment();
1447 do {
1448 const SkOpSpan* start = coinPtTStart()->span()->upCast();
1449 const SkOpSpan* prev = start->prev();
1450 const SkOpPtT* oppPtT;
1451 if (!prev || !(oppPtT = prev->contains(oppSegment))) {
1452 break;
1453 }
1454 double midT = (prev->t() + start->t()) / 2;
1455 if (!segment->isClose(midT, oppSegment)) {
1456 break;
1457 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001458 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, prev->ptT(), oppPtT);
caryclark55888e42016-07-18 10:01:36 -07001459 expanded = true;
1460 } while (false); // actual continues while expansion is possible
1461 do {
1462 const SkOpSpanBase* end = coinPtTEnd()->span();
1463 SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
caryclark30b9fdd2016-08-31 14:36:29 -07001464 if (next && next->deleted()) {
1465 break;
1466 }
caryclark55888e42016-07-18 10:01:36 -07001467 const SkOpPtT* oppPtT;
1468 if (!next || !(oppPtT = next->contains(oppSegment))) {
1469 break;
1470 }
1471 double midT = (end->t() + next->t()) / 2;
1472 if (!segment->isClose(midT, oppSegment)) {
1473 break;
1474 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001475 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, next->ptT(), oppPtT);
caryclark55888e42016-07-18 10:01:36 -07001476 expanded = true;
1477 } while (false); // actual continues while expansion is possible
1478 return expanded;
1479}
1480
Cary Clarkab87d7a2016-10-04 10:01:04 -04001481// description below
1482void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* base, const SkOpSpanBase* testSpan) const {
1483 const SkOpPtT* testPtT = testSpan->ptT();
1484 const SkOpPtT* stopPtT = testPtT;
1485 const SkOpSegment* baseSeg = base->segment();
1486 while ((testPtT = testPtT->next()) != stopPtT) {
1487 const SkOpSegment* testSeg = testPtT->segment();
1488 if (testPtT->deleted()) {
1489 continue;
1490 }
1491 if (testSeg == baseSeg) {
1492 continue;
1493 }
1494 if (testPtT->span()->ptT() != testPtT) {
1495 continue;
1496 }
1497 if (this->contains(baseSeg, testSeg, testPtT->fT)) {
1498 continue;
1499 }
1500 // intersect perp with base->ptT() with testPtT->segment()
1501 SkDVector dxdy = baseSeg->dSlopeAtT(base->t());
1502 const SkPoint& pt = base->pt();
1503 SkDLine ray = {{{pt.fX, pt.fY}, {pt.fX + dxdy.fY, pt.fY - dxdy.fX}}};
1504 SkIntersections i;
1505 (*CurveIntersectRay[testSeg->verb()])(testSeg->pts(), testSeg->weight(), ray, &i);
1506 for (int index = 0; index < i.used(); ++index) {
1507 double t = i[0][index];
1508 if (!between(0, t, 1)) {
1509 continue;
1510 }
1511 SkDPoint oppPt = i.pt(index);
1512 if (!oppPt.approximatelyEqual(pt)) {
1513 continue;
1514 }
1515 SkOpSegment* writableSeg = const_cast<SkOpSegment*>(testSeg);
1516 SkOpPtT* oppStart = writableSeg->addT(t);
1517 if (oppStart == testPtT) {
1518 continue;
1519 }
1520 SkOpSpan* writableBase = const_cast<SkOpSpan*>(base);
1521 oppStart->span()->addOpp(writableBase);
1522 if (oppStart->deleted()) {
1523 continue;
1524 }
1525 SkOpSegment* coinSeg = base->segment();
1526 SkOpSegment* oppSeg = oppStart->segment();
1527 double coinTs, coinTe, oppTs, oppTe;
1528 if (Ordered(coinSeg, oppSeg)) {
1529 coinTs = base->t();
1530 coinTe = testSpan->t();
1531 oppTs = oppStart->fT;
1532 oppTe = testPtT->fT;
1533 } else {
1534 SkTSwap(coinSeg, oppSeg);
1535 coinTs = oppStart->fT;
1536 coinTe = testPtT->fT;
1537 oppTs = base->t();
1538 oppTe = testSpan->t();
1539 }
1540 if (coinTs > coinTe) {
1541 SkTSwap(coinTs, coinTe);
1542 SkTSwap(oppTs, oppTe);
1543 }
1544 bool added;
1545 if (this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &added), false) {
1546 return;
1547 }
1548 }
1549 }
1550 return;
1551}
1552
1553// description below
1554void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* ptT) const {
1555 FAIL_IF(!ptT->span()->upCastable(), ptT->span());
1556 const SkOpSpan* base = ptT->span()->upCast();
1557 const SkOpSpan* prev = base->prev();
1558 FAIL_IF(!prev, ptT->span());
1559 if (!prev->isCanceled()) {
1560 if (this->debugAddEndMovedSpans(log, base, base->prev()), false) {
1561 return;
1562 }
1563 }
1564 if (!base->isCanceled()) {
1565 if (this->debugAddEndMovedSpans(log, base, base->next()), false) {
1566 return;
1567 }
1568 }
1569 return;
1570}
1571
1572/* If A is coincident with B and B includes an endpoint, and A's matching point
1573 is not the endpoint (i.e., there's an implied line connecting B-end and A)
1574 then assume that the same implied line may intersect another curve close to B.
1575 Since we only care about coincidence that was undetected, look at the
1576 ptT list on B-segment adjacent to the B-end/A ptT loop (not in the loop, but
1577 next door) and see if the A matching point is close enough to form another
1578 coincident pair. If so, check for a new coincident span between B-end/A ptT loop
1579 and the adjacent ptT loop.
1580*/
1581void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log) const {
1582 const SkCoincidentSpans* span = fHead;
1583 if (!span) {
1584 return;
1585 }
1586// fTop = span;
1587// fHead = nullptr;
1588 do {
1589 if (span->coinPtTStart()->fPt != span->oppPtTStart()->fPt) {
1590 FAIL_IF(1 == span->coinPtTStart()->fT, span);
1591 bool onEnd = span->coinPtTStart()->fT == 0;
1592 bool oOnEnd = zero_or_one(span->oppPtTStart()->fT);
1593 if (onEnd) {
1594 if (!oOnEnd) { // if both are on end, any nearby intersect was already found
1595 if (this->debugAddEndMovedSpans(log, span->oppPtTStart()), false) {
1596 return;
1597 }
1598 }
1599 } else if (oOnEnd) {
1600 if (this->debugAddEndMovedSpans(log, span->coinPtTStart()), false) {
1601 return;
1602 }
1603 }
1604 }
1605 if (span->coinPtTEnd()->fPt != span->oppPtTEnd()->fPt) {
1606 bool onEnd = span->coinPtTEnd()->fT == 1;
1607 bool oOnEnd = zero_or_one(span->oppPtTEnd()->fT);
1608 if (onEnd) {
1609 if (!oOnEnd) {
1610 if (this->debugAddEndMovedSpans(log, span->oppPtTEnd()), false) {
1611 return;
1612 }
1613 }
1614 } else if (oOnEnd) {
1615 if (this->debugAddEndMovedSpans(log, span->coinPtTEnd()), false) {
1616 return;
1617 }
1618 }
1619 }
1620 } while ((span = span->next()));
1621// this->restoreHead();
1622 return;
1623}
1624
caryclark55888e42016-07-18 10:01:36 -07001625/* Commented-out lines keep this in sync with addExpanded */
1626// for each coincident pair, match the spans
1627// 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 -04001628void SkOpCoincidence::debugAddExpanded(SkPathOpsDebug::GlitchLog* log) const {
caryclarka35ab3e2016-10-20 08:32:18 -07001629// DEBUG_SET_PHASE();
caryclark26ad22a2015-10-16 09:03:38 -07001630 const SkCoincidentSpans* coin = this->fHead;
1631 if (!coin) {
caryclarked0935a2015-10-22 07:23:52 -07001632 return;
1633 }
caryclark26ad22a2015-10-16 09:03:38 -07001634 do {
caryclark55888e42016-07-18 10:01:36 -07001635 const SkOpPtT* startPtT = coin->coinPtTStart();
1636 const SkOpPtT* oStartPtT = coin->oppPtTStart();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001637 double priorT = startPtT->fT;
1638 double oPriorT = oStartPtT->fT;
Cary Clarkff114282016-12-14 11:56:16 -05001639 FAIL_IF(!startPtT->contains(oStartPtT), coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001640 SkOPASSERT(coin->coinPtTEnd()->contains(coin->oppPtTEnd()));
caryclark26ad22a2015-10-16 09:03:38 -07001641 const SkOpSpanBase* start = startPtT->span();
1642 const SkOpSpanBase* oStart = oStartPtT->span();
caryclark55888e42016-07-18 10:01:36 -07001643 const SkOpSpanBase* end = coin->coinPtTEnd()->span();
1644 const SkOpSpanBase* oEnd = coin->oppPtTEnd()->span();
caryclark30b9fdd2016-08-31 14:36:29 -07001645 FAIL_IF(oEnd->deleted(), coin);
1646 FAIL_IF(!start->upCastable(), coin);
caryclark26ad22a2015-10-16 09:03:38 -07001647 const SkOpSpanBase* test = start->upCast()->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001648 FAIL_IF(!coin->flipped() && !oStart->upCastable(), coin);
caryclark55888e42016-07-18 10:01:36 -07001649 const SkOpSpanBase* oTest = coin->flipped() ? oStart->prev() : oStart->upCast()->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001650 FAIL_IF(!oTest, coin);
1651 const SkOpSegment* seg = start->segment();
1652 const SkOpSegment* oSeg = oStart->segment();
caryclark26ad22a2015-10-16 09:03:38 -07001653 while (test != end || oTest != oEnd) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001654 const SkOpPtT* containedOpp = test->ptT()->contains(oSeg);
1655 const SkOpPtT* containedThis = oTest->ptT()->contains(seg);
1656 if (!containedOpp || !containedThis) {
1657 // choose the ends, or the first common pt-t list shared by both
1658 double nextT, oNextT;
1659 if (containedOpp) {
1660 nextT = test->t();
1661 oNextT = containedOpp->fT;
1662 } else if (containedThis) {
1663 nextT = containedThis->fT;
1664 oNextT = oTest->t();
1665 } else {
1666 // iterate through until a pt-t list found that contains the other
1667 const SkOpSpanBase* walk = test;
1668 const SkOpPtT* walkOpp;
1669 do {
1670 FAIL_IF(!walk->upCastable(), coin);
1671 walk = walk->upCast()->next();
1672 } while (!(walkOpp = walk->ptT()->contains(oSeg))
1673 && walk != coin->coinPtTEnd()->span());
caryclarka35ab3e2016-10-20 08:32:18 -07001674 FAIL_IF(!walkOpp, coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001675 nextT = walk->t();
1676 oNextT = walkOpp->fT;
1677 }
caryclark26ad22a2015-10-16 09:03:38 -07001678 // use t ranges to guess which one is missing
caryclarka35ab3e2016-10-20 08:32:18 -07001679 double startRange = nextT - priorT;
caryclark30b9fdd2016-08-31 14:36:29 -07001680 FAIL_IF(!startRange, coin);
caryclarka35ab3e2016-10-20 08:32:18 -07001681 double startPart = (test->t() - priorT) / startRange;
1682 double oStartRange = oNextT - oPriorT;
caryclark30b9fdd2016-08-31 14:36:29 -07001683 FAIL_IF(!oStartRange, coin);
caryclark26ad22a2015-10-16 09:03:38 -07001684 double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange;
caryclark30b9fdd2016-08-31 14:36:29 -07001685 FAIL_IF(startPart == oStartPart, coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001686 bool addToOpp = !containedOpp && !containedThis ? startPart < oStartPart
1687 : !!containedThis;
caryclark55888e42016-07-18 10:01:36 -07001688 bool startOver = false;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001689 addToOpp ? log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch,
1690 oPriorT + oStartRange * startPart, test)
1691 : log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch,
1692 priorT + startRange * oStartPart, oTest);
1693 // FAIL_IF(!success, coin);
caryclark55888e42016-07-18 10:01:36 -07001694 if (startOver) {
1695 test = start;
1696 oTest = oStart;
caryclark26ad22a2015-10-16 09:03:38 -07001697 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001698 end = coin->coinPtTEnd()->span();
1699 oEnd = coin->oppPtTEnd()->span();
caryclark26ad22a2015-10-16 09:03:38 -07001700 }
caryclark55888e42016-07-18 10:01:36 -07001701 if (test != end) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001702 FAIL_IF(!test->upCastable(), coin);
1703 priorT = test->t();
caryclark26ad22a2015-10-16 09:03:38 -07001704 test = test->upCast()->next();
1705 }
caryclark55888e42016-07-18 10:01:36 -07001706 if (oTest != oEnd) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001707 oPriorT = oTest->t();
caryclark55888e42016-07-18 10:01:36 -07001708 oTest = coin->flipped() ? oTest->prev() : oTest->upCast()->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001709 FAIL_IF(!oTest, coin);
caryclark26ad22a2015-10-16 09:03:38 -07001710 }
1711 }
caryclark55888e42016-07-18 10:01:36 -07001712 } while ((coin = coin->next()));
1713 return;
caryclark26ad22a2015-10-16 09:03:38 -07001714}
1715
caryclark55888e42016-07-18 10:01:36 -07001716/* Commented-out lines keep this in sync addIfMissing() */
caryclark8016b262016-09-06 05:59:47 -07001717// note that over1s, over1e, over2s, over2e are ordered
Cary Clarkab87d7a2016-10-04 10:01:04 -04001718void SkOpCoincidence::debugAddIfMissing(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* over1s, const SkOpPtT* over2s,
caryclark81a478c2016-09-09 09:37:57 -07001719 double tStart, double tEnd, const SkOpSegment* coinSeg, const SkOpSegment* oppSeg, bool* added,
caryclark8016b262016-09-06 05:59:47 -07001720 const SkOpPtT* over1e, const SkOpPtT* over2e) const {
1721 SkASSERT(tStart < tEnd);
1722 SkASSERT(over1s->fT < over1e->fT);
1723 SkASSERT(between(over1s->fT, tStart, over1e->fT));
1724 SkASSERT(between(over1s->fT, tEnd, over1e->fT));
1725 SkASSERT(over2s->fT < over2e->fT);
1726 SkASSERT(between(over2s->fT, tStart, over2e->fT));
1727 SkASSERT(between(over2s->fT, tEnd, over2e->fT));
1728 SkASSERT(over1s->segment() == over1e->segment());
1729 SkASSERT(over2s->segment() == over2e->segment());
1730 SkASSERT(over1s->segment() == over2s->segment());
1731 SkASSERT(over1s->segment() != coinSeg);
1732 SkASSERT(over1s->segment() != oppSeg);
1733 SkASSERT(coinSeg != oppSeg);
caryclark26ad22a2015-10-16 09:03:38 -07001734 double coinTs, coinTe, oppTs, oppTe;
caryclark8016b262016-09-06 05:59:47 -07001735 coinTs = TRange(over1s, tStart, coinSeg SkDEBUGPARAMS(over1e));
1736 coinTe = TRange(over1s, tEnd, coinSeg SkDEBUGPARAMS(over1e));
1737 if (coinSeg->collapsed(coinTs, coinTe)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001738 return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, coinSeg);
caryclark8016b262016-09-06 05:59:47 -07001739 }
1740 oppTs = TRange(over2s, tStart, oppSeg SkDEBUGPARAMS(over2e));
1741 oppTe = TRange(over2s, tEnd, oppSeg SkDEBUGPARAMS(over2e));
1742 if (oppSeg->collapsed(oppTs, oppTe)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001743 return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, oppSeg);
caryclark8016b262016-09-06 05:59:47 -07001744 }
1745 if (coinTs > coinTe) {
caryclark55888e42016-07-18 10:01:36 -07001746 SkTSwap(coinTs, coinTe);
caryclark26ad22a2015-10-16 09:03:38 -07001747 SkTSwap(oppTs, oppTe);
1748 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001749 return this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, added
caryclark8016b262016-09-06 05:59:47 -07001750 );
caryclark26ad22a2015-10-16 09:03:38 -07001751}
1752
caryclark55888e42016-07-18 10:01:36 -07001753/* Commented-out lines keep this in sync addOrOverlap() */
caryclark30b9fdd2016-08-31 14:36:29 -07001754// If this is called by addEndMovedSpans(), a returned false propogates out to an abort.
1755// If this is called by AddIfMissing(), a returned false indicates there was nothing to add
Cary Clarkab87d7a2016-10-04 10:01:04 -04001756void SkOpCoincidence::debugAddOrOverlap(SkPathOpsDebug::GlitchLog* log,
caryclark30b9fdd2016-08-31 14:36:29 -07001757 const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
caryclark81a478c2016-09-09 09:37:57 -07001758 double coinTs, double coinTe, double oppTs, double oppTe, bool* added) const {
caryclark55888e42016-07-18 10:01:36 -07001759 SkTDArray<SkCoincidentSpans*> overlaps;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001760 SkOPASSERT(!fTop); // this is (correctly) reversed in addifMissing()
caryclark81a478c2016-09-09 09:37:57 -07001761 if (fTop && !this->checkOverlap(fTop, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe,
1762 &overlaps)) {
caryclark55888e42016-07-18 10:01:36 -07001763 return;
1764 }
1765 if (fHead && !this->checkOverlap(fHead, coinSeg, oppSeg, coinTs,
1766 coinTe, oppTs, oppTe, &overlaps)) {
1767 return;
1768 }
1769 const SkCoincidentSpans* overlap = overlaps.count() ? overlaps[0] : nullptr;
1770 for (int index = 1; index < overlaps.count(); ++index) { // combine overlaps before continuing
1771 const SkCoincidentSpans* test = overlaps[index];
1772 if (overlap->coinPtTStart()->fT > test->coinPtTStart()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001773 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTStart());
caryclark55888e42016-07-18 10:01:36 -07001774 }
1775 if (overlap->coinPtTEnd()->fT < test->coinPtTEnd()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001776 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07001777 }
1778 if (overlap->flipped()
1779 ? overlap->oppPtTStart()->fT < test->oppPtTStart()->fT
1780 : overlap->oppPtTStart()->fT > test->oppPtTStart()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001781 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTStart());
caryclark55888e42016-07-18 10:01:36 -07001782 }
1783 if (overlap->flipped()
1784 ? overlap->oppPtTEnd()->fT > test->oppPtTEnd()->fT
1785 : overlap->oppPtTEnd()->fT < test->oppPtTEnd()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001786 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07001787 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001788 if (!fHead) { this->debugRelease(log, fHead, test);
1789 this->debugRelease(log, fTop, test);
caryclark55888e42016-07-18 10:01:36 -07001790 }
1791 }
1792 const SkOpPtT* cs = coinSeg->existing(coinTs, oppSeg);
1793 const SkOpPtT* ce = coinSeg->existing(coinTe, oppSeg);
caryclark30b9fdd2016-08-31 14:36:29 -07001794 RETURN_FALSE_IF(overlap && cs && ce && overlap->contains(cs, ce), coinSeg);
1795 RETURN_FALSE_IF(cs != ce || !cs, coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001796 const SkOpPtT* os = oppSeg->existing(oppTs, coinSeg);
1797 const SkOpPtT* oe = oppSeg->existing(oppTe, coinSeg);
caryclark30b9fdd2016-08-31 14:36:29 -07001798 RETURN_FALSE_IF(overlap && os && oe && overlap->contains(os, oe), oppSeg);
caryclark55888e42016-07-18 10:01:36 -07001799 SkASSERT(true || !cs || !cs->deleted());
1800 SkASSERT(true || !os || !os->deleted());
1801 SkASSERT(true || !ce || !ce->deleted());
1802 SkASSERT(true || !oe || !oe->deleted());
1803 const SkOpPtT* csExisting = !cs ? coinSeg->existing(coinTs, nullptr) : nullptr;
1804 const SkOpPtT* ceExisting = !ce ? coinSeg->existing(coinTe, nullptr) : nullptr;
caryclark30b9fdd2016-08-31 14:36:29 -07001805 RETURN_FALSE_IF(csExisting && csExisting == ceExisting, coinSeg);
1806 RETURN_FALSE_IF(csExisting && (csExisting == ce ||
1807 csExisting->contains(ceExisting ? ceExisting : ce)), coinSeg);
1808 RETURN_FALSE_IF(ceExisting && (ceExisting == cs ||
1809 ceExisting->contains(csExisting ? csExisting : cs)), coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001810 const SkOpPtT* osExisting = !os ? oppSeg->existing(oppTs, nullptr) : nullptr;
1811 const SkOpPtT* oeExisting = !oe ? oppSeg->existing(oppTe, nullptr) : nullptr;
caryclark30b9fdd2016-08-31 14:36:29 -07001812 RETURN_FALSE_IF(osExisting && osExisting == oeExisting, oppSeg);
1813 RETURN_FALSE_IF(osExisting && (osExisting == oe ||
1814 osExisting->contains(oeExisting ? oeExisting : oe)), oppSeg);
1815 RETURN_FALSE_IF(oeExisting && (oeExisting == os ||
1816 oeExisting->contains(osExisting ? osExisting : os)), oppSeg);
caryclark55888e42016-07-18 10:01:36 -07001817 bool csDeleted = false, osDeleted = false, ceDeleted = false, oeDeleted = false;
1818 this->debugValidate();
1819 if (!cs || !os) {
1820 if (!cs)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001821 cs = coinSeg->debugAddT(coinTs, log);
caryclark55888e42016-07-18 10:01:36 -07001822 if (!os)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001823 os = oppSeg->debugAddT(oppTs, log);
caryclark30b9fdd2016-08-31 14:36:29 -07001824// RETURN_FALSE_IF(callerAborts, !csWritable || !osWritable);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001825 if (cs && os) cs->span()->debugAddOpp(log, os->span());
caryclark55888e42016-07-18 10:01:36 -07001826// cs = csWritable;
caryclark30b9fdd2016-08-31 14:36:29 -07001827// os = osWritable->active();
1828 RETURN_FALSE_IF((ce && ce->deleted()) || (oe && oe->deleted()), coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001829 }
1830 if (!ce || !oe) {
1831 if (!ce)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001832 ce = coinSeg->debugAddT(coinTe, log);
caryclark55888e42016-07-18 10:01:36 -07001833 if (!oe)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001834 oe = oppSeg->debugAddT(oppTe, log);
1835 if (ce && oe) ce->span()->debugAddOpp(log, oe->span());
caryclark55888e42016-07-18 10:01:36 -07001836// ce = ceWritable;
1837// oe = oeWritable;
1838 }
1839 this->debugValidate();
caryclark30b9fdd2016-08-31 14:36:29 -07001840 RETURN_FALSE_IF(csDeleted, coinSeg);
Ben Wagner63fd7602017-10-09 15:45:33 -04001841 RETURN_FALSE_IF(osDeleted, oppSeg);
1842 RETURN_FALSE_IF(ceDeleted, coinSeg);
caryclark30b9fdd2016-08-31 14:36:29 -07001843 RETURN_FALSE_IF(oeDeleted, oppSeg);
caryclark8016b262016-09-06 05:59:47 -07001844 RETURN_FALSE_IF(!cs || !ce || cs == ce || cs->contains(ce) || !os || !oe || os == oe || os->contains(oe), coinSeg);
1845 bool result = true;
caryclark55888e42016-07-18 10:01:36 -07001846 if (overlap) {
1847 if (overlap->coinPtTStart()->segment() == coinSeg) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001848 log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
caryclark55888e42016-07-18 10:01:36 -07001849 } else {
1850 if (oppTs > oppTe) {
1851 SkTSwap(coinTs, coinTe);
1852 SkTSwap(oppTs, oppTe);
1853 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001854 log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, oppSeg, oppTs, oppTe, coinSeg, coinTs, coinTe);
caryclark55888e42016-07-18 10:01:36 -07001855 }
caryclark8016b262016-09-06 05:59:47 -07001856#if 0 && DEBUG_COINCIDENCE_VERBOSE
1857 if (result) {
1858 overlap->debugShow();
1859 }
caryclark55888e42016-07-18 10:01:36 -07001860#endif
1861 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001862 log->record(SkPathOpsDebug::kAddMissingCoin_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
caryclark8016b262016-09-06 05:59:47 -07001863#if 0 && DEBUG_COINCIDENCE_VERBOSE
1864 fHead->debugShow();
caryclark55888e42016-07-18 10:01:36 -07001865#endif
1866 }
1867 this->debugValidate();
caryclark8016b262016-09-06 05:59:47 -07001868 return (void) result;
caryclark55888e42016-07-18 10:01:36 -07001869}
1870
1871// Extra commented-out lines keep this in sync with addMissing()
1872/* detects overlaps of different coincident runs on same segment */
1873/* does not detect overlaps for pairs without any segments in common */
1874// returns true if caller should loop again
Cary Clarkab87d7a2016-10-04 10:01:04 -04001875void SkOpCoincidence::debugAddMissing(SkPathOpsDebug::GlitchLog* log, bool* added) const {
caryclark26ad22a2015-10-16 09:03:38 -07001876 const SkCoincidentSpans* outer = fHead;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001877 *added = false;
caryclark26ad22a2015-10-16 09:03:38 -07001878 if (!outer) {
1879 return;
1880 }
caryclark55888e42016-07-18 10:01:36 -07001881 // fTop = outer;
1882 // fHead = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -07001883 do {
1884 // addifmissing can modify the list that this is walking
1885 // save head so that walker can iterate over old data unperturbed
1886 // addifmissing adds to head freely then add saved head in the end
caryclark8016b262016-09-06 05:59:47 -07001887 const SkOpPtT* ocs = outer->coinPtTStart();
1888 SkASSERT(!ocs->deleted());
1889 const SkOpSegment* outerCoin = ocs->segment();
1890 SkASSERT(!outerCoin->done()); // if it's done, should have already been removed from list
1891 const SkOpPtT* oos = outer->oppPtTStart();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001892 if (oos->deleted()) {
1893 return;
1894 }
caryclark8016b262016-09-06 05:59:47 -07001895 const SkOpSegment* outerOpp = oos->segment();
1896 SkASSERT(!outerOpp->done());
1897// SkOpSegment* outerCoinWritable = const_cast<SkOpSegment*>(outerCoin);
1898// SkOpSegment* outerOppWritable = const_cast<SkOpSegment*>(outerOpp);
caryclark26ad22a2015-10-16 09:03:38 -07001899 const SkCoincidentSpans* inner = outer;
caryclark55888e42016-07-18 10:01:36 -07001900 while ((inner = inner->next())) {
1901 this->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001902 double overS, overE;
caryclark8016b262016-09-06 05:59:47 -07001903 const SkOpPtT* ics = inner->coinPtTStart();
1904 SkASSERT(!ics->deleted());
1905 const SkOpSegment* innerCoin = ics->segment();
1906 SkASSERT(!innerCoin->done());
1907 const SkOpPtT* ios = inner->oppPtTStart();
1908 SkASSERT(!ios->deleted());
1909 const SkOpSegment* innerOpp = ios->segment();
1910 SkASSERT(!innerOpp->done());
1911// SkOpSegment* innerCoinWritable = const_cast<SkOpSegment*>(innerCoin);
1912// SkOpSegment* innerOppWritable = const_cast<SkOpSegment*>(innerOpp);
caryclark55888e42016-07-18 10:01:36 -07001913 if (outerCoin == innerCoin) {
caryclark8016b262016-09-06 05:59:47 -07001914 const SkOpPtT* oce = outer->coinPtTEnd();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001915 if (oce->deleted()) {
1916 return;
1917 }
caryclark8016b262016-09-06 05:59:47 -07001918 const SkOpPtT* ice = inner->coinPtTEnd();
1919 SkASSERT(!ice->deleted());
1920 if (outerOpp != innerOpp && this->overlap(ocs, oce, ics, ice, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001921 this->debugAddIfMissing(log, ocs->starter(oce), ics->starter(ice),
caryclark81a478c2016-09-09 09:37:57 -07001922 overS, overE, outerOpp, innerOpp, added,
caryclark8016b262016-09-06 05:59:47 -07001923 ocs->debugEnder(oce),
1924 ics->debugEnder(ice));
caryclark26ad22a2015-10-16 09:03:38 -07001925 }
caryclark55888e42016-07-18 10:01:36 -07001926 } else if (outerCoin == innerOpp) {
caryclark8016b262016-09-06 05:59:47 -07001927 const SkOpPtT* oce = outer->coinPtTEnd();
1928 SkASSERT(!oce->deleted());
1929 const SkOpPtT* ioe = inner->oppPtTEnd();
1930 SkASSERT(!ioe->deleted());
1931 if (outerOpp != innerCoin && this->overlap(ocs, oce, ios, ioe, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001932 this->debugAddIfMissing(log, ocs->starter(oce), ios->starter(ioe),
caryclark81a478c2016-09-09 09:37:57 -07001933 overS, overE, outerOpp, innerCoin, added,
caryclark8016b262016-09-06 05:59:47 -07001934 ocs->debugEnder(oce),
1935 ios->debugEnder(ioe));
caryclark26ad22a2015-10-16 09:03:38 -07001936 }
caryclark55888e42016-07-18 10:01:36 -07001937 } else if (outerOpp == innerCoin) {
caryclark8016b262016-09-06 05:59:47 -07001938 const SkOpPtT* ooe = outer->oppPtTEnd();
1939 SkASSERT(!ooe->deleted());
1940 const SkOpPtT* ice = inner->coinPtTEnd();
1941 SkASSERT(!ice->deleted());
caryclark55888e42016-07-18 10:01:36 -07001942 SkASSERT(outerCoin != innerOpp);
caryclark8016b262016-09-06 05:59:47 -07001943 if (this->overlap(oos, ooe, ics, ice, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001944 this->debugAddIfMissing(log, oos->starter(ooe), ics->starter(ice),
caryclark81a478c2016-09-09 09:37:57 -07001945 overS, overE, outerCoin, innerOpp, added,
caryclark8016b262016-09-06 05:59:47 -07001946 oos->debugEnder(ooe),
1947 ics->debugEnder(ice));
caryclark26ad22a2015-10-16 09:03:38 -07001948 }
caryclark55888e42016-07-18 10:01:36 -07001949 } else if (outerOpp == innerOpp) {
caryclark8016b262016-09-06 05:59:47 -07001950 const SkOpPtT* ooe = outer->oppPtTEnd();
1951 SkASSERT(!ooe->deleted());
1952 const SkOpPtT* ioe = inner->oppPtTEnd();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001953 if (ioe->deleted()) {
1954 return;
1955 }
caryclark55888e42016-07-18 10:01:36 -07001956 SkASSERT(outerCoin != innerCoin);
caryclark8016b262016-09-06 05:59:47 -07001957 if (this->overlap(oos, ooe, ios, ioe, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001958 this->debugAddIfMissing(log, oos->starter(ooe), ios->starter(ioe),
caryclark81a478c2016-09-09 09:37:57 -07001959 overS, overE, outerCoin, innerCoin, added,
caryclark8016b262016-09-06 05:59:47 -07001960 oos->debugEnder(ooe),
1961 ios->debugEnder(ioe));
caryclark26ad22a2015-10-16 09:03:38 -07001962 }
1963 }
caryclark55888e42016-07-18 10:01:36 -07001964 this->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001965 }
caryclark55888e42016-07-18 10:01:36 -07001966 } while ((outer = outer->next()));
1967 // this->restoreHead();
1968 return;
caryclark26ad22a2015-10-16 09:03:38 -07001969}
1970
caryclark55888e42016-07-18 10:01:36 -07001971// Commented-out lines keep this in sync with release()
Cary Clarkab87d7a2016-10-04 10:01:04 -04001972void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkCoincidentSpans* remove) const {
caryclark30b9fdd2016-08-31 14:36:29 -07001973 const SkCoincidentSpans* head = coin;
1974 const SkCoincidentSpans* prev = nullptr;
1975 const SkCoincidentSpans* next;
1976 do {
1977 next = coin->next();
1978 if (coin == remove) {
1979 if (prev) {
1980// prev->setNext(next);
1981 } else if (head == fHead) {
1982// fHead = next;
1983 } else {
1984// fTop = next;
1985 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001986 log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
caryclark30b9fdd2016-08-31 14:36:29 -07001987 }
1988 prev = coin;
1989 } while ((coin = next));
1990 return;
1991}
1992
Cary Clarkab87d7a2016-10-04 10:01:04 -04001993void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* deleted) const {
caryclark55888e42016-07-18 10:01:36 -07001994 const SkCoincidentSpans* coin = fHead;
1995 if (!coin) {
1996 return;
1997 }
1998 do {
1999 if (coin->coinPtTStart()->segment() == deleted
2000 || coin->coinPtTEnd()->segment() == deleted
2001 || coin->oppPtTStart()->segment() == deleted
2002 || coin->oppPtTEnd()->segment() == deleted) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002003 log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07002004 }
2005 } while ((coin = coin->next()));
2006}
2007
caryclark55888e42016-07-18 10:01:36 -07002008// Commented-out lines keep this in sync with expand()
2009// expand the range by checking adjacent spans for coincidence
Cary Clarkab87d7a2016-10-04 10:01:04 -04002010bool SkOpCoincidence::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07002011 const SkCoincidentSpans* coin = fHead;
2012 if (!coin) {
2013 return false;
2014 }
2015 bool expanded = false;
2016 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002017 if (coin->debugExpand(log)) {
caryclark55888e42016-07-18 10:01:36 -07002018 // check to see if multiple spans expanded so they are now identical
2019 const SkCoincidentSpans* test = fHead;
2020 do {
2021 if (coin == test) {
2022 continue;
2023 }
2024 if (coin->coinPtTStart() == test->coinPtTStart()
2025 && coin->oppPtTStart() == test->oppPtTStart()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002026 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, fHead, test->coinPtTStart());
caryclark55888e42016-07-18 10:01:36 -07002027 break;
2028 }
2029 } while ((test = test->next()));
2030 expanded = true;
caryclark26ad22a2015-10-16 09:03:38 -07002031 }
caryclark55888e42016-07-18 10:01:36 -07002032 } while ((coin = coin->next()));
caryclark26ad22a2015-10-16 09:03:38 -07002033 return expanded;
2034}
2035
caryclark55888e42016-07-18 10:01:36 -07002036// Commented-out lines keep this in sync with mark()
2037/* this sets up the coincidence links in the segments when the coincidence crosses multiple spans */
Cary Clarkab87d7a2016-10-04 10:01:04 -04002038void SkOpCoincidence::debugMark(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07002039 const SkCoincidentSpans* coin = fHead;
2040 if (!coin) {
2041 return;
2042 }
2043 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002044 FAIL_IF(!coin->coinPtTStartWritable()->span()->upCastable(), coin);
caryclark55888e42016-07-18 10:01:36 -07002045 const SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast();
2046// SkASSERT(start->deleted());
2047 const SkOpSpanBase* end = coin->coinPtTEndWritable()->span();
2048// SkASSERT(end->deleted());
2049 const SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span();
2050// SkASSERT(oStart->deleted());
2051 const SkOpSpanBase* oEnd = coin->oppPtTEndWritable()->span();
2052// SkASSERT(oEnd->deleted());
2053 bool flipped = coin->flipped();
caryclark26ad22a2015-10-16 09:03:38 -07002054 if (flipped) {
2055 SkTSwap(oStart, oEnd);
2056 }
caryclark55888e42016-07-18 10:01:36 -07002057 /* coin and opp spans may not match up. Mark the ends, and then let the interior
2058 get marked as many times as the spans allow */
Cary Clarkab87d7a2016-10-04 10:01:04 -04002059 start->debugInsertCoincidence(log, oStart->upCast());
2060 end->debugInsertCoinEnd(log, oEnd);
caryclark55888e42016-07-18 10:01:36 -07002061 const SkOpSegment* segment = start->segment();
2062 const SkOpSegment* oSegment = oStart->segment();
caryclark26ad22a2015-10-16 09:03:38 -07002063 const SkOpSpanBase* next = start;
2064 const SkOpSpanBase* oNext = oStart;
caryclarka35ab3e2016-10-20 08:32:18 -07002065 bool ordered;
2066 FAIL_IF(!coin->ordered(&ordered), coin);
caryclark55888e42016-07-18 10:01:36 -07002067 while ((next = next->upCast()->next()) != end) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002068 FAIL_IF(!next->upCastable(), coin);
2069 if (next->upCast()->debugInsertCoincidence(log, oSegment, flipped, ordered), false) {
caryclark55888e42016-07-18 10:01:36 -07002070 return;
caryclark26ad22a2015-10-16 09:03:38 -07002071 }
caryclark55888e42016-07-18 10:01:36 -07002072 }
2073 while ((oNext = oNext->upCast()->next()) != oEnd) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002074 FAIL_IF(!oNext->upCastable(), coin);
2075 if (oNext->upCast()->debugInsertCoincidence(log, segment, flipped, ordered), false) {
caryclark55888e42016-07-18 10:01:36 -07002076 return;
caryclark26ad22a2015-10-16 09:03:38 -07002077 }
caryclark55888e42016-07-18 10:01:36 -07002078 }
2079 } while ((coin = coin->next()));
2080 return;
caryclark26ad22a2015-10-16 09:03:38 -07002081}
2082#endif
2083
Cary Clarkab87d7a2016-10-04 10:01:04 -04002084#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002085// Commented-out lines keep this in sync with markCollapsed()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002086void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkOpPtT* test) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002087 const SkCoincidentSpans* head = coin;
caryclark55888e42016-07-18 10:01:36 -07002088 while (coin) {
2089 if (coin->collapsed(test)) {
2090 if (zero_or_one(coin->coinPtTStart()->fT) && zero_or_one(coin->coinPtTEnd()->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002091 log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07002092 }
2093 if (zero_or_one(coin->oppPtTStart()->fT) && zero_or_one(coin->oppPtTEnd()->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002094 log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07002095 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002096 this->debugRelease(log, head, coin);
caryclark55888e42016-07-18 10:01:36 -07002097 }
2098 coin = coin->next();
caryclark624637c2015-05-11 07:21:27 -07002099 }
2100}
2101
caryclark55888e42016-07-18 10:01:36 -07002102// Commented-out lines keep this in sync with markCollapsed()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002103void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* test) const {
2104 this->debugMarkCollapsed(log, fHead, test);
2105 this->debugMarkCollapsed(log, fTop, test);
caryclark55888e42016-07-18 10:01:36 -07002106}
2107#endif
2108
2109void SkCoincidentSpans::debugShow() const {
caryclark6c3b9cd2016-09-26 05:36:58 -07002110 SkDebugf("coinSpan - id=%d t=%1.9g tEnd=%1.9g\n", coinPtTStart()->segment()->debugID(),
caryclark55888e42016-07-18 10:01:36 -07002111 coinPtTStart()->fT, coinPtTEnd()->fT);
caryclark6c3b9cd2016-09-26 05:36:58 -07002112 SkDebugf("coinSpan + id=%d t=%1.9g tEnd=%1.9g\n", oppPtTStart()->segment()->debugID(),
caryclark55888e42016-07-18 10:01:36 -07002113 oppPtTStart()->fT, oppPtTEnd()->fT);
2114}
2115
2116void SkOpCoincidence::debugShowCoincidence() const {
caryclark26ad22a2015-10-16 09:03:38 -07002117#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -07002118 const SkCoincidentSpans* span = fHead;
2119 while (span) {
2120 span->debugShow();
2121 span = span->next();
2122 }
2123#endif
2124}
2125
Cary Clarkab87d7a2016-10-04 10:01:04 -04002126#if DEBUG_COIN
caryclark6c3b9cd2016-09-26 05:36:58 -07002127static void DebugCheckBetween(const SkOpSpanBase* next, const SkOpSpanBase* end,
caryclark55888e42016-07-18 10:01:36 -07002128 double oStart, double oEnd, const SkOpSegment* oSegment,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002129 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002130 SkASSERT(next != end);
2131 SkASSERT(!next->contains(end) || log);
2132 if (next->t() > end->t()) {
2133 SkTSwap(next, end);
2134 }
2135 do {
2136 const SkOpPtT* ptT = next->ptT();
2137 int index = 0;
caryclark27c015d2016-09-23 05:47:20 -07002138 bool somethingBetween = false;
caryclark55888e42016-07-18 10:01:36 -07002139 do {
2140 ++index;
2141 ptT = ptT->next();
2142 const SkOpPtT* checkPtT = next->ptT();
2143 if (ptT == checkPtT) {
2144 break;
2145 }
2146 bool looped = false;
2147 for (int check = 0; check < index; ++check) {
2148 if ((looped = checkPtT == ptT)) {
2149 break;
2150 }
2151 checkPtT = checkPtT->next();
2152 }
2153 if (looped) {
2154 SkASSERT(0);
2155 break;
2156 }
2157 if (ptT->deleted()) {
2158 continue;
2159 }
2160 if (ptT->segment() != oSegment) {
2161 continue;
2162 }
2163 somethingBetween |= between(oStart, ptT->fT, oEnd);
2164 } while (true);
2165 SkASSERT(somethingBetween);
2166 } while (next != end && (next = next->upCast()->next()));
2167}
2168
2169static void DebugCheckOverlap(const SkCoincidentSpans* test, const SkCoincidentSpans* list,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002170 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002171 if (!list) {
2172 return;
2173 }
2174 const SkOpSegment* coinSeg = test->coinPtTStart()->segment();
2175 SkASSERT(coinSeg == test->coinPtTEnd()->segment());
2176 const SkOpSegment* oppSeg = test->oppPtTStart()->segment();
2177 SkASSERT(oppSeg == test->oppPtTEnd()->segment());
2178 SkASSERT(coinSeg != test->oppPtTStart()->segment());
2179 SkDEBUGCODE(double tcs = test->coinPtTStart()->fT);
2180 SkASSERT(between(0, tcs, 1));
2181 SkDEBUGCODE(double tce = test->coinPtTEnd()->fT);
2182 SkASSERT(between(0, tce, 1));
2183 SkASSERT(tcs < tce);
2184 double tos = test->oppPtTStart()->fT;
2185 SkASSERT(between(0, tos, 1));
2186 double toe = test->oppPtTEnd()->fT;
2187 SkASSERT(between(0, toe, 1));
2188 SkASSERT(tos != toe);
2189 if (tos > toe) {
2190 SkTSwap(tos, toe);
2191 }
2192 do {
2193 double lcs, lce, los, loe;
2194 if (coinSeg == list->coinPtTStart()->segment()) {
2195 if (oppSeg != list->oppPtTStart()->segment()) {
2196 continue;
2197 }
2198 lcs = list->coinPtTStart()->fT;
2199 lce = list->coinPtTEnd()->fT;
2200 los = list->oppPtTStart()->fT;
2201 loe = list->oppPtTEnd()->fT;
2202 if (los > loe) {
2203 SkTSwap(los, loe);
2204 }
2205 } else if (coinSeg == list->oppPtTStart()->segment()) {
2206 if (oppSeg != list->coinPtTStart()->segment()) {
2207 continue;
2208 }
2209 lcs = list->oppPtTStart()->fT;
2210 lce = list->oppPtTEnd()->fT;
2211 if (lcs > lce) {
2212 SkTSwap(lcs, lce);
2213 }
2214 los = list->coinPtTStart()->fT;
2215 loe = list->coinPtTEnd()->fT;
2216 } else {
2217 continue;
2218 }
2219 SkASSERT(tce < lcs || lce < tcs);
2220 SkASSERT(toe < los || loe < tos);
2221 } while ((list = list->next()));
2222}
2223
2224
2225static void DebugCheckOverlapTop(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002226 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002227 // check for overlapping coincident spans
2228 const SkCoincidentSpans* test = head;
2229 while (test) {
2230 const SkCoincidentSpans* next = test->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04002231 DebugCheckOverlap(test, next, log);
2232 DebugCheckOverlap(test, opt, log);
caryclark55888e42016-07-18 10:01:36 -07002233 test = next;
2234 }
2235}
2236
caryclark55888e42016-07-18 10:01:36 -07002237static void DebugValidate(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002238 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002239 // look for pts inside coincident spans that are not inside the opposite spans
2240 const SkCoincidentSpans* coin = head;
2241 while (coin) {
2242 SkASSERT(SkOpCoincidence::Ordered(coin->coinPtTStart()->segment(),
2243 coin->oppPtTStart()->segment()));
2244 SkASSERT(coin->coinPtTStart()->span()->ptT() == coin->coinPtTStart());
2245 SkASSERT(coin->coinPtTEnd()->span()->ptT() == coin->coinPtTEnd());
2246 SkASSERT(coin->oppPtTStart()->span()->ptT() == coin->oppPtTStart());
2247 SkASSERT(coin->oppPtTEnd()->span()->ptT() == coin->oppPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07002248 coin = coin->next();
2249 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002250 DebugCheckOverlapTop(head, opt, log);
caryclark55888e42016-07-18 10:01:36 -07002251}
2252#endif
2253
2254void SkOpCoincidence::debugValidate() const {
2255#if DEBUG_COINCIDENCE
Cary Clarkab87d7a2016-10-04 10:01:04 -04002256 DebugValidate(fHead, fTop, nullptr);
2257 DebugValidate(fTop, nullptr, nullptr);
caryclark55888e42016-07-18 10:01:36 -07002258#endif
2259}
2260
Cary Clarkab87d7a2016-10-04 10:01:04 -04002261#if DEBUG_COIN
caryclark6c3b9cd2016-09-26 05:36:58 -07002262static void DebugCheckBetween(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002263 SkPathOpsDebug::GlitchLog* log) {
caryclark6c3b9cd2016-09-26 05:36:58 -07002264 // look for pts inside coincident spans that are not inside the opposite spans
2265 const SkCoincidentSpans* coin = head;
2266 while (coin) {
2267 DebugCheckBetween(coin->coinPtTStart()->span(), coin->coinPtTEnd()->span(),
2268 coin->oppPtTStart()->fT, coin->oppPtTEnd()->fT, coin->oppPtTStart()->segment(),
Cary Clarkab87d7a2016-10-04 10:01:04 -04002269 log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002270 DebugCheckBetween(coin->oppPtTStart()->span(), coin->oppPtTEnd()->span(),
2271 coin->coinPtTStart()->fT, coin->coinPtTEnd()->fT, coin->coinPtTStart()->segment(),
Cary Clarkab87d7a2016-10-04 10:01:04 -04002272 log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002273 coin = coin->next();
2274 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002275 DebugCheckOverlapTop(head, opt, log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002276}
2277#endif
2278
2279void SkOpCoincidence::debugCheckBetween() const {
2280#if DEBUG_COINCIDENCE
2281 if (fGlobalState->debugCheckHealth()) {
2282 return;
2283 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002284 DebugCheckBetween(fHead, fTop, nullptr);
2285 DebugCheckBetween(fTop, nullptr, nullptr);
caryclark6c3b9cd2016-09-26 05:36:58 -07002286#endif
2287}
2288
Cary Clarkab87d7a2016-10-04 10:01:04 -04002289#if DEBUG_COIN
2290void SkOpContour::debugCheckHealth(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07002291 const SkOpSegment* segment = &fHead;
2292 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002293 segment->debugCheckHealth(log);
caryclark26ad22a2015-10-16 09:03:38 -07002294 } while ((segment = segment->next()));
2295}
2296
Cary Clarkab87d7a2016-10-04 10:01:04 -04002297void SkOpCoincidence::debugCheckValid(SkPathOpsDebug::GlitchLog* log) const {
2298#if DEBUG_VALIDATE
2299 DebugValidate(fHead, fTop, log);
2300 DebugValidate(fTop, nullptr, log);
2301#endif
2302}
2303
2304void SkOpCoincidence::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
2305 const SkCoincidentSpans* coin = fHead;
2306 if (!coin) {
2307 return;
2308 }
2309 do {
2310 coin->debugCorrectEnds(log);
2311 } while ((coin = coin->next()));
2312}
2313
caryclark55888e42016-07-18 10:01:36 -07002314// commmented-out lines keep this aligned with missingCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002315void SkOpContour::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07002316// SkASSERT(fCount > 0);
caryclark26ad22a2015-10-16 09:03:38 -07002317 const SkOpSegment* segment = &fHead;
caryclark55888e42016-07-18 10:01:36 -07002318// bool result = false;
caryclark26ad22a2015-10-16 09:03:38 -07002319 do {
Cary Clarke47ae292016-10-05 08:51:39 -04002320 if (segment->debugMissingCoincidence(log), false) {
caryclark55888e42016-07-18 10:01:36 -07002321// result = true;
caryclark55888e42016-07-18 10:01:36 -07002322 }
2323 segment = segment->next();
2324 } while (segment);
2325 return;
caryclark26ad22a2015-10-16 09:03:38 -07002326}
Cary Clarkab87d7a2016-10-04 10:01:04 -04002327
2328void SkOpContour::debugMoveMultiples(SkPathOpsDebug::GlitchLog* log) const {
2329 SkASSERT(fCount > 0);
2330 const SkOpSegment* segment = &fHead;
2331 do {
2332 if (segment->debugMoveMultiples(log), false) {
2333 return;
2334 }
2335 } while ((segment = segment->next()));
2336 return;
2337}
2338
2339void SkOpContour::debugMoveNearby(SkPathOpsDebug::GlitchLog* log) const {
2340 SkASSERT(fCount > 0);
2341 const SkOpSegment* segment = &fHead;
2342 do {
2343 segment->debugMoveNearby(log);
2344 } while ((segment = segment->next()));
2345}
caryclark26ad22a2015-10-16 09:03:38 -07002346#endif
2347
caryclark025b11e2016-08-25 05:21:14 -07002348#if DEBUG_COINCIDENCE_ORDER
2349void SkOpSegment::debugResetCoinT() const {
2350 fDebugBaseIndex = -1;
2351 fDebugBaseMin = 1;
2352 fDebugBaseMax = -1;
2353 fDebugLastIndex = -1;
2354 fDebugLastMin = 1;
2355 fDebugLastMax = -1;
2356}
2357#endif
2358
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002359void SkOpSegment::debugValidate() const {
caryclark025b11e2016-08-25 05:21:14 -07002360#if DEBUG_COINCIDENCE_ORDER
2361 {
2362 const SkOpSpanBase* span = &fHead;
2363 do {
2364 span->debugResetCoinT();
2365 } while (!span->final() && (span = span->upCast()->next()));
2366 span = &fHead;
2367 int index = 0;
2368 do {
2369 span->debugSetCoinT(index++);
2370 } while (!span->final() && (span = span->upCast()->next()));
2371 }
2372#endif
caryclark55888e42016-07-18 10:01:36 -07002373#if DEBUG_COINCIDENCE
2374 if (this->globalState()->debugCheckHealth()) {
2375 return;
2376 }
2377#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002378#if DEBUG_VALIDATE
caryclark54359292015-03-26 07:52:43 -07002379 const SkOpSpanBase* span = &fHead;
2380 double lastT = -1;
halcanary96fcdcc2015-08-27 07:41:13 -07002381 const SkOpSpanBase* prev = nullptr;
caryclark54359292015-03-26 07:52:43 -07002382 int count = 0;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002383 int done = 0;
caryclark54359292015-03-26 07:52:43 -07002384 do {
2385 if (!span->final()) {
2386 ++count;
2387 done += span->upCast()->done() ? 1 : 0;
2388 }
2389 SkASSERT(span->segment() == this);
2390 SkASSERT(!prev || prev->upCast()->next() == span);
2391 SkASSERT(!prev || prev == span->prev());
2392 prev = span;
2393 double t = span->ptT()->fT;
2394 SkASSERT(lastT < t);
2395 lastT = t;
2396 span->debugValidate();
2397 } while (!span->final() && (span = span->upCast()->next()));
2398 SkASSERT(count == fCount);
2399 SkASSERT(done == fDoneCount);
caryclark08bc8482015-04-24 09:08:57 -07002400 SkASSERT(count >= fDoneCount);
caryclark54359292015-03-26 07:52:43 -07002401 SkASSERT(span->final());
2402 span->debugValidate();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002403#endif
caryclark54359292015-03-26 07:52:43 -07002404}
2405
Cary Clarkab87d7a2016-10-04 10:01:04 -04002406#if DEBUG_COIN
caryclark30b9fdd2016-08-31 14:36:29 -07002407
2408// Commented-out lines keep this in sync with addOpp()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002409void SkOpSpanBase::debugAddOpp(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002410 const SkOpPtT* oppPrev = this->ptT()->oppPrev(opp->ptT());
2411 if (!oppPrev) {
caryclark55888e42016-07-18 10:01:36 -07002412 return;
caryclark26ad22a2015-10-16 09:03:38 -07002413 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002414 this->debugMergeMatches(log, opp);
caryclark30b9fdd2016-08-31 14:36:29 -07002415 this->ptT()->debugAddOpp(opp->ptT(), oppPrev);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002416 this->debugCheckForCollapsedCoincidence(log);
caryclark26ad22a2015-10-16 09:03:38 -07002417}
2418
caryclark55888e42016-07-18 10:01:36 -07002419// Commented-out lines keep this in sync with checkForCollapsedCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002420void SkOpSpanBase::debugCheckForCollapsedCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07002421 const SkOpCoincidence* coins = this->globalState()->coincidence();
2422 if (coins->isEmpty()) {
2423 return;
2424 }
2425// the insert above may have put both ends of a coincident run in the same span
2426// for each coincident ptT in loop; see if its opposite in is also in the loop
2427// this implementation is the motivation for marking that a ptT is referenced by a coincident span
2428 const SkOpPtT* head = this->ptT();
2429 const SkOpPtT* test = head;
caryclark26ad22a2015-10-16 09:03:38 -07002430 do {
caryclark55888e42016-07-18 10:01:36 -07002431 if (!test->coincident()) {
2432 continue;
caryclark26ad22a2015-10-16 09:03:38 -07002433 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002434 coins->debugMarkCollapsed(log, test);
caryclark55888e42016-07-18 10:01:36 -07002435 } while ((test = test->next()) != head);
caryclark26ad22a2015-10-16 09:03:38 -07002436}
caryclark55888e42016-07-18 10:01:36 -07002437#endif
caryclark26ad22a2015-10-16 09:03:38 -07002438
caryclark54359292015-03-26 07:52:43 -07002439bool SkOpSpanBase::debugCoinEndLoopCheck() const {
2440 int loop = 0;
2441 const SkOpSpanBase* next = this;
2442 SkOpSpanBase* nextCoin;
2443 do {
2444 nextCoin = next->fCoinEnd;
2445 SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin);
2446 for (int check = 1; check < loop - 1; ++check) {
2447 const SkOpSpanBase* checkCoin = this->fCoinEnd;
2448 const SkOpSpanBase* innerCoin = checkCoin;
2449 for (int inner = check + 1; inner < loop; ++inner) {
2450 innerCoin = innerCoin->fCoinEnd;
2451 if (checkCoin == innerCoin) {
2452 SkDebugf("*** bad coincident end loop ***\n");
2453 return false;
2454 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002455 }
2456 }
caryclark54359292015-03-26 07:52:43 -07002457 ++loop;
2458 } while ((next = nextCoin) && next != this);
2459 return true;
2460}
2461
Cary Clarkab87d7a2016-10-04 10:01:04 -04002462#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002463// Commented-out lines keep this in sync with insertCoinEnd()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002464void SkOpSpanBase::debugInsertCoinEnd(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* coin) const {
caryclark55888e42016-07-18 10:01:36 -07002465 if (containsCoinEnd(coin)) {
2466// SkASSERT(coin->containsCoinEnd(this));
2467 return;
2468 }
2469 debugValidate();
2470// SkASSERT(this != coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002471 log->record(SkPathOpsDebug::kMarkCoinEnd_Glitch, this, coin);
caryclark55888e42016-07-18 10:01:36 -07002472// coin->fCoinEnd = this->fCoinEnd;
2473// this->fCoinEnd = coinNext;
2474 debugValidate();
2475}
2476
caryclark30b9fdd2016-08-31 14:36:29 -07002477// Commented-out lines keep this in sync with mergeMatches()
2478// Look to see if pt-t linked list contains same segment more than once
2479// if so, and if each pt-t is directly pointed to by spans in that segment,
2480// merge them
2481// keep the points, but remove spans so that the segment doesn't have 2 or more
2482// spans pointing to the same pt-t loop at different loop elements
Cary Clarkab87d7a2016-10-04 10:01:04 -04002483void SkOpSpanBase::debugMergeMatches(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002484 const SkOpPtT* test = &fPtT;
2485 const SkOpPtT* testNext;
2486 const SkOpPtT* stop = test;
2487 do {
2488 testNext = test->next();
2489 if (test->deleted()) {
2490 continue;
2491 }
2492 const SkOpSpanBase* testBase = test->span();
2493 SkASSERT(testBase->ptT() == test);
2494 const SkOpSegment* segment = test->segment();
2495 if (segment->done()) {
2496 continue;
2497 }
2498 const SkOpPtT* inner = opp->ptT();
2499 const SkOpPtT* innerStop = inner;
2500 do {
2501 if (inner->segment() != segment) {
2502 continue;
2503 }
2504 if (inner->deleted()) {
2505 continue;
2506 }
2507 const SkOpSpanBase* innerBase = inner->span();
2508 SkASSERT(innerBase->ptT() == inner);
Ben Wagner63fd7602017-10-09 15:45:33 -04002509 // when the intersection is first detected, the span base is marked if there are
caryclark30b9fdd2016-08-31 14:36:29 -07002510 // more than one point in the intersection.
2511// if (!innerBase->hasMultipleHint() && !testBase->hasMultipleHint()) {
2512 if (!zero_or_one(inner->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002513 log->record(SkPathOpsDebug::kMergeMatches_Glitch, innerBase, test);
caryclark30b9fdd2016-08-31 14:36:29 -07002514 } else {
2515 SkASSERT(inner->fT != test->fT);
2516 if (!zero_or_one(test->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002517 log->record(SkPathOpsDebug::kMergeMatches_Glitch, testBase, inner);
caryclark30b9fdd2016-08-31 14:36:29 -07002518 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002519 log->record(SkPathOpsDebug::kMergeMatches_Glitch, segment);
caryclark30b9fdd2016-08-31 14:36:29 -07002520// SkDEBUGCODE(testBase->debugSetDeleted());
2521// test->setDeleted();
2522// SkDEBUGCODE(innerBase->debugSetDeleted());
2523// inner->setDeleted();
2524 }
2525 }
2526#ifdef SK_DEBUG // assert if another undeleted entry points to segment
2527 const SkOpPtT* debugInner = inner;
2528 while ((debugInner = debugInner->next()) != innerStop) {
2529 if (debugInner->segment() != segment) {
2530 continue;
2531 }
2532 if (debugInner->deleted()) {
2533 continue;
2534 }
2535 SkOPASSERT(0);
2536 }
2537#endif
Ben Wagner63fd7602017-10-09 15:45:33 -04002538 break;
caryclark30b9fdd2016-08-31 14:36:29 -07002539// }
2540 break;
2541 } while ((inner = inner->next()) != innerStop);
2542 } while ((test = testNext) != stop);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002543 this->debugCheckForCollapsedCoincidence(log);
caryclark30b9fdd2016-08-31 14:36:29 -07002544}
2545
caryclark55888e42016-07-18 10:01:36 -07002546#endif
caryclark26ad22a2015-10-16 09:03:38 -07002547
caryclark025b11e2016-08-25 05:21:14 -07002548void SkOpSpanBase::debugResetCoinT() const {
2549#if DEBUG_COINCIDENCE_ORDER
2550 const SkOpPtT* ptT = &fPtT;
2551 do {
2552 ptT->debugResetCoinT();
2553 ptT = ptT->next();
2554 } while (ptT != &fPtT);
2555#endif
2556}
2557
2558void SkOpSpanBase::debugSetCoinT(int index) const {
2559#if DEBUG_COINCIDENCE_ORDER
2560 const SkOpPtT* ptT = &fPtT;
2561 do {
2562 if (!ptT->deleted()) {
2563 ptT->debugSetCoinT(index);
2564 }
2565 ptT = ptT->next();
2566 } while (ptT != &fPtT);
2567#endif
2568}
2569
caryclark26ad22a2015-10-16 09:03:38 -07002570const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const {
2571 const SkOpSpanBase* end = *endPtr;
2572 SkASSERT(this->segment() == end->segment());
2573 const SkOpSpanBase* result;
2574 if (t() < end->t()) {
2575 result = this;
2576 } else {
2577 result = end;
2578 *endPtr = this;
2579 }
2580 return result->upCast();
2581}
2582
caryclark54359292015-03-26 07:52:43 -07002583void SkOpSpanBase::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07002584#if DEBUG_COINCIDENCE
2585 if (this->globalState()->debugCheckHealth()) {
2586 return;
2587 }
2588#endif
caryclark54359292015-03-26 07:52:43 -07002589#if DEBUG_VALIDATE
2590 const SkOpPtT* ptT = &fPtT;
2591 SkASSERT(ptT->span() == this);
2592 do {
2593// SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt));
2594 ptT->debugValidate();
2595 ptT = ptT->next();
2596 } while (ptT != &fPtT);
2597 SkASSERT(this->debugCoinEndLoopCheck());
2598 if (!this->final()) {
2599 SkASSERT(this->upCast()->debugCoinLoopCheck());
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002600 }
caryclark54359292015-03-26 07:52:43 -07002601 if (fFromAngle) {
2602 fFromAngle->debugValidate();
2603 }
2604 if (!this->final() && this->upCast()->toAngle()) {
2605 this->upCast()->toAngle()->debugValidate();
2606 }
2607#endif
2608}
2609
2610bool SkOpSpan::debugCoinLoopCheck() const {
2611 int loop = 0;
2612 const SkOpSpan* next = this;
2613 SkOpSpan* nextCoin;
2614 do {
2615 nextCoin = next->fCoincident;
2616 SkASSERT(nextCoin == this || nextCoin->fCoincident != nextCoin);
2617 for (int check = 1; check < loop - 1; ++check) {
2618 const SkOpSpan* checkCoin = this->fCoincident;
2619 const SkOpSpan* innerCoin = checkCoin;
2620 for (int inner = check + 1; inner < loop; ++inner) {
2621 innerCoin = innerCoin->fCoincident;
2622 if (checkCoin == innerCoin) {
2623 SkDebugf("*** bad coincident loop ***\n");
2624 return false;
2625 }
2626 }
2627 }
2628 ++loop;
2629 } while ((next = nextCoin) && next != this);
2630 return true;
2631}
2632
Cary Clarkab87d7a2016-10-04 10:01:04 -04002633#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002634// Commented-out lines keep this in sync with insertCoincidence() in header
Cary Clarkab87d7a2016-10-04 10:01:04 -04002635void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* coin) const {
caryclark55888e42016-07-18 10:01:36 -07002636 if (containsCoincidence(coin)) {
2637// SkASSERT(coin->containsCoincidence(this));
2638 return;
2639 }
2640 debugValidate();
2641// SkASSERT(this != coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002642 log->record(SkPathOpsDebug::kMarkCoinStart_Glitch, this, coin);
caryclark55888e42016-07-18 10:01:36 -07002643// coin->fCoincident = this->fCoincident;
2644// this->fCoincident = coinNext;
2645 debugValidate();
2646}
2647
2648// Commented-out lines keep this in sync with insertCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002649void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* segment, bool flipped, bool ordered) const {
caryclark55888e42016-07-18 10:01:36 -07002650 if (this->containsCoincidence(segment)) {
2651 return;
2652 }
2653 const SkOpPtT* next = &fPtT;
2654 while ((next = next->next()) != &fPtT) {
2655 if (next->segment() == segment) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002656 const SkOpSpan* span;
2657 const SkOpSpanBase* base = next->span();
2658 if (!ordered) {
2659 const SkOpSpanBase* spanEnd = fNext->contains(segment)->span();
2660 const SkOpPtT* start = base->ptT()->starter(spanEnd->ptT());
2661 FAIL_IF(!start->span()->upCastable(), this);
2662 span = const_cast<SkOpSpan*>(start->span()->upCast());
2663 }
2664 else if (flipped) {
2665 span = base->prev();
2666 FAIL_IF(!span, this);
2667 }
2668 else {
2669 FAIL_IF(!base->upCastable(), this);
2670 span = base->upCast();
2671 }
2672 log->record(SkPathOpsDebug::kMarkCoinInsert_Glitch, span);
caryclark55888e42016-07-18 10:01:36 -07002673 return;
2674 }
2675 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002676#if DEBUG_COIN
2677 log->record(SkPathOpsDebug::kMarkCoinMissing_Glitch, segment, this);
caryclark55888e42016-07-18 10:01:36 -07002678#endif
Cary Clarkab87d7a2016-10-04 10:01:04 -04002679 return;
caryclark55888e42016-07-18 10:01:36 -07002680}
2681#endif
2682
caryclark624637c2015-05-11 07:21:27 -07002683// called only by test code
2684int SkIntersections::debugCoincidentUsed() const {
2685 if (!fIsCoincident[0]) {
2686 SkASSERT(!fIsCoincident[1]);
2687 return 0;
2688 }
2689 int count = 0;
2690 SkDEBUGCODE(int count2 = 0;)
2691 for (int index = 0; index < fUsed; ++index) {
2692 if (fIsCoincident[0] & (1 << index)) {
2693 ++count;
2694 }
2695#ifdef SK_DEBUG
2696 if (fIsCoincident[1] & (1 << index)) {
2697 ++count2;
2698 }
2699#endif
2700 }
2701 SkASSERT(count == count2);
2702 return count;
2703}
2704
caryclark54359292015-03-26 07:52:43 -07002705#include "SkOpContour.h"
2706
caryclark55888e42016-07-18 10:01:36 -07002707// Commented-out lines keep this in sync with addOpp()
caryclark29b25632016-08-25 11:27:17 -07002708void SkOpPtT::debugAddOpp(const SkOpPtT* opp, const SkOpPtT* oppPrev) const {
2709 SkDEBUGCODE(const SkOpPtT* oldNext = this->fNext);
caryclark55888e42016-07-18 10:01:36 -07002710 SkASSERT(this != opp);
2711// this->fNext = opp;
caryclark29b25632016-08-25 11:27:17 -07002712 SkASSERT(oppPrev != oldNext);
caryclark55888e42016-07-18 10:01:36 -07002713// oppPrev->fNext = oldNext;
caryclark55888e42016-07-18 10:01:36 -07002714}
2715
caryclark26ad22a2015-10-16 09:03:38 -07002716bool SkOpPtT::debugContains(const SkOpPtT* check) const {
2717 SkASSERT(this != check);
2718 const SkOpPtT* ptT = this;
2719 int links = 0;
2720 do {
2721 ptT = ptT->next();
2722 if (ptT == check) {
2723 return true;
2724 }
2725 ++links;
2726 const SkOpPtT* test = this;
2727 for (int index = 0; index < links; ++index) {
2728 if (ptT == test) {
2729 return false;
2730 }
2731 test = test->next();
2732 }
2733 } while (true);
2734}
2735
2736const SkOpPtT* SkOpPtT::debugContains(const SkOpSegment* check) const {
2737 SkASSERT(this->segment() != check);
2738 const SkOpPtT* ptT = this;
2739 int links = 0;
2740 do {
2741 ptT = ptT->next();
2742 if (ptT->segment() == check) {
2743 return ptT;
2744 }
2745 ++links;
2746 const SkOpPtT* test = this;
2747 for (int index = 0; index < links; ++index) {
2748 if (ptT == test) {
2749 return nullptr;
2750 }
2751 test = test->next();
2752 }
2753 } while (true);
2754}
2755
caryclark8016b262016-09-06 05:59:47 -07002756const SkOpPtT* SkOpPtT::debugEnder(const SkOpPtT* end) const {
2757 return fT < end->fT ? end : this;
2758}
2759
caryclark54359292015-03-26 07:52:43 -07002760int SkOpPtT::debugLoopLimit(bool report) const {
2761 int loop = 0;
2762 const SkOpPtT* next = this;
2763 do {
2764 for (int check = 1; check < loop - 1; ++check) {
2765 const SkOpPtT* checkPtT = this->fNext;
2766 const SkOpPtT* innerPtT = checkPtT;
2767 for (int inner = check + 1; inner < loop; ++inner) {
2768 innerPtT = innerPtT->fNext;
2769 if (checkPtT == innerPtT) {
2770 if (report) {
2771 SkDebugf("*** bad ptT loop ***\n");
2772 }
2773 return loop;
2774 }
2775 }
2776 }
caryclark26ad22a2015-10-16 09:03:38 -07002777 // there's nothing wrong with extremely large loop counts -- but this may appear to hang
2778 // by taking a very long time to figure out that no loop entry is a duplicate
2779 // -- and it's likely that a large loop count is indicative of a bug somewhere
2780 if (++loop > 1000) {
2781 SkDebugf("*** loop count exceeds 1000 ***\n");
2782 return 1000;
2783 }
caryclark54359292015-03-26 07:52:43 -07002784 } while ((next = next->fNext) && next != this);
2785 return 0;
2786}
2787
caryclark29b25632016-08-25 11:27:17 -07002788const SkOpPtT* SkOpPtT::debugOppPrev(const SkOpPtT* opp) const {
2789 return this->oppPrev(const_cast<SkOpPtT*>(opp));
2790}
2791
caryclark025b11e2016-08-25 05:21:14 -07002792void SkOpPtT::debugResetCoinT() const {
2793#if DEBUG_COINCIDENCE_ORDER
Ben Wagner63fd7602017-10-09 15:45:33 -04002794 this->segment()->debugResetCoinT();
caryclark025b11e2016-08-25 05:21:14 -07002795#endif
2796}
2797
2798void SkOpPtT::debugSetCoinT(int index) const {
2799#if DEBUG_COINCIDENCE_ORDER
Ben Wagner63fd7602017-10-09 15:45:33 -04002800 this->segment()->debugSetCoinT(index, fT);
caryclark025b11e2016-08-25 05:21:14 -07002801#endif
2802}
2803
caryclark54359292015-03-26 07:52:43 -07002804void SkOpPtT::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07002805#if DEBUG_COINCIDENCE
2806 if (this->globalState()->debugCheckHealth()) {
2807 return;
2808 }
2809#endif
caryclark54359292015-03-26 07:52:43 -07002810#if DEBUG_VALIDATE
Cary Clarkab87d7a2016-10-04 10:01:04 -04002811 SkOpPhase phase = contour()->globalState()->phase();
2812 if (phase == SkOpPhase::kIntersecting || phase == SkOpPhase::kFixWinding) {
caryclark54359292015-03-26 07:52:43 -07002813 return;
2814 }
2815 SkASSERT(fNext);
2816 SkASSERT(fNext != this);
2817 SkASSERT(fNext->fNext);
2818 SkASSERT(debugLoopLimit(false) == 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002819#endif
2820}
caryclark1049f122015-04-20 08:31:59 -07002821
2822static void output_scalar(SkScalar num) {
2823 if (num == (int) num) {
2824 SkDebugf("%d", (int) num);
2825 } else {
2826 SkString str;
2827 str.printf("%1.9g", num);
2828 int width = (int) str.size();
2829 const char* cStr = str.c_str();
2830 while (cStr[width - 1] == '0') {
2831 --width;
2832 }
2833 str.resize(width);
2834 SkDebugf("%sf", str.c_str());
2835 }
2836}
2837
2838static void output_points(const SkPoint* pts, int count) {
2839 for (int index = 0; index < count; ++index) {
2840 output_scalar(pts[index].fX);
2841 SkDebugf(", ");
2842 output_scalar(pts[index].fY);
2843 if (index + 1 < count) {
2844 SkDebugf(", ");
2845 }
2846 }
2847}
2848
2849static void showPathContours(SkPath::RawIter& iter, const char* pathName) {
2850 uint8_t verb;
2851 SkPoint pts[4];
2852 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
2853 switch (verb) {
2854 case SkPath::kMove_Verb:
2855 SkDebugf(" %s.moveTo(", pathName);
2856 output_points(&pts[0], 1);
2857 SkDebugf(");\n");
2858 continue;
2859 case SkPath::kLine_Verb:
2860 SkDebugf(" %s.lineTo(", pathName);
2861 output_points(&pts[1], 1);
2862 SkDebugf(");\n");
2863 break;
2864 case SkPath::kQuad_Verb:
2865 SkDebugf(" %s.quadTo(", pathName);
2866 output_points(&pts[1], 2);
2867 SkDebugf(");\n");
2868 break;
2869 case SkPath::kConic_Verb:
2870 SkDebugf(" %s.conicTo(", pathName);
2871 output_points(&pts[1], 2);
2872 SkDebugf(", %1.9gf);\n", iter.conicWeight());
2873 break;
2874 case SkPath::kCubic_Verb:
2875 SkDebugf(" %s.cubicTo(", pathName);
2876 output_points(&pts[1], 3);
2877 SkDebugf(");\n");
2878 break;
2879 case SkPath::kClose_Verb:
2880 SkDebugf(" %s.close();\n", pathName);
2881 break;
2882 default:
2883 SkDEBUGFAIL("bad verb");
2884 return;
2885 }
2886 }
2887}
2888
2889static const char* gFillTypeStr[] = {
2890 "kWinding_FillType",
2891 "kEvenOdd_FillType",
2892 "kInverseWinding_FillType",
2893 "kInverseEvenOdd_FillType"
2894};
2895
2896void SkPathOpsDebug::ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration) {
2897 SkPath::RawIter iter(path);
2898#define SUPPORT_RECT_CONTOUR_DETECTION 0
2899#if SUPPORT_RECT_CONTOUR_DETECTION
halcanary96fcdcc2015-08-27 07:41:13 -07002900 int rectCount = path.isRectContours() ? path.rectContours(nullptr, nullptr) : 0;
caryclark1049f122015-04-20 08:31:59 -07002901 if (rectCount > 0) {
2902 SkTDArray<SkRect> rects;
2903 SkTDArray<SkPath::Direction> directions;
2904 rects.setCount(rectCount);
2905 directions.setCount(rectCount);
2906 path.rectContours(rects.begin(), directions.begin());
2907 for (int contour = 0; contour < rectCount; ++contour) {
2908 const SkRect& rect = rects[contour];
2909 SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
2910 rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
2911 ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
2912 }
2913 return;
2914 }
2915#endif
2916 SkPath::FillType fillType = path.getFillType();
2917 SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType);
2918 if (includeDeclaration) {
2919 SkDebugf(" SkPath %s;\n", name);
2920 }
2921 SkDebugf(" %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[fillType]);
2922 iter.setPath(path);
2923 showPathContours(iter, name);
2924}
caryclark13260682016-10-24 05:10:14 -07002925
Cary Clark918fb1f2016-11-15 13:22:25 -05002926#if DEBUG_DUMP_VERIFY
caryclark13260682016-10-24 05:10:14 -07002927#include "SkData.h"
2928#include "SkStream.h"
2929
2930static void dump_path(FILE* file, const SkPath& path, bool force, bool dumpAsHex) {
2931 SkDynamicMemoryWStream wStream;
2932 path.dump(&wStream, force, dumpAsHex);
2933 sk_sp<SkData> data(wStream.detachAsData());
2934 fprintf(file, "%.*s\n", (int) data->size(), (char*) data->data());
2935}
2936
2937static int dumpID = 0;
2938
2939void SkPathOpsDebug::DumpOp(const SkPath& one, const SkPath& two, SkPathOp op,
2940 const char* testName) {
2941 FILE* file = sk_fopen("op_dump.txt", kWrite_SkFILE_Flag);
2942 DumpOp(file, one, two, op, testName);
2943}
2944
2945void SkPathOpsDebug::DumpOp(FILE* file, const SkPath& one, const SkPath& two, SkPathOp op,
2946 const char* testName) {
2947 const char* name = testName ? testName : "op";
2948 fprintf(file,
2949 "\nstatic void %s_%d(skiatest::Reporter* reporter, const char* filename) {\n",
2950 name, ++dumpID);
2951 fprintf(file, " SkPath path;\n");
2952 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", one.getFillType());
2953 dump_path(file, one, false, true);
2954 fprintf(file, " SkPath path1(path);\n");
2955 fprintf(file, " path.reset();\n");
2956 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", two.getFillType());
2957 dump_path(file, two, false, true);
2958 fprintf(file, " SkPath path2(path);\n");
2959 fprintf(file, " testPathOp(reporter, path1, path2, (SkPathOp) %d, filename);\n", op);
2960 fprintf(file, "}\n\n");
2961 fclose(file);
2962}
2963
2964void SkPathOpsDebug::DumpSimplify(const SkPath& path, const char* testName) {
2965 FILE* file = sk_fopen("simplify_dump.txt", kWrite_SkFILE_Flag);
2966 DumpSimplify(file, path, testName);
2967}
2968
2969void SkPathOpsDebug::DumpSimplify(FILE* file, const SkPath& path, const char* testName) {
2970 const char* name = testName ? testName : "simplify";
2971 fprintf(file,
2972 "\nstatic void %s_%d(skiatest::Reporter* reporter, const char* filename) {\n",
2973 name, ++dumpID);
2974 fprintf(file, " SkPath path;\n");
2975 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", path.getFillType());
2976 dump_path(file, path, false, true);
2977 fprintf(file, " testSimplify(reporter, path, filename);\n");
2978 fprintf(file, "}\n\n");
2979 fclose(file);
2980}
2981
2982#include "SkBitmap.h"
2983#include "SkCanvas.h"
2984#include "SkPaint.h"
2985
2986const int bitWidth = 64;
2987const int bitHeight = 64;
2988
2989static void debug_scale_matrix(const SkPath& one, const SkPath* two, SkMatrix& scale) {
2990 SkRect larger = one.getBounds();
2991 if (two) {
2992 larger.join(two->getBounds());
2993 }
2994 SkScalar largerWidth = larger.width();
2995 if (largerWidth < 4) {
2996 largerWidth = 4;
2997 }
2998 SkScalar largerHeight = larger.height();
2999 if (largerHeight < 4) {
3000 largerHeight = 4;
3001 }
3002 SkScalar hScale = (bitWidth - 2) / largerWidth;
3003 SkScalar vScale = (bitHeight - 2) / largerHeight;
3004 scale.reset();
3005 scale.preScale(hScale, vScale);
3006 larger.fLeft *= hScale;
3007 larger.fRight *= hScale;
3008 larger.fTop *= vScale;
3009 larger.fBottom *= vScale;
3010 SkScalar dx = -16000 > larger.fLeft ? -16000 - larger.fLeft
3011 : 16000 < larger.fRight ? 16000 - larger.fRight : 0;
3012 SkScalar dy = -16000 > larger.fTop ? -16000 - larger.fTop
3013 : 16000 < larger.fBottom ? 16000 - larger.fBottom : 0;
3014 scale.preTranslate(dx, dy);
3015}
3016
3017static int debug_paths_draw_the_same(const SkPath& one, const SkPath& two, SkBitmap& bits) {
3018 if (bits.width() == 0) {
3019 bits.allocN32Pixels(bitWidth * 2, bitHeight);
3020 }
3021 SkCanvas canvas(bits);
3022 canvas.drawColor(SK_ColorWHITE);
3023 SkPaint paint;
3024 canvas.save();
3025 const SkRect& bounds1 = one.getBounds();
3026 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
3027 canvas.drawPath(one, paint);
3028 canvas.restore();
3029 canvas.save();
3030 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
3031 canvas.drawPath(two, paint);
3032 canvas.restore();
3033 int errors = 0;
3034 for (int y = 0; y < bitHeight - 1; ++y) {
3035 uint32_t* addr1 = bits.getAddr32(0, y);
3036 uint32_t* addr2 = bits.getAddr32(0, y + 1);
3037 uint32_t* addr3 = bits.getAddr32(bitWidth, y);
3038 uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1);
3039 for (int x = 0; x < bitWidth - 1; ++x) {
3040 // count 2x2 blocks
3041 bool err = addr1[x] != addr3[x];
3042 if (err) {
3043 errors += addr1[x + 1] != addr3[x + 1]
3044 && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1];
3045 }
3046 }
3047 }
3048 return errors;
3049}
3050
3051void SkPathOpsDebug::ReportOpFail(const SkPath& one, const SkPath& two, SkPathOp op) {
3052 SkDebugf("// Op did not expect failure\n");
3053 DumpOp(stderr, one, two, op, "opTest");
3054 fflush(stderr);
3055}
3056
3057void SkPathOpsDebug::VerifyOp(const SkPath& one, const SkPath& two, SkPathOp op,
3058 const SkPath& result) {
3059 SkPath pathOut, scaledPathOut;
3060 SkRegion rgnA, rgnB, openClip, rgnOut;
3061 openClip.setRect(-16000, -16000, 16000, 16000);
3062 rgnA.setPath(one, openClip);
3063 rgnB.setPath(two, openClip);
3064 rgnOut.op(rgnA, rgnB, (SkRegion::Op) op);
3065 rgnOut.getBoundaryPath(&pathOut);
3066 SkMatrix scale;
3067 debug_scale_matrix(one, &two, scale);
3068 SkRegion scaledRgnA, scaledRgnB, scaledRgnOut;
3069 SkPath scaledA, scaledB;
3070 scaledA.addPath(one, scale);
3071 scaledA.setFillType(one.getFillType());
3072 scaledB.addPath(two, scale);
3073 scaledB.setFillType(two.getFillType());
3074 scaledRgnA.setPath(scaledA, openClip);
3075 scaledRgnB.setPath(scaledB, openClip);
3076 scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) op);
3077 scaledRgnOut.getBoundaryPath(&scaledPathOut);
3078 SkBitmap bitmap;
3079 SkPath scaledOut;
3080 scaledOut.addPath(result, scale);
3081 scaledOut.setFillType(result.getFillType());
3082 int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
3083 const int MAX_ERRORS = 9;
3084 if (errors > MAX_ERRORS) {
3085 fprintf(stderr, "// Op did not expect errors=%d\n", errors);
3086 DumpOp(stderr, one, two, op, "opTest");
3087 fflush(stderr);
3088 }
3089}
3090
3091void SkPathOpsDebug::ReportSimplifyFail(const SkPath& path) {
3092 SkDebugf("// Simplify did not expect failure\n");
3093 DumpSimplify(stderr, path, "simplifyTest");
3094 fflush(stderr);
3095}
3096
3097void SkPathOpsDebug::VerifySimplify(const SkPath& path, const SkPath& result) {
3098 SkPath pathOut, scaledPathOut;
3099 SkRegion rgnA, openClip, rgnOut;
3100 openClip.setRect(-16000, -16000, 16000, 16000);
3101 rgnA.setPath(path, openClip);
3102 rgnOut.getBoundaryPath(&pathOut);
3103 SkMatrix scale;
3104 debug_scale_matrix(path, nullptr, scale);
3105 SkRegion scaledRgnA;
3106 SkPath scaledA;
3107 scaledA.addPath(path, scale);
3108 scaledA.setFillType(path.getFillType());
3109 scaledRgnA.setPath(scaledA, openClip);
3110 scaledRgnA.getBoundaryPath(&scaledPathOut);
3111 SkBitmap bitmap;
3112 SkPath scaledOut;
3113 scaledOut.addPath(result, scale);
3114 scaledOut.setFillType(result.getFillType());
3115 int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
3116 const int MAX_ERRORS = 9;
3117 if (errors > MAX_ERRORS) {
3118 fprintf(stderr, "// Simplify did not expect errors=%d\n", errors);
3119 DumpSimplify(stderr, path, "simplifyTest");
3120 fflush(stderr);
3121 }
3122}
3123
3124#endif
Cary Clarkb8421ed2018-03-14 15:55:02 -04003125
3126// global path dumps for msvs Visual Studio 17 to use from Immediate Window
3127void Dump(const SkPath& path) {
3128 path.dump();
3129}
3130
3131void DumpHex(const SkPath& path) {
3132 path.dumpHex();
3133}