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(mpd, false, "If true, use MultiPictureDraw for 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     while (FLAGS_bench_record) {
    188         SkPictureRecorder recorder;
    189         picture->playback(recorder.beginRecording(picture->cullRect().width(),
    190                                                   picture->cullRect().height(),
    191                                                   NULL, 0));
    192         SkAutoTUnref<SkPicture> other(recorder.endRecording());
    193     }
    194 
    195     SkDebugf("drawing... [%f %f %f %f] %s\n",
    196              picture->cullRect().fLeft, picture->cullRect().fTop,
    197              picture->cullRect().fRight, picture->cullRect().fBottom,
    198              inputPath.c_str());
    199 
    200     renderer.init(picture, &writePathString, &mismatchPathString, &inputFilename,
    201                   FLAGS_writeChecksumBasedFilenames, FLAGS_mpd);
    202 
    203     renderer.setup();
    204     renderer.enableWrites();
    205 
    206     bool success = renderer.render(out);
    207     if (!success) {
    208         SkDebugf("Failed to render %s\n", inputFilename.c_str());
    209     }
    210 
    211     renderer.end();
    212 
    213     return success;
    214 }
    215 
    216 static inline int getByte(uint32_t value, int index) {
    217     SkASSERT(0 <= index && index < 4);
    218     return (value >> (index * 8)) & 0xFF;
    219 }
    220 
    221 static int MaxByteDiff(uint32_t v1, uint32_t v2) {
    222     return SkMax32(SkMax32(abs(getByte(v1, 0) - getByte(v2, 0)), abs(getByte(v1, 1) - getByte(v2, 1))),
    223                    SkMax32(abs(getByte(v1, 2) - getByte(v2, 2)), abs(getByte(v1, 3) - getByte(v2, 3))));
    224 }
    225 
    226 class AutoRestoreBbhType {
    227 public:
    228     AutoRestoreBbhType() {
    229         fRenderer = NULL;
    230         fSavedBbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
    231     }
    232 
    233     void set(sk_tools::PictureRenderer* renderer,
    234              sk_tools::PictureRenderer::BBoxHierarchyType bbhType) {
    235         fRenderer = renderer;
    236         fSavedBbhType = renderer->getBBoxHierarchyType();
    237         renderer->setBBoxHierarchyType(bbhType);
    238     }
    239 
    240     ~AutoRestoreBbhType() {
    241         if (fRenderer) {
    242             fRenderer->setBBoxHierarchyType(fSavedBbhType);
    243         }
    244     }
    245 
    246 private:
    247     sk_tools::PictureRenderer* fRenderer;
    248     sk_tools::PictureRenderer::BBoxHierarchyType fSavedBbhType;
    249 };
    250 
    251 /**
    252  * Render the SKP file(s) within inputPath.
    253  *
    254  * @param inputPath path to an individual SKP file, or a directory of SKP files
    255  * @param writePath if not NULL, write all image(s) generated into this directory
    256  * @param mismatchPath if not NULL, write any image(s) not matching expectations into this directory
    257  * @param renderer PictureRenderer to use to render the SKPs
    258  * @param jsonSummaryPtr if not NULL, add the image(s) generated to this summary
    259  */
    260 static bool render_picture(const SkString& inputPath, const SkString* writePath,
    261                            const SkString* mismatchPath, sk_tools::PictureRenderer& renderer,
    262                            sk_tools::ImageResultsAndExpectations *jsonSummaryPtr) {
    263     int diffs[256] = {0};
    264     SkBitmap* bitmap = NULL;
    265     renderer.setJsonSummaryPtr(jsonSummaryPtr);
    266     bool success = render_picture_internal(inputPath,
    267         FLAGS_writeWholeImage ? NULL : writePath,
    268         FLAGS_writeWholeImage ? NULL : mismatchPath,
    269         renderer,
    270         FLAGS_validate || FLAGS_writeWholeImage ? &bitmap : NULL);
    271 
    272     if (!success || ((FLAGS_validate || FLAGS_writeWholeImage) && bitmap == NULL)) {
    273         SkDebugf("Failed to draw the picture.\n");
    274         SkDELETE(bitmap);
    275         return false;
    276     }
    277 
    278     if (FLAGS_validate) {
    279         SkBitmap* referenceBitmap = NULL;
    280         sk_tools::PictureRenderer* referenceRenderer;
    281         // If the renderer uses a BBoxHierarchy, then the reference renderer
    282         // will be the same renderer, without the bbh.
    283         AutoRestoreBbhType arbbh;
    284         if (sk_tools::PictureRenderer::kNone_BBoxHierarchyType !=
    285             renderer.getBBoxHierarchyType()) {
    286             referenceRenderer = &renderer;
    287             referenceRenderer->ref();  // to match auto unref below
    288             arbbh.set(referenceRenderer, sk_tools::PictureRenderer::kNone_BBoxHierarchyType);
    289         } else {
    290 #if SK_SUPPORT_GPU
    291             referenceRenderer = SkNEW_ARGS(sk_tools::SimplePictureRenderer,
    292                                            (renderer.getGrContextOptions()));
    293 #else
    294             referenceRenderer = SkNEW(sk_tools::SimplePictureRenderer);
    295 #endif
    296         }
    297         SkAutoTUnref<sk_tools::PictureRenderer> aurReferenceRenderer(referenceRenderer);
    298 
    299         success = render_picture_internal(inputPath, NULL, NULL, *referenceRenderer,
    300                                           &referenceBitmap);
    301 
    302         if (!success || NULL == referenceBitmap || NULL == referenceBitmap->getPixels()) {
    303             SkDebugf("Failed to draw the reference picture.\n");
    304             SkDELETE(bitmap);
    305             SkDELETE(referenceBitmap);
    306             return false;
    307         }
    308 
    309         if (success && (bitmap->width() != referenceBitmap->width())) {
    310             SkDebugf("Expected image width: %i, actual image width %i.\n",
    311                      referenceBitmap->width(), bitmap->width());
    312             SkDELETE(bitmap);
    313             SkDELETE(referenceBitmap);
    314             return false;
    315         }
    316         if (success && (bitmap->height() != referenceBitmap->height())) {
    317             SkDebugf("Expected image height: %i, actual image height %i",
    318                      referenceBitmap->height(), bitmap->height());
    319             SkDELETE(bitmap);
    320             SkDELETE(referenceBitmap);
    321             return false;
    322         }
    323 
    324         for (int y = 0; success && y < bitmap->height(); y++) {
    325             for (int x = 0; success && x < bitmap->width(); x++) {
    326                 int diff = MaxByteDiff(*referenceBitmap->getAddr32(x, y),
    327                                        *bitmap->getAddr32(x, y));
    328                 SkASSERT(diff >= 0 && diff <= 255);
    329                 diffs[diff]++;
    330 
    331                 if (diff > FLAGS_maxComponentDiff) {
    332                     SkDebugf("Expected pixel at (%i %i) exceedds maximum "
    333                                  "component diff of %i: 0x%x, actual 0x%x\n",
    334                              x, y, FLAGS_maxComponentDiff,
    335                              *referenceBitmap->getAddr32(x, y),
    336                              *bitmap->getAddr32(x, y));
    337                     SkDELETE(bitmap);
    338                     SkDELETE(referenceBitmap);
    339                     return false;
    340                 }
    341             }
    342         }
    343         SkDELETE(referenceBitmap);
    344 
    345         for (int i = 1; i <= 255; ++i) {
    346             if(diffs[i] > 0) {
    347                 SkDebugf("Number of pixels with max diff of %i is %i\n", i, diffs[i]);
    348             }
    349         }
    350     }
    351 
    352     if (FLAGS_writeWholeImage) {
    353         sk_tools::force_all_opaque(*bitmap);
    354 
    355         SkString inputFilename = SkOSPath::Basename(inputPath.c_str());
    356         SkString outputFilename(inputFilename);
    357         sk_tools::replace_char(&outputFilename, '.', '_');
    358         outputFilename.append(".png");
    359 
    360         if (jsonSummaryPtr) {
    361             sk_tools::ImageDigest imageDigest(*bitmap);
    362             jsonSummaryPtr->add(inputFilename.c_str(), outputFilename.c_str(), imageDigest);
    363             if ((mismatchPath) && !mismatchPath->isEmpty() &&
    364                 !jsonSummaryPtr->getExpectation(inputFilename.c_str()).matches(imageDigest)) {
    365                 success &= sk_tools::write_bitmap_to_disk(*bitmap, *mismatchPath, NULL,
    366                                                           outputFilename);
    367             }
    368         }
    369 
    370         if ((writePath) && !writePath->isEmpty()) {
    371             success &= sk_tools::write_bitmap_to_disk(*bitmap, *writePath, NULL, outputFilename);
    372         }
    373     }
    374     SkDELETE(bitmap);
    375 
    376     return success;
    377 }
    378 
    379 
    380 static int process_input(const char* input, const SkString* writePath,
    381                          const SkString* mismatchPath, sk_tools::PictureRenderer& renderer,
    382                          sk_tools::ImageResultsAndExpectations *jsonSummaryPtr) {
    383     SkOSFile::Iter iter(input, "skp");
    384     SkString inputFilename;
    385     int failures = 0;
    386     SkDebugf("process_input, %s\n", input);
    387     if (iter.next(&inputFilename)) {
    388         do {
    389             SkString inputPath = SkOSPath::Join(input, inputFilename.c_str());
    390             if (!render_picture(inputPath, writePath, mismatchPath, renderer, jsonSummaryPtr)) {
    391                 ++failures;
    392             }
    393         } while(iter.next(&inputFilename));
    394     } else if (SkStrEndsWith(input, ".skp")) {
    395         SkString inputPath(input);
    396         if (!render_picture(inputPath, writePath, mismatchPath, renderer, jsonSummaryPtr)) {
    397             ++failures;
    398         }
    399     } else {
    400         SkString warning;
    401         warning.printf("Warning: skipping %s\n", input);
    402         SkDebugf("%s", warning.c_str());
    403     }
    404     return failures;
    405 }
    406 
    407 int tool_main(int argc, char** argv);
    408 int tool_main(int argc, char** argv) {
    409     SkCommandLineFlags::SetUsage("Render .skp files.");
    410     SkCommandLineFlags::Parse(argc, argv);
    411 
    412     if (FLAGS_readPath.isEmpty()) {
    413         SkDebugf(".skp files or directories are required.\n");
    414         exit(-1);
    415     }
    416 
    417     if (FLAGS_maxComponentDiff < 0 || FLAGS_maxComponentDiff > 256) {
    418         SkDebugf("--maxComponentDiff must be between 0 and 256\n");
    419         exit(-1);
    420     }
    421 
    422     if (FLAGS_maxComponentDiff != 256 && !FLAGS_validate) {
    423         SkDebugf("--maxComponentDiff requires --validate\n");
    424         exit(-1);
    425     }
    426 
    427     if (FLAGS_writeEncodedImages) {
    428         if (FLAGS_writePath.isEmpty()) {
    429             SkDebugf("--writeEncodedImages requires --writePath\n");
    430             exit(-1);
    431         }
    432         if (FLAGS_deferImageDecoding) {
    433             SkDebugf("--writeEncodedImages is not compatible with --deferImageDecoding\n");
    434             exit(-1);
    435         }
    436     }
    437 
    438     SkString errorString;
    439     SkAutoTUnref<sk_tools::PictureRenderer> renderer(parseRenderer(errorString,
    440                                                                    kRender_PictureTool));
    441     if (errorString.size() > 0) {
    442         SkDebugf("%s\n", errorString.c_str());
    443     }
    444 
    445     if (renderer.get() == NULL) {
    446         exit(-1);
    447     }
    448 
    449     SkAutoGraphics ag;
    450 
    451     SkString writePath;
    452     if (FLAGS_writePath.count() == 1) {
    453         writePath.set(FLAGS_writePath[0]);
    454     }
    455     SkString mismatchPath;
    456     if (FLAGS_mismatchPath.count() == 1) {
    457         mismatchPath.set(FLAGS_mismatchPath[0]);
    458     }
    459     sk_tools::ImageResultsAndExpectations jsonSummary;
    460     sk_tools::ImageResultsAndExpectations* jsonSummaryPtr = NULL;
    461     if (FLAGS_writeJsonSummaryPath.count() == 1) {
    462         jsonSummaryPtr = &jsonSummary;
    463         if (FLAGS_readJsonSummaryPath.count() == 1) {
    464             SkASSERT(jsonSummary.readExpectationsFile(FLAGS_readJsonSummaryPath[0]));
    465         }
    466     }
    467 
    468     int failures = 0;
    469     for (int i = 0; i < FLAGS_readPath.count(); i ++) {
    470         failures += process_input(FLAGS_readPath[i], &writePath, &mismatchPath, *renderer.get(),
    471                                   jsonSummaryPtr);
    472     }
    473     if (failures != 0) {
    474         SkDebugf("Failed to render %i pictures.\n", failures);
    475         return 1;
    476     }
    477 #if GR_CACHE_STATS && SK_SUPPORT_GPU
    478     if (renderer->isUsingGpuDevice()) {
    479         GrContext* ctx = renderer->getGrContext();
    480         ctx->printCacheStats();
    481     }
    482 #endif
    483 
    484 #if GR_GPU_STATS && SK_SUPPORT_GPU
    485     if (FLAGS_gpuStats && renderer->isUsingGpuDevice()) {
    486         renderer->getGrContext()->printGpuStats();
    487     }
    488 #endif
    489 
    490     if (FLAGS_writeJsonSummaryPath.count() == 1) {
    491         // If there were any descriptions on the command line, insert them now.
    492         for (int i=0; i<FLAGS_descriptions.count(); i++) {
    493             SkTArray<SkString> tokens;
    494             SkStrSplit(FLAGS_descriptions[i], "=", &tokens);
    495             SkASSERT(tokens.count() == 2);
    496             jsonSummary.addDescription(tokens[0].c_str(), tokens[1].c_str());
    497         }
    498         if (FLAGS_imageBaseGSUrl.count() == 1) {
    499           jsonSummary.setImageBaseGSUrl(FLAGS_imageBaseGSUrl[0]);
    500         }
    501         jsonSummary.writeToFile(FLAGS_writeJsonSummaryPath[0]);
    502     }
    503     return 0;
    504 }
    505 
    506 #if !defined SK_BUILD_FOR_IOS
    507 int main(int argc, char * const argv[]) {
    508     return tool_main(argc, (char**) argv);
    509 }
    510 #endif
    511