You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
226 lines
7.3 KiB
226 lines
7.3 KiB
/* |
|
* 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; |
|
} |
|
|
|
int mismatchedPixels = 0; |
|
int totalMismatchA = 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 outputDifference = diffFunction(c0, c1); |
|
uint32_t thisA = SkAbs32(SkGetPackedA32(c0) - SkGetPackedA32(c1)); |
|
uint32_t thisR = SkAbs32(SkGetPackedR32(c0) - SkGetPackedR32(c1)); |
|
uint32_t thisG = SkAbs32(SkGetPackedG32(c0) - SkGetPackedG32(c1)); |
|
uint32_t thisB = SkAbs32(SkGetPackedB32(c0) - SkGetPackedB32(c1)); |
|
totalMismatchA += thisA; |
|
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 (thisA > dr->fMaxMismatchA) { |
|
dr->fMaxMismatchA = thisA; |
|
} |
|
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->fTotalMismatchA = totalMismatchA; |
|
dr->fAverageMismatchA = ((float) totalMismatchA) / pixelCount; |
|
dr->fAverageMismatchR = ((float) totalMismatchR) / pixelCount; |
|
dr->fAverageMismatchG = ((float) totalMismatchG) / pixelCount; |
|
dr->fAverageMismatchB = ((float) totalMismatchB) / pixelCount; |
|
}
|
|
|