| epoger@google.com | ec3ed6a | 2011-07-28 14:26:00 +0000 | [diff] [blame] | 1 |  | 
 | 2 | /* | 
 | 3 |  * Copyright 2011 Google Inc. | 
 | 4 |  * | 
 | 5 |  * Use of this source code is governed by a BSD-style license that can be | 
 | 6 |  * found in the LICENSE file. | 
 | 7 |  */ | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 8 | #include "SkColorPriv.h" | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 9 | #include "SkData.h" | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 10 | #include "SkImageDecoder.h" | 
 | 11 | #include "SkImageEncoder.h" | 
 | 12 | #include "SkOSFile.h" | 
 | 13 | #include "SkStream.h" | 
 | 14 | #include "SkTDArray.h" | 
 | 15 | #include "SkTemplates.h" | 
 | 16 | #include "SkTime.h" | 
 | 17 | #include "SkTSearch.h" | 
 | 18 | #include "SkTypes.h" | 
 | 19 |  | 
 | 20 | /** | 
 | 21 |  * skdiff | 
 | 22 |  * | 
 | 23 |  * Given three directory names, expects to find identically-named files in | 
 | 24 |  * each of the first two; the first are treated as a set of baseline, | 
 | 25 |  * the second a set of variant images, and a diff image is written into the | 
 | 26 |  * third directory for each pair. | 
| tomhudson@google.com | 7d04280 | 2011-07-14 13:15:55 +0000 | [diff] [blame] | 27 |  * Creates an index.html in the current third directory to compare each | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 28 |  * pair that does not match exactly. | 
| epoger@google.com | 71329d8 | 2012-08-16 13:42:13 +0000 | [diff] [blame] | 29 |  * Recursively descends directories, unless run with --norecurse. | 
| epoger@google.com | be6188d | 2012-05-31 15:13:45 +0000 | [diff] [blame] | 30 |  * | 
 | 31 |  * Returns zero exit code if all images match across baseDir and comparisonDir. | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 32 |  */ | 
 | 33 |  | 
| bsalomon@google.com | 1a315fe | 2011-09-23 14:56:37 +0000 | [diff] [blame] | 34 | #if SK_BUILD_FOR_WIN32 | 
 | 35 |     #define PATH_DIV_STR "\\" | 
 | 36 |     #define PATH_DIV_CHAR '\\' | 
 | 37 | #else | 
 | 38 |     #define PATH_DIV_STR "/" | 
 | 39 |     #define PATH_DIV_CHAR '/' | 
 | 40 | #endif | 
 | 41 |  | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 42 | // Result of comparison for each pair of files. | 
| epoger@google.com | 28060e7 | 2012-06-28 16:47:34 +0000 | [diff] [blame] | 43 | // Listed from "better" to "worse", for sorting of results. | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 44 | enum Result { | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 45 |     kEqualBits, | 
 | 46 |     kEqualPixels, | 
 | 47 |     kDifferentPixels, | 
 | 48 |     kDifferentSizes, | 
 | 49 |     kDifferentOther, | 
 | 50 |     kComparisonMissing, | 
 | 51 |     kBaseMissing, | 
 | 52 |     kUnknown, | 
| epoger@google.com | 76222c0 | 2012-05-31 15:12:09 +0000 | [diff] [blame] | 53 |     // | 
 | 54 |     kNumResultTypes  // NOT A VALID VALUE--used to set up arrays. Must be last. | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 55 | }; | 
 | 56 |  | 
| epoger@google.com | dfbf24e | 2012-07-13 21:22:02 +0000 | [diff] [blame] | 57 | // Returns the Result with this name. | 
 | 58 | // If there is no Result with this name, returns kNumResultTypes. | 
 | 59 | // TODO: Is there a better return value for the fall-through case? | 
| bsalomon@google.com | 100abf4 | 2012-09-05 17:40:04 +0000 | [diff] [blame] | 60 | static Result getResultByName(const char *name) { | 
| epoger@google.com | dfbf24e | 2012-07-13 21:22:02 +0000 | [diff] [blame] | 61 |     if (0 == strcmp("EqualBits", name)) { | 
 | 62 |         return kEqualBits; | 
 | 63 |     } | 
 | 64 |     if (0 == strcmp("EqualPixels", name)) { | 
 | 65 |         return kEqualPixels; | 
 | 66 |     } | 
 | 67 |     if (0 == strcmp("DifferentPixels", name)) { | 
 | 68 |         return kDifferentPixels; | 
 | 69 |     } | 
 | 70 |     if (0 == strcmp("DifferentSizes", name)) { | 
 | 71 |         return kDifferentSizes; | 
 | 72 |     } | 
 | 73 |     if (0 == strcmp("DifferentOther", name)) { | 
 | 74 |         return kDifferentOther; | 
 | 75 |     } | 
 | 76 |     if (0 == strcmp("ComparisonMissing", name)) { | 
 | 77 |         return kComparisonMissing; | 
 | 78 |     } | 
 | 79 |     if (0 == strcmp("BaseMissing", name)) { | 
 | 80 |         return kBaseMissing; | 
 | 81 |     } | 
 | 82 |     if (0 == strcmp("Unknown", name)) { | 
 | 83 |         return kUnknown; | 
 | 84 |     } | 
 | 85 |     return kNumResultTypes; | 
 | 86 | } | 
 | 87 |  | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 88 | // Returns a text description of the given Result type. | 
| bsalomon@google.com | 100abf4 | 2012-09-05 17:40:04 +0000 | [diff] [blame] | 89 | static const char *getResultDescription(Result result) { | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 90 |     switch (result) { | 
 | 91 |       case kEqualBits: | 
 | 92 |         return "contain exactly the same bits"; | 
 | 93 |       case kEqualPixels: | 
 | 94 |         return "contain the same pixel values, but not the same bits"; | 
 | 95 |       case kDifferentPixels: | 
 | 96 |         return "have identical dimensions but some differing pixels"; | 
 | 97 |       case kDifferentSizes: | 
 | 98 |         return "have differing dimensions"; | 
 | 99 |       case kDifferentOther: | 
 | 100 |         return "contain different bits and are not parsable images"; | 
 | 101 |       case kBaseMissing: | 
 | 102 |         return "missing from baseDir"; | 
 | 103 |       case kComparisonMissing: | 
 | 104 |         return "missing from comparisonDir"; | 
 | 105 |       case kUnknown: | 
 | 106 |         return "not compared yet"; | 
 | 107 |       default: | 
 | 108 |         return NULL; | 
 | 109 |     } | 
 | 110 | } | 
 | 111 |  | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 112 | struct DiffRecord { | 
| tomhudson@google.com | 4e30598 | 2011-07-13 17:42:46 +0000 | [diff] [blame] | 113 |     DiffRecord (const SkString filename, | 
 | 114 |                 const SkString basePath, | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 115 |                 const SkString comparisonPath, | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 116 |                 const Result result = kUnknown) | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 117 |         : fFilename (filename) | 
| tomhudson@google.com | 4e30598 | 2011-07-13 17:42:46 +0000 | [diff] [blame] | 118 |         , fBasePath (basePath) | 
 | 119 |         , fComparisonPath (comparisonPath) | 
| tomhudson@google.com | 9b540ce | 2011-08-02 14:10:04 +0000 | [diff] [blame] | 120 |         , fBaseBitmap (new SkBitmap ()) | 
 | 121 |         , fComparisonBitmap (new SkBitmap ()) | 
 | 122 |         , fDifferenceBitmap (new SkBitmap ()) | 
| epoger@google.com | 25d961c | 2012-02-02 20:50:36 +0000 | [diff] [blame] | 123 |         , fWhiteBitmap (new SkBitmap ()) | 
| tomhudson@google.com | 9b540ce | 2011-08-02 14:10:04 +0000 | [diff] [blame] | 124 |         , fBaseHeight (0) | 
 | 125 |         , fBaseWidth (0) | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 126 |         , fFractionDifference (0) | 
 | 127 |         , fWeightedFraction (0) | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 128 |         , fAverageMismatchR (0) | 
 | 129 |         , fAverageMismatchG (0) | 
 | 130 |         , fAverageMismatchB (0) | 
 | 131 |         , fMaxMismatchR (0) | 
 | 132 |         , fMaxMismatchG (0) | 
| tomhudson@google.com | 8b08c3e | 2011-11-30 17:01:00 +0000 | [diff] [blame] | 133 |         , fMaxMismatchB (0) | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 134 |         , fResult(result) { | 
| tomhudson@google.com | 4e30598 | 2011-07-13 17:42:46 +0000 | [diff] [blame] | 135 |     }; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 136 |  | 
 | 137 |     SkString fFilename; | 
| tomhudson@google.com | 4e30598 | 2011-07-13 17:42:46 +0000 | [diff] [blame] | 138 |     SkString fBasePath; | 
 | 139 |     SkString fComparisonPath; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 140 |  | 
| tomhudson@google.com | 9b540ce | 2011-08-02 14:10:04 +0000 | [diff] [blame] | 141 |     SkBitmap* fBaseBitmap; | 
 | 142 |     SkBitmap* fComparisonBitmap; | 
 | 143 |     SkBitmap* fDifferenceBitmap; | 
| epoger@google.com | 25d961c | 2012-02-02 20:50:36 +0000 | [diff] [blame] | 144 |     SkBitmap* fWhiteBitmap; | 
| tomhudson@google.com | 9b540ce | 2011-08-02 14:10:04 +0000 | [diff] [blame] | 145 |  | 
 | 146 |     int fBaseHeight; | 
 | 147 |     int fBaseWidth; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 148 |  | 
 | 149 |     /// Arbitrary floating-point metric to be used to sort images from most | 
 | 150 |     /// to least different from baseline; values of 0 will be omitted from the | 
 | 151 |     /// summary webpage. | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 152 |     float fFractionDifference; | 
 | 153 |     float fWeightedFraction; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 154 |  | 
 | 155 |     float fAverageMismatchR; | 
 | 156 |     float fAverageMismatchG; | 
 | 157 |     float fAverageMismatchB; | 
 | 158 |  | 
 | 159 |     uint32_t fMaxMismatchR; | 
 | 160 |     uint32_t fMaxMismatchG; | 
 | 161 |     uint32_t fMaxMismatchB; | 
| tomhudson@google.com | 8b08c3e | 2011-11-30 17:01:00 +0000 | [diff] [blame] | 162 |  | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 163 |     /// Which category of diff result. | 
 | 164 |     Result fResult; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 165 | }; | 
 | 166 |  | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 167 | #define MAX2(a,b) (((b) < (a)) ? (a) : (b)) | 
 | 168 | #define MAX3(a,b,c) (((b) < (a)) ? MAX2((a), (c)) : MAX2((b), (c))) | 
 | 169 |  | 
