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 "SkPathOpsDebug.h"
9 #include "SkPath.h"
10 #include "SkString.h"
11 #include "SkThread.h"
12
13 #if DEBUG_VALIDATE
14 extern bool FLAGS_runFail;
15 #endif
16
17 #if DEBUG_SORT
18 int SkPathOpsDebug::gSortCountDefault = SK_MaxS32;
19 int SkPathOpsDebug::gSortCount;
20 #endif
21
22 #if DEBUG_ACTIVE_OP
23 const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor"};
24 #endif
25
26 #if defined SK_DEBUG || !FORCE_RELEASE
27
28 const char* SkPathOpsDebug::kLVerbStr[] = {"", "line", "quad", "cubic"};
29
30 #if defined(SK_DEBUG) || !FORCE_RELEASE
31 int SkPathOpsDebug::gContourID = 0;
32 int SkPathOpsDebug::gSegmentID = 0;
33 #endif
34
ChaseContains(const SkTDArray<SkOpSpanBase * > & chaseArray,const SkOpSpanBase * span)35 bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray,
36 const SkOpSpanBase* span) {
37 for (int index = 0; index < chaseArray.count(); ++index) {
38 const SkOpSpanBase* entry = chaseArray[index];
39 if (entry == span) {
40 return true;
41 }
42 }
43 return false;
44 }
45
MathematicaIze(char * str,size_t bufferLen)46 void SkPathOpsDebug::MathematicaIze(char* str, size_t bufferLen) {
47 size_t len = strlen(str);
48 bool num = false;
49 for (size_t idx = 0; idx < len; ++idx) {
50 if (num && str[idx] == 'e') {
51 if (len + 2 >= bufferLen) {
52 return;
53 }
54 memmove(&str[idx + 2], &str[idx + 1], len - idx);
55 str[idx] = '*';
56 str[idx + 1] = '^';
57 ++len;
58 }
59 num = str[idx] >= '0' && str[idx] <= '9';
60 }
61 }
62
ValidWind(int wind)63 bool SkPathOpsDebug::ValidWind(int wind) {
64 return wind > SK_MinS32 + 0xFFFF && wind < SK_MaxS32 - 0xFFFF;
65 }
66
WindingPrintf(int wind)67 void SkPathOpsDebug::WindingPrintf(int wind) {
68 if (wind == SK_MinS32) {
69 SkDebugf("?");
70 } else {
71 SkDebugf("%d", wind);
72 }
73 }
74 #endif // defined SK_DEBUG || !FORCE_RELEASE
75
76
77 #if DEBUG_SHOW_TEST_NAME
CreateNameStr()78 void* SkPathOpsDebug::CreateNameStr() {
79 return SkNEW_ARRAY(char, DEBUG_FILENAME_STRING_LENGTH);
80 }
81
DeleteNameStr(void * v)82 void SkPathOpsDebug::DeleteNameStr(void* v) {
83 SkDELETE_ARRAY(reinterpret_cast<char* >(v));
84 }
85
BumpTestName(char * test)86 void SkPathOpsDebug::BumpTestName(char* test) {
87 char* num = test + strlen(test);
88 while (num[-1] >= '0' && num[-1] <= '9') {
89 --num;
90 }
91 if (num[0] == '\0') {
92 return;
93 }
94 int dec = atoi(num);
95 if (dec == 0) {
96 return;
97 }
98 ++dec;
99 SK_SNPRINTF(num, DEBUG_FILENAME_STRING_LENGTH - (num - test), "%d", dec);
100 }
101 #endif
102
show_function_header(const char * functionName)103 static void show_function_header(const char* functionName) {
104 SkDebugf("\nstatic void %s(skiatest::Reporter* reporter, const char* filename) {\n", functionName);
105 if (strcmp("skphealth_com76", functionName) == 0) {
106 SkDebugf("found it\n");
107 }
108 }
109
110 static const char* gOpStrs[] = {
111 "kDifference_SkPathOp",
112 "kIntersect_SkPathOp",
113 "kUnion_SkPathOp",
114 "kXor_PathOp",
115 "kReverseDifference_SkPathOp",
116 };
117
OpStr(SkPathOp op)118 const char* SkPathOpsDebug::OpStr(SkPathOp op) {
119 return gOpStrs[op];
120 }
121
show_op(SkPathOp op,const char * pathOne,const char * pathTwo)122 static void show_op(SkPathOp op, const char* pathOne, const char* pathTwo) {
123 SkDebugf(" testPathOp(reporter, %s, %s, %s, filename);\n", pathOne, pathTwo, gOpStrs[op]);
124 SkDebugf("}\n");
125 }
126
127 SK_DECLARE_STATIC_MUTEX(gTestMutex);
128
ShowPath(const SkPath & a,const SkPath & b,SkPathOp shapeOp,const char * testName)129 void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
130 const char* testName) {
131 SkAutoMutexAcquire ac(gTestMutex);
132 show_function_header(testName);
133 ShowOnePath(a, "path", true);
134 ShowOnePath(b, "pathB", true);
135 show_op(shapeOp, "path", "pathB");
136 }
137
138 #include "SkPathOpsCubic.h"
139 #include "SkPathOpsQuad.h"
140
debugToCubic() const141 SkDCubic SkDQuad::debugToCubic() const {
142 SkDCubic cubic;
143 cubic[0] = fPts[0];
144 cubic[2] = fPts[1];
145 cubic[3] = fPts[2];
146 cubic[1].fX = (cubic[0].fX + cubic[2].fX * 2) / 3;
147 cubic[1].fY = (cubic[0].fY + cubic[2].fY * 2) / 3;
148 cubic[2].fX = (cubic[3].fX + cubic[2].fX * 2) / 3;
149 cubic[2].fY = (cubic[3].fY + cubic[2].fY * 2) / 3;
150 return cubic;
151 }
152
153 #include "SkOpAngle.h"
154 #include "SkOpCoincidence.h"
155 #include "SkOpSegment.h"
156
debugLastAngle()157 SkOpAngle* SkOpSegment::debugLastAngle() {
158 SkOpAngle* result = NULL;
159 SkOpSpan* span = this->head();
160 do {
161 if (span->toAngle()) {
162 SkASSERT(!result);
163 result = span->toAngle();
164 }
165 } while ((span = span->next()->upCastable()));
166 SkASSERT(result);
167 return result;
168 }
169
debugReset()170 void SkOpSegment::debugReset() {
171 this->init(this->fPts, this->fWeight, this->contour(), this->verb());
172 }
173
174 #if DEBUG_ACTIVE_SPANS
debugShowActiveSpans() const175 void SkOpSegment::debugShowActiveSpans() const {
176 debugValidate();
177 if (done()) {
178 return;
179 }
180 int lastId = -1;
181 double lastT = -1;
182 const SkOpSpan* span = &fHead;
183 do {
184 if (span->done()) {
185 continue;
186 }
187 if (lastId == this->debugID() && lastT == span->t()) {
188 continue;
189 }
190 lastId = this->debugID();
191 lastT = span->t();
192 SkDebugf("%s id=%d", __FUNCTION__, this->debugID());
193 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
194 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
195 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
196 }
197 if (SkPath::kConic_Verb == fVerb) {
198 SkDebugf(" %1.9gf", fWeight);
199 }
200 const SkOpPtT* ptT = span->ptT();
201 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", ptT->fT, ptT->fPt.fX, ptT->fPt.fY);
202 SkDebugf(" tEnd=%1.9g", span->next()->t());
203 if (span->windSum() == SK_MinS32) {
204 SkDebugf(" windSum=?");
205 } else {
206 SkDebugf(" windSum=%d", span->windSum());
207 }
208 if (span->oppValue() && span->oppSum() == SK_MinS32) {
209 SkDebugf(" oppSum=?");
210 } else if (span->oppValue() || span->oppSum() != SK_MinS32) {
211 SkDebugf(" oppSum=%d", span->oppSum());
212 }
213 SkDebugf(" windValue=%d", span->windValue());
214 if (span->oppValue() || span->oppSum() != SK_MinS32) {
215 SkDebugf(" oppValue=%d", span->oppValue());
216 }
217 SkDebugf("\n");
218 } while ((span = span->next()->upCastable()));
219 }
220 #endif
221
222 #if DEBUG_MARK_DONE
debugShowNewWinding(const char * fun,const SkOpSpan * span,int winding)223 void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding) {
224 const SkPoint& pt = span->ptT()->fPt;
225 SkDebugf("%s id=%d", fun, this->debugID());
226 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
227 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
228 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
229 }
230 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
231 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t());
232 if (winding == SK_MinS32) {
233 SkDebugf("?");
234 } else {
235 SkDebugf("%d", winding);
236 }
237 SkDebugf(" windSum=");
238 if (span->windSum() == SK_MinS32) {
239 SkDebugf("?");
240 } else {
241 SkDebugf("%d", span->windSum());
242 }
243 SkDebugf(" windValue=%d\n", span->windValue());
244 }
245
debugShowNewWinding(const char * fun,const SkOpSpan * span,int winding,int oppWinding)246 void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding,
247 int oppWinding) {
248 const SkPoint& pt = span->ptT()->fPt;
249 SkDebugf("%s id=%d", fun, this->debugID());
250 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
251 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
252 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
253 }
254 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
255 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t(), winding, oppWinding);
256 if (winding == SK_MinS32) {
257 SkDebugf("?");
258 } else {
259 SkDebugf("%d", winding);
260 }
261 SkDebugf(" newOppSum=");
262 if (oppWinding == SK_MinS32) {
263 SkDebugf("?");
264 } else {
265 SkDebugf("%d", oppWinding);
266 }
267 SkDebugf(" oppSum=");
268 if (span->oppSum() == SK_MinS32) {
269 SkDebugf("?");
270 } else {
271 SkDebugf("%d", span->oppSum());
272 }
273 SkDebugf(" windSum=");
274 if (span->windSum() == SK_MinS32) {
275 SkDebugf("?");
276 } else {
277 SkDebugf("%d", span->windSum());
278 }
279 SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue());
280 }
281
282 #endif
283
284 #if DEBUG_ANGLE
debugPart() const285 SkString SkOpAngle::debugPart() const {
286 SkString result;
287 switch (this->segment()->verb()) {
288 case SkPath::kLine_Verb:
289 result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fCurvePart),
290 this->segment()->debugID());
291 break;
292 case SkPath::kQuad_Verb:
293 result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fCurvePart),
294 this->segment()->debugID());
295 break;
296 case SkPath::kConic_Verb:
297 result.printf(CONIC_DEBUG_STR " id=%d",
298 CONIC_DEBUG_DATA(fCurvePart, fCurvePart.fConic.fWeight),
299 this->segment()->debugID());
300 break;
301 case SkPath::kCubic_Verb:
302 result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fCurvePart),
303 this->segment()->debugID());
304 break;
305 default:
306 SkASSERT(0);
307 }
308 return result;
309 }
310 #endif
311
312 #if DEBUG_SORT
debugLoop() const313 void SkOpAngle::debugLoop() const {
314 const SkOpAngle* first = this;
315 const SkOpAngle* next = this;
316 do {
317 next->dumpOne(true);
318 SkDebugf("\n");
319 next = next->fNext;
320 } while (next && next != first);
321 next = first;
322 do {
323 next->debugValidate();
324 next = next->fNext;
325 } while (next && next != first);
326 }
327 #endif
328
debugValidate() const329 void SkOpAngle::debugValidate() const {
330 #if DEBUG_VALIDATE
331 const SkOpAngle* first = this;
332 const SkOpAngle* next = this;
333 int wind = 0;
334 int opp = 0;
335 int lastXor = -1;
336 int lastOppXor = -1;
337 do {
338 if (next->unorderable()) {
339 return;
340 }
341 const SkOpSpan* minSpan = next->start()->starter(next->end());
342 if (minSpan->windValue() == SK_MinS32) {
343 return;
344 }
345 bool op = next->segment()->operand();
346 bool isXor = next->segment()->isXor();
347 bool oppXor = next->segment()->oppXor();
348 SkASSERT(!DEBUG_LIMIT_WIND_SUM || between(0, minSpan->windValue(), DEBUG_LIMIT_WIND_SUM));
349 SkASSERT(!DEBUG_LIMIT_WIND_SUM
350 || between(-DEBUG_LIMIT_WIND_SUM, minSpan->oppValue(), DEBUG_LIMIT_WIND_SUM));
351 bool useXor = op ? oppXor : isXor;
352 SkASSERT(lastXor == -1 || lastXor == (int) useXor);
353 lastXor = (int) useXor;
354 wind += next->debugSign() * (op ? minSpan->oppValue() : minSpan->windValue());
355 if (useXor) {
356 wind &= 1;
357 }
358 useXor = op ? isXor : oppXor;
359 SkASSERT(lastOppXor == -1 || lastOppXor == (int) useXor);
360 lastOppXor = (int) useXor;
361 opp += next->debugSign() * (op ? minSpan->windValue() : minSpan->oppValue());
362 if (useXor) {
363 opp &= 1;
364 }
365 next = next->fNext;
366 } while (next && next != first);
367 SkASSERT(wind == 0 || !FLAGS_runFail);
368 SkASSERT(opp == 0 || !FLAGS_runFail);
369 #endif
370 }
371
debugValidateNext() const372 void SkOpAngle::debugValidateNext() const {
373 #if !FORCE_RELEASE
374 const SkOpAngle* first = this;
375 const SkOpAngle* next = first;
376 SkTDArray<const SkOpAngle*>(angles);
377 do {
378 // SK_ALWAYSBREAK(next->fSegment->debugContains(next));
379 angles.push(next);
380 next = next->next();
381 if (next == first) {
382 break;
383 }
384 SK_ALWAYSBREAK(!angles.contains(next));
385 if (!next) {
386 return;
387 }
388 } while (true);
389 #endif
390 }
391
debugShowCoincidence() const392 void SkOpCoincidence::debugShowCoincidence() const {
393 SkCoincidentSpans* span = fHead;
394 while (span) {
395 SkDebugf("%s - id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__,
396 span->fCoinPtTStart->segment()->debugID(),
397 span->fCoinPtTStart->fT, span->fCoinPtTEnd->fT);
398 SkDebugf("%s + id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__,
399 span->fOppPtTStart->segment()->debugID(),
400 span->fOppPtTStart->fT, span->fOppPtTEnd->fT);
401 span = span->fNext;
402 }
403 }
404
debugValidate() const405 void SkOpSegment::debugValidate() const {
406 #if DEBUG_VALIDATE
407 const SkOpSpanBase* span = &fHead;
408 double lastT = -1;
409 const SkOpSpanBase* prev = NULL;
410 int count = 0;
411 int done = 0;
412 do {
413 if (!span->final()) {
414 ++count;
415 done += span->upCast()->done() ? 1 : 0;
416 }
417 SkASSERT(span->segment() == this);
418 SkASSERT(!prev || prev->upCast()->next() == span);
419 SkASSERT(!prev || prev == span->prev());
420 prev = span;
421 double t = span->ptT()->fT;
422 SkASSERT(lastT < t);
423 lastT = t;
424 span->debugValidate();
425 } while (!span->final() && (span = span->upCast()->next()));
426 SkASSERT(count == fCount);
427 SkASSERT(done == fDoneCount);
428 SkASSERT(count >= fDoneCount);
429 SkASSERT(span->final());
430 span->debugValidate();
431 #endif
432 }
433
debugCoinEndLoopCheck() const434 bool SkOpSpanBase::debugCoinEndLoopCheck() const {
435 int loop = 0;
436 const SkOpSpanBase* next = this;
437 SkOpSpanBase* nextCoin;
438 do {
439 nextCoin = next->fCoinEnd;
440 SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin);
441 for (int check = 1; check < loop - 1; ++check) {
442 const SkOpSpanBase* checkCoin = this->fCoinEnd;
443 const SkOpSpanBase* innerCoin = checkCoin;
444 for (int inner = check + 1; inner < loop; ++inner) {
445 innerCoin = innerCoin->fCoinEnd;
446 if (checkCoin == innerCoin) {
447 SkDebugf("*** bad coincident end loop ***\n");
448 return false;
449 }
450 }
451 }
452 ++loop;
453 } while ((next = nextCoin) && next != this);
454 return true;
455 }
456
debugValidate() const457 void SkOpSpanBase::debugValidate() const {
458 #if DEBUG_VALIDATE
459 const SkOpPtT* ptT = &fPtT;
460 SkASSERT(ptT->span() == this);
461 do {
462 // SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt));
463 ptT->debugValidate();
464 ptT = ptT->next();
465 } while (ptT != &fPtT);
466 SkASSERT(this->debugCoinEndLoopCheck());
467 if (!this->final()) {
468 SkASSERT(this->upCast()->debugCoinLoopCheck());
469 }
470 if (fFromAngle) {
471 fFromAngle->debugValidate();
472 }
473 if (!this->final() && this->upCast()->toAngle()) {
474 this->upCast()->toAngle()->debugValidate();
475 }
476 #endif
477 }
478
debugCoinLoopCheck() const479 bool SkOpSpan::debugCoinLoopCheck() const {
480 int loop = 0;
481 const SkOpSpan* next = this;
482 SkOpSpan* nextCoin;
483 do {
484 nextCoin = next->fCoincident;
485 SkASSERT(nextCoin == this || nextCoin->fCoincident != nextCoin);
486 for (int check = 1; check < loop - 1; ++check) {
487 const SkOpSpan* checkCoin = this->fCoincident;
488 const SkOpSpan* innerCoin = checkCoin;
489 for (int inner = check + 1; inner < loop; ++inner) {
490 innerCoin = innerCoin->fCoincident;
491 if (checkCoin == innerCoin) {
492 SkDebugf("*** bad coincident loop ***\n");
493 return false;
494 }
495 }
496 }
497 ++loop;
498 } while ((next = nextCoin) && next != this);
499 return true;
500 }
501
502 // called only by test code
debugCoincidentUsed() const503 int SkIntersections::debugCoincidentUsed() const {
504 if (!fIsCoincident[0]) {
505 SkASSERT(!fIsCoincident[1]);
506 return 0;
507 }
508 int count = 0;
509 SkDEBUGCODE(int count2 = 0;)
510 for (int index = 0; index < fUsed; ++index) {
511 if (fIsCoincident[0] & (1 << index)) {
512 ++count;
513 }
514 #ifdef SK_DEBUG
515 if (fIsCoincident[1] & (1 << index)) {
516 ++count2;
517 }
518 #endif
519 }
520 SkASSERT(count == count2);
521 return count;
522 }
523
524 #include "SkOpContour.h"
525
debugLoopLimit(bool report) const526 int SkOpPtT::debugLoopLimit(bool report) const {
527 int loop = 0;
528 const SkOpPtT* next = this;
529 do {
530 for (int check = 1; check < loop - 1; ++check) {
531 const SkOpPtT* checkPtT = this->fNext;
532 const SkOpPtT* innerPtT = checkPtT;
533 for (int inner = check + 1; inner < loop; ++inner) {
534 innerPtT = innerPtT->fNext;
535 if (checkPtT == innerPtT) {
536 if (report) {
537 SkDebugf("*** bad ptT loop ***\n");
538 }
539 return loop;
540 }
541 }
542 }
543 ++loop;
544 } while ((next = next->fNext) && next != this);
545 return 0;
546 }
547
debugValidate() const548 void SkOpPtT::debugValidate() const {
549 #if DEBUG_VALIDATE
550 if (contour()->globalState()->phase() == SkOpGlobalState::kIntersecting) {
551 return;
552 }
553 SkASSERT(fNext);
554 SkASSERT(fNext != this);
555 SkASSERT(fNext->fNext);
556 SkASSERT(debugLoopLimit(false) == 0);
557 #endif
558 }
559
output_scalar(SkScalar num)560 static void output_scalar(SkScalar num) {
561 if (num == (int) num) {
562 SkDebugf("%d", (int) num);
563 } else {
564 SkString str;
565 str.printf("%1.9g", num);
566 int width = (int) str.size();
567 const char* cStr = str.c_str();
568 while (cStr[width - 1] == '0') {
569 --width;
570 }
571 str.resize(width);
572 SkDebugf("%sf", str.c_str());
573 }
574 }
575
output_points(const SkPoint * pts,int count)576 static void output_points(const SkPoint* pts, int count) {
577 for (int index = 0; index < count; ++index) {
578 output_scalar(pts[index].fX);
579 SkDebugf(", ");
580 output_scalar(pts[index].fY);
581 if (index + 1 < count) {
582 SkDebugf(", ");
583 }
584 }
585 }
586
showPathContours(SkPath::RawIter & iter,const char * pathName)587 static void showPathContours(SkPath::RawIter& iter, const char* pathName) {
588 uint8_t verb;
589 SkPoint pts[4];
590 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
591 switch (verb) {
592 case SkPath::kMove_Verb:
593 SkDebugf(" %s.moveTo(", pathName);
594 output_points(&pts[0], 1);
595 SkDebugf(");\n");
596 continue;
597 case SkPath::kLine_Verb:
598 SkDebugf(" %s.lineTo(", pathName);
599 output_points(&pts[1], 1);
600 SkDebugf(");\n");
601 break;
602 case SkPath::kQuad_Verb:
603 SkDebugf(" %s.quadTo(", pathName);
604 output_points(&pts[1], 2);
605 SkDebugf(");\n");
606 break;
607 case SkPath::kConic_Verb:
608 SkDebugf(" %s.conicTo(", pathName);
609 output_points(&pts[1], 2);
610 SkDebugf(", %1.9gf);\n", iter.conicWeight());
611 break;
612 case SkPath::kCubic_Verb:
613 SkDebugf(" %s.cubicTo(", pathName);
614 output_points(&pts[1], 3);
615 SkDebugf(");\n");
616 break;
617 case SkPath::kClose_Verb:
618 SkDebugf(" %s.close();\n", pathName);
619 break;
620 default:
621 SkDEBUGFAIL("bad verb");
622 return;
623 }
624 }
625 }
626
627 static const char* gFillTypeStr[] = {
628 "kWinding_FillType",
629 "kEvenOdd_FillType",
630 "kInverseWinding_FillType",
631 "kInverseEvenOdd_FillType"
632 };
633
ShowOnePath(const SkPath & path,const char * name,bool includeDeclaration)634 void SkPathOpsDebug::ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration) {
635 SkPath::RawIter iter(path);
636 #define SUPPORT_RECT_CONTOUR_DETECTION 0
637 #if SUPPORT_RECT_CONTOUR_DETECTION
638 int rectCount = path.isRectContours() ? path.rectContours(NULL, NULL) : 0;
639 if (rectCount > 0) {
640 SkTDArray<SkRect> rects;
641 SkTDArray<SkPath::Direction> directions;
642 rects.setCount(rectCount);
643 directions.setCount(rectCount);
644 path.rectContours(rects.begin(), directions.begin());
645 for (int contour = 0; contour < rectCount; ++contour) {
646 const SkRect& rect = rects[contour];
647 SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
648 rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
649 ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
650 }
651 return;
652 }
653 #endif
654 SkPath::FillType fillType = path.getFillType();
655 SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType);
656 if (includeDeclaration) {
657 SkDebugf(" SkPath %s;\n", name);
658 }
659 SkDebugf(" %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[fillType]);
660 iter.setPath(path);
661 showPathContours(iter, name);
662 }
663