blob: b496cadeb6a91167b96ca388a7b6c3be39f34edc [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
bungeman@google.come3c8ddf2012-12-05 20:13:12 +00007#include "skdiff.h"
8#include "skdiff_html.h"
9#include "skdiff_utils.h"
10#include "SkBitmap.h"
epoger@google.com46256ea2012-05-22 13:45:35 +000011#include "SkData.h"
tomhudson@google.com4b33d282011-04-27 15:39:30 +000012#include "SkImageEncoder.h"
13#include "SkOSFile.h"
Ben Wagnerbf111d72016-11-07 18:05:29 -050014#include "SkOSPath.h"
tomhudson@google.com4b33d282011-04-27 15:39:30 +000015#include "SkStream.h"
bungemana7e9f052016-02-18 08:53:33 -080016#include "../private/SkTDArray.h"
reed76a834a2016-01-03 18:36:05 -080017#include "../private/SkTSearch.h"
tomhudson@google.com4b33d282011-04-27 15:39:30 +000018
bungeman60e0fee2015-08-26 05:15:46 -070019#include <stdlib.h>
20
tomhudson@google.com4b33d282011-04-27 15:39:30 +000021/**
22 * skdiff
23 *
24 * Given three directory names, expects to find identically-named files in
25 * each of the first two; the first are treated as a set of baseline,
26 * the second a set of variant images, and a diff image is written into the
27 * third directory for each pair.
tomhudson@google.com7d042802011-07-14 13:15:55 +000028 * Creates an index.html in the current third directory to compare each
tomhudson@google.com4b33d282011-04-27 15:39:30 +000029 * pair that does not match exactly.
epoger@google.com71329d82012-08-16 13:42:13 +000030 * Recursively descends directories, unless run with --norecurse.
epoger@google.combe6188d2012-05-31 15:13:45 +000031 *
32 * Returns zero exit code if all images match across baseDir and comparisonDir.
tomhudson@google.com4b33d282011-04-27 15:39:30 +000033 */
34
epoger@google.coma5f406e2012-05-01 13:26:16 +000035typedef SkTDArray<SkString*> StringArray;
36typedef StringArray FileArray;
epoger@google.com5fd53852012-03-22 18:20:06 +000037
reed3a3baf62015-01-06 07:39:55 -080038static void add_unique_basename(StringArray* array, const SkString& filename) {
39 // trim off dirs
40 const char* src = filename.c_str();
Ben Wagnerbf111d72016-11-07 18:05:29 -050041 const char* trimmed = strrchr(src, SkOSPath::SEPARATOR);
reed3a3baf62015-01-06 07:39:55 -080042 if (trimmed) {
43 trimmed += 1; // skip the separator
44 } else {
45 trimmed = src;
46 }
47 const char* end = strrchr(trimmed, '.');
48 if (!end) {
49 end = trimmed + strlen(trimmed);
50 }
51 SkString result(trimmed, end - trimmed);
52
53 // only add unique entries
54 for (int i = 0; i < array->count(); ++i) {
55 if (*array->getAt(i) == result) {
56 return;
57 }
58 }
59 *array->append() = new SkString(result);
60}
61
tomhudson@google.com9dc527b2011-06-09 15:47:10 +000062struct DiffSummary {
63 DiffSummary ()
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000064 : fNumMatches(0)
65 , fNumMismatches(0)
66 , fMaxMismatchV(0)
bungemanfe917272016-10-13 17:36:40 -040067 , fMaxMismatchPercent(0) { }
tomhudson@google.com9dc527b2011-06-09 15:47:10 +000068
epoger@google.com5fd53852012-03-22 18:20:06 +000069 ~DiffSummary() {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000070 for (int i = 0; i < DiffRecord::kResultCount; ++i) {
epoger@google.com76222c02012-05-31 15:12:09 +000071 fResultsOfType[i].deleteAll();
72 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000073 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
74 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
75 fStatusOfType[base][comparison].deleteAll();
76 }
skia.committer@gmail.com0264fb42012-12-06 02:01:25 +000077 }
epoger@google.com5fd53852012-03-22 18:20:06 +000078 }
79
tomhudson@google.com9dc527b2011-06-09 15:47:10 +000080 uint32_t fNumMatches;
81 uint32_t fNumMismatches;
82 uint32_t fMaxMismatchV;
83 float fMaxMismatchPercent;
84
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000085 FileArray fResultsOfType[DiffRecord::kResultCount];
86 FileArray fStatusOfType[DiffResource::kStatusCount][DiffResource::kStatusCount];
87
reed3a3baf62015-01-06 07:39:55 -080088 StringArray fFailedBaseNames[DiffRecord::kResultCount];
89
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000090 void printContents(const FileArray& fileArray,
91 const char* baseStatus, const char* comparisonStatus,
92 bool listFilenames) {
93 int n = fileArray.count();
94 printf("%d file pairs %s in baseDir and %s in comparisonDir",
95 n, baseStatus, comparisonStatus);
96 if (listFilenames) {
97 printf(": ");
98 for (int i = 0; i < n; ++i) {
99 printf("%s ", fileArray[i]->c_str());
100 }
101 }
102 printf("\n");
103 }
104
105 void printStatus(bool listFilenames,
106 bool failOnStatusType[DiffResource::kStatusCount]
107 [DiffResource::kStatusCount]) {
108 typedef DiffResource::Status Status;
109
110 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
111 Status baseStatus = static_cast<Status>(base);
112 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
113 Status comparisonStatus = static_cast<Status>(comparison);
114 const FileArray& fileArray = fStatusOfType[base][comparison];
115 if (fileArray.count() > 0) {
116 if (failOnStatusType[base][comparison]) {
117 printf(" [*] ");
118 } else {
119 printf(" [_] ");
120 }
121 printContents(fileArray,
122 DiffResource::getStatusDescription(baseStatus),
123 DiffResource::getStatusDescription(comparisonStatus),
124 listFilenames);
125 }
126 }
127 }
128 }
epoger@google.com76222c02012-05-31 15:12:09 +0000129
epoger@google.com3af4ff42012-07-19 17:35:04 +0000130 // Print a line about the contents of this FileArray to stdout.
epoger@google.com46a45962012-07-12 18:16:02 +0000131 void printContents(const FileArray& fileArray, const char* headerText, bool listFilenames) {
epoger@google.com76222c02012-05-31 15:12:09 +0000132 int n = fileArray.count();
epoger@google.com3af4ff42012-07-19 17:35:04 +0000133 printf("%d file pairs %s", n, headerText);
134 if (listFilenames) {
135 printf(": ");
136 for (int i = 0; i < n; ++i) {
137 printf("%s ", fileArray[i]->c_str());
epoger@google.com76222c02012-05-31 15:12:09 +0000138 }
139 }
epoger@google.com3af4ff42012-07-19 17:35:04 +0000140 printf("\n");
epoger@google.com76222c02012-05-31 15:12:09 +0000141 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000142
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000143 void print(bool listFilenames, bool failOnResultType[DiffRecord::kResultCount],
144 bool failOnStatusType[DiffResource::kStatusCount]
145 [DiffResource::kStatusCount]) {
epoger@google.com3af4ff42012-07-19 17:35:04 +0000146 printf("\ncompared %d file pairs:\n", fNumMatches + fNumMismatches);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000147 for (int resultInt = 0; resultInt < DiffRecord::kResultCount; ++resultInt) {
148 DiffRecord::Result result = static_cast<DiffRecord::Result>(resultInt);
epoger@google.com3af4ff42012-07-19 17:35:04 +0000149 if (failOnResultType[result]) {
150 printf("[*] ");
151 } else {
152 printf("[_] ");
153 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000154 printContents(fResultsOfType[result], DiffRecord::getResultDescription(result),
155 listFilenames);
156 if (DiffRecord::kCouldNotCompare_Result == result) {
157 printStatus(listFilenames, failOnStatusType);
158 }
epoger@google.com46a45962012-07-12 18:16:02 +0000159 }
epoger@google.com3af4ff42012-07-19 17:35:04 +0000160 printf("(results marked with [*] will cause nonzero return value)\n");
161 printf("\nnumber of mismatching file pairs: %d\n", fNumMismatches);
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000162 if (fNumMismatches > 0) {
163 printf("Maximum pixel intensity mismatch %d\n", fMaxMismatchV);
epoger@google.com46a45962012-07-12 18:16:02 +0000164 printf("Largest area mismatch was %.2f%% of pixels\n",fMaxMismatchPercent);
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000165 }
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000166 }
167
reed3a3baf62015-01-06 07:39:55 -0800168 void printfFailingBaseNames(const char separator[]) {
169 for (int resultInt = 0; resultInt < DiffRecord::kResultCount; ++resultInt) {
170 const StringArray& array = fFailedBaseNames[resultInt];
171 if (array.count()) {
172 printf("%s [%d]%s", DiffRecord::ResultNames[resultInt], array.count(), separator);
173 for (int j = 0; j < array.count(); ++j) {
174 printf("%s%s", array[j]->c_str(), separator);
175 }
176 printf("\n");
177 }
178 }
179 }
180
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000181 void add (DiffRecord* drp) {
epoger@google.com46256ea2012-05-22 13:45:35 +0000182 uint32_t mismatchValue;
epoger@google.com292aff62012-05-16 14:57:28 +0000183
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000184 if (drp->fBase.fFilename.equals(drp->fComparison.fFilename)) {
185 fResultsOfType[drp->fResult].push(new SkString(drp->fBase.fFilename));
186 } else {
187 SkString* blame = new SkString("(");
188 blame->append(drp->fBase.fFilename);
189 blame->append(", ");
190 blame->append(drp->fComparison.fFilename);
191 blame->append(")");
192 fResultsOfType[drp->fResult].push(blame);
193 }
epoger@google.com292aff62012-05-16 14:57:28 +0000194 switch (drp->fResult) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000195 case DiffRecord::kEqualBits_Result:
epoger@google.com46256ea2012-05-22 13:45:35 +0000196 fNumMatches++;
197 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000198 case DiffRecord::kEqualPixels_Result:
epoger@google.com292aff62012-05-16 14:57:28 +0000199 fNumMatches++;
200 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000201 case DiffRecord::kDifferentSizes_Result:
epoger@google.com5fd53852012-03-22 18:20:06 +0000202 fNumMismatches++;
epoger@google.com292aff62012-05-16 14:57:28 +0000203 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000204 case DiffRecord::kDifferentPixels_Result:
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000205 fNumMismatches++;
206 if (drp->fFractionDifference * 100 > fMaxMismatchPercent) {
207 fMaxMismatchPercent = drp->fFractionDifference * 100;
208 }
epoger@google.com46256ea2012-05-22 13:45:35 +0000209 mismatchValue = MAX3(drp->fMaxMismatchR, drp->fMaxMismatchG,
210 drp->fMaxMismatchB);
211 if (mismatchValue > fMaxMismatchV) {
212 fMaxMismatchV = mismatchValue;
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000213 }
epoger@google.com292aff62012-05-16 14:57:28 +0000214 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000215 case DiffRecord::kCouldNotCompare_Result:
epoger@google.com46256ea2012-05-22 13:45:35 +0000216 fNumMismatches++;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000217 fStatusOfType[drp->fBase.fStatus][drp->fComparison.fStatus].push(
218 new SkString(drp->fBase.fFilename));
epoger@google.com46256ea2012-05-22 13:45:35 +0000219 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000220 case DiffRecord::kUnknown_Result:
epoger@google.com46256ea2012-05-22 13:45:35 +0000221 SkDEBUGFAIL("adding uncategorized DiffRecord");
222 break;
223 default:
224 SkDEBUGFAIL("adding DiffRecord with unhandled fResult value");
225 break;
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000226 }
reed3a3baf62015-01-06 07:39:55 -0800227
228 switch (drp->fResult) {
229 case DiffRecord::kEqualBits_Result:
230 case DiffRecord::kEqualPixels_Result:
231 break;
232 default:
233 add_unique_basename(&fFailedBaseNames[drp->fResult], drp->fBase.fFilename);
234 break;
235 }
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000236 }
237};
238
epoger@google.coma5f406e2012-05-01 13:26:16 +0000239/// Returns true if string contains any of these substrings.
240static bool string_contains_any_of(const SkString& string,
241 const StringArray& substrings) {
242 for (int i = 0; i < substrings.count(); i++) {
243 if (string.contains(substrings[i]->c_str())) {
244 return true;
245 }
246 }
247 return false;
248}
249
epoger@google.com71329d82012-08-16 13:42:13 +0000250/// Internal (potentially recursive) implementation of get_file_list.
251static void get_file_list_subdir(const SkString& rootDir, const SkString& subDir,
252 const StringArray& matchSubstrings,
253 const StringArray& nomatchSubstrings,
254 bool recurseIntoSubdirs, FileArray *files) {
255 bool isSubDirEmpty = subDir.isEmpty();
256 SkString dir(rootDir);
257 if (!isSubDirEmpty) {
258 dir.append(PATH_DIV_STR);
259 dir.append(subDir);
260 }
261
262 // Iterate over files (not directories) within dir.
263 SkOSFile::Iter fileIterator(dir.c_str());
264 SkString fileName;
265 while (fileIterator.next(&fileName, false)) {
266 if (fileName.startsWith(".")) {
267 continue;
268 }
269 SkString pathRelativeToRootDir(subDir);
270 if (!isSubDirEmpty) {
271 pathRelativeToRootDir.append(PATH_DIV_STR);
272 }
273 pathRelativeToRootDir.append(fileName);
274 if (string_contains_any_of(pathRelativeToRootDir, matchSubstrings) &&
275 !string_contains_any_of(pathRelativeToRootDir, nomatchSubstrings)) {
276 files->push(new SkString(pathRelativeToRootDir));
277 }
278 }
279
280 // Recurse into any non-ignored subdirectories.
281 if (recurseIntoSubdirs) {
282 SkOSFile::Iter dirIterator(dir.c_str());
283 SkString dirName;
284 while (dirIterator.next(&dirName, true)) {
285 if (dirName.startsWith(".")) {
286 continue;
287 }
288 SkString pathRelativeToRootDir(subDir);
289 if (!isSubDirEmpty) {
290 pathRelativeToRootDir.append(PATH_DIV_STR);
291 }
292 pathRelativeToRootDir.append(dirName);
293 if (!string_contains_any_of(pathRelativeToRootDir, nomatchSubstrings)) {
294 get_file_list_subdir(rootDir, pathRelativeToRootDir,
295 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
296 files);
297 }
298 }
299 }
300}
301
302/// Iterate over dir and get all files whose filename:
303/// - matches any of the substrings in matchSubstrings, but...
304/// - DOES NOT match any of the substrings in nomatchSubstrings
305/// - DOES NOT start with a dot (.)
306/// Adds the matching files to the list in *files.
epoger@google.coma5f406e2012-05-01 13:26:16 +0000307static void get_file_list(const SkString& dir,
308 const StringArray& matchSubstrings,
309 const StringArray& nomatchSubstrings,
epoger@google.com71329d82012-08-16 13:42:13 +0000310 bool recurseIntoSubdirs, FileArray *files) {
311 get_file_list_subdir(dir, SkString(""),
312 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
313 files);
epoger@google.com5fd53852012-03-22 18:20:06 +0000314}
315
316static void release_file_list(FileArray *files) {
317 files->deleteAll();
318}
319
320/// Comparison routines for qsort, sort by file names.
321static int compare_file_name_metrics(SkString **lhs, SkString **rhs) {
322 return strcmp((*lhs)->c_str(), (*rhs)->c_str());
323}
324
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000325class AutoReleasePixels {
326public:
327 AutoReleasePixels(DiffRecord* drp)
328 : fDrp(drp) {
halcanary96fcdcc2015-08-27 07:41:13 -0700329 SkASSERT(drp != nullptr);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000330 }
331 ~AutoReleasePixels() {
halcanary96fcdcc2015-08-27 07:41:13 -0700332 fDrp->fBase.fBitmap.setPixelRef(nullptr);
333 fDrp->fComparison.fBitmap.setPixelRef(nullptr);
334 fDrp->fDifference.fBitmap.setPixelRef(nullptr);
335 fDrp->fWhite.fBitmap.setPixelRef(nullptr);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000336 }
337
338private:
339 DiffRecord* fDrp;
340};
341
342static void get_bounds(DiffResource& resource, const char* name) {
343 if (resource.fBitmap.empty() && !DiffResource::isStatusFailed(resource.fStatus)) {
bungeman38d909e2016-08-02 14:40:46 -0700344 sk_sp<SkData> fileBits(read_file(resource.fFullPath.c_str()));
345 if (fileBits) {
reed42943c82016-09-12 12:01:44 -0700346 get_bitmap(fileBits, resource, true);
bungeman38d909e2016-08-02 14:40:46 -0700347 } else {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000348 SkDebugf("WARNING: couldn't read %s file <%s>\n", name, resource.fFullPath.c_str());
349 resource.fStatus = DiffResource::kCouldNotRead_Status;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000350 }
351 }
352}
353
354static void get_bounds(DiffRecord& drp) {
355 get_bounds(drp.fBase, "base");
356 get_bounds(drp.fComparison, "comparison");
357}
358
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000359#ifdef SK_OS_WIN
360#define ANSI_COLOR_RED ""
361#define ANSI_COLOR_GREEN ""
362#define ANSI_COLOR_YELLOW ""
363#define ANSI_COLOR_RESET ""
364#else
365#define ANSI_COLOR_RED "\x1b[31m"
366#define ANSI_COLOR_GREEN "\x1b[32m"
367#define ANSI_COLOR_YELLOW "\x1b[33m"
368#define ANSI_COLOR_RESET "\x1b[0m"
369#endif
370
371#define VERBOSE_STATUS(status,color,filename) if (verbose) printf( "[ " color " %10s " ANSI_COLOR_RESET " ] %s\n", status, filename->c_str())
372
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000373/// Creates difference images, returns the number that have a 0 metric.
epoger@google.coma5f406e2012-05-01 13:26:16 +0000374/// If outputDir.isEmpty(), don't write out diff files.
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000375static void create_diff_images (DiffMetricProc dmp,
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000376 const int colorThreshold,
377 RecordArray* differences,
378 const SkString& baseDir,
379 const SkString& comparisonDir,
380 const SkString& outputDir,
epoger@google.coma5f406e2012-05-01 13:26:16 +0000381 const StringArray& matchSubstrings,
382 const StringArray& nomatchSubstrings,
epoger@google.com71329d82012-08-16 13:42:13 +0000383 bool recurseIntoSubdirs,
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000384 bool getBounds,
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000385 bool verbose,
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000386 DiffSummary* summary) {
epoger@google.coma5f406e2012-05-01 13:26:16 +0000387 SkASSERT(!baseDir.isEmpty());
388 SkASSERT(!comparisonDir.isEmpty());
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000389
epoger@google.com5fd53852012-03-22 18:20:06 +0000390 FileArray baseFiles;
391 FileArray comparisonFiles;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000392
epoger@google.com71329d82012-08-16 13:42:13 +0000393 get_file_list(baseDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, &baseFiles);
394 get_file_list(comparisonDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
epoger@google.coma5f406e2012-05-01 13:26:16 +0000395 &comparisonFiles);
epoger@google.com5fd53852012-03-22 18:20:06 +0000396
epoger@google.coma5f406e2012-05-01 13:26:16 +0000397 if (!baseFiles.isEmpty()) {
reed@google.comc7a67cb2012-05-07 14:52:12 +0000398 qsort(baseFiles.begin(), baseFiles.count(), sizeof(SkString*),
399 SkCastForQSort(compare_file_name_metrics));
epoger@google.coma5f406e2012-05-01 13:26:16 +0000400 }
401 if (!comparisonFiles.isEmpty()) {
reed@google.comc7a67cb2012-05-07 14:52:12 +0000402 qsort(comparisonFiles.begin(), comparisonFiles.count(),
403 sizeof(SkString*), SkCastForQSort(compare_file_name_metrics));
epoger@google.coma5f406e2012-05-01 13:26:16 +0000404 }
epoger@google.com66008522012-05-16 17:40:57 +0000405
brianosman235cbf22016-04-05 11:37:49 -0700406 if (!outputDir.isEmpty()) {
407 sk_mkdir(outputDir.c_str());
408 }
409
epoger@google.com5fd53852012-03-22 18:20:06 +0000410 int i = 0;
411 int j = 0;
412
413 while (i < baseFiles.count() &&
414 j < comparisonFiles.count()) {
415
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000416 SkString basePath(baseDir);
417 SkString comparisonPath(comparisonDir);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000418
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000419 DiffRecord *drp = new DiffRecord;
420 int v = strcmp(baseFiles[i]->c_str(), comparisonFiles[j]->c_str());
epoger@google.com5fd53852012-03-22 18:20:06 +0000421
422 if (v < 0) {
423 // in baseDir, but not in comparisonDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000424 drp->fResult = DiffRecord::kCouldNotCompare_Result;
425
426 basePath.append(*baseFiles[i]);
427 comparisonPath.append(*baseFiles[i]);
428
429 drp->fBase.fFilename = *baseFiles[i];
430 drp->fBase.fFullPath = basePath;
431 drp->fBase.fStatus = DiffResource::kExists_Status;
432
433 drp->fComparison.fFilename = *baseFiles[i];
434 drp->fComparison.fFullPath = comparisonPath;
435 drp->fComparison.fStatus = DiffResource::kDoesNotExist_Status;
436
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000437 VERBOSE_STATUS("MISSING", ANSI_COLOR_YELLOW, baseFiles[i]);
438
epoger@google.com5fd53852012-03-22 18:20:06 +0000439 ++i;
440 } else if (v > 0) {
441 // in comparisonDir, but not in baseDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000442 drp->fResult = DiffRecord::kCouldNotCompare_Result;
443
444 basePath.append(*comparisonFiles[j]);
445 comparisonPath.append(*comparisonFiles[j]);
446
447 drp->fBase.fFilename = *comparisonFiles[j];
448 drp->fBase.fFullPath = basePath;
449 drp->fBase.fStatus = DiffResource::kDoesNotExist_Status;
450
451 drp->fComparison.fFilename = *comparisonFiles[j];
452 drp->fComparison.fFullPath = comparisonPath;
453 drp->fComparison.fStatus = DiffResource::kExists_Status;
454
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000455 VERBOSE_STATUS("MISSING", ANSI_COLOR_YELLOW, comparisonFiles[j]);
456
epoger@google.com5fd53852012-03-22 18:20:06 +0000457 ++j;
458 } else {
epoger@google.com46256ea2012-05-22 13:45:35 +0000459 // Found the same filename in both baseDir and comparisonDir.
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000460 SkASSERT(DiffRecord::kUnknown_Result == drp->fResult);
epoger@google.com5fd53852012-03-22 18:20:06 +0000461
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000462 basePath.append(*baseFiles[i]);
463 comparisonPath.append(*comparisonFiles[j]);
464
465 drp->fBase.fFilename = *baseFiles[i];
466 drp->fBase.fFullPath = basePath;
467 drp->fBase.fStatus = DiffResource::kExists_Status;
468
469 drp->fComparison.fFilename = *comparisonFiles[j];
470 drp->fComparison.fFullPath = comparisonPath;
471 drp->fComparison.fStatus = DiffResource::kExists_Status;
472
bungeman38d909e2016-08-02 14:40:46 -0700473 sk_sp<SkData> baseFileBits(read_file(drp->fBase.fFullPath.c_str()));
bsalomon49f085d2014-09-05 13:34:00 -0700474 if (baseFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000475 drp->fBase.fStatus = DiffResource::kRead_Status;
476 }
bungeman38d909e2016-08-02 14:40:46 -0700477 sk_sp<SkData> comparisonFileBits(read_file(drp->fComparison.fFullPath.c_str()));
bsalomon49f085d2014-09-05 13:34:00 -0700478 if (comparisonFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000479 drp->fComparison.fStatus = DiffResource::kRead_Status;
480 }
halcanary96fcdcc2015-08-27 07:41:13 -0700481 if (nullptr == baseFileBits || nullptr == comparisonFileBits) {
482 if (nullptr == baseFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000483 drp->fBase.fStatus = DiffResource::kCouldNotRead_Status;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000484 VERBOSE_STATUS("READ FAIL", ANSI_COLOR_RED, baseFiles[i]);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000485 }
halcanary96fcdcc2015-08-27 07:41:13 -0700486 if (nullptr == comparisonFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000487 drp->fComparison.fStatus = DiffResource::kCouldNotRead_Status;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000488 VERBOSE_STATUS("READ FAIL", ANSI_COLOR_RED, comparisonFiles[j]);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000489 }
490 drp->fResult = DiffRecord::kCouldNotCompare_Result;
491
bungeman38d909e2016-08-02 14:40:46 -0700492 } else if (are_buffers_equal(baseFileBits.get(), comparisonFileBits.get())) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000493 drp->fResult = DiffRecord::kEqualBits_Result;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000494 VERBOSE_STATUS("MATCH", ANSI_COLOR_GREEN, baseFiles[i]);
epoger@google.com46256ea2012-05-22 13:45:35 +0000495 } else {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000496 AutoReleasePixels arp(drp);
reed42943c82016-09-12 12:01:44 -0700497 get_bitmap(baseFileBits, drp->fBase, false);
498 get_bitmap(comparisonFileBits, drp->fComparison, false);
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000499 VERBOSE_STATUS("DIFFERENT", ANSI_COLOR_RED, baseFiles[i]);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000500 if (DiffResource::kDecoded_Status == drp->fBase.fStatus &&
501 DiffResource::kDecoded_Status == drp->fComparison.fStatus) {
502 create_and_write_diff_image(drp, dmp, colorThreshold,
503 outputDir, drp->fBase.fFilename);
epoger@google.com46256ea2012-05-22 13:45:35 +0000504 } else {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000505 drp->fResult = DiffRecord::kCouldNotCompare_Result;
epoger@google.com46256ea2012-05-22 13:45:35 +0000506 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000507 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000508
epoger@google.com5fd53852012-03-22 18:20:06 +0000509 ++i;
510 ++j;
511 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000512
513 if (getBounds) {
514 get_bounds(*drp);
515 }
516 SkASSERT(DiffRecord::kUnknown_Result != drp->fResult);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000517 differences->push(drp);
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000518 summary->add(drp);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000519 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000520
521 for (; i < baseFiles.count(); ++i) {
522 // files only in baseDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000523 DiffRecord *drp = new DiffRecord();
524 drp->fBase.fFilename = *baseFiles[i];
525 drp->fBase.fFullPath = baseDir;
526 drp->fBase.fFullPath.append(drp->fBase.fFilename);
527 drp->fBase.fStatus = DiffResource::kExists_Status;
528
529 drp->fComparison.fFilename = *baseFiles[i];
530 drp->fComparison.fFullPath = comparisonDir;
531 drp->fComparison.fFullPath.append(drp->fComparison.fFilename);
532 drp->fComparison.fStatus = DiffResource::kDoesNotExist_Status;
533
534 drp->fResult = DiffRecord::kCouldNotCompare_Result;
535 if (getBounds) {
536 get_bounds(*drp);
537 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000538 differences->push(drp);
539 summary->add(drp);
540 }
541
542 for (; j < comparisonFiles.count(); ++j) {
543 // files only in comparisonDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000544 DiffRecord *drp = new DiffRecord();
545 drp->fBase.fFilename = *comparisonFiles[j];
546 drp->fBase.fFullPath = baseDir;
547 drp->fBase.fFullPath.append(drp->fBase.fFilename);
548 drp->fBase.fStatus = DiffResource::kDoesNotExist_Status;
549
550 drp->fComparison.fFilename = *comparisonFiles[j];
551 drp->fComparison.fFullPath = comparisonDir;
552 drp->fComparison.fFullPath.append(drp->fComparison.fFilename);
553 drp->fComparison.fStatus = DiffResource::kExists_Status;
554
555 drp->fResult = DiffRecord::kCouldNotCompare_Result;
556 if (getBounds) {
557 get_bounds(*drp);
558 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000559 differences->push(drp);
560 summary->add(drp);
561 }
562
563 release_file_list(&baseFiles);
564 release_file_list(&comparisonFiles);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000565}
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000566
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000567static void usage (char * argv0) {
568 SkDebugf("Skia baseline image diff tool\n");
epoger@google.coma5f406e2012-05-01 13:26:16 +0000569 SkDebugf("\n"
570"Usage: \n"
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000571" %s <baseDir> <comparisonDir> [outputDir] \n", argv0);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000572 SkDebugf(
epoger@google.com46a45962012-07-12 18:16:02 +0000573"\nArguments:"
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000574"\n --failonresult <result>: After comparing all file pairs, exit with nonzero"
575"\n return code (number of file pairs yielding this"
576"\n result) if any file pairs yielded this result."
577"\n This flag may be repeated, in which case the"
578"\n return code will be the number of fail pairs"
579"\n yielding ANY of these results."
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000580"\n --failonstatus <baseStatus> <comparisonStatus>: exit with nonzero return"
581"\n code if any file pairs yielded this status."
epoger@google.com46a45962012-07-12 18:16:02 +0000582"\n --help: display this info"
epoger@google.com46a45962012-07-12 18:16:02 +0000583"\n --listfilenames: list all filenames for each result type in stdout"
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000584"\n --match <substring>: compare files whose filenames contain this substring;"
585"\n if unspecified, compare ALL files."
586"\n this flag may be repeated."
epoger@google.com46a45962012-07-12 18:16:02 +0000587"\n --nodiffs: don't write out image diffs or index.html, just generate"
588"\n report on stdout"
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000589"\n --nomatch <substring>: regardless of --match, DO NOT compare files whose"
590"\n filenames contain this substring."
591"\n this flag may be repeated."
epoger@google.com46a45962012-07-12 18:16:02 +0000592"\n --noprintdirs: do not print the directories used."
epoger@google.com71329d82012-08-16 13:42:13 +0000593"\n --norecurse: do not recurse into subdirectories."
epoger@google.com46a45962012-07-12 18:16:02 +0000594"\n --sortbymaxmismatch: sort by worst color channel mismatch;"
595"\n break ties with -sortbymismatch"
596"\n --sortbymismatch: sort by average color channel mismatch"
597"\n --threshold <n>: only report differences > n (per color channel) [default 0]"
598"\n --weighted: sort by # pixels different weighted by color difference"
599"\n"
600"\n baseDir: directory to read baseline images from."
601"\n comparisonDir: directory to read comparison images from"
602"\n outputDir: directory to write difference images and index.html to;"
603"\n defaults to comparisonDir"
604"\n"
605"\nIf no sort is specified, it will sort by fraction of pixels mismatching."
606"\n");
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000607}
608
epoger@google.com70044cc2012-07-12 18:37:55 +0000609const int kNoError = 0;
610const int kGenericError = -1;
epoger@google.com46a45962012-07-12 18:16:02 +0000611
caryclark@google.com5987f582012-10-02 18:33:14 +0000612int tool_main(int argc, char** argv);
613int tool_main(int argc, char** argv) {
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000614 DiffMetricProc diffProc = compute_diff_pmcolor;
epoger@google.com28060e72012-06-28 16:47:34 +0000615 int (*sortProc)(const void*, const void*) = compare<CompareDiffMetrics>;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000616
617 // Maximum error tolerated in any one color channel in any one pixel before
618 // a difference is reported.
619 int colorThreshold = 0;
620 SkString baseDir;
621 SkString comparisonDir;
622 SkString outputDir;
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000623
epoger@google.coma5f406e2012-05-01 13:26:16 +0000624 StringArray matchSubstrings;
625 StringArray nomatchSubstrings;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000626
epoger@google.coma5f406e2012-05-01 13:26:16 +0000627 bool generateDiffs = true;
epoger@google.com46a45962012-07-12 18:16:02 +0000628 bool listFilenames = false;
epoger@google.com71329d82012-08-16 13:42:13 +0000629 bool printDirNames = true;
630 bool recurseIntoSubdirs = true;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000631 bool verbose = false;
reed3a3baf62015-01-06 07:39:55 -0800632 bool listFailingBase = false;
tomhudson@google.com7d042802011-07-14 13:15:55 +0000633
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000634 RecordArray differences;
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000635 DiffSummary summary;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000636
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000637 bool failOnResultType[DiffRecord::kResultCount];
638 for (int i = 0; i < DiffRecord::kResultCount; i++) {
epoger@google.com3af4ff42012-07-19 17:35:04 +0000639 failOnResultType[i] = false;
640 }
641
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000642 bool failOnStatusType[DiffResource::kStatusCount][DiffResource::kStatusCount];
643 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
644 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
645 failOnStatusType[base][comparison] = false;
646 }
647 }
648
epoger@google.coma5f406e2012-05-01 13:26:16 +0000649 int i;
650 int numUnflaggedArguments = 0;
651 for (i = 1; i < argc; i++) {
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000652 if (!strcmp(argv[i], "--failonresult")) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000653 if (argc == ++i) {
654 SkDebugf("failonresult expects one argument.\n");
655 continue;
656 }
657 DiffRecord::Result type = DiffRecord::getResultByName(argv[i]);
658 if (type != DiffRecord::kResultCount) {
659 failOnResultType[type] = true;
660 } else {
661 SkDebugf("ignoring unrecognized result <%s>\n", argv[i]);
662 }
663 continue;
664 }
665 if (!strcmp(argv[i], "--failonstatus")) {
666 if (argc == ++i) {
667 SkDebugf("failonstatus missing base status.\n");
668 continue;
669 }
670 bool baseStatuses[DiffResource::kStatusCount];
671 if (!DiffResource::getMatchingStatuses(argv[i], baseStatuses)) {
672 SkDebugf("unrecognized base status <%s>\n", argv[i]);
673 }
674
675 if (argc == ++i) {
676 SkDebugf("failonstatus missing comparison status.\n");
677 continue;
678 }
679 bool comparisonStatuses[DiffResource::kStatusCount];
680 if (!DiffResource::getMatchingStatuses(argv[i], comparisonStatuses)) {
681 SkDebugf("unrecognized comarison status <%s>\n", argv[i]);
682 }
683
684 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
685 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
686 failOnStatusType[base][comparison] |=
687 baseStatuses[base] && comparisonStatuses[comparison];
688 }
689 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000690 continue;
691 }
epoger@google.com46a45962012-07-12 18:16:02 +0000692 if (!strcmp(argv[i], "--help")) {
693 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000694 return kNoError;
epoger@google.com46a45962012-07-12 18:16:02 +0000695 }
696 if (!strcmp(argv[i], "--listfilenames")) {
697 listFilenames = true;
epoger@google.coma5f406e2012-05-01 13:26:16 +0000698 continue;
699 }
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000700 if (!strcmp(argv[i], "--verbose")) {
701 verbose = true;
702 continue;
703 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000704 if (!strcmp(argv[i], "--match")) {
705 matchSubstrings.push(new SkString(argv[++i]));
706 continue;
707 }
epoger@google.com46a45962012-07-12 18:16:02 +0000708 if (!strcmp(argv[i], "--nodiffs")) {
709 generateDiffs = false;
710 continue;
711 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000712 if (!strcmp(argv[i], "--nomatch")) {
713 nomatchSubstrings.push(new SkString(argv[++i]));
714 continue;
715 }
epoger@google.com46a45962012-07-12 18:16:02 +0000716 if (!strcmp(argv[i], "--noprintdirs")) {
epoger@google.com71329d82012-08-16 13:42:13 +0000717 printDirNames = false;
718 continue;
719 }
720 if (!strcmp(argv[i], "--norecurse")) {
721 recurseIntoSubdirs = false;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000722 continue;
723 }
tomhudson@google.com7d042802011-07-14 13:15:55 +0000724 if (!strcmp(argv[i], "--sortbymaxmismatch")) {
epoger@google.com28060e72012-06-28 16:47:34 +0000725 sortProc = compare<CompareDiffMaxMismatches>;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000726 continue;
727 }
epoger@google.com46a45962012-07-12 18:16:02 +0000728 if (!strcmp(argv[i], "--sortbymismatch")) {
729 sortProc = compare<CompareDiffMeanMismatches>;
tomhudson@google.com5b325292011-05-24 19:41:13 +0000730 continue;
731 }
epoger@google.com46a45962012-07-12 18:16:02 +0000732 if (!strcmp(argv[i], "--threshold")) {
733 colorThreshold = atoi(argv[++i]);
734 continue;
735 }
736 if (!strcmp(argv[i], "--weighted")) {
737 sortProc = compare<CompareDiffWeighted>;
keyar@chromium.orga6318192012-07-09 21:01:50 +0000738 continue;
739 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000740 if (argv[i][0] != '-') {
epoger@google.coma5f406e2012-05-01 13:26:16 +0000741 switch (numUnflaggedArguments++) {
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000742 case 0:
743 baseDir.set(argv[i]);
744 continue;
745 case 1:
746 comparisonDir.set(argv[i]);
747 continue;
748 case 2:
749 outputDir.set(argv[i]);
750 continue;
751 default:
epoger@google.coma5f406e2012-05-01 13:26:16 +0000752 SkDebugf("extra unflagged argument <%s>\n", argv[i]);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000753 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000754 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000755 }
756 }
reed3a3baf62015-01-06 07:39:55 -0800757 if (!strcmp(argv[i], "--listFailingBase")) {
758 listFailingBase = true;
759 continue;
760 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000761
762 SkDebugf("Unrecognized argument <%s>\n", argv[i]);
763 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000764 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000765 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000766
epoger@google.coma5f406e2012-05-01 13:26:16 +0000767 if (numUnflaggedArguments == 2) {
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000768 outputDir = comparisonDir;
epoger@google.coma5f406e2012-05-01 13:26:16 +0000769 } else if (numUnflaggedArguments != 3) {
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000770 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000771 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000772 }
773
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000774 if (!baseDir.endsWith(PATH_DIV_STR)) {
775 baseDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000776 }
epoger@google.com71329d82012-08-16 13:42:13 +0000777 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000778 printf("baseDir is [%s]\n", baseDir.c_str());
779 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000780
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000781 if (!comparisonDir.endsWith(PATH_DIV_STR)) {
782 comparisonDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000783 }
epoger@google.com71329d82012-08-16 13:42:13 +0000784 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000785 printf("comparisonDir is [%s]\n", comparisonDir.c_str());
786 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000787
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000788 if (!outputDir.endsWith(PATH_DIV_STR)) {
789 outputDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000790 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000791 if (generateDiffs) {
epoger@google.com71329d82012-08-16 13:42:13 +0000792 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000793 printf("writing diffs to outputDir is [%s]\n", outputDir.c_str());
794 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000795 } else {
epoger@google.com71329d82012-08-16 13:42:13 +0000796 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000797 printf("not writing any diffs to outputDir [%s]\n", outputDir.c_str());
798 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000799 outputDir.set("");
800 }
801
epoger@google.comda4af242012-06-25 18:45:50 +0000802 // If no matchSubstrings were specified, match ALL strings
803 // (except for whatever nomatchSubstrings were specified, if any).
epoger@google.coma5f406e2012-05-01 13:26:16 +0000804 if (matchSubstrings.isEmpty()) {
805 matchSubstrings.push(new SkString(""));
806 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000807
epoger@google.coma611c3e2012-05-18 20:10:06 +0000808 create_diff_images(diffProc, colorThreshold, &differences,
809 baseDir, comparisonDir, outputDir,
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000810 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, generateDiffs,
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000811 verbose, &summary);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000812 summary.print(listFilenames, failOnResultType, failOnStatusType);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000813
reed3a3baf62015-01-06 07:39:55 -0800814 if (listFailingBase) {
815 summary.printfFailingBaseNames("\n");
816 }
817
tomhudson@google.com7d042802011-07-14 13:15:55 +0000818 if (differences.count()) {
reed@google.comc7a67cb2012-05-07 14:52:12 +0000819 qsort(differences.begin(), differences.count(),
820 sizeof(DiffRecord*), sortProc);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000821 }
epoger@google.com66008522012-05-16 17:40:57 +0000822
epoger@google.coma5f406e2012-05-01 13:26:16 +0000823 if (generateDiffs) {
824 print_diff_page(summary.fNumMatches, colorThreshold, differences,
825 baseDir, comparisonDir, outputDir);
826 }
epoger@google.com76222c02012-05-31 15:12:09 +0000827
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000828 for (i = 0; i < differences.count(); i++) {
829 delete differences[i];
830 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000831 matchSubstrings.deleteAll();
832 nomatchSubstrings.deleteAll();
epoger@google.combe6188d2012-05-31 15:13:45 +0000833
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000834 int num_failing_results = 0;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000835 for (int i = 0; i < DiffRecord::kResultCount; i++) {
epoger@google.com3af4ff42012-07-19 17:35:04 +0000836 if (failOnResultType[i]) {
837 num_failing_results += summary.fResultsOfType[i].count();
838 }
epoger@google.com46a45962012-07-12 18:16:02 +0000839 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000840 if (!failOnResultType[DiffRecord::kCouldNotCompare_Result]) {
841 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
842 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
843 if (failOnStatusType[base][comparison]) {
844 num_failing_results += summary.fStatusOfType[base][comparison].count();
845 }
846 }
847 }
848 }
epoger@google.com28659882012-07-16 18:01:06 +0000849
850 // On Linux (and maybe other platforms too), any results outside of the
851 // range [0...255] are wrapped (mod 256). Do the conversion ourselves, to
852 // make sure that we only return 0 when there were no failures.
853 return (num_failing_results > 255) ? 255 : num_failing_results;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000854}
caryclark@google.com5987f582012-10-02 18:33:14 +0000855
856#if !defined SK_BUILD_FOR_IOS
857int main(int argc, char * const argv[]) {
858 return tool_main(argc, (char**) argv);
859}
860#endif