Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(462)

Side by Side Diff: src/pathops/SkPathOpsDebug.cpp

Issue 1394503003: fix some pathops bugs found in 1M skps (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: init to avoid warning Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/pathops/SkPathOpsDebug.h ('k') | src/pathops/SkPathOpsOp.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2013 Google Inc. 2 * Copyright 2013 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkMutex.h" 8 #include "SkMutex.h"
9 #include "SkOpCoincidence.h"
10 #include "SkOpContour.h"
9 #include "SkPath.h" 11 #include "SkPath.h"
10 #include "SkPathOpsDebug.h" 12 #include "SkPathOpsDebug.h"
11 #include "SkString.h" 13 #include "SkString.h"
12 14
15 struct SkCoincidentSpans;
16
13 #if DEBUG_VALIDATE 17 #if DEBUG_VALIDATE
14 extern bool FLAGS_runFail; 18 extern bool FLAGS_runFail;
15 #endif 19 #endif
16 20
17 #if DEBUG_SORT 21 #if DEBUG_SORT
18 int SkPathOpsDebug::gSortCountDefault = SK_MaxS32; 22 int SkPathOpsDebug::gSortCountDefault = SK_MaxS32;
19 int SkPathOpsDebug::gSortCount; 23 int SkPathOpsDebug::gSortCount;
20 #endif 24 #endif
21 25
22 #if DEBUG_ACTIVE_OP 26 #if DEBUG_ACTIVE_OP
23 const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor"}; 27 const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor"};
24 #endif 28 #endif
25 29
26 #if defined SK_DEBUG || !FORCE_RELEASE 30 #if defined SK_DEBUG || !FORCE_RELEASE
27 31
28 const char* SkPathOpsDebug::kLVerbStr[] = {"", "line", "quad", "cubic"}; 32 const char* SkPathOpsDebug::kLVerbStr[] = {"", "line", "quad", "cubic"};
29 33
30 #if defined(SK_DEBUG) || !FORCE_RELEASE
31 int SkPathOpsDebug::gContourID = 0; 34 int SkPathOpsDebug::gContourID = 0;
32 int SkPathOpsDebug::gSegmentID = 0; 35 int SkPathOpsDebug::gSegmentID = 0;
33 #endif
34 36
35 bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray, 37 bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray,
36 const SkOpSpanBase* span) { 38 const SkOpSpanBase* span) {
37 for (int index = 0; index < chaseArray.count(); ++index) { 39 for (int index = 0; index < chaseArray.count(); ++index) {
38 const SkOpSpanBase* entry = chaseArray[index]; 40 const SkOpSpanBase* entry = chaseArray[index];
39 if (entry == span) { 41 if (entry == span) {
40 return true; 42 return true;
41 } 43 }
42 } 44 }
43 return false; 45 return false;
44 } 46 }
47 #endif
45 48
49 #if DEBUG_COINCIDENCE
50 enum GlitchType {
51 kAddCorruptCoin_Glitch,
52 kAddExpandedCoin_Glitch,
53 kAddMissingCoin_Glitch,
54 kCollapsedCoin_Glitch,
55 kCollapsedDone_Glitch,
56 kCollapsedOppValue_Glitch,
57 kCollapsedSpan_Glitch,
58 kCollapsedWindValue_Glitch,
59 kDeletedCoin_Glitch,
60 kExpandCoin_Glitch,
61 kMarkCoinEnd_Glitch,
62 kMarkCoinInsert_Glitch,
63 kMissingCoin_Glitch,
64 kMissingDone_Glitch,
65 kMissingIntersection_Glitch,
66 kMoveMultiple_Glitch,
67 kUnaligned_Glitch,
68 kUnalignedHead_Glitch,
69 kUnalignedTail_Glitch,
70 kUndetachedSpan_Glitch,
71 kUnmergedSpan_Glitch,
72 };
73
74 static const int kGlitchType_Count = kUnmergedSpan_Glitch + 1;
75
76 struct SpanGlitch {
77 const char* fStage;
78 const SkOpSpanBase* fBase;
79 const SkOpSpanBase* fSuspect;
80 const SkCoincidentSpans* fCoin;
81 const SkOpSegment* fSegment;
82 const SkOpPtT* fCoinSpan;
83 const SkOpPtT* fEndSpan;
84 const SkOpPtT* fOppSpan;
85 const SkOpPtT* fOppEndSpan;
86 double fT;
87 SkPoint fPt;
88 GlitchType fType;
89 };
90
91 struct SkPathOpsDebug::GlitchLog {
92 SpanGlitch* recordCommon(GlitchType type, const char* stage) {
93 SpanGlitch* glitch = fGlitches.push();
94 glitch->fStage = stage;
95 glitch->fBase = nullptr;
96 glitch->fSuspect = nullptr;
97 glitch->fCoin = nullptr;
98 glitch->fSegment = nullptr;
99 glitch->fCoinSpan = nullptr;
100 glitch->fEndSpan = nullptr;
101 glitch->fOppSpan = nullptr;
102 glitch->fOppEndSpan = nullptr;
103 glitch->fT = SK_ScalarNaN;
104 glitch->fPt = { SK_ScalarNaN, SK_ScalarNaN };
105 glitch->fType = type;
106 return glitch;
107 }
108
109 void record(GlitchType type, const char* stage, const SkOpSpanBase* base,
110 const SkOpSpanBase* suspect = NULL) {
111 SpanGlitch* glitch = recordCommon(type, stage);
112 glitch->fBase = base;
113 glitch->fSuspect = suspect;
114 }
115
116 void record(GlitchType type, const char* stage, const SkCoincidentSpans* coi n,
117 const SkOpPtT* coinSpan) {
118 SpanGlitch* glitch = recordCommon(type, stage);
119 glitch->fCoin = coin;
120 glitch->fCoinSpan = coinSpan;
121 }
122
123 void record(GlitchType type, const char* stage, const SkOpSpanBase* base,
124 const SkOpSegment* seg, double t, SkPoint pt) {
125 SpanGlitch* glitch = recordCommon(type, stage);
126 glitch->fBase = base;
127 glitch->fSegment = seg;
128 glitch->fT = t;
129 glitch->fPt = pt;
130 }
131
132 void record(GlitchType type, const char* stage, const SkOpSpanBase* base, do uble t,
133 SkPoint pt) {
134 SpanGlitch* glitch = recordCommon(type, stage);
135 glitch->fBase = base;
136 glitch->fT = t;
137 glitch->fPt = pt;
138 }
139
140 void record(GlitchType type, const char* stage, const SkCoincidentSpans* coi n,
141 const SkOpPtT* coinSpan, const SkOpPtT* endSpan) {
142 SpanGlitch* glitch = recordCommon(type, stage);
143 glitch->fCoin = coin;
144 glitch->fCoinSpan = coinSpan;
145 glitch->fEndSpan = endSpan;
146 }
147
148 void record(GlitchType type, const char* stage, const SkCoincidentSpans* coi n,
149 const SkOpSpanBase* suspect) {
150 SpanGlitch* glitch = recordCommon(type, stage);
151 glitch->fSuspect = suspect;
152 glitch->fCoin = coin;
153 }
154
155 void record(GlitchType type, const char* stage, const SkOpPtT* ptTS, const S kOpPtT* ptTE,
156 const SkOpPtT* oPtTS, const SkOpPtT* oPtTE) {
157 SpanGlitch* glitch = recordCommon(type, stage);
158 glitch->fCoinSpan = ptTS;
159 glitch->fEndSpan = ptTE;
160 glitch->fOppSpan = oPtTS;
161 glitch->fOppEndSpan = oPtTE;
162 }
163
164 SkTDArray<SpanGlitch> fGlitches;
165 };
166
167 void SkPathOpsDebug::CheckHealth(SkOpContourHead* contourList, const char* id) {
168 GlitchLog glitches;
169 const SkOpContour* contour = contourList;
170 const SkOpCoincidence* coincidence = contour->globalState()->coincidence();
171 do {
172 contour->debugCheckHealth(id, &glitches);
173 contour->debugMissingCoincidence(id, &glitches, coincidence);
174 } while ((contour = contour->next()));
175 coincidence->debugFixAligned(id, &glitches);
176 coincidence->debugAddMissing(id, &glitches);
177 coincidence->debugExpand(id, &glitches);
178 coincidence->debugAddExpanded(id, &glitches);
179 coincidence->debugMark(id, &glitches);
180 unsigned mask = 0;
181 for (int index = 0; index < glitches.fGlitches.count(); ++index) {
182 const SpanGlitch& glitch = glitches.fGlitches[index];
183 mask |= 1 << glitch.fType;
184 }
185 for (int index = 0; index < kGlitchType_Count; ++index) {
186 SkDebugf(mask & (1 << index) ? "x" : "-");
187 }
188 SkDebugf(" %s\n", id);
189 }
190 #endif
191
192 #if defined SK_DEBUG || !FORCE_RELEASE
46 void SkPathOpsDebug::MathematicaIze(char* str, size_t bufferLen) { 193 void SkPathOpsDebug::MathematicaIze(char* str, size_t bufferLen) {
47 size_t len = strlen(str); 194 size_t len = strlen(str);
48 bool num = false; 195 bool num = false;
49 for (size_t idx = 0; idx < len; ++idx) { 196 for (size_t idx = 0; idx < len; ++idx) {
50 if (num && str[idx] == 'e') { 197 if (num && str[idx] == 'e') {
51 if (len + 2 >= bufferLen) { 198 if (len + 2 >= bufferLen) {
52 return; 199 return;
53 } 200 }
54 memmove(&str[idx + 2], &str[idx + 1], len - idx); 201 memmove(&str[idx + 2], &str[idx + 1], len - idx);
55 str[idx] = '*'; 202 str[idx] = '*';
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
125 void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp , 272 void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp ,
126 const char* testName) { 273 const char* testName) {
127 SkAutoMutexAcquire ac(gTestMutex); 274 SkAutoMutexAcquire ac(gTestMutex);
128 show_function_header(testName); 275 show_function_header(testName);
129 ShowOnePath(a, "path", true); 276 ShowOnePath(a, "path", true);
130 ShowOnePath(b, "pathB", true); 277 ShowOnePath(b, "pathB", true);
131 show_op(shapeOp, "path", "pathB"); 278 show_op(shapeOp, "path", "pathB");
132 } 279 }
133 280
134 #include "SkPathOpsTypes.h" 281 #include "SkPathOpsTypes.h"
282 #include "SkIntersectionHelper.h"
283 #include "SkIntersections.h"
284
285 #if DEBUG_T_SECT_LOOP_COUNT
286 void SkOpGlobalState::debugAddLoopCount(SkIntersections* i, const SkIntersection Helper& wt,
287 const SkIntersectionHelper& wn) {
288 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
289 SkIntersections::DebugLoop looper = (SkIntersections::DebugLoop) index;
290 if (fDebugLoopCount[index] >= i->debugLoopCount(looper)) {
291 continue;
292 }
293 fDebugLoopCount[index] = i->debugLoopCount(looper);
294 fDebugWorstVerb[index * 2] = wt.segment()->verb();
295 fDebugWorstVerb[index * 2 + 1] = wn.segment()->verb();
296 sk_bzero(&fDebugWorstPts[index * 8], sizeof(SkPoint) * 8);
297 memcpy(&fDebugWorstPts[index * 2 * 4], wt.pts(),
298 (SkPathOpsVerbToPoints(wt.segment()->verb()) + 1) * sizeof(SkPoi nt));
299 memcpy(&fDebugWorstPts[(index * 2 + 1) * 4], wn.pts(),
300 (SkPathOpsVerbToPoints(wn.segment()->verb()) + 1) * sizeof(SkPoi nt));
301 fDebugWorstWeight[index * 2] = wt.weight();
302 fDebugWorstWeight[index * 2 + 1] = wn.weight();
303 }
304 i->debugResetLoopCount();
305 }
306
307 void SkOpGlobalState::debugDoYourWorst(SkOpGlobalState* local) {
308 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
309 if (fDebugLoopCount[index] >= local->fDebugLoopCount[index]) {
310 continue;
311 }
312 fDebugLoopCount[index] = local->fDebugLoopCount[index];
313 fDebugWorstVerb[index * 2] = local->fDebugWorstVerb[index * 2];
314 fDebugWorstVerb[index * 2 + 1] = local->fDebugWorstVerb[index * 2 + 1];
315 memcpy(&fDebugWorstPts[index * 2 * 4], &local->fDebugWorstPts[index * 2 * 4],
316 sizeof(SkPoint) * 8);
317 fDebugWorstWeight[index * 2] = local->fDebugWorstWeight[index * 2];
318 fDebugWorstWeight[index * 2 + 1] = local->fDebugWorstWeight[index * 2 + 1];
319 }
320 local->debugResetLoopCounts();
321 }
322
323 static void dump_curve(SkPath::Verb verb, const SkPoint& pts, float weight) {
324 if (!verb) {
325 return;
326 }
327 const char* verbs[] = { "", "line", "quad", "conic", "cubic" };
328 SkDebugf("%s: {{", verbs[verb]);
329 int ptCount = SkPathOpsVerbToPoints(verb);
330 for (int index = 0; index <= ptCount; ++index) {
331 SkDPoint::Dump((&pts)[index]);
332 if (index < ptCount - 1) {
333 SkDebugf(", ");
334 }
335 }
336 SkDebugf("}");
337 if (weight != 1) {
338 SkDebugf(", ");
339 if (weight == floorf(weight)) {
340 SkDebugf("%.0f", weight);
341 } else {
342 SkDebugf("%1.9gf", weight);
343 }
344 }
345 SkDebugf("}\n");
346 }
347
348 void SkOpGlobalState::debugLoopReport() {
349 const char* loops[] = { "iterations", "coinChecks", "perpCalcs" };
350 SkDebugf("\n");
351 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) {
352 SkDebugf("%s: %d\n", loops[index], fDebugLoopCount[index]);
353 dump_curve(fDebugWorstVerb[index * 2], fDebugWorstPts[index * 2 * 4],
354 fDebugWorstWeight[index * 2]);
355 dump_curve(fDebugWorstVerb[index * 2 + 1], fDebugWorstPts[(index * 2 + 1 ) * 4],
356 fDebugWorstWeight[index * 2 + 1]);
357 }
358 }
359
360 void SkOpGlobalState::debugResetLoopCounts() {
361 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
362 sk_bzero(fDebugWorstVerb, sizeof(fDebugWorstVerb));
363 sk_bzero(fDebugWorstPts, sizeof(fDebugWorstPts));
364 sk_bzero(fDebugWorstWeight, sizeof(fDebugWorstWeight));
365 }
366 #endif
135 367
136 #ifdef SK_DEBUG 368 #ifdef SK_DEBUG
137 bool SkOpGlobalState::debugRunFail() const { 369 bool SkOpGlobalState::debugRunFail() const {
138 #if DEBUG_VALIDATE 370 #if DEBUG_VALIDATE
139 return FLAGS_runFail; 371 return FLAGS_runFail;
140 #else 372 #else
141 return false; 373 return false;
142 #endif 374 #endif
143 } 375 }
144 #endif 376 #endif
145 377
378 #if DEBUG_T_SECT_LOOP_COUNT
379 void SkIntersections::debugBumpLoopCount(DebugLoop index) {
380 fDebugLoopCount[index]++;
381 }
382
383 int SkIntersections::debugLoopCount(DebugLoop index) const {
384 return fDebugLoopCount[index];
385 }
386
387 void SkIntersections::debugResetLoopCount() {
388 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount));
389 }
390 #endif
391
146 #include "SkPathOpsCubic.h" 392 #include "SkPathOpsCubic.h"
147 #include "SkPathOpsQuad.h" 393 #include "SkPathOpsQuad.h"
148 394
149 SkDCubic SkDQuad::debugToCubic() const { 395 SkDCubic SkDQuad::debugToCubic() const {
150 SkDCubic cubic; 396 SkDCubic cubic;
151 cubic[0] = fPts[0]; 397 cubic[0] = fPts[0];
152 cubic[2] = fPts[1]; 398 cubic[2] = fPts[1];
153 cubic[3] = fPts[2]; 399 cubic[3] = fPts[2];
154 cubic[1].fX = (cubic[0].fX + cubic[2].fX * 2) / 3; 400 cubic[1].fX = (cubic[0].fX + cubic[2].fX * 2) / 3;
155 cubic[1].fY = (cubic[0].fY + cubic[2].fY * 2) / 3; 401 cubic[1].fY = (cubic[0].fY + cubic[2].fY * 2) / 3;
156 cubic[2].fX = (cubic[3].fX + cubic[2].fX * 2) / 3; 402 cubic[2].fX = (cubic[3].fX + cubic[2].fX * 2) / 3;
157 cubic[2].fY = (cubic[3].fY + cubic[2].fY * 2) / 3; 403 cubic[2].fY = (cubic[3].fY + cubic[2].fY * 2) / 3;
158 return cubic; 404 return cubic;
159 } 405 }
160 406
161 #include "SkOpAngle.h" 407 #include "SkOpAngle.h"
162 #include "SkOpCoincidence.h"
163 #include "SkOpSegment.h" 408 #include "SkOpSegment.h"
164 409
410 #if DEBUG_COINCIDENCE
411 void SkOpSegment::debugAddAlignIntersection(const char* id, SkPathOpsDebug::Glit chLog* log,
412 const SkOpPtT& endPtT, const SkPoint& oldPt, const SkOpContourHead* con tourList) const {
413 const SkPoint& newPt = endPtT.fPt;
414 if (newPt == oldPt) {
415 return;
416 }
417 SkPoint line[2] = { newPt, oldPt };
418 SkPathOpsBounds lineBounds;
419 lineBounds.setBounds(line, 2);
420 SkDLine aLine;
421 aLine.set(line);
422 const SkOpContour* current = contourList;
423 do {
424 if (!SkPathOpsBounds::Intersects(current->bounds(), lineBounds)) {
425 continue;
426 }
427 const SkOpSegment* segment = current->first();
428 do {
429 if (!SkPathOpsBounds::Intersects(segment->bounds(), lineBounds)) {
430 continue;
431 }
432 if (newPt == segment->fPts[0]) {
433 continue;
434 }
435 if (newPt == segment->fPts[SkPathOpsVerbToPoints(segment->fVerb)]) {
436 continue;
437 }
438 if (oldPt == segment->fPts[0]) {
439 continue;
440 }
441 if (oldPt == segment->fPts[SkPathOpsVerbToPoints(segment->fVerb)]) {
442 continue;
443 }
444 if (endPtT.debugContains(segment)) {
445 continue;
446 }
447 SkIntersections i;
448 switch (segment->fVerb) {
449 case SkPath::kLine_Verb: {
450 SkDLine bLine;
451 bLine.set(segment->fPts);
452 i.intersect(bLine, aLine);
453 } break;
454 case SkPath::kQuad_Verb: {
455 SkDQuad bQuad;
456 bQuad.set(segment->fPts);
457 i.intersect(bQuad, aLine);
458 } break;
459 case SkPath::kConic_Verb: {
460 SkDConic bConic;
461 bConic.set(segment->fPts, segment->fWeight);
462 i.intersect(bConic, aLine);
463 } break;
464 case SkPath::kCubic_Verb: {
465 SkDCubic bCubic;
466 bCubic.set(segment->fPts);
467 i.intersect(bCubic, aLine);
468 } break;
469 default:
470 SkASSERT(0);
471 }
472 if (i.used()) {
473 SkASSERT(i.used() == 1);
474 SkASSERT(!zero_or_one(i[0][0]));
475 SkOpSpanBase* checkSpan = fHead.next();
476 while (!checkSpan->final()) {
477 if (checkSpan->contains(segment)) {
478 goto nextSegment;
479 }
480 checkSpan = checkSpan->upCast()->next();
481 }
482 log->record(kMissingIntersection_Glitch, id, checkSpan, segment, i[0][0], newPt);
483 }
484 nextSegment:
485 ;
486 } while ((segment = segment->next()));
487 } while ((current = current->next()));
488 }
489
490 bool SkOpSegment::debugAddMissing(double t, const SkOpSegment* opp) const {
491 const SkOpSpanBase* existing = nullptr;
492 const SkOpSpanBase* test = &fHead;
493 double testT;
494 do {
495 if ((testT = test->ptT()->fT) >= t) {
496 if (testT == t) {
497 existing = test;
498 }
499 break;
500 }
501 } while ((test = test->upCast()->next()));
502 return !existing || !existing->debugContains(opp);
503 }
504
505 void SkOpSegment::debugAlign(const char* id, SkPathOpsDebug::GlitchLog* glitches ) const {
506 const SkOpSpanBase* span = &fHead;
507 if (!span->aligned()) {
508 if (!span->debugAlignedEnd(0, fPts[0])) {
509 glitches->record(kUnalignedHead_Glitch, id, span);
510 }
511 }
512 while ((span = span->upCast()->next())) {
513 if (span == &fTail) {
514 break;
515 }
516 if (!span->aligned()) {
517 glitches->record(kUnaligned_Glitch, id, span);
518 }
519 }
520 if (!span->aligned()) {
521 span->debugAlignedEnd(1, fPts[SkPathOpsVerbToPoints(fVerb)]);
522 }
523 if (this->collapsed()) {
524 const SkOpSpan* span = &fHead;
525 do {
526 if (span->windValue()) {
527 glitches->record(kCollapsedWindValue_Glitch, id, span);
528 }
529 if (span->oppValue()) {
530 glitches->record(kCollapsedOppValue_Glitch, id, span);
531 }
532 if (!span->done()) {
533 glitches->record(kCollapsedDone_Glitch, id, span);
534 }
535 } while ((span = span->next()->upCastable()));
536 }
537 }
538 #endif
539
540 #if DEBUG_ANGLE
541 void SkOpSegment::debugCheckAngleCoin() const {
542 const SkOpSpanBase* base = &fHead;
543 const SkOpSpan* span;
544 do {
545 const SkOpAngle* angle = base->fromAngle();
546 if (angle && angle->fCheckCoincidence) {
547 angle->debugCheckNearCoincidence();
548 }
549 if (base->final()) {
550 break;
551 }
552 span = base->upCast();
553 angle = span->toAngle();
554 if (angle && angle->fCheckCoincidence) {
555 angle->debugCheckNearCoincidence();
556 }
557 } while ((base = span->next()));
558 }
559 #endif
560
561 #if DEBUG_COINCIDENCE
562 // this mimics the order of the checks in handle coincidence
563 void SkOpSegment::debugCheckHealth(const char* id, SkPathOpsDebug::GlitchLog* gl itches) const {
564 debugMoveMultiples(id, glitches);
565 debugFindCollapsed(id, glitches);
566 debugMoveNearby(id, glitches);
567 debugAlign(id, glitches);
568 debugAddAlignIntersections(id, glitches, this->globalState()->contourHead()) ;
569
570 }
571
572 void SkOpSegment::debugFindCollapsed(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
573 if (fHead.contains(&fTail)) {
574 const SkOpSpan* span = this->head();
575 bool missingDone = false;
576 do {
577 missingDone |= !span->done();
578 } while ((span = span->next()->upCastable()));
579 if (missingDone) {
580 glitches->record(kMissingDone_Glitch, id, &fHead);
581 }
582 if (!fHead.debugAlignedEnd(0, fHead.pt())) {
583 glitches->record(kUnalignedHead_Glitch, id, &fHead);
584 }
585 if (!fTail.aligned()) {
586 glitches->record(kUnalignedTail_Glitch, id, &fTail);
587 }
588 }
589 }
590 #endif
591
165 SkOpAngle* SkOpSegment::debugLastAngle() { 592 SkOpAngle* SkOpSegment::debugLastAngle() {
166 SkOpAngle* result = nullptr; 593 SkOpAngle* result = nullptr;
167 SkOpSpan* span = this->head(); 594 SkOpSpan* span = this->head();
168 do { 595 do {
169 if (span->toAngle()) { 596 if (span->toAngle()) {
170 SkASSERT(!result); 597 SkASSERT(!result);
171 result = span->toAngle(); 598 result = span->toAngle();
172 } 599 }
173 } while ((span = span->next()->upCastable())); 600 } while ((span = span->next()->upCastable()));
174 SkASSERT(result); 601 SkASSERT(result);
175 return result; 602 return result;
176 } 603 }
177 604
605 #if DEBUG_COINCIDENCE
606 void SkOpSegment::debugMissingCoincidence(const char* id, SkPathOpsDebug::Glitch Log* log,
607 const SkOpCoincidence* coincidences) const {
608 if (this->verb() != SkPath::kLine_Verb) {
609 return;
610 }
611 if (this->done()) {
612 return;
613 }
614 const SkOpSpan* prior = nullptr;
615 const SkOpSpanBase* spanBase = &fHead;
616 do {
617 const SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT;
618 SkASSERT(ptT->span() == spanBase);
619 while ((ptT = ptT->next()) != spanStopPtT) {
620 if (ptT->deleted()) {
621 continue;
622 }
623 SkOpSegment* opp = ptT->span()->segment();
624 // if (opp->verb() == SkPath::kLine_Verb) {
625 // continue;
626 // }
627 if (opp->done()) {
628 continue;
629 }
630 // when opp is encounted the 1st time, continue; on 2nd encounter, l ook for coincidence
631 if (!opp->visited()) {
632 continue;
633 }
634 if (spanBase == &fHead) {
635 continue;
636 }
637 const SkOpSpan* span = spanBase->upCastable();
638 // FIXME?: this assumes that if the opposite segment is coincident t hen no more
639 // coincidence needs to be detected. This may not be true.
640 if (span && span->segment() != opp && span->containsCoincidence(opp) ) {
641 continue;
642 }
643 if (spanBase->segment() != opp && spanBase->containsCoinEnd(opp)) {
644 continue;
645 }
646 const SkOpPtT* priorPtT = nullptr, * priorStopPtT;
647 // find prior span containing opp segment
648 const SkOpSegment* priorOpp = nullptr;
649 const SkOpSpan* priorTest = spanBase->prev();
650 while (!priorOpp && priorTest) {
651 priorStopPtT = priorPtT = priorTest->ptT();
652 while ((priorPtT = priorPtT->next()) != priorStopPtT) {
653 if (priorPtT->deleted()) {
654 continue;
655 }
656 SkOpSegment* segment = priorPtT->span()->segment();
657 if (segment == opp) {
658 prior = priorTest;
659 priorOpp = opp;
660 break;
661 }
662 }
663 priorTest = priorTest->prev();
664 }
665 if (!priorOpp) {
666 continue;
667 }
668 const SkOpPtT* oppStart = prior->ptT();
669 const SkOpPtT* oppEnd = spanBase->ptT();
670 bool swapped = priorPtT->fT > ptT->fT;
671 if (swapped) {
672 SkTSwap(priorPtT, ptT);
673 SkTSwap(oppStart, oppEnd);
674 }
675 bool flipped = oppStart->fT > oppEnd->fT;
676 bool coincident = false;
677 if (coincidences->contains(priorPtT, ptT, oppStart, oppEnd, flipped) ) {
678 goto swapBack;
679 }
680 if (opp->verb() == SkPath::kLine_Verb) {
681 coincident = (SkDPoint::ApproximatelyEqual(priorPtT->fPt, oppSta rt->fPt) ||
682 SkDPoint::ApproximatelyEqual(priorPtT->fPt, oppEnd->fPt) ) &&
683 (SkDPoint::ApproximatelyEqual(ptT->fPt, oppStart->fPt) | |
684 SkDPoint::ApproximatelyEqual(ptT->fPt, oppEnd->fPt));
685 }
686 if (!coincident) {
687 coincident = testForCoincidence(priorPtT, ptT, prior, spanBase, opp, 5000);
688 }
689 if (coincident) {
690 log->record(kMissingCoin_Glitch, id, priorPtT, ptT, oppStart, op pEnd);
691 }
692 swapBack:
693 if (swapped) {
694 SkTSwap(priorPtT, ptT);
695 }
696 }
697 } while ((spanBase = spanBase->final() ? nullptr : spanBase->upCast()->next( )));
698 }
699
700 void SkOpSegment::debugMoveMultiples(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
701 const SkOpSpanBase* test = &fHead;
702 do {
703 int addCount = test->spanAddsCount();
704 SkASSERT(addCount >= 1);
705 if (addCount == 1) {
706 continue;
707 }
708 const SkOpPtT* startPtT = test->ptT();
709 const SkOpPtT* testPtT = startPtT;
710 do { // iterate through all spans associated with start
711 const SkOpSpanBase* oppSpan = testPtT->span();
712 if (oppSpan->spanAddsCount() == addCount) {
713 continue;
714 }
715 if (oppSpan->deleted()) {
716 continue;
717 }
718 const SkOpSegment* oppSegment = oppSpan->segment();
719 if (oppSegment == this) {
720 continue;
721 }
722 // find range of spans to consider merging
723 const SkOpSpanBase* oppPrev = oppSpan;
724 const SkOpSpanBase* oppFirst = oppSpan;
725 while ((oppPrev = oppPrev->prev())) {
726 if (!roughly_equal(oppPrev->t(), oppSpan->t())) {
727 break;
728 }
729 if (oppPrev->spanAddsCount() == addCount) {
730 continue;
731 }
732 if (oppPrev->deleted()) {
733 continue;
734 }
735 oppFirst = oppPrev;
736 }
737 const SkOpSpanBase* oppNext = oppSpan;
738 const SkOpSpanBase* oppLast = oppSpan;
739 while ((oppNext = oppNext->final() ? nullptr : oppNext->upCast()->ne xt())) {
740 if (!roughly_equal(oppNext->t(), oppSpan->t())) {
741 break;
742 }
743 if (oppNext->spanAddsCount() == addCount) {
744 continue;
745 }
746 if (oppNext->deleted()) {
747 continue;
748 }
749 oppLast = oppNext;
750 }
751 if (oppFirst == oppLast) {
752 continue;
753 }
754 const SkOpSpanBase* oppTest = oppFirst;
755 do {
756 if (oppTest == oppSpan) {
757 continue;
758 }
759 // check to see if the candidate meets specific criteria:
760 // it contains spans of segments in test's loop but not includin g 'this'
761 const SkOpPtT* oppStartPtT = oppTest->ptT();
762 const SkOpPtT* oppPtT = oppStartPtT;
763 while ((oppPtT = oppPtT->next()) != oppStartPtT) {
764 const SkOpSegment* oppPtTSegment = oppPtT->segment();
765 if (oppPtTSegment == this) {
766 goto tryNextSpan;
767 }
768 const SkOpPtT* matchPtT = startPtT;
769 do {
770 if (matchPtT->segment() == oppPtTSegment) {
771 goto foundMatch;
772 }
773 } while ((matchPtT = matchPtT->next()) != startPtT);
774 goto tryNextSpan;
775 foundMatch: // merge oppTest and oppSpan
776 if (oppTest == &oppSegment->fTail || oppTest == &oppSegment- >fHead) {
777 SkASSERT(oppSpan != &oppSegment->fHead); // don't expect collapse
778 SkASSERT(oppSpan != &oppSegment->fTail);
779 glitches->record(kMoveMultiple_Glitch, id, oppTest, oppS pan);
780 } else {
781 glitches->record(kMoveMultiple_Glitch, id, oppSpan, oppT est);
782 }
783 goto checkNextSpan;
784 }
785 tryNextSpan:
786 ;
787 } while (oppTest != oppLast && (oppTest = oppTest->upCast()->next()) );
788 } while ((testPtT = testPtT->next()) != startPtT);
789 checkNextSpan:
790 ;
791 } while ((test = test->final() ? nullptr : test->upCast()->next()));
792 }
793
794 void SkOpSegment::debugMoveNearby(const char* id, SkPathOpsDebug::GlitchLog* gli tches) const {
795 const SkOpSpanBase* spanS = &fHead;
796 do {
797 const SkOpSpanBase* test = spanS->upCast()->next();
798 const SkOpSpanBase* next;
799 if (spanS->contains(test)) {
800 if (!test->final()) {
801 glitches->record(kUndetachedSpan_Glitch, id, test, spanS);
802 } else if (spanS != &fHead) {
803 glitches->record(kUndetachedSpan_Glitch, id, spanS, test);
804 }
805 }
806 do { // iterate through all spans associated with start
807 const SkOpPtT* startBase = spanS->ptT();
808 next = test->final() ? nullptr : test->upCast()->next();
809 do {
810 const SkOpPtT* testBase = test->ptT();
811 do {
812 if (startBase == testBase) {
813 goto checkNextSpan;
814 }
815 if (testBase->duplicate()) {
816 continue;
817 }
818 if (this->match(startBase, testBase->segment(), testBase->fT , testBase->fPt)) {
819 if (test == &this->fTail) {
820 if (spanS == &fHead) {
821 glitches->record(kCollapsedSpan_Glitch, id, span S);
822 } else {
823 glitches->record(kUnmergedSpan_Glitch, id, &this ->fTail, spanS);
824 }
825 } else {
826 glitches->record(kUnmergedSpan_Glitch, id, spanS, te st);
827 goto checkNextSpan;
828 }
829 }
830 } while ((testBase = testBase->next()) != test->ptT());
831 } while ((startBase = startBase->next()) != spanS->ptT());
832 checkNextSpan:
833 ;
834 } while ((test = next));
835 spanS = spanS->upCast()->next();
836 } while (!spanS->final());
837 }
838 #endif
839
178 void SkOpSegment::debugReset() { 840 void SkOpSegment::debugReset() {
179 this->init(this->fPts, this->fWeight, this->contour(), this->verb()); 841 this->init(this->fPts, this->fWeight, this->contour(), this->verb());
180 } 842 }
181 843
182 #if DEBUG_ACTIVE_SPANS 844 #if DEBUG_ACTIVE_SPANS
183 void SkOpSegment::debugShowActiveSpans() const { 845 void SkOpSegment::debugShowActiveSpans() const {
184 debugValidate(); 846 debugValidate();
185 if (done()) { 847 if (done()) {
186 return; 848 return;
187 } 849 }
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
282 if (span->windSum() == SK_MinS32) { 944 if (span->windSum() == SK_MinS32) {
283 SkDebugf("?"); 945 SkDebugf("?");
284 } else { 946 } else {
285 SkDebugf("%d", span->windSum()); 947 SkDebugf("%d", span->windSum());
286 } 948 }
287 SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue()) ; 949 SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue()) ;
288 } 950 }
289 951
290 #endif 952 #endif
291 953
954 // loop looking for a pair of angle parts that are too close to be sorted
955 /* This is called after other more simple intersection and angle sorting tests h ave been exhausted.
956 This should be rarely called -- the test below is thorough and time consuming .
957 This checks the distance between start points; the distance between
958 */
959 #if DEBUG_ANGLE
960 void SkOpAngle::debugCheckNearCoincidence() const {
961 const SkOpAngle* test = this;
962 do {
963 const SkOpSegment* testSegment = test->segment();
964 double testStartT = test->start()->t();
965 SkDPoint testStartPt = testSegment->dPtAtT(testStartT);
966 double testEndT = test->end()->t();
967 SkDPoint testEndPt = testSegment->dPtAtT(testEndT);
968 double testLenSq = testStartPt.distanceSquared(testEndPt);
969 SkDebugf("%s testLenSq=%1.9g id=%d\n", __FUNCTION__, testLenSq, testSegm ent->debugID());
970 double testMidT = (testStartT + testEndT) / 2;
971 const SkOpAngle* next = test;
972 while ((next = next->fNext) != this) {
973 SkOpSegment* nextSegment = next->segment();
974 double testMidDistSq = testSegment->distSq(testMidT, next);
975 double testEndDistSq = testSegment->distSq(testEndT, next);
976 double nextStartT = next->start()->t();
977 SkDPoint nextStartPt = nextSegment->dPtAtT(nextStartT);
978 double distSq = testStartPt.distanceSquared(nextStartPt);
979 double nextEndT = next->end()->t();
980 double nextMidT = (nextStartT + nextEndT) / 2;
981 double nextMidDistSq = nextSegment->distSq(nextMidT, test);
982 double nextEndDistSq = nextSegment->distSq(nextEndT, test);
983 SkDebugf("%s distSq=%1.9g testId=%d nextId=%d\n", __FUNCTION__, dist Sq,
984 testSegment->debugID(), nextSegment->debugID());
985 SkDebugf("%s testMidDistSq=%1.9g\n", __FUNCTION__, testMidDistSq);
986 SkDebugf("%s testEndDistSq=%1.9g\n", __FUNCTION__, testEndDistSq);
987 SkDebugf("%s nextMidDistSq=%1.9g\n", __FUNCTION__, nextMidDistSq);
988 SkDebugf("%s nextEndDistSq=%1.9g\n", __FUNCTION__, nextEndDistSq);
989 SkDPoint nextEndPt = nextSegment->dPtAtT(nextEndT);
990 double nextLenSq = nextStartPt.distanceSquared(nextEndPt);
991 SkDebugf("%s nextLenSq=%1.9g\n", __FUNCTION__, nextLenSq);
992 SkDebugf("\n");
993 }
994 test = test->fNext;
995 } while (test->fNext != this);
996 }
997 #endif
998
292 #if DEBUG_ANGLE 999 #if DEBUG_ANGLE
293 SkString SkOpAngle::debugPart() const { 1000 SkString SkOpAngle::debugPart() const {
294 SkString result; 1001 SkString result;
295 switch (this->segment()->verb()) { 1002 switch (this->segment()->verb()) {
296 case SkPath::kLine_Verb: 1003 case SkPath::kLine_Verb:
297 result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fCurvePart), 1004 result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fCurvePart),
298 this->segment()->debugID()); 1005 this->segment()->debugID());
299 break; 1006 break;
300 case SkPath::kQuad_Verb: 1007 case SkPath::kQuad_Verb:
301 result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fCurvePart), 1008 result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fCurvePart),
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
390 break; 1097 break;
391 } 1098 }
392 SK_ALWAYSBREAK(!angles.contains(next)); 1099 SK_ALWAYSBREAK(!angles.contains(next));
393 if (!next) { 1100 if (!next) {
394 return; 1101 return;
395 } 1102 }
396 } while (true); 1103 } while (true);
397 #endif 1104 #endif
398 } 1105 }
399 1106
1107
1108 #if DEBUG_COINCIDENCE
1109 void SkOpCoincidence::debugAddExpanded(const char* id, SkPathOpsDebug::GlitchLog * log) const {
1110 // for each coincident pair, match the spans
1111 // if the spans don't match, add the mssing pt to the segment and loop it in the opposite span
1112 const SkCoincidentSpans* coin = this->fHead;
1113 if (!coin) {
1114 coin = this->fTop;
1115 }
1116 SkASSERT(coin);
1117 do {
1118 const SkOpPtT* startPtT = coin->fCoinPtTStart;
1119 const SkOpPtT* oStartPtT = coin->fOppPtTStart;
1120 SkASSERT(startPtT->contains(oStartPtT));
1121 SkASSERT(coin->fCoinPtTEnd->contains(coin->fOppPtTEnd));
1122 const SkOpSpanBase* start = startPtT->span();
1123 const SkOpSpanBase* oStart = oStartPtT->span();
1124 const SkOpSpanBase* end = coin->fCoinPtTEnd->span();
1125 const SkOpSpanBase* oEnd = coin->fOppPtTEnd->span();
1126 const SkOpSpanBase* test = start->upCast()->next();
1127 const SkOpSpanBase* oTest = coin->fFlipped ? oStart->prev() : oStart->up Cast()->next();
1128 while (test != end || oTest != oEnd) {
1129 bool bumpTest = true;
1130 bool bumpOTest = true;
1131 if (!test->ptT()->contains(oTest->ptT())) {
1132 // use t ranges to guess which one is missing
1133 double startRange = coin->fCoinPtTEnd->fT - startPtT->fT;
1134 double startPart = (test->t() - startPtT->fT) / startRange;
1135 double oStartRange = coin->fOppPtTEnd->fT - oStartPtT->fT;
1136 double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange;
1137 if (startPart == oStartPart) {
1138 // data is corrupt
1139 log->record(kAddCorruptCoin_Glitch, id, start, oStart);
1140 break;
1141 }
1142 if (startPart < oStartPart) {
1143 double newT = oStartPtT->fT + oStartRange * startPart;
1144 log->record(kAddExpandedCoin_Glitch, id, oStart, newT, test- >pt());
1145 bumpOTest = false;
1146 } else {
1147 double newT = startPtT->fT + startRange * oStartPart;
1148 log->record(kAddExpandedCoin_Glitch, id, start, newT, oTest- >pt());
1149 bumpTest = false;
1150 }
1151 }
1152 if (bumpTest && test != end) {
1153 test = test->upCast()->next();
1154 }
1155 if (bumpOTest && oTest != oEnd) {
1156 oTest = coin->fFlipped ? oTest->prev() : oTest->upCast()->next() ;
1157 }
1158 }
1159 } while ((coin = coin->fNext));
1160 }
1161
1162 static void t_range(const SkOpPtT* overS, const SkOpPtT* overE, double tStart, d ouble tEnd,
1163 const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, double* coinTs, double* coinTe) {
1164 double denom = overE->fT - overS->fT;
1165 double start = 0 < denom ? tStart : tEnd;
1166 double end = 0 < denom ? tEnd : tStart;
1167 double sRatio = (start - overS->fT) / denom;
1168 double eRatio = (end - overS->fT) / denom;
1169 *coinTs = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * sRatio;
1170 *coinTe = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * eRatio;
1171 }
1172
1173 bool SkOpCoincidence::debugAddIfMissing(const SkCoincidentSpans* outer, const Sk OpPtT* over1s,
1174 const SkOpPtT* over1e) const {
1175 const SkCoincidentSpans* check = this->fTop;
1176 while (check) {
1177 if (check->fCoinPtTStart->span() == over1s->span()
1178 && check->fOppPtTStart->span() == outer->fOppPtTStart->span()) {
1179 SkASSERT(check->fCoinPtTEnd->span() == over1e->span()
1180 || !fDebugState->debugRunFail());
1181 SkASSERT(check->fOppPtTEnd->span() == outer->fOppPtTEnd->span()
1182 || !fDebugState->debugRunFail());
1183 return false;
1184 }
1185 if (check->fCoinPtTStart->span() == outer->fCoinPtTStart->span()
1186 && check->fOppPtTStart->span() == over1s->span()) {
1187 SkASSERT(check->fCoinPtTEnd->span() == outer->fCoinPtTEnd->span()
1188 || !fDebugState->debugRunFail());
1189 SkASSERT(check->fOppPtTEnd->span() == over1e->span()
1190 || !fDebugState->debugRunFail());
1191 return false;
1192 }
1193 check = check->fNext;
1194 }
1195 return true;
1196 }
1197
1198 bool SkOpCoincidence::debugAddIfMissing(const SkOpPtT* over1s, const SkOpPtT* ov er1e,
1199 const SkOpPtT* over2s, const SkOpPtT* over2e, double tStar t, double tEnd,
1200 SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
1201 SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) const {
1202 double coinTs, coinTe, oppTs, oppTe;
1203 t_range(over1s, over1e, tStart, tEnd, coinPtTStart, coinPtTEnd, &coinTs, &co inTe);
1204 t_range(over2s, over2e, tStart, tEnd, oppPtTStart, oppPtTEnd, &oppTs, &oppTe );
1205 const SkOpSegment* coinSeg = coinPtTStart->segment();
1206 const SkOpSegment* oppSeg = oppPtTStart->segment();
1207 SkASSERT(coinSeg != oppSeg);
1208 const SkCoincidentSpans* check = this->fTop;
1209 ;
1210 while (check) {
1211 const SkOpSegment* checkCoinSeg = check->fCoinPtTStart->segment();
1212 const SkOpSegment* checkOppSeg;
1213 if (checkCoinSeg != coinSeg && checkCoinSeg != oppSeg) {
1214 goto next;
1215 }
1216 checkOppSeg = check->fOppPtTStart->segment();
1217 if (checkOppSeg != coinSeg && checkOppSeg != oppSeg) {
1218 goto next;
1219 }
1220 {
1221 int cTs = coinTs;
1222 int cTe = coinTe;
1223 int oTs = oppTs;
1224 int oTe = oppTe;
1225 if (checkCoinSeg != coinSeg) {
1226 SkASSERT(checkOppSeg != oppSeg);
1227 SkTSwap(cTs, oTs);
1228 SkTSwap(cTe, oTe);
1229 }
1230 int tweenCount = (int) between(check->fCoinPtTStart->fT, cTs, check- >fCoinPtTEnd->fT)
1231 + (int) between(check->fCoinPtTStart->fT, cTe, check- >fCoinPtTEnd->fT)
1232 + (int) between(check->fOppPtTStart->fT, oTs, check-> fOppPtTEnd->fT)
1233 + (int) between(check->fOppPtTStart->fT, oTe, check-> fOppPtTEnd->fT);
1234 // SkASSERT(tweenCount == 0 || tweenCount == 4);
1235 if (tweenCount) {
1236 return true;
1237 }
1238 }
1239 next:
1240 check = check->fNext;
1241 }
1242 if ((over1s->fT < over1e->fT) != (over2s->fT < over2e->fT)) {
1243 SkTSwap(oppTs, oppTe);
1244 }
1245 if (coinTs > coinTe) {
1246 SkTSwap(coinTs, coinTe);
1247 SkTSwap(oppTs, oppTe);
1248 }
1249 bool cs = coinSeg->debugAddMissing(coinTs, oppSeg);
1250 bool ce = coinSeg->debugAddMissing(coinTe, oppSeg);
1251 if (cs == ce) {
1252 return false;
1253 }
1254 return true;
1255 }
1256
1257 void SkOpCoincidence::debugAddMissing(const char* id, SkPathOpsDebug::GlitchLog* log) const {
1258 const SkCoincidentSpans* outer = fHead;
1259 if (!outer) {
1260 return;
1261 }
1262 do {
1263 // addifmissing can modify the list that this is walking
1264 // save head so that walker can iterate over old data unperturbed
1265 // addifmissing adds to head freely then add saved head in the end
1266 const SkOpSegment* outerCoin = outer->fCoinPtTStart->segment();
1267 SkASSERT(outerCoin == outer->fCoinPtTEnd->segment());
1268 const SkOpSegment* outerOpp = outer->fOppPtTStart->segment();
1269 SkASSERT(outerOpp == outer->fOppPtTEnd->segment());
1270 const SkCoincidentSpans* inner = outer;
1271 while ((inner = inner->fNext)) {
1272 double overS, overE;
1273 const SkOpSegment* innerCoin = inner->fCoinPtTStart->segment();
1274 SkASSERT(innerCoin == inner->fCoinPtTEnd->segment());
1275 const SkOpSegment* innerOpp = inner->fOppPtTStart->segment();
1276 SkASSERT(innerOpp == inner->fOppPtTEnd->segment());
1277 if (outerCoin == innerCoin
1278 && this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
1279 inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) {
1280 if (this->debugAddIfMissing(outer->fCoinPtTStart, outer->fCoinPt TEnd,
1281 inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE,
1282 outer->fOppPtTStart, outer->fOppPtTEnd,
1283 inner->fOppPtTStart, inner->fOppPtTEnd)) {
1284 log->record(kAddMissingCoin_Glitch, id, outer, inner->fCoinP tTStart);
1285 }
1286 } else if (outerCoin == innerOpp
1287 && this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
1288 inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) {
1289 if (this->debugAddIfMissing(outer->fCoinPtTStart, outer->fCoinPt TEnd,
1290 inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE,
1291 outer->fOppPtTStart, outer->fOppPtTEnd,
1292 inner->fCoinPtTStart, inner->fCoinPtTEnd)) {
1293 log->record(kAddMissingCoin_Glitch, id, outer, inner->fOppPt TStart);
1294 }
1295 } else if (outerOpp == innerCoin
1296 && this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
1297 inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) {
1298 if (this->debugAddIfMissing(outer->fOppPtTStart, outer->fOppPtTE nd,
1299 inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE,
1300 outer->fCoinPtTStart, outer->fCoinPtTEnd,
1301 inner->fOppPtTStart, inner->fOppPtTEnd)) {
1302 log->record(kAddMissingCoin_Glitch, id, outer, inner->fCoinP tTStart);
1303 }
1304 } else if (outerOpp == innerOpp
1305 && this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
1306 inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) {
1307 if (this->debugAddIfMissing(outer->fOppPtTStart, outer->fOppPtTE nd,
1308 inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE,
1309 outer->fCoinPtTStart, outer->fCoinPtTEnd,
1310 inner->fCoinPtTStart, inner->fCoinPtTEnd)) {
1311 log->record(kAddMissingCoin_Glitch, id, outer, inner->fOppPt TStart);
1312 }
1313 } else if (outerCoin != innerCoin) {
1314 // check to see if outer span overlaps the inner span
1315 // look for inner segment in pt-t list
1316 // if present, and if t values are in coincident range
1317 // add two pairs of new coincidence
1318 const SkOpPtT* testS = outer->fCoinPtTStart->debugContains(inner Coin);
1319 const SkOpPtT* testE = outer->fCoinPtTEnd->debugContains(innerCo in);
1320 if (testS && testS->fT >= inner->fCoinPtTStart->fT
1321 && testE && testE->fT <= inner->fCoinPtTEnd->fT
1322 && this->testForCoincidence(outer, testS, testE)) {
1323 if (this->debugAddIfMissing(outer, testS, testE)) {
1324 log->record(kAddMissingCoin_Glitch, id, outer, testS, te stE);
1325 }
1326 } else {
1327 testS = inner->fCoinPtTStart->debugContains(outerCoin);
1328 testE = inner->fCoinPtTEnd->debugContains(outerCoin);
1329 if (testS && testS->fT >= outer->fCoinPtTStart->fT
1330 && testE && testE->fT <= outer->fCoinPtTEnd->fT
1331 && this->testForCoincidence(inner, testS, testE)) {
1332 if (this->debugAddIfMissing(inner, testS, testE)) {
1333 log->record(kAddMissingCoin_Glitch, id, inner, testS , testE);
1334 }
1335 }
1336 }
1337 }
1338 }
1339 } while ((outer = outer->fNext));
1340 }
1341
1342 bool SkOpCoincidence::debugExpand(const char* id, SkPathOpsDebug::GlitchLog* log ) const {
1343 const SkCoincidentSpans* coin = fHead;
1344 if (!coin) {
1345 return false;
1346 }
1347 bool expanded = false;
1348 do {
1349 const SkOpSpan* start = coin->fCoinPtTStart->span()->upCast();
1350 const SkOpSpanBase* end = coin->fCoinPtTEnd->span();
1351 const SkOpSegment* segment = coin->fCoinPtTStart->segment();
1352 const SkOpSegment* oppSegment = coin->fOppPtTStart->segment();
1353 const SkOpSpan* prev = start->prev();
1354 if (prev && prev->debugContains(oppSegment)) {
1355 double midT = (prev->t() + start->t()) / 2;
1356 if (segment->isClose(midT, oppSegment)) {
1357 log->record(kExpandCoin_Glitch, id, coin, prev);
1358 }
1359 }
1360 SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
1361 if (next && next->debugContains(oppSegment)) {
1362 double midT = (end->t() + next->t()) / 2;
1363 if (segment->isClose(midT, oppSegment)) {
1364 log->record(kExpandCoin_Glitch, id, coin, next);
1365 }
1366 }
1367 } while ((coin = coin->fNext));
1368 return expanded;
1369 }
1370
1371 void SkOpCoincidence::debugFixAligned(const char* id, SkPathOpsDebug::GlitchLog* log) const {
1372 const SkCoincidentSpans* coin = fHead;
1373 if (!coin) {
1374 return;
1375 }
1376 do {
1377 if (coin->fCoinPtTStart->deleted()) {
1378 log->record(kDeletedCoin_Glitch, id, coin, coin->fCoinPtTStart);
1379 }
1380 if (coin->fCoinPtTEnd->deleted()) {
1381 log->record(kDeletedCoin_Glitch, id, coin, coin->fCoinPtTEnd);
1382 }
1383 if (coin->fOppPtTStart->deleted()) {
1384 log->record(kDeletedCoin_Glitch, id, coin, coin->fOppPtTStart);
1385 }
1386 if (coin->fOppPtTEnd->deleted()) {
1387 log->record(kDeletedCoin_Glitch, id, coin, coin->fOppPtTEnd);
1388 }
1389 } while ((coin = coin->fNext));
1390 coin = fHead;
1391 do {
1392 if (coin->fCoinPtTStart->collapsed(coin->fCoinPtTEnd)) {
1393 log->record(kCollapsedCoin_Glitch, id, coin, coin->fCoinPtTStart);
1394 }
1395 if (coin->fOppPtTStart->collapsed(coin->fOppPtTEnd)) {
1396 log->record(kCollapsedCoin_Glitch, id, coin, coin->fOppPtTStart);
1397 }
1398 } while ((coin = coin->fNext));
1399 }
1400
1401 void SkOpCoincidence::debugMark(const char* id, SkPathOpsDebug::GlitchLog* log) const {
1402 const SkCoincidentSpans* coin = fHead;
1403 if (!coin) {
1404 return;
1405 }
1406 do {
1407 const SkOpSpanBase* end = coin->fCoinPtTEnd->span();
1408 const SkOpSpanBase* oldEnd = end;
1409 const SkOpSpan* start = coin->fCoinPtTStart->span()->debugStarter(&end);
1410 const SkOpSpanBase* oEnd = coin->fOppPtTEnd->span();
1411 const SkOpSpanBase* oOldEnd = oEnd;
1412 const SkOpSpanBase* oStart = coin->fOppPtTStart->span()->debugStarter(&o End);
1413 bool flipped = (end == oldEnd) != (oEnd == oOldEnd);
1414 if (flipped) {
1415 SkTSwap(oStart, oEnd);
1416 }
1417 const SkOpSpanBase* next = start;
1418 const SkOpSpanBase* oNext = oStart;
1419 do {
1420 next = next->upCast()->next();
1421 oNext = flipped ? oNext->prev() : oNext->upCast()->next();
1422 if (next == end || oNext == oEnd) {
1423 break;
1424 }
1425 if (!next->containsCoinEnd(oNext)) {
1426 log->record(kMarkCoinEnd_Glitch, id, next, oNext);
1427 }
1428 const SkOpSpan* nextSpan = next->upCast();
1429 const SkOpSpan* oNextSpan = oNext->upCast();
1430 if (!nextSpan->containsCoincidence(oNextSpan)) {
1431 log->record(kMarkCoinInsert_Glitch, id, nextSpan, oNextSpan);
1432 }
1433 } while (true);
1434 } while ((coin = coin->fNext));
1435 }
1436 #endif
1437
400 void SkOpCoincidence::debugShowCoincidence() const { 1438 void SkOpCoincidence::debugShowCoincidence() const {
401 SkCoincidentSpans* span = fHead; 1439 SkCoincidentSpans* span = fHead;
402 while (span) { 1440 while (span) {
403 SkDebugf("%s - id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__, 1441 SkDebugf("%s - id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__,
404 span->fCoinPtTStart->segment()->debugID(), 1442 span->fCoinPtTStart->segment()->debugID(),
405 span->fCoinPtTStart->fT, span->fCoinPtTEnd->fT); 1443 span->fCoinPtTStart->fT, span->fCoinPtTEnd->fT);
406 SkDebugf("%s + id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__, 1444 SkDebugf("%s + id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__,
407 span->fOppPtTStart->segment()->debugID(), 1445 span->fOppPtTStart->segment()->debugID(),
408 span->fOppPtTStart->fT, span->fOppPtTEnd->fT); 1446 span->fOppPtTStart->fT, span->fOppPtTEnd->fT);
409 span = span->fNext; 1447 span = span->fNext;
410 } 1448 }
411 } 1449 }
412 1450
1451 #if DEBUG_COINCIDENCE
1452 void SkOpContour::debugCheckHealth(const char* id, SkPathOpsDebug::GlitchLog* lo g) const {
1453 const SkOpSegment* segment = &fHead;
1454 do {
1455 segment->debugCheckHealth(id, log);
1456 } while ((segment = segment->next()));
1457 }
1458
1459 void SkOpContour::debugMissingCoincidence(const char* id, SkPathOpsDebug::Glitch Log* log,
1460 const SkOpCoincidence* coincidence) const {
1461 const SkOpSegment* segment = &fHead;
1462 do {
1463 segment->debugMissingCoincidence(id, log, coincidence);
1464 } while ((segment = segment->next()));
1465 }
1466 #endif
1467
413 void SkOpSegment::debugValidate() const { 1468 void SkOpSegment::debugValidate() const {
414 #if DEBUG_VALIDATE 1469 #if DEBUG_VALIDATE
415 const SkOpSpanBase* span = &fHead; 1470 const SkOpSpanBase* span = &fHead;
416 double lastT = -1; 1471 double lastT = -1;
417 const SkOpSpanBase* prev = nullptr; 1472 const SkOpSpanBase* prev = nullptr;
418 int count = 0; 1473 int count = 0;
419 int done = 0; 1474 int done = 0;
420 do { 1475 do {
421 if (!span->final()) { 1476 if (!span->final()) {
422 ++count; 1477 ++count;
423 done += span->upCast()->done() ? 1 : 0; 1478 done += span->upCast()->done() ? 1 : 0;
424 } 1479 }
425 SkASSERT(span->segment() == this); 1480 SkASSERT(span->segment() == this);
426 SkASSERT(!prev || prev->upCast()->next() == span); 1481 SkASSERT(!prev || prev->upCast()->next() == span);
427 SkASSERT(!prev || prev == span->prev()); 1482 SkASSERT(!prev || prev == span->prev());
428 prev = span; 1483 prev = span;
429 double t = span->ptT()->fT; 1484 double t = span->ptT()->fT;
430 SkASSERT(lastT < t); 1485 SkASSERT(lastT < t);
431 lastT = t; 1486 lastT = t;
432 span->debugValidate(); 1487 span->debugValidate();
433 } while (!span->final() && (span = span->upCast()->next())); 1488 } while (!span->final() && (span = span->upCast()->next()));
434 SkASSERT(count == fCount); 1489 SkASSERT(count == fCount);
435 SkASSERT(done == fDoneCount); 1490 SkASSERT(done == fDoneCount);
436 SkASSERT(count >= fDoneCount); 1491 SkASSERT(count >= fDoneCount);
437 SkASSERT(span->final()); 1492 SkASSERT(span->final());
438 span->debugValidate(); 1493 span->debugValidate();
439 #endif 1494 #endif
440 } 1495 }
441 1496
1497 bool SkOpSpanBase::debugAlignedEnd(double t, const SkPoint& pt) const {
1498 SkASSERT(zero_or_one(t));
1499 const SkOpSegment* segment = this->segment();
1500 SkASSERT(t ? segment->lastPt() == pt : segment->pts()[0] == pt);
1501 if (!debugAlignedInner()) {
1502 return false;
1503 }
1504 if ((t ? segment->lastPt() : segment->pts()[0]) != pt) {
1505 return false;
1506 }
1507 const SkOpPtT* ptT = &this->fPtT;
1508 SkASSERT(t == ptT->fT);
1509 SkASSERT(pt == ptT->fPt);
1510 const SkOpPtT* test = ptT, * stopPtT = ptT;
1511 while ((test = test->next()) != stopPtT) {
1512 const SkOpSegment* other = test->segment();
1513 if (other == this->segment()) {
1514 continue;
1515 }
1516 if (!zero_or_one(test->fT)) {
1517 continue;
1518 }
1519 if ((test->fT ? other->lastPt() : other->pts()[0]) != pt) {
1520 return false;
1521 }
1522 }
1523 return this->fAligned;
1524 }
1525
1526 bool SkOpSpanBase::debugAlignedInner() const {
1527 // force the spans to share points and t values
1528 const SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT;
1529 const SkPoint& pt = ptT->fPt;
1530 do {
1531 if (ptT->fPt != pt) {
1532 return false;
1533 }
1534 const SkOpSpanBase* span = ptT->span();
1535 const SkOpPtT* test = ptT;
1536 do {
1537 if ((test = test->next()) == stopPtT) {
1538 break;
1539 }
1540 if (span == test->span() && !span->segment()->ptsDisjoint(*ptT, *tes t)) {
1541 return false;
1542 }
1543 } while (true);
1544 } while ((ptT = ptT->next()) != stopPtT);
1545 return true;
1546 }
1547
442 bool SkOpSpanBase::debugCoinEndLoopCheck() const { 1548 bool SkOpSpanBase::debugCoinEndLoopCheck() const {
443 int loop = 0; 1549 int loop = 0;
444 const SkOpSpanBase* next = this; 1550 const SkOpSpanBase* next = this;
445 SkOpSpanBase* nextCoin; 1551 SkOpSpanBase* nextCoin;
446 do { 1552 do {
447 nextCoin = next->fCoinEnd; 1553 nextCoin = next->fCoinEnd;
448 SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin); 1554 SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin);
449 for (int check = 1; check < loop - 1; ++check) { 1555 for (int check = 1; check < loop - 1; ++check) {
450 const SkOpSpanBase* checkCoin = this->fCoinEnd; 1556 const SkOpSpanBase* checkCoin = this->fCoinEnd;
451 const SkOpSpanBase* innerCoin = checkCoin; 1557 const SkOpSpanBase* innerCoin = checkCoin;
452 for (int inner = check + 1; inner < loop; ++inner) { 1558 for (int inner = check + 1; inner < loop; ++inner) {
453 innerCoin = innerCoin->fCoinEnd; 1559 innerCoin = innerCoin->fCoinEnd;
454 if (checkCoin == innerCoin) { 1560 if (checkCoin == innerCoin) {
455 SkDebugf("*** bad coincident end loop ***\n"); 1561 SkDebugf("*** bad coincident end loop ***\n");
456 return false; 1562 return false;
457 } 1563 }
458 } 1564 }
459 } 1565 }
460 ++loop; 1566 ++loop;
461 } while ((next = nextCoin) && next != this); 1567 } while ((next = nextCoin) && next != this);
462 return true; 1568 return true;
463 } 1569 }
464 1570
1571 bool SkOpSpanBase::debugContains(const SkOpSegment* segment) const {
1572 const SkOpPtT* start = &fPtT;
1573 const SkOpPtT* walk = start;
1574 while ((walk = walk->next()) != start) {
1575 if (walk->segment() == segment) {
1576 return true;
1577 }
1578 }
1579 return false;
1580 }
1581
1582 const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const {
1583 const SkOpSpanBase* end = *endPtr;
1584 SkASSERT(this->segment() == end->segment());
1585 const SkOpSpanBase* result;
1586 if (t() < end->t()) {
1587 result = this;
1588 } else {
1589 result = end;
1590 *endPtr = this;
1591 }
1592 return result->upCast();
1593 }
1594
465 void SkOpSpanBase::debugValidate() const { 1595 void SkOpSpanBase::debugValidate() const {
466 #if DEBUG_VALIDATE 1596 #if DEBUG_VALIDATE
467 const SkOpPtT* ptT = &fPtT; 1597 const SkOpPtT* ptT = &fPtT;
468 SkASSERT(ptT->span() == this); 1598 SkASSERT(ptT->span() == this);
469 do { 1599 do {
470 // SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt)); 1600 // SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt));
471 ptT->debugValidate(); 1601 ptT->debugValidate();
472 ptT = ptT->next(); 1602 ptT = ptT->next();
473 } while (ptT != &fPtT); 1603 } while (ptT != &fPtT);
474 SkASSERT(this->debugCoinEndLoopCheck()); 1604 SkASSERT(this->debugCoinEndLoopCheck());
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
524 ++count2; 1654 ++count2;
525 } 1655 }
526 #endif 1656 #endif
527 } 1657 }
528 SkASSERT(count == count2); 1658 SkASSERT(count == count2);
529 return count; 1659 return count;
530 } 1660 }
531 1661
532 #include "SkOpContour.h" 1662 #include "SkOpContour.h"
533 1663
1664 bool SkOpPtT::debugContains(const SkOpPtT* check) const {
1665 SkASSERT(this != check);
1666 const SkOpPtT* ptT = this;
1667 int links = 0;
1668 do {
1669 ptT = ptT->next();
1670 if (ptT == check) {
1671 return true;
1672 }
1673 ++links;
1674 const SkOpPtT* test = this;
1675 for (int index = 0; index < links; ++index) {
1676 if (ptT == test) {
1677 return false;
1678 }
1679 test = test->next();
1680 }
1681 } while (true);
1682 }
1683
1684 const SkOpPtT* SkOpPtT::debugContains(const SkOpSegment* check) const {
1685 SkASSERT(this->segment() != check);
1686 const SkOpPtT* ptT = this;
1687 int links = 0;
1688 do {
1689 ptT = ptT->next();
1690 if (ptT->segment() == check) {
1691 return ptT;
1692 }
1693 ++links;
1694 const SkOpPtT* test = this;
1695 for (int index = 0; index < links; ++index) {
1696 if (ptT == test) {
1697 return nullptr;
1698 }
1699 test = test->next();
1700 }
1701 } while (true);
1702 }
1703
534 int SkOpPtT::debugLoopLimit(bool report) const { 1704 int SkOpPtT::debugLoopLimit(bool report) const {
535 int loop = 0; 1705 int loop = 0;
536 const SkOpPtT* next = this; 1706 const SkOpPtT* next = this;
537 do { 1707 do {
538 for (int check = 1; check < loop - 1; ++check) { 1708 for (int check = 1; check < loop - 1; ++check) {
539 const SkOpPtT* checkPtT = this->fNext; 1709 const SkOpPtT* checkPtT = this->fNext;
540 const SkOpPtT* innerPtT = checkPtT; 1710 const SkOpPtT* innerPtT = checkPtT;
541 for (int inner = check + 1; inner < loop; ++inner) { 1711 for (int inner = check + 1; inner < loop; ++inner) {
542 innerPtT = innerPtT->fNext; 1712 innerPtT = innerPtT->fNext;
543 if (checkPtT == innerPtT) { 1713 if (checkPtT == innerPtT) {
544 if (report) { 1714 if (report) {
545 SkDebugf("*** bad ptT loop ***\n"); 1715 SkDebugf("*** bad ptT loop ***\n");
546 } 1716 }
547 return loop; 1717 return loop;
548 } 1718 }
549 } 1719 }
550 } 1720 }
551 ++loop; 1721 // there's nothing wrong with extremely large loop counts -- but this ma y appear to hang
1722 // by taking a very long time to figure out that no loop entry is a dupl icate
1723 // -- and it's likely that a large loop count is indicative of a bug som ewhere
1724 if (++loop > 1000) {
1725 SkDebugf("*** loop count exceeds 1000 ***\n");
1726 return 1000;
1727 }
552 } while ((next = next->fNext) && next != this); 1728 } while ((next = next->fNext) && next != this);
553 return 0; 1729 return 0;
554 } 1730 }
555 1731
556 void SkOpPtT::debugValidate() const { 1732 void SkOpPtT::debugValidate() const {
557 #if DEBUG_VALIDATE 1733 #if DEBUG_VALIDATE
558 SkOpGlobalState::Phase phase = contour()->globalState()->phase(); 1734 SkOpGlobalState::Phase phase = contour()->globalState()->phase();
559 if (phase == SkOpGlobalState::kIntersecting 1735 if (phase == SkOpGlobalState::kIntersecting
560 || phase == SkOpGlobalState::kFixWinding) { 1736 || phase == SkOpGlobalState::kFixWinding) {
561 return; 1737 return;
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
663 #endif 1839 #endif
664 SkPath::FillType fillType = path.getFillType(); 1840 SkPath::FillType fillType = path.getFillType();
665 SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInver seEvenOdd_FillType); 1841 SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInver seEvenOdd_FillType);
666 if (includeDeclaration) { 1842 if (includeDeclaration) {
667 SkDebugf(" SkPath %s;\n", name); 1843 SkDebugf(" SkPath %s;\n", name);
668 } 1844 }
669 SkDebugf(" %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[fillType]); 1845 SkDebugf(" %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[fillType]);
670 iter.setPath(path); 1846 iter.setPath(path);
671 showPathContours(iter, name); 1847 showPathContours(iter, name);
672 } 1848 }
OLDNEW
« no previous file with comments | « src/pathops/SkPathOpsDebug.h ('k') | src/pathops/SkPathOpsOp.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698