PathOpsSkpClipTest.cpp revision 78e7b4e1b928fa69f672be3c743df6d6c3ecbced
1 2#include "SkBitmap.h" 3#include "SkCanvas.h" 4#include "SkColor.h" 5#include "SkColorPriv.h" 6#include "SkDevice.h" 7#include "SkGraphics.h" 8#include "SkImageDecoder.h" 9#include "SkImageEncoder.h" 10#include "SkOSFile.h" 11#include "SkPathOpsDebug.h" 12#include "SkPicture.h" 13#include "SkRTConf.h" 14#include "SkStream.h" 15#include "SkString.h" 16#include "SkTArray.h" 17#include "SkTDArray.h" 18#include "SkThreadPool.h" 19#include "SkTime.h" 20#include "Test.h" 21#include "TestClassDef.h" 22 23#ifdef SK_BUILD_FOR_WIN 24 #define PATH_SLASH "\\" 25 #define IN_DIR "D:\\9-30-13\\" 26 #define OUT_DIR "D:\\opSkpClip\\1\\" 27#else 28 #define PATH_SLASH "/" 29 #ifdef SK_BUILD_FOR_MAC 30 #define IN_DIR "/Volumes/tera/9-30-13/skp" 31 #define OUT_DIR "/Volumes/tera/out/9-30-13/1/" 32 #else 33 #define IN_DIR "/usr/local/google/home/caryclark/skps/9-30-13/skp" 34 #define OUT_DIR "/mnt/skia/opSkpClip/1/" 35 #endif 36#endif 37 38const struct { 39 int directory; 40 const char* filename; 41} skipOverSept[] = { 42 {9, "http___www_symptome_ch_.skp"}, // triangle clip with corner at x.999 43 {11, "http___www_menly_fr_.skp"}, 44 {12, "http___www_banrasdr_com_.skp"}, 45}; 46 47size_t skipOverSeptCount = sizeof(skipOverSept) / sizeof(skipOverSept[0]); 48 49enum TestStep { 50 kCompareBits, 51 kEncodeFiles, 52}; 53 54enum { 55 kMaxLength = 128, 56 kMaxFiles = 128, 57 kSmallLimit = 1000, 58}; 59 60struct TestResult { 61 void init(int dirNo) { 62 fDirNo = dirNo; 63 sk_bzero(fFilename, sizeof(fFilename)); 64 fTestStep = kCompareBits; 65 fScaleOversized = true; 66 } 67 68 SkString status() { 69 SkString outStr; 70 outStr.printf("%s %d %d\n", fFilename, fPixelError, fTime); 71 return outStr; 72 } 73 74 static void Test(int dirNo, const char* filename, TestStep testStep) { 75 TestResult test; 76 test.init(dirNo); 77 test.fTestStep = testStep; 78 strcpy(test.fFilename, filename); 79 test.testOne(); 80 } 81 82 void test(int dirNo, const SkString& filename) { 83 init(dirNo); 84 strcpy(fFilename, filename.c_str()); 85 testOne(); 86 } 87 88 void testOne(); 89 90 char fFilename[kMaxLength]; 91 TestStep fTestStep; 92 int fDirNo; 93 int fPixelError; 94 int fTime; 95 bool fScaleOversized; 96}; 97 98struct TestState { 99 void init(int dirNo, skiatest::Reporter* reporter) { 100 fReporter = reporter; 101 fResult.init(dirNo); 102 fFoundCount = 0; 103 TestState::fSmallCount = 0; 104 fSmallestError = 0; 105 sk_bzero(fFilesFound, sizeof(fFilesFound)); 106 sk_bzero(fDirsFound, sizeof(fDirsFound)); 107 sk_bzero(fError, sizeof(fError)); 108 } 109 110 static bool bumpSmallCount() { 111 sk_atomic_inc(&fSmallCount); 112 return fSmallCount > kSmallLimit; 113 } 114 115 static void clearSmallCount() { 116 if (fSmallCount < kSmallLimit) { 117 fSmallCount = 0; 118 } 119 } 120 121 char fFilesFound[kMaxFiles][kMaxLength]; 122 int fDirsFound[kMaxFiles]; 123 int fError[kMaxFiles]; 124 int fFoundCount; 125 static int fSmallCount; 126 int fSmallestError; 127 skiatest::Reporter* fReporter; 128 TestResult fResult; 129}; 130 131int TestState::fSmallCount; 132 133struct TestRunner { 134 TestRunner(skiatest::Reporter* reporter, int threadCount) 135 : fNumThreads(threadCount) 136 , fReporter(reporter) { 137 } 138 139 ~TestRunner(); 140 void render(); 141 int fNumThreads; 142 SkTDArray<class TestRunnable*> fRunnables; 143 skiatest::Reporter* fReporter; 144}; 145 146class TestRunnable : public SkRunnable { 147public: 148 TestRunnable(void (*testFun)(TestState*), int dirNo, TestRunner* runner) { 149 fState.init(dirNo, runner->fReporter); 150 fTestFun = testFun; 151 } 152 153 virtual void run() SK_OVERRIDE { 154 SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024); 155 (*fTestFun)(&fState); 156 } 157 158 TestState fState; 159 void (*fTestFun)(TestState*); 160}; 161 162TestRunner::~TestRunner() { 163 for (int index = 0; index < fRunnables.count(); index++) { 164 SkDELETE(fRunnables[index]); 165 } 166} 167 168void TestRunner::render() { 169 SkThreadPool pool(fNumThreads); 170 for (int index = 0; index < fRunnables.count(); ++ index) { 171 pool.add(fRunnables[index]); 172 } 173} 174 175//////////////////////////////////////////////// 176 177static const char outOpDir[] = OUT_DIR "opClip"; 178static const char outOldDir[] = OUT_DIR "oldClip"; 179static const char outSkpDir[] = OUT_DIR "skpTest"; 180static const char outDiffDir[] = OUT_DIR "outTest"; 181static const char outStatusDir[] = OUT_DIR "statusTest"; 182 183static SkString make_filepath(int dirNo, const char* dir, const char* name) { 184 SkString path(dir); 185 if (dirNo) { 186 path.appendf("%d", dirNo); 187 } 188 path.append(PATH_SLASH); 189 path.append(name); 190 return path; 191} 192 193static SkString make_in_dir_name(int dirNo) { 194 SkString dirName(IN_DIR); 195 dirName.appendf("%d", dirNo); 196 if (!sk_exists(dirName.c_str())) { 197 SkDebugf("could not read dir %s\n", dirName.c_str()); 198 return SkString(); 199 } 200 return dirName; 201} 202 203static bool make_one_out_dir(const char* outDirStr) { 204 SkString outDir = make_filepath(0, outDirStr, ""); 205 if (!sk_exists(outDir.c_str())) { 206 if (!sk_mkdir(outDir.c_str())) { 207 SkDebugf("could not create dir %s\n", outDir.c_str()); 208 return false; 209 } 210 } 211 return true; 212} 213 214static bool make_out_dirs() { 215 SkString outDir = make_filepath(0, OUT_DIR, ""); 216 if (!sk_exists(outDir.c_str())) { 217 if (!sk_mkdir(outDir.c_str())) { 218 SkDebugf("could not create dir %s\n", outDir.c_str()); 219 return false; 220 } 221 } 222 return make_one_out_dir(outOldDir) 223 && make_one_out_dir(outOpDir) 224 && make_one_out_dir(outSkpDir) 225 && make_one_out_dir(outDiffDir) 226 && make_one_out_dir(outStatusDir); 227} 228 229static SkString make_png_name(const char* filename) { 230 SkString pngName = SkString(filename); 231 pngName.remove(pngName.size() - 3, 3); 232 pngName.append("png"); 233 return pngName; 234} 235 236static int similarBits(const SkBitmap& gr, const SkBitmap& sk) { 237 const int kRowCount = 3; 238 const int kThreshold = 3; 239 int width = SkTMin(gr.width(), sk.width()); 240 if (width < kRowCount) { 241 return true; 242 } 243 int height = SkTMin(gr.height(), sk.height()); 244 if (height < kRowCount) { 245 return true; 246 } 247 int errorTotal = 0; 248 SkTArray<int, true> errorRows; 249 errorRows.push_back_n(width * kRowCount); 250 SkAutoLockPixels autoGr(gr); 251 SkAutoLockPixels autoSk(sk); 252 for (int y = 0; y < height; ++y) { 253 SkPMColor* grRow = gr.getAddr32(0, y); 254 SkPMColor* skRow = sk.getAddr32(0, y); 255 int* base = &errorRows[0]; 256 int* cOut = &errorRows[y % kRowCount]; 257 for (int x = 0; x < width; ++x) { 258 SkPMColor grColor = grRow[x]; 259 SkPMColor skColor = skRow[x]; 260 int dr = SkGetPackedR32(grColor) - SkGetPackedR32(skColor); 261 int dg = SkGetPackedG32(grColor) - SkGetPackedG32(skColor); 262 int db = SkGetPackedB32(grColor) - SkGetPackedB32(skColor); 263 int error = cOut[x] = SkTMax(SkAbs32(dr), SkTMax(SkAbs32(dg), SkAbs32(db))); 264 if (error < kThreshold || x < 2) { 265 continue; 266 } 267 if (base[x - 2] < kThreshold 268 || base[width + x - 2] < kThreshold 269 || base[width * 2 + x - 2] < kThreshold 270 || base[x - 1] < kThreshold 271 || base[width + x - 1] < kThreshold 272 || base[width * 2 + x - 1] < kThreshold 273 || base[x] < kThreshold 274 || base[width + x] < kThreshold 275 || base[width * 2 + x] < kThreshold) { 276 continue; 277 } 278 errorTotal += error; 279 } 280 } 281 return errorTotal; 282} 283 284static bool addError(TestState* data, const TestResult& testResult) { 285 bool foundSmaller = false; 286 int dCount = data->fFoundCount; 287 int pixelError = testResult.fPixelError; 288 if (data->fFoundCount < kMaxFiles) { 289 data->fError[dCount] = pixelError; 290 strcpy(data->fFilesFound[dCount], testResult.fFilename); 291 data->fDirsFound[dCount] = testResult.fDirNo; 292 ++data->fFoundCount; 293 } else if (pixelError > data->fSmallestError) { 294 int smallest = SK_MaxS32; 295 int smallestIndex = 0; 296 for (int index = 0; index < kMaxFiles; ++index) { 297 if (smallest > data->fError[index]) { 298 smallest = data->fError[index]; 299 smallestIndex = index; 300 } 301 } 302 data->fError[smallestIndex] = pixelError; 303 strcpy(data->fFilesFound[smallestIndex], testResult.fFilename); 304 data->fDirsFound[smallestIndex] = testResult.fDirNo; 305 data->fSmallestError = SK_MaxS32; 306 for (int index = 0; index < kMaxFiles; ++index) { 307 if (data->fSmallestError > data->fError[index]) { 308 data->fSmallestError = data->fError[index]; 309 } 310 } 311 SkDebugf("*%d*", data->fSmallestError); 312 foundSmaller = true; 313 } 314 return foundSmaller; 315} 316 317 318 319static SkMSec timePict(SkPicture* pic, SkCanvas* canvas) { 320 canvas->save(); 321 int pWidth = pic->width(); 322 int pHeight = pic->height(); 323 const int maxDimension = 1000; 324 const int slices = 3; 325 int xInterval = SkTMax(pWidth - maxDimension, 0) / (slices - 1); 326 int yInterval = SkTMax(pHeight - maxDimension, 0) / (slices - 1); 327 SkRect rect = {0, 0, SkIntToScalar(SkTMin(maxDimension, pWidth)), 328 SkIntToScalar(SkTMin(maxDimension, pHeight))}; 329 canvas->clipRect(rect); 330 SkMSec start = SkTime::GetMSecs(); 331 for (int x = 0; x < slices; ++x) { 332 for (int y = 0; y < slices; ++y) { 333 pic->draw(canvas); 334 canvas->translate(0, SkIntToScalar(yInterval)); 335 } 336 canvas->translate(SkIntToScalar(xInterval), SkIntToScalar(-yInterval * slices)); 337 } 338 SkMSec end = SkTime::GetMSecs(); 339 canvas->restore(); 340 return end - start; 341} 342 343static void drawPict(SkPicture* pic, SkCanvas* canvas, int scale) { 344 canvas->clear(SK_ColorWHITE); 345 if (scale != 1) { 346 canvas->save(); 347 canvas->scale(1.0f / scale, 1.0f / scale); 348 } 349 pic->draw(canvas); 350 if (scale != 1) { 351 canvas->restore(); 352 } 353} 354 355static void writePict(const SkBitmap& bitmap, const char* outDir, const char* pngName) { 356 SkString outFile = make_filepath(0, outDir, pngName); 357 if (!SkImageEncoder::EncodeFile(outFile.c_str(), bitmap, 358 SkImageEncoder::kPNG_Type, 100)) { 359 SkDebugf("unable to encode gr %s (width=%d height=%d)\n", pngName, 360 bitmap.width(), bitmap.height()); 361 } 362} 363 364void TestResult::testOne() { 365 SkPicture* pic = NULL; 366 { 367 #if DEBUG_SHOW_TEST_NAME 368 if (fTestStep == kCompareBits) { 369 SkString testName(fFilename); 370 const char http[] = "http"; 371 if (testName.startsWith(http)) { 372 testName.remove(0, sizeof(http) - 1); 373 } 374 while (testName.startsWith("_")) { 375 testName.remove(0, 1); 376 } 377 const char dotSkp[] = ".skp"; 378 if (testName.endsWith(dotSkp)) { 379 size_t len = testName.size(); 380 testName.remove(len - (sizeof(dotSkp) - 1), sizeof(dotSkp) - 1); 381 } 382 testName.prepend("skp"); 383 testName.append("1"); 384 strncpy(DEBUG_FILENAME_STRING, testName.c_str(), DEBUG_FILENAME_STRING_LENGTH); 385 } else if (fTestStep == kEncodeFiles) { 386 strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH); 387 } 388 #endif 389 SkString path = make_filepath(fDirNo, IN_DIR, fFilename); 390 SkFILEStream stream(path.c_str()); 391 if (!stream.isValid()) { 392 SkDebugf("invalid stream %s\n", path.c_str()); 393 goto finish; 394 } 395 SkPicture* pic = SkPicture::CreateFromStream(&stream, &SkImageDecoder::DecodeMemory); 396 if (!pic) { 397 SkDebugf("unable to decode %s\n", fFilename); 398 goto finish; 399 } 400 int width = pic->width(); 401 int height = pic->height(); 402 SkBitmap oldBitmap, opBitmap; 403 int scale = 1; 404 do { 405 int dimX = (width + scale - 1) / scale; 406 int dimY = (height + scale - 1) / scale; 407 oldBitmap.setConfig(SkBitmap::kARGB_8888_Config, dimX, dimY); 408 opBitmap.setConfig(SkBitmap::kARGB_8888_Config, dimX, dimY); 409 bool success = oldBitmap.allocPixels() && opBitmap.allocPixels(); 410 if (success) { 411 break; 412 } 413 SkDebugf("-%d-", scale); 414 } while ((scale *= 2) < 256); 415 if (scale >= 256) { 416 SkDebugf("unable to allocate bitmap for %s (w=%d h=%d)\n", fFilename, 417 width, height); 418 return; 419 } 420 oldBitmap.eraseColor(SK_ColorWHITE); 421 SkCanvas oldCanvas(oldBitmap); 422 oldCanvas.setAllowSimplifyClip(false); 423 opBitmap.eraseColor(SK_ColorWHITE); 424 SkCanvas opCanvas(opBitmap); 425 opCanvas.setAllowSimplifyClip(true); 426 drawPict(pic, &oldCanvas, fScaleOversized ? scale : 1); 427 drawPict(pic, &opCanvas, fScaleOversized ? scale : 1); 428 if (fTestStep == kCompareBits) { 429 fPixelError = similarBits(oldBitmap, opBitmap); 430 int oldTime = timePict(pic, &oldCanvas); 431 int opTime = timePict(pic, &opCanvas); 432 fTime = oldTime - opTime; 433 } else if (fTestStep == kEncodeFiles) { 434 SkString pngStr = make_png_name(fFilename); 435 const char* pngName = pngStr.c_str(); 436 writePict(oldBitmap, outOldDir, pngName); 437 writePict(opBitmap, outOpDir, pngName); 438 } 439 } 440finish: 441 SkDELETE(pic); 442} 443 444static SkString makeStatusString(int dirNo) { 445 SkString statName; 446 statName.printf("stats%d.txt", dirNo); 447 SkString statusFile = make_filepath(0, outStatusDir, statName.c_str()); 448 return statusFile; 449} 450 451class PreParser { 452public: 453 PreParser(int dirNo) 454 : fDirNo(dirNo) 455 , fIndex(0) { 456 SkString statusPath = makeStatusString(dirNo); 457 if (!sk_exists(statusPath.c_str())) { 458 return; 459 } 460 SkFILEStream reader; 461 reader.setPath(statusPath.c_str()); 462 while (fetch(reader, &fResults.push_back())) 463 ; 464 fResults.pop_back(); 465 } 466 467 bool fetch(SkFILEStream& reader, TestResult* result) { 468 char c; 469 int i = 0; 470 result->init(fDirNo); 471 result->fPixelError = 0; 472 result->fTime = 0; 473 do { 474 bool readOne = reader.read(&c, 1) != 0; 475 if (!readOne) { 476 SkASSERT(i == 0); 477 return false; 478 } 479 if (c == ' ') { 480 result->fFilename[i++] = '\0'; 481 break; 482 } 483 result->fFilename[i++] = c; 484 SkASSERT(i < kMaxLength); 485 } while (true); 486 do { 487 SkAssertResult(reader.read(&c, 1)); 488 if (c == ' ') { 489 break; 490 } 491 SkASSERT(c >= '0' && c <= '9'); 492 result->fPixelError = result->fPixelError * 10 + (c - '0'); 493 } while (true); 494 bool minus = false; 495 do { 496 SkAssertResult(reader.read(&c, 1)); 497 if (c == '\n') { 498 break; 499 } 500 if (c == '-') { 501 minus = true; 502 continue; 503 } 504 SkASSERT(c >= '0' && c <= '9'); 505 result->fTime = result->fTime * 10 + (c - '0'); 506 } while (true); 507 if (minus) { 508 result->fTime = -result->fTime; 509 } 510 return true; 511 } 512 513 bool match(const SkString& filename, SkFILEWStream* stream, TestResult* result) { 514 if (fIndex < fResults.count()) { 515 *result = fResults[fIndex++]; 516 SkASSERT(filename.equals(result->fFilename)); 517 SkString outStr(result->status()); 518 stream->write(outStr.c_str(), outStr.size()); 519 return true; 520 } 521 return false; 522 } 523 524private: 525 int fDirNo; 526 int fIndex; 527 SkTArray<TestResult, true> fResults; 528}; 529 530static bool doOneDir(TestState* state) { 531 int dirNo = state->fResult.fDirNo; 532 skiatest::Reporter* reporter = state->fReporter; 533 SkString dirName = make_in_dir_name(dirNo); 534 SkASSERT(dirName.size()); 535 SkOSFile::Iter iter(dirName.c_str(), "skp"); 536 SkString filename; 537 int testCount = 0; 538 PreParser preParser(dirNo); 539 SkFILEWStream statusStream(makeStatusString(dirNo).c_str()); 540 while (iter.next(&filename)) { 541 for (size_t index = 0; index < skipOverSeptCount; ++index) { 542 if (skipOverSept[index].directory == dirNo 543 && strcmp(filename.c_str(), skipOverSept[index].filename) == 0) { 544 goto skipOver; 545 } 546 } 547 if (preParser.match(filename, &statusStream, &state->fResult)) { 548 addError(state, state->fResult); 549 ++testCount; 550 goto checkEarlyExit; 551 } 552 if (state->fSmallestError > 5000000) { 553 return false; 554 } 555 { 556 TestResult& result = state->fResult; 557 result.test(dirNo, filename); 558 SkString outStr(result.status()); 559 statusStream.write(outStr.c_str(), outStr.size()); 560 statusStream.flush(); 561 if (1) { 562 SkDebugf("%s", outStr.c_str()); 563 } 564 bool noMatch = addError(state, state->fResult); 565 if (noMatch) { 566 state->clearSmallCount(); 567 } else if (state->bumpSmallCount()) { 568 return false; 569 } 570 } 571 ++testCount; 572 if (reporter->verbose()) { 573 SkDebugf("."); 574 if (++testCount % 100 == 0) { 575 SkDebugf("%d\n", testCount); 576 } 577 } 578skipOver: 579 if (reporter->verbose()) { 580 static int threadTestCount; 581 SkDebugf("."); 582 sk_atomic_inc(&threadTestCount); 583 if (threadTestCount % 100 == 0) { 584 SkDebugf("%d\n", threadTestCount); 585 } 586 } 587checkEarlyExit: 588 if (1 && testCount == 20) { 589 return true; 590 } 591 } 592 return true; 593} 594 595static bool initTest() { 596#if !defined SK_BUILD_FOR_WIN && !defined SK_BUILD_FOR_MAC 597 SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true); 598 SK_CONF_SET("images.png.suppressDecoderWarnings", true); 599#endif 600 return make_out_dirs(); 601} 602 603static void encodeFound(skiatest::Reporter* reporter, TestState& state) { 604 if (reporter->verbose()) { 605 for (int index = 0; index < state.fFoundCount; ++index) { 606 SkDebugf("%d %s %d\n", state.fDirsFound[index], state.fFilesFound[index], 607 state.fError[index]); 608 } 609 } 610 for (int index = 0; index < state.fFoundCount; ++index) { 611 TestResult::Test(state.fDirsFound[index], state.fFilesFound[index], kEncodeFiles); 612 if (state.fReporter->verbose()) SkDebugf("+"); 613 } 614} 615 616DEF_TEST(PathOpsSkpClip, reporter) { 617 if (!initTest()) { 618 return; 619 } 620 SkTArray<TestResult, true> errors; 621 TestState state; 622 state.init(0, reporter); 623 for (int dirNo = 1; dirNo <= 100; ++dirNo) { 624 if (reporter->verbose()) { 625 SkDebugf("dirNo=%d\n", dirNo); 626 } 627 state.fResult.fDirNo = dirNo; 628 if (!doOneDir(&state)) { 629 break; 630 } 631 } 632 encodeFound(reporter, state); 633} 634 635static void testSkpClipMain(TestState* data) { 636 (void) doOneDir(data); 637} 638 639DEF_TEST(PathOpsSkpClipThreaded, reporter) { 640 if (!initTest()) { 641 return; 642 } 643 int threadCount = reporter->allowThreaded() ? SkThreadPool::kThreadPerCore : 1; 644 TestRunner testRunner(reporter, threadCount); 645 for (int dirNo = 1; dirNo <= 100; ++dirNo) { 646 *testRunner.fRunnables.append() = SkNEW_ARGS(TestRunnable, 647 (&testSkpClipMain, dirNo, &testRunner)); 648 } 649 testRunner.render(); 650 TestState state; 651 state.init(0, reporter); 652 for (int dirNo = 1; dirNo <= 100; ++dirNo) { 653 TestState& testState = testRunner.fRunnables[dirNo - 1]->fState; 654 for (int inner = 0; inner < testState.fFoundCount; ++inner) { 655 TestResult& testResult = testState.fResult; 656 SkASSERT(testResult.fDirNo == dirNo); 657 testResult.fPixelError = testState.fError[inner]; 658 strcpy(testResult.fFilename, testState.fFilesFound[inner]); 659 addError(&state, testResult); 660 } 661 } 662 encodeFound(reporter, state); 663} 664 665DEF_TEST(PathOpsSkpClipOneOff, reporter) { 666 if (!initTest()) { 667 return; 668 } 669 const int testIndex = 43 - 41; 670 int dirNo = skipOverSept[testIndex].directory; 671 SkAssertResult(make_in_dir_name(dirNo).size()); 672 SkString filename(skipOverSept[testIndex].filename); 673 TestResult state; 674 state.test(dirNo, filename); 675 if (reporter->verbose()) { 676 SkDebugf("%s", state.status().c_str()); 677 } 678 state.fTestStep = kEncodeFiles; 679 state.testOne(); 680} 681