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