| epoger@google.com | 25d961c | 2012-02-02 20:50:36 +0000 | [diff] [blame] | 170 | const SkPMColor PMCOLOR_WHITE = SkPreMultiplyColor(SK_ColorWHITE); | 
 | 171 | const SkPMColor PMCOLOR_BLACK = SkPreMultiplyColor(SK_ColorBLACK); | 
 | 172 |  | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 173 | typedef SkTDArray<SkString*> StringArray; | 
 | 174 | typedef StringArray FileArray; | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 175 |  | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 176 | struct DiffSummary { | 
 | 177 |     DiffSummary () | 
 | 178 |         : fNumMatches (0) | 
 | 179 |         , fNumMismatches (0) | 
 | 180 |         , fMaxMismatchV (0) | 
 | 181 |         , fMaxMismatchPercent (0) { }; | 
 | 182 |  | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 183 |     ~DiffSummary() { | 
| epoger@google.com | 76222c0 | 2012-05-31 15:12:09 +0000 | [diff] [blame] | 184 |         for (int i = 0; i < kNumResultTypes; i++) { | 
 | 185 |             fResultsOfType[i].deleteAll(); | 
 | 186 |         } | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 187 |     } | 
 | 188 |  | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 189 |     uint32_t fNumMatches; | 
 | 190 |     uint32_t fNumMismatches; | 
 | 191 |     uint32_t fMaxMismatchV; | 
 | 192 |     float fMaxMismatchPercent; | 
 | 193 |  | 
| epoger@google.com | 76222c0 | 2012-05-31 15:12:09 +0000 | [diff] [blame] | 194 |     FileArray fResultsOfType[kNumResultTypes]; | 
 | 195 |  | 
| epoger@google.com | 3af4ff4 | 2012-07-19 17:35:04 +0000 | [diff] [blame] | 196 |     // Print a line about the contents of this FileArray to stdout. | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 197 |     void printContents(const FileArray& fileArray, const char* headerText, bool listFilenames) { | 
| epoger@google.com | 76222c0 | 2012-05-31 15:12:09 +0000 | [diff] [blame] | 198 |         int n = fileArray.count(); | 
| epoger@google.com | 3af4ff4 | 2012-07-19 17:35:04 +0000 | [diff] [blame] | 199 |         printf("%d file pairs %s", n, headerText); | 
 | 200 |         if (listFilenames) { | 
 | 201 |             printf(": "); | 
 | 202 |             for (int i = 0; i < n; ++i) { | 
 | 203 |                 printf("%s ", fileArray[i]->c_str()); | 
| epoger@google.com | 76222c0 | 2012-05-31 15:12:09 +0000 | [diff] [blame] | 204 |             } | 
 | 205 |         } | 
| epoger@google.com | 3af4ff4 | 2012-07-19 17:35:04 +0000 | [diff] [blame] | 206 |         printf("\n"); | 
| epoger@google.com | 76222c0 | 2012-05-31 15:12:09 +0000 | [diff] [blame] | 207 |     } | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 208 |  | 
| epoger@google.com | 3af4ff4 | 2012-07-19 17:35:04 +0000 | [diff] [blame] | 209 |     void print(bool listFilenames, bool failOnResultType[kNumResultTypes]) { | 
 | 210 |         printf("\ncompared %d file pairs:\n", fNumMatches + fNumMismatches); | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 211 |         for (int resultInt = 0; resultInt < kNumResultTypes; resultInt++) { | 
 | 212 |             Result result = static_cast<Result>(resultInt); | 
| epoger@google.com | 3af4ff4 | 2012-07-19 17:35:04 +0000 | [diff] [blame] | 213 |             if (failOnResultType[result]) { | 
 | 214 |                 printf("[*] "); | 
 | 215 |             } else { | 
 | 216 |                 printf("[_] "); | 
 | 217 |             } | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 218 |             printContents(fResultsOfType[result], getResultDescription(result), listFilenames); | 
 | 219 |         } | 
| epoger@google.com | 3af4ff4 | 2012-07-19 17:35:04 +0000 | [diff] [blame] | 220 |         printf("(results marked with [*] will cause nonzero return value)\n"); | 
 | 221 |         printf("\nnumber of mismatching file pairs: %d\n", fNumMismatches); | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 222 |         if (fNumMismatches > 0) { | 
 | 223 |             printf("Maximum pixel intensity mismatch %d\n", fMaxMismatchV); | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 224 |             printf("Largest area mismatch was %.2f%% of pixels\n",fMaxMismatchPercent); | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 225 |         } | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 226 |     } | 
 | 227 |  | 
 | 228 |     void add (DiffRecord* drp) { | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 229 |         uint32_t mismatchValue; | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 230 |  | 
| epoger@google.com | 76222c0 | 2012-05-31 15:12:09 +0000 | [diff] [blame] | 231 |         fResultsOfType[drp->fResult].push(new SkString(drp->fFilename)); | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 232 |         switch (drp->fResult) { | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 233 |           case kEqualBits: | 
 | 234 |             fNumMatches++; | 
 | 235 |             break; | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 236 |           case kEqualPixels: | 
 | 237 |             fNumMatches++; | 
 | 238 |             break; | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 239 |           case kDifferentSizes: | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 240 |             fNumMismatches++; | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 241 |             break; | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 242 |           case kDifferentPixels: | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 243 |             fNumMismatches++; | 
 | 244 |             if (drp->fFractionDifference * 100 > fMaxMismatchPercent) { | 
 | 245 |                 fMaxMismatchPercent = drp->fFractionDifference * 100; | 
 | 246 |             } | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 247 |             mismatchValue = MAX3(drp->fMaxMismatchR, drp->fMaxMismatchG, | 
 | 248 |                                  drp->fMaxMismatchB); | 
 | 249 |             if (mismatchValue > fMaxMismatchV) { | 
 | 250 |                 fMaxMismatchV = mismatchValue; | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 251 |             } | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 252 |             break; | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 253 |           case kDifferentOther: | 
 | 254 |             fNumMismatches++; | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 255 |             break; | 
 | 256 |           case kBaseMissing: | 
 | 257 |             fNumMismatches++; | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 258 |             break; | 
 | 259 |           case kComparisonMissing: | 
 | 260 |             fNumMismatches++; | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 261 |             break; | 
 | 262 |           case kUnknown: | 
 | 263 |             SkDEBUGFAIL("adding uncategorized DiffRecord"); | 
 | 264 |             break; | 
 | 265 |           default: | 
 | 266 |             SkDEBUGFAIL("adding DiffRecord with unhandled fResult value"); | 
 | 267 |             break; | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 268 |         } | 
 | 269 |     } | 
 | 270 | }; | 
 | 271 |  | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 272 | typedef SkTDArray<DiffRecord*> RecordArray; | 
 | 273 |  | 
| epoger@google.com | 28060e7 | 2012-06-28 16:47:34 +0000 | [diff] [blame] | 274 | /// A wrapper for any sortProc (comparison routine) which applies a first-order | 
 | 275 | /// sort beforehand, and a tiebreaker if the sortProc returns 0. | 
 | 276 | template<typename T> | 
 | 277 | static int compare(const void* untyped_lhs, const void* untyped_rhs) { | 
 | 278 |     const DiffRecord* lhs = *reinterpret_cast<DiffRecord* const*>(untyped_lhs); | 
 | 279 |     const DiffRecord* rhs = *reinterpret_cast<DiffRecord* const*>(untyped_rhs); | 
 | 280 |  | 
 | 281 |     // First-order sort... these comparisons should be applied before comparing | 
 | 282 |     // pixel values, no matter what. | 
 | 283 |     if (lhs->fResult != rhs->fResult) { | 
 | 284 |         return (lhs->fResult < rhs->fResult) ? 1 : -1; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 285 |     } | 
| epoger@google.com | 28060e7 | 2012-06-28 16:47:34 +0000 | [diff] [blame] | 286 |  | 
 | 287 |     // Passed first-order sort, so call the pixel comparison routine. | 
 | 288 |     int result = T::comparePixels(lhs, rhs); | 
 | 289 |     if (result != 0) { | 
 | 290 |         return result; | 
| tomhudson@google.com | 5b32529 | 2011-05-24 19:41:13 +0000 | [diff] [blame] | 291 |     } | 
| epoger@google.com | 28060e7 | 2012-06-28 16:47:34 +0000 | [diff] [blame] | 292 |  | 
 | 293 |     // Tiebreaker... if we got to this point, we don't really care | 
 | 294 |     // which order they are sorted in, but let's at least be consistent. | 
 | 295 |     return strcmp(lhs->fFilename.c_str(), rhs->fFilename.c_str()); | 
| tomhudson@google.com | 5b32529 | 2011-05-24 19:41:13 +0000 | [diff] [blame] | 296 | } | 
 | 297 |  | 
| epoger@google.com | 28060e7 | 2012-06-28 16:47:34 +0000 | [diff] [blame] | 298 | /// Comparison routine for qsort;  sorts by fFractionDifference | 
 | 299 | /// from largest to smallest. | 
 | 300 | class CompareDiffMetrics { | 
 | 301 | public: | 
 | 302 |     static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) { | 
 | 303 |         if (lhs->fFractionDifference < rhs->fFractionDifference) { | 
 | 304 |           return 1; | 
 | 305 |         } | 
 | 306 |         if (rhs->fFractionDifference < lhs->fFractionDifference) { | 
 | 307 |           return -1; | 
 | 308 |         } | 
 | 309 |         return 0; | 
| tomhudson@google.com | 5b32529 | 2011-05-24 19:41:13 +0000 | [diff] [blame] | 310 |     } | 
| epoger@google.com | 28060e7 | 2012-06-28 16:47:34 +0000 | [diff] [blame] | 311 | }; | 
 | 312 |  | 
 | 313 | class CompareDiffWeighted { | 
 | 314 | public: | 
 | 315 |     static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) { | 
 | 316 |         if (lhs->fWeightedFraction < rhs->fWeightedFraction) { | 
 | 317 |             return 1; | 
 | 318 |         } | 
 | 319 |         if (lhs->fWeightedFraction > rhs->fWeightedFraction) { | 
 | 320 |             return -1; | 
 | 321 |         } | 
 | 322 |         return 0; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 323 |     } | 
| epoger@google.com | 28060e7 | 2012-06-28 16:47:34 +0000 | [diff] [blame] | 324 | }; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 325 |  | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 326 | /// Comparison routine for qsort;  sorts by max(fAverageMismatch{RGB}) | 
 | 327 | /// from largest to smallest. | 
| epoger@google.com | 28060e7 | 2012-06-28 16:47:34 +0000 | [diff] [blame] | 328 | class CompareDiffMeanMismatches { | 
 | 329 | public: | 
 | 330 |     static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) { | 
 | 331 |         float leftValue = MAX3(lhs->fAverageMismatchR, | 
 | 332 |                                lhs->fAverageMismatchG, | 
 | 333 |                                lhs->fAverageMismatchB); | 
 | 334 |         float rightValue = MAX3(rhs->fAverageMismatchR, | 
 | 335 |                                 rhs->fAverageMismatchG, | 
 | 336 |                                 rhs->fAverageMismatchB); | 
 | 337 |         if (leftValue < rightValue) { | 
 | 338 |             return 1; | 
 | 339 |         } | 
 | 340 |         if (rightValue < leftValue) { | 
 | 341 |             return -1; | 
 | 342 |         } | 
 | 343 |         return 0; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 344 |     } | 
| epoger@google.com | 28060e7 | 2012-06-28 16:47:34 +0000 | [diff] [blame] | 345 | }; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 346 |  | 
 | 347 | /// Comparison routine for qsort;  sorts by max(fMaxMismatch{RGB}) | 
 | 348 | /// from largest to smallest. | 
| epoger@google.com | 28060e7 | 2012-06-28 16:47:34 +0000 | [diff] [blame] | 349 | class CompareDiffMaxMismatches { | 
 | 350 | public: | 
 | 351 |     static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) { | 
 | 352 |         uint32_t leftValue = MAX3(lhs->fMaxMismatchR, | 
 | 353 |                                   lhs->fMaxMismatchG, | 
 | 354 |                                   lhs->fMaxMismatchB); | 
 | 355 |         uint32_t rightValue = MAX3(rhs->fMaxMismatchR, | 
 | 356 |                                    rhs->fMaxMismatchG, | 
 | 357 |                                    rhs->fMaxMismatchB); | 
 | 358 |         if (leftValue < rightValue) { | 
 | 359 |             return 1; | 
 | 360 |         } | 
 | 361 |         if (rightValue < leftValue) { | 
 | 362 |             return -1; | 
 | 363 |         } | 
 | 364 |  | 
 | 365 |         return CompareDiffMeanMismatches::comparePixels(lhs, rhs); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 366 |     } | 
| epoger@google.com | 28060e7 | 2012-06-28 16:47:34 +0000 | [diff] [blame] | 367 | }; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 368 |  | 
 | 369 |  | 
 | 370 |  | 
 | 371 | /// Parameterized routine to compute the color of a pixel in a difference image. | 
 | 372 | typedef SkPMColor (*DiffMetricProc)(SkPMColor, SkPMColor); | 
 | 373 |  | 
| caryclark@google.com | 3dd4591 | 2012-06-06 12:11:10 +0000 | [diff] [blame] | 374 | #if 0 // UNUSED | 
| tomhudson@google.com | 8b08c3e | 2011-11-30 17:01:00 +0000 | [diff] [blame] | 375 | static void expand_and_copy (int width, int height, SkBitmap** dest) { | 
 | 376 |     SkBitmap* temp = new SkBitmap (); | 
 | 377 |     temp->reset(); | 
 | 378 |     temp->setConfig((*dest)->config(), width, height); | 
 | 379 |     temp->allocPixels(); | 
 | 380 |     (*dest)->copyPixelsTo(temp->getPixels(), temp->getSize(), | 
 | 381 |                           temp->rowBytes()); | 
 | 382 |     *dest = temp; | 
 | 383 | } | 
