|  | /* | 
|  | * 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 { | 
|  | /** 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 |