Update skdiff.
https://codereview.appspot.com/6850115/
git-svn-id: http://skia.googlecode.com/svn/trunk@6681 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/tools/skdiff_main.cpp b/tools/skdiff_main.cpp
index 3bd14c6..b9b0038 100644
--- a/tools/skdiff_main.cpp
+++ b/tools/skdiff_main.cpp
@@ -1,11 +1,13 @@
-
/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
-#include "SkColorPriv.h"
+#include "skdiff.h"
+#include "skdiff_html.h"
+#include "skdiff_utils.h"
+#include "SkBitmap.h"
#include "SkData.h"
#include "SkImageDecoder.h"
#include "SkImageEncoder.h"
@@ -13,7 +15,6 @@
#include "SkStream.h"
#include "SkTDArray.h"
#include "SkTemplates.h"
-#include "SkTime.h"
#include "SkTSearch.h"
#include "SkTypes.h"
@@ -31,159 +32,25 @@
* Returns zero exit code if all images match across baseDir and comparisonDir.
*/
-#if SK_BUILD_FOR_WIN32
- #define PATH_DIV_STR "\\"
- #define PATH_DIV_CHAR '\\'
-#else
- #define PATH_DIV_STR "/"
- #define PATH_DIV_CHAR '/'
-#endif
-
-// Result of comparison for each pair of files.
-// Listed from "better" to "worse", for sorting of results.
-enum Result {
- kEqualBits,
- kEqualPixels,
- kDifferentPixels,
- kDifferentSizes,
- kDifferentOther,
- kComparisonMissing,
- kBaseMissing,
- kUnknown,
- //
- kNumResultTypes // NOT A VALID VALUE--used to set up arrays. Must be last.
-};
-
-// Returns the Result with this name.
-// If there is no Result with this name, returns kNumResultTypes.
-// TODO: Is there a better return value for the fall-through case?
-static Result getResultByName(const char *name) {
- if (0 == strcmp("EqualBits", name)) {
- return kEqualBits;
- }
- if (0 == strcmp("EqualPixels", name)) {
- return kEqualPixels;
- }
- if (0 == strcmp("DifferentPixels", name)) {
- return kDifferentPixels;
- }
- if (0 == strcmp("DifferentSizes", name)) {
- return kDifferentSizes;
- }
- if (0 == strcmp("DifferentOther", name)) {
- return kDifferentOther;
- }
- if (0 == strcmp("ComparisonMissing", name)) {
- return kComparisonMissing;
- }
- if (0 == strcmp("BaseMissing", name)) {
- return kBaseMissing;
- }
- if (0 == strcmp("Unknown", name)) {
- return kUnknown;
- }
- return kNumResultTypes;
-}
-
-// Returns a text description of the given Result type.
-static const char *getResultDescription(Result result) {
- switch (result) {
- case kEqualBits:
- return "contain exactly the same bits";
- case kEqualPixels:
- return "contain the same pixel values, but not the same bits";
- case kDifferentPixels:
- return "have identical dimensions but some differing pixels";
- case kDifferentSizes:
- return "have differing dimensions";
- case kDifferentOther:
- return "contain different bits and are not parsable images";
- case kBaseMissing:
- return "missing from baseDir";
- case kComparisonMissing:
- return "missing from comparisonDir";
- case kUnknown:
- return "not compared yet";
- default:
- return NULL;
- }
-}
-
-struct DiffRecord {
- DiffRecord (const SkString filename,
- const SkString basePath,
- const SkString comparisonPath,
- const Result result = kUnknown)
- : fFilename (filename)
- , fBasePath (basePath)
- , fComparisonPath (comparisonPath)
- , fBaseBitmap (NULL)
- , fComparisonBitmap (NULL)
- , fDifferenceBitmap (NULL)
- , fWhiteBitmap (NULL)
- , fBaseHeight (0)
- , fBaseWidth (0)
- , fFractionDifference (0)
- , fWeightedFraction (0)
- , fAverageMismatchR (0)
- , fAverageMismatchG (0)
- , fAverageMismatchB (0)
- , fMaxMismatchR (0)
- , fMaxMismatchG (0)
- , fMaxMismatchB (0)
- , fResult(result) {
- };
-
- SkString fFilename;
- SkString fBasePath;
- SkString fComparisonPath;
-
- SkBitmap* fBaseBitmap;
- SkBitmap* fComparisonBitmap;
- SkBitmap* fDifferenceBitmap;
- SkBitmap* fWhiteBitmap;
-
- int fBaseHeight;
- int fBaseWidth;
-
- /// Arbitrary floating-point metric to be used to sort images from most
- /// to least different from baseline; values of 0 will be omitted from the
- /// summary webpage.
- float fFractionDifference;
- float fWeightedFraction;
-
- float fAverageMismatchR;
- float fAverageMismatchG;
- float fAverageMismatchB;
-
- uint32_t fMaxMismatchR;
- uint32_t fMaxMismatchG;
- uint32_t fMaxMismatchB;
-
- /// Which category of diff result.
- Result fResult;
-};
-
-#define MAX2(a,b) (((b) < (a)) ? (a) : (b))
-#define MAX3(a,b,c) (((b) < (a)) ? MAX2((a), (c)) : MAX2((b), (c)))
-
-const SkPMColor PMCOLOR_WHITE = SkPreMultiplyColor(SK_ColorWHITE);
-const SkPMColor PMCOLOR_BLACK = SkPreMultiplyColor(SK_ColorBLACK);
-
typedef SkTDArray<SkString*> StringArray;
typedef StringArray FileArray;
struct DiffSummary {
DiffSummary ()
- : fNumMatches (0)
- , fNumMismatches (0)
- , fMaxMismatchV (0)
- , fMaxMismatchPercent (0) { };
+ : fNumMatches(0)
+ , fNumMismatches(0)
+ , fMaxMismatchV(0)
+ , fMaxMismatchPercent(0) { };
~DiffSummary() {
- for (int i = 0; i < kNumResultTypes; i++) {
+ for (int i = 0; i < DiffRecord::kResultCount; ++i) {
fResultsOfType[i].deleteAll();
}
+ for (int base = 0; base < DiffResource::kStatusCount; ++base) {
+ for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
+ fStatusOfType[base][comparison].deleteAll();
+ }
+ }
}
uint32_t fNumMatches;
@@ -191,7 +58,48 @@
uint32_t fMaxMismatchV;
float fMaxMismatchPercent;
- FileArray fResultsOfType[kNumResultTypes];
+ FileArray fResultsOfType[DiffRecord::kResultCount];
+ FileArray fStatusOfType[DiffResource::kStatusCount][DiffResource::kStatusCount];
+
+ void printContents(const FileArray& fileArray,
+ const char* baseStatus, const char* comparisonStatus,
+ bool listFilenames) {
+ int n = fileArray.count();
+ printf("%d file pairs %s in baseDir and %s in comparisonDir",
+ n, baseStatus, comparisonStatus);
+ if (listFilenames) {
+ printf(": ");
+ for (int i = 0; i < n; ++i) {
+ printf("%s ", fileArray[i]->c_str());
+ }
+ }
+ printf("\n");
+ }
+
+ void printStatus(bool listFilenames,
+ bool failOnStatusType[DiffResource::kStatusCount]
+ [DiffResource::kStatusCount]) {
+ typedef DiffResource::Status Status;
+
+ for (int base = 0; base < DiffResource::kStatusCount; ++base) {
+ Status baseStatus = static_cast<Status>(base);
+ for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
+ Status comparisonStatus = static_cast<Status>(comparison);
+ const FileArray& fileArray = fStatusOfType[base][comparison];
+ if (fileArray.count() > 0) {
+ if (failOnStatusType[base][comparison]) {
+ printf(" [*] ");
+ } else {
+ printf(" [_] ");
+ }
+ printContents(fileArray,
+ DiffResource::getStatusDescription(baseStatus),
+ DiffResource::getStatusDescription(comparisonStatus),
+ listFilenames);
+ }
+ }
+ }
+ }
// Print a line about the contents of this FileArray to stdout.
void printContents(const FileArray& fileArray, const char* headerText, bool listFilenames) {
@@ -206,16 +114,22 @@
printf("\n");
}
- void print(bool listFilenames, bool failOnResultType[kNumResultTypes]) {
+ void print(bool listFilenames, bool failOnResultType[DiffRecord::kResultCount],
+ bool failOnStatusType[DiffResource::kStatusCount]
+ [DiffResource::kStatusCount]) {
printf("\ncompared %d file pairs:\n", fNumMatches + fNumMismatches);
- for (int resultInt = 0; resultInt < kNumResultTypes; resultInt++) {
- Result result = static_cast<Result>(resultInt);
+ for (int resultInt = 0; resultInt < DiffRecord::kResultCount; ++resultInt) {
+ DiffRecord::Result result = static_cast<DiffRecord::Result>(resultInt);
if (failOnResultType[result]) {
printf("[*] ");
} else {
printf("[_] ");
}
- printContents(fResultsOfType[result], getResultDescription(result), listFilenames);
+ printContents(fResultsOfType[result], DiffRecord::getResultDescription(result),
+ listFilenames);
+ if (DiffRecord::kCouldNotCompare_Result == result) {
+ printStatus(listFilenames, failOnStatusType);
+ }
}
printf("(results marked with [*] will cause nonzero return value)\n");
printf("\nnumber of mismatching file pairs: %d\n", fNumMismatches);
@@ -228,18 +142,27 @@
void add (DiffRecord* drp) {
uint32_t mismatchValue;
- fResultsOfType[drp->fResult].push(new SkString(drp->fFilename));
+ if (drp->fBase.fFilename.equals(drp->fComparison.fFilename)) {
+ fResultsOfType[drp->fResult].push(new SkString(drp->fBase.fFilename));
+ } else {
+ SkString* blame = new SkString("(");
+ blame->append(drp->fBase.fFilename);
+ blame->append(", ");
+ blame->append(drp->fComparison.fFilename);
+ blame->append(")");
+ fResultsOfType[drp->fResult].push(blame);
+ }
switch (drp->fResult) {
- case kEqualBits:
+ case DiffRecord::kEqualBits_Result:
fNumMatches++;
break;
- case kEqualPixels:
+ case DiffRecord::kEqualPixels_Result:
fNumMatches++;
break;
- case kDifferentSizes:
+ case DiffRecord::kDifferentSizes_Result:
fNumMismatches++;
break;
- case kDifferentPixels:
+ case DiffRecord::kDifferentPixels_Result:
fNumMismatches++;
if (drp->fFractionDifference * 100 > fMaxMismatchPercent) {
fMaxMismatchPercent = drp->fFractionDifference * 100;
@@ -250,16 +173,12 @@
fMaxMismatchV = mismatchValue;
}
break;
- case kDifferentOther:
+ case DiffRecord::kCouldNotCompare_Result:
fNumMismatches++;
+ fStatusOfType[drp->fBase.fStatus][drp->fComparison.fStatus].push(
+ new SkString(drp->fBase.fFilename));
break;
- case kBaseMissing:
- fNumMismatches++;
- break;
- case kComparisonMissing:
- fNumMismatches++;
- break;
- case kUnknown:
+ case DiffRecord::kUnknown_Result:
SkDEBUGFAIL("adding uncategorized DiffRecord");
break;
default:
@@ -269,449 +188,6 @@
}
};
-typedef SkTDArray<DiffRecord*> RecordArray;
-
-/// A wrapper for any sortProc (comparison routine) which applies a first-order
-/// sort beforehand, and a tiebreaker if the sortProc returns 0.
-template<typename T>
-static int compare(const void* untyped_lhs, const void* untyped_rhs) {
- const DiffRecord* lhs = *reinterpret_cast<DiffRecord* const*>(untyped_lhs);
- const DiffRecord* rhs = *reinterpret_cast<DiffRecord* const*>(untyped_rhs);
-
- // First-order sort... these comparisons should be applied before comparing
- // pixel values, no matter what.
- if (lhs->fResult != rhs->fResult) {
- return (lhs->fResult < rhs->fResult) ? 1 : -1;
- }
-
- // Passed first-order sort, so call the pixel comparison routine.
- int result = T::comparePixels(lhs, rhs);
- if (result != 0) {
- return result;
- }
-
- // Tiebreaker... if we got to this point, we don't really care
- // which order they are sorted in, but let's at least be consistent.
- return strcmp(lhs->fFilename.c_str(), rhs->fFilename.c_str());
-}
-
-/// Comparison routine for qsort; sorts by fFractionDifference
-/// from largest to smallest.
-class CompareDiffMetrics {
-public:
- static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) {
- if (lhs->fFractionDifference < rhs->fFractionDifference) {
- return 1;
- }
- if (rhs->fFractionDifference < lhs->fFractionDifference) {
- return -1;
- }
- return 0;
- }
-};
-
-class CompareDiffWeighted {
-public:
- static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) {
- if (lhs->fWeightedFraction < rhs->fWeightedFraction) {
- return 1;
- }
- if (lhs->fWeightedFraction > rhs->fWeightedFraction) {
- return -1;
- }
- return 0;
- }
-};
-
-/// Comparison routine for qsort; sorts by max(fAverageMismatch{RGB})
-/// from largest to smallest.
-class CompareDiffMeanMismatches {
-public:
- static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) {
- float leftValue = MAX3(lhs->fAverageMismatchR,
- lhs->fAverageMismatchG,
- lhs->fAverageMismatchB);
- float rightValue = MAX3(rhs->fAverageMismatchR,
- rhs->fAverageMismatchG,
- rhs->fAverageMismatchB);
- if (leftValue < rightValue) {
- return 1;
- }
- if (rightValue < leftValue) {
- return -1;
- }
- return 0;
- }
-};
-
-/// Comparison routine for qsort; sorts by max(fMaxMismatch{RGB})
-/// from largest to smallest.
-class CompareDiffMaxMismatches {
-public:
- static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) {
- uint32_t leftValue = MAX3(lhs->fMaxMismatchR,
- lhs->fMaxMismatchG,
- lhs->fMaxMismatchB);
- uint32_t rightValue = MAX3(rhs->fMaxMismatchR,
- rhs->fMaxMismatchG,
- rhs->fMaxMismatchB);
- if (leftValue < rightValue) {
- return 1;
- }
- if (rightValue < leftValue) {
- return -1;
- }
-
- return CompareDiffMeanMismatches::comparePixels(lhs, rhs);
- }
-};
-
-
-
-/// Parameterized routine to compute the color of a pixel in a difference image.
-typedef SkPMColor (*DiffMetricProc)(SkPMColor, SkPMColor);
-
-#if 0 // UNUSED
-static void expand_and_copy (int width, int height, SkBitmap** dest) {
- SkBitmap* temp = new SkBitmap ();
- temp->reset();
- temp->setConfig((*dest)->config(), width, height);
- temp->allocPixels();
- (*dest)->copyPixelsTo(temp->getPixels(), temp->getSize(),
- temp->rowBytes());
- *dest = temp;
-}
-#endif
-
-/// Returns true if the two buffers passed in are both non-NULL, and include
-/// exactly the same byte values (and identical lengths).
-static bool are_buffers_equal(SkData* skdata1, SkData* skdata2) {
- if ((NULL == skdata1) || (NULL == skdata2)) {
- return false;
- }
- if (skdata1->size() != skdata2->size()) {
- return false;
- }
- return (0 == memcmp(skdata1->data(), skdata2->data(), skdata1->size()));
-}
-
-/// Reads the file at the given path and returns its complete contents as an
-/// SkData object (or returns NULL on error).
-static SkData* read_file(const char* file_path) {
- SkFILEStream fileStream(file_path);
- if (!fileStream.isValid()) {
- SkDebugf("WARNING: could not open file <%s> for reading\n", file_path);
- return NULL;
- }
- size_t bytesInFile = fileStream.getLength();
- size_t bytesLeftToRead = bytesInFile;
-
- void* bufferStart = sk_malloc_throw(bytesInFile);
- char* bufferPointer = (char*)bufferStart;
- while (bytesLeftToRead > 0) {
- size_t bytesReadThisTime = fileStream.read(
- bufferPointer, bytesLeftToRead);
- if (0 == bytesReadThisTime) {
- SkDebugf("WARNING: error reading from <%s>\n", file_path);
- sk_free(bufferStart);
- return NULL;
- }
- bytesLeftToRead -= bytesReadThisTime;
- bufferPointer += bytesReadThisTime;
- }
- return SkData::NewFromMalloc(bufferStart, bytesInFile);
-}
-
-/// Decodes binary contents of baseFile and comparisonFile into
-/// diffRecord->fBaseBitmap and diffRecord->fComparisonBitmap.
-/// Returns true if that succeeds.
-static bool get_bitmaps (SkData* baseFileContents,
- SkData* comparisonFileContents,
- DiffRecord* diffRecord) {
- SkMemoryStream compareStream(comparisonFileContents->data(),
- comparisonFileContents->size());
- SkMemoryStream baseStream(baseFileContents->data(),
- baseFileContents->size());
-
- SkImageDecoder* codec = SkImageDecoder::Factory(&baseStream);
- if (NULL == codec) {
- SkDebugf("ERROR: no codec found for basePath <%s>\n",
- diffRecord->fBasePath.c_str());
- return false;
- }
-
- // In debug, the DLL will automatically be unloaded when this is deleted,
- // but that shouldn't be a problem in release mode.
- SkAutoTDelete<SkImageDecoder> ad(codec);
-
- baseStream.rewind();
- if (!codec->decode(&baseStream, diffRecord->fBaseBitmap,
- SkBitmap::kARGB_8888_Config,
- SkImageDecoder::kDecodePixels_Mode)) {
- SkDebugf("ERROR: codec failed for basePath <%s>\n",
- diffRecord->fBasePath.c_str());
- return false;
- }
-
- diffRecord->fBaseWidth = diffRecord->fBaseBitmap->width();
- diffRecord->fBaseHeight = diffRecord->fBaseBitmap->height();
-
- if (!codec->decode(&compareStream, diffRecord->fComparisonBitmap,
- SkBitmap::kARGB_8888_Config,
- SkImageDecoder::kDecodePixels_Mode)) {
- SkDebugf("ERROR: codec failed for comparisonPath <%s>\n",
- diffRecord->fComparisonPath.c_str());
- return false;
- }
-
- return true;
-}
-
-static bool get_bitmap_height_width(const SkString& path,
- int *height, int *width) {
- SkFILEStream stream(path.c_str());
- if (!stream.isValid()) {
- SkDebugf("ERROR: couldn't open file <%s>\n",
- path.c_str());
- return false;
- }
-
- SkImageDecoder* codec = SkImageDecoder::Factory(&stream);
- if (NULL == codec) {
- SkDebugf("ERROR: no codec found for <%s>\n",
- path.c_str());
- return false;
- }
-
- SkAutoTDelete<SkImageDecoder> ad(codec);
- SkBitmap bm;
-
- stream.rewind();
- if (!codec->decode(&stream, &bm,
- SkBitmap::kARGB_8888_Config,
- SkImageDecoder::kDecodePixels_Mode)) {
- SkDebugf("ERROR: codec failed for <%s>\n",
- path.c_str());
- return false;
- }
-
- *height = bm.height();
- *width = bm.width();
-
- return true;
-}
-
-// from gm - thanks to PNG, we need to force all pixels 100% opaque
-static void force_all_opaque(const SkBitmap& bitmap) {
- SkAutoLockPixels lock(bitmap);
- for (int y = 0; y < bitmap.height(); y++) {
- for (int x = 0; x < bitmap.width(); x++) {
- *bitmap.getAddr32(x, y) |= (SK_A32_MASK << SK_A32_SHIFT);
- }
- }
-}
-
-// from gm
-static bool write_bitmap(const SkString& path, const SkBitmap* bitmap) {
- SkBitmap copy;
- bitmap->copyTo(©, SkBitmap::kARGB_8888_Config);
- force_all_opaque(copy);
- return SkImageEncoder::EncodeFile(path.c_str(), copy,
- SkImageEncoder::kPNG_Type, 100);
-}
-
-// from gm
-static inline SkPMColor compute_diff_pmcolor(SkPMColor c0, SkPMColor c1) {
- int dr = SkGetPackedR32(c0) - SkGetPackedR32(c1);
- int dg = SkGetPackedG32(c0) - SkGetPackedG32(c1);
- int db = SkGetPackedB32(c0) - SkGetPackedB32(c1);
-
- return SkPackARGB32(0xFF, SkAbs32(dr), SkAbs32(dg), SkAbs32(db));
-}
-
-static inline bool colors_match_thresholded(SkPMColor c0, SkPMColor c1,
- const int threshold) {
- int da = SkGetPackedA32(c0) - SkGetPackedA32(c1);
- int dr = SkGetPackedR32(c0) - SkGetPackedR32(c1);
- int dg = SkGetPackedG32(c0) - SkGetPackedG32(c1);
- int db = SkGetPackedB32(c0) - SkGetPackedB32(c1);
-
- return ((SkAbs32(da) <= threshold) &&
- (SkAbs32(dr) <= threshold) &&
- (SkAbs32(dg) <= threshold) &&
- (SkAbs32(db) <= threshold));
-}
-
-// based on gm
-// Postcondition: when we exit this method, dr->fResult should have some value
-// other than kUnknown.
-static void compute_diff(DiffRecord* dr,
- DiffMetricProc diffFunction,
- const int colorThreshold) {
- SkAutoLockPixels alpDiff(*dr->fDifferenceBitmap);
- SkAutoLockPixels alpWhite(*dr->fWhiteBitmap);
-
- const int w = dr->fComparisonBitmap->width();
- const int h = dr->fComparisonBitmap->height();
- int mismatchedPixels = 0;
- int totalMismatchR = 0;
- int totalMismatchG = 0;
- int totalMismatchB = 0;
-
- if (w != dr->fBaseWidth || h != dr->fBaseHeight) {
- dr->fResult = kDifferentSizes;
- return;
- }
- // Accumulate fractionally different pixels, then divide out
- // # of pixels at the end.
- dr->fWeightedFraction = 0;
- for (int y = 0; y < h; y++) {
- for (int x = 0; x < w; x++) {
- SkPMColor c0 = *dr->fBaseBitmap->getAddr32(x, y);
- SkPMColor c1 = *dr->fComparisonBitmap->getAddr32(x, y);
- SkPMColor trueDifference = compute_diff_pmcolor(c0, c1);
- SkPMColor outputDifference = diffFunction(c0, c1);
- uint32_t thisR = SkGetPackedR32(trueDifference);
- uint32_t thisG = SkGetPackedG32(trueDifference);
- uint32_t thisB = SkGetPackedB32(trueDifference);
- totalMismatchR += thisR;
- totalMismatchG += thisG;
- totalMismatchB += thisB;
- // In HSV, value is defined as max RGB component.
- int value = MAX3(thisR, thisG, thisB);
- dr->fWeightedFraction += ((float) value) / 255;
- if (thisR > dr->fMaxMismatchR) {
- dr->fMaxMismatchR = thisR;
- }
- if (thisG > dr->fMaxMismatchG) {
- dr->fMaxMismatchG = thisG;
- }
- if (thisB > dr->fMaxMismatchB) {
- dr->fMaxMismatchB = thisB;
- }
- if (!colors_match_thresholded(c0, c1, colorThreshold)) {
- mismatchedPixels++;
- *dr->fDifferenceBitmap->getAddr32(x, y) = outputDifference;
- *dr->fWhiteBitmap->getAddr32(x, y) = PMCOLOR_WHITE;
- } else {
- *dr->fDifferenceBitmap->getAddr32(x, y) = 0;
- *dr->fWhiteBitmap->getAddr32(x, y) = PMCOLOR_BLACK;
- }
- }
- }
- if (0 == mismatchedPixels) {
- dr->fResult = kEqualPixels;
- return;
- }
- dr->fResult = kDifferentPixels;
- int pixelCount = w * h;
- dr->fFractionDifference = ((float) mismatchedPixels) / pixelCount;
- dr->fWeightedFraction /= pixelCount;
- dr->fAverageMismatchR = ((float) totalMismatchR) / pixelCount;
- dr->fAverageMismatchG = ((float) totalMismatchG) / pixelCount;
- dr->fAverageMismatchB = ((float) totalMismatchB) / pixelCount;
-}
-
-/// Return a copy of the "input" string, within which we have replaced all instances
-/// of oldSubstring with newSubstring.
-///
-/// TODO: If we like this, we should move it into the core SkString implementation,
-/// adding more checks and ample test cases, and paying more attention to efficiency.
-static SkString replace_all(const SkString &input,
- const char oldSubstring[], const char newSubstring[]) {
- SkString output;
- const char *input_cstr = input.c_str();
- const char *first_char = input_cstr;
- const char *match_char;
- int oldSubstringLen = strlen(oldSubstring);
- while (NULL != (match_char = strstr(first_char, oldSubstring))) {
- output.append(first_char, (match_char - first_char));
- output.append(newSubstring);
- first_char = match_char + oldSubstringLen;
- }
- output.append(first_char);
- return output;
-}
-
-static SkString filename_to_derived_filename (const SkString& filename,
- const char *suffix) {
- SkString diffName (filename);
- const char* cstring = diffName.c_str();
- int dotOffset = strrchr(cstring, '.') - cstring;
- diffName.remove(dotOffset, diffName.size() - dotOffset);
- diffName.append(suffix);
-
- // In case we recursed into subdirectories, replace slashes with something else
- // so the diffs will all be written into a single flat directory.
- diffName = replace_all(diffName, PATH_DIV_STR, "_");
- return diffName;
-}
-
-/// Given a image filename, returns the name of the file containing the
-/// associated difference image.
-static SkString filename_to_diff_filename (const SkString& filename) {
- return filename_to_derived_filename(filename, "-diff.png");
-}
-
-/// Given a image filename, returns the name of the file containing the
-/// "white" difference image.
-static SkString filename_to_white_filename (const SkString& filename) {
- return filename_to_derived_filename(filename, "-white.png");
-}
-
-class AutoCreateAndReleaseBitmaps {
-
-public:
- AutoCreateAndReleaseBitmaps(DiffRecord* drp)
- : fDrp(drp) {
- SkASSERT(drp != NULL);
- drp->fBaseBitmap = SkNEW(SkBitmap);
- drp->fComparisonBitmap = SkNEW(SkBitmap);
- drp->fDifferenceBitmap = SkNEW(SkBitmap);
- drp->fWhiteBitmap = SkNEW(SkBitmap);
- }
- ~AutoCreateAndReleaseBitmaps() {
- SkDELETE(fDrp->fBaseBitmap);
- fDrp->fBaseBitmap = NULL;
- SkDELETE(fDrp->fComparisonBitmap);
- fDrp->fComparisonBitmap = NULL;
- SkDELETE(fDrp->fDifferenceBitmap);
- fDrp->fDifferenceBitmap = NULL;
- SkDELETE(fDrp->fWhiteBitmap);
- fDrp->fWhiteBitmap = NULL;
- }
-
-private:
- DiffRecord* fDrp;
-};
-
-/// If outputDir.isEmpty(), don't write out diff files.
-static void create_and_write_diff_image(DiffRecord* drp,
- DiffMetricProc dmp,
- const int colorThreshold,
- const SkString& outputDir,
- const SkString& filename) {
- const int w = drp->fBaseWidth;
- const int h = drp->fBaseHeight;
- drp->fDifferenceBitmap->setConfig(SkBitmap::kARGB_8888_Config, w, h);
- drp->fDifferenceBitmap->allocPixels();
- drp->fWhiteBitmap->setConfig(SkBitmap::kARGB_8888_Config, w, h);
- drp->fWhiteBitmap->allocPixels();
-
- SkASSERT(kUnknown == drp->fResult);
- compute_diff(drp, dmp, colorThreshold);
- SkASSERT(kUnknown != drp->fResult);
-
- if ((kDifferentPixels == drp->fResult) && !outputDir.isEmpty()) {
- SkString differencePath (outputDir);
- differencePath.append(filename_to_diff_filename(filename));
- write_bitmap(differencePath, drp->fDifferenceBitmap);
- SkString whitePath (outputDir);
- whitePath.append(filename_to_white_filename(filename));
- write_bitmap(whitePath, drp->fWhiteBitmap);
- }
-}
-
/// Returns true if string contains any of these substrings.
static bool string_contains_any_of(const SkString& string,
const StringArray& substrings) {
@@ -798,6 +274,40 @@
return strcmp((*lhs)->c_str(), (*rhs)->c_str());
}
+class AutoReleasePixels {
+public:
+ AutoReleasePixels(DiffRecord* drp)
+ : fDrp(drp) {
+ SkASSERT(drp != NULL);
+ }
+ ~AutoReleasePixels() {
+ fDrp->fBase.fBitmap.setPixelRef(NULL);
+ fDrp->fComparison.fBitmap.setPixelRef(NULL);
+ fDrp->fDifference.fBitmap.setPixelRef(NULL);
+ fDrp->fWhite.fBitmap.setPixelRef(NULL);
+ }
+
+private:
+ DiffRecord* fDrp;
+};
+
+static void get_bounds(DiffResource& resource, const char* name) {
+ if (resource.fBitmap.empty() && !DiffResource::isStatusFailed(resource.fStatus)) {
+ SkAutoDataUnref fileBits(read_file(resource.fFullPath.c_str()));
+ if (NULL == fileBits) {
+ SkDebugf("WARNING: couldn't read %s file <%s>\n", name, resource.fFullPath.c_str());
+ resource.fStatus = DiffResource::kCouldNotRead_Status;
+ } else {
+ get_bitmap(fileBits, resource, SkImageDecoder::kDecodeBounds_Mode);
+ }
+ }
+}
+
+static void get_bounds(DiffRecord& drp) {
+ get_bounds(drp.fBase, "base");
+ get_bounds(drp.fComparison, "comparison");
+}
+
/// Creates difference images, returns the number that have a 0 metric.
/// If outputDir.isEmpty(), don't write out diff files.
static void create_diff_images (DiffMetricProc dmp,
@@ -809,6 +319,7 @@
const StringArray& matchSubstrings,
const StringArray& nomatchSubstrings,
bool recurseIntoSubdirs,
+ bool getBounds,
DiffSummary* summary) {
SkASSERT(!baseDir.isEmpty());
SkASSERT(!comparisonDir.isEmpty());
@@ -835,85 +346,143 @@
while (i < baseFiles.count() &&
j < comparisonFiles.count()) {
- SkString basePath (baseDir);
- basePath.append(*baseFiles[i]);
- SkString comparisonPath (comparisonDir);
- comparisonPath.append(*comparisonFiles[j]);
+ SkString basePath(baseDir);
+ SkString comparisonPath(comparisonDir);
- DiffRecord *drp = NULL;
- int v = strcmp(baseFiles[i]->c_str(),
- comparisonFiles[j]->c_str());
+ DiffRecord *drp = new DiffRecord;
+ int v = strcmp(baseFiles[i]->c_str(), comparisonFiles[j]->c_str());
if (v < 0) {
// in baseDir, but not in comparisonDir
- drp = new DiffRecord(*baseFiles[i], basePath, comparisonPath,
- kComparisonMissing);
+ drp->fResult = DiffRecord::kCouldNotCompare_Result;
+
+ basePath.append(*baseFiles[i]);
+ comparisonPath.append(*baseFiles[i]);
+
+ drp->fBase.fFilename = *baseFiles[i];
+ drp->fBase.fFullPath = basePath;
+ drp->fBase.fStatus = DiffResource::kExists_Status;
+
+ drp->fComparison.fFilename = *baseFiles[i];
+ drp->fComparison.fFullPath = comparisonPath;
+ drp->fComparison.fStatus = DiffResource::kDoesNotExist_Status;
+
++i;
} else if (v > 0) {
// in comparisonDir, but not in baseDir
- drp = new DiffRecord(*comparisonFiles[j], basePath, comparisonPath,
- kBaseMissing);
+ drp->fResult = DiffRecord::kCouldNotCompare_Result;
+
+ basePath.append(*comparisonFiles[j]);
+ comparisonPath.append(*comparisonFiles[j]);
+
+ drp->fBase.fFilename = *comparisonFiles[j];
+ drp->fBase.fFullPath = basePath;
+ drp->fBase.fStatus = DiffResource::kDoesNotExist_Status;
+
+ drp->fComparison.fFilename = *comparisonFiles[j];
+ drp->fComparison.fFullPath = comparisonPath;
+ drp->fComparison.fStatus = DiffResource::kExists_Status;
+
++j;
} else {
// Found the same filename in both baseDir and comparisonDir.
- drp = new DiffRecord(*baseFiles[i], basePath, comparisonPath);
- SkASSERT(kUnknown == drp->fResult);
+ SkASSERT(DiffRecord::kUnknown_Result == drp->fResult);
- SkData* baseFileBits = NULL;
- SkData* comparisonFileBits = NULL;
- if (NULL == (baseFileBits = read_file(basePath.c_str()))) {
- SkDebugf("WARNING: couldn't read base file <%s>\n",
- basePath.c_str());
- drp->fResult = kBaseMissing;
- } else if (NULL == (comparisonFileBits = read_file(comparisonPath.c_str()))) {
- SkDebugf("WARNING: couldn't read comparison file <%s>\n",
- comparisonPath.c_str());
- drp->fResult = kComparisonMissing;
+ basePath.append(*baseFiles[i]);
+ comparisonPath.append(*comparisonFiles[j]);
+
+ drp->fBase.fFilename = *baseFiles[i];
+ drp->fBase.fFullPath = basePath;
+ drp->fBase.fStatus = DiffResource::kExists_Status;
+
+ drp->fComparison.fFilename = *comparisonFiles[j];
+ drp->fComparison.fFullPath = comparisonPath;
+ drp->fComparison.fStatus = DiffResource::kExists_Status;
+
+ SkAutoDataUnref baseFileBits(read_file(drp->fBase.fFullPath.c_str()));
+ if (NULL != baseFileBits) {
+ drp->fBase.fStatus = DiffResource::kRead_Status;
+ }
+ SkAutoDataUnref comparisonFileBits(read_file(drp->fComparison.fFullPath.c_str()));
+ if (NULL != comparisonFileBits) {
+ drp->fComparison.fStatus = DiffResource::kRead_Status;
+ }
+ if (NULL == baseFileBits || NULL == comparisonFileBits) {
+ if (NULL == baseFileBits) {
+ drp->fBase.fStatus = DiffResource::kCouldNotRead_Status;
+ }
+ if (NULL == comparisonFileBits) {
+ drp->fComparison.fStatus = DiffResource::kCouldNotRead_Status;
+ }
+ drp->fResult = DiffRecord::kCouldNotCompare_Result;
+
+ } else if (are_buffers_equal(baseFileBits, comparisonFileBits)) {
+ drp->fResult = DiffRecord::kEqualBits_Result;
+
} else {
- if (are_buffers_equal(baseFileBits, comparisonFileBits)) {
- drp->fResult = kEqualBits;
+ AutoReleasePixels arp(drp);
+ get_bitmap(baseFileBits, drp->fBase, SkImageDecoder::kDecodePixels_Mode);
+ get_bitmap(comparisonFileBits, drp->fComparison,
+ SkImageDecoder::kDecodePixels_Mode);
+ if (DiffResource::kDecoded_Status == drp->fBase.fStatus &&
+ DiffResource::kDecoded_Status == drp->fComparison.fStatus) {
+ create_and_write_diff_image(drp, dmp, colorThreshold,
+ outputDir, drp->fBase.fFilename);
} else {
- AutoCreateAndReleaseBitmaps createBitmaps(drp);
- if (get_bitmaps(baseFileBits, comparisonFileBits, drp)) {
- create_and_write_diff_image(drp, dmp, colorThreshold,
- outputDir, *baseFiles[i]);
- } else {
- drp->fResult = kDifferentOther;
- }
+ drp->fResult = DiffRecord::kCouldNotCompare_Result;
}
}
- if (baseFileBits) {
- baseFileBits->unref();
- }
- if (comparisonFileBits) {
- comparisonFileBits->unref();
- }
+
++i;
++j;
}
- SkASSERT(kUnknown != drp->fResult);
+
+ if (getBounds) {
+ get_bounds(*drp);
+ }
+ SkASSERT(DiffRecord::kUnknown_Result != drp->fResult);
differences->push(drp);
summary->add(drp);
}
for (; i < baseFiles.count(); ++i) {
// files only in baseDir
- SkString basePath (baseDir);
- basePath.append(*baseFiles[i]);
- SkString comparisonPath;
- DiffRecord *drp = new DiffRecord(*baseFiles[i], basePath,
- comparisonPath, kComparisonMissing);
+ DiffRecord *drp = new DiffRecord();
+ drp->fBase.fFilename = *baseFiles[i];
+ drp->fBase.fFullPath = baseDir;
+ drp->fBase.fFullPath.append(drp->fBase.fFilename);
+ drp->fBase.fStatus = DiffResource::kExists_Status;
+
+ drp->fComparison.fFilename = *baseFiles[i];
+ drp->fComparison.fFullPath = comparisonDir;
+ drp->fComparison.fFullPath.append(drp->fComparison.fFilename);
+ drp->fComparison.fStatus = DiffResource::kDoesNotExist_Status;
+
+ drp->fResult = DiffRecord::kCouldNotCompare_Result;
+ if (getBounds) {
+ get_bounds(*drp);
+ }
differences->push(drp);
summary->add(drp);
}
for (; j < comparisonFiles.count(); ++j) {
// files only in comparisonDir
- SkString basePath;
- SkString comparisonPath(comparisonDir);
- comparisonPath.append(*comparisonFiles[j]);
- DiffRecord *drp = new DiffRecord(*comparisonFiles[j], basePath,
- comparisonPath, kBaseMissing);
+ DiffRecord *drp = new DiffRecord();
+ drp->fBase.fFilename = *comparisonFiles[j];
+ drp->fBase.fFullPath = baseDir;
+ drp->fBase.fFullPath.append(drp->fBase.fFilename);
+ drp->fBase.fStatus = DiffResource::kDoesNotExist_Status;
+
+ drp->fComparison.fFilename = *comparisonFiles[j];
+ drp->fComparison.fFullPath = comparisonDir;
+ drp->fComparison.fFullPath.append(drp->fComparison.fFilename);
+ drp->fComparison.fStatus = DiffResource::kExists_Status;
+
+ drp->fResult = DiffRecord::kCouldNotCompare_Result;
+ if (getBounds) {
+ get_bounds(*drp);
+ }
differences->push(drp);
summary->add(drp);
}
@@ -922,331 +491,11 @@
release_file_list(&comparisonFiles);
}
-/// Make layout more consistent by scaling image to 240 height, 360 width,
-/// or natural size, whichever is smallest.
-static int compute_image_height (int height, int width) {
- int retval = 240;
- if (height < retval) {
- retval = height;
- }
- float scale = (float) retval / height;
- if (width * scale > 360) {
- scale = (float) 360 / width;
- retval = static_cast<int>(height * scale);
- }
- return retval;
-}
-
-static void print_table_header (SkFILEWStream* stream,
- const int matchCount,
- const int colorThreshold,
- const RecordArray& differences,
- const SkString &baseDir,
- const SkString &comparisonDir,
- bool doOutputDate=false) {
- stream->writeText("<table>\n");
- stream->writeText("<tr><th>");
- stream->writeText("select image</th>\n<th>");
- if (doOutputDate) {
- SkTime::DateTime dt;
- SkTime::GetDateTime(&dt);
- stream->writeText("SkDiff run at ");
- stream->writeDecAsText(dt.fHour);
- stream->writeText(":");
- if (dt.fMinute < 10) {
- stream->writeText("0");
- }
- stream->writeDecAsText(dt.fMinute);
- stream->writeText(":");
- if (dt.fSecond < 10) {
- stream->writeText("0");
- }
- stream->writeDecAsText(dt.fSecond);
- stream->writeText("<br>");
- }
- stream->writeDecAsText(matchCount);
- stream->writeText(" of ");
- stream->writeDecAsText(differences.count());
- stream->writeText(" images matched ");
- if (colorThreshold == 0) {
- stream->writeText("exactly");
- } else {
- stream->writeText("within ");
- stream->writeDecAsText(colorThreshold);
- stream->writeText(" color units per component");
- }
- stream->writeText(".<br>");
- stream->writeText("</th>\n<th>");
- stream->writeText("every different pixel shown in white");
- stream->writeText("</th>\n<th>");
- stream->writeText("color difference at each pixel");
- stream->writeText("</th>\n<th>baseDir: ");
- stream->writeText(baseDir.c_str());
- stream->writeText("</th>\n<th>comparisonDir: ");
- stream->writeText(comparisonDir.c_str());
- stream->writeText("</th>\n");
- stream->writeText("</tr>\n");
-}
-
-static void print_pixel_count (SkFILEWStream* stream,
- const DiffRecord& diff) {
- stream->writeText("<br>(");
- stream->writeDecAsText(static_cast<int>(diff.fFractionDifference *
- diff.fBaseWidth *
- diff.fBaseHeight));
- stream->writeText(" pixels)");
-/*
- stream->writeDecAsText(diff.fWeightedFraction *
- diff.fBaseWidth *
- diff.fBaseHeight);
- stream->writeText(" weighted pixels)");
-*/
-}
-
-static void print_checkbox_cell (SkFILEWStream* stream,
- const DiffRecord& diff) {
- stream->writeText("<td><input type=\"checkbox\" name=\"");
- stream->writeText(diff.fFilename.c_str());
- stream->writeText("\" checked=\"yes\"></td>");
-}
-
-static void print_label_cell (SkFILEWStream* stream,
- const DiffRecord& diff) {
- char metricBuf [20];
-
- stream->writeText("<td><b>");
- stream->writeText(diff.fFilename.c_str());
- stream->writeText("</b><br>");
- switch (diff.fResult) {
- case kEqualBits:
- SkDEBUGFAIL("should not encounter DiffRecord with kEqualBits here");
- return;
- case kEqualPixels:
- SkDEBUGFAIL("should not encounter DiffRecord with kEqualPixels here");
- return;
- case kDifferentSizes:
- stream->writeText("Image sizes differ</td>");
- return;
- case kDifferentPixels:
- sprintf(metricBuf, "%12.4f%%", 100 * diff.fFractionDifference);
- stream->writeText(metricBuf);
- stream->writeText(" of pixels differ");
- stream->writeText("\n (");
- sprintf(metricBuf, "%12.4f%%", 100 * diff.fWeightedFraction);
- stream->writeText(metricBuf);
- stream->writeText(" weighted)");
- // Write the actual number of pixels that differ if it's < 1%
- if (diff.fFractionDifference < 0.01) {
- print_pixel_count(stream, diff);
- }
- stream->writeText("<br>Average color mismatch ");
- stream->writeDecAsText(static_cast<int>(MAX3(diff.fAverageMismatchR,
- diff.fAverageMismatchG,
- diff.fAverageMismatchB)));
- stream->writeText("<br>Max color mismatch ");
- stream->writeDecAsText(MAX3(diff.fMaxMismatchR,
- diff.fMaxMismatchG,
- diff.fMaxMismatchB));
- stream->writeText("</td>");
- break;
- case kDifferentOther:
- stream->writeText("Files differ; unable to parse one or both files</td>");
- return;
- case kBaseMissing:
- stream->writeText("Missing from baseDir</td>");
- return;
- case kComparisonMissing:
- stream->writeText("Missing from comparisonDir</td>");
- return;
- default:
- SkDEBUGFAIL("encountered DiffRecord with unknown result type");
- return;
- }
-}
-
-static void print_image_cell (SkFILEWStream* stream,
- const SkString& path,
- int height) {
- stream->writeText("<td><a href=\"");
- stream->writeText(path.c_str());
- stream->writeText("\"><img src=\"");
- stream->writeText(path.c_str());
- stream->writeText("\" height=\"");
- stream->writeDecAsText(height);
- stream->writeText("px\"></a></td>");
-}
-
-#if 0 // UNUSED
-static void print_text_cell (SkFILEWStream* stream, const char* text) {
- stream->writeText("<td align=center>");
- if (NULL != text) {
- stream->writeText(text);
- }
- stream->writeText("</td>");
-}
-#endif
-
-static void print_diff_with_missing_file(SkFILEWStream* stream,
- DiffRecord& diff,
- const SkString& relativePath) {
- stream->writeText("<tr>\n");
- print_checkbox_cell(stream, diff);
- print_label_cell(stream, diff);
- stream->writeText("<td>N/A</td>");
- stream->writeText("<td>N/A</td>");
- if (kBaseMissing != diff.fResult) {
- int h, w;
- if (!get_bitmap_height_width(diff.fBasePath, &h, &w)) {
- stream->writeText("<td>N/A</td>");
- } else {
- int height = compute_image_height(h, w);
- if (!diff.fBasePath.startsWith(PATH_DIV_STR)) {
- diff.fBasePath.prepend(relativePath);
- }
- print_image_cell(stream, diff.fBasePath, height);
- }
- } else {
- stream->writeText("<td>N/A</td>");
- }
- if (kComparisonMissing != diff.fResult) {
- int h, w;
- if (!get_bitmap_height_width(diff.fComparisonPath, &h, &w)) {
- stream->writeText("<td>N/A</td>");
- } else {
- int height = compute_image_height(h, w);
- if (!diff.fComparisonPath.startsWith(PATH_DIV_STR)) {
- diff.fComparisonPath.prepend(relativePath);
- }
- print_image_cell(stream, diff.fComparisonPath, height);
- }
- } else {
- stream->writeText("<td>N/A</td>");
- }
- stream->writeText("</tr>\n");
- stream->flush();
-}
-
-static void print_diff_page (const int matchCount,
- const int colorThreshold,
- const RecordArray& differences,
- const SkString& baseDir,
- const SkString& comparisonDir,
- const SkString& outputDir) {
-
- SkASSERT(!baseDir.isEmpty());
- SkASSERT(!comparisonDir.isEmpty());
- SkASSERT(!outputDir.isEmpty());
-
- SkString outputPath (outputDir);
- outputPath.append("index.html");
- //SkFILEWStream outputStream ("index.html");
- SkFILEWStream outputStream (outputPath.c_str());
-
- // Need to convert paths from relative-to-cwd to relative-to-outputDir
- // FIXME this doesn't work if there are '..' inside the outputDir
-
- bool isPathAbsolute = false;
- // On Windows or Linux, a path starting with PATH_DIV_CHAR is absolute.
- if (outputDir.size() > 0 && PATH_DIV_CHAR == outputDir[0]) {
- isPathAbsolute = true;
- }
-#ifdef SK_BUILD_FOR_WIN32
- // On Windows, absolute paths can also start with "x:", where x is any
- // drive letter.
- if (outputDir.size() > 1 && ':' == outputDir[1]) {
- isPathAbsolute = true;
- }
-#endif
-
- SkString relativePath;
- if (!isPathAbsolute) {
- unsigned int ui;
- for (ui = 0; ui < outputDir.size(); ui++) {
- if (outputDir[ui] == PATH_DIV_CHAR) {
- relativePath.append(".." PATH_DIV_STR);
- }
- }
- }
-
- outputStream.writeText(
- "<html>\n<head>\n"
- "<script src=\"https://ajax.googleapis.com/ajax/"
- "libs/jquery/1.7.2/jquery.min.js\"></script>\n"
- "<script type=\"text/javascript\">\n"
- "function generateCheckedList() {\n"
- "var boxes = $(\":checkbox:checked\");\n"
- "var fileCmdLineString = '';\n"
- "var fileMultiLineString = '';\n"
- "for (var i = 0; i < boxes.length; i++) {\n"
- "fileMultiLineString += boxes[i].name + '<br>';\n"
- "fileCmdLineString += boxes[i].name + ' ';\n"
- "}\n"
- "$(\"#checkedList\").html(fileCmdLineString + "
- "'<br><br>' + fileMultiLineString);\n"
- "}\n"
- "</script>\n</head>\n<body>\n");
- print_table_header(&outputStream, matchCount, colorThreshold, differences,
- baseDir, comparisonDir);
- int i;
- for (i = 0; i < differences.count(); i++) {
- DiffRecord* diff = differences[i];
-
- switch (diff->fResult) {
- // Cases in which there is no diff to report.
- case kEqualBits:
- case kEqualPixels:
- continue;
- // Cases in which we want a detailed pixel diff.
- case kDifferentPixels:
- break;
- // Cases in which the files differed, but we can't display the diff.
- case kDifferentSizes:
- case kDifferentOther:
- case kBaseMissing:
- case kComparisonMissing:
- print_diff_with_missing_file(&outputStream, *diff, relativePath);
- continue;
- default:
- SkDEBUGFAIL("encountered DiffRecord with unknown result type");
- continue;
- }
-
- if (!diff->fBasePath.startsWith(PATH_DIV_STR)) {
- diff->fBasePath.prepend(relativePath);
- }
- if (!diff->fComparisonPath.startsWith(PATH_DIV_STR)) {
- diff->fComparisonPath.prepend(relativePath);
- }
-
- int height = compute_image_height(diff->fBaseHeight, diff->fBaseWidth);
- outputStream.writeText("<tr>\n");
- print_checkbox_cell(&outputStream, *diff);
- print_label_cell(&outputStream, *diff);
- print_image_cell(&outputStream,
- filename_to_white_filename(diff->fFilename), height);
- print_image_cell(&outputStream,
- filename_to_diff_filename(diff->fFilename), height);
- print_image_cell(&outputStream, diff->fBasePath, height);
- print_image_cell(&outputStream, diff->fComparisonPath, height);
- outputStream.writeText("</tr>\n");
- outputStream.flush();
- }
- outputStream.writeText(
- "</table>\n"
- "<input type=\"button\" "
- "onclick=\"generateCheckedList()\" "
- "value=\"Create Rebaseline List\">\n"
- "<div id=\"checkedList\"></div>\n"
- "</body>\n</html>\n");
- outputStream.flush();
-}
-
static void usage (char * argv0) {
SkDebugf("Skia baseline image diff tool\n");
SkDebugf("\n"
"Usage: \n"
-" %s <baseDir> <comparisonDir> [outputDir] \n"
-, argv0, argv0);
+" %s <baseDir> <comparisonDir> [outputDir] \n", argv0);
SkDebugf(
"\nArguments:"
"\n --failonresult <result>: After comparing all file pairs, exit with nonzero"
@@ -1255,6 +504,8 @@
"\n This flag may be repeated, in which case the"
"\n return code will be the number of fail pairs"
"\n yielding ANY of these results."
+"\n --failonstatus <baseStatus> <comparisonStatus>: exit with nonzero return"
+"\n code if any file pairs yielded this status."
"\n --help: display this info"
"\n --listfilenames: list all filenames for each result type in stdout"
"\n --match <substring>: compare files whose filenames contain this substring;"
@@ -1308,17 +559,59 @@
RecordArray differences;
DiffSummary summary;
- bool failOnResultType[kNumResultTypes];
- for (int i = 0; i < kNumResultTypes; i++) {
+ bool failOnResultType[DiffRecord::kResultCount];
+ for (int i = 0; i < DiffRecord::kResultCount; i++) {
failOnResultType[i] = false;
}
+ bool failOnStatusType[DiffResource::kStatusCount][DiffResource::kStatusCount];
+ for (int base = 0; base < DiffResource::kStatusCount; ++base) {
+ for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
+ failOnStatusType[base][comparison] = false;
+ }
+ }
+
int i;
int numUnflaggedArguments = 0;
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--failonresult")) {
- Result type = getResultByName(argv[++i]);
- failOnResultType[type] = true;
+ if (argc == ++i) {
+ SkDebugf("failonresult expects one argument.\n");
+ continue;
+ }
+ DiffRecord::Result type = DiffRecord::getResultByName(argv[i]);
+ if (type != DiffRecord::kResultCount) {
+ failOnResultType[type] = true;
+ } else {
+ SkDebugf("ignoring unrecognized result <%s>\n", argv[i]);
+ }
+ continue;
+ }
+ if (!strcmp(argv[i], "--failonstatus")) {
+ if (argc == ++i) {
+ SkDebugf("failonstatus missing base status.\n");
+ continue;
+ }
+ bool baseStatuses[DiffResource::kStatusCount];
+ if (!DiffResource::getMatchingStatuses(argv[i], baseStatuses)) {
+ SkDebugf("unrecognized base status <%s>\n", argv[i]);
+ }
+
+ if (argc == ++i) {
+ SkDebugf("failonstatus missing comparison status.\n");
+ continue;
+ }
+ bool comparisonStatuses[DiffResource::kStatusCount];
+ if (!DiffResource::getMatchingStatuses(argv[i], comparisonStatuses)) {
+ SkDebugf("unrecognized comarison status <%s>\n", argv[i]);
+ }
+
+ for (int base = 0; base < DiffResource::kStatusCount; ++base) {
+ for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
+ failOnStatusType[base][comparison] |=
+ baseStatuses[base] && comparisonStatuses[comparison];
+ }
+ }
continue;
}
if (!strcmp(argv[i], "--help")) {
@@ -1431,8 +724,9 @@
create_diff_images(diffProc, colorThreshold, &differences,
baseDir, comparisonDir, outputDir,
- matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, &summary);
- summary.print(listFilenames, failOnResultType);
+ matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, generateDiffs,
+ &summary);
+ summary.print(listFilenames, failOnResultType, failOnStatusType);
if (differences.count()) {
qsort(differences.begin(), differences.count(),
@@ -1451,11 +745,20 @@
nomatchSubstrings.deleteAll();
int num_failing_results = 0;
- for (int i = 0; i < kNumResultTypes; i++) {
+ for (int i = 0; i < DiffRecord::kResultCount; i++) {
if (failOnResultType[i]) {
num_failing_results += summary.fResultsOfType[i].count();
}
}
+ if (!failOnResultType[DiffRecord::kCouldNotCompare_Result]) {
+ for (int base = 0; base < DiffResource::kStatusCount; ++base) {
+ for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
+ if (failOnStatusType[base][comparison]) {
+ num_failing_results += summary.fStatusOfType[base][comparison].count();
+ }
+ }
+ }
+ }
// On Linux (and maybe other platforms too), any results outside of the
// range [0...255] are wrapped (mod 256). Do the conversion ourselves, to