| caryclark@google.com | 3dd4591 | 2012-06-06 12:11:10 +0000 | [diff] [blame] | 384 | #endif | 
| tomhudson@google.com | 8b08c3e | 2011-11-30 17:01:00 +0000 | [diff] [blame] | 385 |  | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 386 | /// Returns true if the two buffers passed in are both non-NULL, and include | 
 | 387 | /// exactly the same byte values (and identical lengths). | 
 | 388 | static bool are_buffers_equal(SkData* skdata1, SkData* skdata2) { | 
 | 389 |     if ((NULL == skdata1) || (NULL == skdata2)) { | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 390 |         return false; | 
 | 391 |     } | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 392 |     if (skdata1->size() != skdata2->size()) { | 
 | 393 |         return false; | 
 | 394 |     } | 
 | 395 |     return (0 == memcmp(skdata1->data(), skdata2->data(), skdata1->size())); | 
 | 396 | } | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 397 |  | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 398 | /// Reads the file at the given path and returns its complete contents as an | 
 | 399 | /// SkData object (or returns NULL on error). | 
 | 400 | static SkData* read_file(const char* file_path) { | 
 | 401 |     SkFILEStream fileStream(file_path); | 
 | 402 |     if (!fileStream.isValid()) { | 
 | 403 |         SkDebugf("WARNING: could not open file <%s> for reading\n", file_path); | 
 | 404 |         return NULL; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 405 |     } | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 406 |     size_t bytesInFile = fileStream.getLength(); | 
 | 407 |     size_t bytesLeftToRead = bytesInFile; | 
 | 408 |  | 
 | 409 |     void* bufferStart = sk_malloc_throw(bytesInFile); | 
 | 410 |     char* bufferPointer = (char*)bufferStart; | 
 | 411 |     while (bytesLeftToRead > 0) { | 
 | 412 |         size_t bytesReadThisTime = fileStream.read( | 
 | 413 |             bufferPointer, bytesLeftToRead); | 
 | 414 |         if (0 == bytesReadThisTime) { | 
 | 415 |             SkDebugf("WARNING: error reading from <%s>\n", file_path); | 
 | 416 |             sk_free(bufferStart); | 
 | 417 |             return NULL; | 
 | 418 |         } | 
 | 419 |         bytesLeftToRead -= bytesReadThisTime; | 
 | 420 |         bufferPointer += bytesReadThisTime; | 
 | 421 |     } | 
 | 422 |     return SkData::NewFromMalloc(bufferStart, bytesInFile); | 
 | 423 | } | 
 | 424 |  | 
 | 425 | /// Decodes binary contents of baseFile and comparisonFile into | 
 | 426 | /// diffRecord->fBaseBitmap and diffRecord->fComparisonBitmap. | 
 | 427 | /// Returns true if that succeeds. | 
 | 428 | static bool get_bitmaps (SkData* baseFileContents, | 
 | 429 |                          SkData* comparisonFileContents, | 
 | 430 |                          DiffRecord* diffRecord) { | 
 | 431 |     SkMemoryStream compareStream(comparisonFileContents->data(), | 
 | 432 |                                  comparisonFileContents->size()); | 
 | 433 |     SkMemoryStream baseStream(baseFileContents->data(), | 
 | 434 |                               baseFileContents->size()); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 435 |  | 
 | 436 |     SkImageDecoder* codec = SkImageDecoder::Factory(&baseStream); | 
 | 437 |     if (NULL == codec) { | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 438 |         SkDebugf("ERROR: no codec found for basePath <%s>\n", | 
| tomhudson@google.com | 4e30598 | 2011-07-13 17:42:46 +0000 | [diff] [blame] | 439 |                  diffRecord->fBasePath.c_str()); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 440 |         return false; | 
 | 441 |     } | 
 | 442 |  | 
| tomhudson@google.com | 8b08c3e | 2011-11-30 17:01:00 +0000 | [diff] [blame] | 443 |     // In debug, the DLL will automatically be unloaded when this is deleted, | 
 | 444 |     // but that shouldn't be a problem in release mode. | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 445 |     SkAutoTDelete<SkImageDecoder> ad(codec); | 
 | 446 |  | 
 | 447 |     baseStream.rewind(); | 
| tomhudson@google.com | 9b540ce | 2011-08-02 14:10:04 +0000 | [diff] [blame] | 448 |     if (!codec->decode(&baseStream, diffRecord->fBaseBitmap, | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 449 |                        SkBitmap::kARGB_8888_Config, | 
 | 450 |                        SkImageDecoder::kDecodePixels_Mode)) { | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 451 |         SkDebugf("ERROR: codec failed for basePath <%s>\n", | 
| tomhudson@google.com | 4e30598 | 2011-07-13 17:42:46 +0000 | [diff] [blame] | 452 |                  diffRecord->fBasePath.c_str()); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 453 |         return false; | 
 | 454 |     } | 
 | 455 |  | 
| tomhudson@google.com | 9b540ce | 2011-08-02 14:10:04 +0000 | [diff] [blame] | 456 |     diffRecord->fBaseWidth = diffRecord->fBaseBitmap->width(); | 
 | 457 |     diffRecord->fBaseHeight = diffRecord->fBaseBitmap->height(); | 
 | 458 |  | 
 | 459 |     if (!codec->decode(&compareStream, diffRecord->fComparisonBitmap, | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 460 |                        SkBitmap::kARGB_8888_Config, | 
 | 461 |                        SkImageDecoder::kDecodePixels_Mode)) { | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 462 |         SkDebugf("ERROR: codec failed for comparisonPath <%s>\n", | 
| tomhudson@google.com | 4e30598 | 2011-07-13 17:42:46 +0000 | [diff] [blame] | 463 |                  diffRecord->fComparisonPath.c_str()); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 464 |         return false; | 
 | 465 |     } | 
 | 466 |  | 
 | 467 |     return true; | 
 | 468 | } | 
 | 469 |  | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 470 | static bool get_bitmap_height_width(const SkString& path, | 
 | 471 |                                     int *height, int *width) { | 
 | 472 |     SkFILEStream stream(path.c_str()); | 
 | 473 |     if (!stream.isValid()) { | 
 | 474 |         SkDebugf("ERROR: couldn't open file <%s>\n", | 
 | 475 |                  path.c_str()); | 
 | 476 |         return false; | 
 | 477 |     } | 
 | 478 |  | 
 | 479 |     SkImageDecoder* codec = SkImageDecoder::Factory(&stream); | 
 | 480 |     if (NULL == codec) { | 
 | 481 |         SkDebugf("ERROR: no codec found for <%s>\n", | 
 | 482 |                  path.c_str()); | 
 | 483 |         return false; | 
 | 484 |     } | 
 | 485 |  | 
 | 486 |     SkAutoTDelete<SkImageDecoder> ad(codec); | 
 | 487 |     SkBitmap bm; | 
 | 488 |  | 
 | 489 |     stream.rewind(); | 
 | 490 |     if (!codec->decode(&stream, &bm, | 
 | 491 |                        SkBitmap::kARGB_8888_Config, | 
 | 492 |                        SkImageDecoder::kDecodePixels_Mode)) { | 
 | 493 |         SkDebugf("ERROR: codec failed for <%s>\n", | 
 | 494 |                  path.c_str()); | 
 | 495 |         return false; | 
 | 496 |     } | 
 | 497 |  | 
 | 498 |     *height = bm.height(); | 
 | 499 |     *width = bm.width(); | 
 | 500 |  | 
 | 501 |     return true; | 
 | 502 | } | 
 | 503 |  | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 504 | // from gm - thanks to PNG, we need to force all pixels 100% opaque | 
 | 505 | static void force_all_opaque(const SkBitmap& bitmap) { | 
 | 506 |    SkAutoLockPixels lock(bitmap); | 
 | 507 |    for (int y = 0; y < bitmap.height(); y++) { | 
 | 508 |        for (int x = 0; x < bitmap.width(); x++) { | 
 | 509 |            *bitmap.getAddr32(x, y) |= (SK_A32_MASK << SK_A32_SHIFT); | 
 | 510 |        } | 
 | 511 |    } | 
 | 512 | } | 
 | 513 |  | 
 | 514 | // from gm | 
| tomhudson@google.com | 9b540ce | 2011-08-02 14:10:04 +0000 | [diff] [blame] | 515 | static bool write_bitmap(const SkString& path, const SkBitmap* bitmap) { | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 516 |     SkBitmap copy; | 
| tomhudson@google.com | 9b540ce | 2011-08-02 14:10:04 +0000 | [diff] [blame] | 517 |     bitmap->copyTo(©, SkBitmap::kARGB_8888_Config); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 518 |     force_all_opaque(copy); | 
 | 519 |     return SkImageEncoder::EncodeFile(path.c_str(), copy, | 
 | 520 |                                       SkImageEncoder::kPNG_Type, 100); | 
 | 521 | } | 
 | 522 |  | 
 | 523 | // from gm | 
 | 524 | static inline SkPMColor compute_diff_pmcolor(SkPMColor c0, SkPMColor c1) { | 
 | 525 |     int dr = SkGetPackedR32(c0) - SkGetPackedR32(c1); | 
 | 526 |     int dg = SkGetPackedG32(c0) - SkGetPackedG32(c1); | 
 | 527 |     int db = SkGetPackedB32(c0) - SkGetPackedB32(c1); | 
 | 528 |  | 
 | 529 |     return SkPackARGB32(0xFF, SkAbs32(dr), SkAbs32(dg), SkAbs32(db)); | 
 | 530 | } | 
 | 531 |  | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 532 | static inline bool colors_match_thresholded(SkPMColor c0, SkPMColor c1, | 
 | 533 |                                             const int threshold) { | 
 | 534 |     int da = SkGetPackedA32(c0) - SkGetPackedA32(c1); | 
 | 535 |     int dr = SkGetPackedR32(c0) - SkGetPackedR32(c1); | 
 | 536 |     int dg = SkGetPackedG32(c0) - SkGetPackedG32(c1); | 
 | 537 |     int db = SkGetPackedB32(c0) - SkGetPackedB32(c1); | 
 | 538 |  | 
 | 539 |     return ((SkAbs32(da) <= threshold) && | 
 | 540 |             (SkAbs32(dr) <= threshold) && | 
 | 541 |             (SkAbs32(dg) <= threshold) && | 
 | 542 |             (SkAbs32(db) <= threshold)); | 
 | 543 | } | 
 | 544 |  | 
 | 545 | // based on gm | 
| epoger@google.com | 6600852 | 2012-05-16 17:40:57 +0000 | [diff] [blame] | 546 | // Postcondition: when we exit this method, dr->fResult should have some value | 
 | 547 | // other than kUnknown. | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 548 | static void compute_diff(DiffRecord* dr, | 
 | 549 |                          DiffMetricProc diffFunction, | 
 | 550 |                          const int colorThreshold) { | 
| epoger@google.com | 25d961c | 2012-02-02 20:50:36 +0000 | [diff] [blame] | 551 |     SkAutoLockPixels alpDiff(*dr->fDifferenceBitmap); | 
 | 552 |     SkAutoLockPixels alpWhite(*dr->fWhiteBitmap); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 553 |  | 
| tomhudson@google.com | 9b540ce | 2011-08-02 14:10:04 +0000 | [diff] [blame] | 554 |     const int w = dr->fComparisonBitmap->width(); | 
 | 555 |     const int h = dr->fComparisonBitmap->height(); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 556 |     int mismatchedPixels = 0; | 
 | 557 |     int totalMismatchR = 0; | 
 | 558 |     int totalMismatchG = 0; | 
 | 559 |     int totalMismatchB = 0; | 
| tomhudson@google.com | 8b08c3e | 2011-11-30 17:01:00 +0000 | [diff] [blame] | 560 |  | 
 | 561 |     if (w != dr->fBaseWidth || h != dr->fBaseHeight) { | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 562 |         dr->fResult = kDifferentSizes; | 
| tomhudson@google.com | 8b08c3e | 2011-11-30 17:01:00 +0000 | [diff] [blame] | 563 |         return; | 
 | 564 |     } | 
| tomhudson@google.com | 5b32529 | 2011-05-24 19:41:13 +0000 | [diff] [blame] | 565 |     // Accumulate fractionally different pixels, then divide out | 
 | 566 |     // # of pixels at the end. | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 567 |     dr->fWeightedFraction = 0; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 568 |     for (int y = 0; y < h; y++) { | 
 | 569 |         for (int x = 0; x < w; x++) { | 
| tomhudson@google.com | 9b540ce | 2011-08-02 14:10:04 +0000 | [diff] [blame] | 570 |             SkPMColor c0 = *dr->fBaseBitmap->getAddr32(x, y); | 
 | 571 |             SkPMColor c1 = *dr->fComparisonBitmap->getAddr32(x, y); | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 572 |             SkPMColor trueDifference = compute_diff_pmcolor(c0, c1); | 
 | 573 |             SkPMColor outputDifference = diffFunction(c0, c1); | 
 | 574 |             uint32_t thisR = SkGetPackedR32(trueDifference); | 
 | 575 |             uint32_t thisG = SkGetPackedG32(trueDifference); | 
 | 576 |             uint32_t thisB = SkGetPackedB32(trueDifference); | 
| tomhudson@google.com | 5b32529 | 2011-05-24 19:41:13 +0000 | [diff] [blame] | 577 |             totalMismatchR += thisR; | 
 | 578 |             totalMismatchG += thisG; | 
 | 579 |             totalMismatchB += thisB; | 
 | 580 |             // In HSV, value is defined as max RGB component. | 
 | 581 |             int value = MAX3(thisR, thisG, thisB); | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 582 |             dr->fWeightedFraction += ((float) value) / 255; | 
| tomhudson@google.com | 5b32529 | 2011-05-24 19:41:13 +0000 | [diff] [blame] | 583 |             if (thisR > dr->fMaxMismatchR) { | 
 | 584 |                 dr->fMaxMismatchR = thisR; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 585 |             } | 
| tomhudson@google.com | 5b32529 | 2011-05-24 19:41:13 +0000 | [diff] [blame] | 586 |             if (thisG > dr->fMaxMismatchG) { | 
 | 587 |                 dr->fMaxMismatchG = thisG; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 588 |             } | 
| tomhudson@google.com | 5b32529 | 2011-05-24 19:41:13 +0000 | [diff] [blame] | 589 |             if (thisB > dr->fMaxMismatchB) { | 
 | 590 |                 dr->fMaxMismatchB = thisB; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 591 |             } | 
 | 592 |             if (!colors_match_thresholded(c0, c1, colorThreshold)) { | 
 | 593 |                 mismatchedPixels++; | 
| tomhudson@google.com | 9b540ce | 2011-08-02 14:10:04 +0000 | [diff] [blame] | 594 |                 *dr->fDifferenceBitmap->getAddr32(x, y) = outputDifference; | 
| epoger@google.com | 25d961c | 2012-02-02 20:50:36 +0000 | [diff] [blame] | 595 |                 *dr->fWhiteBitmap->getAddr32(x, y) = PMCOLOR_WHITE; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 596 |             } else { | 
| tomhudson@google.com | 9b540ce | 2011-08-02 14:10:04 +0000 | [diff] [blame] | 597 |                 *dr->fDifferenceBitmap->getAddr32(x, y) = 0; | 
| epoger@google.com | 25d961c | 2012-02-02 20:50:36 +0000 | [diff] [blame] | 598 |                 *dr->fWhiteBitmap->getAddr32(x, y) = PMCOLOR_BLACK; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 599 |             } | 
 | 600 |         } | 
 | 601 |     } | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 602 |     if (0 == mismatchedPixels) { | 
 | 603 |         dr->fResult = kEqualPixels; | 
 | 604 |         return; | 
 | 605 |     } | 
 | 606 |     dr->fResult = kDifferentPixels; | 
