Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright 2012 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 "PathOpsExtendedTest.h"
      9 #include "PathOpsThreadedCommon.h"
     10 #include "SkBitmap.h"
     11 #include "SkCanvas.h"
     12 #include "SkForceLinking.h"
     13 #include "SkMatrix.h"
     14 #include "SkPaint.h"
     15 #include "SkRTConf.h"
     16 #include "SkStream.h"
     17 #include "SkThreadPool.h"
     18 
     19 #ifdef SK_BUILD_FOR_MAC
     20 #include <sys/sysctl.h>
     21 #endif
     22 
     23 __SK_FORCE_IMAGE_DECODER_LINKING;
     24 
     25 static const char marker[] =
     26     "</div>\n"
     27     "\n"
     28     "<script type=\"text/javascript\">\n"
     29     "\n"
     30     "var testDivs = [\n";
     31 
     32 static const char* opStrs[] = {
     33     "kDifference_PathOp",
     34     "kIntersect_PathOp",
     35     "kUnion_PathOp",
     36     "kXor_PathOp",
     37     "kReverseDifference_PathOp",
     38 };
     39 
     40 static const char* opSuffixes[] = {
     41     "d",
     42     "i",
     43     "u",
     44     "o",
     45 };
     46 
     47 static bool gShowPath = false;
     48 static bool gComparePathsAssert = true;
     49 static bool gPathStrAssert = true;
     50 
     51 static const char* gFillTypeStr[] = {
     52     "kWinding_FillType",
     53     "kEvenOdd_FillType",
     54     "kInverseWinding_FillType",
     55     "kInverseEvenOdd_FillType"
     56 };
     57 
     58 static void output_scalar(SkScalar num) {
     59     if (num == (int) num) {
     60         SkDebugf("%d", (int) num);
     61     } else {
     62         SkString str;
     63         str.printf("%1.9g", num);
     64         int width = str.size();
     65         const char* cStr = str.c_str();
     66         while (cStr[width - 1] == '0') {
     67             --width;
     68         }
     69         str.resize(width);
     70         SkDebugf("%sf", str.c_str());
     71     }
     72 }
     73 
     74 static void output_points(const SkPoint* pts, int count) {
     75     for (int index = 0; index < count; ++index) {
     76         output_scalar(pts[index].fX);
     77         SkDebugf(", ");
     78         output_scalar(pts[index].fY);
     79         if (index + 1 < count) {
     80             SkDebugf(", ");
     81         }
     82     }
     83     SkDebugf(");\n");
     84 }
     85 
     86 static void showPathContours(SkPath::RawIter& iter, const char* pathName) {
     87     uint8_t verb;
     88     SkPoint pts[4];
     89     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
     90         switch (verb) {
     91             case SkPath::kMove_Verb:
     92                 SkDebugf("    %s.moveTo(", pathName);
     93                 output_points(&pts[0], 1);
     94                 continue;
     95             case SkPath::kLine_Verb:
     96                 SkDebugf("    %s.lineTo(", pathName);
     97                 output_points(&pts[1], 1);
     98                 break;
     99             case SkPath::kQuad_Verb:
    100                 SkDebugf("    %s.quadTo(", pathName);
    101                 output_points(&pts[1], 2);
    102                 break;
    103             case SkPath::kCubic_Verb:
    104                 SkDebugf("    %s.cubicTo(", pathName);
    105                 output_points(&pts[1], 3);
    106                 break;
    107             case SkPath::kClose_Verb:
    108                 SkDebugf("    %s.close();\n", pathName);
    109                 break;
    110             default:
    111                 SkDEBUGFAIL("bad verb");
    112                 return;
    113         }
    114     }
    115 }
    116 
    117 static void showPath(const SkPath& path, const char* pathName, bool includeDeclaration) {
    118     SkPath::RawIter iter(path);
    119 #define SUPPORT_RECT_CONTOUR_DETECTION 0
    120 #if SUPPORT_RECT_CONTOUR_DETECTION
    121     int rectCount = path.isRectContours() ? path.rectContours(NULL, NULL) : 0;
    122     if (rectCount > 0) {
    123         SkTDArray<SkRect> rects;
    124         SkTDArray<SkPath::Direction> directions;
    125         rects.setCount(rectCount);
    126         directions.setCount(rectCount);
    127         path.rectContours(rects.begin(), directions.begin());
    128         for (int contour = 0; contour < rectCount; ++contour) {
    129             const SkRect& rect = rects[contour];
    130             SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
    131                     rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
    132                     ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
    133         }
    134         return;
    135     }
    136 #endif
    137     SkPath::FillType fillType = path.getFillType();
    138     SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType);
    139     if (includeDeclaration) {
    140         SkDebugf("    SkPath %s;\n", pathName);
    141     }
    142     SkDebugf("    %s.setFillType(SkPath::%s);\n", pathName, gFillTypeStr[fillType]);
    143     iter.setPath(path);
    144     showPathContours(iter, pathName);
    145 }
    146 
    147 #if DEBUG_SHOW_TEST_NAME
    148 static void showPathData(const SkPath& path) {
    149     SkPath::RawIter iter(path);
    150     uint8_t verb;
    151     SkPoint pts[4];
    152     SkPoint firstPt, lastPt;
    153     bool firstPtSet = false;
    154     bool lastPtSet = true;
    155     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
    156         switch (verb) {
    157             case SkPath::kMove_Verb:
    158                 firstPt = pts[0];
    159                 firstPtSet = true;
    160                 continue;
    161             case SkPath::kLine_Verb:
    162                 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", pts[0].fX, pts[0].fY,
    163                         pts[1].fX, pts[1].fY);
    164                 lastPt = pts[1];
    165                 lastPtSet = true;
    166                 break;
    167             case SkPath::kQuad_Verb:
    168                 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
    169                         pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
    170                 lastPt = pts[2];
    171                 lastPtSet = true;
    172                 break;
    173             case SkPath::kCubic_Verb:
    174                 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
    175                         pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
    176                         pts[3].fX, pts[3].fY);
    177                 lastPt = pts[3];
    178                 lastPtSet = true;
    179                 break;
    180             case SkPath::kClose_Verb:
    181                 if (firstPtSet && lastPtSet && firstPt != lastPt) {
    182                     SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", lastPt.fX, lastPt.fY,
    183                             firstPt.fX, firstPt.fY);
    184                 }
    185                 firstPtSet = lastPtSet = false;
    186                 break;
    187             default:
    188                 SkDEBUGFAIL("bad verb");
    189                 return;
    190         }
    191     }
    192 }
    193 #endif
    194 
    195 void showOp(const SkPathOp op) {
    196     switch (op) {
    197         case kDifference_PathOp:
    198             SkDebugf("op difference\n");
    199             break;
    200         case kIntersect_PathOp:
    201             SkDebugf("op intersect\n");
    202             break;
    203         case kUnion_PathOp:
    204             SkDebugf("op union\n");
    205             break;
    206         case kXOR_PathOp:
    207             SkDebugf("op xor\n");
    208             break;
    209         case kReverseDifference_PathOp:
    210             SkDebugf("op reverse difference\n");
    211             break;
    212         default:
    213             SkASSERT(0);
    214     }
    215 }
    216 
    217 #if DEBUG_SHOW_TEST_NAME
    218 
    219 void ShowFunctionHeader(const char* functionName) {
    220     SkDebugf("\nstatic void %s(skiatest::Reporter* reporter) {\n", functionName);
    221     if (strcmp("skphealth_com76", functionName) == 0) {
    222         SkDebugf("found it\n");
    223     }
    224 }
    225 
    226 static const char* gOpStrs[] = {
    227     "kDifference_PathOp",
    228     "kIntersect_PathOp",
    229     "kUnion_PathOp",
    230     "kXor_PathOp",
    231     "kReverseDifference_PathOp",
    232 };
    233 
    234 void ShowOp(SkPathOp op, const char* pathOne, const char* pathTwo) {
    235     SkDebugf("    testPathOp(reporter, %s, %s, %s);\n", pathOne, pathTwo, gOpStrs[op]);
    236     SkDebugf("}\n");
    237 }
    238 #endif
    239 
    240 #if DEBUG_SHOW_TEST_NAME
    241 static char hexorator(int x) {
    242     if (x < 10) {
    243         return x + '0';
    244     }
    245     x -= 10;
    246     SkASSERT(x < 26);
    247     return x + 'A';
    248 }
    249 #endif
    250 
    251 void ShowTestName(PathOpsThreadState* state, int a, int b, int c, int d) {
    252 #if DEBUG_SHOW_TEST_NAME
    253     state->fSerialNo[0] = hexorator(state->fA);
    254     state->fSerialNo[1] = hexorator(state->fB);
    255     state->fSerialNo[2] = hexorator(state->fC);
    256     state->fSerialNo[3] = hexorator(state->fD);
    257     state->fSerialNo[4] = hexorator(a);
    258     state->fSerialNo[5] = hexorator(b);
    259     state->fSerialNo[6] = hexorator(c);
    260     state->fSerialNo[7] = hexorator(d);
    261     state->fSerialNo[8] = '\0';
    262     SkDebugf("%s\n", state->fSerialNo);
    263     if (strcmp(state->fSerialNo, state->fKey) == 0) {
    264         SkDebugf("%s\n", state->fPathStr);
    265     }
    266 #endif
    267 }
    268 
    269 const int bitWidth = 64;
    270 const int bitHeight = 64;
    271 
    272 static void scaleMatrix(const SkPath& one, const SkPath& two, SkMatrix& scale) {
    273     SkRect larger = one.getBounds();
    274     larger.join(two.getBounds());
    275     SkScalar largerWidth = larger.width();
    276     if (largerWidth < 4) {
    277         largerWidth = 4;
    278     }
    279     SkScalar largerHeight = larger.height();
    280     if (largerHeight < 4) {
    281         largerHeight = 4;
    282     }
    283     SkScalar hScale = (bitWidth - 2) / largerWidth;
    284     SkScalar vScale = (bitHeight - 2) / largerHeight;
    285     scale.reset();
    286     scale.preScale(hScale, vScale);
    287 }
    288 
    289 static int pathsDrawTheSame(SkBitmap& bits, const SkPath& scaledOne, const SkPath& scaledTwo,
    290         int& error2x2) {
    291     if (bits.width() == 0) {
    292         bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight);
    293         bits.allocPixels();
    294     }
    295     SkCanvas canvas(bits);
    296     canvas.drawColor(SK_ColorWHITE);
    297     SkPaint paint;
    298     canvas.save();
    299     const SkRect& bounds1 = scaledOne.getBounds();
    300     canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
    301     canvas.drawPath(scaledOne, paint);
    302     canvas.restore();
    303     canvas.save();
    304     canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
    305     canvas.drawPath(scaledTwo, paint);
    306     canvas.restore();
    307     int errors2 = 0;
    308     int errors = 0;
    309     for (int y = 0; y < bitHeight - 1; ++y) {
    310         uint32_t* addr1 = bits.getAddr32(0, y);
    311         uint32_t* addr2 = bits.getAddr32(0, y + 1);
    312         uint32_t* addr3 = bits.getAddr32(bitWidth, y);
    313         uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1);
    314         for (int x = 0; x < bitWidth - 1; ++x) {
    315             // count 2x2 blocks
    316             bool err = addr1[x] != addr3[x];
    317             if (err) {
    318                 errors2 += addr1[x + 1] != addr3[x + 1]
    319                         && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1];
    320                 errors++;
    321             }
    322         }
    323     }
    324     if (errors2 >= 6 || errors > 160) {
    325         SkDebugf("%s errors2=%d errors=%d\n", __FUNCTION__, errors2, errors);
    326     }
    327     error2x2 = errors2;
    328     return errors;
    329 }
    330 
    331 static int pathsDrawTheSame(const SkPath& one, const SkPath& two, SkBitmap& bits, SkPath& scaledOne,
    332         SkPath& scaledTwo, int& error2x2) {
    333     SkMatrix scale;
    334     scaleMatrix(one, two, scale);
    335     one.transform(scale, &scaledOne);
    336     two.transform(scale, &scaledTwo);
    337     return pathsDrawTheSame(bits, scaledOne, scaledTwo, error2x2);
    338 }
    339 
    340 bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawPaths) {
    341     if (!drawPaths) {
    342         return true;
    343     }
    344     const SkRect& bounds1 = one.getBounds();
    345     const SkRect& bounds2 = two.getBounds();
    346     SkRect larger = bounds1;
    347     larger.join(bounds2);
    348     SkBitmap bits;
    349     char out[256];
    350     int bitWidth = SkScalarCeil(larger.width()) + 2;
    351     if (bitWidth * 2 + 1 >= (int) sizeof(out)) {
    352         return false;
    353     }
    354     int bitHeight = SkScalarCeil(larger.height()) + 2;
    355     if (bitHeight >= (int) sizeof(out)) {
    356         return false;
    357     }
    358     bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight);
    359     bits.allocPixels();
    360     SkCanvas canvas(bits);
    361     canvas.drawColor(SK_ColorWHITE);
    362     SkPaint paint;
    363     canvas.save();
    364     canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
    365     canvas.drawPath(one, paint);
    366     canvas.restore();
    367     canvas.save();
    368     canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
    369     canvas.drawPath(two, paint);
    370     canvas.restore();
    371     for (int y = 0; y < bitHeight; ++y) {
    372         uint32_t* addr1 = bits.getAddr32(0, y);
    373         int x;
    374         char* outPtr = out;
    375         for (x = 0; x < bitWidth; ++x) {
    376             *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x';
    377         }
    378         *outPtr++ = '|';
    379         for (x = bitWidth; x < bitWidth * 2; ++x) {
    380             *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x';
    381         }
    382         *outPtr++ = '\0';
    383         SkDebugf("%s\n", out);
    384     }
    385     return true;
    386 }
    387 
    388 static void showSimplifiedPath(const SkPath& one, const SkPath& two,
    389         const SkPath& scaledOne, const SkPath& scaledTwo) {
    390     showPath(one, "path", false);
    391     drawAsciiPaths(scaledOne, scaledTwo, true);
    392 }
    393 
    394 static int comparePaths(skiatest::Reporter* reporter, const SkPath& one, const SkPath& two,
    395                  SkBitmap& bitmap) {
    396     int errors2x2;
    397     SkPath scaledOne, scaledTwo;
    398     int errors = pathsDrawTheSame(one, two, bitmap, scaledOne, scaledTwo, errors2x2);
    399     if (errors2x2 == 0) {
    400         return 0;
    401     }
    402     const int MAX_ERRORS = 9;
    403     if (errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
    404         showSimplifiedPath(one, two, scaledOne, scaledTwo);
    405     }
    406     if (errors2x2 > MAX_ERRORS && gComparePathsAssert) {
    407         SkDebugf("%s errors=%d\n", __FUNCTION__, errors);
    408         showSimplifiedPath(one, two, scaledOne, scaledTwo);
    409         REPORTER_ASSERT(reporter, 0);
    410     }
    411     return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
    412 }
    413 
    414 static void showPathOpPath(const SkPath& one, const SkPath& two, const SkPath& a, const SkPath& b,
    415         const SkPath& scaledOne, const SkPath& scaledTwo, const SkPathOp shapeOp,
    416         const SkMatrix& scale) {
    417     SkASSERT((unsigned) shapeOp < SK_ARRAY_COUNT(opStrs));
    418     SkDebugf("static void xOp#%s(skiatest::Reporter* reporter) {\n", opSuffixes[shapeOp]);
    419     SkDebugf("    SkPath path, pathB;\n");
    420     showPath(a, "path", false);
    421     showPath(b, "pathB", false);
    422     SkDebugf("    testPathOp(reporter, path, pathB, %s);\n", opStrs[shapeOp]);
    423     SkDebugf("}\n");
    424     drawAsciiPaths(scaledOne, scaledTwo, true);
    425 }
    426 
    427 static int comparePaths(skiatest::Reporter* reporter, const SkPath& one, const SkPath& scaledOne,
    428                         const SkPath& two, const SkPath& scaledTwo, SkBitmap& bitmap,
    429                         const SkPath& a, const SkPath& b, const SkPathOp shapeOp,
    430                         const SkMatrix& scale) {
    431     int errors2x2;
    432     int errors = pathsDrawTheSame(bitmap, scaledOne, scaledTwo, errors2x2);
    433     if (errors2x2 == 0) {
    434         if (gShowPath) {
    435             showPathOpPath(one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
    436         }
    437         return 0;
    438     }
    439     const int MAX_ERRORS = 8;
    440     if (gShowPath || errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
    441         showPathOpPath(one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
    442     }
    443     if (errors2x2 > MAX_ERRORS && gComparePathsAssert) {
    444         SkDebugf("%s errors=%d\n", __FUNCTION__, errors);
    445         showPathOpPath(one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
    446         REPORTER_ASSERT(reporter, 0);
    447     }
    448     return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
    449 }
    450 
    451 // Default values for when reporter->verbose() is false.
    452 static int testNumber = 1;
    453 static const char* testName = "pathOpTest";
    454 
    455 static void writeTestName(const char* nameSuffix, SkMemoryWStream& outFile) {
    456     outFile.writeText(testName);
    457     outFile.writeDecAsText(testNumber);
    458     if (nameSuffix) {
    459         outFile.writeText(nameSuffix);
    460     }
    461 }
    462 
    463 static void outputToStream(const char* pathStr, const char* pathPrefix, const char* nameSuffix,
    464         const char* testFunction, bool twoPaths, SkMemoryWStream& outFile) {
    465     outFile.writeText("<div id=\"");
    466     writeTestName(nameSuffix, outFile);
    467     outFile.writeText("\">\n");
    468     if (pathPrefix) {
    469         outFile.writeText(pathPrefix);
    470     }
    471     outFile.writeText(pathStr);
    472     outFile.writeText("</div>\n\n");
    473 
    474     outFile.writeText(marker);
    475     outFile.writeText("    ");
    476     writeTestName(nameSuffix, outFile);
    477     outFile.writeText(",\n\n\n");
    478 
    479     outFile.writeText("static void ");
    480     writeTestName(nameSuffix, outFile);
    481     outFile.writeText("() {\n    SkPath path");
    482     if (twoPaths) {
    483         outFile.writeText(", pathB");
    484     }
    485     outFile.writeText(";\n");
    486     if (pathPrefix) {
    487         outFile.writeText(pathPrefix);
    488     }
    489     outFile.writeText(pathStr);
    490     outFile.writeText("    ");
    491     outFile.writeText(testFunction);
    492     outFile.writeText("\n}\n\n");
    493     outFile.writeText("static void (*firstTest)() = ");
    494     writeTestName(nameSuffix, outFile);
    495     outFile.writeText(";\n\n");
    496 
    497     outFile.writeText("static struct {\n");
    498     outFile.writeText("    void (*fun)();\n");
    499     outFile.writeText("    const char* str;\n");
    500     outFile.writeText("} tests[] = {\n");
    501     outFile.writeText("    TEST(");
    502     writeTestName(nameSuffix, outFile);
    503     outFile.writeText("),\n");
    504     outFile.flush();
    505 }
    506 
    507 bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state,
    508                   const char* pathStr) {
    509     SkPath::FillType fillType = useXor ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
    510     path.setFillType(fillType);
    511     if (gShowPath) {
    512         showPath(path, "path", false);
    513     }
    514     if (!Simplify(path, &out)) {
    515         SkDebugf("%s did not expect failure\n", __FUNCTION__);
    516         REPORTER_ASSERT(state.fReporter, 0);
    517         return false;
    518     }
    519     if (!state.fReporter->verbose()) {
    520         return true;
    521     }
    522     int result = comparePaths(state.fReporter, path, out, *state.fBitmap);
    523     if (result && gPathStrAssert) {
    524         char temp[8192];
    525         sk_bzero(temp, sizeof(temp));
    526         SkMemoryWStream stream(temp, sizeof(temp));
    527         const char* pathPrefix = NULL;
    528         const char* nameSuffix = NULL;
    529         if (fillType == SkPath::kEvenOdd_FillType) {
    530             pathPrefix = "    path.setFillType(SkPath::kEvenOdd_FillType);\n";
    531             nameSuffix = "x";
    532         }
    533         const char testFunction[] = "testSimplifyx(path);";
    534         outputToStream(pathStr, pathPrefix, nameSuffix, testFunction, false, stream);
    535         SkDebugf(temp);
    536         REPORTER_ASSERT(state.fReporter, 0);
    537     }
    538     state.fReporter->bumpTestCount();
    539     return result == 0;
    540 }
    541 
    542 bool testSimplify(skiatest::Reporter* reporter, const SkPath& path) {
    543 #if DEBUG_SHOW_TEST_NAME
    544     showPathData(path);
    545 #endif
    546     SkPath out;
    547     if (!Simplify(path, &out)) {
    548         SkDebugf("%s did not expect failure\n", __FUNCTION__);
    549         REPORTER_ASSERT(reporter, 0);
    550         return false;
    551     }
    552     SkBitmap bitmap;
    553     int result = comparePaths(reporter, path, out, bitmap);
    554     if (result && gPathStrAssert) {
    555         REPORTER_ASSERT(reporter, 0);
    556     }
    557     reporter->bumpTestCount();
    558     return result == 0;
    559 }
    560 
    561 #if DEBUG_SHOW_TEST_NAME
    562 void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
    563         const char* testName) {
    564     ShowFunctionHeader(testName);
    565     showPath(a, "path", true);
    566     showPath(b, "pathB", true);
    567     ShowOp(shapeOp, "path", "pathB");
    568 }
    569 #endif
    570 
    571 static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
    572                  const SkPathOp shapeOp, const char* testName, bool threaded) {
    573 #if DEBUG_SHOW_TEST_NAME
    574     if (testName == NULL) {
    575         SkDebugf("\n");
    576         showPathData(a);
    577         showOp(shapeOp);
    578         showPathData(b);
    579     } else {
    580         SkPathOpsDebug::ShowPath(a, b, shapeOp, testName);
    581     }
    582 #endif
    583     SkPath out;
    584     if (!Op(a, b, shapeOp, &out) ) {
    585         SkDebugf("%s did not expect failure\n", __FUNCTION__);
    586         REPORTER_ASSERT(reporter, 0);
    587         return false;
    588     }
    589     if (threaded && !reporter->verbose()) {
    590         return true;
    591     }
    592     SkPath pathOut, scaledPathOut;
    593     SkRegion rgnA, rgnB, openClip, rgnOut;
    594     openClip.setRect(-16000, -16000, 16000, 16000);
    595     rgnA.setPath(a, openClip);
    596     rgnB.setPath(b, openClip);
    597     rgnOut.op(rgnA, rgnB, (SkRegion::Op) shapeOp);
    598     rgnOut.getBoundaryPath(&pathOut);
    599 
    600     SkMatrix scale;
    601     scaleMatrix(a, b, scale);
    602     SkRegion scaledRgnA, scaledRgnB, scaledRgnOut;
    603     SkPath scaledA, scaledB;
    604     scaledA.addPath(a, scale);
    605     scaledA.setFillType(a.getFillType());
    606     scaledB.addPath(b, scale);
    607     scaledB.setFillType(b.getFillType());
    608     scaledRgnA.setPath(scaledA, openClip);
    609     scaledRgnB.setPath(scaledB, openClip);
    610     scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) shapeOp);
    611     scaledRgnOut.getBoundaryPath(&scaledPathOut);
    612     SkBitmap bitmap;
    613     SkPath scaledOut;
    614     scaledOut.addPath(out, scale);
    615     scaledOut.setFillType(out.getFillType());
    616     int result = comparePaths(reporter, pathOut, scaledPathOut, out, scaledOut, bitmap, a, b,
    617                               shapeOp, scale);
    618     if (result && gPathStrAssert) {
    619         REPORTER_ASSERT(reporter, 0);
    620     }
    621     reporter->bumpTestCount();
    622     return result == 0;
    623 }
    624 
    625 bool testPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
    626                  const SkPathOp shapeOp, const char* testName) {
    627     return innerPathOp(reporter, a, b, shapeOp, testName, false);
    628 }
    629 
    630 bool testThreadedPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
    631                  const SkPathOp shapeOp, const char* testName) {
    632     return innerPathOp(reporter, a, b, shapeOp, testName, true);
    633 }
    634 
    635 SK_DECLARE_STATIC_MUTEX(gMutex);
    636 
    637 int initializeTests(skiatest::Reporter* reporter, const char* test) {
    638 #if 0  // doesn't work yet
    639     SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true);
    640     SK_CONF_SET("images.png.suppressDecoderWarnings", true);
    641 #endif
    642 #ifdef SK_DEBUG
    643     SkPathOpsDebug::gMaxWindSum = 4;
    644     SkPathOpsDebug::gMaxWindValue = 4;
    645 #endif
    646     if (reporter->verbose()) {
    647         SkAutoMutexAcquire lock(gMutex);
    648         testName = test;
    649         size_t testNameSize = strlen(test);
    650         SkFILEStream inFile("../../experimental/Intersection/op.htm");
    651         if (inFile.isValid()) {
    652             SkTDArray<char> inData;
    653             inData.setCount(inFile.getLength());
    654             size_t inLen = inData.count();
    655             inFile.read(inData.begin(), inLen);
    656             inFile.setPath(NULL);
    657             char* insert = strstr(inData.begin(), marker);
    658             if (insert) {
    659                 insert += sizeof(marker) - 1;
    660                 const char* numLoc = insert + 4 /* indent spaces */ + testNameSize - 1;
    661                 testNumber = atoi(numLoc) + 1;
    662             }
    663         }
    664     }
    665     return reporter->allowThreaded() ? SkThreadPool::kThreadPerCore : 1;
    666 }
    667 
    668 void outputProgress(char* ramStr, const char* pathStr, SkPath::FillType pathFillType) {
    669     const char testFunction[] = "testSimplify(path);";
    670     const char* pathPrefix = NULL;
    671     const char* nameSuffix = NULL;
    672     if (pathFillType == SkPath::kEvenOdd_FillType) {
    673         pathPrefix = "    path.setFillType(SkPath::kEvenOdd_FillType);\n";
    674         nameSuffix = "x";
    675     }
    676     SkMemoryWStream rRamStream(ramStr, PATH_STR_SIZE);
    677     outputToStream(pathStr, pathPrefix, nameSuffix, testFunction, false, rRamStream);
    678 }
    679 
    680 void outputProgress(char* ramStr, const char* pathStr, SkPathOp op) {
    681     const char testFunction[] = "testOp(path);";
    682     SkASSERT((size_t) op < SK_ARRAY_COUNT(opSuffixes));
    683     const char* nameSuffix = opSuffixes[op];
    684     SkMemoryWStream rRamStream(ramStr, PATH_STR_SIZE);
    685     outputToStream(pathStr, NULL, nameSuffix, testFunction, true, rRamStream);
    686 }
    687 
    688 void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count,
    689                 void (*firstTest)(skiatest::Reporter* ),
    690                 void (*stopTest)(skiatest::Reporter* ), bool reverse) {
    691     size_t index;
    692     if (firstTest) {
    693         index = count - 1;
    694         while (index > 0 && tests[index].fun != firstTest) {
    695             --index;
    696         }
    697 #if DEBUG_SHOW_TEST_NAME
    698             SkDebugf("<div id=\"%s\">\n", tests[index].str);
    699             SkDebugf("  %s [%s]\n", __FUNCTION__, tests[index].str);
    700 #endif
    701         (*tests[index].fun)(reporter);
    702     }
    703     index = reverse ? count - 1 : 0;
    704     size_t last = reverse ? 0 : count - 1;
    705     do {
    706         if (tests[index].fun != firstTest) {
    707     #if DEBUG_SHOW_TEST_NAME
    708             SkDebugf("<div id=\"%s\">\n", tests[index].str);
    709             SkDebugf("  %s [%s]\n", __FUNCTION__, tests[index].str);
    710     #endif
    711             (*tests[index].fun)(reporter);
    712         }
    713         if (tests[index].fun == stopTest) {
    714             SkDebugf("lastTest\n");
    715         }
    716         if (index == last) {
    717             break;
    718         }
    719         index += reverse ? -1 : 1;
    720     } while (true);
    721 }
    722