blob: 45a11384882d2cb17341658d7791478907efc18d [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
45const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor"};
46#endif
47
caryclark@google.com07393ca2013-04-08 11:47:37 +000048#if defined SK_DEBUG || !FORCE_RELEASE
49
caryclark@google.com570863f2013-09-16 15:55:01 +000050const char* SkPathOpsDebug::kLVerbStr[] = {"", "line", "quad", "cubic"};
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +000051
commit-bot@chromium.orgfe41b8f2014-04-25 14:03:52 +000052int SkPathOpsDebug::gContourID = 0;
53int SkPathOpsDebug::gSegmentID = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +000054
caryclark54359292015-03-26 07:52:43 -070055bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray,
56 const SkOpSpanBase* span) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000057 for (int index = 0; index < chaseArray.count(); ++index) {
caryclark54359292015-03-26 07:52:43 -070058 const SkOpSpanBase* entry = chaseArray[index];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000059 if (entry == span) {
60 return true;
61 }
62 }
63 return false;
64}
caryclark26ad22a2015-10-16 09:03:38 -070065#endif
Cary Clarkff114282016-12-14 11:56:16 -050066
67#if DEBUG_ACTIVE_SPANS
68SkString SkPathOpsDebug::gActiveSpans;
69#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000070
Cary Clarkab87d7a2016-10-04 10:01:04 -040071#if DEBUG_COIN
caryclark26ad22a2015-10-16 09:03:38 -070072
Cary Clarkab87d7a2016-10-04 10:01:04 -040073SkPathOpsDebug::CoinDict SkPathOpsDebug::gCoinSumChangedDict;
74SkPathOpsDebug::CoinDict SkPathOpsDebug::gCoinSumVisitedDict;
75
76static const int kGlitchType_Count = SkPathOpsDebug::kUnalignedTail_Glitch + 1;
caryclark26ad22a2015-10-16 09:03:38 -070077
78struct SpanGlitch {
caryclark26ad22a2015-10-16 09:03:38 -070079 const SkOpSpanBase* fBase;
80 const SkOpSpanBase* fSuspect;
caryclark26ad22a2015-10-16 09:03:38 -070081 const SkOpSegment* fSegment;
caryclark55888e42016-07-18 10:01:36 -070082 const SkOpSegment* fOppSegment;
caryclark26ad22a2015-10-16 09:03:38 -070083 const SkOpPtT* fCoinSpan;
84 const SkOpPtT* fEndSpan;
85 const SkOpPtT* fOppSpan;
86 const SkOpPtT* fOppEndSpan;
caryclark55888e42016-07-18 10:01:36 -070087 double fStartT;
88 double fEndT;
89 double fOppStartT;
90 double fOppEndT;
caryclark26ad22a2015-10-16 09:03:38 -070091 SkPoint fPt;
Cary Clarkab87d7a2016-10-04 10:01:04 -040092 SkPathOpsDebug::GlitchType fType;
93
94 void dumpType() const;
caryclark26ad22a2015-10-16 09:03:38 -070095};
96
97struct SkPathOpsDebug::GlitchLog {
Cary Clarkab87d7a2016-10-04 10:01:04 -040098 void init(const SkOpGlobalState* state) {
99 fGlobalState = state;
100 }
101
102 SpanGlitch* recordCommon(GlitchType type) {
caryclark26ad22a2015-10-16 09:03:38 -0700103 SpanGlitch* glitch = fGlitches.push();
caryclark26ad22a2015-10-16 09:03:38 -0700104 glitch->fBase = nullptr;
105 glitch->fSuspect = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -0700106 glitch->fSegment = nullptr;
caryclark55888e42016-07-18 10:01:36 -0700107 glitch->fOppSegment = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -0700108 glitch->fCoinSpan = nullptr;
109 glitch->fEndSpan = nullptr;
110 glitch->fOppSpan = nullptr;
111 glitch->fOppEndSpan = nullptr;
caryclark55888e42016-07-18 10:01:36 -0700112 glitch->fStartT = SK_ScalarNaN;
113 glitch->fEndT = SK_ScalarNaN;
114 glitch->fOppStartT = SK_ScalarNaN;
115 glitch->fOppEndT = SK_ScalarNaN;
caryclark26ad22a2015-10-16 09:03:38 -0700116 glitch->fPt = { SK_ScalarNaN, SK_ScalarNaN };
117 glitch->fType = type;
118 return glitch;
119 }
120
Cary Clarkab87d7a2016-10-04 10:01:04 -0400121 void record(GlitchType type, const SkOpSpanBase* base,
caryclark26ad22a2015-10-16 09:03:38 -0700122 const SkOpSpanBase* suspect = NULL) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400123 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700124 glitch->fBase = base;
125 glitch->fSuspect = suspect;
126 }
127
Cary Clarkab87d7a2016-10-04 10:01:04 -0400128 void record(GlitchType type, const SkOpSpanBase* base,
caryclark55888e42016-07-18 10:01:36 -0700129 const SkOpPtT* ptT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400130 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700131 glitch->fBase = base;
132 glitch->fCoinSpan = ptT;
133 }
134
Cary Clarkab87d7a2016-10-04 10:01:04 -0400135 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark55888e42016-07-18 10:01:36 -0700136 const SkCoincidentSpans* opp = NULL) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400137 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700138 glitch->fCoinSpan = coin->coinPtTStart();
139 glitch->fEndSpan = coin->coinPtTEnd();
140 if (opp) {
141 glitch->fOppSpan = opp->coinPtTStart();
142 glitch->fOppEndSpan = opp->coinPtTEnd();
143 }
caryclark26ad22a2015-10-16 09:03:38 -0700144 }
145
Cary Clarkab87d7a2016-10-04 10:01:04 -0400146 void record(GlitchType type, const SkOpSpanBase* base,
caryclark26ad22a2015-10-16 09:03:38 -0700147 const SkOpSegment* seg, double t, SkPoint pt) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400148 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700149 glitch->fBase = base;
150 glitch->fSegment = seg;
caryclark55888e42016-07-18 10:01:36 -0700151 glitch->fStartT = t;
caryclark26ad22a2015-10-16 09:03:38 -0700152 glitch->fPt = pt;
153 }
154
Cary Clarkab87d7a2016-10-04 10:01:04 -0400155 void record(GlitchType type, const SkOpSpanBase* base, double t,
caryclark26ad22a2015-10-16 09:03:38 -0700156 SkPoint pt) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400157 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700158 glitch->fBase = base;
caryclark55888e42016-07-18 10:01:36 -0700159 glitch->fStartT = t;
caryclark26ad22a2015-10-16 09:03:38 -0700160 glitch->fPt = pt;
161 }
162
Cary Clarkab87d7a2016-10-04 10:01:04 -0400163 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark26ad22a2015-10-16 09:03:38 -0700164 const SkOpPtT* coinSpan, const SkOpPtT* endSpan) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400165 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700166 glitch->fCoinSpan = coin->coinPtTStart();
167 glitch->fEndSpan = coin->coinPtTEnd();
caryclark26ad22a2015-10-16 09:03:38 -0700168 glitch->fEndSpan = endSpan;
caryclark55888e42016-07-18 10:01:36 -0700169 glitch->fOppSpan = coinSpan;
170 glitch->fOppEndSpan = endSpan;
caryclark26ad22a2015-10-16 09:03:38 -0700171 }
172
Cary Clarkab87d7a2016-10-04 10:01:04 -0400173 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark55888e42016-07-18 10:01:36 -0700174 const SkOpSpanBase* base) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400175 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700176 glitch->fBase = base;
177 glitch->fCoinSpan = coin->coinPtTStart();
178 glitch->fEndSpan = coin->coinPtTEnd();
caryclark26ad22a2015-10-16 09:03:38 -0700179 }
180
Cary Clarkab87d7a2016-10-04 10:01:04 -0400181 void record(GlitchType type, const SkOpPtT* ptTS, const SkOpPtT* ptTE,
caryclark26ad22a2015-10-16 09:03:38 -0700182 const SkOpPtT* oPtTS, const SkOpPtT* oPtTE) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400183 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700184 glitch->fCoinSpan = ptTS;
185 glitch->fEndSpan = ptTE;
186 glitch->fOppSpan = oPtTS;
187 glitch->fOppEndSpan = oPtTE;
188 }
189
Cary Clarkab87d7a2016-10-04 10:01:04 -0400190 void record(GlitchType type, const SkOpSegment* seg, double startT,
caryclark55888e42016-07-18 10:01:36 -0700191 double endT, const SkOpSegment* oppSeg, double oppStartT, double oppEndT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400192 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700193 glitch->fSegment = seg;
194 glitch->fStartT = startT;
195 glitch->fEndT = endT;
196 glitch->fOppSegment = oppSeg;
197 glitch->fOppStartT = oppStartT;
198 glitch->fOppEndT = oppEndT;
199 }
200
Cary Clarkab87d7a2016-10-04 10:01:04 -0400201 void record(GlitchType type, const SkOpSegment* seg,
caryclark55888e42016-07-18 10:01:36 -0700202 const SkOpSpan* span) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400203 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700204 glitch->fSegment = seg;
205 glitch->fBase = span;
206 }
207
Cary Clarkab87d7a2016-10-04 10:01:04 -0400208 void record(GlitchType type, double t, const SkOpSpanBase* span) {
209 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700210 glitch->fStartT = t;
211 glitch->fBase = span;
212 }
213
Cary Clarkab87d7a2016-10-04 10:01:04 -0400214 void record(GlitchType type, const SkOpSegment* seg) {
215 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700216 glitch->fSegment = seg;
217 }
218
Cary Clarkab87d7a2016-10-04 10:01:04 -0400219 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark55888e42016-07-18 10:01:36 -0700220 const SkOpPtT* ptT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400221 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700222 glitch->fCoinSpan = coin->coinPtTStart();
223 glitch->fEndSpan = ptT;
224 }
225
caryclark26ad22a2015-10-16 09:03:38 -0700226 SkTDArray<SpanGlitch> fGlitches;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400227 const SkOpGlobalState* fGlobalState;
caryclark26ad22a2015-10-16 09:03:38 -0700228};
Cary Clarkab87d7a2016-10-04 10:01:04 -0400229
230
231void SkPathOpsDebug::CoinDict::add(const SkPathOpsDebug::CoinDict& dict) {
232 int count = dict.fDict.count();
233 for (int index = 0; index < count; ++index) {
234 this->add(dict.fDict[index]);
235 }
236}
237
238void SkPathOpsDebug::CoinDict::add(const CoinDictEntry& key) {
239 int count = fDict.count();
240 for (int index = 0; index < count; ++index) {
241 CoinDictEntry* entry = &fDict[index];
242 if (entry->fIteration == key.fIteration && entry->fLineNumber == key.fLineNumber) {
243 SkASSERT(!strcmp(entry->fFunctionName, key.fFunctionName));
244 if (entry->fGlitchType == kUninitialized_Glitch) {
245 entry->fGlitchType = key.fGlitchType;
246 }
247 return;
248 }
249 }
250 *fDict.append() = key;
251}
252
253#endif
254
255#if DEBUG_COIN
256static void missing_coincidence(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) {
257 const SkOpContour* contour = contourList;
258 // bool result = false;
259 do {
260 /* result |= */ contour->debugMissingCoincidence(glitches);
261 } while ((contour = contour->next()));
262 return;
263}
264
265static void move_multiples(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) {
266 const SkOpContour* contour = contourList;
267 do {
268 if (contour->debugMoveMultiples(glitches), false) {
269 return;
270 }
271 } while ((contour = contour->next()));
272 return;
273}
274
275static void move_nearby(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) {
276 const SkOpContour* contour = contourList;
277 do {
278 contour->debugMoveNearby(glitches);
279 } while ((contour = contour->next()));
280}
281
282
283#endif
284
285#if DEBUG_COIN
286void SkOpGlobalState::debugAddToCoinChangedDict() {
287
288#if DEBUG_COINCIDENCE
caryclarke6522ea2016-10-17 07:54:33 -0700289 SkPathOpsDebug::CheckHealth(fContourHead);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400290#endif
291 // see if next coincident operation makes a change; if so, record it
292 SkPathOpsDebug::GlitchLog glitches;
293 const char* funcName = fCoinDictEntry.fFunctionName;
294 if (!strcmp("calc_angles", funcName)) {
295 ;
296 } else if (!strcmp("missing_coincidence", funcName)) {
297 missing_coincidence(&glitches, fContourHead);
298 } else if (!strcmp("move_multiples", funcName)) {
299 move_multiples(&glitches, fContourHead);
300 } else if (!strcmp("move_nearby", funcName)) {
301 move_nearby(&glitches, fContourHead);
302 } else if (!strcmp("addExpanded", funcName)) {
303 fCoincidence->debugAddExpanded(&glitches);
304 } else if (!strcmp("addMissing", funcName)) {
305 bool added;
306 fCoincidence->debugAddMissing(&glitches, &added);
307 } else if (!strcmp("addEndMovedSpans", funcName)) {
308 fCoincidence->debugAddEndMovedSpans(&glitches);
309 } else if (!strcmp("correctEnds", funcName)) {
310 fCoincidence->debugCorrectEnds(&glitches);
311 } else if (!strcmp("expand", funcName)) {
312 fCoincidence->debugExpand(&glitches);
313 } else if (!strcmp("findOverlaps", funcName)) {
314 ;
315 } else if (!strcmp("mark", funcName)) {
316 fCoincidence->debugMark(&glitches);
317 } else if (!strcmp("apply", funcName)) {
318 ;
319 } else {
320 SkASSERT(0); // add missing case
321 }
322 if (glitches.fGlitches.count()) {
323 fCoinDictEntry.fGlitchType = glitches.fGlitches[0].fType;
324 }
325 fCoinChangedDict.add(fCoinDictEntry);
326}
caryclark55888e42016-07-18 10:01:36 -0700327#endif
caryclark26ad22a2015-10-16 09:03:38 -0700328
caryclark55888e42016-07-18 10:01:36 -0700329void SkPathOpsDebug::ShowActiveSpans(SkOpContourHead* contourList) {
330#if DEBUG_ACTIVE_SPANS
Cary Clarkff114282016-12-14 11:56:16 -0500331 SkString str;
caryclark55888e42016-07-18 10:01:36 -0700332 SkOpContour* contour = contourList;
333 do {
Cary Clarkff114282016-12-14 11:56:16 -0500334 contour->debugShowActiveSpans(&str);
caryclark55888e42016-07-18 10:01:36 -0700335 } while ((contour = contour->next()));
Cary Clarkff114282016-12-14 11:56:16 -0500336 if (!gActiveSpans.equals(str)) {
337 const char* s = str.c_str();
338 const char* end;
339 while ((end = strchr(s, '\n'))) {
340 SkDebugf("%.*s", end - s + 1, s);
341 s = end + 1;
342 }
343 gActiveSpans.set(str);
344 }
caryclark55888e42016-07-18 10:01:36 -0700345#endif
346}
347
Cary Clarkab87d7a2016-10-04 10:01:04 -0400348#if DEBUG_COINCIDENCE || DEBUG_COIN
349void SkPathOpsDebug::CheckHealth(SkOpContourHead* contourList) {
caryclark55888e42016-07-18 10:01:36 -0700350#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -0700351 contourList->globalState()->debugSetCheckHealth(true);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400352#endif
353#if DEBUG_COIN
caryclark26ad22a2015-10-16 09:03:38 -0700354 GlitchLog glitches;
355 const SkOpContour* contour = contourList;
356 const SkOpCoincidence* coincidence = contour->globalState()->coincidence();
Cary Clarkab87d7a2016-10-04 10:01:04 -0400357 coincidence->debugCheckValid(&glitches); // don't call validate; spans may be inconsistent
caryclark26ad22a2015-10-16 09:03:38 -0700358 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400359 contour->debugCheckHealth(&glitches);
360 contour->debugMissingCoincidence(&glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700361 } while ((contour = contour->next()));
caryclark81a478c2016-09-09 09:37:57 -0700362 bool added;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400363 coincidence->debugAddMissing(&glitches, &added);
364 coincidence->debugExpand(&glitches);
365 coincidence->debugAddExpanded(&glitches);
366 coincidence->debugMark(&glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700367 unsigned mask = 0;
368 for (int index = 0; index < glitches.fGlitches.count(); ++index) {
369 const SpanGlitch& glitch = glitches.fGlitches[index];
370 mask |= 1 << glitch.fType;
371 }
372 for (int index = 0; index < kGlitchType_Count; ++index) {
373 SkDebugf(mask & (1 << index) ? "x" : "-");
374 }
Cary Clarkff114282016-12-14 11:56:16 -0500375 SkDebugf(" %s\n", contourList->globalState()->debugCoinDictEntry().fFunctionName);
caryclark55888e42016-07-18 10:01:36 -0700376 for (int index = 0; index < glitches.fGlitches.count(); ++index) {
377 const SpanGlitch& glitch = glitches.fGlitches[index];
378 SkDebugf("%02d: ", index);
379 if (glitch.fBase) {
caryclark8016b262016-09-06 05:59:47 -0700380 SkDebugf(" seg/base=%d/%d", glitch.fBase->segment()->debugID(),
381 glitch.fBase->debugID());
caryclark55888e42016-07-18 10:01:36 -0700382 }
383 if (glitch.fSuspect) {
caryclark8016b262016-09-06 05:59:47 -0700384 SkDebugf(" seg/base=%d/%d", glitch.fSuspect->segment()->debugID(),
385 glitch.fSuspect->debugID());
caryclark55888e42016-07-18 10:01:36 -0700386 }
387 if (glitch.fSegment) {
388 SkDebugf(" segment=%d", glitch.fSegment->debugID());
389 }
390 if (glitch.fCoinSpan) {
caryclark8016b262016-09-06 05:59:47 -0700391 SkDebugf(" coinSeg/Span/PtT=%d/%d/%d", glitch.fCoinSpan->segment()->debugID(),
392 glitch.fCoinSpan->span()->debugID(), glitch.fCoinSpan->debugID());
caryclark55888e42016-07-18 10:01:36 -0700393 }
394 if (glitch.fEndSpan) {
395 SkDebugf(" endSpan=%d", glitch.fEndSpan->debugID());
396 }
397 if (glitch.fOppSpan) {
caryclark8016b262016-09-06 05:59:47 -0700398 SkDebugf(" oppSeg/Span/PtT=%d/%d/%d", glitch.fOppSpan->segment()->debugID(),
399 glitch.fOppSpan->span()->debugID(), glitch.fOppSpan->debugID());
caryclark55888e42016-07-18 10:01:36 -0700400 }
401 if (glitch.fOppEndSpan) {
402 SkDebugf(" oppEndSpan=%d", glitch.fOppEndSpan->debugID());
403 }
404 if (!SkScalarIsNaN(glitch.fStartT)) {
405 SkDebugf(" startT=%g", glitch.fStartT);
406 }
407 if (!SkScalarIsNaN(glitch.fEndT)) {
408 SkDebugf(" endT=%g", glitch.fEndT);
409 }
410 if (glitch.fOppSegment) {
411 SkDebugf(" segment=%d", glitch.fOppSegment->debugID());
412 }
413 if (!SkScalarIsNaN(glitch.fOppStartT)) {
414 SkDebugf(" oppStartT=%g", glitch.fOppStartT);
415 }
416 if (!SkScalarIsNaN(glitch.fOppEndT)) {
417 SkDebugf(" oppEndT=%g", glitch.fOppEndT);
418 }
419 if (!SkScalarIsNaN(glitch.fPt.fX) || !SkScalarIsNaN(glitch.fPt.fY)) {
420 SkDebugf(" pt=%g,%g", glitch.fPt.fX, glitch.fPt.fY);
421 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400422 DumpGlitchType(glitch.fType);
caryclark55888e42016-07-18 10:01:36 -0700423 SkDebugf("\n");
424 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400425#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -0700426 contourList->globalState()->debugSetCheckHealth(false);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400427#endif
caryclark6c3b9cd2016-09-26 05:36:58 -0700428#if 01 && DEBUG_ACTIVE_SPANS
Cary Clarkab87d7a2016-10-04 10:01:04 -0400429// SkDebugf("active after %s:\n", id);
caryclark55888e42016-07-18 10:01:36 -0700430 ShowActiveSpans(contourList);
431#endif
432#endif
caryclark26ad22a2015-10-16 09:03:38 -0700433}
434#endif
435
Cary Clarkab87d7a2016-10-04 10:01:04 -0400436#if DEBUG_COIN
437void SkPathOpsDebug::DumpGlitchType(GlitchType glitchType) {
438 switch (glitchType) {
439 case kAddCorruptCoin_Glitch: SkDebugf(" AddCorruptCoin"); break;
440 case kAddExpandedCoin_Glitch: SkDebugf(" AddExpandedCoin"); break;
441 case kAddExpandedFail_Glitch: SkDebugf(" AddExpandedFail"); break;
442 case kAddIfCollapsed_Glitch: SkDebugf(" AddIfCollapsed"); break;; break;
443 case kAddIfMissingCoin_Glitch: SkDebugf(" AddIfMissingCoin"); break;
444 case kAddMissingCoin_Glitch: SkDebugf(" AddMissingCoin"); break;
445 case kAddMissingExtend_Glitch: SkDebugf(" AddMissingExtend"); break;
446 case kAddOrOverlap_Glitch: SkDebugf(" AAddOrOverlap"); break;
447 case kCollapsedCoin_Glitch: SkDebugf(" CollapsedCoin"); break;
448 case kCollapsedDone_Glitch: SkDebugf(" CollapsedDone"); break;
449 case kCollapsedOppValue_Glitch: SkDebugf(" CollapsedOppValue"); break;
450 case kCollapsedSpan_Glitch: SkDebugf(" CollapsedSpan"); break;
451 case kCollapsedWindValue_Glitch: SkDebugf(" CollapsedWindValue"); break;
452 case kCorrectEnd_Glitch: SkDebugf(" CorrectEnd"); break;
453 case kDeletedCoin_Glitch: SkDebugf(" DeletedCoin"); break;
454 case kExpandCoin_Glitch: SkDebugf(" ExpandCoin"); break;
455 case kFail_Glitch: SkDebugf(" Fail"); break;
456 case kMarkCoinEnd_Glitch: SkDebugf(" MarkCoinEnd"); break;
457 case kMarkCoinInsert_Glitch: SkDebugf(" MarkCoinInsert"); break;
458 case kMarkCoinMissing_Glitch: SkDebugf(" MarkCoinMissing"); break;
459 case kMarkCoinStart_Glitch: SkDebugf(" MarkCoinStart"); break;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400460 case kMergeMatches_Glitch: SkDebugf(" MergeMatches"); break;
461 case kMissingCoin_Glitch: SkDebugf(" MissingCoin"); break;
462 case kMissingDone_Glitch: SkDebugf(" MissingDone"); break;
463 case kMissingIntersection_Glitch: SkDebugf(" MissingIntersection"); break;
464 case kMoveMultiple_Glitch: SkDebugf(" MoveMultiple"); break;
465 case kMoveNearbyClearAll_Glitch: SkDebugf(" MoveNearbyClearAll"); break;
466 case kMoveNearbyClearAll2_Glitch: SkDebugf(" MoveNearbyClearAll2"); break;
467 case kMoveNearbyMerge_Glitch: SkDebugf(" MoveNearbyMerge"); break;
468 case kMoveNearbyMergeFinal_Glitch: SkDebugf(" MoveNearbyMergeFinal"); break;
469 case kMoveNearbyRelease_Glitch: SkDebugf(" MoveNearbyRelease"); break;
470 case kMoveNearbyReleaseFinal_Glitch: SkDebugf(" MoveNearbyReleaseFinal"); break;
471 case kReleasedSpan_Glitch: SkDebugf(" ReleasedSpan"); break;
472 case kReturnFalse_Glitch: SkDebugf(" ReturnFalse"); break;
473 case kUnaligned_Glitch: SkDebugf(" Unaligned"); break;
474 case kUnalignedHead_Glitch: SkDebugf(" UnalignedHead"); break;
475 case kUnalignedTail_Glitch: SkDebugf(" UnalignedTail"); break;
476 case kUninitialized_Glitch: break;
477 default: SkASSERT(0);
478 }
479}
480#endif
481
caryclark26ad22a2015-10-16 09:03:38 -0700482#if defined SK_DEBUG || !FORCE_RELEASE
caryclark@google.com570863f2013-09-16 15:55:01 +0000483void SkPathOpsDebug::MathematicaIze(char* str, size_t bufferLen) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000484 size_t len = strlen(str);
485 bool num = false;
486 for (size_t idx = 0; idx < len; ++idx) {
487 if (num && str[idx] == 'e') {
488 if (len + 2 >= bufferLen) {
489 return;
490 }
491 memmove(&str[idx + 2], &str[idx + 1], len - idx);
492 str[idx] = '*';
493 str[idx + 1] = '^';
494 ++len;
495 }
496 num = str[idx] >= '0' && str[idx] <= '9';
497 }
498}
499
caryclark@google.com570863f2013-09-16 15:55:01 +0000500bool SkPathOpsDebug::ValidWind(int wind) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000501 return wind > SK_MinS32 + 0xFFFF && wind < SK_MaxS32 - 0xFFFF;
502}
503
caryclark@google.com570863f2013-09-16 15:55:01 +0000504void SkPathOpsDebug::WindingPrintf(int wind) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000505 if (wind == SK_MinS32) {
506 SkDebugf("?");
507 } else {
508 SkDebugf("%d", wind);
509 }
510}
caryclark54359292015-03-26 07:52:43 -0700511#endif // defined SK_DEBUG || !FORCE_RELEASE
512
caryclark@google.coma5e55922013-05-07 18:51:31 +0000513
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000514#if DEBUG_SHOW_TEST_NAME
halcanary385fe4d2015-08-26 13:07:48 -0700515void* SkPathOpsDebug::CreateNameStr() { return new char[DEBUG_FILENAME_STRING_LENGTH]; }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000516
halcanary385fe4d2015-08-26 13:07:48 -0700517void SkPathOpsDebug::DeleteNameStr(void* v) { delete[] reinterpret_cast<char*>(v); }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000518
caryclark@google.com570863f2013-09-16 15:55:01 +0000519void SkPathOpsDebug::BumpTestName(char* test) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000520 char* num = test + strlen(test);
521 while (num[-1] >= '0' && num[-1] <= '9') {
522 --num;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000523 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000524 if (num[0] == '\0') {
525 return;
526 }
527 int dec = atoi(num);
528 if (dec == 0) {
529 return;
530 }
531 ++dec;
532 SK_SNPRINTF(num, DEBUG_FILENAME_STRING_LENGTH - (num - test), "%d", dec);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000533}
534#endif
caryclark@google.com570863f2013-09-16 15:55:01 +0000535
caryclark1049f122015-04-20 08:31:59 -0700536static void show_function_header(const char* functionName) {
537 SkDebugf("\nstatic void %s(skiatest::Reporter* reporter, const char* filename) {\n", functionName);
538 if (strcmp("skphealth_com76", functionName) == 0) {
539 SkDebugf("found it\n");
540 }
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000541}
caryclark1049f122015-04-20 08:31:59 -0700542
543static const char* gOpStrs[] = {
544 "kDifference_SkPathOp",
545 "kIntersect_SkPathOp",
546 "kUnion_SkPathOp",
caryclark55888e42016-07-18 10:01:36 -0700547 "kXOR_PathOp",
caryclark1049f122015-04-20 08:31:59 -0700548 "kReverseDifference_SkPathOp",
549};
550
caryclark03b03ca2015-04-23 09:13:37 -0700551const char* SkPathOpsDebug::OpStr(SkPathOp op) {
552 return gOpStrs[op];
553}
554
caryclark1049f122015-04-20 08:31:59 -0700555static void show_op(SkPathOp op, const char* pathOne, const char* pathTwo) {
556 SkDebugf(" testPathOp(reporter, %s, %s, %s, filename);\n", pathOne, pathTwo, gOpStrs[op]);
557 SkDebugf("}\n");
558}
559
reed086eea92016-05-04 17:12:46 -0700560SK_DECLARE_STATIC_MUTEX(gTestMutex);
caryclark1049f122015-04-20 08:31:59 -0700561
562void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
563 const char* testName) {
564 SkAutoMutexAcquire ac(gTestMutex);
565 show_function_header(testName);
566 ShowOnePath(a, "path", true);
567 ShowOnePath(b, "pathB", true);
568 show_op(shapeOp, "path", "pathB");
569}
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000570
caryclark27c8eb82015-07-06 11:38:33 -0700571#include "SkPathOpsTypes.h"
caryclark26ad22a2015-10-16 09:03:38 -0700572#include "SkIntersectionHelper.h"
573#include "SkIntersections.h"
574
Cary Clarkab87d7a2016-10-04 10:01:04 -0400575#if DEBUG_COIN
576
577SK_DECLARE_STATIC_MUTEX(gCoinDictMutex);
578
579void SkOpGlobalState::debugAddToGlobalCoinDicts() {
580 SkAutoMutexAcquire ac(&gCoinDictMutex);
581 SkPathOpsDebug::gCoinSumChangedDict.add(fCoinChangedDict);
582 SkPathOpsDebug::gCoinSumVisitedDict.add(fCoinVisitedDict);
583}
584
585#endif
586
caryclark26ad22a2015-10-16 09:03:38 -0700587#if DEBUG_T_SECT_LOOP_COUNT
588void SkOpGlobalState::debugAddLoopCount(SkIntersections* i, const SkIntersectionHelper& wt,
589 const SkIntersectionHelper& wn) {
590 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
591 SkIntersections::DebugLoop looper = (SkIntersections::DebugLoop) index;
592 if (fDebugLoopCount[index] >= i->debugLoopCount(looper)) {
593 continue;
594 }
595 fDebugLoopCount[index] = i->debugLoopCount(looper);
596 fDebugWorstVerb[index * 2] = wt.segment()->verb();
597 fDebugWorstVerb[index * 2 + 1] = wn.segment()->verb();
598 sk_bzero(&fDebugWorstPts[index * 8], sizeof(SkPoint) * 8);
599 memcpy(&fDebugWorstPts[index * 2 * 4], wt.pts(),
600 (SkPathOpsVerbToPoints(wt.segment()->verb()) + 1) * sizeof(SkPoint));
601 memcpy(&fDebugWorstPts[(index * 2 + 1) * 4], wn.pts(),
602 (SkPathOpsVerbToPoints(wn.segment()->verb()) + 1) * sizeof(SkPoint));
603 fDebugWorstWeight[index * 2] = wt.weight();
604 fDebugWorstWeight[index * 2 + 1] = wn.weight();
605 }
606 i->debugResetLoopCount();
607}
608
609void SkOpGlobalState::debugDoYourWorst(SkOpGlobalState* local) {
610 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
611 if (fDebugLoopCount[index] >= local->fDebugLoopCount[index]) {
612 continue;
613 }
614 fDebugLoopCount[index] = local->fDebugLoopCount[index];
615 fDebugWorstVerb[index * 2] = local->fDebugWorstVerb[index * 2];
616 fDebugWorstVerb[index * 2 + 1] = local->fDebugWorstVerb[index * 2 + 1];
617 memcpy(&fDebugWorstPts[index * 2 * 4], &local->fDebugWorstPts[index * 2 * 4],
618 sizeof(SkPoint) * 8);
619 fDebugWorstWeight[index * 2] = local->fDebugWorstWeight[index * 2];
620 fDebugWorstWeight[index * 2 + 1] = local->fDebugWorstWeight[index * 2 + 1];
621 }
622 local->debugResetLoopCounts();
623}
624
625static void dump_curve(SkPath::Verb verb, const SkPoint& pts, float weight) {
626 if (!verb) {
627 return;
628 }
629 const char* verbs[] = { "", "line", "quad", "conic", "cubic" };
630 SkDebugf("%s: {{", verbs[verb]);
631 int ptCount = SkPathOpsVerbToPoints(verb);
632 for (int index = 0; index <= ptCount; ++index) {
633 SkDPoint::Dump((&pts)[index]);
634 if (index < ptCount - 1) {
635 SkDebugf(", ");
636 }
637 }
638 SkDebugf("}");
639 if (weight != 1) {
640 SkDebugf(", ");
641 if (weight == floorf(weight)) {
642 SkDebugf("%.0f", weight);
643 } else {
644 SkDebugf("%1.9gf", weight);
645 }
646 }
647 SkDebugf("}\n");
648}
649
650void SkOpGlobalState::debugLoopReport() {
651 const char* loops[] = { "iterations", "coinChecks", "perpCalcs" };
652 SkDebugf("\n");
653 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
654 SkDebugf("%s: %d\n", loops[index], fDebugLoopCount[index]);
655 dump_curve(fDebugWorstVerb[index * 2], fDebugWorstPts[index * 2 * 4],
656 fDebugWorstWeight[index * 2]);
657 dump_curve(fDebugWorstVerb[index * 2 + 1], fDebugWorstPts[(index * 2 + 1) * 4],
658 fDebugWorstWeight[index * 2 + 1]);
659 }
660}
661
662void SkOpGlobalState::debugResetLoopCounts() {
663 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
664 sk_bzero(fDebugWorstVerb, sizeof(fDebugWorstVerb));
665 sk_bzero(fDebugWorstPts, sizeof(fDebugWorstPts));
666 sk_bzero(fDebugWorstWeight, sizeof(fDebugWorstWeight));
667}
668#endif
caryclark27c8eb82015-07-06 11:38:33 -0700669
Cary Clark59d5a0e2017-01-23 14:38:52 +0000670bool SkOpGlobalState::DebugRunFail() {
671 return SkPathOpsDebug::gRunFail;
672}
673
Cary Clarkab87d7a2016-10-04 10:01:04 -0400674// this is const so it can be called by const methods that overwise don't alter state
675#if DEBUG_VALIDATE || DEBUG_COIN
676void SkOpGlobalState::debugSetPhase(const char* funcName DEBUG_COIN_DECLARE_PARAMS()) const {
677 auto writable = const_cast<SkOpGlobalState*>(this);
678#if DEBUG_VALIDATE
679 writable->setPhase(phase);
680#endif
681#if DEBUG_COIN
682 SkPathOpsDebug::CoinDictEntry* entry = &writable->fCoinDictEntry;
683 writable->fPreviousFuncName = entry->fFunctionName;
684 entry->fIteration = iteration;
685 entry->fLineNumber = lineNo;
686 entry->fGlitchType = SkPathOpsDebug::kUninitialized_Glitch;
687 entry->fFunctionName = funcName;
688 writable->fCoinVisitedDict.add(*entry);
689 writable->debugAddToCoinChangedDict();
690#endif
691}
692#endif
693
caryclark26ad22a2015-10-16 09:03:38 -0700694#if DEBUG_T_SECT_LOOP_COUNT
695void SkIntersections::debugBumpLoopCount(DebugLoop index) {
696 fDebugLoopCount[index]++;
697}
698
699int SkIntersections::debugLoopCount(DebugLoop index) const {
700 return fDebugLoopCount[index];
701}
702
703void SkIntersections::debugResetLoopCount() {
704 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
705}
706#endif
707
caryclarka35ab3e2016-10-20 08:32:18 -0700708#include "SkPathOpsConic.h"
caryclark624637c2015-05-11 07:21:27 -0700709#include "SkPathOpsCubic.h"
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000710
caryclark624637c2015-05-11 07:21:27 -0700711SkDCubic SkDQuad::debugToCubic() const {
712 SkDCubic cubic;
713 cubic[0] = fPts[0];
714 cubic[2] = fPts[1];
715 cubic[3] = fPts[2];
716 cubic[1].fX = (cubic[0].fX + cubic[2].fX * 2) / 3;
717 cubic[1].fY = (cubic[0].fY + cubic[2].fY * 2) / 3;
718 cubic[2].fX = (cubic[3].fX + cubic[2].fX * 2) / 3;
719 cubic[2].fY = (cubic[3].fY + cubic[2].fY * 2) / 3;
720 return cubic;
caryclark54359292015-03-26 07:52:43 -0700721}
caryclark624637c2015-05-11 07:21:27 -0700722
caryclarka35ab3e2016-10-20 08:32:18 -0700723void SkDQuad::debugSet(const SkDPoint* pts) {
724 memcpy(fPts, pts, sizeof(fPts));
725 SkDEBUGCODE(fDebugGlobalState = nullptr);
726}
727
728void SkDCubic::debugSet(const SkDPoint* pts) {
729 memcpy(fPts, pts, sizeof(fPts));
730 SkDEBUGCODE(fDebugGlobalState = nullptr);
731}
732
733void SkDConic::debugSet(const SkDPoint* pts, SkScalar weight) {
734 fPts.debugSet(pts);
735 fWeight = weight;
736}
737
caryclarked0935a2015-10-22 07:23:52 -0700738void SkDRect::debugInit() {
739 fLeft = fTop = fRight = fBottom = SK_ScalarNaN;
740}
741
caryclark624637c2015-05-11 07:21:27 -0700742#include "SkOpAngle.h"
caryclark624637c2015-05-11 07:21:27 -0700743#include "SkOpSegment.h"
caryclark54359292015-03-26 07:52:43 -0700744
Cary Clarkab87d7a2016-10-04 10:01:04 -0400745#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -0700746// commented-out lines keep this in sync with addT()
Cary Clarkab87d7a2016-10-04 10:01:04 -0400747 const SkOpPtT* SkOpSegment::debugAddT(double t, SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -0700748 debugValidate();
749 SkPoint pt = this->ptAtT(t);
caryclark26ad22a2015-10-16 09:03:38 -0700750 const SkOpSpanBase* span = &fHead;
caryclark55888e42016-07-18 10:01:36 -0700751 do {
752 const SkOpPtT* result = span->ptT();
caryclark29b25632016-08-25 11:27:17 -0700753 if (t == result->fT || this->match(result, this, t, pt)) {
caryclark55888e42016-07-18 10:01:36 -0700754// span->bumpSpanAdds();
caryclarkef4f32a2016-08-24 09:24:18 -0700755 return result;
caryclark26ad22a2015-10-16 09:03:38 -0700756 }
caryclark55888e42016-07-18 10:01:36 -0700757 if (t < result->fT) {
758 const SkOpSpan* prev = result->span()->prev();
caryclark30b9fdd2016-08-31 14:36:29 -0700759 FAIL_WITH_NULL_IF(!prev, span);
caryclark29b25632016-08-25 11:27:17 -0700760 // marks in global state that new op span has been allocated
761 this->globalState()->setAllocatedOpSpan();
caryclark55888e42016-07-18 10:01:36 -0700762// span->init(this, prev, t, pt);
763 this->debugValidate();
764// #if DEBUG_ADD_T
765// SkDebugf("%s insert t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
766// span->segment()->debugID(), span->debugID());
767// #endif
768// span->bumpSpanAdds();
caryclark55888e42016-07-18 10:01:36 -0700769 return nullptr;
caryclark26ad22a2015-10-16 09:03:38 -0700770 }
caryclark30b9fdd2016-08-31 14:36:29 -0700771 FAIL_WITH_NULL_IF(span != &fTail, span);
caryclark55888e42016-07-18 10:01:36 -0700772 } while ((span = span->upCast()->next()));
773 SkASSERT(0);
caryclark29b25632016-08-25 11:27:17 -0700774 return nullptr; // we never get here, but need this to satisfy compiler
caryclark26ad22a2015-10-16 09:03:38 -0700775}
776#endif
777
778#if DEBUG_ANGLE
779void SkOpSegment::debugCheckAngleCoin() const {
780 const SkOpSpanBase* base = &fHead;
781 const SkOpSpan* span;
782 do {
783 const SkOpAngle* angle = base->fromAngle();
caryclark55888e42016-07-18 10:01:36 -0700784 if (angle && angle->debugCheckCoincidence()) {
caryclark26ad22a2015-10-16 09:03:38 -0700785 angle->debugCheckNearCoincidence();
786 }
787 if (base->final()) {
788 break;
789 }
790 span = base->upCast();
791 angle = span->toAngle();
caryclark55888e42016-07-18 10:01:36 -0700792 if (angle && angle->debugCheckCoincidence()) {
caryclark26ad22a2015-10-16 09:03:38 -0700793 angle->debugCheckNearCoincidence();
794 }
795 } while ((base = span->next()));
796}
797#endif
798
Cary Clarkab87d7a2016-10-04 10:01:04 -0400799#if DEBUG_COIN
caryclark26ad22a2015-10-16 09:03:38 -0700800// this mimics the order of the checks in handle coincidence
Cary Clarkab87d7a2016-10-04 10:01:04 -0400801void SkOpSegment::debugCheckHealth(SkPathOpsDebug::GlitchLog* glitches) const {
802 debugMoveMultiples(glitches);
803 debugMoveNearby(glitches);
804 debugMissingCoincidence(glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700805}
806
caryclark55888e42016-07-18 10:01:36 -0700807// commented-out lines keep this in sync with clearAll()
Cary Clarkab87d7a2016-10-04 10:01:04 -0400808void SkOpSegment::debugClearAll(SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -0700809 const SkOpSpan* span = &fHead;
810 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400811 this->debugClearOne(span, glitches);
caryclark55888e42016-07-18 10:01:36 -0700812 } while ((span = span->next()->upCastable()));
Cary Clarkab87d7a2016-10-04 10:01:04 -0400813 this->globalState()->coincidence()->debugRelease(glitches, this);
caryclark55888e42016-07-18 10:01:36 -0700814}
815
816// commented-out lines keep this in sync with clearOne()
Cary Clarkab87d7a2016-10-04 10:01:04 -0400817void SkOpSegment::debugClearOne(const SkOpSpan* span, SkPathOpsDebug::GlitchLog* glitches) const {
818 if (span->windValue()) glitches->record(SkPathOpsDebug::kCollapsedWindValue_Glitch, span);
819 if (span->oppValue()) glitches->record(SkPathOpsDebug::kCollapsedOppValue_Glitch, span);
820 if (!span->done()) glitches->record(SkPathOpsDebug::kCollapsedDone_Glitch, span);
caryclark26ad22a2015-10-16 09:03:38 -0700821}
822#endif
823
caryclark54359292015-03-26 07:52:43 -0700824SkOpAngle* SkOpSegment::debugLastAngle() {
halcanary96fcdcc2015-08-27 07:41:13 -0700825 SkOpAngle* result = nullptr;
caryclark54359292015-03-26 07:52:43 -0700826 SkOpSpan* span = this->head();
827 do {
828 if (span->toAngle()) {
829 SkASSERT(!result);
830 result = span->toAngle();
831 }
832 } while ((span = span->next()->upCastable()));
833 SkASSERT(result);
834 return result;
835}
836
Cary Clarkab87d7a2016-10-04 10:01:04 -0400837#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -0700838// commented-out lines keep this in sync with ClearVisited
839void SkOpSegment::DebugClearVisited(const SkOpSpanBase* span) {
840 // reset visited flag back to false
841 do {
842 const SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
843 while ((ptT = ptT->next()) != stopPtT) {
844 const SkOpSegment* opp = ptT->segment();
845 opp->resetDebugVisited();
846 }
847 } while (!span->final() && (span = span->upCast()->next()));
848}
849#endif
850
Cary Clarkab87d7a2016-10-04 10:01:04 -0400851#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -0700852// commented-out lines keep this in sync with missingCoincidence()
853// look for pairs of undetected coincident curves
854// assumes that segments going in have visited flag clear
855// Even though pairs of curves correct detect coincident runs, a run may be missed
856// if the coincidence is a product of multiple intersections. For instance, given
857// curves A, B, and C:
858// A-B intersect at a point 1; A-C and B-C intersect at point 2, so near
859// the end of C that the intersection is replaced with the end of C.
860// Even though A-B correctly do not detect an intersection at point 2,
861// the resulting run from point 1 to point 2 is coincident on A and B.
Cary Clarkab87d7a2016-10-04 10:01:04 -0400862void SkOpSegment::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -0700863 if (this->done()) {
864 return;
865 }
866 const SkOpSpan* prior = nullptr;
867 const SkOpSpanBase* spanBase = &fHead;
caryclark55888e42016-07-18 10:01:36 -0700868// bool result = false;
caryclark26ad22a2015-10-16 09:03:38 -0700869 do {
870 const SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT;
871 SkASSERT(ptT->span() == spanBase);
872 while ((ptT = ptT->next()) != spanStopPtT) {
873 if (ptT->deleted()) {
874 continue;
875 }
caryclark55888e42016-07-18 10:01:36 -0700876 const SkOpSegment* opp = ptT->span()->segment();
caryclark26ad22a2015-10-16 09:03:38 -0700877 if (opp->done()) {
878 continue;
879 }
880 // when opp is encounted the 1st time, continue; on 2nd encounter, look for coincidence
caryclark55888e42016-07-18 10:01:36 -0700881 if (!opp->debugVisited()) {
caryclark26ad22a2015-10-16 09:03:38 -0700882 continue;
883 }
884 if (spanBase == &fHead) {
885 continue;
886 }
caryclark55888e42016-07-18 10:01:36 -0700887 if (ptT->segment() == this) {
888 continue;
889 }
caryclark26ad22a2015-10-16 09:03:38 -0700890 const SkOpSpan* span = spanBase->upCastable();
891 // FIXME?: this assumes that if the opposite segment is coincident then no more
892 // coincidence needs to be detected. This may not be true.
caryclark55888e42016-07-18 10:01:36 -0700893 if (span && span->segment() != opp && span->containsCoincidence(opp)) { // debug has additional condition since it may be called before inner duplicate points have been deleted
caryclark26ad22a2015-10-16 09:03:38 -0700894 continue;
895 }
caryclark55888e42016-07-18 10:01:36 -0700896 if (spanBase->segment() != opp && spanBase->containsCoinEnd(opp)) { // debug has additional condition since it may be called before inner duplicate points have been deleted
caryclark26ad22a2015-10-16 09:03:38 -0700897 continue;
caryclark55888e42016-07-18 10:01:36 -0700898 }
caryclark26ad22a2015-10-16 09:03:38 -0700899 const SkOpPtT* priorPtT = nullptr, * priorStopPtT;
900 // find prior span containing opp segment
901 const SkOpSegment* priorOpp = nullptr;
902 const SkOpSpan* priorTest = spanBase->prev();
903 while (!priorOpp && priorTest) {
904 priorStopPtT = priorPtT = priorTest->ptT();
905 while ((priorPtT = priorPtT->next()) != priorStopPtT) {
906 if (priorPtT->deleted()) {
907 continue;
908 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400909 const SkOpSegment* segment = priorPtT->span()->segment();
caryclark26ad22a2015-10-16 09:03:38 -0700910 if (segment == opp) {
911 prior = priorTest;
912 priorOpp = opp;
913 break;
914 }
915 }
916 priorTest = priorTest->prev();
917 }
918 if (!priorOpp) {
919 continue;
920 }
caryclark55888e42016-07-18 10:01:36 -0700921 if (priorPtT == ptT) {
922 continue;
923 }
caryclark26ad22a2015-10-16 09:03:38 -0700924 const SkOpPtT* oppStart = prior->ptT();
925 const SkOpPtT* oppEnd = spanBase->ptT();
926 bool swapped = priorPtT->fT > ptT->fT;
927 if (swapped) {
928 SkTSwap(priorPtT, ptT);
929 SkTSwap(oppStart, oppEnd);
930 }
caryclark55888e42016-07-18 10:01:36 -0700931 const SkOpCoincidence* coincidence = this->globalState()->coincidence();
932 const SkOpPtT* rootPriorPtT = priorPtT->span()->ptT();
933 const SkOpPtT* rootPtT = ptT->span()->ptT();
934 const SkOpPtT* rootOppStart = oppStart->span()->ptT();
935 const SkOpPtT* rootOppEnd = oppEnd->span()->ptT();
936 if (coincidence->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) {
caryclark26ad22a2015-10-16 09:03:38 -0700937 goto swapBack;
938 }
caryclark55888e42016-07-18 10:01:36 -0700939 if (testForCoincidence(rootPriorPtT, rootPtT, prior, spanBase, opp)) {
940 // mark coincidence
caryclark30b9fdd2016-08-31 14:36:29 -0700941#if DEBUG_COINCIDENCE_VERBOSE
caryclark55888e42016-07-18 10:01:36 -0700942// SkDebugf("%s coinSpan=%d endSpan=%d oppSpan=%d oppEndSpan=%d\n", __FUNCTION__,
943// rootPriorPtT->debugID(), rootPtT->debugID(), rootOppStart->debugID(),
944// rootOppEnd->debugID());
945#endif
Cary Clarkab87d7a2016-10-04 10:01:04 -0400946 log->record(SkPathOpsDebug::kMissingCoin_Glitch, priorPtT, ptT, oppStart, oppEnd);
caryclark55888e42016-07-18 10:01:36 -0700947 // coincidences->add(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
948 // }
949#if DEBUG_COINCIDENCE
caryclarkd6562002016-07-27 12:02:07 -0700950// SkASSERT(coincidences->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
caryclark55888e42016-07-18 10:01:36 -0700951#endif
952 // result = true;
caryclark26ad22a2015-10-16 09:03:38 -0700953 }
954 swapBack:
955 if (swapped) {
956 SkTSwap(priorPtT, ptT);
957 }
958 }
959 } while ((spanBase = spanBase->final() ? nullptr : spanBase->upCast()->next()));
caryclark55888e42016-07-18 10:01:36 -0700960 DebugClearVisited(&fHead);
961 return;
caryclark26ad22a2015-10-16 09:03:38 -0700962}
963
caryclark55888e42016-07-18 10:01:36 -0700964// commented-out lines keep this in sync with moveMultiples()
965// if a span has more than one intersection, merge the other segments' span as needed
Cary Clarkab87d7a2016-10-04 10:01:04 -0400966void SkOpSegment::debugMoveMultiples(SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -0700967 debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -0700968 const SkOpSpanBase* test = &fHead;
969 do {
970 int addCount = test->spanAddsCount();
Cary Clarkff114282016-12-14 11:56:16 -0500971// SkASSERT(addCount >= 1);
972 if (addCount <= 1) {
caryclark26ad22a2015-10-16 09:03:38 -0700973 continue;
974 }
975 const SkOpPtT* startPtT = test->ptT();
976 const SkOpPtT* testPtT = startPtT;
977 do { // iterate through all spans associated with start
978 const SkOpSpanBase* oppSpan = testPtT->span();
979 if (oppSpan->spanAddsCount() == addCount) {
980 continue;
981 }
982 if (oppSpan->deleted()) {
983 continue;
984 }
985 const SkOpSegment* oppSegment = oppSpan->segment();
986 if (oppSegment == this) {
987 continue;
988 }
989 // find range of spans to consider merging
990 const SkOpSpanBase* oppPrev = oppSpan;
991 const SkOpSpanBase* oppFirst = oppSpan;
992 while ((oppPrev = oppPrev->prev())) {
993 if (!roughly_equal(oppPrev->t(), oppSpan->t())) {
994 break;
995 }
996 if (oppPrev->spanAddsCount() == addCount) {
997 continue;
998 }
999 if (oppPrev->deleted()) {
1000 continue;
1001 }
1002 oppFirst = oppPrev;
1003 }
1004 const SkOpSpanBase* oppNext = oppSpan;
1005 const SkOpSpanBase* oppLast = oppSpan;
1006 while ((oppNext = oppNext->final() ? nullptr : oppNext->upCast()->next())) {
1007 if (!roughly_equal(oppNext->t(), oppSpan->t())) {
1008 break;
1009 }
1010 if (oppNext->spanAddsCount() == addCount) {
1011 continue;
1012 }
1013 if (oppNext->deleted()) {
1014 continue;
1015 }
1016 oppLast = oppNext;
1017 }
1018 if (oppFirst == oppLast) {
1019 continue;
1020 }
1021 const SkOpSpanBase* oppTest = oppFirst;
1022 do {
1023 if (oppTest == oppSpan) {
1024 continue;
1025 }
1026 // check to see if the candidate meets specific criteria:
1027 // it contains spans of segments in test's loop but not including 'this'
1028 const SkOpPtT* oppStartPtT = oppTest->ptT();
1029 const SkOpPtT* oppPtT = oppStartPtT;
1030 while ((oppPtT = oppPtT->next()) != oppStartPtT) {
1031 const SkOpSegment* oppPtTSegment = oppPtT->segment();
1032 if (oppPtTSegment == this) {
1033 goto tryNextSpan;
1034 }
1035 const SkOpPtT* matchPtT = startPtT;
1036 do {
1037 if (matchPtT->segment() == oppPtTSegment) {
1038 goto foundMatch;
1039 }
1040 } while ((matchPtT = matchPtT->next()) != startPtT);
1041 goto tryNextSpan;
1042 foundMatch: // merge oppTest and oppSpan
caryclark55888e42016-07-18 10:01:36 -07001043 oppSegment->debugValidate();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001044 oppTest->debugMergeMatches(glitches, oppSpan);
1045 oppTest->debugAddOpp(glitches, oppSpan);
caryclark55888e42016-07-18 10:01:36 -07001046 oppSegment->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001047 goto checkNextSpan;
1048 }
caryclark55888e42016-07-18 10:01:36 -07001049 tryNextSpan:
caryclark26ad22a2015-10-16 09:03:38 -07001050 ;
1051 } while (oppTest != oppLast && (oppTest = oppTest->upCast()->next()));
1052 } while ((testPtT = testPtT->next()) != startPtT);
caryclark55888e42016-07-18 10:01:36 -07001053checkNextSpan:
caryclark26ad22a2015-10-16 09:03:38 -07001054 ;
1055 } while ((test = test->final() ? nullptr : test->upCast()->next()));
caryclark55888e42016-07-18 10:01:36 -07001056 debugValidate();
1057 return;
caryclark26ad22a2015-10-16 09:03:38 -07001058}
1059
caryclark55888e42016-07-18 10:01:36 -07001060// commented-out lines keep this in sync with moveNearby()
1061// Move nearby t values and pts so they all hang off the same span. Alignment happens later.
Cary Clarkab87d7a2016-10-04 10:01:04 -04001062void SkOpSegment::debugMoveNearby(SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -07001063 debugValidate();
1064 // release undeleted spans pointing to this seg that are linked to the primary span
1065 const SkOpSpanBase* spanBase = &fHead;
caryclark26ad22a2015-10-16 09:03:38 -07001066 do {
caryclark55888e42016-07-18 10:01:36 -07001067 const SkOpPtT* ptT = spanBase->ptT();
1068 const SkOpPtT* headPtT = ptT;
1069 while ((ptT = ptT->next()) != headPtT) {
1070 const SkOpSpanBase* test = ptT->span();
1071 if (ptT->segment() == this && !ptT->deleted() && test != spanBase
1072 && test->ptT() == ptT) {
1073 if (test->final()) {
1074 if (spanBase == &fHead) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001075 glitches->record(SkPathOpsDebug::kMoveNearbyClearAll_Glitch, this);
caryclark55888e42016-07-18 10:01:36 -07001076// return;
1077 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001078 glitches->record(SkPathOpsDebug::kMoveNearbyReleaseFinal_Glitch, spanBase, ptT);
caryclark55888e42016-07-18 10:01:36 -07001079 } else if (test->prev()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001080 glitches->record(SkPathOpsDebug::kMoveNearbyRelease_Glitch, test, headPtT);
caryclark55888e42016-07-18 10:01:36 -07001081 }
1082// break;
caryclark26ad22a2015-10-16 09:03:38 -07001083 }
1084 }
caryclark55888e42016-07-18 10:01:36 -07001085 spanBase = spanBase->upCast()->next();
1086 } while (!spanBase->final());
1087
1088 // This loop looks for adjacent spans which are near by
1089 spanBase = &fHead;
1090 do { // iterate through all spans associated with start
1091 const SkOpSpanBase* test = spanBase->upCast()->next();
1092 if (this->spansNearby(spanBase, test)) {
1093 if (test->final()) {
1094 if (spanBase->prev()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001095 glitches->record(SkPathOpsDebug::kMoveNearbyMergeFinal_Glitch, test);
caryclark55888e42016-07-18 10:01:36 -07001096 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001097 glitches->record(SkPathOpsDebug::kMoveNearbyClearAll2_Glitch, this);
caryclark55888e42016-07-18 10:01:36 -07001098 // return
1099 }
1100 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001101 glitches->record(SkPathOpsDebug::kMoveNearbyMerge_Glitch, spanBase);
caryclark55888e42016-07-18 10:01:36 -07001102 }
1103 }
1104 spanBase = test;
1105 } while (!spanBase->final());
1106 debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001107}
1108#endif
1109
caryclark54359292015-03-26 07:52:43 -07001110void SkOpSegment::debugReset() {
caryclark1049f122015-04-20 08:31:59 -07001111 this->init(this->fPts, this->fWeight, this->contour(), this->verb());
caryclark54359292015-03-26 07:52:43 -07001112}
1113
caryclark025b11e2016-08-25 05:21:14 -07001114#if DEBUG_COINCIDENCE_ORDER
1115void SkOpSegment::debugSetCoinT(int index, SkScalar t) const {
1116 if (fDebugBaseMax < 0 || fDebugBaseIndex == index) {
1117 fDebugBaseIndex = index;
1118 fDebugBaseMin = SkTMin(t, fDebugBaseMin);
1119 fDebugBaseMax = SkTMax(t, fDebugBaseMax);
1120 return;
1121 }
1122 SkASSERT(fDebugBaseMin >= t || t >= fDebugBaseMax);
1123 if (fDebugLastMax < 0 || fDebugLastIndex == index) {
1124 fDebugLastIndex = index;
1125 fDebugLastMin = SkTMin(t, fDebugLastMin);
1126 fDebugLastMax = SkTMax(t, fDebugLastMax);
1127 return;
1128 }
1129 SkASSERT(fDebugLastMin >= t || t >= fDebugLastMax);
1130 SkASSERT((t - fDebugBaseMin > 0) == (fDebugLastMin - fDebugBaseMin > 0));
1131}
1132#endif
1133
caryclark54359292015-03-26 07:52:43 -07001134#if DEBUG_ACTIVE_SPANS
Cary Clarkff114282016-12-14 11:56:16 -05001135void SkOpSegment::debugShowActiveSpans(SkString* str) const {
caryclark54359292015-03-26 07:52:43 -07001136 debugValidate();
1137 if (done()) {
1138 return;
1139 }
1140 int lastId = -1;
1141 double lastT = -1;
1142 const SkOpSpan* span = &fHead;
1143 do {
1144 if (span->done()) {
1145 continue;
1146 }
caryclark1049f122015-04-20 08:31:59 -07001147 if (lastId == this->debugID() && lastT == span->t()) {
caryclark54359292015-03-26 07:52:43 -07001148 continue;
1149 }
caryclark1049f122015-04-20 08:31:59 -07001150 lastId = this->debugID();
caryclark54359292015-03-26 07:52:43 -07001151 lastT = span->t();
Cary Clarkff114282016-12-14 11:56:16 -05001152 str->appendf("%s id=%d", __FUNCTION__, this->debugID());
caryclark55888e42016-07-18 10:01:36 -07001153 // since endpoints may have be adjusted, show actual computed curves
1154 SkDCurve curvePart;
1155 this->subDivide(span, span->next(), &curvePart);
1156 const SkDPoint* pts = curvePart.fCubic.fPts;
Cary Clarkff114282016-12-14 11:56:16 -05001157 str->appendf(" (%1.9g,%1.9g", pts[0].fX, pts[0].fY);
caryclark54359292015-03-26 07:52:43 -07001158 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
Cary Clarkff114282016-12-14 11:56:16 -05001159 str->appendf(" %1.9g,%1.9g", pts[vIndex].fX, pts[vIndex].fY);
caryclark54359292015-03-26 07:52:43 -07001160 }
caryclark1049f122015-04-20 08:31:59 -07001161 if (SkPath::kConic_Verb == fVerb) {
Cary Clarkff114282016-12-14 11:56:16 -05001162 str->appendf(" %1.9gf", curvePart.fConic.fWeight);
caryclark1049f122015-04-20 08:31:59 -07001163 }
Cary Clarkff114282016-12-14 11:56:16 -05001164 str->appendf(") t=%1.9g tEnd=%1.9g", span->t(), span->next()->t());
caryclark54359292015-03-26 07:52:43 -07001165 if (span->windSum() == SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001166 str->appendf(" windSum=?");
caryclark54359292015-03-26 07:52:43 -07001167 } else {
Cary Clarkff114282016-12-14 11:56:16 -05001168 str->appendf(" windSum=%d", span->windSum());
caryclark54359292015-03-26 07:52:43 -07001169 }
caryclark624637c2015-05-11 07:21:27 -07001170 if (span->oppValue() && span->oppSum() == SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001171 str->appendf(" oppSum=?");
caryclark624637c2015-05-11 07:21:27 -07001172 } else if (span->oppValue() || span->oppSum() != SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001173 str->appendf(" oppSum=%d", span->oppSum());
caryclark624637c2015-05-11 07:21:27 -07001174 }
Cary Clarkff114282016-12-14 11:56:16 -05001175 str->appendf(" windValue=%d", span->windValue());
caryclark624637c2015-05-11 07:21:27 -07001176 if (span->oppValue() || span->oppSum() != SK_MinS32) {
Cary Clarkff114282016-12-14 11:56:16 -05001177 str->appendf(" oppValue=%d", span->oppValue());
caryclark624637c2015-05-11 07:21:27 -07001178 }
Cary Clarkff114282016-12-14 11:56:16 -05001179 str->appendf("\n");
caryclark54359292015-03-26 07:52:43 -07001180 } while ((span = span->next()->upCastable()));
1181}
1182#endif
1183
1184#if DEBUG_MARK_DONE
1185void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding) {
1186 const SkPoint& pt = span->ptT()->fPt;
caryclark1049f122015-04-20 08:31:59 -07001187 SkDebugf("%s id=%d", fun, this->debugID());
caryclark54359292015-03-26 07:52:43 -07001188 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1189 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1190 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1191 }
1192 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
1193 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t());
1194 if (winding == SK_MinS32) {
1195 SkDebugf("?");
1196 } else {
1197 SkDebugf("%d", winding);
1198 }
1199 SkDebugf(" windSum=");
1200 if (span->windSum() == SK_MinS32) {
1201 SkDebugf("?");
1202 } else {
1203 SkDebugf("%d", span->windSum());
1204 }
1205 SkDebugf(" windValue=%d\n", span->windValue());
1206}
1207
1208void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding,
1209 int oppWinding) {
1210 const SkPoint& pt = span->ptT()->fPt;
caryclark1049f122015-04-20 08:31:59 -07001211 SkDebugf("%s id=%d", fun, this->debugID());
caryclark54359292015-03-26 07:52:43 -07001212 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1213 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1214 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1215 }
1216 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
1217 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t(), winding, oppWinding);
1218 if (winding == SK_MinS32) {
1219 SkDebugf("?");
1220 } else {
1221 SkDebugf("%d", winding);
1222 }
1223 SkDebugf(" newOppSum=");
1224 if (oppWinding == SK_MinS32) {
1225 SkDebugf("?");
1226 } else {
1227 SkDebugf("%d", oppWinding);
1228 }
1229 SkDebugf(" oppSum=");
1230 if (span->oppSum() == SK_MinS32) {
1231 SkDebugf("?");
1232 } else {
1233 SkDebugf("%d", span->oppSum());
1234 }
1235 SkDebugf(" windSum=");
1236 if (span->windSum() == SK_MinS32) {
1237 SkDebugf("?");
1238 } else {
1239 SkDebugf("%d", span->windSum());
1240 }
1241 SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue());
1242}
1243
1244#endif
1245
caryclark26ad22a2015-10-16 09:03:38 -07001246// loop looking for a pair of angle parts that are too close to be sorted
1247/* This is called after other more simple intersection and angle sorting tests have been exhausted.
1248 This should be rarely called -- the test below is thorough and time consuming.
caryclark55888e42016-07-18 10:01:36 -07001249 This checks the distance between start points; the distance between
caryclark26ad22a2015-10-16 09:03:38 -07001250*/
1251#if DEBUG_ANGLE
1252void SkOpAngle::debugCheckNearCoincidence() const {
1253 const SkOpAngle* test = this;
1254 do {
1255 const SkOpSegment* testSegment = test->segment();
1256 double testStartT = test->start()->t();
1257 SkDPoint testStartPt = testSegment->dPtAtT(testStartT);
1258 double testEndT = test->end()->t();
1259 SkDPoint testEndPt = testSegment->dPtAtT(testEndT);
1260 double testLenSq = testStartPt.distanceSquared(testEndPt);
1261 SkDebugf("%s testLenSq=%1.9g id=%d\n", __FUNCTION__, testLenSq, testSegment->debugID());
1262 double testMidT = (testStartT + testEndT) / 2;
1263 const SkOpAngle* next = test;
1264 while ((next = next->fNext) != this) {
1265 SkOpSegment* nextSegment = next->segment();
1266 double testMidDistSq = testSegment->distSq(testMidT, next);
1267 double testEndDistSq = testSegment->distSq(testEndT, next);
1268 double nextStartT = next->start()->t();
1269 SkDPoint nextStartPt = nextSegment->dPtAtT(nextStartT);
1270 double distSq = testStartPt.distanceSquared(nextStartPt);
1271 double nextEndT = next->end()->t();
1272 double nextMidT = (nextStartT + nextEndT) / 2;
1273 double nextMidDistSq = nextSegment->distSq(nextMidT, test);
1274 double nextEndDistSq = nextSegment->distSq(nextEndT, test);
1275 SkDebugf("%s distSq=%1.9g testId=%d nextId=%d\n", __FUNCTION__, distSq,
1276 testSegment->debugID(), nextSegment->debugID());
1277 SkDebugf("%s testMidDistSq=%1.9g\n", __FUNCTION__, testMidDistSq);
1278 SkDebugf("%s testEndDistSq=%1.9g\n", __FUNCTION__, testEndDistSq);
1279 SkDebugf("%s nextMidDistSq=%1.9g\n", __FUNCTION__, nextMidDistSq);
1280 SkDebugf("%s nextEndDistSq=%1.9g\n", __FUNCTION__, nextEndDistSq);
1281 SkDPoint nextEndPt = nextSegment->dPtAtT(nextEndT);
1282 double nextLenSq = nextStartPt.distanceSquared(nextEndPt);
1283 SkDebugf("%s nextLenSq=%1.9g\n", __FUNCTION__, nextLenSq);
1284 SkDebugf("\n");
1285 }
1286 test = test->fNext;
caryclark55888e42016-07-18 10:01:36 -07001287 } while (test->fNext != this);
caryclark26ad22a2015-10-16 09:03:38 -07001288}
1289#endif
1290
caryclark54359292015-03-26 07:52:43 -07001291#if DEBUG_ANGLE
1292SkString SkOpAngle::debugPart() const {
1293 SkString result;
1294 switch (this->segment()->verb()) {
1295 case SkPath::kLine_Verb:
caryclarkeed356d2016-09-14 07:18:20 -07001296 result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fPart.fCurve),
caryclark54359292015-03-26 07:52:43 -07001297 this->segment()->debugID());
1298 break;
1299 case SkPath::kQuad_Verb:
caryclarkeed356d2016-09-14 07:18:20 -07001300 result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fPart.fCurve),
caryclark54359292015-03-26 07:52:43 -07001301 this->segment()->debugID());
1302 break;
caryclark1049f122015-04-20 08:31:59 -07001303 case SkPath::kConic_Verb:
1304 result.printf(CONIC_DEBUG_STR " id=%d",
caryclarkeed356d2016-09-14 07:18:20 -07001305 CONIC_DEBUG_DATA(fPart.fCurve, fPart.fCurve.fConic.fWeight),
caryclark1049f122015-04-20 08:31:59 -07001306 this->segment()->debugID());
1307 break;
caryclark54359292015-03-26 07:52:43 -07001308 case SkPath::kCubic_Verb:
caryclarkeed356d2016-09-14 07:18:20 -07001309 result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fPart.fCurve),
caryclark54359292015-03-26 07:52:43 -07001310 this->segment()->debugID());
1311 break;
1312 default:
1313 SkASSERT(0);
mtklein1b249332015-07-07 12:21:21 -07001314 }
caryclark54359292015-03-26 07:52:43 -07001315 return result;
1316}
1317#endif
1318
caryclark624637c2015-05-11 07:21:27 -07001319#if DEBUG_SORT
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001320void SkOpAngle::debugLoop() const {
caryclarke4097e32014-06-18 07:24:19 -07001321 const SkOpAngle* first = this;
1322 const SkOpAngle* next = this;
1323 do {
1324 next->dumpOne(true);
caryclark19eb3b22014-07-18 05:08:14 -07001325 SkDebugf("\n");
caryclarke4097e32014-06-18 07:24:19 -07001326 next = next->fNext;
1327 } while (next && next != first);
caryclark54359292015-03-26 07:52:43 -07001328 next = first;
1329 do {
1330 next->debugValidate();
1331 next = next->fNext;
1332 } while (next && next != first);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001333}
1334#endif
1335
caryclark54359292015-03-26 07:52:43 -07001336void SkOpAngle::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07001337#if DEBUG_COINCIDENCE
1338 if (this->globalState()->debugCheckHealth()) {
1339 return;
1340 }
1341#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001342#if DEBUG_VALIDATE
caryclark54359292015-03-26 07:52:43 -07001343 const SkOpAngle* first = this;
1344 const SkOpAngle* next = this;
1345 int wind = 0;
1346 int opp = 0;
1347 int lastXor = -1;
1348 int lastOppXor = -1;
1349 do {
1350 if (next->unorderable()) {
1351 return;
1352 }
1353 const SkOpSpan* minSpan = next->start()->starter(next->end());
1354 if (minSpan->windValue() == SK_MinS32) {
1355 return;
1356 }
1357 bool op = next->segment()->operand();
1358 bool isXor = next->segment()->isXor();
1359 bool oppXor = next->segment()->oppXor();
1360 SkASSERT(!DEBUG_LIMIT_WIND_SUM || between(0, minSpan->windValue(), DEBUG_LIMIT_WIND_SUM));
1361 SkASSERT(!DEBUG_LIMIT_WIND_SUM
1362 || between(-DEBUG_LIMIT_WIND_SUM, minSpan->oppValue(), DEBUG_LIMIT_WIND_SUM));
1363 bool useXor = op ? oppXor : isXor;
1364 SkASSERT(lastXor == -1 || lastXor == (int) useXor);
1365 lastXor = (int) useXor;
caryclark624637c2015-05-11 07:21:27 -07001366 wind += next->debugSign() * (op ? minSpan->oppValue() : minSpan->windValue());
caryclark54359292015-03-26 07:52:43 -07001367 if (useXor) {
1368 wind &= 1;
1369 }
1370 useXor = op ? isXor : oppXor;
1371 SkASSERT(lastOppXor == -1 || lastOppXor == (int) useXor);
1372 lastOppXor = (int) useXor;
caryclark624637c2015-05-11 07:21:27 -07001373 opp += next->debugSign() * (op ? minSpan->windValue() : minSpan->oppValue());
caryclark54359292015-03-26 07:52:43 -07001374 if (useXor) {
1375 opp &= 1;
1376 }
1377 next = next->fNext;
1378 } while (next && next != first);
Cary Clark59d5a0e2017-01-23 14:38:52 +00001379 SkASSERT(wind == 0 || !SkPathOpsDebug::gRunFail);
1380 SkASSERT(opp == 0 || !SkPathOpsDebug::gRunFail);
caryclark54359292015-03-26 07:52:43 -07001381#endif
1382}
1383
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001384void SkOpAngle::debugValidateNext() const {
caryclark54359292015-03-26 07:52:43 -07001385#if !FORCE_RELEASE
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001386 const SkOpAngle* first = this;
1387 const SkOpAngle* next = first;
1388 SkTDArray<const SkOpAngle*>(angles);
1389 do {
djsollenf2b340f2016-01-29 08:51:04 -08001390// SkASSERT_RELEASE(next->fSegment->debugContains(next));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001391 angles.push(next);
1392 next = next->next();
1393 if (next == first) {
1394 break;
1395 }
djsollenf2b340f2016-01-29 08:51:04 -08001396 SkASSERT_RELEASE(!angles.contains(next));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001397 if (!next) {
1398 return;
1399 }
1400 } while (true);
reed0dc4dd62015-03-24 13:55:33 -07001401#endif
reed0dc4dd62015-03-24 13:55:33 -07001402}
reed0dc4dd62015-03-24 13:55:33 -07001403
caryclark55888e42016-07-18 10:01:36 -07001404#ifdef SK_DEBUG
1405void SkCoincidentSpans::debugStartCheck(const SkOpSpanBase* outer, const SkOpSpanBase* over,
1406 const SkOpGlobalState* debugState) const {
Cary Clark59d5a0e2017-01-23 14:38:52 +00001407 SkASSERT(coinPtTEnd()->span() == over || !SkOpGlobalState::DebugRunFail());
1408 SkASSERT(oppPtTEnd()->span() == outer || !SkOpGlobalState::DebugRunFail());
caryclark55888e42016-07-18 10:01:36 -07001409}
1410#endif
caryclark26ad22a2015-10-16 09:03:38 -07001411
Cary Clarkab87d7a2016-10-04 10:01:04 -04001412#if DEBUG_COIN
1413// sets the span's end to the ptT referenced by the previous-next
1414void SkCoincidentSpans::debugCorrectOneEnd(SkPathOpsDebug::GlitchLog* log,
1415 const SkOpPtT* (SkCoincidentSpans::* getEnd)() const,
1416 void (SkCoincidentSpans::*setEnd)(const SkOpPtT* ptT) const ) const {
1417 const SkOpPtT* origPtT = (this->*getEnd)();
1418 const SkOpSpanBase* origSpan = origPtT->span();
1419 const SkOpSpan* prev = origSpan->prev();
1420 const SkOpPtT* testPtT = prev ? prev->next()->ptT()
1421 : origSpan->upCast()->next()->prev()->ptT();
1422 if (origPtT != testPtT) {
1423 log->record(SkPathOpsDebug::kCorrectEnd_Glitch, this, origPtT, testPtT);
1424 }
1425}
1426
1427
1428/* Commented-out lines keep this in sync with correctEnds */
1429// FIXME: member pointers have fallen out of favor and can be replaced with
1430// an alternative approach.
1431// makes all span ends agree with the segment's spans that define them
1432void SkCoincidentSpans::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
1433 this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTStart, nullptr);
1434 this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTEnd, nullptr);
1435 this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTStart, nullptr);
1436 this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTEnd, nullptr);
1437}
1438
caryclark55888e42016-07-18 10:01:36 -07001439/* Commented-out lines keep this in sync with expand */
caryclark30b9fdd2016-08-31 14:36:29 -07001440// expand the range by checking adjacent spans for coincidence
Cary Clarkab87d7a2016-10-04 10:01:04 -04001441bool SkCoincidentSpans::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07001442 bool expanded = false;
1443 const SkOpSegment* segment = coinPtTStart()->segment();
1444 const SkOpSegment* oppSegment = oppPtTStart()->segment();
1445 do {
1446 const SkOpSpan* start = coinPtTStart()->span()->upCast();
1447 const SkOpSpan* prev = start->prev();
1448 const SkOpPtT* oppPtT;
1449 if (!prev || !(oppPtT = prev->contains(oppSegment))) {
1450 break;
1451 }
1452 double midT = (prev->t() + start->t()) / 2;
1453 if (!segment->isClose(midT, oppSegment)) {
1454 break;
1455 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001456 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, prev->ptT(), oppPtT);
caryclark55888e42016-07-18 10:01:36 -07001457 expanded = true;
1458 } while (false); // actual continues while expansion is possible
1459 do {
1460 const SkOpSpanBase* end = coinPtTEnd()->span();
1461 SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
caryclark30b9fdd2016-08-31 14:36:29 -07001462 if (next && next->deleted()) {
1463 break;
1464 }
caryclark55888e42016-07-18 10:01:36 -07001465 const SkOpPtT* oppPtT;
1466 if (!next || !(oppPtT = next->contains(oppSegment))) {
1467 break;
1468 }
1469 double midT = (end->t() + next->t()) / 2;
1470 if (!segment->isClose(midT, oppSegment)) {
1471 break;
1472 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001473 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, next->ptT(), oppPtT);
caryclark55888e42016-07-18 10:01:36 -07001474 expanded = true;
1475 } while (false); // actual continues while expansion is possible
1476 return expanded;
1477}
1478
Cary Clarkab87d7a2016-10-04 10:01:04 -04001479// description below
1480void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* base, const SkOpSpanBase* testSpan) const {
1481 const SkOpPtT* testPtT = testSpan->ptT();
1482 const SkOpPtT* stopPtT = testPtT;
1483 const SkOpSegment* baseSeg = base->segment();
1484 while ((testPtT = testPtT->next()) != stopPtT) {
1485 const SkOpSegment* testSeg = testPtT->segment();
1486 if (testPtT->deleted()) {
1487 continue;
1488 }
1489 if (testSeg == baseSeg) {
1490 continue;
1491 }
1492 if (testPtT->span()->ptT() != testPtT) {
1493 continue;
1494 }
1495 if (this->contains(baseSeg, testSeg, testPtT->fT)) {
1496 continue;
1497 }
1498 // intersect perp with base->ptT() with testPtT->segment()
1499 SkDVector dxdy = baseSeg->dSlopeAtT(base->t());
1500 const SkPoint& pt = base->pt();
1501 SkDLine ray = {{{pt.fX, pt.fY}, {pt.fX + dxdy.fY, pt.fY - dxdy.fX}}};
1502 SkIntersections i;
1503 (*CurveIntersectRay[testSeg->verb()])(testSeg->pts(), testSeg->weight(), ray, &i);
1504 for (int index = 0; index < i.used(); ++index) {
1505 double t = i[0][index];
1506 if (!between(0, t, 1)) {
1507 continue;
1508 }
1509 SkDPoint oppPt = i.pt(index);
1510 if (!oppPt.approximatelyEqual(pt)) {
1511 continue;
1512 }
1513 SkOpSegment* writableSeg = const_cast<SkOpSegment*>(testSeg);
1514 SkOpPtT* oppStart = writableSeg->addT(t);
1515 if (oppStart == testPtT) {
1516 continue;
1517 }
1518 SkOpSpan* writableBase = const_cast<SkOpSpan*>(base);
1519 oppStart->span()->addOpp(writableBase);
1520 if (oppStart->deleted()) {
1521 continue;
1522 }
1523 SkOpSegment* coinSeg = base->segment();
1524 SkOpSegment* oppSeg = oppStart->segment();
1525 double coinTs, coinTe, oppTs, oppTe;
1526 if (Ordered(coinSeg, oppSeg)) {
1527 coinTs = base->t();
1528 coinTe = testSpan->t();
1529 oppTs = oppStart->fT;
1530 oppTe = testPtT->fT;
1531 } else {
1532 SkTSwap(coinSeg, oppSeg);
1533 coinTs = oppStart->fT;
1534 coinTe = testPtT->fT;
1535 oppTs = base->t();
1536 oppTe = testSpan->t();
1537 }
1538 if (coinTs > coinTe) {
1539 SkTSwap(coinTs, coinTe);
1540 SkTSwap(oppTs, oppTe);
1541 }
1542 bool added;
1543 if (this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &added), false) {
1544 return;
1545 }
1546 }
1547 }
1548 return;
1549}
1550
1551// description below
1552void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* ptT) const {
1553 FAIL_IF(!ptT->span()->upCastable(), ptT->span());
1554 const SkOpSpan* base = ptT->span()->upCast();
1555 const SkOpSpan* prev = base->prev();
1556 FAIL_IF(!prev, ptT->span());
1557 if (!prev->isCanceled()) {
1558 if (this->debugAddEndMovedSpans(log, base, base->prev()), false) {
1559 return;
1560 }
1561 }
1562 if (!base->isCanceled()) {
1563 if (this->debugAddEndMovedSpans(log, base, base->next()), false) {
1564 return;
1565 }
1566 }
1567 return;
1568}
1569
1570/* If A is coincident with B and B includes an endpoint, and A's matching point
1571 is not the endpoint (i.e., there's an implied line connecting B-end and A)
1572 then assume that the same implied line may intersect another curve close to B.
1573 Since we only care about coincidence that was undetected, look at the
1574 ptT list on B-segment adjacent to the B-end/A ptT loop (not in the loop, but
1575 next door) and see if the A matching point is close enough to form another
1576 coincident pair. If so, check for a new coincident span between B-end/A ptT loop
1577 and the adjacent ptT loop.
1578*/
1579void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log) const {
1580 const SkCoincidentSpans* span = fHead;
1581 if (!span) {
1582 return;
1583 }
1584// fTop = span;
1585// fHead = nullptr;
1586 do {
1587 if (span->coinPtTStart()->fPt != span->oppPtTStart()->fPt) {
1588 FAIL_IF(1 == span->coinPtTStart()->fT, span);
1589 bool onEnd = span->coinPtTStart()->fT == 0;
1590 bool oOnEnd = zero_or_one(span->oppPtTStart()->fT);
1591 if (onEnd) {
1592 if (!oOnEnd) { // if both are on end, any nearby intersect was already found
1593 if (this->debugAddEndMovedSpans(log, span->oppPtTStart()), false) {
1594 return;
1595 }
1596 }
1597 } else if (oOnEnd) {
1598 if (this->debugAddEndMovedSpans(log, span->coinPtTStart()), false) {
1599 return;
1600 }
1601 }
1602 }
1603 if (span->coinPtTEnd()->fPt != span->oppPtTEnd()->fPt) {
1604 bool onEnd = span->coinPtTEnd()->fT == 1;
1605 bool oOnEnd = zero_or_one(span->oppPtTEnd()->fT);
1606 if (onEnd) {
1607 if (!oOnEnd) {
1608 if (this->debugAddEndMovedSpans(log, span->oppPtTEnd()), false) {
1609 return;
1610 }
1611 }
1612 } else if (oOnEnd) {
1613 if (this->debugAddEndMovedSpans(log, span->coinPtTEnd()), false) {
1614 return;
1615 }
1616 }
1617 }
1618 } while ((span = span->next()));
1619// this->restoreHead();
1620 return;
1621}
1622
caryclark55888e42016-07-18 10:01:36 -07001623/* Commented-out lines keep this in sync with addExpanded */
1624// for each coincident pair, match the spans
1625// 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 -04001626void SkOpCoincidence::debugAddExpanded(SkPathOpsDebug::GlitchLog* log) const {
caryclarka35ab3e2016-10-20 08:32:18 -07001627// DEBUG_SET_PHASE();
caryclark26ad22a2015-10-16 09:03:38 -07001628 const SkCoincidentSpans* coin = this->fHead;
1629 if (!coin) {
caryclarked0935a2015-10-22 07:23:52 -07001630 return;
1631 }
caryclark26ad22a2015-10-16 09:03:38 -07001632 do {
caryclark55888e42016-07-18 10:01:36 -07001633 const SkOpPtT* startPtT = coin->coinPtTStart();
1634 const SkOpPtT* oStartPtT = coin->oppPtTStart();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001635 double priorT = startPtT->fT;
1636 double oPriorT = oStartPtT->fT;
Cary Clarkff114282016-12-14 11:56:16 -05001637 FAIL_IF(!startPtT->contains(oStartPtT), coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001638 SkOPASSERT(coin->coinPtTEnd()->contains(coin->oppPtTEnd()));
caryclark26ad22a2015-10-16 09:03:38 -07001639 const SkOpSpanBase* start = startPtT->span();
1640 const SkOpSpanBase* oStart = oStartPtT->span();
caryclark55888e42016-07-18 10:01:36 -07001641 const SkOpSpanBase* end = coin->coinPtTEnd()->span();
1642 const SkOpSpanBase* oEnd = coin->oppPtTEnd()->span();
caryclark30b9fdd2016-08-31 14:36:29 -07001643 FAIL_IF(oEnd->deleted(), coin);
1644 FAIL_IF(!start->upCastable(), coin);
caryclark26ad22a2015-10-16 09:03:38 -07001645 const SkOpSpanBase* test = start->upCast()->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001646 FAIL_IF(!coin->flipped() && !oStart->upCastable(), coin);
caryclark55888e42016-07-18 10:01:36 -07001647 const SkOpSpanBase* oTest = coin->flipped() ? oStart->prev() : oStart->upCast()->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001648 FAIL_IF(!oTest, coin);
1649 const SkOpSegment* seg = start->segment();
1650 const SkOpSegment* oSeg = oStart->segment();
caryclark26ad22a2015-10-16 09:03:38 -07001651 while (test != end || oTest != oEnd) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001652 const SkOpPtT* containedOpp = test->ptT()->contains(oSeg);
1653 const SkOpPtT* containedThis = oTest->ptT()->contains(seg);
1654 if (!containedOpp || !containedThis) {
1655 // choose the ends, or the first common pt-t list shared by both
1656 double nextT, oNextT;
1657 if (containedOpp) {
1658 nextT = test->t();
1659 oNextT = containedOpp->fT;
1660 } else if (containedThis) {
1661 nextT = containedThis->fT;
1662 oNextT = oTest->t();
1663 } else {
1664 // iterate through until a pt-t list found that contains the other
1665 const SkOpSpanBase* walk = test;
1666 const SkOpPtT* walkOpp;
1667 do {
1668 FAIL_IF(!walk->upCastable(), coin);
1669 walk = walk->upCast()->next();
1670 } while (!(walkOpp = walk->ptT()->contains(oSeg))
1671 && walk != coin->coinPtTEnd()->span());
caryclarka35ab3e2016-10-20 08:32:18 -07001672 FAIL_IF(!walkOpp, coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001673 nextT = walk->t();
1674 oNextT = walkOpp->fT;
1675 }
caryclark26ad22a2015-10-16 09:03:38 -07001676 // use t ranges to guess which one is missing
caryclarka35ab3e2016-10-20 08:32:18 -07001677 double startRange = nextT - priorT;
caryclark30b9fdd2016-08-31 14:36:29 -07001678 FAIL_IF(!startRange, coin);
caryclarka35ab3e2016-10-20 08:32:18 -07001679 double startPart = (test->t() - priorT) / startRange;
1680 double oStartRange = oNextT - oPriorT;
caryclark30b9fdd2016-08-31 14:36:29 -07001681 FAIL_IF(!oStartRange, coin);
caryclark26ad22a2015-10-16 09:03:38 -07001682 double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange;
caryclark30b9fdd2016-08-31 14:36:29 -07001683 FAIL_IF(startPart == oStartPart, coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001684 bool addToOpp = !containedOpp && !containedThis ? startPart < oStartPart
1685 : !!containedThis;
caryclark55888e42016-07-18 10:01:36 -07001686 bool startOver = false;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001687 addToOpp ? log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch,
1688 oPriorT + oStartRange * startPart, test)
1689 : log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch,
1690 priorT + startRange * oStartPart, oTest);
1691 // FAIL_IF(!success, coin);
caryclark55888e42016-07-18 10:01:36 -07001692 if (startOver) {
1693 test = start;
1694 oTest = oStart;
caryclark26ad22a2015-10-16 09:03:38 -07001695 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001696 end = coin->coinPtTEnd()->span();
1697 oEnd = coin->oppPtTEnd()->span();
caryclark26ad22a2015-10-16 09:03:38 -07001698 }
caryclark55888e42016-07-18 10:01:36 -07001699 if (test != end) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001700 FAIL_IF(!test->upCastable(), coin);
1701 priorT = test->t();
caryclark26ad22a2015-10-16 09:03:38 -07001702 test = test->upCast()->next();
1703 }
caryclark55888e42016-07-18 10:01:36 -07001704 if (oTest != oEnd) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001705 oPriorT = oTest->t();
caryclark55888e42016-07-18 10:01:36 -07001706 oTest = coin->flipped() ? oTest->prev() : oTest->upCast()->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001707 FAIL_IF(!oTest, coin);
caryclark26ad22a2015-10-16 09:03:38 -07001708 }
1709 }
caryclark55888e42016-07-18 10:01:36 -07001710 } while ((coin = coin->next()));
1711 return;
caryclark26ad22a2015-10-16 09:03:38 -07001712}
1713
caryclark55888e42016-07-18 10:01:36 -07001714/* Commented-out lines keep this in sync addIfMissing() */
caryclark8016b262016-09-06 05:59:47 -07001715// note that over1s, over1e, over2s, over2e are ordered
Cary Clarkab87d7a2016-10-04 10:01:04 -04001716void SkOpCoincidence::debugAddIfMissing(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* over1s, const SkOpPtT* over2s,
caryclark81a478c2016-09-09 09:37:57 -07001717 double tStart, double tEnd, const SkOpSegment* coinSeg, const SkOpSegment* oppSeg, bool* added,
caryclark8016b262016-09-06 05:59:47 -07001718 const SkOpPtT* over1e, const SkOpPtT* over2e) const {
1719 SkASSERT(tStart < tEnd);
1720 SkASSERT(over1s->fT < over1e->fT);
1721 SkASSERT(between(over1s->fT, tStart, over1e->fT));
1722 SkASSERT(between(over1s->fT, tEnd, over1e->fT));
1723 SkASSERT(over2s->fT < over2e->fT);
1724 SkASSERT(between(over2s->fT, tStart, over2e->fT));
1725 SkASSERT(between(over2s->fT, tEnd, over2e->fT));
1726 SkASSERT(over1s->segment() == over1e->segment());
1727 SkASSERT(over2s->segment() == over2e->segment());
1728 SkASSERT(over1s->segment() == over2s->segment());
1729 SkASSERT(over1s->segment() != coinSeg);
1730 SkASSERT(over1s->segment() != oppSeg);
1731 SkASSERT(coinSeg != oppSeg);
caryclark26ad22a2015-10-16 09:03:38 -07001732 double coinTs, coinTe, oppTs, oppTe;
caryclark8016b262016-09-06 05:59:47 -07001733 coinTs = TRange(over1s, tStart, coinSeg SkDEBUGPARAMS(over1e));
1734 coinTe = TRange(over1s, tEnd, coinSeg SkDEBUGPARAMS(over1e));
1735 if (coinSeg->collapsed(coinTs, coinTe)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001736 return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, coinSeg);
caryclark8016b262016-09-06 05:59:47 -07001737 }
1738 oppTs = TRange(over2s, tStart, oppSeg SkDEBUGPARAMS(over2e));
1739 oppTe = TRange(over2s, tEnd, oppSeg SkDEBUGPARAMS(over2e));
1740 if (oppSeg->collapsed(oppTs, oppTe)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001741 return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, oppSeg);
caryclark8016b262016-09-06 05:59:47 -07001742 }
1743 if (coinTs > coinTe) {
caryclark55888e42016-07-18 10:01:36 -07001744 SkTSwap(coinTs, coinTe);
caryclark26ad22a2015-10-16 09:03:38 -07001745 SkTSwap(oppTs, oppTe);
1746 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001747 return this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, added
caryclark8016b262016-09-06 05:59:47 -07001748 );
caryclark26ad22a2015-10-16 09:03:38 -07001749}
1750
caryclark55888e42016-07-18 10:01:36 -07001751/* Commented-out lines keep this in sync addOrOverlap() */
caryclark30b9fdd2016-08-31 14:36:29 -07001752// If this is called by addEndMovedSpans(), a returned false propogates out to an abort.
1753// If this is called by AddIfMissing(), a returned false indicates there was nothing to add
Cary Clarkab87d7a2016-10-04 10:01:04 -04001754void SkOpCoincidence::debugAddOrOverlap(SkPathOpsDebug::GlitchLog* log,
caryclark30b9fdd2016-08-31 14:36:29 -07001755 const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
caryclark81a478c2016-09-09 09:37:57 -07001756 double coinTs, double coinTe, double oppTs, double oppTe, bool* added) const {
caryclark55888e42016-07-18 10:01:36 -07001757 SkTDArray<SkCoincidentSpans*> overlaps;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001758 SkOPASSERT(!fTop); // this is (correctly) reversed in addifMissing()
caryclark81a478c2016-09-09 09:37:57 -07001759 if (fTop && !this->checkOverlap(fTop, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe,
1760 &overlaps)) {
caryclark55888e42016-07-18 10:01:36 -07001761 return;
1762 }
1763 if (fHead && !this->checkOverlap(fHead, coinSeg, oppSeg, coinTs,
1764 coinTe, oppTs, oppTe, &overlaps)) {
1765 return;
1766 }
1767 const SkCoincidentSpans* overlap = overlaps.count() ? overlaps[0] : nullptr;
1768 for (int index = 1; index < overlaps.count(); ++index) { // combine overlaps before continuing
1769 const SkCoincidentSpans* test = overlaps[index];
1770 if (overlap->coinPtTStart()->fT > test->coinPtTStart()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001771 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTStart());
caryclark55888e42016-07-18 10:01:36 -07001772 }
1773 if (overlap->coinPtTEnd()->fT < test->coinPtTEnd()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001774 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07001775 }
1776 if (overlap->flipped()
1777 ? overlap->oppPtTStart()->fT < test->oppPtTStart()->fT
1778 : overlap->oppPtTStart()->fT > test->oppPtTStart()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001779 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTStart());
caryclark55888e42016-07-18 10:01:36 -07001780 }
1781 if (overlap->flipped()
1782 ? overlap->oppPtTEnd()->fT > test->oppPtTEnd()->fT
1783 : overlap->oppPtTEnd()->fT < test->oppPtTEnd()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001784 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07001785 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001786 if (!fHead) { this->debugRelease(log, fHead, test);
1787 this->debugRelease(log, fTop, test);
caryclark55888e42016-07-18 10:01:36 -07001788 }
1789 }
1790 const SkOpPtT* cs = coinSeg->existing(coinTs, oppSeg);
1791 const SkOpPtT* ce = coinSeg->existing(coinTe, oppSeg);
caryclark30b9fdd2016-08-31 14:36:29 -07001792 RETURN_FALSE_IF(overlap && cs && ce && overlap->contains(cs, ce), coinSeg);
1793 RETURN_FALSE_IF(cs != ce || !cs, coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001794 const SkOpPtT* os = oppSeg->existing(oppTs, coinSeg);
1795 const SkOpPtT* oe = oppSeg->existing(oppTe, coinSeg);
caryclark30b9fdd2016-08-31 14:36:29 -07001796 RETURN_FALSE_IF(overlap && os && oe && overlap->contains(os, oe), oppSeg);
caryclark55888e42016-07-18 10:01:36 -07001797 SkASSERT(true || !cs || !cs->deleted());
1798 SkASSERT(true || !os || !os->deleted());
1799 SkASSERT(true || !ce || !ce->deleted());
1800 SkASSERT(true || !oe || !oe->deleted());
1801 const SkOpPtT* csExisting = !cs ? coinSeg->existing(coinTs, nullptr) : nullptr;
1802 const SkOpPtT* ceExisting = !ce ? coinSeg->existing(coinTe, nullptr) : nullptr;
caryclark30b9fdd2016-08-31 14:36:29 -07001803 RETURN_FALSE_IF(csExisting && csExisting == ceExisting, coinSeg);
1804 RETURN_FALSE_IF(csExisting && (csExisting == ce ||
1805 csExisting->contains(ceExisting ? ceExisting : ce)), coinSeg);
1806 RETURN_FALSE_IF(ceExisting && (ceExisting == cs ||
1807 ceExisting->contains(csExisting ? csExisting : cs)), coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001808 const SkOpPtT* osExisting = !os ? oppSeg->existing(oppTs, nullptr) : nullptr;
1809 const SkOpPtT* oeExisting = !oe ? oppSeg->existing(oppTe, nullptr) : nullptr;
caryclark30b9fdd2016-08-31 14:36:29 -07001810 RETURN_FALSE_IF(osExisting && osExisting == oeExisting, oppSeg);
1811 RETURN_FALSE_IF(osExisting && (osExisting == oe ||
1812 osExisting->contains(oeExisting ? oeExisting : oe)), oppSeg);
1813 RETURN_FALSE_IF(oeExisting && (oeExisting == os ||
1814 oeExisting->contains(osExisting ? osExisting : os)), oppSeg);
caryclark55888e42016-07-18 10:01:36 -07001815 bool csDeleted = false, osDeleted = false, ceDeleted = false, oeDeleted = false;
1816 this->debugValidate();
1817 if (!cs || !os) {
1818 if (!cs)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001819 cs = coinSeg->debugAddT(coinTs, log);
caryclark55888e42016-07-18 10:01:36 -07001820 if (!os)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001821 os = oppSeg->debugAddT(oppTs, log);
caryclark30b9fdd2016-08-31 14:36:29 -07001822// RETURN_FALSE_IF(callerAborts, !csWritable || !osWritable);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001823 if (cs && os) cs->span()->debugAddOpp(log, os->span());
caryclark55888e42016-07-18 10:01:36 -07001824// cs = csWritable;
caryclark30b9fdd2016-08-31 14:36:29 -07001825// os = osWritable->active();
1826 RETURN_FALSE_IF((ce && ce->deleted()) || (oe && oe->deleted()), coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001827 }
1828 if (!ce || !oe) {
1829 if (!ce)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001830 ce = coinSeg->debugAddT(coinTe, log);
caryclark55888e42016-07-18 10:01:36 -07001831 if (!oe)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001832 oe = oppSeg->debugAddT(oppTe, log);
1833 if (ce && oe) ce->span()->debugAddOpp(log, oe->span());
caryclark55888e42016-07-18 10:01:36 -07001834// ce = ceWritable;
1835// oe = oeWritable;
1836 }
1837 this->debugValidate();
caryclark30b9fdd2016-08-31 14:36:29 -07001838 RETURN_FALSE_IF(csDeleted, coinSeg);
1839 RETURN_FALSE_IF(osDeleted, oppSeg);
1840 RETURN_FALSE_IF(ceDeleted, coinSeg);
1841 RETURN_FALSE_IF(oeDeleted, oppSeg);
caryclark8016b262016-09-06 05:59:47 -07001842 RETURN_FALSE_IF(!cs || !ce || cs == ce || cs->contains(ce) || !os || !oe || os == oe || os->contains(oe), coinSeg);
1843 bool result = true;
caryclark55888e42016-07-18 10:01:36 -07001844 if (overlap) {
1845 if (overlap->coinPtTStart()->segment() == coinSeg) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001846 log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
caryclark55888e42016-07-18 10:01:36 -07001847 } else {
1848 if (oppTs > oppTe) {
1849 SkTSwap(coinTs, coinTe);
1850 SkTSwap(oppTs, oppTe);
1851 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001852 log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, oppSeg, oppTs, oppTe, coinSeg, coinTs, coinTe);
caryclark55888e42016-07-18 10:01:36 -07001853 }
caryclark8016b262016-09-06 05:59:47 -07001854#if 0 && DEBUG_COINCIDENCE_VERBOSE
1855 if (result) {
1856 overlap->debugShow();
1857 }
caryclark55888e42016-07-18 10:01:36 -07001858#endif
1859 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001860 log->record(SkPathOpsDebug::kAddMissingCoin_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
caryclark8016b262016-09-06 05:59:47 -07001861#if 0 && DEBUG_COINCIDENCE_VERBOSE
1862 fHead->debugShow();
caryclark55888e42016-07-18 10:01:36 -07001863#endif
1864 }
1865 this->debugValidate();
caryclark8016b262016-09-06 05:59:47 -07001866 return (void) result;
caryclark55888e42016-07-18 10:01:36 -07001867}
1868
1869// Extra commented-out lines keep this in sync with addMissing()
1870/* detects overlaps of different coincident runs on same segment */
1871/* does not detect overlaps for pairs without any segments in common */
1872// returns true if caller should loop again
Cary Clarkab87d7a2016-10-04 10:01:04 -04001873void SkOpCoincidence::debugAddMissing(SkPathOpsDebug::GlitchLog* log, bool* added) const {
caryclark26ad22a2015-10-16 09:03:38 -07001874 const SkCoincidentSpans* outer = fHead;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001875 *added = false;
caryclark26ad22a2015-10-16 09:03:38 -07001876 if (!outer) {
1877 return;
1878 }
caryclark55888e42016-07-18 10:01:36 -07001879 // fTop = outer;
1880 // fHead = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -07001881 do {
1882 // addifmissing can modify the list that this is walking
1883 // save head so that walker can iterate over old data unperturbed
1884 // addifmissing adds to head freely then add saved head in the end
caryclark8016b262016-09-06 05:59:47 -07001885 const SkOpPtT* ocs = outer->coinPtTStart();
1886 SkASSERT(!ocs->deleted());
1887 const SkOpSegment* outerCoin = ocs->segment();
1888 SkASSERT(!outerCoin->done()); // if it's done, should have already been removed from list
1889 const SkOpPtT* oos = outer->oppPtTStart();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001890 if (oos->deleted()) {
1891 return;
1892 }
caryclark8016b262016-09-06 05:59:47 -07001893 const SkOpSegment* outerOpp = oos->segment();
1894 SkASSERT(!outerOpp->done());
1895// SkOpSegment* outerCoinWritable = const_cast<SkOpSegment*>(outerCoin);
1896// SkOpSegment* outerOppWritable = const_cast<SkOpSegment*>(outerOpp);
caryclark26ad22a2015-10-16 09:03:38 -07001897 const SkCoincidentSpans* inner = outer;
caryclark55888e42016-07-18 10:01:36 -07001898 while ((inner = inner->next())) {
1899 this->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001900 double overS, overE;
caryclark8016b262016-09-06 05:59:47 -07001901 const SkOpPtT* ics = inner->coinPtTStart();
1902 SkASSERT(!ics->deleted());
1903 const SkOpSegment* innerCoin = ics->segment();
1904 SkASSERT(!innerCoin->done());
1905 const SkOpPtT* ios = inner->oppPtTStart();
1906 SkASSERT(!ios->deleted());
1907 const SkOpSegment* innerOpp = ios->segment();
1908 SkASSERT(!innerOpp->done());
1909// SkOpSegment* innerCoinWritable = const_cast<SkOpSegment*>(innerCoin);
1910// SkOpSegment* innerOppWritable = const_cast<SkOpSegment*>(innerOpp);
caryclark55888e42016-07-18 10:01:36 -07001911 if (outerCoin == innerCoin) {
caryclark8016b262016-09-06 05:59:47 -07001912 const SkOpPtT* oce = outer->coinPtTEnd();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001913 if (oce->deleted()) {
1914 return;
1915 }
caryclark8016b262016-09-06 05:59:47 -07001916 const SkOpPtT* ice = inner->coinPtTEnd();
1917 SkASSERT(!ice->deleted());
1918 if (outerOpp != innerOpp && this->overlap(ocs, oce, ics, ice, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001919 this->debugAddIfMissing(log, ocs->starter(oce), ics->starter(ice),
caryclark81a478c2016-09-09 09:37:57 -07001920 overS, overE, outerOpp, innerOpp, added,
caryclark8016b262016-09-06 05:59:47 -07001921 ocs->debugEnder(oce),
1922 ics->debugEnder(ice));
caryclark26ad22a2015-10-16 09:03:38 -07001923 }
caryclark55888e42016-07-18 10:01:36 -07001924 } else if (outerCoin == innerOpp) {
caryclark8016b262016-09-06 05:59:47 -07001925 const SkOpPtT* oce = outer->coinPtTEnd();
1926 SkASSERT(!oce->deleted());
1927 const SkOpPtT* ioe = inner->oppPtTEnd();
1928 SkASSERT(!ioe->deleted());
1929 if (outerOpp != innerCoin && this->overlap(ocs, oce, ios, ioe, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001930 this->debugAddIfMissing(log, ocs->starter(oce), ios->starter(ioe),
caryclark81a478c2016-09-09 09:37:57 -07001931 overS, overE, outerOpp, innerCoin, added,
caryclark8016b262016-09-06 05:59:47 -07001932 ocs->debugEnder(oce),
1933 ios->debugEnder(ioe));
caryclark26ad22a2015-10-16 09:03:38 -07001934 }
caryclark55888e42016-07-18 10:01:36 -07001935 } else if (outerOpp == innerCoin) {
caryclark8016b262016-09-06 05:59:47 -07001936 const SkOpPtT* ooe = outer->oppPtTEnd();
1937 SkASSERT(!ooe->deleted());
1938 const SkOpPtT* ice = inner->coinPtTEnd();
1939 SkASSERT(!ice->deleted());
caryclark55888e42016-07-18 10:01:36 -07001940 SkASSERT(outerCoin != innerOpp);
caryclark8016b262016-09-06 05:59:47 -07001941 if (this->overlap(oos, ooe, ics, ice, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001942 this->debugAddIfMissing(log, oos->starter(ooe), ics->starter(ice),
caryclark81a478c2016-09-09 09:37:57 -07001943 overS, overE, outerCoin, innerOpp, added,
caryclark8016b262016-09-06 05:59:47 -07001944 oos->debugEnder(ooe),
1945 ics->debugEnder(ice));
caryclark26ad22a2015-10-16 09:03:38 -07001946 }
caryclark55888e42016-07-18 10:01:36 -07001947 } else if (outerOpp == innerOpp) {
caryclark8016b262016-09-06 05:59:47 -07001948 const SkOpPtT* ooe = outer->oppPtTEnd();
1949 SkASSERT(!ooe->deleted());
1950 const SkOpPtT* ioe = inner->oppPtTEnd();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001951 if (ioe->deleted()) {
1952 return;
1953 }
caryclark55888e42016-07-18 10:01:36 -07001954 SkASSERT(outerCoin != innerCoin);
caryclark8016b262016-09-06 05:59:47 -07001955 if (this->overlap(oos, ooe, ios, ioe, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001956 this->debugAddIfMissing(log, oos->starter(ooe), ios->starter(ioe),
caryclark81a478c2016-09-09 09:37:57 -07001957 overS, overE, outerCoin, innerCoin, added,
caryclark8016b262016-09-06 05:59:47 -07001958 oos->debugEnder(ooe),
1959 ios->debugEnder(ioe));
caryclark26ad22a2015-10-16 09:03:38 -07001960 }
1961 }
caryclark55888e42016-07-18 10:01:36 -07001962 this->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001963 }
caryclark55888e42016-07-18 10:01:36 -07001964 } while ((outer = outer->next()));
1965 // this->restoreHead();
1966 return;
caryclark26ad22a2015-10-16 09:03:38 -07001967}
1968
caryclark55888e42016-07-18 10:01:36 -07001969// Commented-out lines keep this in sync with release()
Cary Clarkab87d7a2016-10-04 10:01:04 -04001970void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkCoincidentSpans* remove) const {
caryclark30b9fdd2016-08-31 14:36:29 -07001971 const SkCoincidentSpans* head = coin;
1972 const SkCoincidentSpans* prev = nullptr;
1973 const SkCoincidentSpans* next;
1974 do {
1975 next = coin->next();
1976 if (coin == remove) {
1977 if (prev) {
1978// prev->setNext(next);
1979 } else if (head == fHead) {
1980// fHead = next;
1981 } else {
1982// fTop = next;
1983 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001984 log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
caryclark30b9fdd2016-08-31 14:36:29 -07001985 }
1986 prev = coin;
1987 } while ((coin = next));
1988 return;
1989}
1990
Cary Clarkab87d7a2016-10-04 10:01:04 -04001991void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* deleted) const {
caryclark55888e42016-07-18 10:01:36 -07001992 const SkCoincidentSpans* coin = fHead;
1993 if (!coin) {
1994 return;
1995 }
1996 do {
1997 if (coin->coinPtTStart()->segment() == deleted
1998 || coin->coinPtTEnd()->segment() == deleted
1999 || coin->oppPtTStart()->segment() == deleted
2000 || coin->oppPtTEnd()->segment() == deleted) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002001 log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07002002 }
2003 } while ((coin = coin->next()));
2004}
2005
caryclark55888e42016-07-18 10:01:36 -07002006// Commented-out lines keep this in sync with expand()
2007// expand the range by checking adjacent spans for coincidence
Cary Clarkab87d7a2016-10-04 10:01:04 -04002008bool SkOpCoincidence::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07002009 const SkCoincidentSpans* coin = fHead;
2010 if (!coin) {
2011 return false;
2012 }
2013 bool expanded = false;
2014 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002015 if (coin->debugExpand(log)) {
caryclark55888e42016-07-18 10:01:36 -07002016 // check to see if multiple spans expanded so they are now identical
2017 const SkCoincidentSpans* test = fHead;
2018 do {
2019 if (coin == test) {
2020 continue;
2021 }
2022 if (coin->coinPtTStart() == test->coinPtTStart()
2023 && coin->oppPtTStart() == test->oppPtTStart()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002024 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, fHead, test->coinPtTStart());
caryclark55888e42016-07-18 10:01:36 -07002025 break;
2026 }
2027 } while ((test = test->next()));
2028 expanded = true;
caryclark26ad22a2015-10-16 09:03:38 -07002029 }
caryclark55888e42016-07-18 10:01:36 -07002030 } while ((coin = coin->next()));
caryclark26ad22a2015-10-16 09:03:38 -07002031 return expanded;
2032}
2033
caryclark55888e42016-07-18 10:01:36 -07002034// Commented-out lines keep this in sync with mark()
2035/* this sets up the coincidence links in the segments when the coincidence crosses multiple spans */
Cary Clarkab87d7a2016-10-04 10:01:04 -04002036void SkOpCoincidence::debugMark(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07002037 const SkCoincidentSpans* coin = fHead;
2038 if (!coin) {
2039 return;
2040 }
2041 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002042 FAIL_IF(!coin->coinPtTStartWritable()->span()->upCastable(), coin);
caryclark55888e42016-07-18 10:01:36 -07002043 const SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast();
2044// SkASSERT(start->deleted());
2045 const SkOpSpanBase* end = coin->coinPtTEndWritable()->span();
2046// SkASSERT(end->deleted());
2047 const SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span();
2048// SkASSERT(oStart->deleted());
2049 const SkOpSpanBase* oEnd = coin->oppPtTEndWritable()->span();
2050// SkASSERT(oEnd->deleted());
2051 bool flipped = coin->flipped();
caryclark26ad22a2015-10-16 09:03:38 -07002052 if (flipped) {
2053 SkTSwap(oStart, oEnd);
2054 }
caryclark55888e42016-07-18 10:01:36 -07002055 /* coin and opp spans may not match up. Mark the ends, and then let the interior
2056 get marked as many times as the spans allow */
Cary Clarkab87d7a2016-10-04 10:01:04 -04002057 start->debugInsertCoincidence(log, oStart->upCast());
2058 end->debugInsertCoinEnd(log, oEnd);
caryclark55888e42016-07-18 10:01:36 -07002059 const SkOpSegment* segment = start->segment();
2060 const SkOpSegment* oSegment = oStart->segment();
caryclark26ad22a2015-10-16 09:03:38 -07002061 const SkOpSpanBase* next = start;
2062 const SkOpSpanBase* oNext = oStart;
caryclarka35ab3e2016-10-20 08:32:18 -07002063 bool ordered;
2064 FAIL_IF(!coin->ordered(&ordered), coin);
caryclark55888e42016-07-18 10:01:36 -07002065 while ((next = next->upCast()->next()) != end) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002066 FAIL_IF(!next->upCastable(), coin);
2067 if (next->upCast()->debugInsertCoincidence(log, oSegment, flipped, ordered), false) {
caryclark55888e42016-07-18 10:01:36 -07002068 return;
caryclark26ad22a2015-10-16 09:03:38 -07002069 }
caryclark55888e42016-07-18 10:01:36 -07002070 }
2071 while ((oNext = oNext->upCast()->next()) != oEnd) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002072 FAIL_IF(!oNext->upCastable(), coin);
2073 if (oNext->upCast()->debugInsertCoincidence(log, segment, flipped, ordered), false) {
caryclark55888e42016-07-18 10:01:36 -07002074 return;
caryclark26ad22a2015-10-16 09:03:38 -07002075 }
caryclark55888e42016-07-18 10:01:36 -07002076 }
2077 } while ((coin = coin->next()));
2078 return;
caryclark26ad22a2015-10-16 09:03:38 -07002079}
2080#endif
2081
Cary Clarkab87d7a2016-10-04 10:01:04 -04002082#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002083// Commented-out lines keep this in sync with markCollapsed()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002084void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkOpPtT* test) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002085 const SkCoincidentSpans* head = coin;
caryclark55888e42016-07-18 10:01:36 -07002086 while (coin) {
2087 if (coin->collapsed(test)) {
2088 if (zero_or_one(coin->coinPtTStart()->fT) && zero_or_one(coin->coinPtTEnd()->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002089 log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07002090 }
2091 if (zero_or_one(coin->oppPtTStart()->fT) && zero_or_one(coin->oppPtTEnd()->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002092 log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07002093 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002094 this->debugRelease(log, head, coin);
caryclark55888e42016-07-18 10:01:36 -07002095 }
2096 coin = coin->next();
caryclark624637c2015-05-11 07:21:27 -07002097 }
2098}
2099
caryclark55888e42016-07-18 10:01:36 -07002100// Commented-out lines keep this in sync with markCollapsed()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002101void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* test) const {
2102 this->debugMarkCollapsed(log, fHead, test);
2103 this->debugMarkCollapsed(log, fTop, test);
caryclark55888e42016-07-18 10:01:36 -07002104}
2105#endif
2106
2107void SkCoincidentSpans::debugShow() const {
caryclark6c3b9cd2016-09-26 05:36:58 -07002108 SkDebugf("coinSpan - id=%d t=%1.9g tEnd=%1.9g\n", coinPtTStart()->segment()->debugID(),
caryclark55888e42016-07-18 10:01:36 -07002109 coinPtTStart()->fT, coinPtTEnd()->fT);
caryclark6c3b9cd2016-09-26 05:36:58 -07002110 SkDebugf("coinSpan + id=%d t=%1.9g tEnd=%1.9g\n", oppPtTStart()->segment()->debugID(),
caryclark55888e42016-07-18 10:01:36 -07002111 oppPtTStart()->fT, oppPtTEnd()->fT);
2112}
2113
2114void SkOpCoincidence::debugShowCoincidence() const {
caryclark26ad22a2015-10-16 09:03:38 -07002115#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -07002116 const SkCoincidentSpans* span = fHead;
2117 while (span) {
2118 span->debugShow();
2119 span = span->next();
2120 }
2121#endif
2122}
2123
Cary Clarkab87d7a2016-10-04 10:01:04 -04002124#if DEBUG_COIN
caryclark6c3b9cd2016-09-26 05:36:58 -07002125static void DebugCheckBetween(const SkOpSpanBase* next, const SkOpSpanBase* end,
caryclark55888e42016-07-18 10:01:36 -07002126 double oStart, double oEnd, const SkOpSegment* oSegment,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002127 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002128 SkASSERT(next != end);
2129 SkASSERT(!next->contains(end) || log);
2130 if (next->t() > end->t()) {
2131 SkTSwap(next, end);
2132 }
2133 do {
2134 const SkOpPtT* ptT = next->ptT();
2135 int index = 0;
caryclark27c015d2016-09-23 05:47:20 -07002136 bool somethingBetween = false;
caryclark55888e42016-07-18 10:01:36 -07002137 do {
2138 ++index;
2139 ptT = ptT->next();
2140 const SkOpPtT* checkPtT = next->ptT();
2141 if (ptT == checkPtT) {
2142 break;
2143 }
2144 bool looped = false;
2145 for (int check = 0; check < index; ++check) {
2146 if ((looped = checkPtT == ptT)) {
2147 break;
2148 }
2149 checkPtT = checkPtT->next();
2150 }
2151 if (looped) {
2152 SkASSERT(0);
2153 break;
2154 }
2155 if (ptT->deleted()) {
2156 continue;
2157 }
2158 if (ptT->segment() != oSegment) {
2159 continue;
2160 }
2161 somethingBetween |= between(oStart, ptT->fT, oEnd);
2162 } while (true);
2163 SkASSERT(somethingBetween);
2164 } while (next != end && (next = next->upCast()->next()));
2165}
2166
2167static void DebugCheckOverlap(const SkCoincidentSpans* test, const SkCoincidentSpans* list,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002168 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002169 if (!list) {
2170 return;
2171 }
2172 const SkOpSegment* coinSeg = test->coinPtTStart()->segment();
2173 SkASSERT(coinSeg == test->coinPtTEnd()->segment());
2174 const SkOpSegment* oppSeg = test->oppPtTStart()->segment();
2175 SkASSERT(oppSeg == test->oppPtTEnd()->segment());
2176 SkASSERT(coinSeg != test->oppPtTStart()->segment());
2177 SkDEBUGCODE(double tcs = test->coinPtTStart()->fT);
2178 SkASSERT(between(0, tcs, 1));
2179 SkDEBUGCODE(double tce = test->coinPtTEnd()->fT);
2180 SkASSERT(between(0, tce, 1));
2181 SkASSERT(tcs < tce);
2182 double tos = test->oppPtTStart()->fT;
2183 SkASSERT(between(0, tos, 1));
2184 double toe = test->oppPtTEnd()->fT;
2185 SkASSERT(between(0, toe, 1));
2186 SkASSERT(tos != toe);
2187 if (tos > toe) {
2188 SkTSwap(tos, toe);
2189 }
2190 do {
2191 double lcs, lce, los, loe;
2192 if (coinSeg == list->coinPtTStart()->segment()) {
2193 if (oppSeg != list->oppPtTStart()->segment()) {
2194 continue;
2195 }
2196 lcs = list->coinPtTStart()->fT;
2197 lce = list->coinPtTEnd()->fT;
2198 los = list->oppPtTStart()->fT;
2199 loe = list->oppPtTEnd()->fT;
2200 if (los > loe) {
2201 SkTSwap(los, loe);
2202 }
2203 } else if (coinSeg == list->oppPtTStart()->segment()) {
2204 if (oppSeg != list->coinPtTStart()->segment()) {
2205 continue;
2206 }
2207 lcs = list->oppPtTStart()->fT;
2208 lce = list->oppPtTEnd()->fT;
2209 if (lcs > lce) {
2210 SkTSwap(lcs, lce);
2211 }
2212 los = list->coinPtTStart()->fT;
2213 loe = list->coinPtTEnd()->fT;
2214 } else {
2215 continue;
2216 }
2217 SkASSERT(tce < lcs || lce < tcs);
2218 SkASSERT(toe < los || loe < tos);
2219 } while ((list = list->next()));
2220}
2221
2222
2223static void DebugCheckOverlapTop(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002224 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002225 // check for overlapping coincident spans
2226 const SkCoincidentSpans* test = head;
2227 while (test) {
2228 const SkCoincidentSpans* next = test->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04002229 DebugCheckOverlap(test, next, log);
2230 DebugCheckOverlap(test, opt, log);
caryclark55888e42016-07-18 10:01:36 -07002231 test = next;
2232 }
2233}
2234
caryclark55888e42016-07-18 10:01:36 -07002235static void DebugValidate(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002236 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002237 // look for pts inside coincident spans that are not inside the opposite spans
2238 const SkCoincidentSpans* coin = head;
2239 while (coin) {
2240 SkASSERT(SkOpCoincidence::Ordered(coin->coinPtTStart()->segment(),
2241 coin->oppPtTStart()->segment()));
2242 SkASSERT(coin->coinPtTStart()->span()->ptT() == coin->coinPtTStart());
2243 SkASSERT(coin->coinPtTEnd()->span()->ptT() == coin->coinPtTEnd());
2244 SkASSERT(coin->oppPtTStart()->span()->ptT() == coin->oppPtTStart());
2245 SkASSERT(coin->oppPtTEnd()->span()->ptT() == coin->oppPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07002246 coin = coin->next();
2247 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002248 DebugCheckOverlapTop(head, opt, log);
caryclark55888e42016-07-18 10:01:36 -07002249}
2250#endif
2251
2252void SkOpCoincidence::debugValidate() const {
2253#if DEBUG_COINCIDENCE
Cary Clarkab87d7a2016-10-04 10:01:04 -04002254 DebugValidate(fHead, fTop, nullptr);
2255 DebugValidate(fTop, nullptr, nullptr);
caryclark55888e42016-07-18 10:01:36 -07002256#endif
2257}
2258
Cary Clarkab87d7a2016-10-04 10:01:04 -04002259#if DEBUG_COIN
caryclark6c3b9cd2016-09-26 05:36:58 -07002260static void DebugCheckBetween(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002261 SkPathOpsDebug::GlitchLog* log) {
caryclark6c3b9cd2016-09-26 05:36:58 -07002262 // look for pts inside coincident spans that are not inside the opposite spans
2263 const SkCoincidentSpans* coin = head;
2264 while (coin) {
2265 DebugCheckBetween(coin->coinPtTStart()->span(), coin->coinPtTEnd()->span(),
2266 coin->oppPtTStart()->fT, coin->oppPtTEnd()->fT, coin->oppPtTStart()->segment(),
Cary Clarkab87d7a2016-10-04 10:01:04 -04002267 log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002268 DebugCheckBetween(coin->oppPtTStart()->span(), coin->oppPtTEnd()->span(),
2269 coin->coinPtTStart()->fT, coin->coinPtTEnd()->fT, coin->coinPtTStart()->segment(),
Cary Clarkab87d7a2016-10-04 10:01:04 -04002270 log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002271 coin = coin->next();
2272 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002273 DebugCheckOverlapTop(head, opt, log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002274}
2275#endif
2276
2277void SkOpCoincidence::debugCheckBetween() const {
2278#if DEBUG_COINCIDENCE
2279 if (fGlobalState->debugCheckHealth()) {
2280 return;
2281 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002282 DebugCheckBetween(fHead, fTop, nullptr);
2283 DebugCheckBetween(fTop, nullptr, nullptr);
caryclark6c3b9cd2016-09-26 05:36:58 -07002284#endif
2285}
2286
Cary Clarkab87d7a2016-10-04 10:01:04 -04002287#if DEBUG_COIN
2288void SkOpContour::debugCheckHealth(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07002289 const SkOpSegment* segment = &fHead;
2290 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002291 segment->debugCheckHealth(log);
caryclark26ad22a2015-10-16 09:03:38 -07002292 } while ((segment = segment->next()));
2293}
2294
Cary Clarkab87d7a2016-10-04 10:01:04 -04002295void SkOpCoincidence::debugCheckValid(SkPathOpsDebug::GlitchLog* log) const {
2296#if DEBUG_VALIDATE
2297 DebugValidate(fHead, fTop, log);
2298 DebugValidate(fTop, nullptr, log);
2299#endif
2300}
2301
2302void SkOpCoincidence::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
2303 const SkCoincidentSpans* coin = fHead;
2304 if (!coin) {
2305 return;
2306 }
2307 do {
2308 coin->debugCorrectEnds(log);
2309 } while ((coin = coin->next()));
2310}
2311
caryclark55888e42016-07-18 10:01:36 -07002312// commmented-out lines keep this aligned with missingCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002313void SkOpContour::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07002314// SkASSERT(fCount > 0);
caryclark26ad22a2015-10-16 09:03:38 -07002315 const SkOpSegment* segment = &fHead;
caryclark55888e42016-07-18 10:01:36 -07002316// bool result = false;
caryclark26ad22a2015-10-16 09:03:38 -07002317 do {
Cary Clarke47ae292016-10-05 08:51:39 -04002318 if (segment->debugMissingCoincidence(log), false) {
caryclark55888e42016-07-18 10:01:36 -07002319// result = true;
caryclark55888e42016-07-18 10:01:36 -07002320 }
2321 segment = segment->next();
2322 } while (segment);
2323 return;
caryclark26ad22a2015-10-16 09:03:38 -07002324}
Cary Clarkab87d7a2016-10-04 10:01:04 -04002325
2326void SkOpContour::debugMoveMultiples(SkPathOpsDebug::GlitchLog* log) const {
2327 SkASSERT(fCount > 0);
2328 const SkOpSegment* segment = &fHead;
2329 do {
2330 if (segment->debugMoveMultiples(log), false) {
2331 return;
2332 }
2333 } while ((segment = segment->next()));
2334 return;
2335}
2336
2337void SkOpContour::debugMoveNearby(SkPathOpsDebug::GlitchLog* log) const {
2338 SkASSERT(fCount > 0);
2339 const SkOpSegment* segment = &fHead;
2340 do {
2341 segment->debugMoveNearby(log);
2342 } while ((segment = segment->next()));
2343}
caryclark26ad22a2015-10-16 09:03:38 -07002344#endif
2345
caryclark025b11e2016-08-25 05:21:14 -07002346#if DEBUG_COINCIDENCE_ORDER
2347void SkOpSegment::debugResetCoinT() const {
2348 fDebugBaseIndex = -1;
2349 fDebugBaseMin = 1;
2350 fDebugBaseMax = -1;
2351 fDebugLastIndex = -1;
2352 fDebugLastMin = 1;
2353 fDebugLastMax = -1;
2354}
2355#endif
2356
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002357void SkOpSegment::debugValidate() const {
caryclark025b11e2016-08-25 05:21:14 -07002358#if DEBUG_COINCIDENCE_ORDER
2359 {
2360 const SkOpSpanBase* span = &fHead;
2361 do {
2362 span->debugResetCoinT();
2363 } while (!span->final() && (span = span->upCast()->next()));
2364 span = &fHead;
2365 int index = 0;
2366 do {
2367 span->debugSetCoinT(index++);
2368 } while (!span->final() && (span = span->upCast()->next()));
2369 }
2370#endif
caryclark55888e42016-07-18 10:01:36 -07002371#if DEBUG_COINCIDENCE
2372 if (this->globalState()->debugCheckHealth()) {
2373 return;
2374 }
2375#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002376#if DEBUG_VALIDATE
caryclark54359292015-03-26 07:52:43 -07002377 const SkOpSpanBase* span = &fHead;
2378 double lastT = -1;
halcanary96fcdcc2015-08-27 07:41:13 -07002379 const SkOpSpanBase* prev = nullptr;
caryclark54359292015-03-26 07:52:43 -07002380 int count = 0;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002381 int done = 0;
caryclark54359292015-03-26 07:52:43 -07002382 do {
2383 if (!span->final()) {
2384 ++count;
2385 done += span->upCast()->done() ? 1 : 0;
2386 }
2387 SkASSERT(span->segment() == this);
2388 SkASSERT(!prev || prev->upCast()->next() == span);
2389 SkASSERT(!prev || prev == span->prev());
2390 prev = span;
2391 double t = span->ptT()->fT;
2392 SkASSERT(lastT < t);
2393 lastT = t;
2394 span->debugValidate();
2395 } while (!span->final() && (span = span->upCast()->next()));
2396 SkASSERT(count == fCount);
2397 SkASSERT(done == fDoneCount);
caryclark08bc8482015-04-24 09:08:57 -07002398 SkASSERT(count >= fDoneCount);
caryclark54359292015-03-26 07:52:43 -07002399 SkASSERT(span->final());
2400 span->debugValidate();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002401#endif
caryclark54359292015-03-26 07:52:43 -07002402}
2403
Cary Clarkab87d7a2016-10-04 10:01:04 -04002404#if DEBUG_COIN
caryclark30b9fdd2016-08-31 14:36:29 -07002405
2406// Commented-out lines keep this in sync with addOpp()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002407void SkOpSpanBase::debugAddOpp(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002408 const SkOpPtT* oppPrev = this->ptT()->oppPrev(opp->ptT());
2409 if (!oppPrev) {
caryclark55888e42016-07-18 10:01:36 -07002410 return;
caryclark26ad22a2015-10-16 09:03:38 -07002411 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002412 this->debugMergeMatches(log, opp);
caryclark30b9fdd2016-08-31 14:36:29 -07002413 this->ptT()->debugAddOpp(opp->ptT(), oppPrev);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002414 this->debugCheckForCollapsedCoincidence(log);
caryclark26ad22a2015-10-16 09:03:38 -07002415}
2416
caryclark55888e42016-07-18 10:01:36 -07002417// Commented-out lines keep this in sync with checkForCollapsedCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002418void SkOpSpanBase::debugCheckForCollapsedCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07002419 const SkOpCoincidence* coins = this->globalState()->coincidence();
2420 if (coins->isEmpty()) {
2421 return;
2422 }
2423// the insert above may have put both ends of a coincident run in the same span
2424// for each coincident ptT in loop; see if its opposite in is also in the loop
2425// this implementation is the motivation for marking that a ptT is referenced by a coincident span
2426 const SkOpPtT* head = this->ptT();
2427 const SkOpPtT* test = head;
caryclark26ad22a2015-10-16 09:03:38 -07002428 do {
caryclark55888e42016-07-18 10:01:36 -07002429 if (!test->coincident()) {
2430 continue;
caryclark26ad22a2015-10-16 09:03:38 -07002431 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002432 coins->debugMarkCollapsed(log, test);
caryclark55888e42016-07-18 10:01:36 -07002433 } while ((test = test->next()) != head);
caryclark26ad22a2015-10-16 09:03:38 -07002434}
caryclark55888e42016-07-18 10:01:36 -07002435#endif
caryclark26ad22a2015-10-16 09:03:38 -07002436
caryclark54359292015-03-26 07:52:43 -07002437bool SkOpSpanBase::debugCoinEndLoopCheck() const {
2438 int loop = 0;
2439 const SkOpSpanBase* next = this;
2440 SkOpSpanBase* nextCoin;
2441 do {
2442 nextCoin = next->fCoinEnd;
2443 SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin);
2444 for (int check = 1; check < loop - 1; ++check) {
2445 const SkOpSpanBase* checkCoin = this->fCoinEnd;
2446 const SkOpSpanBase* innerCoin = checkCoin;
2447 for (int inner = check + 1; inner < loop; ++inner) {
2448 innerCoin = innerCoin->fCoinEnd;
2449 if (checkCoin == innerCoin) {
2450 SkDebugf("*** bad coincident end loop ***\n");
2451 return false;
2452 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002453 }
2454 }
caryclark54359292015-03-26 07:52:43 -07002455 ++loop;
2456 } while ((next = nextCoin) && next != this);
2457 return true;
2458}
2459
Cary Clarkab87d7a2016-10-04 10:01:04 -04002460#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002461// Commented-out lines keep this in sync with insertCoinEnd()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002462void SkOpSpanBase::debugInsertCoinEnd(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* coin) const {
caryclark55888e42016-07-18 10:01:36 -07002463 if (containsCoinEnd(coin)) {
2464// SkASSERT(coin->containsCoinEnd(this));
2465 return;
2466 }
2467 debugValidate();
2468// SkASSERT(this != coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002469 log->record(SkPathOpsDebug::kMarkCoinEnd_Glitch, this, coin);
caryclark55888e42016-07-18 10:01:36 -07002470// coin->fCoinEnd = this->fCoinEnd;
2471// this->fCoinEnd = coinNext;
2472 debugValidate();
2473}
2474
caryclark30b9fdd2016-08-31 14:36:29 -07002475// Commented-out lines keep this in sync with mergeMatches()
2476// Look to see if pt-t linked list contains same segment more than once
2477// if so, and if each pt-t is directly pointed to by spans in that segment,
2478// merge them
2479// keep the points, but remove spans so that the segment doesn't have 2 or more
2480// spans pointing to the same pt-t loop at different loop elements
Cary Clarkab87d7a2016-10-04 10:01:04 -04002481void SkOpSpanBase::debugMergeMatches(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002482 const SkOpPtT* test = &fPtT;
2483 const SkOpPtT* testNext;
2484 const SkOpPtT* stop = test;
2485 do {
2486 testNext = test->next();
2487 if (test->deleted()) {
2488 continue;
2489 }
2490 const SkOpSpanBase* testBase = test->span();
2491 SkASSERT(testBase->ptT() == test);
2492 const SkOpSegment* segment = test->segment();
2493 if (segment->done()) {
2494 continue;
2495 }
2496 const SkOpPtT* inner = opp->ptT();
2497 const SkOpPtT* innerStop = inner;
2498 do {
2499 if (inner->segment() != segment) {
2500 continue;
2501 }
2502 if (inner->deleted()) {
2503 continue;
2504 }
2505 const SkOpSpanBase* innerBase = inner->span();
2506 SkASSERT(innerBase->ptT() == inner);
2507 // when the intersection is first detected, the span base is marked if there are
2508 // more than one point in the intersection.
2509// if (!innerBase->hasMultipleHint() && !testBase->hasMultipleHint()) {
2510 if (!zero_or_one(inner->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002511 log->record(SkPathOpsDebug::kMergeMatches_Glitch, innerBase, test);
caryclark30b9fdd2016-08-31 14:36:29 -07002512 } else {
2513 SkASSERT(inner->fT != test->fT);
2514 if (!zero_or_one(test->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002515 log->record(SkPathOpsDebug::kMergeMatches_Glitch, testBase, inner);
caryclark30b9fdd2016-08-31 14:36:29 -07002516 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002517 log->record(SkPathOpsDebug::kMergeMatches_Glitch, segment);
caryclark30b9fdd2016-08-31 14:36:29 -07002518// SkDEBUGCODE(testBase->debugSetDeleted());
2519// test->setDeleted();
2520// SkDEBUGCODE(innerBase->debugSetDeleted());
2521// inner->setDeleted();
2522 }
2523 }
2524#ifdef SK_DEBUG // assert if another undeleted entry points to segment
2525 const SkOpPtT* debugInner = inner;
2526 while ((debugInner = debugInner->next()) != innerStop) {
2527 if (debugInner->segment() != segment) {
2528 continue;
2529 }
2530 if (debugInner->deleted()) {
2531 continue;
2532 }
2533 SkOPASSERT(0);
2534 }
2535#endif
2536 break;
2537// }
2538 break;
2539 } while ((inner = inner->next()) != innerStop);
2540 } while ((test = testNext) != stop);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002541 this->debugCheckForCollapsedCoincidence(log);
caryclark30b9fdd2016-08-31 14:36:29 -07002542}
2543
caryclark55888e42016-07-18 10:01:36 -07002544#endif
caryclark26ad22a2015-10-16 09:03:38 -07002545
caryclark025b11e2016-08-25 05:21:14 -07002546void SkOpSpanBase::debugResetCoinT() const {
2547#if DEBUG_COINCIDENCE_ORDER
2548 const SkOpPtT* ptT = &fPtT;
2549 do {
2550 ptT->debugResetCoinT();
2551 ptT = ptT->next();
2552 } while (ptT != &fPtT);
2553#endif
2554}
2555
2556void SkOpSpanBase::debugSetCoinT(int index) const {
2557#if DEBUG_COINCIDENCE_ORDER
2558 const SkOpPtT* ptT = &fPtT;
2559 do {
2560 if (!ptT->deleted()) {
2561 ptT->debugSetCoinT(index);
2562 }
2563 ptT = ptT->next();
2564 } while (ptT != &fPtT);
2565#endif
2566}
2567
caryclark26ad22a2015-10-16 09:03:38 -07002568const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const {
2569 const SkOpSpanBase* end = *endPtr;
2570 SkASSERT(this->segment() == end->segment());
2571 const SkOpSpanBase* result;
2572 if (t() < end->t()) {
2573 result = this;
2574 } else {
2575 result = end;
2576 *endPtr = this;
2577 }
2578 return result->upCast();
2579}
2580
caryclark54359292015-03-26 07:52:43 -07002581void SkOpSpanBase::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07002582#if DEBUG_COINCIDENCE
2583 if (this->globalState()->debugCheckHealth()) {
2584 return;
2585 }
2586#endif
caryclark54359292015-03-26 07:52:43 -07002587#if DEBUG_VALIDATE
2588 const SkOpPtT* ptT = &fPtT;
2589 SkASSERT(ptT->span() == this);
2590 do {
2591// SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt));
2592 ptT->debugValidate();
2593 ptT = ptT->next();
2594 } while (ptT != &fPtT);
2595 SkASSERT(this->debugCoinEndLoopCheck());
2596 if (!this->final()) {
2597 SkASSERT(this->upCast()->debugCoinLoopCheck());
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002598 }
caryclark54359292015-03-26 07:52:43 -07002599 if (fFromAngle) {
2600 fFromAngle->debugValidate();
2601 }
2602 if (!this->final() && this->upCast()->toAngle()) {
2603 this->upCast()->toAngle()->debugValidate();
2604 }
2605#endif
2606}
2607
2608bool SkOpSpan::debugCoinLoopCheck() const {
2609 int loop = 0;
2610 const SkOpSpan* next = this;
2611 SkOpSpan* nextCoin;
2612 do {
2613 nextCoin = next->fCoincident;
2614 SkASSERT(nextCoin == this || nextCoin->fCoincident != nextCoin);
2615 for (int check = 1; check < loop - 1; ++check) {
2616 const SkOpSpan* checkCoin = this->fCoincident;
2617 const SkOpSpan* innerCoin = checkCoin;
2618 for (int inner = check + 1; inner < loop; ++inner) {
2619 innerCoin = innerCoin->fCoincident;
2620 if (checkCoin == innerCoin) {
2621 SkDebugf("*** bad coincident loop ***\n");
2622 return false;
2623 }
2624 }
2625 }
2626 ++loop;
2627 } while ((next = nextCoin) && next != this);
2628 return true;
2629}
2630
Cary Clarkab87d7a2016-10-04 10:01:04 -04002631#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002632// Commented-out lines keep this in sync with insertCoincidence() in header
Cary Clarkab87d7a2016-10-04 10:01:04 -04002633void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* coin) const {
caryclark55888e42016-07-18 10:01:36 -07002634 if (containsCoincidence(coin)) {
2635// SkASSERT(coin->containsCoincidence(this));
2636 return;
2637 }
2638 debugValidate();
2639// SkASSERT(this != coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002640 log->record(SkPathOpsDebug::kMarkCoinStart_Glitch, this, coin);
caryclark55888e42016-07-18 10:01:36 -07002641// coin->fCoincident = this->fCoincident;
2642// this->fCoincident = coinNext;
2643 debugValidate();
2644}
2645
2646// Commented-out lines keep this in sync with insertCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002647void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* segment, bool flipped, bool ordered) const {
caryclark55888e42016-07-18 10:01:36 -07002648 if (this->containsCoincidence(segment)) {
2649 return;
2650 }
2651 const SkOpPtT* next = &fPtT;
2652 while ((next = next->next()) != &fPtT) {
2653 if (next->segment() == segment) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002654 const SkOpSpan* span;
2655 const SkOpSpanBase* base = next->span();
2656 if (!ordered) {
2657 const SkOpSpanBase* spanEnd = fNext->contains(segment)->span();
2658 const SkOpPtT* start = base->ptT()->starter(spanEnd->ptT());
2659 FAIL_IF(!start->span()->upCastable(), this);
2660 span = const_cast<SkOpSpan*>(start->span()->upCast());
2661 }
2662 else if (flipped) {
2663 span = base->prev();
2664 FAIL_IF(!span, this);
2665 }
2666 else {
2667 FAIL_IF(!base->upCastable(), this);
2668 span = base->upCast();
2669 }
2670 log->record(SkPathOpsDebug::kMarkCoinInsert_Glitch, span);
caryclark55888e42016-07-18 10:01:36 -07002671 return;
2672 }
2673 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002674#if DEBUG_COIN
2675 log->record(SkPathOpsDebug::kMarkCoinMissing_Glitch, segment, this);
caryclark55888e42016-07-18 10:01:36 -07002676#endif
Cary Clarkab87d7a2016-10-04 10:01:04 -04002677 return;
caryclark55888e42016-07-18 10:01:36 -07002678}
2679#endif
2680
caryclark624637c2015-05-11 07:21:27 -07002681// called only by test code
2682int SkIntersections::debugCoincidentUsed() const {
2683 if (!fIsCoincident[0]) {
2684 SkASSERT(!fIsCoincident[1]);
2685 return 0;
2686 }
2687 int count = 0;
2688 SkDEBUGCODE(int count2 = 0;)
2689 for (int index = 0; index < fUsed; ++index) {
2690 if (fIsCoincident[0] & (1 << index)) {
2691 ++count;
2692 }
2693#ifdef SK_DEBUG
2694 if (fIsCoincident[1] & (1 << index)) {
2695 ++count2;
2696 }
2697#endif
2698 }
2699 SkASSERT(count == count2);
2700 return count;
2701}
2702
caryclark54359292015-03-26 07:52:43 -07002703#include "SkOpContour.h"
2704
caryclark55888e42016-07-18 10:01:36 -07002705// Commented-out lines keep this in sync with addOpp()
caryclark29b25632016-08-25 11:27:17 -07002706void SkOpPtT::debugAddOpp(const SkOpPtT* opp, const SkOpPtT* oppPrev) const {
2707 SkDEBUGCODE(const SkOpPtT* oldNext = this->fNext);
caryclark55888e42016-07-18 10:01:36 -07002708 SkASSERT(this != opp);
2709// this->fNext = opp;
caryclark29b25632016-08-25 11:27:17 -07002710 SkASSERT(oppPrev != oldNext);
caryclark55888e42016-07-18 10:01:36 -07002711// oppPrev->fNext = oldNext;
caryclark55888e42016-07-18 10:01:36 -07002712}
2713
caryclark26ad22a2015-10-16 09:03:38 -07002714bool SkOpPtT::debugContains(const SkOpPtT* check) const {
2715 SkASSERT(this != check);
2716 const SkOpPtT* ptT = this;
2717 int links = 0;
2718 do {
2719 ptT = ptT->next();
2720 if (ptT == check) {
2721 return true;
2722 }
2723 ++links;
2724 const SkOpPtT* test = this;
2725 for (int index = 0; index < links; ++index) {
2726 if (ptT == test) {
2727 return false;
2728 }
2729 test = test->next();
2730 }
2731 } while (true);
2732}
2733
2734const SkOpPtT* SkOpPtT::debugContains(const SkOpSegment* check) const {
2735 SkASSERT(this->segment() != check);
2736 const SkOpPtT* ptT = this;
2737 int links = 0;
2738 do {
2739 ptT = ptT->next();
2740 if (ptT->segment() == check) {
2741 return ptT;
2742 }
2743 ++links;
2744 const SkOpPtT* test = this;
2745 for (int index = 0; index < links; ++index) {
2746 if (ptT == test) {
2747 return nullptr;
2748 }
2749 test = test->next();
2750 }
2751 } while (true);
2752}
2753
caryclark8016b262016-09-06 05:59:47 -07002754const SkOpPtT* SkOpPtT::debugEnder(const SkOpPtT* end) const {
2755 return fT < end->fT ? end : this;
2756}
2757
caryclark54359292015-03-26 07:52:43 -07002758int SkOpPtT::debugLoopLimit(bool report) const {
2759 int loop = 0;
2760 const SkOpPtT* next = this;
2761 do {
2762 for (int check = 1; check < loop - 1; ++check) {
2763 const SkOpPtT* checkPtT = this->fNext;
2764 const SkOpPtT* innerPtT = checkPtT;
2765 for (int inner = check + 1; inner < loop; ++inner) {
2766 innerPtT = innerPtT->fNext;
2767 if (checkPtT == innerPtT) {
2768 if (report) {
2769 SkDebugf("*** bad ptT loop ***\n");
2770 }
2771 return loop;
2772 }
2773 }
2774 }
caryclark26ad22a2015-10-16 09:03:38 -07002775 // there's nothing wrong with extremely large loop counts -- but this may appear to hang
2776 // by taking a very long time to figure out that no loop entry is a duplicate
2777 // -- and it's likely that a large loop count is indicative of a bug somewhere
2778 if (++loop > 1000) {
2779 SkDebugf("*** loop count exceeds 1000 ***\n");
2780 return 1000;
2781 }
caryclark54359292015-03-26 07:52:43 -07002782 } while ((next = next->fNext) && next != this);
2783 return 0;
2784}
2785
caryclark29b25632016-08-25 11:27:17 -07002786const SkOpPtT* SkOpPtT::debugOppPrev(const SkOpPtT* opp) const {
2787 return this->oppPrev(const_cast<SkOpPtT*>(opp));
2788}
2789
caryclark025b11e2016-08-25 05:21:14 -07002790void SkOpPtT::debugResetCoinT() const {
2791#if DEBUG_COINCIDENCE_ORDER
2792 this->segment()->debugResetCoinT();
2793#endif
2794}
2795
2796void SkOpPtT::debugSetCoinT(int index) const {
2797#if DEBUG_COINCIDENCE_ORDER
2798 this->segment()->debugSetCoinT(index, fT);
2799#endif
2800}
2801
caryclark54359292015-03-26 07:52:43 -07002802void SkOpPtT::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07002803#if DEBUG_COINCIDENCE
2804 if (this->globalState()->debugCheckHealth()) {
2805 return;
2806 }
2807#endif
caryclark54359292015-03-26 07:52:43 -07002808#if DEBUG_VALIDATE
Cary Clarkab87d7a2016-10-04 10:01:04 -04002809 SkOpPhase phase = contour()->globalState()->phase();
2810 if (phase == SkOpPhase::kIntersecting || phase == SkOpPhase::kFixWinding) {
caryclark54359292015-03-26 07:52:43 -07002811 return;
2812 }
2813 SkASSERT(fNext);
2814 SkASSERT(fNext != this);
2815 SkASSERT(fNext->fNext);
2816 SkASSERT(debugLoopLimit(false) == 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002817#endif
2818}
caryclark1049f122015-04-20 08:31:59 -07002819
2820static void output_scalar(SkScalar num) {
2821 if (num == (int) num) {
2822 SkDebugf("%d", (int) num);
2823 } else {
2824 SkString str;
2825 str.printf("%1.9g", num);
2826 int width = (int) str.size();
2827 const char* cStr = str.c_str();
2828 while (cStr[width - 1] == '0') {
2829 --width;
2830 }
2831 str.resize(width);
2832 SkDebugf("%sf", str.c_str());
2833 }
2834}
2835
2836static void output_points(const SkPoint* pts, int count) {
2837 for (int index = 0; index < count; ++index) {
2838 output_scalar(pts[index].fX);
2839 SkDebugf(", ");
2840 output_scalar(pts[index].fY);
2841 if (index + 1 < count) {
2842 SkDebugf(", ");
2843 }
2844 }
2845}
2846
2847static void showPathContours(SkPath::RawIter& iter, const char* pathName) {
2848 uint8_t verb;
2849 SkPoint pts[4];
2850 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
2851 switch (verb) {
2852 case SkPath::kMove_Verb:
2853 SkDebugf(" %s.moveTo(", pathName);
2854 output_points(&pts[0], 1);
2855 SkDebugf(");\n");
2856 continue;
2857 case SkPath::kLine_Verb:
2858 SkDebugf(" %s.lineTo(", pathName);
2859 output_points(&pts[1], 1);
2860 SkDebugf(");\n");
2861 break;
2862 case SkPath::kQuad_Verb:
2863 SkDebugf(" %s.quadTo(", pathName);
2864 output_points(&pts[1], 2);
2865 SkDebugf(");\n");
2866 break;
2867 case SkPath::kConic_Verb:
2868 SkDebugf(" %s.conicTo(", pathName);
2869 output_points(&pts[1], 2);
2870 SkDebugf(", %1.9gf);\n", iter.conicWeight());
2871 break;
2872 case SkPath::kCubic_Verb:
2873 SkDebugf(" %s.cubicTo(", pathName);
2874 output_points(&pts[1], 3);
2875 SkDebugf(");\n");
2876 break;
2877 case SkPath::kClose_Verb:
2878 SkDebugf(" %s.close();\n", pathName);
2879 break;
2880 default:
2881 SkDEBUGFAIL("bad verb");
2882 return;
2883 }
2884 }
2885}
2886
2887static const char* gFillTypeStr[] = {
2888 "kWinding_FillType",
2889 "kEvenOdd_FillType",
2890 "kInverseWinding_FillType",
2891 "kInverseEvenOdd_FillType"
2892};
2893
2894void SkPathOpsDebug::ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration) {
2895 SkPath::RawIter iter(path);
2896#define SUPPORT_RECT_CONTOUR_DETECTION 0
2897#if SUPPORT_RECT_CONTOUR_DETECTION
halcanary96fcdcc2015-08-27 07:41:13 -07002898 int rectCount = path.isRectContours() ? path.rectContours(nullptr, nullptr) : 0;
caryclark1049f122015-04-20 08:31:59 -07002899 if (rectCount > 0) {
2900 SkTDArray<SkRect> rects;
2901 SkTDArray<SkPath::Direction> directions;
2902 rects.setCount(rectCount);
2903 directions.setCount(rectCount);
2904 path.rectContours(rects.begin(), directions.begin());
2905 for (int contour = 0; contour < rectCount; ++contour) {
2906 const SkRect& rect = rects[contour];
2907 SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
2908 rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
2909 ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
2910 }
2911 return;
2912 }
2913#endif
2914 SkPath::FillType fillType = path.getFillType();
2915 SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType);
2916 if (includeDeclaration) {
2917 SkDebugf(" SkPath %s;\n", name);
2918 }
2919 SkDebugf(" %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[fillType]);
2920 iter.setPath(path);
2921 showPathContours(iter, name);
2922}
caryclark13260682016-10-24 05:10:14 -07002923
Cary Clark918fb1f2016-11-15 13:22:25 -05002924#if DEBUG_DUMP_VERIFY
caryclark13260682016-10-24 05:10:14 -07002925#include "SkData.h"
2926#include "SkStream.h"
2927
2928static void dump_path(FILE* file, const SkPath& path, bool force, bool dumpAsHex) {
2929 SkDynamicMemoryWStream wStream;
2930 path.dump(&wStream, force, dumpAsHex);
2931 sk_sp<SkData> data(wStream.detachAsData());
2932 fprintf(file, "%.*s\n", (int) data->size(), (char*) data->data());
2933}
2934
2935static int dumpID = 0;
2936
2937void SkPathOpsDebug::DumpOp(const SkPath& one, const SkPath& two, SkPathOp op,
2938 const char* testName) {
2939 FILE* file = sk_fopen("op_dump.txt", kWrite_SkFILE_Flag);
2940 DumpOp(file, one, two, op, testName);
2941}
2942
2943void SkPathOpsDebug::DumpOp(FILE* file, const SkPath& one, const SkPath& two, SkPathOp op,
2944 const char* testName) {
2945 const char* name = testName ? testName : "op";
2946 fprintf(file,
2947 "\nstatic void %s_%d(skiatest::Reporter* reporter, const char* filename) {\n",
2948 name, ++dumpID);
2949 fprintf(file, " SkPath path;\n");
2950 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", one.getFillType());
2951 dump_path(file, one, false, true);
2952 fprintf(file, " SkPath path1(path);\n");
2953 fprintf(file, " path.reset();\n");
2954 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", two.getFillType());
2955 dump_path(file, two, false, true);
2956 fprintf(file, " SkPath path2(path);\n");
2957 fprintf(file, " testPathOp(reporter, path1, path2, (SkPathOp) %d, filename);\n", op);
2958 fprintf(file, "}\n\n");
2959 fclose(file);
2960}
2961
2962void SkPathOpsDebug::DumpSimplify(const SkPath& path, const char* testName) {
2963 FILE* file = sk_fopen("simplify_dump.txt", kWrite_SkFILE_Flag);
2964 DumpSimplify(file, path, testName);
2965}
2966
2967void SkPathOpsDebug::DumpSimplify(FILE* file, const SkPath& path, const char* testName) {
2968 const char* name = testName ? testName : "simplify";
2969 fprintf(file,
2970 "\nstatic void %s_%d(skiatest::Reporter* reporter, const char* filename) {\n",
2971 name, ++dumpID);
2972 fprintf(file, " SkPath path;\n");
2973 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", path.getFillType());
2974 dump_path(file, path, false, true);
2975 fprintf(file, " testSimplify(reporter, path, filename);\n");
2976 fprintf(file, "}\n\n");
2977 fclose(file);
2978}
2979
2980#include "SkBitmap.h"
2981#include "SkCanvas.h"
2982#include "SkPaint.h"
2983
2984const int bitWidth = 64;
2985const int bitHeight = 64;
2986
2987static void debug_scale_matrix(const SkPath& one, const SkPath* two, SkMatrix& scale) {
2988 SkRect larger = one.getBounds();
2989 if (two) {
2990 larger.join(two->getBounds());
2991 }
2992 SkScalar largerWidth = larger.width();
2993 if (largerWidth < 4) {
2994 largerWidth = 4;
2995 }
2996 SkScalar largerHeight = larger.height();
2997 if (largerHeight < 4) {
2998 largerHeight = 4;
2999 }
3000 SkScalar hScale = (bitWidth - 2) / largerWidth;
3001 SkScalar vScale = (bitHeight - 2) / largerHeight;
3002 scale.reset();
3003 scale.preScale(hScale, vScale);
3004 larger.fLeft *= hScale;
3005 larger.fRight *= hScale;
3006 larger.fTop *= vScale;
3007 larger.fBottom *= vScale;
3008 SkScalar dx = -16000 > larger.fLeft ? -16000 - larger.fLeft
3009 : 16000 < larger.fRight ? 16000 - larger.fRight : 0;
3010 SkScalar dy = -16000 > larger.fTop ? -16000 - larger.fTop
3011 : 16000 < larger.fBottom ? 16000 - larger.fBottom : 0;
3012 scale.preTranslate(dx, dy);
3013}
3014
3015static int debug_paths_draw_the_same(const SkPath& one, const SkPath& two, SkBitmap& bits) {
3016 if (bits.width() == 0) {
3017 bits.allocN32Pixels(bitWidth * 2, bitHeight);
3018 }
3019 SkCanvas canvas(bits);
3020 canvas.drawColor(SK_ColorWHITE);
3021 SkPaint paint;
3022 canvas.save();
3023 const SkRect& bounds1 = one.getBounds();
3024 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
3025 canvas.drawPath(one, paint);
3026 canvas.restore();
3027 canvas.save();
3028 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
3029 canvas.drawPath(two, paint);
3030 canvas.restore();
3031 int errors = 0;
3032 for (int y = 0; y < bitHeight - 1; ++y) {
3033 uint32_t* addr1 = bits.getAddr32(0, y);
3034 uint32_t* addr2 = bits.getAddr32(0, y + 1);
3035 uint32_t* addr3 = bits.getAddr32(bitWidth, y);
3036 uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1);
3037 for (int x = 0; x < bitWidth - 1; ++x) {
3038 // count 2x2 blocks
3039 bool err = addr1[x] != addr3[x];
3040 if (err) {
3041 errors += addr1[x + 1] != addr3[x + 1]
3042 && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1];
3043 }
3044 }
3045 }
3046 return errors;
3047}
3048
3049void SkPathOpsDebug::ReportOpFail(const SkPath& one, const SkPath& two, SkPathOp op) {
3050 SkDebugf("// Op did not expect failure\n");
3051 DumpOp(stderr, one, two, op, "opTest");
3052 fflush(stderr);
3053}
3054
3055void SkPathOpsDebug::VerifyOp(const SkPath& one, const SkPath& two, SkPathOp op,
3056 const SkPath& result) {
3057 SkPath pathOut, scaledPathOut;
3058 SkRegion rgnA, rgnB, openClip, rgnOut;
3059 openClip.setRect(-16000, -16000, 16000, 16000);
3060 rgnA.setPath(one, openClip);
3061 rgnB.setPath(two, openClip);
3062 rgnOut.op(rgnA, rgnB, (SkRegion::Op) op);
3063 rgnOut.getBoundaryPath(&pathOut);
3064 SkMatrix scale;
3065 debug_scale_matrix(one, &two, scale);
3066 SkRegion scaledRgnA, scaledRgnB, scaledRgnOut;
3067 SkPath scaledA, scaledB;
3068 scaledA.addPath(one, scale);
3069 scaledA.setFillType(one.getFillType());
3070 scaledB.addPath(two, scale);
3071 scaledB.setFillType(two.getFillType());
3072 scaledRgnA.setPath(scaledA, openClip);
3073 scaledRgnB.setPath(scaledB, openClip);
3074 scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) op);
3075 scaledRgnOut.getBoundaryPath(&scaledPathOut);
3076 SkBitmap bitmap;
3077 SkPath scaledOut;
3078 scaledOut.addPath(result, scale);
3079 scaledOut.setFillType(result.getFillType());
3080 int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
3081 const int MAX_ERRORS = 9;
3082 if (errors > MAX_ERRORS) {
3083 fprintf(stderr, "// Op did not expect errors=%d\n", errors);
3084 DumpOp(stderr, one, two, op, "opTest");
3085 fflush(stderr);
3086 }
3087}
3088
3089void SkPathOpsDebug::ReportSimplifyFail(const SkPath& path) {
3090 SkDebugf("// Simplify did not expect failure\n");
3091 DumpSimplify(stderr, path, "simplifyTest");
3092 fflush(stderr);
3093}
3094
3095void SkPathOpsDebug::VerifySimplify(const SkPath& path, const SkPath& result) {
3096 SkPath pathOut, scaledPathOut;
3097 SkRegion rgnA, openClip, rgnOut;
3098 openClip.setRect(-16000, -16000, 16000, 16000);
3099 rgnA.setPath(path, openClip);
3100 rgnOut.getBoundaryPath(&pathOut);
3101 SkMatrix scale;
3102 debug_scale_matrix(path, nullptr, scale);
3103 SkRegion scaledRgnA;
3104 SkPath scaledA;
3105 scaledA.addPath(path, scale);
3106 scaledA.setFillType(path.getFillType());
3107 scaledRgnA.setPath(scaledA, openClip);
3108 scaledRgnA.getBoundaryPath(&scaledPathOut);
3109 SkBitmap bitmap;
3110 SkPath scaledOut;
3111 scaledOut.addPath(result, scale);
3112 scaledOut.setFillType(result.getFillType());
3113 int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
3114 const int MAX_ERRORS = 9;
3115 if (errors > MAX_ERRORS) {
3116 fprintf(stderr, "// Simplify did not expect errors=%d\n", errors);
3117 DumpSimplify(stderr, path, "simplifyTest");
3118 fflush(stderr);
3119 }
3120}
3121
3122#endif