blob: a0fcff58e01e31e0d3f8f4c7eb200e0f9dbde27c [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"
jvanverth02802f62015-07-02 06:42:49 -070011#include "SkPath.h"
mtklein1b249332015-07-07 12:21:21 -070012#include "SkPathOpsDebug.h"
caryclark54359292015-03-26 07:52:43 -070013#include "SkString.h"
caryclark54359292015-03-26 07:52:43 -070014
caryclark30b9fdd2016-08-31 14:36:29 -070015#undef FAIL_IF
16#define FAIL_IF(cond, coin) \
Cary Clarkab87d7a2016-10-04 10:01:04 -040017 do { if (cond) log->record(SkPathOpsDebug::kFail_Glitch, coin); } while (false)
caryclark30b9fdd2016-08-31 14:36:29 -070018
19#undef FAIL_WITH_NULL_IF
20#define FAIL_WITH_NULL_IF(cond, span) \
Cary Clarkab87d7a2016-10-04 10:01:04 -040021 do { if (cond) log->record(SkPathOpsDebug::kFail_Glitch, span); } while (false)
caryclark30b9fdd2016-08-31 14:36:29 -070022
23#undef RETURN_FALSE_IF
24#define RETURN_FALSE_IF(cond, span) \
Cary Clarkab87d7a2016-10-04 10:01:04 -040025 do { if (cond) log->record(SkPathOpsDebug::kReturnFalse_Glitch, span); \
26 } while (false)
caryclark30b9fdd2016-08-31 14:36:29 -070027
caryclark55888e42016-07-18 10:01:36 -070028class SkCoincidentSpans;
caryclark26ad22a2015-10-16 09:03:38 -070029
caryclark54359292015-03-26 07:52:43 -070030#if DEBUG_VALIDATE
31extern bool FLAGS_runFail;
32#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +000033
caryclark624637c2015-05-11 07:21:27 -070034#if DEBUG_SORT
35int SkPathOpsDebug::gSortCountDefault = SK_MaxS32;
36int SkPathOpsDebug::gSortCount;
37#endif
38
39#if DEBUG_ACTIVE_OP
40const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor"};
41#endif
42
caryclark@google.com07393ca2013-04-08 11:47:37 +000043#if defined SK_DEBUG || !FORCE_RELEASE
44
caryclark@google.com570863f2013-09-16 15:55:01 +000045const char* SkPathOpsDebug::kLVerbStr[] = {"", "line", "quad", "cubic"};
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +000046
commit-bot@chromium.orgfe41b8f2014-04-25 14:03:52 +000047int SkPathOpsDebug::gContourID = 0;
48int SkPathOpsDebug::gSegmentID = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +000049
caryclark54359292015-03-26 07:52:43 -070050bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray,
51 const SkOpSpanBase* span) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000052 for (int index = 0; index < chaseArray.count(); ++index) {
caryclark54359292015-03-26 07:52:43 -070053 const SkOpSpanBase* entry = chaseArray[index];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000054 if (entry == span) {
55 return true;
56 }
57 }
58 return false;
59}
caryclark26ad22a2015-10-16 09:03:38 -070060#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000061
Cary Clarkab87d7a2016-10-04 10:01:04 -040062#if DEBUG_COIN
caryclark26ad22a2015-10-16 09:03:38 -070063
Cary Clarkab87d7a2016-10-04 10:01:04 -040064SkPathOpsDebug::CoinDict SkPathOpsDebug::gCoinSumChangedDict;
65SkPathOpsDebug::CoinDict SkPathOpsDebug::gCoinSumVisitedDict;
66
67static const int kGlitchType_Count = SkPathOpsDebug::kUnalignedTail_Glitch + 1;
caryclark26ad22a2015-10-16 09:03:38 -070068
69struct SpanGlitch {
caryclark26ad22a2015-10-16 09:03:38 -070070 const SkOpSpanBase* fBase;
71 const SkOpSpanBase* fSuspect;
caryclark26ad22a2015-10-16 09:03:38 -070072 const SkOpSegment* fSegment;
caryclark55888e42016-07-18 10:01:36 -070073 const SkOpSegment* fOppSegment;
caryclark26ad22a2015-10-16 09:03:38 -070074 const SkOpPtT* fCoinSpan;
75 const SkOpPtT* fEndSpan;
76 const SkOpPtT* fOppSpan;
77 const SkOpPtT* fOppEndSpan;
caryclark55888e42016-07-18 10:01:36 -070078 double fStartT;
79 double fEndT;
80 double fOppStartT;
81 double fOppEndT;
caryclark26ad22a2015-10-16 09:03:38 -070082 SkPoint fPt;
Cary Clarkab87d7a2016-10-04 10:01:04 -040083 SkPathOpsDebug::GlitchType fType;
84
85 void dumpType() const;
caryclark26ad22a2015-10-16 09:03:38 -070086};
87
88struct SkPathOpsDebug::GlitchLog {
Cary Clarkab87d7a2016-10-04 10:01:04 -040089 void init(const SkOpGlobalState* state) {
90 fGlobalState = state;
91 }
92
93 SpanGlitch* recordCommon(GlitchType type) {
caryclark26ad22a2015-10-16 09:03:38 -070094 SpanGlitch* glitch = fGlitches.push();
caryclark26ad22a2015-10-16 09:03:38 -070095 glitch->fBase = nullptr;
96 glitch->fSuspect = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -070097 glitch->fSegment = nullptr;
caryclark55888e42016-07-18 10:01:36 -070098 glitch->fOppSegment = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -070099 glitch->fCoinSpan = nullptr;
100 glitch->fEndSpan = nullptr;
101 glitch->fOppSpan = nullptr;
102 glitch->fOppEndSpan = nullptr;
caryclark55888e42016-07-18 10:01:36 -0700103 glitch->fStartT = SK_ScalarNaN;
104 glitch->fEndT = SK_ScalarNaN;
105 glitch->fOppStartT = SK_ScalarNaN;
106 glitch->fOppEndT = SK_ScalarNaN;
caryclark26ad22a2015-10-16 09:03:38 -0700107 glitch->fPt = { SK_ScalarNaN, SK_ScalarNaN };
108 glitch->fType = type;
109 return glitch;
110 }
111
Cary Clarkab87d7a2016-10-04 10:01:04 -0400112 void record(GlitchType type, const SkOpSpanBase* base,
caryclark26ad22a2015-10-16 09:03:38 -0700113 const SkOpSpanBase* suspect = NULL) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400114 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700115 glitch->fBase = base;
116 glitch->fSuspect = suspect;
117 }
118
Cary Clarkab87d7a2016-10-04 10:01:04 -0400119 void record(GlitchType type, const SkOpSpanBase* base,
caryclark55888e42016-07-18 10:01:36 -0700120 const SkOpPtT* ptT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400121 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700122 glitch->fBase = base;
123 glitch->fCoinSpan = ptT;
124 }
125
Cary Clarkab87d7a2016-10-04 10:01:04 -0400126 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark55888e42016-07-18 10:01:36 -0700127 const SkCoincidentSpans* opp = NULL) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400128 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700129 glitch->fCoinSpan = coin->coinPtTStart();
130 glitch->fEndSpan = coin->coinPtTEnd();
131 if (opp) {
132 glitch->fOppSpan = opp->coinPtTStart();
133 glitch->fOppEndSpan = opp->coinPtTEnd();
134 }
caryclark26ad22a2015-10-16 09:03:38 -0700135 }
136
Cary Clarkab87d7a2016-10-04 10:01:04 -0400137 void record(GlitchType type, const SkOpSpanBase* base,
caryclark26ad22a2015-10-16 09:03:38 -0700138 const SkOpSegment* seg, double t, SkPoint pt) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400139 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700140 glitch->fBase = base;
141 glitch->fSegment = seg;
caryclark55888e42016-07-18 10:01:36 -0700142 glitch->fStartT = t;
caryclark26ad22a2015-10-16 09:03:38 -0700143 glitch->fPt = pt;
144 }
145
Cary Clarkab87d7a2016-10-04 10:01:04 -0400146 void record(GlitchType type, const SkOpSpanBase* base, double t,
caryclark26ad22a2015-10-16 09:03:38 -0700147 SkPoint pt) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400148 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700149 glitch->fBase = base;
caryclark55888e42016-07-18 10:01:36 -0700150 glitch->fStartT = t;
caryclark26ad22a2015-10-16 09:03:38 -0700151 glitch->fPt = pt;
152 }
153
Cary Clarkab87d7a2016-10-04 10:01:04 -0400154 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark26ad22a2015-10-16 09:03:38 -0700155 const SkOpPtT* coinSpan, const SkOpPtT* endSpan) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400156 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700157 glitch->fCoinSpan = coin->coinPtTStart();
158 glitch->fEndSpan = coin->coinPtTEnd();
caryclark26ad22a2015-10-16 09:03:38 -0700159 glitch->fEndSpan = endSpan;
caryclark55888e42016-07-18 10:01:36 -0700160 glitch->fOppSpan = coinSpan;
161 glitch->fOppEndSpan = endSpan;
caryclark26ad22a2015-10-16 09:03:38 -0700162 }
163
Cary Clarkab87d7a2016-10-04 10:01:04 -0400164 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark55888e42016-07-18 10:01:36 -0700165 const SkOpSpanBase* base) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400166 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700167 glitch->fBase = base;
168 glitch->fCoinSpan = coin->coinPtTStart();
169 glitch->fEndSpan = coin->coinPtTEnd();
caryclark26ad22a2015-10-16 09:03:38 -0700170 }
171
Cary Clarkab87d7a2016-10-04 10:01:04 -0400172 void record(GlitchType type, const SkOpPtT* ptTS, const SkOpPtT* ptTE,
caryclark26ad22a2015-10-16 09:03:38 -0700173 const SkOpPtT* oPtTS, const SkOpPtT* oPtTE) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400174 SpanGlitch* glitch = recordCommon(type);
caryclark26ad22a2015-10-16 09:03:38 -0700175 glitch->fCoinSpan = ptTS;
176 glitch->fEndSpan = ptTE;
177 glitch->fOppSpan = oPtTS;
178 glitch->fOppEndSpan = oPtTE;
179 }
180
Cary Clarkab87d7a2016-10-04 10:01:04 -0400181 void record(GlitchType type, const SkOpSegment* seg, double startT,
caryclark55888e42016-07-18 10:01:36 -0700182 double endT, const SkOpSegment* oppSeg, double oppStartT, double oppEndT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400183 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700184 glitch->fSegment = seg;
185 glitch->fStartT = startT;
186 glitch->fEndT = endT;
187 glitch->fOppSegment = oppSeg;
188 glitch->fOppStartT = oppStartT;
189 glitch->fOppEndT = oppEndT;
190 }
191
Cary Clarkab87d7a2016-10-04 10:01:04 -0400192 void record(GlitchType type, const SkOpSegment* seg,
caryclark55888e42016-07-18 10:01:36 -0700193 const SkOpSpan* span) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400194 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700195 glitch->fSegment = seg;
196 glitch->fBase = span;
197 }
198
Cary Clarkab87d7a2016-10-04 10:01:04 -0400199 void record(GlitchType type, double t, const SkOpSpanBase* span) {
200 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700201 glitch->fStartT = t;
202 glitch->fBase = span;
203 }
204
Cary Clarkab87d7a2016-10-04 10:01:04 -0400205 void record(GlitchType type, const SkOpSegment* seg) {
206 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700207 glitch->fSegment = seg;
208 }
209
Cary Clarkab87d7a2016-10-04 10:01:04 -0400210 void record(GlitchType type, const SkCoincidentSpans* coin,
caryclark55888e42016-07-18 10:01:36 -0700211 const SkOpPtT* ptT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400212 SpanGlitch* glitch = recordCommon(type);
caryclark55888e42016-07-18 10:01:36 -0700213 glitch->fCoinSpan = coin->coinPtTStart();
214 glitch->fEndSpan = ptT;
215 }
216
caryclark26ad22a2015-10-16 09:03:38 -0700217 SkTDArray<SpanGlitch> fGlitches;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400218 const SkOpGlobalState* fGlobalState;
caryclark26ad22a2015-10-16 09:03:38 -0700219};
Cary Clarkab87d7a2016-10-04 10:01:04 -0400220
221
222void SkPathOpsDebug::CoinDict::add(const SkPathOpsDebug::CoinDict& dict) {
223 int count = dict.fDict.count();
224 for (int index = 0; index < count; ++index) {
225 this->add(dict.fDict[index]);
226 }
227}
228
229void SkPathOpsDebug::CoinDict::add(const CoinDictEntry& key) {
230 int count = fDict.count();
231 for (int index = 0; index < count; ++index) {
232 CoinDictEntry* entry = &fDict[index];
233 if (entry->fIteration == key.fIteration && entry->fLineNumber == key.fLineNumber) {
234 SkASSERT(!strcmp(entry->fFunctionName, key.fFunctionName));
235 if (entry->fGlitchType == kUninitialized_Glitch) {
236 entry->fGlitchType = key.fGlitchType;
237 }
238 return;
239 }
240 }
241 *fDict.append() = key;
242}
243
244#endif
245
246#if DEBUG_COIN
247static void missing_coincidence(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) {
248 const SkOpContour* contour = contourList;
249 // bool result = false;
250 do {
251 /* result |= */ contour->debugMissingCoincidence(glitches);
252 } while ((contour = contour->next()));
253 return;
254}
255
256static void move_multiples(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) {
257 const SkOpContour* contour = contourList;
258 do {
259 if (contour->debugMoveMultiples(glitches), false) {
260 return;
261 }
262 } while ((contour = contour->next()));
263 return;
264}
265
266static void move_nearby(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) {
267 const SkOpContour* contour = contourList;
268 do {
269 contour->debugMoveNearby(glitches);
270 } while ((contour = contour->next()));
271}
272
273
274#endif
275
276#if DEBUG_COIN
277void SkOpGlobalState::debugAddToCoinChangedDict() {
278
279#if DEBUG_COINCIDENCE
caryclarke6522ea2016-10-17 07:54:33 -0700280 SkPathOpsDebug::CheckHealth(fContourHead);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400281#endif
282 // see if next coincident operation makes a change; if so, record it
283 SkPathOpsDebug::GlitchLog glitches;
284 const char* funcName = fCoinDictEntry.fFunctionName;
285 if (!strcmp("calc_angles", funcName)) {
286 ;
287 } else if (!strcmp("missing_coincidence", funcName)) {
288 missing_coincidence(&glitches, fContourHead);
289 } else if (!strcmp("move_multiples", funcName)) {
290 move_multiples(&glitches, fContourHead);
291 } else if (!strcmp("move_nearby", funcName)) {
292 move_nearby(&glitches, fContourHead);
293 } else if (!strcmp("addExpanded", funcName)) {
294 fCoincidence->debugAddExpanded(&glitches);
295 } else if (!strcmp("addMissing", funcName)) {
296 bool added;
297 fCoincidence->debugAddMissing(&glitches, &added);
298 } else if (!strcmp("addEndMovedSpans", funcName)) {
299 fCoincidence->debugAddEndMovedSpans(&glitches);
300 } else if (!strcmp("correctEnds", funcName)) {
301 fCoincidence->debugCorrectEnds(&glitches);
302 } else if (!strcmp("expand", funcName)) {
303 fCoincidence->debugExpand(&glitches);
304 } else if (!strcmp("findOverlaps", funcName)) {
305 ;
306 } else if (!strcmp("mark", funcName)) {
307 fCoincidence->debugMark(&glitches);
308 } else if (!strcmp("apply", funcName)) {
309 ;
310 } else {
311 SkASSERT(0); // add missing case
312 }
313 if (glitches.fGlitches.count()) {
314 fCoinDictEntry.fGlitchType = glitches.fGlitches[0].fType;
315 }
316 fCoinChangedDict.add(fCoinDictEntry);
317}
caryclark55888e42016-07-18 10:01:36 -0700318#endif
caryclark26ad22a2015-10-16 09:03:38 -0700319
caryclark55888e42016-07-18 10:01:36 -0700320void SkPathOpsDebug::ShowActiveSpans(SkOpContourHead* contourList) {
321#if DEBUG_ACTIVE_SPANS
322 SkOpContour* contour = contourList;
323 do {
324 contour->debugShowActiveSpans();
325 } while ((contour = contour->next()));
326#endif
327}
328
Cary Clarkab87d7a2016-10-04 10:01:04 -0400329#if DEBUG_COINCIDENCE || DEBUG_COIN
330void SkPathOpsDebug::CheckHealth(SkOpContourHead* contourList) {
caryclark55888e42016-07-18 10:01:36 -0700331#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -0700332 contourList->globalState()->debugSetCheckHealth(true);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400333#endif
334#if DEBUG_COIN
caryclark26ad22a2015-10-16 09:03:38 -0700335 GlitchLog glitches;
336 const SkOpContour* contour = contourList;
337 const SkOpCoincidence* coincidence = contour->globalState()->coincidence();
Cary Clarkab87d7a2016-10-04 10:01:04 -0400338 coincidence->debugCheckValid(&glitches); // don't call validate; spans may be inconsistent
caryclark26ad22a2015-10-16 09:03:38 -0700339 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400340 contour->debugCheckHealth(&glitches);
341 contour->debugMissingCoincidence(&glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700342 } while ((contour = contour->next()));
caryclark81a478c2016-09-09 09:37:57 -0700343 bool added;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400344 coincidence->debugAddMissing(&glitches, &added);
345 coincidence->debugExpand(&glitches);
346 coincidence->debugAddExpanded(&glitches);
347 coincidence->debugMark(&glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700348 unsigned mask = 0;
349 for (int index = 0; index < glitches.fGlitches.count(); ++index) {
350 const SpanGlitch& glitch = glitches.fGlitches[index];
351 mask |= 1 << glitch.fType;
352 }
353 for (int index = 0; index < kGlitchType_Count; ++index) {
354 SkDebugf(mask & (1 << index) ? "x" : "-");
355 }
caryclark55888e42016-07-18 10:01:36 -0700356 for (int index = 0; index < glitches.fGlitches.count(); ++index) {
357 const SpanGlitch& glitch = glitches.fGlitches[index];
358 SkDebugf("%02d: ", index);
359 if (glitch.fBase) {
caryclark8016b262016-09-06 05:59:47 -0700360 SkDebugf(" seg/base=%d/%d", glitch.fBase->segment()->debugID(),
361 glitch.fBase->debugID());
caryclark55888e42016-07-18 10:01:36 -0700362 }
363 if (glitch.fSuspect) {
caryclark8016b262016-09-06 05:59:47 -0700364 SkDebugf(" seg/base=%d/%d", glitch.fSuspect->segment()->debugID(),
365 glitch.fSuspect->debugID());
caryclark55888e42016-07-18 10:01:36 -0700366 }
367 if (glitch.fSegment) {
368 SkDebugf(" segment=%d", glitch.fSegment->debugID());
369 }
370 if (glitch.fCoinSpan) {
caryclark8016b262016-09-06 05:59:47 -0700371 SkDebugf(" coinSeg/Span/PtT=%d/%d/%d", glitch.fCoinSpan->segment()->debugID(),
372 glitch.fCoinSpan->span()->debugID(), glitch.fCoinSpan->debugID());
caryclark55888e42016-07-18 10:01:36 -0700373 }
374 if (glitch.fEndSpan) {
375 SkDebugf(" endSpan=%d", glitch.fEndSpan->debugID());
376 }
377 if (glitch.fOppSpan) {
caryclark8016b262016-09-06 05:59:47 -0700378 SkDebugf(" oppSeg/Span/PtT=%d/%d/%d", glitch.fOppSpan->segment()->debugID(),
379 glitch.fOppSpan->span()->debugID(), glitch.fOppSpan->debugID());
caryclark55888e42016-07-18 10:01:36 -0700380 }
381 if (glitch.fOppEndSpan) {
382 SkDebugf(" oppEndSpan=%d", glitch.fOppEndSpan->debugID());
383 }
384 if (!SkScalarIsNaN(glitch.fStartT)) {
385 SkDebugf(" startT=%g", glitch.fStartT);
386 }
387 if (!SkScalarIsNaN(glitch.fEndT)) {
388 SkDebugf(" endT=%g", glitch.fEndT);
389 }
390 if (glitch.fOppSegment) {
391 SkDebugf(" segment=%d", glitch.fOppSegment->debugID());
392 }
393 if (!SkScalarIsNaN(glitch.fOppStartT)) {
394 SkDebugf(" oppStartT=%g", glitch.fOppStartT);
395 }
396 if (!SkScalarIsNaN(glitch.fOppEndT)) {
397 SkDebugf(" oppEndT=%g", glitch.fOppEndT);
398 }
399 if (!SkScalarIsNaN(glitch.fPt.fX) || !SkScalarIsNaN(glitch.fPt.fY)) {
400 SkDebugf(" pt=%g,%g", glitch.fPt.fX, glitch.fPt.fY);
401 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400402 DumpGlitchType(glitch.fType);
caryclark55888e42016-07-18 10:01:36 -0700403 SkDebugf("\n");
404 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400405#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -0700406 contourList->globalState()->debugSetCheckHealth(false);
Cary Clarkab87d7a2016-10-04 10:01:04 -0400407#endif
caryclark6c3b9cd2016-09-26 05:36:58 -0700408#if 01 && DEBUG_ACTIVE_SPANS
Cary Clarkab87d7a2016-10-04 10:01:04 -0400409// SkDebugf("active after %s:\n", id);
caryclark55888e42016-07-18 10:01:36 -0700410 ShowActiveSpans(contourList);
411#endif
412#endif
caryclark26ad22a2015-10-16 09:03:38 -0700413}
414#endif
415
Cary Clarkab87d7a2016-10-04 10:01:04 -0400416#if DEBUG_COIN
417void SkPathOpsDebug::DumpGlitchType(GlitchType glitchType) {
418 switch (glitchType) {
419 case kAddCorruptCoin_Glitch: SkDebugf(" AddCorruptCoin"); break;
420 case kAddExpandedCoin_Glitch: SkDebugf(" AddExpandedCoin"); break;
421 case kAddExpandedFail_Glitch: SkDebugf(" AddExpandedFail"); break;
422 case kAddIfCollapsed_Glitch: SkDebugf(" AddIfCollapsed"); break;; break;
423 case kAddIfMissingCoin_Glitch: SkDebugf(" AddIfMissingCoin"); break;
424 case kAddMissingCoin_Glitch: SkDebugf(" AddMissingCoin"); break;
425 case kAddMissingExtend_Glitch: SkDebugf(" AddMissingExtend"); break;
426 case kAddOrOverlap_Glitch: SkDebugf(" AAddOrOverlap"); break;
427 case kCollapsedCoin_Glitch: SkDebugf(" CollapsedCoin"); break;
428 case kCollapsedDone_Glitch: SkDebugf(" CollapsedDone"); break;
429 case kCollapsedOppValue_Glitch: SkDebugf(" CollapsedOppValue"); break;
430 case kCollapsedSpan_Glitch: SkDebugf(" CollapsedSpan"); break;
431 case kCollapsedWindValue_Glitch: SkDebugf(" CollapsedWindValue"); break;
432 case kCorrectEnd_Glitch: SkDebugf(" CorrectEnd"); break;
433 case kDeletedCoin_Glitch: SkDebugf(" DeletedCoin"); break;
434 case kExpandCoin_Glitch: SkDebugf(" ExpandCoin"); break;
435 case kFail_Glitch: SkDebugf(" Fail"); break;
436 case kMarkCoinEnd_Glitch: SkDebugf(" MarkCoinEnd"); break;
437 case kMarkCoinInsert_Glitch: SkDebugf(" MarkCoinInsert"); break;
438 case kMarkCoinMissing_Glitch: SkDebugf(" MarkCoinMissing"); break;
439 case kMarkCoinStart_Glitch: SkDebugf(" MarkCoinStart"); break;
Cary Clarkab87d7a2016-10-04 10:01:04 -0400440 case kMergeMatches_Glitch: SkDebugf(" MergeMatches"); break;
441 case kMissingCoin_Glitch: SkDebugf(" MissingCoin"); break;
442 case kMissingDone_Glitch: SkDebugf(" MissingDone"); break;
443 case kMissingIntersection_Glitch: SkDebugf(" MissingIntersection"); break;
444 case kMoveMultiple_Glitch: SkDebugf(" MoveMultiple"); break;
445 case kMoveNearbyClearAll_Glitch: SkDebugf(" MoveNearbyClearAll"); break;
446 case kMoveNearbyClearAll2_Glitch: SkDebugf(" MoveNearbyClearAll2"); break;
447 case kMoveNearbyMerge_Glitch: SkDebugf(" MoveNearbyMerge"); break;
448 case kMoveNearbyMergeFinal_Glitch: SkDebugf(" MoveNearbyMergeFinal"); break;
449 case kMoveNearbyRelease_Glitch: SkDebugf(" MoveNearbyRelease"); break;
450 case kMoveNearbyReleaseFinal_Glitch: SkDebugf(" MoveNearbyReleaseFinal"); break;
451 case kReleasedSpan_Glitch: SkDebugf(" ReleasedSpan"); break;
452 case kReturnFalse_Glitch: SkDebugf(" ReturnFalse"); break;
453 case kUnaligned_Glitch: SkDebugf(" Unaligned"); break;
454 case kUnalignedHead_Glitch: SkDebugf(" UnalignedHead"); break;
455 case kUnalignedTail_Glitch: SkDebugf(" UnalignedTail"); break;
456 case kUninitialized_Glitch: break;
457 default: SkASSERT(0);
458 }
459}
460#endif
461
caryclark26ad22a2015-10-16 09:03:38 -0700462#if defined SK_DEBUG || !FORCE_RELEASE
caryclark@google.com570863f2013-09-16 15:55:01 +0000463void SkPathOpsDebug::MathematicaIze(char* str, size_t bufferLen) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000464 size_t len = strlen(str);
465 bool num = false;
466 for (size_t idx = 0; idx < len; ++idx) {
467 if (num && str[idx] == 'e') {
468 if (len + 2 >= bufferLen) {
469 return;
470 }
471 memmove(&str[idx + 2], &str[idx + 1], len - idx);
472 str[idx] = '*';
473 str[idx + 1] = '^';
474 ++len;
475 }
476 num = str[idx] >= '0' && str[idx] <= '9';
477 }
478}
479
caryclark@google.com570863f2013-09-16 15:55:01 +0000480bool SkPathOpsDebug::ValidWind(int wind) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000481 return wind > SK_MinS32 + 0xFFFF && wind < SK_MaxS32 - 0xFFFF;
482}
483
caryclark@google.com570863f2013-09-16 15:55:01 +0000484void SkPathOpsDebug::WindingPrintf(int wind) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000485 if (wind == SK_MinS32) {
486 SkDebugf("?");
487 } else {
488 SkDebugf("%d", wind);
489 }
490}
caryclark54359292015-03-26 07:52:43 -0700491#endif // defined SK_DEBUG || !FORCE_RELEASE
492
caryclark@google.coma5e55922013-05-07 18:51:31 +0000493
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000494#if DEBUG_SHOW_TEST_NAME
halcanary385fe4d2015-08-26 13:07:48 -0700495void* SkPathOpsDebug::CreateNameStr() { return new char[DEBUG_FILENAME_STRING_LENGTH]; }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000496
halcanary385fe4d2015-08-26 13:07:48 -0700497void SkPathOpsDebug::DeleteNameStr(void* v) { delete[] reinterpret_cast<char*>(v); }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000498
caryclark@google.com570863f2013-09-16 15:55:01 +0000499void SkPathOpsDebug::BumpTestName(char* test) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000500 char* num = test + strlen(test);
501 while (num[-1] >= '0' && num[-1] <= '9') {
502 --num;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000503 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000504 if (num[0] == '\0') {
505 return;
506 }
507 int dec = atoi(num);
508 if (dec == 0) {
509 return;
510 }
511 ++dec;
512 SK_SNPRINTF(num, DEBUG_FILENAME_STRING_LENGTH - (num - test), "%d", dec);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000513}
514#endif
caryclark@google.com570863f2013-09-16 15:55:01 +0000515
caryclark1049f122015-04-20 08:31:59 -0700516static void show_function_header(const char* functionName) {
517 SkDebugf("\nstatic void %s(skiatest::Reporter* reporter, const char* filename) {\n", functionName);
518 if (strcmp("skphealth_com76", functionName) == 0) {
519 SkDebugf("found it\n");
520 }
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000521}
caryclark1049f122015-04-20 08:31:59 -0700522
523static const char* gOpStrs[] = {
524 "kDifference_SkPathOp",
525 "kIntersect_SkPathOp",
526 "kUnion_SkPathOp",
caryclark55888e42016-07-18 10:01:36 -0700527 "kXOR_PathOp",
caryclark1049f122015-04-20 08:31:59 -0700528 "kReverseDifference_SkPathOp",
529};
530
caryclark03b03ca2015-04-23 09:13:37 -0700531const char* SkPathOpsDebug::OpStr(SkPathOp op) {
532 return gOpStrs[op];
533}
534
caryclark1049f122015-04-20 08:31:59 -0700535static void show_op(SkPathOp op, const char* pathOne, const char* pathTwo) {
536 SkDebugf(" testPathOp(reporter, %s, %s, %s, filename);\n", pathOne, pathTwo, gOpStrs[op]);
537 SkDebugf("}\n");
538}
539
reed086eea92016-05-04 17:12:46 -0700540SK_DECLARE_STATIC_MUTEX(gTestMutex);
caryclark1049f122015-04-20 08:31:59 -0700541
542void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
543 const char* testName) {
544 SkAutoMutexAcquire ac(gTestMutex);
545 show_function_header(testName);
546 ShowOnePath(a, "path", true);
547 ShowOnePath(b, "pathB", true);
548 show_op(shapeOp, "path", "pathB");
549}
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000550
caryclark27c8eb82015-07-06 11:38:33 -0700551#include "SkPathOpsTypes.h"
caryclark26ad22a2015-10-16 09:03:38 -0700552#include "SkIntersectionHelper.h"
553#include "SkIntersections.h"
554
Cary Clarkab87d7a2016-10-04 10:01:04 -0400555#if DEBUG_COIN
556
557SK_DECLARE_STATIC_MUTEX(gCoinDictMutex);
558
559void SkOpGlobalState::debugAddToGlobalCoinDicts() {
560 SkAutoMutexAcquire ac(&gCoinDictMutex);
561 SkPathOpsDebug::gCoinSumChangedDict.add(fCoinChangedDict);
562 SkPathOpsDebug::gCoinSumVisitedDict.add(fCoinVisitedDict);
563}
564
565#endif
566
caryclark26ad22a2015-10-16 09:03:38 -0700567#if DEBUG_T_SECT_LOOP_COUNT
568void SkOpGlobalState::debugAddLoopCount(SkIntersections* i, const SkIntersectionHelper& wt,
569 const SkIntersectionHelper& wn) {
570 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
571 SkIntersections::DebugLoop looper = (SkIntersections::DebugLoop) index;
572 if (fDebugLoopCount[index] >= i->debugLoopCount(looper)) {
573 continue;
574 }
575 fDebugLoopCount[index] = i->debugLoopCount(looper);
576 fDebugWorstVerb[index * 2] = wt.segment()->verb();
577 fDebugWorstVerb[index * 2 + 1] = wn.segment()->verb();
578 sk_bzero(&fDebugWorstPts[index * 8], sizeof(SkPoint) * 8);
579 memcpy(&fDebugWorstPts[index * 2 * 4], wt.pts(),
580 (SkPathOpsVerbToPoints(wt.segment()->verb()) + 1) * sizeof(SkPoint));
581 memcpy(&fDebugWorstPts[(index * 2 + 1) * 4], wn.pts(),
582 (SkPathOpsVerbToPoints(wn.segment()->verb()) + 1) * sizeof(SkPoint));
583 fDebugWorstWeight[index * 2] = wt.weight();
584 fDebugWorstWeight[index * 2 + 1] = wn.weight();
585 }
586 i->debugResetLoopCount();
587}
588
589void SkOpGlobalState::debugDoYourWorst(SkOpGlobalState* local) {
590 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
591 if (fDebugLoopCount[index] >= local->fDebugLoopCount[index]) {
592 continue;
593 }
594 fDebugLoopCount[index] = local->fDebugLoopCount[index];
595 fDebugWorstVerb[index * 2] = local->fDebugWorstVerb[index * 2];
596 fDebugWorstVerb[index * 2 + 1] = local->fDebugWorstVerb[index * 2 + 1];
597 memcpy(&fDebugWorstPts[index * 2 * 4], &local->fDebugWorstPts[index * 2 * 4],
598 sizeof(SkPoint) * 8);
599 fDebugWorstWeight[index * 2] = local->fDebugWorstWeight[index * 2];
600 fDebugWorstWeight[index * 2 + 1] = local->fDebugWorstWeight[index * 2 + 1];
601 }
602 local->debugResetLoopCounts();
603}
604
605static void dump_curve(SkPath::Verb verb, const SkPoint& pts, float weight) {
606 if (!verb) {
607 return;
608 }
609 const char* verbs[] = { "", "line", "quad", "conic", "cubic" };
610 SkDebugf("%s: {{", verbs[verb]);
611 int ptCount = SkPathOpsVerbToPoints(verb);
612 for (int index = 0; index <= ptCount; ++index) {
613 SkDPoint::Dump((&pts)[index]);
614 if (index < ptCount - 1) {
615 SkDebugf(", ");
616 }
617 }
618 SkDebugf("}");
619 if (weight != 1) {
620 SkDebugf(", ");
621 if (weight == floorf(weight)) {
622 SkDebugf("%.0f", weight);
623 } else {
624 SkDebugf("%1.9gf", weight);
625 }
626 }
627 SkDebugf("}\n");
628}
629
630void SkOpGlobalState::debugLoopReport() {
631 const char* loops[] = { "iterations", "coinChecks", "perpCalcs" };
632 SkDebugf("\n");
633 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
634 SkDebugf("%s: %d\n", loops[index], fDebugLoopCount[index]);
635 dump_curve(fDebugWorstVerb[index * 2], fDebugWorstPts[index * 2 * 4],
636 fDebugWorstWeight[index * 2]);
637 dump_curve(fDebugWorstVerb[index * 2 + 1], fDebugWorstPts[(index * 2 + 1) * 4],
638 fDebugWorstWeight[index * 2 + 1]);
639 }
640}
641
642void SkOpGlobalState::debugResetLoopCounts() {
643 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
644 sk_bzero(fDebugWorstVerb, sizeof(fDebugWorstVerb));
645 sk_bzero(fDebugWorstPts, sizeof(fDebugWorstPts));
646 sk_bzero(fDebugWorstWeight, sizeof(fDebugWorstWeight));
647}
648#endif
caryclark27c8eb82015-07-06 11:38:33 -0700649
650#ifdef SK_DEBUG
651bool SkOpGlobalState::debugRunFail() const {
652#if DEBUG_VALIDATE
653 return FLAGS_runFail;
654#else
655 return false;
656#endif
657}
658#endif
659
Cary Clarkab87d7a2016-10-04 10:01:04 -0400660// this is const so it can be called by const methods that overwise don't alter state
661#if DEBUG_VALIDATE || DEBUG_COIN
662void SkOpGlobalState::debugSetPhase(const char* funcName DEBUG_COIN_DECLARE_PARAMS()) const {
663 auto writable = const_cast<SkOpGlobalState*>(this);
664#if DEBUG_VALIDATE
665 writable->setPhase(phase);
666#endif
667#if DEBUG_COIN
668 SkPathOpsDebug::CoinDictEntry* entry = &writable->fCoinDictEntry;
669 writable->fPreviousFuncName = entry->fFunctionName;
670 entry->fIteration = iteration;
671 entry->fLineNumber = lineNo;
672 entry->fGlitchType = SkPathOpsDebug::kUninitialized_Glitch;
673 entry->fFunctionName = funcName;
674 writable->fCoinVisitedDict.add(*entry);
675 writable->debugAddToCoinChangedDict();
676#endif
677}
678#endif
679
caryclark26ad22a2015-10-16 09:03:38 -0700680#if DEBUG_T_SECT_LOOP_COUNT
681void SkIntersections::debugBumpLoopCount(DebugLoop index) {
682 fDebugLoopCount[index]++;
683}
684
685int SkIntersections::debugLoopCount(DebugLoop index) const {
686 return fDebugLoopCount[index];
687}
688
689void SkIntersections::debugResetLoopCount() {
690 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
691}
692#endif
693
caryclark624637c2015-05-11 07:21:27 -0700694#include "SkPathOpsCubic.h"
695#include "SkPathOpsQuad.h"
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000696
caryclark624637c2015-05-11 07:21:27 -0700697SkDCubic SkDQuad::debugToCubic() const {
698 SkDCubic cubic;
699 cubic[0] = fPts[0];
700 cubic[2] = fPts[1];
701 cubic[3] = fPts[2];
702 cubic[1].fX = (cubic[0].fX + cubic[2].fX * 2) / 3;
703 cubic[1].fY = (cubic[0].fY + cubic[2].fY * 2) / 3;
704 cubic[2].fX = (cubic[3].fX + cubic[2].fX * 2) / 3;
705 cubic[2].fY = (cubic[3].fY + cubic[2].fY * 2) / 3;
706 return cubic;
caryclark54359292015-03-26 07:52:43 -0700707}
caryclark624637c2015-05-11 07:21:27 -0700708
caryclarked0935a2015-10-22 07:23:52 -0700709void SkDRect::debugInit() {
710 fLeft = fTop = fRight = fBottom = SK_ScalarNaN;
711}
712
caryclark624637c2015-05-11 07:21:27 -0700713#include "SkOpAngle.h"
caryclark624637c2015-05-11 07:21:27 -0700714#include "SkOpSegment.h"
caryclark54359292015-03-26 07:52:43 -0700715
Cary Clarkab87d7a2016-10-04 10:01:04 -0400716#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -0700717// commented-out lines keep this in sync with addT()
Cary Clarkab87d7a2016-10-04 10:01:04 -0400718 const SkOpPtT* SkOpSegment::debugAddT(double t, SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -0700719 debugValidate();
720 SkPoint pt = this->ptAtT(t);
caryclark26ad22a2015-10-16 09:03:38 -0700721 const SkOpSpanBase* span = &fHead;
caryclark55888e42016-07-18 10:01:36 -0700722 do {
723 const SkOpPtT* result = span->ptT();
caryclark29b25632016-08-25 11:27:17 -0700724 if (t == result->fT || this->match(result, this, t, pt)) {
caryclark55888e42016-07-18 10:01:36 -0700725// span->bumpSpanAdds();
caryclarkef4f32a2016-08-24 09:24:18 -0700726 return result;
caryclark26ad22a2015-10-16 09:03:38 -0700727 }
caryclark55888e42016-07-18 10:01:36 -0700728 if (t < result->fT) {
729 const SkOpSpan* prev = result->span()->prev();
caryclark30b9fdd2016-08-31 14:36:29 -0700730 FAIL_WITH_NULL_IF(!prev, span);
caryclark29b25632016-08-25 11:27:17 -0700731 // marks in global state that new op span has been allocated
732 this->globalState()->setAllocatedOpSpan();
caryclark55888e42016-07-18 10:01:36 -0700733// span->init(this, prev, t, pt);
734 this->debugValidate();
735// #if DEBUG_ADD_T
736// SkDebugf("%s insert t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
737// span->segment()->debugID(), span->debugID());
738// #endif
739// span->bumpSpanAdds();
caryclark55888e42016-07-18 10:01:36 -0700740 return nullptr;
caryclark26ad22a2015-10-16 09:03:38 -0700741 }
caryclark30b9fdd2016-08-31 14:36:29 -0700742 FAIL_WITH_NULL_IF(span != &fTail, span);
caryclark55888e42016-07-18 10:01:36 -0700743 } while ((span = span->upCast()->next()));
744 SkASSERT(0);
caryclark29b25632016-08-25 11:27:17 -0700745 return nullptr; // we never get here, but need this to satisfy compiler
caryclark26ad22a2015-10-16 09:03:38 -0700746}
747#endif
748
749#if DEBUG_ANGLE
750void SkOpSegment::debugCheckAngleCoin() const {
751 const SkOpSpanBase* base = &fHead;
752 const SkOpSpan* span;
753 do {
754 const SkOpAngle* angle = base->fromAngle();
caryclark55888e42016-07-18 10:01:36 -0700755 if (angle && angle->debugCheckCoincidence()) {
caryclark26ad22a2015-10-16 09:03:38 -0700756 angle->debugCheckNearCoincidence();
757 }
758 if (base->final()) {
759 break;
760 }
761 span = base->upCast();
762 angle = span->toAngle();
caryclark55888e42016-07-18 10:01:36 -0700763 if (angle && angle->debugCheckCoincidence()) {
caryclark26ad22a2015-10-16 09:03:38 -0700764 angle->debugCheckNearCoincidence();
765 }
766 } while ((base = span->next()));
767}
768#endif
769
Cary Clarkab87d7a2016-10-04 10:01:04 -0400770#if DEBUG_COIN
caryclark26ad22a2015-10-16 09:03:38 -0700771// this mimics the order of the checks in handle coincidence
Cary Clarkab87d7a2016-10-04 10:01:04 -0400772void SkOpSegment::debugCheckHealth(SkPathOpsDebug::GlitchLog* glitches) const {
773 debugMoveMultiples(glitches);
774 debugMoveNearby(glitches);
775 debugMissingCoincidence(glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700776}
777
caryclark55888e42016-07-18 10:01:36 -0700778// commented-out lines keep this in sync with clearAll()
Cary Clarkab87d7a2016-10-04 10:01:04 -0400779void SkOpSegment::debugClearAll(SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -0700780 const SkOpSpan* span = &fHead;
781 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -0400782 this->debugClearOne(span, glitches);
caryclark55888e42016-07-18 10:01:36 -0700783 } while ((span = span->next()->upCastable()));
Cary Clarkab87d7a2016-10-04 10:01:04 -0400784 this->globalState()->coincidence()->debugRelease(glitches, this);
caryclark55888e42016-07-18 10:01:36 -0700785}
786
787// commented-out lines keep this in sync with clearOne()
Cary Clarkab87d7a2016-10-04 10:01:04 -0400788void SkOpSegment::debugClearOne(const SkOpSpan* span, SkPathOpsDebug::GlitchLog* glitches) const {
789 if (span->windValue()) glitches->record(SkPathOpsDebug::kCollapsedWindValue_Glitch, span);
790 if (span->oppValue()) glitches->record(SkPathOpsDebug::kCollapsedOppValue_Glitch, span);
791 if (!span->done()) glitches->record(SkPathOpsDebug::kCollapsedDone_Glitch, span);
caryclark26ad22a2015-10-16 09:03:38 -0700792}
793#endif
794
caryclark54359292015-03-26 07:52:43 -0700795SkOpAngle* SkOpSegment::debugLastAngle() {
halcanary96fcdcc2015-08-27 07:41:13 -0700796 SkOpAngle* result = nullptr;
caryclark54359292015-03-26 07:52:43 -0700797 SkOpSpan* span = this->head();
798 do {
799 if (span->toAngle()) {
800 SkASSERT(!result);
801 result = span->toAngle();
802 }
803 } while ((span = span->next()->upCastable()));
804 SkASSERT(result);
805 return result;
806}
807
Cary Clarkab87d7a2016-10-04 10:01:04 -0400808#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -0700809// commented-out lines keep this in sync with ClearVisited
810void SkOpSegment::DebugClearVisited(const SkOpSpanBase* span) {
811 // reset visited flag back to false
812 do {
813 const SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
814 while ((ptT = ptT->next()) != stopPtT) {
815 const SkOpSegment* opp = ptT->segment();
816 opp->resetDebugVisited();
817 }
818 } while (!span->final() && (span = span->upCast()->next()));
819}
820#endif
821
Cary Clarkab87d7a2016-10-04 10:01:04 -0400822#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -0700823// commented-out lines keep this in sync with missingCoincidence()
824// look for pairs of undetected coincident curves
825// assumes that segments going in have visited flag clear
826// Even though pairs of curves correct detect coincident runs, a run may be missed
827// if the coincidence is a product of multiple intersections. For instance, given
828// curves A, B, and C:
829// A-B intersect at a point 1; A-C and B-C intersect at point 2, so near
830// the end of C that the intersection is replaced with the end of C.
831// Even though A-B correctly do not detect an intersection at point 2,
832// the resulting run from point 1 to point 2 is coincident on A and B.
Cary Clarkab87d7a2016-10-04 10:01:04 -0400833void SkOpSegment::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -0700834 if (this->done()) {
835 return;
836 }
837 const SkOpSpan* prior = nullptr;
838 const SkOpSpanBase* spanBase = &fHead;
caryclark55888e42016-07-18 10:01:36 -0700839// bool result = false;
caryclark26ad22a2015-10-16 09:03:38 -0700840 do {
841 const SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT;
842 SkASSERT(ptT->span() == spanBase);
843 while ((ptT = ptT->next()) != spanStopPtT) {
844 if (ptT->deleted()) {
845 continue;
846 }
caryclark55888e42016-07-18 10:01:36 -0700847 const SkOpSegment* opp = ptT->span()->segment();
caryclark26ad22a2015-10-16 09:03:38 -0700848 if (opp->done()) {
849 continue;
850 }
851 // when opp is encounted the 1st time, continue; on 2nd encounter, look for coincidence
caryclark55888e42016-07-18 10:01:36 -0700852 if (!opp->debugVisited()) {
caryclark26ad22a2015-10-16 09:03:38 -0700853 continue;
854 }
855 if (spanBase == &fHead) {
856 continue;
857 }
caryclark55888e42016-07-18 10:01:36 -0700858 if (ptT->segment() == this) {
859 continue;
860 }
caryclark26ad22a2015-10-16 09:03:38 -0700861 const SkOpSpan* span = spanBase->upCastable();
862 // FIXME?: this assumes that if the opposite segment is coincident then no more
863 // coincidence needs to be detected. This may not be true.
caryclark55888e42016-07-18 10:01:36 -0700864 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 -0700865 continue;
866 }
caryclark55888e42016-07-18 10:01:36 -0700867 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 -0700868 continue;
caryclark55888e42016-07-18 10:01:36 -0700869 }
caryclark26ad22a2015-10-16 09:03:38 -0700870 const SkOpPtT* priorPtT = nullptr, * priorStopPtT;
871 // find prior span containing opp segment
872 const SkOpSegment* priorOpp = nullptr;
873 const SkOpSpan* priorTest = spanBase->prev();
874 while (!priorOpp && priorTest) {
875 priorStopPtT = priorPtT = priorTest->ptT();
876 while ((priorPtT = priorPtT->next()) != priorStopPtT) {
877 if (priorPtT->deleted()) {
878 continue;
879 }
Cary Clarkab87d7a2016-10-04 10:01:04 -0400880 const SkOpSegment* segment = priorPtT->span()->segment();
caryclark26ad22a2015-10-16 09:03:38 -0700881 if (segment == opp) {
882 prior = priorTest;
883 priorOpp = opp;
884 break;
885 }
886 }
887 priorTest = priorTest->prev();
888 }
889 if (!priorOpp) {
890 continue;
891 }
caryclark55888e42016-07-18 10:01:36 -0700892 if (priorPtT == ptT) {
893 continue;
894 }
caryclark26ad22a2015-10-16 09:03:38 -0700895 const SkOpPtT* oppStart = prior->ptT();
896 const SkOpPtT* oppEnd = spanBase->ptT();
897 bool swapped = priorPtT->fT > ptT->fT;
898 if (swapped) {
899 SkTSwap(priorPtT, ptT);
900 SkTSwap(oppStart, oppEnd);
901 }
caryclark55888e42016-07-18 10:01:36 -0700902 const SkOpCoincidence* coincidence = this->globalState()->coincidence();
903 const SkOpPtT* rootPriorPtT = priorPtT->span()->ptT();
904 const SkOpPtT* rootPtT = ptT->span()->ptT();
905 const SkOpPtT* rootOppStart = oppStart->span()->ptT();
906 const SkOpPtT* rootOppEnd = oppEnd->span()->ptT();
907 if (coincidence->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) {
caryclark26ad22a2015-10-16 09:03:38 -0700908 goto swapBack;
909 }
caryclark55888e42016-07-18 10:01:36 -0700910 if (testForCoincidence(rootPriorPtT, rootPtT, prior, spanBase, opp)) {
911 // mark coincidence
caryclark30b9fdd2016-08-31 14:36:29 -0700912#if DEBUG_COINCIDENCE_VERBOSE
caryclark55888e42016-07-18 10:01:36 -0700913// SkDebugf("%s coinSpan=%d endSpan=%d oppSpan=%d oppEndSpan=%d\n", __FUNCTION__,
914// rootPriorPtT->debugID(), rootPtT->debugID(), rootOppStart->debugID(),
915// rootOppEnd->debugID());
916#endif
Cary Clarkab87d7a2016-10-04 10:01:04 -0400917 log->record(SkPathOpsDebug::kMissingCoin_Glitch, priorPtT, ptT, oppStart, oppEnd);
caryclark55888e42016-07-18 10:01:36 -0700918 // coincidences->add(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
919 // }
920#if DEBUG_COINCIDENCE
caryclarkd6562002016-07-27 12:02:07 -0700921// SkASSERT(coincidences->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
caryclark55888e42016-07-18 10:01:36 -0700922#endif
923 // result = true;
caryclark26ad22a2015-10-16 09:03:38 -0700924 }
925 swapBack:
926 if (swapped) {
927 SkTSwap(priorPtT, ptT);
928 }
929 }
930 } while ((spanBase = spanBase->final() ? nullptr : spanBase->upCast()->next()));
caryclark55888e42016-07-18 10:01:36 -0700931 DebugClearVisited(&fHead);
932 return;
caryclark26ad22a2015-10-16 09:03:38 -0700933}
934
caryclark55888e42016-07-18 10:01:36 -0700935// commented-out lines keep this in sync with moveMultiples()
936// if a span has more than one intersection, merge the other segments' span as needed
Cary Clarkab87d7a2016-10-04 10:01:04 -0400937void SkOpSegment::debugMoveMultiples(SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -0700938 debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -0700939 const SkOpSpanBase* test = &fHead;
940 do {
941 int addCount = test->spanAddsCount();
942 SkASSERT(addCount >= 1);
943 if (addCount == 1) {
944 continue;
945 }
946 const SkOpPtT* startPtT = test->ptT();
947 const SkOpPtT* testPtT = startPtT;
948 do { // iterate through all spans associated with start
949 const SkOpSpanBase* oppSpan = testPtT->span();
950 if (oppSpan->spanAddsCount() == addCount) {
951 continue;
952 }
953 if (oppSpan->deleted()) {
954 continue;
955 }
956 const SkOpSegment* oppSegment = oppSpan->segment();
957 if (oppSegment == this) {
958 continue;
959 }
960 // find range of spans to consider merging
961 const SkOpSpanBase* oppPrev = oppSpan;
962 const SkOpSpanBase* oppFirst = oppSpan;
963 while ((oppPrev = oppPrev->prev())) {
964 if (!roughly_equal(oppPrev->t(), oppSpan->t())) {
965 break;
966 }
967 if (oppPrev->spanAddsCount() == addCount) {
968 continue;
969 }
970 if (oppPrev->deleted()) {
971 continue;
972 }
973 oppFirst = oppPrev;
974 }
975 const SkOpSpanBase* oppNext = oppSpan;
976 const SkOpSpanBase* oppLast = oppSpan;
977 while ((oppNext = oppNext->final() ? nullptr : oppNext->upCast()->next())) {
978 if (!roughly_equal(oppNext->t(), oppSpan->t())) {
979 break;
980 }
981 if (oppNext->spanAddsCount() == addCount) {
982 continue;
983 }
984 if (oppNext->deleted()) {
985 continue;
986 }
987 oppLast = oppNext;
988 }
989 if (oppFirst == oppLast) {
990 continue;
991 }
992 const SkOpSpanBase* oppTest = oppFirst;
993 do {
994 if (oppTest == oppSpan) {
995 continue;
996 }
997 // check to see if the candidate meets specific criteria:
998 // it contains spans of segments in test's loop but not including 'this'
999 const SkOpPtT* oppStartPtT = oppTest->ptT();
1000 const SkOpPtT* oppPtT = oppStartPtT;
1001 while ((oppPtT = oppPtT->next()) != oppStartPtT) {
1002 const SkOpSegment* oppPtTSegment = oppPtT->segment();
1003 if (oppPtTSegment == this) {
1004 goto tryNextSpan;
1005 }
1006 const SkOpPtT* matchPtT = startPtT;
1007 do {
1008 if (matchPtT->segment() == oppPtTSegment) {
1009 goto foundMatch;
1010 }
1011 } while ((matchPtT = matchPtT->next()) != startPtT);
1012 goto tryNextSpan;
1013 foundMatch: // merge oppTest and oppSpan
caryclark55888e42016-07-18 10:01:36 -07001014 oppSegment->debugValidate();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001015 oppTest->debugMergeMatches(glitches, oppSpan);
1016 oppTest->debugAddOpp(glitches, oppSpan);
caryclark55888e42016-07-18 10:01:36 -07001017 oppSegment->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001018 goto checkNextSpan;
1019 }
caryclark55888e42016-07-18 10:01:36 -07001020 tryNextSpan:
caryclark26ad22a2015-10-16 09:03:38 -07001021 ;
1022 } while (oppTest != oppLast && (oppTest = oppTest->upCast()->next()));
1023 } while ((testPtT = testPtT->next()) != startPtT);
caryclark55888e42016-07-18 10:01:36 -07001024checkNextSpan:
caryclark26ad22a2015-10-16 09:03:38 -07001025 ;
1026 } while ((test = test->final() ? nullptr : test->upCast()->next()));
caryclark55888e42016-07-18 10:01:36 -07001027 debugValidate();
1028 return;
caryclark26ad22a2015-10-16 09:03:38 -07001029}
1030
caryclark55888e42016-07-18 10:01:36 -07001031// commented-out lines keep this in sync with moveNearby()
1032// Move nearby t values and pts so they all hang off the same span. Alignment happens later.
Cary Clarkab87d7a2016-10-04 10:01:04 -04001033void SkOpSegment::debugMoveNearby(SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -07001034 debugValidate();
1035 // release undeleted spans pointing to this seg that are linked to the primary span
1036 const SkOpSpanBase* spanBase = &fHead;
caryclark26ad22a2015-10-16 09:03:38 -07001037 do {
caryclark55888e42016-07-18 10:01:36 -07001038 const SkOpPtT* ptT = spanBase->ptT();
1039 const SkOpPtT* headPtT = ptT;
1040 while ((ptT = ptT->next()) != headPtT) {
1041 const SkOpSpanBase* test = ptT->span();
1042 if (ptT->segment() == this && !ptT->deleted() && test != spanBase
1043 && test->ptT() == ptT) {
1044 if (test->final()) {
1045 if (spanBase == &fHead) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001046 glitches->record(SkPathOpsDebug::kMoveNearbyClearAll_Glitch, this);
caryclark55888e42016-07-18 10:01:36 -07001047// return;
1048 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001049 glitches->record(SkPathOpsDebug::kMoveNearbyReleaseFinal_Glitch, spanBase, ptT);
caryclark55888e42016-07-18 10:01:36 -07001050 } else if (test->prev()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001051 glitches->record(SkPathOpsDebug::kMoveNearbyRelease_Glitch, test, headPtT);
caryclark55888e42016-07-18 10:01:36 -07001052 }
1053// break;
caryclark26ad22a2015-10-16 09:03:38 -07001054 }
1055 }
caryclark55888e42016-07-18 10:01:36 -07001056 spanBase = spanBase->upCast()->next();
1057 } while (!spanBase->final());
1058
1059 // This loop looks for adjacent spans which are near by
1060 spanBase = &fHead;
1061 do { // iterate through all spans associated with start
1062 const SkOpSpanBase* test = spanBase->upCast()->next();
1063 if (this->spansNearby(spanBase, test)) {
1064 if (test->final()) {
1065 if (spanBase->prev()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001066 glitches->record(SkPathOpsDebug::kMoveNearbyMergeFinal_Glitch, test);
caryclark55888e42016-07-18 10:01:36 -07001067 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001068 glitches->record(SkPathOpsDebug::kMoveNearbyClearAll2_Glitch, this);
caryclark55888e42016-07-18 10:01:36 -07001069 // return
1070 }
1071 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001072 glitches->record(SkPathOpsDebug::kMoveNearbyMerge_Glitch, spanBase);
caryclark55888e42016-07-18 10:01:36 -07001073 }
1074 }
1075 spanBase = test;
1076 } while (!spanBase->final());
1077 debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001078}
1079#endif
1080
caryclark54359292015-03-26 07:52:43 -07001081void SkOpSegment::debugReset() {
caryclark1049f122015-04-20 08:31:59 -07001082 this->init(this->fPts, this->fWeight, this->contour(), this->verb());
caryclark54359292015-03-26 07:52:43 -07001083}
1084
caryclark025b11e2016-08-25 05:21:14 -07001085#if DEBUG_COINCIDENCE_ORDER
1086void SkOpSegment::debugSetCoinT(int index, SkScalar t) const {
1087 if (fDebugBaseMax < 0 || fDebugBaseIndex == index) {
1088 fDebugBaseIndex = index;
1089 fDebugBaseMin = SkTMin(t, fDebugBaseMin);
1090 fDebugBaseMax = SkTMax(t, fDebugBaseMax);
1091 return;
1092 }
1093 SkASSERT(fDebugBaseMin >= t || t >= fDebugBaseMax);
1094 if (fDebugLastMax < 0 || fDebugLastIndex == index) {
1095 fDebugLastIndex = index;
1096 fDebugLastMin = SkTMin(t, fDebugLastMin);
1097 fDebugLastMax = SkTMax(t, fDebugLastMax);
1098 return;
1099 }
1100 SkASSERT(fDebugLastMin >= t || t >= fDebugLastMax);
1101 SkASSERT((t - fDebugBaseMin > 0) == (fDebugLastMin - fDebugBaseMin > 0));
1102}
1103#endif
1104
caryclark54359292015-03-26 07:52:43 -07001105#if DEBUG_ACTIVE_SPANS
1106void SkOpSegment::debugShowActiveSpans() const {
1107 debugValidate();
1108 if (done()) {
1109 return;
1110 }
1111 int lastId = -1;
1112 double lastT = -1;
1113 const SkOpSpan* span = &fHead;
1114 do {
1115 if (span->done()) {
1116 continue;
1117 }
caryclark1049f122015-04-20 08:31:59 -07001118 if (lastId == this->debugID() && lastT == span->t()) {
caryclark54359292015-03-26 07:52:43 -07001119 continue;
1120 }
caryclark1049f122015-04-20 08:31:59 -07001121 lastId = this->debugID();
caryclark54359292015-03-26 07:52:43 -07001122 lastT = span->t();
caryclark1049f122015-04-20 08:31:59 -07001123 SkDebugf("%s id=%d", __FUNCTION__, this->debugID());
caryclark55888e42016-07-18 10:01:36 -07001124 // since endpoints may have be adjusted, show actual computed curves
1125 SkDCurve curvePart;
1126 this->subDivide(span, span->next(), &curvePart);
1127 const SkDPoint* pts = curvePart.fCubic.fPts;
1128 SkDebugf(" (%1.9g,%1.9g", pts[0].fX, pts[0].fY);
caryclark54359292015-03-26 07:52:43 -07001129 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
caryclark55888e42016-07-18 10:01:36 -07001130 SkDebugf(" %1.9g,%1.9g", pts[vIndex].fX, pts[vIndex].fY);
caryclark54359292015-03-26 07:52:43 -07001131 }
caryclark1049f122015-04-20 08:31:59 -07001132 if (SkPath::kConic_Verb == fVerb) {
caryclark55888e42016-07-18 10:01:36 -07001133 SkDebugf(" %1.9gf", curvePart.fConic.fWeight);
caryclark1049f122015-04-20 08:31:59 -07001134 }
caryclark55888e42016-07-18 10:01:36 -07001135 SkDebugf(") t=%1.9g tEnd=%1.9g", span->t(), span->next()->t());
caryclark54359292015-03-26 07:52:43 -07001136 if (span->windSum() == SK_MinS32) {
caryclark624637c2015-05-11 07:21:27 -07001137 SkDebugf(" windSum=?");
caryclark54359292015-03-26 07:52:43 -07001138 } else {
caryclark624637c2015-05-11 07:21:27 -07001139 SkDebugf(" windSum=%d", span->windSum());
caryclark54359292015-03-26 07:52:43 -07001140 }
caryclark624637c2015-05-11 07:21:27 -07001141 if (span->oppValue() && span->oppSum() == SK_MinS32) {
1142 SkDebugf(" oppSum=?");
1143 } else if (span->oppValue() || span->oppSum() != SK_MinS32) {
1144 SkDebugf(" oppSum=%d", span->oppSum());
1145 }
1146 SkDebugf(" windValue=%d", span->windValue());
1147 if (span->oppValue() || span->oppSum() != SK_MinS32) {
1148 SkDebugf(" oppValue=%d", span->oppValue());
1149 }
caryclark54359292015-03-26 07:52:43 -07001150 SkDebugf("\n");
1151 } while ((span = span->next()->upCastable()));
1152}
1153#endif
1154
1155#if DEBUG_MARK_DONE
1156void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding) {
1157 const SkPoint& pt = span->ptT()->fPt;
caryclark1049f122015-04-20 08:31:59 -07001158 SkDebugf("%s id=%d", fun, this->debugID());
caryclark54359292015-03-26 07:52:43 -07001159 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1160 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1161 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1162 }
1163 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
1164 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t());
1165 if (winding == SK_MinS32) {
1166 SkDebugf("?");
1167 } else {
1168 SkDebugf("%d", winding);
1169 }
1170 SkDebugf(" windSum=");
1171 if (span->windSum() == SK_MinS32) {
1172 SkDebugf("?");
1173 } else {
1174 SkDebugf("%d", span->windSum());
1175 }
1176 SkDebugf(" windValue=%d\n", span->windValue());
1177}
1178
1179void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding,
1180 int oppWinding) {
1181 const SkPoint& pt = span->ptT()->fPt;
caryclark1049f122015-04-20 08:31:59 -07001182 SkDebugf("%s id=%d", fun, this->debugID());
caryclark54359292015-03-26 07:52:43 -07001183 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1184 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1185 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1186 }
1187 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
1188 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t(), winding, oppWinding);
1189 if (winding == SK_MinS32) {
1190 SkDebugf("?");
1191 } else {
1192 SkDebugf("%d", winding);
1193 }
1194 SkDebugf(" newOppSum=");
1195 if (oppWinding == SK_MinS32) {
1196 SkDebugf("?");
1197 } else {
1198 SkDebugf("%d", oppWinding);
1199 }
1200 SkDebugf(" oppSum=");
1201 if (span->oppSum() == SK_MinS32) {
1202 SkDebugf("?");
1203 } else {
1204 SkDebugf("%d", span->oppSum());
1205 }
1206 SkDebugf(" windSum=");
1207 if (span->windSum() == SK_MinS32) {
1208 SkDebugf("?");
1209 } else {
1210 SkDebugf("%d", span->windSum());
1211 }
1212 SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue());
1213}
1214
1215#endif
1216
caryclark26ad22a2015-10-16 09:03:38 -07001217// loop looking for a pair of angle parts that are too close to be sorted
1218/* This is called after other more simple intersection and angle sorting tests have been exhausted.
1219 This should be rarely called -- the test below is thorough and time consuming.
caryclark55888e42016-07-18 10:01:36 -07001220 This checks the distance between start points; the distance between
caryclark26ad22a2015-10-16 09:03:38 -07001221*/
1222#if DEBUG_ANGLE
1223void SkOpAngle::debugCheckNearCoincidence() const {
1224 const SkOpAngle* test = this;
1225 do {
1226 const SkOpSegment* testSegment = test->segment();
1227 double testStartT = test->start()->t();
1228 SkDPoint testStartPt = testSegment->dPtAtT(testStartT);
1229 double testEndT = test->end()->t();
1230 SkDPoint testEndPt = testSegment->dPtAtT(testEndT);
1231 double testLenSq = testStartPt.distanceSquared(testEndPt);
1232 SkDebugf("%s testLenSq=%1.9g id=%d\n", __FUNCTION__, testLenSq, testSegment->debugID());
1233 double testMidT = (testStartT + testEndT) / 2;
1234 const SkOpAngle* next = test;
1235 while ((next = next->fNext) != this) {
1236 SkOpSegment* nextSegment = next->segment();
1237 double testMidDistSq = testSegment->distSq(testMidT, next);
1238 double testEndDistSq = testSegment->distSq(testEndT, next);
1239 double nextStartT = next->start()->t();
1240 SkDPoint nextStartPt = nextSegment->dPtAtT(nextStartT);
1241 double distSq = testStartPt.distanceSquared(nextStartPt);
1242 double nextEndT = next->end()->t();
1243 double nextMidT = (nextStartT + nextEndT) / 2;
1244 double nextMidDistSq = nextSegment->distSq(nextMidT, test);
1245 double nextEndDistSq = nextSegment->distSq(nextEndT, test);
1246 SkDebugf("%s distSq=%1.9g testId=%d nextId=%d\n", __FUNCTION__, distSq,
1247 testSegment->debugID(), nextSegment->debugID());
1248 SkDebugf("%s testMidDistSq=%1.9g\n", __FUNCTION__, testMidDistSq);
1249 SkDebugf("%s testEndDistSq=%1.9g\n", __FUNCTION__, testEndDistSq);
1250 SkDebugf("%s nextMidDistSq=%1.9g\n", __FUNCTION__, nextMidDistSq);
1251 SkDebugf("%s nextEndDistSq=%1.9g\n", __FUNCTION__, nextEndDistSq);
1252 SkDPoint nextEndPt = nextSegment->dPtAtT(nextEndT);
1253 double nextLenSq = nextStartPt.distanceSquared(nextEndPt);
1254 SkDebugf("%s nextLenSq=%1.9g\n", __FUNCTION__, nextLenSq);
1255 SkDebugf("\n");
1256 }
1257 test = test->fNext;
caryclark55888e42016-07-18 10:01:36 -07001258 } while (test->fNext != this);
caryclark26ad22a2015-10-16 09:03:38 -07001259}
1260#endif
1261
caryclark54359292015-03-26 07:52:43 -07001262#if DEBUG_ANGLE
1263SkString SkOpAngle::debugPart() const {
1264 SkString result;
1265 switch (this->segment()->verb()) {
1266 case SkPath::kLine_Verb:
caryclarkeed356d2016-09-14 07:18:20 -07001267 result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fPart.fCurve),
caryclark54359292015-03-26 07:52:43 -07001268 this->segment()->debugID());
1269 break;
1270 case SkPath::kQuad_Verb:
caryclarkeed356d2016-09-14 07:18:20 -07001271 result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fPart.fCurve),
caryclark54359292015-03-26 07:52:43 -07001272 this->segment()->debugID());
1273 break;
caryclark1049f122015-04-20 08:31:59 -07001274 case SkPath::kConic_Verb:
1275 result.printf(CONIC_DEBUG_STR " id=%d",
caryclarkeed356d2016-09-14 07:18:20 -07001276 CONIC_DEBUG_DATA(fPart.fCurve, fPart.fCurve.fConic.fWeight),
caryclark1049f122015-04-20 08:31:59 -07001277 this->segment()->debugID());
1278 break;
caryclark54359292015-03-26 07:52:43 -07001279 case SkPath::kCubic_Verb:
caryclarkeed356d2016-09-14 07:18:20 -07001280 result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fPart.fCurve),
caryclark54359292015-03-26 07:52:43 -07001281 this->segment()->debugID());
1282 break;
1283 default:
1284 SkASSERT(0);
mtklein1b249332015-07-07 12:21:21 -07001285 }
caryclark54359292015-03-26 07:52:43 -07001286 return result;
1287}
1288#endif
1289
caryclark624637c2015-05-11 07:21:27 -07001290#if DEBUG_SORT
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001291void SkOpAngle::debugLoop() const {
caryclarke4097e32014-06-18 07:24:19 -07001292 const SkOpAngle* first = this;
1293 const SkOpAngle* next = this;
1294 do {
1295 next->dumpOne(true);
caryclark19eb3b22014-07-18 05:08:14 -07001296 SkDebugf("\n");
caryclarke4097e32014-06-18 07:24:19 -07001297 next = next->fNext;
1298 } while (next && next != first);
caryclark54359292015-03-26 07:52:43 -07001299 next = first;
1300 do {
1301 next->debugValidate();
1302 next = next->fNext;
1303 } while (next && next != first);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001304}
1305#endif
1306
caryclark54359292015-03-26 07:52:43 -07001307void SkOpAngle::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07001308#if DEBUG_COINCIDENCE
1309 if (this->globalState()->debugCheckHealth()) {
1310 return;
1311 }
1312#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001313#if DEBUG_VALIDATE
caryclark54359292015-03-26 07:52:43 -07001314 const SkOpAngle* first = this;
1315 const SkOpAngle* next = this;
1316 int wind = 0;
1317 int opp = 0;
1318 int lastXor = -1;
1319 int lastOppXor = -1;
1320 do {
1321 if (next->unorderable()) {
1322 return;
1323 }
1324 const SkOpSpan* minSpan = next->start()->starter(next->end());
1325 if (minSpan->windValue() == SK_MinS32) {
1326 return;
1327 }
1328 bool op = next->segment()->operand();
1329 bool isXor = next->segment()->isXor();
1330 bool oppXor = next->segment()->oppXor();
1331 SkASSERT(!DEBUG_LIMIT_WIND_SUM || between(0, minSpan->windValue(), DEBUG_LIMIT_WIND_SUM));
1332 SkASSERT(!DEBUG_LIMIT_WIND_SUM
1333 || between(-DEBUG_LIMIT_WIND_SUM, minSpan->oppValue(), DEBUG_LIMIT_WIND_SUM));
1334 bool useXor = op ? oppXor : isXor;
1335 SkASSERT(lastXor == -1 || lastXor == (int) useXor);
1336 lastXor = (int) useXor;
caryclark624637c2015-05-11 07:21:27 -07001337 wind += next->debugSign() * (op ? minSpan->oppValue() : minSpan->windValue());
caryclark54359292015-03-26 07:52:43 -07001338 if (useXor) {
1339 wind &= 1;
1340 }
1341 useXor = op ? isXor : oppXor;
1342 SkASSERT(lastOppXor == -1 || lastOppXor == (int) useXor);
1343 lastOppXor = (int) useXor;
caryclark624637c2015-05-11 07:21:27 -07001344 opp += next->debugSign() * (op ? minSpan->windValue() : minSpan->oppValue());
caryclark54359292015-03-26 07:52:43 -07001345 if (useXor) {
1346 opp &= 1;
1347 }
1348 next = next->fNext;
1349 } while (next && next != first);
caryclark182b4992015-05-14 05:45:54 -07001350 SkASSERT(wind == 0 || !FLAGS_runFail);
caryclark54359292015-03-26 07:52:43 -07001351 SkASSERT(opp == 0 || !FLAGS_runFail);
1352#endif
1353}
1354
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001355void SkOpAngle::debugValidateNext() const {
caryclark54359292015-03-26 07:52:43 -07001356#if !FORCE_RELEASE
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001357 const SkOpAngle* first = this;
1358 const SkOpAngle* next = first;
1359 SkTDArray<const SkOpAngle*>(angles);
1360 do {
djsollenf2b340f2016-01-29 08:51:04 -08001361// SkASSERT_RELEASE(next->fSegment->debugContains(next));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001362 angles.push(next);
1363 next = next->next();
1364 if (next == first) {
1365 break;
1366 }
djsollenf2b340f2016-01-29 08:51:04 -08001367 SkASSERT_RELEASE(!angles.contains(next));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001368 if (!next) {
1369 return;
1370 }
1371 } while (true);
reed0dc4dd62015-03-24 13:55:33 -07001372#endif
reed0dc4dd62015-03-24 13:55:33 -07001373}
reed0dc4dd62015-03-24 13:55:33 -07001374
caryclark55888e42016-07-18 10:01:36 -07001375#ifdef SK_DEBUG
1376void SkCoincidentSpans::debugStartCheck(const SkOpSpanBase* outer, const SkOpSpanBase* over,
1377 const SkOpGlobalState* debugState) const {
1378 SkASSERT(coinPtTEnd()->span() == over || !debugState->debugRunFail());
1379 SkASSERT(oppPtTEnd()->span() == outer || !debugState->debugRunFail());
1380}
1381#endif
caryclark26ad22a2015-10-16 09:03:38 -07001382
Cary Clarkab87d7a2016-10-04 10:01:04 -04001383#if DEBUG_COIN
1384// sets the span's end to the ptT referenced by the previous-next
1385void SkCoincidentSpans::debugCorrectOneEnd(SkPathOpsDebug::GlitchLog* log,
1386 const SkOpPtT* (SkCoincidentSpans::* getEnd)() const,
1387 void (SkCoincidentSpans::*setEnd)(const SkOpPtT* ptT) const ) const {
1388 const SkOpPtT* origPtT = (this->*getEnd)();
1389 const SkOpSpanBase* origSpan = origPtT->span();
1390 const SkOpSpan* prev = origSpan->prev();
1391 const SkOpPtT* testPtT = prev ? prev->next()->ptT()
1392 : origSpan->upCast()->next()->prev()->ptT();
1393 if (origPtT != testPtT) {
1394 log->record(SkPathOpsDebug::kCorrectEnd_Glitch, this, origPtT, testPtT);
1395 }
1396}
1397
1398
1399/* Commented-out lines keep this in sync with correctEnds */
1400// FIXME: member pointers have fallen out of favor and can be replaced with
1401// an alternative approach.
1402// makes all span ends agree with the segment's spans that define them
1403void SkCoincidentSpans::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
1404 this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTStart, nullptr);
1405 this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTEnd, nullptr);
1406 this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTStart, nullptr);
1407 this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTEnd, nullptr);
1408}
1409
caryclark55888e42016-07-18 10:01:36 -07001410/* Commented-out lines keep this in sync with expand */
caryclark30b9fdd2016-08-31 14:36:29 -07001411// expand the range by checking adjacent spans for coincidence
Cary Clarkab87d7a2016-10-04 10:01:04 -04001412bool SkCoincidentSpans::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07001413 bool expanded = false;
1414 const SkOpSegment* segment = coinPtTStart()->segment();
1415 const SkOpSegment* oppSegment = oppPtTStart()->segment();
1416 do {
1417 const SkOpSpan* start = coinPtTStart()->span()->upCast();
1418 const SkOpSpan* prev = start->prev();
1419 const SkOpPtT* oppPtT;
1420 if (!prev || !(oppPtT = prev->contains(oppSegment))) {
1421 break;
1422 }
1423 double midT = (prev->t() + start->t()) / 2;
1424 if (!segment->isClose(midT, oppSegment)) {
1425 break;
1426 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001427 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, prev->ptT(), oppPtT);
caryclark55888e42016-07-18 10:01:36 -07001428 expanded = true;
1429 } while (false); // actual continues while expansion is possible
1430 do {
1431 const SkOpSpanBase* end = coinPtTEnd()->span();
1432 SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
caryclark30b9fdd2016-08-31 14:36:29 -07001433 if (next && next->deleted()) {
1434 break;
1435 }
caryclark55888e42016-07-18 10:01:36 -07001436 const SkOpPtT* oppPtT;
1437 if (!next || !(oppPtT = next->contains(oppSegment))) {
1438 break;
1439 }
1440 double midT = (end->t() + next->t()) / 2;
1441 if (!segment->isClose(midT, oppSegment)) {
1442 break;
1443 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001444 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, next->ptT(), oppPtT);
caryclark55888e42016-07-18 10:01:36 -07001445 expanded = true;
1446 } while (false); // actual continues while expansion is possible
1447 return expanded;
1448}
1449
Cary Clarkab87d7a2016-10-04 10:01:04 -04001450// description below
1451void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* base, const SkOpSpanBase* testSpan) const {
1452 const SkOpPtT* testPtT = testSpan->ptT();
1453 const SkOpPtT* stopPtT = testPtT;
1454 const SkOpSegment* baseSeg = base->segment();
1455 while ((testPtT = testPtT->next()) != stopPtT) {
1456 const SkOpSegment* testSeg = testPtT->segment();
1457 if (testPtT->deleted()) {
1458 continue;
1459 }
1460 if (testSeg == baseSeg) {
1461 continue;
1462 }
1463 if (testPtT->span()->ptT() != testPtT) {
1464 continue;
1465 }
1466 if (this->contains(baseSeg, testSeg, testPtT->fT)) {
1467 continue;
1468 }
1469 // intersect perp with base->ptT() with testPtT->segment()
1470 SkDVector dxdy = baseSeg->dSlopeAtT(base->t());
1471 const SkPoint& pt = base->pt();
1472 SkDLine ray = {{{pt.fX, pt.fY}, {pt.fX + dxdy.fY, pt.fY - dxdy.fX}}};
1473 SkIntersections i;
1474 (*CurveIntersectRay[testSeg->verb()])(testSeg->pts(), testSeg->weight(), ray, &i);
1475 for (int index = 0; index < i.used(); ++index) {
1476 double t = i[0][index];
1477 if (!between(0, t, 1)) {
1478 continue;
1479 }
1480 SkDPoint oppPt = i.pt(index);
1481 if (!oppPt.approximatelyEqual(pt)) {
1482 continue;
1483 }
1484 SkOpSegment* writableSeg = const_cast<SkOpSegment*>(testSeg);
1485 SkOpPtT* oppStart = writableSeg->addT(t);
1486 if (oppStart == testPtT) {
1487 continue;
1488 }
1489 SkOpSpan* writableBase = const_cast<SkOpSpan*>(base);
1490 oppStart->span()->addOpp(writableBase);
1491 if (oppStart->deleted()) {
1492 continue;
1493 }
1494 SkOpSegment* coinSeg = base->segment();
1495 SkOpSegment* oppSeg = oppStart->segment();
1496 double coinTs, coinTe, oppTs, oppTe;
1497 if (Ordered(coinSeg, oppSeg)) {
1498 coinTs = base->t();
1499 coinTe = testSpan->t();
1500 oppTs = oppStart->fT;
1501 oppTe = testPtT->fT;
1502 } else {
1503 SkTSwap(coinSeg, oppSeg);
1504 coinTs = oppStart->fT;
1505 coinTe = testPtT->fT;
1506 oppTs = base->t();
1507 oppTe = testSpan->t();
1508 }
1509 if (coinTs > coinTe) {
1510 SkTSwap(coinTs, coinTe);
1511 SkTSwap(oppTs, oppTe);
1512 }
1513 bool added;
1514 if (this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &added), false) {
1515 return;
1516 }
1517 }
1518 }
1519 return;
1520}
1521
1522// description below
1523void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* ptT) const {
1524 FAIL_IF(!ptT->span()->upCastable(), ptT->span());
1525 const SkOpSpan* base = ptT->span()->upCast();
1526 const SkOpSpan* prev = base->prev();
1527 FAIL_IF(!prev, ptT->span());
1528 if (!prev->isCanceled()) {
1529 if (this->debugAddEndMovedSpans(log, base, base->prev()), false) {
1530 return;
1531 }
1532 }
1533 if (!base->isCanceled()) {
1534 if (this->debugAddEndMovedSpans(log, base, base->next()), false) {
1535 return;
1536 }
1537 }
1538 return;
1539}
1540
1541/* If A is coincident with B and B includes an endpoint, and A's matching point
1542 is not the endpoint (i.e., there's an implied line connecting B-end and A)
1543 then assume that the same implied line may intersect another curve close to B.
1544 Since we only care about coincidence that was undetected, look at the
1545 ptT list on B-segment adjacent to the B-end/A ptT loop (not in the loop, but
1546 next door) and see if the A matching point is close enough to form another
1547 coincident pair. If so, check for a new coincident span between B-end/A ptT loop
1548 and the adjacent ptT loop.
1549*/
1550void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log) const {
1551 const SkCoincidentSpans* span = fHead;
1552 if (!span) {
1553 return;
1554 }
1555// fTop = span;
1556// fHead = nullptr;
1557 do {
1558 if (span->coinPtTStart()->fPt != span->oppPtTStart()->fPt) {
1559 FAIL_IF(1 == span->coinPtTStart()->fT, span);
1560 bool onEnd = span->coinPtTStart()->fT == 0;
1561 bool oOnEnd = zero_or_one(span->oppPtTStart()->fT);
1562 if (onEnd) {
1563 if (!oOnEnd) { // if both are on end, any nearby intersect was already found
1564 if (this->debugAddEndMovedSpans(log, span->oppPtTStart()), false) {
1565 return;
1566 }
1567 }
1568 } else if (oOnEnd) {
1569 if (this->debugAddEndMovedSpans(log, span->coinPtTStart()), false) {
1570 return;
1571 }
1572 }
1573 }
1574 if (span->coinPtTEnd()->fPt != span->oppPtTEnd()->fPt) {
1575 bool onEnd = span->coinPtTEnd()->fT == 1;
1576 bool oOnEnd = zero_or_one(span->oppPtTEnd()->fT);
1577 if (onEnd) {
1578 if (!oOnEnd) {
1579 if (this->debugAddEndMovedSpans(log, span->oppPtTEnd()), false) {
1580 return;
1581 }
1582 }
1583 } else if (oOnEnd) {
1584 if (this->debugAddEndMovedSpans(log, span->coinPtTEnd()), false) {
1585 return;
1586 }
1587 }
1588 }
1589 } while ((span = span->next()));
1590// this->restoreHead();
1591 return;
1592}
1593
caryclark55888e42016-07-18 10:01:36 -07001594/* Commented-out lines keep this in sync with addExpanded */
1595// for each coincident pair, match the spans
1596// 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 -04001597void SkOpCoincidence::debugAddExpanded(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07001598 const SkCoincidentSpans* coin = this->fHead;
1599 if (!coin) {
caryclarked0935a2015-10-22 07:23:52 -07001600 return;
1601 }
caryclark26ad22a2015-10-16 09:03:38 -07001602 do {
caryclark55888e42016-07-18 10:01:36 -07001603 const SkOpPtT* startPtT = coin->coinPtTStart();
1604 const SkOpPtT* oStartPtT = coin->oppPtTStart();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001605 double priorT = startPtT->fT;
1606 double oPriorT = oStartPtT->fT;
1607 FAIL_IF(startPtT->contains(oStartPtT), coin);
1608 SkOPASSERT(coin->coinPtTEnd()->contains(coin->oppPtTEnd()));
caryclark26ad22a2015-10-16 09:03:38 -07001609 const SkOpSpanBase* start = startPtT->span();
1610 const SkOpSpanBase* oStart = oStartPtT->span();
caryclark55888e42016-07-18 10:01:36 -07001611 const SkOpSpanBase* end = coin->coinPtTEnd()->span();
1612 const SkOpSpanBase* oEnd = coin->oppPtTEnd()->span();
caryclark30b9fdd2016-08-31 14:36:29 -07001613 FAIL_IF(oEnd->deleted(), coin);
1614 FAIL_IF(!start->upCastable(), coin);
caryclark26ad22a2015-10-16 09:03:38 -07001615 const SkOpSpanBase* test = start->upCast()->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001616 FAIL_IF(!coin->flipped() && !oStart->upCastable(), coin);
caryclark55888e42016-07-18 10:01:36 -07001617 const SkOpSpanBase* oTest = coin->flipped() ? oStart->prev() : oStart->upCast()->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001618 FAIL_IF(!oTest, coin);
1619 const SkOpSegment* seg = start->segment();
1620 const SkOpSegment* oSeg = oStart->segment();
caryclark26ad22a2015-10-16 09:03:38 -07001621 while (test != end || oTest != oEnd) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001622 const SkOpPtT* containedOpp = test->ptT()->contains(oSeg);
1623 const SkOpPtT* containedThis = oTest->ptT()->contains(seg);
1624 if (!containedOpp || !containedThis) {
1625 // choose the ends, or the first common pt-t list shared by both
1626 double nextT, oNextT;
1627 if (containedOpp) {
1628 nextT = test->t();
1629 oNextT = containedOpp->fT;
1630 } else if (containedThis) {
1631 nextT = containedThis->fT;
1632 oNextT = oTest->t();
1633 } else {
1634 // iterate through until a pt-t list found that contains the other
1635 const SkOpSpanBase* walk = test;
1636 const SkOpPtT* walkOpp;
1637 do {
1638 FAIL_IF(!walk->upCastable(), coin);
1639 walk = walk->upCast()->next();
1640 } while (!(walkOpp = walk->ptT()->contains(oSeg))
1641 && walk != coin->coinPtTEnd()->span());
1642 nextT = walk->t();
1643 oNextT = walkOpp->fT;
1644 }
caryclark26ad22a2015-10-16 09:03:38 -07001645 // use t ranges to guess which one is missing
caryclark55888e42016-07-18 10:01:36 -07001646 double startRange = coin->coinPtTEnd()->fT - startPtT->fT;
caryclark30b9fdd2016-08-31 14:36:29 -07001647 FAIL_IF(!startRange, coin);
caryclark26ad22a2015-10-16 09:03:38 -07001648 double startPart = (test->t() - startPtT->fT) / startRange;
caryclark55888e42016-07-18 10:01:36 -07001649 double oStartRange = coin->oppPtTEnd()->fT - oStartPtT->fT;
caryclark30b9fdd2016-08-31 14:36:29 -07001650 FAIL_IF(!oStartRange, coin);
caryclark26ad22a2015-10-16 09:03:38 -07001651 double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange;
caryclark30b9fdd2016-08-31 14:36:29 -07001652 FAIL_IF(startPart == oStartPart, coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001653 bool addToOpp = !containedOpp && !containedThis ? startPart < oStartPart
1654 : !!containedThis;
caryclark55888e42016-07-18 10:01:36 -07001655 bool startOver = false;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001656 addToOpp ? log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch,
1657 oPriorT + oStartRange * startPart, test)
1658 : log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch,
1659 priorT + startRange * oStartPart, oTest);
1660 // FAIL_IF(!success, coin);
caryclark55888e42016-07-18 10:01:36 -07001661 if (startOver) {
1662 test = start;
1663 oTest = oStart;
caryclark26ad22a2015-10-16 09:03:38 -07001664 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001665 end = coin->coinPtTEnd()->span();
1666 oEnd = coin->oppPtTEnd()->span();
caryclark26ad22a2015-10-16 09:03:38 -07001667 }
caryclark55888e42016-07-18 10:01:36 -07001668 if (test != end) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001669 FAIL_IF(!test->upCastable(), coin);
1670 priorT = test->t();
caryclark26ad22a2015-10-16 09:03:38 -07001671 test = test->upCast()->next();
1672 }
caryclark55888e42016-07-18 10:01:36 -07001673 if (oTest != oEnd) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001674 oPriorT = oTest->t();
caryclark55888e42016-07-18 10:01:36 -07001675 oTest = coin->flipped() ? oTest->prev() : oTest->upCast()->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001676 FAIL_IF(!oTest, coin);
caryclark26ad22a2015-10-16 09:03:38 -07001677 }
1678 }
caryclark55888e42016-07-18 10:01:36 -07001679 } while ((coin = coin->next()));
1680 return;
caryclark26ad22a2015-10-16 09:03:38 -07001681}
1682
caryclark55888e42016-07-18 10:01:36 -07001683/* Commented-out lines keep this in sync addIfMissing() */
caryclark8016b262016-09-06 05:59:47 -07001684// note that over1s, over1e, over2s, over2e are ordered
Cary Clarkab87d7a2016-10-04 10:01:04 -04001685void SkOpCoincidence::debugAddIfMissing(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* over1s, const SkOpPtT* over2s,
caryclark81a478c2016-09-09 09:37:57 -07001686 double tStart, double tEnd, const SkOpSegment* coinSeg, const SkOpSegment* oppSeg, bool* added,
caryclark8016b262016-09-06 05:59:47 -07001687 const SkOpPtT* over1e, const SkOpPtT* over2e) const {
1688 SkASSERT(tStart < tEnd);
1689 SkASSERT(over1s->fT < over1e->fT);
1690 SkASSERT(between(over1s->fT, tStart, over1e->fT));
1691 SkASSERT(between(over1s->fT, tEnd, over1e->fT));
1692 SkASSERT(over2s->fT < over2e->fT);
1693 SkASSERT(between(over2s->fT, tStart, over2e->fT));
1694 SkASSERT(between(over2s->fT, tEnd, over2e->fT));
1695 SkASSERT(over1s->segment() == over1e->segment());
1696 SkASSERT(over2s->segment() == over2e->segment());
1697 SkASSERT(over1s->segment() == over2s->segment());
1698 SkASSERT(over1s->segment() != coinSeg);
1699 SkASSERT(over1s->segment() != oppSeg);
1700 SkASSERT(coinSeg != oppSeg);
caryclark26ad22a2015-10-16 09:03:38 -07001701 double coinTs, coinTe, oppTs, oppTe;
caryclark8016b262016-09-06 05:59:47 -07001702 coinTs = TRange(over1s, tStart, coinSeg SkDEBUGPARAMS(over1e));
1703 coinTe = TRange(over1s, tEnd, coinSeg SkDEBUGPARAMS(over1e));
1704 if (coinSeg->collapsed(coinTs, coinTe)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001705 return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, coinSeg);
caryclark8016b262016-09-06 05:59:47 -07001706 }
1707 oppTs = TRange(over2s, tStart, oppSeg SkDEBUGPARAMS(over2e));
1708 oppTe = TRange(over2s, tEnd, oppSeg SkDEBUGPARAMS(over2e));
1709 if (oppSeg->collapsed(oppTs, oppTe)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001710 return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, oppSeg);
caryclark8016b262016-09-06 05:59:47 -07001711 }
1712 if (coinTs > coinTe) {
caryclark55888e42016-07-18 10:01:36 -07001713 SkTSwap(coinTs, coinTe);
caryclark26ad22a2015-10-16 09:03:38 -07001714 SkTSwap(oppTs, oppTe);
1715 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001716 return this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, added
caryclark8016b262016-09-06 05:59:47 -07001717 );
caryclark26ad22a2015-10-16 09:03:38 -07001718}
1719
caryclark55888e42016-07-18 10:01:36 -07001720/* Commented-out lines keep this in sync addOrOverlap() */
caryclark30b9fdd2016-08-31 14:36:29 -07001721// If this is called by addEndMovedSpans(), a returned false propogates out to an abort.
1722// If this is called by AddIfMissing(), a returned false indicates there was nothing to add
Cary Clarkab87d7a2016-10-04 10:01:04 -04001723void SkOpCoincidence::debugAddOrOverlap(SkPathOpsDebug::GlitchLog* log,
caryclark30b9fdd2016-08-31 14:36:29 -07001724 const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
caryclark81a478c2016-09-09 09:37:57 -07001725 double coinTs, double coinTe, double oppTs, double oppTe, bool* added) const {
caryclark55888e42016-07-18 10:01:36 -07001726 SkTDArray<SkCoincidentSpans*> overlaps;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001727 SkOPASSERT(!fTop); // this is (correctly) reversed in addifMissing()
caryclark81a478c2016-09-09 09:37:57 -07001728 if (fTop && !this->checkOverlap(fTop, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe,
1729 &overlaps)) {
caryclark55888e42016-07-18 10:01:36 -07001730 return;
1731 }
1732 if (fHead && !this->checkOverlap(fHead, coinSeg, oppSeg, coinTs,
1733 coinTe, oppTs, oppTe, &overlaps)) {
1734 return;
1735 }
1736 const SkCoincidentSpans* overlap = overlaps.count() ? overlaps[0] : nullptr;
1737 for (int index = 1; index < overlaps.count(); ++index) { // combine overlaps before continuing
1738 const SkCoincidentSpans* test = overlaps[index];
1739 if (overlap->coinPtTStart()->fT > test->coinPtTStart()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001740 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTStart());
caryclark55888e42016-07-18 10:01:36 -07001741 }
1742 if (overlap->coinPtTEnd()->fT < test->coinPtTEnd()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001743 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07001744 }
1745 if (overlap->flipped()
1746 ? overlap->oppPtTStart()->fT < test->oppPtTStart()->fT
1747 : overlap->oppPtTStart()->fT > test->oppPtTStart()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001748 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTStart());
caryclark55888e42016-07-18 10:01:36 -07001749 }
1750 if (overlap->flipped()
1751 ? overlap->oppPtTEnd()->fT > test->oppPtTEnd()->fT
1752 : overlap->oppPtTEnd()->fT < test->oppPtTEnd()->fT) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001753 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07001754 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001755 if (!fHead) { this->debugRelease(log, fHead, test);
1756 this->debugRelease(log, fTop, test);
caryclark55888e42016-07-18 10:01:36 -07001757 }
1758 }
1759 const SkOpPtT* cs = coinSeg->existing(coinTs, oppSeg);
1760 const SkOpPtT* ce = coinSeg->existing(coinTe, oppSeg);
caryclark30b9fdd2016-08-31 14:36:29 -07001761 RETURN_FALSE_IF(overlap && cs && ce && overlap->contains(cs, ce), coinSeg);
1762 RETURN_FALSE_IF(cs != ce || !cs, coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001763 const SkOpPtT* os = oppSeg->existing(oppTs, coinSeg);
1764 const SkOpPtT* oe = oppSeg->existing(oppTe, coinSeg);
caryclark30b9fdd2016-08-31 14:36:29 -07001765 RETURN_FALSE_IF(overlap && os && oe && overlap->contains(os, oe), oppSeg);
caryclark55888e42016-07-18 10:01:36 -07001766 SkASSERT(true || !cs || !cs->deleted());
1767 SkASSERT(true || !os || !os->deleted());
1768 SkASSERT(true || !ce || !ce->deleted());
1769 SkASSERT(true || !oe || !oe->deleted());
1770 const SkOpPtT* csExisting = !cs ? coinSeg->existing(coinTs, nullptr) : nullptr;
1771 const SkOpPtT* ceExisting = !ce ? coinSeg->existing(coinTe, nullptr) : nullptr;
caryclark30b9fdd2016-08-31 14:36:29 -07001772 RETURN_FALSE_IF(csExisting && csExisting == ceExisting, coinSeg);
1773 RETURN_FALSE_IF(csExisting && (csExisting == ce ||
1774 csExisting->contains(ceExisting ? ceExisting : ce)), coinSeg);
1775 RETURN_FALSE_IF(ceExisting && (ceExisting == cs ||
1776 ceExisting->contains(csExisting ? csExisting : cs)), coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001777 const SkOpPtT* osExisting = !os ? oppSeg->existing(oppTs, nullptr) : nullptr;
1778 const SkOpPtT* oeExisting = !oe ? oppSeg->existing(oppTe, nullptr) : nullptr;
caryclark30b9fdd2016-08-31 14:36:29 -07001779 RETURN_FALSE_IF(osExisting && osExisting == oeExisting, oppSeg);
1780 RETURN_FALSE_IF(osExisting && (osExisting == oe ||
1781 osExisting->contains(oeExisting ? oeExisting : oe)), oppSeg);
1782 RETURN_FALSE_IF(oeExisting && (oeExisting == os ||
1783 oeExisting->contains(osExisting ? osExisting : os)), oppSeg);
caryclark55888e42016-07-18 10:01:36 -07001784 bool csDeleted = false, osDeleted = false, ceDeleted = false, oeDeleted = false;
1785 this->debugValidate();
1786 if (!cs || !os) {
1787 if (!cs)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001788 cs = coinSeg->debugAddT(coinTs, log);
caryclark55888e42016-07-18 10:01:36 -07001789 if (!os)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001790 os = oppSeg->debugAddT(oppTs, log);
caryclark30b9fdd2016-08-31 14:36:29 -07001791// RETURN_FALSE_IF(callerAborts, !csWritable || !osWritable);
Cary Clarkab87d7a2016-10-04 10:01:04 -04001792 if (cs && os) cs->span()->debugAddOpp(log, os->span());
caryclark55888e42016-07-18 10:01:36 -07001793// cs = csWritable;
caryclark30b9fdd2016-08-31 14:36:29 -07001794// os = osWritable->active();
1795 RETURN_FALSE_IF((ce && ce->deleted()) || (oe && oe->deleted()), coinSeg);
caryclark55888e42016-07-18 10:01:36 -07001796 }
1797 if (!ce || !oe) {
1798 if (!ce)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001799 ce = coinSeg->debugAddT(coinTe, log);
caryclark55888e42016-07-18 10:01:36 -07001800 if (!oe)
Cary Clarkab87d7a2016-10-04 10:01:04 -04001801 oe = oppSeg->debugAddT(oppTe, log);
1802 if (ce && oe) ce->span()->debugAddOpp(log, oe->span());
caryclark55888e42016-07-18 10:01:36 -07001803// ce = ceWritable;
1804// oe = oeWritable;
1805 }
1806 this->debugValidate();
caryclark30b9fdd2016-08-31 14:36:29 -07001807 RETURN_FALSE_IF(csDeleted, coinSeg);
1808 RETURN_FALSE_IF(osDeleted, oppSeg);
1809 RETURN_FALSE_IF(ceDeleted, coinSeg);
1810 RETURN_FALSE_IF(oeDeleted, oppSeg);
caryclark8016b262016-09-06 05:59:47 -07001811 RETURN_FALSE_IF(!cs || !ce || cs == ce || cs->contains(ce) || !os || !oe || os == oe || os->contains(oe), coinSeg);
1812 bool result = true;
caryclark55888e42016-07-18 10:01:36 -07001813 if (overlap) {
1814 if (overlap->coinPtTStart()->segment() == coinSeg) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001815 log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
caryclark55888e42016-07-18 10:01:36 -07001816 } else {
1817 if (oppTs > oppTe) {
1818 SkTSwap(coinTs, coinTe);
1819 SkTSwap(oppTs, oppTe);
1820 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001821 log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, oppSeg, oppTs, oppTe, coinSeg, coinTs, coinTe);
caryclark55888e42016-07-18 10:01:36 -07001822 }
caryclark8016b262016-09-06 05:59:47 -07001823#if 0 && DEBUG_COINCIDENCE_VERBOSE
1824 if (result) {
1825 overlap->debugShow();
1826 }
caryclark55888e42016-07-18 10:01:36 -07001827#endif
1828 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001829 log->record(SkPathOpsDebug::kAddMissingCoin_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
caryclark8016b262016-09-06 05:59:47 -07001830#if 0 && DEBUG_COINCIDENCE_VERBOSE
1831 fHead->debugShow();
caryclark55888e42016-07-18 10:01:36 -07001832#endif
1833 }
1834 this->debugValidate();
caryclark8016b262016-09-06 05:59:47 -07001835 return (void) result;
caryclark55888e42016-07-18 10:01:36 -07001836}
1837
1838// Extra commented-out lines keep this in sync with addMissing()
1839/* detects overlaps of different coincident runs on same segment */
1840/* does not detect overlaps for pairs without any segments in common */
1841// returns true if caller should loop again
Cary Clarkab87d7a2016-10-04 10:01:04 -04001842void SkOpCoincidence::debugAddMissing(SkPathOpsDebug::GlitchLog* log, bool* added) const {
caryclark26ad22a2015-10-16 09:03:38 -07001843 const SkCoincidentSpans* outer = fHead;
Cary Clarkab87d7a2016-10-04 10:01:04 -04001844 *added = false;
caryclark26ad22a2015-10-16 09:03:38 -07001845 if (!outer) {
1846 return;
1847 }
caryclark55888e42016-07-18 10:01:36 -07001848 // fTop = outer;
1849 // fHead = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -07001850 do {
1851 // addifmissing can modify the list that this is walking
1852 // save head so that walker can iterate over old data unperturbed
1853 // addifmissing adds to head freely then add saved head in the end
caryclark8016b262016-09-06 05:59:47 -07001854 const SkOpPtT* ocs = outer->coinPtTStart();
1855 SkASSERT(!ocs->deleted());
1856 const SkOpSegment* outerCoin = ocs->segment();
1857 SkASSERT(!outerCoin->done()); // if it's done, should have already been removed from list
1858 const SkOpPtT* oos = outer->oppPtTStart();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001859 if (oos->deleted()) {
1860 return;
1861 }
caryclark8016b262016-09-06 05:59:47 -07001862 const SkOpSegment* outerOpp = oos->segment();
1863 SkASSERT(!outerOpp->done());
1864// SkOpSegment* outerCoinWritable = const_cast<SkOpSegment*>(outerCoin);
1865// SkOpSegment* outerOppWritable = const_cast<SkOpSegment*>(outerOpp);
caryclark26ad22a2015-10-16 09:03:38 -07001866 const SkCoincidentSpans* inner = outer;
caryclark55888e42016-07-18 10:01:36 -07001867 while ((inner = inner->next())) {
1868 this->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001869 double overS, overE;
caryclark8016b262016-09-06 05:59:47 -07001870 const SkOpPtT* ics = inner->coinPtTStart();
1871 SkASSERT(!ics->deleted());
1872 const SkOpSegment* innerCoin = ics->segment();
1873 SkASSERT(!innerCoin->done());
1874 const SkOpPtT* ios = inner->oppPtTStart();
1875 SkASSERT(!ios->deleted());
1876 const SkOpSegment* innerOpp = ios->segment();
1877 SkASSERT(!innerOpp->done());
1878// SkOpSegment* innerCoinWritable = const_cast<SkOpSegment*>(innerCoin);
1879// SkOpSegment* innerOppWritable = const_cast<SkOpSegment*>(innerOpp);
caryclark55888e42016-07-18 10:01:36 -07001880 if (outerCoin == innerCoin) {
caryclark8016b262016-09-06 05:59:47 -07001881 const SkOpPtT* oce = outer->coinPtTEnd();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001882 if (oce->deleted()) {
1883 return;
1884 }
caryclark8016b262016-09-06 05:59:47 -07001885 const SkOpPtT* ice = inner->coinPtTEnd();
1886 SkASSERT(!ice->deleted());
1887 if (outerOpp != innerOpp && this->overlap(ocs, oce, ics, ice, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001888 this->debugAddIfMissing(log, ocs->starter(oce), ics->starter(ice),
caryclark81a478c2016-09-09 09:37:57 -07001889 overS, overE, outerOpp, innerOpp, added,
caryclark8016b262016-09-06 05:59:47 -07001890 ocs->debugEnder(oce),
1891 ics->debugEnder(ice));
caryclark26ad22a2015-10-16 09:03:38 -07001892 }
caryclark55888e42016-07-18 10:01:36 -07001893 } else if (outerCoin == innerOpp) {
caryclark8016b262016-09-06 05:59:47 -07001894 const SkOpPtT* oce = outer->coinPtTEnd();
1895 SkASSERT(!oce->deleted());
1896 const SkOpPtT* ioe = inner->oppPtTEnd();
1897 SkASSERT(!ioe->deleted());
1898 if (outerOpp != innerCoin && this->overlap(ocs, oce, ios, ioe, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001899 this->debugAddIfMissing(log, ocs->starter(oce), ios->starter(ioe),
caryclark81a478c2016-09-09 09:37:57 -07001900 overS, overE, outerOpp, innerCoin, added,
caryclark8016b262016-09-06 05:59:47 -07001901 ocs->debugEnder(oce),
1902 ios->debugEnder(ioe));
caryclark26ad22a2015-10-16 09:03:38 -07001903 }
caryclark55888e42016-07-18 10:01:36 -07001904 } else if (outerOpp == innerCoin) {
caryclark8016b262016-09-06 05:59:47 -07001905 const SkOpPtT* ooe = outer->oppPtTEnd();
1906 SkASSERT(!ooe->deleted());
1907 const SkOpPtT* ice = inner->coinPtTEnd();
1908 SkASSERT(!ice->deleted());
caryclark55888e42016-07-18 10:01:36 -07001909 SkASSERT(outerCoin != innerOpp);
caryclark8016b262016-09-06 05:59:47 -07001910 if (this->overlap(oos, ooe, ics, ice, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001911 this->debugAddIfMissing(log, oos->starter(ooe), ics->starter(ice),
caryclark81a478c2016-09-09 09:37:57 -07001912 overS, overE, outerCoin, innerOpp, added,
caryclark8016b262016-09-06 05:59:47 -07001913 oos->debugEnder(ooe),
1914 ics->debugEnder(ice));
caryclark26ad22a2015-10-16 09:03:38 -07001915 }
caryclark55888e42016-07-18 10:01:36 -07001916 } else if (outerOpp == innerOpp) {
caryclark8016b262016-09-06 05:59:47 -07001917 const SkOpPtT* ooe = outer->oppPtTEnd();
1918 SkASSERT(!ooe->deleted());
1919 const SkOpPtT* ioe = inner->oppPtTEnd();
Cary Clarkab87d7a2016-10-04 10:01:04 -04001920 if (ioe->deleted()) {
1921 return;
1922 }
caryclark55888e42016-07-18 10:01:36 -07001923 SkASSERT(outerCoin != innerCoin);
caryclark8016b262016-09-06 05:59:47 -07001924 if (this->overlap(oos, ooe, ios, ioe, &overS, &overE)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001925 this->debugAddIfMissing(log, oos->starter(ooe), ios->starter(ioe),
caryclark81a478c2016-09-09 09:37:57 -07001926 overS, overE, outerCoin, innerCoin, added,
caryclark8016b262016-09-06 05:59:47 -07001927 oos->debugEnder(ooe),
1928 ios->debugEnder(ioe));
caryclark26ad22a2015-10-16 09:03:38 -07001929 }
1930 }
caryclark55888e42016-07-18 10:01:36 -07001931 this->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001932 }
caryclark55888e42016-07-18 10:01:36 -07001933 } while ((outer = outer->next()));
1934 // this->restoreHead();
1935 return;
caryclark26ad22a2015-10-16 09:03:38 -07001936}
1937
caryclark55888e42016-07-18 10:01:36 -07001938// Commented-out lines keep this in sync with release()
Cary Clarkab87d7a2016-10-04 10:01:04 -04001939void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkCoincidentSpans* remove) const {
caryclark30b9fdd2016-08-31 14:36:29 -07001940 const SkCoincidentSpans* head = coin;
1941 const SkCoincidentSpans* prev = nullptr;
1942 const SkCoincidentSpans* next;
1943 do {
1944 next = coin->next();
1945 if (coin == remove) {
1946 if (prev) {
1947// prev->setNext(next);
1948 } else if (head == fHead) {
1949// fHead = next;
1950 } else {
1951// fTop = next;
1952 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04001953 log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
caryclark30b9fdd2016-08-31 14:36:29 -07001954 }
1955 prev = coin;
1956 } while ((coin = next));
1957 return;
1958}
1959
Cary Clarkab87d7a2016-10-04 10:01:04 -04001960void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* deleted) const {
caryclark55888e42016-07-18 10:01:36 -07001961 const SkCoincidentSpans* coin = fHead;
1962 if (!coin) {
1963 return;
1964 }
1965 do {
1966 if (coin->coinPtTStart()->segment() == deleted
1967 || coin->coinPtTEnd()->segment() == deleted
1968 || coin->oppPtTStart()->segment() == deleted
1969 || coin->oppPtTEnd()->segment() == deleted) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001970 log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07001971 }
1972 } while ((coin = coin->next()));
1973}
1974
caryclark55888e42016-07-18 10:01:36 -07001975// Commented-out lines keep this in sync with expand()
1976// expand the range by checking adjacent spans for coincidence
Cary Clarkab87d7a2016-10-04 10:01:04 -04001977bool SkOpCoincidence::debugExpand(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07001978 const SkCoincidentSpans* coin = fHead;
1979 if (!coin) {
1980 return false;
1981 }
1982 bool expanded = false;
1983 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001984 if (coin->debugExpand(log)) {
caryclark55888e42016-07-18 10:01:36 -07001985 // check to see if multiple spans expanded so they are now identical
1986 const SkCoincidentSpans* test = fHead;
1987 do {
1988 if (coin == test) {
1989 continue;
1990 }
1991 if (coin->coinPtTStart() == test->coinPtTStart()
1992 && coin->oppPtTStart() == test->oppPtTStart()) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04001993 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, fHead, test->coinPtTStart());
caryclark55888e42016-07-18 10:01:36 -07001994 break;
1995 }
1996 } while ((test = test->next()));
1997 expanded = true;
caryclark26ad22a2015-10-16 09:03:38 -07001998 }
caryclark55888e42016-07-18 10:01:36 -07001999 } while ((coin = coin->next()));
caryclark26ad22a2015-10-16 09:03:38 -07002000 return expanded;
2001}
2002
caryclark55888e42016-07-18 10:01:36 -07002003// Commented-out lines keep this in sync with mark()
2004/* this sets up the coincidence links in the segments when the coincidence crosses multiple spans */
Cary Clarkab87d7a2016-10-04 10:01:04 -04002005void SkOpCoincidence::debugMark(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07002006 const SkCoincidentSpans* coin = fHead;
2007 if (!coin) {
2008 return;
2009 }
2010 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002011 FAIL_IF(!coin->coinPtTStartWritable()->span()->upCastable(), coin);
caryclark55888e42016-07-18 10:01:36 -07002012 const SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast();
2013// SkASSERT(start->deleted());
2014 const SkOpSpanBase* end = coin->coinPtTEndWritable()->span();
2015// SkASSERT(end->deleted());
2016 const SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span();
2017// SkASSERT(oStart->deleted());
2018 const SkOpSpanBase* oEnd = coin->oppPtTEndWritable()->span();
2019// SkASSERT(oEnd->deleted());
2020 bool flipped = coin->flipped();
caryclark26ad22a2015-10-16 09:03:38 -07002021 if (flipped) {
2022 SkTSwap(oStart, oEnd);
2023 }
caryclark55888e42016-07-18 10:01:36 -07002024 /* coin and opp spans may not match up. Mark the ends, and then let the interior
2025 get marked as many times as the spans allow */
Cary Clarkab87d7a2016-10-04 10:01:04 -04002026 start->debugInsertCoincidence(log, oStart->upCast());
2027 end->debugInsertCoinEnd(log, oEnd);
caryclark55888e42016-07-18 10:01:36 -07002028 const SkOpSegment* segment = start->segment();
2029 const SkOpSegment* oSegment = oStart->segment();
caryclark26ad22a2015-10-16 09:03:38 -07002030 const SkOpSpanBase* next = start;
2031 const SkOpSpanBase* oNext = oStart;
Cary Clarkab87d7a2016-10-04 10:01:04 -04002032 bool ordered = coin->ordered();
caryclark55888e42016-07-18 10:01:36 -07002033 while ((next = next->upCast()->next()) != end) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002034 FAIL_IF(!next->upCastable(), coin);
2035 if (next->upCast()->debugInsertCoincidence(log, oSegment, flipped, ordered), false) {
caryclark55888e42016-07-18 10:01:36 -07002036 return;
caryclark26ad22a2015-10-16 09:03:38 -07002037 }
caryclark55888e42016-07-18 10:01:36 -07002038 }
2039 while ((oNext = oNext->upCast()->next()) != oEnd) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002040 FAIL_IF(!oNext->upCastable(), coin);
2041 if (oNext->upCast()->debugInsertCoincidence(log, segment, flipped, ordered), false) {
caryclark55888e42016-07-18 10:01:36 -07002042 return;
caryclark26ad22a2015-10-16 09:03:38 -07002043 }
caryclark55888e42016-07-18 10:01:36 -07002044 }
2045 } while ((coin = coin->next()));
2046 return;
caryclark26ad22a2015-10-16 09:03:38 -07002047}
2048#endif
2049
Cary Clarkab87d7a2016-10-04 10:01:04 -04002050#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002051// Commented-out lines keep this in sync with markCollapsed()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002052void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkOpPtT* test) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002053 const SkCoincidentSpans* head = coin;
caryclark55888e42016-07-18 10:01:36 -07002054 while (coin) {
2055 if (coin->collapsed(test)) {
2056 if (zero_or_one(coin->coinPtTStart()->fT) && zero_or_one(coin->coinPtTEnd()->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002057 log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07002058 }
2059 if (zero_or_one(coin->oppPtTStart()->fT) && zero_or_one(coin->oppPtTEnd()->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002060 log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin);
caryclark55888e42016-07-18 10:01:36 -07002061 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002062 this->debugRelease(log, head, coin);
caryclark55888e42016-07-18 10:01:36 -07002063 }
2064 coin = coin->next();
caryclark624637c2015-05-11 07:21:27 -07002065 }
2066}
2067
caryclark55888e42016-07-18 10:01:36 -07002068// Commented-out lines keep this in sync with markCollapsed()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002069void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* test) const {
2070 this->debugMarkCollapsed(log, fHead, test);
2071 this->debugMarkCollapsed(log, fTop, test);
caryclark55888e42016-07-18 10:01:36 -07002072}
2073#endif
2074
2075void SkCoincidentSpans::debugShow() const {
caryclark6c3b9cd2016-09-26 05:36:58 -07002076 SkDebugf("coinSpan - id=%d t=%1.9g tEnd=%1.9g\n", coinPtTStart()->segment()->debugID(),
caryclark55888e42016-07-18 10:01:36 -07002077 coinPtTStart()->fT, coinPtTEnd()->fT);
caryclark6c3b9cd2016-09-26 05:36:58 -07002078 SkDebugf("coinSpan + id=%d t=%1.9g tEnd=%1.9g\n", oppPtTStart()->segment()->debugID(),
caryclark55888e42016-07-18 10:01:36 -07002079 oppPtTStart()->fT, oppPtTEnd()->fT);
2080}
2081
2082void SkOpCoincidence::debugShowCoincidence() const {
caryclark26ad22a2015-10-16 09:03:38 -07002083#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -07002084 const SkCoincidentSpans* span = fHead;
2085 while (span) {
2086 span->debugShow();
2087 span = span->next();
2088 }
2089#endif
2090}
2091
Cary Clarkab87d7a2016-10-04 10:01:04 -04002092#if DEBUG_COIN
caryclark6c3b9cd2016-09-26 05:36:58 -07002093static void DebugCheckBetween(const SkOpSpanBase* next, const SkOpSpanBase* end,
caryclark55888e42016-07-18 10:01:36 -07002094 double oStart, double oEnd, const SkOpSegment* oSegment,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002095 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002096 SkASSERT(next != end);
2097 SkASSERT(!next->contains(end) || log);
2098 if (next->t() > end->t()) {
2099 SkTSwap(next, end);
2100 }
2101 do {
2102 const SkOpPtT* ptT = next->ptT();
2103 int index = 0;
caryclark27c015d2016-09-23 05:47:20 -07002104 bool somethingBetween = false;
caryclark55888e42016-07-18 10:01:36 -07002105 do {
2106 ++index;
2107 ptT = ptT->next();
2108 const SkOpPtT* checkPtT = next->ptT();
2109 if (ptT == checkPtT) {
2110 break;
2111 }
2112 bool looped = false;
2113 for (int check = 0; check < index; ++check) {
2114 if ((looped = checkPtT == ptT)) {
2115 break;
2116 }
2117 checkPtT = checkPtT->next();
2118 }
2119 if (looped) {
2120 SkASSERT(0);
2121 break;
2122 }
2123 if (ptT->deleted()) {
2124 continue;
2125 }
2126 if (ptT->segment() != oSegment) {
2127 continue;
2128 }
2129 somethingBetween |= between(oStart, ptT->fT, oEnd);
2130 } while (true);
2131 SkASSERT(somethingBetween);
2132 } while (next != end && (next = next->upCast()->next()));
2133}
2134
2135static void DebugCheckOverlap(const SkCoincidentSpans* test, const SkCoincidentSpans* list,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002136 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002137 if (!list) {
2138 return;
2139 }
2140 const SkOpSegment* coinSeg = test->coinPtTStart()->segment();
2141 SkASSERT(coinSeg == test->coinPtTEnd()->segment());
2142 const SkOpSegment* oppSeg = test->oppPtTStart()->segment();
2143 SkASSERT(oppSeg == test->oppPtTEnd()->segment());
2144 SkASSERT(coinSeg != test->oppPtTStart()->segment());
2145 SkDEBUGCODE(double tcs = test->coinPtTStart()->fT);
2146 SkASSERT(between(0, tcs, 1));
2147 SkDEBUGCODE(double tce = test->coinPtTEnd()->fT);
2148 SkASSERT(between(0, tce, 1));
2149 SkASSERT(tcs < tce);
2150 double tos = test->oppPtTStart()->fT;
2151 SkASSERT(between(0, tos, 1));
2152 double toe = test->oppPtTEnd()->fT;
2153 SkASSERT(between(0, toe, 1));
2154 SkASSERT(tos != toe);
2155 if (tos > toe) {
2156 SkTSwap(tos, toe);
2157 }
2158 do {
2159 double lcs, lce, los, loe;
2160 if (coinSeg == list->coinPtTStart()->segment()) {
2161 if (oppSeg != list->oppPtTStart()->segment()) {
2162 continue;
2163 }
2164 lcs = list->coinPtTStart()->fT;
2165 lce = list->coinPtTEnd()->fT;
2166 los = list->oppPtTStart()->fT;
2167 loe = list->oppPtTEnd()->fT;
2168 if (los > loe) {
2169 SkTSwap(los, loe);
2170 }
2171 } else if (coinSeg == list->oppPtTStart()->segment()) {
2172 if (oppSeg != list->coinPtTStart()->segment()) {
2173 continue;
2174 }
2175 lcs = list->oppPtTStart()->fT;
2176 lce = list->oppPtTEnd()->fT;
2177 if (lcs > lce) {
2178 SkTSwap(lcs, lce);
2179 }
2180 los = list->coinPtTStart()->fT;
2181 loe = list->coinPtTEnd()->fT;
2182 } else {
2183 continue;
2184 }
2185 SkASSERT(tce < lcs || lce < tcs);
2186 SkASSERT(toe < los || loe < tos);
2187 } while ((list = list->next()));
2188}
2189
2190
2191static void DebugCheckOverlapTop(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002192 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002193 // check for overlapping coincident spans
2194 const SkCoincidentSpans* test = head;
2195 while (test) {
2196 const SkCoincidentSpans* next = test->next();
Cary Clarkab87d7a2016-10-04 10:01:04 -04002197 DebugCheckOverlap(test, next, log);
2198 DebugCheckOverlap(test, opt, log);
caryclark55888e42016-07-18 10:01:36 -07002199 test = next;
2200 }
2201}
2202
caryclark55888e42016-07-18 10:01:36 -07002203static void DebugValidate(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002204 SkPathOpsDebug::GlitchLog* log) {
caryclark55888e42016-07-18 10:01:36 -07002205 // look for pts inside coincident spans that are not inside the opposite spans
2206 const SkCoincidentSpans* coin = head;
2207 while (coin) {
2208 SkASSERT(SkOpCoincidence::Ordered(coin->coinPtTStart()->segment(),
2209 coin->oppPtTStart()->segment()));
2210 SkASSERT(coin->coinPtTStart()->span()->ptT() == coin->coinPtTStart());
2211 SkASSERT(coin->coinPtTEnd()->span()->ptT() == coin->coinPtTEnd());
2212 SkASSERT(coin->oppPtTStart()->span()->ptT() == coin->oppPtTStart());
2213 SkASSERT(coin->oppPtTEnd()->span()->ptT() == coin->oppPtTEnd());
caryclark55888e42016-07-18 10:01:36 -07002214 coin = coin->next();
2215 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002216 DebugCheckOverlapTop(head, opt, log);
caryclark55888e42016-07-18 10:01:36 -07002217}
2218#endif
2219
2220void SkOpCoincidence::debugValidate() const {
2221#if DEBUG_COINCIDENCE
Cary Clarkab87d7a2016-10-04 10:01:04 -04002222 DebugValidate(fHead, fTop, nullptr);
2223 DebugValidate(fTop, nullptr, nullptr);
caryclark55888e42016-07-18 10:01:36 -07002224#endif
2225}
2226
Cary Clarkab87d7a2016-10-04 10:01:04 -04002227#if DEBUG_COIN
caryclark6c3b9cd2016-09-26 05:36:58 -07002228static void DebugCheckBetween(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
Cary Clarkab87d7a2016-10-04 10:01:04 -04002229 SkPathOpsDebug::GlitchLog* log) {
caryclark6c3b9cd2016-09-26 05:36:58 -07002230 // look for pts inside coincident spans that are not inside the opposite spans
2231 const SkCoincidentSpans* coin = head;
2232 while (coin) {
2233 DebugCheckBetween(coin->coinPtTStart()->span(), coin->coinPtTEnd()->span(),
2234 coin->oppPtTStart()->fT, coin->oppPtTEnd()->fT, coin->oppPtTStart()->segment(),
Cary Clarkab87d7a2016-10-04 10:01:04 -04002235 log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002236 DebugCheckBetween(coin->oppPtTStart()->span(), coin->oppPtTEnd()->span(),
2237 coin->coinPtTStart()->fT, coin->coinPtTEnd()->fT, coin->coinPtTStart()->segment(),
Cary Clarkab87d7a2016-10-04 10:01:04 -04002238 log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002239 coin = coin->next();
2240 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002241 DebugCheckOverlapTop(head, opt, log);
caryclark6c3b9cd2016-09-26 05:36:58 -07002242}
2243#endif
2244
2245void SkOpCoincidence::debugCheckBetween() const {
2246#if DEBUG_COINCIDENCE
2247 if (fGlobalState->debugCheckHealth()) {
2248 return;
2249 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002250 DebugCheckBetween(fHead, fTop, nullptr);
2251 DebugCheckBetween(fTop, nullptr, nullptr);
caryclark6c3b9cd2016-09-26 05:36:58 -07002252#endif
2253}
2254
Cary Clarkab87d7a2016-10-04 10:01:04 -04002255#if DEBUG_COIN
2256void SkOpContour::debugCheckHealth(SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07002257 const SkOpSegment* segment = &fHead;
2258 do {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002259 segment->debugCheckHealth(log);
caryclark26ad22a2015-10-16 09:03:38 -07002260 } while ((segment = segment->next()));
2261}
2262
Cary Clarkab87d7a2016-10-04 10:01:04 -04002263void SkOpCoincidence::debugCheckValid(SkPathOpsDebug::GlitchLog* log) const {
2264#if DEBUG_VALIDATE
2265 DebugValidate(fHead, fTop, log);
2266 DebugValidate(fTop, nullptr, log);
2267#endif
2268}
2269
2270void SkOpCoincidence::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const {
2271 const SkCoincidentSpans* coin = fHead;
2272 if (!coin) {
2273 return;
2274 }
2275 do {
2276 coin->debugCorrectEnds(log);
2277 } while ((coin = coin->next()));
2278}
2279
caryclark55888e42016-07-18 10:01:36 -07002280// commmented-out lines keep this aligned with missingCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002281void SkOpContour::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07002282// SkASSERT(fCount > 0);
caryclark26ad22a2015-10-16 09:03:38 -07002283 const SkOpSegment* segment = &fHead;
caryclark55888e42016-07-18 10:01:36 -07002284// bool result = false;
caryclark26ad22a2015-10-16 09:03:38 -07002285 do {
Cary Clarke47ae292016-10-05 08:51:39 -04002286 if (segment->debugMissingCoincidence(log), false) {
caryclark55888e42016-07-18 10:01:36 -07002287// result = true;
caryclark55888e42016-07-18 10:01:36 -07002288 }
2289 segment = segment->next();
2290 } while (segment);
2291 return;
caryclark26ad22a2015-10-16 09:03:38 -07002292}
Cary Clarkab87d7a2016-10-04 10:01:04 -04002293
2294void SkOpContour::debugMoveMultiples(SkPathOpsDebug::GlitchLog* log) const {
2295 SkASSERT(fCount > 0);
2296 const SkOpSegment* segment = &fHead;
2297 do {
2298 if (segment->debugMoveMultiples(log), false) {
2299 return;
2300 }
2301 } while ((segment = segment->next()));
2302 return;
2303}
2304
2305void SkOpContour::debugMoveNearby(SkPathOpsDebug::GlitchLog* log) const {
2306 SkASSERT(fCount > 0);
2307 const SkOpSegment* segment = &fHead;
2308 do {
2309 segment->debugMoveNearby(log);
2310 } while ((segment = segment->next()));
2311}
caryclark26ad22a2015-10-16 09:03:38 -07002312#endif
2313
caryclark025b11e2016-08-25 05:21:14 -07002314#if DEBUG_COINCIDENCE_ORDER
2315void SkOpSegment::debugResetCoinT() const {
2316 fDebugBaseIndex = -1;
2317 fDebugBaseMin = 1;
2318 fDebugBaseMax = -1;
2319 fDebugLastIndex = -1;
2320 fDebugLastMin = 1;
2321 fDebugLastMax = -1;
2322}
2323#endif
2324
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002325void SkOpSegment::debugValidate() const {
caryclark025b11e2016-08-25 05:21:14 -07002326#if DEBUG_COINCIDENCE_ORDER
2327 {
2328 const SkOpSpanBase* span = &fHead;
2329 do {
2330 span->debugResetCoinT();
2331 } while (!span->final() && (span = span->upCast()->next()));
2332 span = &fHead;
2333 int index = 0;
2334 do {
2335 span->debugSetCoinT(index++);
2336 } while (!span->final() && (span = span->upCast()->next()));
2337 }
2338#endif
caryclark55888e42016-07-18 10:01:36 -07002339#if DEBUG_COINCIDENCE
2340 if (this->globalState()->debugCheckHealth()) {
2341 return;
2342 }
2343#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002344#if DEBUG_VALIDATE
caryclark54359292015-03-26 07:52:43 -07002345 const SkOpSpanBase* span = &fHead;
2346 double lastT = -1;
halcanary96fcdcc2015-08-27 07:41:13 -07002347 const SkOpSpanBase* prev = nullptr;
caryclark54359292015-03-26 07:52:43 -07002348 int count = 0;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002349 int done = 0;
caryclark54359292015-03-26 07:52:43 -07002350 do {
2351 if (!span->final()) {
2352 ++count;
2353 done += span->upCast()->done() ? 1 : 0;
2354 }
2355 SkASSERT(span->segment() == this);
2356 SkASSERT(!prev || prev->upCast()->next() == span);
2357 SkASSERT(!prev || prev == span->prev());
2358 prev = span;
2359 double t = span->ptT()->fT;
2360 SkASSERT(lastT < t);
2361 lastT = t;
2362 span->debugValidate();
2363 } while (!span->final() && (span = span->upCast()->next()));
2364 SkASSERT(count == fCount);
2365 SkASSERT(done == fDoneCount);
caryclark08bc8482015-04-24 09:08:57 -07002366 SkASSERT(count >= fDoneCount);
caryclark54359292015-03-26 07:52:43 -07002367 SkASSERT(span->final());
2368 span->debugValidate();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002369#endif
caryclark54359292015-03-26 07:52:43 -07002370}
2371
Cary Clarkab87d7a2016-10-04 10:01:04 -04002372#if DEBUG_COIN
caryclark30b9fdd2016-08-31 14:36:29 -07002373
2374// Commented-out lines keep this in sync with addOpp()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002375void SkOpSpanBase::debugAddOpp(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002376 const SkOpPtT* oppPrev = this->ptT()->oppPrev(opp->ptT());
2377 if (!oppPrev) {
caryclark55888e42016-07-18 10:01:36 -07002378 return;
caryclark26ad22a2015-10-16 09:03:38 -07002379 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002380 this->debugMergeMatches(log, opp);
caryclark30b9fdd2016-08-31 14:36:29 -07002381 this->ptT()->debugAddOpp(opp->ptT(), oppPrev);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002382 this->debugCheckForCollapsedCoincidence(log);
caryclark26ad22a2015-10-16 09:03:38 -07002383}
2384
caryclark55888e42016-07-18 10:01:36 -07002385// Commented-out lines keep this in sync with checkForCollapsedCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002386void SkOpSpanBase::debugCheckForCollapsedCoincidence(SkPathOpsDebug::GlitchLog* log) const {
caryclark55888e42016-07-18 10:01:36 -07002387 const SkOpCoincidence* coins = this->globalState()->coincidence();
2388 if (coins->isEmpty()) {
2389 return;
2390 }
2391// the insert above may have put both ends of a coincident run in the same span
2392// for each coincident ptT in loop; see if its opposite in is also in the loop
2393// this implementation is the motivation for marking that a ptT is referenced by a coincident span
2394 const SkOpPtT* head = this->ptT();
2395 const SkOpPtT* test = head;
caryclark26ad22a2015-10-16 09:03:38 -07002396 do {
caryclark55888e42016-07-18 10:01:36 -07002397 if (!test->coincident()) {
2398 continue;
caryclark26ad22a2015-10-16 09:03:38 -07002399 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002400 coins->debugMarkCollapsed(log, test);
caryclark55888e42016-07-18 10:01:36 -07002401 } while ((test = test->next()) != head);
caryclark26ad22a2015-10-16 09:03:38 -07002402}
caryclark55888e42016-07-18 10:01:36 -07002403#endif
caryclark26ad22a2015-10-16 09:03:38 -07002404
caryclark54359292015-03-26 07:52:43 -07002405bool SkOpSpanBase::debugCoinEndLoopCheck() const {
2406 int loop = 0;
2407 const SkOpSpanBase* next = this;
2408 SkOpSpanBase* nextCoin;
2409 do {
2410 nextCoin = next->fCoinEnd;
2411 SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin);
2412 for (int check = 1; check < loop - 1; ++check) {
2413 const SkOpSpanBase* checkCoin = this->fCoinEnd;
2414 const SkOpSpanBase* innerCoin = checkCoin;
2415 for (int inner = check + 1; inner < loop; ++inner) {
2416 innerCoin = innerCoin->fCoinEnd;
2417 if (checkCoin == innerCoin) {
2418 SkDebugf("*** bad coincident end loop ***\n");
2419 return false;
2420 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002421 }
2422 }
caryclark54359292015-03-26 07:52:43 -07002423 ++loop;
2424 } while ((next = nextCoin) && next != this);
2425 return true;
2426}
2427
Cary Clarkab87d7a2016-10-04 10:01:04 -04002428#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002429// Commented-out lines keep this in sync with insertCoinEnd()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002430void SkOpSpanBase::debugInsertCoinEnd(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* coin) const {
caryclark55888e42016-07-18 10:01:36 -07002431 if (containsCoinEnd(coin)) {
2432// SkASSERT(coin->containsCoinEnd(this));
2433 return;
2434 }
2435 debugValidate();
2436// SkASSERT(this != coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002437 log->record(SkPathOpsDebug::kMarkCoinEnd_Glitch, this, coin);
caryclark55888e42016-07-18 10:01:36 -07002438// coin->fCoinEnd = this->fCoinEnd;
2439// this->fCoinEnd = coinNext;
2440 debugValidate();
2441}
2442
caryclark30b9fdd2016-08-31 14:36:29 -07002443// Commented-out lines keep this in sync with mergeMatches()
2444// Look to see if pt-t linked list contains same segment more than once
2445// if so, and if each pt-t is directly pointed to by spans in that segment,
2446// merge them
2447// keep the points, but remove spans so that the segment doesn't have 2 or more
2448// spans pointing to the same pt-t loop at different loop elements
Cary Clarkab87d7a2016-10-04 10:01:04 -04002449void SkOpSpanBase::debugMergeMatches(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const {
caryclark30b9fdd2016-08-31 14:36:29 -07002450 const SkOpPtT* test = &fPtT;
2451 const SkOpPtT* testNext;
2452 const SkOpPtT* stop = test;
2453 do {
2454 testNext = test->next();
2455 if (test->deleted()) {
2456 continue;
2457 }
2458 const SkOpSpanBase* testBase = test->span();
2459 SkASSERT(testBase->ptT() == test);
2460 const SkOpSegment* segment = test->segment();
2461 if (segment->done()) {
2462 continue;
2463 }
2464 const SkOpPtT* inner = opp->ptT();
2465 const SkOpPtT* innerStop = inner;
2466 do {
2467 if (inner->segment() != segment) {
2468 continue;
2469 }
2470 if (inner->deleted()) {
2471 continue;
2472 }
2473 const SkOpSpanBase* innerBase = inner->span();
2474 SkASSERT(innerBase->ptT() == inner);
2475 // when the intersection is first detected, the span base is marked if there are
2476 // more than one point in the intersection.
2477// if (!innerBase->hasMultipleHint() && !testBase->hasMultipleHint()) {
2478 if (!zero_or_one(inner->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002479 log->record(SkPathOpsDebug::kMergeMatches_Glitch, innerBase, test);
caryclark30b9fdd2016-08-31 14:36:29 -07002480 } else {
2481 SkASSERT(inner->fT != test->fT);
2482 if (!zero_or_one(test->fT)) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002483 log->record(SkPathOpsDebug::kMergeMatches_Glitch, testBase, inner);
caryclark30b9fdd2016-08-31 14:36:29 -07002484 } else {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002485 log->record(SkPathOpsDebug::kMergeMatches_Glitch, segment);
caryclark30b9fdd2016-08-31 14:36:29 -07002486// SkDEBUGCODE(testBase->debugSetDeleted());
2487// test->setDeleted();
2488// SkDEBUGCODE(innerBase->debugSetDeleted());
2489// inner->setDeleted();
2490 }
2491 }
2492#ifdef SK_DEBUG // assert if another undeleted entry points to segment
2493 const SkOpPtT* debugInner = inner;
2494 while ((debugInner = debugInner->next()) != innerStop) {
2495 if (debugInner->segment() != segment) {
2496 continue;
2497 }
2498 if (debugInner->deleted()) {
2499 continue;
2500 }
2501 SkOPASSERT(0);
2502 }
2503#endif
2504 break;
2505// }
2506 break;
2507 } while ((inner = inner->next()) != innerStop);
2508 } while ((test = testNext) != stop);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002509 this->debugCheckForCollapsedCoincidence(log);
caryclark30b9fdd2016-08-31 14:36:29 -07002510}
2511
caryclark55888e42016-07-18 10:01:36 -07002512#endif
caryclark26ad22a2015-10-16 09:03:38 -07002513
caryclark025b11e2016-08-25 05:21:14 -07002514void SkOpSpanBase::debugResetCoinT() const {
2515#if DEBUG_COINCIDENCE_ORDER
2516 const SkOpPtT* ptT = &fPtT;
2517 do {
2518 ptT->debugResetCoinT();
2519 ptT = ptT->next();
2520 } while (ptT != &fPtT);
2521#endif
2522}
2523
2524void SkOpSpanBase::debugSetCoinT(int index) const {
2525#if DEBUG_COINCIDENCE_ORDER
2526 const SkOpPtT* ptT = &fPtT;
2527 do {
2528 if (!ptT->deleted()) {
2529 ptT->debugSetCoinT(index);
2530 }
2531 ptT = ptT->next();
2532 } while (ptT != &fPtT);
2533#endif
2534}
2535
caryclark26ad22a2015-10-16 09:03:38 -07002536const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const {
2537 const SkOpSpanBase* end = *endPtr;
2538 SkASSERT(this->segment() == end->segment());
2539 const SkOpSpanBase* result;
2540 if (t() < end->t()) {
2541 result = this;
2542 } else {
2543 result = end;
2544 *endPtr = this;
2545 }
2546 return result->upCast();
2547}
2548
caryclark54359292015-03-26 07:52:43 -07002549void SkOpSpanBase::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07002550#if DEBUG_COINCIDENCE
2551 if (this->globalState()->debugCheckHealth()) {
2552 return;
2553 }
2554#endif
caryclark54359292015-03-26 07:52:43 -07002555#if DEBUG_VALIDATE
2556 const SkOpPtT* ptT = &fPtT;
2557 SkASSERT(ptT->span() == this);
2558 do {
2559// SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt));
2560 ptT->debugValidate();
2561 ptT = ptT->next();
2562 } while (ptT != &fPtT);
2563 SkASSERT(this->debugCoinEndLoopCheck());
2564 if (!this->final()) {
2565 SkASSERT(this->upCast()->debugCoinLoopCheck());
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002566 }
caryclark54359292015-03-26 07:52:43 -07002567 if (fFromAngle) {
2568 fFromAngle->debugValidate();
2569 }
2570 if (!this->final() && this->upCast()->toAngle()) {
2571 this->upCast()->toAngle()->debugValidate();
2572 }
2573#endif
2574}
2575
2576bool SkOpSpan::debugCoinLoopCheck() const {
2577 int loop = 0;
2578 const SkOpSpan* next = this;
2579 SkOpSpan* nextCoin;
2580 do {
2581 nextCoin = next->fCoincident;
2582 SkASSERT(nextCoin == this || nextCoin->fCoincident != nextCoin);
2583 for (int check = 1; check < loop - 1; ++check) {
2584 const SkOpSpan* checkCoin = this->fCoincident;
2585 const SkOpSpan* innerCoin = checkCoin;
2586 for (int inner = check + 1; inner < loop; ++inner) {
2587 innerCoin = innerCoin->fCoincident;
2588 if (checkCoin == innerCoin) {
2589 SkDebugf("*** bad coincident loop ***\n");
2590 return false;
2591 }
2592 }
2593 }
2594 ++loop;
2595 } while ((next = nextCoin) && next != this);
2596 return true;
2597}
2598
Cary Clarkab87d7a2016-10-04 10:01:04 -04002599#if DEBUG_COIN
caryclark55888e42016-07-18 10:01:36 -07002600// Commented-out lines keep this in sync with insertCoincidence() in header
Cary Clarkab87d7a2016-10-04 10:01:04 -04002601void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* coin) const {
caryclark55888e42016-07-18 10:01:36 -07002602 if (containsCoincidence(coin)) {
2603// SkASSERT(coin->containsCoincidence(this));
2604 return;
2605 }
2606 debugValidate();
2607// SkASSERT(this != coin);
Cary Clarkab87d7a2016-10-04 10:01:04 -04002608 log->record(SkPathOpsDebug::kMarkCoinStart_Glitch, this, coin);
caryclark55888e42016-07-18 10:01:36 -07002609// coin->fCoincident = this->fCoincident;
2610// this->fCoincident = coinNext;
2611 debugValidate();
2612}
2613
2614// Commented-out lines keep this in sync with insertCoincidence()
Cary Clarkab87d7a2016-10-04 10:01:04 -04002615void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* segment, bool flipped, bool ordered) const {
caryclark55888e42016-07-18 10:01:36 -07002616 if (this->containsCoincidence(segment)) {
2617 return;
2618 }
2619 const SkOpPtT* next = &fPtT;
2620 while ((next = next->next()) != &fPtT) {
2621 if (next->segment() == segment) {
Cary Clarkab87d7a2016-10-04 10:01:04 -04002622 const SkOpSpan* span;
2623 const SkOpSpanBase* base = next->span();
2624 if (!ordered) {
2625 const SkOpSpanBase* spanEnd = fNext->contains(segment)->span();
2626 const SkOpPtT* start = base->ptT()->starter(spanEnd->ptT());
2627 FAIL_IF(!start->span()->upCastable(), this);
2628 span = const_cast<SkOpSpan*>(start->span()->upCast());
2629 }
2630 else if (flipped) {
2631 span = base->prev();
2632 FAIL_IF(!span, this);
2633 }
2634 else {
2635 FAIL_IF(!base->upCastable(), this);
2636 span = base->upCast();
2637 }
2638 log->record(SkPathOpsDebug::kMarkCoinInsert_Glitch, span);
caryclark55888e42016-07-18 10:01:36 -07002639 return;
2640 }
2641 }
Cary Clarkab87d7a2016-10-04 10:01:04 -04002642#if DEBUG_COIN
2643 log->record(SkPathOpsDebug::kMarkCoinMissing_Glitch, segment, this);
caryclark55888e42016-07-18 10:01:36 -07002644#endif
Cary Clarkab87d7a2016-10-04 10:01:04 -04002645 return;
caryclark55888e42016-07-18 10:01:36 -07002646}
2647#endif
2648
caryclark624637c2015-05-11 07:21:27 -07002649// called only by test code
2650int SkIntersections::debugCoincidentUsed() const {
2651 if (!fIsCoincident[0]) {
2652 SkASSERT(!fIsCoincident[1]);
2653 return 0;
2654 }
2655 int count = 0;
2656 SkDEBUGCODE(int count2 = 0;)
2657 for (int index = 0; index < fUsed; ++index) {
2658 if (fIsCoincident[0] & (1 << index)) {
2659 ++count;
2660 }
2661#ifdef SK_DEBUG
2662 if (fIsCoincident[1] & (1 << index)) {
2663 ++count2;
2664 }
2665#endif
2666 }
2667 SkASSERT(count == count2);
2668 return count;
2669}
2670
caryclark54359292015-03-26 07:52:43 -07002671#include "SkOpContour.h"
2672
caryclark55888e42016-07-18 10:01:36 -07002673// Commented-out lines keep this in sync with addOpp()
caryclark29b25632016-08-25 11:27:17 -07002674void SkOpPtT::debugAddOpp(const SkOpPtT* opp, const SkOpPtT* oppPrev) const {
2675 SkDEBUGCODE(const SkOpPtT* oldNext = this->fNext);
caryclark55888e42016-07-18 10:01:36 -07002676 SkASSERT(this != opp);
2677// this->fNext = opp;
caryclark29b25632016-08-25 11:27:17 -07002678 SkASSERT(oppPrev != oldNext);
caryclark55888e42016-07-18 10:01:36 -07002679// oppPrev->fNext = oldNext;
caryclark55888e42016-07-18 10:01:36 -07002680}
2681
caryclark26ad22a2015-10-16 09:03:38 -07002682bool SkOpPtT::debugContains(const SkOpPtT* check) const {
2683 SkASSERT(this != check);
2684 const SkOpPtT* ptT = this;
2685 int links = 0;
2686 do {
2687 ptT = ptT->next();
2688 if (ptT == check) {
2689 return true;
2690 }
2691 ++links;
2692 const SkOpPtT* test = this;
2693 for (int index = 0; index < links; ++index) {
2694 if (ptT == test) {
2695 return false;
2696 }
2697 test = test->next();
2698 }
2699 } while (true);
2700}
2701
2702const SkOpPtT* SkOpPtT::debugContains(const SkOpSegment* check) const {
2703 SkASSERT(this->segment() != check);
2704 const SkOpPtT* ptT = this;
2705 int links = 0;
2706 do {
2707 ptT = ptT->next();
2708 if (ptT->segment() == check) {
2709 return ptT;
2710 }
2711 ++links;
2712 const SkOpPtT* test = this;
2713 for (int index = 0; index < links; ++index) {
2714 if (ptT == test) {
2715 return nullptr;
2716 }
2717 test = test->next();
2718 }
2719 } while (true);
2720}
2721
caryclark8016b262016-09-06 05:59:47 -07002722const SkOpPtT* SkOpPtT::debugEnder(const SkOpPtT* end) const {
2723 return fT < end->fT ? end : this;
2724}
2725
caryclark54359292015-03-26 07:52:43 -07002726int SkOpPtT::debugLoopLimit(bool report) const {
2727 int loop = 0;
2728 const SkOpPtT* next = this;
2729 do {
2730 for (int check = 1; check < loop - 1; ++check) {
2731 const SkOpPtT* checkPtT = this->fNext;
2732 const SkOpPtT* innerPtT = checkPtT;
2733 for (int inner = check + 1; inner < loop; ++inner) {
2734 innerPtT = innerPtT->fNext;
2735 if (checkPtT == innerPtT) {
2736 if (report) {
2737 SkDebugf("*** bad ptT loop ***\n");
2738 }
2739 return loop;
2740 }
2741 }
2742 }
caryclark26ad22a2015-10-16 09:03:38 -07002743 // there's nothing wrong with extremely large loop counts -- but this may appear to hang
2744 // by taking a very long time to figure out that no loop entry is a duplicate
2745 // -- and it's likely that a large loop count is indicative of a bug somewhere
2746 if (++loop > 1000) {
2747 SkDebugf("*** loop count exceeds 1000 ***\n");
2748 return 1000;
2749 }
caryclark54359292015-03-26 07:52:43 -07002750 } while ((next = next->fNext) && next != this);
2751 return 0;
2752}
2753
caryclark29b25632016-08-25 11:27:17 -07002754const SkOpPtT* SkOpPtT::debugOppPrev(const SkOpPtT* opp) const {
2755 return this->oppPrev(const_cast<SkOpPtT*>(opp));
2756}
2757
caryclark025b11e2016-08-25 05:21:14 -07002758void SkOpPtT::debugResetCoinT() const {
2759#if DEBUG_COINCIDENCE_ORDER
2760 this->segment()->debugResetCoinT();
2761#endif
2762}
2763
2764void SkOpPtT::debugSetCoinT(int index) const {
2765#if DEBUG_COINCIDENCE_ORDER
2766 this->segment()->debugSetCoinT(index, fT);
2767#endif
2768}
2769
caryclark54359292015-03-26 07:52:43 -07002770void SkOpPtT::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07002771#if DEBUG_COINCIDENCE
2772 if (this->globalState()->debugCheckHealth()) {
2773 return;
2774 }
2775#endif
caryclark54359292015-03-26 07:52:43 -07002776#if DEBUG_VALIDATE
Cary Clarkab87d7a2016-10-04 10:01:04 -04002777 SkOpPhase phase = contour()->globalState()->phase();
2778 if (phase == SkOpPhase::kIntersecting || phase == SkOpPhase::kFixWinding) {
caryclark54359292015-03-26 07:52:43 -07002779 return;
2780 }
2781 SkASSERT(fNext);
2782 SkASSERT(fNext != this);
2783 SkASSERT(fNext->fNext);
2784 SkASSERT(debugLoopLimit(false) == 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002785#endif
2786}
caryclark1049f122015-04-20 08:31:59 -07002787
2788static void output_scalar(SkScalar num) {
2789 if (num == (int) num) {
2790 SkDebugf("%d", (int) num);
2791 } else {
2792 SkString str;
2793 str.printf("%1.9g", num);
2794 int width = (int) str.size();
2795 const char* cStr = str.c_str();
2796 while (cStr[width - 1] == '0') {
2797 --width;
2798 }
2799 str.resize(width);
2800 SkDebugf("%sf", str.c_str());
2801 }
2802}
2803
2804static void output_points(const SkPoint* pts, int count) {
2805 for (int index = 0; index < count; ++index) {
2806 output_scalar(pts[index].fX);
2807 SkDebugf(", ");
2808 output_scalar(pts[index].fY);
2809 if (index + 1 < count) {
2810 SkDebugf(", ");
2811 }
2812 }
2813}
2814
2815static void showPathContours(SkPath::RawIter& iter, const char* pathName) {
2816 uint8_t verb;
2817 SkPoint pts[4];
2818 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
2819 switch (verb) {
2820 case SkPath::kMove_Verb:
2821 SkDebugf(" %s.moveTo(", pathName);
2822 output_points(&pts[0], 1);
2823 SkDebugf(");\n");
2824 continue;
2825 case SkPath::kLine_Verb:
2826 SkDebugf(" %s.lineTo(", pathName);
2827 output_points(&pts[1], 1);
2828 SkDebugf(");\n");
2829 break;
2830 case SkPath::kQuad_Verb:
2831 SkDebugf(" %s.quadTo(", pathName);
2832 output_points(&pts[1], 2);
2833 SkDebugf(");\n");
2834 break;
2835 case SkPath::kConic_Verb:
2836 SkDebugf(" %s.conicTo(", pathName);
2837 output_points(&pts[1], 2);
2838 SkDebugf(", %1.9gf);\n", iter.conicWeight());
2839 break;
2840 case SkPath::kCubic_Verb:
2841 SkDebugf(" %s.cubicTo(", pathName);
2842 output_points(&pts[1], 3);
2843 SkDebugf(");\n");
2844 break;
2845 case SkPath::kClose_Verb:
2846 SkDebugf(" %s.close();\n", pathName);
2847 break;
2848 default:
2849 SkDEBUGFAIL("bad verb");
2850 return;
2851 }
2852 }
2853}
2854
2855static const char* gFillTypeStr[] = {
2856 "kWinding_FillType",
2857 "kEvenOdd_FillType",
2858 "kInverseWinding_FillType",
2859 "kInverseEvenOdd_FillType"
2860};
2861
2862void SkPathOpsDebug::ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration) {
2863 SkPath::RawIter iter(path);
2864#define SUPPORT_RECT_CONTOUR_DETECTION 0
2865#if SUPPORT_RECT_CONTOUR_DETECTION
halcanary96fcdcc2015-08-27 07:41:13 -07002866 int rectCount = path.isRectContours() ? path.rectContours(nullptr, nullptr) : 0;
caryclark1049f122015-04-20 08:31:59 -07002867 if (rectCount > 0) {
2868 SkTDArray<SkRect> rects;
2869 SkTDArray<SkPath::Direction> directions;
2870 rects.setCount(rectCount);
2871 directions.setCount(rectCount);
2872 path.rectContours(rects.begin(), directions.begin());
2873 for (int contour = 0; contour < rectCount; ++contour) {
2874 const SkRect& rect = rects[contour];
2875 SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
2876 rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
2877 ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
2878 }
2879 return;
2880 }
2881#endif
2882 SkPath::FillType fillType = path.getFillType();
2883 SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType);
2884 if (includeDeclaration) {
2885 SkDebugf(" SkPath %s;\n", name);
2886 }
2887 SkDebugf(" %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[fillType]);
2888 iter.setPath(path);
2889 showPathContours(iter, name);
2890}