Home | History | Annotate | Download | only in skdiff
      1 /*
      2  * Copyright 2012 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #ifndef skdiff_DEFINED
      9 #define skdiff_DEFINED
     10 
     11 #include "SkBitmap.h"
     12 #include "SkColor.h"
     13 #include "SkColorPriv.h"
     14 #include "SkString.h"
     15 #include "../private/SkTDArray.h"
     16 
     17 #if defined(SK_BUILD_FOR_WIN32)
     18     #define PATH_DIV_STR "\\"
     19     #define PATH_DIV_CHAR '\\'
     20 #else
     21     #define PATH_DIV_STR "/"
     22     #define PATH_DIV_CHAR '/'
     23 #endif
     24 
     25 #define MAX2(a,b) (((b) < (a)) ? (a) : (b))
     26 #define MAX3(a,b,c) (((b) < (a)) ? MAX2((a), (c)) : MAX2((b), (c)))
     27 
     28 
     29 struct DiffResource {
     30     enum Status {
     31         /** The resource was specified, exists, read, and decoded. */
     32         kDecoded_Status,
     33         /** The resource was specified, exists, read, but could not be decoded. */
     34         kCouldNotDecode_Status,
     35 
     36         /** The resource was specified, exists, and read. */
     37         kRead_Status,
     38         /** The resource was specified, exists, but could not be read. */
     39         kCouldNotRead_Status,
     40 
     41         /** The resource was specified and exists. */
     42         kExists_Status,
     43         /** The resource was specified, but does not exist. */
     44         kDoesNotExist_Status,
     45 
     46         /** The resource was specified. */
     47         kSpecified_Status,
     48         /** The resource was not specified. */
     49         kUnspecified_Status,
     50 
     51         /** Nothing is yet known about the resource. */
     52         kUnknown_Status,
     53 
     54         /** NOT A VALID VALUE -- used to set up arrays and to represent an unknown value. */
     55         kStatusCount
     56     };
     57     static char const * const StatusNames[DiffResource::kStatusCount];
     58 
     59     /** Returns the Status with this name.
     60      *  If there is no Status with this name, returns kStatusCount.
     61      */
     62     static Status getStatusByName(const char *name);
     63 
     64     /** Returns a text description of the given Status type. */
     65     static const char *getStatusDescription(Status status);
     66 
     67     /** Returns true if the Status indicates some kind of failure. */
     68     static bool isStatusFailed(Status status);
     69 
     70     /** Sets statuses[i] if it is implied by selector, unsets it if not.
     71      *  Selector may be a comma delimited list of status names, "any", or "failed".
     72      *  Returns true if the selector was entirely understood, false otherwise.
     73      */
     74     static bool getMatchingStatuses(char* selector, bool statuses[kStatusCount]);
     75 
     76     DiffResource() : fFilename(), fFullPath(), fBitmap(), fStatus(kUnknown_Status) { }
     77 
     78     /** If isEmpty() indicates no filename available. */
     79     SkString fFilename;
     80     /** If isEmpty() indicates no path available. */
     81     SkString fFullPath;
     82     /** If empty() indicates the bitmap could not be created. */
     83     SkBitmap fBitmap;
     84     Status fStatus;
     85 };
     86 
     87 struct DiffRecord {
     88 
     89     // Result of comparison for each pair of files.
     90     // Listed from "better" to "worse", for sorting of results.
     91     enum Result {
     92         kEqualBits_Result,
     93         kEqualPixels_Result,
     94         kDifferentPixels_Result,
     95         kDifferentSizes_Result,
     96         kCouldNotCompare_Result,
     97         kUnknown_Result,
     98 
     99         kResultCount  // NOT A VALID VALUE--used to set up arrays. Must be last.
    100     };
    101     static char const * const ResultNames[DiffRecord::kResultCount];
    102 
    103     /** Returns the Result with this name.
    104      *  If there is no Result with this name, returns kResultCount.
    105      */
    106     static Result getResultByName(const char *name);
    107 
    108     /** Returns a text description of the given Result type. */
    109     static const char *getResultDescription(Result result);
    110 
    111     DiffRecord()
    112         : fBase()
    113         , fComparison()
    114         , fDifference()
    115         , fWhite()
    116         , fFractionDifference(0)
    117         , fWeightedFraction(0)
    118         , fAverageMismatchA(0)
    119         , fAverageMismatchR(0)
    120         , fAverageMismatchG(0)
    121         , fAverageMismatchB(0)
    122         , fTotalMismatchA(0)
    123         , fMaxMismatchA(0)
    124         , fMaxMismatchR(0)
    125         , fMaxMismatchG(0)
    126         , fMaxMismatchB(0)
    127         , fResult(kUnknown_Result) {
    128     }
    129 
    130     DiffResource fBase;
    131     DiffResource fComparison;
    132     DiffResource fDifference;
    133     DiffResource fWhite;
    134 
    135     /// Arbitrary floating-point metric to be used to sort images from most
    136     /// to least different from baseline; values of 0 will be omitted from the
    137     /// summary webpage.
    138     float fFractionDifference;
    139     float fWeightedFraction;
    140 
    141     float fAverageMismatchA;
    142     float fAverageMismatchR;
    143     float fAverageMismatchG;
    144     float fAverageMismatchB;
    145 
    146     uint32_t fTotalMismatchA;
    147 
    148     uint32_t fMaxMismatchA;
    149     uint32_t fMaxMismatchR;
    150     uint32_t fMaxMismatchG;
    151     uint32_t fMaxMismatchB;
    152 
    153     /// Which category of diff result.
    154     Result fResult;
    155 };
    156 
    157 typedef SkTDArray<DiffRecord*> RecordArray;
    158 
    159 /// A wrapper for any sortProc (comparison routine) which applies a first-order
    160 /// sort beforehand, and a tiebreaker if the sortProc returns 0.
    161 template<typename T> static int compare(const void* untyped_lhs, const void* untyped_rhs) {
    162     const DiffRecord* lhs = *reinterpret_cast<DiffRecord* const *>(untyped_lhs);
    163     const DiffRecord* rhs = *reinterpret_cast<DiffRecord* const *>(untyped_rhs);
    164 
    165     // First-order sort... these comparisons should be applied before comparing
    166     // pixel values, no matter what.
    167     if (lhs->fResult != rhs->fResult) {
    168         return (lhs->fResult < rhs->fResult) ? 1 : -1;
    169     }
    170 
    171     // Passed first-order sort, so call the pixel comparison routine.
    172     int result = T::comparePixels(lhs, rhs);
    173     if (result != 0) {
    174         return result;
    175     }
    176 
    177     // Tiebreaker... if we got to this point, we don't really care
    178     // which order they are sorted in, but let's at least be consistent.
    179     return strcmp(lhs->fBase.fFilename.c_str(), rhs->fBase.fFilename.c_str());
    180 }
    181 
    182 /// Comparison routine for qsort; sorts by fFractionDifference
    183 /// from largest to smallest.
    184 class CompareDiffMetrics {
    185 public:
    186     static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) {
    187         if (lhs->fFractionDifference < rhs->fFractionDifference) {
    188           return 1;
    189         }
    190         if (rhs->fFractionDifference < lhs->fFractionDifference) {
    191           return -1;
    192         }
    193         return 0;
    194     }
    195 };
    196 
    197 class CompareDiffWeighted {
    198 public:
    199     static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) {
    200         if (lhs->fWeightedFraction < rhs->fWeightedFraction) {
    201             return 1;
    202         }
    203         if (lhs->fWeightedFraction > rhs->fWeightedFraction) {
    204             return -1;
    205         }
    206         return 0;
    207     }
    208 };
    209 
    210 /// Comparison routine for qsort;  sorts by max(fAverageMismatch{RGB})
    211 /// from largest to smallest.
    212 class CompareDiffMeanMismatches {
    213 public:
    214     static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) {
    215         float leftValue = MAX3(lhs->fAverageMismatchR,
    216                                lhs->fAverageMismatchG,
    217                                lhs->fAverageMismatchB);
    218         float rightValue = MAX3(rhs->fAverageMismatchR,
    219                                 rhs->fAverageMismatchG,
    220                                 rhs->fAverageMismatchB);
    221         if (leftValue < rightValue) {
    222             return 1;
    223         }
    224         if (rightValue < leftValue) {
    225             return -1;
    226         }
    227         return 0;
    228     }
    229 };
    230 
    231 /// Comparison routine for qsort;  sorts by max(fMaxMismatch{RGB})
    232 /// from largest to smallest.
    233 class CompareDiffMaxMismatches {
    234 public:
    235     static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) {
    236         uint32_t leftValue = MAX3(lhs->fMaxMismatchR,
    237                                   lhs->fMaxMismatchG,
    238                                   lhs->fMaxMismatchB);
    239         uint32_t rightValue = MAX3(rhs->fMaxMismatchR,
    240                                    rhs->fMaxMismatchG,
    241                                    rhs->fMaxMismatchB);
    242         if (leftValue < rightValue) {
    243             return 1;
    244         }
    245         if (rightValue < leftValue) {
    246             return -1;
    247         }
    248 
    249         return CompareDiffMeanMismatches::comparePixels(lhs, rhs);
    250     }
    251 };
    252 
    253 
    254 /// Parameterized routine to compute the color of a pixel in a difference image.
    255 typedef SkPMColor (*DiffMetricProc)(SkPMColor, SkPMColor);
    256 
    257 // from gm
    258 static inline SkPMColor compute_diff_pmcolor(SkPMColor c0, SkPMColor c1) {
    259     int dr = SkGetPackedR32(c0) - SkGetPackedR32(c1);
    260     int dg = SkGetPackedG32(c0) - SkGetPackedG32(c1);
    261     int db = SkGetPackedB32(c0) - SkGetPackedB32(c1);
    262 
    263     return SkPackARGB32(0xFF, SkAbs32(dr), SkAbs32(dg), SkAbs32(db));
    264 }
    265 
    266 /** When finished, dr->fResult should have some value other than kUnknown_Result.
    267  *  Expects dr->fWhite.fBitmap and dr->fDifference.fBitmap to have the same bounds as
    268  *  dr->fBase.fBitmap and have a valid pixelref.
    269  */
    270 void compute_diff(DiffRecord* dr, DiffMetricProc diffFunction, const int colorThreshold);
    271 
    272 #endif
    273