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