blob: 93743f3d6cfc3f8a18ab75ac0f5332e10d9bc206 [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
caryclark55888e42016-07-18 10:01:36 -070015class SkCoincidentSpans;
caryclark26ad22a2015-10-16 09:03:38 -070016
caryclark54359292015-03-26 07:52:43 -070017#if DEBUG_VALIDATE
18extern bool FLAGS_runFail;
19#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +000020
caryclark624637c2015-05-11 07:21:27 -070021#if DEBUG_SORT
22int SkPathOpsDebug::gSortCountDefault = SK_MaxS32;
23int SkPathOpsDebug::gSortCount;
24#endif
25
26#if DEBUG_ACTIVE_OP
27const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor"};
28#endif
29
caryclark@google.com07393ca2013-04-08 11:47:37 +000030#if defined SK_DEBUG || !FORCE_RELEASE
31
caryclark@google.com570863f2013-09-16 15:55:01 +000032const char* SkPathOpsDebug::kLVerbStr[] = {"", "line", "quad", "cubic"};
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +000033
commit-bot@chromium.orgfe41b8f2014-04-25 14:03:52 +000034int SkPathOpsDebug::gContourID = 0;
35int SkPathOpsDebug::gSegmentID = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +000036
caryclark54359292015-03-26 07:52:43 -070037bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray,
38 const SkOpSpanBase* span) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000039 for (int index = 0; index < chaseArray.count(); ++index) {
caryclark54359292015-03-26 07:52:43 -070040 const SkOpSpanBase* entry = chaseArray[index];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000041 if (entry == span) {
42 return true;
43 }
44 }
45 return false;
46}
caryclark26ad22a2015-10-16 09:03:38 -070047#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000048
caryclark55888e42016-07-18 10:01:36 -070049#if DEBUG_COINCIDENCE_VERBOSE
caryclark26ad22a2015-10-16 09:03:38 -070050enum GlitchType {
51 kAddCorruptCoin_Glitch,
52 kAddExpandedCoin_Glitch,
caryclark55888e42016-07-18 10:01:36 -070053 kAddExpandedFail_Glitch,
54 kAddIfMissingCoin_Glitch,
caryclark26ad22a2015-10-16 09:03:38 -070055 kAddMissingCoin_Glitch,
caryclark55888e42016-07-18 10:01:36 -070056 kAddMissingExtend_Glitch,
57 kAddOrOverlap_Glitch,
caryclark26ad22a2015-10-16 09:03:38 -070058 kCollapsedCoin_Glitch,
59 kCollapsedDone_Glitch,
60 kCollapsedOppValue_Glitch,
61 kCollapsedSpan_Glitch,
62 kCollapsedWindValue_Glitch,
63 kDeletedCoin_Glitch,
64 kExpandCoin_Glitch,
65 kMarkCoinEnd_Glitch,
66 kMarkCoinInsert_Glitch,
caryclark55888e42016-07-18 10:01:36 -070067 kMarkCoinMissing_Glitch,
68 kMarkCoinStart_Glitch,
69 kMergeContained_Glitch,
caryclark26ad22a2015-10-16 09:03:38 -070070 kMissingCoin_Glitch,
71 kMissingDone_Glitch,
72 kMissingIntersection_Glitch,
73 kMoveMultiple_Glitch,
caryclark55888e42016-07-18 10:01:36 -070074 kMoveNearbyClearAll_Glitch,
75 kMoveNearbyClearAll2_Glitch,
76 kMoveNearbyMerge_Glitch,
77 kMoveNearbyMergeFinal_Glitch,
78 kMoveNearbyRelease_Glitch,
79 kMoveNearbyReleaseFinal_Glitch,
80 kReleasedSpan_Glitch,
caryclark26ad22a2015-10-16 09:03:38 -070081 kUnaligned_Glitch,
82 kUnalignedHead_Glitch,
83 kUnalignedTail_Glitch,
caryclark26ad22a2015-10-16 09:03:38 -070084};
85
caryclark55888e42016-07-18 10:01:36 -070086static const int kGlitchType_Count = kUnalignedTail_Glitch + 1;
caryclark26ad22a2015-10-16 09:03:38 -070087
88struct SpanGlitch {
89 const char* fStage;
90 const SkOpSpanBase* fBase;
91 const SkOpSpanBase* fSuspect;
caryclark26ad22a2015-10-16 09:03:38 -070092 const SkOpSegment* fSegment;
caryclark55888e42016-07-18 10:01:36 -070093 const SkOpSegment* fOppSegment;
caryclark26ad22a2015-10-16 09:03:38 -070094 const SkOpPtT* fCoinSpan;
95 const SkOpPtT* fEndSpan;
96 const SkOpPtT* fOppSpan;
97 const SkOpPtT* fOppEndSpan;
caryclark55888e42016-07-18 10:01:36 -070098 double fStartT;
99 double fEndT;
100 double fOppStartT;
101 double fOppEndT;
caryclark26ad22a2015-10-16 09:03:38 -0700102 SkPoint fPt;
103 GlitchType fType;
104};
105
106struct SkPathOpsDebug::GlitchLog {
107 SpanGlitch* recordCommon(GlitchType type, const char* stage) {
108 SpanGlitch* glitch = fGlitches.push();
109 glitch->fStage = stage;
110 glitch->fBase = nullptr;
111 glitch->fSuspect = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -0700112 glitch->fSegment = nullptr;
caryclark55888e42016-07-18 10:01:36 -0700113 glitch->fOppSegment = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -0700114 glitch->fCoinSpan = nullptr;
115 glitch->fEndSpan = nullptr;
116 glitch->fOppSpan = nullptr;
117 glitch->fOppEndSpan = nullptr;
caryclark55888e42016-07-18 10:01:36 -0700118 glitch->fStartT = SK_ScalarNaN;
119 glitch->fEndT = SK_ScalarNaN;
120 glitch->fOppStartT = SK_ScalarNaN;
121 glitch->fOppEndT = SK_ScalarNaN;
caryclark26ad22a2015-10-16 09:03:38 -0700122 glitch->fPt = { SK_ScalarNaN, SK_ScalarNaN };
123 glitch->fType = type;
124 return glitch;
125 }
126
127 void record(GlitchType type, const char* stage, const SkOpSpanBase* base,
128 const SkOpSpanBase* suspect = NULL) {
129 SpanGlitch* glitch = recordCommon(type, stage);
130 glitch->fBase = base;
131 glitch->fSuspect = suspect;
132 }
133
caryclark55888e42016-07-18 10:01:36 -0700134 void record(GlitchType type, const char* stage, const SkOpSpanBase* base,
135 const SkOpPtT* ptT) {
caryclark26ad22a2015-10-16 09:03:38 -0700136 SpanGlitch* glitch = recordCommon(type, stage);
caryclark55888e42016-07-18 10:01:36 -0700137 glitch->fBase = base;
138 glitch->fCoinSpan = ptT;
139 }
140
141 void record(GlitchType type, const char* stage, const SkCoincidentSpans* coin,
142 const SkCoincidentSpans* opp = NULL) {
143 SpanGlitch* glitch = recordCommon(type, stage);
144 glitch->fCoinSpan = coin->coinPtTStart();
145 glitch->fEndSpan = coin->coinPtTEnd();
146 if (opp) {
147 glitch->fOppSpan = opp->coinPtTStart();
148 glitch->fOppEndSpan = opp->coinPtTEnd();
149 }
caryclark26ad22a2015-10-16 09:03:38 -0700150 }
151
152 void record(GlitchType type, const char* stage, const SkOpSpanBase* base,
153 const SkOpSegment* seg, double t, SkPoint pt) {
154 SpanGlitch* glitch = recordCommon(type, stage);
155 glitch->fBase = base;
156 glitch->fSegment = seg;
caryclark55888e42016-07-18 10:01:36 -0700157 glitch->fStartT = t;
caryclark26ad22a2015-10-16 09:03:38 -0700158 glitch->fPt = pt;
159 }
160
161 void record(GlitchType type, const char* stage, const SkOpSpanBase* base, double t,
162 SkPoint pt) {
163 SpanGlitch* glitch = recordCommon(type, stage);
164 glitch->fBase = base;
caryclark55888e42016-07-18 10:01:36 -0700165 glitch->fStartT = t;
caryclark26ad22a2015-10-16 09:03:38 -0700166 glitch->fPt = pt;
167 }
168
169 void record(GlitchType type, const char* stage, const SkCoincidentSpans* coin,
170 const SkOpPtT* coinSpan, const SkOpPtT* endSpan) {
171 SpanGlitch* glitch = recordCommon(type, stage);
caryclark55888e42016-07-18 10:01:36 -0700172 glitch->fCoinSpan = coin->coinPtTStart();
173 glitch->fEndSpan = coin->coinPtTEnd();
caryclark26ad22a2015-10-16 09:03:38 -0700174 glitch->fEndSpan = endSpan;
caryclark55888e42016-07-18 10:01:36 -0700175 glitch->fOppSpan = coinSpan;
176 glitch->fOppEndSpan = endSpan;
caryclark26ad22a2015-10-16 09:03:38 -0700177 }
178
179 void record(GlitchType type, const char* stage, const SkCoincidentSpans* coin,
caryclark55888e42016-07-18 10:01:36 -0700180 const SkOpSpanBase* base) {
caryclark26ad22a2015-10-16 09:03:38 -0700181 SpanGlitch* glitch = recordCommon(type, stage);
caryclark55888e42016-07-18 10:01:36 -0700182 glitch->fBase = base;
183 glitch->fCoinSpan = coin->coinPtTStart();
184 glitch->fEndSpan = coin->coinPtTEnd();
caryclark26ad22a2015-10-16 09:03:38 -0700185 }
186
187 void record(GlitchType type, const char* stage, const SkOpPtT* ptTS, const SkOpPtT* ptTE,
188 const SkOpPtT* oPtTS, const SkOpPtT* oPtTE) {
189 SpanGlitch* glitch = recordCommon(type, stage);
190 glitch->fCoinSpan = ptTS;
191 glitch->fEndSpan = ptTE;
192 glitch->fOppSpan = oPtTS;
193 glitch->fOppEndSpan = oPtTE;
194 }
195
caryclark55888e42016-07-18 10:01:36 -0700196 void record(GlitchType type, const char* stage, const SkOpSegment* seg, double startT,
197 double endT, const SkOpSegment* oppSeg, double oppStartT, double oppEndT) {
198 SpanGlitch* glitch = recordCommon(type, stage);
199 glitch->fSegment = seg;
200 glitch->fStartT = startT;
201 glitch->fEndT = endT;
202 glitch->fOppSegment = oppSeg;
203 glitch->fOppStartT = oppStartT;
204 glitch->fOppEndT = oppEndT;
205 }
206
207 void record(GlitchType type, const char* stage, const SkOpSegment* seg,
208 const SkOpSpan* span) {
209 SpanGlitch* glitch = recordCommon(type, stage);
210 glitch->fSegment = seg;
211 glitch->fBase = span;
212 }
213
214 void record(GlitchType type, const char* stage, double t, const SkOpSpanBase* span) {
215 SpanGlitch* glitch = recordCommon(type, stage);
216 glitch->fStartT = t;
217 glitch->fBase = span;
218 }
219
220 void record(GlitchType type, const char* stage, const SkOpSegment* seg) {
221 SpanGlitch* glitch = recordCommon(type, stage);
222 glitch->fSegment = seg;
223 }
224
225 void record(GlitchType type, const char* stage, const SkCoincidentSpans* coin,
226 const SkOpPtT* ptT) {
227 SpanGlitch* glitch = recordCommon(type, stage);
228 glitch->fCoinSpan = coin->coinPtTStart();
229 glitch->fEndSpan = ptT;
230 }
231
caryclark26ad22a2015-10-16 09:03:38 -0700232 SkTDArray<SpanGlitch> fGlitches;
233};
caryclark55888e42016-07-18 10:01:36 -0700234#endif
caryclark26ad22a2015-10-16 09:03:38 -0700235
caryclark55888e42016-07-18 10:01:36 -0700236void SkPathOpsDebug::ShowActiveSpans(SkOpContourHead* contourList) {
237#if DEBUG_ACTIVE_SPANS
238 SkOpContour* contour = contourList;
239 do {
240 contour->debugShowActiveSpans();
241 } while ((contour = contour->next()));
242#endif
243}
244
245#if DEBUG_COINCIDENCE
caryclark26ad22a2015-10-16 09:03:38 -0700246void SkPathOpsDebug::CheckHealth(SkOpContourHead* contourList, const char* id) {
caryclark55888e42016-07-18 10:01:36 -0700247 contourList->globalState()->debugSetCheckHealth(true);
248#if DEBUG_COINCIDENCE_VERBOSE
caryclark26ad22a2015-10-16 09:03:38 -0700249 GlitchLog glitches;
250 const SkOpContour* contour = contourList;
251 const SkOpCoincidence* coincidence = contour->globalState()->coincidence();
caryclark55888e42016-07-18 10:01:36 -0700252 coincidence->debugCheckValid(id, &glitches); // don't call validate; spans may be inconsistent
caryclark26ad22a2015-10-16 09:03:38 -0700253 do {
254 contour->debugCheckHealth(id, &glitches);
caryclark55888e42016-07-18 10:01:36 -0700255 contour->debugMissingCoincidence(id, &glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700256 } while ((contour = contour->next()));
caryclark55888e42016-07-18 10:01:36 -0700257 coincidence->debugRemoveCollapsed(id, &glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700258 coincidence->debugAddMissing(id, &glitches);
259 coincidence->debugExpand(id, &glitches);
260 coincidence->debugAddExpanded(id, &glitches);
261 coincidence->debugMark(id, &glitches);
caryclark55888e42016-07-18 10:01:36 -0700262 coincidence->debugReorder(id, &glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700263 unsigned mask = 0;
264 for (int index = 0; index < glitches.fGlitches.count(); ++index) {
265 const SpanGlitch& glitch = glitches.fGlitches[index];
266 mask |= 1 << glitch.fType;
267 }
268 for (int index = 0; index < kGlitchType_Count; ++index) {
269 SkDebugf(mask & (1 << index) ? "x" : "-");
270 }
271 SkDebugf(" %s\n", id);
caryclark55888e42016-07-18 10:01:36 -0700272 for (int index = 0; index < glitches.fGlitches.count(); ++index) {
273 const SpanGlitch& glitch = glitches.fGlitches[index];
274 SkDebugf("%02d: ", index);
275 if (glitch.fBase) {
276 SkDebugf(" base=%d", glitch.fBase->debugID());
277 }
278 if (glitch.fSuspect) {
279 SkDebugf(" base=%d", glitch.fSuspect->debugID());
280 }
281 if (glitch.fSegment) {
282 SkDebugf(" segment=%d", glitch.fSegment->debugID());
283 }
284 if (glitch.fCoinSpan) {
285 SkDebugf(" coinSpan=%d", glitch.fCoinSpan->debugID());
286 }
287 if (glitch.fEndSpan) {
288 SkDebugf(" endSpan=%d", glitch.fEndSpan->debugID());
289 }
290 if (glitch.fOppSpan) {
291 SkDebugf(" oppSpan=%d", glitch.fOppSpan->debugID());
292 }
293 if (glitch.fOppEndSpan) {
294 SkDebugf(" oppEndSpan=%d", glitch.fOppEndSpan->debugID());
295 }
296 if (!SkScalarIsNaN(glitch.fStartT)) {
297 SkDebugf(" startT=%g", glitch.fStartT);
298 }
299 if (!SkScalarIsNaN(glitch.fEndT)) {
300 SkDebugf(" endT=%g", glitch.fEndT);
301 }
302 if (glitch.fOppSegment) {
303 SkDebugf(" segment=%d", glitch.fOppSegment->debugID());
304 }
305 if (!SkScalarIsNaN(glitch.fOppStartT)) {
306 SkDebugf(" oppStartT=%g", glitch.fOppStartT);
307 }
308 if (!SkScalarIsNaN(glitch.fOppEndT)) {
309 SkDebugf(" oppEndT=%g", glitch.fOppEndT);
310 }
311 if (!SkScalarIsNaN(glitch.fPt.fX) || !SkScalarIsNaN(glitch.fPt.fY)) {
312 SkDebugf(" pt=%g,%g", glitch.fPt.fX, glitch.fPt.fY);
313 }
314 switch (glitch.fType) {
315 case kAddCorruptCoin_Glitch: SkDebugf(" AddCorruptCoin"); break;
316 case kAddExpandedCoin_Glitch: SkDebugf(" AddExpandedCoin"); break;
317 case kAddExpandedFail_Glitch: SkDebugf(" AddExpandedFail"); break;
318 case kAddIfMissingCoin_Glitch: SkDebugf(" AddIfMissingCoin"); break;
319 case kAddMissingCoin_Glitch: SkDebugf(" AddMissingCoin"); break;
320 case kAddMissingExtend_Glitch: SkDebugf(" AddMissingExtend"); break;
321 case kAddOrOverlap_Glitch: SkDebugf(" AAddOrOverlap"); break;
322 case kCollapsedCoin_Glitch: SkDebugf(" CollapsedCoin"); break;
323 case kCollapsedDone_Glitch: SkDebugf(" CollapsedDone"); break;
324 case kCollapsedOppValue_Glitch: SkDebugf(" CollapsedOppValue"); break;
325 case kCollapsedSpan_Glitch: SkDebugf(" CollapsedSpan"); break;
326 case kCollapsedWindValue_Glitch: SkDebugf(" CollapsedWindValue"); break;
327 case kDeletedCoin_Glitch: SkDebugf(" DeletedCoin"); break;
328 case kExpandCoin_Glitch: SkDebugf(" ExpandCoin"); break;
329 case kMarkCoinEnd_Glitch: SkDebugf(" MarkCoinEnd"); break;
330 case kMarkCoinInsert_Glitch: SkDebugf(" MarkCoinInsert"); break;
331 case kMarkCoinMissing_Glitch: SkDebugf(" MarkCoinMissing"); break;
332 case kMarkCoinStart_Glitch: SkDebugf(" MarkCoinStart"); break;
333 case kMergeContained_Glitch: SkDebugf(" MergeContained"); break;
334 case kMissingCoin_Glitch: SkDebugf(" MissingCoin"); break;
335 case kMissingDone_Glitch: SkDebugf(" MissingDone"); break;
336 case kMissingIntersection_Glitch: SkDebugf(" MissingIntersection"); break;
337 case kMoveMultiple_Glitch: SkDebugf(" MoveMultiple"); break;
338 case kMoveNearbyClearAll_Glitch: SkDebugf(" MoveNearbyClearAll"); break;
339 case kMoveNearbyClearAll2_Glitch: SkDebugf(" MoveNearbyClearAll2"); break;
340 case kMoveNearbyMerge_Glitch: SkDebugf(" MoveNearbyMerge"); break;
341 case kMoveNearbyMergeFinal_Glitch: SkDebugf(" MoveNearbyMergeFinal"); break;
342 case kMoveNearbyRelease_Glitch: SkDebugf(" MoveNearbyRelease"); break;
343 case kMoveNearbyReleaseFinal_Glitch: SkDebugf(" MoveNearbyReleaseFinal"); break;
344 case kReleasedSpan_Glitch: SkDebugf(" ReleasedSpan"); break;
345 case kUnaligned_Glitch: SkDebugf(" Unaligned"); break;
346 case kUnalignedHead_Glitch: SkDebugf(" UnalignedHead"); break;
347 case kUnalignedTail_Glitch: SkDebugf(" UnalignedTail"); break;
348 default: SkASSERT(0);
349 }
350 SkDebugf("\n");
351 }
352 contourList->globalState()->debugSetCheckHealth(false);
353#if DEBUG_ACTIVE_SPANS
354 SkDebugf("active after %s:\n", id);
355 ShowActiveSpans(contourList);
356#endif
357#endif
caryclark26ad22a2015-10-16 09:03:38 -0700358}
359#endif
360
361#if defined SK_DEBUG || !FORCE_RELEASE
caryclark@google.com570863f2013-09-16 15:55:01 +0000362void SkPathOpsDebug::MathematicaIze(char* str, size_t bufferLen) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000363 size_t len = strlen(str);
364 bool num = false;
365 for (size_t idx = 0; idx < len; ++idx) {
366 if (num && str[idx] == 'e') {
367 if (len + 2 >= bufferLen) {
368 return;
369 }
370 memmove(&str[idx + 2], &str[idx + 1], len - idx);
371 str[idx] = '*';
372 str[idx + 1] = '^';
373 ++len;
374 }
375 num = str[idx] >= '0' && str[idx] <= '9';
376 }
377}
378
caryclark@google.com570863f2013-09-16 15:55:01 +0000379bool SkPathOpsDebug::ValidWind(int wind) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000380 return wind > SK_MinS32 + 0xFFFF && wind < SK_MaxS32 - 0xFFFF;
381}
382
caryclark@google.com570863f2013-09-16 15:55:01 +0000383void SkPathOpsDebug::WindingPrintf(int wind) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000384 if (wind == SK_MinS32) {
385 SkDebugf("?");
386 } else {
387 SkDebugf("%d", wind);
388 }
389}
caryclark54359292015-03-26 07:52:43 -0700390#endif // defined SK_DEBUG || !FORCE_RELEASE
391
caryclark@google.coma5e55922013-05-07 18:51:31 +0000392
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000393#if DEBUG_SHOW_TEST_NAME
halcanary385fe4d2015-08-26 13:07:48 -0700394void* SkPathOpsDebug::CreateNameStr() { return new char[DEBUG_FILENAME_STRING_LENGTH]; }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000395
halcanary385fe4d2015-08-26 13:07:48 -0700396void SkPathOpsDebug::DeleteNameStr(void* v) { delete[] reinterpret_cast<char*>(v); }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000397
caryclark@google.com570863f2013-09-16 15:55:01 +0000398void SkPathOpsDebug::BumpTestName(char* test) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000399 char* num = test + strlen(test);
400 while (num[-1] >= '0' && num[-1] <= '9') {
401 --num;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000402 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000403 if (num[0] == '\0') {
404 return;
405 }
406 int dec = atoi(num);
407 if (dec == 0) {
408 return;
409 }
410 ++dec;
411 SK_SNPRINTF(num, DEBUG_FILENAME_STRING_LENGTH - (num - test), "%d", dec);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000412}
413#endif
caryclark@google.com570863f2013-09-16 15:55:01 +0000414
caryclark1049f122015-04-20 08:31:59 -0700415static void show_function_header(const char* functionName) {
416 SkDebugf("\nstatic void %s(skiatest::Reporter* reporter, const char* filename) {\n", functionName);
417 if (strcmp("skphealth_com76", functionName) == 0) {
418 SkDebugf("found it\n");
419 }
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000420}
caryclark1049f122015-04-20 08:31:59 -0700421
422static const char* gOpStrs[] = {
423 "kDifference_SkPathOp",
424 "kIntersect_SkPathOp",
425 "kUnion_SkPathOp",
caryclark55888e42016-07-18 10:01:36 -0700426 "kXOR_PathOp",
caryclark1049f122015-04-20 08:31:59 -0700427 "kReverseDifference_SkPathOp",
428};
429
caryclark03b03ca2015-04-23 09:13:37 -0700430const char* SkPathOpsDebug::OpStr(SkPathOp op) {
431 return gOpStrs[op];
432}
433
caryclark1049f122015-04-20 08:31:59 -0700434static void show_op(SkPathOp op, const char* pathOne, const char* pathTwo) {
435 SkDebugf(" testPathOp(reporter, %s, %s, %s, filename);\n", pathOne, pathTwo, gOpStrs[op]);
436 SkDebugf("}\n");
437}
438
reed086eea92016-05-04 17:12:46 -0700439SK_DECLARE_STATIC_MUTEX(gTestMutex);
caryclark1049f122015-04-20 08:31:59 -0700440
441void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
442 const char* testName) {
443 SkAutoMutexAcquire ac(gTestMutex);
444 show_function_header(testName);
445 ShowOnePath(a, "path", true);
446 ShowOnePath(b, "pathB", true);
447 show_op(shapeOp, "path", "pathB");
448}
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000449
caryclark27c8eb82015-07-06 11:38:33 -0700450#include "SkPathOpsTypes.h"
caryclark26ad22a2015-10-16 09:03:38 -0700451#include "SkIntersectionHelper.h"
452#include "SkIntersections.h"
453
454#if DEBUG_T_SECT_LOOP_COUNT
455void SkOpGlobalState::debugAddLoopCount(SkIntersections* i, const SkIntersectionHelper& wt,
456 const SkIntersectionHelper& wn) {
457 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
458 SkIntersections::DebugLoop looper = (SkIntersections::DebugLoop) index;
459 if (fDebugLoopCount[index] >= i->debugLoopCount(looper)) {
460 continue;
461 }
462 fDebugLoopCount[index] = i->debugLoopCount(looper);
463 fDebugWorstVerb[index * 2] = wt.segment()->verb();
464 fDebugWorstVerb[index * 2 + 1] = wn.segment()->verb();
465 sk_bzero(&fDebugWorstPts[index * 8], sizeof(SkPoint) * 8);
466 memcpy(&fDebugWorstPts[index * 2 * 4], wt.pts(),
467 (SkPathOpsVerbToPoints(wt.segment()->verb()) + 1) * sizeof(SkPoint));
468 memcpy(&fDebugWorstPts[(index * 2 + 1) * 4], wn.pts(),
469 (SkPathOpsVerbToPoints(wn.segment()->verb()) + 1) * sizeof(SkPoint));
470 fDebugWorstWeight[index * 2] = wt.weight();
471 fDebugWorstWeight[index * 2 + 1] = wn.weight();
472 }
473 i->debugResetLoopCount();
474}
475
476void SkOpGlobalState::debugDoYourWorst(SkOpGlobalState* local) {
477 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
478 if (fDebugLoopCount[index] >= local->fDebugLoopCount[index]) {
479 continue;
480 }
481 fDebugLoopCount[index] = local->fDebugLoopCount[index];
482 fDebugWorstVerb[index * 2] = local->fDebugWorstVerb[index * 2];
483 fDebugWorstVerb[index * 2 + 1] = local->fDebugWorstVerb[index * 2 + 1];
484 memcpy(&fDebugWorstPts[index * 2 * 4], &local->fDebugWorstPts[index * 2 * 4],
485 sizeof(SkPoint) * 8);
486 fDebugWorstWeight[index * 2] = local->fDebugWorstWeight[index * 2];
487 fDebugWorstWeight[index * 2 + 1] = local->fDebugWorstWeight[index * 2 + 1];
488 }
489 local->debugResetLoopCounts();
490}
491
492static void dump_curve(SkPath::Verb verb, const SkPoint& pts, float weight) {
493 if (!verb) {
494 return;
495 }
496 const char* verbs[] = { "", "line", "quad", "conic", "cubic" };
497 SkDebugf("%s: {{", verbs[verb]);
498 int ptCount = SkPathOpsVerbToPoints(verb);
499 for (int index = 0; index <= ptCount; ++index) {
500 SkDPoint::Dump((&pts)[index]);
501 if (index < ptCount - 1) {
502 SkDebugf(", ");
503 }
504 }
505 SkDebugf("}");
506 if (weight != 1) {
507 SkDebugf(", ");
508 if (weight == floorf(weight)) {
509 SkDebugf("%.0f", weight);
510 } else {
511 SkDebugf("%1.9gf", weight);
512 }
513 }
514 SkDebugf("}\n");
515}
516
517void SkOpGlobalState::debugLoopReport() {
518 const char* loops[] = { "iterations", "coinChecks", "perpCalcs" };
519 SkDebugf("\n");
520 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
521 SkDebugf("%s: %d\n", loops[index], fDebugLoopCount[index]);
522 dump_curve(fDebugWorstVerb[index * 2], fDebugWorstPts[index * 2 * 4],
523 fDebugWorstWeight[index * 2]);
524 dump_curve(fDebugWorstVerb[index * 2 + 1], fDebugWorstPts[(index * 2 + 1) * 4],
525 fDebugWorstWeight[index * 2 + 1]);
526 }
527}
528
529void SkOpGlobalState::debugResetLoopCounts() {
530 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
531 sk_bzero(fDebugWorstVerb, sizeof(fDebugWorstVerb));
532 sk_bzero(fDebugWorstPts, sizeof(fDebugWorstPts));
533 sk_bzero(fDebugWorstWeight, sizeof(fDebugWorstWeight));
534}
535#endif
caryclark27c8eb82015-07-06 11:38:33 -0700536
537#ifdef SK_DEBUG
538bool SkOpGlobalState::debugRunFail() const {
539#if DEBUG_VALIDATE
540 return FLAGS_runFail;
541#else
542 return false;
543#endif
544}
545#endif
546
caryclark26ad22a2015-10-16 09:03:38 -0700547#if DEBUG_T_SECT_LOOP_COUNT
548void SkIntersections::debugBumpLoopCount(DebugLoop index) {
549 fDebugLoopCount[index]++;
550}
551
552int SkIntersections::debugLoopCount(DebugLoop index) const {
553 return fDebugLoopCount[index];
554}
555
556void SkIntersections::debugResetLoopCount() {
557 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
558}
559#endif
560
caryclark624637c2015-05-11 07:21:27 -0700561#include "SkPathOpsCubic.h"
562#include "SkPathOpsQuad.h"
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000563
caryclark624637c2015-05-11 07:21:27 -0700564SkDCubic SkDQuad::debugToCubic() const {
565 SkDCubic cubic;
566 cubic[0] = fPts[0];
567 cubic[2] = fPts[1];
568 cubic[3] = fPts[2];
569 cubic[1].fX = (cubic[0].fX + cubic[2].fX * 2) / 3;
570 cubic[1].fY = (cubic[0].fY + cubic[2].fY * 2) / 3;
571 cubic[2].fX = (cubic[3].fX + cubic[2].fX * 2) / 3;
572 cubic[2].fY = (cubic[3].fY + cubic[2].fY * 2) / 3;
573 return cubic;
caryclark54359292015-03-26 07:52:43 -0700574}
caryclark624637c2015-05-11 07:21:27 -0700575
caryclarked0935a2015-10-22 07:23:52 -0700576void SkDRect::debugInit() {
577 fLeft = fTop = fRight = fBottom = SK_ScalarNaN;
578}
579
caryclark624637c2015-05-11 07:21:27 -0700580#include "SkOpAngle.h"
caryclark624637c2015-05-11 07:21:27 -0700581#include "SkOpSegment.h"
caryclark54359292015-03-26 07:52:43 -0700582
caryclark26ad22a2015-10-16 09:03:38 -0700583#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -0700584// commented-out lines keep this in sync with addT()
585 const SkOpPtT* SkOpSegment::debugAddT(double t, AliasMatch allowAlias, bool* allocated) const {
586 debugValidate();
587 SkPoint pt = this->ptAtT(t);
caryclark26ad22a2015-10-16 09:03:38 -0700588 const SkOpSpanBase* span = &fHead;
caryclark55888e42016-07-18 10:01:36 -0700589 do {
590 const SkOpPtT* result = span->ptT();
591 const SkOpPtT* loop;
592 bool duplicatePt;
593 if (t == result->fT) {
594 goto bumpSpan;
caryclark26ad22a2015-10-16 09:03:38 -0700595 }
caryclark55888e42016-07-18 10:01:36 -0700596 if (this->match(result, this, t, pt, allowAlias)) {
597 // see if any existing alias matches segment, pt, and t
598 loop = result->next();
599 duplicatePt = false;
600 while (loop != result) {
601 bool ptMatch = loop->fPt == pt;
602 if (loop->segment() == this && loop->fT == t && ptMatch) {
603 goto bumpSpan;
604 }
605 duplicatePt |= ptMatch;
606 loop = loop->next();
607 }
608 if (kNoAliasMatch == allowAlias) {
609 bumpSpan:
610// span->bumpSpanAdds();
611 return result;
612 }
613// SkOpPtT* alias = SkOpTAllocator<SkOpPtT>::Allocate(allocator);
614// alias->init(result->span(), t, pt, duplicatePt);
615// result->insert(alias);
616// result->span()->unaligned();
617 this->debugValidate();
618// #if DEBUG_ADD_T
619// SkDebugf("%s alias t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
620// alias->segment()->debugID(), alias->span()->debugID());
621// #endif
622// span->bumpSpanAdds();
623 if (allocated) {
624 *allocated = true;
625 }
626 return nullptr;
caryclark26ad22a2015-10-16 09:03:38 -0700627 }
caryclark55888e42016-07-18 10:01:36 -0700628 if (t < result->fT) {
629 const SkOpSpan* prev = result->span()->prev();
630 if (!prev) {
631 return nullptr; // FIXME: this is a fail case; nullptr return elsewhere means result was allocated in non-const version
632 }
633// SkOpSpan* span = insert(prev, allocator);
634// span->init(this, prev, t, pt);
635 this->debugValidate();
636// #if DEBUG_ADD_T
637// SkDebugf("%s insert t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
638// span->segment()->debugID(), span->debugID());
639// #endif
640// span->bumpSpanAdds();
641 if (allocated) {
642 *allocated = true;
643 }
644 return nullptr;
caryclark26ad22a2015-10-16 09:03:38 -0700645 }
caryclark55888e42016-07-18 10:01:36 -0700646 SkASSERT(span != &fTail);
647 } while ((span = span->upCast()->next()));
648 SkASSERT(0);
649 return nullptr;
caryclark26ad22a2015-10-16 09:03:38 -0700650}
651#endif
652
653#if DEBUG_ANGLE
654void SkOpSegment::debugCheckAngleCoin() const {
655 const SkOpSpanBase* base = &fHead;
656 const SkOpSpan* span;
657 do {
658 const SkOpAngle* angle = base->fromAngle();
caryclark55888e42016-07-18 10:01:36 -0700659 if (angle && angle->debugCheckCoincidence()) {
caryclark26ad22a2015-10-16 09:03:38 -0700660 angle->debugCheckNearCoincidence();
661 }
662 if (base->final()) {
663 break;
664 }
665 span = base->upCast();
666 angle = span->toAngle();
caryclark55888e42016-07-18 10:01:36 -0700667 if (angle && angle->debugCheckCoincidence()) {
caryclark26ad22a2015-10-16 09:03:38 -0700668 angle->debugCheckNearCoincidence();
669 }
670 } while ((base = span->next()));
671}
672#endif
673
caryclark55888e42016-07-18 10:01:36 -0700674#if DEBUG_COINCIDENCE_VERBOSE
caryclark26ad22a2015-10-16 09:03:38 -0700675// this mimics the order of the checks in handle coincidence
676void SkOpSegment::debugCheckHealth(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
677 debugMoveMultiples(id, glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700678 debugMoveNearby(id, glitches);
caryclark55888e42016-07-18 10:01:36 -0700679 debugMissingCoincidence(id, glitches);
caryclark26ad22a2015-10-16 09:03:38 -0700680}
681
caryclark55888e42016-07-18 10:01:36 -0700682// commented-out lines keep this in sync with clearAll()
683void SkOpSegment::debugClearAll(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
684 const SkOpSpan* span = &fHead;
685 do {
686 this->debugClearOne(span, id, glitches);
687 } while ((span = span->next()->upCastable()));
688 this->globalState()->coincidence()->debugRelease(id, glitches, this);
689}
690
691// commented-out lines keep this in sync with clearOne()
692void SkOpSegment::debugClearOne(const SkOpSpan* span, const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
693 if (span->windValue()) glitches->record(kCollapsedWindValue_Glitch, id, span);
694 if (span->oppValue()) glitches->record(kCollapsedOppValue_Glitch, id, span);
695 if (!span->done()) glitches->record(kCollapsedDone_Glitch, id, span);
caryclark26ad22a2015-10-16 09:03:38 -0700696}
697#endif
698
caryclark54359292015-03-26 07:52:43 -0700699SkOpAngle* SkOpSegment::debugLastAngle() {
halcanary96fcdcc2015-08-27 07:41:13 -0700700 SkOpAngle* result = nullptr;
caryclark54359292015-03-26 07:52:43 -0700701 SkOpSpan* span = this->head();
702 do {
703 if (span->toAngle()) {
704 SkASSERT(!result);
705 result = span->toAngle();
706 }
707 } while ((span = span->next()->upCastable()));
708 SkASSERT(result);
709 return result;
710}
711
caryclark26ad22a2015-10-16 09:03:38 -0700712#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -0700713// commented-out lines keep this in sync with ClearVisited
714void SkOpSegment::DebugClearVisited(const SkOpSpanBase* span) {
715 // reset visited flag back to false
716 do {
717 const SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
718 while ((ptT = ptT->next()) != stopPtT) {
719 const SkOpSegment* opp = ptT->segment();
720 opp->resetDebugVisited();
721 }
722 } while (!span->final() && (span = span->upCast()->next()));
723}
724#endif
725
726#if DEBUG_COINCIDENCE_VERBOSE
727// commented-out lines keep this in sync with missingCoincidence()
728// look for pairs of undetected coincident curves
729// assumes that segments going in have visited flag clear
730// Even though pairs of curves correct detect coincident runs, a run may be missed
731// if the coincidence is a product of multiple intersections. For instance, given
732// curves A, B, and C:
733// A-B intersect at a point 1; A-C and B-C intersect at point 2, so near
734// the end of C that the intersection is replaced with the end of C.
735// Even though A-B correctly do not detect an intersection at point 2,
736// the resulting run from point 1 to point 2 is coincident on A and B.
737void SkOpSegment::debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -0700738 if (this->done()) {
739 return;
740 }
741 const SkOpSpan* prior = nullptr;
742 const SkOpSpanBase* spanBase = &fHead;
caryclark55888e42016-07-18 10:01:36 -0700743// bool result = false;
caryclark26ad22a2015-10-16 09:03:38 -0700744 do {
745 const SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT;
746 SkASSERT(ptT->span() == spanBase);
747 while ((ptT = ptT->next()) != spanStopPtT) {
748 if (ptT->deleted()) {
749 continue;
750 }
caryclark55888e42016-07-18 10:01:36 -0700751 const SkOpSegment* opp = ptT->span()->segment();
caryclark26ad22a2015-10-16 09:03:38 -0700752 if (opp->done()) {
753 continue;
754 }
755 // when opp is encounted the 1st time, continue; on 2nd encounter, look for coincidence
caryclark55888e42016-07-18 10:01:36 -0700756 if (!opp->debugVisited()) {
caryclark26ad22a2015-10-16 09:03:38 -0700757 continue;
758 }
759 if (spanBase == &fHead) {
760 continue;
761 }
caryclark55888e42016-07-18 10:01:36 -0700762 if (ptT->segment() == this) {
763 continue;
764 }
caryclark26ad22a2015-10-16 09:03:38 -0700765 const SkOpSpan* span = spanBase->upCastable();
766 // FIXME?: this assumes that if the opposite segment is coincident then no more
767 // coincidence needs to be detected. This may not be true.
caryclark55888e42016-07-18 10:01:36 -0700768 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 -0700769 continue;
770 }
caryclark55888e42016-07-18 10:01:36 -0700771 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 -0700772 continue;
caryclark55888e42016-07-18 10:01:36 -0700773 }
caryclark26ad22a2015-10-16 09:03:38 -0700774 const SkOpPtT* priorPtT = nullptr, * priorStopPtT;
775 // find prior span containing opp segment
776 const SkOpSegment* priorOpp = nullptr;
777 const SkOpSpan* priorTest = spanBase->prev();
778 while (!priorOpp && priorTest) {
779 priorStopPtT = priorPtT = priorTest->ptT();
780 while ((priorPtT = priorPtT->next()) != priorStopPtT) {
781 if (priorPtT->deleted()) {
782 continue;
783 }
784 SkOpSegment* segment = priorPtT->span()->segment();
785 if (segment == opp) {
786 prior = priorTest;
787 priorOpp = opp;
788 break;
789 }
790 }
791 priorTest = priorTest->prev();
792 }
793 if (!priorOpp) {
794 continue;
795 }
caryclark55888e42016-07-18 10:01:36 -0700796 if (priorPtT == ptT) {
797 continue;
798 }
caryclark26ad22a2015-10-16 09:03:38 -0700799 const SkOpPtT* oppStart = prior->ptT();
800 const SkOpPtT* oppEnd = spanBase->ptT();
801 bool swapped = priorPtT->fT > ptT->fT;
802 if (swapped) {
803 SkTSwap(priorPtT, ptT);
804 SkTSwap(oppStart, oppEnd);
805 }
caryclark55888e42016-07-18 10:01:36 -0700806 const SkOpCoincidence* coincidence = this->globalState()->coincidence();
807 const SkOpPtT* rootPriorPtT = priorPtT->span()->ptT();
808 const SkOpPtT* rootPtT = ptT->span()->ptT();
809 const SkOpPtT* rootOppStart = oppStart->span()->ptT();
810 const SkOpPtT* rootOppEnd = oppEnd->span()->ptT();
811 if (coincidence->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) {
caryclark26ad22a2015-10-16 09:03:38 -0700812 goto swapBack;
813 }
caryclark55888e42016-07-18 10:01:36 -0700814 if (testForCoincidence(rootPriorPtT, rootPtT, prior, spanBase, opp)) {
815 // mark coincidence
816#if DEBUG_COINCIDENCE
817// SkDebugf("%s coinSpan=%d endSpan=%d oppSpan=%d oppEndSpan=%d\n", __FUNCTION__,
818// rootPriorPtT->debugID(), rootPtT->debugID(), rootOppStart->debugID(),
819// rootOppEnd->debugID());
820#endif
caryclark26ad22a2015-10-16 09:03:38 -0700821 log->record(kMissingCoin_Glitch, id, priorPtT, ptT, oppStart, oppEnd);
caryclark55888e42016-07-18 10:01:36 -0700822 // coincidences->add(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
823 // }
824#if DEBUG_COINCIDENCE
825// SkASSERT(coincidences->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)
826#endif
827 // result = true;
caryclark26ad22a2015-10-16 09:03:38 -0700828 }
829 swapBack:
830 if (swapped) {
831 SkTSwap(priorPtT, ptT);
832 }
833 }
834 } while ((spanBase = spanBase->final() ? nullptr : spanBase->upCast()->next()));
caryclark55888e42016-07-18 10:01:36 -0700835 DebugClearVisited(&fHead);
836 return;
caryclark26ad22a2015-10-16 09:03:38 -0700837}
838
caryclark55888e42016-07-18 10:01:36 -0700839// commented-out lines keep this in sync with moveMultiples()
840// if a span has more than one intersection, merge the other segments' span as needed
caryclark26ad22a2015-10-16 09:03:38 -0700841void SkOpSegment::debugMoveMultiples(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -0700842 debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -0700843 const SkOpSpanBase* test = &fHead;
844 do {
845 int addCount = test->spanAddsCount();
846 SkASSERT(addCount >= 1);
847 if (addCount == 1) {
848 continue;
849 }
850 const SkOpPtT* startPtT = test->ptT();
851 const SkOpPtT* testPtT = startPtT;
852 do { // iterate through all spans associated with start
853 const SkOpSpanBase* oppSpan = testPtT->span();
854 if (oppSpan->spanAddsCount() == addCount) {
855 continue;
856 }
857 if (oppSpan->deleted()) {
858 continue;
859 }
860 const SkOpSegment* oppSegment = oppSpan->segment();
861 if (oppSegment == this) {
862 continue;
863 }
864 // find range of spans to consider merging
865 const SkOpSpanBase* oppPrev = oppSpan;
866 const SkOpSpanBase* oppFirst = oppSpan;
867 while ((oppPrev = oppPrev->prev())) {
868 if (!roughly_equal(oppPrev->t(), oppSpan->t())) {
869 break;
870 }
871 if (oppPrev->spanAddsCount() == addCount) {
872 continue;
873 }
874 if (oppPrev->deleted()) {
875 continue;
876 }
877 oppFirst = oppPrev;
878 }
879 const SkOpSpanBase* oppNext = oppSpan;
880 const SkOpSpanBase* oppLast = oppSpan;
881 while ((oppNext = oppNext->final() ? nullptr : oppNext->upCast()->next())) {
882 if (!roughly_equal(oppNext->t(), oppSpan->t())) {
883 break;
884 }
885 if (oppNext->spanAddsCount() == addCount) {
886 continue;
887 }
888 if (oppNext->deleted()) {
889 continue;
890 }
891 oppLast = oppNext;
892 }
893 if (oppFirst == oppLast) {
894 continue;
895 }
896 const SkOpSpanBase* oppTest = oppFirst;
897 do {
898 if (oppTest == oppSpan) {
899 continue;
900 }
901 // check to see if the candidate meets specific criteria:
902 // it contains spans of segments in test's loop but not including 'this'
903 const SkOpPtT* oppStartPtT = oppTest->ptT();
904 const SkOpPtT* oppPtT = oppStartPtT;
905 while ((oppPtT = oppPtT->next()) != oppStartPtT) {
906 const SkOpSegment* oppPtTSegment = oppPtT->segment();
907 if (oppPtTSegment == this) {
908 goto tryNextSpan;
909 }
910 const SkOpPtT* matchPtT = startPtT;
911 do {
912 if (matchPtT->segment() == oppPtTSegment) {
913 goto foundMatch;
914 }
915 } while ((matchPtT = matchPtT->next()) != startPtT);
916 goto tryNextSpan;
917 foundMatch: // merge oppTest and oppSpan
caryclark55888e42016-07-18 10:01:36 -0700918 oppSegment->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -0700919 if (oppTest == &oppSegment->fTail || oppTest == &oppSegment->fHead) {
920 SkASSERT(oppSpan != &oppSegment->fHead); // don't expect collapse
921 SkASSERT(oppSpan != &oppSegment->fTail);
922 glitches->record(kMoveMultiple_Glitch, id, oppTest, oppSpan);
923 } else {
924 glitches->record(kMoveMultiple_Glitch, id, oppSpan, oppTest);
925 }
caryclark55888e42016-07-18 10:01:36 -0700926 oppSegment->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -0700927 goto checkNextSpan;
928 }
caryclark55888e42016-07-18 10:01:36 -0700929 tryNextSpan:
caryclark26ad22a2015-10-16 09:03:38 -0700930 ;
931 } while (oppTest != oppLast && (oppTest = oppTest->upCast()->next()));
932 } while ((testPtT = testPtT->next()) != startPtT);
caryclark55888e42016-07-18 10:01:36 -0700933checkNextSpan:
caryclark26ad22a2015-10-16 09:03:38 -0700934 ;
935 } while ((test = test->final() ? nullptr : test->upCast()->next()));
caryclark55888e42016-07-18 10:01:36 -0700936 debugValidate();
937 return;
caryclark26ad22a2015-10-16 09:03:38 -0700938}
939
caryclark55888e42016-07-18 10:01:36 -0700940// commented-out lines keep this in sync with moveNearby()
941// Move nearby t values and pts so they all hang off the same span. Alignment happens later.
caryclark26ad22a2015-10-16 09:03:38 -0700942void SkOpSegment::debugMoveNearby(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
caryclark55888e42016-07-18 10:01:36 -0700943 debugValidate();
944 // release undeleted spans pointing to this seg that are linked to the primary span
945 const SkOpSpanBase* spanBase = &fHead;
caryclark26ad22a2015-10-16 09:03:38 -0700946 do {
caryclark55888e42016-07-18 10:01:36 -0700947 const SkOpPtT* ptT = spanBase->ptT();
948 const SkOpPtT* headPtT = ptT;
949 while ((ptT = ptT->next()) != headPtT) {
950 const SkOpSpanBase* test = ptT->span();
951 if (ptT->segment() == this && !ptT->deleted() && test != spanBase
952 && test->ptT() == ptT) {
953 if (test->final()) {
954 if (spanBase == &fHead) {
955 glitches->record(kMoveNearbyClearAll_Glitch, id, this);
956// return;
957 }
958 glitches->record(kMoveNearbyReleaseFinal_Glitch, id, spanBase, ptT);
959 } else if (test->prev()) {
960 glitches->record(kMoveNearbyRelease_Glitch, id, test, headPtT);
961 }
962// break;
caryclark26ad22a2015-10-16 09:03:38 -0700963 }
964 }
caryclark55888e42016-07-18 10:01:36 -0700965 spanBase = spanBase->upCast()->next();
966 } while (!spanBase->final());
967
968 // This loop looks for adjacent spans which are near by
969 spanBase = &fHead;
970 do { // iterate through all spans associated with start
971 const SkOpSpanBase* test = spanBase->upCast()->next();
972 if (this->spansNearby(spanBase, test)) {
973 if (test->final()) {
974 if (spanBase->prev()) {
975 glitches->record(kMoveNearbyMergeFinal_Glitch, id, test);
976 } else {
977 glitches->record(kMoveNearbyClearAll2_Glitch, id, this);
978 // return
979 }
980 } else {
981 glitches->record(kMoveNearbyMerge_Glitch, id, spanBase);
982 }
983 }
984 spanBase = test;
985 } while (!spanBase->final());
986 debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -0700987}
988#endif
989
caryclark54359292015-03-26 07:52:43 -0700990void SkOpSegment::debugReset() {
caryclark1049f122015-04-20 08:31:59 -0700991 this->init(this->fPts, this->fWeight, this->contour(), this->verb());
caryclark54359292015-03-26 07:52:43 -0700992}
993
994#if DEBUG_ACTIVE_SPANS
995void SkOpSegment::debugShowActiveSpans() const {
996 debugValidate();
997 if (done()) {
998 return;
999 }
1000 int lastId = -1;
1001 double lastT = -1;
1002 const SkOpSpan* span = &fHead;
1003 do {
1004 if (span->done()) {
1005 continue;
1006 }
caryclark1049f122015-04-20 08:31:59 -07001007 if (lastId == this->debugID() && lastT == span->t()) {
caryclark54359292015-03-26 07:52:43 -07001008 continue;
1009 }
caryclark1049f122015-04-20 08:31:59 -07001010 lastId = this->debugID();
caryclark54359292015-03-26 07:52:43 -07001011 lastT = span->t();
caryclark1049f122015-04-20 08:31:59 -07001012 SkDebugf("%s id=%d", __FUNCTION__, this->debugID());
caryclark55888e42016-07-18 10:01:36 -07001013 // since endpoints may have be adjusted, show actual computed curves
1014 SkDCurve curvePart;
1015 this->subDivide(span, span->next(), &curvePart);
1016 const SkDPoint* pts = curvePart.fCubic.fPts;
1017 SkDebugf(" (%1.9g,%1.9g", pts[0].fX, pts[0].fY);
caryclark54359292015-03-26 07:52:43 -07001018 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
caryclark55888e42016-07-18 10:01:36 -07001019 SkDebugf(" %1.9g,%1.9g", pts[vIndex].fX, pts[vIndex].fY);
caryclark54359292015-03-26 07:52:43 -07001020 }
caryclark1049f122015-04-20 08:31:59 -07001021 if (SkPath::kConic_Verb == fVerb) {
caryclark55888e42016-07-18 10:01:36 -07001022 SkDebugf(" %1.9gf", curvePart.fConic.fWeight);
caryclark1049f122015-04-20 08:31:59 -07001023 }
caryclark55888e42016-07-18 10:01:36 -07001024 SkDebugf(") t=%1.9g tEnd=%1.9g", span->t(), span->next()->t());
caryclark54359292015-03-26 07:52:43 -07001025 if (span->windSum() == SK_MinS32) {
caryclark624637c2015-05-11 07:21:27 -07001026 SkDebugf(" windSum=?");
caryclark54359292015-03-26 07:52:43 -07001027 } else {
caryclark624637c2015-05-11 07:21:27 -07001028 SkDebugf(" windSum=%d", span->windSum());
caryclark54359292015-03-26 07:52:43 -07001029 }
caryclark624637c2015-05-11 07:21:27 -07001030 if (span->oppValue() && span->oppSum() == SK_MinS32) {
1031 SkDebugf(" oppSum=?");
1032 } else if (span->oppValue() || span->oppSum() != SK_MinS32) {
1033 SkDebugf(" oppSum=%d", span->oppSum());
1034 }
1035 SkDebugf(" windValue=%d", span->windValue());
1036 if (span->oppValue() || span->oppSum() != SK_MinS32) {
1037 SkDebugf(" oppValue=%d", span->oppValue());
1038 }
caryclark54359292015-03-26 07:52:43 -07001039 SkDebugf("\n");
1040 } while ((span = span->next()->upCastable()));
1041}
1042#endif
1043
1044#if DEBUG_MARK_DONE
1045void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding) {
1046 const SkPoint& pt = span->ptT()->fPt;
caryclark1049f122015-04-20 08:31:59 -07001047 SkDebugf("%s id=%d", fun, this->debugID());
caryclark54359292015-03-26 07:52:43 -07001048 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1049 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1050 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1051 }
1052 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
1053 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t());
1054 if (winding == SK_MinS32) {
1055 SkDebugf("?");
1056 } else {
1057 SkDebugf("%d", winding);
1058 }
1059 SkDebugf(" windSum=");
1060 if (span->windSum() == SK_MinS32) {
1061 SkDebugf("?");
1062 } else {
1063 SkDebugf("%d", span->windSum());
1064 }
1065 SkDebugf(" windValue=%d\n", span->windValue());
1066}
1067
1068void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding,
1069 int oppWinding) {
1070 const SkPoint& pt = span->ptT()->fPt;
caryclark1049f122015-04-20 08:31:59 -07001071 SkDebugf("%s id=%d", fun, this->debugID());
caryclark54359292015-03-26 07:52:43 -07001072 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1073 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
1074 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1075 }
1076 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
1077 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t(), winding, oppWinding);
1078 if (winding == SK_MinS32) {
1079 SkDebugf("?");
1080 } else {
1081 SkDebugf("%d", winding);
1082 }
1083 SkDebugf(" newOppSum=");
1084 if (oppWinding == SK_MinS32) {
1085 SkDebugf("?");
1086 } else {
1087 SkDebugf("%d", oppWinding);
1088 }
1089 SkDebugf(" oppSum=");
1090 if (span->oppSum() == SK_MinS32) {
1091 SkDebugf("?");
1092 } else {
1093 SkDebugf("%d", span->oppSum());
1094 }
1095 SkDebugf(" windSum=");
1096 if (span->windSum() == SK_MinS32) {
1097 SkDebugf("?");
1098 } else {
1099 SkDebugf("%d", span->windSum());
1100 }
1101 SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue());
1102}
1103
1104#endif
1105
caryclark26ad22a2015-10-16 09:03:38 -07001106// loop looking for a pair of angle parts that are too close to be sorted
1107/* This is called after other more simple intersection and angle sorting tests have been exhausted.
1108 This should be rarely called -- the test below is thorough and time consuming.
caryclark55888e42016-07-18 10:01:36 -07001109 This checks the distance between start points; the distance between
caryclark26ad22a2015-10-16 09:03:38 -07001110*/
1111#if DEBUG_ANGLE
1112void SkOpAngle::debugCheckNearCoincidence() const {
1113 const SkOpAngle* test = this;
1114 do {
1115 const SkOpSegment* testSegment = test->segment();
1116 double testStartT = test->start()->t();
1117 SkDPoint testStartPt = testSegment->dPtAtT(testStartT);
1118 double testEndT = test->end()->t();
1119 SkDPoint testEndPt = testSegment->dPtAtT(testEndT);
1120 double testLenSq = testStartPt.distanceSquared(testEndPt);
1121 SkDebugf("%s testLenSq=%1.9g id=%d\n", __FUNCTION__, testLenSq, testSegment->debugID());
1122 double testMidT = (testStartT + testEndT) / 2;
1123 const SkOpAngle* next = test;
1124 while ((next = next->fNext) != this) {
1125 SkOpSegment* nextSegment = next->segment();
1126 double testMidDistSq = testSegment->distSq(testMidT, next);
1127 double testEndDistSq = testSegment->distSq(testEndT, next);
1128 double nextStartT = next->start()->t();
1129 SkDPoint nextStartPt = nextSegment->dPtAtT(nextStartT);
1130 double distSq = testStartPt.distanceSquared(nextStartPt);
1131 double nextEndT = next->end()->t();
1132 double nextMidT = (nextStartT + nextEndT) / 2;
1133 double nextMidDistSq = nextSegment->distSq(nextMidT, test);
1134 double nextEndDistSq = nextSegment->distSq(nextEndT, test);
1135 SkDebugf("%s distSq=%1.9g testId=%d nextId=%d\n", __FUNCTION__, distSq,
1136 testSegment->debugID(), nextSegment->debugID());
1137 SkDebugf("%s testMidDistSq=%1.9g\n", __FUNCTION__, testMidDistSq);
1138 SkDebugf("%s testEndDistSq=%1.9g\n", __FUNCTION__, testEndDistSq);
1139 SkDebugf("%s nextMidDistSq=%1.9g\n", __FUNCTION__, nextMidDistSq);
1140 SkDebugf("%s nextEndDistSq=%1.9g\n", __FUNCTION__, nextEndDistSq);
1141 SkDPoint nextEndPt = nextSegment->dPtAtT(nextEndT);
1142 double nextLenSq = nextStartPt.distanceSquared(nextEndPt);
1143 SkDebugf("%s nextLenSq=%1.9g\n", __FUNCTION__, nextLenSq);
1144 SkDebugf("\n");
1145 }
1146 test = test->fNext;
caryclark55888e42016-07-18 10:01:36 -07001147 } while (test->fNext != this);
caryclark26ad22a2015-10-16 09:03:38 -07001148}
1149#endif
1150
caryclark54359292015-03-26 07:52:43 -07001151#if DEBUG_ANGLE
1152SkString SkOpAngle::debugPart() const {
1153 SkString result;
1154 switch (this->segment()->verb()) {
1155 case SkPath::kLine_Verb:
1156 result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fCurvePart),
1157 this->segment()->debugID());
1158 break;
1159 case SkPath::kQuad_Verb:
1160 result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fCurvePart),
1161 this->segment()->debugID());
1162 break;
caryclark1049f122015-04-20 08:31:59 -07001163 case SkPath::kConic_Verb:
1164 result.printf(CONIC_DEBUG_STR " id=%d",
1165 CONIC_DEBUG_DATA(fCurvePart, fCurvePart.fConic.fWeight),
1166 this->segment()->debugID());
1167 break;
caryclark54359292015-03-26 07:52:43 -07001168 case SkPath::kCubic_Verb:
1169 result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fCurvePart),
1170 this->segment()->debugID());
1171 break;
1172 default:
1173 SkASSERT(0);
mtklein1b249332015-07-07 12:21:21 -07001174 }
caryclark54359292015-03-26 07:52:43 -07001175 return result;
1176}
1177#endif
1178
caryclark624637c2015-05-11 07:21:27 -07001179#if DEBUG_SORT
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001180void SkOpAngle::debugLoop() const {
caryclarke4097e32014-06-18 07:24:19 -07001181 const SkOpAngle* first = this;
1182 const SkOpAngle* next = this;
1183 do {
1184 next->dumpOne(true);
caryclark19eb3b22014-07-18 05:08:14 -07001185 SkDebugf("\n");
caryclarke4097e32014-06-18 07:24:19 -07001186 next = next->fNext;
1187 } while (next && next != first);
caryclark54359292015-03-26 07:52:43 -07001188 next = first;
1189 do {
1190 next->debugValidate();
1191 next = next->fNext;
1192 } while (next && next != first);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001193}
1194#endif
1195
caryclark54359292015-03-26 07:52:43 -07001196void SkOpAngle::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07001197#if DEBUG_COINCIDENCE
1198 if (this->globalState()->debugCheckHealth()) {
1199 return;
1200 }
1201#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001202#if DEBUG_VALIDATE
caryclark54359292015-03-26 07:52:43 -07001203 const SkOpAngle* first = this;
1204 const SkOpAngle* next = this;
1205 int wind = 0;
1206 int opp = 0;
1207 int lastXor = -1;
1208 int lastOppXor = -1;
1209 do {
1210 if (next->unorderable()) {
1211 return;
1212 }
1213 const SkOpSpan* minSpan = next->start()->starter(next->end());
1214 if (minSpan->windValue() == SK_MinS32) {
1215 return;
1216 }
1217 bool op = next->segment()->operand();
1218 bool isXor = next->segment()->isXor();
1219 bool oppXor = next->segment()->oppXor();
1220 SkASSERT(!DEBUG_LIMIT_WIND_SUM || between(0, minSpan->windValue(), DEBUG_LIMIT_WIND_SUM));
1221 SkASSERT(!DEBUG_LIMIT_WIND_SUM
1222 || between(-DEBUG_LIMIT_WIND_SUM, minSpan->oppValue(), DEBUG_LIMIT_WIND_SUM));
1223 bool useXor = op ? oppXor : isXor;
1224 SkASSERT(lastXor == -1 || lastXor == (int) useXor);
1225 lastXor = (int) useXor;
caryclark624637c2015-05-11 07:21:27 -07001226 wind += next->debugSign() * (op ? minSpan->oppValue() : minSpan->windValue());
caryclark54359292015-03-26 07:52:43 -07001227 if (useXor) {
1228 wind &= 1;
1229 }
1230 useXor = op ? isXor : oppXor;
1231 SkASSERT(lastOppXor == -1 || lastOppXor == (int) useXor);
1232 lastOppXor = (int) useXor;
caryclark624637c2015-05-11 07:21:27 -07001233 opp += next->debugSign() * (op ? minSpan->windValue() : minSpan->oppValue());
caryclark54359292015-03-26 07:52:43 -07001234 if (useXor) {
1235 opp &= 1;
1236 }
1237 next = next->fNext;
1238 } while (next && next != first);
caryclark182b4992015-05-14 05:45:54 -07001239 SkASSERT(wind == 0 || !FLAGS_runFail);
caryclark54359292015-03-26 07:52:43 -07001240 SkASSERT(opp == 0 || !FLAGS_runFail);
1241#endif
1242}
1243
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001244void SkOpAngle::debugValidateNext() const {
caryclark54359292015-03-26 07:52:43 -07001245#if !FORCE_RELEASE
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001246 const SkOpAngle* first = this;
1247 const SkOpAngle* next = first;
1248 SkTDArray<const SkOpAngle*>(angles);
1249 do {
djsollenf2b340f2016-01-29 08:51:04 -08001250// SkASSERT_RELEASE(next->fSegment->debugContains(next));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001251 angles.push(next);
1252 next = next->next();
1253 if (next == first) {
1254 break;
1255 }
djsollenf2b340f2016-01-29 08:51:04 -08001256 SkASSERT_RELEASE(!angles.contains(next));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001257 if (!next) {
1258 return;
1259 }
1260 } while (true);
reed0dc4dd62015-03-24 13:55:33 -07001261#endif
reed0dc4dd62015-03-24 13:55:33 -07001262}
reed0dc4dd62015-03-24 13:55:33 -07001263
caryclark55888e42016-07-18 10:01:36 -07001264#ifdef SK_DEBUG
1265void SkCoincidentSpans::debugStartCheck(const SkOpSpanBase* outer, const SkOpSpanBase* over,
1266 const SkOpGlobalState* debugState) const {
1267 SkASSERT(coinPtTEnd()->span() == over || !debugState->debugRunFail());
1268 SkASSERT(oppPtTEnd()->span() == outer || !debugState->debugRunFail());
1269}
1270#endif
caryclark26ad22a2015-10-16 09:03:38 -07001271
caryclark55888e42016-07-18 10:01:36 -07001272#if DEBUG_COINCIDENCE_VERBOSE
1273/* Commented-out lines keep this in sync with expand */
1274bool SkCoincidentSpans::debugExpand(const char* id, SkPathOpsDebug::GlitchLog* log) const {
1275 bool expanded = false;
1276 const SkOpSegment* segment = coinPtTStart()->segment();
1277 const SkOpSegment* oppSegment = oppPtTStart()->segment();
1278 do {
1279 const SkOpSpan* start = coinPtTStart()->span()->upCast();
1280 const SkOpSpan* prev = start->prev();
1281 const SkOpPtT* oppPtT;
1282 if (!prev || !(oppPtT = prev->contains(oppSegment))) {
1283 break;
1284 }
1285 double midT = (prev->t() + start->t()) / 2;
1286 if (!segment->isClose(midT, oppSegment)) {
1287 break;
1288 }
1289 if (log) log->record(kExpandCoin_Glitch, id, this, prev->ptT(), oppPtT);
1290 expanded = true;
1291 } while (false); // actual continues while expansion is possible
1292 do {
1293 const SkOpSpanBase* end = coinPtTEnd()->span();
1294 SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
1295 const SkOpPtT* oppPtT;
1296 if (!next || !(oppPtT = next->contains(oppSegment))) {
1297 break;
1298 }
1299 double midT = (end->t() + next->t()) / 2;
1300 if (!segment->isClose(midT, oppSegment)) {
1301 break;
1302 }
1303 if (log) log->record(kExpandCoin_Glitch, id, this, next->ptT(), oppPtT);
1304 expanded = true;
1305 } while (false); // actual continues while expansion is possible
1306 return expanded;
1307}
1308
1309#define FAIL_IF(cond) do { if (cond) log->record(kAddExpandedFail_Glitch, id, coin); } while (false)
1310
1311/* Commented-out lines keep this in sync with addExpanded */
1312// for each coincident pair, match the spans
1313// if the spans don't match, add the mssing pt to the segment and loop it in the opposite span
caryclark26ad22a2015-10-16 09:03:38 -07001314void SkOpCoincidence::debugAddExpanded(const char* id, SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07001315 const SkCoincidentSpans* coin = this->fHead;
1316 if (!coin) {
caryclarked0935a2015-10-22 07:23:52 -07001317 return;
1318 }
caryclark26ad22a2015-10-16 09:03:38 -07001319 do {
caryclark55888e42016-07-18 10:01:36 -07001320 const SkOpPtT* startPtT = coin->coinPtTStart();
1321 const SkOpPtT* oStartPtT = coin->oppPtTStart();
caryclark26ad22a2015-10-16 09:03:38 -07001322 SkASSERT(startPtT->contains(oStartPtT));
caryclark55888e42016-07-18 10:01:36 -07001323 SkASSERT(coin->coinPtTEnd()->contains(coin->oppPtTEnd()));
caryclark26ad22a2015-10-16 09:03:38 -07001324 const SkOpSpanBase* start = startPtT->span();
1325 const SkOpSpanBase* oStart = oStartPtT->span();
caryclark55888e42016-07-18 10:01:36 -07001326 const SkOpSpanBase* end = coin->coinPtTEnd()->span();
1327 const SkOpSpanBase* oEnd = coin->oppPtTEnd()->span();
1328 FAIL_IF(oEnd->deleted());
caryclark26ad22a2015-10-16 09:03:38 -07001329 const SkOpSpanBase* test = start->upCast()->next();
caryclark55888e42016-07-18 10:01:36 -07001330 const SkOpSpanBase* oTest = coin->flipped() ? oStart->prev() : oStart->upCast()->next();
1331 if (!oTest) {
1332 return;
1333 }
caryclark26ad22a2015-10-16 09:03:38 -07001334 while (test != end || oTest != oEnd) {
caryclark55888e42016-07-18 10:01:36 -07001335 if (!test->ptT()->contains(oTest->segment())
1336 || !oTest->ptT()->contains(start->segment())) {
caryclark26ad22a2015-10-16 09:03:38 -07001337 // use t ranges to guess which one is missing
caryclark55888e42016-07-18 10:01:36 -07001338 double startRange = coin->coinPtTEnd()->fT - startPtT->fT;
1339 FAIL_IF(!startRange);
caryclark26ad22a2015-10-16 09:03:38 -07001340 double startPart = (test->t() - startPtT->fT) / startRange;
caryclark55888e42016-07-18 10:01:36 -07001341 double oStartRange = coin->oppPtTEnd()->fT - oStartPtT->fT;
1342 FAIL_IF(!oStartRange);
caryclark26ad22a2015-10-16 09:03:38 -07001343 double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange;
caryclark55888e42016-07-18 10:01:36 -07001344 FAIL_IF(startPart == oStartPart);
1345 bool startOver = false;
1346 if (startPart < oStartPart)
1347 log->record(kAddExpandedCoin_Glitch, id, // strange debug formatting lines up with original
1348 oStartPtT->fT + oStartRange * startPart, test);
1349 else log->record(kAddExpandedCoin_Glitch, id,
1350 startPtT->fT + startRange * oStartPart, oTest);
1351 if (false) {
1352 SkASSERT(0);
1353 return;
caryclark26ad22a2015-10-16 09:03:38 -07001354 }
caryclark55888e42016-07-18 10:01:36 -07001355 if (startOver) {
1356 test = start;
1357 oTest = oStart;
caryclark26ad22a2015-10-16 09:03:38 -07001358 }
1359 }
caryclark55888e42016-07-18 10:01:36 -07001360 if (test != end) {
caryclark26ad22a2015-10-16 09:03:38 -07001361 test = test->upCast()->next();
1362 }
caryclark55888e42016-07-18 10:01:36 -07001363 if (oTest != oEnd) {
1364 oTest = coin->flipped() ? oTest->prev() : oTest->upCast()->next();
1365 if (!oTest) {
1366 return;
1367 }
caryclark26ad22a2015-10-16 09:03:38 -07001368 }
1369 }
caryclark55888e42016-07-18 10:01:36 -07001370 } while ((coin = coin->next()));
1371 return;
caryclark26ad22a2015-10-16 09:03:38 -07001372}
1373
caryclark55888e42016-07-18 10:01:36 -07001374/* Commented-out lines keep this in sync with addIfMissing() */
1375void SkOpCoincidence::debugAddIfMissing(const SkCoincidentSpans* outer, const SkOpPtT* over1s,
1376 const SkOpPtT* over1e, const char* id, SkPathOpsDebug::GlitchLog* log) const {
1377// SkASSERT(fTop);
1378 if (fTop && alreadyAdded(fTop, outer, over1s, over1e)) { // in debug, fTop may be null
1379 return;
caryclark26ad22a2015-10-16 09:03:38 -07001380 }
caryclark55888e42016-07-18 10:01:36 -07001381 if (fHead && alreadyAdded(fHead, outer, over1s, over1e)) {
1382 return;
1383 }
1384 log->record(kAddIfMissingCoin_Glitch, id, outer->coinPtTStart(), outer->coinPtTEnd(), over1s, over1e);
1385 this->debugValidate();
1386 return;
caryclark26ad22a2015-10-16 09:03:38 -07001387}
1388
caryclark55888e42016-07-18 10:01:36 -07001389/* Commented-out lines keep this in sync addIfMissing() */
1390void SkOpCoincidence::debugAddIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
1391 const SkOpPtT* over2s, const SkOpPtT* over2e, double tStart, double tEnd,
1392 const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
1393 const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd, const char* id, SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07001394 double coinTs, coinTe, oppTs, oppTe;
caryclark55888e42016-07-18 10:01:36 -07001395 TRange(over1s, over1e, tStart, tEnd, coinPtTStart, coinPtTEnd, &coinTs, &coinTe);
1396 TRange(over2s, over2e, tStart, tEnd, oppPtTStart, oppPtTEnd, &oppTs, &oppTe);
1397 bool swap = coinTs > coinTe;
1398 if (swap) {
1399 SkTSwap(coinTs, coinTe);
caryclark26ad22a2015-10-16 09:03:38 -07001400 }
1401 if ((over1s->fT < over1e->fT) != (over2s->fT < over2e->fT)) {
1402 SkTSwap(oppTs, oppTe);
1403 }
caryclark55888e42016-07-18 10:01:36 -07001404 if (swap) {
caryclark26ad22a2015-10-16 09:03:38 -07001405 SkTSwap(oppTs, oppTe);
1406 }
caryclark55888e42016-07-18 10:01:36 -07001407 const SkOpSegment* coinSeg = coinPtTStart->segment();
1408 const SkOpSegment* oppSeg = oppPtTStart->segment();
1409 if (coinSeg == oppSeg) {
1410 return;
caryclark26ad22a2015-10-16 09:03:38 -07001411 }
caryclark55888e42016-07-18 10:01:36 -07001412 return this->debugAddOrOverlap(coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, id, log);
caryclark26ad22a2015-10-16 09:03:38 -07001413}
1414
caryclark55888e42016-07-18 10:01:36 -07001415/* Commented-out lines keep this in sync addOrOverlap() */
1416void SkOpCoincidence::debugAddOrOverlap(const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
1417 double coinTs, double coinTe, double oppTs, double oppTe, const char* id, SkPathOpsDebug::GlitchLog* log) const {
1418 SkTDArray<SkCoincidentSpans*> overlaps;
1419 SkASSERT(!fTop); // this is (correctly) reversed in addifMissing()
1420 if (fTop && !this->checkOverlap(fTop, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &overlaps)) {
1421 return;
1422 }
1423 if (fHead && !this->checkOverlap(fHead, coinSeg, oppSeg, coinTs,
1424 coinTe, oppTs, oppTe, &overlaps)) {
1425 return;
1426 }
1427 const SkCoincidentSpans* overlap = overlaps.count() ? overlaps[0] : nullptr;
1428 for (int index = 1; index < overlaps.count(); ++index) { // combine overlaps before continuing
1429 const SkCoincidentSpans* test = overlaps[index];
1430 if (overlap->coinPtTStart()->fT > test->coinPtTStart()->fT) {
1431 log->record(kAddOrOverlap_Glitch, id, overlap, test->coinPtTStart());
1432 }
1433 if (overlap->coinPtTEnd()->fT < test->coinPtTEnd()->fT) {
1434 log->record(kAddOrOverlap_Glitch, id, overlap, test->coinPtTEnd());
1435 }
1436 if (overlap->flipped()
1437 ? overlap->oppPtTStart()->fT < test->oppPtTStart()->fT
1438 : overlap->oppPtTStart()->fT > test->oppPtTStart()->fT) {
1439 log->record(kAddOrOverlap_Glitch, id, overlap, test->oppPtTStart());
1440 }
1441 if (overlap->flipped()
1442 ? overlap->oppPtTEnd()->fT > test->oppPtTEnd()->fT
1443 : overlap->oppPtTEnd()->fT < test->oppPtTEnd()->fT) {
1444 log->record(kAddOrOverlap_Glitch, id, overlap, test->oppPtTEnd());
1445 }
1446 if (!fHead) {
1447 SkAssertResult(true);
1448 }
1449 }
1450 const SkOpPtT* cs = coinSeg->existing(coinTs, oppSeg);
1451 const SkOpPtT* ce = coinSeg->existing(coinTe, oppSeg);
1452 if (overlap && cs && ce && overlap->contains(cs, ce)) {
1453 return;
1454 }
1455 SkASSERT(cs != ce || !cs);
1456 const SkOpPtT* os = oppSeg->existing(oppTs, coinSeg);
1457 const SkOpPtT* oe = oppSeg->existing(oppTe, coinSeg);
1458 if (overlap && os && oe && overlap->contains(os, oe)) {
1459 return;
1460 }
1461 SkASSERT(true || !cs || !cs->deleted());
1462 SkASSERT(true || !os || !os->deleted());
1463 SkASSERT(true || !ce || !ce->deleted());
1464 SkASSERT(true || !oe || !oe->deleted());
1465 const SkOpPtT* csExisting = !cs ? coinSeg->existing(coinTs, nullptr) : nullptr;
1466 const SkOpPtT* ceExisting = !ce ? coinSeg->existing(coinTe, nullptr) : nullptr;
1467 if (csExisting && csExisting == ceExisting) {
1468 return;
1469 }
1470 if (csExisting && (csExisting == ce || csExisting->contains(ceExisting ? ceExisting : ce))) {
1471 return;
1472 }
1473 if (ceExisting && (ceExisting == cs || ceExisting->contains(csExisting ? csExisting : cs))) {
1474 return;
1475 }
1476 const SkOpPtT* osExisting = !os ? oppSeg->existing(oppTs, nullptr) : nullptr;
1477 const SkOpPtT* oeExisting = !oe ? oppSeg->existing(oppTe, nullptr) : nullptr;
1478 if (osExisting && osExisting == oeExisting) {
1479 return;
1480 }
1481 if (osExisting && (osExisting == oe || osExisting->contains(oeExisting ? oeExisting : oe))) {
1482 return;
1483 }
1484 if (oeExisting && (oeExisting == os || oeExisting->contains(osExisting ? osExisting : os))) {
1485 return;
1486 }
1487 bool csDeleted = false, osDeleted = false, ceDeleted = false, oeDeleted = false;
1488 this->debugValidate();
1489 if (!cs || !os) {
1490 if (!cs)
1491 cs = coinSeg->debugAddT(coinTs, SkOpSegment::kNoAliasMatch, nullptr);
1492 if (!os)
1493 os = oppSeg->debugAddT(oppTs, SkOpSegment::kNoAliasMatch, nullptr);
1494 if (cs && os) cs->span()->debugAddOppAndMerge(id, log, os->span(), &csDeleted, &osDeleted);
1495// cs = csWritable;
1496// os = osWritable;
1497 if ((ce && ce->deleted()) || (oe && oe->deleted())) {
1498 return;
1499 }
1500 }
1501 if (!ce || !oe) {
1502 if (!ce)
1503 ce = coinSeg->debugAddT(coinTe, SkOpSegment::kNoAliasMatch, nullptr);
1504 if (!oe)
1505 oe = oppSeg->debugAddT(oppTe, SkOpSegment::kNoAliasMatch, nullptr);
1506 if (ce && oe) ce->span()->debugAddOppAndMerge(id, log, oe->span(), &ceDeleted, &oeDeleted);
1507// ce = ceWritable;
1508// oe = oeWritable;
1509 }
1510 this->debugValidate();
1511 if (csDeleted || osDeleted || ceDeleted || oeDeleted) {
1512 return;
1513 }
1514 if (!cs || !ce || cs->contains(ce) || !os || !oe || os->contains(oe)) {
1515 return;
1516 }
1517// bool result = true;
1518 if (overlap) {
1519 if (overlap->coinPtTStart()->segment() == coinSeg) {
1520 log->record(kAddMissingExtend_Glitch, id, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
1521 } else {
1522 if (oppTs > oppTe) {
1523 SkTSwap(coinTs, coinTe);
1524 SkTSwap(oppTs, oppTe);
1525 }
1526 log->record(kAddMissingExtend_Glitch, id, oppSeg, oppTs, oppTe, coinSeg, coinTs, coinTe);
1527 }
1528#if DEBUG_COINCIDENCE_VERBOSE
1529// if (result) {
1530// overlap->debugShow();
1531// }
1532#endif
1533 } else {
1534 log->record(kAddMissingCoin_Glitch, id, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
1535#if DEBUG_COINCIDENCE_VERBOSE
1536// fHead->debugShow();
1537#endif
1538 }
1539 this->debugValidate();
1540 return;
1541}
1542
1543// Extra commented-out lines keep this in sync with addMissing()
1544/* detects overlaps of different coincident runs on same segment */
1545/* does not detect overlaps for pairs without any segments in common */
1546// returns true if caller should loop again
caryclark26ad22a2015-10-16 09:03:38 -07001547void SkOpCoincidence::debugAddMissing(const char* id, SkPathOpsDebug::GlitchLog* log) const {
1548 const SkCoincidentSpans* outer = fHead;
1549 if (!outer) {
1550 return;
1551 }
caryclark55888e42016-07-18 10:01:36 -07001552 // bool added = false;
1553 // fTop = outer;
1554 // fHead = nullptr;
caryclark26ad22a2015-10-16 09:03:38 -07001555 do {
1556 // addifmissing can modify the list that this is walking
1557 // save head so that walker can iterate over old data unperturbed
1558 // addifmissing adds to head freely then add saved head in the end
caryclark55888e42016-07-18 10:01:36 -07001559 const SkOpSegment* outerCoin = outer->coinPtTStart()->segment();
1560 const SkOpSegment* outerOpp = outer->oppPtTStart()->segment();
1561 if (outerCoin->done() || outerOpp->done()) {
1562 continue;
1563 }
caryclark26ad22a2015-10-16 09:03:38 -07001564 const SkCoincidentSpans* inner = outer;
caryclark55888e42016-07-18 10:01:36 -07001565 while ((inner = inner->next())) {
1566 this->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001567 double overS, overE;
caryclark55888e42016-07-18 10:01:36 -07001568 const SkOpSegment* innerCoin = inner->coinPtTStart()->segment();
1569 const SkOpSegment* innerOpp = inner->oppPtTStart()->segment();
1570 if (innerCoin->done() || innerOpp->done()) {
1571 continue;
1572 }
1573 if (outerCoin == innerCoin) {
1574 if (outerOpp != innerOpp
1575 && this->overlap(outer->coinPtTStart(), outer->coinPtTEnd(),
1576 inner->coinPtTStart(), inner->coinPtTEnd(), &overS, &overE)) {
1577 this->debugAddIfMissing(outer->coinPtTStart(), outer->coinPtTEnd(),
1578 inner->coinPtTStart(), inner->coinPtTEnd(), overS, overE,
1579 outer->oppPtTStart(), outer->oppPtTEnd(),
1580 inner->oppPtTStart(), inner->oppPtTEnd(), id, log);
caryclark26ad22a2015-10-16 09:03:38 -07001581 }
caryclark55888e42016-07-18 10:01:36 -07001582 } else if (outerCoin == innerOpp) {
1583 if (outerOpp != innerCoin
1584 && this->overlap(outer->coinPtTStart(), outer->coinPtTEnd(),
1585 inner->oppPtTStart(), inner->oppPtTEnd(), &overS, &overE)) {
1586 this->debugAddIfMissing(outer->coinPtTStart(), outer->coinPtTEnd(),
1587 inner->oppPtTStart(), inner->oppPtTEnd(), overS, overE,
1588 outer->oppPtTStart(), outer->oppPtTEnd(),
1589 inner->coinPtTStart(), inner->coinPtTEnd(), id, log);
caryclark26ad22a2015-10-16 09:03:38 -07001590 }
caryclark55888e42016-07-18 10:01:36 -07001591 } else if (outerOpp == innerCoin) {
1592 SkASSERT(outerCoin != innerOpp);
1593 if (this->overlap(outer->oppPtTStart(), outer->oppPtTEnd(),
1594 inner->coinPtTStart(), inner->coinPtTEnd(), &overS, &overE)) {
1595 this->debugAddIfMissing(outer->oppPtTStart(), outer->oppPtTEnd(),
1596 inner->coinPtTStart(), inner->coinPtTEnd(), overS, overE,
1597 outer->coinPtTStart(), outer->coinPtTEnd(),
1598 inner->oppPtTStart(), inner->oppPtTEnd(), id, log);
caryclark26ad22a2015-10-16 09:03:38 -07001599 }
caryclark55888e42016-07-18 10:01:36 -07001600 } else if (outerOpp == innerOpp) {
1601 SkASSERT(outerCoin != innerCoin);
1602 if (this->overlap(outer->oppPtTStart(), outer->oppPtTEnd(),
1603 inner->oppPtTStart(), inner->oppPtTEnd(), &overS, &overE)) {
1604 this->debugAddIfMissing(outer->oppPtTStart(), outer->oppPtTEnd(),
1605 inner->oppPtTStart(), inner->oppPtTEnd(), overS, overE,
1606 outer->coinPtTStart(), outer->coinPtTEnd(),
1607 inner->coinPtTStart(), inner->coinPtTEnd(), id, log);
caryclark26ad22a2015-10-16 09:03:38 -07001608 }
1609 }
caryclark55888e42016-07-18 10:01:36 -07001610 this->debugValidate();
caryclark26ad22a2015-10-16 09:03:38 -07001611 }
caryclark55888e42016-07-18 10:01:36 -07001612 } while ((outer = outer->next()));
1613 // this->restoreHead();
1614 return;
caryclark26ad22a2015-10-16 09:03:38 -07001615}
1616
caryclark55888e42016-07-18 10:01:36 -07001617// Commented-out lines keep this in sync with release()
1618void SkOpCoincidence::debugRelease(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpSegment* deleted) const {
1619 const SkCoincidentSpans* coin = fHead;
1620 if (!coin) {
1621 return;
1622 }
1623 do {
1624 if (coin->coinPtTStart()->segment() == deleted
1625 || coin->coinPtTEnd()->segment() == deleted
1626 || coin->oppPtTStart()->segment() == deleted
1627 || coin->oppPtTEnd()->segment() == deleted) {
1628 log->record(kReleasedSpan_Glitch, id, coin);
1629 }
1630 } while ((coin = coin->next()));
1631}
1632
1633// Commented-out lines keep this in sync with reorder()
1634// iterate through all coincident pairs, looking for ranges greater than 1
1635// if found, see if the opposite pair can match it -- which may require
1636// reordering the ptT pairs
1637void SkOpCoincidence::debugReorder(const char* id, SkPathOpsDebug::GlitchLog* log) const {
1638 const SkCoincidentSpans* coin = fHead;
1639 if (!coin) {
1640 return;
1641 }
1642 do {
1643 // most commonly, concidence are one span long; check for that first
1644 int intervals = coin->spanCount();
1645 if (intervals = 1) {
1646#if DEBUG_COINCIDENCE_VERBOSE
1647 // SkASSERT(!coin->debugExpand(nullptr, nullptr));
1648#endif
1649 continue;
1650 }
1651 coin->debugExpand(id, log);
1652 if (coin->spanCount() <= 0) {
1653 return;
1654 }
1655 // check to see if every span in coin has a mate in opp
1656 const SkOpSpan* start = coin->coinPtTStart()->span()->upCast();
1657 bool flipped = coin->flipped();
1658 const SkOpSpanBase* oppStartBase = coin->oppPtTStart()->span();
1659 const SkOpSpan* oppStart = flipped ? oppStartBase->prev() : oppStartBase->upCast();
1660 SkDebugf("", start, oppStart);
1661 } while ((coin = coin->next()));
1662 return;
1663}
1664
1665// Commented-out lines keep this in sync with expand()
1666// expand the range by checking adjacent spans for coincidence
caryclark26ad22a2015-10-16 09:03:38 -07001667bool SkOpCoincidence::debugExpand(const char* id, SkPathOpsDebug::GlitchLog* log) const {
1668 const SkCoincidentSpans* coin = fHead;
1669 if (!coin) {
1670 return false;
1671 }
1672 bool expanded = false;
1673 do {
caryclark55888e42016-07-18 10:01:36 -07001674 if (coin->debugExpand(id, log)) {
1675 // check to see if multiple spans expanded so they are now identical
1676 const SkCoincidentSpans* test = fHead;
1677 do {
1678 if (coin == test) {
1679 continue;
1680 }
1681 if (coin->coinPtTStart() == test->coinPtTStart()
1682 && coin->oppPtTStart() == test->oppPtTStart()) {
1683 if (log) log->record(kExpandCoin_Glitch, id, fHead, test->coinPtTStart());
1684 break;
1685 }
1686 } while ((test = test->next()));
1687 expanded = true;
caryclark26ad22a2015-10-16 09:03:38 -07001688 }
caryclark55888e42016-07-18 10:01:36 -07001689 } while ((coin = coin->next()));
caryclark26ad22a2015-10-16 09:03:38 -07001690 return expanded;
1691}
1692
caryclark55888e42016-07-18 10:01:36 -07001693// Commented-out lines keep this in sync with removeCollapsed()
1694void SkOpCoincidence::debugRemoveCollapsed(const char* id, SkPathOpsDebug::GlitchLog* log) const {
caryclark26ad22a2015-10-16 09:03:38 -07001695 const SkCoincidentSpans* coin = fHead;
1696 if (!coin) {
1697 return;
1698 }
caryclark55888e42016-07-18 10:01:36 -07001699 // SkCoincidentSpans** priorPtr = &fHead;
caryclark26ad22a2015-10-16 09:03:38 -07001700 do {
caryclark55888e42016-07-18 10:01:36 -07001701 if (coin->coinPtTStart() == coin->coinPtTEnd()) {
1702 return;
caryclark26ad22a2015-10-16 09:03:38 -07001703 }
caryclark55888e42016-07-18 10:01:36 -07001704 if (coin->oppPtTStart() == coin->oppPtTEnd()) {
1705 return;
caryclark26ad22a2015-10-16 09:03:38 -07001706 }
caryclark55888e42016-07-18 10:01:36 -07001707 if (coin->coinPtTStart()->collapsed(coin->coinPtTEnd())) {
1708 log->record(kCollapsedCoin_Glitch, id, coin);
1709// continue;
caryclark26ad22a2015-10-16 09:03:38 -07001710 }
caryclark55888e42016-07-18 10:01:36 -07001711 if (coin->oppPtTStart()->collapsed(coin->oppPtTEnd())) {
1712 log->record(kCollapsedCoin_Glitch, id, coin, coin);
1713// continue;
caryclark26ad22a2015-10-16 09:03:38 -07001714 }
caryclark55888e42016-07-18 10:01:36 -07001715 // priorPtr = &coin->nextPtr();
1716 } while ((coin = coin->next()));
1717 return;
caryclark26ad22a2015-10-16 09:03:38 -07001718}
1719
caryclark55888e42016-07-18 10:01:36 -07001720// Commented-out lines keep this in sync with mark()
1721/* this sets up the coincidence links in the segments when the coincidence crosses multiple spans */
caryclark26ad22a2015-10-16 09:03:38 -07001722void SkOpCoincidence::debugMark(const char* id, SkPathOpsDebug::GlitchLog* log) const {
1723 const SkCoincidentSpans* coin = fHead;
1724 if (!coin) {
1725 return;
1726 }
1727 do {
caryclark55888e42016-07-18 10:01:36 -07001728 const SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast();
1729// SkASSERT(start->deleted());
1730 const SkOpSpanBase* end = coin->coinPtTEndWritable()->span();
1731// SkASSERT(end->deleted());
1732 const SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span();
1733// SkASSERT(oStart->deleted());
1734 const SkOpSpanBase* oEnd = coin->oppPtTEndWritable()->span();
1735// SkASSERT(oEnd->deleted());
1736 bool flipped = coin->flipped();
caryclark26ad22a2015-10-16 09:03:38 -07001737 if (flipped) {
1738 SkTSwap(oStart, oEnd);
1739 }
caryclark55888e42016-07-18 10:01:36 -07001740 /* coin and opp spans may not match up. Mark the ends, and then let the interior
1741 get marked as many times as the spans allow */
1742 start->debugInsertCoincidence(id, log, oStart->upCast());
1743 end->debugInsertCoinEnd(id, log, oEnd);
1744 const SkOpSegment* segment = start->segment();
1745 const SkOpSegment* oSegment = oStart->segment();
caryclark26ad22a2015-10-16 09:03:38 -07001746 const SkOpSpanBase* next = start;
1747 const SkOpSpanBase* oNext = oStart;
caryclark55888e42016-07-18 10:01:36 -07001748 while ((next = next->upCast()->next()) != end) {
1749 if (next->upCast()->debugInsertCoincidence(id, log, oSegment, flipped), false) {
1750 return;
caryclark26ad22a2015-10-16 09:03:38 -07001751 }
caryclark55888e42016-07-18 10:01:36 -07001752 }
1753 while ((oNext = oNext->upCast()->next()) != oEnd) {
1754 if (oNext->upCast()->debugInsertCoincidence(id, log, segment, flipped), false) {
1755 return;
caryclark26ad22a2015-10-16 09:03:38 -07001756 }
caryclark55888e42016-07-18 10:01:36 -07001757 }
1758 } while ((coin = coin->next()));
1759 return;
caryclark26ad22a2015-10-16 09:03:38 -07001760}
1761#endif
1762
caryclark55888e42016-07-18 10:01:36 -07001763#if DEBUG_COINCIDENCE_VERBOSE
1764// Commented-out lines keep this in sync with markCollapsed()
1765void SkOpCoincidence::debugMarkCollapsed(const char* id, SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkOpPtT* test) const {
1766 while (coin) {
1767 if (coin->collapsed(test)) {
1768 if (zero_or_one(coin->coinPtTStart()->fT) && zero_or_one(coin->coinPtTEnd()->fT)) {
1769 log->record(kCollapsedCoin_Glitch, id, coin);
1770 }
1771 if (zero_or_one(coin->oppPtTStart()->fT) && zero_or_one(coin->oppPtTEnd()->fT)) {
1772 log->record(kCollapsedCoin_Glitch, id, coin);
1773 }
1774 }
1775 coin = coin->next();
caryclark624637c2015-05-11 07:21:27 -07001776 }
1777}
1778
caryclark55888e42016-07-18 10:01:36 -07001779// Commented-out lines keep this in sync with markCollapsed()
1780void SkOpCoincidence::debugMarkCollapsed(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpPtT* test) const {
1781 this->debugMarkCollapsed(id, log, fHead, test);
1782 this->debugMarkCollapsed(id, log, fTop, test);
1783}
1784#endif
1785
1786void SkCoincidentSpans::debugShow() const {
1787 SkDebugf("%s - id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__,
1788 coinPtTStart()->segment()->debugID(),
1789 coinPtTStart()->fT, coinPtTEnd()->fT);
1790 SkDebugf("%s + id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__,
1791 oppPtTStart()->segment()->debugID(),
1792 oppPtTStart()->fT, oppPtTEnd()->fT);
1793}
1794
1795void SkOpCoincidence::debugShowCoincidence() const {
caryclark26ad22a2015-10-16 09:03:38 -07001796#if DEBUG_COINCIDENCE
caryclark55888e42016-07-18 10:01:36 -07001797 const SkCoincidentSpans* span = fHead;
1798 while (span) {
1799 span->debugShow();
1800 span = span->next();
1801 }
1802#endif
1803}
1804
1805#if DEBUG_COINCIDENCE
1806static void DebugValidate(const SkOpSpanBase* next, const SkOpSpanBase* end,
1807 double oStart, double oEnd, const SkOpSegment* oSegment,
1808 const char* id, SkPathOpsDebug::GlitchLog* log) {
1809 SkASSERT(next != end);
1810 SkASSERT(!next->contains(end) || log);
1811 if (next->t() > end->t()) {
1812 SkTSwap(next, end);
1813 }
1814 do {
1815 const SkOpPtT* ptT = next->ptT();
1816 int index = 0;
1817 bool somethingBetween;
1818 do {
1819 ++index;
1820 ptT = ptT->next();
1821 const SkOpPtT* checkPtT = next->ptT();
1822 if (ptT == checkPtT) {
1823 break;
1824 }
1825 bool looped = false;
1826 for (int check = 0; check < index; ++check) {
1827 if ((looped = checkPtT == ptT)) {
1828 break;
1829 }
1830 checkPtT = checkPtT->next();
1831 }
1832 if (looped) {
1833 SkASSERT(0);
1834 break;
1835 }
1836 if (ptT->deleted()) {
1837 continue;
1838 }
1839 if (ptT->segment() != oSegment) {
1840 continue;
1841 }
1842 somethingBetween |= between(oStart, ptT->fT, oEnd);
1843 } while (true);
1844 SkASSERT(somethingBetween);
1845 } while (next != end && (next = next->upCast()->next()));
1846}
1847
1848static void DebugCheckOverlap(const SkCoincidentSpans* test, const SkCoincidentSpans* list,
1849 const char* id, SkPathOpsDebug::GlitchLog* log) {
1850 if (!list) {
1851 return;
1852 }
1853 const SkOpSegment* coinSeg = test->coinPtTStart()->segment();
1854 SkASSERT(coinSeg == test->coinPtTEnd()->segment());
1855 const SkOpSegment* oppSeg = test->oppPtTStart()->segment();
1856 SkASSERT(oppSeg == test->oppPtTEnd()->segment());
1857 SkASSERT(coinSeg != test->oppPtTStart()->segment());
1858 SkDEBUGCODE(double tcs = test->coinPtTStart()->fT);
1859 SkASSERT(between(0, tcs, 1));
1860 SkDEBUGCODE(double tce = test->coinPtTEnd()->fT);
1861 SkASSERT(between(0, tce, 1));
1862 SkASSERT(tcs < tce);
1863 double tos = test->oppPtTStart()->fT;
1864 SkASSERT(between(0, tos, 1));
1865 double toe = test->oppPtTEnd()->fT;
1866 SkASSERT(between(0, toe, 1));
1867 SkASSERT(tos != toe);
1868 if (tos > toe) {
1869 SkTSwap(tos, toe);
1870 }
1871 do {
1872 double lcs, lce, los, loe;
1873 if (coinSeg == list->coinPtTStart()->segment()) {
1874 if (oppSeg != list->oppPtTStart()->segment()) {
1875 continue;
1876 }
1877 lcs = list->coinPtTStart()->fT;
1878 lce = list->coinPtTEnd()->fT;
1879 los = list->oppPtTStart()->fT;
1880 loe = list->oppPtTEnd()->fT;
1881 if (los > loe) {
1882 SkTSwap(los, loe);
1883 }
1884 } else if (coinSeg == list->oppPtTStart()->segment()) {
1885 if (oppSeg != list->coinPtTStart()->segment()) {
1886 continue;
1887 }
1888 lcs = list->oppPtTStart()->fT;
1889 lce = list->oppPtTEnd()->fT;
1890 if (lcs > lce) {
1891 SkTSwap(lcs, lce);
1892 }
1893 los = list->coinPtTStart()->fT;
1894 loe = list->coinPtTEnd()->fT;
1895 } else {
1896 continue;
1897 }
1898 SkASSERT(tce < lcs || lce < tcs);
1899 SkASSERT(toe < los || loe < tos);
1900 } while ((list = list->next()));
1901}
1902
1903
1904static void DebugCheckOverlapTop(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
1905 const char* id, SkPathOpsDebug::GlitchLog* log) {
1906 // check for overlapping coincident spans
1907 const SkCoincidentSpans* test = head;
1908 while (test) {
1909 const SkCoincidentSpans* next = test->next();
1910 DebugCheckOverlap(test, next, id, log);
1911 DebugCheckOverlap(test, opt, id, log);
1912 test = next;
1913 }
1914}
1915
1916#if DEBUG_COINCIDENCE_VERBOSE
1917void SkOpCoincidence::debugCheckOverlap(const char* id, SkPathOpsDebug::GlitchLog* log) const {
1918 DebugCheckOverlapTop(fHead, fTop, id, log);
1919 DebugCheckOverlapTop(fTop, nullptr, id, log);
1920}
1921#endif
1922
1923static void DebugValidate(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
1924 const char* id, SkPathOpsDebug::GlitchLog* log) {
1925 // look for pts inside coincident spans that are not inside the opposite spans
1926 const SkCoincidentSpans* coin = head;
1927 while (coin) {
1928 SkASSERT(SkOpCoincidence::Ordered(coin->coinPtTStart()->segment(),
1929 coin->oppPtTStart()->segment()));
1930 SkASSERT(coin->coinPtTStart()->span()->ptT() == coin->coinPtTStart());
1931 SkASSERT(coin->coinPtTEnd()->span()->ptT() == coin->coinPtTEnd());
1932 SkASSERT(coin->oppPtTStart()->span()->ptT() == coin->oppPtTStart());
1933 SkASSERT(coin->oppPtTEnd()->span()->ptT() == coin->oppPtTEnd());
1934 DebugValidate(coin->coinPtTStart()->span(), coin->coinPtTEnd()->span(),
1935 coin->oppPtTStart()->fT, coin->oppPtTEnd()->fT, coin->oppPtTStart()->segment(),
1936 id, log);
1937 DebugValidate(coin->oppPtTStart()->span(), coin->oppPtTEnd()->span(),
1938 coin->coinPtTStart()->fT, coin->coinPtTEnd()->fT, coin->coinPtTStart()->segment(),
1939 id, log);
1940 coin = coin->next();
1941 }
1942 DebugCheckOverlapTop(head, opt, id, log);
1943}
1944#endif
1945
1946void SkOpCoincidence::debugValidate() const {
1947#if DEBUG_COINCIDENCE
1948 // if (fGlobalState->debugCheckHealth()) {
1949// return;
1950// }
1951 DebugValidate(fHead, fTop, nullptr, nullptr);
1952 DebugValidate(fTop, nullptr, nullptr, nullptr);
1953#endif
1954}
1955
1956#if DEBUG_COINCIDENCE_VERBOSE
1957void SkOpCoincidence::debugCheckValid(const char* id, SkPathOpsDebug::GlitchLog* log) const {
1958 DebugValidate(fHead, fTop, id, log);
1959 DebugValidate(fTop, nullptr, id, log);
1960}
1961#endif
1962
1963#if DEBUG_COINCIDENCE_VERBOSE
caryclark26ad22a2015-10-16 09:03:38 -07001964void SkOpContour::debugCheckHealth(const char* id, SkPathOpsDebug::GlitchLog* log) const {
1965 const SkOpSegment* segment = &fHead;
1966 do {
1967 segment->debugCheckHealth(id, log);
1968 } while ((segment = segment->next()));
1969}
1970
caryclark55888e42016-07-18 10:01:36 -07001971// commmented-out lines keep this aligned with missingCoincidence()
1972void SkOpContour::debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log) const {
1973// SkASSERT(fCount > 0);
caryclark26ad22a2015-10-16 09:03:38 -07001974 const SkOpSegment* segment = &fHead;
caryclark55888e42016-07-18 10:01:36 -07001975// bool result = false;
caryclark26ad22a2015-10-16 09:03:38 -07001976 do {
caryclark55888e42016-07-18 10:01:36 -07001977 if (fState->angleCoincidence()) {
1978// #if DEBUG_ANGLE
1979// segment->debugCheckAngleCoin();
1980// #endif
1981 } else if (segment->debugMissingCoincidence(id, log), false) {
1982// result = true;
1983// see FIXME in missingCoincidence()
1984//
1985//
1986//
1987 // continue;
1988 }
1989 segment = segment->next();
1990 } while (segment);
1991 return;
caryclark26ad22a2015-10-16 09:03:38 -07001992}
1993#endif
1994
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001995void SkOpSegment::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07001996#if DEBUG_COINCIDENCE
1997 if (this->globalState()->debugCheckHealth()) {
1998 return;
1999 }
2000#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002001#if DEBUG_VALIDATE
caryclark54359292015-03-26 07:52:43 -07002002 const SkOpSpanBase* span = &fHead;
2003 double lastT = -1;
halcanary96fcdcc2015-08-27 07:41:13 -07002004 const SkOpSpanBase* prev = nullptr;
caryclark54359292015-03-26 07:52:43 -07002005 int count = 0;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002006 int done = 0;
caryclark54359292015-03-26 07:52:43 -07002007 do {
2008 if (!span->final()) {
2009 ++count;
2010 done += span->upCast()->done() ? 1 : 0;
2011 }
2012 SkASSERT(span->segment() == this);
2013 SkASSERT(!prev || prev->upCast()->next() == span);
2014 SkASSERT(!prev || prev == span->prev());
2015 prev = span;
2016 double t = span->ptT()->fT;
2017 SkASSERT(lastT < t);
2018 lastT = t;
2019 span->debugValidate();
2020 } while (!span->final() && (span = span->upCast()->next()));
2021 SkASSERT(count == fCount);
2022 SkASSERT(done == fDoneCount);
caryclark08bc8482015-04-24 09:08:57 -07002023 SkASSERT(count >= fDoneCount);
caryclark54359292015-03-26 07:52:43 -07002024 SkASSERT(span->final());
2025 span->debugValidate();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002026#endif
caryclark54359292015-03-26 07:52:43 -07002027}
2028
caryclark55888e42016-07-18 10:01:36 -07002029#if DEBUG_COINCIDENCE_VERBOSE
2030// Commented-out lines keep this in sync with addOppAndMerge()
2031// If the added points envelop adjacent spans, merge them in.
2032void SkOpSpanBase::debugAddOppAndMerge(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp, bool* spanDeleted, bool* oppDeleted) const {
2033 if (this->ptT()->debugAddOpp(opp->ptT())) {
2034 this->debugCheckForCollapsedCoincidence(id, log);
caryclark26ad22a2015-10-16 09:03:38 -07002035 }
caryclark55888e42016-07-18 10:01:36 -07002036 // compute bounds of points in span
2037 SkPathOpsBounds bounds;
2038 bounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMin, SK_ScalarMin);
2039 const SkOpPtT* head = this->ptT();
2040 const SkOpPtT* nextPt = head;
2041 do {
2042 bounds.add(nextPt->fPt);
2043 } while ((nextPt = nextPt->next()) != head);
2044 if (!bounds.width() && !bounds.height()) {
2045 return;
caryclark26ad22a2015-10-16 09:03:38 -07002046 }
caryclark55888e42016-07-18 10:01:36 -07002047 this->debugMergeContained(id, log, bounds, spanDeleted);
2048 opp->debugMergeContained(id, log, bounds, oppDeleted);
caryclark26ad22a2015-10-16 09:03:38 -07002049}
2050
caryclark55888e42016-07-18 10:01:36 -07002051// Commented-out lines keep this in sync with checkForCollapsedCoincidence()
2052void SkOpSpanBase::debugCheckForCollapsedCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log) const {
2053 const SkOpCoincidence* coins = this->globalState()->coincidence();
2054 if (coins->isEmpty()) {
2055 return;
2056 }
2057// the insert above may have put both ends of a coincident run in the same span
2058// for each coincident ptT in loop; see if its opposite in is also in the loop
2059// this implementation is the motivation for marking that a ptT is referenced by a coincident span
2060 const SkOpPtT* head = this->ptT();
2061 const SkOpPtT* test = head;
caryclark26ad22a2015-10-16 09:03:38 -07002062 do {
caryclark55888e42016-07-18 10:01:36 -07002063 if (!test->coincident()) {
2064 continue;
caryclark26ad22a2015-10-16 09:03:38 -07002065 }
caryclark55888e42016-07-18 10:01:36 -07002066 coins->debugMarkCollapsed(id, log, test);
2067 } while ((test = test->next()) != head);
caryclark26ad22a2015-10-16 09:03:38 -07002068}
caryclark55888e42016-07-18 10:01:36 -07002069#endif
caryclark26ad22a2015-10-16 09:03:38 -07002070
caryclark54359292015-03-26 07:52:43 -07002071bool SkOpSpanBase::debugCoinEndLoopCheck() const {
2072 int loop = 0;
2073 const SkOpSpanBase* next = this;
2074 SkOpSpanBase* nextCoin;
2075 do {
2076 nextCoin = next->fCoinEnd;
2077 SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin);
2078 for (int check = 1; check < loop - 1; ++check) {
2079 const SkOpSpanBase* checkCoin = this->fCoinEnd;
2080 const SkOpSpanBase* innerCoin = checkCoin;
2081 for (int inner = check + 1; inner < loop; ++inner) {
2082 innerCoin = innerCoin->fCoinEnd;
2083 if (checkCoin == innerCoin) {
2084 SkDebugf("*** bad coincident end loop ***\n");
2085 return false;
2086 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002087 }
2088 }
caryclark54359292015-03-26 07:52:43 -07002089 ++loop;
2090 } while ((next = nextCoin) && next != this);
2091 return true;
2092}
2093
caryclark55888e42016-07-18 10:01:36 -07002094#if DEBUG_COINCIDENCE_VERBOSE
2095// Commented-out lines keep this in sync with insertCoinEnd()
2096void SkOpSpanBase::debugInsertCoinEnd(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* coin) const {
2097 if (containsCoinEnd(coin)) {
2098// SkASSERT(coin->containsCoinEnd(this));
2099 return;
2100 }
2101 debugValidate();
2102// SkASSERT(this != coin);
2103 log->record(kMarkCoinEnd_Glitch, id, this, coin);
2104// coin->fCoinEnd = this->fCoinEnd;
2105// this->fCoinEnd = coinNext;
2106 debugValidate();
2107}
2108
2109// Commented-out lines keep this in sync with mergeContained()
2110void SkOpSpanBase::debugMergeContained(const char* id, SkPathOpsDebug::GlitchLog* log, const SkPathOpsBounds& bounds, bool* deleted) const {
2111 // while adjacent spans' points are contained by the bounds, merge them
2112 const SkOpSpanBase* prev = this;
2113 const SkOpSegment* seg = this->segment();
2114 while ((prev = prev->prev()) && bounds.contains(prev->pt()) && !seg->ptsDisjoint(prev, this)) {
2115 if (prev->prev()) {
2116 log->record(kMergeContained_Glitch, id, this, prev);
2117 } else if (this->final()) {
2118 log->record(kMergeContained_Glitch, id, this, prev);
2119 // return;
2120 } else {
2121 log->record(kMergeContained_Glitch, id, prev, this);
caryclark26ad22a2015-10-16 09:03:38 -07002122 }
2123 }
caryclark55888e42016-07-18 10:01:36 -07002124 const SkOpSpanBase* current = this;
2125 const SkOpSpanBase* next = this;
2126 while (next->upCastable() && (next = next->upCast()->next())
2127 && bounds.contains(next->pt()) && !seg->ptsDisjoint(this, next)) {
2128 if (!current->prev() && next->final()) {
2129 log->record(kMergeContained_Glitch, id, next, current);
2130 current = next;
2131 }
2132 if (current->prev()) {
2133 log->record(kMergeContained_Glitch, id, next, current);
2134 current = next;
2135 } else {
2136 log->record(kMergeContained_Glitch, id, next, current);
2137 current = next;
2138 }
2139 }
2140#if DEBUG_COINCIDENCE
2141 // this->globalState()->coincidence()->debugValidate();
2142#endif
caryclark26ad22a2015-10-16 09:03:38 -07002143}
caryclark55888e42016-07-18 10:01:36 -07002144#endif
caryclark26ad22a2015-10-16 09:03:38 -07002145
2146const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const {
2147 const SkOpSpanBase* end = *endPtr;
2148 SkASSERT(this->segment() == end->segment());
2149 const SkOpSpanBase* result;
2150 if (t() < end->t()) {
2151 result = this;
2152 } else {
2153 result = end;
2154 *endPtr = this;
2155 }
2156 return result->upCast();
2157}
2158
caryclark54359292015-03-26 07:52:43 -07002159void SkOpSpanBase::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07002160#if DEBUG_COINCIDENCE
2161 if (this->globalState()->debugCheckHealth()) {
2162 return;
2163 }
2164#endif
caryclark54359292015-03-26 07:52:43 -07002165#if DEBUG_VALIDATE
2166 const SkOpPtT* ptT = &fPtT;
2167 SkASSERT(ptT->span() == this);
2168 do {
2169// SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt));
2170 ptT->debugValidate();
2171 ptT = ptT->next();
2172 } while (ptT != &fPtT);
2173 SkASSERT(this->debugCoinEndLoopCheck());
2174 if (!this->final()) {
2175 SkASSERT(this->upCast()->debugCoinLoopCheck());
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002176 }
caryclark54359292015-03-26 07:52:43 -07002177 if (fFromAngle) {
2178 fFromAngle->debugValidate();
2179 }
2180 if (!this->final() && this->upCast()->toAngle()) {
2181 this->upCast()->toAngle()->debugValidate();
2182 }
2183#endif
2184}
2185
2186bool SkOpSpan::debugCoinLoopCheck() const {
2187 int loop = 0;
2188 const SkOpSpan* next = this;
2189 SkOpSpan* nextCoin;
2190 do {
2191 nextCoin = next->fCoincident;
2192 SkASSERT(nextCoin == this || nextCoin->fCoincident != nextCoin);
2193 for (int check = 1; check < loop - 1; ++check) {
2194 const SkOpSpan* checkCoin = this->fCoincident;
2195 const SkOpSpan* innerCoin = checkCoin;
2196 for (int inner = check + 1; inner < loop; ++inner) {
2197 innerCoin = innerCoin->fCoincident;
2198 if (checkCoin == innerCoin) {
2199 SkDebugf("*** bad coincident loop ***\n");
2200 return false;
2201 }
2202 }
2203 }
2204 ++loop;
2205 } while ((next = nextCoin) && next != this);
2206 return true;
2207}
2208
caryclark55888e42016-07-18 10:01:36 -07002209#if DEBUG_COINCIDENCE_VERBOSE
2210// Commented-out lines keep this in sync with insertCoincidence() in header
2211void SkOpSpan::debugInsertCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpSpan* coin) const {
2212 if (containsCoincidence(coin)) {
2213// SkASSERT(coin->containsCoincidence(this));
2214 return;
2215 }
2216 debugValidate();
2217// SkASSERT(this != coin);
2218 log->record(kMarkCoinStart_Glitch, id, this, coin);
2219// coin->fCoincident = this->fCoincident;
2220// this->fCoincident = coinNext;
2221 debugValidate();
2222}
2223
2224// Commented-out lines keep this in sync with insertCoincidence()
2225void SkOpSpan::debugInsertCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpSegment* segment, bool flipped) const {
2226 if (this->containsCoincidence(segment)) {
2227 return;
2228 }
2229 const SkOpPtT* next = &fPtT;
2230 while ((next = next->next()) != &fPtT) {
2231 if (next->segment() == segment) {
2232 log->record(kMarkCoinInsert_Glitch, id, flipped ? next->span()->prev() : next->span());
2233 return;
2234 }
2235 }
2236#if DEBUG_COINCIDENCE
2237 log->record(kMarkCoinMissing_Glitch, id, segment, this);
2238#endif
2239}
2240#endif
2241
caryclark624637c2015-05-11 07:21:27 -07002242// called only by test code
2243int SkIntersections::debugCoincidentUsed() const {
2244 if (!fIsCoincident[0]) {
2245 SkASSERT(!fIsCoincident[1]);
2246 return 0;
2247 }
2248 int count = 0;
2249 SkDEBUGCODE(int count2 = 0;)
2250 for (int index = 0; index < fUsed; ++index) {
2251 if (fIsCoincident[0] & (1 << index)) {
2252 ++count;
2253 }
2254#ifdef SK_DEBUG
2255 if (fIsCoincident[1] & (1 << index)) {
2256 ++count2;
2257 }
2258#endif
2259 }
2260 SkASSERT(count == count2);
2261 return count;
2262}
2263
caryclark54359292015-03-26 07:52:43 -07002264#include "SkOpContour.h"
2265
caryclark55888e42016-07-18 10:01:36 -07002266// Commented-out lines keep this in sync with addOpp()
2267bool SkOpPtT::debugAddOpp(const SkOpPtT* opp) const {
2268 // find the fOpp ptr to opp
2269 const SkOpPtT* oppPrev = opp->fNext;
2270 if (oppPrev == this) {
2271 return false;
2272 }
2273 while (oppPrev->fNext != opp) {
2274 oppPrev = oppPrev->fNext;
2275 if (oppPrev == this) {
2276 return false;
2277 }
2278 }
2279// const SkOpPtT* oldNext = this->fNext;
2280 SkASSERT(this != opp);
2281// this->fNext = opp;
2282// SkASSERT(oppPrev != oldNext);
2283// oppPrev->fNext = oldNext;
2284 return true;
2285}
2286
caryclark26ad22a2015-10-16 09:03:38 -07002287bool SkOpPtT::debugContains(const SkOpPtT* check) const {
2288 SkASSERT(this != check);
2289 const SkOpPtT* ptT = this;
2290 int links = 0;
2291 do {
2292 ptT = ptT->next();
2293 if (ptT == check) {
2294 return true;
2295 }
2296 ++links;
2297 const SkOpPtT* test = this;
2298 for (int index = 0; index < links; ++index) {
2299 if (ptT == test) {
2300 return false;
2301 }
2302 test = test->next();
2303 }
2304 } while (true);
2305}
2306
2307const SkOpPtT* SkOpPtT::debugContains(const SkOpSegment* check) const {
2308 SkASSERT(this->segment() != check);
2309 const SkOpPtT* ptT = this;
2310 int links = 0;
2311 do {
2312 ptT = ptT->next();
2313 if (ptT->segment() == check) {
2314 return ptT;
2315 }
2316 ++links;
2317 const SkOpPtT* test = this;
2318 for (int index = 0; index < links; ++index) {
2319 if (ptT == test) {
2320 return nullptr;
2321 }
2322 test = test->next();
2323 }
2324 } while (true);
2325}
2326
caryclark54359292015-03-26 07:52:43 -07002327int SkOpPtT::debugLoopLimit(bool report) const {
2328 int loop = 0;
2329 const SkOpPtT* next = this;
2330 do {
2331 for (int check = 1; check < loop - 1; ++check) {
2332 const SkOpPtT* checkPtT = this->fNext;
2333 const SkOpPtT* innerPtT = checkPtT;
2334 for (int inner = check + 1; inner < loop; ++inner) {
2335 innerPtT = innerPtT->fNext;
2336 if (checkPtT == innerPtT) {
2337 if (report) {
2338 SkDebugf("*** bad ptT loop ***\n");
2339 }
2340 return loop;
2341 }
2342 }
2343 }
caryclark26ad22a2015-10-16 09:03:38 -07002344 // there's nothing wrong with extremely large loop counts -- but this may appear to hang
2345 // by taking a very long time to figure out that no loop entry is a duplicate
2346 // -- and it's likely that a large loop count is indicative of a bug somewhere
2347 if (++loop > 1000) {
2348 SkDebugf("*** loop count exceeds 1000 ***\n");
2349 return 1000;
2350 }
caryclark54359292015-03-26 07:52:43 -07002351 } while ((next = next->fNext) && next != this);
2352 return 0;
2353}
2354
2355void SkOpPtT::debugValidate() const {
caryclark55888e42016-07-18 10:01:36 -07002356#if DEBUG_COINCIDENCE
2357 if (this->globalState()->debugCheckHealth()) {
2358 return;
2359 }
2360#endif
caryclark54359292015-03-26 07:52:43 -07002361#if DEBUG_VALIDATE
caryclark4e1a4c92015-05-18 12:56:57 -07002362 SkOpGlobalState::Phase phase = contour()->globalState()->phase();
2363 if (phase == SkOpGlobalState::kIntersecting
2364 || phase == SkOpGlobalState::kFixWinding) {
caryclark54359292015-03-26 07:52:43 -07002365 return;
2366 }
2367 SkASSERT(fNext);
2368 SkASSERT(fNext != this);
2369 SkASSERT(fNext->fNext);
2370 SkASSERT(debugLoopLimit(false) == 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002371#endif
2372}
caryclark1049f122015-04-20 08:31:59 -07002373
2374static void output_scalar(SkScalar num) {
2375 if (num == (int) num) {
2376 SkDebugf("%d", (int) num);
2377 } else {
2378 SkString str;
2379 str.printf("%1.9g", num);
2380 int width = (int) str.size();
2381 const char* cStr = str.c_str();
2382 while (cStr[width - 1] == '0') {
2383 --width;
2384 }
2385 str.resize(width);
2386 SkDebugf("%sf", str.c_str());
2387 }
2388}
2389
2390static void output_points(const SkPoint* pts, int count) {
2391 for (int index = 0; index < count; ++index) {
2392 output_scalar(pts[index].fX);
2393 SkDebugf(", ");
2394 output_scalar(pts[index].fY);
2395 if (index + 1 < count) {
2396 SkDebugf(", ");
2397 }
2398 }
2399}
2400
2401static void showPathContours(SkPath::RawIter& iter, const char* pathName) {
2402 uint8_t verb;
2403 SkPoint pts[4];
2404 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
2405 switch (verb) {
2406 case SkPath::kMove_Verb:
2407 SkDebugf(" %s.moveTo(", pathName);
2408 output_points(&pts[0], 1);
2409 SkDebugf(");\n");
2410 continue;
2411 case SkPath::kLine_Verb:
2412 SkDebugf(" %s.lineTo(", pathName);
2413 output_points(&pts[1], 1);
2414 SkDebugf(");\n");
2415 break;
2416 case SkPath::kQuad_Verb:
2417 SkDebugf(" %s.quadTo(", pathName);
2418 output_points(&pts[1], 2);
2419 SkDebugf(");\n");
2420 break;
2421 case SkPath::kConic_Verb:
2422 SkDebugf(" %s.conicTo(", pathName);
2423 output_points(&pts[1], 2);
2424 SkDebugf(", %1.9gf);\n", iter.conicWeight());
2425 break;
2426 case SkPath::kCubic_Verb:
2427 SkDebugf(" %s.cubicTo(", pathName);
2428 output_points(&pts[1], 3);
2429 SkDebugf(");\n");
2430 break;
2431 case SkPath::kClose_Verb:
2432 SkDebugf(" %s.close();\n", pathName);
2433 break;
2434 default:
2435 SkDEBUGFAIL("bad verb");
2436 return;
2437 }
2438 }
2439}
2440
2441static const char* gFillTypeStr[] = {
2442 "kWinding_FillType",
2443 "kEvenOdd_FillType",
2444 "kInverseWinding_FillType",
2445 "kInverseEvenOdd_FillType"
2446};
2447
2448void SkPathOpsDebug::ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration) {
2449 SkPath::RawIter iter(path);
2450#define SUPPORT_RECT_CONTOUR_DETECTION 0
2451#if SUPPORT_RECT_CONTOUR_DETECTION
halcanary96fcdcc2015-08-27 07:41:13 -07002452 int rectCount = path.isRectContours() ? path.rectContours(nullptr, nullptr) : 0;
caryclark1049f122015-04-20 08:31:59 -07002453 if (rectCount > 0) {
2454 SkTDArray<SkRect> rects;
2455 SkTDArray<SkPath::Direction> directions;
2456 rects.setCount(rectCount);
2457 directions.setCount(rectCount);
2458 path.rectContours(rects.begin(), directions.begin());
2459 for (int contour = 0; contour < rectCount; ++contour) {
2460 const SkRect& rect = rects[contour];
2461 SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
2462 rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
2463 ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
2464 }
2465 return;
2466 }
2467#endif
2468 SkPath::FillType fillType = path.getFillType();
2469 SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType);
2470 if (includeDeclaration) {
2471 SkDebugf(" SkPath %s;\n", name);
2472 }
2473 SkDebugf(" %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[fillType]);
2474 iter.setPath(path);
2475 showPathContours(iter, name);
2476}