SkPathOpsDebug.cpp revision ab87d7abf1df007c90bef2e916294ca325d81c81
1/* 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 8#include "SkMutex.h" 9#include "SkOpCoincidence.h" 10#include "SkOpContour.h" 11#include "SkPath.h" 12#include "SkPathOpsDebug.h" 13#include "SkString.h" 14 15#undef FAIL_IF 16#define FAIL_IF(cond, coin) \ 17 do { if (cond) log->record(SkPathOpsDebug::kFail_Glitch, coin); } while (false) 18 19#undef FAIL_WITH_NULL_IF 20#define FAIL_WITH_NULL_IF(cond, span) \ 21 do { if (cond) log->record(SkPathOpsDebug::kFail_Glitch, span); } while (false) 22 23#undef RETURN_FALSE_IF 24#define RETURN_FALSE_IF(cond, span) \ 25 do { if (cond) log->record(SkPathOpsDebug::kReturnFalse_Glitch, span); \ 26 } while (false) 27 28class SkCoincidentSpans; 29 30#if DEBUG_VALIDATE 31extern bool FLAGS_runFail; 32#endif 33 34#if DEBUG_SORT 35int SkPathOpsDebug::gSortCountDefault = SK_MaxS32; 36int SkPathOpsDebug::gSortCount; 37#endif 38 39#if DEBUG_ACTIVE_OP 40const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor"}; 41#endif 42 43#if defined SK_DEBUG || !FORCE_RELEASE 44 45const char* SkPathOpsDebug::kLVerbStr[] = {"", "line", "quad", "cubic"}; 46 47int SkPathOpsDebug::gContourID = 0; 48int SkPathOpsDebug::gSegmentID = 0; 49 50bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray, 51 const SkOpSpanBase* span) { 52 for (int index = 0; index < chaseArray.count(); ++index) { 53 const SkOpSpanBase* entry = chaseArray[index]; 54 if (entry == span) { 55 return true; 56 } 57 } 58 return false; 59} 60#endif 61 62#if DEBUG_COIN 63 64SkPathOpsDebug::CoinDict SkPathOpsDebug::gCoinSumChangedDict; 65SkPathOpsDebug::CoinDict SkPathOpsDebug::gCoinSumVisitedDict; 66 67static const int kGlitchType_Count = SkPathOpsDebug::kUnalignedTail_Glitch + 1; 68 69struct SpanGlitch { 70 const SkOpSpanBase* fBase; 71 const SkOpSpanBase* fSuspect; 72 const SkOpSegment* fSegment; 73 const SkOpSegment* fOppSegment; 74 const SkOpPtT* fCoinSpan; 75 const SkOpPtT* fEndSpan; 76 const SkOpPtT* fOppSpan; 77 const SkOpPtT* fOppEndSpan; 78 double fStartT; 79 double fEndT; 80 double fOppStartT; 81 double fOppEndT; 82 SkPoint fPt; 83 SkPathOpsDebug::GlitchType fType; 84 85 void dumpType() const; 86}; 87 88struct SkPathOpsDebug::GlitchLog { 89 void init(const SkOpGlobalState* state) { 90 fGlobalState = state; 91 } 92 93 SpanGlitch* recordCommon(GlitchType type) { 94 SpanGlitch* glitch = fGlitches.push(); 95 glitch->fBase = nullptr; 96 glitch->fSuspect = nullptr; 97 glitch->fSegment = nullptr; 98 glitch->fOppSegment = nullptr; 99 glitch->fCoinSpan = nullptr; 100 glitch->fEndSpan = nullptr; 101 glitch->fOppSpan = nullptr; 102 glitch->fOppEndSpan = nullptr; 103 glitch->fStartT = SK_ScalarNaN; 104 glitch->fEndT = SK_ScalarNaN; 105 glitch->fOppStartT = SK_ScalarNaN; 106 glitch->fOppEndT = SK_ScalarNaN; 107 glitch->fPt = { SK_ScalarNaN, SK_ScalarNaN }; 108 glitch->fType = type; 109 return glitch; 110 } 111 112 void record(GlitchType type, const SkOpSpanBase* base, 113 const SkOpSpanBase* suspect = NULL) { 114 SpanGlitch* glitch = recordCommon(type); 115 glitch->fBase = base; 116 glitch->fSuspect = suspect; 117 } 118 119 void record(GlitchType type, const SkOpSpanBase* base, 120 const SkOpPtT* ptT) { 121 SpanGlitch* glitch = recordCommon(type); 122 glitch->fBase = base; 123 glitch->fCoinSpan = ptT; 124 } 125 126 void record(GlitchType type, const SkCoincidentSpans* coin, 127 const SkCoincidentSpans* opp = NULL) { 128 SpanGlitch* glitch = recordCommon(type); 129 glitch->fCoinSpan = coin->coinPtTStart(); 130 glitch->fEndSpan = coin->coinPtTEnd(); 131 if (opp) { 132 glitch->fOppSpan = opp->coinPtTStart(); 133 glitch->fOppEndSpan = opp->coinPtTEnd(); 134 } 135 } 136 137 void record(GlitchType type, const SkOpSpanBase* base, 138 const SkOpSegment* seg, double t, SkPoint pt) { 139 SpanGlitch* glitch = recordCommon(type); 140 glitch->fBase = base; 141 glitch->fSegment = seg; 142 glitch->fStartT = t; 143 glitch->fPt = pt; 144 } 145 146 void record(GlitchType type, const SkOpSpanBase* base, double t, 147 SkPoint pt) { 148 SpanGlitch* glitch = recordCommon(type); 149 glitch->fBase = base; 150 glitch->fStartT = t; 151 glitch->fPt = pt; 152 } 153 154 void record(GlitchType type, const SkCoincidentSpans* coin, 155 const SkOpPtT* coinSpan, const SkOpPtT* endSpan) { 156 SpanGlitch* glitch = recordCommon(type); 157 glitch->fCoinSpan = coin->coinPtTStart(); 158 glitch->fEndSpan = coin->coinPtTEnd(); 159 glitch->fEndSpan = endSpan; 160 glitch->fOppSpan = coinSpan; 161 glitch->fOppEndSpan = endSpan; 162 } 163 164 void record(GlitchType type, const SkCoincidentSpans* coin, 165 const SkOpSpanBase* base) { 166 SpanGlitch* glitch = recordCommon(type); 167 glitch->fBase = base; 168 glitch->fCoinSpan = coin->coinPtTStart(); 169 glitch->fEndSpan = coin->coinPtTEnd(); 170 } 171 172 void record(GlitchType type, const SkOpPtT* ptTS, const SkOpPtT* ptTE, 173 const SkOpPtT* oPtTS, const SkOpPtT* oPtTE) { 174 SpanGlitch* glitch = recordCommon(type); 175 glitch->fCoinSpan = ptTS; 176 glitch->fEndSpan = ptTE; 177 glitch->fOppSpan = oPtTS; 178 glitch->fOppEndSpan = oPtTE; 179 } 180 181 void record(GlitchType type, const SkOpSegment* seg, double startT, 182 double endT, const SkOpSegment* oppSeg, double oppStartT, double oppEndT) { 183 SpanGlitch* glitch = recordCommon(type); 184 glitch->fSegment = seg; 185 glitch->fStartT = startT; 186 glitch->fEndT = endT; 187 glitch->fOppSegment = oppSeg; 188 glitch->fOppStartT = oppStartT; 189 glitch->fOppEndT = oppEndT; 190 } 191 192 void record(GlitchType type, const SkOpSegment* seg, 193 const SkOpSpan* span) { 194 SpanGlitch* glitch = recordCommon(type); 195 glitch->fSegment = seg; 196 glitch->fBase = span; 197 } 198 199 void record(GlitchType type, double t, const SkOpSpanBase* span) { 200 SpanGlitch* glitch = recordCommon(type); 201 glitch->fStartT = t; 202 glitch->fBase = span; 203 } 204 205 void record(GlitchType type, const SkOpSegment* seg) { 206 SpanGlitch* glitch = recordCommon(type); 207 glitch->fSegment = seg; 208 } 209 210 void record(GlitchType type, const SkCoincidentSpans* coin, 211 const SkOpPtT* ptT) { 212 SpanGlitch* glitch = recordCommon(type); 213 glitch->fCoinSpan = coin->coinPtTStart(); 214 glitch->fEndSpan = ptT; 215 } 216 217 SkTDArray<SpanGlitch> fGlitches; 218 const SkOpGlobalState* fGlobalState; 219}; 220 221 222void SkPathOpsDebug::CoinDict::add(const SkPathOpsDebug::CoinDict& dict) { 223 int count = dict.fDict.count(); 224 for (int index = 0; index < count; ++index) { 225 this->add(dict.fDict[index]); 226 } 227} 228 229void SkPathOpsDebug::CoinDict::add(const CoinDictEntry& key) { 230 int count = fDict.count(); 231 for (int index = 0; index < count; ++index) { 232 CoinDictEntry* entry = &fDict[index]; 233 if (entry->fIteration == key.fIteration && entry->fLineNumber == key.fLineNumber) { 234 SkASSERT(!strcmp(entry->fFunctionName, key.fFunctionName)); 235 if (entry->fGlitchType == kUninitialized_Glitch) { 236 entry->fGlitchType = key.fGlitchType; 237 } 238 return; 239 } 240 } 241 *fDict.append() = key; 242} 243 244#endif 245 246#if DEBUG_COIN 247static void missing_coincidence(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) { 248 const SkOpContour* contour = contourList; 249 // bool result = false; 250 do { 251 /* result |= */ contour->debugMissingCoincidence(glitches); 252 } while ((contour = contour->next())); 253 return; 254} 255 256static void move_multiples(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) { 257 const SkOpContour* contour = contourList; 258 do { 259 if (contour->debugMoveMultiples(glitches), false) { 260 return; 261 } 262 } while ((contour = contour->next())); 263 return; 264} 265 266static void move_nearby(SkPathOpsDebug::GlitchLog* glitches, const SkOpContourHead* contourList) { 267 const SkOpContour* contour = contourList; 268 do { 269 contour->debugMoveNearby(glitches); 270 } while ((contour = contour->next())); 271} 272 273 274#endif 275 276#if DEBUG_COIN 277void SkOpGlobalState::debugAddToCoinChangedDict() { 278 279#if DEBUG_COINCIDENCE 280 CheckHealth(contourList); 281#endif 282 // see if next coincident operation makes a change; if so, record it 283 SkPathOpsDebug::GlitchLog glitches; 284 const char* funcName = fCoinDictEntry.fFunctionName; 285 if (!strcmp("calc_angles", funcName)) { 286 ; 287 } else if (!strcmp("missing_coincidence", funcName)) { 288 missing_coincidence(&glitches, fContourHead); 289 } else if (!strcmp("move_multiples", funcName)) { 290 move_multiples(&glitches, fContourHead); 291 } else if (!strcmp("move_nearby", funcName)) { 292 move_nearby(&glitches, fContourHead); 293 } else if (!strcmp("addExpanded", funcName)) { 294 fCoincidence->debugAddExpanded(&glitches); 295 } else if (!strcmp("addMissing", funcName)) { 296 bool added; 297 fCoincidence->debugAddMissing(&glitches, &added); 298 } else if (!strcmp("addEndMovedSpans", funcName)) { 299 fCoincidence->debugAddEndMovedSpans(&glitches); 300 } else if (!strcmp("correctEnds", funcName)) { 301 fCoincidence->debugCorrectEnds(&glitches); 302 } else if (!strcmp("expand", funcName)) { 303 fCoincidence->debugExpand(&glitches); 304 } else if (!strcmp("findOverlaps", funcName)) { 305 ; 306 } else if (!strcmp("mark", funcName)) { 307 fCoincidence->debugMark(&glitches); 308 } else if (!strcmp("apply", funcName)) { 309 ; 310 } else { 311 SkASSERT(0); // add missing case 312 } 313 if (glitches.fGlitches.count()) { 314 fCoinDictEntry.fGlitchType = glitches.fGlitches[0].fType; 315 } 316 fCoinChangedDict.add(fCoinDictEntry); 317} 318#endif 319 320void SkPathOpsDebug::ShowActiveSpans(SkOpContourHead* contourList) { 321#if DEBUG_ACTIVE_SPANS 322 SkOpContour* contour = contourList; 323 do { 324 contour->debugShowActiveSpans(); 325 } while ((contour = contour->next())); 326#endif 327} 328 329#if DEBUG_COINCIDENCE || DEBUG_COIN 330void SkPathOpsDebug::CheckHealth(SkOpContourHead* contourList) { 331#if DEBUG_COINCIDENCE 332 contourList->globalState()->debugSetCheckHealth(true); 333#endif 334#if DEBUG_COIN 335 GlitchLog glitches; 336 const SkOpContour* contour = contourList; 337 const SkOpCoincidence* coincidence = contour->globalState()->coincidence(); 338 coincidence->debugCheckValid(&glitches); // don't call validate; spans may be inconsistent 339 do { 340 contour->debugCheckHealth(&glitches); 341 contour->debugMissingCoincidence(&glitches); 342 } while ((contour = contour->next())); 343 bool added; 344 coincidence->debugAddMissing(&glitches, &added); 345 coincidence->debugExpand(&glitches); 346 coincidence->debugAddExpanded(&glitches); 347 coincidence->debugMark(&glitches); 348 unsigned mask = 0; 349 for (int index = 0; index < glitches.fGlitches.count(); ++index) { 350 const SpanGlitch& glitch = glitches.fGlitches[index]; 351 mask |= 1 << glitch.fType; 352 } 353 for (int index = 0; index < kGlitchType_Count; ++index) { 354 SkDebugf(mask & (1 << index) ? "x" : "-"); 355 } 356 for (int index = 0; index < glitches.fGlitches.count(); ++index) { 357 const SpanGlitch& glitch = glitches.fGlitches[index]; 358 SkDebugf("%02d: ", index); 359 if (glitch.fBase) { 360 SkDebugf(" seg/base=%d/%d", glitch.fBase->segment()->debugID(), 361 glitch.fBase->debugID()); 362 } 363 if (glitch.fSuspect) { 364 SkDebugf(" seg/base=%d/%d", glitch.fSuspect->segment()->debugID(), 365 glitch.fSuspect->debugID()); 366 } 367 if (glitch.fSegment) { 368 SkDebugf(" segment=%d", glitch.fSegment->debugID()); 369 } 370 if (glitch.fCoinSpan) { 371 SkDebugf(" coinSeg/Span/PtT=%d/%d/%d", glitch.fCoinSpan->segment()->debugID(), 372 glitch.fCoinSpan->span()->debugID(), glitch.fCoinSpan->debugID()); 373 } 374 if (glitch.fEndSpan) { 375 SkDebugf(" endSpan=%d", glitch.fEndSpan->debugID()); 376 } 377 if (glitch.fOppSpan) { 378 SkDebugf(" oppSeg/Span/PtT=%d/%d/%d", glitch.fOppSpan->segment()->debugID(), 379 glitch.fOppSpan->span()->debugID(), glitch.fOppSpan->debugID()); 380 } 381 if (glitch.fOppEndSpan) { 382 SkDebugf(" oppEndSpan=%d", glitch.fOppEndSpan->debugID()); 383 } 384 if (!SkScalarIsNaN(glitch.fStartT)) { 385 SkDebugf(" startT=%g", glitch.fStartT); 386 } 387 if (!SkScalarIsNaN(glitch.fEndT)) { 388 SkDebugf(" endT=%g", glitch.fEndT); 389 } 390 if (glitch.fOppSegment) { 391 SkDebugf(" segment=%d", glitch.fOppSegment->debugID()); 392 } 393 if (!SkScalarIsNaN(glitch.fOppStartT)) { 394 SkDebugf(" oppStartT=%g", glitch.fOppStartT); 395 } 396 if (!SkScalarIsNaN(glitch.fOppEndT)) { 397 SkDebugf(" oppEndT=%g", glitch.fOppEndT); 398 } 399 if (!SkScalarIsNaN(glitch.fPt.fX) || !SkScalarIsNaN(glitch.fPt.fY)) { 400 SkDebugf(" pt=%g,%g", glitch.fPt.fX, glitch.fPt.fY); 401 } 402 DumpGlitchType(glitch.fType); 403 SkDebugf("\n"); 404 } 405#if DEBUG_COINCIDENCE 406 contourList->globalState()->debugSetCheckHealth(false); 407#endif 408#if 01 && DEBUG_ACTIVE_SPANS 409// SkDebugf("active after %s:\n", id); 410 ShowActiveSpans(contourList); 411#endif 412#endif 413} 414#endif 415 416#if DEBUG_COIN 417void SkPathOpsDebug::DumpGlitchType(GlitchType glitchType) { 418 switch (glitchType) { 419 case kAddCorruptCoin_Glitch: SkDebugf(" AddCorruptCoin"); break; 420 case kAddExpandedCoin_Glitch: SkDebugf(" AddExpandedCoin"); break; 421 case kAddExpandedFail_Glitch: SkDebugf(" AddExpandedFail"); break; 422 case kAddIfCollapsed_Glitch: SkDebugf(" AddIfCollapsed"); break;; break; 423 case kAddIfMissingCoin_Glitch: SkDebugf(" AddIfMissingCoin"); break; 424 case kAddMissingCoin_Glitch: SkDebugf(" AddMissingCoin"); break; 425 case kAddMissingExtend_Glitch: SkDebugf(" AddMissingExtend"); break; 426 case kAddOrOverlap_Glitch: SkDebugf(" AAddOrOverlap"); break; 427 case kCollapsedCoin_Glitch: SkDebugf(" CollapsedCoin"); break; 428 case kCollapsedDone_Glitch: SkDebugf(" CollapsedDone"); break; 429 case kCollapsedOppValue_Glitch: SkDebugf(" CollapsedOppValue"); break; 430 case kCollapsedSpan_Glitch: SkDebugf(" CollapsedSpan"); break; 431 case kCollapsedWindValue_Glitch: SkDebugf(" CollapsedWindValue"); break; 432 case kCorrectEnd_Glitch: SkDebugf(" CorrectEnd"); break; 433 case kDeletedCoin_Glitch: SkDebugf(" DeletedCoin"); break; 434 case kExpandCoin_Glitch: SkDebugf(" ExpandCoin"); break; 435 case kFail_Glitch: SkDebugf(" Fail"); break; 436 case kMarkCoinEnd_Glitch: SkDebugf(" MarkCoinEnd"); break; 437 case kMarkCoinInsert_Glitch: SkDebugf(" MarkCoinInsert"); break; 438 case kMarkCoinMissing_Glitch: SkDebugf(" MarkCoinMissing"); break; 439 case kMarkCoinStart_Glitch: SkDebugf(" MarkCoinStart"); break; 440 case kMergeContained_Glitch: SkDebugf(" MergeContained"); break; 441 case kMergeMatches_Glitch: SkDebugf(" MergeMatches"); break; 442 case kMissingCoin_Glitch: SkDebugf(" MissingCoin"); break; 443 case kMissingDone_Glitch: SkDebugf(" MissingDone"); break; 444 case kMissingIntersection_Glitch: SkDebugf(" MissingIntersection"); break; 445 case kMoveMultiple_Glitch: SkDebugf(" MoveMultiple"); break; 446 case kMoveNearbyClearAll_Glitch: SkDebugf(" MoveNearbyClearAll"); break; 447 case kMoveNearbyClearAll2_Glitch: SkDebugf(" MoveNearbyClearAll2"); break; 448 case kMoveNearbyMerge_Glitch: SkDebugf(" MoveNearbyMerge"); break; 449 case kMoveNearbyMergeFinal_Glitch: SkDebugf(" MoveNearbyMergeFinal"); break; 450 case kMoveNearbyRelease_Glitch: SkDebugf(" MoveNearbyRelease"); break; 451 case kMoveNearbyReleaseFinal_Glitch: SkDebugf(" MoveNearbyReleaseFinal"); break; 452 case kReleasedSpan_Glitch: SkDebugf(" ReleasedSpan"); break; 453 case kReturnFalse_Glitch: SkDebugf(" ReturnFalse"); break; 454 case kUnaligned_Glitch: SkDebugf(" Unaligned"); break; 455 case kUnalignedHead_Glitch: SkDebugf(" UnalignedHead"); break; 456 case kUnalignedTail_Glitch: SkDebugf(" UnalignedTail"); break; 457 case kUninitialized_Glitch: break; 458 default: SkASSERT(0); 459 } 460} 461#endif 462 463#if defined SK_DEBUG || !FORCE_RELEASE 464void SkPathOpsDebug::MathematicaIze(char* str, size_t bufferLen) { 465 size_t len = strlen(str); 466 bool num = false; 467 for (size_t idx = 0; idx < len; ++idx) { 468 if (num && str[idx] == 'e') { 469 if (len + 2 >= bufferLen) { 470 return; 471 } 472 memmove(&str[idx + 2], &str[idx + 1], len - idx); 473 str[idx] = '*'; 474 str[idx + 1] = '^'; 475 ++len; 476 } 477 num = str[idx] >= '0' && str[idx] <= '9'; 478 } 479} 480 481#if DEBUG_VALIDATE 482void SkPathOpsDebug::SetPhase(SkOpContourHead* contourList, CoinID next, 483 int lineNumber, SkOpPhase phase) { 484 AddedCoin(contourList, next, 0, lineNumber); 485 contourList->globalState()->setPhase(phase); 486} 487#endif 488 489bool SkPathOpsDebug::ValidWind(int wind) { 490 return wind > SK_MinS32 + 0xFFFF && wind < SK_MaxS32 - 0xFFFF; 491} 492 493void SkPathOpsDebug::WindingPrintf(int wind) { 494 if (wind == SK_MinS32) { 495 SkDebugf("?"); 496 } else { 497 SkDebugf("%d", wind); 498 } 499} 500#endif // defined SK_DEBUG || !FORCE_RELEASE 501 502 503#if DEBUG_SHOW_TEST_NAME 504void* SkPathOpsDebug::CreateNameStr() { return new char[DEBUG_FILENAME_STRING_LENGTH]; } 505 506void SkPathOpsDebug::DeleteNameStr(void* v) { delete[] reinterpret_cast<char*>(v); } 507 508void SkPathOpsDebug::BumpTestName(char* test) { 509 char* num = test + strlen(test); 510 while (num[-1] >= '0' && num[-1] <= '9') { 511 --num; 512 } 513 if (num[0] == '\0') { 514 return; 515 } 516 int dec = atoi(num); 517 if (dec == 0) { 518 return; 519 } 520 ++dec; 521 SK_SNPRINTF(num, DEBUG_FILENAME_STRING_LENGTH - (num - test), "%d", dec); 522} 523#endif 524 525static void show_function_header(const char* functionName) { 526 SkDebugf("\nstatic void %s(skiatest::Reporter* reporter, const char* filename) {\n", functionName); 527 if (strcmp("skphealth_com76", functionName) == 0) { 528 SkDebugf("found it\n"); 529 } 530} 531 532static const char* gOpStrs[] = { 533 "kDifference_SkPathOp", 534 "kIntersect_SkPathOp", 535 "kUnion_SkPathOp", 536 "kXOR_PathOp", 537 "kReverseDifference_SkPathOp", 538}; 539 540const char* SkPathOpsDebug::OpStr(SkPathOp op) { 541 return gOpStrs[op]; 542} 543 544static void show_op(SkPathOp op, const char* pathOne, const char* pathTwo) { 545 SkDebugf(" testPathOp(reporter, %s, %s, %s, filename);\n", pathOne, pathTwo, gOpStrs[op]); 546 SkDebugf("}\n"); 547} 548 549SK_DECLARE_STATIC_MUTEX(gTestMutex); 550 551void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp, 552 const char* testName) { 553 SkAutoMutexAcquire ac(gTestMutex); 554 show_function_header(testName); 555 ShowOnePath(a, "path", true); 556 ShowOnePath(b, "pathB", true); 557 show_op(shapeOp, "path", "pathB"); 558} 559 560#include "SkPathOpsTypes.h" 561#include "SkIntersectionHelper.h" 562#include "SkIntersections.h" 563 564#if DEBUG_COIN 565 566SK_DECLARE_STATIC_MUTEX(gCoinDictMutex); 567 568void SkOpGlobalState::debugAddToGlobalCoinDicts() { 569 SkAutoMutexAcquire ac(&gCoinDictMutex); 570 SkPathOpsDebug::gCoinSumChangedDict.add(fCoinChangedDict); 571 SkPathOpsDebug::gCoinSumVisitedDict.add(fCoinVisitedDict); 572} 573 574#endif 575 576#if DEBUG_T_SECT_LOOP_COUNT 577void SkOpGlobalState::debugAddLoopCount(SkIntersections* i, const SkIntersectionHelper& wt, 578 const SkIntersectionHelper& wn) { 579 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) { 580 SkIntersections::DebugLoop looper = (SkIntersections::DebugLoop) index; 581 if (fDebugLoopCount[index] >= i->debugLoopCount(looper)) { 582 continue; 583 } 584 fDebugLoopCount[index] = i->debugLoopCount(looper); 585 fDebugWorstVerb[index * 2] = wt.segment()->verb(); 586 fDebugWorstVerb[index * 2 + 1] = wn.segment()->verb(); 587 sk_bzero(&fDebugWorstPts[index * 8], sizeof(SkPoint) * 8); 588 memcpy(&fDebugWorstPts[index * 2 * 4], wt.pts(), 589 (SkPathOpsVerbToPoints(wt.segment()->verb()) + 1) * sizeof(SkPoint)); 590 memcpy(&fDebugWorstPts[(index * 2 + 1) * 4], wn.pts(), 591 (SkPathOpsVerbToPoints(wn.segment()->verb()) + 1) * sizeof(SkPoint)); 592 fDebugWorstWeight[index * 2] = wt.weight(); 593 fDebugWorstWeight[index * 2 + 1] = wn.weight(); 594 } 595 i->debugResetLoopCount(); 596} 597 598void SkOpGlobalState::debugDoYourWorst(SkOpGlobalState* local) { 599 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) { 600 if (fDebugLoopCount[index] >= local->fDebugLoopCount[index]) { 601 continue; 602 } 603 fDebugLoopCount[index] = local->fDebugLoopCount[index]; 604 fDebugWorstVerb[index * 2] = local->fDebugWorstVerb[index * 2]; 605 fDebugWorstVerb[index * 2 + 1] = local->fDebugWorstVerb[index * 2 + 1]; 606 memcpy(&fDebugWorstPts[index * 2 * 4], &local->fDebugWorstPts[index * 2 * 4], 607 sizeof(SkPoint) * 8); 608 fDebugWorstWeight[index * 2] = local->fDebugWorstWeight[index * 2]; 609 fDebugWorstWeight[index * 2 + 1] = local->fDebugWorstWeight[index * 2 + 1]; 610 } 611 local->debugResetLoopCounts(); 612} 613 614static void dump_curve(SkPath::Verb verb, const SkPoint& pts, float weight) { 615 if (!verb) { 616 return; 617 } 618 const char* verbs[] = { "", "line", "quad", "conic", "cubic" }; 619 SkDebugf("%s: {{", verbs[verb]); 620 int ptCount = SkPathOpsVerbToPoints(verb); 621 for (int index = 0; index <= ptCount; ++index) { 622 SkDPoint::Dump((&pts)[index]); 623 if (index < ptCount - 1) { 624 SkDebugf(", "); 625 } 626 } 627 SkDebugf("}"); 628 if (weight != 1) { 629 SkDebugf(", "); 630 if (weight == floorf(weight)) { 631 SkDebugf("%.0f", weight); 632 } else { 633 SkDebugf("%1.9gf", weight); 634 } 635 } 636 SkDebugf("}\n"); 637} 638 639void SkOpGlobalState::debugLoopReport() { 640 const char* loops[] = { "iterations", "coinChecks", "perpCalcs" }; 641 SkDebugf("\n"); 642 for (int index = 0; index < (int) SK_ARRAY_COUNT(fDebugLoopCount); ++index) { 643 SkDebugf("%s: %d\n", loops[index], fDebugLoopCount[index]); 644 dump_curve(fDebugWorstVerb[index * 2], fDebugWorstPts[index * 2 * 4], 645 fDebugWorstWeight[index * 2]); 646 dump_curve(fDebugWorstVerb[index * 2 + 1], fDebugWorstPts[(index * 2 + 1) * 4], 647 fDebugWorstWeight[index * 2 + 1]); 648 } 649} 650 651void SkOpGlobalState::debugResetLoopCounts() { 652 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount)); 653 sk_bzero(fDebugWorstVerb, sizeof(fDebugWorstVerb)); 654 sk_bzero(fDebugWorstPts, sizeof(fDebugWorstPts)); 655 sk_bzero(fDebugWorstWeight, sizeof(fDebugWorstWeight)); 656} 657#endif 658 659#ifdef SK_DEBUG 660bool SkOpGlobalState::debugRunFail() const { 661#if DEBUG_VALIDATE 662 return FLAGS_runFail; 663#else 664 return false; 665#endif 666} 667#endif 668 669// this is const so it can be called by const methods that overwise don't alter state 670#if DEBUG_VALIDATE || DEBUG_COIN 671void SkOpGlobalState::debugSetPhase(const char* funcName DEBUG_COIN_DECLARE_PARAMS()) const { 672 auto writable = const_cast<SkOpGlobalState*>(this); 673#if DEBUG_VALIDATE 674 writable->setPhase(phase); 675#endif 676#if DEBUG_COIN 677 SkPathOpsDebug::CoinDictEntry* entry = &writable->fCoinDictEntry; 678 writable->fPreviousFuncName = entry->fFunctionName; 679 entry->fIteration = iteration; 680 entry->fLineNumber = lineNo; 681 entry->fGlitchType = SkPathOpsDebug::kUninitialized_Glitch; 682 entry->fFunctionName = funcName; 683 writable->fCoinVisitedDict.add(*entry); 684 writable->debugAddToCoinChangedDict(); 685#endif 686} 687#endif 688 689#if DEBUG_T_SECT_LOOP_COUNT 690void SkIntersections::debugBumpLoopCount(DebugLoop index) { 691 fDebugLoopCount[index]++; 692} 693 694int SkIntersections::debugLoopCount(DebugLoop index) const { 695 return fDebugLoopCount[index]; 696} 697 698void SkIntersections::debugResetLoopCount() { 699 sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount)); 700} 701#endif 702 703#include "SkPathOpsCubic.h" 704#include "SkPathOpsQuad.h" 705 706SkDCubic SkDQuad::debugToCubic() const { 707 SkDCubic cubic; 708 cubic[0] = fPts[0]; 709 cubic[2] = fPts[1]; 710 cubic[3] = fPts[2]; 711 cubic[1].fX = (cubic[0].fX + cubic[2].fX * 2) / 3; 712 cubic[1].fY = (cubic[0].fY + cubic[2].fY * 2) / 3; 713 cubic[2].fX = (cubic[3].fX + cubic[2].fX * 2) / 3; 714 cubic[2].fY = (cubic[3].fY + cubic[2].fY * 2) / 3; 715 return cubic; 716} 717 718void SkDRect::debugInit() { 719 fLeft = fTop = fRight = fBottom = SK_ScalarNaN; 720} 721 722#include "SkOpAngle.h" 723#include "SkOpSegment.h" 724 725#if DEBUG_COIN 726// commented-out lines keep this in sync with addT() 727 const SkOpPtT* SkOpSegment::debugAddT(double t, SkPathOpsDebug::GlitchLog* log) const { 728 debugValidate(); 729 SkPoint pt = this->ptAtT(t); 730 const SkOpSpanBase* span = &fHead; 731 do { 732 const SkOpPtT* result = span->ptT(); 733 if (t == result->fT || this->match(result, this, t, pt)) { 734// span->bumpSpanAdds(); 735 return result; 736 } 737 if (t < result->fT) { 738 const SkOpSpan* prev = result->span()->prev(); 739 FAIL_WITH_NULL_IF(!prev, span); 740 // marks in global state that new op span has been allocated 741 this->globalState()->setAllocatedOpSpan(); 742// span->init(this, prev, t, pt); 743 this->debugValidate(); 744// #if DEBUG_ADD_T 745// SkDebugf("%s insert t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t, 746// span->segment()->debugID(), span->debugID()); 747// #endif 748// span->bumpSpanAdds(); 749 return nullptr; 750 } 751 FAIL_WITH_NULL_IF(span != &fTail, span); 752 } while ((span = span->upCast()->next())); 753 SkASSERT(0); 754 return nullptr; // we never get here, but need this to satisfy compiler 755} 756#endif 757 758#if DEBUG_ANGLE 759void SkOpSegment::debugCheckAngleCoin() const { 760 const SkOpSpanBase* base = &fHead; 761 const SkOpSpan* span; 762 do { 763 const SkOpAngle* angle = base->fromAngle(); 764 if (angle && angle->debugCheckCoincidence()) { 765 angle->debugCheckNearCoincidence(); 766 } 767 if (base->final()) { 768 break; 769 } 770 span = base->upCast(); 771 angle = span->toAngle(); 772 if (angle && angle->debugCheckCoincidence()) { 773 angle->debugCheckNearCoincidence(); 774 } 775 } while ((base = span->next())); 776} 777#endif 778 779#if DEBUG_COIN 780// this mimics the order of the checks in handle coincidence 781void SkOpSegment::debugCheckHealth(SkPathOpsDebug::GlitchLog* glitches) const { 782 debugMoveMultiples(glitches); 783 debugMoveNearby(glitches); 784 debugMissingCoincidence(glitches); 785} 786 787// commented-out lines keep this in sync with clearAll() 788void SkOpSegment::debugClearAll(SkPathOpsDebug::GlitchLog* glitches) const { 789 const SkOpSpan* span = &fHead; 790 do { 791 this->debugClearOne(span, glitches); 792 } while ((span = span->next()->upCastable())); 793 this->globalState()->coincidence()->debugRelease(glitches, this); 794} 795 796// commented-out lines keep this in sync with clearOne() 797void SkOpSegment::debugClearOne(const SkOpSpan* span, SkPathOpsDebug::GlitchLog* glitches) const { 798 if (span->windValue()) glitches->record(SkPathOpsDebug::kCollapsedWindValue_Glitch, span); 799 if (span->oppValue()) glitches->record(SkPathOpsDebug::kCollapsedOppValue_Glitch, span); 800 if (!span->done()) glitches->record(SkPathOpsDebug::kCollapsedDone_Glitch, span); 801} 802#endif 803 804SkOpAngle* SkOpSegment::debugLastAngle() { 805 SkOpAngle* result = nullptr; 806 SkOpSpan* span = this->head(); 807 do { 808 if (span->toAngle()) { 809 SkASSERT(!result); 810 result = span->toAngle(); 811 } 812 } while ((span = span->next()->upCastable())); 813 SkASSERT(result); 814 return result; 815} 816 817#if DEBUG_COIN 818// commented-out lines keep this in sync with ClearVisited 819void SkOpSegment::DebugClearVisited(const SkOpSpanBase* span) { 820 // reset visited flag back to false 821 do { 822 const SkOpPtT* ptT = span->ptT(), * stopPtT = ptT; 823 while ((ptT = ptT->next()) != stopPtT) { 824 const SkOpSegment* opp = ptT->segment(); 825 opp->resetDebugVisited(); 826 } 827 } while (!span->final() && (span = span->upCast()->next())); 828} 829#endif 830 831#if DEBUG_COIN 832// commented-out lines keep this in sync with missingCoincidence() 833// look for pairs of undetected coincident curves 834// assumes that segments going in have visited flag clear 835// Even though pairs of curves correct detect coincident runs, a run may be missed 836// if the coincidence is a product of multiple intersections. For instance, given 837// curves A, B, and C: 838// A-B intersect at a point 1; A-C and B-C intersect at point 2, so near 839// the end of C that the intersection is replaced with the end of C. 840// Even though A-B correctly do not detect an intersection at point 2, 841// the resulting run from point 1 to point 2 is coincident on A and B. 842void SkOpSegment::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const { 843 if (this->done()) { 844 return; 845 } 846 const SkOpSpan* prior = nullptr; 847 const SkOpSpanBase* spanBase = &fHead; 848// bool result = false; 849 do { 850 const SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT; 851 SkASSERT(ptT->span() == spanBase); 852 while ((ptT = ptT->next()) != spanStopPtT) { 853 if (ptT->deleted()) { 854 continue; 855 } 856 const SkOpSegment* opp = ptT->span()->segment(); 857 if (opp->done()) { 858 continue; 859 } 860 // when opp is encounted the 1st time, continue; on 2nd encounter, look for coincidence 861 if (!opp->debugVisited()) { 862 continue; 863 } 864 if (spanBase == &fHead) { 865 continue; 866 } 867 if (ptT->segment() == this) { 868 continue; 869 } 870 const SkOpSpan* span = spanBase->upCastable(); 871 // FIXME?: this assumes that if the opposite segment is coincident then no more 872 // coincidence needs to be detected. This may not be true. 873 if (span && span->segment() != opp && span->containsCoincidence(opp)) { // debug has additional condition since it may be called before inner duplicate points have been deleted 874 continue; 875 } 876 if (spanBase->segment() != opp && spanBase->containsCoinEnd(opp)) { // debug has additional condition since it may be called before inner duplicate points have been deleted 877 continue; 878 } 879 const SkOpPtT* priorPtT = nullptr, * priorStopPtT; 880 // find prior span containing opp segment 881 const SkOpSegment* priorOpp = nullptr; 882 const SkOpSpan* priorTest = spanBase->prev(); 883 while (!priorOpp && priorTest) { 884 priorStopPtT = priorPtT = priorTest->ptT(); 885 while ((priorPtT = priorPtT->next()) != priorStopPtT) { 886 if (priorPtT->deleted()) { 887 continue; 888 } 889 const SkOpSegment* segment = priorPtT->span()->segment(); 890 if (segment == opp) { 891 prior = priorTest; 892 priorOpp = opp; 893 break; 894 } 895 } 896 priorTest = priorTest->prev(); 897 } 898 if (!priorOpp) { 899 continue; 900 } 901 if (priorPtT == ptT) { 902 continue; 903 } 904 const SkOpPtT* oppStart = prior->ptT(); 905 const SkOpPtT* oppEnd = spanBase->ptT(); 906 bool swapped = priorPtT->fT > ptT->fT; 907 if (swapped) { 908 SkTSwap(priorPtT, ptT); 909 SkTSwap(oppStart, oppEnd); 910 } 911 const SkOpCoincidence* coincidence = this->globalState()->coincidence(); 912 const SkOpPtT* rootPriorPtT = priorPtT->span()->ptT(); 913 const SkOpPtT* rootPtT = ptT->span()->ptT(); 914 const SkOpPtT* rootOppStart = oppStart->span()->ptT(); 915 const SkOpPtT* rootOppEnd = oppEnd->span()->ptT(); 916 if (coincidence->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) { 917 goto swapBack; 918 } 919 if (testForCoincidence(rootPriorPtT, rootPtT, prior, spanBase, opp)) { 920 // mark coincidence 921#if DEBUG_COINCIDENCE_VERBOSE 922// SkDebugf("%s coinSpan=%d endSpan=%d oppSpan=%d oppEndSpan=%d\n", __FUNCTION__, 923// rootPriorPtT->debugID(), rootPtT->debugID(), rootOppStart->debugID(), 924// rootOppEnd->debugID()); 925#endif 926 log->record(SkPathOpsDebug::kMissingCoin_Glitch, priorPtT, ptT, oppStart, oppEnd); 927 // coincidences->add(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd); 928 // } 929#if DEBUG_COINCIDENCE 930// SkASSERT(coincidences->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd); 931#endif 932 // result = true; 933 } 934 swapBack: 935 if (swapped) { 936 SkTSwap(priorPtT, ptT); 937 } 938 } 939 } while ((spanBase = spanBase->final() ? nullptr : spanBase->upCast()->next())); 940 DebugClearVisited(&fHead); 941 return; 942} 943 944// commented-out lines keep this in sync with moveMultiples() 945// if a span has more than one intersection, merge the other segments' span as needed 946void SkOpSegment::debugMoveMultiples(SkPathOpsDebug::GlitchLog* glitches) const { 947 debugValidate(); 948 const SkOpSpanBase* test = &fHead; 949 do { 950 int addCount = test->spanAddsCount(); 951 SkASSERT(addCount >= 1); 952 if (addCount == 1) { 953 continue; 954 } 955 const SkOpPtT* startPtT = test->ptT(); 956 const SkOpPtT* testPtT = startPtT; 957 do { // iterate through all spans associated with start 958 const SkOpSpanBase* oppSpan = testPtT->span(); 959 if (oppSpan->spanAddsCount() == addCount) { 960 continue; 961 } 962 if (oppSpan->deleted()) { 963 continue; 964 } 965 const SkOpSegment* oppSegment = oppSpan->segment(); 966 if (oppSegment == this) { 967 continue; 968 } 969 // find range of spans to consider merging 970 const SkOpSpanBase* oppPrev = oppSpan; 971 const SkOpSpanBase* oppFirst = oppSpan; 972 while ((oppPrev = oppPrev->prev())) { 973 if (!roughly_equal(oppPrev->t(), oppSpan->t())) { 974 break; 975 } 976 if (oppPrev->spanAddsCount() == addCount) { 977 continue; 978 } 979 if (oppPrev->deleted()) { 980 continue; 981 } 982 oppFirst = oppPrev; 983 } 984 const SkOpSpanBase* oppNext = oppSpan; 985 const SkOpSpanBase* oppLast = oppSpan; 986 while ((oppNext = oppNext->final() ? nullptr : oppNext->upCast()->next())) { 987 if (!roughly_equal(oppNext->t(), oppSpan->t())) { 988 break; 989 } 990 if (oppNext->spanAddsCount() == addCount) { 991 continue; 992 } 993 if (oppNext->deleted()) { 994 continue; 995 } 996 oppLast = oppNext; 997 } 998 if (oppFirst == oppLast) { 999 continue; 1000 } 1001 const SkOpSpanBase* oppTest = oppFirst; 1002 do { 1003 if (oppTest == oppSpan) { 1004 continue; 1005 } 1006 // check to see if the candidate meets specific criteria: 1007 // it contains spans of segments in test's loop but not including 'this' 1008 const SkOpPtT* oppStartPtT = oppTest->ptT(); 1009 const SkOpPtT* oppPtT = oppStartPtT; 1010 while ((oppPtT = oppPtT->next()) != oppStartPtT) { 1011 const SkOpSegment* oppPtTSegment = oppPtT->segment(); 1012 if (oppPtTSegment == this) { 1013 goto tryNextSpan; 1014 } 1015 const SkOpPtT* matchPtT = startPtT; 1016 do { 1017 if (matchPtT->segment() == oppPtTSegment) { 1018 goto foundMatch; 1019 } 1020 } while ((matchPtT = matchPtT->next()) != startPtT); 1021 goto tryNextSpan; 1022 foundMatch: // merge oppTest and oppSpan 1023 oppSegment->debugValidate(); 1024 oppTest->debugMergeMatches(glitches, oppSpan); 1025 oppTest->debugAddOpp(glitches, oppSpan); 1026 oppSegment->debugValidate(); 1027 goto checkNextSpan; 1028 } 1029 tryNextSpan: 1030 ; 1031 } while (oppTest != oppLast && (oppTest = oppTest->upCast()->next())); 1032 } while ((testPtT = testPtT->next()) != startPtT); 1033checkNextSpan: 1034 ; 1035 } while ((test = test->final() ? nullptr : test->upCast()->next())); 1036 debugValidate(); 1037 return; 1038} 1039 1040// commented-out lines keep this in sync with moveNearby() 1041// Move nearby t values and pts so they all hang off the same span. Alignment happens later. 1042void SkOpSegment::debugMoveNearby(SkPathOpsDebug::GlitchLog* glitches) const { 1043 debugValidate(); 1044 // release undeleted spans pointing to this seg that are linked to the primary span 1045 const SkOpSpanBase* spanBase = &fHead; 1046 do { 1047 const SkOpPtT* ptT = spanBase->ptT(); 1048 const SkOpPtT* headPtT = ptT; 1049 while ((ptT = ptT->next()) != headPtT) { 1050 const SkOpSpanBase* test = ptT->span(); 1051 if (ptT->segment() == this && !ptT->deleted() && test != spanBase 1052 && test->ptT() == ptT) { 1053 if (test->final()) { 1054 if (spanBase == &fHead) { 1055 glitches->record(SkPathOpsDebug::kMoveNearbyClearAll_Glitch, this); 1056// return; 1057 } 1058 glitches->record(SkPathOpsDebug::kMoveNearbyReleaseFinal_Glitch, spanBase, ptT); 1059 } else if (test->prev()) { 1060 glitches->record(SkPathOpsDebug::kMoveNearbyRelease_Glitch, test, headPtT); 1061 } 1062// break; 1063 } 1064 } 1065 spanBase = spanBase->upCast()->next(); 1066 } while (!spanBase->final()); 1067 1068 // This loop looks for adjacent spans which are near by 1069 spanBase = &fHead; 1070 do { // iterate through all spans associated with start 1071 const SkOpSpanBase* test = spanBase->upCast()->next(); 1072 if (this->spansNearby(spanBase, test)) { 1073 if (test->final()) { 1074 if (spanBase->prev()) { 1075 glitches->record(SkPathOpsDebug::kMoveNearbyMergeFinal_Glitch, test); 1076 } else { 1077 glitches->record(SkPathOpsDebug::kMoveNearbyClearAll2_Glitch, this); 1078 // return 1079 } 1080 } else { 1081 glitches->record(SkPathOpsDebug::kMoveNearbyMerge_Glitch, spanBase); 1082 } 1083 } 1084 spanBase = test; 1085 } while (!spanBase->final()); 1086 debugValidate(); 1087} 1088#endif 1089 1090void SkOpSegment::debugReset() { 1091 this->init(this->fPts, this->fWeight, this->contour(), this->verb()); 1092} 1093 1094#if DEBUG_COINCIDENCE_ORDER 1095void SkOpSegment::debugSetCoinT(int index, SkScalar t) const { 1096 if (fDebugBaseMax < 0 || fDebugBaseIndex == index) { 1097 fDebugBaseIndex = index; 1098 fDebugBaseMin = SkTMin(t, fDebugBaseMin); 1099 fDebugBaseMax = SkTMax(t, fDebugBaseMax); 1100 return; 1101 } 1102 SkASSERT(fDebugBaseMin >= t || t >= fDebugBaseMax); 1103 if (fDebugLastMax < 0 || fDebugLastIndex == index) { 1104 fDebugLastIndex = index; 1105 fDebugLastMin = SkTMin(t, fDebugLastMin); 1106 fDebugLastMax = SkTMax(t, fDebugLastMax); 1107 return; 1108 } 1109 SkASSERT(fDebugLastMin >= t || t >= fDebugLastMax); 1110 SkASSERT((t - fDebugBaseMin > 0) == (fDebugLastMin - fDebugBaseMin > 0)); 1111} 1112#endif 1113 1114#if DEBUG_ACTIVE_SPANS 1115void SkOpSegment::debugShowActiveSpans() const { 1116 debugValidate(); 1117 if (done()) { 1118 return; 1119 } 1120 int lastId = -1; 1121 double lastT = -1; 1122 const SkOpSpan* span = &fHead; 1123 do { 1124 if (span->done()) { 1125 continue; 1126 } 1127 if (lastId == this->debugID() && lastT == span->t()) { 1128 continue; 1129 } 1130 lastId = this->debugID(); 1131 lastT = span->t(); 1132 SkDebugf("%s id=%d", __FUNCTION__, this->debugID()); 1133 // since endpoints may have be adjusted, show actual computed curves 1134 SkDCurve curvePart; 1135 this->subDivide(span, span->next(), &curvePart); 1136 const SkDPoint* pts = curvePart.fCubic.fPts; 1137 SkDebugf(" (%1.9g,%1.9g", pts[0].fX, pts[0].fY); 1138 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) { 1139 SkDebugf(" %1.9g,%1.9g", pts[vIndex].fX, pts[vIndex].fY); 1140 } 1141 if (SkPath::kConic_Verb == fVerb) { 1142 SkDebugf(" %1.9gf", curvePart.fConic.fWeight); 1143 } 1144 SkDebugf(") t=%1.9g tEnd=%1.9g", span->t(), span->next()->t()); 1145 if (span->windSum() == SK_MinS32) { 1146 SkDebugf(" windSum=?"); 1147 } else { 1148 SkDebugf(" windSum=%d", span->windSum()); 1149 } 1150 if (span->oppValue() && span->oppSum() == SK_MinS32) { 1151 SkDebugf(" oppSum=?"); 1152 } else if (span->oppValue() || span->oppSum() != SK_MinS32) { 1153 SkDebugf(" oppSum=%d", span->oppSum()); 1154 } 1155 SkDebugf(" windValue=%d", span->windValue()); 1156 if (span->oppValue() || span->oppSum() != SK_MinS32) { 1157 SkDebugf(" oppValue=%d", span->oppValue()); 1158 } 1159 SkDebugf("\n"); 1160 } while ((span = span->next()->upCastable())); 1161} 1162#endif 1163 1164#if DEBUG_MARK_DONE 1165void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding) { 1166 const SkPoint& pt = span->ptT()->fPt; 1167 SkDebugf("%s id=%d", fun, this->debugID()); 1168 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY); 1169 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) { 1170 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY); 1171 } 1172 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=", 1173 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t()); 1174 if (winding == SK_MinS32) { 1175 SkDebugf("?"); 1176 } else { 1177 SkDebugf("%d", winding); 1178 } 1179 SkDebugf(" windSum="); 1180 if (span->windSum() == SK_MinS32) { 1181 SkDebugf("?"); 1182 } else { 1183 SkDebugf("%d", span->windSum()); 1184 } 1185 SkDebugf(" windValue=%d\n", span->windValue()); 1186} 1187 1188void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding, 1189 int oppWinding) { 1190 const SkPoint& pt = span->ptT()->fPt; 1191 SkDebugf("%s id=%d", fun, this->debugID()); 1192 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY); 1193 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) { 1194 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY); 1195 } 1196 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=", 1197 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t(), winding, oppWinding); 1198 if (winding == SK_MinS32) { 1199 SkDebugf("?"); 1200 } else { 1201 SkDebugf("%d", winding); 1202 } 1203 SkDebugf(" newOppSum="); 1204 if (oppWinding == SK_MinS32) { 1205 SkDebugf("?"); 1206 } else { 1207 SkDebugf("%d", oppWinding); 1208 } 1209 SkDebugf(" oppSum="); 1210 if (span->oppSum() == SK_MinS32) { 1211 SkDebugf("?"); 1212 } else { 1213 SkDebugf("%d", span->oppSum()); 1214 } 1215 SkDebugf(" windSum="); 1216 if (span->windSum() == SK_MinS32) { 1217 SkDebugf("?"); 1218 } else { 1219 SkDebugf("%d", span->windSum()); 1220 } 1221 SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue()); 1222} 1223 1224#endif 1225 1226// loop looking for a pair of angle parts that are too close to be sorted 1227/* This is called after other more simple intersection and angle sorting tests have been exhausted. 1228 This should be rarely called -- the test below is thorough and time consuming. 1229 This checks the distance between start points; the distance between 1230*/ 1231#if DEBUG_ANGLE 1232void SkOpAngle::debugCheckNearCoincidence() const { 1233 const SkOpAngle* test = this; 1234 do { 1235 const SkOpSegment* testSegment = test->segment(); 1236 double testStartT = test->start()->t(); 1237 SkDPoint testStartPt = testSegment->dPtAtT(testStartT); 1238 double testEndT = test->end()->t(); 1239 SkDPoint testEndPt = testSegment->dPtAtT(testEndT); 1240 double testLenSq = testStartPt.distanceSquared(testEndPt); 1241 SkDebugf("%s testLenSq=%1.9g id=%d\n", __FUNCTION__, testLenSq, testSegment->debugID()); 1242 double testMidT = (testStartT + testEndT) / 2; 1243 const SkOpAngle* next = test; 1244 while ((next = next->fNext) != this) { 1245 SkOpSegment* nextSegment = next->segment(); 1246 double testMidDistSq = testSegment->distSq(testMidT, next); 1247 double testEndDistSq = testSegment->distSq(testEndT, next); 1248 double nextStartT = next->start()->t(); 1249 SkDPoint nextStartPt = nextSegment->dPtAtT(nextStartT); 1250 double distSq = testStartPt.distanceSquared(nextStartPt); 1251 double nextEndT = next->end()->t(); 1252 double nextMidT = (nextStartT + nextEndT) / 2; 1253 double nextMidDistSq = nextSegment->distSq(nextMidT, test); 1254 double nextEndDistSq = nextSegment->distSq(nextEndT, test); 1255 SkDebugf("%s distSq=%1.9g testId=%d nextId=%d\n", __FUNCTION__, distSq, 1256 testSegment->debugID(), nextSegment->debugID()); 1257 SkDebugf("%s testMidDistSq=%1.9g\n", __FUNCTION__, testMidDistSq); 1258 SkDebugf("%s testEndDistSq=%1.9g\n", __FUNCTION__, testEndDistSq); 1259 SkDebugf("%s nextMidDistSq=%1.9g\n", __FUNCTION__, nextMidDistSq); 1260 SkDebugf("%s nextEndDistSq=%1.9g\n", __FUNCTION__, nextEndDistSq); 1261 SkDPoint nextEndPt = nextSegment->dPtAtT(nextEndT); 1262 double nextLenSq = nextStartPt.distanceSquared(nextEndPt); 1263 SkDebugf("%s nextLenSq=%1.9g\n", __FUNCTION__, nextLenSq); 1264 SkDebugf("\n"); 1265 } 1266 test = test->fNext; 1267 } while (test->fNext != this); 1268} 1269#endif 1270 1271#if DEBUG_ANGLE 1272SkString SkOpAngle::debugPart() const { 1273 SkString result; 1274 switch (this->segment()->verb()) { 1275 case SkPath::kLine_Verb: 1276 result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fPart.fCurve), 1277 this->segment()->debugID()); 1278 break; 1279 case SkPath::kQuad_Verb: 1280 result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fPart.fCurve), 1281 this->segment()->debugID()); 1282 break; 1283 case SkPath::kConic_Verb: 1284 result.printf(CONIC_DEBUG_STR " id=%d", 1285 CONIC_DEBUG_DATA(fPart.fCurve, fPart.fCurve.fConic.fWeight), 1286 this->segment()->debugID()); 1287 break; 1288 case SkPath::kCubic_Verb: 1289 result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fPart.fCurve), 1290 this->segment()->debugID()); 1291 break; 1292 default: 1293 SkASSERT(0); 1294 } 1295 return result; 1296} 1297#endif 1298 1299#if DEBUG_SORT 1300void SkOpAngle::debugLoop() const { 1301 const SkOpAngle* first = this; 1302 const SkOpAngle* next = this; 1303 do { 1304 next->dumpOne(true); 1305 SkDebugf("\n"); 1306 next = next->fNext; 1307 } while (next && next != first); 1308 next = first; 1309 do { 1310 next->debugValidate(); 1311 next = next->fNext; 1312 } while (next && next != first); 1313} 1314#endif 1315 1316void SkOpAngle::debugValidate() const { 1317#if DEBUG_COINCIDENCE 1318 if (this->globalState()->debugCheckHealth()) { 1319 return; 1320 } 1321#endif 1322#if DEBUG_VALIDATE 1323 const SkOpAngle* first = this; 1324 const SkOpAngle* next = this; 1325 int wind = 0; 1326 int opp = 0; 1327 int lastXor = -1; 1328 int lastOppXor = -1; 1329 do { 1330 if (next->unorderable()) { 1331 return; 1332 } 1333 const SkOpSpan* minSpan = next->start()->starter(next->end()); 1334 if (minSpan->windValue() == SK_MinS32) { 1335 return; 1336 } 1337 bool op = next->segment()->operand(); 1338 bool isXor = next->segment()->isXor(); 1339 bool oppXor = next->segment()->oppXor(); 1340 SkASSERT(!DEBUG_LIMIT_WIND_SUM || between(0, minSpan->windValue(), DEBUG_LIMIT_WIND_SUM)); 1341 SkASSERT(!DEBUG_LIMIT_WIND_SUM 1342 || between(-DEBUG_LIMIT_WIND_SUM, minSpan->oppValue(), DEBUG_LIMIT_WIND_SUM)); 1343 bool useXor = op ? oppXor : isXor; 1344 SkASSERT(lastXor == -1 || lastXor == (int) useXor); 1345 lastXor = (int) useXor; 1346 wind += next->debugSign() * (op ? minSpan->oppValue() : minSpan->windValue()); 1347 if (useXor) { 1348 wind &= 1; 1349 } 1350 useXor = op ? isXor : oppXor; 1351 SkASSERT(lastOppXor == -1 || lastOppXor == (int) useXor); 1352 lastOppXor = (int) useXor; 1353 opp += next->debugSign() * (op ? minSpan->windValue() : minSpan->oppValue()); 1354 if (useXor) { 1355 opp &= 1; 1356 } 1357 next = next->fNext; 1358 } while (next && next != first); 1359 SkASSERT(wind == 0 || !FLAGS_runFail); 1360 SkASSERT(opp == 0 || !FLAGS_runFail); 1361#endif 1362} 1363 1364void SkOpAngle::debugValidateNext() const { 1365#if !FORCE_RELEASE 1366 const SkOpAngle* first = this; 1367 const SkOpAngle* next = first; 1368 SkTDArray<const SkOpAngle*>(angles); 1369 do { 1370// SkASSERT_RELEASE(next->fSegment->debugContains(next)); 1371 angles.push(next); 1372 next = next->next(); 1373 if (next == first) { 1374 break; 1375 } 1376 SkASSERT_RELEASE(!angles.contains(next)); 1377 if (!next) { 1378 return; 1379 } 1380 } while (true); 1381#endif 1382} 1383 1384#ifdef SK_DEBUG 1385void SkCoincidentSpans::debugStartCheck(const SkOpSpanBase* outer, const SkOpSpanBase* over, 1386 const SkOpGlobalState* debugState) const { 1387 SkASSERT(coinPtTEnd()->span() == over || !debugState->debugRunFail()); 1388 SkASSERT(oppPtTEnd()->span() == outer || !debugState->debugRunFail()); 1389} 1390#endif 1391 1392#if DEBUG_COIN 1393// sets the span's end to the ptT referenced by the previous-next 1394void SkCoincidentSpans::debugCorrectOneEnd(SkPathOpsDebug::GlitchLog* log, 1395 const SkOpPtT* (SkCoincidentSpans::* getEnd)() const, 1396 void (SkCoincidentSpans::*setEnd)(const SkOpPtT* ptT) const ) const { 1397 const SkOpPtT* origPtT = (this->*getEnd)(); 1398 const SkOpSpanBase* origSpan = origPtT->span(); 1399 const SkOpSpan* prev = origSpan->prev(); 1400 const SkOpPtT* testPtT = prev ? prev->next()->ptT() 1401 : origSpan->upCast()->next()->prev()->ptT(); 1402 if (origPtT != testPtT) { 1403 log->record(SkPathOpsDebug::kCorrectEnd_Glitch, this, origPtT, testPtT); 1404 } 1405} 1406 1407 1408/* Commented-out lines keep this in sync with correctEnds */ 1409// FIXME: member pointers have fallen out of favor and can be replaced with 1410// an alternative approach. 1411// makes all span ends agree with the segment's spans that define them 1412void SkCoincidentSpans::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const { 1413 this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTStart, nullptr); 1414 this->debugCorrectOneEnd(log, &SkCoincidentSpans::coinPtTEnd, nullptr); 1415 this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTStart, nullptr); 1416 this->debugCorrectOneEnd(log, &SkCoincidentSpans::oppPtTEnd, nullptr); 1417} 1418 1419/* Commented-out lines keep this in sync with expand */ 1420// expand the range by checking adjacent spans for coincidence 1421bool SkCoincidentSpans::debugExpand(SkPathOpsDebug::GlitchLog* log) const { 1422 bool expanded = false; 1423 const SkOpSegment* segment = coinPtTStart()->segment(); 1424 const SkOpSegment* oppSegment = oppPtTStart()->segment(); 1425 do { 1426 const SkOpSpan* start = coinPtTStart()->span()->upCast(); 1427 const SkOpSpan* prev = start->prev(); 1428 const SkOpPtT* oppPtT; 1429 if (!prev || !(oppPtT = prev->contains(oppSegment))) { 1430 break; 1431 } 1432 double midT = (prev->t() + start->t()) / 2; 1433 if (!segment->isClose(midT, oppSegment)) { 1434 break; 1435 } 1436 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, prev->ptT(), oppPtT); 1437 expanded = true; 1438 } while (false); // actual continues while expansion is possible 1439 do { 1440 const SkOpSpanBase* end = coinPtTEnd()->span(); 1441 SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next(); 1442 if (next && next->deleted()) { 1443 break; 1444 } 1445 const SkOpPtT* oppPtT; 1446 if (!next || !(oppPtT = next->contains(oppSegment))) { 1447 break; 1448 } 1449 double midT = (end->t() + next->t()) / 2; 1450 if (!segment->isClose(midT, oppSegment)) { 1451 break; 1452 } 1453 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, this, next->ptT(), oppPtT); 1454 expanded = true; 1455 } while (false); // actual continues while expansion is possible 1456 return expanded; 1457} 1458 1459// description below 1460void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* base, const SkOpSpanBase* testSpan) const { 1461 const SkOpPtT* testPtT = testSpan->ptT(); 1462 const SkOpPtT* stopPtT = testPtT; 1463 const SkOpSegment* baseSeg = base->segment(); 1464 while ((testPtT = testPtT->next()) != stopPtT) { 1465 const SkOpSegment* testSeg = testPtT->segment(); 1466 if (testPtT->deleted()) { 1467 continue; 1468 } 1469 if (testSeg == baseSeg) { 1470 continue; 1471 } 1472 if (testPtT->span()->ptT() != testPtT) { 1473 continue; 1474 } 1475 if (this->contains(baseSeg, testSeg, testPtT->fT)) { 1476 continue; 1477 } 1478 // intersect perp with base->ptT() with testPtT->segment() 1479 SkDVector dxdy = baseSeg->dSlopeAtT(base->t()); 1480 const SkPoint& pt = base->pt(); 1481 SkDLine ray = {{{pt.fX, pt.fY}, {pt.fX + dxdy.fY, pt.fY - dxdy.fX}}}; 1482 SkIntersections i; 1483 (*CurveIntersectRay[testSeg->verb()])(testSeg->pts(), testSeg->weight(), ray, &i); 1484 for (int index = 0; index < i.used(); ++index) { 1485 double t = i[0][index]; 1486 if (!between(0, t, 1)) { 1487 continue; 1488 } 1489 SkDPoint oppPt = i.pt(index); 1490 if (!oppPt.approximatelyEqual(pt)) { 1491 continue; 1492 } 1493 SkOpSegment* writableSeg = const_cast<SkOpSegment*>(testSeg); 1494 SkOpPtT* oppStart = writableSeg->addT(t); 1495 if (oppStart == testPtT) { 1496 continue; 1497 } 1498 SkOpSpan* writableBase = const_cast<SkOpSpan*>(base); 1499 oppStart->span()->addOpp(writableBase); 1500 if (oppStart->deleted()) { 1501 continue; 1502 } 1503 SkOpSegment* coinSeg = base->segment(); 1504 SkOpSegment* oppSeg = oppStart->segment(); 1505 double coinTs, coinTe, oppTs, oppTe; 1506 if (Ordered(coinSeg, oppSeg)) { 1507 coinTs = base->t(); 1508 coinTe = testSpan->t(); 1509 oppTs = oppStart->fT; 1510 oppTe = testPtT->fT; 1511 } else { 1512 SkTSwap(coinSeg, oppSeg); 1513 coinTs = oppStart->fT; 1514 coinTe = testPtT->fT; 1515 oppTs = base->t(); 1516 oppTe = testSpan->t(); 1517 } 1518 if (coinTs > coinTe) { 1519 SkTSwap(coinTs, coinTe); 1520 SkTSwap(oppTs, oppTe); 1521 } 1522 bool added; 1523 if (this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &added), false) { 1524 return; 1525 } 1526 } 1527 } 1528 return; 1529} 1530 1531// description below 1532void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* ptT) const { 1533 FAIL_IF(!ptT->span()->upCastable(), ptT->span()); 1534 const SkOpSpan* base = ptT->span()->upCast(); 1535 const SkOpSpan* prev = base->prev(); 1536 FAIL_IF(!prev, ptT->span()); 1537 if (!prev->isCanceled()) { 1538 if (this->debugAddEndMovedSpans(log, base, base->prev()), false) { 1539 return; 1540 } 1541 } 1542 if (!base->isCanceled()) { 1543 if (this->debugAddEndMovedSpans(log, base, base->next()), false) { 1544 return; 1545 } 1546 } 1547 return; 1548} 1549 1550/* If A is coincident with B and B includes an endpoint, and A's matching point 1551 is not the endpoint (i.e., there's an implied line connecting B-end and A) 1552 then assume that the same implied line may intersect another curve close to B. 1553 Since we only care about coincidence that was undetected, look at the 1554 ptT list on B-segment adjacent to the B-end/A ptT loop (not in the loop, but 1555 next door) and see if the A matching point is close enough to form another 1556 coincident pair. If so, check for a new coincident span between B-end/A ptT loop 1557 and the adjacent ptT loop. 1558*/ 1559void SkOpCoincidence::debugAddEndMovedSpans(SkPathOpsDebug::GlitchLog* log) const { 1560 const SkCoincidentSpans* span = fHead; 1561 if (!span) { 1562 return; 1563 } 1564// fTop = span; 1565// fHead = nullptr; 1566 do { 1567 if (span->coinPtTStart()->fPt != span->oppPtTStart()->fPt) { 1568 FAIL_IF(1 == span->coinPtTStart()->fT, span); 1569 bool onEnd = span->coinPtTStart()->fT == 0; 1570 bool oOnEnd = zero_or_one(span->oppPtTStart()->fT); 1571 if (onEnd) { 1572 if (!oOnEnd) { // if both are on end, any nearby intersect was already found 1573 if (this->debugAddEndMovedSpans(log, span->oppPtTStart()), false) { 1574 return; 1575 } 1576 } 1577 } else if (oOnEnd) { 1578 if (this->debugAddEndMovedSpans(log, span->coinPtTStart()), false) { 1579 return; 1580 } 1581 } 1582 } 1583 if (span->coinPtTEnd()->fPt != span->oppPtTEnd()->fPt) { 1584 bool onEnd = span->coinPtTEnd()->fT == 1; 1585 bool oOnEnd = zero_or_one(span->oppPtTEnd()->fT); 1586 if (onEnd) { 1587 if (!oOnEnd) { 1588 if (this->debugAddEndMovedSpans(log, span->oppPtTEnd()), false) { 1589 return; 1590 } 1591 } 1592 } else if (oOnEnd) { 1593 if (this->debugAddEndMovedSpans(log, span->coinPtTEnd()), false) { 1594 return; 1595 } 1596 } 1597 } 1598 } while ((span = span->next())); 1599// this->restoreHead(); 1600 return; 1601} 1602 1603/* Commented-out lines keep this in sync with addExpanded */ 1604// for each coincident pair, match the spans 1605// if the spans don't match, add the mssing pt to the segment and loop it in the opposite span 1606void SkOpCoincidence::debugAddExpanded(SkPathOpsDebug::GlitchLog* log) const { 1607 const SkCoincidentSpans* coin = this->fHead; 1608 if (!coin) { 1609 return; 1610 } 1611 do { 1612 const SkOpPtT* startPtT = coin->coinPtTStart(); 1613 const SkOpPtT* oStartPtT = coin->oppPtTStart(); 1614 double priorT = startPtT->fT; 1615 double oPriorT = oStartPtT->fT; 1616 FAIL_IF(startPtT->contains(oStartPtT), coin); 1617 SkOPASSERT(coin->coinPtTEnd()->contains(coin->oppPtTEnd())); 1618 const SkOpSpanBase* start = startPtT->span(); 1619 const SkOpSpanBase* oStart = oStartPtT->span(); 1620 const SkOpSpanBase* end = coin->coinPtTEnd()->span(); 1621 const SkOpSpanBase* oEnd = coin->oppPtTEnd()->span(); 1622 FAIL_IF(oEnd->deleted(), coin); 1623 FAIL_IF(!start->upCastable(), coin); 1624 const SkOpSpanBase* test = start->upCast()->next(); 1625 FAIL_IF(!coin->flipped() && !oStart->upCastable(), coin); 1626 const SkOpSpanBase* oTest = coin->flipped() ? oStart->prev() : oStart->upCast()->next(); 1627 FAIL_IF(!oTest, coin); 1628 const SkOpSegment* seg = start->segment(); 1629 const SkOpSegment* oSeg = oStart->segment(); 1630 while (test != end || oTest != oEnd) { 1631 const SkOpPtT* containedOpp = test->ptT()->contains(oSeg); 1632 const SkOpPtT* containedThis = oTest->ptT()->contains(seg); 1633 if (!containedOpp || !containedThis) { 1634 // choose the ends, or the first common pt-t list shared by both 1635 double nextT, oNextT; 1636 if (containedOpp) { 1637 nextT = test->t(); 1638 oNextT = containedOpp->fT; 1639 } else if (containedThis) { 1640 nextT = containedThis->fT; 1641 oNextT = oTest->t(); 1642 } else { 1643 // iterate through until a pt-t list found that contains the other 1644 const SkOpSpanBase* walk = test; 1645 const SkOpPtT* walkOpp; 1646 do { 1647 FAIL_IF(!walk->upCastable(), coin); 1648 walk = walk->upCast()->next(); 1649 } while (!(walkOpp = walk->ptT()->contains(oSeg)) 1650 && walk != coin->coinPtTEnd()->span()); 1651 nextT = walk->t(); 1652 oNextT = walkOpp->fT; 1653 } 1654 // use t ranges to guess which one is missing 1655 double startRange = coin->coinPtTEnd()->fT - startPtT->fT; 1656 FAIL_IF(!startRange, coin); 1657 double startPart = (test->t() - startPtT->fT) / startRange; 1658 double oStartRange = coin->oppPtTEnd()->fT - oStartPtT->fT; 1659 FAIL_IF(!oStartRange, coin); 1660 double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange; 1661 FAIL_IF(startPart == oStartPart, coin); 1662 bool addToOpp = !containedOpp && !containedThis ? startPart < oStartPart 1663 : !!containedThis; 1664 bool startOver = false; 1665 addToOpp ? log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch, 1666 oPriorT + oStartRange * startPart, test) 1667 : log->record(SkPathOpsDebug::kAddExpandedCoin_Glitch, 1668 priorT + startRange * oStartPart, oTest); 1669 // FAIL_IF(!success, coin); 1670 if (startOver) { 1671 test = start; 1672 oTest = oStart; 1673 } 1674 end = coin->coinPtTEnd()->span(); 1675 oEnd = coin->oppPtTEnd()->span(); 1676 } 1677 if (test != end) { 1678 FAIL_IF(!test->upCastable(), coin); 1679 priorT = test->t(); 1680 test = test->upCast()->next(); 1681 } 1682 if (oTest != oEnd) { 1683 oPriorT = oTest->t(); 1684 oTest = coin->flipped() ? oTest->prev() : oTest->upCast()->next(); 1685 FAIL_IF(!oTest, coin); 1686 } 1687 } 1688 } while ((coin = coin->next())); 1689 return; 1690} 1691 1692/* Commented-out lines keep this in sync with addIfMissing() */ 1693void SkOpCoincidence::debugAddIfMissing(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* outer, const SkOpPtT* over1s, 1694 const SkOpPtT* over1e) const { 1695// SkASSERT(fTop); 1696 if (fTop && alreadyAdded(fTop, outer, over1s, over1e)) { // in debug, fTop may be null 1697 return; 1698 } 1699 if (fHead && alreadyAdded(fHead, outer, over1s, over1e)) { 1700 return; 1701 } 1702 log->record(SkPathOpsDebug::kAddIfMissingCoin_Glitch, outer->coinPtTStart(), outer->coinPtTEnd(), over1s, over1e); 1703 this->debugValidate(); 1704 return; 1705} 1706 1707/* Commented-out lines keep this in sync addIfMissing() */ 1708// note that over1s, over1e, over2s, over2e are ordered 1709void SkOpCoincidence::debugAddIfMissing(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* over1s, const SkOpPtT* over2s, 1710 double tStart, double tEnd, const SkOpSegment* coinSeg, const SkOpSegment* oppSeg, bool* added, 1711 const SkOpPtT* over1e, const SkOpPtT* over2e) const { 1712 SkASSERT(tStart < tEnd); 1713 SkASSERT(over1s->fT < over1e->fT); 1714 SkASSERT(between(over1s->fT, tStart, over1e->fT)); 1715 SkASSERT(between(over1s->fT, tEnd, over1e->fT)); 1716 SkASSERT(over2s->fT < over2e->fT); 1717 SkASSERT(between(over2s->fT, tStart, over2e->fT)); 1718 SkASSERT(between(over2s->fT, tEnd, over2e->fT)); 1719 SkASSERT(over1s->segment() == over1e->segment()); 1720 SkASSERT(over2s->segment() == over2e->segment()); 1721 SkASSERT(over1s->segment() == over2s->segment()); 1722 SkASSERT(over1s->segment() != coinSeg); 1723 SkASSERT(over1s->segment() != oppSeg); 1724 SkASSERT(coinSeg != oppSeg); 1725 double coinTs, coinTe, oppTs, oppTe; 1726 coinTs = TRange(over1s, tStart, coinSeg SkDEBUGPARAMS(over1e)); 1727 coinTe = TRange(over1s, tEnd, coinSeg SkDEBUGPARAMS(over1e)); 1728 if (coinSeg->collapsed(coinTs, coinTe)) { 1729 return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, coinSeg); 1730 } 1731 oppTs = TRange(over2s, tStart, oppSeg SkDEBUGPARAMS(over2e)); 1732 oppTe = TRange(over2s, tEnd, oppSeg SkDEBUGPARAMS(over2e)); 1733 if (oppSeg->collapsed(oppTs, oppTe)) { 1734 return log->record(SkPathOpsDebug::kAddIfCollapsed_Glitch, oppSeg); 1735 } 1736 if (coinTs > coinTe) { 1737 SkTSwap(coinTs, coinTe); 1738 SkTSwap(oppTs, oppTe); 1739 } 1740 return this->debugAddOrOverlap(log, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, added 1741 ); 1742} 1743 1744/* Commented-out lines keep this in sync addOrOverlap() */ 1745// If this is called by addEndMovedSpans(), a returned false propogates out to an abort. 1746// If this is called by AddIfMissing(), a returned false indicates there was nothing to add 1747void SkOpCoincidence::debugAddOrOverlap(SkPathOpsDebug::GlitchLog* log, 1748 const SkOpSegment* coinSeg, const SkOpSegment* oppSeg, 1749 double coinTs, double coinTe, double oppTs, double oppTe, bool* added) const { 1750 SkTDArray<SkCoincidentSpans*> overlaps; 1751 SkOPASSERT(!fTop); // this is (correctly) reversed in addifMissing() 1752 if (fTop && !this->checkOverlap(fTop, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, 1753 &overlaps)) { 1754 return; 1755 } 1756 if (fHead && !this->checkOverlap(fHead, coinSeg, oppSeg, coinTs, 1757 coinTe, oppTs, oppTe, &overlaps)) { 1758 return; 1759 } 1760 const SkCoincidentSpans* overlap = overlaps.count() ? overlaps[0] : nullptr; 1761 for (int index = 1; index < overlaps.count(); ++index) { // combine overlaps before continuing 1762 const SkCoincidentSpans* test = overlaps[index]; 1763 if (overlap->coinPtTStart()->fT > test->coinPtTStart()->fT) { 1764 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTStart()); 1765 } 1766 if (overlap->coinPtTEnd()->fT < test->coinPtTEnd()->fT) { 1767 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->coinPtTEnd()); 1768 } 1769 if (overlap->flipped() 1770 ? overlap->oppPtTStart()->fT < test->oppPtTStart()->fT 1771 : overlap->oppPtTStart()->fT > test->oppPtTStart()->fT) { 1772 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTStart()); 1773 } 1774 if (overlap->flipped() 1775 ? overlap->oppPtTEnd()->fT > test->oppPtTEnd()->fT 1776 : overlap->oppPtTEnd()->fT < test->oppPtTEnd()->fT) { 1777 log->record(SkPathOpsDebug::kAddOrOverlap_Glitch, overlap, test->oppPtTEnd()); 1778 } 1779 if (!fHead) { this->debugRelease(log, fHead, test); 1780 this->debugRelease(log, fTop, test); 1781 } 1782 } 1783 const SkOpPtT* cs = coinSeg->existing(coinTs, oppSeg); 1784 const SkOpPtT* ce = coinSeg->existing(coinTe, oppSeg); 1785 RETURN_FALSE_IF(overlap && cs && ce && overlap->contains(cs, ce), coinSeg); 1786 RETURN_FALSE_IF(cs != ce || !cs, coinSeg); 1787 const SkOpPtT* os = oppSeg->existing(oppTs, coinSeg); 1788 const SkOpPtT* oe = oppSeg->existing(oppTe, coinSeg); 1789 RETURN_FALSE_IF(overlap && os && oe && overlap->contains(os, oe), oppSeg); 1790 SkASSERT(true || !cs || !cs->deleted()); 1791 SkASSERT(true || !os || !os->deleted()); 1792 SkASSERT(true || !ce || !ce->deleted()); 1793 SkASSERT(true || !oe || !oe->deleted()); 1794 const SkOpPtT* csExisting = !cs ? coinSeg->existing(coinTs, nullptr) : nullptr; 1795 const SkOpPtT* ceExisting = !ce ? coinSeg->existing(coinTe, nullptr) : nullptr; 1796 RETURN_FALSE_IF(csExisting && csExisting == ceExisting, coinSeg); 1797 RETURN_FALSE_IF(csExisting && (csExisting == ce || 1798 csExisting->contains(ceExisting ? ceExisting : ce)), coinSeg); 1799 RETURN_FALSE_IF(ceExisting && (ceExisting == cs || 1800 ceExisting->contains(csExisting ? csExisting : cs)), coinSeg); 1801 const SkOpPtT* osExisting = !os ? oppSeg->existing(oppTs, nullptr) : nullptr; 1802 const SkOpPtT* oeExisting = !oe ? oppSeg->existing(oppTe, nullptr) : nullptr; 1803 RETURN_FALSE_IF(osExisting && osExisting == oeExisting, oppSeg); 1804 RETURN_FALSE_IF(osExisting && (osExisting == oe || 1805 osExisting->contains(oeExisting ? oeExisting : oe)), oppSeg); 1806 RETURN_FALSE_IF(oeExisting && (oeExisting == os || 1807 oeExisting->contains(osExisting ? osExisting : os)), oppSeg); 1808 bool csDeleted = false, osDeleted = false, ceDeleted = false, oeDeleted = false; 1809 this->debugValidate(); 1810 if (!cs || !os) { 1811 if (!cs) 1812 cs = coinSeg->debugAddT(coinTs, log); 1813 if (!os) 1814 os = oppSeg->debugAddT(oppTs, log); 1815// RETURN_FALSE_IF(callerAborts, !csWritable || !osWritable); 1816 if (cs && os) cs->span()->debugAddOpp(log, os->span()); 1817// cs = csWritable; 1818// os = osWritable->active(); 1819 RETURN_FALSE_IF((ce && ce->deleted()) || (oe && oe->deleted()), coinSeg); 1820 } 1821 if (!ce || !oe) { 1822 if (!ce) 1823 ce = coinSeg->debugAddT(coinTe, log); 1824 if (!oe) 1825 oe = oppSeg->debugAddT(oppTe, log); 1826 if (ce && oe) ce->span()->debugAddOpp(log, oe->span()); 1827// ce = ceWritable; 1828// oe = oeWritable; 1829 } 1830 this->debugValidate(); 1831 RETURN_FALSE_IF(csDeleted, coinSeg); 1832 RETURN_FALSE_IF(osDeleted, oppSeg); 1833 RETURN_FALSE_IF(ceDeleted, coinSeg); 1834 RETURN_FALSE_IF(oeDeleted, oppSeg); 1835 RETURN_FALSE_IF(!cs || !ce || cs == ce || cs->contains(ce) || !os || !oe || os == oe || os->contains(oe), coinSeg); 1836 bool result = true; 1837 if (overlap) { 1838 if (overlap->coinPtTStart()->segment() == coinSeg) { 1839 log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe); 1840 } else { 1841 if (oppTs > oppTe) { 1842 SkTSwap(coinTs, coinTe); 1843 SkTSwap(oppTs, oppTe); 1844 } 1845 log->record(SkPathOpsDebug::kAddMissingExtend_Glitch, oppSeg, oppTs, oppTe, coinSeg, coinTs, coinTe); 1846 } 1847#if 0 && DEBUG_COINCIDENCE_VERBOSE 1848 if (result) { 1849 overlap->debugShow(); 1850 } 1851#endif 1852 } else { 1853 log->record(SkPathOpsDebug::kAddMissingCoin_Glitch, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe); 1854#if 0 && DEBUG_COINCIDENCE_VERBOSE 1855 fHead->debugShow(); 1856#endif 1857 } 1858 this->debugValidate(); 1859 return (void) result; 1860} 1861 1862// Extra commented-out lines keep this in sync with addMissing() 1863/* detects overlaps of different coincident runs on same segment */ 1864/* does not detect overlaps for pairs without any segments in common */ 1865// returns true if caller should loop again 1866void SkOpCoincidence::debugAddMissing(SkPathOpsDebug::GlitchLog* log, bool* added) const { 1867 const SkCoincidentSpans* outer = fHead; 1868 *added = false; 1869 if (!outer) { 1870 return; 1871 } 1872 // fTop = outer; 1873 // fHead = nullptr; 1874 do { 1875 // addifmissing can modify the list that this is walking 1876 // save head so that walker can iterate over old data unperturbed 1877 // addifmissing adds to head freely then add saved head in the end 1878 const SkOpPtT* ocs = outer->coinPtTStart(); 1879 SkASSERT(!ocs->deleted()); 1880 const SkOpSegment* outerCoin = ocs->segment(); 1881 SkASSERT(!outerCoin->done()); // if it's done, should have already been removed from list 1882 const SkOpPtT* oos = outer->oppPtTStart(); 1883 if (oos->deleted()) { 1884 return; 1885 } 1886 const SkOpSegment* outerOpp = oos->segment(); 1887 SkASSERT(!outerOpp->done()); 1888// SkOpSegment* outerCoinWritable = const_cast<SkOpSegment*>(outerCoin); 1889// SkOpSegment* outerOppWritable = const_cast<SkOpSegment*>(outerOpp); 1890 const SkCoincidentSpans* inner = outer; 1891 while ((inner = inner->next())) { 1892 this->debugValidate(); 1893 double overS, overE; 1894 const SkOpPtT* ics = inner->coinPtTStart(); 1895 SkASSERT(!ics->deleted()); 1896 const SkOpSegment* innerCoin = ics->segment(); 1897 SkASSERT(!innerCoin->done()); 1898 const SkOpPtT* ios = inner->oppPtTStart(); 1899 SkASSERT(!ios->deleted()); 1900 const SkOpSegment* innerOpp = ios->segment(); 1901 SkASSERT(!innerOpp->done()); 1902// SkOpSegment* innerCoinWritable = const_cast<SkOpSegment*>(innerCoin); 1903// SkOpSegment* innerOppWritable = const_cast<SkOpSegment*>(innerOpp); 1904 if (outerCoin == innerCoin) { 1905 const SkOpPtT* oce = outer->coinPtTEnd(); 1906 if (oce->deleted()) { 1907 return; 1908 } 1909 const SkOpPtT* ice = inner->coinPtTEnd(); 1910 SkASSERT(!ice->deleted()); 1911 if (outerOpp != innerOpp && this->overlap(ocs, oce, ics, ice, &overS, &overE)) { 1912 this->debugAddIfMissing(log, ocs->starter(oce), ics->starter(ice), 1913 overS, overE, outerOpp, innerOpp, added, 1914 ocs->debugEnder(oce), 1915 ics->debugEnder(ice)); 1916 } 1917 } else if (outerCoin == innerOpp) { 1918 const SkOpPtT* oce = outer->coinPtTEnd(); 1919 SkASSERT(!oce->deleted()); 1920 const SkOpPtT* ioe = inner->oppPtTEnd(); 1921 SkASSERT(!ioe->deleted()); 1922 if (outerOpp != innerCoin && this->overlap(ocs, oce, ios, ioe, &overS, &overE)) { 1923 this->debugAddIfMissing(log, ocs->starter(oce), ios->starter(ioe), 1924 overS, overE, outerOpp, innerCoin, added, 1925 ocs->debugEnder(oce), 1926 ios->debugEnder(ioe)); 1927 } 1928 } else if (outerOpp == innerCoin) { 1929 const SkOpPtT* ooe = outer->oppPtTEnd(); 1930 SkASSERT(!ooe->deleted()); 1931 const SkOpPtT* ice = inner->coinPtTEnd(); 1932 SkASSERT(!ice->deleted()); 1933 SkASSERT(outerCoin != innerOpp); 1934 if (this->overlap(oos, ooe, ics, ice, &overS, &overE)) { 1935 this->debugAddIfMissing(log, oos->starter(ooe), ics->starter(ice), 1936 overS, overE, outerCoin, innerOpp, added, 1937 oos->debugEnder(ooe), 1938 ics->debugEnder(ice)); 1939 } 1940 } else if (outerOpp == innerOpp) { 1941 const SkOpPtT* ooe = outer->oppPtTEnd(); 1942 SkASSERT(!ooe->deleted()); 1943 const SkOpPtT* ioe = inner->oppPtTEnd(); 1944 if (ioe->deleted()) { 1945 return; 1946 } 1947 SkASSERT(outerCoin != innerCoin); 1948 if (this->overlap(oos, ooe, ios, ioe, &overS, &overE)) { 1949 this->debugAddIfMissing(log, oos->starter(ooe), ios->starter(ioe), 1950 overS, overE, outerCoin, innerCoin, added, 1951 oos->debugEnder(ooe), 1952 ios->debugEnder(ioe)); 1953 } 1954 } 1955 this->debugValidate(); 1956 } 1957 } while ((outer = outer->next())); 1958 // this->restoreHead(); 1959 return; 1960} 1961 1962// Commented-out lines keep this in sync with release() 1963void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkCoincidentSpans* remove) const { 1964 const SkCoincidentSpans* head = coin; 1965 const SkCoincidentSpans* prev = nullptr; 1966 const SkCoincidentSpans* next; 1967 do { 1968 next = coin->next(); 1969 if (coin == remove) { 1970 if (prev) { 1971// prev->setNext(next); 1972 } else if (head == fHead) { 1973// fHead = next; 1974 } else { 1975// fTop = next; 1976 } 1977 log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin); 1978 } 1979 prev = coin; 1980 } while ((coin = next)); 1981 return; 1982} 1983 1984void SkOpCoincidence::debugRelease(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* deleted) const { 1985 const SkCoincidentSpans* coin = fHead; 1986 if (!coin) { 1987 return; 1988 } 1989 do { 1990 if (coin->coinPtTStart()->segment() == deleted 1991 || coin->coinPtTEnd()->segment() == deleted 1992 || coin->oppPtTStart()->segment() == deleted 1993 || coin->oppPtTEnd()->segment() == deleted) { 1994 log->record(SkPathOpsDebug::kReleasedSpan_Glitch, coin); 1995 } 1996 } while ((coin = coin->next())); 1997} 1998 1999// Commented-out lines keep this in sync with expand() 2000// expand the range by checking adjacent spans for coincidence 2001bool SkOpCoincidence::debugExpand(SkPathOpsDebug::GlitchLog* log) const { 2002 const SkCoincidentSpans* coin = fHead; 2003 if (!coin) { 2004 return false; 2005 } 2006 bool expanded = false; 2007 do { 2008 if (coin->debugExpand(log)) { 2009 // check to see if multiple spans expanded so they are now identical 2010 const SkCoincidentSpans* test = fHead; 2011 do { 2012 if (coin == test) { 2013 continue; 2014 } 2015 if (coin->coinPtTStart() == test->coinPtTStart() 2016 && coin->oppPtTStart() == test->oppPtTStart()) { 2017 if (log) log->record(SkPathOpsDebug::kExpandCoin_Glitch, fHead, test->coinPtTStart()); 2018 break; 2019 } 2020 } while ((test = test->next())); 2021 expanded = true; 2022 } 2023 } while ((coin = coin->next())); 2024 return expanded; 2025} 2026 2027// Commented-out lines keep this in sync with mark() 2028/* this sets up the coincidence links in the segments when the coincidence crosses multiple spans */ 2029void SkOpCoincidence::debugMark(SkPathOpsDebug::GlitchLog* log) const { 2030 const SkCoincidentSpans* coin = fHead; 2031 if (!coin) { 2032 return; 2033 } 2034 do { 2035 FAIL_IF(!coin->coinPtTStartWritable()->span()->upCastable(), coin); 2036 const SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast(); 2037// SkASSERT(start->deleted()); 2038 const SkOpSpanBase* end = coin->coinPtTEndWritable()->span(); 2039// SkASSERT(end->deleted()); 2040 const SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span(); 2041// SkASSERT(oStart->deleted()); 2042 const SkOpSpanBase* oEnd = coin->oppPtTEndWritable()->span(); 2043// SkASSERT(oEnd->deleted()); 2044 bool flipped = coin->flipped(); 2045 if (flipped) { 2046 SkTSwap(oStart, oEnd); 2047 } 2048 /* coin and opp spans may not match up. Mark the ends, and then let the interior 2049 get marked as many times as the spans allow */ 2050 start->debugInsertCoincidence(log, oStart->upCast()); 2051 end->debugInsertCoinEnd(log, oEnd); 2052 const SkOpSegment* segment = start->segment(); 2053 const SkOpSegment* oSegment = oStart->segment(); 2054 const SkOpSpanBase* next = start; 2055 const SkOpSpanBase* oNext = oStart; 2056 bool ordered = coin->ordered(); 2057 while ((next = next->upCast()->next()) != end) { 2058 FAIL_IF(!next->upCastable(), coin); 2059 if (next->upCast()->debugInsertCoincidence(log, oSegment, flipped, ordered), false) { 2060 return; 2061 } 2062 } 2063 while ((oNext = oNext->upCast()->next()) != oEnd) { 2064 FAIL_IF(!oNext->upCastable(), coin); 2065 if (oNext->upCast()->debugInsertCoincidence(log, segment, flipped, ordered), false) { 2066 return; 2067 } 2068 } 2069 } while ((coin = coin->next())); 2070 return; 2071} 2072#endif 2073 2074#if DEBUG_COIN 2075// Commented-out lines keep this in sync with markCollapsed() 2076void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkOpPtT* test) const { 2077 const SkCoincidentSpans* head = coin; 2078 while (coin) { 2079 if (coin->collapsed(test)) { 2080 if (zero_or_one(coin->coinPtTStart()->fT) && zero_or_one(coin->coinPtTEnd()->fT)) { 2081 log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin); 2082 } 2083 if (zero_or_one(coin->oppPtTStart()->fT) && zero_or_one(coin->oppPtTEnd()->fT)) { 2084 log->record(SkPathOpsDebug::kCollapsedCoin_Glitch, coin); 2085 } 2086 this->debugRelease(log, head, coin); 2087 } 2088 coin = coin->next(); 2089 } 2090} 2091 2092// Commented-out lines keep this in sync with markCollapsed() 2093void SkOpCoincidence::debugMarkCollapsed(SkPathOpsDebug::GlitchLog* log, const SkOpPtT* test) const { 2094 this->debugMarkCollapsed(log, fHead, test); 2095 this->debugMarkCollapsed(log, fTop, test); 2096} 2097#endif 2098 2099void SkCoincidentSpans::debugShow() const { 2100 SkDebugf("coinSpan - id=%d t=%1.9g tEnd=%1.9g\n", coinPtTStart()->segment()->debugID(), 2101 coinPtTStart()->fT, coinPtTEnd()->fT); 2102 SkDebugf("coinSpan + id=%d t=%1.9g tEnd=%1.9g\n", oppPtTStart()->segment()->debugID(), 2103 oppPtTStart()->fT, oppPtTEnd()->fT); 2104} 2105 2106void SkOpCoincidence::debugShowCoincidence() const { 2107#if DEBUG_COINCIDENCE 2108 const SkCoincidentSpans* span = fHead; 2109 while (span) { 2110 span->debugShow(); 2111 span = span->next(); 2112 } 2113#endif 2114} 2115 2116#if DEBUG_COIN 2117static void DebugCheckBetween(const SkOpSpanBase* next, const SkOpSpanBase* end, 2118 double oStart, double oEnd, const SkOpSegment* oSegment, 2119 SkPathOpsDebug::GlitchLog* log) { 2120 SkASSERT(next != end); 2121 SkASSERT(!next->contains(end) || log); 2122 if (next->t() > end->t()) { 2123 SkTSwap(next, end); 2124 } 2125 do { 2126 const SkOpPtT* ptT = next->ptT(); 2127 int index = 0; 2128 bool somethingBetween = false; 2129 do { 2130 ++index; 2131 ptT = ptT->next(); 2132 const SkOpPtT* checkPtT = next->ptT(); 2133 if (ptT == checkPtT) { 2134 break; 2135 } 2136 bool looped = false; 2137 for (int check = 0; check < index; ++check) { 2138 if ((looped = checkPtT == ptT)) { 2139 break; 2140 } 2141 checkPtT = checkPtT->next(); 2142 } 2143 if (looped) { 2144 SkASSERT(0); 2145 break; 2146 } 2147 if (ptT->deleted()) { 2148 continue; 2149 } 2150 if (ptT->segment() != oSegment) { 2151 continue; 2152 } 2153 somethingBetween |= between(oStart, ptT->fT, oEnd); 2154 } while (true); 2155 SkASSERT(somethingBetween); 2156 } while (next != end && (next = next->upCast()->next())); 2157} 2158 2159static void DebugCheckOverlap(const SkCoincidentSpans* test, const SkCoincidentSpans* list, 2160 SkPathOpsDebug::GlitchLog* log) { 2161 if (!list) { 2162 return; 2163 } 2164 const SkOpSegment* coinSeg = test->coinPtTStart()->segment(); 2165 SkASSERT(coinSeg == test->coinPtTEnd()->segment()); 2166 const SkOpSegment* oppSeg = test->oppPtTStart()->segment(); 2167 SkASSERT(oppSeg == test->oppPtTEnd()->segment()); 2168 SkASSERT(coinSeg != test->oppPtTStart()->segment()); 2169 SkDEBUGCODE(double tcs = test->coinPtTStart()->fT); 2170 SkASSERT(between(0, tcs, 1)); 2171 SkDEBUGCODE(double tce = test->coinPtTEnd()->fT); 2172 SkASSERT(between(0, tce, 1)); 2173 SkASSERT(tcs < tce); 2174 double tos = test->oppPtTStart()->fT; 2175 SkASSERT(between(0, tos, 1)); 2176 double toe = test->oppPtTEnd()->fT; 2177 SkASSERT(between(0, toe, 1)); 2178 SkASSERT(tos != toe); 2179 if (tos > toe) { 2180 SkTSwap(tos, toe); 2181 } 2182 do { 2183 double lcs, lce, los, loe; 2184 if (coinSeg == list->coinPtTStart()->segment()) { 2185 if (oppSeg != list->oppPtTStart()->segment()) { 2186 continue; 2187 } 2188 lcs = list->coinPtTStart()->fT; 2189 lce = list->coinPtTEnd()->fT; 2190 los = list->oppPtTStart()->fT; 2191 loe = list->oppPtTEnd()->fT; 2192 if (los > loe) { 2193 SkTSwap(los, loe); 2194 } 2195 } else if (coinSeg == list->oppPtTStart()->segment()) { 2196 if (oppSeg != list->coinPtTStart()->segment()) { 2197 continue; 2198 } 2199 lcs = list->oppPtTStart()->fT; 2200 lce = list->oppPtTEnd()->fT; 2201 if (lcs > lce) { 2202 SkTSwap(lcs, lce); 2203 } 2204 los = list->coinPtTStart()->fT; 2205 loe = list->coinPtTEnd()->fT; 2206 } else { 2207 continue; 2208 } 2209 SkASSERT(tce < lcs || lce < tcs); 2210 SkASSERT(toe < los || loe < tos); 2211 } while ((list = list->next())); 2212} 2213 2214 2215static void DebugCheckOverlapTop(const SkCoincidentSpans* head, const SkCoincidentSpans* opt, 2216 SkPathOpsDebug::GlitchLog* log) { 2217 // check for overlapping coincident spans 2218 const SkCoincidentSpans* test = head; 2219 while (test) { 2220 const SkCoincidentSpans* next = test->next(); 2221 DebugCheckOverlap(test, next, log); 2222 DebugCheckOverlap(test, opt, log); 2223 test = next; 2224 } 2225} 2226 2227static void DebugValidate(const SkCoincidentSpans* head, const SkCoincidentSpans* opt, 2228 SkPathOpsDebug::GlitchLog* log) { 2229 // look for pts inside coincident spans that are not inside the opposite spans 2230 const SkCoincidentSpans* coin = head; 2231 while (coin) { 2232 SkASSERT(SkOpCoincidence::Ordered(coin->coinPtTStart()->segment(), 2233 coin->oppPtTStart()->segment())); 2234 SkASSERT(coin->coinPtTStart()->span()->ptT() == coin->coinPtTStart()); 2235 SkASSERT(coin->coinPtTEnd()->span()->ptT() == coin->coinPtTEnd()); 2236 SkASSERT(coin->oppPtTStart()->span()->ptT() == coin->oppPtTStart()); 2237 SkASSERT(coin->oppPtTEnd()->span()->ptT() == coin->oppPtTEnd()); 2238 coin = coin->next(); 2239 } 2240 DebugCheckOverlapTop(head, opt, log); 2241} 2242#endif 2243 2244void SkOpCoincidence::debugValidate() const { 2245#if DEBUG_COINCIDENCE 2246 DebugValidate(fHead, fTop, nullptr); 2247 DebugValidate(fTop, nullptr, nullptr); 2248#endif 2249} 2250 2251#if DEBUG_COIN 2252static void DebugCheckBetween(const SkCoincidentSpans* head, const SkCoincidentSpans* opt, 2253 SkPathOpsDebug::GlitchLog* log) { 2254 // look for pts inside coincident spans that are not inside the opposite spans 2255 const SkCoincidentSpans* coin = head; 2256 while (coin) { 2257 DebugCheckBetween(coin->coinPtTStart()->span(), coin->coinPtTEnd()->span(), 2258 coin->oppPtTStart()->fT, coin->oppPtTEnd()->fT, coin->oppPtTStart()->segment(), 2259 log); 2260 DebugCheckBetween(coin->oppPtTStart()->span(), coin->oppPtTEnd()->span(), 2261 coin->coinPtTStart()->fT, coin->coinPtTEnd()->fT, coin->coinPtTStart()->segment(), 2262 log); 2263 coin = coin->next(); 2264 } 2265 DebugCheckOverlapTop(head, opt, log); 2266} 2267#endif 2268 2269void SkOpCoincidence::debugCheckBetween() const { 2270#if DEBUG_COINCIDENCE 2271 if (fGlobalState->debugCheckHealth()) { 2272 return; 2273 } 2274 DebugCheckBetween(fHead, fTop, nullptr); 2275 DebugCheckBetween(fTop, nullptr, nullptr); 2276#endif 2277} 2278 2279#if DEBUG_COIN 2280void SkOpContour::debugCheckHealth(SkPathOpsDebug::GlitchLog* log) const { 2281 const SkOpSegment* segment = &fHead; 2282 do { 2283 segment->debugCheckHealth(log); 2284 } while ((segment = segment->next())); 2285} 2286 2287void SkOpCoincidence::debugCheckValid(SkPathOpsDebug::GlitchLog* log) const { 2288#if DEBUG_VALIDATE 2289 DebugValidate(fHead, fTop, log); 2290 DebugValidate(fTop, nullptr, log); 2291#endif 2292} 2293 2294void SkOpCoincidence::debugCorrectEnds(SkPathOpsDebug::GlitchLog* log) const { 2295 const SkCoincidentSpans* coin = fHead; 2296 if (!coin) { 2297 return; 2298 } 2299 do { 2300 coin->debugCorrectEnds(log); 2301 } while ((coin = coin->next())); 2302} 2303 2304// commmented-out lines keep this aligned with missingCoincidence() 2305void SkOpContour::debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const { 2306// SkASSERT(fCount > 0); 2307 const SkOpSegment* segment = &fHead; 2308// bool result = false; 2309 do { 2310 if (fState->angleCoincidence()) { 2311// #if DEBUG_ANGLE 2312// segment->debugCheckAngleCoin(); 2313// #endif 2314 } else if (segment->debugMissingCoincidence(log), false) { 2315// result = true; 2316 } 2317 segment = segment->next(); 2318 } while (segment); 2319 return; 2320} 2321 2322void SkOpContour::debugMoveMultiples(SkPathOpsDebug::GlitchLog* log) const { 2323 SkASSERT(fCount > 0); 2324 const SkOpSegment* segment = &fHead; 2325 do { 2326 if (segment->debugMoveMultiples(log), false) { 2327 return; 2328 } 2329 } while ((segment = segment->next())); 2330 return; 2331} 2332 2333void SkOpContour::debugMoveNearby(SkPathOpsDebug::GlitchLog* log) const { 2334 SkASSERT(fCount > 0); 2335 const SkOpSegment* segment = &fHead; 2336 do { 2337 segment->debugMoveNearby(log); 2338 } while ((segment = segment->next())); 2339} 2340#endif 2341 2342#if DEBUG_COINCIDENCE_ORDER 2343void SkOpSegment::debugResetCoinT() const { 2344 fDebugBaseIndex = -1; 2345 fDebugBaseMin = 1; 2346 fDebugBaseMax = -1; 2347 fDebugLastIndex = -1; 2348 fDebugLastMin = 1; 2349 fDebugLastMax = -1; 2350} 2351#endif 2352 2353void SkOpSegment::debugValidate() const { 2354#if DEBUG_COINCIDENCE_ORDER 2355 { 2356 const SkOpSpanBase* span = &fHead; 2357 do { 2358 span->debugResetCoinT(); 2359 } while (!span->final() && (span = span->upCast()->next())); 2360 span = &fHead; 2361 int index = 0; 2362 do { 2363 span->debugSetCoinT(index++); 2364 } while (!span->final() && (span = span->upCast()->next())); 2365 } 2366#endif 2367#if DEBUG_COINCIDENCE 2368 if (this->globalState()->debugCheckHealth()) { 2369 return; 2370 } 2371#endif 2372#if DEBUG_VALIDATE 2373 const SkOpSpanBase* span = &fHead; 2374 double lastT = -1; 2375 const SkOpSpanBase* prev = nullptr; 2376 int count = 0; 2377 int done = 0; 2378 do { 2379 if (!span->final()) { 2380 ++count; 2381 done += span->upCast()->done() ? 1 : 0; 2382 } 2383 SkASSERT(span->segment() == this); 2384 SkASSERT(!prev || prev->upCast()->next() == span); 2385 SkASSERT(!prev || prev == span->prev()); 2386 prev = span; 2387 double t = span->ptT()->fT; 2388 SkASSERT(lastT < t); 2389 lastT = t; 2390 span->debugValidate(); 2391 } while (!span->final() && (span = span->upCast()->next())); 2392 SkASSERT(count == fCount); 2393 SkASSERT(done == fDoneCount); 2394 SkASSERT(count >= fDoneCount); 2395 SkASSERT(span->final()); 2396 span->debugValidate(); 2397#endif 2398} 2399 2400#if DEBUG_COIN 2401 2402// Commented-out lines keep this in sync with addOpp() 2403void SkOpSpanBase::debugAddOpp(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const { 2404 const SkOpPtT* oppPrev = this->ptT()->oppPrev(opp->ptT()); 2405 if (!oppPrev) { 2406 return; 2407 } 2408 this->debugMergeMatches(log, opp); 2409 this->ptT()->debugAddOpp(opp->ptT(), oppPrev); 2410 this->debugCheckForCollapsedCoincidence(log); 2411} 2412 2413// Commented-out lines keep this in sync with checkForCollapsedCoincidence() 2414void SkOpSpanBase::debugCheckForCollapsedCoincidence(SkPathOpsDebug::GlitchLog* log) const { 2415 const SkOpCoincidence* coins = this->globalState()->coincidence(); 2416 if (coins->isEmpty()) { 2417 return; 2418 } 2419// the insert above may have put both ends of a coincident run in the same span 2420// for each coincident ptT in loop; see if its opposite in is also in the loop 2421// this implementation is the motivation for marking that a ptT is referenced by a coincident span 2422 const SkOpPtT* head = this->ptT(); 2423 const SkOpPtT* test = head; 2424 do { 2425 if (!test->coincident()) { 2426 continue; 2427 } 2428 coins->debugMarkCollapsed(log, test); 2429 } while ((test = test->next()) != head); 2430} 2431#endif 2432 2433bool SkOpSpanBase::debugCoinEndLoopCheck() const { 2434 int loop = 0; 2435 const SkOpSpanBase* next = this; 2436 SkOpSpanBase* nextCoin; 2437 do { 2438 nextCoin = next->fCoinEnd; 2439 SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin); 2440 for (int check = 1; check < loop - 1; ++check) { 2441 const SkOpSpanBase* checkCoin = this->fCoinEnd; 2442 const SkOpSpanBase* innerCoin = checkCoin; 2443 for (int inner = check + 1; inner < loop; ++inner) { 2444 innerCoin = innerCoin->fCoinEnd; 2445 if (checkCoin == innerCoin) { 2446 SkDebugf("*** bad coincident end loop ***\n"); 2447 return false; 2448 } 2449 } 2450 } 2451 ++loop; 2452 } while ((next = nextCoin) && next != this); 2453 return true; 2454} 2455 2456#if DEBUG_COIN 2457// Commented-out lines keep this in sync with insertCoinEnd() 2458void SkOpSpanBase::debugInsertCoinEnd(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* coin) const { 2459 if (containsCoinEnd(coin)) { 2460// SkASSERT(coin->containsCoinEnd(this)); 2461 return; 2462 } 2463 debugValidate(); 2464// SkASSERT(this != coin); 2465 log->record(SkPathOpsDebug::kMarkCoinEnd_Glitch, this, coin); 2466// coin->fCoinEnd = this->fCoinEnd; 2467// this->fCoinEnd = coinNext; 2468 debugValidate(); 2469} 2470 2471// Commented-out lines keep this in sync with mergeContained() 2472void SkOpSpanBase::debugMergeContained(SkPathOpsDebug::GlitchLog* log, const SkPathOpsBounds& bounds, bool* deleted) const { 2473 // while adjacent spans' points are contained by the bounds, merge them 2474 const SkOpSpanBase* prev = this; 2475 const SkOpSegment* seg = this->segment(); 2476 while ((prev = prev->prev()) && bounds.contains(prev->pt()) && !seg->ptsDisjoint(prev, this)) { 2477 if (prev->prev()) { 2478 log->record(SkPathOpsDebug::kMergeContained_Glitch, this, prev); 2479 } else if (this->final()) { 2480 log->record(SkPathOpsDebug::kMergeContained_Glitch, this, prev); 2481 // return; 2482 } else { 2483 log->record(SkPathOpsDebug::kMergeContained_Glitch, prev, this); 2484 } 2485 } 2486 const SkOpSpanBase* current = this; 2487 const SkOpSpanBase* next = this; 2488 while (next->upCastable() && (next = next->upCast()->next()) 2489 && bounds.contains(next->pt()) && !seg->ptsDisjoint(this, next)) { 2490 if (!current->prev() && next->final()) { 2491 log->record(SkPathOpsDebug::kMergeContained_Glitch, next, current); 2492 current = next; 2493 } 2494 if (current->prev()) { 2495 log->record(SkPathOpsDebug::kMergeContained_Glitch, next, current); 2496 current = next; 2497 } else { 2498 log->record(SkPathOpsDebug::kMergeContained_Glitch, next, current); 2499 current = next; 2500 } 2501 } 2502#if DEBUG_COINCIDENCE 2503 // this->globalState()->coincidence()->debugValidate(); 2504#endif 2505} 2506 2507// Commented-out lines keep this in sync with mergeMatches() 2508// Look to see if pt-t linked list contains same segment more than once 2509// if so, and if each pt-t is directly pointed to by spans in that segment, 2510// merge them 2511// keep the points, but remove spans so that the segment doesn't have 2 or more 2512// spans pointing to the same pt-t loop at different loop elements 2513void SkOpSpanBase::debugMergeMatches(SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp) const { 2514 const SkOpPtT* test = &fPtT; 2515 const SkOpPtT* testNext; 2516 const SkOpPtT* stop = test; 2517 do { 2518 testNext = test->next(); 2519 if (test->deleted()) { 2520 continue; 2521 } 2522 const SkOpSpanBase* testBase = test->span(); 2523 SkASSERT(testBase->ptT() == test); 2524 const SkOpSegment* segment = test->segment(); 2525 if (segment->done()) { 2526 continue; 2527 } 2528 const SkOpPtT* inner = opp->ptT(); 2529 const SkOpPtT* innerStop = inner; 2530 do { 2531 if (inner->segment() != segment) { 2532 continue; 2533 } 2534 if (inner->deleted()) { 2535 continue; 2536 } 2537 const SkOpSpanBase* innerBase = inner->span(); 2538 SkASSERT(innerBase->ptT() == inner); 2539 // when the intersection is first detected, the span base is marked if there are 2540 // more than one point in the intersection. 2541// if (!innerBase->hasMultipleHint() && !testBase->hasMultipleHint()) { 2542 if (!zero_or_one(inner->fT)) { 2543 log->record(SkPathOpsDebug::kMergeMatches_Glitch, innerBase, test); 2544 } else { 2545 SkASSERT(inner->fT != test->fT); 2546 if (!zero_or_one(test->fT)) { 2547 log->record(SkPathOpsDebug::kMergeMatches_Glitch, testBase, inner); 2548 } else { 2549 log->record(SkPathOpsDebug::kMergeMatches_Glitch, segment); 2550// SkDEBUGCODE(testBase->debugSetDeleted()); 2551// test->setDeleted(); 2552// SkDEBUGCODE(innerBase->debugSetDeleted()); 2553// inner->setDeleted(); 2554 } 2555 } 2556#ifdef SK_DEBUG // assert if another undeleted entry points to segment 2557 const SkOpPtT* debugInner = inner; 2558 while ((debugInner = debugInner->next()) != innerStop) { 2559 if (debugInner->segment() != segment) { 2560 continue; 2561 } 2562 if (debugInner->deleted()) { 2563 continue; 2564 } 2565 SkOPASSERT(0); 2566 } 2567#endif 2568 break; 2569// } 2570 break; 2571 } while ((inner = inner->next()) != innerStop); 2572 } while ((test = testNext) != stop); 2573 this->debugCheckForCollapsedCoincidence(log); 2574} 2575 2576#endif 2577 2578void SkOpSpanBase::debugResetCoinT() const { 2579#if DEBUG_COINCIDENCE_ORDER 2580 const SkOpPtT* ptT = &fPtT; 2581 do { 2582 ptT->debugResetCoinT(); 2583 ptT = ptT->next(); 2584 } while (ptT != &fPtT); 2585#endif 2586} 2587 2588void SkOpSpanBase::debugSetCoinT(int index) const { 2589#if DEBUG_COINCIDENCE_ORDER 2590 const SkOpPtT* ptT = &fPtT; 2591 do { 2592 if (!ptT->deleted()) { 2593 ptT->debugSetCoinT(index); 2594 } 2595 ptT = ptT->next(); 2596 } while (ptT != &fPtT); 2597#endif 2598} 2599 2600const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const { 2601 const SkOpSpanBase* end = *endPtr; 2602 SkASSERT(this->segment() == end->segment()); 2603 const SkOpSpanBase* result; 2604 if (t() < end->t()) { 2605 result = this; 2606 } else { 2607 result = end; 2608 *endPtr = this; 2609 } 2610 return result->upCast(); 2611} 2612 2613void SkOpSpanBase::debugValidate() const { 2614#if DEBUG_COINCIDENCE 2615 if (this->globalState()->debugCheckHealth()) { 2616 return; 2617 } 2618#endif 2619#if DEBUG_VALIDATE 2620 const SkOpPtT* ptT = &fPtT; 2621 SkASSERT(ptT->span() == this); 2622 do { 2623// SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt)); 2624 ptT->debugValidate(); 2625 ptT = ptT->next(); 2626 } while (ptT != &fPtT); 2627 SkASSERT(this->debugCoinEndLoopCheck()); 2628 if (!this->final()) { 2629 SkASSERT(this->upCast()->debugCoinLoopCheck()); 2630 } 2631 if (fFromAngle) { 2632 fFromAngle->debugValidate(); 2633 } 2634 if (!this->final() && this->upCast()->toAngle()) { 2635 this->upCast()->toAngle()->debugValidate(); 2636 } 2637#endif 2638} 2639 2640bool SkOpSpan::debugCoinLoopCheck() const { 2641 int loop = 0; 2642 const SkOpSpan* next = this; 2643 SkOpSpan* nextCoin; 2644 do { 2645 nextCoin = next->fCoincident; 2646 SkASSERT(nextCoin == this || nextCoin->fCoincident != nextCoin); 2647 for (int check = 1; check < loop - 1; ++check) { 2648 const SkOpSpan* checkCoin = this->fCoincident; 2649 const SkOpSpan* innerCoin = checkCoin; 2650 for (int inner = check + 1; inner < loop; ++inner) { 2651 innerCoin = innerCoin->fCoincident; 2652 if (checkCoin == innerCoin) { 2653 SkDebugf("*** bad coincident loop ***\n"); 2654 return false; 2655 } 2656 } 2657 } 2658 ++loop; 2659 } while ((next = nextCoin) && next != this); 2660 return true; 2661} 2662 2663#if DEBUG_COIN 2664// Commented-out lines keep this in sync with insertCoincidence() in header 2665void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSpan* coin) const { 2666 if (containsCoincidence(coin)) { 2667// SkASSERT(coin->containsCoincidence(this)); 2668 return; 2669 } 2670 debugValidate(); 2671// SkASSERT(this != coin); 2672 log->record(SkPathOpsDebug::kMarkCoinStart_Glitch, this, coin); 2673// coin->fCoincident = this->fCoincident; 2674// this->fCoincident = coinNext; 2675 debugValidate(); 2676} 2677 2678// Commented-out lines keep this in sync with insertCoincidence() 2679void SkOpSpan::debugInsertCoincidence(SkPathOpsDebug::GlitchLog* log, const SkOpSegment* segment, bool flipped, bool ordered) const { 2680 if (this->containsCoincidence(segment)) { 2681 return; 2682 } 2683 const SkOpPtT* next = &fPtT; 2684 while ((next = next->next()) != &fPtT) { 2685 if (next->segment() == segment) { 2686 const SkOpSpan* span; 2687 const SkOpSpanBase* base = next->span(); 2688 if (!ordered) { 2689 const SkOpSpanBase* spanEnd = fNext->contains(segment)->span(); 2690 const SkOpPtT* start = base->ptT()->starter(spanEnd->ptT()); 2691 FAIL_IF(!start->span()->upCastable(), this); 2692 span = const_cast<SkOpSpan*>(start->span()->upCast()); 2693 } 2694 else if (flipped) { 2695 span = base->prev(); 2696 FAIL_IF(!span, this); 2697 } 2698 else { 2699 FAIL_IF(!base->upCastable(), this); 2700 span = base->upCast(); 2701 } 2702 log->record(SkPathOpsDebug::kMarkCoinInsert_Glitch, span); 2703 return; 2704 } 2705 } 2706#if DEBUG_COIN 2707 log->record(SkPathOpsDebug::kMarkCoinMissing_Glitch, segment, this); 2708#endif 2709 return; 2710} 2711#endif 2712 2713// called only by test code 2714int SkIntersections::debugCoincidentUsed() const { 2715 if (!fIsCoincident[0]) { 2716 SkASSERT(!fIsCoincident[1]); 2717 return 0; 2718 } 2719 int count = 0; 2720 SkDEBUGCODE(int count2 = 0;) 2721 for (int index = 0; index < fUsed; ++index) { 2722 if (fIsCoincident[0] & (1 << index)) { 2723 ++count; 2724 } 2725#ifdef SK_DEBUG 2726 if (fIsCoincident[1] & (1 << index)) { 2727 ++count2; 2728 } 2729#endif 2730 } 2731 SkASSERT(count == count2); 2732 return count; 2733} 2734 2735#include "SkOpContour.h" 2736 2737// Commented-out lines keep this in sync with addOpp() 2738void SkOpPtT::debugAddOpp(const SkOpPtT* opp, const SkOpPtT* oppPrev) const { 2739 SkDEBUGCODE(const SkOpPtT* oldNext = this->fNext); 2740 SkASSERT(this != opp); 2741// this->fNext = opp; 2742 SkASSERT(oppPrev != oldNext); 2743// oppPrev->fNext = oldNext; 2744} 2745 2746bool SkOpPtT::debugContains(const SkOpPtT* check) const { 2747 SkASSERT(this != check); 2748 const SkOpPtT* ptT = this; 2749 int links = 0; 2750 do { 2751 ptT = ptT->next(); 2752 if (ptT == check) { 2753 return true; 2754 } 2755 ++links; 2756 const SkOpPtT* test = this; 2757 for (int index = 0; index < links; ++index) { 2758 if (ptT == test) { 2759 return false; 2760 } 2761 test = test->next(); 2762 } 2763 } while (true); 2764} 2765 2766const SkOpPtT* SkOpPtT::debugContains(const SkOpSegment* check) const { 2767 SkASSERT(this->segment() != check); 2768 const SkOpPtT* ptT = this; 2769 int links = 0; 2770 do { 2771 ptT = ptT->next(); 2772 if (ptT->segment() == check) { 2773 return ptT; 2774 } 2775 ++links; 2776 const SkOpPtT* test = this; 2777 for (int index = 0; index < links; ++index) { 2778 if (ptT == test) { 2779 return nullptr; 2780 } 2781 test = test->next(); 2782 } 2783 } while (true); 2784} 2785 2786const SkOpPtT* SkOpPtT::debugEnder(const SkOpPtT* end) const { 2787 return fT < end->fT ? end : this; 2788} 2789 2790int SkOpPtT::debugLoopLimit(bool report) const { 2791 int loop = 0; 2792 const SkOpPtT* next = this; 2793 do { 2794 for (int check = 1; check < loop - 1; ++check) { 2795 const SkOpPtT* checkPtT = this->fNext; 2796 const SkOpPtT* innerPtT = checkPtT; 2797 for (int inner = check + 1; inner < loop; ++inner) { 2798 innerPtT = innerPtT->fNext; 2799 if (checkPtT == innerPtT) { 2800 if (report) { 2801 SkDebugf("*** bad ptT loop ***\n"); 2802 } 2803 return loop; 2804 } 2805 } 2806 } 2807 // there's nothing wrong with extremely large loop counts -- but this may appear to hang 2808 // by taking a very long time to figure out that no loop entry is a duplicate 2809 // -- and it's likely that a large loop count is indicative of a bug somewhere 2810 if (++loop > 1000) { 2811 SkDebugf("*** loop count exceeds 1000 ***\n"); 2812 return 1000; 2813 } 2814 } while ((next = next->fNext) && next != this); 2815 return 0; 2816} 2817 2818const SkOpPtT* SkOpPtT::debugOppPrev(const SkOpPtT* opp) const { 2819 return this->oppPrev(const_cast<SkOpPtT*>(opp)); 2820} 2821 2822void SkOpPtT::debugResetCoinT() const { 2823#if DEBUG_COINCIDENCE_ORDER 2824 this->segment()->debugResetCoinT(); 2825#endif 2826} 2827 2828void SkOpPtT::debugSetCoinT(int index) const { 2829#if DEBUG_COINCIDENCE_ORDER 2830 this->segment()->debugSetCoinT(index, fT); 2831#endif 2832} 2833 2834void SkOpPtT::debugValidate() const { 2835#if DEBUG_COINCIDENCE 2836 if (this->globalState()->debugCheckHealth()) { 2837 return; 2838 } 2839#endif 2840#if DEBUG_VALIDATE 2841 SkOpPhase phase = contour()->globalState()->phase(); 2842 if (phase == SkOpPhase::kIntersecting || phase == SkOpPhase::kFixWinding) { 2843 return; 2844 } 2845 SkASSERT(fNext); 2846 SkASSERT(fNext != this); 2847 SkASSERT(fNext->fNext); 2848 SkASSERT(debugLoopLimit(false) == 0); 2849#endif 2850} 2851 2852static void output_scalar(SkScalar num) { 2853 if (num == (int) num) { 2854 SkDebugf("%d", (int) num); 2855 } else { 2856 SkString str; 2857 str.printf("%1.9g", num); 2858 int width = (int) str.size(); 2859 const char* cStr = str.c_str(); 2860 while (cStr[width - 1] == '0') { 2861 --width; 2862 } 2863 str.resize(width); 2864 SkDebugf("%sf", str.c_str()); 2865 } 2866} 2867 2868static void output_points(const SkPoint* pts, int count) { 2869 for (int index = 0; index < count; ++index) { 2870 output_scalar(pts[index].fX); 2871 SkDebugf(", "); 2872 output_scalar(pts[index].fY); 2873 if (index + 1 < count) { 2874 SkDebugf(", "); 2875 } 2876 } 2877} 2878 2879static void showPathContours(SkPath::RawIter& iter, const char* pathName) { 2880 uint8_t verb; 2881 SkPoint pts[4]; 2882 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { 2883 switch (verb) { 2884 case SkPath::kMove_Verb: 2885 SkDebugf(" %s.moveTo(", pathName); 2886 output_points(&pts[0], 1); 2887 SkDebugf(");\n"); 2888 continue; 2889 case SkPath::kLine_Verb: 2890 SkDebugf(" %s.lineTo(", pathName); 2891 output_points(&pts[1], 1); 2892 SkDebugf(");\n"); 2893 break; 2894 case SkPath::kQuad_Verb: 2895 SkDebugf(" %s.quadTo(", pathName); 2896 output_points(&pts[1], 2); 2897 SkDebugf(");\n"); 2898 break; 2899 case SkPath::kConic_Verb: 2900 SkDebugf(" %s.conicTo(", pathName); 2901 output_points(&pts[1], 2); 2902 SkDebugf(", %1.9gf);\n", iter.conicWeight()); 2903 break; 2904 case SkPath::kCubic_Verb: 2905 SkDebugf(" %s.cubicTo(", pathName); 2906 output_points(&pts[1], 3); 2907 SkDebugf(");\n"); 2908 break; 2909 case SkPath::kClose_Verb: 2910 SkDebugf(" %s.close();\n", pathName); 2911 break; 2912 default: 2913 SkDEBUGFAIL("bad verb"); 2914 return; 2915 } 2916 } 2917} 2918 2919static const char* gFillTypeStr[] = { 2920 "kWinding_FillType", 2921 "kEvenOdd_FillType", 2922 "kInverseWinding_FillType", 2923 "kInverseEvenOdd_FillType" 2924}; 2925 2926void SkPathOpsDebug::ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration) { 2927 SkPath::RawIter iter(path); 2928#define SUPPORT_RECT_CONTOUR_DETECTION 0 2929#if SUPPORT_RECT_CONTOUR_DETECTION 2930 int rectCount = path.isRectContours() ? path.rectContours(nullptr, nullptr) : 0; 2931 if (rectCount > 0) { 2932 SkTDArray<SkRect> rects; 2933 SkTDArray<SkPath::Direction> directions; 2934 rects.setCount(rectCount); 2935 directions.setCount(rectCount); 2936 path.rectContours(rects.begin(), directions.begin()); 2937 for (int contour = 0; contour < rectCount; ++contour) { 2938 const SkRect& rect = rects[contour]; 2939 SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop, 2940 rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction 2941 ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction"); 2942 } 2943 return; 2944 } 2945#endif 2946 SkPath::FillType fillType = path.getFillType(); 2947 SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType); 2948 if (includeDeclaration) { 2949 SkDebugf(" SkPath %s;\n", name); 2950 } 2951 SkDebugf(" %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[fillType]); 2952 iter.setPath(path); 2953 showPathContours(iter, name); 2954} 2955