blob: eec6306b96ea4b1fdbb8e6c88846f098a8e4e210 [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 */
Mike Kleinc0bd9f92019-04-23 12:05:21 -05007#include "include/core/SkBitmap.h"
8#include "include/core/SkData.h"
9#include "include/core/SkImageEncoder.h"
10#include "include/core/SkPixelRef.h"
11#include "include/core/SkStream.h"
12#include "include/private/SkTDArray.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "src/core/SkOSFile.h"
Ben Wagner8bd6e8f2019-05-15 09:28:52 -040014#include "src/core/SkTSearch.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050015#include "src/utils/SkOSPath.h"
16#include "tools/skdiff/skdiff.h"
17#include "tools/skdiff/skdiff_html.h"
18#include "tools/skdiff/skdiff_utils.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)) {
Mike Reed5edcd312018-08-08 11:23:41 -0400186 fResultsOfType[drp->fResult].push_back(new SkString(drp->fBase.fFilename));
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000187 } else {
188 SkString* blame = new SkString("(");
189 blame->append(drp->fBase.fFilename);
190 blame->append(", ");
191 blame->append(drp->fComparison.fFilename);
192 blame->append(")");
Mike Reed5edcd312018-08-08 11:23:41 -0400193 fResultsOfType[drp->fResult].push_back(blame);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000194 }
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++;
Mike Reed5edcd312018-08-08 11:23:41 -0400218 fStatusOfType[drp->fBase.fStatus][drp->fComparison.fStatus].push_back(
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000219 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)) {
Mike Reed5edcd312018-08-08 11:23:41 -0400277 files->push_back(new SkString(pathRelativeToRootDir));
epoger@google.com71329d82012-08-16 13:42:13 +0000278 }
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
John Stiles70474c12020-07-13 18:09:07 -0400321/// Comparison routine for sorting by file name.
322static int compare_file_name_metrics(const SkString* lhs, const SkString* rhs) {
323 return strcmp(lhs->c_str(), rhs->c_str()) < 0;
epoger@google.com5fd53852012-03-22 18:20:06 +0000324}
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) {
Brian Osman9889c242018-07-17 16:45:40 -0400347 get_bitmap(fileBits, resource, true, 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,
Brian Osman9889c242018-07-17 16:45:40 -0400378 bool ignoreColorSpace,
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000379 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()) {
John Stiles70474c12020-07-13 18:09:07 -0400400 std::sort(baseFiles.begin(), baseFiles.end(), compare_file_name_metrics);
epoger@google.coma5f406e2012-05-01 13:26:16 +0000401 }
402 if (!comparisonFiles.isEmpty()) {
John Stiles70474c12020-07-13 18:09:07 -0400403 std::sort(comparisonFiles.begin(), comparisonFiles.end(), compare_file_name_metrics);
epoger@google.coma5f406e2012-05-01 13:26:16 +0000404 }
epoger@google.com66008522012-05-16 17:40:57 +0000405
brianosman235cbf22016-04-05 11:37:49 -0700406 if (!outputDir.isEmpty()) {
407 sk_mkdir(outputDir.c_str());
408 }
409
epoger@google.com5fd53852012-03-22 18:20:06 +0000410 int i = 0;
411 int j = 0;
412
413 while (i < baseFiles.count() &&
414 j < comparisonFiles.count()) {
415
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000416 SkString basePath(baseDir);
417 SkString comparisonPath(comparisonDir);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000418
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000419 DiffRecord *drp = new DiffRecord;
420 int v = strcmp(baseFiles[i]->c_str(), comparisonFiles[j]->c_str());
epoger@google.com5fd53852012-03-22 18:20:06 +0000421
422 if (v < 0) {
423 // in baseDir, but not in comparisonDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000424 drp->fResult = DiffRecord::kCouldNotCompare_Result;
425
426 basePath.append(*baseFiles[i]);
427 comparisonPath.append(*baseFiles[i]);
428
429 drp->fBase.fFilename = *baseFiles[i];
430 drp->fBase.fFullPath = basePath;
431 drp->fBase.fStatus = DiffResource::kExists_Status;
432
433 drp->fComparison.fFilename = *baseFiles[i];
434 drp->fComparison.fFullPath = comparisonPath;
435 drp->fComparison.fStatus = DiffResource::kDoesNotExist_Status;
436
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000437 VERBOSE_STATUS("MISSING", ANSI_COLOR_YELLOW, baseFiles[i]);
438
epoger@google.com5fd53852012-03-22 18:20:06 +0000439 ++i;
440 } else if (v > 0) {
441 // in comparisonDir, but not in baseDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000442 drp->fResult = DiffRecord::kCouldNotCompare_Result;
443
444 basePath.append(*comparisonFiles[j]);
445 comparisonPath.append(*comparisonFiles[j]);
446
447 drp->fBase.fFilename = *comparisonFiles[j];
448 drp->fBase.fFullPath = basePath;
449 drp->fBase.fStatus = DiffResource::kDoesNotExist_Status;
450
451 drp->fComparison.fFilename = *comparisonFiles[j];
452 drp->fComparison.fFullPath = comparisonPath;
453 drp->fComparison.fStatus = DiffResource::kExists_Status;
454
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000455 VERBOSE_STATUS("MISSING", ANSI_COLOR_YELLOW, comparisonFiles[j]);
456
epoger@google.com5fd53852012-03-22 18:20:06 +0000457 ++j;
458 } else {
epoger@google.com46256ea2012-05-22 13:45:35 +0000459 // Found the same filename in both baseDir and comparisonDir.
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000460 SkASSERT(DiffRecord::kUnknown_Result == drp->fResult);
epoger@google.com5fd53852012-03-22 18:20:06 +0000461
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000462 basePath.append(*baseFiles[i]);
463 comparisonPath.append(*comparisonFiles[j]);
464
465 drp->fBase.fFilename = *baseFiles[i];
466 drp->fBase.fFullPath = basePath;
467 drp->fBase.fStatus = DiffResource::kExists_Status;
468
469 drp->fComparison.fFilename = *comparisonFiles[j];
470 drp->fComparison.fFullPath = comparisonPath;
471 drp->fComparison.fStatus = DiffResource::kExists_Status;
472
bungeman38d909e2016-08-02 14:40:46 -0700473 sk_sp<SkData> baseFileBits(read_file(drp->fBase.fFullPath.c_str()));
bsalomon49f085d2014-09-05 13:34:00 -0700474 if (baseFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000475 drp->fBase.fStatus = DiffResource::kRead_Status;
476 }
bungeman38d909e2016-08-02 14:40:46 -0700477 sk_sp<SkData> comparisonFileBits(read_file(drp->fComparison.fFullPath.c_str()));
bsalomon49f085d2014-09-05 13:34:00 -0700478 if (comparisonFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000479 drp->fComparison.fStatus = DiffResource::kRead_Status;
480 }
halcanary96fcdcc2015-08-27 07:41:13 -0700481 if (nullptr == baseFileBits || nullptr == comparisonFileBits) {
482 if (nullptr == baseFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000483 drp->fBase.fStatus = DiffResource::kCouldNotRead_Status;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000484 VERBOSE_STATUS("READ FAIL", ANSI_COLOR_RED, baseFiles[i]);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000485 }
halcanary96fcdcc2015-08-27 07:41:13 -0700486 if (nullptr == comparisonFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000487 drp->fComparison.fStatus = DiffResource::kCouldNotRead_Status;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000488 VERBOSE_STATUS("READ FAIL", ANSI_COLOR_RED, comparisonFiles[j]);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000489 }
490 drp->fResult = DiffRecord::kCouldNotCompare_Result;
491
bungeman38d909e2016-08-02 14:40:46 -0700492 } else if (are_buffers_equal(baseFileBits.get(), comparisonFileBits.get())) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000493 drp->fResult = DiffRecord::kEqualBits_Result;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000494 VERBOSE_STATUS("MATCH", ANSI_COLOR_GREEN, baseFiles[i]);
epoger@google.com46256ea2012-05-22 13:45:35 +0000495 } else {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000496 AutoReleasePixels arp(drp);
Brian Osman9889c242018-07-17 16:45:40 -0400497 get_bitmap(baseFileBits, drp->fBase, false, ignoreColorSpace);
498 get_bitmap(comparisonFileBits, drp->fComparison, false, ignoreColorSpace);
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000499 VERBOSE_STATUS("DIFFERENT", ANSI_COLOR_RED, baseFiles[i]);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000500 if (DiffResource::kDecoded_Status == drp->fBase.fStatus &&
501 DiffResource::kDecoded_Status == drp->fComparison.fStatus) {
502 create_and_write_diff_image(drp, dmp, colorThreshold,
503 outputDir, drp->fBase.fFilename);
epoger@google.com46256ea2012-05-22 13:45:35 +0000504 } else {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000505 drp->fResult = DiffRecord::kCouldNotCompare_Result;
epoger@google.com46256ea2012-05-22 13:45:35 +0000506 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000507 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000508
epoger@google.com5fd53852012-03-22 18:20:06 +0000509 ++i;
510 ++j;
511 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000512
513 if (getBounds) {
514 get_bounds(*drp);
515 }
516 SkASSERT(DiffRecord::kUnknown_Result != drp->fResult);
Mike Reed5edcd312018-08-08 11:23:41 -0400517 differences->push_back(drp);
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000518 summary->add(drp);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000519 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000520
521 for (; i < baseFiles.count(); ++i) {
522 // files only in baseDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000523 DiffRecord *drp = new DiffRecord();
524 drp->fBase.fFilename = *baseFiles[i];
525 drp->fBase.fFullPath = baseDir;
526 drp->fBase.fFullPath.append(drp->fBase.fFilename);
527 drp->fBase.fStatus = DiffResource::kExists_Status;
528
529 drp->fComparison.fFilename = *baseFiles[i];
530 drp->fComparison.fFullPath = comparisonDir;
531 drp->fComparison.fFullPath.append(drp->fComparison.fFilename);
532 drp->fComparison.fStatus = DiffResource::kDoesNotExist_Status;
533
534 drp->fResult = DiffRecord::kCouldNotCompare_Result;
535 if (getBounds) {
536 get_bounds(*drp);
537 }
Mike Reed5edcd312018-08-08 11:23:41 -0400538 differences->push_back(drp);
epoger@google.com5fd53852012-03-22 18:20:06 +0000539 summary->add(drp);
540 }
541
542 for (; j < comparisonFiles.count(); ++j) {
543 // files only in comparisonDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000544 DiffRecord *drp = new DiffRecord();
545 drp->fBase.fFilename = *comparisonFiles[j];
546 drp->fBase.fFullPath = baseDir;
547 drp->fBase.fFullPath.append(drp->fBase.fFilename);
548 drp->fBase.fStatus = DiffResource::kDoesNotExist_Status;
549
550 drp->fComparison.fFilename = *comparisonFiles[j];
551 drp->fComparison.fFullPath = comparisonDir;
552 drp->fComparison.fFullPath.append(drp->fComparison.fFilename);
553 drp->fComparison.fStatus = DiffResource::kExists_Status;
554
555 drp->fResult = DiffRecord::kCouldNotCompare_Result;
556 if (getBounds) {
557 get_bounds(*drp);
558 }
Mike Reed5edcd312018-08-08 11:23:41 -0400559 differences->push_back(drp);
epoger@google.com5fd53852012-03-22 18:20:06 +0000560 summary->add(drp);
561 }
562
563 release_file_list(&baseFiles);
564 release_file_list(&comparisonFiles);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000565}
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000566
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000567static void usage (char * argv0) {
568 SkDebugf("Skia baseline image diff tool\n");
epoger@google.coma5f406e2012-05-01 13:26:16 +0000569 SkDebugf("\n"
570"Usage: \n"
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000571" %s <baseDir> <comparisonDir> [outputDir] \n", argv0);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000572 SkDebugf(
epoger@google.com46a45962012-07-12 18:16:02 +0000573"\nArguments:"
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000574"\n --failonresult <result>: After comparing all file pairs, exit with nonzero"
575"\n return code (number of file pairs yielding this"
576"\n result) if any file pairs yielded this result."
577"\n This flag may be repeated, in which case the"
578"\n return code will be the number of fail pairs"
579"\n yielding ANY of these results."
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000580"\n --failonstatus <baseStatus> <comparisonStatus>: exit with nonzero return"
581"\n code if any file pairs yielded this status."
epoger@google.com46a45962012-07-12 18:16:02 +0000582"\n --help: display this info"
epoger@google.com46a45962012-07-12 18:16:02 +0000583"\n --listfilenames: list all filenames for each result type in stdout"
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000584"\n --match <substring>: compare files whose filenames contain this substring;"
585"\n if unspecified, compare ALL files."
586"\n this flag may be repeated."
Brian Osman9889c242018-07-17 16:45:40 -0400587"\n --nocolorspace: Ignore color space of images."
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
Mike Kleinbe28ee22017-02-06 12:46:20 -0500613int main(int argc, char** argv) {
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000614 DiffMetricProc diffProc = compute_diff_pmcolor;
epoger@google.com28060e72012-06-28 16:47:34 +0000615 int (*sortProc)(const void*, const void*) = compare<CompareDiffMetrics>;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000616
617 // Maximum error tolerated in any one color channel in any one pixel before
618 // a difference is reported.
619 int colorThreshold = 0;
620 SkString baseDir;
621 SkString comparisonDir;
622 SkString outputDir;
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000623
epoger@google.coma5f406e2012-05-01 13:26:16 +0000624 StringArray matchSubstrings;
625 StringArray nomatchSubstrings;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000626
epoger@google.coma5f406e2012-05-01 13:26:16 +0000627 bool generateDiffs = true;
epoger@google.com46a45962012-07-12 18:16:02 +0000628 bool listFilenames = false;
epoger@google.com71329d82012-08-16 13:42:13 +0000629 bool printDirNames = true;
630 bool recurseIntoSubdirs = true;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000631 bool verbose = false;
reed3a3baf62015-01-06 07:39:55 -0800632 bool listFailingBase = false;
Brian Osman9889c242018-07-17 16:45:40 -0400633 bool ignoreColorSpace = 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")) {
Mike Reed5edcd312018-08-08 11:23:41 -0400706 matchSubstrings.push_back(new SkString(argv[++i]));
epoger@google.coma5f406e2012-05-01 13:26:16 +0000707 continue;
708 }
Brian Osman9889c242018-07-17 16:45:40 -0400709 if (!strcmp(argv[i], "--nocolorspace")) {
710 ignoreColorSpace = true;
711 continue;
712 }
epoger@google.com46a45962012-07-12 18:16:02 +0000713 if (!strcmp(argv[i], "--nodiffs")) {
714 generateDiffs = false;
715 continue;
716 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000717 if (!strcmp(argv[i], "--nomatch")) {
Mike Reed5edcd312018-08-08 11:23:41 -0400718 nomatchSubstrings.push_back(new SkString(argv[++i]));
epoger@google.coma5f406e2012-05-01 13:26:16 +0000719 continue;
720 }
epoger@google.com46a45962012-07-12 18:16:02 +0000721 if (!strcmp(argv[i], "--noprintdirs")) {
epoger@google.com71329d82012-08-16 13:42:13 +0000722 printDirNames = false;
723 continue;
724 }
725 if (!strcmp(argv[i], "--norecurse")) {
726 recurseIntoSubdirs = false;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000727 continue;
728 }
tomhudson@google.com7d042802011-07-14 13:15:55 +0000729 if (!strcmp(argv[i], "--sortbymaxmismatch")) {
epoger@google.com28060e72012-06-28 16:47:34 +0000730 sortProc = compare<CompareDiffMaxMismatches>;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000731 continue;
732 }
epoger@google.com46a45962012-07-12 18:16:02 +0000733 if (!strcmp(argv[i], "--sortbymismatch")) {
734 sortProc = compare<CompareDiffMeanMismatches>;
tomhudson@google.com5b325292011-05-24 19:41:13 +0000735 continue;
736 }
epoger@google.com46a45962012-07-12 18:16:02 +0000737 if (!strcmp(argv[i], "--threshold")) {
738 colorThreshold = atoi(argv[++i]);
739 continue;
740 }
741 if (!strcmp(argv[i], "--weighted")) {
742 sortProc = compare<CompareDiffWeighted>;
keyar@chromium.orga6318192012-07-09 21:01:50 +0000743 continue;
744 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000745 if (argv[i][0] != '-') {
epoger@google.coma5f406e2012-05-01 13:26:16 +0000746 switch (numUnflaggedArguments++) {
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000747 case 0:
748 baseDir.set(argv[i]);
749 continue;
750 case 1:
751 comparisonDir.set(argv[i]);
752 continue;
753 case 2:
754 outputDir.set(argv[i]);
755 continue;
756 default:
epoger@google.coma5f406e2012-05-01 13:26:16 +0000757 SkDebugf("extra unflagged argument <%s>\n", argv[i]);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000758 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000759 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000760 }
761 }
reed3a3baf62015-01-06 07:39:55 -0800762 if (!strcmp(argv[i], "--listFailingBase")) {
763 listFailingBase = true;
764 continue;
765 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000766
767 SkDebugf("Unrecognized argument <%s>\n", argv[i]);
768 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000769 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000770 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000771
epoger@google.coma5f406e2012-05-01 13:26:16 +0000772 if (numUnflaggedArguments == 2) {
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000773 outputDir = comparisonDir;
epoger@google.coma5f406e2012-05-01 13:26:16 +0000774 } else if (numUnflaggedArguments != 3) {
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000775 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000776 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000777 }
778
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000779 if (!baseDir.endsWith(PATH_DIV_STR)) {
780 baseDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000781 }
epoger@google.com71329d82012-08-16 13:42:13 +0000782 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000783 printf("baseDir is [%s]\n", baseDir.c_str());
784 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000785
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000786 if (!comparisonDir.endsWith(PATH_DIV_STR)) {
787 comparisonDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000788 }
epoger@google.com71329d82012-08-16 13:42:13 +0000789 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000790 printf("comparisonDir is [%s]\n", comparisonDir.c_str());
791 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000792
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000793 if (!outputDir.endsWith(PATH_DIV_STR)) {
794 outputDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000795 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000796 if (generateDiffs) {
epoger@google.com71329d82012-08-16 13:42:13 +0000797 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000798 printf("writing diffs to outputDir is [%s]\n", outputDir.c_str());
799 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000800 } else {
epoger@google.com71329d82012-08-16 13:42:13 +0000801 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000802 printf("not writing any diffs to outputDir [%s]\n", outputDir.c_str());
803 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000804 outputDir.set("");
805 }
806
epoger@google.comda4af242012-06-25 18:45:50 +0000807 // If no matchSubstrings were specified, match ALL strings
808 // (except for whatever nomatchSubstrings were specified, if any).
epoger@google.coma5f406e2012-05-01 13:26:16 +0000809 if (matchSubstrings.isEmpty()) {
Mike Reed5edcd312018-08-08 11:23:41 -0400810 matchSubstrings.push_back(new SkString(""));
epoger@google.coma5f406e2012-05-01 13:26:16 +0000811 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000812
Brian Osman9889c242018-07-17 16:45:40 -0400813 create_diff_images(diffProc, colorThreshold, ignoreColorSpace, &differences,
epoger@google.coma611c3e2012-05-18 20:10:06 +0000814 baseDir, comparisonDir, outputDir,
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000815 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, generateDiffs,
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000816 verbose, &summary);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000817 summary.print(listFilenames, failOnResultType, failOnStatusType);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000818
reed3a3baf62015-01-06 07:39:55 -0800819 if (listFailingBase) {
820 summary.printfFailingBaseNames("\n");
821 }
822
tomhudson@google.com7d042802011-07-14 13:15:55 +0000823 if (differences.count()) {
reed@google.comc7a67cb2012-05-07 14:52:12 +0000824 qsort(differences.begin(), differences.count(),
825 sizeof(DiffRecord*), sortProc);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000826 }
epoger@google.com66008522012-05-16 17:40:57 +0000827
epoger@google.coma5f406e2012-05-01 13:26:16 +0000828 if (generateDiffs) {
829 print_diff_page(summary.fNumMatches, colorThreshold, differences,
830 baseDir, comparisonDir, outputDir);
831 }
epoger@google.com76222c02012-05-31 15:12:09 +0000832
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000833 for (i = 0; i < differences.count(); i++) {
834 delete differences[i];
835 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000836 matchSubstrings.deleteAll();
837 nomatchSubstrings.deleteAll();
epoger@google.combe6188d2012-05-31 15:13:45 +0000838
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000839 int num_failing_results = 0;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000840 for (int i = 0; i < DiffRecord::kResultCount; i++) {
epoger@google.com3af4ff42012-07-19 17:35:04 +0000841 if (failOnResultType[i]) {
842 num_failing_results += summary.fResultsOfType[i].count();
843 }
epoger@google.com46a45962012-07-12 18:16:02 +0000844 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000845 if (!failOnResultType[DiffRecord::kCouldNotCompare_Result]) {
846 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
847 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
848 if (failOnStatusType[base][comparison]) {
849 num_failing_results += summary.fStatusOfType[base][comparison].count();
850 }
851 }
852 }
853 }
epoger@google.com28659882012-07-16 18:01:06 +0000854
855 // On Linux (and maybe other platforms too), any results outside of the
856 // range [0...255] are wrapped (mod 256). Do the conversion ourselves, to
857 // make sure that we only return 0 when there were no failures.
858 return (num_failing_results > 255) ? 255 : num_failing_results;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000859}