blob: bb611e96fc4baa3bcfe1173141427a2077f245c5 [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"
15#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
zachr@google.com6ef5a852013-06-18 21:23:31 +000021__SK_FORCE_IMAGE_DECODER_LINKING;
22
tomhudson@google.com4b33d282011-04-27 15:39:30 +000023/**
24 * skdiff
25 *
26 * Given three directory names, expects to find identically-named files in
27 * each of the first two; the first are treated as a set of baseline,
28 * the second a set of variant images, and a diff image is written into the
29 * third directory for each pair.
tomhudson@google.com7d042802011-07-14 13:15:55 +000030 * Creates an index.html in the current third directory to compare each
tomhudson@google.com4b33d282011-04-27 15:39:30 +000031 * pair that does not match exactly.
epoger@google.com71329d82012-08-16 13:42:13 +000032 * Recursively descends directories, unless run with --norecurse.
epoger@google.combe6188d2012-05-31 15:13:45 +000033 *
34 * Returns zero exit code if all images match across baseDir and comparisonDir.
tomhudson@google.com4b33d282011-04-27 15:39:30 +000035 */
36
epoger@google.coma5f406e2012-05-01 13:26:16 +000037typedef SkTDArray<SkString*> StringArray;
38typedef StringArray FileArray;
epoger@google.com5fd53852012-03-22 18:20:06 +000039
reed3a3baf62015-01-06 07:39:55 -080040static void add_unique_basename(StringArray* array, const SkString& filename) {
41 // trim off dirs
42 const char* src = filename.c_str();
43 const char* trimmed = strrchr(src, SkPATH_SEPARATOR);
44 if (trimmed) {
45 trimmed += 1; // skip the separator
46 } else {
47 trimmed = src;
48 }
49 const char* end = strrchr(trimmed, '.');
50 if (!end) {
51 end = trimmed + strlen(trimmed);
52 }
53 SkString result(trimmed, end - trimmed);
54
55 // only add unique entries
56 for (int i = 0; i < array->count(); ++i) {
57 if (*array->getAt(i) == result) {
58 return;
59 }
60 }
61 *array->append() = new SkString(result);
62}
63
tomhudson@google.com9dc527b2011-06-09 15:47:10 +000064struct DiffSummary {
65 DiffSummary ()
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000066 : fNumMatches(0)
67 , fNumMismatches(0)
68 , fMaxMismatchV(0)
69 , fMaxMismatchPercent(0) { };
tomhudson@google.com9dc527b2011-06-09 15:47:10 +000070
epoger@google.com5fd53852012-03-22 18:20:06 +000071 ~DiffSummary() {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000072 for (int i = 0; i < DiffRecord::kResultCount; ++i) {
epoger@google.com76222c02012-05-31 15:12:09 +000073 fResultsOfType[i].deleteAll();
74 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000075 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
76 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
77 fStatusOfType[base][comparison].deleteAll();
78 }
skia.committer@gmail.com0264fb42012-12-06 02:01:25 +000079 }
epoger@google.com5fd53852012-03-22 18:20:06 +000080 }
81
tomhudson@google.com9dc527b2011-06-09 15:47:10 +000082 uint32_t fNumMatches;
83 uint32_t fNumMismatches;
84 uint32_t fMaxMismatchV;
85 float fMaxMismatchPercent;
86
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000087 FileArray fResultsOfType[DiffRecord::kResultCount];
88 FileArray fStatusOfType[DiffResource::kStatusCount][DiffResource::kStatusCount];
89
reed3a3baf62015-01-06 07:39:55 -080090 StringArray fFailedBaseNames[DiffRecord::kResultCount];
91
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000092 void printContents(const FileArray& fileArray,
93 const char* baseStatus, const char* comparisonStatus,
94 bool listFilenames) {
95 int n = fileArray.count();
96 printf("%d file pairs %s in baseDir and %s in comparisonDir",
97 n, baseStatus, comparisonStatus);
98 if (listFilenames) {
99 printf(": ");
100 for (int i = 0; i < n; ++i) {
101 printf("%s ", fileArray[i]->c_str());
102 }
103 }
104 printf("\n");
105 }
106
107 void printStatus(bool listFilenames,
108 bool failOnStatusType[DiffResource::kStatusCount]
109 [DiffResource::kStatusCount]) {
110 typedef DiffResource::Status Status;
111
112 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
113 Status baseStatus = static_cast<Status>(base);
114 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
115 Status comparisonStatus = static_cast<Status>(comparison);
116 const FileArray& fileArray = fStatusOfType[base][comparison];
117 if (fileArray.count() > 0) {
118 if (failOnStatusType[base][comparison]) {
119 printf(" [*] ");
120 } else {
121 printf(" [_] ");
122 }
123 printContents(fileArray,
124 DiffResource::getStatusDescription(baseStatus),
125 DiffResource::getStatusDescription(comparisonStatus),
126 listFilenames);
127 }
128 }
129 }
130 }
epoger@google.com76222c02012-05-31 15:12:09 +0000131
epoger@google.com3af4ff42012-07-19 17:35:04 +0000132 // Print a line about the contents of this FileArray to stdout.
epoger@google.com46a45962012-07-12 18:16:02 +0000133 void printContents(const FileArray& fileArray, const char* headerText, bool listFilenames) {
epoger@google.com76222c02012-05-31 15:12:09 +0000134 int n = fileArray.count();
epoger@google.com3af4ff42012-07-19 17:35:04 +0000135 printf("%d file pairs %s", n, headerText);
136 if (listFilenames) {
137 printf(": ");
138 for (int i = 0; i < n; ++i) {
139 printf("%s ", fileArray[i]->c_str());
epoger@google.com76222c02012-05-31 15:12:09 +0000140 }
141 }
epoger@google.com3af4ff42012-07-19 17:35:04 +0000142 printf("\n");
epoger@google.com76222c02012-05-31 15:12:09 +0000143 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000144
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000145 void print(bool listFilenames, bool failOnResultType[DiffRecord::kResultCount],
146 bool failOnStatusType[DiffResource::kStatusCount]
147 [DiffResource::kStatusCount]) {
epoger@google.com3af4ff42012-07-19 17:35:04 +0000148 printf("\ncompared %d file pairs:\n", fNumMatches + fNumMismatches);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000149 for (int resultInt = 0; resultInt < DiffRecord::kResultCount; ++resultInt) {
150 DiffRecord::Result result = static_cast<DiffRecord::Result>(resultInt);
epoger@google.com3af4ff42012-07-19 17:35:04 +0000151 if (failOnResultType[result]) {
152 printf("[*] ");
153 } else {
154 printf("[_] ");
155 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000156 printContents(fResultsOfType[result], DiffRecord::getResultDescription(result),
157 listFilenames);
158 if (DiffRecord::kCouldNotCompare_Result == result) {
159 printStatus(listFilenames, failOnStatusType);
160 }
epoger@google.com46a45962012-07-12 18:16:02 +0000161 }
epoger@google.com3af4ff42012-07-19 17:35:04 +0000162 printf("(results marked with [*] will cause nonzero return value)\n");
163 printf("\nnumber of mismatching file pairs: %d\n", fNumMismatches);
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000164 if (fNumMismatches > 0) {
165 printf("Maximum pixel intensity mismatch %d\n", fMaxMismatchV);
epoger@google.com46a45962012-07-12 18:16:02 +0000166 printf("Largest area mismatch was %.2f%% of pixels\n",fMaxMismatchPercent);
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000167 }
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000168 }
169
reed3a3baf62015-01-06 07:39:55 -0800170 void printfFailingBaseNames(const char separator[]) {
171 for (int resultInt = 0; resultInt < DiffRecord::kResultCount; ++resultInt) {
172 const StringArray& array = fFailedBaseNames[resultInt];
173 if (array.count()) {
174 printf("%s [%d]%s", DiffRecord::ResultNames[resultInt], array.count(), separator);
175 for (int j = 0; j < array.count(); ++j) {
176 printf("%s%s", array[j]->c_str(), separator);
177 }
178 printf("\n");
179 }
180 }
181 }
182
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000183 void add (DiffRecord* drp) {
epoger@google.com46256ea2012-05-22 13:45:35 +0000184 uint32_t mismatchValue;
epoger@google.com292aff62012-05-16 14:57:28 +0000185
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000186 if (drp->fBase.fFilename.equals(drp->fComparison.fFilename)) {
187 fResultsOfType[drp->fResult].push(new SkString(drp->fBase.fFilename));
188 } else {
189 SkString* blame = new SkString("(");
190 blame->append(drp->fBase.fFilename);
191 blame->append(", ");
192 blame->append(drp->fComparison.fFilename);
193 blame->append(")");
194 fResultsOfType[drp->fResult].push(blame);
195 }
epoger@google.com292aff62012-05-16 14:57:28 +0000196 switch (drp->fResult) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000197 case DiffRecord::kEqualBits_Result:
epoger@google.com46256ea2012-05-22 13:45:35 +0000198 fNumMatches++;
199 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000200 case DiffRecord::kEqualPixels_Result:
epoger@google.com292aff62012-05-16 14:57:28 +0000201 fNumMatches++;
202 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000203 case DiffRecord::kDifferentSizes_Result:
epoger@google.com5fd53852012-03-22 18:20:06 +0000204 fNumMismatches++;
epoger@google.com292aff62012-05-16 14:57:28 +0000205 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000206 case DiffRecord::kDifferentPixels_Result:
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000207 fNumMismatches++;
208 if (drp->fFractionDifference * 100 > fMaxMismatchPercent) {
209 fMaxMismatchPercent = drp->fFractionDifference * 100;
210 }
epoger@google.com46256ea2012-05-22 13:45:35 +0000211 mismatchValue = MAX3(drp->fMaxMismatchR, drp->fMaxMismatchG,
212 drp->fMaxMismatchB);
213 if (mismatchValue > fMaxMismatchV) {
214 fMaxMismatchV = mismatchValue;
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000215 }
epoger@google.com292aff62012-05-16 14:57:28 +0000216 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000217 case DiffRecord::kCouldNotCompare_Result:
epoger@google.com46256ea2012-05-22 13:45:35 +0000218 fNumMismatches++;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000219 fStatusOfType[drp->fBase.fStatus][drp->fComparison.fStatus].push(
220 new SkString(drp->fBase.fFilename));
epoger@google.com46256ea2012-05-22 13:45:35 +0000221 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000222 case DiffRecord::kUnknown_Result:
epoger@google.com46256ea2012-05-22 13:45:35 +0000223 SkDEBUGFAIL("adding uncategorized DiffRecord");
224 break;
225 default:
226 SkDEBUGFAIL("adding DiffRecord with unhandled fResult value");
227 break;
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000228 }
reed3a3baf62015-01-06 07:39:55 -0800229
230 switch (drp->fResult) {
231 case DiffRecord::kEqualBits_Result:
232 case DiffRecord::kEqualPixels_Result:
233 break;
234 default:
235 add_unique_basename(&fFailedBaseNames[drp->fResult], drp->fBase.fFilename);
236 break;
237 }
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000238 }
239};
240
epoger@google.coma5f406e2012-05-01 13:26:16 +0000241/// Returns true if string contains any of these substrings.
242static bool string_contains_any_of(const SkString& string,
243 const StringArray& substrings) {
244 for (int i = 0; i < substrings.count(); i++) {
245 if (string.contains(substrings[i]->c_str())) {
246 return true;
247 }
248 }
249 return false;
250}
251
epoger@google.com71329d82012-08-16 13:42:13 +0000252/// Internal (potentially recursive) implementation of get_file_list.
253static void get_file_list_subdir(const SkString& rootDir, const SkString& subDir,
254 const StringArray& matchSubstrings,
255 const StringArray& nomatchSubstrings,
256 bool recurseIntoSubdirs, FileArray *files) {
257 bool isSubDirEmpty = subDir.isEmpty();
258 SkString dir(rootDir);
259 if (!isSubDirEmpty) {
260 dir.append(PATH_DIV_STR);
261 dir.append(subDir);
262 }
263
264 // Iterate over files (not directories) within dir.
265 SkOSFile::Iter fileIterator(dir.c_str());
266 SkString fileName;
267 while (fileIterator.next(&fileName, false)) {
268 if (fileName.startsWith(".")) {
269 continue;
270 }
271 SkString pathRelativeToRootDir(subDir);
272 if (!isSubDirEmpty) {
273 pathRelativeToRootDir.append(PATH_DIV_STR);
274 }
275 pathRelativeToRootDir.append(fileName);
276 if (string_contains_any_of(pathRelativeToRootDir, matchSubstrings) &&
277 !string_contains_any_of(pathRelativeToRootDir, nomatchSubstrings)) {
278 files->push(new SkString(pathRelativeToRootDir));
279 }
280 }
281
282 // Recurse into any non-ignored subdirectories.
283 if (recurseIntoSubdirs) {
284 SkOSFile::Iter dirIterator(dir.c_str());
285 SkString dirName;
286 while (dirIterator.next(&dirName, true)) {
287 if (dirName.startsWith(".")) {
288 continue;
289 }
290 SkString pathRelativeToRootDir(subDir);
291 if (!isSubDirEmpty) {
292 pathRelativeToRootDir.append(PATH_DIV_STR);
293 }
294 pathRelativeToRootDir.append(dirName);
295 if (!string_contains_any_of(pathRelativeToRootDir, nomatchSubstrings)) {
296 get_file_list_subdir(rootDir, pathRelativeToRootDir,
297 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
298 files);
299 }
300 }
301 }
302}
303
304/// Iterate over dir and get all files whose filename:
305/// - matches any of the substrings in matchSubstrings, but...
306/// - DOES NOT match any of the substrings in nomatchSubstrings
307/// - DOES NOT start with a dot (.)
308/// Adds the matching files to the list in *files.
epoger@google.coma5f406e2012-05-01 13:26:16 +0000309static void get_file_list(const SkString& dir,
310 const StringArray& matchSubstrings,
311 const StringArray& nomatchSubstrings,
epoger@google.com71329d82012-08-16 13:42:13 +0000312 bool recurseIntoSubdirs, FileArray *files) {
313 get_file_list_subdir(dir, SkString(""),
314 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
315 files);
epoger@google.com5fd53852012-03-22 18:20:06 +0000316}
317
318static void release_file_list(FileArray *files) {
319 files->deleteAll();
320}
321
322/// Comparison routines for qsort, sort by file names.
323static int compare_file_name_metrics(SkString **lhs, SkString **rhs) {
324 return strcmp((*lhs)->c_str(), (*rhs)->c_str());
325}
326
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000327class AutoReleasePixels {
328public:
329 AutoReleasePixels(DiffRecord* drp)
330 : fDrp(drp) {
halcanary96fcdcc2015-08-27 07:41:13 -0700331 SkASSERT(drp != nullptr);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000332 }
333 ~AutoReleasePixels() {
halcanary96fcdcc2015-08-27 07:41:13 -0700334 fDrp->fBase.fBitmap.setPixelRef(nullptr);
335 fDrp->fComparison.fBitmap.setPixelRef(nullptr);
336 fDrp->fDifference.fBitmap.setPixelRef(nullptr);
337 fDrp->fWhite.fBitmap.setPixelRef(nullptr);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000338 }
339
340private:
341 DiffRecord* fDrp;
342};
343
344static void get_bounds(DiffResource& resource, const char* name) {
345 if (resource.fBitmap.empty() && !DiffResource::isStatusFailed(resource.fStatus)) {
346 SkAutoDataUnref fileBits(read_file(resource.fFullPath.c_str()));
halcanary96fcdcc2015-08-27 07:41:13 -0700347 if (nullptr == fileBits) {
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;
350 } else {
msarett667433f2016-03-17 07:17:54 -0700351 get_bitmap(fileBits, resource, true);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000352 }
353 }
354}
355
356static void get_bounds(DiffRecord& drp) {
357 get_bounds(drp.fBase, "base");
358 get_bounds(drp.fComparison, "comparison");
359}
360
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000361#ifdef SK_OS_WIN
362#define ANSI_COLOR_RED ""
363#define ANSI_COLOR_GREEN ""
364#define ANSI_COLOR_YELLOW ""
365#define ANSI_COLOR_RESET ""
366#else
367#define ANSI_COLOR_RED "\x1b[31m"
368#define ANSI_COLOR_GREEN "\x1b[32m"
369#define ANSI_COLOR_YELLOW "\x1b[33m"
370#define ANSI_COLOR_RESET "\x1b[0m"
371#endif
372
373#define VERBOSE_STATUS(status,color,filename) if (verbose) printf( "[ " color " %10s " ANSI_COLOR_RESET " ] %s\n", status, filename->c_str())
374
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000375/// Creates difference images, returns the number that have a 0 metric.
epoger@google.coma5f406e2012-05-01 13:26:16 +0000376/// If outputDir.isEmpty(), don't write out diff files.
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000377static void create_diff_images (DiffMetricProc dmp,
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000378 const int colorThreshold,
379 RecordArray* differences,
380 const SkString& baseDir,
381 const SkString& comparisonDir,
382 const SkString& outputDir,
epoger@google.coma5f406e2012-05-01 13:26:16 +0000383 const StringArray& matchSubstrings,
384 const StringArray& nomatchSubstrings,
epoger@google.com71329d82012-08-16 13:42:13 +0000385 bool recurseIntoSubdirs,
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000386 bool getBounds,
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000387 bool verbose,
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000388 DiffSummary* summary) {
epoger@google.coma5f406e2012-05-01 13:26:16 +0000389 SkASSERT(!baseDir.isEmpty());
390 SkASSERT(!comparisonDir.isEmpty());
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000391
epoger@google.com5fd53852012-03-22 18:20:06 +0000392 FileArray baseFiles;
393 FileArray comparisonFiles;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000394
epoger@google.com71329d82012-08-16 13:42:13 +0000395 get_file_list(baseDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, &baseFiles);
396 get_file_list(comparisonDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
epoger@google.coma5f406e2012-05-01 13:26:16 +0000397 &comparisonFiles);
epoger@google.com5fd53852012-03-22 18:20:06 +0000398
epoger@google.coma5f406e2012-05-01 13:26:16 +0000399 if (!baseFiles.isEmpty()) {
reed@google.comc7a67cb2012-05-07 14:52:12 +0000400 qsort(baseFiles.begin(), baseFiles.count(), sizeof(SkString*),
401 SkCastForQSort(compare_file_name_metrics));
epoger@google.coma5f406e2012-05-01 13:26:16 +0000402 }
403 if (!comparisonFiles.isEmpty()) {
reed@google.comc7a67cb2012-05-07 14:52:12 +0000404 qsort(comparisonFiles.begin(), comparisonFiles.count(),
405 sizeof(SkString*), SkCastForQSort(compare_file_name_metrics));
epoger@google.coma5f406e2012-05-01 13:26:16 +0000406 }
epoger@google.com66008522012-05-16 17:40:57 +0000407
brianosman235cbf22016-04-05 11:37:49 -0700408 if (!outputDir.isEmpty()) {
409 sk_mkdir(outputDir.c_str());
410 }
411
epoger@google.com5fd53852012-03-22 18:20:06 +0000412 int i = 0;
413 int j = 0;
414
415 while (i < baseFiles.count() &&
416 j < comparisonFiles.count()) {
417
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000418 SkString basePath(baseDir);
419 SkString comparisonPath(comparisonDir);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000420
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000421 DiffRecord *drp = new DiffRecord;
422 int v = strcmp(baseFiles[i]->c_str(), comparisonFiles[j]->c_str());
epoger@google.com5fd53852012-03-22 18:20:06 +0000423
424 if (v < 0) {
425 // in baseDir, but not in comparisonDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000426 drp->fResult = DiffRecord::kCouldNotCompare_Result;
427
428 basePath.append(*baseFiles[i]);
429 comparisonPath.append(*baseFiles[i]);
430
431 drp->fBase.fFilename = *baseFiles[i];
432 drp->fBase.fFullPath = basePath;
433 drp->fBase.fStatus = DiffResource::kExists_Status;
434
435 drp->fComparison.fFilename = *baseFiles[i];
436 drp->fComparison.fFullPath = comparisonPath;
437 drp->fComparison.fStatus = DiffResource::kDoesNotExist_Status;
438
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000439 VERBOSE_STATUS("MISSING", ANSI_COLOR_YELLOW, baseFiles[i]);
440
epoger@google.com5fd53852012-03-22 18:20:06 +0000441 ++i;
442 } else if (v > 0) {
443 // in comparisonDir, but not in baseDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000444 drp->fResult = DiffRecord::kCouldNotCompare_Result;
445
446 basePath.append(*comparisonFiles[j]);
447 comparisonPath.append(*comparisonFiles[j]);
448
449 drp->fBase.fFilename = *comparisonFiles[j];
450 drp->fBase.fFullPath = basePath;
451 drp->fBase.fStatus = DiffResource::kDoesNotExist_Status;
452
453 drp->fComparison.fFilename = *comparisonFiles[j];
454 drp->fComparison.fFullPath = comparisonPath;
455 drp->fComparison.fStatus = DiffResource::kExists_Status;
456
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000457 VERBOSE_STATUS("MISSING", ANSI_COLOR_YELLOW, comparisonFiles[j]);
458
epoger@google.com5fd53852012-03-22 18:20:06 +0000459 ++j;
460 } else {
epoger@google.com46256ea2012-05-22 13:45:35 +0000461 // Found the same filename in both baseDir and comparisonDir.
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000462 SkASSERT(DiffRecord::kUnknown_Result == drp->fResult);
epoger@google.com5fd53852012-03-22 18:20:06 +0000463
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000464 basePath.append(*baseFiles[i]);
465 comparisonPath.append(*comparisonFiles[j]);
466
467 drp->fBase.fFilename = *baseFiles[i];
468 drp->fBase.fFullPath = basePath;
469 drp->fBase.fStatus = DiffResource::kExists_Status;
470
471 drp->fComparison.fFilename = *comparisonFiles[j];
472 drp->fComparison.fFullPath = comparisonPath;
473 drp->fComparison.fStatus = DiffResource::kExists_Status;
474
475 SkAutoDataUnref baseFileBits(read_file(drp->fBase.fFullPath.c_str()));
bsalomon49f085d2014-09-05 13:34:00 -0700476 if (baseFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000477 drp->fBase.fStatus = DiffResource::kRead_Status;
478 }
479 SkAutoDataUnref comparisonFileBits(read_file(drp->fComparison.fFullPath.c_str()));
bsalomon49f085d2014-09-05 13:34:00 -0700480 if (comparisonFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000481 drp->fComparison.fStatus = DiffResource::kRead_Status;
482 }
halcanary96fcdcc2015-08-27 07:41:13 -0700483 if (nullptr == baseFileBits || nullptr == comparisonFileBits) {
484 if (nullptr == baseFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000485 drp->fBase.fStatus = DiffResource::kCouldNotRead_Status;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000486 VERBOSE_STATUS("READ FAIL", ANSI_COLOR_RED, baseFiles[i]);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000487 }
halcanary96fcdcc2015-08-27 07:41:13 -0700488 if (nullptr == comparisonFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000489 drp->fComparison.fStatus = DiffResource::kCouldNotRead_Status;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000490 VERBOSE_STATUS("READ FAIL", ANSI_COLOR_RED, comparisonFiles[j]);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000491 }
492 drp->fResult = DiffRecord::kCouldNotCompare_Result;
493
494 } else if (are_buffers_equal(baseFileBits, comparisonFileBits)) {
495 drp->fResult = DiffRecord::kEqualBits_Result;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000496 VERBOSE_STATUS("MATCH", ANSI_COLOR_GREEN, baseFiles[i]);
epoger@google.com46256ea2012-05-22 13:45:35 +0000497 } else {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000498 AutoReleasePixels arp(drp);
msarett667433f2016-03-17 07:17:54 -0700499 get_bitmap(baseFileBits, drp->fBase, false);
500 get_bitmap(comparisonFileBits, drp->fComparison, false);
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000501 VERBOSE_STATUS("DIFFERENT", ANSI_COLOR_RED, baseFiles[i]);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000502 if (DiffResource::kDecoded_Status == drp->fBase.fStatus &&
503 DiffResource::kDecoded_Status == drp->fComparison.fStatus) {
504 create_and_write_diff_image(drp, dmp, colorThreshold,
505 outputDir, drp->fBase.fFilename);
epoger@google.com46256ea2012-05-22 13:45:35 +0000506 } else {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000507 drp->fResult = DiffRecord::kCouldNotCompare_Result;
epoger@google.com46256ea2012-05-22 13:45:35 +0000508 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000509 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000510
epoger@google.com5fd53852012-03-22 18:20:06 +0000511 ++i;
512 ++j;
513 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000514
515 if (getBounds) {
516 get_bounds(*drp);
517 }
518 SkASSERT(DiffRecord::kUnknown_Result != drp->fResult);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000519 differences->push(drp);
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000520 summary->add(drp);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000521 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000522
523 for (; i < baseFiles.count(); ++i) {
524 // files only in baseDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000525 DiffRecord *drp = new DiffRecord();
526 drp->fBase.fFilename = *baseFiles[i];
527 drp->fBase.fFullPath = baseDir;
528 drp->fBase.fFullPath.append(drp->fBase.fFilename);
529 drp->fBase.fStatus = DiffResource::kExists_Status;
530
531 drp->fComparison.fFilename = *baseFiles[i];
532 drp->fComparison.fFullPath = comparisonDir;
533 drp->fComparison.fFullPath.append(drp->fComparison.fFilename);
534 drp->fComparison.fStatus = DiffResource::kDoesNotExist_Status;
535
536 drp->fResult = DiffRecord::kCouldNotCompare_Result;
537 if (getBounds) {
538 get_bounds(*drp);
539 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000540 differences->push(drp);
541 summary->add(drp);
542 }
543
544 for (; j < comparisonFiles.count(); ++j) {
545 // files only in comparisonDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000546 DiffRecord *drp = new DiffRecord();
547 drp->fBase.fFilename = *comparisonFiles[j];
548 drp->fBase.fFullPath = baseDir;
549 drp->fBase.fFullPath.append(drp->fBase.fFilename);
550 drp->fBase.fStatus = DiffResource::kDoesNotExist_Status;
551
552 drp->fComparison.fFilename = *comparisonFiles[j];
553 drp->fComparison.fFullPath = comparisonDir;
554 drp->fComparison.fFullPath.append(drp->fComparison.fFilename);
555 drp->fComparison.fStatus = DiffResource::kExists_Status;
556
557 drp->fResult = DiffRecord::kCouldNotCompare_Result;
558 if (getBounds) {
559 get_bounds(*drp);
560 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000561 differences->push(drp);
562 summary->add(drp);
563 }
564
565 release_file_list(&baseFiles);
566 release_file_list(&comparisonFiles);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000567}
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000568
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000569static void usage (char * argv0) {
570 SkDebugf("Skia baseline image diff tool\n");
epoger@google.coma5f406e2012-05-01 13:26:16 +0000571 SkDebugf("\n"
572"Usage: \n"
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000573" %s <baseDir> <comparisonDir> [outputDir] \n", argv0);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000574 SkDebugf(
epoger@google.com46a45962012-07-12 18:16:02 +0000575"\nArguments:"
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000576"\n --failonresult <result>: After comparing all file pairs, exit with nonzero"
577"\n return code (number of file pairs yielding this"
578"\n result) if any file pairs yielded this result."
579"\n This flag may be repeated, in which case the"
580"\n return code will be the number of fail pairs"
581"\n yielding ANY of these results."
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000582"\n --failonstatus <baseStatus> <comparisonStatus>: exit with nonzero return"
583"\n code if any file pairs yielded this status."
epoger@google.com46a45962012-07-12 18:16:02 +0000584"\n --help: display this info"
epoger@google.com46a45962012-07-12 18:16:02 +0000585"\n --listfilenames: list all filenames for each result type in stdout"
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000586"\n --match <substring>: compare files whose filenames contain this substring;"
587"\n if unspecified, compare ALL files."
588"\n this flag may be repeated."
epoger@google.com46a45962012-07-12 18:16:02 +0000589"\n --nodiffs: don't write out image diffs or index.html, just generate"
590"\n report on stdout"
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000591"\n --nomatch <substring>: regardless of --match, DO NOT compare files whose"
592"\n filenames contain this substring."
593"\n this flag may be repeated."
epoger@google.com46a45962012-07-12 18:16:02 +0000594"\n --noprintdirs: do not print the directories used."
epoger@google.com71329d82012-08-16 13:42:13 +0000595"\n --norecurse: do not recurse into subdirectories."
epoger@google.com46a45962012-07-12 18:16:02 +0000596"\n --sortbymaxmismatch: sort by worst color channel mismatch;"
597"\n break ties with -sortbymismatch"
598"\n --sortbymismatch: sort by average color channel mismatch"
599"\n --threshold <n>: only report differences > n (per color channel) [default 0]"
600"\n --weighted: sort by # pixels different weighted by color difference"
601"\n"
602"\n baseDir: directory to read baseline images from."
603"\n comparisonDir: directory to read comparison images from"
604"\n outputDir: directory to write difference images and index.html to;"
605"\n defaults to comparisonDir"
606"\n"
607"\nIf no sort is specified, it will sort by fraction of pixels mismatching."
608"\n");
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000609}
610
epoger@google.com70044cc2012-07-12 18:37:55 +0000611const int kNoError = 0;
612const int kGenericError = -1;
epoger@google.com46a45962012-07-12 18:16:02 +0000613
caryclark@google.com5987f582012-10-02 18:33:14 +0000614int tool_main(int argc, char** argv);
615int tool_main(int argc, char** argv) {
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000616 DiffMetricProc diffProc = compute_diff_pmcolor;
epoger@google.com28060e72012-06-28 16:47:34 +0000617 int (*sortProc)(const void*, const void*) = compare<CompareDiffMetrics>;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000618
619 // Maximum error tolerated in any one color channel in any one pixel before
620 // a difference is reported.
621 int colorThreshold = 0;
622 SkString baseDir;
623 SkString comparisonDir;
624 SkString outputDir;
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000625
epoger@google.coma5f406e2012-05-01 13:26:16 +0000626 StringArray matchSubstrings;
627 StringArray nomatchSubstrings;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000628
epoger@google.coma5f406e2012-05-01 13:26:16 +0000629 bool generateDiffs = true;
epoger@google.com46a45962012-07-12 18:16:02 +0000630 bool listFilenames = false;
epoger@google.com71329d82012-08-16 13:42:13 +0000631 bool printDirNames = true;
632 bool recurseIntoSubdirs = true;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000633 bool verbose = false;
reed3a3baf62015-01-06 07:39:55 -0800634 bool listFailingBase = false;
tomhudson@google.com7d042802011-07-14 13:15:55 +0000635
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000636 RecordArray differences;
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000637 DiffSummary summary;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000638
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000639 bool failOnResultType[DiffRecord::kResultCount];
640 for (int i = 0; i < DiffRecord::kResultCount; i++) {
epoger@google.com3af4ff42012-07-19 17:35:04 +0000641 failOnResultType[i] = false;
642 }
643
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000644 bool failOnStatusType[DiffResource::kStatusCount][DiffResource::kStatusCount];
645 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
646 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
647 failOnStatusType[base][comparison] = false;
648 }
649 }
650
epoger@google.coma5f406e2012-05-01 13:26:16 +0000651 int i;
652 int numUnflaggedArguments = 0;
653 for (i = 1; i < argc; i++) {
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000654 if (!strcmp(argv[i], "--failonresult")) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000655 if (argc == ++i) {
656 SkDebugf("failonresult expects one argument.\n");
657 continue;
658 }
659 DiffRecord::Result type = DiffRecord::getResultByName(argv[i]);
660 if (type != DiffRecord::kResultCount) {
661 failOnResultType[type] = true;
662 } else {
663 SkDebugf("ignoring unrecognized result <%s>\n", argv[i]);
664 }
665 continue;
666 }
667 if (!strcmp(argv[i], "--failonstatus")) {
668 if (argc == ++i) {
669 SkDebugf("failonstatus missing base status.\n");
670 continue;
671 }
672 bool baseStatuses[DiffResource::kStatusCount];
673 if (!DiffResource::getMatchingStatuses(argv[i], baseStatuses)) {
674 SkDebugf("unrecognized base status <%s>\n", argv[i]);
675 }
676
677 if (argc == ++i) {
678 SkDebugf("failonstatus missing comparison status.\n");
679 continue;
680 }
681 bool comparisonStatuses[DiffResource::kStatusCount];
682 if (!DiffResource::getMatchingStatuses(argv[i], comparisonStatuses)) {
683 SkDebugf("unrecognized comarison status <%s>\n", argv[i]);
684 }
685
686 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
687 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
688 failOnStatusType[base][comparison] |=
689 baseStatuses[base] && comparisonStatuses[comparison];
690 }
691 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000692 continue;
693 }
epoger@google.com46a45962012-07-12 18:16:02 +0000694 if (!strcmp(argv[i], "--help")) {
695 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000696 return kNoError;
epoger@google.com46a45962012-07-12 18:16:02 +0000697 }
698 if (!strcmp(argv[i], "--listfilenames")) {
699 listFilenames = true;
epoger@google.coma5f406e2012-05-01 13:26:16 +0000700 continue;
701 }
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000702 if (!strcmp(argv[i], "--verbose")) {
703 verbose = true;
704 continue;
705 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000706 if (!strcmp(argv[i], "--match")) {
707 matchSubstrings.push(new SkString(argv[++i]));
708 continue;
709 }
epoger@google.com46a45962012-07-12 18:16:02 +0000710 if (!strcmp(argv[i], "--nodiffs")) {
711 generateDiffs = false;
712 continue;
713 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000714 if (!strcmp(argv[i], "--nomatch")) {
715 nomatchSubstrings.push(new SkString(argv[++i]));
716 continue;
717 }
epoger@google.com46a45962012-07-12 18:16:02 +0000718 if (!strcmp(argv[i], "--noprintdirs")) {
epoger@google.com71329d82012-08-16 13:42:13 +0000719 printDirNames = false;
720 continue;
721 }
722 if (!strcmp(argv[i], "--norecurse")) {
723 recurseIntoSubdirs = false;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000724 continue;
725 }
tomhudson@google.com7d042802011-07-14 13:15:55 +0000726 if (!strcmp(argv[i], "--sortbymaxmismatch")) {
epoger@google.com28060e72012-06-28 16:47:34 +0000727 sortProc = compare<CompareDiffMaxMismatches>;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000728 continue;
729 }
epoger@google.com46a45962012-07-12 18:16:02 +0000730 if (!strcmp(argv[i], "--sortbymismatch")) {
731 sortProc = compare<CompareDiffMeanMismatches>;
tomhudson@google.com5b325292011-05-24 19:41:13 +0000732 continue;
733 }
epoger@google.com46a45962012-07-12 18:16:02 +0000734 if (!strcmp(argv[i], "--threshold")) {
735 colorThreshold = atoi(argv[++i]);
736 continue;
737 }
738 if (!strcmp(argv[i], "--weighted")) {
739 sortProc = compare<CompareDiffWeighted>;
keyar@chromium.orga6318192012-07-09 21:01:50 +0000740 continue;
741 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000742 if (argv[i][0] != '-') {
epoger@google.coma5f406e2012-05-01 13:26:16 +0000743 switch (numUnflaggedArguments++) {
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000744 case 0:
745 baseDir.set(argv[i]);
746 continue;
747 case 1:
748 comparisonDir.set(argv[i]);
749 continue;
750 case 2:
751 outputDir.set(argv[i]);
752 continue;
753 default:
epoger@google.coma5f406e2012-05-01 13:26:16 +0000754 SkDebugf("extra unflagged argument <%s>\n", argv[i]);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000755 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000756 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000757 }
758 }
reed3a3baf62015-01-06 07:39:55 -0800759 if (!strcmp(argv[i], "--listFailingBase")) {
760 listFailingBase = true;
761 continue;
762 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000763
764 SkDebugf("Unrecognized argument <%s>\n", argv[i]);
765 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000766 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000767 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000768
epoger@google.coma5f406e2012-05-01 13:26:16 +0000769 if (numUnflaggedArguments == 2) {
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000770 outputDir = comparisonDir;
epoger@google.coma5f406e2012-05-01 13:26:16 +0000771 } else if (numUnflaggedArguments != 3) {
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000772 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000773 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000774 }
775
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000776 if (!baseDir.endsWith(PATH_DIV_STR)) {
777 baseDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000778 }
epoger@google.com71329d82012-08-16 13:42:13 +0000779 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000780 printf("baseDir is [%s]\n", baseDir.c_str());
781 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000782
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000783 if (!comparisonDir.endsWith(PATH_DIV_STR)) {
784 comparisonDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000785 }
epoger@google.com71329d82012-08-16 13:42:13 +0000786 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000787 printf("comparisonDir is [%s]\n", comparisonDir.c_str());
788 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000789
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000790 if (!outputDir.endsWith(PATH_DIV_STR)) {
791 outputDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000792 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000793 if (generateDiffs) {
epoger@google.com71329d82012-08-16 13:42:13 +0000794 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000795 printf("writing diffs to outputDir is [%s]\n", outputDir.c_str());
796 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000797 } else {
epoger@google.com71329d82012-08-16 13:42:13 +0000798 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000799 printf("not writing any diffs to outputDir [%s]\n", outputDir.c_str());
800 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000801 outputDir.set("");
802 }
803
epoger@google.comda4af242012-06-25 18:45:50 +0000804 // If no matchSubstrings were specified, match ALL strings
805 // (except for whatever nomatchSubstrings were specified, if any).
epoger@google.coma5f406e2012-05-01 13:26:16 +0000806 if (matchSubstrings.isEmpty()) {
807 matchSubstrings.push(new SkString(""));
808 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000809
epoger@google.coma611c3e2012-05-18 20:10:06 +0000810 create_diff_images(diffProc, colorThreshold, &differences,
811 baseDir, comparisonDir, outputDir,
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000812 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, generateDiffs,
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000813 verbose, &summary);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000814 summary.print(listFilenames, failOnResultType, failOnStatusType);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000815
reed3a3baf62015-01-06 07:39:55 -0800816 if (listFailingBase) {
817 summary.printfFailingBaseNames("\n");
818 }
819
tomhudson@google.com7d042802011-07-14 13:15:55 +0000820 if (differences.count()) {
reed@google.comc7a67cb2012-05-07 14:52:12 +0000821 qsort(differences.begin(), differences.count(),
822 sizeof(DiffRecord*), sortProc);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000823 }
epoger@google.com66008522012-05-16 17:40:57 +0000824
epoger@google.coma5f406e2012-05-01 13:26:16 +0000825 if (generateDiffs) {
826 print_diff_page(summary.fNumMatches, colorThreshold, differences,
827 baseDir, comparisonDir, outputDir);
828 }
epoger@google.com76222c02012-05-31 15:12:09 +0000829
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000830 for (i = 0; i < differences.count(); i++) {
831 delete differences[i];
832 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000833 matchSubstrings.deleteAll();
834 nomatchSubstrings.deleteAll();
epoger@google.combe6188d2012-05-31 15:13:45 +0000835
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000836 int num_failing_results = 0;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000837 for (int i = 0; i < DiffRecord::kResultCount; i++) {
epoger@google.com3af4ff42012-07-19 17:35:04 +0000838 if (failOnResultType[i]) {
839 num_failing_results += summary.fResultsOfType[i].count();
840 }
epoger@google.com46a45962012-07-12 18:16:02 +0000841 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000842 if (!failOnResultType[DiffRecord::kCouldNotCompare_Result]) {
843 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
844 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
845 if (failOnStatusType[base][comparison]) {
846 num_failing_results += summary.fStatusOfType[base][comparison].count();
847 }
848 }
849 }
850 }
epoger@google.com28659882012-07-16 18:01:06 +0000851
852 // On Linux (and maybe other platforms too), any results outside of the
853 // range [0...255] are wrapped (mod 256). Do the conversion ourselves, to
854 // make sure that we only return 0 when there were no failures.
855 return (num_failing_results > 255) ? 255 : num_failing_results;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000856}
caryclark@google.com5987f582012-10-02 18:33:14 +0000857
858#if !defined SK_BUILD_FOR_IOS
859int main(int argc, char * const argv[]) {
860 return tool_main(argc, (char**) argv);
861}
862#endif