| tomhudson@google.com | 5b32529 | 2011-05-24 19:41:13 +0000 | [diff] [blame] | 607 |     int pixelCount = w * h; | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 608 |     dr->fFractionDifference = ((float) mismatchedPixels) / pixelCount; | 
 | 609 |     dr->fWeightedFraction /= pixelCount; | 
| tomhudson@google.com | 5b32529 | 2011-05-24 19:41:13 +0000 | [diff] [blame] | 610 |     dr->fAverageMismatchR = ((float) totalMismatchR) / pixelCount; | 
 | 611 |     dr->fAverageMismatchG = ((float) totalMismatchG) / pixelCount; | 
 | 612 |     dr->fAverageMismatchB = ((float) totalMismatchB) / pixelCount; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 613 | } | 
 | 614 |  | 
| epoger@google.com | 71329d8 | 2012-08-16 13:42:13 +0000 | [diff] [blame] | 615 | /// Return a copy of the "input" string, within which we have replaced all instances | 
 | 616 | /// of oldSubstring with newSubstring. | 
 | 617 | /// | 
 | 618 | /// TODO: If we like this, we should move it into the core SkString implementation, | 
 | 619 | /// adding more checks and ample test cases, and paying more attention to efficiency. | 
 | 620 | static SkString replace_all(const SkString &input, | 
 | 621 |                             const char oldSubstring[], const char newSubstring[]) { | 
 | 622 |     SkString output; | 
 | 623 |     const char *input_cstr = input.c_str(); | 
 | 624 |     const char *first_char = input_cstr; | 
 | 625 |     const char *match_char; | 
 | 626 |     int oldSubstringLen = strlen(oldSubstring); | 
 | 627 |     while (NULL != (match_char = strstr(first_char, oldSubstring))) { | 
 | 628 |         output.append(first_char, (match_char - first_char)); | 
 | 629 |         output.append(newSubstring); | 
 | 630 |         first_char = match_char + oldSubstringLen; | 
 | 631 |     } | 
 | 632 |     output.append(first_char); | 
 | 633 |     return output; | 
 | 634 | } | 
 | 635 |  | 
| epoger@google.com | 25d961c | 2012-02-02 20:50:36 +0000 | [diff] [blame] | 636 | static SkString filename_to_derived_filename (const SkString& filename, | 
 | 637 |                                               const char *suffix) { | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 638 |     SkString diffName (filename); | 
 | 639 |     const char* cstring = diffName.c_str(); | 
 | 640 |     int dotOffset = strrchr(cstring, '.') - cstring; | 
 | 641 |     diffName.remove(dotOffset, diffName.size() - dotOffset); | 
| epoger@google.com | 25d961c | 2012-02-02 20:50:36 +0000 | [diff] [blame] | 642 |     diffName.append(suffix); | 
| epoger@google.com | 71329d8 | 2012-08-16 13:42:13 +0000 | [diff] [blame] | 643 |  | 
 | 644 |     // In case we recursed into subdirectories, replace slashes with something else | 
 | 645 |     // so the diffs will all be written into a single flat directory. | 
 | 646 |     diffName = replace_all(diffName, PATH_DIV_STR, "_"); | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 647 |     return diffName; | 
 | 648 | } | 
 | 649 |  | 
| epoger@google.com | 25d961c | 2012-02-02 20:50:36 +0000 | [diff] [blame] | 650 | /// Given a image filename, returns the name of the file containing the | 
 | 651 | /// associated difference image. | 
 | 652 | static SkString filename_to_diff_filename (const SkString& filename) { | 
 | 653 |     return filename_to_derived_filename(filename, "-diff.png"); | 
 | 654 | } | 
 | 655 |  | 
 | 656 | /// Given a image filename, returns the name of the file containing the | 
 | 657 | /// "white" difference image. | 
 | 658 | static SkString filename_to_white_filename (const SkString& filename) { | 
 | 659 |     return filename_to_derived_filename(filename, "-white.png"); | 
 | 660 | } | 
 | 661 |  | 
| tomhudson@google.com | 9b540ce | 2011-08-02 14:10:04 +0000 | [diff] [blame] | 662 | static void release_bitmaps(DiffRecord* drp) { | 
 | 663 |     delete drp->fBaseBitmap; | 
 | 664 |     drp->fBaseBitmap = NULL; | 
 | 665 |     delete drp->fComparisonBitmap; | 
 | 666 |     drp->fComparisonBitmap = NULL; | 
 | 667 |     delete drp->fDifferenceBitmap; | 
 | 668 |     drp->fDifferenceBitmap = NULL; | 
| epoger@google.com | 25d961c | 2012-02-02 20:50:36 +0000 | [diff] [blame] | 669 |     delete drp->fWhiteBitmap; | 
 | 670 |     drp->fWhiteBitmap = NULL; | 
| tomhudson@google.com | 9b540ce | 2011-08-02 14:10:04 +0000 | [diff] [blame] | 671 | } | 
 | 672 |  | 
| tomhudson@google.com | 7d04280 | 2011-07-14 13:15:55 +0000 | [diff] [blame] | 673 |  | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 674 | /// If outputDir.isEmpty(), don't write out diff files. | 
| tomhudson@google.com | 7d04280 | 2011-07-14 13:15:55 +0000 | [diff] [blame] | 675 | static void create_and_write_diff_image(DiffRecord* drp, | 
 | 676 |                                         DiffMetricProc dmp, | 
 | 677 |                                         const int colorThreshold, | 
 | 678 |                                         const SkString& outputDir, | 
 | 679 |                                         const SkString& filename) { | 
| tomhudson@google.com | 9b540ce | 2011-08-02 14:10:04 +0000 | [diff] [blame] | 680 |     const int w = drp->fBaseWidth; | 
 | 681 |     const int h = drp->fBaseHeight; | 
 | 682 |     drp->fDifferenceBitmap->setConfig(SkBitmap::kARGB_8888_Config, w, h); | 
 | 683 |     drp->fDifferenceBitmap->allocPixels(); | 
| epoger@google.com | 25d961c | 2012-02-02 20:50:36 +0000 | [diff] [blame] | 684 |     drp->fWhiteBitmap->setConfig(SkBitmap::kARGB_8888_Config, w, h); | 
 | 685 |     drp->fWhiteBitmap->allocPixels(); | 
| tomhudson@google.com | 7d04280 | 2011-07-14 13:15:55 +0000 | [diff] [blame] | 686 |  | 
| epoger@google.com | 6600852 | 2012-05-16 17:40:57 +0000 | [diff] [blame] | 687 |     SkASSERT(kUnknown == drp->fResult); | 
 | 688 |     compute_diff(drp, dmp, colorThreshold); | 
 | 689 |     SkASSERT(kUnknown != drp->fResult); | 
 | 690 |  | 
 | 691 |     if ((kDifferentPixels == drp->fResult) && !outputDir.isEmpty()) { | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 692 |         SkString differencePath (outputDir); | 
 | 693 |         differencePath.append(filename_to_diff_filename(filename)); | 
 | 694 |         write_bitmap(differencePath, drp->fDifferenceBitmap); | 
 | 695 |         SkString whitePath (outputDir); | 
 | 696 |         whitePath.append(filename_to_white_filename(filename)); | 
 | 697 |         write_bitmap(whitePath, drp->fWhiteBitmap); | 
 | 698 |     } | 
| epoger@google.com | 6600852 | 2012-05-16 17:40:57 +0000 | [diff] [blame] | 699 |  | 
| tomhudson@google.com | 9b540ce | 2011-08-02 14:10:04 +0000 | [diff] [blame] | 700 |     release_bitmaps(drp); | 
| tomhudson@google.com | 4e30598 | 2011-07-13 17:42:46 +0000 | [diff] [blame] | 701 | } | 
 | 702 |  | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 703 | /// Returns true if string contains any of these substrings. | 
 | 704 | static bool string_contains_any_of(const SkString& string, | 
 | 705 |                                    const StringArray& substrings) { | 
 | 706 |     for (int i = 0; i < substrings.count(); i++) { | 
 | 707 |         if (string.contains(substrings[i]->c_str())) { | 
 | 708 |             return true; | 
 | 709 |         } | 
 | 710 |     } | 
 | 711 |     return false; | 
 | 712 | } | 
 | 713 |  | 
| epoger@google.com | 71329d8 | 2012-08-16 13:42:13 +0000 | [diff] [blame] | 714 | /// Internal (potentially recursive) implementation of get_file_list. | 
 | 715 | static void get_file_list_subdir(const SkString& rootDir, const SkString& subDir, | 
 | 716 |                                  const StringArray& matchSubstrings, | 
 | 717 |                                  const StringArray& nomatchSubstrings, | 
 | 718 |                                  bool recurseIntoSubdirs, FileArray *files) { | 
 | 719 |     bool isSubDirEmpty = subDir.isEmpty(); | 
 | 720 |     SkString dir(rootDir); | 
 | 721 |     if (!isSubDirEmpty) { | 
 | 722 |         dir.append(PATH_DIV_STR); | 
 | 723 |         dir.append(subDir); | 
 | 724 |     } | 
 | 725 |  | 
 | 726 |     // Iterate over files (not directories) within dir. | 
 | 727 |     SkOSFile::Iter fileIterator(dir.c_str()); | 
 | 728 |     SkString fileName; | 
 | 729 |     while (fileIterator.next(&fileName, false)) { | 
 | 730 |         if (fileName.startsWith(".")) { | 
 | 731 |             continue; | 
 | 732 |         } | 
 | 733 |         SkString pathRelativeToRootDir(subDir); | 
 | 734 |         if (!isSubDirEmpty) { | 
 | 735 |             pathRelativeToRootDir.append(PATH_DIV_STR); | 
 | 736 |         } | 
 | 737 |         pathRelativeToRootDir.append(fileName); | 
 | 738 |         if (string_contains_any_of(pathRelativeToRootDir, matchSubstrings) && | 
 | 739 |             !string_contains_any_of(pathRelativeToRootDir, nomatchSubstrings)) { | 
 | 740 |             files->push(new SkString(pathRelativeToRootDir)); | 
 | 741 |         } | 
 | 742 |     } | 
 | 743 |  | 
 | 744 |     // Recurse into any non-ignored subdirectories. | 
 | 745 |     if (recurseIntoSubdirs) { | 
 | 746 |         SkOSFile::Iter dirIterator(dir.c_str()); | 
 | 747 |         SkString dirName; | 
 | 748 |         while (dirIterator.next(&dirName, true)) { | 
 | 749 |             if (dirName.startsWith(".")) { | 
 | 750 |                 continue; | 
 | 751 |             } | 
 | 752 |             SkString pathRelativeToRootDir(subDir); | 
 | 753 |             if (!isSubDirEmpty) { | 
 | 754 |                 pathRelativeToRootDir.append(PATH_DIV_STR); | 
 | 755 |             } | 
 | 756 |             pathRelativeToRootDir.append(dirName); | 
 | 757 |             if (!string_contains_any_of(pathRelativeToRootDir, nomatchSubstrings)) { | 
 | 758 |                 get_file_list_subdir(rootDir, pathRelativeToRootDir, | 
 | 759 |                                      matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, | 
 | 760 |                                      files); | 
 | 761 |             } | 
 | 762 |         } | 
 | 763 |     } | 
 | 764 | } | 
 | 765 |  | 
 | 766 | /// Iterate over dir and get all files whose filename: | 
 | 767 | ///  - matches any of the substrings in matchSubstrings, but... | 
 | 768 | ///  - DOES NOT match any of the substrings in nomatchSubstrings | 
 | 769 | ///  - DOES NOT start with a dot (.) | 
 | 770 | /// Adds the matching files to the list in *files. | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 771 | static void get_file_list(const SkString& dir, | 
 | 772 |                           const StringArray& matchSubstrings, | 
 | 773 |                           const StringArray& nomatchSubstrings, | 
