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