| /* | |
| * 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) | |
| , 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; | |
| /// 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 fAverageMismatchA; | |
| float fAverageMismatchR; | |
| float fAverageMismatchG; | |
| float fAverageMismatchB; | |
| uint32_t fTotalMismatchA; | |
| uint32_t fMaxMismatchA; | |
| 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 |