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