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 #ifndef SkPathOpsDebug_DEFINED 8 #define SkPathOpsDebug_DEFINED 9 10 #include "include/core/SkString.h" 11 #include "include/core/SkTypes.h" 12 #include "include/pathops/SkPathOps.h" 13 14 #include <stdlib.h> 15 #include <stdio.h> 16 17 enum class SkOpPhase : char; 18 struct SkDQuad; 19 class SkOpAngle; 20 class SkOpCoincidence; 21 class SkOpContour; 22 class SkOpContourHead; 23 class SkOpPtT; 24 class SkOpSegment; 25 class SkOpSpan; 26 class SkOpSpanBase; 27 struct SkDPoint; 28 struct SkDLine; 29 struct SkDQuad; 30 struct SkDConic; 31 struct SkDCubic; 32 class SkTSect; 33 34 // define this when running fuzz 35 // #define SK_BUILD_FOR_FUZZER 36 37 // fake classes to fool msvs Visual Studio 2018 Immediate Window 38 #define FakeClasses(a, b) \ 39 class SkDebugTCoincident##a##b; \ 40 class SkDebugTSect##a##b; \ 41 class SkDebugTSpan##a##b 42 43 FakeClasses(Quad, Quad); 44 FakeClasses(Conic, Quad); 45 FakeClasses(Conic, Conic); 46 FakeClasses(Cubic, Quad); 47 FakeClasses(Cubic, Conic); 48 FakeClasses(Cubic, Cubic); 49 50 #undef FakeClasses 51 52 #ifdef SK_RELEASE 53 #define FORCE_RELEASE 1 54 #else 55 #define FORCE_RELEASE 1 // set force release to 1 for multiple thread -- no debugging 56 #endif 57 58 #define DEBUG_UNDER_DEVELOPMENT 0 59 60 #define ONE_OFF_DEBUG 0 61 #define ONE_OFF_DEBUG_MATHEMATICA 0 62 63 #if defined(SK_BUILD_FOR_WIN) || defined(SK_BUILD_FOR_ANDROID) 64 #define SK_RAND(seed) rand() 65 #else 66 #define SK_RAND(seed) rand_r(&seed) 67 #endif 68 #ifdef SK_BUILD_FOR_WIN 69 #define SK_SNPRINTF _snprintf 70 #else 71 #define SK_SNPRINTF snprintf 72 #endif 73 74 #define WIND_AS_STRING(x) char x##Str[12]; \ 75 if (!SkPathOpsDebug::ValidWind(x)) strcpy(x##Str, "?"); \ 76 else SK_SNPRINTF(x##Str, sizeof(x##Str), "%d", x) 77 78 #if FORCE_RELEASE 79 80 #define DEBUG_ACTIVE_OP 0 81 #define DEBUG_ACTIVE_SPANS 0 82 #define DEBUG_ADD_INTERSECTING_TS 0 83 #define DEBUG_ADD_T 0 84 #define DEBUG_ALIGNMENT 0 85 #define DEBUG_ANGLE 0 86 #define DEBUG_ASSEMBLE 0 87 #define DEBUG_COINCIDENCE 0 88 #define DEBUG_COINCIDENCE_DUMP 0 // accumulate and dump which algorithms fired 89 #define DEBUG_COINCIDENCE_ORDER 0 // for well behaved curves, check if pairs match up in t-order 90 #define DEBUG_COINCIDENCE_VERBOSE 0 // usually whether the next function generates coincidence 91 #define DEBUG_CUBIC_BINARY_SEARCH 0 92 #define DEBUG_CUBIC_SPLIT 0 93 #define DEBUG_DUMP_SEGMENTS 0 94 #define DEBUG_DUMP_VERIFY 0 95 #define DEBUG_FLOW 0 96 #define DEBUG_LIMIT_WIND_SUM 0 97 #define DEBUG_MARK_DONE 0 98 #define DEBUG_PATH_CONSTRUCTION 0 99 #define DEBUG_PERP 0 100 #define DEBUG_SORT 0 101 #define DEBUG_T_SECT 0 102 #define DEBUG_T_SECT_DUMP 0 103 #define DEBUG_T_SECT_LOOP_COUNT 0 104 #define DEBUG_VALIDATE 0 105 #define DEBUG_WINDING 0 106 #define DEBUG_WINDING_AT_T 0 107 108 #else 109 110 #define DEBUG_ACTIVE_OP 1 111 #define DEBUG_ACTIVE_SPANS 1 112 #define DEBUG_ADD_INTERSECTING_TS 1 113 #define DEBUG_ADD_T 1 114 #define DEBUG_ALIGNMENT 0 115 #define DEBUG_ANGLE 1 116 #define DEBUG_ASSEMBLE 1 117 #define DEBUG_COINCIDENCE 1 118 #define DEBUG_COINCIDENCE_DUMP 0 119 #define DEBUG_COINCIDENCE_ORDER 0 // tight arc quads may generate out-of-order coincidence spans 120 #define DEBUG_COINCIDENCE_VERBOSE 1 121 #define DEBUG_CUBIC_BINARY_SEARCH 0 122 #define DEBUG_CUBIC_SPLIT 1 123 #define DEBUG_DUMP_VERIFY 0 124 #define DEBUG_DUMP_SEGMENTS 1 125 #define DEBUG_FLOW 1 126 #define DEBUG_LIMIT_WIND_SUM 15 127 #define DEBUG_MARK_DONE 1 128 #define DEBUG_PATH_CONSTRUCTION 1 129 #define DEBUG_PERP 1 130 #define DEBUG_SORT 1 131 #define DEBUG_T_SECT 0 // enabling may trigger validate asserts even though op does not fail 132 #define DEBUG_T_SECT_DUMP 0 // Use 1 normally. Use 2 to number segments, 3 for script output 133 #define DEBUG_T_SECT_LOOP_COUNT 0 134 #define DEBUG_VALIDATE 1 135 #define DEBUG_WINDING 1 136 #define DEBUG_WINDING_AT_T 1 137 138 #endif 139 140 #ifdef SK_RELEASE 141 #define SkDEBUGRELEASE(a, b) b 142 #define SkDEBUGPARAMS(...) 143 #else 144 #define SkDEBUGRELEASE(a, b) a 145 #define SkDEBUGPARAMS(...) , __VA_ARGS__ 146 #endif 147 148 #if DEBUG_VALIDATE == 0 149 #define PATH_OPS_DEBUG_VALIDATE_PARAMS(...) 150 #else 151 #define PATH_OPS_DEBUG_VALIDATE_PARAMS(...) , __VA_ARGS__ 152 #endif 153 154 #if DEBUG_T_SECT == 0 155 #define PATH_OPS_DEBUG_T_SECT_RELEASE(a, b) b 156 #define PATH_OPS_DEBUG_T_SECT_PARAMS(...) 157 #define PATH_OPS_DEBUG_T_SECT_CODE(...) 158 #else 159 #define PATH_OPS_DEBUG_T_SECT_RELEASE(a, b) a 160 #define PATH_OPS_DEBUG_T_SECT_PARAMS(...) , __VA_ARGS__ 161 #define PATH_OPS_DEBUG_T_SECT_CODE(...) __VA_ARGS__ 162 #endif 163 164 #if DEBUG_T_SECT_DUMP > 1 165 extern int gDumpTSectNum; 166 #endif 167 168 #if DEBUG_COINCIDENCE || DEBUG_COINCIDENCE_DUMP 169 #define DEBUG_COIN 1 170 #else 171 #define DEBUG_COIN 0 172 #endif 173 174 #if DEBUG_COIN 175 #define DEBUG_COIN_DECLARE_ONLY_PARAMS() \ 176 int lineNo, SkOpPhase phase, int iteration 177 #define DEBUG_COIN_DECLARE_PARAMS() \ 178 , DEBUG_COIN_DECLARE_ONLY_PARAMS() 179 #define DEBUG_COIN_ONLY_PARAMS() \ 180 __LINE__, SkOpPhase::kNoChange, 0 181 #define DEBUG_COIN_PARAMS() \ 182 , DEBUG_COIN_ONLY_PARAMS() 183 #define DEBUG_ITER_ONLY_PARAMS(iteration) \ 184 __LINE__, SkOpPhase::kNoChange, iteration 185 #define DEBUG_ITER_PARAMS(iteration) \ 186 , DEBUG_ITER_ONLY_PARAMS(iteration) 187 #define DEBUG_PHASE_ONLY_PARAMS(phase) \ 188 __LINE__, SkOpPhase::phase, 0 189 #define DEBUG_PHASE_PARAMS(phase) \ 190 , DEBUG_PHASE_ONLY_PARAMS(phase) 191 #define DEBUG_SET_PHASE() \ 192 this->globalState()->debugSetPhase(__func__, lineNo, phase, iteration) 193 #define DEBUG_STATIC_SET_PHASE(obj) \ 194 obj->globalState()->debugSetPhase(__func__, lineNo, phase, iteration) 195 #elif DEBUG_VALIDATE 196 #define DEBUG_COIN_DECLARE_ONLY_PARAMS() \ 197 SkOpPhase phase 198 #define DEBUG_COIN_DECLARE_PARAMS() \ 199 , DEBUG_COIN_DECLARE_ONLY_PARAMS() 200 #define DEBUG_COIN_ONLY_PARAMS() \ 201 SkOpPhase::kNoChange 202 #define DEBUG_COIN_PARAMS() \ 203 , DEBUG_COIN_ONLY_PARAMS() 204 #define DEBUG_ITER_ONLY_PARAMS(iteration) \ 205 SkOpPhase::kNoChange 206 #define DEBUG_ITER_PARAMS(iteration) \ 207 , DEBUG_ITER_ONLY_PARAMS(iteration) 208 #define DEBUG_PHASE_ONLY_PARAMS(phase) \ 209 SkOpPhase::phase 210 #define DEBUG_PHASE_PARAMS(phase) \ 211 , DEBUG_PHASE_ONLY_PARAMS(phase) 212 #define DEBUG_SET_PHASE() \ 213 this->globalState()->debugSetPhase(phase) 214 #define DEBUG_STATIC_SET_PHASE(obj) \ 215 obj->globalState()->debugSetPhase(phase) 216 #else 217 #define DEBUG_COIN_DECLARE_ONLY_PARAMS() 218 #define DEBUG_COIN_DECLARE_PARAMS() 219 #define DEBUG_COIN_ONLY_PARAMS() 220 #define DEBUG_COIN_PARAMS() 221 #define DEBUG_ITER_ONLY_PARAMS(iteration) 222 #define DEBUG_ITER_PARAMS(iteration) 223 #define DEBUG_PHASE_ONLY_PARAMS(phase) 224 #define DEBUG_PHASE_PARAMS(phase) 225 #define DEBUG_SET_PHASE() 226 #define DEBUG_STATIC_SET_PHASE(obj) 227 #endif 228 229 #define CUBIC_DEBUG_STR "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}" 230 #define CONIC_DEBUG_STR "{{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}, %1.9g}" 231 #define QUAD_DEBUG_STR "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}" 232 #define LINE_DEBUG_STR "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}" 233 #define PT_DEBUG_STR "{{%1.9g,%1.9g}}" 234 235 #define T_DEBUG_STR(t, n) #t "[" #n "]=%1.9g" 236 #define TX_DEBUG_STR(t) #t "[%d]=%1.9g" 237 #define CUBIC_DEBUG_DATA(c) c[0].fX, c[0].fY, c[1].fX, c[1].fY, c[2].fX, c[2].fY, c[3].fX, c[3].fY 238 #define CONIC_DEBUG_DATA(c, w) c[0].fX, c[0].fY, c[1].fX, c[1].fY, c[2].fX, c[2].fY, w 239 #define QUAD_DEBUG_DATA(q) q[0].fX, q[0].fY, q[1].fX, q[1].fY, q[2].fX, q[2].fY 240 #define LINE_DEBUG_DATA(l) l[0].fX, l[0].fY, l[1].fX, l[1].fY 241 #define PT_DEBUG_DATA(i, n) i.pt(n).asSkPoint().fX, i.pt(n).asSkPoint().fY 242 243 #ifndef DEBUG_TEST 244 #define DEBUG_TEST 0 245 #endif 246 247 // Tests with extreme numbers may fail, but all other tests should never fail. 248 #define FAIL_IF(cond) \ 249 do { bool fail = (cond); SkOPASSERT(!fail); if (fail) return false; } while (false) 250 251 #define FAIL_WITH_NULL_IF(cond) \ 252 do { bool fail = (cond); SkOPASSERT(!fail); if (fail) return nullptr; } while (false) 253 254 class SkPathOpsDebug { 255 public: 256 #if DEBUG_COIN 257 struct GlitchLog; 258 259 enum GlitchType { 260 kUninitialized_Glitch, 261 kAddCorruptCoin_Glitch, 262 kAddExpandedCoin_Glitch, 263 kAddExpandedFail_Glitch, 264 kAddIfCollapsed_Glitch, 265 kAddIfMissingCoin_Glitch, 266 kAddMissingCoin_Glitch, 267 kAddMissingExtend_Glitch, 268 kAddOrOverlap_Glitch, 269 kCollapsedCoin_Glitch, 270 kCollapsedDone_Glitch, 271 kCollapsedOppValue_Glitch, 272 kCollapsedSpan_Glitch, 273 kCollapsedWindValue_Glitch, 274 kCorrectEnd_Glitch, 275 kDeletedCoin_Glitch, 276 kExpandCoin_Glitch, 277 kFail_Glitch, 278 kMarkCoinEnd_Glitch, 279 kMarkCoinInsert_Glitch, 280 kMarkCoinMissing_Glitch, 281 kMarkCoinStart_Glitch, 282 kMergeMatches_Glitch, 283 kMissingCoin_Glitch, 284 kMissingDone_Glitch, 285 kMissingIntersection_Glitch, 286 kMoveMultiple_Glitch, 287 kMoveNearbyClearAll_Glitch, 288 kMoveNearbyClearAll2_Glitch, 289 kMoveNearbyMerge_Glitch, 290 kMoveNearbyMergeFinal_Glitch, 291 kMoveNearbyRelease_Glitch, 292 kMoveNearbyReleaseFinal_Glitch, 293 kReleasedSpan_Glitch, 294 kReturnFalse_Glitch, 295 kUnaligned_Glitch, 296 kUnalignedHead_Glitch, 297 kUnalignedTail_Glitch, 298 }; 299 300 struct CoinDictEntry { 301 int fIteration; 302 int fLineNumber; 303 GlitchType fGlitchType; 304 const char* fFunctionName; 305 }; 306 307 struct CoinDict { 308 void add(const CoinDictEntry& key); 309 void add(const CoinDict& dict); 310 void dump(const char* str, bool visitCheck) const; 311 SkTDArray<CoinDictEntry> fDict; 312 }; 313 314 static CoinDict gCoinSumChangedDict; 315 static CoinDict gCoinSumVisitedDict; 316 static CoinDict gCoinVistedDict; 317 #endif 318 319 #if defined(SK_DEBUG) || !FORCE_RELEASE 320 static int gContourID; 321 static int gSegmentID; 322 #endif 323 324 #if DEBUG_SORT 325 static int gSortCountDefault; 326 static int gSortCount; 327 #endif 328 329 #if DEBUG_ACTIVE_OP 330 static const char* kPathOpStr[]; 331 #endif 332 static bool gRunFail; 333 static bool gVeryVerbose; 334 335 #if DEBUG_ACTIVE_SPANS 336 static SkString gActiveSpans; 337 #endif 338 #if DEBUG_DUMP_VERIFY 339 static bool gDumpOp; 340 static bool gVerifyOp; 341 #endif 342 343 static const char* OpStr(SkPathOp ); 344 static void MathematicaIze(char* str, size_t bufferSize); 345 static bool ValidWind(int winding); 346 static void WindingPrintf(int winding); 347 348 static void ShowActiveSpans(SkOpContourHead* contourList); 349 static void ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration); 350 static void ShowPath(const SkPath& one, const SkPath& two, SkPathOp op, const char* name); 351 352 static bool ChaseContains(const SkTDArray<SkOpSpanBase*>& , const SkOpSpanBase* ); 353 354 static void CheckHealth(class SkOpContourHead* contourList); 355 356 #if DEBUG_COIN 357 static void DumpCoinDict(); 358 static void DumpGlitchType(GlitchType ); 359 #endif 360 361 }; 362 363 // Visual Studio 2017 does not permit calling member functions from the Immediate Window. 364 // Global functions work fine, however. Use globals rather than static members inside a class. 365 const SkOpAngle* AngleAngle(const SkOpAngle*, int id); 366 SkOpContour* AngleContour(SkOpAngle*, int id); 367 const SkOpPtT* AnglePtT(const SkOpAngle*, int id); 368 const SkOpSegment* AngleSegment(const SkOpAngle*, int id); 369 const SkOpSpanBase* AngleSpan(const SkOpAngle*, int id); 370 371 const SkOpAngle* ContourAngle(SkOpContour*, int id); 372 SkOpContour* ContourContour(SkOpContour*, int id); 373 const SkOpPtT* ContourPtT(SkOpContour*, int id); 374 const SkOpSegment* ContourSegment(SkOpContour*, int id); 375 const SkOpSpanBase* ContourSpan(SkOpContour*, int id); 376 377 const SkOpAngle* CoincidenceAngle(SkOpCoincidence*, int id); 378 SkOpContour* CoincidenceContour(SkOpCoincidence*, int id); 379 const SkOpPtT* CoincidencePtT(SkOpCoincidence*, int id); 380 const SkOpSegment* CoincidenceSegment(SkOpCoincidence*, int id); 381 const SkOpSpanBase* CoincidenceSpan(SkOpCoincidence*, int id); 382 383 const SkOpAngle* PtTAngle(const SkOpPtT*, int id); 384 SkOpContour* PtTContour(SkOpPtT*, int id); 385 const SkOpPtT* PtTPtT(const SkOpPtT*, int id); 386 const SkOpSegment* PtTSegment(const SkOpPtT*, int id); 387 const SkOpSpanBase* PtTSpan(const SkOpPtT*, int id); 388 389 const SkOpAngle* SegmentAngle(const SkOpSegment*, int id); 390 SkOpContour* SegmentContour(SkOpSegment*, int id); 391 const SkOpPtT* SegmentPtT(const SkOpSegment*, int id); 392 const SkOpSegment* SegmentSegment(const SkOpSegment*, int id); 393 const SkOpSpanBase* SegmentSpan(const SkOpSegment*, int id); 394 395 const SkOpAngle* SpanAngle(const SkOpSpanBase*, int id); 396 SkOpContour* SpanContour(SkOpSpanBase*, int id); 397 const SkOpPtT* SpanPtT(const SkOpSpanBase*, int id); 398 const SkOpSegment* SpanSegment(const SkOpSpanBase*, int id); 399 const SkOpSpanBase* SpanSpan(const SkOpSpanBase*, int id); 400 401 #if DEBUG_DUMP_VERIFY 402 void DumpOp(const SkPath& one, const SkPath& two, SkPathOp op, 403 const char* testName); 404 void DumpOp(FILE* file, const SkPath& one, const SkPath& two, SkPathOp op, 405 const char* testName); 406 void DumpSimplify(const SkPath& path, const char* testName); 407 void DumpSimplify(FILE* file, const SkPath& path, const char* testName); 408 void ReportOpFail(const SkPath& one, const SkPath& two, SkPathOp op); 409 void ReportSimplifyFail(const SkPath& path); 410 void VerifyOp(const SkPath& one, const SkPath& two, SkPathOp op, 411 const SkPath& result); 412 void VerifySimplify(const SkPath& path, const SkPath& result); 413 #endif 414 415 // global path dumps for msvs Visual Studio 17 to use from Immediate Window 416 void Dump(const SkOpContour& ); 417 void DumpAll(const SkOpContour& ); 418 void DumpAngles(const SkOpContour& ); 419 void DumpContours(const SkOpContour& ); 420 void DumpContoursAll(const SkOpContour& ); 421 void DumpContoursAngles(const SkOpContour& ); 422 void DumpContoursPts(const SkOpContour& ); 423 void DumpContoursPt(const SkOpContour& , int segmentID); 424 void DumpContoursSegment(const SkOpContour& , int segmentID); 425 void DumpContoursSpan(const SkOpContour& , int segmentID); 426 void DumpContoursSpans(const SkOpContour& ); 427 void DumpPt(const SkOpContour& , int ); 428 void DumpPts(const SkOpContour& , const char* prefix = "seg"); 429 void DumpSegment(const SkOpContour& , int ); 430 void DumpSegments(const SkOpContour& , const char* prefix = "seg", SkPathOp op = (SkPathOp) -1); 431 void DumpSpan(const SkOpContour& , int ); 432 void DumpSpans(const SkOpContour& ); 433 434 void Dump(const SkOpSegment& ); 435 void DumpAll(const SkOpSegment& ); 436 void DumpAngles(const SkOpSegment& ); 437 void DumpCoin(const SkOpSegment& ); 438 void DumpPts(const SkOpSegment& , const char* prefix = "seg"); 439 440 void Dump(const SkOpPtT& ); 441 void DumpAll(const SkOpPtT& ); 442 443 void Dump(const SkOpSpanBase& ); 444 void DumpCoin(const SkOpSpanBase& ); 445 void DumpAll(const SkOpSpanBase& ); 446 447 void DumpCoin(const SkOpSpan& ); 448 bool DumpSpan(const SkOpSpan& ); 449 450 void Dump(const SkDConic& ); 451 void DumpID(const SkDConic& , int id); 452 453 void Dump(const SkDCubic& ); 454 void DumpID(const SkDCubic& , int id); 455 456 void Dump(const SkDLine& ); 457 void DumpID(const SkDLine& , int id); 458 459 void Dump(const SkDQuad& ); 460 void DumpID(const SkDQuad& , int id); 461 462 void Dump(const SkDPoint& ); 463 464 void Dump(const SkOpAngle& ); 465 466 // generates tools/path_sorter.htm and path_visualizer.htm compatible data 467 void DumpQ(const SkDQuad& quad1, const SkDQuad& quad2, int testNo); 468 void DumpT(const SkDQuad& quad, double t); 469 470 // global path dumps for msvs Visual Studio 17 to use from Immediate Window 471 void Dump(const SkPath& path); 472 void DumpHex(const SkPath& path); 473 474 #endif 475