| /* | 
 |  * 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 "../private/SkTDArray.h" | 
 |  | 
 | #if defined(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 |