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