blob: 4646b1f90661515950d0a4080fee58e0efe5180f [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"
tomhudson@google.com4b33d282011-04-27 15:39:30 +000018#include "SkTSearch.h"
tomhudson@google.com4b33d282011-04-27 15:39:30 +000019
zachr@google.com6ef5a852013-06-18 21:23:31 +000020__SK_FORCE_IMAGE_DECODER_LINKING;
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();
42 const char* trimmed = strrchr(src, SkPATH_SEPARATOR);
43 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)
68 , fMaxMismatchPercent(0) { };
tomhudson@google.com9dc527b2011-06-09 15:47:10 +000069
epoger@google.com5fd53852012-03-22 18:20:06 +000070 ~DiffSummary() {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000071 for (int i = 0; i < DiffRecord::kResultCount; ++i) {
epoger@google.com76222c02012-05-31 15:12:09 +000072 fResultsOfType[i].deleteAll();
73 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000074 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
75 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
76 fStatusOfType[base][comparison].deleteAll();
77 }
skia.committer@gmail.com0264fb42012-12-06 02:01:25 +000078 }
epoger@google.com5fd53852012-03-22 18:20:06 +000079 }
80
tomhudson@google.com9dc527b2011-06-09 15:47:10 +000081 uint32_t fNumMatches;
82 uint32_t fNumMismatches;
83 uint32_t fMaxMismatchV;
84 float fMaxMismatchPercent;
85
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000086 FileArray fResultsOfType[DiffRecord::kResultCount];
87 FileArray fStatusOfType[DiffResource::kStatusCount][DiffResource::kStatusCount];
88
reed3a3baf62015-01-06 07:39:55 -080089 StringArray fFailedBaseNames[DiffRecord::kResultCount];
90
bungeman@google.come3c8ddf2012-12-05 20:13:12 +000091 void printContents(const FileArray& fileArray,
92 const char* baseStatus, const char* comparisonStatus,
93 bool listFilenames) {
94 int n = fileArray.count();
95 printf("%d file pairs %s in baseDir and %s in comparisonDir",
96 n, baseStatus, comparisonStatus);
97 if (listFilenames) {
98 printf(": ");
99 for (int i = 0; i < n; ++i) {
100 printf("%s ", fileArray[i]->c_str());
101 }
102 }
103 printf("\n");
104 }
105
106 void printStatus(bool listFilenames,
107 bool failOnStatusType[DiffResource::kStatusCount]
108 [DiffResource::kStatusCount]) {
109 typedef DiffResource::Status Status;
110
111 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
112 Status baseStatus = static_cast<Status>(base);
113 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
114 Status comparisonStatus = static_cast<Status>(comparison);
115 const FileArray& fileArray = fStatusOfType[base][comparison];
116 if (fileArray.count() > 0) {
117 if (failOnStatusType[base][comparison]) {
118 printf(" [*] ");
119 } else {
120 printf(" [_] ");
121 }
122 printContents(fileArray,
123 DiffResource::getStatusDescription(baseStatus),
124 DiffResource::getStatusDescription(comparisonStatus),
125 listFilenames);
126 }
127 }
128 }
129 }
epoger@google.com76222c02012-05-31 15:12:09 +0000130
epoger@google.com3af4ff42012-07-19 17:35:04 +0000131 // Print a line about the contents of this FileArray to stdout.
epoger@google.com46a45962012-07-12 18:16:02 +0000132 void printContents(const FileArray& fileArray, const char* headerText, bool listFilenames) {
epoger@google.com76222c02012-05-31 15:12:09 +0000133 int n = fileArray.count();
epoger@google.com3af4ff42012-07-19 17:35:04 +0000134 printf("%d file pairs %s", n, headerText);
135 if (listFilenames) {
136 printf(": ");
137 for (int i = 0; i < n; ++i) {
138 printf("%s ", fileArray[i]->c_str());
epoger@google.com76222c02012-05-31 15:12:09 +0000139 }
140 }
epoger@google.com3af4ff42012-07-19 17:35:04 +0000141 printf("\n");
epoger@google.com76222c02012-05-31 15:12:09 +0000142 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000143
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000144 void print(bool listFilenames, bool failOnResultType[DiffRecord::kResultCount],
145 bool failOnStatusType[DiffResource::kStatusCount]
146 [DiffResource::kStatusCount]) {
epoger@google.com3af4ff42012-07-19 17:35:04 +0000147 printf("\ncompared %d file pairs:\n", fNumMatches + fNumMismatches);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000148 for (int resultInt = 0; resultInt < DiffRecord::kResultCount; ++resultInt) {
149 DiffRecord::Result result = static_cast<DiffRecord::Result>(resultInt);
epoger@google.com3af4ff42012-07-19 17:35:04 +0000150 if (failOnResultType[result]) {
151 printf("[*] ");
152 } else {
153 printf("[_] ");
154 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000155 printContents(fResultsOfType[result], DiffRecord::getResultDescription(result),
156 listFilenames);
157 if (DiffRecord::kCouldNotCompare_Result == result) {
158 printStatus(listFilenames, failOnStatusType);
159 }
epoger@google.com46a45962012-07-12 18:16:02 +0000160 }
epoger@google.com3af4ff42012-07-19 17:35:04 +0000161 printf("(results marked with [*] will cause nonzero return value)\n");
162 printf("\nnumber of mismatching file pairs: %d\n", fNumMismatches);
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000163 if (fNumMismatches > 0) {
164 printf("Maximum pixel intensity mismatch %d\n", fMaxMismatchV);
epoger@google.com46a45962012-07-12 18:16:02 +0000165 printf("Largest area mismatch was %.2f%% of pixels\n",fMaxMismatchPercent);
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000166 }
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000167 }
168
reed3a3baf62015-01-06 07:39:55 -0800169 void printfFailingBaseNames(const char separator[]) {
170 for (int resultInt = 0; resultInt < DiffRecord::kResultCount; ++resultInt) {
171 const StringArray& array = fFailedBaseNames[resultInt];
172 if (array.count()) {
173 printf("%s [%d]%s", DiffRecord::ResultNames[resultInt], array.count(), separator);
174 for (int j = 0; j < array.count(); ++j) {
175 printf("%s%s", array[j]->c_str(), separator);
176 }
177 printf("\n");
178 }
179 }
180 }
181
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000182 void add (DiffRecord* drp) {
epoger@google.com46256ea2012-05-22 13:45:35 +0000183 uint32_t mismatchValue;
epoger@google.com292aff62012-05-16 14:57:28 +0000184
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000185 if (drp->fBase.fFilename.equals(drp->fComparison.fFilename)) {
186 fResultsOfType[drp->fResult].push(new SkString(drp->fBase.fFilename));
187 } else {
188 SkString* blame = new SkString("(");
189 blame->append(drp->fBase.fFilename);
190 blame->append(", ");
191 blame->append(drp->fComparison.fFilename);
192 blame->append(")");
193 fResultsOfType[drp->fResult].push(blame);
194 }
epoger@google.com292aff62012-05-16 14:57:28 +0000195 switch (drp->fResult) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000196 case DiffRecord::kEqualBits_Result:
epoger@google.com46256ea2012-05-22 13:45:35 +0000197 fNumMatches++;
198 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000199 case DiffRecord::kEqualPixels_Result:
epoger@google.com292aff62012-05-16 14:57:28 +0000200 fNumMatches++;
201 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000202 case DiffRecord::kDifferentSizes_Result:
epoger@google.com5fd53852012-03-22 18:20:06 +0000203 fNumMismatches++;
epoger@google.com292aff62012-05-16 14:57:28 +0000204 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000205 case DiffRecord::kDifferentPixels_Result:
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000206 fNumMismatches++;
207 if (drp->fFractionDifference * 100 > fMaxMismatchPercent) {
208 fMaxMismatchPercent = drp->fFractionDifference * 100;
209 }
epoger@google.com46256ea2012-05-22 13:45:35 +0000210 mismatchValue = MAX3(drp->fMaxMismatchR, drp->fMaxMismatchG,
211 drp->fMaxMismatchB);
212 if (mismatchValue > fMaxMismatchV) {
213 fMaxMismatchV = mismatchValue;
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000214 }
epoger@google.com292aff62012-05-16 14:57:28 +0000215 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000216 case DiffRecord::kCouldNotCompare_Result:
epoger@google.com46256ea2012-05-22 13:45:35 +0000217 fNumMismatches++;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000218 fStatusOfType[drp->fBase.fStatus][drp->fComparison.fStatus].push(
219 new SkString(drp->fBase.fFilename));
epoger@google.com46256ea2012-05-22 13:45:35 +0000220 break;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000221 case DiffRecord::kUnknown_Result:
epoger@google.com46256ea2012-05-22 13:45:35 +0000222 SkDEBUGFAIL("adding uncategorized DiffRecord");
223 break;
224 default:
225 SkDEBUGFAIL("adding DiffRecord with unhandled fResult value");
226 break;
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000227 }
reed3a3baf62015-01-06 07:39:55 -0800228
229 switch (drp->fResult) {
230 case DiffRecord::kEqualBits_Result:
231 case DiffRecord::kEqualPixels_Result:
232 break;
233 default:
234 add_unique_basename(&fFailedBaseNames[drp->fResult], drp->fBase.fFilename);
235 break;
236 }
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000237 }
238};
239
epoger@google.coma5f406e2012-05-01 13:26:16 +0000240/// Returns true if string contains any of these substrings.
241static bool string_contains_any_of(const SkString& string,
242 const StringArray& substrings) {
243 for (int i = 0; i < substrings.count(); i++) {
244 if (string.contains(substrings[i]->c_str())) {
245 return true;
246 }
247 }
248 return false;
249}
250
epoger@google.com71329d82012-08-16 13:42:13 +0000251/// Internal (potentially recursive) implementation of get_file_list.
252static void get_file_list_subdir(const SkString& rootDir, const SkString& subDir,
253 const StringArray& matchSubstrings,
254 const StringArray& nomatchSubstrings,
255 bool recurseIntoSubdirs, FileArray *files) {
256 bool isSubDirEmpty = subDir.isEmpty();
257 SkString dir(rootDir);
258 if (!isSubDirEmpty) {
259 dir.append(PATH_DIV_STR);
260 dir.append(subDir);
261 }
262
263 // Iterate over files (not directories) within dir.
264 SkOSFile::Iter fileIterator(dir.c_str());
265 SkString fileName;
266 while (fileIterator.next(&fileName, false)) {
267 if (fileName.startsWith(".")) {
268 continue;
269 }
270 SkString pathRelativeToRootDir(subDir);
271 if (!isSubDirEmpty) {
272 pathRelativeToRootDir.append(PATH_DIV_STR);
273 }
274 pathRelativeToRootDir.append(fileName);
275 if (string_contains_any_of(pathRelativeToRootDir, matchSubstrings) &&
276 !string_contains_any_of(pathRelativeToRootDir, nomatchSubstrings)) {
277 files->push(new SkString(pathRelativeToRootDir));
278 }
279 }
280
281 // Recurse into any non-ignored subdirectories.
282 if (recurseIntoSubdirs) {
283 SkOSFile::Iter dirIterator(dir.c_str());
284 SkString dirName;
285 while (dirIterator.next(&dirName, true)) {
286 if (dirName.startsWith(".")) {
287 continue;
288 }
289 SkString pathRelativeToRootDir(subDir);
290 if (!isSubDirEmpty) {
291 pathRelativeToRootDir.append(PATH_DIV_STR);
292 }
293 pathRelativeToRootDir.append(dirName);
294 if (!string_contains_any_of(pathRelativeToRootDir, nomatchSubstrings)) {
295 get_file_list_subdir(rootDir, pathRelativeToRootDir,
296 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
297 files);
298 }
299 }
300 }
301}
302
303/// Iterate over dir and get all files whose filename:
304/// - matches any of the substrings in matchSubstrings, but...
305/// - DOES NOT match any of the substrings in nomatchSubstrings
306/// - DOES NOT start with a dot (.)
307/// Adds the matching files to the list in *files.
epoger@google.coma5f406e2012-05-01 13:26:16 +0000308static void get_file_list(const SkString& dir,
309 const StringArray& matchSubstrings,
310 const StringArray& nomatchSubstrings,
epoger@google.com71329d82012-08-16 13:42:13 +0000311 bool recurseIntoSubdirs, FileArray *files) {
312 get_file_list_subdir(dir, SkString(""),
313 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
314 files);
epoger@google.com5fd53852012-03-22 18:20:06 +0000315}
316
317static void release_file_list(FileArray *files) {
318 files->deleteAll();
319}
320
321/// Comparison routines for qsort, sort by file names.
322static int compare_file_name_metrics(SkString **lhs, SkString **rhs) {
323 return strcmp((*lhs)->c_str(), (*rhs)->c_str());
324}
325
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000326class AutoReleasePixels {
327public:
328 AutoReleasePixels(DiffRecord* drp)
329 : fDrp(drp) {
330 SkASSERT(drp != NULL);
331 }
332 ~AutoReleasePixels() {
333 fDrp->fBase.fBitmap.setPixelRef(NULL);
334 fDrp->fComparison.fBitmap.setPixelRef(NULL);
335 fDrp->fDifference.fBitmap.setPixelRef(NULL);
336 fDrp->fWhite.fBitmap.setPixelRef(NULL);
337 }
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)) {
345 SkAutoDataUnref fileBits(read_file(resource.fFullPath.c_str()));
346 if (NULL == fileBits) {
347 SkDebugf("WARNING: couldn't read %s file <%s>\n", name, resource.fFullPath.c_str());
348 resource.fStatus = DiffResource::kCouldNotRead_Status;
349 } else {
350 get_bitmap(fileBits, resource, SkImageDecoder::kDecodeBounds_Mode);
351 }
352 }
353}
354
355static void get_bounds(DiffRecord& drp) {
356 get_bounds(drp.fBase, "base");
357 get_bounds(drp.fComparison, "comparison");
358}
359
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000360#ifdef SK_OS_WIN
361#define ANSI_COLOR_RED ""
362#define ANSI_COLOR_GREEN ""
363#define ANSI_COLOR_YELLOW ""
364#define ANSI_COLOR_RESET ""
365#else
366#define ANSI_COLOR_RED "\x1b[31m"
367#define ANSI_COLOR_GREEN "\x1b[32m"
368#define ANSI_COLOR_YELLOW "\x1b[33m"
369#define ANSI_COLOR_RESET "\x1b[0m"
370#endif
371
372#define VERBOSE_STATUS(status,color,filename) if (verbose) printf( "[ " color " %10s " ANSI_COLOR_RESET " ] %s\n", status, filename->c_str())
373
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000374/// Creates difference images, returns the number that have a 0 metric.
epoger@google.coma5f406e2012-05-01 13:26:16 +0000375/// If outputDir.isEmpty(), don't write out diff files.
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000376static void create_diff_images (DiffMetricProc dmp,
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000377 const int colorThreshold,
378 RecordArray* differences,
379 const SkString& baseDir,
380 const SkString& comparisonDir,
381 const SkString& outputDir,
epoger@google.coma5f406e2012-05-01 13:26:16 +0000382 const StringArray& matchSubstrings,
383 const StringArray& nomatchSubstrings,
epoger@google.com71329d82012-08-16 13:42:13 +0000384 bool recurseIntoSubdirs,
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000385 bool getBounds,
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000386 bool verbose,
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000387 DiffSummary* summary) {
epoger@google.coma5f406e2012-05-01 13:26:16 +0000388 SkASSERT(!baseDir.isEmpty());
389 SkASSERT(!comparisonDir.isEmpty());
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000390
epoger@google.com5fd53852012-03-22 18:20:06 +0000391 FileArray baseFiles;
392 FileArray comparisonFiles;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000393
epoger@google.com71329d82012-08-16 13:42:13 +0000394 get_file_list(baseDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, &baseFiles);
395 get_file_list(comparisonDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
epoger@google.coma5f406e2012-05-01 13:26:16 +0000396 &comparisonFiles);
epoger@google.com5fd53852012-03-22 18:20:06 +0000397
epoger@google.coma5f406e2012-05-01 13:26:16 +0000398 if (!baseFiles.isEmpty()) {
reed@google.comc7a67cb2012-05-07 14:52:12 +0000399 qsort(baseFiles.begin(), baseFiles.count(), sizeof(SkString*),
400 SkCastForQSort(compare_file_name_metrics));
epoger@google.coma5f406e2012-05-01 13:26:16 +0000401 }
402 if (!comparisonFiles.isEmpty()) {
reed@google.comc7a67cb2012-05-07 14:52:12 +0000403 qsort(comparisonFiles.begin(), comparisonFiles.count(),
404 sizeof(SkString*), SkCastForQSort(compare_file_name_metrics));
epoger@google.coma5f406e2012-05-01 13:26:16 +0000405 }
epoger@google.com66008522012-05-16 17:40:57 +0000406
epoger@google.com5fd53852012-03-22 18:20:06 +0000407 int i = 0;
408 int j = 0;
409
410 while (i < baseFiles.count() &&
411 j < comparisonFiles.count()) {
412
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000413 SkString basePath(baseDir);
414 SkString comparisonPath(comparisonDir);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000415
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000416 DiffRecord *drp = new DiffRecord;
417 int v = strcmp(baseFiles[i]->c_str(), comparisonFiles[j]->c_str());
epoger@google.com5fd53852012-03-22 18:20:06 +0000418
419 if (v < 0) {
420 // in baseDir, but not in comparisonDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000421 drp->fResult = DiffRecord::kCouldNotCompare_Result;
422
423 basePath.append(*baseFiles[i]);
424 comparisonPath.append(*baseFiles[i]);
425
426 drp->fBase.fFilename = *baseFiles[i];
427 drp->fBase.fFullPath = basePath;
428 drp->fBase.fStatus = DiffResource::kExists_Status;
429
430 drp->fComparison.fFilename = *baseFiles[i];
431 drp->fComparison.fFullPath = comparisonPath;
432 drp->fComparison.fStatus = DiffResource::kDoesNotExist_Status;
433
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000434 VERBOSE_STATUS("MISSING", ANSI_COLOR_YELLOW, baseFiles[i]);
435
epoger@google.com5fd53852012-03-22 18:20:06 +0000436 ++i;
437 } else if (v > 0) {
438 // in comparisonDir, but not in baseDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000439 drp->fResult = DiffRecord::kCouldNotCompare_Result;
440
441 basePath.append(*comparisonFiles[j]);
442 comparisonPath.append(*comparisonFiles[j]);
443
444 drp->fBase.fFilename = *comparisonFiles[j];
445 drp->fBase.fFullPath = basePath;
446 drp->fBase.fStatus = DiffResource::kDoesNotExist_Status;
447
448 drp->fComparison.fFilename = *comparisonFiles[j];
449 drp->fComparison.fFullPath = comparisonPath;
450 drp->fComparison.fStatus = DiffResource::kExists_Status;
451
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000452 VERBOSE_STATUS("MISSING", ANSI_COLOR_YELLOW, comparisonFiles[j]);
453
epoger@google.com5fd53852012-03-22 18:20:06 +0000454 ++j;
455 } else {
epoger@google.com46256ea2012-05-22 13:45:35 +0000456 // Found the same filename in both baseDir and comparisonDir.
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000457 SkASSERT(DiffRecord::kUnknown_Result == drp->fResult);
epoger@google.com5fd53852012-03-22 18:20:06 +0000458
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000459 basePath.append(*baseFiles[i]);
460 comparisonPath.append(*comparisonFiles[j]);
461
462 drp->fBase.fFilename = *baseFiles[i];
463 drp->fBase.fFullPath = basePath;
464 drp->fBase.fStatus = DiffResource::kExists_Status;
465
466 drp->fComparison.fFilename = *comparisonFiles[j];
467 drp->fComparison.fFullPath = comparisonPath;
468 drp->fComparison.fStatus = DiffResource::kExists_Status;
469
470 SkAutoDataUnref baseFileBits(read_file(drp->fBase.fFullPath.c_str()));
bsalomon49f085d2014-09-05 13:34:00 -0700471 if (baseFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000472 drp->fBase.fStatus = DiffResource::kRead_Status;
473 }
474 SkAutoDataUnref comparisonFileBits(read_file(drp->fComparison.fFullPath.c_str()));
bsalomon49f085d2014-09-05 13:34:00 -0700475 if (comparisonFileBits) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000476 drp->fComparison.fStatus = DiffResource::kRead_Status;
477 }
478 if (NULL == baseFileBits || NULL == comparisonFileBits) {
479 if (NULL == baseFileBits) {
480 drp->fBase.fStatus = DiffResource::kCouldNotRead_Status;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000481 VERBOSE_STATUS("READ FAIL", ANSI_COLOR_RED, baseFiles[i]);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000482 }
483 if (NULL == comparisonFileBits) {
484 drp->fComparison.fStatus = DiffResource::kCouldNotRead_Status;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000485 VERBOSE_STATUS("READ FAIL", ANSI_COLOR_RED, comparisonFiles[j]);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000486 }
487 drp->fResult = DiffRecord::kCouldNotCompare_Result;
488
489 } else if (are_buffers_equal(baseFileBits, comparisonFileBits)) {
490 drp->fResult = DiffRecord::kEqualBits_Result;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000491 VERBOSE_STATUS("MATCH", ANSI_COLOR_GREEN, baseFiles[i]);
epoger@google.com46256ea2012-05-22 13:45:35 +0000492 } else {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000493 AutoReleasePixels arp(drp);
494 get_bitmap(baseFileBits, drp->fBase, SkImageDecoder::kDecodePixels_Mode);
495 get_bitmap(comparisonFileBits, drp->fComparison,
496 SkImageDecoder::kDecodePixels_Mode);
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000497 VERBOSE_STATUS("DIFFERENT", ANSI_COLOR_RED, baseFiles[i]);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000498 if (DiffResource::kDecoded_Status == drp->fBase.fStatus &&
499 DiffResource::kDecoded_Status == drp->fComparison.fStatus) {
500 create_and_write_diff_image(drp, dmp, colorThreshold,
501 outputDir, drp->fBase.fFilename);
epoger@google.com46256ea2012-05-22 13:45:35 +0000502 } else {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000503 drp->fResult = DiffRecord::kCouldNotCompare_Result;
epoger@google.com46256ea2012-05-22 13:45:35 +0000504 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000505 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000506
epoger@google.com5fd53852012-03-22 18:20:06 +0000507 ++i;
508 ++j;
509 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000510
511 if (getBounds) {
512 get_bounds(*drp);
513 }
514 SkASSERT(DiffRecord::kUnknown_Result != drp->fResult);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000515 differences->push(drp);
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000516 summary->add(drp);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000517 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000518
519 for (; i < baseFiles.count(); ++i) {
520 // files only in baseDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000521 DiffRecord *drp = new DiffRecord();
522 drp->fBase.fFilename = *baseFiles[i];
523 drp->fBase.fFullPath = baseDir;
524 drp->fBase.fFullPath.append(drp->fBase.fFilename);
525 drp->fBase.fStatus = DiffResource::kExists_Status;
526
527 drp->fComparison.fFilename = *baseFiles[i];
528 drp->fComparison.fFullPath = comparisonDir;
529 drp->fComparison.fFullPath.append(drp->fComparison.fFilename);
530 drp->fComparison.fStatus = DiffResource::kDoesNotExist_Status;
531
532 drp->fResult = DiffRecord::kCouldNotCompare_Result;
533 if (getBounds) {
534 get_bounds(*drp);
535 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000536 differences->push(drp);
537 summary->add(drp);
538 }
539
540 for (; j < comparisonFiles.count(); ++j) {
541 // files only in comparisonDir
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000542 DiffRecord *drp = new DiffRecord();
543 drp->fBase.fFilename = *comparisonFiles[j];
544 drp->fBase.fFullPath = baseDir;
545 drp->fBase.fFullPath.append(drp->fBase.fFilename);
546 drp->fBase.fStatus = DiffResource::kDoesNotExist_Status;
547
548 drp->fComparison.fFilename = *comparisonFiles[j];
549 drp->fComparison.fFullPath = comparisonDir;
550 drp->fComparison.fFullPath.append(drp->fComparison.fFilename);
551 drp->fComparison.fStatus = DiffResource::kExists_Status;
552
553 drp->fResult = DiffRecord::kCouldNotCompare_Result;
554 if (getBounds) {
555 get_bounds(*drp);
556 }
epoger@google.com5fd53852012-03-22 18:20:06 +0000557 differences->push(drp);
558 summary->add(drp);
559 }
560
561 release_file_list(&baseFiles);
562 release_file_list(&comparisonFiles);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000563}
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000564
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000565static void usage (char * argv0) {
566 SkDebugf("Skia baseline image diff tool\n");
epoger@google.coma5f406e2012-05-01 13:26:16 +0000567 SkDebugf("\n"
568"Usage: \n"
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000569" %s <baseDir> <comparisonDir> [outputDir] \n", argv0);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000570 SkDebugf(
epoger@google.com46a45962012-07-12 18:16:02 +0000571"\nArguments:"
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000572"\n --failonresult <result>: After comparing all file pairs, exit with nonzero"
573"\n return code (number of file pairs yielding this"
574"\n result) if any file pairs yielded this result."
575"\n This flag may be repeated, in which case the"
576"\n return code will be the number of fail pairs"
577"\n yielding ANY of these results."
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000578"\n --failonstatus <baseStatus> <comparisonStatus>: exit with nonzero return"
579"\n code if any file pairs yielded this status."
epoger@google.com46a45962012-07-12 18:16:02 +0000580"\n --help: display this info"
epoger@google.com46a45962012-07-12 18:16:02 +0000581"\n --listfilenames: list all filenames for each result type in stdout"
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000582"\n --match <substring>: compare files whose filenames contain this substring;"
583"\n if unspecified, compare ALL files."
584"\n this flag may be repeated."
epoger@google.com46a45962012-07-12 18:16:02 +0000585"\n --nodiffs: don't write out image diffs or index.html, just generate"
586"\n report on stdout"
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000587"\n --nomatch <substring>: regardless of --match, DO NOT compare files whose"
588"\n filenames contain this substring."
589"\n this flag may be repeated."
epoger@google.com46a45962012-07-12 18:16:02 +0000590"\n --noprintdirs: do not print the directories used."
epoger@google.com71329d82012-08-16 13:42:13 +0000591"\n --norecurse: do not recurse into subdirectories."
epoger@google.com46a45962012-07-12 18:16:02 +0000592"\n --sortbymaxmismatch: sort by worst color channel mismatch;"
593"\n break ties with -sortbymismatch"
594"\n --sortbymismatch: sort by average color channel mismatch"
595"\n --threshold <n>: only report differences > n (per color channel) [default 0]"
596"\n --weighted: sort by # pixels different weighted by color difference"
597"\n"
598"\n baseDir: directory to read baseline images from."
599"\n comparisonDir: directory to read comparison images from"
600"\n outputDir: directory to write difference images and index.html to;"
601"\n defaults to comparisonDir"
602"\n"
603"\nIf no sort is specified, it will sort by fraction of pixels mismatching."
604"\n");
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000605}
606
epoger@google.com70044cc2012-07-12 18:37:55 +0000607const int kNoError = 0;
608const int kGenericError = -1;
epoger@google.com46a45962012-07-12 18:16:02 +0000609
caryclark@google.com5987f582012-10-02 18:33:14 +0000610int tool_main(int argc, char** argv);
611int tool_main(int argc, char** argv) {
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000612 DiffMetricProc diffProc = compute_diff_pmcolor;
epoger@google.com28060e72012-06-28 16:47:34 +0000613 int (*sortProc)(const void*, const void*) = compare<CompareDiffMetrics>;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000614
615 // Maximum error tolerated in any one color channel in any one pixel before
616 // a difference is reported.
617 int colorThreshold = 0;
618 SkString baseDir;
619 SkString comparisonDir;
620 SkString outputDir;
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000621
epoger@google.coma5f406e2012-05-01 13:26:16 +0000622 StringArray matchSubstrings;
623 StringArray nomatchSubstrings;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000624
epoger@google.coma5f406e2012-05-01 13:26:16 +0000625 bool generateDiffs = true;
epoger@google.com46a45962012-07-12 18:16:02 +0000626 bool listFilenames = false;
epoger@google.com71329d82012-08-16 13:42:13 +0000627 bool printDirNames = true;
628 bool recurseIntoSubdirs = true;
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000629 bool verbose = false;
reed3a3baf62015-01-06 07:39:55 -0800630 bool listFailingBase = false;
tomhudson@google.com7d042802011-07-14 13:15:55 +0000631
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000632 RecordArray differences;
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000633 DiffSummary summary;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000634
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000635 bool failOnResultType[DiffRecord::kResultCount];
636 for (int i = 0; i < DiffRecord::kResultCount; i++) {
epoger@google.com3af4ff42012-07-19 17:35:04 +0000637 failOnResultType[i] = false;
638 }
639
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000640 bool failOnStatusType[DiffResource::kStatusCount][DiffResource::kStatusCount];
641 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
642 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
643 failOnStatusType[base][comparison] = false;
644 }
645 }
646
epoger@google.coma5f406e2012-05-01 13:26:16 +0000647 int i;
648 int numUnflaggedArguments = 0;
649 for (i = 1; i < argc; i++) {
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000650 if (!strcmp(argv[i], "--failonresult")) {
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000651 if (argc == ++i) {
652 SkDebugf("failonresult expects one argument.\n");
653 continue;
654 }
655 DiffRecord::Result type = DiffRecord::getResultByName(argv[i]);
656 if (type != DiffRecord::kResultCount) {
657 failOnResultType[type] = true;
658 } else {
659 SkDebugf("ignoring unrecognized result <%s>\n", argv[i]);
660 }
661 continue;
662 }
663 if (!strcmp(argv[i], "--failonstatus")) {
664 if (argc == ++i) {
665 SkDebugf("failonstatus missing base status.\n");
666 continue;
667 }
668 bool baseStatuses[DiffResource::kStatusCount];
669 if (!DiffResource::getMatchingStatuses(argv[i], baseStatuses)) {
670 SkDebugf("unrecognized base status <%s>\n", argv[i]);
671 }
672
673 if (argc == ++i) {
674 SkDebugf("failonstatus missing comparison status.\n");
675 continue;
676 }
677 bool comparisonStatuses[DiffResource::kStatusCount];
678 if (!DiffResource::getMatchingStatuses(argv[i], comparisonStatuses)) {
679 SkDebugf("unrecognized comarison status <%s>\n", argv[i]);
680 }
681
682 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
683 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
684 failOnStatusType[base][comparison] |=
685 baseStatuses[base] && comparisonStatuses[comparison];
686 }
687 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000688 continue;
689 }
epoger@google.com46a45962012-07-12 18:16:02 +0000690 if (!strcmp(argv[i], "--help")) {
691 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000692 return kNoError;
epoger@google.com46a45962012-07-12 18:16:02 +0000693 }
694 if (!strcmp(argv[i], "--listfilenames")) {
695 listFilenames = true;
epoger@google.coma5f406e2012-05-01 13:26:16 +0000696 continue;
697 }
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000698 if (!strcmp(argv[i], "--verbose")) {
699 verbose = true;
700 continue;
701 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000702 if (!strcmp(argv[i], "--match")) {
703 matchSubstrings.push(new SkString(argv[++i]));
704 continue;
705 }
epoger@google.com46a45962012-07-12 18:16:02 +0000706 if (!strcmp(argv[i], "--nodiffs")) {
707 generateDiffs = false;
708 continue;
709 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000710 if (!strcmp(argv[i], "--nomatch")) {
711 nomatchSubstrings.push(new SkString(argv[++i]));
712 continue;
713 }
epoger@google.com46a45962012-07-12 18:16:02 +0000714 if (!strcmp(argv[i], "--noprintdirs")) {
epoger@google.com71329d82012-08-16 13:42:13 +0000715 printDirNames = false;
716 continue;
717 }
718 if (!strcmp(argv[i], "--norecurse")) {
719 recurseIntoSubdirs = false;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000720 continue;
721 }
tomhudson@google.com7d042802011-07-14 13:15:55 +0000722 if (!strcmp(argv[i], "--sortbymaxmismatch")) {
epoger@google.com28060e72012-06-28 16:47:34 +0000723 sortProc = compare<CompareDiffMaxMismatches>;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000724 continue;
725 }
epoger@google.com46a45962012-07-12 18:16:02 +0000726 if (!strcmp(argv[i], "--sortbymismatch")) {
727 sortProc = compare<CompareDiffMeanMismatches>;
tomhudson@google.com5b325292011-05-24 19:41:13 +0000728 continue;
729 }
epoger@google.com46a45962012-07-12 18:16:02 +0000730 if (!strcmp(argv[i], "--threshold")) {
731 colorThreshold = atoi(argv[++i]);
732 continue;
733 }
734 if (!strcmp(argv[i], "--weighted")) {
735 sortProc = compare<CompareDiffWeighted>;
keyar@chromium.orga6318192012-07-09 21:01:50 +0000736 continue;
737 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000738 if (argv[i][0] != '-') {
epoger@google.coma5f406e2012-05-01 13:26:16 +0000739 switch (numUnflaggedArguments++) {
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000740 case 0:
741 baseDir.set(argv[i]);
742 continue;
743 case 1:
744 comparisonDir.set(argv[i]);
745 continue;
746 case 2:
747 outputDir.set(argv[i]);
748 continue;
749 default:
epoger@google.coma5f406e2012-05-01 13:26:16 +0000750 SkDebugf("extra unflagged argument <%s>\n", argv[i]);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000751 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000752 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000753 }
754 }
reed3a3baf62015-01-06 07:39:55 -0800755 if (!strcmp(argv[i], "--listFailingBase")) {
756 listFailingBase = true;
757 continue;
758 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000759
760 SkDebugf("Unrecognized argument <%s>\n", argv[i]);
761 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000762 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000763 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000764
epoger@google.coma5f406e2012-05-01 13:26:16 +0000765 if (numUnflaggedArguments == 2) {
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000766 outputDir = comparisonDir;
epoger@google.coma5f406e2012-05-01 13:26:16 +0000767 } else if (numUnflaggedArguments != 3) {
tomhudson@google.com9dc527b2011-06-09 15:47:10 +0000768 usage(argv[0]);
epoger@google.com70044cc2012-07-12 18:37:55 +0000769 return kGenericError;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000770 }
771
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000772 if (!baseDir.endsWith(PATH_DIV_STR)) {
773 baseDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000774 }
epoger@google.com71329d82012-08-16 13:42:13 +0000775 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000776 printf("baseDir is [%s]\n", baseDir.c_str());
777 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000778
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000779 if (!comparisonDir.endsWith(PATH_DIV_STR)) {
780 comparisonDir.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("comparisonDir is [%s]\n", comparisonDir.c_str());
784 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000785
bsalomon@google.com1a315fe2011-09-23 14:56:37 +0000786 if (!outputDir.endsWith(PATH_DIV_STR)) {
787 outputDir.append(PATH_DIV_STR);
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000788 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000789 if (generateDiffs) {
epoger@google.com71329d82012-08-16 13:42:13 +0000790 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000791 printf("writing diffs to outputDir is [%s]\n", outputDir.c_str());
792 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000793 } else {
epoger@google.com71329d82012-08-16 13:42:13 +0000794 if (printDirNames) {
keyar@chromium.orga6318192012-07-09 21:01:50 +0000795 printf("not writing any diffs to outputDir [%s]\n", outputDir.c_str());
796 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000797 outputDir.set("");
798 }
799
epoger@google.comda4af242012-06-25 18:45:50 +0000800 // If no matchSubstrings were specified, match ALL strings
801 // (except for whatever nomatchSubstrings were specified, if any).
epoger@google.coma5f406e2012-05-01 13:26:16 +0000802 if (matchSubstrings.isEmpty()) {
803 matchSubstrings.push(new SkString(""));
804 }
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000805
epoger@google.coma611c3e2012-05-18 20:10:06 +0000806 create_diff_images(diffProc, colorThreshold, &differences,
807 baseDir, comparisonDir, outputDir,
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000808 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, generateDiffs,
commit-bot@chromium.org93d7bb62014-05-28 18:26:00 +0000809 verbose, &summary);
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000810 summary.print(listFilenames, failOnResultType, failOnStatusType);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000811
reed3a3baf62015-01-06 07:39:55 -0800812 if (listFailingBase) {
813 summary.printfFailingBaseNames("\n");
814 }
815
tomhudson@google.com7d042802011-07-14 13:15:55 +0000816 if (differences.count()) {
reed@google.comc7a67cb2012-05-07 14:52:12 +0000817 qsort(differences.begin(), differences.count(),
818 sizeof(DiffRecord*), sortProc);
tomhudson@google.com7d042802011-07-14 13:15:55 +0000819 }
epoger@google.com66008522012-05-16 17:40:57 +0000820
epoger@google.coma5f406e2012-05-01 13:26:16 +0000821 if (generateDiffs) {
822 print_diff_page(summary.fNumMatches, colorThreshold, differences,
823 baseDir, comparisonDir, outputDir);
824 }
epoger@google.com76222c02012-05-31 15:12:09 +0000825
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000826 for (i = 0; i < differences.count(); i++) {
827 delete differences[i];
828 }
epoger@google.coma5f406e2012-05-01 13:26:16 +0000829 matchSubstrings.deleteAll();
830 nomatchSubstrings.deleteAll();
epoger@google.combe6188d2012-05-31 15:13:45 +0000831
epoger@google.comdfbf24e2012-07-13 21:22:02 +0000832 int num_failing_results = 0;
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000833 for (int i = 0; i < DiffRecord::kResultCount; i++) {
epoger@google.com3af4ff42012-07-19 17:35:04 +0000834 if (failOnResultType[i]) {
835 num_failing_results += summary.fResultsOfType[i].count();
836 }
epoger@google.com46a45962012-07-12 18:16:02 +0000837 }
bungeman@google.come3c8ddf2012-12-05 20:13:12 +0000838 if (!failOnResultType[DiffRecord::kCouldNotCompare_Result]) {
839 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
840 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
841 if (failOnStatusType[base][comparison]) {
842 num_failing_results += summary.fStatusOfType[base][comparison].count();
843 }
844 }
845 }
846 }
epoger@google.com28659882012-07-16 18:01:06 +0000847
848 // On Linux (and maybe other platforms too), any results outside of the
849 // range [0...255] are wrapped (mod 256). Do the conversion ourselves, to
850 // make sure that we only return 0 when there were no failures.
851 return (num_failing_results > 255) ? 255 : num_failing_results;
tomhudson@google.com4b33d282011-04-27 15:39:30 +0000852}
caryclark@google.com5987f582012-10-02 18:33:14 +0000853
854#if !defined SK_BUILD_FOR_IOS
855int main(int argc, char * const argv[]) {
856 return tool_main(argc, (char**) argv);
857}
858#endif