Home | History | Annotate | Download | only in pathops
      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 
     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 
     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 
     63 bool SkPathOpsDebug::ValidWind(int wind) {
     64     return wind > SK_MinS32 + 0xFFFF && wind < SK_MaxS32 - 0xFFFF;
     65 }
     66 
     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
     78 void* SkPathOpsDebug::CreateNameStr() {
     79     return SkNEW_ARRAY(char, DEBUG_FILENAME_STRING_LENGTH);
     80 }
     81 
     82 void SkPathOpsDebug::DeleteNameStr(void* v) {
     83     SkDELETE_ARRAY(reinterpret_cast<char* >(v));
     84 }
     85 
     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 
    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 
    118 const char* SkPathOpsDebug::OpStr(SkPathOp op) {
    119     return gOpStrs[op];
    120 }
    121 
    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 
    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 
    141 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 
    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 
    170 void SkOpSegment::debugReset() {
    171     this->init(this->fPts, this->fWeight, this->contour(), this->verb());
    172 }
    173 
    174 #if DEBUG_ACTIVE_SPANS
    175 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
    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 
    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
    285 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
    313 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 
    329 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 
    372 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 
    392 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 
    405 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 
    434 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 
    457 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 
    479 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
    503 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 
    526 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 
    548 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 
    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 
    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 
    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 
    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