blob: fdd83a60360faa263c9aa194ec37a832c0dcb328 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
bungeman@google.come3c8ddf2012-12-05 20:13:12 +00007#include "skdiff.h"
8#include "skdiff_html.h"
9#include "skdiff_utils.h"
10#include "SkBitmap.h"
epoger@google.com46256ea2012-05-22 13:45:35 +000011#include "SkData.h"
zachr@google.com6ef5a852013-06-18 21:23:31 +000012#include "SkForceLinking.h"
tomhudson@google.com4b33d282011-04-27 15:39:30 +000013#include "SkImageDecoder.h"
14#include "SkImageEncoder.h"
15#include "SkOSFile.h"
16#include "SkStream.h"
17#include "SkTDArray.h"
18#include "SkTemplates.h"
tomhudson@google.com4b33d282011-04-27 15:39:30 +000019#include "SkTSearch.h"
tomhudson@google.com4b33d282011-04-27 15:39:30 +000020
zachr@google.com6ef5a852013-06-18 21:23:31 +000021__SK_FORCE_IMAGE_DECODER_LINKING;
22
tomhudson@google.com4b33d282011-04-27 15:39:30 +000023/**
24 * skdiff
25 *
26 * Given three directory names, expects to find identically-named files in
27 * each of the first two; the first are treated as a set of baseline,
28 * the second a set of variant images, and a diff image is written into the
29 * third directory for each pair.
tomhudson@google.com7d042802011-07-14 13:15:55 +000030 * Creates an index.html in the current third directory to compare each
tomhudson@google.com4b33d282011-04-27 15:39:30 +000031 * pair that does not match exactly.
epoger@google.com71329d82012-08-16 13:42:13 +000032 * Recursively descends directories, unless run with --norecurse.
epoger@google.combe6188d2012-05-31 15:13:45 +000033 *
34 * Returns zero exit code if all images match across baseDir and comparisonDir.
tomhudson@google.com4b33d282011-04-27 15:39:30 +000035 */
36
epoger@google.coma5f406e2012-05-01 13:26:16 +000037typedef SkTDArray<SkString*> StringArray;
38typedef StringArray FileArray;
epoger@google.com5fd53852012-03-22 18:20:06 +000039
reed3a3baf62015-01-06 07:39:55 -080040static void add_unique_basename(StringArray* array, const SkString& filename) {
41 // trim off dirs
42 const char* src = filename.c_str();
43 const char* trimmed = strrchr(src, SkPATH_SEPARATOR);
44 if (trimmed) {
45 trimmed += 1; // skip the separator
46 } else {
47 trimmed = src;
48 }
49 const char* end = strrchr(trimmed, '.');
50 if (!end) {
51 end = trimmed + strlen(trimmed);
52 }
53 SkString result(trimmed, end - trimmed);
54
55 // only add unique entries
56 for (int i = 0; i < array->count(); ++i) {
57 if (*array->getAt(i) == result) {
58 return;
59 }
60 }
61 *array->append() = new SkString(result);
62}
63
tomhudson@google.com9dc527b2011-06-09 15:47:10 +000064struct DiffSummary {
65 DiffSummary ()
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000066 : fNumMatches(0)
67 , fNumMismatches(0)
68 , fMaxMismatchV(0)
69 , fMaxMismatchPercent(0) { };
tomhudson@google.com9dc527b2011-06-09 15:47:10 +000070
epoger@google.com5fd53852012-03-22 18:20:06 +000071 ~DiffSummary() {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000072 for (int i = 0; i < DiffRecord::kResultCount; ++i) {
epoger@google.com76222c02012-05-31 15:12:09 +000073 fResultsOfType[i].deleteAll();
74 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000075 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
76 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
77 fStatusOfType[base][comparison].deleteAll();
78 }
skia.committer@gmail.com0264fb42012-12-06 02:01:25 +000079 }
epoger@google.com5fd53852012-03-22 18:20:06 +000080 }
81
tomhudson@google.com9dc527b2011-06-09 15:47:10 +000082 uint32_t fNumMatches;
83 uint32_t fNumMismatches;
84 uint32_t fMaxMismatchV;
85 float fMaxMismatchPercent;
86
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000087 FileArray fResultsOfType[DiffRecord::kResultCount];
88 FileArray fStatusOfType[DiffResource::kStatusCount][DiffResource::kStatusCount];
89
reed3a3baf62015-01-06 07:39:55 -080090 StringArray fFailedBaseNames[DiffRecord::kResultCount];
91
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000092 void printContents(const FileArray& fileArray,
93 const char* baseStatus, const char* comparisonStatus,
94 bool listFilenames) {
95 int n = fileArray.count();
96 printf("%d file pairs %s in baseDir and %s in comparisonDir",
97 n, baseStatus, comparisonStatus);
98 if (listFilenames) {
99 printf(": ");
100 for (int i = 0; i < n; ++i) {
101 printf("%s ", fileArray[i]->c_str());
102 }
103 }
104 printf("\n");
105 }
106
107 void printStatus(bool listFilenames,
108 bool failOnStatusType[DiffResource::kStatusCount]
109 [DiffResource::kStatusCount]) {
110 typedef DiffResource::Status Status;
111
112 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
113 Status baseStatus = static_cast<Status>(base);
114 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
115 Status comparisonStatus = static_cast<Status>(comparison);
116 const FileArray& fileArray = fStatusOfType[base][comparison];
117 if (fileArray.count() > 0) {
118 if (failOnStatusType[base][comparison]) {
119 printf(" [*] ");
120 } else {
121 printf(" [_] ");
122 }
123 printContents(fileArray,
124 DiffResource::getStatusDescription(baseStatus),
125 DiffResource::getStatusDescription(comparisonStatus),
126 listFilenames);
127 }
128 }
129 }
130 }
epoger@google.com76222c02012-05-31 15:12:09 +0000131
epoger@google.com3af4ff42012-07-19 17:35:04 +0000132 // Print a line about the contents of this FileArray to stdout.
epoger@google.com46a45962012-07-12 18:16:02 +0000133 void printContents(const FileArray& fileArray, const char* headerText, bool listFilenames) {
epoger@google.com76222c02012-05-31 15:12:09 +0000134 int n = fileArray.count();
epoger@google.com3af4ff42012-07-19 17:35:04 +0000135 printf("%d file pairs %s", n, headerText);
136 if (listFilenames) {
137 printf(": ");
138 for (int i = 0; i < n; ++i) {
139 printf("%s ", fileArray[i]->c_str());
epoger@google.com76222c02012-05-31 15:12:09 +0000140 }
141 }
epoger@google.com3af4ff42012-07-19 17:35:04 +0000142 printf("\n");
epoger@google.com76222c02012-05-31 15:12:09 +0000143 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000144
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000145 void print(bool listFilenames, bool failOnResultType[DiffRecord::kResultCount],
146 bool failOnStatusType[DiffResource::kStatusCount]
147 [DiffResource::kStatusCount]) {
epoger@google.com3af4ff42012-07-19 17:35:04 +0000148 printf("\ncompared %d file pairs:\n", fNumMatches + fNumMismatches);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000149 for (int resultInt = 0; resultInt < DiffRecord::kResultCount; ++resultInt) {
150 DiffRecord::Result result = static_cast<DiffRecord::Result>(resultInt);
epoger@google.com3af4ff42012-07-19 17:35:04 +0000151 if (failOnResultType[result]) {
152 printf("[*] ");
153 } else {
154 printf("[_] ");
155 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000156 printContents(fResultsOfType[result], DiffRecord::getResultDescription(result),
157 listFilenames);
158 if (DiffRecord::kCouldNotCompare_Result == result) {
159 printStatus(listFilenames, failOnStatusType);
160 }
epoger@google.com46a45962012-07-12 18:16:02 +0000161 }
epoger@google.com3af4ff42012-07-19 17:35:04 +0000162 printf("(results marked with [*] will cause nonzero return value)\n");
163 printf("\nnumber of mismatching file pairs: %d\n", fNumMismatches);
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000164 if (fNumMismatches > 0) {
165 printf("Maximum pixel intensity mismatch %d\n", fMaxMismatchV);
epoger@google.com46a45962012-07-12 18:16:02 +0000166 printf("Largest area mismatch was %.2f%% of pixels\n",fMaxMismatchPercent);
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000167 }
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000168 }
169
reed3a3baf62015-01-06 07:39:55 -0800170 void printfFailingBaseNames(const char separator[]) {
171 for (int resultInt = 0; resultInt < DiffRecord::kResultCount; ++resultInt) {
172 const StringArray& array = fFailedBaseNames[resultInt];
173 if (array.count()) {
174 printf("%s [%d]%s", DiffRecord::ResultNames[resultInt], array.count(), separator);
175 for (int j = 0; j < array.count(); ++j) {
176 printf("%s%s", array[j]->c_str(), separator);
177 }
178 printf("\n");
179 }
180 }
181 }
182
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000183 void add (DiffRecord* drp) {
epoger@google.com46256ea2012-05-22 13:45:35 +0000184 uint32_t mismatchValue;
epoger@google.com292aff62012-05-16 14:57:28 +0000185
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000186 if (drp->fBase.fFilename.equals(drp->fComparison.fFilename)) {
187 fResultsOfType[drp->fResult].push(new SkString(drp->fBase.fFilename));
188 } else {
189 SkString* blame = new SkString("(");
190 blame->append(drp->fBase.fFilename);
191 blame->append(", ");
192 blame->append(drp->fComparison.fFilename);
193 blame->append(")");
194 fResultsOfType[drp->fResult].push(blame);
195 }
epoger@google.com292aff62012-05-16 14:57:28 +0000196 switch (drp->fResult) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000197 case DiffRecord::kEqualBits_Result:
epoger@google.com46256ea2012-05-22 13:45:35 +0000198 fNumMatches++;
199 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000200 case DiffRecord::kEqualPixels_Result:
epoger@google.com292aff62012-05-16 14:57:28 +0000201 fNumMatches++;
202 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000203 case DiffRecord::kDifferentSizes_Result:
epoger@google.com5fd53852012-03-22 18:20:06 +0000204 fNumMismatches++;
epoger@google.com292aff62012-05-16 14:57:28 +0000205 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000206 case DiffRecord::kDifferentPixels_Result:
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000207 fNumMismatches++;
208 if (drp->fFractionDifference * 100 > fMaxMismatchPercent) {
209 fMaxMismatchPercent = drp->fFractionDifference * 100;
210 }
epoger@google.com46256ea2012-05-22 13:45:35 +0000211 mismatchValue = MAX3(drp->fMaxMismatchR, drp->fMaxMismatchG,
212 drp->fMaxMismatchB);
213 if (mismatchValue > fMaxMismatchV) {
214 fMaxMismatchV = mismatchValue;
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000215 }
epoger@google.com292aff62012-05-16 14:57:28 +0000216 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000217 case DiffRecord::kCouldNotCompare_Result:
epoger@google.com46256ea2012-05-22 13:45:35 +0000218 fNumMismatches++;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000219 fStatusOfType[drp->fBase.fStatus][drp->fComparison.fStatus].push(
220 new SkString(drp->fBase.fFilename));
epoger@google.com46256ea2012-05-22 13:45:35 +0000221 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000222 case DiffRecord::kUnknown_Result:
epoger@google.com46256ea2012-05-22 13:45:35 +0000223 SkDEBUGFAIL("adding uncategorized DiffRecord");
224 break;
225 default:
226 SkDEBUGFAIL("adding DiffRecord with unhandled fResult value");
227 break;
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000228 }
reed3a3baf62015-01-06 07:39:55 -0800229
230 switch (drp->fResult) {
231 case DiffRecord::kEqualBits_Result:
232 case DiffRecord::kEqualPixels_Result:
233 break;
234 default:
235 add_unique_basename(&fFailedBaseNames[drp->fResult], drp->fBase.fFilename);
236 break;
237 }
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000238 }
239};
240
epoger@google.coma5f406e2012-05-01 13:26:16 +0000241/// Returns true if string contains any of these substrings.
242static bool string_contains_any_of(const SkString& string,
243 const StringArray& substrings) {
244 for (int i = 0; i < substrings.count(); i++) {
245 if (string.contains(substrings[i]->c_str())) {
246 return true;
247 }
248 }
249 return false;
250}
251
epoger@google.com71329d82012-08-16 13:42:13 +0000252/// Internal (potentially recursive) implementation of get_file_list.
253static void get_file_list_subdir(const SkString& rootDir, const SkString& subDir,
254 const StringArray& matchSubstrings,
255 const StringArray& nomatchSubstrings,
256 bool recurseIntoSubdirs, FileArray *files) {
257 bool isSubDirEmpty = subDir.isEmpty();
258 SkString dir(rootDir);
259 if (!isSubDirEmpty) {
260 dir.append(PATH_DIV_STR);
261 dir.append(subDir);
262 }
263
264 // Iterate over files (not directories) within dir.
265 SkOSFile::Iter fileIterator(dir.c_str());
266 SkString fileName;
267 while (fileIterator.next(&fileName, false)) {
268 if (fileName.startsWith(".")) {
269 continue;
270 }
271 SkString pathRelativeToRootDir(subDir);
272 if (!isSubDirEmpty) {
273 pathRelativeToRootDir.append(PATH_DIV_STR);
274 }
275 pathRelativeToRootDir.append(fileName);
276 if (string_contains_any_of(pathRelativeToRootDir, matchSubstrings) &&
277 !string_contains_any_of(pathRelativeToRootDir, nomatchSubstrings)) {
278 files->push(new SkString(pathRelativeToRootDir));
279 }
280 }
281
282 // Recurse into any non-ignored subdirectories.
283 if (recurseIntoSubdirs) {
284 SkOSFile::Iter dirIterator(dir.c_str());
285 SkString dirName;
286 while (dirIterator.next(&dirName, true)) {
287 if (dirName.startsWith(".")) {
288 continue;
289 }
290 SkString pathRelativeToRootDir(subDir);
291 if (!isSubDirEmpty) {
292 pathRelativeToRootDir.append(PATH_DIV_STR);
293 }
294 pathRelativeToRootDir.append(dirName);
295 if (!string_contains_any_of(pathRelativeToRootDir, nomatchSubstrings)) {
296 get_file_list_subdir(rootDir, pathRelativeToRootDir,
297 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
298 files);
299 }
300 }
301 }
302}
303
304/// Iterate over dir and get all files whose filename:
305/// - matches any of the substrings in matchSubstrings, but...
306/// - DOES NOT match any of the substrings in nomatchSubstrings
307/// - DOES NOT start with a dot (.)
308/// Adds the matching files to the list in *files.
epoger@google.coma5f406e2012-05-01 13:26:16 +0000309static void get_file_list(const SkString& dir,
310 const StringArray& matchSubstrings,
311 const StringArray& nomatchSubstrings,
epoger@google.com71329d82012-08-16 13:42:13 +0000312 bool recurseIntoSubdirs, FileArray *files) {
313 get_file_list_subdir(dir, SkString(""),
314 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
315 files);
epoger@google.com5fd53852012-03-22 18:20:06 +0000316}
317
318static void release_file_list(FileArray *files) {
319 files->deleteAll();
320}
321
322/// Comparison routines for qsort, sort by file names.
323static int compare_file_name_metrics(SkString **lhs, SkString **rhs) {
324 return strcmp((*lhs)->c_str(), (*rhs)->c_str());
325}
326
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000327class AutoReleasePixels {
328public:
329 AutoReleasePixels(DiffRecord* drp)
330 : fDrp(drp) {
331 SkASSERT(drp != NULL);
332 }
333 ~AutoReleasePixels() {
334 fDrp->fBase.fBitmap.setPixelRef(NULL);
335 fDrp->fComparison.fBitmap.setPixelRef(NULL);
336 fDrp->fDifference.fBitmap.setPixelRef(NULL);
337 fDrp->fWhite.fBitmap.setPixelRef(NULL);
338 }
339
340private:
341 DiffRecord* fDrp;
342};
343
344static void get_bounds(DiffResource& resource, const char* name) {
345 if (resource.fBitmap.empty() && !DiffResource::isStatusFailed(resource.fStatus)) {
346 SkAutoDataUnref fileBits(read_file(resource.fFullPath.c_str()));
347 if (NULL == fileBits) {
348 SkDebugf("WARNING: couldn't read %s file <%s>\n", name, resource.fFullPath.c_str());
349 resource.fStatus = DiffResource::kCouldNotRead_Status;
350 } else {
351 get_bitmap(fileBits, resource, SkImageDecoder::kDecodeBounds_Mode);
352 }
353 }
354}
355
356static void get_bounds(DiffRecord& drp) {
357 get_bounds(drp.fBase, "base");
358 get_bounds(drp.fComparison, "comparison");
359}
360
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000361#ifdef SK_OS_WIN
362#define ANSI_COLOR_RED ""
363#define ANSI_COLOR_GREEN ""
364#define ANSI_COLOR_YELLOW ""
365#define ANSI_COLOR_RESET ""
366#else
367#define ANSI_COLOR_RED "\x1b[31m"
368#define ANSI_COLOR_GREEN "\x1b[32m"
369#define ANSI_COLOR_YELLOW "\x1b[33m"
370#define ANSI_COLOR_RESET "\x1b[0m"
371#endif
372
373#define VERBOSE_STATUS(status,color,filename) if (verbose) printf( "[ " color " %10s " ANSI_COLOR_RESET " ] %s\n", status, filename->c_str())
374
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000375/// Creates difference images, returns the number that have a 0 metric.
epoger@google.coma5f406e2012-05-01 13:26:16 +0000376/// If outputDir.isEmpty(), don't write out diff files.
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000377static void create_diff_images (DiffMetricProc dmp,
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000378 const int colorThreshold,
379 RecordArray* differences,
380 const SkString& baseDir,
381 const SkString& comparisonDir,
382 const SkString& outputDir,
epoger@google.coma5f406e2012-05-01 13:26:16 +0000383 const StringArray& matchSubstrings,
384 const StringArray& nomatchSubstrings,
epoger@google.com71329d82012-08-16 13:42:13 +0000385 bool recurseIntoSubdirs,
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000386 bool getBounds,
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000387 bool verbose,
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000388 DiffSummary* summary) {
epoger@google.coma5f406e2012-05-01 13:26:16 +0000389 SkASSERT(!baseDir.isEmpty());
390 SkASSERT(!comparisonDir.isEmpty());
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000391
epoger@google.com5fd53852012-03-22 18:20:06 +0000392 FileArray baseFiles;
393 FileArray comparisonFiles;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000394
epoger@google.com71329d82012-08-16 13:42:13 +0000395 get_file_list(baseDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, &baseFiles);
396 get_file_list(comparisonDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
epoger@google.coma5f406e2012-05-01 13:26:16 +0000397 &comparisonFiles);
epoger@google.com5fd53852012-03-22 18:20:06 +0000398
epoger@google.coma5f406e2012-05-01 13:26:16 +0000399 if (!baseFiles.isEmpty()) {
reed@google.comc7a67cb2012-05-07 14:52:12 +0000400 qsort(baseFiles.begin(), baseFiles.count(), sizeof(SkString*),
401 SkCastForQSort(compare_file_name_metrics));
epoger@google.coma5f406e2012-05-01 13:26:16 +0000402 }
403 if (!comparisonFiles.isEmpty()) {
reed@google.comc7a67cb2012-05-07 14:52:12 +0000404 qsort(comparisonFiles.begin(), comparisonFiles.count(),
405 sizeof(SkString*), SkCastForQSort(compare_file_name_metrics));
epoger@google.coma5f406e2012-05-01 13:26:16 +0000406 }
epoger@google.com66008522012-05-16 17:40:57 +0000407
epoger@google.com5fd53852012-03-22 18:20:06 +0000408 int i = 0;
409 int j = 0;
410
411 while (i < baseFiles.count() &&
412 j < comparisonFiles.count()) {
413
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000414 SkString basePath(baseDir);
415 SkString comparisonPath(comparisonDir);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000416
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000417 DiffRecord *drp = new DiffRecord;
418 int v = strcmp(baseFiles[i]->c_str(), comparisonFiles[j]->c_str());
epoger@google.com5fd53852012-03-22 18:20:06 +0000419
420 if (v < 0) {
421 // in baseDir, but not in comparisonDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000422 drp->fResult = DiffRecord::kCouldNotCompare_Result;
423
424 basePath.append(*baseFiles[i]);
425 comparisonPath.append(*baseFiles[i]);
426
427 drp->fBase.fFilename = *baseFiles[i];
428 drp->fBase.fFullPath = basePath;
429 drp->fBase.fStatus = DiffResource::kExists_Status;
430
431 drp->fComparison.fFilename = *baseFiles[i];
432 drp->fComparison.fFullPath = comparisonPath;
433 drp->fComparison.fStatus = DiffResource::kDoesNotExist_Status;
434
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000435 VERBOSE_STATUS("MISSING", ANSI_COLOR_YELLOW, baseFiles[i]);
436
epoger@google.com5fd53852012-03-22 18:20:06 +0000437 ++i;
438 } else if (v > 0) {
439 // in comparisonDir, but not in baseDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000440 drp->fResult = DiffRecord::kCouldNotCompare_Result;
441
442 basePath.append(*comparisonFiles[j]);
443 comparisonPath.append(*comparisonFiles[j]);
444
445 drp->fBase.fFilename = *comparisonFiles[j];
446 drp->fBase.fFullPath = basePath;
447 drp->fBase.fStatus = DiffResource::kDoesNotExist_Status;
448
449 drp->fComparison.fFilename = *comparisonFiles[j];
450 drp->fComparison.fFullPath = comparisonPath;
451 drp->fComparison.fStatus = DiffResource::kExists_Status;
452
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000453 VERBOSE_STATUS("MISSING", ANSI_COLOR_YELLOW, comparisonFiles[j]);
454
epoger@google.com5fd53852012-03-22 18:20:06 +0000455 ++j;
456 } else {
epoger@google.com46256ea2012-05-22 13:45:35 +0000457 // Found the same filename in both baseDir and comparisonDir.
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000458 SkASSERT(DiffRecord::kUnknown_Result == drp->fResult);
epoger@google.com5fd53852012-03-22 18:20:06 +0000459
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000460 basePath.append(*baseFiles[i]);
461 comparisonPath.append(*comparisonFiles[j]);
462
463 drp->fBase.fFilename = *baseFiles[i];
464 drp->fBase.fFullPath = basePath;
465 drp->fBase.fStatus = DiffResource::kExists_Status;
466
467 drp->fComparison.fFilename = *comparisonFiles[j];
468 drp->fComparison.fFullPath = comparisonPath;
469 drp->fComparison.fStatus = DiffResource::kExists_Status;
470
471 SkAutoDataUnref baseFileBits(read_file(drp->fBase.fFullPath.c_str()));
bsalomon49f085d2014-09-05 13:34:00 -0700472 if (baseFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000473 drp->fBase.fStatus = DiffResource::kRead_Status;
474 }
475 SkAutoDataUnref comparisonFileBits(read_file(drp->fComparison.fFullPath.c_str()));
bsalomon49f085d2014-09-05 13:34:00 -0700476 if (comparisonFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000477 drp->fComparison.fStatus = DiffResource::kRead_Status;
478 }
479 if (NULL == baseFileBits || NULL == comparisonFileBits) {
480 if (NULL == baseFileBits) {
481 drp->fBase.fStatus = DiffResource::kCouldNotRead_Status;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000482 VERBOSE_STATUS("READ FAIL", ANSI_COLOR_RED, baseFiles[i]);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000483 }
484 if (NULL == comparisonFileBits) {
485 drp->fComparison.fStatus = DiffResource::kCouldNotRead_Status;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000486 VERBOSE_STATUS("READ FAIL", ANSI_COLOR_RED, comparisonFiles[j]);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000487 }
488 drp->fResult = DiffRecord::kCouldNotCompare_Result;
489
490 } else if (are_buffers_equal(baseFileBits, comparisonFileBits)) {
491 drp->fResult = DiffRecord::kEqualBits_Result;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000492 VERBOSE_STATUS("MATCH", ANSI_COLOR_GREEN, baseFiles[i]);
epoger@google.com46256ea2012-05-22 13:45:35 +0000493 } else {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000494 AutoReleasePixels arp(drp);
495 get_bitmap(baseFileBits, drp->fBase, SkImageDecoder::kDecodePixels_Mode);
496 get_bitmap(comparisonFileBits, drp->fComparison,
497 SkImageDecoder::kDecodePixels_Mode);
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000498 VERBOSE_STATUS("DIFFERENT", ANSI_COLOR_RED, baseFiles[i]);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000499 if (DiffResource::kDecoded_Status == drp->fBase.fStatus &&
500 DiffResource::kDecoded_Status == drp->fComparison.fStatus) {
501 create_and_write_diff_image(drp, dmp, colorThreshold,
502 outputDir, drp->fBase.fFilename);
epoger@google.com46256ea2012-05-22 13:45:35 +0000503 } else {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000504 drp->fResult = DiffRecord::kCouldNotCompare_Result;
epoger@google.com46256ea2012-05-22 13:45:35 +0000505 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000506 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000507
epoger@google.com5fd53852012-03-22 18:20:06 +0000508 ++i;
509 ++j;
510 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000511
512 if (getBounds) {
513 get_bounds(*drp);
514 }
515 SkASSERT(DiffRecord::kUnknown_Result != drp->fResult);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000516 differences->push(drp);
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000517 summary->add(drp);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000518 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000519
520 for (; i < baseFiles.count(); ++i) {
521 // files only in baseDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000522 DiffRecord *drp = new DiffRecord();
523 drp->fBase.fFilename = *baseFiles[i];
524 drp->fBase.fFullPath = baseDir;
525 drp->fBase.fFullPath.append(drp->fBase.fFilename);
526 drp->fBase.fStatus = DiffResource::kExists_Status;
527
528 drp->fComparison.fFilename = *baseFiles[i];
529 drp->fComparison.fFullPath = comparisonDir;
530 drp->fComparison.fFullPath.append(drp->fComparison.fFilename);
531 drp->fComparison.fStatus = DiffResource::kDoesNotExist_Status;
532
533 drp->fResult = DiffRecord::kCouldNotCompare_Result;
534 if (getBounds) {
535 get_bounds(*drp);
536 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000537 differences->push(drp);
538 summary->add(drp);
539 }
540
541 for (; j < comparisonFiles.count(); ++j) {
542 // files only in comparisonDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000543 DiffRecord *drp = new DiffRecord();
544 drp->fBase.fFilename = *comparisonFiles[j];
545 drp->fBase.fFullPath = baseDir;
546 drp->fBase.fFullPath.append(drp->fBase.fFilename);
547 drp->fBase.fStatus = DiffResource::kDoesNotExist_Status;
548
549 drp->fComparison.fFilename = *comparisonFiles[j];
550 drp->fComparison.fFullPath = comparisonDir;
551 drp->fComparison.fFullPath.append(drp->fComparison.fFilename);
552 drp->fComparison.fStatus = DiffResource::kExists_Status;
553
554 drp->fResult = DiffRecord::kCouldNotCompare_Result;
555 if (getBounds) {
556 get_bounds(*drp);
557 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000558 differences->push(drp);
559 summary->add(drp);
560 }
561
562 release_file_list(&baseFiles);
563 release_file_list(&comparisonFiles);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000564}
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000565
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000566static void usage (char * argv0) {
567 SkDebugf("Skia baseline image diff tool\n");
epoger@google.coma5f406e2012-05-01 13:26:16 +0000568 SkDebugf("\n"
569"Usage: \n"
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000570" %s <baseDir> <comparisonDir> [outputDir] \n", argv0);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000571 SkDebugf(
epoger@google.com46a45962012-07-12 18:16:02 +0000572"\nArguments:"
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000573"\n --failonresult <result>: After comparing all file pairs, exit with nonzero"
574"\n return code (number of file pairs yielding this"
575"\n result) if any file pairs yielded this result."
576"\n This flag may be repeated, in which case the"
577"\n return code will be the number of fail pairs"
578"\n yielding ANY of these results."
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000579"\n --failonstatus <baseStatus> <comparisonStatus>: exit with nonzero return"
580"\n code if any file pairs yielded this status."
epoger@google.com46a45962012-07-12 18:16:02 +0000581"\n --help: display this info"
epoger@google.com46a45962012-07-12 18:16:02 +0000582"\n --listfilenames: list all filenames for each result type in stdout"
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000583"\n --match <substring>: compare files whose filenames contain this substring;"
584"\n if unspecified, compare ALL files."
585"\n this flag may be repeated."
epoger@google.com46a45962012-07-12 18:16:02 +0000586"\n --nodiffs: don't write out image diffs or index.html, just generate"
587"\n report on stdout"
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000588"\n --nomatch <substring>: regardless of --match, DO NOT compare files whose"
589"\n filenames contain this substring."
590"\n this flag may be repeated."
epoger@google.com46a45962012-07-12 18:16:02 +0000591"\n --noprintdirs: do not print the directories used."
epoger@google.com71329d82012-08-16 13:42:13 +0000592"\n --norecurse: do not recurse into subdirectories."
epoger@google.com46a45962012-07-12 18:16:02 +0000593"\n --sortbymaxmismatch: sort by worst color channel mismatch;"
594"\n break ties with -sortbymismatch"
595"\n --sortbymismatch: sort by average color channel mismatch"
596"\n --threshold <n>: only report differences > n (per color channel) [default 0]"
597"\n --weighted: sort by # pixels different weighted by color difference"
598"\n"
599"\n baseDir: directory to read baseline images from."
600"\n comparisonDir: directory to read comparison images from"
601"\n outputDir: directory to write difference images and index.html to;"
602"\n defaults to comparisonDir"
603"\n"
604"\nIf no sort is specified, it will sort by fraction of pixels mismatching."
605"\n");
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000606}
607
epoger@google.com70044cc2012-07-12 18:37:55 +0000608const int kNoError = 0;
609const int kGenericError = -1;
epoger@google.com46a45962012-07-12 18:16:02 +0000610
caryclark@google.com5987f582012-10-02 18:33:14 +0000611int tool_main(int argc, char** argv);
612int tool_main(int argc, char** argv) {
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000613 DiffMetricProc diffProc = compute_diff_pmcolor;
epoger@google.com28060e72012-06-28 16:47:34 +0000614 int (*sortProc)(const void*, const void*) = compare<CompareDiffMetrics>;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000615
616 // Maximum error tolerated in any one color channel in any one pixel before
617 // a difference is reported.
618 int colorThreshold = 0;
619 SkString baseDir;
620 SkString comparisonDir;
621 SkString outputDir;
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000622
epoger@google.coma5f406e2012-05-01 13:26:16 +0000623 StringArray matchSubstrings;
624 StringArray nomatchSubstrings;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000625
epoger@google.coma5f406e2012-05-01 13:26:16 +0000626 bool generateDiffs = true;
epoger@google.com46a45962012-07-12 18:16:02 +0000627 bool listFilenames = false;
epoger@google.com71329d82012-08-16 13:42:13 +0000628 bool printDirNames = true;
629 bool recurseIntoSubdirs = true;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000630 bool verbose = false;
reed3a3baf62015-01-06 07:39:55 -0800631 bool listFailingBase = false;
tomhudson@google.com7d042802011-07-14 13:15:55 +0000632
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000633 RecordArray differences;
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000634 DiffSummary summary;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000635
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000636 bool failOnResultType[DiffRecord::kResultCount];
637 for (int i = 0; i < DiffRecord::kResultCount; i++) {
epoger@google.com3af4ff42012-07-19 17:35:04 +0000638 failOnResultType[i] = false;
639 }
640
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000641 bool failOnStatusType[DiffResource::kStatusCount][DiffResource::kStatusCount];
642 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
643 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
644 failOnStatusType[base][comparison] = false;
645 }
646 }
647
epoger@google.coma5f406e2012-05-01 13:26:16 +0000648 int i;
649 int numUnflaggedArguments = 0;
650 for (i = 1; i < argc; i++) {
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000651 if (!strcmp(argv[i], "--failonresult")) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000652 if (argc == ++i) {
653 SkDebugf("failonresult expects one argument.\n");
654 continue;
655 }
656 DiffRecord::Result type = DiffRecord::getResultByName(argv[i]);
657 if (type != DiffRecord::kResultCount) {
658 failOnResultType[type] = true;
659 } else {
660 SkDebugf("ignoring unrecognized result <%s>\n", argv[i]);
661 }
662 continue;
663 }
664 if (!strcmp(argv[i], "--failonstatus")) {
665 if (argc == ++i) {
666 SkDebugf("failonstatus missing base status.\n");
667 continue;
668 }
669 bool baseStatuses[DiffResource::kStatusCount];
670 if (!DiffResource::getMatchingStatuses(argv[i], baseStatuses)) {
671 SkDebugf("unrecognized base status <%s>\n", argv[i]);
672 }
673
674 if (argc == ++i) {
675 SkDebugf("failonstatus missing comparison status.\n");
676 continue;
677 }
678 bool comparisonStatuses[DiffResource::kStatusCount];
679 if (!DiffResource::getMatchingStatuses(argv[i], comparisonStatuses)) {
680 SkDebugf("unrecognized comarison status <%s>\n", argv[i]);
681 }
682
683 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
684 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
685 failOnStatusType[base][comparison] |=
686 baseStatuses[base] && comparisonStatuses[comparison];
687 }
688 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000689 continue;
690 }
epoger@google.com46a45962012-07-12 18:16:02 +0000691 if (!strcmp(argv[i], "--help")) {
692 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000693 return kNoError;
epoger@google.com46a45962012-07-12 18:16:02 +0000694 }
695 if (!strcmp(argv[i], "--listfilenames")) {
696 listFilenames = true;
epoger@google.coma5f406e2012-05-01 13:26:16 +0000697 continue;
698 }
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000699 if (!strcmp(argv[i], "--verbose")) {
700 verbose = true;
701 continue;
702 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000703 if (!strcmp(argv[i], "--match")) {
704 matchSubstrings.push(new SkString(argv[++i]));
705 continue;
706 }
epoger@google.com46a45962012-07-12 18:16:02 +0000707 if (!strcmp(argv[i], "--nodiffs")) {
708 generateDiffs = false;
709 continue;
710 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000711 if (!strcmp(argv[i], "--nomatch")) {
712 nomatchSubstrings.push(new SkString(argv[++i]));
713 continue;
714 }
epoger@google.com46a45962012-07-12 18:16:02 +0000715 if (!strcmp(argv[i], "--noprintdirs")) {
epoger@google.com71329d82012-08-16 13:42:13 +0000716 printDirNames = false;
717 continue;
718 }
719 if (!strcmp(argv[i], "--norecurse")) {
720 recurseIntoSubdirs = false;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000721 continue;
722 }
tomhudson@google.com7d042802011-07-14 13:15:55 +0000723 if (!strcmp(argv[i], "--sortbymaxmismatch")) {
epoger@google.com28060e72012-06-28 16:47:34 +0000724 sortProc = compare<CompareDiffMaxMismatches>;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000725 continue;
726 }
epoger@google.com46a45962012-07-12 18:16:02 +0000727 if (!strcmp(argv[i], "--sortbymismatch")) {
728 sortProc = compare<CompareDiffMeanMismatches>;
tomhudson@google.com5b325292011-05-24 19:41:13 +0000729 continue;
730 }
epoger@google.com46a45962012-07-12 18:16:02 +0000731 if (!strcmp(argv[i], "--threshold")) {
732 colorThreshold = atoi(argv[++i]);
733 continue;
734 }
735 if (!strcmp(argv[i], "--weighted")) {
736 sortProc = compare<CompareDiffWeighted>;
keyar@chromium.orga6318192012-07-09 21:01:50 +0000737 continue;
738 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000739 if (argv[i][0] != '-') {
epoger@google.coma5f406e2012-05-01 13:26:16 +0000740 switch (numUnflaggedArguments++) {
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000741 case 0:
742 baseDir.set(argv[i]);
743 continue;
744 case 1:
745 comparisonDir.set(argv[i]);
746 continue;
747 case 2:
748 outputDir.set(argv[i]);
749 continue;
750 default:
epoger@google.coma5f406e2012-05-01 13:26:16 +0000751 SkDebugf("extra unflagged argument <%s>\n", argv[i]);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000752 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000753 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000754 }
755 }
reed3a3baf62015-01-06 07:39:55 -0800756 if (!strcmp(argv[i], "--listFailingBase")) {
757 listFailingBase = true;
758 continue;
759 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000760
761 SkDebugf("Unrecognized argument <%s>\n", argv[i]);
762 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000763 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000764 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000765
epoger@google.coma5f406e2012-05-01 13:26:16 +0000766 if (numUnflaggedArguments == 2) {
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000767 outputDir = comparisonDir;
epoger@google.coma5f406e2012-05-01 13:26:16 +0000768 } else if (numUnflaggedArguments != 3) {
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000769 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000770 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000771 }
772
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000773 if (!baseDir.endsWith(PATH_DIV_STR)) {
774 baseDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000775 }
epoger@google.com71329d82012-08-16 13:42:13 +0000776 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000777 printf("baseDir is [%s]\n", baseDir.c_str());
778 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000779
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000780 if (!comparisonDir.endsWith(PATH_DIV_STR)) {
781 comparisonDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000782 }
epoger@google.com71329d82012-08-16 13:42:13 +0000783 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000784 printf("comparisonDir is [%s]\n", comparisonDir.c_str());
785 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000786
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000787 if (!outputDir.endsWith(PATH_DIV_STR)) {
788 outputDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000789 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000790 if (generateDiffs) {
epoger@google.com71329d82012-08-16 13:42:13 +0000791 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000792 printf("writing diffs to outputDir is [%s]\n", outputDir.c_str());
793 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000794 } else {
epoger@google.com71329d82012-08-16 13:42:13 +0000795 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000796 printf("not writing any diffs to outputDir [%s]\n", outputDir.c_str());
797 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000798 outputDir.set("");
799 }
800
epoger@google.comda4af242012-06-25 18:45:50 +0000801 // If no matchSubstrings were specified, match ALL strings
802 // (except for whatever nomatchSubstrings were specified, if any).
epoger@google.coma5f406e2012-05-01 13:26:16 +0000803 if (matchSubstrings.isEmpty()) {
804 matchSubstrings.push(new SkString(""));
805 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000806
epoger@google.coma611c3e2012-05-18 20:10:06 +0000807 create_diff_images(diffProc, colorThreshold, &differences,
808 baseDir, comparisonDir, outputDir,
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000809 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, generateDiffs,
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000810 verbose, &summary);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000811 summary.print(listFilenames, failOnResultType, failOnStatusType);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000812
reed3a3baf62015-01-06 07:39:55 -0800813 if (listFailingBase) {
814 summary.printfFailingBaseNames("\n");
815 }
816
tomhudson@google.com7d042802011-07-14 13:15:55 +0000817 if (differences.count()) {
reed@google.comc7a67cb2012-05-07 14:52:12 +0000818 qsort(differences.begin(), differences.count(),
819 sizeof(DiffRecord*), sortProc);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000820 }
epoger@google.com66008522012-05-16 17:40:57 +0000821
epoger@google.coma5f406e2012-05-01 13:26:16 +0000822 if (generateDiffs) {
823 print_diff_page(summary.fNumMatches, colorThreshold, differences,
824 baseDir, comparisonDir, outputDir);
825 }
epoger@google.com76222c02012-05-31 15:12:09 +0000826
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000827 for (i = 0; i < differences.count(); i++) {
828 delete differences[i];
829 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000830 matchSubstrings.deleteAll();
831 nomatchSubstrings.deleteAll();
epoger@google.combe6188d2012-05-31 15:13:45 +0000832
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000833 int num_failing_results = 0;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000834 for (int i = 0; i < DiffRecord::kResultCount; i++) {
epoger@google.com3af4ff42012-07-19 17:35:04 +0000835 if (failOnResultType[i]) {
836 num_failing_results += summary.fResultsOfType[i].count();
837 }
epoger@google.com46a45962012-07-12 18:16:02 +0000838 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000839 if (!failOnResultType[DiffRecord::kCouldNotCompare_Result]) {
840 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
841 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
842 if (failOnStatusType[base][comparison]) {
843 num_failing_results += summary.fStatusOfType[base][comparison].count();
844 }
845 }
846 }
847 }
epoger@google.com28659882012-07-16 18:01:06 +0000848
849 // On Linux (and maybe other platforms too), any results outside of the
850 // range [0...255] are wrapped (mod 256). Do the conversion ourselves, to
851 // make sure that we only return 0 when there were no failures.
852 return (num_failing_results > 255) ? 255 : num_failing_results;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000853}
caryclark@google.com5987f582012-10-02 18:33:14 +0000854
855#if !defined SK_BUILD_FOR_IOS
856int main(int argc, char * const argv[]) {
857 return tool_main(argc, (char**) argv);
858}
859#endif