diff options
author | scroggo@google.com <scroggo@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-05-08 19:14:23 +0000 |
---|---|---|
committer | scroggo@google.com <scroggo@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-05-08 19:14:23 +0000 |
commit | 6843bdb7061364c100990e7e0feb3757fca08bb0 (patch) | |
tree | bd6a8529059cdfe4d8e7b26c5dfdd0cd1bcf337c /tools | |
parent | 94b366a3e8ed7f03b4417f45999572399e6e591c (diff) |
Write/compare against expectations in skimage tool.
skimage:
Add two new modes: one to write expectations to a json file, and
another to compare results against expectations.
Use SkPATH_SEPARATOR instead of '/'.
gm_expectations:
Split into a static library so it can be used by skimage.
Make functions non static and move function definitions into source
file.
Capitalize static member functions to follow the coding style
guidelines.
BUG=https://code.google.com/p/skia/issues/detail?id=1241
R=epoger@google.com
Review URL: https://codereview.chromium.org/14670021
git-svn-id: http://skia.googlecode.com/svn/trunk@9069 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'tools')
-rw-r--r-- | tools/skimage_main.cpp | 139 |
1 files changed, 130 insertions, 9 deletions
diff --git a/tools/skimage_main.cpp b/tools/skimage_main.cpp index b545af3edb..2041f7bbcc 100644 --- a/tools/skimage_main.cpp +++ b/tools/skimage_main.cpp @@ -5,7 +5,9 @@ * found in the LICENSE file. */ +#include "gm_expectations.h" #include "SkBitmap.h" +#include "SkBitmapHasher.h" #include "SkColorPriv.h" #include "SkCommandLineFlags.h" #include "SkData.h" @@ -18,7 +20,9 @@ #include "SkTArray.h" #include "SkTemplates.h" +DEFINE_string(createExpectationsPath, "", "Path to write JSON expectations."); DEFINE_string2(readPath, r, "", "Folder(s) and files to decode images. Required."); +DEFINE_string(readExpectationsPath, "", "Path to read JSON expectations from."); DEFINE_string2(writePath, w, "", "Write rendered images into this directory."); DEFINE_bool(reencode, true, "Reencode the images to test encoding."); DEFINE_bool(testSubsetDecoding, true, "Test decoding subsets of images."); @@ -97,6 +101,10 @@ static SkTArray<SkString, false> gSuccessfulDecodes; static SkTArray<SkString, false> gSuccessfulSubsetDecodes; static SkTArray<SkString, false> gFailedSubsetDecodes; +// Expections read from a file specified by readExpectationsPath. The expectations must have been +// previously written using createExpectationsPath. +SkAutoTUnref<skiagm::JsonExpectationsSource> gJsonExpectations; + static bool write_bitmap(const char outName[], SkBitmap* bm) { SkBitmap bitmap8888; if (SkBitmap::kARGB_8888_Config != bm->config()) { @@ -157,6 +165,79 @@ static SkIRect generate_random_rect(SkRandom* rand, int32_t maxX, int32_t maxY) return rect; } +// Stored expectations to be written to a file if createExpectationsPath is specified. +static Json::Value gExpectationsToWrite; + +/** + * If expectations are to be recorded, record the expected checksum of bitmap into global + * expectations array. + */ +static void write_expectations(const SkBitmap& bitmap, const char* filename) { + if (!FLAGS_createExpectationsPath.isEmpty()) { + // Creates an Expectations object, and add it to the list to write. + skiagm::Expectations expectation(bitmap); + Json::Value value = expectation.asJsonValue(); + gExpectationsToWrite[filename] = value; + } +} + +/** + * Return the name of the file, ignoring the directory structure. + * Does not create a new string. + * @param fullPath Full path to the file. + * @return string The basename of the file - anything beyond the final slash, or the full name + * if there is no slash. + * TODO: Might this be useful as a utility function in SkOSFile? Would it be more appropriate to + * create a new string? + */ +static const char* SkBasename(const char* fullPath) { + const char* filename = strrchr(fullPath, SkPATH_SEPARATOR); + if (NULL == filename || ++filename == '\0') { + filename = fullPath; + } + return filename; +} + +/** + * Compare against an expectation for this filename, if there is one. + * @param bitmap SkBitmap to compare to the expected value. + * @param filename String used to find the expected value. + * @return bool True if the bitmap matched the expectation, or if there was no expectation. False + * if there was an expecation that the bitmap did not match, or if an expectation could not be + * computed from an expectation. + */ +static bool compare_to_expectations_if_necessary(const SkBitmap& bitmap, const char* filename, + SkTArray<SkString, false>* failureArray) { + if (NULL == gJsonExpectations.get()) { + return true; + } + + skiagm::Expectations jsExpectation = gJsonExpectations->get(filename); + if (jsExpectation.empty()) { + return true; + } + + SkHashDigest checksum; + if (!SkBitmapHasher::ComputeDigest(bitmap, &checksum)) { + if (failureArray != NULL) { + failureArray->push_back().printf("decoded %s, but could not create a checksum.", + filename); + } + return false; + } + + if (jsExpectation.match(checksum)) { + return true; + } + + if (failureArray != NULL) { + failureArray->push_back().printf("decoded %s, but the result does not match " + "expectations.", + filename); + } + return false; +} + static void decodeFileAndWrite(const char srcPath[], const SkString* writePath) { SkBitmap bitmap; SkFILEStream stream(srcPath); @@ -180,7 +261,15 @@ static void decodeFileAndWrite(const char srcPath[], const SkString* writePath) return; } - gSuccessfulDecodes.push_back().printf("%s [%d %d]", srcPath, bitmap.width(), bitmap.height()); + // Create a string representing just the filename itself, for use in json expectations. + const char* filename = SkBasename(srcPath); + + if (compare_to_expectations_if_necessary(bitmap, filename, &gDecodeFailures)) { + gSuccessfulDecodes.push_back().printf("%s [%d %d]", srcPath, bitmap.width(), + bitmap.height()); + } + + write_expectations(bitmap, filename); if (FLAGS_testSubsetDecoding) { SkDEBUGCODE(bool couldRewind =) stream.rewind(); @@ -199,8 +288,16 @@ static void decodeFileAndWrite(const char srcPath[], const SkString* writePath) SkString subsetDim = SkStringPrintf("[%d,%d,%d,%d]", rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); if (codec->decodeSubset(&bitmapFromDecodeSubset, rect, SkBitmap::kNo_Config)) { - gSuccessfulSubsetDecodes.push_back().printf("Decoded subset %s from %s", - subsetDim.c_str(), srcPath); + SkString subsetName = SkStringPrintf("%s_%s", filename, subsetDim.c_str()); + if (compare_to_expectations_if_necessary(bitmapFromDecodeSubset, + subsetName.c_str(), + &gFailedSubsetDecodes)) { + gSuccessfulSubsetDecodes.push_back().printf("Decoded subset %s from %s", + subsetDim.c_str(), srcPath); + } + + write_expectations(bitmapFromDecodeSubset, subsetName.c_str()); + if (writePath != NULL) { // Write the region to a file whose name includes the dimensions. SkString suffix = SkStringPrintf("_%s.png", subsetDim.c_str()); @@ -322,6 +419,17 @@ static bool print_strings(const char* title, const SkTArray<SkString, false>& st return false; } +/** + * If directory is non null and does not end with a path separator, append one. + * @param directory SkString representing the path to a directory. If the last character is not a + * path separator (specific to the current OS), append one. + */ +static void append_path_separator_if_necessary(SkString* directory) { + if (directory != NULL && directory->c_str()[directory->size() - 1] != SkPATH_SEPARATOR) { + directory->appendf("%c", SkPATH_SEPARATOR); + } +} + int tool_main(int argc, char** argv); int tool_main(int argc, char** argv) { SkCommandLineFlags::SetUsage("Decode files, and optionally write the results to files."); @@ -335,14 +443,17 @@ int tool_main(int argc, char** argv) { SkAutoGraphics ag; + if (!FLAGS_readExpectationsPath.isEmpty()) { + gJsonExpectations.reset(SkNEW_ARGS(skiagm::JsonExpectationsSource, + (FLAGS_readExpectationsPath[0]))); + } + SkString outDir; SkString* outDirPtr; if (FLAGS_writePath.count() == 1) { outDir.set(FLAGS_writePath[0]); - if (outDir.c_str()[outDir.size() - 1] != '/') { - outDir.append("/"); - } + append_path_separator_if_necessary(&outDir); outDirPtr = &outDir; } else { outDirPtr = NULL; @@ -356,9 +467,7 @@ int tool_main(int argc, char** argv) { SkString filename; if (iter.next(&filename)) { SkString directory(FLAGS_readPath[i]); - if (directory[directory.size() - 1] != '/') { - directory.append("/"); - } + append_path_separator_if_necessary(&directory); do { SkString fullname(directory); fullname.append(filename); @@ -369,6 +478,18 @@ int tool_main(int argc, char** argv) { } } + if (!FLAGS_createExpectationsPath.isEmpty()) { + // Use an empty value for everything besides expectations, since the reader only cares + // about the expectations. + Json::Value nullValue; + Json::Value root = skiagm::CreateJsonTree(gExpectationsToWrite, nullValue, nullValue, + nullValue, nullValue); + std::string jsonStdString = root.toStyledString(); + SkString path = SkStringPrintf("%s%cresults.json", FLAGS_createExpectationsPath[0], + SkPATH_SEPARATOR); + SkFILEWStream stream(path.c_str()); + stream.write(jsonStdString.c_str(), jsonStdString.length()); + } // Add some space, since codecs may print warnings without newline. SkDebugf("\n\n"); |