| epoger@google.com | 71329d8 | 2012-08-16 13:42:13 +0000 | [diff] [blame] | 774 |                           bool recurseIntoSubdirs, FileArray *files) { | 
 | 775 |     get_file_list_subdir(dir, SkString(""), | 
 | 776 |                          matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, | 
 | 777 |                          files); | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 778 | } | 
 | 779 |  | 
 | 780 | static void release_file_list(FileArray *files) { | 
 | 781 |     files->deleteAll(); | 
 | 782 | } | 
 | 783 |  | 
 | 784 | /// Comparison routines for qsort, sort by file names. | 
 | 785 | static int compare_file_name_metrics(SkString **lhs, SkString **rhs) { | 
 | 786 |     return strcmp((*lhs)->c_str(), (*rhs)->c_str()); | 
 | 787 | } | 
 | 788 |  | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 789 | /// Creates difference images, returns the number that have a 0 metric. | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 790 | /// If outputDir.isEmpty(), don't write out diff files. | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 791 | static void create_diff_images (DiffMetricProc dmp, | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 792 |                                 const int colorThreshold, | 
 | 793 |                                 RecordArray* differences, | 
 | 794 |                                 const SkString& baseDir, | 
 | 795 |                                 const SkString& comparisonDir, | 
 | 796 |                                 const SkString& outputDir, | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 797 |                                 const StringArray& matchSubstrings, | 
 | 798 |                                 const StringArray& nomatchSubstrings, | 
| epoger@google.com | 71329d8 | 2012-08-16 13:42:13 +0000 | [diff] [blame] | 799 |                                 bool recurseIntoSubdirs, | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 800 |                                 DiffSummary* summary) { | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 801 |     SkASSERT(!baseDir.isEmpty()); | 
 | 802 |     SkASSERT(!comparisonDir.isEmpty()); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 803 |  | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 804 |     FileArray baseFiles; | 
 | 805 |     FileArray comparisonFiles; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 806 |  | 
| epoger@google.com | 71329d8 | 2012-08-16 13:42:13 +0000 | [diff] [blame] | 807 |     get_file_list(baseDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, &baseFiles); | 
 | 808 |     get_file_list(comparisonDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 809 |                   &comparisonFiles); | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 810 |  | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 811 |     if (!baseFiles.isEmpty()) { | 
| reed@google.com | c7a67cb | 2012-05-07 14:52:12 +0000 | [diff] [blame] | 812 |         qsort(baseFiles.begin(), baseFiles.count(), sizeof(SkString*), | 
 | 813 |               SkCastForQSort(compare_file_name_metrics)); | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 814 |     } | 
 | 815 |     if (!comparisonFiles.isEmpty()) { | 
| reed@google.com | c7a67cb | 2012-05-07 14:52:12 +0000 | [diff] [blame] | 816 |         qsort(comparisonFiles.begin(), comparisonFiles.count(), | 
 | 817 |               sizeof(SkString*), SkCastForQSort(compare_file_name_metrics)); | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 818 |     } | 
| epoger@google.com | 6600852 | 2012-05-16 17:40:57 +0000 | [diff] [blame] | 819 |  | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 820 |     int i = 0; | 
 | 821 |     int j = 0; | 
 | 822 |  | 
 | 823 |     while (i < baseFiles.count() && | 
 | 824 |            j < comparisonFiles.count()) { | 
 | 825 |  | 
| tomhudson@google.com | 4e30598 | 2011-07-13 17:42:46 +0000 | [diff] [blame] | 826 |         SkString basePath (baseDir); | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 827 |         basePath.append(*baseFiles[i]); | 
| tomhudson@google.com | 7d04280 | 2011-07-14 13:15:55 +0000 | [diff] [blame] | 828 |         SkString comparisonPath (comparisonDir); | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 829 |         comparisonPath.append(*comparisonFiles[j]); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 830 |  | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 831 |         DiffRecord *drp = NULL; | 
 | 832 |         int v = strcmp(baseFiles[i]->c_str(), | 
 | 833 |                        comparisonFiles[j]->c_str()); | 
 | 834 |  | 
 | 835 |         if (v < 0) { | 
 | 836 |             // in baseDir, but not in comparisonDir | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 837 |             drp = new DiffRecord(*baseFiles[i], basePath, comparisonPath, | 
 | 838 |                                  kComparisonMissing); | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 839 |             ++i; | 
 | 840 |         } else if (v > 0) { | 
 | 841 |             // in comparisonDir, but not in baseDir | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 842 |             drp = new DiffRecord(*comparisonFiles[j], basePath, comparisonPath, | 
 | 843 |                                  kBaseMissing); | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 844 |             ++j; | 
 | 845 |         } else { | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 846 |             // Found the same filename in both baseDir and comparisonDir. | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 847 |             drp = new DiffRecord(*baseFiles[i], basePath, comparisonPath); | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 848 |             SkASSERT(kUnknown == drp->fResult); | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 849 |  | 
| tomhudson@google.com | 8afae61 | 2012-08-14 15:03:35 +0000 | [diff] [blame] | 850 |             SkData* baseFileBits = NULL; | 
 | 851 |             SkData* comparisonFileBits = NULL; | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 852 |             if (NULL == (baseFileBits = read_file(basePath.c_str()))) { | 
 | 853 |                 SkDebugf("WARNING: couldn't read base file <%s>\n", | 
 | 854 |                          basePath.c_str()); | 
 | 855 |                 drp->fResult = kBaseMissing; | 
| tomhudson@google.com | 8afae61 | 2012-08-14 15:03:35 +0000 | [diff] [blame] | 856 |             } else if (NULL == (comparisonFileBits = read_file(comparisonPath.c_str()))) { | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 857 |                 SkDebugf("WARNING: couldn't read comparison file <%s>\n", | 
 | 858 |                          comparisonPath.c_str()); | 
 | 859 |                 drp->fResult = kComparisonMissing; | 
 | 860 |             } else { | 
 | 861 |                 if (are_buffers_equal(baseFileBits, comparisonFileBits)) { | 
 | 862 |                     drp->fResult = kEqualBits; | 
 | 863 |                 } else if (get_bitmaps(baseFileBits, comparisonFileBits, drp)) { | 
 | 864 |                     create_and_write_diff_image(drp, dmp, colorThreshold, | 
 | 865 |                                                 outputDir, *baseFiles[i]); | 
 | 866 |                 } else { | 
 | 867 |                     drp->fResult = kDifferentOther; | 
 | 868 |                 } | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 869 |             } | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 870 |             if (baseFileBits) { | 
 | 871 |                 baseFileBits->unref(); | 
 | 872 |             } | 
 | 873 |             if (comparisonFileBits) { | 
 | 874 |                 comparisonFileBits->unref(); | 
 | 875 |             } | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 876 |             ++i; | 
 | 877 |             ++j; | 
 | 878 |         } | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 879 |         SkASSERT(kUnknown != drp->fResult); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 880 |         differences->push(drp); | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 881 |         summary->add(drp); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 882 |     } | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 883 |  | 
 | 884 |     for (; i < baseFiles.count(); ++i) { | 
 | 885 |         // files only in baseDir | 
 | 886 |         SkString basePath (baseDir); | 
 | 887 |         basePath.append(*baseFiles[i]); | 
 | 888 |         SkString comparisonPath; | 
 | 889 |         DiffRecord *drp = new DiffRecord(*baseFiles[i], basePath, | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 890 |                                          comparisonPath, kComparisonMissing); | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 891 |         differences->push(drp); | 
 | 892 |         summary->add(drp); | 
 | 893 |     } | 
 | 894 |  | 
 | 895 |     for (; j < comparisonFiles.count(); ++j) { | 
 | 896 |         // files only in comparisonDir | 
 | 897 |         SkString basePath; | 
 | 898 |         SkString comparisonPath(comparisonDir); | 
 | 899 |         comparisonPath.append(*comparisonFiles[j]); | 
 | 900 |         DiffRecord *drp = new DiffRecord(*comparisonFiles[j], basePath, | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 901 |                                          comparisonPath, kBaseMissing); | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 902 |         differences->push(drp); | 
 | 903 |         summary->add(drp); | 
 | 904 |     } | 
 | 905 |  | 
 | 906 |     release_file_list(&baseFiles); | 
 | 907 |     release_file_list(&comparisonFiles); | 
| tomhudson@google.com | 7d04280 | 2011-07-14 13:15:55 +0000 | [diff] [blame] | 908 | } | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 909 |  | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 910 | /// Make layout more consistent by scaling image to 240 height, 360 width, | 
 | 911 | /// or natural size, whichever is smallest. | 
| tomhudson@google.com | 9b540ce | 2011-08-02 14:10:04 +0000 | [diff] [blame] | 912 | static int compute_image_height (int height, int width) { | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 913 |     int retval = 240; | 
| tomhudson@google.com | 9b540ce | 2011-08-02 14:10:04 +0000 | [diff] [blame] | 914 |     if (height < retval) { | 
 | 915 |         retval = height; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 916 |     } | 
| tomhudson@google.com | 9b540ce | 2011-08-02 14:10:04 +0000 | [diff] [blame] | 917 |     float scale = (float) retval / height; | 
 | 918 |     if (width * scale > 360) { | 
 | 919 |         scale = (float) 360 / width; | 
| bsalomon@google.com | 8e06dab | 2011-10-07 20:03:39 +0000 | [diff] [blame] | 920 |         retval = static_cast<int>(height * scale); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 921 |     } | 
 | 922 |     return retval; | 
 | 923 | } | 
 | 924 |  | 
