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