blob: f03ccb04b0a4183014c90803bcfd99d490462977 [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"
Hal Canary1b3387b2016-12-12 13:48:12 -050016#include "SkPixelRef.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
tomhudson@google.com4b33d282011-04-27 15:39:30 +000022/**
23 * skdiff
24 *
25 * Given three directory names, expects to find identically-named files in
26 * each of the first two; the first are treated as a set of baseline,
27 * the second a set of variant images, and a diff image is written into the
28 * third directory for each pair.
tomhudson@google.com7d042802011-07-14 13:15:55 +000029 * Creates an index.html in the current third directory to compare each
tomhudson@google.com4b33d282011-04-27 15:39:30 +000030 * pair that does not match exactly.
epoger@google.com71329d82012-08-16 13:42:13 +000031 * Recursively descends directories, unless run with --norecurse.
epoger@google.combe6188d2012-05-31 15:13:45 +000032 *
33 * Returns zero exit code if all images match across baseDir and comparisonDir.
tomhudson@google.com4b33d282011-04-27 15:39:30 +000034 */
35
epoger@google.coma5f406e2012-05-01 13:26:16 +000036typedef SkTDArray<SkString*> StringArray;
37typedef StringArray FileArray;
epoger@google.com5fd53852012-03-22 18:20:06 +000038
reed3a3baf62015-01-06 07:39:55 -080039static void add_unique_basename(StringArray* array, const SkString& filename) {
40 // trim off dirs
41 const char* src = filename.c_str();
Ben Wagnerbf111d72016-11-07 18:05:29 -050042 const char* trimmed = strrchr(src, SkOSPath::SEPARATOR);
reed3a3baf62015-01-06 07:39:55 -080043 if (trimmed) {
44 trimmed += 1; // skip the separator
45 } else {
46 trimmed = src;
47 }
48 const char* end = strrchr(trimmed, '.');
49 if (!end) {
50 end = trimmed + strlen(trimmed);
51 }
52 SkString result(trimmed, end - trimmed);
53
54 // only add unique entries
55 for (int i = 0; i < array->count(); ++i) {
56 if (*array->getAt(i) == result) {
57 return;
58 }
59 }
60 *array->append() = new SkString(result);
61}
62
tomhudson@google.com9dc527b2011-06-09 15:47:10 +000063struct DiffSummary {
64 DiffSummary ()
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000065 : fNumMatches(0)
66 , fNumMismatches(0)
67 , fMaxMismatchV(0)
bungemanfe917272016-10-13 17:36:40 -040068 , fMaxMismatchPercent(0) { }
tomhudson@google.com9dc527b2011-06-09 15:47:10 +000069
epoger@google.com5fd53852012-03-22 18:20:06 +000070 ~DiffSummary() {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000071 for (int i = 0; i < DiffRecord::kResultCount; ++i) {
epoger@google.com76222c02012-05-31 15:12:09 +000072 fResultsOfType[i].deleteAll();
73 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000074 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
75 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
76 fStatusOfType[base][comparison].deleteAll();
77 }
skia.committer@gmail.com0264fb42012-12-06 02:01:25 +000078 }
epoger@google.com5fd53852012-03-22 18:20:06 +000079 }
80
tomhudson@google.com9dc527b2011-06-09 15:47:10 +000081 uint32_t fNumMatches;
82 uint32_t fNumMismatches;
83 uint32_t fMaxMismatchV;
84 float fMaxMismatchPercent;
85
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000086 FileArray fResultsOfType[DiffRecord::kResultCount];
87 FileArray fStatusOfType[DiffResource::kStatusCount][DiffResource::kStatusCount];
88
reed3a3baf62015-01-06 07:39:55 -080089 StringArray fFailedBaseNames[DiffRecord::kResultCount];
90
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000091 void printContents(const FileArray& fileArray,
92 const char* baseStatus, const char* comparisonStatus,
93 bool listFilenames) {
94 int n = fileArray.count();
95 printf("%d file pairs %s in baseDir and %s in comparisonDir",
96 n, baseStatus, comparisonStatus);
97 if (listFilenames) {
98 printf(": ");
99 for (int i = 0; i < n; ++i) {
100 printf("%s ", fileArray[i]->c_str());
101 }
102 }
103 printf("\n");
104 }
105
106 void printStatus(bool listFilenames,
107 bool failOnStatusType[DiffResource::kStatusCount]
108 [DiffResource::kStatusCount]) {
109 typedef DiffResource::Status Status;
110
111 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
112 Status baseStatus = static_cast<Status>(base);
113 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
114 Status comparisonStatus = static_cast<Status>(comparison);
115 const FileArray& fileArray = fStatusOfType[base][comparison];
116 if (fileArray.count() > 0) {
117 if (failOnStatusType[base][comparison]) {
118 printf(" [*] ");
119 } else {
120 printf(" [_] ");
121 }
122 printContents(fileArray,
123 DiffResource::getStatusDescription(baseStatus),
124 DiffResource::getStatusDescription(comparisonStatus),
125 listFilenames);
126 }
127 }
128 }
129 }
epoger@google.com76222c02012-05-31 15:12:09 +0000130
epoger@google.com3af4ff42012-07-19 17:35:04 +0000131 // Print a line about the contents of this FileArray to stdout.
epoger@google.com46a45962012-07-12 18:16:02 +0000132 void printContents(const FileArray& fileArray, const char* headerText, bool listFilenames) {
epoger@google.com76222c02012-05-31 15:12:09 +0000133 int n = fileArray.count();
epoger@google.com3af4ff42012-07-19 17:35:04 +0000134 printf("%d file pairs %s", n, headerText);
135 if (listFilenames) {
136 printf(": ");
137 for (int i = 0; i < n; ++i) {
138 printf("%s ", fileArray[i]->c_str());
epoger@google.com76222c02012-05-31 15:12:09 +0000139 }
140 }
epoger@google.com3af4ff42012-07-19 17:35:04 +0000141 printf("\n");
epoger@google.com76222c02012-05-31 15:12:09 +0000142 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000143
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000144 void print(bool listFilenames, bool failOnResultType[DiffRecord::kResultCount],
145 bool failOnStatusType[DiffResource::kStatusCount]
146 [DiffResource::kStatusCount]) {
epoger@google.com3af4ff42012-07-19 17:35:04 +0000147 printf("\ncompared %d file pairs:\n", fNumMatches + fNumMismatches);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000148 for (int resultInt = 0; resultInt < DiffRecord::kResultCount; ++resultInt) {
149 DiffRecord::Result result = static_cast<DiffRecord::Result>(resultInt);
epoger@google.com3af4ff42012-07-19 17:35:04 +0000150 if (failOnResultType[result]) {
151 printf("[*] ");
152 } else {
153 printf("[_] ");
154 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000155 printContents(fResultsOfType[result], DiffRecord::getResultDescription(result),
156 listFilenames);
157 if (DiffRecord::kCouldNotCompare_Result == result) {
158 printStatus(listFilenames, failOnStatusType);
159 }
epoger@google.com46a45962012-07-12 18:16:02 +0000160 }
epoger@google.com3af4ff42012-07-19 17:35:04 +0000161 printf("(results marked with [*] will cause nonzero return value)\n");
162 printf("\nnumber of mismatching file pairs: %d\n", fNumMismatches);
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000163 if (fNumMismatches > 0) {
164 printf("Maximum pixel intensity mismatch %d\n", fMaxMismatchV);
epoger@google.com46a45962012-07-12 18:16:02 +0000165 printf("Largest area mismatch was %.2f%% of pixels\n",fMaxMismatchPercent);
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000166 }
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000167 }
168
reed3a3baf62015-01-06 07:39:55 -0800169 void printfFailingBaseNames(const char separator[]) {
170 for (int resultInt = 0; resultInt < DiffRecord::kResultCount; ++resultInt) {
171 const StringArray& array = fFailedBaseNames[resultInt];
172 if (array.count()) {
173 printf("%s [%d]%s", DiffRecord::ResultNames[resultInt], array.count(), separator);
174 for (int j = 0; j < array.count(); ++j) {
175 printf("%s%s", array[j]->c_str(), separator);
176 }
177 printf("\n");
178 }
179 }
180 }
181
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000182 void add (DiffRecord* drp) {
epoger@google.com46256ea2012-05-22 13:45:35 +0000183 uint32_t mismatchValue;
epoger@google.com292aff62012-05-16 14:57:28 +0000184
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000185 if (drp->fBase.fFilename.equals(drp->fComparison.fFilename)) {
186 fResultsOfType[drp->fResult].push(new SkString(drp->fBase.fFilename));
187 } else {
188 SkString* blame = new SkString("(");
189 blame->append(drp->fBase.fFilename);
190 blame->append(", ");
191 blame->append(drp->fComparison.fFilename);
192 blame->append(")");
193 fResultsOfType[drp->fResult].push(blame);
194 }
epoger@google.com292aff62012-05-16 14:57:28 +0000195 switch (drp->fResult) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000196 case DiffRecord::kEqualBits_Result:
epoger@google.com46256ea2012-05-22 13:45:35 +0000197 fNumMatches++;
198 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000199 case DiffRecord::kEqualPixels_Result:
epoger@google.com292aff62012-05-16 14:57:28 +0000200 fNumMatches++;
201 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000202 case DiffRecord::kDifferentSizes_Result:
epoger@google.com5fd53852012-03-22 18:20:06 +0000203 fNumMismatches++;
epoger@google.com292aff62012-05-16 14:57:28 +0000204 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000205 case DiffRecord::kDifferentPixels_Result:
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000206 fNumMismatches++;
207 if (drp->fFractionDifference * 100 > fMaxMismatchPercent) {
208 fMaxMismatchPercent = drp->fFractionDifference * 100;
209 }
epoger@google.com46256ea2012-05-22 13:45:35 +0000210 mismatchValue = MAX3(drp->fMaxMismatchR, drp->fMaxMismatchG,
211 drp->fMaxMismatchB);
212 if (mismatchValue > fMaxMismatchV) {
213 fMaxMismatchV = mismatchValue;
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000214 }
epoger@google.com292aff62012-05-16 14:57:28 +0000215 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000216 case DiffRecord::kCouldNotCompare_Result:
epoger@google.com46256ea2012-05-22 13:45:35 +0000217 fNumMismatches++;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000218 fStatusOfType[drp->fBase.fStatus][drp->fComparison.fStatus].push(
219 new SkString(drp->fBase.fFilename));
epoger@google.com46256ea2012-05-22 13:45:35 +0000220 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000221 case DiffRecord::kUnknown_Result:
epoger@google.com46256ea2012-05-22 13:45:35 +0000222 SkDEBUGFAIL("adding uncategorized DiffRecord");
223 break;
224 default:
225 SkDEBUGFAIL("adding DiffRecord with unhandled fResult value");
226 break;
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000227 }
reed3a3baf62015-01-06 07:39:55 -0800228
229 switch (drp->fResult) {
230 case DiffRecord::kEqualBits_Result:
231 case DiffRecord::kEqualPixels_Result:
232 break;
233 default:
234 add_unique_basename(&fFailedBaseNames[drp->fResult], drp->fBase.fFilename);
235 break;
236 }
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000237 }
238};
239
epoger@google.coma5f406e2012-05-01 13:26:16 +0000240/// Returns true if string contains any of these substrings.
241static bool string_contains_any_of(const SkString& string,
242 const StringArray& substrings) {
243 for (int i = 0; i < substrings.count(); i++) {
244 if (string.contains(substrings[i]->c_str())) {
245 return true;
246 }
247 }
248 return false;
249}
250
epoger@google.com71329d82012-08-16 13:42:13 +0000251/// Internal (potentially recursive) implementation of get_file_list.
252static void get_file_list_subdir(const SkString& rootDir, const SkString& subDir,
253 const StringArray& matchSubstrings,
254 const StringArray& nomatchSubstrings,
255 bool recurseIntoSubdirs, FileArray *files) {
256 bool isSubDirEmpty = subDir.isEmpty();
257 SkString dir(rootDir);
258 if (!isSubDirEmpty) {
259 dir.append(PATH_DIV_STR);
260 dir.append(subDir);
261 }
262
263 // Iterate over files (not directories) within dir.
264 SkOSFile::Iter fileIterator(dir.c_str());
265 SkString fileName;
266 while (fileIterator.next(&fileName, false)) {
267 if (fileName.startsWith(".")) {
268 continue;
269 }
270 SkString pathRelativeToRootDir(subDir);
271 if (!isSubDirEmpty) {
272 pathRelativeToRootDir.append(PATH_DIV_STR);
273 }
274 pathRelativeToRootDir.append(fileName);
275 if (string_contains_any_of(pathRelativeToRootDir, matchSubstrings) &&
276 !string_contains_any_of(pathRelativeToRootDir, nomatchSubstrings)) {
277 files->push(new SkString(pathRelativeToRootDir));
278 }
279 }
280
281 // Recurse into any non-ignored subdirectories.
282 if (recurseIntoSubdirs) {
283 SkOSFile::Iter dirIterator(dir.c_str());
284 SkString dirName;
285 while (dirIterator.next(&dirName, true)) {
286 if (dirName.startsWith(".")) {
287 continue;
288 }
289 SkString pathRelativeToRootDir(subDir);
290 if (!isSubDirEmpty) {
291 pathRelativeToRootDir.append(PATH_DIV_STR);
292 }
293 pathRelativeToRootDir.append(dirName);
294 if (!string_contains_any_of(pathRelativeToRootDir, nomatchSubstrings)) {
295 get_file_list_subdir(rootDir, pathRelativeToRootDir,
296 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
297 files);
298 }
299 }
300 }
301}
302
303/// Iterate over dir and get all files whose filename:
304/// - matches any of the substrings in matchSubstrings, but...
305/// - DOES NOT match any of the substrings in nomatchSubstrings
306/// - DOES NOT start with a dot (.)
307/// Adds the matching files to the list in *files.
epoger@google.coma5f406e2012-05-01 13:26:16 +0000308static void get_file_list(const SkString& dir,
309 const StringArray& matchSubstrings,
310 const StringArray& nomatchSubstrings,
epoger@google.com71329d82012-08-16 13:42:13 +0000311 bool recurseIntoSubdirs, FileArray *files) {
312 get_file_list_subdir(dir, SkString(""),
313 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
314 files);
epoger@google.com5fd53852012-03-22 18:20:06 +0000315}
316
317static void release_file_list(FileArray *files) {
318 files->deleteAll();
319}
320
321/// Comparison routines for qsort, sort by file names.
322static int compare_file_name_metrics(SkString **lhs, SkString **rhs) {
323 return strcmp((*lhs)->c_str(), (*rhs)->c_str());
324}
325
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000326class AutoReleasePixels {
327public:
328 AutoReleasePixels(DiffRecord* drp)
329 : fDrp(drp) {
halcanary96fcdcc2015-08-27 07:41:13 -0700330 SkASSERT(drp != nullptr);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000331 }
332 ~AutoReleasePixels() {
Hal Canary1b3387b2016-12-12 13:48:12 -0500333 fDrp->fBase.fBitmap.setPixelRef(nullptr, 0, 0);
334 fDrp->fComparison.fBitmap.setPixelRef(nullptr, 0, 0);
335 fDrp->fDifference.fBitmap.setPixelRef(nullptr, 0, 0);
336 fDrp->fWhite.fBitmap.setPixelRef(nullptr, 0, 0);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000337 }
338
339private:
340 DiffRecord* fDrp;
341};
342
343static void get_bounds(DiffResource& resource, const char* name) {
344 if (resource.fBitmap.empty() && !DiffResource::isStatusFailed(resource.fStatus)) {
bungeman38d909e2016-08-02 14:40:46 -0700345 sk_sp<SkData> fileBits(read_file(resource.fFullPath.c_str()));
346 if (fileBits) {
reed42943c82016-09-12 12:01:44 -0700347 get_bitmap(fileBits, resource, true);
bungeman38d909e2016-08-02 14:40:46 -0700348 } else {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000349 SkDebugf("WARNING: couldn't read %s file <%s>\n", name, resource.fFullPath.c_str());
350 resource.fStatus = DiffResource::kCouldNotRead_Status;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000351 }
352 }
353}
354
355static void get_bounds(DiffRecord& drp) {
356 get_bounds(drp.fBase, "base");
357 get_bounds(drp.fComparison, "comparison");
358}
359
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000360#ifdef SK_OS_WIN
361#define ANSI_COLOR_RED ""
362#define ANSI_COLOR_GREEN ""
363#define ANSI_COLOR_YELLOW ""
364#define ANSI_COLOR_RESET ""
365#else
366#define ANSI_COLOR_RED "\x1b[31m"
367#define ANSI_COLOR_GREEN "\x1b[32m"
368#define ANSI_COLOR_YELLOW "\x1b[33m"
369#define ANSI_COLOR_RESET "\x1b[0m"
370#endif
371
372#define VERBOSE_STATUS(status,color,filename) if (verbose) printf( "[ " color " %10s " ANSI_COLOR_RESET " ] %s\n", status, filename->c_str())
373
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000374/// Creates difference images, returns the number that have a 0 metric.
epoger@google.coma5f406e2012-05-01 13:26:16 +0000375/// If outputDir.isEmpty(), don't write out diff files.
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000376static void create_diff_images (DiffMetricProc dmp,
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000377 const int colorThreshold,
378 RecordArray* differences,
379 const SkString& baseDir,
380 const SkString& comparisonDir,
381 const SkString& outputDir,
epoger@google.coma5f406e2012-05-01 13:26:16 +0000382 const StringArray& matchSubstrings,
383 const StringArray& nomatchSubstrings,
epoger@google.com71329d82012-08-16 13:42:13 +0000384 bool recurseIntoSubdirs,
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000385 bool getBounds,
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000386 bool verbose,
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000387 DiffSummary* summary) {
epoger@google.coma5f406e2012-05-01 13:26:16 +0000388 SkASSERT(!baseDir.isEmpty());
389 SkASSERT(!comparisonDir.isEmpty());
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000390
epoger@google.com5fd53852012-03-22 18:20:06 +0000391 FileArray baseFiles;
392 FileArray comparisonFiles;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000393
epoger@google.com71329d82012-08-16 13:42:13 +0000394 get_file_list(baseDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, &baseFiles);
395 get_file_list(comparisonDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
epoger@google.coma5f406e2012-05-01 13:26:16 +0000396 &comparisonFiles);
epoger@google.com5fd53852012-03-22 18:20:06 +0000397
epoger@google.coma5f406e2012-05-01 13:26:16 +0000398 if (!baseFiles.isEmpty()) {
reed@google.comc7a67cb2012-05-07 14:52:12 +0000399 qsort(baseFiles.begin(), baseFiles.count(), sizeof(SkString*),
400 SkCastForQSort(compare_file_name_metrics));
epoger@google.coma5f406e2012-05-01 13:26:16 +0000401 }
402 if (!comparisonFiles.isEmpty()) {
reed@google.comc7a67cb2012-05-07 14:52:12 +0000403 qsort(comparisonFiles.begin(), comparisonFiles.count(),
404 sizeof(SkString*), SkCastForQSort(compare_file_name_metrics));
epoger@google.coma5f406e2012-05-01 13:26:16 +0000405 }
epoger@google.com66008522012-05-16 17:40:57 +0000406
brianosman235cbf22016-04-05 11:37:49 -0700407 if (!outputDir.isEmpty()) {
408 sk_mkdir(outputDir.c_str());
409 }
410
epoger@google.com5fd53852012-03-22 18:20:06 +0000411 int i = 0;
412 int j = 0;
413
414 while (i < baseFiles.count() &&
415 j < comparisonFiles.count()) {
416
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000417 SkString basePath(baseDir);
418 SkString comparisonPath(comparisonDir);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000419
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000420 DiffRecord *drp = new DiffRecord;
421 int v = strcmp(baseFiles[i]->c_str(), comparisonFiles[j]->c_str());
epoger@google.com5fd53852012-03-22 18:20:06 +0000422
423 if (v < 0) {
424 // in baseDir, but not in comparisonDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000425 drp->fResult = DiffRecord::kCouldNotCompare_Result;
426
427 basePath.append(*baseFiles[i]);
428 comparisonPath.append(*baseFiles[i]);
429
430 drp->fBase.fFilename = *baseFiles[i];
431 drp->fBase.fFullPath = basePath;
432 drp->fBase.fStatus = DiffResource::kExists_Status;
433
434 drp->fComparison.fFilename = *baseFiles[i];
435 drp->fComparison.fFullPath = comparisonPath;
436 drp->fComparison.fStatus = DiffResource::kDoesNotExist_Status;
437
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000438 VERBOSE_STATUS("MISSING", ANSI_COLOR_YELLOW, baseFiles[i]);
439
epoger@google.com5fd53852012-03-22 18:20:06 +0000440 ++i;
441 } else if (v > 0) {
442 // in comparisonDir, but not in baseDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000443 drp->fResult = DiffRecord::kCouldNotCompare_Result;
444
445 basePath.append(*comparisonFiles[j]);
446 comparisonPath.append(*comparisonFiles[j]);
447
448 drp->fBase.fFilename = *comparisonFiles[j];
449 drp->fBase.fFullPath = basePath;
450 drp->fBase.fStatus = DiffResource::kDoesNotExist_Status;
451
452 drp->fComparison.fFilename = *comparisonFiles[j];
453 drp->fComparison.fFullPath = comparisonPath;
454 drp->fComparison.fStatus = DiffResource::kExists_Status;
455
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000456 VERBOSE_STATUS("MISSING", ANSI_COLOR_YELLOW, comparisonFiles[j]);
457
epoger@google.com5fd53852012-03-22 18:20:06 +0000458 ++j;
459 } else {
epoger@google.com46256ea2012-05-22 13:45:35 +0000460 // Found the same filename in both baseDir and comparisonDir.
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000461 SkASSERT(DiffRecord::kUnknown_Result == drp->fResult);
epoger@google.com5fd53852012-03-22 18:20:06 +0000462
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000463 basePath.append(*baseFiles[i]);
464 comparisonPath.append(*comparisonFiles[j]);
465
466 drp->fBase.fFilename = *baseFiles[i];
467 drp->fBase.fFullPath = basePath;
468 drp->fBase.fStatus = DiffResource::kExists_Status;
469
470 drp->fComparison.fFilename = *comparisonFiles[j];
471 drp->fComparison.fFullPath = comparisonPath;
472 drp->fComparison.fStatus = DiffResource::kExists_Status;
473
bungeman38d909e2016-08-02 14:40:46 -0700474 sk_sp<SkData> baseFileBits(read_file(drp->fBase.fFullPath.c_str()));
bsalomon49f085d2014-09-05 13:34:00 -0700475 if (baseFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000476 drp->fBase.fStatus = DiffResource::kRead_Status;
477 }
bungeman38d909e2016-08-02 14:40:46 -0700478 sk_sp<SkData> comparisonFileBits(read_file(drp->fComparison.fFullPath.c_str()));
bsalomon49f085d2014-09-05 13:34:00 -0700479 if (comparisonFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000480 drp->fComparison.fStatus = DiffResource::kRead_Status;
481 }
halcanary96fcdcc2015-08-27 07:41:13 -0700482 if (nullptr == baseFileBits || nullptr == comparisonFileBits) {
483 if (nullptr == baseFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000484 drp->fBase.fStatus = DiffResource::kCouldNotRead_Status;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000485 VERBOSE_STATUS("READ FAIL", ANSI_COLOR_RED, baseFiles[i]);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000486 }
halcanary96fcdcc2015-08-27 07:41:13 -0700487 if (nullptr == comparisonFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000488 drp->fComparison.fStatus = DiffResource::kCouldNotRead_Status;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000489 VERBOSE_STATUS("READ FAIL", ANSI_COLOR_RED, comparisonFiles[j]);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000490 }
491 drp->fResult = DiffRecord::kCouldNotCompare_Result;
492
bungeman38d909e2016-08-02 14:40:46 -0700493 } else if (are_buffers_equal(baseFileBits.get(), comparisonFileBits.get())) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000494 drp->fResult = DiffRecord::kEqualBits_Result;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000495 VERBOSE_STATUS("MATCH", ANSI_COLOR_GREEN, baseFiles[i]);
epoger@google.com46256ea2012-05-22 13:45:35 +0000496 } else {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000497 AutoReleasePixels arp(drp);
reed42943c82016-09-12 12:01:44 -0700498 get_bitmap(baseFileBits, drp->fBase, false);
499 get_bitmap(comparisonFileBits, drp->fComparison, false);
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000500 VERBOSE_STATUS("DIFFERENT", ANSI_COLOR_RED, baseFiles[i]);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000501 if (DiffResource::kDecoded_Status == drp->fBase.fStatus &&
502 DiffResource::kDecoded_Status == drp->fComparison.fStatus) {
503 create_and_write_diff_image(drp, dmp, colorThreshold,
504 outputDir, drp->fBase.fFilename);
epoger@google.com46256ea2012-05-22 13:45:35 +0000505 } else {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000506 drp->fResult = DiffRecord::kCouldNotCompare_Result;
epoger@google.com46256ea2012-05-22 13:45:35 +0000507 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000508 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000509
epoger@google.com5fd53852012-03-22 18:20:06 +0000510 ++i;
511 ++j;
512 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000513
514 if (getBounds) {
515 get_bounds(*drp);
516 }
517 SkASSERT(DiffRecord::kUnknown_Result != drp->fResult);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000518 differences->push(drp);
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000519 summary->add(drp);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000520 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000521
522 for (; i < baseFiles.count(); ++i) {
523 // files only in baseDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000524 DiffRecord *drp = new DiffRecord();
525 drp->fBase.fFilename = *baseFiles[i];
526 drp->fBase.fFullPath = baseDir;
527 drp->fBase.fFullPath.append(drp->fBase.fFilename);
528 drp->fBase.fStatus = DiffResource::kExists_Status;
529
530 drp->fComparison.fFilename = *baseFiles[i];
531 drp->fComparison.fFullPath = comparisonDir;
532 drp->fComparison.fFullPath.append(drp->fComparison.fFilename);
533 drp->fComparison.fStatus = DiffResource::kDoesNotExist_Status;
534
535 drp->fResult = DiffRecord::kCouldNotCompare_Result;
536 if (getBounds) {
537 get_bounds(*drp);
538 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000539 differences->push(drp);
540 summary->add(drp);
541 }
542
543 for (; j < comparisonFiles.count(); ++j) {
544 // files only in comparisonDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000545 DiffRecord *drp = new DiffRecord();
546 drp->fBase.fFilename = *comparisonFiles[j];
547 drp->fBase.fFullPath = baseDir;
548 drp->fBase.fFullPath.append(drp->fBase.fFilename);
549 drp->fBase.fStatus = DiffResource::kDoesNotExist_Status;
550
551 drp->fComparison.fFilename = *comparisonFiles[j];
552 drp->fComparison.fFullPath = comparisonDir;
553 drp->fComparison.fFullPath.append(drp->fComparison.fFilename);
554 drp->fComparison.fStatus = DiffResource::kExists_Status;
555
556 drp->fResult = DiffRecord::kCouldNotCompare_Result;
557 if (getBounds) {
558 get_bounds(*drp);
559 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000560 differences->push(drp);
561 summary->add(drp);
562 }
563
564 release_file_list(&baseFiles);
565 release_file_list(&comparisonFiles);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000566}
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000567
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000568static void usage (char * argv0) {
569 SkDebugf("Skia baseline image diff tool\n");
epoger@google.coma5f406e2012-05-01 13:26:16 +0000570 SkDebugf("\n"
571"Usage: \n"
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000572" %s <baseDir> <comparisonDir> [outputDir] \n", argv0);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000573 SkDebugf(
epoger@google.com46a45962012-07-12 18:16:02 +0000574"\nArguments:"
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000575"\n --failonresult <result>: After comparing all file pairs, exit with nonzero"
576"\n return code (number of file pairs yielding this"
577"\n result) if any file pairs yielded this result."
578"\n This flag may be repeated, in which case the"
579"\n return code will be the number of fail pairs"
580"\n yielding ANY of these results."
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000581"\n --failonstatus <baseStatus> <comparisonStatus>: exit with nonzero return"
582"\n code if any file pairs yielded this status."
epoger@google.com46a45962012-07-12 18:16:02 +0000583"\n --help: display this info"
epoger@google.com46a45962012-07-12 18:16:02 +0000584"\n --listfilenames: list all filenames for each result type in stdout"
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000585"\n --match <substring>: compare files whose filenames contain this substring;"
586"\n if unspecified, compare ALL files."
587"\n this flag may be repeated."
epoger@google.com46a45962012-07-12 18:16:02 +0000588"\n --nodiffs: don't write out image diffs or index.html, just generate"
589"\n report on stdout"
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000590"\n --nomatch <substring>: regardless of --match, DO NOT compare files whose"
591"\n filenames contain this substring."
592"\n this flag may be repeated."
epoger@google.com46a45962012-07-12 18:16:02 +0000593"\n --noprintdirs: do not print the directories used."
epoger@google.com71329d82012-08-16 13:42:13 +0000594"\n --norecurse: do not recurse into subdirectories."
epoger@google.com46a45962012-07-12 18:16:02 +0000595"\n --sortbymaxmismatch: sort by worst color channel mismatch;"
596"\n break ties with -sortbymismatch"
597"\n --sortbymismatch: sort by average color channel mismatch"
598"\n --threshold <n>: only report differences > n (per color channel) [default 0]"
599"\n --weighted: sort by # pixels different weighted by color difference"
600"\n"
601"\n baseDir: directory to read baseline images from."
602"\n comparisonDir: directory to read comparison images from"
603"\n outputDir: directory to write difference images and index.html to;"
604"\n defaults to comparisonDir"
605"\n"
606"\nIf no sort is specified, it will sort by fraction of pixels mismatching."
607"\n");
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000608}
609
epoger@google.com70044cc2012-07-12 18:37:55 +0000610const int kNoError = 0;
611const int kGenericError = -1;
epoger@google.com46a45962012-07-12 18:16:02 +0000612
caryclark@google.com5987f582012-10-02 18:33:14 +0000613int tool_main(int argc, char** argv);
614int tool_main(int argc, char** argv) {
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000615 DiffMetricProc diffProc = compute_diff_pmcolor;
epoger@google.com28060e72012-06-28 16:47:34 +0000616 int (*sortProc)(const void*, const void*) = compare<CompareDiffMetrics>;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000617
618 // Maximum error tolerated in any one color channel in any one pixel before
619 // a difference is reported.
620 int colorThreshold = 0;
621 SkString baseDir;
622 SkString comparisonDir;
623 SkString outputDir;
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000624
epoger@google.coma5f406e2012-05-01 13:26:16 +0000625 StringArray matchSubstrings;
626 StringArray nomatchSubstrings;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000627
epoger@google.coma5f406e2012-05-01 13:26:16 +0000628 bool generateDiffs = true;
epoger@google.com46a45962012-07-12 18:16:02 +0000629 bool listFilenames = false;
epoger@google.com71329d82012-08-16 13:42:13 +0000630 bool printDirNames = true;
631 bool recurseIntoSubdirs = true;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000632 bool verbose = false;
reed3a3baf62015-01-06 07:39:55 -0800633 bool listFailingBase = false;
tomhudson@google.com7d042802011-07-14 13:15:55 +0000634
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000635 RecordArray differences;
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000636 DiffSummary summary;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000637
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000638 bool failOnResultType[DiffRecord::kResultCount];
639 for (int i = 0; i < DiffRecord::kResultCount; i++) {
epoger@google.com3af4ff42012-07-19 17:35:04 +0000640 failOnResultType[i] = false;
641 }
642
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000643 bool failOnStatusType[DiffResource::kStatusCount][DiffResource::kStatusCount];
644 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
645 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
646 failOnStatusType[base][comparison] = false;
647 }
648 }
649
epoger@google.coma5f406e2012-05-01 13:26:16 +0000650 int i;
651 int numUnflaggedArguments = 0;
652 for (i = 1; i < argc; i++) {
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000653 if (!strcmp(argv[i], "--failonresult")) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000654 if (argc == ++i) {
655 SkDebugf("failonresult expects one argument.\n");
656 continue;
657 }
658 DiffRecord::Result type = DiffRecord::getResultByName(argv[i]);
659 if (type != DiffRecord::kResultCount) {
660 failOnResultType[type] = true;
661 } else {
662 SkDebugf("ignoring unrecognized result <%s>\n", argv[i]);
663 }
664 continue;
665 }
666 if (!strcmp(argv[i], "--failonstatus")) {
667 if (argc == ++i) {
668 SkDebugf("failonstatus missing base status.\n");
669 continue;
670 }
671 bool baseStatuses[DiffResource::kStatusCount];
672 if (!DiffResource::getMatchingStatuses(argv[i], baseStatuses)) {
673 SkDebugf("unrecognized base status <%s>\n", argv[i]);
674 }
675
676 if (argc == ++i) {
677 SkDebugf("failonstatus missing comparison status.\n");
678 continue;
679 }
680 bool comparisonStatuses[DiffResource::kStatusCount];
681 if (!DiffResource::getMatchingStatuses(argv[i], comparisonStatuses)) {
682 SkDebugf("unrecognized comarison status <%s>\n", argv[i]);
683 }
684
685 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
686 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
687 failOnStatusType[base][comparison] |=
688 baseStatuses[base] && comparisonStatuses[comparison];
689 }
690 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000691 continue;
692 }
epoger@google.com46a45962012-07-12 18:16:02 +0000693 if (!strcmp(argv[i], "--help")) {
694 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000695 return kNoError;
epoger@google.com46a45962012-07-12 18:16:02 +0000696 }
697 if (!strcmp(argv[i], "--listfilenames")) {
698 listFilenames = true;
epoger@google.coma5f406e2012-05-01 13:26:16 +0000699 continue;
700 }
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000701 if (!strcmp(argv[i], "--verbose")) {
702 verbose = true;
703 continue;
704 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000705 if (!strcmp(argv[i], "--match")) {
706 matchSubstrings.push(new SkString(argv[++i]));
707 continue;
708 }
epoger@google.com46a45962012-07-12 18:16:02 +0000709 if (!strcmp(argv[i], "--nodiffs")) {
710 generateDiffs = false;
711 continue;
712 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000713 if (!strcmp(argv[i], "--nomatch")) {
714 nomatchSubstrings.push(new SkString(argv[++i]));
715 continue;
716 }
epoger@google.com46a45962012-07-12 18:16:02 +0000717 if (!strcmp(argv[i], "--noprintdirs")) {
epoger@google.com71329d82012-08-16 13:42:13 +0000718 printDirNames = false;
719 continue;
720 }
721 if (!strcmp(argv[i], "--norecurse")) {
722 recurseIntoSubdirs = false;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000723 continue;
724 }
tomhudson@google.com7d042802011-07-14 13:15:55 +0000725 if (!strcmp(argv[i], "--sortbymaxmismatch")) {
epoger@google.com28060e72012-06-28 16:47:34 +0000726 sortProc = compare<CompareDiffMaxMismatches>;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000727 continue;
728 }
epoger@google.com46a45962012-07-12 18:16:02 +0000729 if (!strcmp(argv[i], "--sortbymismatch")) {
730 sortProc = compare<CompareDiffMeanMismatches>;
tomhudson@google.com5b325292011-05-24 19:41:13 +0000731 continue;
732 }
epoger@google.com46a45962012-07-12 18:16:02 +0000733 if (!strcmp(argv[i], "--threshold")) {
734 colorThreshold = atoi(argv[++i]);
735 continue;
736 }
737 if (!strcmp(argv[i], "--weighted")) {
738 sortProc = compare<CompareDiffWeighted>;
keyar@chromium.orga6318192012-07-09 21:01:50 +0000739 continue;
740 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000741 if (argv[i][0] != '-') {
epoger@google.coma5f406e2012-05-01 13:26:16 +0000742 switch (numUnflaggedArguments++) {
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000743 case 0:
744 baseDir.set(argv[i]);
745 continue;
746 case 1:
747 comparisonDir.set(argv[i]);
748 continue;
749 case 2:
750 outputDir.set(argv[i]);
751 continue;
752 default:
epoger@google.coma5f406e2012-05-01 13:26:16 +0000753 SkDebugf("extra unflagged argument <%s>\n", argv[i]);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000754 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000755 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000756 }
757 }
reed3a3baf62015-01-06 07:39:55 -0800758 if (!strcmp(argv[i], "--listFailingBase")) {
759 listFailingBase = true;
760 continue;
761 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000762
763 SkDebugf("Unrecognized argument <%s>\n", argv[i]);
764 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000765 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000766 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000767
epoger@google.coma5f406e2012-05-01 13:26:16 +0000768 if (numUnflaggedArguments == 2) {
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000769 outputDir = comparisonDir;
epoger@google.coma5f406e2012-05-01 13:26:16 +0000770 } else if (numUnflaggedArguments != 3) {
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000771 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000772 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000773 }
774
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000775 if (!baseDir.endsWith(PATH_DIV_STR)) {
776 baseDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000777 }
epoger@google.com71329d82012-08-16 13:42:13 +0000778 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000779 printf("baseDir is [%s]\n", baseDir.c_str());
780 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000781
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000782 if (!comparisonDir.endsWith(PATH_DIV_STR)) {
783 comparisonDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000784 }
epoger@google.com71329d82012-08-16 13:42:13 +0000785 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000786 printf("comparisonDir is [%s]\n", comparisonDir.c_str());
787 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000788
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000789 if (!outputDir.endsWith(PATH_DIV_STR)) {
790 outputDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000791 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000792 if (generateDiffs) {
epoger@google.com71329d82012-08-16 13:42:13 +0000793 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000794 printf("writing diffs to outputDir is [%s]\n", outputDir.c_str());
795 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000796 } else {
epoger@google.com71329d82012-08-16 13:42:13 +0000797 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000798 printf("not writing any diffs to outputDir [%s]\n", outputDir.c_str());
799 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000800 outputDir.set("");
801 }
802
epoger@google.comda4af242012-06-25 18:45:50 +0000803 // If no matchSubstrings were specified, match ALL strings
804 // (except for whatever nomatchSubstrings were specified, if any).
epoger@google.coma5f406e2012-05-01 13:26:16 +0000805 if (matchSubstrings.isEmpty()) {
806 matchSubstrings.push(new SkString(""));
807 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000808
epoger@google.coma611c3e2012-05-18 20:10:06 +0000809 create_diff_images(diffProc, colorThreshold, &differences,
810 baseDir, comparisonDir, outputDir,
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000811 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, generateDiffs,
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000812 verbose, &summary);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000813 summary.print(listFilenames, failOnResultType, failOnStatusType);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000814
reed3a3baf62015-01-06 07:39:55 -0800815 if (listFailingBase) {
816 summary.printfFailingBaseNames("\n");
817 }
818
tomhudson@google.com7d042802011-07-14 13:15:55 +0000819 if (differences.count()) {
reed@google.comc7a67cb2012-05-07 14:52:12 +0000820 qsort(differences.begin(), differences.count(),
821 sizeof(DiffRecord*), sortProc);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000822 }
epoger@google.com66008522012-05-16 17:40:57 +0000823
epoger@google.coma5f406e2012-05-01 13:26:16 +0000824 if (generateDiffs) {
825 print_diff_page(summary.fNumMatches, colorThreshold, differences,
826 baseDir, comparisonDir, outputDir);
827 }
epoger@google.com76222c02012-05-31 15:12:09 +0000828
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000829 for (i = 0; i < differences.count(); i++) {
830 delete differences[i];
831 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000832 matchSubstrings.deleteAll();
833 nomatchSubstrings.deleteAll();
epoger@google.combe6188d2012-05-31 15:13:45 +0000834
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000835 int num_failing_results = 0;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000836 for (int i = 0; i < DiffRecord::kResultCount; i++) {
epoger@google.com3af4ff42012-07-19 17:35:04 +0000837 if (failOnResultType[i]) {
838 num_failing_results += summary.fResultsOfType[i].count();
839 }
epoger@google.com46a45962012-07-12 18:16:02 +0000840 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000841 if (!failOnResultType[DiffRecord::kCouldNotCompare_Result]) {
842 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
843 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
844 if (failOnStatusType[base][comparison]) {
845 num_failing_results += summary.fStatusOfType[base][comparison].count();
846 }
847 }
848 }
849 }
epoger@google.com28659882012-07-16 18:01:06 +0000850
851 // On Linux (and maybe other platforms too), any results outside of the
852 // range [0...255] are wrapped (mod 256). Do the conversion ourselves, to
853 // make sure that we only return 0 when there were no failures.
854 return (num_failing_results > 255) ? 255 : num_failing_results;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000855}
caryclark@google.com5987f582012-10-02 18:33:14 +0000856
857#if !defined SK_BUILD_FOR_IOS
858int main(int argc, char * const argv[]) {
859 return tool_main(argc, (char**) argv);
860}
861#endif