| epoger@google.com | 25d961c | 2012-02-02 20:50:36 +0000 | [diff] [blame] | 925 | static void print_table_header (SkFILEWStream* stream, | 
 | 926 |                                 const int matchCount, | 
 | 927 |                                 const int colorThreshold, | 
 | 928 |                                 const RecordArray& differences, | 
 | 929 |                                 const SkString &baseDir, | 
| epoger@google.com | a2b793c | 2012-05-15 14:58:53 +0000 | [diff] [blame] | 930 |                                 const SkString &comparisonDir, | 
 | 931 |                                 bool doOutputDate=false) { | 
| epoger@google.com | 25d961c | 2012-02-02 20:50:36 +0000 | [diff] [blame] | 932 |     stream->writeText("<table>\n"); | 
 | 933 |     stream->writeText("<tr><th>"); | 
| jkleinert@google.com | ed01f12 | 2012-08-30 22:05:53 +0000 | [diff] [blame] | 934 |     stream->writeText("select image</th>\n<th>"); | 
| epoger@google.com | a2b793c | 2012-05-15 14:58:53 +0000 | [diff] [blame] | 935 |     if (doOutputDate) { | 
 | 936 |         SkTime::DateTime dt; | 
 | 937 |         SkTime::GetDateTime(&dt); | 
 | 938 |         stream->writeText("SkDiff run at "); | 
 | 939 |         stream->writeDecAsText(dt.fHour); | 
 | 940 |         stream->writeText(":"); | 
 | 941 |         if (dt.fMinute < 10) { | 
 | 942 |             stream->writeText("0"); | 
 | 943 |         } | 
 | 944 |         stream->writeDecAsText(dt.fMinute); | 
 | 945 |         stream->writeText(":"); | 
 | 946 |         if (dt.fSecond < 10) { | 
 | 947 |             stream->writeText("0"); | 
 | 948 |         } | 
 | 949 |         stream->writeDecAsText(dt.fSecond); | 
 | 950 |         stream->writeText("<br>"); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 951 |     } | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 952 |     stream->writeDecAsText(matchCount); | 
 | 953 |     stream->writeText(" of "); | 
 | 954 |     stream->writeDecAsText(differences.count()); | 
 | 955 |     stream->writeText(" images matched "); | 
 | 956 |     if (colorThreshold == 0) { | 
 | 957 |         stream->writeText("exactly"); | 
 | 958 |     } else { | 
 | 959 |         stream->writeText("within "); | 
 | 960 |         stream->writeDecAsText(colorThreshold); | 
 | 961 |         stream->writeText(" color units per component"); | 
 | 962 |     } | 
 | 963 |     stream->writeText(".<br>"); | 
| epoger@google.com | 25d961c | 2012-02-02 20:50:36 +0000 | [diff] [blame] | 964 |     stream->writeText("</th>\n<th>"); | 
 | 965 |     stream->writeText("every different pixel shown in white"); | 
 | 966 |     stream->writeText("</th>\n<th>"); | 
 | 967 |     stream->writeText("color difference at each pixel"); | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 968 |     stream->writeText("</th>\n<th>baseDir: "); | 
| epoger@google.com | 25d961c | 2012-02-02 20:50:36 +0000 | [diff] [blame] | 969 |     stream->writeText(baseDir.c_str()); | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 970 |     stream->writeText("</th>\n<th>comparisonDir: "); | 
| epoger@google.com | 25d961c | 2012-02-02 20:50:36 +0000 | [diff] [blame] | 971 |     stream->writeText(comparisonDir.c_str()); | 
 | 972 |     stream->writeText("</th>\n"); | 
 | 973 |     stream->writeText("</tr>\n"); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 974 | } | 
 | 975 |  | 
 | 976 | static void print_pixel_count (SkFILEWStream* stream, | 
 | 977 |                                const DiffRecord& diff) { | 
 | 978 |     stream->writeText("<br>("); | 
| bsalomon@google.com | 8e06dab | 2011-10-07 20:03:39 +0000 | [diff] [blame] | 979 |     stream->writeDecAsText(static_cast<int>(diff.fFractionDifference * | 
 | 980 |                                             diff.fBaseWidth * | 
 | 981 |                                             diff.fBaseHeight)); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 982 |     stream->writeText(" pixels)"); | 
| tomhudson@google.com | 5b32529 | 2011-05-24 19:41:13 +0000 | [diff] [blame] | 983 | /* | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 984 |     stream->writeDecAsText(diff.fWeightedFraction * | 
| tomhudson@google.com | 9b540ce | 2011-08-02 14:10:04 +0000 | [diff] [blame] | 985 |                            diff.fBaseWidth * | 
 | 986 |                            diff.fBaseHeight); | 
| tomhudson@google.com | 5b32529 | 2011-05-24 19:41:13 +0000 | [diff] [blame] | 987 |     stream->writeText(" weighted pixels)"); | 
 | 988 | */ | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 989 | } | 
 | 990 |  | 
| jkleinert@google.com | ed01f12 | 2012-08-30 22:05:53 +0000 | [diff] [blame] | 991 | static void print_checkbox_cell (SkFILEWStream* stream, | 
 | 992 |                                  const DiffRecord& diff) { | 
 | 993 |     stream->writeText("<td><input type=\"checkbox\" name=\""); | 
 | 994 |     stream->writeText(diff.fFilename.c_str()); | 
 | 995 |     stream->writeText("\" checked=\"yes\"></td>"); | 
 | 996 | } | 
 | 997 |  | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 998 | static void print_label_cell (SkFILEWStream* stream, | 
 | 999 |                               const DiffRecord& diff) { | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 1000 |     char metricBuf [20]; | 
 | 1001 |  | 
 | 1002 |     stream->writeText("<td><b>"); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1003 |     stream->writeText(diff.fFilename.c_str()); | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 1004 |     stream->writeText("</b><br>"); | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 1005 |     switch (diff.fResult) { | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 1006 |       case kEqualBits: | 
 | 1007 |         SkDEBUGFAIL("should not encounter DiffRecord with kEqualBits here"); | 
 | 1008 |         return; | 
 | 1009 |       case kEqualPixels: | 
 | 1010 |         SkDEBUGFAIL("should not encounter DiffRecord with kEqualPixels here"); | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 1011 |         return; | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 1012 |       case kDifferentSizes: | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 1013 |         stream->writeText("Image sizes differ</td>"); | 
 | 1014 |         return; | 
 | 1015 |       case kDifferentPixels: | 
 | 1016 |         sprintf(metricBuf, "%12.4f%%", 100 * diff.fFractionDifference); | 
 | 1017 |         stream->writeText(metricBuf); | 
 | 1018 |         stream->writeText(" of pixels differ"); | 
 | 1019 |         stream->writeText("\n  ("); | 
 | 1020 |         sprintf(metricBuf, "%12.4f%%", 100 * diff.fWeightedFraction); | 
 | 1021 |         stream->writeText(metricBuf); | 
 | 1022 |         stream->writeText(" weighted)"); | 
 | 1023 |         // Write the actual number of pixels that differ if it's < 1% | 
 | 1024 |         if (diff.fFractionDifference < 0.01) { | 
 | 1025 |             print_pixel_count(stream, diff); | 
 | 1026 |         } | 
 | 1027 |         stream->writeText("<br>Average color mismatch "); | 
 | 1028 |         stream->writeDecAsText(static_cast<int>(MAX3(diff.fAverageMismatchR, | 
 | 1029 |                                                      diff.fAverageMismatchG, | 
 | 1030 |                                                      diff.fAverageMismatchB))); | 
 | 1031 |         stream->writeText("<br>Max color mismatch "); | 
 | 1032 |         stream->writeDecAsText(MAX3(diff.fMaxMismatchR, | 
 | 1033 |                                     diff.fMaxMismatchG, | 
 | 1034 |                                     diff.fMaxMismatchB)); | 
| tomhudson@google.com | 8b08c3e | 2011-11-30 17:01:00 +0000 | [diff] [blame] | 1035 |         stream->writeText("</td>"); | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 1036 |         break; | 
 | 1037 |       case kDifferentOther: | 
 | 1038 |         stream->writeText("Files differ; unable to parse one or both files</td>"); | 
 | 1039 |         return; | 
 | 1040 |       case kBaseMissing: | 
 | 1041 |         stream->writeText("Missing from baseDir</td>"); | 
 | 1042 |         return; | 
 | 1043 |       case kComparisonMissing: | 
 | 1044 |         stream->writeText("Missing from comparisonDir</td>"); | 
| tomhudson@google.com | 8b08c3e | 2011-11-30 17:01:00 +0000 | [diff] [blame] | 1045 |         return; | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 1046 |       default: | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 1047 |         SkDEBUGFAIL("encountered DiffRecord with unknown result type"); | 
 | 1048 |         return; | 
| tomhudson@google.com | 8b08c3e | 2011-11-30 17:01:00 +0000 | [diff] [blame] | 1049 |     } | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1050 | } | 
 | 1051 |  | 
 | 1052 | static void print_image_cell (SkFILEWStream* stream, | 
| tomhudson@google.com | 4e30598 | 2011-07-13 17:42:46 +0000 | [diff] [blame] | 1053 |                               const SkString& path, | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1054 |                               int height) { | 
 | 1055 |     stream->writeText("<td><a href=\""); | 
| tomhudson@google.com | 4e30598 | 2011-07-13 17:42:46 +0000 | [diff] [blame] | 1056 |     stream->writeText(path.c_str()); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1057 |     stream->writeText("\"><img src=\""); | 
| tomhudson@google.com | 4e30598 | 2011-07-13 17:42:46 +0000 | [diff] [blame] | 1058 |     stream->writeText(path.c_str()); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1059 |     stream->writeText("\" height=\""); | 
 | 1060 |     stream->writeDecAsText(height); | 
 | 1061 |     stream->writeText("px\"></a></td>"); | 
 | 1062 | } | 
 | 1063 |  | 
| caryclark@google.com | 3dd4591 | 2012-06-06 12:11:10 +0000 | [diff] [blame] | 1064 | #if 0 // UNUSED | 
| epoger@google.com | 01f7870 | 2012-04-12 16:32:04 +0000 | [diff] [blame] | 1065 | static void print_text_cell (SkFILEWStream* stream, const char* text) { | 
 | 1066 |     stream->writeText("<td align=center>"); | 
 | 1067 |     if (NULL != text) { | 
 | 1068 |         stream->writeText(text); | 
 | 1069 |     } | 
 | 1070 |     stream->writeText("</td>"); | 
 | 1071 | } | 
