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 "SkTDArray.h" 16 17 #if 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