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 IS_FUZZING_WITH_LIBFUZZER |
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 (const SkOpContour& , int segmentID); |
425 | void (const SkOpContour& , int segmentID); |
426 | void (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 | |