| caryclark@google.com | 3dd4591 | 2012-06-06 12:11:10 +0000 | [diff] [blame] | 1072 | #endif | 
| epoger@google.com | 01f7870 | 2012-04-12 16:32:04 +0000 | [diff] [blame] | 1073 |  | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 1074 | static void print_diff_with_missing_file(SkFILEWStream* stream, | 
 | 1075 |                                          DiffRecord& diff, | 
 | 1076 |                                          const SkString& relativePath) { | 
 | 1077 |     stream->writeText("<tr>\n"); | 
| jkleinert@google.com | ed01f12 | 2012-08-30 22:05:53 +0000 | [diff] [blame] | 1078 |     print_checkbox_cell(stream, diff); | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 1079 |     print_label_cell(stream, diff); | 
 | 1080 |     stream->writeText("<td>N/A</td>"); | 
 | 1081 |     stream->writeText("<td>N/A</td>"); | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 1082 |     if (kBaseMissing != diff.fResult) { | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 1083 |         int h, w; | 
 | 1084 |         if (!get_bitmap_height_width(diff.fBasePath, &h, &w)) { | 
 | 1085 |             stream->writeText("<td>N/A</td>"); | 
 | 1086 |         } else { | 
 | 1087 |             int height = compute_image_height(h, w); | 
 | 1088 |             if (!diff.fBasePath.startsWith(PATH_DIV_STR)) { | 
 | 1089 |                 diff.fBasePath.prepend(relativePath); | 
 | 1090 |             } | 
 | 1091 |             print_image_cell(stream, diff.fBasePath, height); | 
 | 1092 |         } | 
 | 1093 |     } else { | 
 | 1094 |         stream->writeText("<td>N/A</td>"); | 
 | 1095 |     } | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 1096 |     if (kComparisonMissing != diff.fResult) { | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 1097 |         int h, w; | 
 | 1098 |         if (!get_bitmap_height_width(diff.fComparisonPath, &h, &w)) { | 
 | 1099 |             stream->writeText("<td>N/A</td>"); | 
 | 1100 |         } else { | 
 | 1101 |             int height = compute_image_height(h, w); | 
 | 1102 |             if (!diff.fComparisonPath.startsWith(PATH_DIV_STR)) { | 
 | 1103 |                 diff.fComparisonPath.prepend(relativePath); | 
 | 1104 |             } | 
 | 1105 |             print_image_cell(stream, diff.fComparisonPath, height); | 
 | 1106 |         } | 
 | 1107 |     } else { | 
 | 1108 |         stream->writeText("<td>N/A</td>"); | 
 | 1109 |     } | 
 | 1110 |     stream->writeText("</tr>\n"); | 
 | 1111 |     stream->flush(); | 
 | 1112 | } | 
 | 1113 |  | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1114 | static void print_diff_page (const int matchCount, | 
 | 1115 |                              const int colorThreshold, | 
 | 1116 |                              const RecordArray& differences, | 
 | 1117 |                              const SkString& baseDir, | 
 | 1118 |                              const SkString& comparisonDir, | 
 | 1119 |                              const SkString& outputDir) { | 
 | 1120 |  | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 1121 |     SkASSERT(!baseDir.isEmpty()); | 
 | 1122 |     SkASSERT(!comparisonDir.isEmpty()); | 
 | 1123 |     SkASSERT(!outputDir.isEmpty()); | 
 | 1124 |  | 
| tomhudson@google.com | 5b32529 | 2011-05-24 19:41:13 +0000 | [diff] [blame] | 1125 |     SkString outputPath (outputDir); | 
 | 1126 |     outputPath.append("index.html"); | 
 | 1127 |     //SkFILEWStream outputStream ("index.html"); | 
 | 1128 |     SkFILEWStream outputStream (outputPath.c_str()); | 
 | 1129 |  | 
| tomhudson@google.com | 4e30598 | 2011-07-13 17:42:46 +0000 | [diff] [blame] | 1130 |     // Need to convert paths from relative-to-cwd to relative-to-outputDir | 
| tomhudson@google.com | 5b32529 | 2011-05-24 19:41:13 +0000 | [diff] [blame] | 1131 |     // FIXME this doesn't work if there are '..' inside the outputDir | 
| bsalomon@google.com | f1478f8 | 2012-07-30 19:16:02 +0000 | [diff] [blame] | 1132 |  | 
| epoger@google.com | 21ecba8 | 2012-08-08 13:47:39 +0000 | [diff] [blame] | 1133 |     bool isPathAbsolute = false; | 
 | 1134 |     // On Windows or Linux, a path starting with PATH_DIV_CHAR is absolute. | 
 | 1135 |     if (outputDir.size() > 0 && PATH_DIV_CHAR == outputDir[0]) { | 
 | 1136 |         isPathAbsolute = true; | 
 | 1137 |     } | 
| bsalomon@google.com | f1478f8 | 2012-07-30 19:16:02 +0000 | [diff] [blame] | 1138 | #ifdef SK_BUILD_FOR_WIN32 | 
| epoger@google.com | 21ecba8 | 2012-08-08 13:47:39 +0000 | [diff] [blame] | 1139 |     // On Windows, absolute paths can also start with "x:", where x is any | 
 | 1140 |     // drive letter. | 
 | 1141 |     if (outputDir.size() > 1 && ':' == outputDir[1]) { | 
 | 1142 |         isPathAbsolute = true; | 
 | 1143 |     } | 
| bsalomon@google.com | f1478f8 | 2012-07-30 19:16:02 +0000 | [diff] [blame] | 1144 | #endif | 
| epoger@google.com | 21ecba8 | 2012-08-08 13:47:39 +0000 | [diff] [blame] | 1145 |  | 
| tomhudson@google.com | 5b32529 | 2011-05-24 19:41:13 +0000 | [diff] [blame] | 1146 |     SkString relativePath; | 
| epoger@google.com | 21ecba8 | 2012-08-08 13:47:39 +0000 | [diff] [blame] | 1147 |     if (!isPathAbsolute) { | 
| bsalomon@google.com | f1478f8 | 2012-07-30 19:16:02 +0000 | [diff] [blame] | 1148 |         unsigned int ui; | 
 | 1149 |         for (ui = 0; ui < outputDir.size(); ui++) { | 
 | 1150 |             if (outputDir[ui] == PATH_DIV_CHAR) { | 
 | 1151 |                 relativePath.append(".." PATH_DIV_STR); | 
 | 1152 |             } | 
| tomhudson@google.com | 5b32529 | 2011-05-24 19:41:13 +0000 | [diff] [blame] | 1153 |         } | 
 | 1154 |     } | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1155 |  | 
| jkleinert@google.com | ed01f12 | 2012-08-30 22:05:53 +0000 | [diff] [blame] | 1156 |     outputStream.writeText( | 
 | 1157 |         "<html>\n<head>\n" | 
 | 1158 |         "<script src=\"https://ajax.googleapis.com/ajax/" | 
 | 1159 |         "libs/jquery/1.7.2/jquery.min.js\"></script>\n" | 
 | 1160 |         "<script type=\"text/javascript\">\n" | 
 | 1161 |         "function generateCheckedList() {\n" | 
 | 1162 |         "var boxes = $(\":checkbox:checked\");\n" | 
 | 1163 |         "var fileCmdLineString = '';\n" | 
 | 1164 |         "var fileMultiLineString = '';\n" | 
 | 1165 |         "for (var i = 0; i < boxes.length; i++) {\n" | 
 | 1166 |         "fileMultiLineString += boxes[i].name + '<br>';\n" | 
| skia.committer@gmail.com | 11f8692 | 2012-08-31 17:14:46 +0000 | [diff] [blame] | 1167 |         "fileCmdLineString += boxes[i].name + ' ';\n" | 
| jkleinert@google.com | ed01f12 | 2012-08-30 22:05:53 +0000 | [diff] [blame] | 1168 |         "}\n" | 
 | 1169 |         "$(\"#checkedList\").html(fileCmdLineString + " | 
 | 1170 |         "'<br><br>' + fileMultiLineString);\n" | 
 | 1171 |         "}\n" | 
 | 1172 |         "</script>\n</head>\n<body>\n"); | 
| epoger@google.com | 25d961c | 2012-02-02 20:50:36 +0000 | [diff] [blame] | 1173 |     print_table_header(&outputStream, matchCount, colorThreshold, differences, | 
 | 1174 |                        baseDir, comparisonDir); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1175 |     int i; | 
 | 1176 |     for (i = 0; i < differences.count(); i++) { | 
 | 1177 |         DiffRecord* diff = differences[i]; | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 1178 |  | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 1179 |         switch (diff->fResult) { | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 1180 |           // Cases in which there is no diff to report. | 
 | 1181 |           case kEqualBits: | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 1182 |           case kEqualPixels: | 
 | 1183 |             continue; | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 1184 |           // Cases in which we want a detailed pixel diff. | 
 | 1185 |           case kDifferentPixels: | 
 | 1186 |             break; | 
 | 1187 |           // Cases in which the files differed, but we can't display the diff. | 
 | 1188 |           case kDifferentSizes: | 
 | 1189 |           case kDifferentOther: | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 1190 |           case kBaseMissing: | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 1191 |           case kComparisonMissing: | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 1192 |             print_diff_with_missing_file(&outputStream, *diff, relativePath); | 
 | 1193 |             continue; | 
| epoger@google.com | 292aff6 | 2012-05-16 14:57:28 +0000 | [diff] [blame] | 1194 |           default: | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 1195 |             SkDEBUGFAIL("encountered DiffRecord with unknown result type"); | 
 | 1196 |             continue; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1197 |         } | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 1198 |  | 
| bsalomon@google.com | 1a315fe | 2011-09-23 14:56:37 +0000 | [diff] [blame] | 1199 |         if (!diff->fBasePath.startsWith(PATH_DIV_STR)) { | 
| tomhudson@google.com | 4e30598 | 2011-07-13 17:42:46 +0000 | [diff] [blame] | 1200 |             diff->fBasePath.prepend(relativePath); | 
 | 1201 |         } | 
| bsalomon@google.com | 1a315fe | 2011-09-23 14:56:37 +0000 | [diff] [blame] | 1202 |         if (!diff->fComparisonPath.startsWith(PATH_DIV_STR)) { | 
| tomhudson@google.com | 4e30598 | 2011-07-13 17:42:46 +0000 | [diff] [blame] | 1203 |             diff->fComparisonPath.prepend(relativePath); | 
 | 1204 |         } | 
| epoger@google.com | 5fd5385 | 2012-03-22 18:20:06 +0000 | [diff] [blame] | 1205 |  | 
| tomhudson@google.com | 9b540ce | 2011-08-02 14:10:04 +0000 | [diff] [blame] | 1206 |         int height = compute_image_height(diff->fBaseHeight, diff->fBaseWidth); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1207 |         outputStream.writeText("<tr>\n"); | 
| jkleinert@google.com | ed01f12 | 2012-08-30 22:05:53 +0000 | [diff] [blame] | 1208 |         print_checkbox_cell(&outputStream, *diff); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1209 |         print_label_cell(&outputStream, *diff); | 
| epoger@google.com | 46256ea | 2012-05-22 13:45:35 +0000 | [diff] [blame] | 1210 |         print_image_cell(&outputStream, | 
 | 1211 |                          filename_to_white_filename(diff->fFilename), height); | 
 | 1212 |         print_image_cell(&outputStream, | 
 | 1213 |                          filename_to_diff_filename(diff->fFilename), height); | 
| epoger@google.com | 25d961c | 2012-02-02 20:50:36 +0000 | [diff] [blame] | 1214 |         print_image_cell(&outputStream, diff->fBasePath, height); | 
| tomhudson@google.com | 4e30598 | 2011-07-13 17:42:46 +0000 | [diff] [blame] | 1215 |         print_image_cell(&outputStream, diff->fComparisonPath, height); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1216 |         outputStream.writeText("</tr>\n"); | 
 | 1217 |         outputStream.flush(); | 
 | 1218 |     } | 
| jkleinert@google.com | ed01f12 | 2012-08-30 22:05:53 +0000 | [diff] [blame] | 1219 |     outputStream.writeText( | 
 | 1220 |         "</table>\n" | 
 | 1221 |         "<input type=\"button\" " | 
 | 1222 |         "onclick=\"generateCheckedList()\" " | 
 | 1223 |         "value=\"Create Rebaseline List\">\n" | 
 | 1224 |         "<div id=\"checkedList\"></div>\n" | 
 | 1225 |         "</body>\n</html>\n"); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1226 |     outputStream.flush(); | 
 | 1227 | } | 
 | 1228 |  | 
 | 1229 | static void usage (char * argv0) { | 
 | 1230 |     SkDebugf("Skia baseline image diff tool\n"); | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 1231 |     SkDebugf("\n" | 
 | 1232 | "Usage: \n" | 
 | 1233 | "    %s <baseDir> <comparisonDir> [outputDir] \n" | 
| epoger@google.com | a611c3e | 2012-05-18 20:10:06 +0000 | [diff] [blame] | 1234 | , argv0, argv0); | 
| tomhudson@google.com | 7d04280 | 2011-07-14 13:15:55 +0000 | [diff] [blame] | 1235 |     SkDebugf( | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 1236 | "\nArguments:" | 
| epoger@google.com | dfbf24e | 2012-07-13 21:22:02 +0000 | [diff] [blame] | 1237 | "\n    --failonresult <result>: After comparing all file pairs, exit with nonzero" | 
 | 1238 | "\n                             return code (number of file pairs yielding this" | 
 | 1239 | "\n                             result) if any file pairs yielded this result." | 
 | 1240 | "\n                             This flag may be repeated, in which case the" | 
 | 1241 | "\n                             return code will be the number of fail pairs" | 
 | 1242 | "\n                             yielding ANY of these results." | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 1243 | "\n    --help: display this info" | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 1244 | "\n    --listfilenames: list all filenames for each result type in stdout" | 
| epoger@google.com | dfbf24e | 2012-07-13 21:22:02 +0000 | [diff] [blame] | 1245 | "\n    --match <substring>: compare files whose filenames contain this substring;" | 
 | 1246 | "\n                         if unspecified, compare ALL files." | 
 | 1247 | "\n                         this flag may be repeated." | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 1248 | "\n    --nodiffs: don't write out image diffs or index.html, just generate" | 
 | 1249 | "\n               report on stdout" | 
| epoger@google.com | dfbf24e | 2012-07-13 21:22:02 +0000 | [diff] [blame] | 1250 | "\n    --nomatch <substring>: regardless of --match, DO NOT compare files whose" | 
 | 1251 | "\n                           filenames contain this substring." | 
 | 1252 | "\n                           this flag may be repeated." | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 1253 | "\n    --noprintdirs: do not print the directories used." | 
| epoger@google.com | 71329d8 | 2012-08-16 13:42:13 +0000 | [diff] [blame] | 1254 | "\n    --norecurse: do not recurse into subdirectories." | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 1255 | "\n    --sortbymaxmismatch: sort by worst color channel mismatch;" | 
 | 1256 | "\n                         break ties with -sortbymismatch" | 
 | 1257 | "\n    --sortbymismatch: sort by average color channel mismatch" | 
 | 1258 | "\n    --threshold <n>: only report differences > n (per color channel) [default 0]" | 
 | 1259 | "\n    --weighted: sort by # pixels different weighted by color difference" | 
 | 1260 | "\n" | 
 | 1261 | "\n    baseDir: directory to read baseline images from." | 
 | 1262 | "\n    comparisonDir: directory to read comparison images from" | 
 | 1263 | "\n    outputDir: directory to write difference images and index.html to;" | 
 | 1264 | "\n               defaults to comparisonDir" | 
 | 1265 | "\n" | 
 | 1266 | "\nIf no sort is specified, it will sort by fraction of pixels mismatching." | 
 | 1267 | "\n"); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1268 | } | 
 | 1269 |  | 
| epoger@google.com | 70044cc | 2012-07-12 18:37:55 +0000 | [diff] [blame] | 1270 | const int kNoError = 0; | 
 | 1271 | const int kGenericError = -1; | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 1272 |  | 
