gmmain.cpp revision 65caeaf32d09f5886f3c740cfef2f1c26ef9cb50
1/* 2 * Copyright 2011 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/* 9 * Code for the "gm" (Golden Master) rendering comparison tool. 10 * 11 * If you make changes to this, re-run the self-tests at gm/tests/run.sh 12 * to make sure they still pass... you may need to change the expected 13 * results of the self-test. 14 */ 15 16#include "gm.h" 17#include "gm_error.h" 18#include "gm_expectations.h" 19#include "system_preferences.h" 20#include "SkBitmap.h" 21#include "SkColorPriv.h" 22#include "SkCommandLineFlags.h" 23#include "SkData.h" 24#include "SkDeferredCanvas.h" 25#include "SkDevice.h" 26#include "SkDrawFilter.h" 27#include "SkForceLinking.h" 28#include "SkGPipe.h" 29#include "SkGraphics.h" 30#include "SkImageDecoder.h" 31#include "SkImageEncoder.h" 32#include "SkOSFile.h" 33#include "SkPDFRasterizer.h" 34#include "SkPicture.h" 35#include "SkRefCnt.h" 36#include "SkStream.h" 37#include "SkTArray.h" 38#include "SkTDict.h" 39#include "SkTileGridPicture.h" 40#include "SamplePipeControllers.h" 41 42#ifdef SK_DEBUG 43static const bool kDebugOnly = true; 44#else 45static const bool kDebugOnly = false; 46#endif 47 48__SK_FORCE_IMAGE_DECODER_LINKING; 49 50#ifdef SK_BUILD_FOR_WIN 51 // json includes xlocale which generates warning 4530 because we're compiling without 52 // exceptions; see https://code.google.com/p/skia/issues/detail?id=1067 53 #pragma warning(push) 54 #pragma warning(disable : 4530) 55#endif 56#include "json/value.h" 57#ifdef SK_BUILD_FOR_WIN 58 #pragma warning(pop) 59#endif 60 61#if SK_SUPPORT_GPU 62#include "GrContextFactory.h" 63#include "SkGpuDevice.h" 64typedef GrContextFactory::GLContextType GLContextType; 65#define DEFAULT_CACHE_VALUE -1 66static int gGpuCacheSizeBytes; 67static int gGpuCacheSizeCount; 68#else 69class GrContextFactory; 70class GrContext; 71class GrSurface; 72typedef int GLContextType; 73#endif 74 75#define DEBUGFAIL_SEE_STDERR SkDEBUGFAIL("see stderr for message") 76 77extern bool gSkSuppressFontCachePurgeSpew; 78 79#ifdef SK_SUPPORT_PDF 80 #include "SkPDFDevice.h" 81 #include "SkPDFDocument.h" 82#endif 83 84// Until we resolve http://code.google.com/p/skia/issues/detail?id=455 , 85// stop writing out XPS-format image baselines in gm. 86#undef SK_SUPPORT_XPS 87#ifdef SK_SUPPORT_XPS 88 #include "SkXPSDevice.h" 89#endif 90 91#ifdef SK_BUILD_FOR_MAC 92 #include "SkCGUtils.h" 93#endif 94 95using namespace skiagm; 96 97class Iter { 98public: 99 Iter() { 100 this->reset(); 101 } 102 103 void reset() { 104 fReg = GMRegistry::Head(); 105 } 106 107 GM* next() { 108 if (fReg) { 109 GMRegistry::Factory fact = fReg->factory(); 110 fReg = fReg->next(); 111 return fact(0); 112 } 113 return NULL; 114 } 115 116 static int Count() { 117 const GMRegistry* reg = GMRegistry::Head(); 118 int count = 0; 119 while (reg) { 120 count += 1; 121 reg = reg->next(); 122 } 123 return count; 124 } 125 126private: 127 const GMRegistry* fReg; 128}; 129 130// TODO(epoger): Right now, various places in this code assume that all the 131// image files read/written by GM use this file extension. 132// Search for references to this constant to find these assumptions. 133const static char kPNG_FileExtension[] = "png"; 134 135enum Backend { 136 kRaster_Backend, 137 kGPU_Backend, 138 kPDF_Backend, 139 kXPS_Backend, 140}; 141 142enum BbhType { 143 kNone_BbhType, 144 kRTree_BbhType, 145 kTileGrid_BbhType, 146}; 147 148enum ConfigFlags { 149 kNone_ConfigFlag = 0x0, 150 /* Write GM images if a write path is provided. */ 151 kWrite_ConfigFlag = 0x1, 152 /* Read reference GM images if a read path is provided. */ 153 kRead_ConfigFlag = 0x2, 154 kRW_ConfigFlag = (kWrite_ConfigFlag | kRead_ConfigFlag), 155}; 156 157struct ConfigData { 158 SkBitmap::Config fConfig; 159 Backend fBackend; 160 GLContextType fGLContextType; // GPU backend only 161 int fSampleCnt; // GPU backend only 162 ConfigFlags fFlags; 163 const char* fName; 164 bool fRunByDefault; 165}; 166 167struct PDFRasterizerData { 168 bool (*fRasterizerFunction)(SkStream*, SkBitmap*); 169 const char* fName; 170 bool fRunByDefault; 171}; 172 173class BWTextDrawFilter : public SkDrawFilter { 174public: 175 virtual bool filter(SkPaint*, Type) SK_OVERRIDE; 176}; 177bool BWTextDrawFilter::filter(SkPaint* p, Type t) { 178 if (kText_Type == t) { 179 p->setAntiAlias(false); 180 } 181 return true; 182} 183 184struct PipeFlagComboData { 185 const char* name; 186 uint32_t flags; 187}; 188 189static PipeFlagComboData gPipeWritingFlagCombos[] = { 190 { "", 0 }, 191 { " cross-process", SkGPipeWriter::kCrossProcess_Flag }, 192 { " cross-process, shared address", SkGPipeWriter::kCrossProcess_Flag 193 | SkGPipeWriter::kSharedAddressSpace_Flag } 194}; 195 196static bool encode_to_dct_stream(SkWStream* stream, const SkBitmap& bitmap, const SkIRect& rect); 197 198const static ErrorCombination kDefaultIgnorableErrorTypes = ErrorCombination() 199 .plus(kMissingExpectations_ErrorType) 200 .plus(kIntentionallySkipped_ErrorType); 201 202class GMMain { 203public: 204 GMMain() : fUseFileHierarchy(false), fWriteChecksumBasedFilenames(false), 205 fIgnorableErrorTypes(kDefaultIgnorableErrorTypes), 206 fMismatchPath(NULL), fMissingExpectationsPath(NULL), fTestsRun(0), 207 fRenderModesEncountered(1) {} 208 209 /** 210 * Assemble shortNamePlusConfig from (surprise!) shortName and configName. 211 * 212 * The method for doing so depends on whether we are using hierarchical naming. 213 * For example, shortName "selftest1" and configName "8888" could be assembled into 214 * either "selftest1_8888" or "8888/selftest1". 215 */ 216 SkString make_shortname_plus_config(const char *shortName, const char *configName) { 217 SkString name; 218 if (0 == strlen(configName)) { 219 name.append(shortName); 220 } else if (fUseFileHierarchy) { 221 name.appendf("%s%c%s", configName, SkPATH_SEPARATOR, shortName); 222 } else { 223 name.appendf("%s_%s", shortName, configName); 224 } 225 return name; 226 } 227 228 /** 229 * Assemble filename, suitable for writing out the results of a particular test. 230 */ 231 SkString make_filename(const char *path, 232 const char *shortName, 233 const char *configName, 234 const char *renderModeDescriptor, 235 const char *suffix) { 236 SkString filename = make_shortname_plus_config(shortName, configName); 237 filename.append(renderModeDescriptor); 238 filename.appendUnichar('.'); 239 filename.append(suffix); 240 return SkOSPath::SkPathJoin(path, filename.c_str()); 241 } 242 243 /** 244 * Assemble filename suitable for writing out an SkBitmap. 245 */ 246 SkString make_bitmap_filename(const char *path, 247 const char *shortName, 248 const char *configName, 249 const char *renderModeDescriptor, 250 const GmResultDigest &bitmapDigest) { 251 if (fWriteChecksumBasedFilenames) { 252 SkString filename; 253 filename.append(bitmapDigest.getHashType()); 254 filename.appendUnichar('_'); 255 filename.append(shortName); 256 filename.appendUnichar('_'); 257 filename.append(bitmapDigest.getDigestValue()); 258 filename.appendUnichar('.'); 259 filename.append(kPNG_FileExtension); 260 return SkOSPath::SkPathJoin(path, filename.c_str()); 261 } else { 262 return make_filename(path, shortName, configName, renderModeDescriptor, 263 kPNG_FileExtension); 264 } 265 } 266 267 /* since PNG insists on unpremultiplying our alpha, we take no 268 precision chances and force all pixels to be 100% opaque, 269 otherwise on compare we may not get a perfect match. 270 */ 271 static void force_all_opaque(const SkBitmap& bitmap) { 272 SkBitmap::Config config = bitmap.config(); 273 switch (config) { 274 case SkBitmap::kARGB_8888_Config: 275 force_all_opaque_8888(bitmap); 276 break; 277 case SkBitmap::kRGB_565_Config: 278 // nothing to do here; 565 bitmaps are inherently opaque 279 break; 280 default: 281 gm_fprintf(stderr, "unsupported bitmap config %d\n", config); 282 DEBUGFAIL_SEE_STDERR; 283 } 284 } 285 286 static void force_all_opaque_8888(const SkBitmap& bitmap) { 287 SkAutoLockPixels lock(bitmap); 288 for (int y = 0; y < bitmap.height(); y++) { 289 for (int x = 0; x < bitmap.width(); x++) { 290 *bitmap.getAddr32(x, y) |= (SK_A32_MASK << SK_A32_SHIFT); 291 } 292 } 293 } 294 295 static ErrorCombination write_bitmap(const SkString& path, const SkBitmap& bitmap) { 296 // TODO(epoger): Now that we have removed force_all_opaque() 297 // from this method, we should be able to get rid of the 298 // transformation to 8888 format also. 299 SkBitmap copy; 300 bitmap.copyTo(©, SkBitmap::kARGB_8888_Config); 301 if (!SkImageEncoder::EncodeFile(path.c_str(), copy, 302 SkImageEncoder::kPNG_Type, 303 100)) { 304 gm_fprintf(stderr, "FAILED to write bitmap: %s\n", path.c_str()); 305 return ErrorCombination(kWritingReferenceImage_ErrorType); 306 } 307 return kEmpty_ErrorCombination; 308 } 309 310 /** 311 * Add all render modes encountered thus far to the "modes" array. 312 */ 313 void GetRenderModesEncountered(SkTArray<SkString> &modes) { 314 SkTDict<int>::Iter iter(this->fRenderModesEncountered); 315 const char* mode; 316 while ((mode = iter.next(NULL)) != NULL) { 317 SkString modeAsString = SkString(mode); 318 // TODO(epoger): It seems a bit silly that all of these modes were 319 // recorded with a leading "-" which we have to remove here 320 // (except for mode "", which means plain old original mode). 321 // But that's how renderModeDescriptor has been passed into 322 // compare_test_results_to_reference_bitmap() historically, 323 // and changing that now may affect other parts of our code. 324 if (modeAsString.startsWith("-")) { 325 modeAsString.remove(0, 1); 326 modes.push_back(modeAsString); 327 } 328 } 329 } 330 331 /** 332 * Records the results of this test in fTestsRun and fFailedTests. 333 * 334 * We even record successes, and errors that we regard as 335 * "ignorable"; we can filter them out later. 336 */ 337 void RecordTestResults(const ErrorCombination& errorCombination, 338 const SkString& shortNamePlusConfig, 339 const char renderModeDescriptor []) { 340 // Things to do regardless of errorCombination. 341 fTestsRun++; 342 int renderModeCount = 0; 343 this->fRenderModesEncountered.find(renderModeDescriptor, &renderModeCount); 344 renderModeCount++; 345 this->fRenderModesEncountered.set(renderModeDescriptor, renderModeCount); 346 347 if (errorCombination.isEmpty()) { 348 return; 349 } 350 351 // Things to do only if there is some error condition. 352 SkString fullName = shortNamePlusConfig; 353 fullName.append(renderModeDescriptor); 354 for (int typeInt = 0; typeInt <= kLast_ErrorType; typeInt++) { 355 ErrorType type = static_cast<ErrorType>(typeInt); 356 if (errorCombination.includes(type)) { 357 fFailedTests[type].push_back(fullName); 358 } 359 } 360 } 361 362 /** 363 * Return the number of significant (non-ignorable) errors we have 364 * encountered so far. 365 */ 366 int NumSignificantErrors() { 367 int significantErrors = 0; 368 for (int typeInt = 0; typeInt <= kLast_ErrorType; typeInt++) { 369 ErrorType type = static_cast<ErrorType>(typeInt); 370 if (!fIgnorableErrorTypes.includes(type)) { 371 significantErrors += fFailedTests[type].count(); 372 } 373 } 374 return significantErrors; 375 } 376 377 /** 378 * Display the summary of results with this ErrorType. 379 * 380 * @param type which ErrorType 381 * @param verbose whether to be all verbose about it 382 */ 383 void DisplayResultTypeSummary(ErrorType type, bool verbose) { 384 bool isIgnorableType = fIgnorableErrorTypes.includes(type); 385 386 SkString line; 387 if (isIgnorableType) { 388 line.append("[ ] "); 389 } else { 390 line.append("[*] "); 391 } 392 393 SkTArray<SkString> *failedTestsOfThisType = &fFailedTests[type]; 394 int count = failedTestsOfThisType->count(); 395 line.appendf("%d %s", count, getErrorTypeName(type)); 396 if (!isIgnorableType || verbose) { 397 line.append(":"); 398 for (int i = 0; i < count; ++i) { 399 line.append(" "); 400 line.append((*failedTestsOfThisType)[i]); 401 } 402 } 403 gm_fprintf(stdout, "%s\n", line.c_str()); 404 } 405 406 /** 407 * List contents of fFailedTests to stdout. 408 * 409 * @param verbose whether to be all verbose about it 410 */ 411 void ListErrors(bool verbose) { 412 // First, print a single summary line. 413 SkString summary; 414 summary.appendf("Ran %d tests:", fTestsRun); 415 for (int typeInt = 0; typeInt <= kLast_ErrorType; typeInt++) { 416 ErrorType type = static_cast<ErrorType>(typeInt); 417 summary.appendf(" %s=%d", getErrorTypeName(type), fFailedTests[type].count()); 418 } 419 gm_fprintf(stdout, "%s\n", summary.c_str()); 420 421 // Now, for each failure type, list the tests that failed that way. 422 for (int typeInt = 0; typeInt <= kLast_ErrorType; typeInt++) { 423 this->DisplayResultTypeSummary(static_cast<ErrorType>(typeInt), verbose); 424 } 425 gm_fprintf(stdout, "(results marked with [*] will cause nonzero return value)\n"); 426 } 427 428 static ErrorCombination write_document(const SkString& path, SkStreamAsset* asset) { 429 SkFILEWStream stream(path.c_str()); 430 if (!stream.writeStream(asset, asset->getLength())) { 431 gm_fprintf(stderr, "FAILED to write document: %s\n", path.c_str()); 432 return ErrorCombination(kWritingReferenceImage_ErrorType); 433 } 434 return kEmpty_ErrorCombination; 435 } 436 437 /** 438 * Prepare an SkBitmap to render a GM into. 439 * 440 * After you've rendered the GM into the SkBitmap, you must call 441 * complete_bitmap()! 442 * 443 * @todo thudson 22 April 2011 - could refactor this to take in 444 * a factory to generate the context, always call readPixels() 445 * (logically a noop for rasters, if wasted time), and thus collapse the 446 * GPU special case and also let this be used for SkPicture testing. 447 */ 448 static void setup_bitmap(const ConfigData& gRec, SkISize& size, 449 SkBitmap* bitmap) { 450 bitmap->setConfig(gRec.fConfig, size.width(), size.height()); 451 bitmap->allocPixels(); 452 bitmap->eraseColor(SK_ColorTRANSPARENT); 453 } 454 455 /** 456 * Any finalization steps we need to perform on the SkBitmap after 457 * we have rendered the GM into it. 458 * 459 * It's too bad that we are throwing away alpha channel data 460 * we could otherwise be examining, but this had always been happening 461 * before... it was buried within the compare() method at 462 * https://code.google.com/p/skia/source/browse/trunk/gm/gmmain.cpp?r=7289#305 . 463 * 464 * Apparently we need this, at least for bitmaps that are either: 465 * (a) destined to be written out as PNG files, or 466 * (b) compared against bitmaps read in from PNG files 467 * for the reasons described just above the force_all_opaque() method. 468 * 469 * Neglecting to do this led to the difficult-to-diagnose 470 * http://code.google.com/p/skia/issues/detail?id=1079 ('gm generating 471 * spurious pixel_error messages as of r7258') 472 * 473 * TODO(epoger): Come up with a better solution that allows us to 474 * compare full pixel data, including alpha channel, while still being 475 * robust in the face of transformations to/from PNG files. 476 * Options include: 477 * 478 * 1. Continue to call force_all_opaque(), but ONLY for bitmaps that 479 * will be written to, or compared against, PNG files. 480 * PRO: Preserve/compare alpha channel info for the non-PNG cases 481 * (comparing different renderModes in-memory) 482 * CON: The bitmaps (and hash digests) for these non-PNG cases would be 483 * different than those for the PNG-compared cases, and in the 484 * case of a failed renderMode comparison, how would we write the 485 * image to disk for examination? 486 * 487 * 2. Always compute image hash digests from PNG format (either 488 * directly from the the bytes of a PNG file, or capturing the 489 * bytes we would have written to disk if we were writing the 490 * bitmap out as a PNG). 491 * PRO: I think this would allow us to never force opaque, and to 492 * the extent that alpha channel data can be preserved in a PNG 493 * file, we could observe it. 494 * CON: If we read a bitmap from disk, we need to take its hash digest 495 * from the source PNG (we can't compute it from the bitmap we 496 * read out of the PNG, because we will have already premultiplied 497 * the alpha). 498 * CON: Seems wasteful to convert a bitmap to PNG format just to take 499 * its hash digest. (Although we're wasting lots of effort already 500 * calling force_all_opaque().) 501 * 502 * 3. Make the alpha premultiply/unpremultiply routines 100% consistent, 503 * so we can transform images back and forth without fear of off-by-one 504 * errors. 505 * CON: Math is hard. 506 * 507 * 4. Perform a "close enough" comparison of bitmaps (+/- 1 bit in each 508 * channel), rather than demanding absolute equality. 509 * CON: Can't do this with hash digests. 510 */ 511 static void complete_bitmap(SkBitmap* bitmap) { 512 force_all_opaque(*bitmap); 513 } 514 515 static void installFilter(SkCanvas* canvas); 516 517 static void invokeGM(GM* gm, SkCanvas* canvas, bool isPDF, bool isDeferred) { 518 SkAutoCanvasRestore acr(canvas, true); 519 520 if (!isPDF) { 521 canvas->concat(gm->getInitialTransform()); 522 } 523 installFilter(canvas); 524 gm->setCanvasIsDeferred(isDeferred); 525 gm->draw(canvas); 526 canvas->setDrawFilter(NULL); 527 } 528 529 static ErrorCombination generate_image(GM* gm, const ConfigData& gRec, 530 GrSurface* gpuTarget, 531 SkBitmap* bitmap, 532 bool deferred) { 533 SkISize size (gm->getISize()); 534 setup_bitmap(gRec, size, bitmap); 535 536 SkAutoTUnref<SkCanvas> canvas; 537 538 if (gRec.fBackend == kRaster_Backend) { 539 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (*bitmap))); 540 if (deferred) { 541 canvas.reset(SkDeferredCanvas::Create(device)); 542 } else { 543 canvas.reset(SkNEW_ARGS(SkCanvas, (device))); 544 } 545 invokeGM(gm, canvas, false, deferred); 546 canvas->flush(); 547 } 548#if SK_SUPPORT_GPU 549 else { // GPU 550 SkAutoTUnref<SkBaseDevice> device(SkGpuDevice::Create(gpuTarget)); 551 if (deferred) { 552 canvas.reset(SkDeferredCanvas::Create(device)); 553 } else { 554 canvas.reset(SkNEW_ARGS(SkCanvas, (device))); 555 } 556 invokeGM(gm, canvas, false, deferred); 557 // the device is as large as the current rendertarget, so 558 // we explicitly only readback the amount we expect (in 559 // size) overwrite our previous allocation 560 bitmap->setConfig(SkBitmap::kARGB_8888_Config, size.fWidth, 561 size.fHeight); 562 canvas->readPixels(bitmap, 0, 0); 563 } 564#endif 565 complete_bitmap(bitmap); 566 return kEmpty_ErrorCombination; 567 } 568 569 static void generate_image_from_picture(GM* gm, const ConfigData& gRec, 570 SkPicture* pict, SkBitmap* bitmap, 571 SkScalar scale = SK_Scalar1, 572 bool tile = false) { 573 SkISize size = gm->getISize(); 574 setup_bitmap(gRec, size, bitmap); 575 576 if (tile) { 577 // Generate the result image by rendering to tiles and accumulating 578 // the results in 'bitmap' 579 580 // This 16x16 tiling matches the settings applied to 'pict' in 581 // 'generate_new_picture' 582 SkISize tileSize = SkISize::Make(16, 16); 583 584 SkBitmap tileBM; 585 setup_bitmap(gRec, tileSize, &tileBM); 586 SkCanvas tileCanvas(tileBM); 587 installFilter(&tileCanvas); 588 589 SkCanvas bmpCanvas(*bitmap); 590 SkPaint bmpPaint; 591 bmpPaint.setXfermodeMode(SkXfermode::kSrc_Mode); 592 593 for (int yTile = 0; yTile < (size.height()+15)/16; ++yTile) { 594 for (int xTile = 0; xTile < (size.width()+15)/16; ++xTile) { 595 int saveCount = tileCanvas.save(); 596 SkMatrix mat(tileCanvas.getTotalMatrix()); 597 mat.postTranslate(SkIntToScalar(-xTile*tileSize.width()), 598 SkIntToScalar(-yTile*tileSize.height())); 599 tileCanvas.setMatrix(mat); 600 pict->draw(&tileCanvas); 601 tileCanvas.flush(); 602 tileCanvas.restoreToCount(saveCount); 603 bmpCanvas.drawBitmap(tileBM, 604 SkIntToScalar(xTile * tileSize.width()), 605 SkIntToScalar(yTile * tileSize.height()), 606 &bmpPaint); 607 } 608 } 609 } else { 610 SkCanvas canvas(*bitmap); 611 installFilter(&canvas); 612 canvas.scale(scale, scale); 613 canvas.drawPicture(*pict); 614 complete_bitmap(bitmap); 615 } 616 } 617 618 static void generate_pdf(GM* gm, SkDynamicMemoryWStream& pdf) { 619#ifdef SK_SUPPORT_PDF 620 SkMatrix initialTransform = gm->getInitialTransform(); 621 SkISize pageSize = gm->getISize(); 622 SkPDFDevice* dev = NULL; 623 if (initialTransform.isIdentity()) { 624 dev = new SkPDFDevice(pageSize, pageSize, initialTransform); 625 } else { 626 SkRect content = SkRect::MakeWH(SkIntToScalar(pageSize.width()), 627 SkIntToScalar(pageSize.height())); 628 initialTransform.mapRect(&content); 629 content.intersect(0, 0, SkIntToScalar(pageSize.width()), 630 SkIntToScalar(pageSize.height())); 631 SkISize contentSize = 632 SkISize::Make(SkScalarRoundToInt(content.width()), 633 SkScalarRoundToInt(content.height())); 634 dev = new SkPDFDevice(pageSize, contentSize, initialTransform); 635 } 636 dev->setDCTEncoder(encode_to_dct_stream); 637 SkAutoUnref aur(dev); 638 639 SkCanvas c(dev); 640 invokeGM(gm, &c, true, false); 641 642 SkPDFDocument doc; 643 doc.appendPage(dev); 644 doc.emitPDF(&pdf); 645#endif 646 } 647 648 static void generate_xps(GM* gm, SkDynamicMemoryWStream& xps) { 649#ifdef SK_SUPPORT_XPS 650 SkISize size = gm->getISize(); 651 652 SkSize trimSize = SkSize::Make(SkIntToScalar(size.width()), 653 SkIntToScalar(size.height())); 654 static const SkScalar inchesPerMeter = SkScalarDiv(10000, 254); 655 static const SkScalar upm = 72 * inchesPerMeter; 656 SkVector unitsPerMeter = SkPoint::Make(upm, upm); 657 static const SkScalar ppm = 200 * inchesPerMeter; 658 SkVector pixelsPerMeter = SkPoint::Make(ppm, ppm); 659 660 SkXPSDevice* dev = new SkXPSDevice(); 661 SkAutoUnref aur(dev); 662 663 SkCanvas c(dev); 664 dev->beginPortfolio(&xps); 665 dev->beginSheet(unitsPerMeter, pixelsPerMeter, trimSize); 666 invokeGM(gm, &c, false, false); 667 dev->endSheet(); 668 dev->endPortfolio(); 669 670#endif 671 } 672 673 /** 674 * Log more detail about the mistmatch between expectedBitmap and 675 * actualBitmap. 676 */ 677 void report_bitmap_diffs(const SkBitmap& expectedBitmap, const SkBitmap& actualBitmap, 678 const char *testName) { 679 const int expectedWidth = expectedBitmap.width(); 680 const int expectedHeight = expectedBitmap.height(); 681 const int width = actualBitmap.width(); 682 const int height = actualBitmap.height(); 683 if ((expectedWidth != width) || (expectedHeight != height)) { 684 gm_fprintf(stderr, "---- %s: dimension mismatch --" 685 " expected [%d %d], actual [%d %d]\n", 686 testName, expectedWidth, expectedHeight, width, height); 687 return; 688 } 689 690 if ((SkBitmap::kARGB_8888_Config != expectedBitmap.config()) || 691 (SkBitmap::kARGB_8888_Config != actualBitmap.config())) { 692 gm_fprintf(stderr, "---- %s: not computing max per-channel" 693 " pixel mismatch because non-8888\n", testName); 694 return; 695 } 696 697 SkAutoLockPixels alp0(expectedBitmap); 698 SkAutoLockPixels alp1(actualBitmap); 699 int errR = 0; 700 int errG = 0; 701 int errB = 0; 702 int errA = 0; 703 int differingPixels = 0; 704 705 for (int y = 0; y < height; ++y) { 706 const SkPMColor* expectedPixelPtr = expectedBitmap.getAddr32(0, y); 707 const SkPMColor* actualPixelPtr = actualBitmap.getAddr32(0, y); 708 for (int x = 0; x < width; ++x) { 709 SkPMColor expectedPixel = *expectedPixelPtr++; 710 SkPMColor actualPixel = *actualPixelPtr++; 711 if (expectedPixel != actualPixel) { 712 differingPixels++; 713 errR = SkMax32(errR, SkAbs32((int)SkGetPackedR32(expectedPixel) - 714 (int)SkGetPackedR32(actualPixel))); 715 errG = SkMax32(errG, SkAbs32((int)SkGetPackedG32(expectedPixel) - 716 (int)SkGetPackedG32(actualPixel))); 717 errB = SkMax32(errB, SkAbs32((int)SkGetPackedB32(expectedPixel) - 718 (int)SkGetPackedB32(actualPixel))); 719 errA = SkMax32(errA, SkAbs32((int)SkGetPackedA32(expectedPixel) - 720 (int)SkGetPackedA32(actualPixel))); 721 } 722 } 723 } 724 gm_fprintf(stderr, "---- %s: %d (of %d) differing pixels," 725 " max per-channel mismatch R=%d G=%d B=%d A=%d\n", 726 testName, differingPixels, width*height, errR, errG, errB, errA); 727 } 728 729 /** 730 * Compares actual hash digest to expectations, returning the set of errors 731 * (if any) that we saw along the way. 732 * 733 * If fMismatchPath has been set, and there are pixel diffs, then the 734 * actual bitmap will be written out to a file within fMismatchPath. 735 * And similarly for fMissingExpectationsPath... 736 * 737 * @param expectations what expectations to compare actualBitmap against 738 * @param actualBitmapAndDigest the SkBitmap we actually generated, and its GmResultDigest 739 * @param shortName name of test, e.g. "selftest1" 740 * @param configName name of config, e.g. "8888" 741 * @param renderModeDescriptor e.g., "-rtree", "-deferred" 742 * @param addToJsonSummary whether to add these results (both actual and 743 * expected) to the JSON summary. Regardless of this setting, if 744 * we find an image mismatch in this test, we will write these 745 * results to the JSON summary. (This is so that we will always 746 * report errors across rendering modes, such as pipe vs tiled. 747 * See https://codereview.chromium.org/13650002/ ) 748 */ 749 ErrorCombination compare_to_expectations(Expectations expectations, 750 const BitmapAndDigest& actualBitmapAndDigest, 751 const char *shortName, const char *configName, 752 const char *renderModeDescriptor, 753 bool addToJsonSummary) { 754 ErrorCombination errors; 755 SkString shortNamePlusConfig = make_shortname_plus_config(shortName, configName); 756 SkString completeNameString(shortNamePlusConfig); 757 completeNameString.append(renderModeDescriptor); 758 completeNameString.append("."); 759 completeNameString.append(kPNG_FileExtension); 760 const char* completeName = completeNameString.c_str(); 761 762 if (expectations.empty()) { 763 errors.add(kMissingExpectations_ErrorType); 764 765 // Write out the "actuals" for any tests without expectations, if we have 766 // been directed to do so. 767 if (fMissingExpectationsPath) { 768 SkString path = make_bitmap_filename(fMissingExpectationsPath, shortName, 769 configName, renderModeDescriptor, 770 actualBitmapAndDigest.fDigest); 771 write_bitmap(path, actualBitmapAndDigest.fBitmap); 772 } 773 774 } else if (!expectations.match(actualBitmapAndDigest.fDigest)) { 775 addToJsonSummary = true; 776 // The error mode we record depends on whether this was running 777 // in a non-standard renderMode. 778 if ('\0' == *renderModeDescriptor) { 779 errors.add(kExpectationsMismatch_ErrorType); 780 } else { 781 errors.add(kRenderModeMismatch_ErrorType); 782 } 783 784 // Write out the "actuals" for any mismatches, if we have 785 // been directed to do so. 786 if (fMismatchPath) { 787 SkString path = make_bitmap_filename(fMismatchPath, shortName, configName, 788 renderModeDescriptor, 789 actualBitmapAndDigest.fDigest); 790 write_bitmap(path, actualBitmapAndDigest.fBitmap); 791 } 792 793 // If we have access to a single expected bitmap, log more 794 // detail about the mismatch. 795 const SkBitmap *expectedBitmapPtr = expectations.asBitmap(); 796 if (NULL != expectedBitmapPtr) { 797 report_bitmap_diffs(*expectedBitmapPtr, actualBitmapAndDigest.fBitmap, 798 completeName); 799 } 800 } 801 802 if (addToJsonSummary) { 803 add_actual_results_to_json_summary(completeName, actualBitmapAndDigest.fDigest, errors, 804 expectations.ignoreFailure()); 805 add_expected_results_to_json_summary(completeName, expectations); 806 } 807 808 return errors; 809 } 810 811 /** 812 * Add this result to the appropriate JSON collection of actual results, 813 * depending on errors encountered. 814 */ 815 void add_actual_results_to_json_summary(const char testName[], 816 const GmResultDigest &actualResultDigest, 817 ErrorCombination errors, 818 bool ignoreFailure) { 819 Json::Value jsonActualResults = actualResultDigest.asJsonTypeValuePair(); 820 if (errors.isEmpty()) { 821 this->fJsonActualResults_Succeeded[testName] = jsonActualResults; 822 } else { 823 if (ignoreFailure) { 824 this->fJsonActualResults_FailureIgnored[testName] = 825 jsonActualResults; 826 } else { 827 if (errors.includes(kMissingExpectations_ErrorType)) { 828 // TODO: What about the case where there IS an 829 // expected image hash digest, but that gm test 830 // doesn't actually run? For now, those cases 831 // will always be ignored, because gm only looks 832 // at expectations that correspond to gm tests 833 // that were actually run. 834 // 835 // Once we have the ability to express 836 // expectations as a JSON file, we should fix this 837 // (and add a test case for which an expectation 838 // is given but the test is never run). 839 this->fJsonActualResults_NoComparison[testName] = 840 jsonActualResults; 841 } 842 if (errors.includes(kExpectationsMismatch_ErrorType) || 843 errors.includes(kRenderModeMismatch_ErrorType)) { 844 this->fJsonActualResults_Failed[testName] = jsonActualResults; 845 } 846 } 847 } 848 } 849 850 /** 851 * Add this test to the JSON collection of expected results. 852 */ 853 void add_expected_results_to_json_summary(const char testName[], 854 Expectations expectations) { 855 this->fJsonExpectedResults[testName] = expectations.asJsonValue(); 856 } 857 858 /** 859 * Compare actualBitmap to expectations stored in this->fExpectationsSource. 860 * 861 * @param gm which test generated the actualBitmap 862 * @param gRec 863 * @param configName The config name to look for in the expectation file. 864 * @param actualBitmapAndDigest ptr to bitmap generated by this run, or NULL 865 * if we don't have a usable bitmap representation 866 */ 867 ErrorCombination compare_test_results_to_stored_expectations( 868 GM* gm, const ConfigData& gRec, const char* configName, 869 const BitmapAndDigest* actualBitmapAndDigest) { 870 871 SkString shortNamePlusConfig = make_shortname_plus_config(gm->shortName(), configName); 872 873 ErrorCombination errors; 874 875 if (NULL == actualBitmapAndDigest) { 876 // Note that we intentionally skipped validating the results for 877 // this test, because we don't know how to generate an SkBitmap 878 // version of the output. 879 errors.add(ErrorCombination(kIntentionallySkipped_ErrorType)); 880 } else if (!(gRec.fFlags & kWrite_ConfigFlag)) { 881 // We don't record the results for this test or compare them 882 // against any expectations, because the output image isn't 883 // meaningful. 884 // See https://code.google.com/p/skia/issues/detail?id=1410 ('some 885 // GM result images not available for download from Google Storage') 886 errors.add(ErrorCombination(kIntentionallySkipped_ErrorType)); 887 } else { 888 ExpectationsSource *expectationsSource = this->fExpectationsSource.get(); 889 SkString nameWithExtension(shortNamePlusConfig); 890 nameWithExtension.append("."); 891 nameWithExtension.append(kPNG_FileExtension); 892 893 if (expectationsSource && (gRec.fFlags & kRead_ConfigFlag)) { 894 /* 895 * Get the expected results for this test, as one or more allowed 896 * hash digests. The current implementation of expectationsSource 897 * get this by computing the hash digest of a single PNG file on disk. 898 * 899 * TODO(epoger): This relies on the fact that 900 * force_all_opaque() was called on the bitmap before it 901 * was written to disk as a PNG in the first place. If 902 * not, the hash digest returned here may not match the 903 * hash digest of actualBitmap, which *has* been run through 904 * force_all_opaque(). 905 * See comments above complete_bitmap() for more detail. 906 */ 907 Expectations expectations = expectationsSource->get(nameWithExtension.c_str()); 908 if (gm->isIgnoringFailures()) { 909 expectations.setIgnoreFailure(true); 910 } 911 errors.add(compare_to_expectations(expectations, *actualBitmapAndDigest, 912 gm->shortName(), configName, "", true)); 913 } else { 914 // If we are running without expectations, we still want to 915 // record the actual results. 916 add_actual_results_to_json_summary(nameWithExtension.c_str(), 917 actualBitmapAndDigest->fDigest, 918 ErrorCombination(kMissingExpectations_ErrorType), 919 false); 920 errors.add(ErrorCombination(kMissingExpectations_ErrorType)); 921 } 922 } 923 return errors; 924 } 925 926 /** 927 * Compare actualBitmap to referenceBitmap. 928 * 929 * @param shortName test name, e.g. "selftest1" 930 * @param configName configuration name, e.g. "8888" 931 * @param renderModeDescriptor 932 * @param actualBitmap actual bitmap generated by this run 933 * @param referenceBitmap bitmap we expected to be generated 934 */ 935 ErrorCombination compare_test_results_to_reference_bitmap( 936 const char *shortName, const char *configName, const char *renderModeDescriptor, 937 SkBitmap& actualBitmap, const SkBitmap* referenceBitmap) { 938 939 SkASSERT(referenceBitmap); 940 Expectations expectations(*referenceBitmap); 941 BitmapAndDigest actualBitmapAndDigest(actualBitmap); 942 943 // TODO: Eliminate RecordTestResults from here. 944 // Results recording code for the test_drawing path has been refactored so that 945 // RecordTestResults is only called once, at the topmost level. However, the 946 // other paths have not yet been refactored, and RecordTestResults has been added 947 // here to maintain proper behavior for calls not coming from the test_drawing path. 948 ErrorCombination errors; 949 errors.add(compare_to_expectations(expectations, actualBitmapAndDigest, shortName, 950 configName, renderModeDescriptor, false)); 951 SkString shortNamePlusConfig = make_shortname_plus_config(shortName, configName); 952 RecordTestResults(errors, shortNamePlusConfig, renderModeDescriptor); 953 954 return errors; 955 } 956 957 static SkPicture* generate_new_picture(GM* gm, BbhType bbhType, uint32_t recordFlags, 958 SkScalar scale = SK_Scalar1) { 959 // Pictures are refcounted so must be on heap 960 SkPicture* pict; 961 int width = SkScalarCeilToInt(SkScalarMul(SkIntToScalar(gm->getISize().width()), scale)); 962 int height = SkScalarCeilToInt(SkScalarMul(SkIntToScalar(gm->getISize().height()), scale)); 963 964 if (kTileGrid_BbhType == bbhType) { 965 SkTileGridPicture::TileGridInfo info; 966 info.fMargin.setEmpty(); 967 info.fOffset.setZero(); 968 info.fTileInterval.set(16, 16); 969 pict = new SkTileGridPicture(width, height, info); 970 } else { 971 pict = new SkPicture; 972 } 973 if (kNone_BbhType != bbhType) { 974 recordFlags |= SkPicture::kOptimizeForClippedPlayback_RecordingFlag; 975 } 976 SkCanvas* cv = pict->beginRecording(width, height, recordFlags); 977 cv->scale(scale, scale); 978 invokeGM(gm, cv, false, false); 979 pict->endRecording(); 980 981 return pict; 982 } 983 984 static SkData* bitmap_encoder(size_t* pixelRefOffset, const SkBitmap& bm) { 985 SkPixelRef* pr = bm.pixelRef(); 986 if (pr != NULL) { 987 SkData* data = pr->refEncodedData(); 988 if (data != NULL) { 989 *pixelRefOffset = bm.pixelRefOffset(); 990 return data; 991 } 992 } 993 return NULL; 994 } 995 996 static SkPicture* stream_to_new_picture(const SkPicture& src) { 997 SkDynamicMemoryWStream storage; 998 src.serialize(&storage, &bitmap_encoder); 999 SkAutoTUnref<SkStreamAsset> pictReadback(storage.detachAsStream()); 1000 SkPicture* retval = SkPicture::CreateFromStream(pictReadback, 1001 &SkImageDecoder::DecodeMemory); 1002 return retval; 1003 } 1004 1005 // Test: draw into a bitmap or pdf. 1006 // Depending on flags, possibly compare to an expected image. 1007 // If writePath is not NULL, also write images (or documents) to the specified path. 1008 ErrorCombination test_drawing(GM* gm, const ConfigData& gRec, 1009 const SkTDArray<const PDFRasterizerData*> &pdfRasterizers, 1010 const char writePath [], 1011 GrSurface* gpuTarget, 1012 SkBitmap* bitmap) { 1013 ErrorCombination errors; 1014 SkDynamicMemoryWStream document; 1015 SkString path; 1016 1017 if (gRec.fBackend == kRaster_Backend || 1018 gRec.fBackend == kGPU_Backend) { 1019 // Early exit if we can't generate the image. 1020 errors.add(generate_image(gm, gRec, gpuTarget, bitmap, false)); 1021 if (!errors.isEmpty()) { 1022 // TODO: Add a test to exercise what the stdout and 1023 // JSON look like if we get an "early error" while 1024 // trying to generate the image. 1025 return errors; 1026 } 1027 BitmapAndDigest bitmapAndDigest(*bitmap); 1028 errors.add(compare_test_results_to_stored_expectations( 1029 gm, gRec, gRec.fName, &bitmapAndDigest)); 1030 1031 if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) { 1032 path = make_bitmap_filename(writePath, gm->shortName(), gRec.fName, 1033 "", bitmapAndDigest.fDigest); 1034 errors.add(write_bitmap(path, bitmapAndDigest.fBitmap)); 1035 } 1036 } else if (gRec.fBackend == kPDF_Backend) { 1037 generate_pdf(gm, document); 1038 1039 SkAutoTUnref<SkStreamAsset> documentStream(document.detachAsStream()); 1040 if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) { 1041 path = make_filename(writePath, gm->shortName(), gRec.fName, "", "pdf"); 1042 errors.add(write_document(path, documentStream)); 1043 } 1044 1045 if (!(gm->getFlags() & GM::kSkipPDFRasterization_Flag)) { 1046 for (int i = 0; i < pdfRasterizers.count(); i++) { 1047 SkBitmap pdfBitmap; 1048 SkASSERT(documentStream->rewind()); 1049 bool success = (*pdfRasterizers[i]->fRasterizerFunction)( 1050 documentStream.get(), &pdfBitmap); 1051 if (!success) { 1052 gm_fprintf(stderr, "FAILED to render PDF for %s using renderer %s\n", 1053 gm->shortName(), 1054 pdfRasterizers[i]->fName); 1055 continue; 1056 } 1057 1058 SkString configName(gRec.fName); 1059 configName.append("-"); 1060 configName.append(pdfRasterizers[i]->fName); 1061 1062 BitmapAndDigest bitmapAndDigest(pdfBitmap); 1063 errors.add(compare_test_results_to_stored_expectations( 1064 gm, gRec, configName.c_str(), &bitmapAndDigest)); 1065 1066 if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) { 1067 path = make_bitmap_filename(writePath, gm->shortName(), configName.c_str(), 1068 "", bitmapAndDigest.fDigest); 1069 errors.add(write_bitmap(path, bitmapAndDigest.fBitmap)); 1070 } 1071 } 1072 } else { 1073 errors.add(kIntentionallySkipped_ErrorType); 1074 } 1075 } else if (gRec.fBackend == kXPS_Backend) { 1076 generate_xps(gm, document); 1077 SkAutoTUnref<SkStreamAsset> documentStream(document.detachAsStream()); 1078 1079 errors.add(compare_test_results_to_stored_expectations( 1080 gm, gRec, gRec.fName, NULL)); 1081 1082 if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) { 1083 path = make_filename(writePath, gm->shortName(), gRec.fName, "", "xps"); 1084 errors.add(write_document(path, documentStream)); 1085 } 1086 } else { 1087 SkASSERT(false); 1088 } 1089 return errors; 1090 } 1091 1092 ErrorCombination test_deferred_drawing(GM* gm, 1093 const ConfigData& gRec, 1094 const SkBitmap& referenceBitmap, 1095 GrSurface* gpuTarget) { 1096 if (gRec.fBackend == kRaster_Backend || 1097 gRec.fBackend == kGPU_Backend) { 1098 const char renderModeDescriptor[] = "-deferred"; 1099 SkBitmap bitmap; 1100 // Early exit if we can't generate the image, but this is 1101 // expected in some cases, so don't report a test failure. 1102 ErrorCombination errors = generate_image(gm, gRec, gpuTarget, &bitmap, true); 1103 // TODO(epoger): This logic is the opposite of what is 1104 // described above... if we succeeded in generating the 1105 // -deferred image, we exit early! We should fix this 1106 // ASAP, because it is hiding -deferred errors... but for 1107 // now, I'm leaving the logic as it is so that the 1108 // refactoring change 1109 // https://codereview.chromium.org/12992003/ is unblocked. 1110 // 1111 // Filed as https://code.google.com/p/skia/issues/detail?id=1180 1112 // ('image-surface gm test is failing in "deferred" mode, 1113 // and gm is not reporting the failure') 1114 if (errors.isEmpty()) { 1115 // TODO(epoger): Report this as a new ErrorType, 1116 // something like kImageGeneration_ErrorType? 1117 return kEmpty_ErrorCombination; 1118 } 1119 return compare_test_results_to_reference_bitmap( 1120 gm->shortName(), gRec.fName, renderModeDescriptor, bitmap, &referenceBitmap); 1121 } 1122 return kEmpty_ErrorCombination; 1123 } 1124 1125 ErrorCombination test_pipe_playback(GM* gm, const ConfigData& gRec, 1126 const SkBitmap& referenceBitmap, bool simulateFailure) { 1127 const SkString shortNamePlusConfig = make_shortname_plus_config(gm->shortName(), 1128 gRec.fName); 1129 ErrorCombination errors; 1130 for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) { 1131 SkString renderModeDescriptor("-pipe"); 1132 renderModeDescriptor.append(gPipeWritingFlagCombos[i].name); 1133 1134 if (gm->getFlags() & GM::kSkipPipe_Flag 1135 || (gPipeWritingFlagCombos[i].flags == SkGPipeWriter::kCrossProcess_Flag 1136 && gm->getFlags() & GM::kSkipPipeCrossProcess_Flag)) { 1137 RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig, 1138 renderModeDescriptor.c_str()); 1139 errors.add(kIntentionallySkipped_ErrorType); 1140 } else { 1141 SkBitmap bitmap; 1142 SkISize size = gm->getISize(); 1143 setup_bitmap(gRec, size, &bitmap); 1144 SkCanvas canvas(bitmap); 1145 installFilter(&canvas); 1146 // Pass a decoding function so the factory GM (which has an SkBitmap 1147 // with encoded data) will not fail playback. 1148 PipeController pipeController(&canvas, &SkImageDecoder::DecodeMemory); 1149 SkGPipeWriter writer; 1150 SkCanvas* pipeCanvas = writer.startRecording(&pipeController, 1151 gPipeWritingFlagCombos[i].flags, 1152 size.width(), size.height()); 1153 if (!simulateFailure) { 1154 invokeGM(gm, pipeCanvas, false, false); 1155 } 1156 complete_bitmap(&bitmap); 1157 writer.endRecording(); 1158 errors.add(compare_test_results_to_reference_bitmap( 1159 gm->shortName(), gRec.fName, renderModeDescriptor.c_str(), bitmap, 1160 &referenceBitmap)); 1161 if (!errors.isEmpty()) { 1162 break; 1163 } 1164 } 1165 } 1166 return errors; 1167 } 1168 1169 ErrorCombination test_tiled_pipe_playback(GM* gm, const ConfigData& gRec, 1170 const SkBitmap& referenceBitmap) { 1171 const SkString shortNamePlusConfig = make_shortname_plus_config(gm->shortName(), 1172 gRec.fName); 1173 ErrorCombination errors; 1174 for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) { 1175 SkString renderModeDescriptor("-tiled pipe"); 1176 renderModeDescriptor.append(gPipeWritingFlagCombos[i].name); 1177 1178 if ((gm->getFlags() & GM::kSkipPipe_Flag) || 1179 (gm->getFlags() & GM::kSkipTiled_Flag)) { 1180 RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig, 1181 renderModeDescriptor.c_str()); 1182 errors.add(kIntentionallySkipped_ErrorType); 1183 } else { 1184 SkBitmap bitmap; 1185 SkISize size = gm->getISize(); 1186 setup_bitmap(gRec, size, &bitmap); 1187 SkCanvas canvas(bitmap); 1188 installFilter(&canvas); 1189 TiledPipeController pipeController(bitmap, &SkImageDecoder::DecodeMemory); 1190 SkGPipeWriter writer; 1191 SkCanvas* pipeCanvas = writer.startRecording(&pipeController, 1192 gPipeWritingFlagCombos[i].flags, 1193 size.width(), size.height()); 1194 invokeGM(gm, pipeCanvas, false, false); 1195 complete_bitmap(&bitmap); 1196 writer.endRecording(); 1197 errors.add(compare_test_results_to_reference_bitmap(gm->shortName(), gRec.fName, 1198 renderModeDescriptor.c_str(), 1199 bitmap, &referenceBitmap)); 1200 if (!errors.isEmpty()) { 1201 break; 1202 } 1203 } 1204 } 1205 return errors; 1206 } 1207 1208 // 1209 // member variables. 1210 // They are public for now, to allow easier setting by tool_main(). 1211 // 1212 1213 bool fUseFileHierarchy, fWriteChecksumBasedFilenames; 1214 ErrorCombination fIgnorableErrorTypes; 1215 1216 const char* fMismatchPath; 1217 const char* fMissingExpectationsPath; 1218 1219 // collection of tests that have failed with each ErrorType 1220 SkTArray<SkString> fFailedTests[kLast_ErrorType+1]; 1221 int fTestsRun; 1222 SkTDict<int> fRenderModesEncountered; 1223 1224 // Where to read expectations (expected image hash digests, etc.) from. 1225 // If unset, we don't do comparisons. 1226 SkAutoTUnref<ExpectationsSource> fExpectationsSource; 1227 1228 // JSON summaries that we generate as we go (just for output). 1229 Json::Value fJsonExpectedResults; 1230 Json::Value fJsonActualResults_Failed; 1231 Json::Value fJsonActualResults_FailureIgnored; 1232 Json::Value fJsonActualResults_NoComparison; 1233 Json::Value fJsonActualResults_Succeeded; 1234 1235}; // end of GMMain class definition 1236 1237#if SK_SUPPORT_GPU 1238static const GLContextType kDontCare_GLContextType = GrContextFactory::kNative_GLContextType; 1239#else 1240static const GLContextType kDontCare_GLContextType = 0; 1241#endif 1242 1243static const ConfigData gRec[] = { 1244 { SkBitmap::kARGB_8888_Config, kRaster_Backend, kDontCare_GLContextType, 0, kRW_ConfigFlag, "8888", true }, 1245#if 0 // stop testing this (for now at least) since we want to remove support for it (soon please!!!) 1246 { SkBitmap::kARGB_4444_Config, kRaster_Backend, kDontCare_GLContextType, 0, kRW_ConfigFlag, "4444", true }, 1247#endif 1248 { SkBitmap::kRGB_565_Config, kRaster_Backend, kDontCare_GLContextType, 0, kRW_ConfigFlag, "565", true }, 1249#if SK_SUPPORT_GPU 1250 { SkBitmap::kARGB_8888_Config, kGPU_Backend, GrContextFactory::kNative_GLContextType, 0, kRW_ConfigFlag, "gpu", true }, 1251 { SkBitmap::kARGB_8888_Config, kGPU_Backend, GrContextFactory::kNative_GLContextType, 16, kRW_ConfigFlag, "msaa16", false}, 1252 { SkBitmap::kARGB_8888_Config, kGPU_Backend, GrContextFactory::kNative_GLContextType, 4, kRW_ConfigFlag, "msaa4", false}, 1253 /* The gpudebug context does not generate meaningful images, so don't record 1254 * the images it generates! We only run it to look for asserts. */ 1255 { SkBitmap::kARGB_8888_Config, kGPU_Backend, GrContextFactory::kDebug_GLContextType, 0, kNone_ConfigFlag, "gpudebug", kDebugOnly}, 1256 /* The gpunull context does the least amount of work possible and doesn't 1257 generate meaninful images, so don't record them!. It can be run to 1258 isolate the CPU-side processing expense from the GPU-side. 1259 */ 1260 { SkBitmap::kARGB_8888_Config, kGPU_Backend, GrContextFactory::kNull_GLContextType, 0, kNone_ConfigFlag, "gpunull", kDebugOnly}, 1261#if SK_ANGLE 1262 { SkBitmap::kARGB_8888_Config, kGPU_Backend, GrContextFactory::kANGLE_GLContextType, 0, kRW_ConfigFlag, "angle", true }, 1263 { SkBitmap::kARGB_8888_Config, kGPU_Backend, GrContextFactory::kANGLE_GLContextType, 16, kRW_ConfigFlag, "anglemsaa16", true }, 1264#endif // SK_ANGLE 1265#ifdef SK_MESA 1266 { SkBitmap::kARGB_8888_Config, kGPU_Backend, GrContextFactory::kMESA_GLContextType, 0, kRW_ConfigFlag, "mesa", true }, 1267#endif // SK_MESA 1268#endif // SK_SUPPORT_GPU 1269#ifdef SK_SUPPORT_XPS 1270 /* At present we have no way of comparing XPS files (either natively or by converting to PNG). */ 1271 { SkBitmap::kARGB_8888_Config, kXPS_Backend, kDontCare_GLContextType, 0, kWrite_ConfigFlag, "xps", true }, 1272#endif // SK_SUPPORT_XPS 1273#ifdef SK_SUPPORT_PDF 1274 { SkBitmap::kARGB_8888_Config, kPDF_Backend, kDontCare_GLContextType, 0, kRW_ConfigFlag, "pdf", true }, 1275#endif // SK_SUPPORT_PDF 1276}; 1277 1278static const PDFRasterizerData kPDFRasterizers[] = { 1279#ifdef SK_BUILD_FOR_MAC 1280 { &SkPDFDocumentToBitmap, "mac", true }, 1281#endif 1282#ifdef SK_BUILD_POPPLER 1283 { &SkPopplerRasterizePDF, "poppler", true }, 1284#endif 1285}; 1286 1287static const char kDefaultsConfigStr[] = "defaults"; 1288static const char kExcludeConfigChar = '~'; 1289 1290static SkString configUsage() { 1291 SkString result; 1292 result.appendf("Space delimited list of which configs to run. Possible options: ["); 1293 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { 1294 SkASSERT(gRec[i].fName != kDefaultsConfigStr); 1295 if (i > 0) { 1296 result.append("|"); 1297 } 1298 result.appendf("%s", gRec[i].fName); 1299 } 1300 result.append("]\n"); 1301 result.appendf("The default value is: \""); 1302 SkString firstDefault; 1303 SkString allButFirstDefaults; 1304 SkString nonDefault; 1305 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { 1306 if (gRec[i].fRunByDefault) { 1307 if (i > 0) { 1308 result.append(" "); 1309 } 1310 result.append(gRec[i].fName); 1311 if (firstDefault.isEmpty()) { 1312 firstDefault = gRec[i].fName; 1313 } else { 1314 if (!allButFirstDefaults.isEmpty()) { 1315 allButFirstDefaults.append(" "); 1316 } 1317 allButFirstDefaults.append(gRec[i].fName); 1318 } 1319 } else { 1320 nonDefault = gRec[i].fName; 1321 } 1322 } 1323 result.append("\"\n"); 1324 result.appendf("\"%s\" evaluates to the default set of configs.\n", kDefaultsConfigStr); 1325 result.appendf("Prepending \"%c\" on a config name excludes it from the set of configs to run.\n" 1326 "Exclusions always override inclusions regardless of order.\n", 1327 kExcludeConfigChar); 1328 result.appendf("E.g. \"--config %s %c%s %s\" will run these configs:\n\t%s %s", 1329 kDefaultsConfigStr, 1330 kExcludeConfigChar, 1331 firstDefault.c_str(), 1332 nonDefault.c_str(), 1333 allButFirstDefaults.c_str(), 1334 nonDefault.c_str()); 1335 return result; 1336} 1337 1338static SkString pdfRasterizerUsage() { 1339 SkString result; 1340 result.appendf("Space delimited list of which PDF rasterizers to run. Possible options: ["); 1341 // For this (and further) loops through kPDFRasterizers, there is a typecast to int to avoid 1342 // the compiler giving an "comparison of unsigned expression < 0 is always false" warning 1343 // and turning it into a build-breaking error. 1344 for (int i = 0; i < (int)SK_ARRAY_COUNT(kPDFRasterizers); ++i) { 1345 if (i > 0) { 1346 result.append(" "); 1347 } 1348 result.append(kPDFRasterizers[i].fName); 1349 } 1350 result.append("]\n"); 1351 result.append("The default value is: \""); 1352 for (int i = 0; i < (int)SK_ARRAY_COUNT(kPDFRasterizers); ++i) { 1353 if (kPDFRasterizers[i].fRunByDefault) { 1354 if (i > 0) { 1355 result.append(" "); 1356 } 1357 result.append(kPDFRasterizers[i].fName); 1358 } 1359 } 1360 result.append("\""); 1361 return result; 1362} 1363 1364// Macro magic to convert a numeric preprocessor token into a string. 1365// Adapted from http://stackoverflow.com/questions/240353/convert-a-preprocessor-token-to-a-string 1366// This should probably be moved into one of our common headers... 1367#define TOSTRING_INTERNAL(x) #x 1368#define TOSTRING(x) TOSTRING_INTERNAL(x) 1369 1370// Alphabetized ignoring "no" prefix ("readPath", "noreplay", "resourcePath"). 1371DEFINE_string(config, "", configUsage().c_str()); 1372DEFINE_string(pdfRasterizers, "", pdfRasterizerUsage().c_str()); 1373DEFINE_bool(deferred, false, "Exercise the deferred rendering test pass."); 1374DEFINE_string(excludeConfig, "", "Space delimited list of configs to skip."); 1375DEFINE_bool(forceBWtext, false, "Disable text anti-aliasing."); 1376#if SK_SUPPORT_GPU 1377DEFINE_string(gpuCacheSize, "", "<bytes> <count>: Limit the gpu cache to byte size or " 1378 "object count. " TOSTRING(DEFAULT_CACHE_VALUE) " for either value means " 1379 "use the default. 0 for either disables the cache."); 1380#endif 1381DEFINE_bool(hierarchy, false, "Whether to use multilevel directory structure " 1382 "when reading/writing files."); 1383DEFINE_string(ignoreErrorTypes, kDefaultIgnorableErrorTypes.asString(" ").c_str(), 1384 "Space-separated list of ErrorTypes that should be ignored. If any *other* error " 1385 "types are encountered, the tool will exit with a nonzero return value."); 1386DEFINE_string(ignoreTests, "", "Space delimited list of tests for which we should ignore " 1387 "failures."); 1388DEFINE_string(match, "", "[~][^]substring[$] [...] of test name to run.\n" 1389 "Multiple matches may be separated by spaces.\n" 1390 "~ causes a matching test to always be skipped\n" 1391 "^ requires the start of the test to match\n" 1392 "$ requires the end of the test to match\n" 1393 "^ and $ requires an exact match\n" 1394 "If a test does not match any list entry,\n" 1395 "it is skipped unless some list entry starts with ~"); 1396DEFINE_string(missingExpectationsPath, "", "Write images for tests without expectations " 1397 "into this directory."); 1398DEFINE_string(mismatchPath, "", "Write images for tests that failed due to " 1399 "pixel mismatches into this directory."); 1400DEFINE_string(modulo, "", "[--modulo <remainder> <divisor>]: only run tests for which " 1401 "testIndex %% divisor == remainder."); 1402DEFINE_bool(pdf, true, "Exercise the pdf rendering test pass."); 1403DEFINE_bool(pipe, false, "Exercise the SkGPipe replay test pass."); 1404DEFINE_string2(readPath, r, "", "Read reference images from this dir, and report " 1405 "any differences between those and the newly generated ones."); 1406DEFINE_bool(replay, false, "Exercise the SkPicture replay test pass."); 1407DEFINE_string2(resourcePath, i, "", "Directory that stores image resources."); 1408DEFINE_bool(rtree, false, "Exercise the R-Tree variant of SkPicture test pass."); 1409DEFINE_bool(serialize, false, "Exercise the SkPicture serialization & deserialization test pass."); 1410DEFINE_bool(simulatePipePlaybackFailure, false, "Simulate a rendering failure in pipe mode only."); 1411DEFINE_bool(tiledPipe, false, "Exercise tiled SkGPipe replay."); 1412DEFINE_bool(tileGrid, false, "Exercise the tile grid variant of SkPicture."); 1413DEFINE_string(tileGridReplayScales, "", "Space separated list of floating-point scale " 1414 "factors to be used for tileGrid playback testing. Default value: 1.0"); 1415DEFINE_bool2(verbose, v, false, "Give more detail (e.g. list all GMs run, more info about " 1416 "each test)."); 1417DEFINE_bool(writeChecksumBasedFilenames, false, "When writing out actual images, use checksum-" 1418 "based filenames, as rebaseline.py will use when downloading them from Google Storage"); 1419DEFINE_string(writeJsonSummaryPath, "", "Write a JSON-formatted result summary to this file."); 1420DEFINE_string2(writePath, w, "", "Write rendered images into this directory."); 1421DEFINE_string2(writePicturePath, p, "", "Write .skp files into this directory."); 1422DEFINE_int32(pdfJpegQuality, -1, "Encodes images in JPEG at quality level N, " 1423 "which can be in range 0-100). N = -1 will disable JPEG compression. " 1424 "Default is N = 100, maximum quality."); 1425 1426// TODO(edisonn): pass a matrix instead of forcePerspectiveMatrix 1427// Either the 9 numbers defining the matrix 1428// or probably more readable would be to replace it with a set of a few predicates 1429// Like --prerotate 100 200 10 --posttranslate 10, 10 1430// Probably define spacial names like centerx, centery, top, bottom, left, right 1431// then we can write something reabable like --rotate centerx centery 90 1432DEFINE_bool(forcePerspectiveMatrix, false, "Force a perspective matrix."); 1433 1434static bool encode_to_dct_stream(SkWStream* stream, const SkBitmap& bitmap, const SkIRect& rect) { 1435 // Filter output of warnings that JPEG is not available for the image. 1436 if (bitmap.width() >= 65500 || bitmap.height() >= 65500) return false; 1437 if (FLAGS_pdfJpegQuality == -1) return false; 1438 1439 SkIRect bitmapBounds; 1440 SkBitmap subset; 1441 const SkBitmap* bitmapToUse = &bitmap; 1442 bitmap.getBounds(&bitmapBounds); 1443 if (rect != bitmapBounds) { 1444 SkAssertResult(bitmap.extractSubset(&subset, rect)); 1445 bitmapToUse = ⊂ 1446 } 1447 1448#if defined(SK_BUILD_FOR_MAC) 1449 // Workaround bug #1043 where bitmaps with referenced pixels cause 1450 // CGImageDestinationFinalize to crash 1451 SkBitmap copy; 1452 bitmapToUse->deepCopyTo(©, bitmapToUse->config()); 1453 bitmapToUse = © 1454#endif 1455 1456 return SkImageEncoder::EncodeStream(stream, 1457 *bitmapToUse, 1458 SkImageEncoder::kJPEG_Type, 1459 FLAGS_pdfJpegQuality); 1460} 1461 1462static int findConfig(const char config[]) { 1463 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) { 1464 if (!strcmp(config, gRec[i].fName)) { 1465 return (int) i; 1466 } 1467 } 1468 return -1; 1469} 1470 1471static const PDFRasterizerData* findPDFRasterizer(const char rasterizer[]) { 1472 for (int i = 0; i < (int)SK_ARRAY_COUNT(kPDFRasterizers); i++) { 1473 if (!strcmp(rasterizer, kPDFRasterizers[i].fName)) { 1474 return &kPDFRasterizers[i]; 1475 } 1476 } 1477 return NULL; 1478} 1479 1480template <typename T> void appendUnique(SkTDArray<T>* array, const T& value) { 1481 int index = array->find(value); 1482 if (index < 0) { 1483 *array->append() = value; 1484 } 1485} 1486 1487/** 1488 * Run this test in a number of different configs (8888, 565, PDF, 1489 * etc.), confirming that the resulting bitmaps match expectations 1490 * (which may be different for each config). 1491 * 1492 * Returns all errors encountered while doing so. 1493 */ 1494ErrorCombination run_multiple_configs(GMMain &gmmain, GM *gm, 1495 const SkTDArray<size_t> &configs, 1496 const SkTDArray<const PDFRasterizerData*> &pdfRasterizers, 1497 GrContextFactory *grFactory); 1498ErrorCombination run_multiple_configs(GMMain &gmmain, GM *gm, 1499 const SkTDArray<size_t> &configs, 1500 const SkTDArray<const PDFRasterizerData*> &pdfRasterizers, 1501 GrContextFactory *grFactory) { 1502 const char renderModeDescriptor[] = ""; 1503 ErrorCombination errorsForAllConfigs; 1504 uint32_t gmFlags = gm->getFlags(); 1505 1506 for (int i = 0; i < configs.count(); i++) { 1507 ConfigData config = gRec[configs[i]]; 1508 const SkString shortNamePlusConfig = gmmain.make_shortname_plus_config(gm->shortName(), 1509 config.fName); 1510 1511 // Skip any tests that we don't even need to try. 1512 // If any of these were skipped on a per-GM basis, record them as 1513 // kIntentionallySkipped. 1514 if (kPDF_Backend == config.fBackend) { 1515 if (!FLAGS_pdf) { 1516 continue; 1517 } 1518 if (gmFlags & GM::kSkipPDF_Flag) { 1519 gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig, 1520 renderModeDescriptor); 1521 errorsForAllConfigs.add(kIntentionallySkipped_ErrorType); 1522 continue; 1523 } 1524 } 1525 if ((gmFlags & GM::kSkip565_Flag) && 1526 (kRaster_Backend == config.fBackend) && 1527 (SkBitmap::kRGB_565_Config == config.fConfig)) { 1528 gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig, 1529 renderModeDescriptor); 1530 errorsForAllConfigs.add(kIntentionallySkipped_ErrorType); 1531 continue; 1532 } 1533 if (((gmFlags & GM::kSkipGPU_Flag) && kGPU_Backend == config.fBackend) || 1534 ((gmFlags & GM::kGPUOnly_Flag) && kGPU_Backend != config.fBackend)) { 1535 gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig, 1536 renderModeDescriptor); 1537 errorsForAllConfigs.add(kIntentionallySkipped_ErrorType); 1538 continue; 1539 } 1540 1541 // Now we know that we want to run this test and record its 1542 // success or failure. 1543 ErrorCombination errorsForThisConfig; 1544 GrSurface* gpuTarget = NULL; 1545#if SK_SUPPORT_GPU 1546 SkAutoTUnref<GrSurface> auGpuTarget; 1547 if ((errorsForThisConfig.isEmpty()) && (kGPU_Backend == config.fBackend)) { 1548 GrContext* gr = grFactory->get(config.fGLContextType); 1549 bool grSuccess = false; 1550 if (gr) { 1551 // create a render target to back the device 1552 GrTextureDesc desc; 1553 desc.fConfig = kSkia8888_GrPixelConfig; 1554 desc.fFlags = kRenderTarget_GrTextureFlagBit; 1555 desc.fWidth = gm->getISize().width(); 1556 desc.fHeight = gm->getISize().height(); 1557 desc.fSampleCnt = config.fSampleCnt; 1558 auGpuTarget.reset(gr->createUncachedTexture(desc, NULL, 0)); 1559 if (NULL != auGpuTarget) { 1560 gpuTarget = auGpuTarget; 1561 grSuccess = true; 1562 // Set the user specified cache limits if non-default. 1563 size_t bytes; 1564 int count; 1565 gr->getTextureCacheLimits(&count, &bytes); 1566 if (DEFAULT_CACHE_VALUE != gGpuCacheSizeBytes) { 1567 bytes = static_cast<size_t>(gGpuCacheSizeBytes); 1568 } 1569 if (DEFAULT_CACHE_VALUE != gGpuCacheSizeCount) { 1570 count = gGpuCacheSizeCount; 1571 } 1572 gr->setTextureCacheLimits(count, bytes); 1573 } 1574 } 1575 if (!grSuccess) { 1576 errorsForThisConfig.add(kNoGpuContext_ErrorType); 1577 } 1578 } 1579#endif 1580 1581 SkBitmap comparisonBitmap; 1582 1583 const char* writePath; 1584 if (FLAGS_writePath.count() == 1) { 1585 writePath = FLAGS_writePath[0]; 1586 } else { 1587 writePath = NULL; 1588 } 1589 1590 if (errorsForThisConfig.isEmpty()) { 1591 errorsForThisConfig.add(gmmain.test_drawing(gm, config, pdfRasterizers, 1592 writePath, gpuTarget, 1593 &comparisonBitmap)); 1594 gmmain.RecordTestResults(errorsForThisConfig, shortNamePlusConfig, ""); 1595 } 1596 1597 if (FLAGS_deferred && errorsForThisConfig.isEmpty() && 1598 (kGPU_Backend == config.fBackend || kRaster_Backend == config.fBackend)) { 1599 errorsForThisConfig.add(gmmain.test_deferred_drawing(gm, config, comparisonBitmap, 1600 gpuTarget)); 1601 } 1602 1603 errorsForAllConfigs.add(errorsForThisConfig); 1604 } 1605 return errorsForAllConfigs; 1606} 1607 1608/** 1609 * Run this test in a number of different drawing modes (pipe, 1610 * deferred, tiled, etc.), confirming that the resulting bitmaps all 1611 * *exactly* match comparisonBitmap. 1612 * 1613 * Returns all errors encountered while doing so. 1614 */ 1615ErrorCombination run_multiple_modes(GMMain &gmmain, GM *gm, const ConfigData &compareConfig, 1616 const SkBitmap &comparisonBitmap, 1617 const SkTDArray<SkScalar> &tileGridReplayScales); 1618ErrorCombination run_multiple_modes(GMMain &gmmain, GM *gm, const ConfigData &compareConfig, 1619 const SkBitmap &comparisonBitmap, 1620 const SkTDArray<SkScalar> &tileGridReplayScales) { 1621 ErrorCombination errorsForAllModes; 1622 uint32_t gmFlags = gm->getFlags(); 1623 const SkString shortNamePlusConfig = gmmain.make_shortname_plus_config(gm->shortName(), 1624 compareConfig.fName); 1625 1626 SkPicture* pict = gmmain.generate_new_picture(gm, kNone_BbhType, 0); 1627 SkAutoUnref aur(pict); 1628 if (FLAGS_replay) { 1629 const char renderModeDescriptor[] = "-replay"; 1630 if (gmFlags & GM::kSkipPicture_Flag) { 1631 gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig, 1632 renderModeDescriptor); 1633 errorsForAllModes.add(kIntentionallySkipped_ErrorType); 1634 } else { 1635 SkBitmap bitmap; 1636 gmmain.generate_image_from_picture(gm, compareConfig, pict, &bitmap); 1637 errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitmap( 1638 gm->shortName(), compareConfig.fName, renderModeDescriptor, bitmap, 1639 &comparisonBitmap)); 1640 } 1641 } 1642 1643 if (FLAGS_serialize) { 1644 const char renderModeDescriptor[] = "-serialize"; 1645 if (gmFlags & GM::kSkipPicture_Flag) { 1646 gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig, 1647 renderModeDescriptor); 1648 errorsForAllModes.add(kIntentionallySkipped_ErrorType); 1649 } else { 1650 SkPicture* repict = gmmain.stream_to_new_picture(*pict); 1651 SkAutoUnref aurr(repict); 1652 SkBitmap bitmap; 1653 gmmain.generate_image_from_picture(gm, compareConfig, repict, &bitmap); 1654 errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitmap( 1655 gm->shortName(), compareConfig.fName, renderModeDescriptor, bitmap, 1656 &comparisonBitmap)); 1657 } 1658 } 1659 1660 if ((1 == FLAGS_writePicturePath.count()) && 1661 !(gmFlags & GM::kSkipPicture_Flag)) { 1662 const char* pictureSuffix = "skp"; 1663 // TODO(epoger): Make sure this still works even though the 1664 // filename now contains the config name (it used to contain 1665 // just the shortName). I think this is actually an 1666 // *improvement*, because now runs with different configs will 1667 // write out their SkPictures to separate files rather than 1668 // overwriting each other. But we should make sure it doesn't 1669 // break anybody. 1670 SkString path = gmmain.make_filename(FLAGS_writePicturePath[0], gm->shortName(), 1671 compareConfig.fName, "", pictureSuffix); 1672 SkFILEWStream stream(path.c_str()); 1673 pict->serialize(&stream); 1674 } 1675 1676 if (FLAGS_rtree) { 1677 const char renderModeDescriptor[] = "-rtree"; 1678 if (gmFlags & GM::kSkipPicture_Flag) { 1679 gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig, 1680 renderModeDescriptor); 1681 errorsForAllModes.add(kIntentionallySkipped_ErrorType); 1682 } else { 1683 SkPicture* pict = gmmain.generate_new_picture( 1684 gm, kRTree_BbhType, SkPicture::kUsePathBoundsForClip_RecordingFlag); 1685 SkAutoUnref aur(pict); 1686 SkBitmap bitmap; 1687 gmmain.generate_image_from_picture(gm, compareConfig, pict, &bitmap); 1688 errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitmap( 1689 gm->shortName(), compareConfig.fName, renderModeDescriptor, bitmap, 1690 &comparisonBitmap)); 1691 } 1692 } 1693 1694 if (FLAGS_tileGrid) { 1695 for(int scaleIndex = 0; scaleIndex < tileGridReplayScales.count(); ++scaleIndex) { 1696 SkScalar replayScale = tileGridReplayScales[scaleIndex]; 1697 SkString renderModeDescriptor("-tilegrid"); 1698 if (SK_Scalar1 != replayScale) { 1699 renderModeDescriptor += "-scale-"; 1700 renderModeDescriptor.appendScalar(replayScale); 1701 } 1702 1703 if ((gmFlags & GM::kSkipPicture_Flag) || 1704 ((gmFlags & GM::kSkipScaledReplay_Flag) && replayScale != 1)) { 1705 gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig, 1706 renderModeDescriptor.c_str()); 1707 errorsForAllModes.add(kIntentionallySkipped_ErrorType); 1708 } else { 1709 // We record with the reciprocal scale to obtain a replay 1710 // result that can be validated against comparisonBitmap. 1711 SkScalar recordScale = SkScalarInvert(replayScale); 1712 SkPicture* pict = gmmain.generate_new_picture( 1713 gm, kTileGrid_BbhType, SkPicture::kUsePathBoundsForClip_RecordingFlag, 1714 recordScale); 1715 SkAutoUnref aur(pict); 1716 SkBitmap bitmap; 1717 // We cannot yet pass 'true' to generate_image_from_picture to 1718 // perform actual tiled rendering (see Issue 1198 - 1719 // https://code.google.com/p/skia/issues/detail?id=1198) 1720 gmmain.generate_image_from_picture(gm, compareConfig, pict, &bitmap, 1721 replayScale /*, true */); 1722 errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitmap( 1723 gm->shortName(), compareConfig.fName, renderModeDescriptor.c_str(), bitmap, 1724 &comparisonBitmap)); 1725 } 1726 } 1727 } 1728 1729 // run the pipe centric GM steps 1730 if (FLAGS_pipe) { 1731 errorsForAllModes.add(gmmain.test_pipe_playback(gm, compareConfig, comparisonBitmap, 1732 FLAGS_simulatePipePlaybackFailure)); 1733 if (FLAGS_tiledPipe) { 1734 errorsForAllModes.add(gmmain.test_tiled_pipe_playback(gm, compareConfig, 1735 comparisonBitmap)); 1736 } 1737 } 1738 return errorsForAllModes; 1739} 1740 1741/** 1742 * Return a list of all entries in an array of strings as a single string 1743 * of this form: 1744 * "item1", "item2", "item3" 1745 */ 1746SkString list_all(const SkTArray<SkString> &stringArray); 1747SkString list_all(const SkTArray<SkString> &stringArray) { 1748 SkString total; 1749 for (int i = 0; i < stringArray.count(); i++) { 1750 if (i > 0) { 1751 total.append(", "); 1752 } 1753 total.append("\""); 1754 total.append(stringArray[i]); 1755 total.append("\""); 1756 } 1757 return total; 1758} 1759 1760/** 1761 * Return a list of configuration names, as a single string of this form: 1762 * "item1", "item2", "item3" 1763 * 1764 * @param configs configurations, as a list of indices into gRec 1765 */ 1766SkString list_all_config_names(const SkTDArray<size_t> &configs); 1767SkString list_all_config_names(const SkTDArray<size_t> &configs) { 1768 SkString total; 1769 for (int i = 0; i < configs.count(); i++) { 1770 if (i > 0) { 1771 total.append(", "); 1772 } 1773 total.append("\""); 1774 total.append(gRec[configs[i]].fName); 1775 total.append("\""); 1776 } 1777 return total; 1778} 1779 1780static bool prepare_subdirectories(const char *root, bool useFileHierarchy, 1781 const SkTDArray<size_t> &configs, 1782 const SkTDArray<const PDFRasterizerData*>& pdfRasterizers) { 1783 if (!sk_mkdir(root)) { 1784 return false; 1785 } 1786 if (useFileHierarchy) { 1787 for (int i = 0; i < configs.count(); i++) { 1788 ConfigData config = gRec[configs[i]]; 1789 SkString subdir; 1790 subdir.appendf("%s%c%s", root, SkPATH_SEPARATOR, config.fName); 1791 if (!sk_mkdir(subdir.c_str())) { 1792 return false; 1793 } 1794 1795 if (config.fBackend == kPDF_Backend) { 1796 for (int j = 0; j < pdfRasterizers.count(); j++) { 1797 SkString pdfSubdir = subdir; 1798 pdfSubdir.appendf("-%s", pdfRasterizers[j]->fName); 1799 if (!sk_mkdir(pdfSubdir.c_str())) { 1800 return false; 1801 } 1802 } 1803 } 1804 } 1805 } 1806 return true; 1807} 1808 1809static bool parse_flags_configs(SkTDArray<size_t>* outConfigs, 1810 GrContextFactory* grFactory) { 1811 SkTDArray<size_t> excludeConfigs; 1812 1813 for (int i = 0; i < FLAGS_config.count(); i++) { 1814 const char* config = FLAGS_config[i]; 1815 bool exclude = false; 1816 if (*config == kExcludeConfigChar) { 1817 exclude = true; 1818 config += 1; 1819 } 1820 int index = findConfig(config); 1821 if (index >= 0) { 1822 if (exclude) { 1823 *excludeConfigs.append() = index; 1824 } else { 1825 appendUnique<size_t>(outConfigs, index); 1826 } 1827 } else if (0 == strcmp(kDefaultsConfigStr, config)) { 1828 if (exclude) { 1829 gm_fprintf(stderr, "%c%s is not allowed.\n", 1830 kExcludeConfigChar, kDefaultsConfigStr); 1831 return false; 1832 } 1833 for (size_t c = 0; c < SK_ARRAY_COUNT(gRec); ++c) { 1834 if (gRec[c].fRunByDefault) { 1835 appendUnique<size_t>(outConfigs, c); 1836 } 1837 } 1838 } else { 1839 gm_fprintf(stderr, "unrecognized config %s\n", config); 1840 return false; 1841 } 1842 } 1843 1844 for (int i = 0; i < FLAGS_excludeConfig.count(); i++) { 1845 int index = findConfig(FLAGS_excludeConfig[i]); 1846 if (index >= 0) { 1847 *excludeConfigs.append() = index; 1848 } else { 1849 gm_fprintf(stderr, "unrecognized excludeConfig %s\n", FLAGS_excludeConfig[i]); 1850 return false; 1851 } 1852 } 1853 1854 if (outConfigs->count() == 0) { 1855 // if no config is specified by user, add the defaults 1856 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { 1857 if (gRec[i].fRunByDefault) { 1858 *outConfigs->append() = i; 1859 } 1860 } 1861 } 1862 // now remove any explicitly excluded configs 1863 for (int i = 0; i < excludeConfigs.count(); ++i) { 1864 int index = outConfigs->find(excludeConfigs[i]); 1865 if (index >= 0) { 1866 outConfigs->remove(index); 1867 // now assert that there was only one copy in configs[] 1868 SkASSERT(outConfigs->find(excludeConfigs[i]) < 0); 1869 } 1870 } 1871 1872#if SK_SUPPORT_GPU 1873 SkASSERT(grFactory != NULL); 1874 for (int i = 0; i < outConfigs->count(); ++i) { 1875 size_t index = (*outConfigs)[i]; 1876 if (kGPU_Backend == gRec[index].fBackend) { 1877 GrContext* ctx = grFactory->get(gRec[index].fGLContextType); 1878 if (NULL == ctx) { 1879 gm_fprintf(stderr, "GrContext could not be created for config %s." 1880 " Config will be skipped.\n", gRec[index].fName); 1881 outConfigs->remove(i); 1882 --i; 1883 continue; 1884 } 1885 if (gRec[index].fSampleCnt > ctx->getMaxSampleCount()) { 1886 gm_fprintf(stderr, "Sample count (%d) of config %s is not supported." 1887 " Config will be skipped.\n", 1888 gRec[index].fSampleCnt, gRec[index].fName); 1889 outConfigs->remove(i); 1890 --i; 1891 } 1892 } 1893 } 1894#endif 1895 1896 if (outConfigs->isEmpty()) { 1897 gm_fprintf(stderr, "No configs to run."); 1898 return false; 1899 } 1900 1901 // now show the user the set of configs that will be run. 1902 SkString configStr("These configs will be run:"); 1903 // show the user the config that will run. 1904 for (int i = 0; i < outConfigs->count(); ++i) { 1905 configStr.appendf(" %s", gRec[(*outConfigs)[i]].fName); 1906 } 1907 gm_fprintf(stdout, "%s\n", configStr.c_str()); 1908 1909 return true; 1910} 1911 1912static bool parse_flags_pdf_rasterizers(const SkTDArray<size_t>& configs, 1913 SkTDArray<const PDFRasterizerData*>* outRasterizers) { 1914 // No need to run this check (and display the PDF rasterizers message) 1915 // if no PDF backends are in the configs. 1916 bool configHasPDF = false; 1917 for (int i = 0; i < configs.count(); i++) { 1918 if (gRec[configs[i]].fBackend == kPDF_Backend) { 1919 configHasPDF = true; 1920 break; 1921 } 1922 } 1923 if (!configHasPDF) { 1924 return true; 1925 } 1926 1927 for (int i = 0; i < FLAGS_pdfRasterizers.count(); i++) { 1928 const char* rasterizer = FLAGS_pdfRasterizers[i]; 1929 const PDFRasterizerData* rasterizerPtr = findPDFRasterizer(rasterizer); 1930 1931 if (rasterizerPtr == NULL) { 1932 gm_fprintf(stderr, "unrecognized rasterizer %s\n", rasterizer); 1933 return false; 1934 } 1935 appendUnique<const PDFRasterizerData*>(outRasterizers, 1936 rasterizerPtr); 1937 } 1938 1939 if (outRasterizers->count() == 0) { 1940 // if no config is specified by user, add the defaults 1941 for (int i = 0; i < (int)SK_ARRAY_COUNT(kPDFRasterizers); ++i) { 1942 if (kPDFRasterizers[i].fRunByDefault) { 1943 *outRasterizers->append() = &kPDFRasterizers[i]; 1944 } 1945 } 1946 } 1947 1948 // now show the user the set of configs that will be run. 1949 SkString configStr("These PDF rasterizers will be run:"); 1950 // show the user the config that will run. 1951 for (int i = 0; i < outRasterizers->count(); ++i) { 1952 configStr.appendf(" %s", (*outRasterizers)[i]->fName); 1953 } 1954 gm_fprintf(stdout, "%s\n", configStr.c_str()); 1955 1956 return true; 1957} 1958 1959static bool parse_flags_ignore_error_types(ErrorCombination* outErrorTypes) { 1960 if (FLAGS_ignoreErrorTypes.count() > 0) { 1961 *outErrorTypes = ErrorCombination(); 1962 for (int i = 0; i < FLAGS_ignoreErrorTypes.count(); i++) { 1963 ErrorType type; 1964 const char *name = FLAGS_ignoreErrorTypes[i]; 1965 if (!getErrorTypeByName(name, &type)) { 1966 gm_fprintf(stderr, "cannot find ErrorType with name '%s'\n", name); 1967 return false; 1968 } else { 1969 outErrorTypes->add(type); 1970 } 1971 } 1972 } 1973 return true; 1974} 1975 1976static bool parse_flags_modulo(int* moduloRemainder, int* moduloDivisor) { 1977 if (FLAGS_modulo.count() == 2) { 1978 *moduloRemainder = atoi(FLAGS_modulo[0]); 1979 *moduloDivisor = atoi(FLAGS_modulo[1]); 1980 if (*moduloRemainder < 0 || *moduloDivisor <= 0 || 1981 *moduloRemainder >= *moduloDivisor) { 1982 gm_fprintf(stderr, "invalid modulo values."); 1983 return false; 1984 } 1985 } 1986 return true; 1987} 1988 1989#if SK_SUPPORT_GPU 1990static bool parse_flags_gpu_cache(int* sizeBytes, int* sizeCount) { 1991 if (FLAGS_gpuCacheSize.count() > 0) { 1992 if (FLAGS_gpuCacheSize.count() != 2) { 1993 gm_fprintf(stderr, "--gpuCacheSize requires two arguments\n"); 1994 return false; 1995 } 1996 *sizeBytes = atoi(FLAGS_gpuCacheSize[0]); 1997 *sizeCount = atoi(FLAGS_gpuCacheSize[1]); 1998 } else { 1999 *sizeBytes = DEFAULT_CACHE_VALUE; 2000 *sizeCount = DEFAULT_CACHE_VALUE; 2001 } 2002 return true; 2003} 2004#endif 2005 2006static bool parse_flags_tile_grid_replay_scales(SkTDArray<SkScalar>* outScales) { 2007 *outScales->append() = SK_Scalar1; // By default only test at scale 1.0 2008 if (FLAGS_tileGridReplayScales.count() > 0) { 2009 outScales->reset(); 2010 for (int i = 0; i < FLAGS_tileGridReplayScales.count(); i++) { 2011 double val = atof(FLAGS_tileGridReplayScales[i]); 2012 if (0 < val) { 2013 *outScales->append() = SkDoubleToScalar(val); 2014 } 2015 } 2016 if (0 == outScales->count()) { 2017 // Should have at least one scale 2018 gm_fprintf(stderr, "--tileGridReplayScales requires at least one scale.\n"); 2019 return false; 2020 } 2021 } 2022 return true; 2023} 2024 2025static bool parse_flags_gmmain_paths(GMMain* gmmain) { 2026 gmmain->fUseFileHierarchy = FLAGS_hierarchy; 2027 gmmain->fWriteChecksumBasedFilenames = FLAGS_writeChecksumBasedFilenames; 2028 2029 if (FLAGS_mismatchPath.count() == 1) { 2030 gmmain->fMismatchPath = FLAGS_mismatchPath[0]; 2031 } 2032 2033 if (FLAGS_missingExpectationsPath.count() == 1) { 2034 gmmain->fMissingExpectationsPath = FLAGS_missingExpectationsPath[0]; 2035 } 2036 2037 if (FLAGS_readPath.count() == 1) { 2038 const char* readPath = FLAGS_readPath[0]; 2039 if (!sk_exists(readPath)) { 2040 gm_fprintf(stderr, "readPath %s does not exist!\n", readPath); 2041 return false; 2042 } 2043 if (sk_isdir(readPath)) { 2044 if (FLAGS_verbose) { 2045 gm_fprintf(stdout, "reading from %s\n", readPath); 2046 } 2047 gmmain->fExpectationsSource.reset(SkNEW_ARGS( 2048 IndividualImageExpectationsSource, (readPath))); 2049 } else { 2050 if (FLAGS_verbose) { 2051 gm_fprintf(stdout, "reading expectations from JSON summary file %s\n", readPath); 2052 } 2053 gmmain->fExpectationsSource.reset(SkNEW_ARGS(JsonExpectationsSource, (readPath))); 2054 } 2055 } 2056 return true; 2057} 2058 2059static bool parse_flags_resource_path() { 2060 if (FLAGS_resourcePath.count() == 1) { 2061 GM::SetResourcePath(FLAGS_resourcePath[0]); 2062 } 2063 return true; 2064} 2065 2066static bool parse_flags_jpeg_quality() { 2067 if (FLAGS_pdfJpegQuality < -1 || FLAGS_pdfJpegQuality > 100) { 2068 gm_fprintf(stderr, "%s\n", "pdfJpegQuality must be in [-1 .. 100] range."); 2069 return false; 2070 } 2071 return true; 2072} 2073 2074int tool_main(int argc, char** argv); 2075int tool_main(int argc, char** argv) { 2076 2077#if SK_ENABLE_INST_COUNT 2078 gPrintInstCount = true; 2079#endif 2080 2081 SkGraphics::Init(); 2082 // we don't need to see this during a run 2083 gSkSuppressFontCachePurgeSpew = true; 2084 2085 setSystemPreferences(); 2086 GMMain gmmain; 2087 2088 SkString usage; 2089 usage.printf("Run the golden master tests.\n"); 2090 SkCommandLineFlags::SetUsage(usage.c_str()); 2091 SkCommandLineFlags::Parse(argc, argv); 2092 2093 SkTDArray<size_t> configs; 2094 2095 int moduloRemainder = -1; 2096 int moduloDivisor = -1; 2097 SkTDArray<const PDFRasterizerData*> pdfRasterizers; 2098 SkTDArray<SkScalar> tileGridReplayScales; 2099#if SK_SUPPORT_GPU 2100 GrContextFactory* grFactory = new GrContextFactory; 2101#else 2102 GrContextFactory* grFactory = NULL; 2103#endif 2104 2105 if (!parse_flags_modulo(&moduloRemainder, &moduloDivisor) || 2106 !parse_flags_ignore_error_types(&gmmain.fIgnorableErrorTypes) || 2107#if SK_SUPPORT_GPU 2108 !parse_flags_gpu_cache(&gGpuCacheSizeBytes, &gGpuCacheSizeCount) || 2109#endif 2110 !parse_flags_tile_grid_replay_scales(&tileGridReplayScales) || 2111 !parse_flags_resource_path() || 2112 !parse_flags_jpeg_quality() || 2113 !parse_flags_configs(&configs, grFactory) || 2114 !parse_flags_pdf_rasterizers(configs, &pdfRasterizers) || 2115 !parse_flags_gmmain_paths(&gmmain)) { 2116 return -1; 2117 } 2118 2119 if (FLAGS_verbose) { 2120 if (FLAGS_writePath.count() == 1) { 2121 gm_fprintf(stdout, "writing to %s\n", FLAGS_writePath[0]); 2122 } 2123 if (NULL != gmmain.fMismatchPath) { 2124 gm_fprintf(stdout, "writing mismatches to %s\n", gmmain.fMismatchPath); 2125 } 2126 if (NULL != gmmain.fMissingExpectationsPath) { 2127 gm_fprintf(stdout, "writing images without expectations to %s\n", 2128 gmmain.fMissingExpectationsPath); 2129 } 2130 if (FLAGS_writePicturePath.count() == 1) { 2131 gm_fprintf(stdout, "writing pictures to %s\n", FLAGS_writePicturePath[0]); 2132 } 2133 if (FLAGS_resourcePath.count() == 1) { 2134 gm_fprintf(stdout, "reading resources from %s\n", FLAGS_resourcePath[0]); 2135 } 2136 } 2137 2138 int gmsRun = 0; 2139 int gmIndex = -1; 2140 SkString moduloStr; 2141 2142 // If we will be writing out files, prepare subdirectories. 2143 if (FLAGS_writePath.count() == 1) { 2144 if (!prepare_subdirectories(FLAGS_writePath[0], gmmain.fUseFileHierarchy, 2145 configs, pdfRasterizers)) { 2146 return -1; 2147 } 2148 } 2149 if (NULL != gmmain.fMismatchPath) { 2150 if (!prepare_subdirectories(gmmain.fMismatchPath, gmmain.fUseFileHierarchy, 2151 configs, pdfRasterizers)) { 2152 return -1; 2153 } 2154 } 2155 if (NULL != gmmain.fMissingExpectationsPath) { 2156 if (!prepare_subdirectories(gmmain.fMissingExpectationsPath, gmmain.fUseFileHierarchy, 2157 configs, pdfRasterizers)) { 2158 return -1; 2159 } 2160 } 2161 2162 Iter iter; 2163 GM* gm; 2164 while ((gm = iter.next()) != NULL) { 2165 if (FLAGS_forcePerspectiveMatrix) { 2166 SkMatrix perspective; 2167 perspective.setIdentity(); 2168 perspective.setPerspY(SkScalarDiv(SK_Scalar1, SkIntToScalar(1000))); 2169 perspective.setSkewX(SkScalarDiv(SkIntToScalar(8), 2170 SkIntToScalar(25))); 2171 2172 gm->setStarterMatrix(perspective); 2173 } 2174 SkAutoTDelete<GM> adgm(gm); 2175 ++gmIndex; 2176 if (moduloRemainder >= 0) { 2177 if ((gmIndex % moduloDivisor) != moduloRemainder) { 2178 continue; 2179 } 2180 moduloStr.printf("[%d.%d] ", gmIndex, moduloDivisor); 2181 } 2182 2183 const char* shortName = gm->shortName(); 2184 2185 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, shortName)) { 2186 continue; 2187 } 2188 if (FLAGS_ignoreTests.contains(shortName)) { 2189 gm->setIgnoreFailures(true); 2190 } 2191 2192 gmsRun++; 2193 SkISize size = gm->getISize(); 2194 gm_fprintf(stdout, "%sdrawing... %s [%d %d]\n", moduloStr.c_str(), shortName, 2195 size.width(), size.height()); 2196 2197 run_multiple_configs(gmmain, gm, configs, pdfRasterizers, grFactory); 2198 2199 SkBitmap comparisonBitmap; 2200 const ConfigData compareConfig = 2201 { SkBitmap::kARGB_8888_Config, kRaster_Backend, kDontCare_GLContextType, 0, kRW_ConfigFlag, "comparison", false }; 2202 gmmain.generate_image(gm, compareConfig, NULL, &comparisonBitmap, false); 2203 2204 // TODO(epoger): only run this if gmmain.generate_image() succeeded? 2205 // Otherwise, what are we comparing against? 2206 run_multiple_modes(gmmain, gm, compareConfig, comparisonBitmap, tileGridReplayScales); 2207 } 2208 2209 SkTArray<SkString> modes; 2210 gmmain.GetRenderModesEncountered(modes); 2211 bool reportError = false; 2212 if (gmmain.NumSignificantErrors() > 0) { 2213 reportError = true; 2214 } 2215 int expectedNumberOfTests = gmsRun * (configs.count() + modes.count()); 2216 2217 // Output summary to stdout. 2218 if (FLAGS_verbose) { 2219 gm_fprintf(stdout, "Ran %d GMs\n", gmsRun); 2220 gm_fprintf(stdout, "... over %2d configs [%s]\n", configs.count(), 2221 list_all_config_names(configs).c_str()); 2222 gm_fprintf(stdout, "... and %2d modes [%s]\n", modes.count(), list_all(modes).c_str()); 2223 gm_fprintf(stdout, "... so there should be a total of %d tests.\n", expectedNumberOfTests); 2224 } 2225 gmmain.ListErrors(FLAGS_verbose); 2226 2227 // TODO(epoger): Enable this check for Android, too, once we resolve 2228 // https://code.google.com/p/skia/issues/detail?id=1222 2229 // ('GM is unexpectedly skipping tests on Android') 2230#ifndef SK_BUILD_FOR_ANDROID 2231 if (expectedNumberOfTests != gmmain.fTestsRun) { 2232 gm_fprintf(stderr, "expected %d tests, but ran or skipped %d tests\n", 2233 expectedNumberOfTests, gmmain.fTestsRun); 2234 reportError = true; 2235 } 2236#endif 2237 2238 if (FLAGS_writeJsonSummaryPath.count() == 1) { 2239 Json::Value root = CreateJsonTree( 2240 gmmain.fJsonExpectedResults, 2241 gmmain.fJsonActualResults_Failed, gmmain.fJsonActualResults_FailureIgnored, 2242 gmmain.fJsonActualResults_NoComparison, gmmain.fJsonActualResults_Succeeded); 2243 std::string jsonStdString = root.toStyledString(); 2244 SkFILEWStream stream(FLAGS_writeJsonSummaryPath[0]); 2245 stream.write(jsonStdString.c_str(), jsonStdString.length()); 2246 } 2247 2248#if SK_SUPPORT_GPU 2249 2250#if GR_CACHE_STATS 2251 for (int i = 0; i < configs.count(); i++) { 2252 ConfigData config = gRec[configs[i]]; 2253 2254 if (FLAGS_verbose && (kGPU_Backend == config.fBackend)) { 2255 GrContext* gr = grFactory->get(config.fGLContextType); 2256 2257 gm_fprintf(stdout, "config: %s %x\n", config.fName, gr); 2258 gr->printCacheStats(); 2259 } 2260 } 2261#endif 2262 2263 delete grFactory; 2264#endif 2265 SkGraphics::Term(); 2266 2267 return (reportError) ? -1 : 0; 2268} 2269 2270void GMMain::installFilter(SkCanvas* canvas) { 2271 if (FLAGS_forceBWtext) { 2272 canvas->setDrawFilter(SkNEW(BWTextDrawFilter))->unref(); 2273 } 2274} 2275 2276#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL) 2277int main(int argc, char * const argv[]) { 2278 return tool_main(argc, (char**) argv); 2279} 2280#endif 2281