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 "include/core/SkBitmap.h" 9#include "include/core/SkCanvas.h" 10#include "include/core/SkMatrix.h" 11#include "include/core/SkPaint.h" 12#include "include/core/SkRegion.h" 13#include "include/core/SkStream.h" 14#include "include/private/SkMutex.h" 15#include "include/utils/SkParsePath.h" 16#include "src/core/SkPathPriv.h" 17#include "tests/PathOpsDebug.h" 18#include "tests/PathOpsExtendedTest.h" 19#include "tests/PathOpsThreadedCommon.h" 20 21#include <stdlib.h> 22#include <vector> 23#include <string> 24#include <algorithm> 25 26std::vector<std::string> gUniqueNames; 27 28#ifdef SK_BUILD_FOR_MAC 29#include <sys/sysctl.h> 30#endif 31 32// std::to_string isn't implemented on android 33#include <sstream> 34 35template <typename T> 36std::string std_to_string(T value) 37{ 38 std::ostringstream os ; 39 os << value ; 40 return os.str() ; 41} 42 43bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result 44 SkDEBUGPARAMS(bool skipAssert) 45 SkDEBUGPARAMS(const char* testName)); 46 47bool SimplifyDebug(const SkPath& one, SkPath* result 48 SkDEBUGPARAMS(bool skipAssert) 49 SkDEBUGPARAMS(const char* testName)); 50 51static const char marker[] = 52 "</div>\n" 53 "\n" 54 "<script type=\"text/javascript\">\n" 55 "\n" 56 "var testDivs = [\n"; 57 58static const char* opStrs[] = { 59 "kDifference_SkPathOp", 60 "kIntersect_SkPathOp", 61 "kUnion_SkPathOp", 62 "kXOR_PathOp", 63 "kReverseDifference_SkPathOp", 64}; 65 66static const char* opSuffixes[] = { 67 "d", 68 "i", 69 "u", 70 "o", 71 "r", 72}; 73 74enum class ExpectSuccess { 75 kNo, 76 kYes, 77 kFlaky 78}; 79 80enum class SkipAssert { 81 kNo, 82 kYes 83}; 84 85enum class ExpectMatch { 86 kNo, 87 kYes, 88 kFlaky 89}; 90 91void showOp(const SkPathOp op) { 92 switch (op) { 93 case kDifference_SkPathOp: 94 SkDebugf("op difference\n"); 95 break; 96 case kIntersect_SkPathOp: 97 SkDebugf("op intersect\n"); 98 break; 99 case kUnion_SkPathOp: 100 SkDebugf("op union\n"); 101 break; 102 case kXOR_SkPathOp: 103 SkDebugf("op xor\n"); 104 break; 105 case kReverseDifference_SkPathOp: 106 SkDebugf("op reverse difference\n"); 107 break; 108 default: 109 SkASSERT(0); 110 } 111} 112 113const int kBitWidth = 64; 114const int kBitHeight = 64; 115 116static void scaleMatrix(const SkPath& one, const SkPath& two, SkMatrix& scale) { 117 SkRect larger = one.getBounds(); 118 larger.join(two.getBounds()); 119 SkScalar largerWidth = larger.width(); 120 if (largerWidth < 4) { 121 largerWidth = 4; 122 } 123 SkScalar largerHeight = larger.height(); 124 if (largerHeight < 4) { 125 largerHeight = 4; 126 } 127 SkScalar hScale = (kBitWidth - 2) / largerWidth; 128 SkScalar vScale = (kBitHeight - 2) / largerHeight; 129 scale.reset(); 130 scale.preScale(hScale, vScale); 131 larger.fLeft *= hScale; 132 larger.fRight *= hScale; 133 larger.fTop *= vScale; 134 larger.fBottom *= vScale; 135 SkScalar dx = -16000 > larger.fLeft ? -16000 - larger.fLeft 136 : 16000 < larger.fRight ? 16000 - larger.fRight : 0; 137 SkScalar dy = -16000 > larger.fTop ? -16000 - larger.fTop 138 : 16000 < larger.fBottom ? 16000 - larger.fBottom : 0; 139 scale.postTranslate(dx, dy); 140} 141 142static int pathsDrawTheSame(SkBitmap& bits, const SkPath& scaledOne, const SkPath& scaledTwo, 143 int& error2x2) { 144 if (bits.width() == 0) { 145 bits.allocN32Pixels(kBitWidth * 2, kBitHeight); 146 } 147 SkCanvas canvas(bits); 148 canvas.drawColor(SK_ColorWHITE); 149 SkPaint paint; 150 canvas.save(); 151 const SkRect& bounds1 = scaledOne.getBounds(); 152 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1); 153 canvas.drawPath(scaledOne, paint); 154 canvas.restore(); 155 canvas.save(); 156 canvas.translate(-bounds1.fLeft + 1 + kBitWidth, -bounds1.fTop + 1); 157 canvas.drawPath(scaledTwo, paint); 158 canvas.restore(); 159 int errors2 = 0; 160 int errors = 0; 161 for (int y = 0; y < kBitHeight - 1; ++y) { 162 uint32_t* addr1 = bits.getAddr32(0, y); 163 uint32_t* addr2 = bits.getAddr32(0, y + 1); 164 uint32_t* addr3 = bits.getAddr32(kBitWidth, y); 165 uint32_t* addr4 = bits.getAddr32(kBitWidth, y + 1); 166 for (int x = 0; x < kBitWidth - 1; ++x) { 167 // count 2x2 blocks 168 bool err = addr1[x] != addr3[x]; 169 if (err) { 170 errors2 += addr1[x + 1] != addr3[x + 1] 171 && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1]; 172 errors++; 173 } 174 } 175 } 176 error2x2 = errors2; 177 return errors; 178} 179 180static int pathsDrawTheSame(const SkPath& one, const SkPath& two, SkBitmap& bits, SkPath& scaledOne, 181 SkPath& scaledTwo, int& error2x2) { 182 SkMatrix scale; 183 scaleMatrix(one, two, scale); 184 one.transform(scale, &scaledOne); 185 two.transform(scale, &scaledTwo); 186 return pathsDrawTheSame(bits, scaledOne, scaledTwo, error2x2); 187} 188 189bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawPaths) { 190 if (!drawPaths) { 191 return true; 192 } 193 const SkRect& bounds1 = one.getBounds(); 194 const SkRect& bounds2 = two.getBounds(); 195 SkRect larger = bounds1; 196 larger.join(bounds2); 197 SkBitmap bits; 198 char out[256]; 199 int bitWidth = SkScalarCeilToInt(larger.width()) + 2; 200 if (bitWidth * 2 + 1 >= (int) sizeof(out)) { 201 return false; 202 } 203 int bitHeight = SkScalarCeilToInt(larger.height()) + 2; 204 if (bitHeight >= (int) sizeof(out)) { 205 return false; 206 } 207 bits.allocN32Pixels(bitWidth * 2, bitHeight); 208 SkCanvas canvas(bits); 209 canvas.drawColor(SK_ColorWHITE); 210 SkPaint paint; 211 canvas.save(); 212 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1); 213 canvas.drawPath(one, paint); 214 canvas.restore(); 215 canvas.save(); 216 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1); 217 canvas.drawPath(two, paint); 218 canvas.restore(); 219 for (int y = 0; y < bitHeight; ++y) { 220 uint32_t* addr1 = bits.getAddr32(0, y); 221 int x; 222 char* outPtr = out; 223 for (x = 0; x < bitWidth; ++x) { 224 *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x'; 225 } 226 *outPtr++ = '|'; 227 for (x = bitWidth; x < bitWidth * 2; ++x) { 228 *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x'; 229 } 230 *outPtr++ = '\0'; 231 SkDebugf("%s\n", out); 232 } 233 return true; 234} 235 236int comparePaths(skiatest::Reporter* reporter, const char* filename, const SkPath& one, 237 const SkPath& two, SkBitmap& bitmap) { 238 int errors2x2; 239 SkPath scaledOne, scaledTwo; 240 (void) pathsDrawTheSame(one, two, bitmap, scaledOne, scaledTwo, errors2x2); 241 if (errors2x2 == 0) { 242 return 0; 243 } 244 const int MAX_ERRORS = 9; 245 return errors2x2 > MAX_ERRORS ? errors2x2 : 0; 246} 247 248static SkTDArray<SkPathOp> gTestOp; 249 250static void showPathOpPath(const char* testName, const SkPath& one, const SkPath& two, 251 const SkPath& a, const SkPath& b, const SkPath& scaledOne, const SkPath& scaledTwo, 252 const SkPathOp shapeOp, const SkMatrix& scale) { 253 SkASSERT((unsigned) shapeOp < SK_ARRAY_COUNT(opStrs)); 254 if (!testName) { 255 testName = "xOp"; 256 } 257 SkDebugf("static void %s_%s(skiatest::Reporter* reporter, const char* filename) {\n", 258 testName, opSuffixes[shapeOp]); 259 *gTestOp.append() = shapeOp; 260 SkDebugf(" SkPath path, pathB;\n"); 261 SkPathOpsDebug::ShowOnePath(a, "path", false); 262 SkPathOpsDebug::ShowOnePath(b, "pathB", false); 263 SkDebugf(" testPathOp(reporter, path, pathB, %s, filename);\n", opStrs[shapeOp]); 264 SkDebugf("}\n"); 265 drawAsciiPaths(scaledOne, scaledTwo, true); 266} 267 268static int comparePaths(skiatest::Reporter* reporter, const char* testName, const SkPath& one, 269 const SkPath& scaledOne, const SkPath& two, const SkPath& scaledTwo, SkBitmap& bitmap, 270 const SkPath& a, const SkPath& b, const SkPathOp shapeOp, const SkMatrix& scale, 271 ExpectMatch expectMatch) { 272 static SkMutex& compareDebugOut3 = *(new SkMutex); 273 int errors2x2; 274 const int MAX_ERRORS = 8; 275 (void) pathsDrawTheSame(bitmap, scaledOne, scaledTwo, errors2x2); 276 if (ExpectMatch::kNo == expectMatch) { 277 if (errors2x2 < MAX_ERRORS) { 278 REPORTER_ASSERT(reporter, 0); 279 } 280 return 0; 281 } 282 if (errors2x2 == 0) { 283 return 0; 284 } 285 if (ExpectMatch::kYes == expectMatch && errors2x2 >= MAX_ERRORS) { 286 SkAutoMutexExclusive autoM(compareDebugOut3); 287 showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale); 288 SkDebugf("\n/*"); 289 REPORTER_ASSERT(reporter, 0); 290 SkDebugf(" */\n"); 291 } 292 return errors2x2 >= MAX_ERRORS ? errors2x2 : 0; 293} 294 295// Default values for when reporter->verbose() is false. 296static int sTestNumber = 55; 297static const char* sTestName = "pathOpTest"; 298 299static void appendTestName(const char* nameSuffix, std::string& out) { 300 out += sTestName; 301 out += std_to_string(sTestNumber); 302 ++sTestNumber; 303 if (nameSuffix) { 304 out.append(nameSuffix); 305 } 306} 307 308static void appendTest(const char* pathStr, const char* pathPrefix, const char* nameSuffix, 309 const char* testFunction, bool twoPaths, std::string& out) { 310#if 0 311 out.append("\n<div id=\""); 312 appendTestName(nameSuffix, out); 313 out.append("\">\n"); 314 if (pathPrefix) { 315 out.append(pathPrefix); 316 } 317 out.append(pathStr); 318 out.append("</div>\n\n"); 319 320 out.append(marker); 321 out.append(" "); 322 appendTestName(nameSuffix, out); 323 out.append(",\n\n\n"); 324#endif 325 out.append("static void "); 326 appendTestName(nameSuffix, out); 327 out.append("(skiatest::Reporter* reporter) {\n SkPath path"); 328 if (twoPaths) { 329 out.append(", pathB"); 330 } 331 out.append(";\n"); 332 if (pathPrefix) { 333 out.append(pathPrefix); 334 } 335 out += pathStr; 336 out += " "; 337 out += testFunction; 338#if 0 339 out.append("static void (*firstTest)() = "); 340 appendTestName(nameSuffix, out); 341 out.append(";\n\n"); 342 343 out.append("static struct {\n"); 344 out.append(" void (*fun)();\n"); 345 out.append(" const char* str;\n"); 346 out.append("} tests[] = {\n"); 347 out.append(" TEST("); 348 appendTestName(nameSuffix, out); 349 out.append("),\n"); 350#endif 351} 352 353void markTestFlakyForPathKit() { 354 if (PathOpsDebug::gJson) { 355 SkASSERT(!PathOpsDebug::gMarkJsonFlaky); 356 PathOpsDebug::gMarkJsonFlaky = true; 357 } 358} 359 360bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state, 361 const char* pathStr) { 362 static SkMutex& simplifyDebugOut = *(new SkMutex); 363 SkPathFillType fillType = useXor ? SkPathFillType::kEvenOdd : SkPathFillType::kWinding; 364 path.setFillType(fillType); 365 state.fReporter->bumpTestCount(); 366 if (!Simplify(path, &out)) { 367 SkDebugf("%s did not expect failure\n", __FUNCTION__); 368 REPORTER_ASSERT(state.fReporter, 0); 369 return false; 370 } 371 if (!state.fReporter->verbose()) { 372 return true; 373 } 374 int result = comparePaths(state.fReporter, nullptr, path, out, *state.fBitmap); 375 if (result) { 376 SkAutoMutexExclusive autoM(simplifyDebugOut); 377 std::string str; 378 const char* pathPrefix = nullptr; 379 const char* nameSuffix = nullptr; 380 if (fillType == SkPathFillType::kEvenOdd) { 381 pathPrefix = " path.setFillType(SkPathFillType::kEvenOdd);\n"; 382 nameSuffix = "x"; 383 } 384 const char testFunction[] = "testSimplify(reporter, path);"; 385 appendTest(pathStr, pathPrefix, nameSuffix, testFunction, false, str); 386 SkDebugf("%s", str.c_str()); 387 REPORTER_ASSERT(state.fReporter, 0); 388 } 389 state.fReporter->bumpTestCount(); 390 return result == 0; 391} 392 393static void json_status(ExpectSuccess expectSuccess, ExpectMatch expectMatch, bool opSucceeded) { 394 fprintf(PathOpsDebug::gOut, " \"expectSuccess\": \"%s\",\n", 395 ExpectSuccess::kNo == expectSuccess ? "no" : 396 ExpectSuccess::kYes == expectSuccess ? "yes" : "flaky"); 397 if (PathOpsDebug::gMarkJsonFlaky) { 398 expectMatch = ExpectMatch::kFlaky; 399 PathOpsDebug::gMarkJsonFlaky = false; 400 } 401 fprintf(PathOpsDebug::gOut, " \"expectMatch\": \"%s\",\n", 402 ExpectMatch::kNo == expectMatch ? "no" : 403 ExpectMatch::kYes == expectMatch ? "yes" : "flaky"); 404 fprintf(PathOpsDebug::gOut, " \"succeeded\": %s,\n", opSucceeded ? "true" : "false"); 405} 406 407static void json_path_out(const SkPath& path, const char* pathName, const char* fillTypeName, 408 bool lastField) { 409 char const * const gFillTypeStrs[] = { 410 "Winding", 411 "EvenOdd", 412 "InverseWinding", 413 "InverseEvenOdd", 414 }; 415 if (PathOpsDebug::gOutputSVG) { 416 SkString svg; 417 SkParsePath::ToSVGString(path, &svg); 418 fprintf(PathOpsDebug::gOut, " \"%s\": \"%s\",\n", pathName, svg.c_str()); 419 } else { 420 // MOVE, LINE, QUAD, CONIC, CUBIC, CLOSE 421 const int verbConst[] = { 0, 1, 2, 3, 4, 5 }; 422 const int pointIndex[] = { 0, 1, 1, 1, 1, 0 }; 423 const int pointCount[] = { 1, 2, 3, 3, 4, 0 }; 424 fprintf(PathOpsDebug::gOut, " \"%s\": [", pathName); 425 bool first = true; 426 for (auto [verb, points, w] : SkPathPriv::Iterate(path)) { 427 if (first) { 428 first = false; 429 } else { 430 fprintf(PathOpsDebug::gOut, ",\n "); 431 } 432 int verbIndex = (int) verb; 433 fprintf(PathOpsDebug::gOut, "[%d", verbConst[verbIndex]); 434 for (int i = pointIndex[verbIndex]; i < pointCount[verbIndex]; ++i) { 435 fprintf(PathOpsDebug::gOut, ", \"0x%08x\", \"0x%08x\"", 436 SkFloat2Bits(points[i].fX), SkFloat2Bits(points[i].fY)); 437 } 438 if (SkPathVerb::kConic == verb) { 439 fprintf(PathOpsDebug::gOut, ", \"0x%08x\"", SkFloat2Bits(*w)); 440 } 441 fprintf(PathOpsDebug::gOut, "]"); 442 } 443 fprintf(PathOpsDebug::gOut, "],\n"); 444 } 445 fprintf(PathOpsDebug::gOut, " \"fillType%s\": \"k%s_FillType\"%s", fillTypeName, 446 gFillTypeStrs[(int) path.getFillType()], lastField ? "\n}" : ",\n"); 447} 448 449static bool check_for_duplicate_names(const char* testName) { 450 if (PathOpsDebug::gCheckForDuplicateNames) { 451 if (gUniqueNames.end() != std::find(gUniqueNames.begin(), gUniqueNames.end(), 452 std::string(testName))) { 453 SkDebugf("%s", ""); // convenience for setting breakpoints 454 } 455 gUniqueNames.push_back(std::string(testName)); 456 return true; 457 } 458 return false; 459} 460 461static bool inner_simplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename, 462 ExpectSuccess expectSuccess, SkipAssert skipAssert, ExpectMatch expectMatch) { 463 if (PathOpsDebug::gJson) { 464 if (check_for_duplicate_names(filename)) { 465 return true; 466 } 467 if (!PathOpsDebug::gOutFirst) { 468 fprintf(PathOpsDebug::gOut, ",\n"); 469 } 470 PathOpsDebug::gOutFirst = false; 471 fprintf(PathOpsDebug::gOut, "\"%s\": {\n", filename); 472 json_path_out(path, "path", "", false); 473 } 474 SkPath out; 475 if (!SimplifyDebug(path, &out SkDEBUGPARAMS(SkipAssert::kYes == skipAssert) 476 SkDEBUGPARAMS(sTestName))) { 477 if (ExpectSuccess::kYes == expectSuccess) { 478 SkDebugf("%s did not expect %s failure\n", __FUNCTION__, filename); 479 REPORTER_ASSERT(reporter, 0); 480 } 481 if (PathOpsDebug::gJson) { 482 json_status(expectSuccess, expectMatch, false); 483 fprintf(PathOpsDebug::gOut, " \"out\": \"\"\n}"); 484 } 485 return false; 486 } else { 487 if (ExpectSuccess::kNo == expectSuccess) { 488 SkDebugf("%s %s unexpected success\n", __FUNCTION__, filename); 489 REPORTER_ASSERT(reporter, 0); 490 } 491 if (PathOpsDebug::gJson) { 492 json_status(expectSuccess, expectMatch, true); 493 json_path_out(out, "out", "Out", true); 494 } 495 } 496 SkBitmap bitmap; 497 int errors = comparePaths(reporter, filename, path, out, bitmap); 498 if (ExpectMatch::kNo == expectMatch) { 499 if (!errors) { 500 SkDebugf("%s failing test %s now succeeds\n", __FUNCTION__, filename); 501 REPORTER_ASSERT(reporter, 0); 502 return false; 503 } 504 } else if (ExpectMatch::kYes == expectMatch && errors) { 505 REPORTER_ASSERT(reporter, 0); 506 } 507 reporter->bumpTestCount(); 508 return errors == 0; 509} 510 511bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename) { 512 return inner_simplify(reporter, path, filename, ExpectSuccess::kYes, SkipAssert::kNo, 513 ExpectMatch::kYes); 514} 515 516bool testSimplifyFuzz(skiatest::Reporter* reporter, const SkPath& path, const char* filename) { 517 return inner_simplify(reporter, path, filename, ExpectSuccess::kFlaky, SkipAssert::kYes, 518 ExpectMatch::kFlaky); 519} 520 521bool testSimplifyCheck(skiatest::Reporter* reporter, const SkPath& path, const char* filename, 522 bool checkFail) { 523 return inner_simplify(reporter, path, filename, checkFail ? 524 ExpectSuccess::kYes : ExpectSuccess::kNo, SkipAssert::kNo, ExpectMatch::kNo); 525} 526 527bool testSimplifyFail(skiatest::Reporter* reporter, const SkPath& path, const char* filename) { 528 return inner_simplify(reporter, path, filename, 529 ExpectSuccess::kNo, SkipAssert::kYes, ExpectMatch::kNo); 530} 531 532static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, 533 const SkPathOp shapeOp, const char* testName, ExpectSuccess expectSuccess, 534 SkipAssert skipAssert, ExpectMatch expectMatch) { 535 if (PathOpsDebug::gJson) { 536 if (check_for_duplicate_names(testName)) { 537 return true; 538 } 539 if (!PathOpsDebug::gOutFirst) { 540 fprintf(PathOpsDebug::gOut, ",\n"); 541 } 542 PathOpsDebug::gOutFirst = false; 543 fprintf(PathOpsDebug::gOut, "\"%s\": {\n", testName); 544 json_path_out(a, "p1", "1", false); 545 json_path_out(b, "p2", "2", false); 546 fprintf(PathOpsDebug::gOut, " \"op\": \"%s\",\n", opStrs[shapeOp]); 547 } 548 SkPath out; 549 if (!OpDebug(a, b, shapeOp, &out SkDEBUGPARAMS(SkipAssert::kYes == skipAssert) 550 SkDEBUGPARAMS(testName))) { 551 if (ExpectSuccess::kYes == expectSuccess) { 552 SkDebugf("%s %s did not expect failure\n", __FUNCTION__, testName); 553 REPORTER_ASSERT(reporter, 0); 554 } 555 if (PathOpsDebug::gJson) { 556 json_status(expectSuccess, expectMatch, false); 557 fprintf(PathOpsDebug::gOut, " \"out\": \"\"\n}"); 558 } 559 return false; 560 } else { 561 if (ExpectSuccess::kNo == expectSuccess) { 562 SkDebugf("%s %s unexpected success\n", __FUNCTION__, testName); 563 REPORTER_ASSERT(reporter, 0); 564 } 565 if (PathOpsDebug::gJson) { 566 json_status(expectSuccess, expectMatch, true); 567 json_path_out(out, "out", "Out", true); 568 } 569 } 570 if (!reporter->verbose()) { 571 return true; 572 } 573 SkPath pathOut, scaledPathOut; 574 SkRegion rgnA, rgnB, openClip, rgnOut; 575 openClip.setRect({-16000, -16000, 16000, 16000}); 576 rgnA.setPath(a, openClip); 577 rgnB.setPath(b, openClip); 578 rgnOut.op(rgnA, rgnB, (SkRegion::Op) shapeOp); 579 rgnOut.getBoundaryPath(&pathOut); 580 581 SkMatrix scale; 582 scaleMatrix(a, b, scale); 583 SkRegion scaledRgnA, scaledRgnB, scaledRgnOut; 584 SkPath scaledA, scaledB; 585 scaledA.addPath(a, scale); 586 scaledA.setFillType(a.getFillType()); 587 scaledB.addPath(b, scale); 588 scaledB.setFillType(b.getFillType()); 589 scaledRgnA.setPath(scaledA, openClip); 590 scaledRgnB.setPath(scaledB, openClip); 591 scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) shapeOp); 592 scaledRgnOut.getBoundaryPath(&scaledPathOut); 593 SkBitmap bitmap; 594 SkPath scaledOut; 595 scaledOut.addPath(out, scale); 596 scaledOut.setFillType(out.getFillType()); 597 int result = comparePaths(reporter, testName, pathOut, scaledPathOut, out, scaledOut, bitmap, 598 a, b, shapeOp, scale, expectMatch); 599 reporter->bumpTestCount(); 600 return result == 0; 601} 602 603bool testPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, 604 const SkPathOp shapeOp, const char* testName) { 605 return innerPathOp(reporter, a, b, shapeOp, testName, ExpectSuccess::kYes, SkipAssert::kNo, 606 ExpectMatch::kYes); 607} 608 609bool testPathOpCheck(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, 610 const SkPathOp shapeOp, const char* testName, bool checkFail) { 611 return innerPathOp(reporter, a, b, shapeOp, testName, checkFail ? 612 ExpectSuccess::kYes : ExpectSuccess::kNo, SkipAssert::kNo, ExpectMatch::kNo); 613} 614 615bool testPathOpFuzz(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, 616 const SkPathOp shapeOp, const char* testName) { 617 return innerPathOp(reporter, a, b, shapeOp, testName, ExpectSuccess::kFlaky, SkipAssert::kYes, 618 ExpectMatch::kFlaky); 619} 620 621bool testPathOpFail(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, 622 const SkPathOp shapeOp, const char* testName) { 623 SkPath orig; 624 orig.lineTo(54, 43); 625 SkPath out = orig; 626 if (Op(a, b, shapeOp, &out) ) { 627 SkDebugf("%s test is expected to fail\n", __FUNCTION__); 628 REPORTER_ASSERT(reporter, 0); 629 return false; 630 } 631 SkASSERT(out == orig); 632 return true; 633} 634 635void initializeTests(skiatest::Reporter* reporter, const char* test) { 636 static SkMutex& mu = *(new SkMutex); 637 if (reporter->verbose()) { 638 SkAutoMutexExclusive lock(mu); 639 sTestName = test; 640 size_t testNameSize = strlen(test); 641 SkFILEStream inFile("../../experimental/Intersection/op.htm"); 642 if (inFile.isValid()) { 643 SkTDArray<char> inData; 644 inData.setCount((int) inFile.getLength()); 645 size_t inLen = inData.count(); 646 inFile.read(inData.begin(), inLen); 647 inFile.close(); 648 char* insert = strstr(inData.begin(), marker); 649 if (insert) { 650 insert += sizeof(marker) - 1; 651 const char* numLoc = insert + 4 /* indent spaces */ + testNameSize - 1; 652 sTestNumber = atoi(numLoc) + 1; 653 } 654 } 655 } 656} 657 658void PathOpsThreadState::outputProgress(const char* pathStr, SkPathFillType pathFillType) { 659 const char testFunction[] = "testSimplify(path);"; 660 const char* pathPrefix = nullptr; 661 const char* nameSuffix = nullptr; 662 if (pathFillType == SkPathFillType::kEvenOdd) { 663 pathPrefix = " path.setFillType(SkPathFillType::kEvenOdd);\n"; 664 nameSuffix = "x"; 665 } 666 appendTest(pathStr, pathPrefix, nameSuffix, testFunction, false, fPathStr); 667} 668 669void PathOpsThreadState::outputProgress(const char* pathStr, SkPathOp op) { 670 const char testFunction[] = "testOp(path);"; 671 SkASSERT((size_t) op < SK_ARRAY_COUNT(opSuffixes)); 672 const char* nameSuffix = opSuffixes[op]; 673 appendTest(pathStr, nullptr, nameSuffix, testFunction, true, fPathStr); 674} 675 676void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count, 677 void (*firstTest)(skiatest::Reporter* , const char* filename), 678 void (*skipTest)(skiatest::Reporter* , const char* filename), 679 void (*stopTest)(skiatest::Reporter* , const char* filename), bool reverse) { 680 size_t index; 681 if (firstTest) { 682 index = count - 1; 683 while (index > 0 && tests[index].fun != firstTest) { 684 --index; 685 } 686 (*tests[index].fun)(reporter, tests[index].str); 687 if (tests[index].fun == stopTest) { 688 return; 689 } 690 } 691 index = reverse ? count - 1 : 0; 692 size_t last = reverse ? 0 : count - 1; 693 bool foundSkip = !skipTest; 694 do { 695 if (tests[index].fun == skipTest) { 696 foundSkip = true; 697 } 698 if (foundSkip && tests[index].fun != firstTest) { 699 (*tests[index].fun)(reporter, tests[index].str); 700 } 701 if (tests[index].fun == stopTest || index == last) { 702 break; 703 } 704 index += reverse ? -1 : 1; 705 } while (true); 706} 707