| caryclark@google.com | 5987f58 | 2012-10-02 18:33:14 +0000 | [diff] [blame] | 1273 | int tool_main(int argc, char** argv); | 
 | 1274 | int tool_main(int argc, char** argv) { | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1275 |     DiffMetricProc diffProc = compute_diff_pmcolor; | 
| epoger@google.com | 28060e7 | 2012-06-28 16:47:34 +0000 | [diff] [blame] | 1276 |     int (*sortProc)(const void*, const void*) = compare<CompareDiffMetrics>; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1277 |  | 
 | 1278 |     // Maximum error tolerated in any one color channel in any one pixel before | 
 | 1279 |     // a difference is reported. | 
 | 1280 |     int colorThreshold = 0; | 
 | 1281 |     SkString baseDir; | 
 | 1282 |     SkString comparisonDir; | 
 | 1283 |     SkString outputDir; | 
| epoger@google.com | dfbf24e | 2012-07-13 21:22:02 +0000 | [diff] [blame] | 1284 |  | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 1285 |     StringArray matchSubstrings; | 
 | 1286 |     StringArray nomatchSubstrings; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1287 |  | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 1288 |     bool generateDiffs = true; | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 1289 |     bool listFilenames = false; | 
| epoger@google.com | 71329d8 | 2012-08-16 13:42:13 +0000 | [diff] [blame] | 1290 |     bool printDirNames = true; | 
 | 1291 |     bool recurseIntoSubdirs = true; | 
| tomhudson@google.com | 7d04280 | 2011-07-14 13:15:55 +0000 | [diff] [blame] | 1292 |  | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1293 |     RecordArray differences; | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 1294 |     DiffSummary summary; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1295 |  | 
| epoger@google.com | 3af4ff4 | 2012-07-19 17:35:04 +0000 | [diff] [blame] | 1296 |     bool failOnResultType[kNumResultTypes]; | 
 | 1297 |     for (int i = 0; i < kNumResultTypes; i++) { | 
 | 1298 |         failOnResultType[i] = false; | 
 | 1299 |     } | 
 | 1300 |  | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 1301 |     int i; | 
 | 1302 |     int numUnflaggedArguments = 0; | 
 | 1303 |     for (i = 1; i < argc; i++) { | 
| epoger@google.com | dfbf24e | 2012-07-13 21:22:02 +0000 | [diff] [blame] | 1304 |         if (!strcmp(argv[i], "--failonresult")) { | 
 | 1305 |             Result type = getResultByName(argv[++i]); | 
| epoger@google.com | 3af4ff4 | 2012-07-19 17:35:04 +0000 | [diff] [blame] | 1306 |             failOnResultType[type] = true; | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 1307 |             continue; | 
 | 1308 |         } | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 1309 |         if (!strcmp(argv[i], "--help")) { | 
 | 1310 |             usage(argv[0]); | 
| epoger@google.com | 70044cc | 2012-07-12 18:37:55 +0000 | [diff] [blame] | 1311 |             return kNoError; | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 1312 |         } | 
 | 1313 |         if (!strcmp(argv[i], "--listfilenames")) { | 
 | 1314 |             listFilenames = true; | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 1315 |             continue; | 
 | 1316 |         } | 
 | 1317 |         if (!strcmp(argv[i], "--match")) { | 
 | 1318 |             matchSubstrings.push(new SkString(argv[++i])); | 
 | 1319 |             continue; | 
 | 1320 |         } | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 1321 |         if (!strcmp(argv[i], "--nodiffs")) { | 
 | 1322 |             generateDiffs = false; | 
 | 1323 |             continue; | 
 | 1324 |         } | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 1325 |         if (!strcmp(argv[i], "--nomatch")) { | 
 | 1326 |             nomatchSubstrings.push(new SkString(argv[++i])); | 
 | 1327 |             continue; | 
 | 1328 |         } | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 1329 |         if (!strcmp(argv[i], "--noprintdirs")) { | 
| epoger@google.com | 71329d8 | 2012-08-16 13:42:13 +0000 | [diff] [blame] | 1330 |             printDirNames = false; | 
 | 1331 |             continue; | 
 | 1332 |         } | 
 | 1333 |         if (!strcmp(argv[i], "--norecurse")) { | 
 | 1334 |             recurseIntoSubdirs = false; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1335 |             continue; | 
 | 1336 |         } | 
| tomhudson@google.com | 7d04280 | 2011-07-14 13:15:55 +0000 | [diff] [blame] | 1337 |         if (!strcmp(argv[i], "--sortbymaxmismatch")) { | 
| epoger@google.com | 28060e7 | 2012-06-28 16:47:34 +0000 | [diff] [blame] | 1338 |             sortProc = compare<CompareDiffMaxMismatches>; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1339 |             continue; | 
 | 1340 |         } | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 1341 |         if (!strcmp(argv[i], "--sortbymismatch")) { | 
 | 1342 |             sortProc = compare<CompareDiffMeanMismatches>; | 
| tomhudson@google.com | 5b32529 | 2011-05-24 19:41:13 +0000 | [diff] [blame] | 1343 |             continue; | 
 | 1344 |         } | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 1345 |         if (!strcmp(argv[i], "--threshold")) { | 
 | 1346 |             colorThreshold = atoi(argv[++i]); | 
 | 1347 |             continue; | 
 | 1348 |         } | 
 | 1349 |         if (!strcmp(argv[i], "--weighted")) { | 
 | 1350 |             sortProc = compare<CompareDiffWeighted>; | 
| keyar@chromium.org | a631819 | 2012-07-09 21:01:50 +0000 | [diff] [blame] | 1351 |             continue; | 
 | 1352 |         } | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1353 |         if (argv[i][0] != '-') { | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 1354 |             switch (numUnflaggedArguments++) { | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1355 |                 case 0: | 
 | 1356 |                     baseDir.set(argv[i]); | 
 | 1357 |                     continue; | 
 | 1358 |                 case 1: | 
 | 1359 |                     comparisonDir.set(argv[i]); | 
 | 1360 |                     continue; | 
 | 1361 |                 case 2: | 
 | 1362 |                     outputDir.set(argv[i]); | 
 | 1363 |                     continue; | 
 | 1364 |                 default: | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 1365 |                     SkDebugf("extra unflagged argument <%s>\n", argv[i]); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1366 |                     usage(argv[0]); | 
| epoger@google.com | 70044cc | 2012-07-12 18:37:55 +0000 | [diff] [blame] | 1367 |                     return kGenericError; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1368 |             } | 
 | 1369 |         } | 
 | 1370 |  | 
 | 1371 |         SkDebugf("Unrecognized argument <%s>\n", argv[i]); | 
 | 1372 |         usage(argv[0]); | 
| epoger@google.com | 70044cc | 2012-07-12 18:37:55 +0000 | [diff] [blame] | 1373 |         return kGenericError; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1374 |     } | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 1375 |  | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 1376 |     if (numUnflaggedArguments == 2) { | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 1377 |         outputDir = comparisonDir; | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 1378 |     } else if (numUnflaggedArguments != 3) { | 
| tomhudson@google.com | 9dc527b | 2011-06-09 15:47:10 +0000 | [diff] [blame] | 1379 |         usage(argv[0]); | 
| epoger@google.com | 70044cc | 2012-07-12 18:37:55 +0000 | [diff] [blame] | 1380 |         return kGenericError; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1381 |     } | 
 | 1382 |  | 
| bsalomon@google.com | 1a315fe | 2011-09-23 14:56:37 +0000 | [diff] [blame] | 1383 |     if (!baseDir.endsWith(PATH_DIV_STR)) { | 
 | 1384 |         baseDir.append(PATH_DIV_STR); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1385 |     } | 
| epoger@google.com | 71329d8 | 2012-08-16 13:42:13 +0000 | [diff] [blame] | 1386 |     if (printDirNames) { | 
| keyar@chromium.org | a631819 | 2012-07-09 21:01:50 +0000 | [diff] [blame] | 1387 |         printf("baseDir is [%s]\n", baseDir.c_str()); | 
 | 1388 |     } | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 1389 |  | 
| bsalomon@google.com | 1a315fe | 2011-09-23 14:56:37 +0000 | [diff] [blame] | 1390 |     if (!comparisonDir.endsWith(PATH_DIV_STR)) { | 
 | 1391 |         comparisonDir.append(PATH_DIV_STR); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1392 |     } | 
| epoger@google.com | 71329d8 | 2012-08-16 13:42:13 +0000 | [diff] [blame] | 1393 |     if (printDirNames) { | 
| keyar@chromium.org | a631819 | 2012-07-09 21:01:50 +0000 | [diff] [blame] | 1394 |         printf("comparisonDir is [%s]\n", comparisonDir.c_str()); | 
 | 1395 |     } | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 1396 |  | 
| bsalomon@google.com | 1a315fe | 2011-09-23 14:56:37 +0000 | [diff] [blame] | 1397 |     if (!outputDir.endsWith(PATH_DIV_STR)) { | 
 | 1398 |         outputDir.append(PATH_DIV_STR); | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1399 |     } | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 1400 |     if (generateDiffs) { | 
| epoger@google.com | 71329d8 | 2012-08-16 13:42:13 +0000 | [diff] [blame] | 1401 |         if (printDirNames) { | 
| keyar@chromium.org | a631819 | 2012-07-09 21:01:50 +0000 | [diff] [blame] | 1402 |             printf("writing diffs to outputDir is [%s]\n", outputDir.c_str()); | 
 | 1403 |         } | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 1404 |     } else { | 
| epoger@google.com | 71329d8 | 2012-08-16 13:42:13 +0000 | [diff] [blame] | 1405 |         if (printDirNames) { | 
| keyar@chromium.org | a631819 | 2012-07-09 21:01:50 +0000 | [diff] [blame] | 1406 |             printf("not writing any diffs to outputDir [%s]\n", outputDir.c_str()); | 
 | 1407 |         } | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 1408 |         outputDir.set(""); | 
 | 1409 |     } | 
 | 1410 |  | 
| epoger@google.com | da4af24 | 2012-06-25 18:45:50 +0000 | [diff] [blame] | 1411 |     // If no matchSubstrings were specified, match ALL strings | 
 | 1412 |     // (except for whatever nomatchSubstrings were specified, if any). | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 1413 |     if (matchSubstrings.isEmpty()) { | 
 | 1414 |         matchSubstrings.push(new SkString("")); | 
 | 1415 |     } | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1416 |  | 
| epoger@google.com | a611c3e | 2012-05-18 20:10:06 +0000 | [diff] [blame] | 1417 |     create_diff_images(diffProc, colorThreshold, &differences, | 
 | 1418 |                        baseDir, comparisonDir, outputDir, | 
| epoger@google.com | 71329d8 | 2012-08-16 13:42:13 +0000 | [diff] [blame] | 1419 |                        matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, &summary); | 
| epoger@google.com | 3af4ff4 | 2012-07-19 17:35:04 +0000 | [diff] [blame] | 1420 |     summary.print(listFilenames, failOnResultType); | 
| tomhudson@google.com | 7d04280 | 2011-07-14 13:15:55 +0000 | [diff] [blame] | 1421 |  | 
 | 1422 |     if (differences.count()) { | 
| reed@google.com | c7a67cb | 2012-05-07 14:52:12 +0000 | [diff] [blame] | 1423 |         qsort(differences.begin(), differences.count(), | 
 | 1424 |               sizeof(DiffRecord*), sortProc); | 
| tomhudson@google.com | 7d04280 | 2011-07-14 13:15:55 +0000 | [diff] [blame] | 1425 |     } | 
| epoger@google.com | 6600852 | 2012-05-16 17:40:57 +0000 | [diff] [blame] | 1426 |  | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 1427 |     if (generateDiffs) { | 
 | 1428 |         print_diff_page(summary.fNumMatches, colorThreshold, differences, | 
 | 1429 |                         baseDir, comparisonDir, outputDir); | 
 | 1430 |     } | 
| epoger@google.com | 76222c0 | 2012-05-31 15:12:09 +0000 | [diff] [blame] | 1431 |  | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1432 |     for (i = 0; i < differences.count(); i++) { | 
 | 1433 |         delete differences[i]; | 
 | 1434 |     } | 
| epoger@google.com | a5f406e | 2012-05-01 13:26:16 +0000 | [diff] [blame] | 1435 |     matchSubstrings.deleteAll(); | 
 | 1436 |     nomatchSubstrings.deleteAll(); | 
| epoger@google.com | be6188d | 2012-05-31 15:13:45 +0000 | [diff] [blame] | 1437 |  | 
| epoger@google.com | dfbf24e | 2012-07-13 21:22:02 +0000 | [diff] [blame] | 1438 |     int num_failing_results = 0; | 
| epoger@google.com | 3af4ff4 | 2012-07-19 17:35:04 +0000 | [diff] [blame] | 1439 |     for (int i = 0; i < kNumResultTypes; i++) { | 
 | 1440 |         if (failOnResultType[i]) { | 
 | 1441 |             num_failing_results += summary.fResultsOfType[i].count(); | 
 | 1442 |         } | 
| epoger@google.com | 46a4596 | 2012-07-12 18:16:02 +0000 | [diff] [blame] | 1443 |     } | 
| epoger@google.com | 2865988 | 2012-07-16 18:01:06 +0000 | [diff] [blame] | 1444 |  | 
 | 1445 |     // On Linux (and maybe other platforms too), any results outside of the | 
 | 1446 |     // range [0...255] are wrapped (mod 256).  Do the conversion ourselves, to | 
 | 1447 |     // make sure that we only return 0 when there were no failures. | 
 | 1448 |     return (num_failing_results > 255) ? 255 : num_failing_results; | 
| tomhudson@google.com | 4b33d28 | 2011-04-27 15:39:30 +0000 | [diff] [blame] | 1449 | } | 
| caryclark@google.com | 5987f58 | 2012-10-02 18:33:14 +0000 | [diff] [blame] | 1450 |  | 
 | 1451 | #if !defined SK_BUILD_FOR_IOS | 
 | 1452 | int main(int argc, char * const argv[]) { | 
 | 1453 |     return tool_main(argc, (char**) argv); | 
 | 1454 | } | 
 | 1455 | #endif |