Update skdiff.
https://codereview.appspot.com/6850115/ git-svn-id: http://skia.googlecode.com/svn/trunk@6681 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
b959a9cbd9
commit
e3c8ddfd03
@ -28,7 +28,31 @@
|
||||
'target_name': 'skdiff',
|
||||
'type': 'executable',
|
||||
'sources': [
|
||||
'../tools/skdiff.cpp',
|
||||
'../tools/skdiff.h',
|
||||
'../tools/skdiff_html.cpp',
|
||||
'../tools/skdiff_html.h',
|
||||
'../tools/skdiff_main.cpp',
|
||||
'../tools/skdiff_utils.cpp',
|
||||
'../tools/skdiff_utils.h',
|
||||
],
|
||||
'dependencies': [
|
||||
'skia_base_libs.gyp:skia_base_libs',
|
||||
'effects.gyp:effects',
|
||||
'images.gyp:images',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'skimagediff',
|
||||
'type': 'executable',
|
||||
'sources': [
|
||||
'../tools/skdiff.cpp',
|
||||
'../tools/skdiff.h',
|
||||
'../tools/skdiff_html.cpp',
|
||||
'../tools/skdiff_html.h',
|
||||
'../tools/skdiff_image.cpp',
|
||||
'../tools/skdiff_utils.cpp',
|
||||
'../tools/skdiff_utils.h',
|
||||
],
|
||||
'dependencies': [
|
||||
'skia_base_libs.gyp:skia_base_libs',
|
||||
|
221
tools/skdiff.cpp
Normal file
221
tools/skdiff.cpp
Normal file
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "skdiff.h"
|
||||
#include "SkBitmap.h"
|
||||
#include "SkColor.h"
|
||||
#include "SkColorPriv.h"
|
||||
#include "SkTypes.h"
|
||||
|
||||
/*static*/ char const * const DiffRecord::ResultNames[DiffRecord::kResultCount] = {
|
||||
"EqualBits",
|
||||
"EqualPixels",
|
||||
"DifferentPixels",
|
||||
"DifferentSizes",
|
||||
"CouldNotCompare",
|
||||
"Unknown",
|
||||
};
|
||||
|
||||
DiffRecord::Result DiffRecord::getResultByName(const char *name) {
|
||||
for (int result = 0; result < DiffRecord::kResultCount; ++result) {
|
||||
if (0 == strcmp(DiffRecord::ResultNames[result], name)) {
|
||||
return static_cast<DiffRecord::Result>(result);
|
||||
}
|
||||
}
|
||||
return DiffRecord::kResultCount;
|
||||
}
|
||||
|
||||
static char const * const ResultDescriptions[DiffRecord::kResultCount] = {
|
||||
"contain exactly the same bits",
|
||||
"contain the same pixel values, but not the same bits",
|
||||
"have identical dimensions but some differing pixels",
|
||||
"have differing dimensions",
|
||||
"could not be compared",
|
||||
"not compared yet",
|
||||
};
|
||||
|
||||
const char* DiffRecord::getResultDescription(DiffRecord::Result result) {
|
||||
return ResultDescriptions[result];
|
||||
}
|
||||
|
||||
/*static*/ char const * const DiffResource::StatusNames[DiffResource::kStatusCount] = {
|
||||
"Decoded",
|
||||
"CouldNotDecode",
|
||||
|
||||
"Read",
|
||||
"CouldNotRead",
|
||||
|
||||
"Exists",
|
||||
"DoesNotExist",
|
||||
|
||||
"Specified",
|
||||
"Unspecified",
|
||||
|
||||
"Unknown",
|
||||
};
|
||||
|
||||
DiffResource::Status DiffResource::getStatusByName(const char *name) {
|
||||
for (int status = 0; status < DiffResource::kStatusCount; ++status) {
|
||||
if (0 == strcmp(DiffResource::StatusNames[status], name)) {
|
||||
return static_cast<DiffResource::Status>(status);
|
||||
}
|
||||
}
|
||||
return DiffResource::kStatusCount;
|
||||
}
|
||||
|
||||
static char const * const StatusDescriptions[DiffResource::kStatusCount] = {
|
||||
"decoded",
|
||||
"could not be decoded",
|
||||
|
||||
"read",
|
||||
"could not be read",
|
||||
|
||||
"found",
|
||||
"not found",
|
||||
|
||||
"specified",
|
||||
"unspecified",
|
||||
|
||||
"unknown",
|
||||
};
|
||||
|
||||
const char* DiffResource::getStatusDescription(DiffResource::Status status) {
|
||||
return StatusDescriptions[status];
|
||||
}
|
||||
|
||||
bool DiffResource::isStatusFailed(DiffResource::Status status) {
|
||||
return DiffResource::kCouldNotDecode_Status == status ||
|
||||
DiffResource::kCouldNotRead_Status == status ||
|
||||
DiffResource::kDoesNotExist_Status == status ||
|
||||
DiffResource::kUnspecified_Status == status ||
|
||||
DiffResource::kUnknown_Status == status;
|
||||
}
|
||||
|
||||
bool DiffResource::getMatchingStatuses(char* selector, bool statuses[kStatusCount]) {
|
||||
if (!strcmp(selector, "any")) {
|
||||
for (int statusIndex = 0; statusIndex < kStatusCount; ++statusIndex) {
|
||||
statuses[statusIndex] = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int statusIndex = 0; statusIndex < kStatusCount; ++statusIndex) {
|
||||
statuses[statusIndex] = false;
|
||||
}
|
||||
|
||||
static const char kDelimiterChar = ',';
|
||||
bool understood = true;
|
||||
while (true) {
|
||||
char* delimiterPtr = strchr(selector, kDelimiterChar);
|
||||
|
||||
if (delimiterPtr) {
|
||||
*delimiterPtr = '\0';
|
||||
}
|
||||
|
||||
if (!strcmp(selector, "failed")) {
|
||||
for (int statusIndex = 0; statusIndex < kStatusCount; ++statusIndex) {
|
||||
Status status = static_cast<Status>(statusIndex);
|
||||
statuses[statusIndex] |= isStatusFailed(status);
|
||||
}
|
||||
} else {
|
||||
Status status = getStatusByName(selector);
|
||||
if (status == kStatusCount) {
|
||||
understood = false;
|
||||
} else {
|
||||
statuses[status] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!delimiterPtr) {
|
||||
break;
|
||||
}
|
||||
|
||||
*delimiterPtr = kDelimiterChar;
|
||||
selector = delimiterPtr + 1;
|
||||
}
|
||||
return understood;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
const SkPMColor PMCOLOR_WHITE = SkPreMultiplyColor(SK_ColorWHITE);
|
||||
const SkPMColor PMCOLOR_BLACK = SkPreMultiplyColor(SK_ColorBLACK);
|
||||
|
||||
void compute_diff(DiffRecord* dr, DiffMetricProc diffFunction, const int colorThreshold) {
|
||||
const int w = dr->fComparison.fBitmap.width();
|
||||
const int h = dr->fComparison.fBitmap.height();
|
||||
if (w != dr->fBase.fBitmap.width() || h != dr->fBase.fBitmap.height()) {
|
||||
dr->fResult = DiffRecord::kDifferentSizes_Result;
|
||||
return;
|
||||
}
|
||||
|
||||
SkAutoLockPixels alpDiff(dr->fDifference.fBitmap);
|
||||
SkAutoLockPixels alpWhite(dr->fWhite.fBitmap);
|
||||
int mismatchedPixels = 0;
|
||||
int totalMismatchR = 0;
|
||||
int totalMismatchG = 0;
|
||||
int totalMismatchB = 0;
|
||||
|
||||
// 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->fBase.fBitmap.getAddr32(x, y);
|
||||
SkPMColor c1 = *dr->fComparison.fBitmap.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->fDifference.fBitmap.getAddr32(x, y) = outputDifference;
|
||||
*dr->fWhite.fBitmap.getAddr32(x, y) = PMCOLOR_WHITE;
|
||||
} else {
|
||||
*dr->fDifference.fBitmap.getAddr32(x, y) = 0;
|
||||
*dr->fWhite.fBitmap.getAddr32(x, y) = PMCOLOR_BLACK;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (0 == mismatchedPixels) {
|
||||
dr->fResult = DiffRecord::kEqualPixels_Result;
|
||||
return;
|
||||
}
|
||||
dr->fResult = DiffRecord::kDifferentPixels_Result;
|
||||
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;
|
||||
}
|
265
tools/skdiff.h
Normal file
265
tools/skdiff.h
Normal file
@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef skdiff_DEFINED
|
||||
#define skdiff_DEFINED
|
||||
|
||||
#include "SkBitmap.h"
|
||||
#include "SkColor.h"
|
||||
#include "SkColorPriv.h"
|
||||
#include "SkString.h"
|
||||
#include "SkTDArray.h"
|
||||
|
||||
#if SK_BUILD_FOR_WIN32
|
||||
#define PATH_DIV_STR "\\"
|
||||
#define PATH_DIV_CHAR '\\'
|
||||
#else
|
||||
#define PATH_DIV_STR "/"
|
||||
#define PATH_DIV_CHAR '/'
|
||||
#endif
|
||||
|
||||
#define MAX2(a,b) (((b) < (a)) ? (a) : (b))
|
||||
#define MAX3(a,b,c) (((b) < (a)) ? MAX2((a), (c)) : MAX2((b), (c)))
|
||||
|
||||
|
||||
struct DiffResource {
|
||||
enum Status {
|
||||
/** The resource was specified, exists, read, and decoded. */
|
||||
kDecoded_Status,
|
||||
/** The resource was specified, exists, read, but could not be decoded. */
|
||||
kCouldNotDecode_Status,
|
||||
|
||||
/** The resource was specified, exists, and read. */
|
||||
kRead_Status,
|
||||
/** The resource was specified, exists, but could not be read. */
|
||||
kCouldNotRead_Status,
|
||||
|
||||
/** The resource was specified and exists. */
|
||||
kExists_Status,
|
||||
/** The resource was specified, but does not exist. */
|
||||
kDoesNotExist_Status,
|
||||
|
||||
/** The resource was specified. */
|
||||
kSpecified_Status,
|
||||
/** The resource was not specified. */
|
||||
kUnspecified_Status,
|
||||
|
||||
/** Nothing is yet known about the resource. */
|
||||
kUnknown_Status,
|
||||
|
||||
/** NOT A VALID VALUE -- used to set up arrays and to represent an unknown value. */
|
||||
kStatusCount
|
||||
};
|
||||
static char const * const StatusNames[DiffResource::kStatusCount];
|
||||
|
||||
/** Returns the Status with this name.
|
||||
* If there is no Status with this name, returns kStatusCount.
|
||||
*/
|
||||
static Status getStatusByName(const char *name);
|
||||
|
||||
/** Returns a text description of the given Status type. */
|
||||
static const char *getStatusDescription(Status status);
|
||||
|
||||
/** Returns true if the Status indicates some kind of failure. */
|
||||
static bool isStatusFailed(Status status);
|
||||
|
||||
/** Sets statuses[i] if it is implied by selector, unsets it if not.
|
||||
* Selector may be a comma delimited list of status names, "any", or "failed".
|
||||
* Returns true if the selector was entirely understood, false otherwise.
|
||||
*/
|
||||
static bool getMatchingStatuses(char* selector, bool statuses[kStatusCount]);
|
||||
|
||||
DiffResource() : fFilename(), fFullPath(), fBitmap(), fStatus(kUnknown_Status) { };
|
||||
|
||||
/** If isEmpty() indicates no filename available. */
|
||||
SkString fFilename;
|
||||
/** If isEmpty() indicates no path available. */
|
||||
SkString fFullPath;
|
||||
/** If empty() indicates the bitmap could not be created. */
|
||||
SkBitmap fBitmap;
|
||||
Status fStatus;
|
||||
};
|
||||
|
||||
struct DiffRecord {
|
||||
|
||||
// Result of comparison for each pair of files.
|
||||
// Listed from "better" to "worse", for sorting of results.
|
||||
enum Result {
|
||||
kEqualBits_Result,
|
||||
kEqualPixels_Result,
|
||||
kDifferentPixels_Result,
|
||||
kDifferentSizes_Result,
|
||||
kCouldNotCompare_Result,
|
||||
kUnknown_Result,
|
||||
|
||||
kResultCount // NOT A VALID VALUE--used to set up arrays. Must be last.
|
||||
};
|
||||
static char const * const ResultNames[DiffRecord::kResultCount];
|
||||
|
||||
/** Returns the Result with this name.
|
||||
* If there is no Result with this name, returns kResultCount.
|
||||
*/
|
||||
static Result getResultByName(const char *name);
|
||||
|
||||
/** Returns a text description of the given Result type. */
|
||||
static const char *getResultDescription(Result result);
|
||||
|
||||
DiffRecord()
|
||||
: fBase()
|
||||
, fComparison()
|
||||
, fDifference()
|
||||
, fWhite()
|
||||
, fFractionDifference(0)
|
||||
, fWeightedFraction(0)
|
||||
, fAverageMismatchR(0)
|
||||
, fAverageMismatchG(0)
|
||||
, fAverageMismatchB(0)
|
||||
, fMaxMismatchR(0)
|
||||
, fMaxMismatchG(0)
|
||||
, fMaxMismatchB(0)
|
||||
, fResult(kUnknown_Result) {
|
||||
};
|
||||
|
||||
DiffResource fBase;
|
||||
DiffResource fComparison;
|
||||
DiffResource fDifference;
|
||||
DiffResource fWhite;
|
||||
|
||||
/// 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;
|
||||
};
|
||||
|
||||
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->fBase.fFilename.c_str(), rhs->fBase.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);
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
/** When finished, dr->fResult should have some value other than kUnknown_Result.
|
||||
* Expects dr->fWhite.fBitmap and dr->fDifference.fBitmap to have the same bounds as
|
||||
* dr->fBase.fBitmap and have a valid pixelref.
|
||||
*/
|
||||
void compute_diff(DiffRecord* dr, DiffMetricProc diffFunction, const int colorThreshold);
|
||||
|
||||
#endif
|
300
tools/skdiff_html.cpp
Normal file
300
tools/skdiff_html.cpp
Normal file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "skdiff.h"
|
||||
#include "skdiff_html.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkTime.h"
|
||||
|
||||
/// 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(" diffs 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.fBase.fBitmap.width() *
|
||||
diff.fBase.fBitmap.height()));
|
||||
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.fBase.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.fBase.fFilename.c_str());
|
||||
stream->writeText("</b><br>");
|
||||
switch (diff.fResult) {
|
||||
case DiffRecord::kEqualBits_Result:
|
||||
SkDEBUGFAIL("should not encounter DiffRecord with kEqualBits here");
|
||||
return;
|
||||
case DiffRecord::kEqualPixels_Result:
|
||||
SkDEBUGFAIL("should not encounter DiffRecord with kEqualPixels here");
|
||||
return;
|
||||
case DiffRecord::kDifferentSizes_Result:
|
||||
stream->writeText("Image sizes differ</td>");
|
||||
return;
|
||||
case DiffRecord::kDifferentPixels_Result:
|
||||
sprintf(metricBuf, "%.4f%%", 100 * diff.fFractionDifference);
|
||||
stream->writeText(metricBuf);
|
||||
stream->writeText(" of pixels differ");
|
||||
stream->writeText("\n (");
|
||||
sprintf(metricBuf, "%.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 DiffRecord::kCouldNotCompare_Result:
|
||||
stream->writeText("Could not compare.<br>base: ");
|
||||
stream->writeText(DiffResource::getStatusDescription(diff.fBase.fStatus));
|
||||
stream->writeText("<br>comparison: ");
|
||||
stream->writeText(DiffResource::getStatusDescription(diff.fComparison.fStatus));
|
||||
stream->writeText("</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>");
|
||||
}
|
||||
|
||||
static void print_link_cell(SkFILEWStream* stream, const SkString& path, const char* text) {
|
||||
stream->writeText("<td><a href=\"");
|
||||
stream->writeText(path.c_str());
|
||||
stream->writeText("\">");
|
||||
stream->writeText(text);
|
||||
stream->writeText("</a></td>");
|
||||
}
|
||||
|
||||
static void print_diff_resource_cell(SkFILEWStream* stream, DiffResource& resource,
|
||||
const SkString& relativePath, bool local) {
|
||||
if (resource.fBitmap.empty()) {
|
||||
if (DiffResource::kCouldNotDecode_Status == resource.fStatus) {
|
||||
if (local && !resource.fFilename.isEmpty()) {
|
||||
print_link_cell(stream, resource.fFilename, "N/A");
|
||||
return;
|
||||
}
|
||||
if (!resource.fFullPath.isEmpty()) {
|
||||
if (!resource.fFullPath.startsWith(PATH_DIV_STR)) {
|
||||
resource.fFullPath.prepend(relativePath);
|
||||
}
|
||||
print_link_cell(stream, resource.fFullPath, "N/A");
|
||||
return;
|
||||
}
|
||||
}
|
||||
stream->writeText("<td>N/A</td>");
|
||||
return;
|
||||
}
|
||||
|
||||
int height = compute_image_height(resource.fBitmap.height(), resource.fBitmap.width());
|
||||
if (local) {
|
||||
print_image_cell(stream, resource.fFilename, height);
|
||||
return;
|
||||
}
|
||||
if (!resource.fFullPath.startsWith(PATH_DIV_STR)) {
|
||||
resource.fFullPath.prepend(relativePath);
|
||||
}
|
||||
print_image_cell(stream, resource.fFullPath, height);
|
||||
}
|
||||
|
||||
static void print_diff_row(SkFILEWStream* stream, DiffRecord& diff, const SkString& relativePath) {
|
||||
stream->writeText("<tr>\n");
|
||||
print_checkbox_cell(stream, diff);
|
||||
print_label_cell(stream, diff);
|
||||
print_diff_resource_cell(stream, diff.fWhite, relativePath, true);
|
||||
print_diff_resource_cell(stream, diff.fDifference, relativePath, true);
|
||||
print_diff_resource_cell(stream, diff.fBase, relativePath, false);
|
||||
print_diff_resource_cell(stream, diff.fComparison, relativePath, false);
|
||||
stream->writeText("</tr>\n");
|
||||
stream->flush();
|
||||
}
|
||||
|
||||
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 DiffRecord::kEqualBits_Result:
|
||||
case DiffRecord::kEqualPixels_Result:
|
||||
continue;
|
||||
// Cases in which we want a detailed pixel diff.
|
||||
case DiffRecord::kDifferentPixels_Result:
|
||||
case DiffRecord::kDifferentSizes_Result:
|
||||
case DiffRecord::kCouldNotCompare_Result:
|
||||
print_diff_row(&outputStream, *diff, relativePath);
|
||||
continue;
|
||||
default:
|
||||
SkDEBUGFAIL("encountered DiffRecord with unknown result type");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
21
tools/skdiff_html.h
Normal file
21
tools/skdiff_html.h
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef skdiff_html_DEFINED
|
||||
#define skdiff_html_DEFINED
|
||||
|
||||
#include "skdiff.h"
|
||||
class SkString;
|
||||
|
||||
void print_diff_page(const int matchCount,
|
||||
const int colorThreshold,
|
||||
const RecordArray& differences,
|
||||
const SkString& baseDir,
|
||||
const SkString& comparisonDir,
|
||||
const SkString& outputDir);
|
||||
|
||||
#endif
|
375
tools/skdiff_image.cpp
Normal file
375
tools/skdiff_image.cpp
Normal file
@ -0,0 +1,375 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#include "skdiff.h"
|
||||
#include "skdiff_utils.h"
|
||||
#include "SkBitmap.h"
|
||||
#include "SkData.h"
|
||||
#include "SkImageDecoder.h"
|
||||
#include "SkImageEncoder.h"
|
||||
#include "SkOSFile.h"
|
||||
#include "SkTDArray.h"
|
||||
#include "SkTemplates.h"
|
||||
#include "SkTypes.h"
|
||||
|
||||
/// If outputDir.isEmpty(), don't write out diff files.
|
||||
static void create_diff_images (DiffMetricProc dmp,
|
||||
const int colorThreshold,
|
||||
const SkString& baseFile,
|
||||
const SkString& comparisonFile,
|
||||
const SkString& outputDir,
|
||||
const SkString& outputFilename,
|
||||
DiffRecord* drp) {
|
||||
SkASSERT(!baseFile.isEmpty());
|
||||
SkASSERT(!comparisonFile.isEmpty());
|
||||
|
||||
drp->fBase.fFilename = baseFile;
|
||||
drp->fBase.fFullPath = baseFile;
|
||||
drp->fBase.fStatus = DiffResource::kSpecified_Status;
|
||||
|
||||
drp->fComparison.fFilename = comparisonFile;
|
||||
drp->fComparison.fFullPath = comparisonFile;
|
||||
drp->fComparison.fStatus = DiffResource::kSpecified_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;
|
||||
return;
|
||||
}
|
||||
|
||||
if (are_buffers_equal(baseFileBits, comparisonFileBits)) {
|
||||
drp->fResult = DiffRecord::kEqualBits_Result;
|
||||
return;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
drp->fResult = DiffRecord::kCouldNotCompare_Result;
|
||||
return;
|
||||
}
|
||||
|
||||
create_and_write_diff_image(drp, dmp, colorThreshold, outputDir, outputFilename);
|
||||
//TODO: copy fBase.fFilename and fComparison.fFilename to outputDir
|
||||
// svn and git often present tmp files to diff tools which are promptly deleted
|
||||
|
||||
//TODO: serialize drp to outputDir
|
||||
// write a tool to deserialize them and call print_diff_page
|
||||
|
||||
SkASSERT(DiffRecord::kUnknown_Result != drp->fResult);
|
||||
}
|
||||
|
||||
static void usage (char * argv0) {
|
||||
SkDebugf("Skia image diff tool\n");
|
||||
SkDebugf("\n"
|
||||
"Usage: \n"
|
||||
" %s <baseFile> <comparisonFile>\n" , argv0);
|
||||
SkDebugf(
|
||||
"\nArguments:"
|
||||
"\n --failonresult <result>: After comparing all file pairs, exit with nonzero"
|
||||
"\n return code (number of file pairs yielding this"
|
||||
"\n result) if any file pairs yielded this result."
|
||||
"\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 yeilded this status."
|
||||
"\n --help: display this info"
|
||||
"\n --listfilenames: list all filenames for each result type in stdout"
|
||||
"\n --nodiffs: don't write out image diffs, just generate report on stdout"
|
||||
"\n --outputdir: directory to write difference images"
|
||||
"\n --threshold <n>: only report differences > n (per color channel) [default 0]"
|
||||
"\n -u: ignored. Recognized for compatibility with svn diff."
|
||||
"\n -L: first occurrence label for base, second occurrence label for comparison."
|
||||
"\n Labels must be of the form \"<filename>(\t<specifier>)?\"."
|
||||
"\n The base <filename> will be used to create files in outputdir."
|
||||
"\n"
|
||||
"\n baseFile: baseline image file."
|
||||
"\n comparisonFile: comparison image file"
|
||||
"\n"
|
||||
"\nIf no sort is specified, it will sort by fraction of pixels mismatching."
|
||||
"\n");
|
||||
}
|
||||
|
||||
const int kNoError = 0;
|
||||
const int kGenericError = -1;
|
||||
|
||||
int tool_main(int argc, char** argv);
|
||||
int tool_main(int argc, char** argv) {
|
||||
DiffMetricProc diffProc = compute_diff_pmcolor;
|
||||
|
||||
// Maximum error tolerated in any one color channel in any one pixel before
|
||||
// a difference is reported.
|
||||
int colorThreshold = 0;
|
||||
SkString baseFile;
|
||||
SkString baseLabel;
|
||||
SkString comparisonFile;
|
||||
SkString comparisonLabel;
|
||||
SkString outputDir;
|
||||
|
||||
bool listFilenames = false;
|
||||
|
||||
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;
|
||||
int numLabelArguments = 0;
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "--failonresult")) {
|
||||
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")) {
|
||||
usage(argv[0]);
|
||||
return kNoError;
|
||||
}
|
||||
if (!strcmp(argv[i], "--listfilenames")) {
|
||||
listFilenames = true;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i], "--outputdir")) {
|
||||
if (argc == ++i) {
|
||||
SkDebugf("outputdir expects one argument.\n");
|
||||
continue;
|
||||
}
|
||||
outputDir.set(argv[i]);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i], "--threshold")) {
|
||||
colorThreshold = atoi(argv[++i]);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i], "-u")) {
|
||||
//we don't produce unified diffs, ignore parameter to work with svn diff
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i], "-L")) {
|
||||
if (argc == ++i) {
|
||||
SkDebugf("label expects one argument.\n");
|
||||
continue;
|
||||
}
|
||||
switch (numLabelArguments++) {
|
||||
case 0:
|
||||
baseLabel.set(argv[i]);
|
||||
continue;
|
||||
case 1:
|
||||
comparisonLabel.set(argv[i]);
|
||||
continue;
|
||||
default:
|
||||
SkDebugf("extra label argument <%s>\n", argv[i]);
|
||||
usage(argv[0]);
|
||||
return kGenericError;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (argv[i][0] != '-') {
|
||||
switch (numUnflaggedArguments++) {
|
||||
case 0:
|
||||
baseFile.set(argv[i]);
|
||||
continue;
|
||||
case 1:
|
||||
comparisonFile.set(argv[i]);
|
||||
continue;
|
||||
default:
|
||||
SkDebugf("extra unflagged argument <%s>\n", argv[i]);
|
||||
usage(argv[0]);
|
||||
return kGenericError;
|
||||
}
|
||||
}
|
||||
|
||||
SkDebugf("Unrecognized argument <%s>\n", argv[i]);
|
||||
usage(argv[0]);
|
||||
return kGenericError;
|
||||
}
|
||||
|
||||
if (numUnflaggedArguments != 2) {
|
||||
usage(argv[0]);
|
||||
return kGenericError;
|
||||
}
|
||||
|
||||
if (listFilenames) {
|
||||
printf("Base file is [%s]\n", baseFile.c_str());
|
||||
}
|
||||
|
||||
if (listFilenames) {
|
||||
printf("Comparison file is [%s]\n", comparisonFile.c_str());
|
||||
}
|
||||
|
||||
if (outputDir.isEmpty()) {
|
||||
if (listFilenames) {
|
||||
printf("Not writing any diffs. No output dir specified.\n");
|
||||
}
|
||||
} else {
|
||||
if (!outputDir.endsWith(PATH_DIV_STR)) {
|
||||
outputDir.append(PATH_DIV_STR);
|
||||
}
|
||||
if (listFilenames) {
|
||||
printf("Writing diffs. Output dir is [%s]\n", outputDir.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Some obscure documentation about diff/patch labels:
|
||||
//
|
||||
// Posix says the format is: <filename><tab><date>
|
||||
// It also states that if a filename contains <tab> or <newline>
|
||||
// the result is implementation defined
|
||||
//
|
||||
// Svn diff --diff-cmd provides labels of the form: <filename><tab><revision>
|
||||
//
|
||||
// Git diff --ext-diff does not supply arguments compatible with diff.
|
||||
// However, it does provide the filename directly.
|
||||
// skimagediff_git.sh: skimagediff %2 %5 -L "%1\t(%3)" -L "%1\t(%6)"
|
||||
//
|
||||
// Git difftool sets $LOCAL, $REMOTE, $MERGED, and $BASE instead of command line parameters.
|
||||
// difftool.<>.cmd: skimagediff $LOCAL $REMOTE -L "$MERGED\t(local)" -L "$MERGED\t(remote)"
|
||||
//
|
||||
// Diff will write any specified label verbatim. Without a specified label diff will write
|
||||
// <filename><tab><date>
|
||||
// However, diff will encode the filename as a cstring if the filename contains
|
||||
// Any of <space> or <double quote>
|
||||
// A char less than 32
|
||||
// Any escapable character \a, \b, \t, \n, \v, \f, \r, \\
|
||||
//
|
||||
// Patch decodes:
|
||||
// If first <non-white-space> is <double quote>, parse filename from cstring.
|
||||
// If there is a <tab> after the first <non-white-space>, filename is
|
||||
// [first <non-white-space>, the next run of <white-space> with an embedded <tab>).
|
||||
// Otherwise the filename is [first <non-space>, the next <white-space>).
|
||||
//
|
||||
// The filename /dev/null means the file does not exist (used in adds and deletes).
|
||||
|
||||
// Considering the above, skimagediff will consider the contents of a -L parameter as
|
||||
// <filename>(\t<specifier>)?
|
||||
SkString outputFile;
|
||||
|
||||
if (baseLabel.isEmpty()) {
|
||||
baseLabel.set(baseFile);
|
||||
outputFile = baseLabel;
|
||||
} else {
|
||||
const char* baseLabelCstr = baseLabel.c_str();
|
||||
const char* tab = strchr(baseLabelCstr, '\t');
|
||||
if (NULL == tab) {
|
||||
outputFile = baseLabel;
|
||||
} else {
|
||||
outputFile.set(baseLabelCstr, tab - baseLabelCstr);
|
||||
}
|
||||
}
|
||||
if (comparisonLabel.isEmpty()) {
|
||||
comparisonLabel.set(comparisonFile);
|
||||
}
|
||||
printf("Base: %s\n", baseLabel.c_str());
|
||||
printf("Comparison: %s\n", comparisonLabel.c_str());
|
||||
|
||||
DiffRecord dr;
|
||||
create_diff_images(diffProc, colorThreshold, baseFile, comparisonFile, outputDir, outputFile,
|
||||
&dr);
|
||||
|
||||
if (DiffResource::isStatusFailed(dr.fBase.fStatus)) {
|
||||
printf("Base %s.\n", DiffResource::getStatusDescription(dr.fBase.fStatus));
|
||||
}
|
||||
if (DiffResource::isStatusFailed(dr.fComparison.fStatus)) {
|
||||
printf("Comparison %s.\n", DiffResource::getStatusDescription(dr.fComparison.fStatus));
|
||||
}
|
||||
printf("Base and Comparison %s.\n", DiffRecord::getResultDescription(dr.fResult));
|
||||
|
||||
if (DiffRecord::kDifferentPixels_Result == dr.fResult) {
|
||||
printf("%.4f%% of pixels differ", 100 * dr.fFractionDifference);
|
||||
printf(" (%.4f%% weighted)", 100 * dr.fWeightedFraction);
|
||||
if (dr.fFractionDifference < 0.01) {
|
||||
printf(" %d pixels", static_cast<int>(dr.fFractionDifference *
|
||||
dr.fBase.fBitmap.width() *
|
||||
dr.fBase.fBitmap.height()));
|
||||
}
|
||||
|
||||
printf("\nAverage color mismatch: ");
|
||||
printf("%d", static_cast<int>(MAX3(dr.fAverageMismatchR,
|
||||
dr.fAverageMismatchG,
|
||||
dr.fAverageMismatchB)));
|
||||
printf("\nMax color mismatch: ");
|
||||
printf("%d", MAX3(dr.fMaxMismatchR,
|
||||
dr.fMaxMismatchG,
|
||||
dr.fMaxMismatchB));
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
int num_failing_results = 0;
|
||||
if (failOnResultType[dr.fResult]) {
|
||||
++num_failing_results;
|
||||
}
|
||||
if (failOnStatusType[dr.fBase.fStatus][dr.fComparison.fStatus]) {
|
||||
++num_failing_results;
|
||||
}
|
||||
|
||||
return num_failing_results;
|
||||
}
|
||||
|
||||
#if !defined SK_BUILD_FOR_IOS
|
||||
int main(int argc, char * const argv[]) {
|
||||
return tool_main(argc, (char**) argv);
|
||||
}
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
186
tools/skdiff_utils.cpp
Normal file
186
tools/skdiff_utils.cpp
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#include "skdiff.h"
|
||||
#include "skdiff_utils.h"
|
||||
#include "SkBitmap.h"
|
||||
#include "SkData.h"
|
||||
#include "SkImageDecoder.h"
|
||||
#include "SkImageEncoder.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkTemplates.h"
|
||||
#include "SkTypes.h"
|
||||
|
||||
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()));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bool get_bitmap(SkData* fileBits, DiffResource& resource, SkImageDecoder::Mode mode) {
|
||||
SkMemoryStream stream(fileBits->data(), fileBits->size());
|
||||
|
||||
SkImageDecoder* codec = SkImageDecoder::Factory(&stream);
|
||||
if (NULL == codec) {
|
||||
SkDebugf("ERROR: no codec found for <%s>\n", resource.fFullPath.c_str());
|
||||
resource.fStatus = DiffResource::kCouldNotDecode_Status;
|
||||
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);
|
||||
|
||||
stream.rewind();
|
||||
if (!codec->decode(&stream, &resource.fBitmap, SkBitmap::kARGB_8888_Config, mode)) {
|
||||
SkDebugf("ERROR: codec failed for basePath <%s>\n", resource.fFullPath.c_str());
|
||||
resource.fStatus = DiffResource::kCouldNotDecode_Status;
|
||||
return false;
|
||||
}
|
||||
|
||||
resource.fStatus = DiffResource::kDecoded_Status;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
SkString filename_to_diff_filename(const SkString& filename) {
|
||||
return filename_to_derived_filename(filename, "-diff.png");
|
||||
}
|
||||
|
||||
SkString filename_to_white_filename(const SkString& filename) {
|
||||
return filename_to_derived_filename(filename, "-white.png");
|
||||
}
|
||||
|
||||
void create_and_write_diff_image(DiffRecord* drp,
|
||||
DiffMetricProc dmp,
|
||||
const int colorThreshold,
|
||||
const SkString& outputDir,
|
||||
const SkString& filename) {
|
||||
const int w = drp->fBase.fBitmap.width();
|
||||
const int h = drp->fBase.fBitmap.height();
|
||||
|
||||
if (w != drp->fComparison.fBitmap.width() || h != drp->fComparison.fBitmap.height()) {
|
||||
drp->fResult = DiffRecord::kDifferentSizes_Result;
|
||||
} else {
|
||||
drp->fDifference.fBitmap.setConfig(SkBitmap::kARGB_8888_Config, w, h);
|
||||
drp->fDifference.fBitmap.allocPixels();
|
||||
|
||||
drp->fWhite.fBitmap.setConfig(SkBitmap::kARGB_8888_Config, w, h);
|
||||
drp->fWhite.fBitmap.allocPixels();
|
||||
|
||||
SkASSERT(DiffRecord::kUnknown_Result == drp->fResult);
|
||||
compute_diff(drp, dmp, colorThreshold);
|
||||
SkASSERT(DiffRecord::kUnknown_Result != drp->fResult);
|
||||
}
|
||||
|
||||
if (outputDir.isEmpty()) {
|
||||
drp->fDifference.fStatus = DiffResource::kUnspecified_Status;
|
||||
drp->fWhite.fStatus = DiffResource::kUnspecified_Status;
|
||||
|
||||
} else {
|
||||
drp->fDifference.fFilename = filename_to_diff_filename(filename);
|
||||
drp->fDifference.fFullPath = outputDir;
|
||||
drp->fDifference.fFullPath.append(drp->fDifference.fFilename);
|
||||
drp->fDifference.fStatus = DiffResource::kSpecified_Status;
|
||||
|
||||
drp->fWhite.fFilename = filename_to_white_filename(filename);
|
||||
drp->fWhite.fFullPath = outputDir;
|
||||
drp->fWhite.fFullPath.append(drp->fWhite.fFilename);
|
||||
drp->fWhite.fStatus = DiffResource::kSpecified_Status;
|
||||
|
||||
if (DiffRecord::kDifferentPixels_Result == drp->fResult) {
|
||||
if (write_bitmap(drp->fDifference.fFullPath, drp->fDifference.fBitmap)) {
|
||||
drp->fDifference.fStatus = DiffResource::kExists_Status;
|
||||
} else {
|
||||
drp->fDifference.fStatus = DiffResource::kDoesNotExist_Status;
|
||||
}
|
||||
if (write_bitmap(drp->fWhite.fFullPath, drp->fWhite.fBitmap)) {
|
||||
drp->fWhite.fStatus = DiffResource::kExists_Status;
|
||||
} else {
|
||||
drp->fWhite.fStatus = DiffResource::kDoesNotExist_Status;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
53
tools/skdiff_utils.h
Normal file
53
tools/skdiff_utils.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef skdiff_utils_DEFINED
|
||||
#define skdiff_utils_DEFINED
|
||||
|
||||
#include "skdiff.h"
|
||||
#include "SkImageDecoder.h"
|
||||
|
||||
class SkBitmap;
|
||||
class SkData;
|
||||
class SkString;
|
||||
|
||||
/** Returns true if the two buffers passed in are both non-NULL,
|
||||
* have the same length, and contain exactly the same byte values.
|
||||
*/
|
||||
bool are_buffers_equal(SkData* skdata1, SkData* skdata2);
|
||||
|
||||
/** Reads the file at the given path and returns its complete contents as an
|
||||
* SkData object (or returns NULL on error).
|
||||
*/
|
||||
SkData* read_file(const char* file_path);
|
||||
|
||||
/** Decodes the fileBits into the resource.fBitmap. Returns false on failure. */
|
||||
bool get_bitmap(SkData* fileBits, DiffResource& resource, SkImageDecoder::Mode mode);
|
||||
|
||||
/** Writes the bitmap as a PNG to the path specified. */
|
||||
bool write_bitmap(const SkString& path, const SkBitmap& bitmap);
|
||||
|
||||
/** Given an image filename, returns the name of the file containing
|
||||
* the associated difference image.
|
||||
*/
|
||||
SkString filename_to_diff_filename(const SkString& filename);
|
||||
|
||||
/** Given an image filename, returns the name of the file containing
|
||||
* the "white" difference image.
|
||||
*/
|
||||
SkString filename_to_white_filename(const SkString& filename);
|
||||
|
||||
/** Calls compute_diff and handles the difference and white diff resources.
|
||||
* If !outputDir.isEmpty(), writes out difference and white images.
|
||||
*/
|
||||
void create_and_write_diff_image(DiffRecord* drp,
|
||||
DiffMetricProc dmp,
|
||||
const int colorThreshold,
|
||||
const SkString& outputDir,
|
||||
const SkString& filename);
|
||||
|
||||
#endif
|
@ -59,7 +59,7 @@ skdiff_test "$SKDIFF_TESTDIR/baseDir $SKDIFF_TESTDIR/comparisonDir" "$SKDIFF_TES
|
||||
# baseDir or comparisonDir)
|
||||
# - list filenames with each result type to stdout
|
||||
# - don't generate HTML output files
|
||||
skdiff_test "--failonresult DifferentPixels --failonresult DifferentSizes --failonresult DifferentOther --failonresult Unknown --listfilenames --nodiffs $SKDIFF_TESTDIR/baseDir $SKDIFF_TESTDIR/comparisonDir" "$SKDIFF_TESTDIR/test2"
|
||||
skdiff_test "--failonresult DifferentPixels --failonresult DifferentSizes --failonresult Unknown --failonstatus CouldNotDecode,CouldNotRead any --failonstatus any CouldNotDecode,CouldNotRead --listfilenames --nodiffs $SKDIFF_TESTDIR/baseDir $SKDIFF_TESTDIR/comparisonDir" "$SKDIFF_TESTDIR/test2"
|
||||
|
||||
# Run skdiff over just the files that have identical bits.
|
||||
skdiff_test "--nodiffs --match identical-bits $SKDIFF_TESTDIR/baseDir $SKDIFF_TESTDIR/comparisonDir" "$SKDIFF_TESTDIR/identical-bits"
|
||||
|
@ -7,9 +7,7 @@ compared 3 file pairs:
|
||||
[_] 1 file pairs contain the same pixel values, but not the same bits
|
||||
[_] 0 file pairs have identical dimensions but some differing pixels
|
||||
[_] 0 file pairs have differing dimensions
|
||||
[_] 0 file pairs contain different bits and are not parsable images
|
||||
[_] 0 file pairs missing from comparisonDir
|
||||
[_] 0 file pairs missing from baseDir
|
||||
[_] 0 file pairs could not be compared
|
||||
[_] 0 file pairs not compared yet
|
||||
(results marked with [*] will cause nonzero return value)
|
||||
|
||||
|
@ -7,9 +7,7 @@ compared 2 file pairs:
|
||||
[_] 0 file pairs contain the same pixel values, but not the same bits
|
||||
[_] 0 file pairs have identical dimensions but some differing pixels
|
||||
[_] 0 file pairs have differing dimensions
|
||||
[_] 0 file pairs contain different bits and are not parsable images
|
||||
[_] 0 file pairs missing from comparisonDir
|
||||
[_] 0 file pairs missing from baseDir
|
||||
[_] 0 file pairs could not be compared
|
||||
[_] 0 file pairs not compared yet
|
||||
(results marked with [*] will cause nonzero return value)
|
||||
|
||||
|
@ -17,32 +17,32 @@ $("#checkedList").html(fileCmdLineString + '<br><br>' + fileMultiLineString);
|
||||
<body>
|
||||
<table>
|
||||
<tr><th>select image</th>
|
||||
<th>3 of 12 images matched exactly.<br></th>
|
||||
<th>3 of 12 diffs matched exactly.<br></th>
|
||||
<th>every different pixel shown in white</th>
|
||||
<th>color difference at each pixel</th>
|
||||
<th>baseDir: tools/tests/skdiff/baseDir/</th>
|
||||
<th>comparisonDir: tools/tests/skdiff/comparisonDir/</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="missing-files/missing-from-baseDir.png" checked="yes"></td><td><b>missing-files/missing-from-baseDir.png</b><br>Missing from baseDir</td><td>N/A</td><td>N/A</td><td>N/A</td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/missing-files/missing-from-baseDir.png"><img src="../../../../../tools/tests/skdiff/comparisonDir/missing-files/missing-from-baseDir.png" height="240px"></a></td></tr>
|
||||
<td><input type="checkbox" name="different-bits/different-bits-unknown-format.xyz" checked="yes"></td><td><b>different-bits/different-bits-unknown-format.xyz</b><br>Could not compare.<br>base: could not be decoded<br>comparison: could not be decoded</td><td>N/A</td><td>N/A</td><td><a href="../../../../../tools/tests/skdiff/baseDir/different-bits/different-bits-unknown-format.xyz">N/A</a></td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/different-bits/different-bits-unknown-format.xyz">N/A</a></td></tr>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="missing-files/missing-from-baseDir.xyz" checked="yes"></td><td><b>missing-files/missing-from-baseDir.xyz</b><br>Missing from baseDir</td><td>N/A</td><td>N/A</td><td>N/A</td><td>N/A</td></tr>
|
||||
<td><input type="checkbox" name="missing-files/missing-from-baseDir.png" checked="yes"></td><td><b>missing-files/missing-from-baseDir.png</b><br>Could not compare.<br>base: not found<br>comparison: decoded</td><td>N/A</td><td>N/A</td><td>N/A</td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/missing-files/missing-from-baseDir.png"><img src="../../../../../tools/tests/skdiff/comparisonDir/missing-files/missing-from-baseDir.png" height="240px"></a></td></tr>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="missing-files/missing-from-comparisonDir.png" checked="yes"></td><td><b>missing-files/missing-from-comparisonDir.png</b><br>Missing from comparisonDir</td><td>N/A</td><td>N/A</td><td><a href="../../../../../tools/tests/skdiff/baseDir/missing-files/missing-from-comparisonDir.png"><img src="../../../../../tools/tests/skdiff/baseDir/missing-files/missing-from-comparisonDir.png" height="240px"></a></td><td>N/A</td></tr>
|
||||
<td><input type="checkbox" name="missing-files/missing-from-baseDir.xyz" checked="yes"></td><td><b>missing-files/missing-from-baseDir.xyz</b><br>Could not compare.<br>base: not found<br>comparison: could not be decoded</td><td>N/A</td><td>N/A</td><td>N/A</td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/missing-files/missing-from-baseDir.xyz">N/A</a></td></tr>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="missing-files/missing-from-comparisonDir.xyz" checked="yes"></td><td><b>missing-files/missing-from-comparisonDir.xyz</b><br>Missing from comparisonDir</td><td>N/A</td><td>N/A</td><td>N/A</td><td>N/A</td></tr>
|
||||
<td><input type="checkbox" name="missing-files/missing-from-comparisonDir.png" checked="yes"></td><td><b>missing-files/missing-from-comparisonDir.png</b><br>Could not compare.<br>base: decoded<br>comparison: not found</td><td>N/A</td><td>N/A</td><td><a href="../../../../../tools/tests/skdiff/baseDir/missing-files/missing-from-comparisonDir.png"><img src="../../../../../tools/tests/skdiff/baseDir/missing-files/missing-from-comparisonDir.png" height="240px"></a></td><td>N/A</td></tr>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="different-bits/different-bits-unknown-format.xyz" checked="yes"></td><td><b>different-bits/different-bits-unknown-format.xyz</b><br>Files differ; unable to parse one or both files</td><td>N/A</td><td>N/A</td><td>N/A</td><td>N/A</td></tr>
|
||||
<td><input type="checkbox" name="missing-files/missing-from-comparisonDir.xyz" checked="yes"></td><td><b>missing-files/missing-from-comparisonDir.xyz</b><br>Could not compare.<br>base: could not be decoded<br>comparison: not found</td><td>N/A</td><td>N/A</td><td><a href="../../../../../tools/tests/skdiff/baseDir/missing-files/missing-from-comparisonDir.xyz">N/A</a></td><td>N/A</td></tr>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="different-bits/slightly-different-sizes.png" checked="yes"></td><td><b>different-bits/slightly-different-sizes.png</b><br>Image sizes differ</td><td>N/A</td><td>N/A</td><td><a href="../../../../../tools/tests/skdiff/baseDir/different-bits/slightly-different-sizes.png"><img src="../../../../../tools/tests/skdiff/baseDir/different-bits/slightly-different-sizes.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/different-bits/slightly-different-sizes.png"><img src="../../../../../tools/tests/skdiff/comparisonDir/different-bits/slightly-different-sizes.png" height="240px"></a></td></tr>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="different-bits/very-different-sizes.png" checked="yes"></td><td><b>different-bits/very-different-sizes.png</b><br>Image sizes differ</td><td>N/A</td><td>N/A</td><td><a href="../../../../../tools/tests/skdiff/baseDir/different-bits/very-different-sizes.png"><img src="../../../../../tools/tests/skdiff/baseDir/different-bits/very-different-sizes.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/different-bits/very-different-sizes.png"><img src="../../../../../tools/tests/skdiff/comparisonDir/different-bits/very-different-sizes.png" height="128px"></a></td></tr>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="different-bits/very-different-pixels-same-size.png" checked="yes"></td><td><b>different-bits/very-different-pixels-same-size.png</b><br> 97.9926% of pixels differ
|
||||
( 42.8911% weighted)<br>Average color mismatch 89<br>Max color mismatch 239</td><td><a href="different-bits_very-different-pixels-same-size-white.png"><img src="different-bits_very-different-pixels-same-size-white.png" height="240px"></a></td><td><a href="different-bits_very-different-pixels-same-size-diff.png"><img src="different-bits_very-different-pixels-same-size-diff.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/baseDir/different-bits/very-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/baseDir/different-bits/very-different-pixels-same-size.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/different-bits/very-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/comparisonDir/different-bits/very-different-pixels-same-size.png" height="240px"></a></td></tr>
|
||||
<td><input type="checkbox" name="different-bits/very-different-pixels-same-size.png" checked="yes"></td><td><b>different-bits/very-different-pixels-same-size.png</b><br>97.9926% of pixels differ
|
||||
(42.8911% weighted)<br>Average color mismatch 89<br>Max color mismatch 239</td><td><a href="different-bits_very-different-pixels-same-size-white.png"><img src="different-bits_very-different-pixels-same-size-white.png" height="240px"></a></td><td><a href="different-bits_very-different-pixels-same-size-diff.png"><img src="different-bits_very-different-pixels-same-size-diff.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/baseDir/different-bits/very-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/baseDir/different-bits/very-different-pixels-same-size.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/different-bits/very-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/comparisonDir/different-bits/very-different-pixels-same-size.png" height="240px"></a></td></tr>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="different-bits/slightly-different-pixels-same-size.png" checked="yes"></td><td><b>different-bits/slightly-different-pixels-same-size.png</b><br> 0.6630% of pixels differ
|
||||
( 0.1904% weighted)<br>(2164 pixels)<br>Average color mismatch 0<br>Max color mismatch 213</td><td><a href="different-bits_slightly-different-pixels-same-size-white.png"><img src="different-bits_slightly-different-pixels-same-size-white.png" height="240px"></a></td><td><a href="different-bits_slightly-different-pixels-same-size-diff.png"><img src="different-bits_slightly-different-pixels-same-size-diff.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/baseDir/different-bits/slightly-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/baseDir/different-bits/slightly-different-pixels-same-size.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/different-bits/slightly-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/comparisonDir/different-bits/slightly-different-pixels-same-size.png" height="240px"></a></td></tr>
|
||||
<td><input type="checkbox" name="different-bits/slightly-different-pixels-same-size.png" checked="yes"></td><td><b>different-bits/slightly-different-pixels-same-size.png</b><br>0.6630% of pixels differ
|
||||
(0.1904% weighted)<br>(2164 pixels)<br>Average color mismatch 0<br>Max color mismatch 213</td><td><a href="different-bits_slightly-different-pixels-same-size-white.png"><img src="different-bits_slightly-different-pixels-same-size-white.png" height="240px"></a></td><td><a href="different-bits_slightly-different-pixels-same-size-diff.png"><img src="different-bits_slightly-different-pixels-same-size-diff.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/baseDir/different-bits/slightly-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/baseDir/different-bits/slightly-different-pixels-same-size.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/different-bits/slightly-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/comparisonDir/different-bits/slightly-different-pixels-same-size.png" height="240px"></a></td></tr>
|
||||
</table>
|
||||
<input type="button" onclick="generateCheckedList()" value="Create Rebaseline List">
|
||||
<div id="checkedList"></div>
|
||||
|
@ -1,8 +1,9 @@
|
||||
ERROR: no codec found for basePath <tools/tests/skdiff/baseDir/different-bits/different-bits-unknown-format.xyz>
|
||||
ERROR: no codec found for <tools/tests/skdiff/comparisonDir/missing-files/missing-from-baseDir.xyz>
|
||||
ERROR: no codec found for <tools/tests/skdiff/baseDir/missing-files/missing-from-comparisonDir.xyz>
|
||||
ERROR: no codec found for <tools/tests/skdiff/baseDir/different-bits/different-bits-unknown-format.xyz>
|
||||
ERROR: no codec found for <tools/tests/skdiff/comparisonDir/different-bits/different-bits-unknown-format.xyz>
|
||||
ERROR: no codec found for <tools/tests/skdiff/baseDir/identical-bits/identical-bits-unknown-format.xyz>
|
||||
ERROR: no codec found for <tools/tests/skdiff/comparisonDir/identical-bits/identical-bits-unknown-format.xyz>
|
||||
ERROR: no codec found for <tools/tests/skdiff/comparisonDir/missing-files/missing-from-baseDir.xyz>
|
||||
ERROR: no codec found for <tools/tests/skdiff/baseDir/missing-files/missing-from-comparisonDir.xyz>
|
||||
baseDir is [tools/tests/skdiff/baseDir/]
|
||||
comparisonDir is [tools/tests/skdiff/comparisonDir/]
|
||||
writing diffs to outputDir is [tools/tests/skdiff/test1/output-actual/]
|
||||
@ -12,9 +13,12 @@ compared 12 file pairs:
|
||||
[_] 1 file pairs contain the same pixel values, but not the same bits
|
||||
[_] 2 file pairs have identical dimensions but some differing pixels
|
||||
[_] 2 file pairs have differing dimensions
|
||||
[_] 1 file pairs contain different bits and are not parsable images
|
||||
[_] 2 file pairs missing from comparisonDir
|
||||
[_] 2 file pairs missing from baseDir
|
||||
[_] 5 file pairs could not be compared
|
||||
[_] 1 file pairs decoded in baseDir and not found in comparisonDir
|
||||
[_] 1 file pairs could not be decoded in baseDir and could not be decoded in comparisonDir
|
||||
[_] 1 file pairs could not be decoded in baseDir and not found in comparisonDir
|
||||
[_] 1 file pairs not found in baseDir and decoded in comparisonDir
|
||||
[_] 1 file pairs not found in baseDir and could not be decoded in comparisonDir
|
||||
[_] 0 file pairs not compared yet
|
||||
(results marked with [*] will cause nonzero return value)
|
||||
|
||||
|
@ -1 +1 @@
|
||||
out/Debug/skdiff --failonresult DifferentPixels --failonresult DifferentSizes --failonresult DifferentOther --failonresult Unknown --listfilenames --nodiffs tools/tests/skdiff/baseDir tools/tests/skdiff/comparisonDir tools/tests/skdiff/test2/output-actual
|
||||
out/Debug/skdiff --failonresult DifferentPixels --failonresult DifferentSizes --failonresult Unknown --failonstatus CouldNotDecode,CouldNotRead any --failonstatus any CouldNotDecode,CouldNotRead --listfilenames --nodiffs tools/tests/skdiff/baseDir tools/tests/skdiff/comparisonDir tools/tests/skdiff/test2/output-actual
|
||||
|
@ -1,4 +1,5 @@
|
||||
ERROR: no codec found for basePath <tools/tests/skdiff/baseDir/different-bits/different-bits-unknown-format.xyz>
|
||||
ERROR: no codec found for <tools/tests/skdiff/baseDir/different-bits/different-bits-unknown-format.xyz>
|
||||
ERROR: no codec found for <tools/tests/skdiff/comparisonDir/different-bits/different-bits-unknown-format.xyz>
|
||||
baseDir is [tools/tests/skdiff/baseDir/]
|
||||
comparisonDir is [tools/tests/skdiff/comparisonDir/]
|
||||
not writing any diffs to outputDir [tools/tests/skdiff/test2/output-actual/]
|
||||
@ -8,9 +9,10 @@ compared 12 file pairs:
|
||||
[_] 1 file pairs contain the same pixel values, but not the same bits: different-bits/different-bits-identical-pixels.png
|
||||
[*] 2 file pairs have identical dimensions but some differing pixels: different-bits/slightly-different-pixels-same-size.png different-bits/very-different-pixels-same-size.png
|
||||
[*] 2 file pairs have differing dimensions: different-bits/slightly-different-sizes.png different-bits/very-different-sizes.png
|
||||
[*] 1 file pairs contain different bits and are not parsable images: different-bits/different-bits-unknown-format.xyz
|
||||
[_] 2 file pairs missing from comparisonDir: missing-files/missing-from-comparisonDir.png missing-files/missing-from-comparisonDir.xyz
|
||||
[_] 2 file pairs missing from baseDir: missing-files/missing-from-baseDir.png missing-files/missing-from-baseDir.xyz
|
||||
[_] 5 file pairs could not be compared: different-bits/different-bits-unknown-format.xyz missing-files/missing-from-baseDir.png missing-files/missing-from-baseDir.xyz missing-files/missing-from-comparisonDir.png missing-files/missing-from-comparisonDir.xyz
|
||||
[*] 1 file pairs could not be decoded in baseDir and could not be decoded in comparisonDir: different-bits/different-bits-unknown-format.xyz
|
||||
[_] 2 file pairs found in baseDir and not found in comparisonDir: missing-files/missing-from-comparisonDir.png missing-files/missing-from-comparisonDir.xyz
|
||||
[_] 2 file pairs not found in baseDir and found in comparisonDir: missing-files/missing-from-baseDir.png missing-files/missing-from-baseDir.xyz
|
||||
[*] 0 file pairs not compared yet:
|
||||
(results marked with [*] will cause nonzero return value)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user