blob: 56aebe7ffbdc61379665f2b7df587056050fb913 [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)) {
Mike Reedf9ecb4e2018-08-08 15:14:28 +0000186 fResultsOfType[drp->fResult].push(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 Reedf9ecb4e2018-08-08 15:14:28 +0000193 fResultsOfType[drp->fResult].push(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 Reedf9ecb4e2018-08-08 15:14:28 +0000218 fStatusOfType[drp->fBase.fStatus][drp->fComparison.fStatus].push(
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 Reedf9ecb4e2018-08-08 15:14:28 +0000277 files->push(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
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) {
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()) {
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
bungeman38d909e2016-08-02 14:40:46 -0700475 sk_sp<SkData> 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 }
bungeman38d909e2016-08-02 14:40:46 -0700479 sk_sp<SkData> 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
bungeman38d909e2016-08-02 14:40:46 -0700494 } else if (are_buffers_equal(baseFileBits.get(), comparisonFileBits.get())) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000495 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);
Brian Osman9889c242018-07-17 16:45:40 -0400499 get_bitmap(baseFileBits, drp->fBase, false, ignoreColorSpace);
500 get_bitmap(comparisonFileBits, drp->fComparison, false, ignoreColorSpace);
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);
Mike Reedf9ecb4e2018-08-08 15:14:28 +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 }
Mike Reedf9ecb4e2018-08-08 15:14:28 +0000540 differences->push(drp);
epoger@google.com5fd53852012-03-22 18:20:06 +0000541 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 }
Mike Reedf9ecb4e2018-08-08 15:14:28 +0000561 differences->push(drp);
epoger@google.com5fd53852012-03-22 18:20:06 +0000562 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."
Brian Osman9889c242018-07-17 16:45:40 -0400589"\n --nocolorspace: Ignore color space of images."
epoger@google.com46a45962012-07-12 18:16:02 +0000590"\n --nodiffs: don't write out image diffs or index.html, just generate"
591"\n report on stdout"
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000592"\n --nomatch <substring>: regardless of --match, DO NOT compare files whose"
593"\n filenames contain this substring."
594"\n this flag may be repeated."
epoger@google.com46a45962012-07-12 18:16:02 +0000595"\n --noprintdirs: do not print the directories used."
epoger@google.com71329d82012-08-16 13:42:13 +0000596"\n --norecurse: do not recurse into subdirectories."
epoger@google.com46a45962012-07-12 18:16:02 +0000597"\n --sortbymaxmismatch: sort by worst color channel mismatch;"
598"\n break ties with -sortbymismatch"
599"\n --sortbymismatch: sort by average color channel mismatch"
600"\n --threshold <n>: only report differences > n (per color channel) [default 0]"
601"\n --weighted: sort by # pixels different weighted by color difference"
602"\n"
603"\n baseDir: directory to read baseline images from."
604"\n comparisonDir: directory to read comparison images from"
605"\n outputDir: directory to write difference images and index.html to;"
606"\n defaults to comparisonDir"
607"\n"
608"\nIf no sort is specified, it will sort by fraction of pixels mismatching."
609"\n");
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000610}
611
epoger@google.com70044cc2012-07-12 18:37:55 +0000612const int kNoError = 0;
613const int kGenericError = -1;
epoger@google.com46a45962012-07-12 18:16:02 +0000614
Mike Kleinbe28ee22017-02-06 12:46:20 -0500615int 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;
Brian Osman9889c242018-07-17 16:45:40 -0400635 bool ignoreColorSpace = false;
tomhudson@google.com7d042802011-07-14 13:15:55 +0000636
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000637 RecordArray differences;
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000638 DiffSummary summary;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000639
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000640 bool failOnResultType[DiffRecord::kResultCount];
641 for (int i = 0; i < DiffRecord::kResultCount; i++) {
epoger@google.com3af4ff42012-07-19 17:35:04 +0000642 failOnResultType[i] = false;
643 }
644
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000645 bool failOnStatusType[DiffResource::kStatusCount][DiffResource::kStatusCount];
646 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
647 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
648 failOnStatusType[base][comparison] = false;
649 }
650 }
651
epoger@google.coma5f406e2012-05-01 13:26:16 +0000652 int i;
653 int numUnflaggedArguments = 0;
654 for (i = 1; i < argc; i++) {
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000655 if (!strcmp(argv[i], "--failonresult")) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000656 if (argc == ++i) {
657 SkDebugf("failonresult expects one argument.\n");
658 continue;
659 }
660 DiffRecord::Result type = DiffRecord::getResultByName(argv[i]);
661 if (type != DiffRecord::kResultCount) {
662 failOnResultType[type] = true;
663 } else {
664 SkDebugf("ignoring unrecognized result <%s>\n", argv[i]);
665 }
666 continue;
667 }
668 if (!strcmp(argv[i], "--failonstatus")) {
669 if (argc == ++i) {
670 SkDebugf("failonstatus missing base status.\n");
671 continue;
672 }
673 bool baseStatuses[DiffResource::kStatusCount];
674 if (!DiffResource::getMatchingStatuses(argv[i], baseStatuses)) {
675 SkDebugf("unrecognized base status <%s>\n", argv[i]);
676 }
677
678 if (argc == ++i) {
679 SkDebugf("failonstatus missing comparison status.\n");
680 continue;
681 }
682 bool comparisonStatuses[DiffResource::kStatusCount];
683 if (!DiffResource::getMatchingStatuses(argv[i], comparisonStatuses)) {
684 SkDebugf("unrecognized comarison status <%s>\n", argv[i]);
685 }
686
687 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
688 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
689 failOnStatusType[base][comparison] |=
690 baseStatuses[base] && comparisonStatuses[comparison];
691 }
692 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000693 continue;
694 }
epoger@google.com46a45962012-07-12 18:16:02 +0000695 if (!strcmp(argv[i], "--help")) {
696 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000697 return kNoError;
epoger@google.com46a45962012-07-12 18:16:02 +0000698 }
699 if (!strcmp(argv[i], "--listfilenames")) {
700 listFilenames = true;
epoger@google.coma5f406e2012-05-01 13:26:16 +0000701 continue;
702 }
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000703 if (!strcmp(argv[i], "--verbose")) {
704 verbose = true;
705 continue;
706 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000707 if (!strcmp(argv[i], "--match")) {
Mike Reedf9ecb4e2018-08-08 15:14:28 +0000708 matchSubstrings.push(new SkString(argv[++i]));
epoger@google.coma5f406e2012-05-01 13:26:16 +0000709 continue;
710 }
Brian Osman9889c242018-07-17 16:45:40 -0400711 if (!strcmp(argv[i], "--nocolorspace")) {
712 ignoreColorSpace = true;
713 continue;
714 }
epoger@google.com46a45962012-07-12 18:16:02 +0000715 if (!strcmp(argv[i], "--nodiffs")) {
716 generateDiffs = false;
717 continue;
718 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000719 if (!strcmp(argv[i], "--nomatch")) {
Mike Reedf9ecb4e2018-08-08 15:14:28 +0000720 nomatchSubstrings.push(new SkString(argv[++i]));
epoger@google.coma5f406e2012-05-01 13:26:16 +0000721 continue;
722 }
epoger@google.com46a45962012-07-12 18:16:02 +0000723 if (!strcmp(argv[i], "--noprintdirs")) {
epoger@google.com71329d82012-08-16 13:42:13 +0000724 printDirNames = false;
725 continue;
726 }
727 if (!strcmp(argv[i], "--norecurse")) {
728 recurseIntoSubdirs = false;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000729 continue;
730 }
tomhudson@google.com7d042802011-07-14 13:15:55 +0000731 if (!strcmp(argv[i], "--sortbymaxmismatch")) {
epoger@google.com28060e72012-06-28 16:47:34 +0000732 sortProc = compare<CompareDiffMaxMismatches>;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000733 continue;
734 }
epoger@google.com46a45962012-07-12 18:16:02 +0000735 if (!strcmp(argv[i], "--sortbymismatch")) {
736 sortProc = compare<CompareDiffMeanMismatches>;
tomhudson@google.com5b325292011-05-24 19:41:13 +0000737 continue;
738 }
epoger@google.com46a45962012-07-12 18:16:02 +0000739 if (!strcmp(argv[i], "--threshold")) {
740 colorThreshold = atoi(argv[++i]);
741 continue;
742 }
743 if (!strcmp(argv[i], "--weighted")) {
744 sortProc = compare<CompareDiffWeighted>;
keyar@chromium.orga6318192012-07-09 21:01:50 +0000745 continue;
746 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000747 if (argv[i][0] != '-') {
epoger@google.coma5f406e2012-05-01 13:26:16 +0000748 switch (numUnflaggedArguments++) {
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000749 case 0:
750 baseDir.set(argv[i]);
751 continue;
752 case 1:
753 comparisonDir.set(argv[i]);
754 continue;
755 case 2:
756 outputDir.set(argv[i]);
757 continue;
758 default:
epoger@google.coma5f406e2012-05-01 13:26:16 +0000759 SkDebugf("extra unflagged argument <%s>\n", argv[i]);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000760 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000761 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000762 }
763 }
reed3a3baf62015-01-06 07:39:55 -0800764 if (!strcmp(argv[i], "--listFailingBase")) {
765 listFailingBase = true;
766 continue;
767 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000768
769 SkDebugf("Unrecognized argument <%s>\n", argv[i]);
770 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000771 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000772 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000773
epoger@google.coma5f406e2012-05-01 13:26:16 +0000774 if (numUnflaggedArguments == 2) {
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000775 outputDir = comparisonDir;
epoger@google.coma5f406e2012-05-01 13:26:16 +0000776 } else if (numUnflaggedArguments != 3) {
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000777 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000778 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000779 }
780
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000781 if (!baseDir.endsWith(PATH_DIV_STR)) {
782 baseDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000783 }
epoger@google.com71329d82012-08-16 13:42:13 +0000784 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000785 printf("baseDir is [%s]\n", baseDir.c_str());
786 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000787
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000788 if (!comparisonDir.endsWith(PATH_DIV_STR)) {
789 comparisonDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000790 }
epoger@google.com71329d82012-08-16 13:42:13 +0000791 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000792 printf("comparisonDir is [%s]\n", comparisonDir.c_str());
793 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000794
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000795 if (!outputDir.endsWith(PATH_DIV_STR)) {
796 outputDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000797 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000798 if (generateDiffs) {
epoger@google.com71329d82012-08-16 13:42:13 +0000799 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000800 printf("writing diffs to outputDir is [%s]\n", outputDir.c_str());
801 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000802 } else {
epoger@google.com71329d82012-08-16 13:42:13 +0000803 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000804 printf("not writing any diffs to outputDir [%s]\n", outputDir.c_str());
805 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000806 outputDir.set("");
807 }
808
epoger@google.comda4af242012-06-25 18:45:50 +0000809 // If no matchSubstrings were specified, match ALL strings
810 // (except for whatever nomatchSubstrings were specified, if any).
epoger@google.coma5f406e2012-05-01 13:26:16 +0000811 if (matchSubstrings.isEmpty()) {
Mike Reedf9ecb4e2018-08-08 15:14:28 +0000812 matchSubstrings.push(new SkString(""));
epoger@google.coma5f406e2012-05-01 13:26:16 +0000813 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000814
Brian Osman9889c242018-07-17 16:45:40 -0400815 create_diff_images(diffProc, colorThreshold, ignoreColorSpace, &differences,
epoger@google.coma611c3e2012-05-18 20:10:06 +0000816 baseDir, comparisonDir, outputDir,
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000817 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, generateDiffs,
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000818 verbose, &summary);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000819 summary.print(listFilenames, failOnResultType, failOnStatusType);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000820
reed3a3baf62015-01-06 07:39:55 -0800821 if (listFailingBase) {
822 summary.printfFailingBaseNames("\n");
823 }
824
tomhudson@google.com7d042802011-07-14 13:15:55 +0000825 if (differences.count()) {
reed@google.comc7a67cb2012-05-07 14:52:12 +0000826 qsort(differences.begin(), differences.count(),
827 sizeof(DiffRecord*), sortProc);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000828 }
epoger@google.com66008522012-05-16 17:40:57 +0000829
epoger@google.coma5f406e2012-05-01 13:26:16 +0000830 if (generateDiffs) {
831 print_diff_page(summary.fNumMatches, colorThreshold, differences,
832 baseDir, comparisonDir, outputDir);
833 }
epoger@google.com76222c02012-05-31 15:12:09 +0000834
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000835 for (i = 0; i < differences.count(); i++) {
836 delete differences[i];
837 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000838 matchSubstrings.deleteAll();
839 nomatchSubstrings.deleteAll();
epoger@google.combe6188d2012-05-31 15:13:45 +0000840
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000841 int num_failing_results = 0;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000842 for (int i = 0; i < DiffRecord::kResultCount; i++) {
epoger@google.com3af4ff42012-07-19 17:35:04 +0000843 if (failOnResultType[i]) {
844 num_failing_results += summary.fResultsOfType[i].count();
845 }
epoger@google.com46a45962012-07-12 18:16:02 +0000846 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000847 if (!failOnResultType[DiffRecord::kCouldNotCompare_Result]) {
848 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
849 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
850 if (failOnStatusType[base][comparison]) {
851 num_failing_results += summary.fStatusOfType[base][comparison].count();
852 }
853 }
854 }
855 }
epoger@google.com28659882012-07-16 18:01:06 +0000856
857 // On Linux (and maybe other platforms too), any results outside of the
858 // range [0...255] are wrapped (mod 256). Do the conversion ourselves, to
859 // make sure that we only return 0 when there were no failures.
860 return (num_failing_results > 255) ? 255 : num_failing_results;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000861}