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