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 "SkStream.h"
     20 #include "SkString.h"
     21 #include "PictureRenderer.h"
     22 #include "PictureRenderingFlags.h"
     23 #include "picture_utils.h"
     24 
     25 // Flags used by this file, alphabetically:
     26 DEFINE_int32(clone, 0, "Clone the picture n times before rendering.");
     27 DECLARE_bool(deferImageDecoding);
     28 DEFINE_int32(maxComponentDiff, 256, "Maximum diff on a component, 0 - 256. Components that differ "
     29              "by more than this amount are considered errors, though all diffs are reported. "
     30              "Requires --validate.");
     31 DECLARE_string(readPath);
     32 DEFINE_bool(writeEncodedImages, false, "Any time the skp contains an encoded image, write it to a "
     33             "file rather than decoding it. Requires writePath to be set. Skips drawing the full "
     34             "skp to a file. Not compatible with deferImageDecoding.");
     35 DEFINE_string(writeJsonSummaryPath, "", "File to write a JSON summary of image results to. "
     36               "TODO(epoger): Currently, this only works if --writePath is also specified.");
     37 DEFINE_string2(writePath, w, "", "Directory to write the rendered images.");
     38 DEFINE_bool(writeWholeImage, false, "In tile mode, write the entire rendered image to a "
     39             "file, instead of an image for each tile.");
     40 DEFINE_bool(validate, false, "Verify that the rendered image contains the same pixels as "
     41             "the picture rendered in simple mode. When used in conjunction with --bbh, results "
     42             "are validated against the picture rendered in the same mode, but without the bbh.");
     43 
     44 DEFINE_bool(bench_record, false, "If true, drop into an infinite loop of recording the picture.");
     45 
     46 static void make_output_filepath(SkString* path, const SkString& dir,
     47                                  const SkString& name) {
     48     sk_tools::make_filepath(path, dir, name);
     49     // Remove ".skp"
     50     path->remove(path->size() - 4, 4);
     51 }
     52 
     53 ////////////////////////////////////////////////////////////////////////////////////////////////////
     54 
     55 /**
     56  *  Table for translating from format of data to a suffix.
     57  */
     58 struct Format {
     59     SkImageDecoder::Format  fFormat;
     60     const char*             fSuffix;
     61 };
     62 static const Format gFormats[] = {
     63     { SkImageDecoder::kBMP_Format, ".bmp" },
     64     { SkImageDecoder::kGIF_Format, ".gif" },
     65     { SkImageDecoder::kICO_Format, ".ico" },
     66     { SkImageDecoder::kJPEG_Format, ".jpg" },
     67     { SkImageDecoder::kPNG_Format, ".png" },
     68     { SkImageDecoder::kWBMP_Format, ".wbmp" },
     69     { SkImageDecoder::kWEBP_Format, ".webp" },
     70     { SkImageDecoder::kUnknown_Format, "" },
     71 };
     72 
     73 /**
     74  *  Get an appropriate suffix for an image format.
     75  */
     76 static const char* get_suffix_from_format(SkImageDecoder::Format format) {
     77     for (size_t i = 0; i < SK_ARRAY_COUNT(gFormats); i++) {
     78         if (gFormats[i].fFormat == format) {
     79             return gFormats[i].fSuffix;
     80         }
     81     }
     82     return "";
     83 }
     84 
     85 /**
     86  *  Base name for an image file created from the encoded data in an skp.
     87  */
     88 static SkString gInputFileName;
     89 
     90 /**
     91  *  Number to be appended to the image file name so that it is unique.
     92  */
     93 static uint32_t gImageNo;
     94 
     95 /**
     96  *  Set up the name for writing encoded data to a file.
     97  *  Sets gInputFileName to name, minus any extension ".*"
     98  *  Sets gImageNo to 0, so images from file "X.skp" will
     99  *  look like "X_<gImageNo>.<suffix>", beginning with 0
    100  *  for each new skp.
    101  */
    102 static void reset_image_file_base_name(const SkString& name) {
    103     gImageNo = 0;
    104     // Remove ".skp"
    105     const char* cName = name.c_str();
    106     const char* dot = strrchr(cName, '.');
    107     if (dot != NULL) {
    108         gInputFileName.set(cName, dot - cName);
    109     } else {
    110         gInputFileName.set(name);
    111     }
    112 }
    113 
    114 /**
    115  *  Write the raw encoded bitmap data to a file.
    116  */
    117 static bool write_image_to_file(const void* buffer, size_t size, SkBitmap* bitmap) {
    118     SkASSERT(!FLAGS_writePath.isEmpty());
    119     SkMemoryStream memStream(buffer, size);
    120     SkString outPath;
    121     SkImageDecoder::Format format = SkImageDecoder::GetStreamFormat(&memStream);
    122     SkString name = SkStringPrintf("%s_%d%s", gInputFileName.c_str(), gImageNo++,
    123                                    get_suffix_from_format(format));
    124     SkString dir(FLAGS_writePath[0]);
    125     sk_tools::make_filepath(&outPath, dir, name);
    126     SkFILEWStream fileStream(outPath.c_str());
    127     if (!(fileStream.isValid() && fileStream.write(buffer, size))) {
    128         SkDebugf("Failed to write encoded data to \"%s\"\n", outPath.c_str());
    129     }
    130     // Put in a dummy bitmap.
    131     return SkImageDecoder::DecodeStream(&memStream, bitmap, SkBitmap::kNo_Config,
    132                                         SkImageDecoder::kDecodeBounds_Mode);
    133 }
    134 
    135 ////////////////////////////////////////////////////////////////////////////////////////////////////
    136 
    137 /**
    138  * Called only by render_picture().
    139  */
    140 static bool render_picture_internal(const SkString& inputPath, const SkString* outputDir,
    141                                     sk_tools::PictureRenderer& renderer,
    142                                     SkBitmap** out) {
    143     SkString inputFilename;
    144     sk_tools::get_basename(&inputFilename, inputPath);
    145 
    146     SkFILEStream inputStream;
    147     inputStream.setPath(inputPath.c_str());
    148     if (!inputStream.isValid()) {
    149         SkDebugf("Could not open file %s\n", inputPath.c_str());
    150         return false;
    151     }
    152 
    153     SkPicture::InstallPixelRefProc proc;
    154     if (FLAGS_deferImageDecoding) {
    155         proc = &sk_tools::LazyDecodeBitmap;
    156     } else if (FLAGS_writeEncodedImages) {
    157         SkASSERT(!FLAGS_writePath.isEmpty());
    158         reset_image_file_base_name(inputFilename);
    159         proc = &write_image_to_file;
    160     } else {
    161         proc = &SkImageDecoder::DecodeMemory;
    162     }
    163 
    164     SkDebugf("deserializing... %s\n", inputPath.c_str());
    165 
    166     SkPicture* picture = SkPicture::CreateFromStream(&inputStream, proc);
    167 
    168     if (NULL == picture) {
    169         SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str());
    170         return false;
    171     }
    172 
    173     while (FLAGS_bench_record) {
    174         const int kRecordFlags = 0;
    175         SkPicture other;
    176         picture->draw(other.beginRecording(picture->width(), picture->height(), kRecordFlags));
    177         other.endRecording();
    178     }
    179 
    180     for (int i = 0; i < FLAGS_clone; ++i) {
    181         SkPicture* clone = picture->clone();
    182         SkDELETE(picture);
    183         picture = clone;
    184     }
    185 
    186     SkDebugf("drawing... [%i %i] %s\n", picture->width(), picture->height(),
    187              inputPath.c_str());
    188 
    189     renderer.init(picture);
    190     renderer.setup();
    191 
    192     SkString* outputPath = NULL;
    193     if (NULL != outputDir && outputDir->size() > 0 && !FLAGS_writeEncodedImages) {
    194         outputPath = SkNEW(SkString);
    195         make_output_filepath(outputPath, *outputDir, inputFilename);
    196     }
    197 
    198     bool success = renderer.render(outputPath, out);
    199     if (outputPath) {
    200         if (!success) {
    201             SkDebugf("Could not write to file %s\n", outputPath->c_str());
    202         }
    203         SkDELETE(outputPath);
    204     }
    205 
    206     renderer.end();
    207 
    208     SkDELETE(picture);
    209     return success;
    210 }
    211 
    212 static inline int getByte(uint32_t value, int index) {
    213     SkASSERT(0 <= index && index < 4);
    214     return (value >> (index * 8)) & 0xFF;
    215 }
    216 
    217 static int MaxByteDiff(uint32_t v1, uint32_t v2) {
    218     return SkMax32(SkMax32(abs(getByte(v1, 0) - getByte(v2, 0)), abs(getByte(v1, 1) - getByte(v2, 1))),
    219                    SkMax32(abs(getByte(v1, 2) - getByte(v2, 2)), abs(getByte(v1, 3) - getByte(v2, 3))));
    220 }
    221 
    222 namespace {
    223 class AutoRestoreBbhType {
    224 public:
    225     AutoRestoreBbhType() {
    226         fRenderer = NULL;
    227         fSavedBbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
    228     }
    229 
    230     void set(sk_tools::PictureRenderer* renderer,
    231              sk_tools::PictureRenderer::BBoxHierarchyType bbhType) {
    232         fRenderer = renderer;
    233         fSavedBbhType = renderer->getBBoxHierarchyType();
    234         renderer->setBBoxHierarchyType(bbhType);
    235     }
    236 
    237     ~AutoRestoreBbhType() {
    238         if (NULL != fRenderer) {
    239             fRenderer->setBBoxHierarchyType(fSavedBbhType);
    240         }
    241     }
    242 
    243 private:
    244     sk_tools::PictureRenderer* fRenderer;
    245     sk_tools::PictureRenderer::BBoxHierarchyType fSavedBbhType;
    246 };
    247 }
    248 
    249 /**
    250  * Render the SKP file(s) within inputPath, writing their bitmap images into outputDir.
    251  *
    252  * @param inputPath path to an individual SKP file, or a directory of SKP files
    253  * @param outputDir if not NULL, write the image(s) generated into this directory
    254  * @param renderer PictureRenderer to use to render the SKPs
    255  * @param jsonSummaryPtr if not NULL, add the image(s) generated to this summary
    256  */
    257 static bool render_picture(const SkString& inputPath, const SkString* outputDir,
    258                            sk_tools::PictureRenderer& renderer,
    259                            sk_tools::ImageResultsSummary *jsonSummaryPtr) {
    260     int diffs[256] = {0};
    261     SkBitmap* bitmap = NULL;
    262     renderer.setJsonSummaryPtr(jsonSummaryPtr);
    263     bool success = render_picture_internal(inputPath,
    264         FLAGS_writeWholeImage ? NULL : outputDir,
    265         renderer,
    266         FLAGS_validate || FLAGS_writeWholeImage ? &bitmap : NULL);
    267 
    268     if (!success || ((FLAGS_validate || FLAGS_writeWholeImage) && bitmap == NULL)) {
    269         SkDebugf("Failed to draw the picture.\n");
    270         SkDELETE(bitmap);
    271         return false;
    272     }
    273 
    274     if (FLAGS_validate) {
    275         SkBitmap* referenceBitmap = NULL;
    276         sk_tools::PictureRenderer* referenceRenderer;
    277         // If the renderer uses a BBoxHierarchy, then the reference renderer
    278         // will be the same renderer, without the bbh.
    279         AutoRestoreBbhType arbbh;
    280         if (sk_tools::PictureRenderer::kNone_BBoxHierarchyType !=
    281             renderer.getBBoxHierarchyType()) {
    282             referenceRenderer = &renderer;
    283             referenceRenderer->ref();  // to match auto unref below
    284             arbbh.set(referenceRenderer, sk_tools::PictureRenderer::kNone_BBoxHierarchyType);
    285         } else {
    286             referenceRenderer = SkNEW(sk_tools::SimplePictureRenderer);
    287         }
    288         SkAutoTUnref<sk_tools::PictureRenderer> aurReferenceRenderer(referenceRenderer);
    289 
    290         success = render_picture_internal(inputPath, NULL, *referenceRenderer,
    291                                           &referenceBitmap);
    292 
    293         if (!success || NULL == referenceBitmap || NULL == referenceBitmap->getPixels()) {
    294             SkDebugf("Failed to draw the reference picture.\n");
    295             SkDELETE(bitmap);
    296             SkDELETE(referenceBitmap);
    297             return false;
    298         }
    299 
    300         if (success && (bitmap->width() != referenceBitmap->width())) {
    301             SkDebugf("Expected image width: %i, actual image width %i.\n",
    302                      referenceBitmap->width(), bitmap->width());
    303             SkDELETE(bitmap);
    304             SkDELETE(referenceBitmap);
    305             return false;
    306         }
    307         if (success && (bitmap->height() != referenceBitmap->height())) {
    308             SkDebugf("Expected image height: %i, actual image height %i",
    309                      referenceBitmap->height(), bitmap->height());
    310             SkDELETE(bitmap);
    311             SkDELETE(referenceBitmap);
    312             return false;
    313         }
    314 
    315         for (int y = 0; success && y < bitmap->height(); y++) {
    316             for (int x = 0; success && x < bitmap->width(); x++) {
    317                 int diff = MaxByteDiff(*referenceBitmap->getAddr32(x, y),
    318                                        *bitmap->getAddr32(x, y));
    319                 SkASSERT(diff >= 0 && diff <= 255);
    320                 diffs[diff]++;
    321 
    322                 if (diff > FLAGS_maxComponentDiff) {
    323                     SkDebugf("Expected pixel at (%i %i) exceedds maximum "
    324                                  "component diff of %i: 0x%x, actual 0x%x\n",
    325                              x, y, FLAGS_maxComponentDiff,
    326                              *referenceBitmap->getAddr32(x, y),
    327                              *bitmap->getAddr32(x, y));
    328                     SkDELETE(bitmap);
    329                     SkDELETE(referenceBitmap);
    330                     return false;
    331                 }
    332             }
    333         }
    334         SkDELETE(referenceBitmap);
    335 
    336         for (int i = 1; i <= 255; ++i) {
    337             if(diffs[i] > 0) {
    338                 SkDebugf("Number of pixels with max diff of %i is %i\n", i, diffs[i]);
    339             }
    340         }
    341     }
    342 
    343     if (FLAGS_writeWholeImage) {
    344         sk_tools::force_all_opaque(*bitmap);
    345 
    346         if (NULL != jsonSummaryPtr) {
    347             // EPOGER: This is a hacky way of constructing the filename associated with the
    348             // image checksum; we basically are repeating the logic of make_output_filepath()
    349             // and code below here, within here.
    350             // It would be better for the filename (without outputDir) to be passed in here,
    351             // and used both for the checksum file and writing into outputDir.
    352             //
    353             // EPOGER: what about including the config type within hashFilename?  That way,
    354             // we could combine results of different config types without conflicting filenames.
    355             SkString hashFilename;
    356             sk_tools::get_basename(&hashFilename, inputPath);
    357             hashFilename.remove(hashFilename.size() - 4, 4); // Remove ".skp"
    358             hashFilename.append(".png");
    359             jsonSummaryPtr->add(hashFilename.c_str(), *bitmap);
    360         }
    361 
    362         if (NULL != outputDir) {
    363             SkString inputFilename;
    364             sk_tools::get_basename(&inputFilename, inputPath);
    365             SkString outputPath;
    366             make_output_filepath(&outputPath, *outputDir, inputFilename);
    367             outputPath.append(".png");
    368             if (!SkImageEncoder::EncodeFile(outputPath.c_str(), *bitmap,
    369                                             SkImageEncoder::kPNG_Type, 100)) {
    370                 SkDebugf("Failed to draw the picture.\n");
    371                 success = false;
    372             }
    373         }
    374     }
    375     SkDELETE(bitmap);
    376 
    377     return success;
    378 }
    379 
    380 
    381 static int process_input(const char* input, const SkString* outputDir,
    382                          sk_tools::PictureRenderer& renderer,
    383                          sk_tools::ImageResultsSummary *jsonSummaryPtr) {
    384     SkOSFile::Iter iter(input, "skp");
    385     SkString inputFilename;
    386     int failures = 0;
    387     SkDebugf("process_input, %s\n", input);
    388     if (iter.next(&inputFilename)) {
    389         do {
    390             SkString inputPath;
    391             SkString inputAsSkString(input);
    392             sk_tools::make_filepath(&inputPath, inputAsSkString, inputFilename);
    393             if (!render_picture(inputPath, outputDir, renderer, jsonSummaryPtr)) {
    394                 ++failures;
    395             }
    396         } while(iter.next(&inputFilename));
    397     } else if (SkStrEndsWith(input, ".skp")) {
    398         SkString inputPath(input);
    399         if (!render_picture(inputPath, outputDir, renderer, jsonSummaryPtr)) {
    400             ++failures;
    401         }
    402     } else {
    403         SkString warning;
    404         warning.printf("Warning: skipping %s\n", input);
    405         SkDebugf(warning.c_str());
    406     }
    407     return failures;
    408 }
    409 
    410 int tool_main(int argc, char** argv);
    411 int tool_main(int argc, char** argv) {
    412     SkCommandLineFlags::SetUsage("Render .skp files.");
    413     SkCommandLineFlags::Parse(argc, argv);
    414 
    415     if (FLAGS_readPath.isEmpty()) {
    416         SkDebugf(".skp files or directories are required.\n");
    417         exit(-1);
    418     }
    419 
    420     if (FLAGS_maxComponentDiff < 0 || FLAGS_maxComponentDiff > 256) {
    421         SkDebugf("--maxComponentDiff must be between 0 and 256\n");
    422         exit(-1);
    423     }
    424 
    425     if (FLAGS_maxComponentDiff != 256 && !FLAGS_validate) {
    426         SkDebugf("--maxComponentDiff requires --validate\n");
    427         exit(-1);
    428     }
    429 
    430     if (FLAGS_clone < 0) {
    431         SkDebugf("--clone must be >= 0. Was %i\n", FLAGS_clone);
    432         exit(-1);
    433     }
    434 
    435     if (FLAGS_writeEncodedImages) {
    436         if (FLAGS_writePath.isEmpty()) {
    437             SkDebugf("--writeEncodedImages requires --writePath\n");
    438             exit(-1);
    439         }
    440         if (FLAGS_deferImageDecoding) {
    441             SkDebugf("--writeEncodedImages is not compatible with --deferImageDecoding\n");
    442             exit(-1);
    443         }
    444     }
    445 
    446     SkString errorString;
    447     SkAutoTUnref<sk_tools::PictureRenderer> renderer(parseRenderer(errorString,
    448                                                                    kRender_PictureTool));
    449     if (errorString.size() > 0) {
    450         SkDebugf("%s\n", errorString.c_str());
    451     }
    452 
    453     if (renderer.get() == NULL) {
    454         exit(-1);
    455     }
    456 
    457     SkAutoGraphics ag;
    458 
    459     SkString outputDir;
    460     if (FLAGS_writePath.count() == 1) {
    461         outputDir.set(FLAGS_writePath[0]);
    462     }
    463     sk_tools::ImageResultsSummary jsonSummary;
    464     sk_tools::ImageResultsSummary* jsonSummaryPtr = NULL;
    465     if (FLAGS_writeJsonSummaryPath.count() == 1) {
    466         jsonSummaryPtr = &jsonSummary;
    467     }
    468 
    469     int failures = 0;
    470     for (int i = 0; i < FLAGS_readPath.count(); i ++) {
    471         failures += process_input(FLAGS_readPath[i], &outputDir, *renderer.get(), jsonSummaryPtr);
    472     }
    473     if (failures != 0) {
    474         SkDebugf("Failed to render %i pictures.\n", failures);
    475         return 1;
    476     }
    477 #if SK_SUPPORT_GPU
    478 #if GR_CACHE_STATS
    479     if (renderer->isUsingGpuDevice()) {
    480         GrContext* ctx = renderer->getGrContext();
    481         ctx->printCacheStats();
    482 #ifdef SK_DEVELOPER
    483         ctx->dumpFontCache();
    484 #endif
    485     }
    486 #endif
    487 #endif
    488     if (FLAGS_writeJsonSummaryPath.count() == 1) {
    489         jsonSummary.writeToFile(FLAGS_writeJsonSummaryPath[0]);
    490     }
    491     return 0;
    492 }
    493 
    494 #if !defined SK_BUILD_FOR_IOS
    495 int main(int argc, char * const argv[]) {
    496     return tool_main(argc, (char**) argv);
    497 }
    498 #endif
    499