* 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 "include/core/SkBitmap.h"
#include "include/core/SkColor.h"
#include "include/core/SkColorPriv.h"
#include "include/core/SkString.h"
#include "include/private/SkTDArray.h"
#if defined(SK_BUILD_FOR_WIN)
#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 {
kDecoded_Status,
kCouldNotDecode_Status,
kRead_Status,
kCouldNotRead_Status,
kExists_Status,
kDoesNotExist_Status,
kSpecified_Status,
kUnspecified_Status,
kUnknown_Status,
kStatusCount
};
static char const * const StatusNames[DiffResource::kStatusCount];
* If there is no Status with this name, returns kStatusCount.
*/
static Status getStatusByName(const char *name);
static const char *getStatusDescription(Status status);
static bool isStatusFailed(Status status);
* 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) { }
SkString fFilename;
SkString fFullPath;
SkBitmap fBitmap;
Status fStatus;
};
struct DiffRecord {
enum Result {
kEqualBits_Result,
kEqualPixels_Result,
kDifferentPixels_Result,
kDifferentSizes_Result,
kCouldNotCompare_Result,
kUnknown_Result,
kResultCount
};
static char const * const ResultNames[DiffRecord::kResultCount];
* If there is no Result with this name, returns kResultCount.
*/
static Result getResultByName(const char *name);
static const char *getResultDescription(Result result);
DiffRecord()
: fBase()
, fComparison()
, fDifference()
, fWhite()
, fFractionDifference(0)
, fWeightedFraction(0)
, fAverageMismatchA(0)
, fAverageMismatchR(0)
, fAverageMismatchG(0)
, fAverageMismatchB(0)
, fTotalMismatchA(0)
, fMaxMismatchA(0)
, fMaxMismatchR(0)
, fMaxMismatchG(0)
, fMaxMismatchB(0)
, fResult(kUnknown_Result) {
}
DiffResource fBase;
DiffResource fComparison;
DiffResource fDifference;
DiffResource fWhite;
float fFractionDifference;
float fWeightedFraction;
float fAverageMismatchA;
float fAverageMismatchR;
float fAverageMismatchG;
float fAverageMismatchB;
uint32_t fTotalMismatchA;
uint32_t fMaxMismatchA;
uint32_t fMaxMismatchR;
uint32_t fMaxMismatchG;
uint32_t fMaxMismatchB;
Result fResult;
};
typedef SkTDArray<DiffRecord*> RecordArray;
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);
if (lhs->fResult != rhs->fResult) {
return (lhs->fResult < rhs->fResult) ? 1 : -1;
}
int result = T::comparePixels(lhs, rhs);
if (result != 0) {
return result;
}
return strcmp(lhs->fBase.fFilename.c_str(), rhs->fBase.fFilename.c_str());
}
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;
}
};
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;
}
};
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);
}
};
typedef SkPMColor (*DiffMetricProc)(SkPMColor, SkPMColor);
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));
}
* 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