blob: b4e560729cedec5661c82cb992571c0afa96eb76 [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"
zachr@google.com6ef5a852013-06-18 21:23:31 +000012#include "SkForceLinking.h"
tomhudson@google.com4b33d282011-04-27 15:39:30 +000013#include "SkImageEncoder.h"
14#include "SkOSFile.h"
Ben Wagnerbf111d72016-11-07 18:05:29 -050015#include "SkOSPath.h"
tomhudson@google.com4b33d282011-04-27 15:39:30 +000016#include "SkStream.h"
bungemana7e9f052016-02-18 08:53:33 -080017#include "../private/SkTDArray.h"
reed76a834a2016-01-03 18:36:05 -080018#include "../private/SkTSearch.h"
tomhudson@google.com4b33d282011-04-27 15:39:30 +000019
bungeman60e0fee2015-08-26 05:15:46 -070020#include <stdlib.h>
21
zachr@google.com6ef5a852013-06-18 21:23:31 +000022__SK_FORCE_IMAGE_DECODER_LINKING;
23
tomhudson@google.com4b33d282011-04-27 15:39:30 +000024/**
25 * skdiff
26 *
27 * Given three directory names, expects to find identically-named files in
28 * each of the first two; the first are treated as a set of baseline,
29 * the second a set of variant images, and a diff image is written into the
30 * third directory for each pair.
tomhudson@google.com7d042802011-07-14 13:15:55 +000031 * Creates an index.html in the current third directory to compare each
tomhudson@google.com4b33d282011-04-27 15:39:30 +000032 * pair that does not match exactly.
epoger@google.com71329d82012-08-16 13:42:13 +000033 * Recursively descends directories, unless run with --norecurse.
epoger@google.combe6188d2012-05-31 15:13:45 +000034 *
35 * Returns zero exit code if all images match across baseDir and comparisonDir.
tomhudson@google.com4b33d282011-04-27 15:39:30 +000036 */
37
epoger@google.coma5f406e2012-05-01 13:26:16 +000038typedef SkTDArray<SkString*> StringArray;
39typedef StringArray FileArray;
epoger@google.com5fd53852012-03-22 18:20:06 +000040
reed3a3baf62015-01-06 07:39:55 -080041static void add_unique_basename(StringArray* array, const SkString& filename) {
42 // trim off dirs
43 const char* src = filename.c_str();
Ben Wagnerbf111d72016-11-07 18:05:29 -050044 const char* trimmed = strrchr(src, SkOSPath::SEPARATOR);
reed3a3baf62015-01-06 07:39:55 -080045 if (trimmed) {
46 trimmed += 1; // skip the separator
47 } else {
48 trimmed = src;
49 }
50 const char* end = strrchr(trimmed, '.');
51 if (!end) {
52 end = trimmed + strlen(trimmed);
53 }
54 SkString result(trimmed, end - trimmed);
55
56 // only add unique entries
57 for (int i = 0; i < array->count(); ++i) {
58 if (*array->getAt(i) == result) {
59 return;
60 }
61 }
62 *array->append() = new SkString(result);
63}
64
tomhudson@google.com9dc527b2011-06-09 15:47:10 +000065struct DiffSummary {
66 DiffSummary ()
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000067 : fNumMatches(0)
68 , fNumMismatches(0)
69 , fMaxMismatchV(0)
bungemanfe917272016-10-13 17:36:40 -040070 , fMaxMismatchPercent(0) { }
tomhudson@google.com9dc527b2011-06-09 15:47:10 +000071
epoger@google.com5fd53852012-03-22 18:20:06 +000072 ~DiffSummary() {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000073 for (int i = 0; i < DiffRecord::kResultCount; ++i) {
epoger@google.com76222c02012-05-31 15:12:09 +000074 fResultsOfType[i].deleteAll();
75 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000076 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
77 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
78 fStatusOfType[base][comparison].deleteAll();
79 }
skia.committer@gmail.com0264fb42012-12-06 02:01:25 +000080 }
epoger@google.com5fd53852012-03-22 18:20:06 +000081 }
82
tomhudson@google.com9dc527b2011-06-09 15:47:10 +000083 uint32_t fNumMatches;
84 uint32_t fNumMismatches;
85 uint32_t fMaxMismatchV;
86 float fMaxMismatchPercent;
87
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000088 FileArray fResultsOfType[DiffRecord::kResultCount];
89 FileArray fStatusOfType[DiffResource::kStatusCount][DiffResource::kStatusCount];
90
reed3a3baf62015-01-06 07:39:55 -080091 StringArray fFailedBaseNames[DiffRecord::kResultCount];
92
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000093 void printContents(const FileArray& fileArray,
94 const char* baseStatus, const char* comparisonStatus,
95 bool listFilenames) {
96 int n = fileArray.count();
97 printf("%d file pairs %s in baseDir and %s in comparisonDir",
98 n, baseStatus, comparisonStatus);
99 if (listFilenames) {
100 printf(": ");
101 for (int i = 0; i < n; ++i) {
102 printf("%s ", fileArray[i]->c_str());
103 }
104 }
105 printf("\n");
106 }
107
108 void printStatus(bool listFilenames,
109 bool failOnStatusType[DiffResource::kStatusCount]
110 [DiffResource::kStatusCount]) {
111 typedef DiffResource::Status Status;
112
113 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
114 Status baseStatus = static_cast<Status>(base);
115 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
116 Status comparisonStatus = static_cast<Status>(comparison);
117 const FileArray& fileArray = fStatusOfType[base][comparison];
118 if (fileArray.count() > 0) {
119 if (failOnStatusType[base][comparison]) {
120 printf(" [*] ");
121 } else {
122 printf(" [_] ");
123 }
124 printContents(fileArray,
125 DiffResource::getStatusDescription(baseStatus),
126 DiffResource::getStatusDescription(comparisonStatus),
127 listFilenames);
128 }
129 }
130 }
131 }
epoger@google.com76222c02012-05-31 15:12:09 +0000132
epoger@google.com3af4ff42012-07-19 17:35:04 +0000133 // Print a line about the contents of this FileArray to stdout.
epoger@google.com46a45962012-07-12 18:16:02 +0000134 void printContents(const FileArray& fileArray, const char* headerText, bool listFilenames) {
epoger@google.com76222c02012-05-31 15:12:09 +0000135 int n = fileArray.count();
epoger@google.com3af4ff42012-07-19 17:35:04 +0000136 printf("%d file pairs %s", n, headerText);
137 if (listFilenames) {
138 printf(": ");
139 for (int i = 0; i < n; ++i) {
140 printf("%s ", fileArray[i]->c_str());
epoger@google.com76222c02012-05-31 15:12:09 +0000141 }
142 }
epoger@google.com3af4ff42012-07-19 17:35:04 +0000143 printf("\n");
epoger@google.com76222c02012-05-31 15:12:09 +0000144 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000145
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000146 void print(bool listFilenames, bool failOnResultType[DiffRecord::kResultCount],
147 bool failOnStatusType[DiffResource::kStatusCount]
148 [DiffResource::kStatusCount]) {
epoger@google.com3af4ff42012-07-19 17:35:04 +0000149 printf("\ncompared %d file pairs:\n", fNumMatches + fNumMismatches);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000150 for (int resultInt = 0; resultInt < DiffRecord::kResultCount; ++resultInt) {
151 DiffRecord::Result result = static_cast<DiffRecord::Result>(resultInt);
epoger@google.com3af4ff42012-07-19 17:35:04 +0000152 if (failOnResultType[result]) {
153 printf("[*] ");
154 } else {
155 printf("[_] ");
156 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000157 printContents(fResultsOfType[result], DiffRecord::getResultDescription(result),
158 listFilenames);
159 if (DiffRecord::kCouldNotCompare_Result == result) {
160 printStatus(listFilenames, failOnStatusType);
161 }
epoger@google.com46a45962012-07-12 18:16:02 +0000162 }
epoger@google.com3af4ff42012-07-19 17:35:04 +0000163 printf("(results marked with [*] will cause nonzero return value)\n");
164 printf("\nnumber of mismatching file pairs: %d\n", fNumMismatches);
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000165 if (fNumMismatches > 0) {
166 printf("Maximum pixel intensity mismatch %d\n", fMaxMismatchV);
epoger@google.com46a45962012-07-12 18:16:02 +0000167 printf("Largest area mismatch was %.2f%% of pixels\n",fMaxMismatchPercent);
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000168 }
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000169 }
170
reed3a3baf62015-01-06 07:39:55 -0800171 void printfFailingBaseNames(const char separator[]) {
172 for (int resultInt = 0; resultInt < DiffRecord::kResultCount; ++resultInt) {
173 const StringArray& array = fFailedBaseNames[resultInt];
174 if (array.count()) {
175 printf("%s [%d]%s", DiffRecord::ResultNames[resultInt], array.count(), separator);
176 for (int j = 0; j < array.count(); ++j) {
177 printf("%s%s", array[j]->c_str(), separator);
178 }
179 printf("\n");
180 }
181 }
182 }
183
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000184 void add (DiffRecord* drp) {
epoger@google.com46256ea2012-05-22 13:45:35 +0000185 uint32_t mismatchValue;
epoger@google.com292aff62012-05-16 14:57:28 +0000186
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000187 if (drp->fBase.fFilename.equals(drp->fComparison.fFilename)) {
188 fResultsOfType[drp->fResult].push(new SkString(drp->fBase.fFilename));
189 } else {
190 SkString* blame = new SkString("(");
191 blame->append(drp->fBase.fFilename);
192 blame->append(", ");
193 blame->append(drp->fComparison.fFilename);
194 blame->append(")");
195 fResultsOfType[drp->fResult].push(blame);
196 }
epoger@google.com292aff62012-05-16 14:57:28 +0000197 switch (drp->fResult) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000198 case DiffRecord::kEqualBits_Result:
epoger@google.com46256ea2012-05-22 13:45:35 +0000199 fNumMatches++;
200 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000201 case DiffRecord::kEqualPixels_Result:
epoger@google.com292aff62012-05-16 14:57:28 +0000202 fNumMatches++;
203 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000204 case DiffRecord::kDifferentSizes_Result:
epoger@google.com5fd53852012-03-22 18:20:06 +0000205 fNumMismatches++;
epoger@google.com292aff62012-05-16 14:57:28 +0000206 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000207 case DiffRecord::kDifferentPixels_Result:
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000208 fNumMismatches++;
209 if (drp->fFractionDifference * 100 > fMaxMismatchPercent) {
210 fMaxMismatchPercent = drp->fFractionDifference * 100;
211 }
epoger@google.com46256ea2012-05-22 13:45:35 +0000212 mismatchValue = MAX3(drp->fMaxMismatchR, drp->fMaxMismatchG,
213 drp->fMaxMismatchB);
214 if (mismatchValue > fMaxMismatchV) {
215 fMaxMismatchV = mismatchValue;
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000216 }
epoger@google.com292aff62012-05-16 14:57:28 +0000217 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000218 case DiffRecord::kCouldNotCompare_Result:
epoger@google.com46256ea2012-05-22 13:45:35 +0000219 fNumMismatches++;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000220 fStatusOfType[drp->fBase.fStatus][drp->fComparison.fStatus].push(
221 new SkString(drp->fBase.fFilename));
epoger@google.com46256ea2012-05-22 13:45:35 +0000222 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000223 case DiffRecord::kUnknown_Result:
epoger@google.com46256ea2012-05-22 13:45:35 +0000224 SkDEBUGFAIL("adding uncategorized DiffRecord");
225 break;
226 default:
227 SkDEBUGFAIL("adding DiffRecord with unhandled fResult value");
228 break;
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000229 }
reed3a3baf62015-01-06 07:39:55 -0800230
231 switch (drp->fResult) {
232 case DiffRecord::kEqualBits_Result:
233 case DiffRecord::kEqualPixels_Result:
234 break;
235 default:
236 add_unique_basename(&fFailedBaseNames[drp->fResult], drp->fBase.fFilename);
237 break;
238 }
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000239 }
240};
241
epoger@google.coma5f406e2012-05-01 13:26:16 +0000242/// Returns true if string contains any of these substrings.
243static bool string_contains_any_of(const SkString& string,
244 const StringArray& substrings) {
245 for (int i = 0; i < substrings.count(); i++) {
246 if (string.contains(substrings[i]->c_str())) {
247 return true;
248 }
249 }
250 return false;
251}
252
epoger@google.com71329d82012-08-16 13:42:13 +0000253/// Internal (potentially recursive) implementation of get_file_list.
254static void get_file_list_subdir(const SkString& rootDir, const SkString& subDir,
255 const StringArray& matchSubstrings,
256 const StringArray& nomatchSubstrings,
257 bool recurseIntoSubdirs, FileArray *files) {
258 bool isSubDirEmpty = subDir.isEmpty();
259 SkString dir(rootDir);
260 if (!isSubDirEmpty) {
261 dir.append(PATH_DIV_STR);
262 dir.append(subDir);
263 }
264
265 // Iterate over files (not directories) within dir.
266 SkOSFile::Iter fileIterator(dir.c_str());
267 SkString fileName;
268 while (fileIterator.next(&fileName, false)) {
269 if (fileName.startsWith(".")) {
270 continue;
271 }
272 SkString pathRelativeToRootDir(subDir);
273 if (!isSubDirEmpty) {
274 pathRelativeToRootDir.append(PATH_DIV_STR);
275 }
276 pathRelativeToRootDir.append(fileName);
277 if (string_contains_any_of(pathRelativeToRootDir, matchSubstrings) &&
278 !string_contains_any_of(pathRelativeToRootDir, nomatchSubstrings)) {
279 files->push(new SkString(pathRelativeToRootDir));
280 }
281 }
282
283 // Recurse into any non-ignored subdirectories.
284 if (recurseIntoSubdirs) {
285 SkOSFile::Iter dirIterator(dir.c_str());
286 SkString dirName;
287 while (dirIterator.next(&dirName, true)) {
288 if (dirName.startsWith(".")) {
289 continue;
290 }
291 SkString pathRelativeToRootDir(subDir);
292 if (!isSubDirEmpty) {
293 pathRelativeToRootDir.append(PATH_DIV_STR);
294 }
295 pathRelativeToRootDir.append(dirName);
296 if (!string_contains_any_of(pathRelativeToRootDir, nomatchSubstrings)) {
297 get_file_list_subdir(rootDir, pathRelativeToRootDir,
298 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
299 files);
300 }
301 }
302 }
303}
304
305/// Iterate over dir and get all files whose filename:
306/// - matches any of the substrings in matchSubstrings, but...
307/// - DOES NOT match any of the substrings in nomatchSubstrings
308/// - DOES NOT start with a dot (.)
309/// Adds the matching files to the list in *files.
epoger@google.coma5f406e2012-05-01 13:26:16 +0000310static void get_file_list(const SkString& dir,
311 const StringArray& matchSubstrings,
312 const StringArray& nomatchSubstrings,
epoger@google.com71329d82012-08-16 13:42:13 +0000313 bool recurseIntoSubdirs, FileArray *files) {
314 get_file_list_subdir(dir, SkString(""),
315 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
316 files);
epoger@google.com5fd53852012-03-22 18:20:06 +0000317}
318
319static void release_file_list(FileArray *files) {
320 files->deleteAll();
321}
322
323/// Comparison routines for qsort, sort by file names.
324static int compare_file_name_metrics(SkString **lhs, SkString **rhs) {
325 return strcmp((*lhs)->c_str(), (*rhs)->c_str());
326}
327
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000328class AutoReleasePixels {
329public:
330 AutoReleasePixels(DiffRecord* drp)
331 : fDrp(drp) {
halcanary96fcdcc2015-08-27 07:41:13 -0700332 SkASSERT(drp != nullptr);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000333 }
334 ~AutoReleasePixels() {
halcanary96fcdcc2015-08-27 07:41:13 -0700335 fDrp->fBase.fBitmap.setPixelRef(nullptr);
336 fDrp->fComparison.fBitmap.setPixelRef(nullptr);
337 fDrp->fDifference.fBitmap.setPixelRef(nullptr);
338 fDrp->fWhite.fBitmap.setPixelRef(nullptr);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000339 }
340
341private:
342 DiffRecord* fDrp;
343};
344
345static void get_bounds(DiffResource& resource, const char* name) {
346 if (resource.fBitmap.empty() && !DiffResource::isStatusFailed(resource.fStatus)) {
bungeman38d909e2016-08-02 14:40:46 -0700347 sk_sp<SkData> fileBits(read_file(resource.fFullPath.c_str()));
348 if (fileBits) {
reed42943c82016-09-12 12:01:44 -0700349 get_bitmap(fileBits, resource, true);
bungeman38d909e2016-08-02 14:40:46 -0700350 } else {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000351 SkDebugf("WARNING: couldn't read %s file <%s>\n", name, resource.fFullPath.c_str());
352 resource.fStatus = DiffResource::kCouldNotRead_Status;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000353 }
354 }
355}
356
357static void get_bounds(DiffRecord& drp) {
358 get_bounds(drp.fBase, "base");
359 get_bounds(drp.fComparison, "comparison");
360}
361
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000362#ifdef SK_OS_WIN
363#define ANSI_COLOR_RED ""
364#define ANSI_COLOR_GREEN ""
365#define ANSI_COLOR_YELLOW ""
366#define ANSI_COLOR_RESET ""
367#else
368#define ANSI_COLOR_RED "\x1b[31m"
369#define ANSI_COLOR_GREEN "\x1b[32m"
370#define ANSI_COLOR_YELLOW "\x1b[33m"
371#define ANSI_COLOR_RESET "\x1b[0m"
372#endif
373
374#define VERBOSE_STATUS(status,color,filename) if (verbose) printf( "[ " color " %10s " ANSI_COLOR_RESET " ] %s\n", status, filename->c_str())
375
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000376/// Creates difference images, returns the number that have a 0 metric.
epoger@google.coma5f406e2012-05-01 13:26:16 +0000377/// If outputDir.isEmpty(), don't write out diff files.
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000378static void create_diff_images (DiffMetricProc dmp,
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000379 const int colorThreshold,
380 RecordArray* differences,
381 const SkString& baseDir,
382 const SkString& comparisonDir,
383 const SkString& outputDir,
epoger@google.coma5f406e2012-05-01 13:26:16 +0000384 const StringArray& matchSubstrings,
385 const StringArray& nomatchSubstrings,
epoger@google.com71329d82012-08-16 13:42:13 +0000386 bool recurseIntoSubdirs,
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000387 bool getBounds,
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000388 bool verbose,
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000389 DiffSummary* summary) {
epoger@google.coma5f406e2012-05-01 13:26:16 +0000390 SkASSERT(!baseDir.isEmpty());
391 SkASSERT(!comparisonDir.isEmpty());
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000392
epoger@google.com5fd53852012-03-22 18:20:06 +0000393 FileArray baseFiles;
394 FileArray comparisonFiles;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000395
epoger@google.com71329d82012-08-16 13:42:13 +0000396 get_file_list(baseDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, &baseFiles);
397 get_file_list(comparisonDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
epoger@google.coma5f406e2012-05-01 13:26:16 +0000398 &comparisonFiles);
epoger@google.com5fd53852012-03-22 18:20:06 +0000399
epoger@google.coma5f406e2012-05-01 13:26:16 +0000400 if (!baseFiles.isEmpty()) {
reed@google.comc7a67cb2012-05-07 14:52:12 +0000401 qsort(baseFiles.begin(), baseFiles.count(), sizeof(SkString*),
402 SkCastForQSort(compare_file_name_metrics));
epoger@google.coma5f406e2012-05-01 13:26:16 +0000403 }
404 if (!comparisonFiles.isEmpty()) {
reed@google.comc7a67cb2012-05-07 14:52:12 +0000405 qsort(comparisonFiles.begin(), comparisonFiles.count(),
406 sizeof(SkString*), SkCastForQSort(compare_file_name_metrics));
epoger@google.coma5f406e2012-05-01 13:26:16 +0000407 }
epoger@google.com66008522012-05-16 17:40:57 +0000408
brianosman235cbf22016-04-05 11:37:49 -0700409 if (!outputDir.isEmpty()) {
410 sk_mkdir(outputDir.c_str());
411 }
412
epoger@google.com5fd53852012-03-22 18:20:06 +0000413 int i = 0;
414 int j = 0;
415
416 while (i < baseFiles.count() &&
417 j < comparisonFiles.count()) {
418
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000419 SkString basePath(baseDir);
420 SkString comparisonPath(comparisonDir);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000421
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000422 DiffRecord *drp = new DiffRecord;
423 int v = strcmp(baseFiles[i]->c_str(), comparisonFiles[j]->c_str());
epoger@google.com5fd53852012-03-22 18:20:06 +0000424
425 if (v < 0) {
426 // in baseDir, but not in comparisonDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000427 drp->fResult = DiffRecord::kCouldNotCompare_Result;
428
429 basePath.append(*baseFiles[i]);
430 comparisonPath.append(*baseFiles[i]);
431
432 drp->fBase.fFilename = *baseFiles[i];
433 drp->fBase.fFullPath = basePath;
434 drp->fBase.fStatus = DiffResource::kExists_Status;
435
436 drp->fComparison.fFilename = *baseFiles[i];
437 drp->fComparison.fFullPath = comparisonPath;
438 drp->fComparison.fStatus = DiffResource::kDoesNotExist_Status;
439
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000440 VERBOSE_STATUS("MISSING", ANSI_COLOR_YELLOW, baseFiles[i]);
441
epoger@google.com5fd53852012-03-22 18:20:06 +0000442 ++i;
443 } else if (v > 0) {
444 // in comparisonDir, but not in baseDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000445 drp->fResult = DiffRecord::kCouldNotCompare_Result;
446
447 basePath.append(*comparisonFiles[j]);
448 comparisonPath.append(*comparisonFiles[j]);
449
450 drp->fBase.fFilename = *comparisonFiles[j];
451 drp->fBase.fFullPath = basePath;
452 drp->fBase.fStatus = DiffResource::kDoesNotExist_Status;
453
454 drp->fComparison.fFilename = *comparisonFiles[j];
455 drp->fComparison.fFullPath = comparisonPath;
456 drp->fComparison.fStatus = DiffResource::kExists_Status;
457
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000458 VERBOSE_STATUS("MISSING", ANSI_COLOR_YELLOW, comparisonFiles[j]);
459
epoger@google.com5fd53852012-03-22 18:20:06 +0000460 ++j;
461 } else {
epoger@google.com46256ea2012-05-22 13:45:35 +0000462 // Found the same filename in both baseDir and comparisonDir.
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000463 SkASSERT(DiffRecord::kUnknown_Result == drp->fResult);
epoger@google.com5fd53852012-03-22 18:20:06 +0000464
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000465 basePath.append(*baseFiles[i]);
466 comparisonPath.append(*comparisonFiles[j]);
467
468 drp->fBase.fFilename = *baseFiles[i];
469 drp->fBase.fFullPath = basePath;
470 drp->fBase.fStatus = DiffResource::kExists_Status;
471
472 drp->fComparison.fFilename = *comparisonFiles[j];
473 drp->fComparison.fFullPath = comparisonPath;
474 drp->fComparison.fStatus = DiffResource::kExists_Status;
475
bungeman38d909e2016-08-02 14:40:46 -0700476 sk_sp<SkData> baseFileBits(read_file(drp->fBase.fFullPath.c_str()));
bsalomon49f085d2014-09-05 13:34:00 -0700477 if (baseFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000478 drp->fBase.fStatus = DiffResource::kRead_Status;
479 }
bungeman38d909e2016-08-02 14:40:46 -0700480 sk_sp<SkData> comparisonFileBits(read_file(drp->fComparison.fFullPath.c_str()));
bsalomon49f085d2014-09-05 13:34:00 -0700481 if (comparisonFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000482 drp->fComparison.fStatus = DiffResource::kRead_Status;
483 }
halcanary96fcdcc2015-08-27 07:41:13 -0700484 if (nullptr == baseFileBits || nullptr == comparisonFileBits) {
485 if (nullptr == baseFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000486 drp->fBase.fStatus = DiffResource::kCouldNotRead_Status;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000487 VERBOSE_STATUS("READ FAIL", ANSI_COLOR_RED, baseFiles[i]);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000488 }
halcanary96fcdcc2015-08-27 07:41:13 -0700489 if (nullptr == comparisonFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000490 drp->fComparison.fStatus = DiffResource::kCouldNotRead_Status;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000491 VERBOSE_STATUS("READ FAIL", ANSI_COLOR_RED, comparisonFiles[j]);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000492 }
493 drp->fResult = DiffRecord::kCouldNotCompare_Result;
494
bungeman38d909e2016-08-02 14:40:46 -0700495 } else if (are_buffers_equal(baseFileBits.get(), comparisonFileBits.get())) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000496 drp->fResult = DiffRecord::kEqualBits_Result;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000497 VERBOSE_STATUS("MATCH", ANSI_COLOR_GREEN, baseFiles[i]);
epoger@google.com46256ea2012-05-22 13:45:35 +0000498 } else {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000499 AutoReleasePixels arp(drp);
reed42943c82016-09-12 12:01:44 -0700500 get_bitmap(baseFileBits, drp->fBase, false);
501 get_bitmap(comparisonFileBits, drp->fComparison, false);
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000502 VERBOSE_STATUS("DIFFERENT", ANSI_COLOR_RED, baseFiles[i]);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000503 if (DiffResource::kDecoded_Status == drp->fBase.fStatus &&
504 DiffResource::kDecoded_Status == drp->fComparison.fStatus) {
505 create_and_write_diff_image(drp, dmp, colorThreshold,
506 outputDir, drp->fBase.fFilename);
epoger@google.com46256ea2012-05-22 13:45:35 +0000507 } else {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000508 drp->fResult = DiffRecord::kCouldNotCompare_Result;
epoger@google.com46256ea2012-05-22 13:45:35 +0000509 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000510 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000511
epoger@google.com5fd53852012-03-22 18:20:06 +0000512 ++i;
513 ++j;
514 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000515
516 if (getBounds) {
517 get_bounds(*drp);
518 }
519 SkASSERT(DiffRecord::kUnknown_Result != drp->fResult);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000520 differences->push(drp);
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000521 summary->add(drp);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000522 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000523
524 for (; i < baseFiles.count(); ++i) {
525 // files only in baseDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000526 DiffRecord *drp = new DiffRecord();
527 drp->fBase.fFilename = *baseFiles[i];
528 drp->fBase.fFullPath = baseDir;
529 drp->fBase.fFullPath.append(drp->fBase.fFilename);
530 drp->fBase.fStatus = DiffResource::kExists_Status;
531
532 drp->fComparison.fFilename = *baseFiles[i];
533 drp->fComparison.fFullPath = comparisonDir;
534 drp->fComparison.fFullPath.append(drp->fComparison.fFilename);
535 drp->fComparison.fStatus = DiffResource::kDoesNotExist_Status;
536
537 drp->fResult = DiffRecord::kCouldNotCompare_Result;
538 if (getBounds) {
539 get_bounds(*drp);
540 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000541 differences->push(drp);
542 summary->add(drp);
543 }
544
545 for (; j < comparisonFiles.count(); ++j) {
546 // files only in comparisonDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000547 DiffRecord *drp = new DiffRecord();
548 drp->fBase.fFilename = *comparisonFiles[j];
549 drp->fBase.fFullPath = baseDir;
550 drp->fBase.fFullPath.append(drp->fBase.fFilename);
551 drp->fBase.fStatus = DiffResource::kDoesNotExist_Status;
552
553 drp->fComparison.fFilename = *comparisonFiles[j];
554 drp->fComparison.fFullPath = comparisonDir;
555 drp->fComparison.fFullPath.append(drp->fComparison.fFilename);
556 drp->fComparison.fStatus = DiffResource::kExists_Status;
557
558 drp->fResult = DiffRecord::kCouldNotCompare_Result;
559 if (getBounds) {
560 get_bounds(*drp);
561 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000562 differences->push(drp);
563 summary->add(drp);
564 }
565
566 release_file_list(&baseFiles);
567 release_file_list(&comparisonFiles);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000568}
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000569
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000570static void usage (char * argv0) {
571 SkDebugf("Skia baseline image diff tool\n");
epoger@google.coma5f406e2012-05-01 13:26:16 +0000572 SkDebugf("\n"
573"Usage: \n"
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000574" %s <baseDir> <comparisonDir> [outputDir] \n", argv0);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000575 SkDebugf(
epoger@google.com46a45962012-07-12 18:16:02 +0000576"\nArguments:"
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000577"\n --failonresult <result>: After comparing all file pairs, exit with nonzero"
578"\n return code (number of file pairs yielding this"
579"\n result) if any file pairs yielded this result."
580"\n This flag may be repeated, in which case the"
581"\n return code will be the number of fail pairs"
582"\n yielding ANY of these results."
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000583"\n --failonstatus <baseStatus> <comparisonStatus>: exit with nonzero return"
584"\n code if any file pairs yielded this status."
epoger@google.com46a45962012-07-12 18:16:02 +0000585"\n --help: display this info"
epoger@google.com46a45962012-07-12 18:16:02 +0000586"\n --listfilenames: list all filenames for each result type in stdout"
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000587"\n --match <substring>: compare files whose filenames contain this substring;"
588"\n if unspecified, compare ALL files."
589"\n this flag may be repeated."
epoger@google.com46a45962012-07-12 18:16:02 +0000590"\n --nodiffs: don't write out image diffs or index.html, just generate"
591"\n report on stdout"
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000592"\n --nomatch <substring>: regardless of --match, DO NOT compare files whose"
593"\n filenames contain this substring."
594"\n this flag may be repeated."
epoger@google.com46a45962012-07-12 18:16:02 +0000595"\n --noprintdirs: do not print the directories used."
epoger@google.com71329d82012-08-16 13:42:13 +0000596"\n --norecurse: do not recurse into subdirectories."
epoger@google.com46a45962012-07-12 18:16:02 +0000597"\n --sortbymaxmismatch: sort by worst color channel mismatch;"
598"\n break ties with -sortbymismatch"
599"\n --sortbymismatch: sort by average color channel mismatch"
600"\n --threshold <n>: only report differences > n (per color channel) [default 0]"
601"\n --weighted: sort by # pixels different weighted by color difference"
602"\n"
603"\n baseDir: directory to read baseline images from."
604"\n comparisonDir: directory to read comparison images from"
605"\n outputDir: directory to write difference images and index.html to;"
606"\n defaults to comparisonDir"
607"\n"
608"\nIf no sort is specified, it will sort by fraction of pixels mismatching."
609"\n");
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000610}
611
epoger@google.com70044cc2012-07-12 18:37:55 +0000612const int kNoError = 0;
613const int kGenericError = -1;
epoger@google.com46a45962012-07-12 18:16:02 +0000614
caryclark@google.com5987f582012-10-02 18:33:14 +0000615int tool_main(int argc, char** argv);
616int tool_main(int argc, char** argv) {
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000617 DiffMetricProc diffProc = compute_diff_pmcolor;
epoger@google.com28060e72012-06-28 16:47:34 +0000618 int (*sortProc)(const void*, const void*) = compare<CompareDiffMetrics>;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000619
620 // Maximum error tolerated in any one color channel in any one pixel before
621 // a difference is reported.
622 int colorThreshold = 0;
623 SkString baseDir;
624 SkString comparisonDir;
625 SkString outputDir;
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000626
epoger@google.coma5f406e2012-05-01 13:26:16 +0000627 StringArray matchSubstrings;
628 StringArray nomatchSubstrings;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000629
epoger@google.coma5f406e2012-05-01 13:26:16 +0000630 bool generateDiffs = true;
epoger@google.com46a45962012-07-12 18:16:02 +0000631 bool listFilenames = false;
epoger@google.com71329d82012-08-16 13:42:13 +0000632 bool printDirNames = true;
633 bool recurseIntoSubdirs = true;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000634 bool verbose = false;
reed3a3baf62015-01-06 07:39:55 -0800635 bool listFailingBase = false;
tomhudson@google.com7d042802011-07-14 13:15:55 +0000636
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000637 RecordArray differences;
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000638 DiffSummary summary;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000639
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000640 bool failOnResultType[DiffRecord::kResultCount];
641 for (int i = 0; i < DiffRecord::kResultCount; i++) {
epoger@google.com3af4ff42012-07-19 17:35:04 +0000642 failOnResultType[i] = false;
643 }
644
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000645 bool failOnStatusType[DiffResource::kStatusCount][DiffResource::kStatusCount];
646 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
647 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
648 failOnStatusType[base][comparison] = false;
649 }
650 }
651
epoger@google.coma5f406e2012-05-01 13:26:16 +0000652 int i;
653 int numUnflaggedArguments = 0;
654 for (i = 1; i < argc; i++) {
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000655 if (!strcmp(argv[i], "--failonresult")) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000656 if (argc == ++i) {
657 SkDebugf("failonresult expects one argument.\n");
658 continue;
659 }
660 DiffRecord::Result type = DiffRecord::getResultByName(argv[i]);
661 if (type != DiffRecord::kResultCount) {
662 failOnResultType[type] = true;
663 } else {
664 SkDebugf("ignoring unrecognized result <%s>\n", argv[i]);
665 }
666 continue;
667 }
668 if (!strcmp(argv[i], "--failonstatus")) {
669 if (argc == ++i) {
670 SkDebugf("failonstatus missing base status.\n");
671 continue;
672 }
673 bool baseStatuses[DiffResource::kStatusCount];
674 if (!DiffResource::getMatchingStatuses(argv[i], baseStatuses)) {
675 SkDebugf("unrecognized base status <%s>\n", argv[i]);
676 }
677
678 if (argc == ++i) {
679 SkDebugf("failonstatus missing comparison status.\n");
680 continue;
681 }
682 bool comparisonStatuses[DiffResource::kStatusCount];
683 if (!DiffResource::getMatchingStatuses(argv[i], comparisonStatuses)) {
684 SkDebugf("unrecognized comarison status <%s>\n", argv[i]);
685 }
686
687 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
688 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
689 failOnStatusType[base][comparison] |=
690 baseStatuses[base] && comparisonStatuses[comparison];
691 }
692 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000693 continue;
694 }
epoger@google.com46a45962012-07-12 18:16:02 +0000695 if (!strcmp(argv[i], "--help")) {
696 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000697 return kNoError;
epoger@google.com46a45962012-07-12 18:16:02 +0000698 }
699 if (!strcmp(argv[i], "--listfilenames")) {
700 listFilenames = true;
epoger@google.coma5f406e2012-05-01 13:26:16 +0000701 continue;
702 }
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000703 if (!strcmp(argv[i], "--verbose")) {
704 verbose = true;
705 continue;
706 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000707 if (!strcmp(argv[i], "--match")) {
708 matchSubstrings.push(new SkString(argv[++i]));
709 continue;
710 }
epoger@google.com46a45962012-07-12 18:16:02 +0000711 if (!strcmp(argv[i], "--nodiffs")) {
712 generateDiffs = false;
713 continue;
714 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000715 if (!strcmp(argv[i], "--nomatch")) {
716 nomatchSubstrings.push(new SkString(argv[++i]));
717 continue;
718 }
epoger@google.com46a45962012-07-12 18:16:02 +0000719 if (!strcmp(argv[i], "--noprintdirs")) {
epoger@google.com71329d82012-08-16 13:42:13 +0000720 printDirNames = false;
721 continue;
722 }
723 if (!strcmp(argv[i], "--norecurse")) {
724 recurseIntoSubdirs = false;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000725 continue;
726 }
tomhudson@google.com7d042802011-07-14 13:15:55 +0000727 if (!strcmp(argv[i], "--sortbymaxmismatch")) {
epoger@google.com28060e72012-06-28 16:47:34 +0000728 sortProc = compare<CompareDiffMaxMismatches>;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000729 continue;
730 }
epoger@google.com46a45962012-07-12 18:16:02 +0000731 if (!strcmp(argv[i], "--sortbymismatch")) {
732 sortProc = compare<CompareDiffMeanMismatches>;
tomhudson@google.com5b325292011-05-24 19:41:13 +0000733 continue;
734 }
epoger@google.com46a45962012-07-12 18:16:02 +0000735 if (!strcmp(argv[i], "--threshold")) {
736 colorThreshold = atoi(argv[++i]);
737 continue;
738 }
739 if (!strcmp(argv[i], "--weighted")) {
740 sortProc = compare<CompareDiffWeighted>;
keyar@chromium.orga6318192012-07-09 21:01:50 +0000741 continue;
742 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000743 if (argv[i][0] != '-') {
epoger@google.coma5f406e2012-05-01 13:26:16 +0000744 switch (numUnflaggedArguments++) {
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000745 case 0:
746 baseDir.set(argv[i]);
747 continue;
748 case 1:
749 comparisonDir.set(argv[i]);
750 continue;
751 case 2:
752 outputDir.set(argv[i]);
753 continue;
754 default:
epoger@google.coma5f406e2012-05-01 13:26:16 +0000755 SkDebugf("extra unflagged argument <%s>\n", argv[i]);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000756 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000757 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000758 }
759 }
reed3a3baf62015-01-06 07:39:55 -0800760 if (!strcmp(argv[i], "--listFailingBase")) {
761 listFailingBase = true;
762 continue;
763 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000764
765 SkDebugf("Unrecognized argument <%s>\n", argv[i]);
766 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000767 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000768 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000769
epoger@google.coma5f406e2012-05-01 13:26:16 +0000770 if (numUnflaggedArguments == 2) {
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000771 outputDir = comparisonDir;
epoger@google.coma5f406e2012-05-01 13:26:16 +0000772 } else if (numUnflaggedArguments != 3) {
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000773 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000774 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000775 }
776
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000777 if (!baseDir.endsWith(PATH_DIV_STR)) {
778 baseDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000779 }
epoger@google.com71329d82012-08-16 13:42:13 +0000780 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000781 printf("baseDir is [%s]\n", baseDir.c_str());
782 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000783
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000784 if (!comparisonDir.endsWith(PATH_DIV_STR)) {
785 comparisonDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000786 }
epoger@google.com71329d82012-08-16 13:42:13 +0000787 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000788 printf("comparisonDir is [%s]\n", comparisonDir.c_str());
789 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000790
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000791 if (!outputDir.endsWith(PATH_DIV_STR)) {
792 outputDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000793 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000794 if (generateDiffs) {
epoger@google.com71329d82012-08-16 13:42:13 +0000795 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000796 printf("writing diffs to outputDir is [%s]\n", outputDir.c_str());
797 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000798 } else {
epoger@google.com71329d82012-08-16 13:42:13 +0000799 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000800 printf("not writing any diffs to outputDir [%s]\n", outputDir.c_str());
801 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000802 outputDir.set("");
803 }
804
epoger@google.comda4af242012-06-25 18:45:50 +0000805 // If no matchSubstrings were specified, match ALL strings
806 // (except for whatever nomatchSubstrings were specified, if any).
epoger@google.coma5f406e2012-05-01 13:26:16 +0000807 if (matchSubstrings.isEmpty()) {
808 matchSubstrings.push(new SkString(""));
809 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000810
epoger@google.coma611c3e2012-05-18 20:10:06 +0000811 create_diff_images(diffProc, colorThreshold, &differences,
812 baseDir, comparisonDir, outputDir,
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000813 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, generateDiffs,
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000814 verbose, &summary);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000815 summary.print(listFilenames, failOnResultType, failOnStatusType);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000816
reed3a3baf62015-01-06 07:39:55 -0800817 if (listFailingBase) {
818 summary.printfFailingBaseNames("\n");
819 }
820
tomhudson@google.com7d042802011-07-14 13:15:55 +0000821 if (differences.count()) {
reed@google.comc7a67cb2012-05-07 14:52:12 +0000822 qsort(differences.begin(), differences.count(),
823 sizeof(DiffRecord*), sortProc);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000824 }
epoger@google.com66008522012-05-16 17:40:57 +0000825
epoger@google.coma5f406e2012-05-01 13:26:16 +0000826 if (generateDiffs) {
827 print_diff_page(summary.fNumMatches, colorThreshold, differences,
828 baseDir, comparisonDir, outputDir);
829 }
epoger@google.com76222c02012-05-31 15:12:09 +0000830
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000831 for (i = 0; i < differences.count(); i++) {
832 delete differences[i];
833 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000834 matchSubstrings.deleteAll();
835 nomatchSubstrings.deleteAll();
epoger@google.combe6188d2012-05-31 15:13:45 +0000836
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000837 int num_failing_results = 0;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000838 for (int i = 0; i < DiffRecord::kResultCount; i++) {
epoger@google.com3af4ff42012-07-19 17:35:04 +0000839 if (failOnResultType[i]) {
840 num_failing_results += summary.fResultsOfType[i].count();
841 }
epoger@google.com46a45962012-07-12 18:16:02 +0000842 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000843 if (!failOnResultType[DiffRecord::kCouldNotCompare_Result]) {
844 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
845 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
846 if (failOnStatusType[base][comparison]) {
847 num_failing_results += summary.fStatusOfType[base][comparison].count();
848 }
849 }
850 }
851 }
epoger@google.com28659882012-07-16 18:01:06 +0000852
853 // On Linux (and maybe other platforms too), any results outside of the
854 // range [0...255] are wrapped (mod 256). Do the conversion ourselves, to
855 // make sure that we only return 0 when there were no failures.
856 return (num_failing_results > 255) ? 255 : num_failing_results;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000857}
caryclark@google.com5987f582012-10-02 18:33:14 +0000858
859#if !defined SK_BUILD_FOR_IOS
860int main(int argc, char * const argv[]) {
861 return tool_main(argc, (char**) argv);
862}
863#endif