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