Home | History | Annotate | Download | only in tools
      1 /*
      2  * Copyright 2012 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "LazyDecodeBitmap.h"
      9 #include "CopyTilesRenderer.h"
     10 #include "SkBitmap.h"
     11 #include "SkDevice.h"
     12 #include "SkCommandLineFlags.h"
     13 #include "SkGraphics.h"
     14 #include "SkImageDecoder.h"
     15 #include "SkImageEncoder.h"
     16 #include "SkMath.h"
     17 #include "SkOSFile.h"
     18 #include "SkPicture.h"
     19 #include "SkPictureRecorder.h"
     20 #include "SkStream.h"
     21 #include "SkString.h"
     22 
     23 #include "image_expectations.h"
     24 #include "PictureRenderer.h"
     25 #include "PictureRenderingFlags.h"
     26 #include "picture_utils.h"
     27 
     28 // Flags used by this file, alphabetically:
     29 DEFINE_bool(bench_record, false, "If true, drop into an infinite loop of recording the picture.");
     30 DECLARE_bool(deferImageDecoding);
     31 DEFINE_string(descriptions, "", "one or more key=value pairs to add to the descriptions section "
     32               "of the JSON summary.");
     33 DEFINE_string(imageBaseGSUrl, "", "The Google Storage image base URL the images are stored in.");
     34 DEFINE_int32(maxComponentDiff, 256, "Maximum diff on a component, 0 - 256. Components that differ "
     35              "by more than this amount are considered errors, though all diffs are reported. "
     36              "Requires --validate.");
     37 DEFINE_string(mismatchPath, "", "Write images for tests that failed due to "
     38               "pixel mismatches into this directory.");
     39 #if GR_GPU_STATS
     40 DEFINE_bool(gpuStats, false, "Only meaningful with gpu configurations. "
     41             "Report some GPU call statistics.");
     42 #endif
     43 DEFINE_bool(preprocess, false, "If true, perform device specific preprocessing before rendering.");
     44 DEFINE_string(readJsonSummaryPath, "", "JSON file to read image expectations from.");
     45 DECLARE_string(readPath);
     46 DEFINE_bool(writeChecksumBasedFilenames, false,
     47             "When writing out images, use checksum-based filenames.");
     48 DEFINE_bool(writeEncodedImages, false, "Any time the skp contains an encoded image, write it to a "
     49             "file rather than decoding it. Requires writePath to be set. Skips drawing the full "
     50             "skp to a file. Not compatible with deferImageDecoding.");
     51 DEFINE_string(writeJsonSummaryPath, "", "File to write a JSON summary of image results to.");
     52 DEFINE_string2(writePath, w, "", "Directory to write the rendered images into.");
     53 DEFINE_bool(writeWholeImage, false, "In tile mode, write the entire rendered image to a "
     54             "file, instead of an image for each tile.");
     55 DEFINE_bool(validate, false, "Verify that the rendered image contains the same pixels as "
     56             "the picture rendered in simple mode. When used in conjunction with --bbh, results "
     57             "are validated against the picture rendered in the same mode, but without the bbh.");
     58 
     59 ////////////////////////////////////////////////////////////////////////////////////////////////////
     60 
     61 /**
     62  *  Table for translating from format of data to a suffix.
     63  */
     64 struct Format {
     65     SkImageDecoder::Format  fFormat;
     66     const char*             fSuffix;
     67 };
     68 static const Format gFormats[] = {
     69     { SkImageDecoder::kBMP_Format, ".bmp" },
     70     { SkImageDecoder::kGIF_Format, ".gif" },
     71     { SkImageDecoder::kICO_Format, ".ico" },
     72     { SkImageDecoder::kJPEG_Format, ".jpg" },
     73     { SkImageDecoder::kPNG_Format, ".png" },
     74     { SkImageDecoder::kWBMP_Format, ".wbmp" },
     75     { SkImageDecoder::kWEBP_Format, ".webp" },
     76     { SkImageDecoder::kUnknown_Format, "" },
     77 };
     78 
     79 /**
     80  *  Get an appropriate suffix for an image format.
     81  */
     82 static const char* get_suffix_from_format(SkImageDecoder::Format format) {
     83     for (size_t i = 0; i < SK_ARRAY_COUNT(gFormats); i++) {
     84         if (gFormats[i].fFormat == format) {
     85             return gFormats[i].fSuffix;
     86         }
     87     }
     88     return "";
     89 }
     90 
     91 /**
     92  *  Base name for an image file created from the encoded data in an skp.
     93  */
     94 static SkString gInputFileName;
     95 
     96 /**
     97  *  Number to be appended to the image file name so that it is unique.
     98  */
     99 static uint32_t gImageNo;
    100 
    101 /**
    102  *  Set up the name for writing encoded data to a file.
    103  *  Sets gInputFileName to name, minus any extension ".*"
    104  *  Sets gImageNo to 0, so images from file "X.skp" will
    105  *  look like "X_<gImageNo>.<suffix>", beginning with 0
    106  *  for each new skp.
    107  */
    108 static void reset_image_file_base_name(const SkString& name) {
    109     gImageNo = 0;
    110     // Remove ".skp"
    111     const char* cName = name.c_str();
    112     const char* dot = strrchr(cName, '.');
    113     if (dot != NULL) {
    114         gInputFileName.set(cName, dot - cName);
    115     } else {
    116         gInputFileName.set(name);
    117     }
    118 }
    119 
    120 /**
    121  *  Write the raw encoded bitmap data to a file.
    122  */
    123 static bool write_image_to_file(const void* buffer, size_t size, SkBitmap* bitmap) {
    124     SkASSERT(!FLAGS_writePath.isEmpty());
    125     SkMemoryStream memStream(buffer, size);
    126     SkString outPath;
    127     SkImageDecoder::Format format = SkImageDecoder::GetStreamFormat(&memStream);
    128     SkString name = SkStringPrintf("%s_%d%s", gInputFileName.c_str(), gImageNo++,
    129                                    get_suffix_from_format(format));
    130     SkString dir(FLAGS_writePath[0]);
    131     outPath = SkOSPath::Join(dir.c_str(), name.c_str());
    132     SkFILEWStream fileStream(outPath.c_str());
    133     if (!(fileStream.isValid() && fileStream.write(buffer, size))) {
    134         SkDebugf("Failed to write encoded data to \"%s\"\n", outPath.c_str());
    135     }
    136     // Put in a dummy bitmap.
    137     return SkImageDecoder::DecodeStream(&memStream, bitmap, kUnknown_SkColorType,
    138                                         SkImageDecoder::kDecodeBounds_Mode);
    139 }
    140 
    141 ////////////////////////////////////////////////////////////////////////////////////////////////////
    142 
    143 /**
    144  * Called only by render_picture().
    145  */
    146 static bool render_picture_internal(const SkString& inputPath, const SkString* writePath,
    147                                     const SkString* mismatchPath,
    148                                     sk_tools::PictureRenderer& renderer,
    149                                     SkBitmap** out) {
    150     SkString inputFilename = SkOSPath::Basename(inputPath.c_str());
    151     SkString writePathString;
    152     if (writePath && writePath->size() > 0 && !FLAGS_writeEncodedImages) {
    153         writePathString.set(*writePath);
    154     }
    155     SkString mismatchPathString;
    156     if (mismatchPath && mismatchPath->size() > 0) {
    157         mismatchPathString.set(*mismatchPath);
    158     }
    159 
    160     SkFILEStream inputStream;
    161     inputStream.setPath(inputPath.c_str());
    162     if (!inputStream.isValid()) {
    163         SkDebugf("Could not open file %s\n", inputPath.c_str());
    164         return false;
    165     }
    166 
    167     SkPicture::InstallPixelRefProc proc;
    168     if (FLAGS_deferImageDecoding) {
    169         proc = &sk_tools::LazyDecodeBitmap;
    170     } else if (FLAGS_writeEncodedImages) {
    171         SkASSERT(!FLAGS_writePath.isEmpty());
    172         reset_image_file_base_name(inputFilename);
    173         proc = &write_image_to_file;
    174     } else {
    175         proc = &SkImageDecoder::DecodeMemory;
    176     }
    177 
    178     SkDebugf("deserializing... %s\n", inputPath.c_str());
    179 
    180     SkAutoTUnref<SkPicture> picture(SkPicture::CreateFromStream(&inputStream, proc));
    181 
    182     if (NULL == picture) {
    183         SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str());
    184         return false;
    185     }
    186 
    187     if (FLAGS_preprocess) {
    188         // Because the GPU preprocessing step relies on the in-memory picture
    189         // statistics we need to rerecord the picture here
    190         SkPictureRecorder recorder;
    191         picture->playback(recorder.beginRecording(picture->cullRect().width(),
    192                                                   picture->cullRect().height(),
    193                                                   NULL, 0));
    194         picture.reset(recorder.endRecording());
    195     }
    196 
    197     while (FLAGS_bench_record) {
    198         SkPictureRecorder recorder;
    199         picture->playback(recorder.beginRecording(picture->cullRect().width(),
    200                                                   picture->cullRect().height(),
    201                                                   NULL, 0));
    202         SkAutoTUnref<SkPicture> other(recorder.endRecording());
    203     }
    204 
    205     SkDebugf("drawing... [%f %f %f %f] %s\n",
    206              picture->cullRect().fLeft, picture->cullRect().fTop,
    207              picture->cullRect().fRight, picture->cullRect().fBottom,
    208              inputPath.c_str());
    209 
    210     renderer.init(picture, &writePathString, &mismatchPathString, &inputFilename,
    211                   FLAGS_writeChecksumBasedFilenames);
    212 
    213     if (FLAGS_preprocess) {
    214         if (renderer.getCanvas()) {
    215             renderer.getCanvas()->EXPERIMENTAL_optimize(renderer.getPicture());
    216         }
    217     }
    218 
    219     renderer.setup();
    220     renderer.enableWrites();
    221 
    222     bool success = renderer.render(out);
    223     if (!success) {
    224         SkDebugf("Failed to render %s\n", inputFilename.c_str());
    225     }
    226 
    227     renderer.end();
    228 
    229     return success;
    230 }
    231 
    232 static inline int getByte(uint32_t value, int index) {
    233     SkASSERT(0 <= index && index < 4);
    234     return (value >> (index * 8)) & 0xFF;
    235 }
    236 
    237 static int MaxByteDiff(uint32_t v1, uint32_t v2) {
    238     return SkMax32(SkMax32(abs(getByte(v1, 0) - getByte(v2, 0)), abs(getByte(v1, 1) - getByte(v2, 1))),
    239                    SkMax32(abs(getByte(v1, 2) - getByte(v2, 2)), abs(getByte(v1, 3) - getByte(v2, 3))));
    240 }
    241 
    242 class AutoRestoreBbhType {
    243 public:
    244     AutoRestoreBbhType() {
    245         fRenderer = NULL;
    246         fSavedBbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
    247     }
    248 
    249     void set(sk_tools::PictureRenderer* renderer,
    250              sk_tools::PictureRenderer::BBoxHierarchyType bbhType) {
    251         fRenderer = renderer;
    252         fSavedBbhType = renderer->getBBoxHierarchyType();
    253         renderer->setBBoxHierarchyType(bbhType);
    254     }
    255 
    256     ~AutoRestoreBbhType() {
    257         if (fRenderer) {
    258             fRenderer->setBBoxHierarchyType(fSavedBbhType);
    259         }
    260     }
    261 
    262 private:
    263     sk_tools::PictureRenderer* fRenderer;
    264     sk_tools::PictureRenderer::BBoxHierarchyType fSavedBbhType;
    265 };
    266 
    267 /**
    268  * Render the SKP file(s) within inputPath.
    269  *
    270  * @param inputPath path to an individual SKP file, or a directory of SKP files
    271  * @param writePath if not NULL, write all image(s) generated into this directory
    272  * @param mismatchPath if not NULL, write any image(s) not matching expectations into this directory
    273  * @param renderer PictureRenderer to use to render the SKPs
    274  * @param jsonSummaryPtr if not NULL, add the image(s) generated to this summary
    275  */
    276 static bool render_picture(const SkString& inputPath, const SkString* writePath,
    277                            const SkString* mismatchPath, sk_tools::PictureRenderer& renderer,
    278                            sk_tools::ImageResultsAndExpectations *jsonSummaryPtr) {
    279     int diffs[256] = {0};
    280     SkBitmap* bitmap = NULL;
    281     renderer.setJsonSummaryPtr(jsonSummaryPtr);
    282     bool success = render_picture_internal(inputPath,
    283         FLAGS_writeWholeImage ? NULL : writePath,
    284         FLAGS_writeWholeImage ? NULL : mismatchPath,
    285         renderer,
    286         FLAGS_validate || FLAGS_writeWholeImage ? &bitmap : NULL);
    287 
    288     if (!success || ((FLAGS_validate || FLAGS_writeWholeImage) && bitmap == NULL)) {
    289         SkDebugf("Failed to draw the picture.\n");
    290         SkDELETE(bitmap);
    291         return false;
    292     }
    293 
    294     if (FLAGS_validate) {
    295         SkBitmap* referenceBitmap = NULL;
    296         sk_tools::PictureRenderer* referenceRenderer;
    297         // If the renderer uses a BBoxHierarchy, then the reference renderer
    298         // will be the same renderer, without the bbh.
    299         AutoRestoreBbhType arbbh;
    300         if (sk_tools::PictureRenderer::kNone_BBoxHierarchyType !=
    301             renderer.getBBoxHierarchyType()) {
    302             referenceRenderer = &renderer;
    303             referenceRenderer->ref();  // to match auto unref below
    304             arbbh.set(referenceRenderer, sk_tools::PictureRenderer::kNone_BBoxHierarchyType);
    305         } else {
    306 #if SK_SUPPORT_GPU
    307             referenceRenderer = SkNEW_ARGS(sk_tools::SimplePictureRenderer,
    308                                            (renderer.getGrContextOptions()));
    309 #else
    310             referenceRenderer = SkNEW(sk_tools::SimplePictureRenderer);
    311 #endif
    312         }
    313         SkAutoTUnref<sk_tools::PictureRenderer> aurReferenceRenderer(referenceRenderer);
    314 
    315         success = render_picture_internal(inputPath, NULL, NULL, *referenceRenderer,
    316                                           &referenceBitmap);
    317 
    318         if (!success || NULL == referenceBitmap || NULL == referenceBitmap->getPixels()) {
    319             SkDebugf("Failed to draw the reference picture.\n");
    320             SkDELETE(bitmap);
    321             SkDELETE(referenceBitmap);
    322             return false;
    323         }
    324 
    325         if (success && (bitmap->width() != referenceBitmap->width())) {
    326             SkDebugf("Expected image width: %i, actual image width %i.\n",
    327                      referenceBitmap->width(), bitmap->width());
    328             SkDELETE(bitmap);
    329             SkDELETE(referenceBitmap);
    330             return false;
    331         }
    332         if (success && (bitmap->height() != referenceBitmap->height())) {
    333             SkDebugf("Expected image height: %i, actual image height %i",
    334                      referenceBitmap->height(), bitmap->height());
    335             SkDELETE(bitmap);
    336             SkDELETE(referenceBitmap);
    337             return false;
    338         }
    339 
    340         for (int y = 0; success && y < bitmap->height(); y++) {
    341             for (int x = 0; success && x < bitmap->width(); x++) {
    342                 int diff = MaxByteDiff(*referenceBitmap->getAddr32(x, y),
    343                                        *bitmap->getAddr32(x, y));
    344                 SkASSERT(diff >= 0 && diff <= 255);
    345                 diffs[diff]++;
    346 
    347                 if (diff > FLAGS_maxComponentDiff) {
    348                     SkDebugf("Expected pixel at (%i %i) exceedds maximum "
    349                                  "component diff of %i: 0x%x, actual 0x%x\n",
    350                              x, y, FLAGS_maxComponentDiff,
    351                              *referenceBitmap->getAddr32(x, y),
    352                              *bitmap->getAddr32(x, y));
    353                     SkDELETE(bitmap);
    354                     SkDELETE(referenceBitmap);
    355                     return false;
    356                 }
    357             }
    358         }
    359         SkDELETE(referenceBitmap);
    360 
    361         for (int i = 1; i <= 255; ++i) {
    362             if(diffs[i] > 0) {
    363                 SkDebugf("Number of pixels with max diff of %i is %i\n", i, diffs[i]);
    364             }
    365         }
    366     }
    367 
    368     if (FLAGS_writeWholeImage) {
    369         sk_tools::force_all_opaque(*bitmap);
    370 
    371         SkString inputFilename = SkOSPath::Basename(inputPath.c_str());
    372         SkString outputFilename(inputFilename);
    373         sk_tools::replace_char(&outputFilename, '.', '_');
    374         outputFilename.append(".png");
    375 
    376         if (jsonSummaryPtr) {
    377             sk_tools::ImageDigest imageDigest(*bitmap);
    378             jsonSummaryPtr->add(inputFilename.c_str(), outputFilename.c_str(), imageDigest);
    379             if ((mismatchPath) && !mismatchPath->isEmpty() &&
    380                 !jsonSummaryPtr->getExpectation(inputFilename.c_str()).matches(imageDigest)) {
    381                 success &= sk_tools::write_bitmap_to_disk(*bitmap, *mismatchPath, NULL,
    382                                                           outputFilename);
    383             }
    384         }
    385 
    386         if ((writePath) && !writePath->isEmpty()) {
    387             success &= sk_tools::write_bitmap_to_disk(*bitmap, *writePath, NULL, outputFilename);
    388         }
    389     }
    390     SkDELETE(bitmap);
    391 
    392     return success;
    393 }
    394 
    395 
    396 static int process_input(const char* input, const SkString* writePath,
    397                          const SkString* mismatchPath, sk_tools::PictureRenderer& renderer,
    398                          sk_tools::ImageResultsAndExpectations *jsonSummaryPtr) {
    399     SkOSFile::Iter iter(input, "skp");
    400     SkString inputFilename;
    401     int failures = 0;
    402     SkDebugf("process_input, %s\n", input);
    403     if (iter.next(&inputFilename)) {
    404         do {
    405             SkString inputPath = SkOSPath::Join(input, inputFilename.c_str());
    406             if (!render_picture(inputPath, writePath, mismatchPath, renderer, jsonSummaryPtr)) {
    407                 ++failures;
    408             }
    409         } while(iter.next(&inputFilename));
    410     } else if (SkStrEndsWith(input, ".skp")) {
    411         SkString inputPath(input);
    412         if (!render_picture(inputPath, writePath, mismatchPath, renderer, jsonSummaryPtr)) {
    413             ++failures;
    414         }
    415     } else {
    416         SkString warning;
    417         warning.printf("Warning: skipping %s\n", input);
    418         SkDebugf(warning.c_str());
    419     }
    420     return failures;
    421 }
    422 
    423 int tool_main(int argc, char** argv);
    424 int tool_main(int argc, char** argv) {
    425     SkCommandLineFlags::SetUsage("Render .skp files.");
    426     SkCommandLineFlags::Parse(argc, argv);
    427 
    428     if (FLAGS_readPath.isEmpty()) {
    429         SkDebugf(".skp files or directories are required.\n");
    430         exit(-1);
    431     }
    432 
    433     if (FLAGS_maxComponentDiff < 0 || FLAGS_maxComponentDiff > 256) {
    434         SkDebugf("--maxComponentDiff must be between 0 and 256\n");
    435         exit(-1);
    436     }
    437 
    438     if (FLAGS_maxComponentDiff != 256 && !FLAGS_validate) {
    439         SkDebugf("--maxComponentDiff requires --validate\n");
    440         exit(-1);
    441     }
    442 
    443     if (FLAGS_writeEncodedImages) {
    444         if (FLAGS_writePath.isEmpty()) {
    445             SkDebugf("--writeEncodedImages requires --writePath\n");
    446             exit(-1);
    447         }
    448         if (FLAGS_deferImageDecoding) {
    449             SkDebugf("--writeEncodedImages is not compatible with --deferImageDecoding\n");
    450             exit(-1);
    451         }
    452     }
    453 
    454     SkString errorString;
    455     SkAutoTUnref<sk_tools::PictureRenderer> renderer(parseRenderer(errorString,
    456                                                                    kRender_PictureTool));
    457     if (errorString.size() > 0) {
    458         SkDebugf("%s\n", errorString.c_str());
    459     }
    460 
    461     if (renderer.get() == NULL) {
    462         exit(-1);
    463     }
    464 
    465     SkAutoGraphics ag;
    466 
    467     SkString writePath;
    468     if (FLAGS_writePath.count() == 1) {
    469         writePath.set(FLAGS_writePath[0]);
    470     }
    471     SkString mismatchPath;
    472     if (FLAGS_mismatchPath.count() == 1) {
    473         mismatchPath.set(FLAGS_mismatchPath[0]);
    474     }
    475     sk_tools::ImageResultsAndExpectations jsonSummary;
    476     sk_tools::ImageResultsAndExpectations* jsonSummaryPtr = NULL;
    477     if (FLAGS_writeJsonSummaryPath.count() == 1) {
    478         jsonSummaryPtr = &jsonSummary;
    479         if (FLAGS_readJsonSummaryPath.count() == 1) {
    480             SkASSERT(jsonSummary.readExpectationsFile(FLAGS_readJsonSummaryPath[0]));
    481         }
    482     }
    483 
    484     int failures = 0;
    485     for (int i = 0; i < FLAGS_readPath.count(); i ++) {
    486         failures += process_input(FLAGS_readPath[i], &writePath, &mismatchPath, *renderer.get(),
    487                                   jsonSummaryPtr);
    488     }
    489     if (failures != 0) {
    490         SkDebugf("Failed to render %i pictures.\n", failures);
    491         return 1;
    492     }
    493 #if SK_SUPPORT_GPU
    494 #if GR_CACHE_STATS
    495     if (renderer->isUsingGpuDevice()) {
    496         GrContext* ctx = renderer->getGrContext();
    497         ctx->printCacheStats();
    498 #ifdef SK_DEVELOPER
    499         ctx->dumpFontCache();
    500 #endif
    501     }
    502 #endif
    503 #if GR_GPU_STATS
    504     if (FLAGS_gpuStats && renderer->isUsingGpuDevice()) {
    505         GrContext* ctx = renderer->getGrContext();
    506         SkDebugf("RenderTarget Binds: %d\n", ctx->gpuStats()->renderTargetBinds());
    507         SkDebugf("Shader Compilations: %d\n", ctx->gpuStats()->shaderCompilations());
    508     }
    509 #endif
    510 #endif
    511     if (FLAGS_writeJsonSummaryPath.count() == 1) {
    512         // If there were any descriptions on the command line, insert them now.
    513         for (int i=0; i<FLAGS_descriptions.count(); i++) {
    514             SkTArray<SkString> tokens;
    515             SkStrSplit(FLAGS_descriptions[i], "=", &tokens);
    516             SkASSERT(tokens.count() == 2);
    517             jsonSummary.addDescription(tokens[0].c_str(), tokens[1].c_str());
    518         }
    519         if (FLAGS_imageBaseGSUrl.count() == 1) {
    520           jsonSummary.setImageBaseGSUrl(FLAGS_imageBaseGSUrl[0]);
    521         }
    522         jsonSummary.writeToFile(FLAGS_writeJsonSummaryPath[0]);
    523     }
    524     return 0;
    525 }
    526 
    527 #if !defined SK_BUILD_FOR_IOS
    528 int main(int argc, char * const argv[]) {
    529     return tool_main(argc, (char**) argv);
    530 }
    531 #endif
    532