Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
skdiff_main.cpp
Go to the documentation of this file.
1/*
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 */
12#include "src/base/SkTSearch.h"
13#include "src/core/SkOSFile.h"
14#include "src/utils/SkOSPath.h"
15#include "tools/skdiff/skdiff.h"
18
19#include <stdlib.h>
20
21using namespace skia_private;
22
23/**
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.
30 * Creates an index.html in the current third directory to compare each
31 * pair that does not match exactly.
32 * Recursively descends directories, unless run with --norecurse.
33 *
34 * Returns zero exit code if all images match across baseDir and comparisonDir.
35 */
36
39
40static 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, SkOSPath::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->size(); ++i) {
57 if (array->at(i) == result) {
58 return;
59 }
60 }
61 array->push_back(std::move(result));
62}
63
70
71 uint32_t fNumMatches;
73 uint32_t fMaxMismatchV;
75
78
80
81 void printContents(const FileArray& fileArray,
82 const char* baseStatus, const char* comparisonStatus,
83 bool listFilenames) {
84 int n = fileArray.size();
85 printf("%d file pairs %s in baseDir and %s in comparisonDir",
86 n, baseStatus, comparisonStatus);
87 if (listFilenames) {
88 printf(": ");
89 for (int i = 0; i < n; ++i) {
90 printf("%s ", fileArray[i].c_str());
91 }
92 }
93 printf("\n");
94 }
95
96 void printStatus(bool listFilenames,
97 bool failOnStatusType[DiffResource::kStatusCount]
99 typedef DiffResource::Status Status;
100
101 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
102 Status baseStatus = static_cast<Status>(base);
103 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
104 Status comparisonStatus = static_cast<Status>(comparison);
105 const FileArray& fileArray = fStatusOfType[base][comparison];
106 if (fileArray.size() > 0) {
107 if (failOnStatusType[base][comparison]) {
108 printf(" [*] ");
109 } else {
110 printf(" [_] ");
111 }
112 printContents(fileArray,
114 DiffResource::getStatusDescription(comparisonStatus),
115 listFilenames);
116 }
117 }
118 }
119 }
120
121 // Print a line about the contents of this FileArray to stdout.
122 void printContents(const FileArray& fileArray, const char* headerText, bool listFilenames) {
123 int n = fileArray.size();
124 printf("%d file pairs %s", n, headerText);
125 if (listFilenames) {
126 printf(": ");
127 for (int i = 0; i < n; ++i) {
128 printf("%s ", fileArray[i].c_str());
129 }
130 }
131 printf("\n");
132 }
133
134 void print(bool listFilenames, bool failOnResultType[DiffRecord::kResultCount],
135 bool failOnStatusType[DiffResource::kStatusCount]
137 printf("\ncompared %u file pairs:\n", fNumMatches + fNumMismatches);
138 for (int resultInt = 0; resultInt < DiffRecord::kResultCount; ++resultInt) {
139 DiffRecord::Result result = static_cast<DiffRecord::Result>(resultInt);
140 if (failOnResultType[result]) {
141 printf("[*] ");
142 } else {
143 printf("[_] ");
144 }
146 listFilenames);
148 printStatus(listFilenames, failOnStatusType);
149 }
150 }
151 printf("(results marked with [*] will cause nonzero return value)\n");
152 printf("\nnumber of mismatching file pairs: %u\n", fNumMismatches);
153 if (fNumMismatches > 0) {
154 printf("Maximum pixel intensity mismatch %u\n", fMaxMismatchV);
155 printf("Largest area mismatch was %.2f%% of pixels\n",fMaxMismatchPercent);
156 }
157 }
158
159 void printfFailingBaseNames(const char separator[]) {
160 for (int resultInt = 0; resultInt < DiffRecord::kResultCount; ++resultInt) {
161 const StringArray& array = fFailedBaseNames[resultInt];
162 if (array.size()) {
163 printf("%s [%d]%s", DiffRecord::ResultNames[resultInt], array.size(), separator);
164 for (int j = 0; j < array.size(); ++j) {
165 printf("%s%s", array[j].c_str(), separator);
166 }
167 printf("\n");
168 }
169 }
170 }
171
172 void add (const DiffRecord& drp) {
173 uint32_t mismatchValue;
174
177 } else {
178 SkString blame("(");
179 blame.append(drp.fBase.fFilename);
180 blame.append(", ");
181 blame.append(drp.fComparison.fFilename);
182 blame.append(")");
183 fResultsOfType[drp.fResult].push_back(std::move(blame));
184 }
185 switch (drp.fResult) {
187 fNumMatches++;
188 break;
190 fNumMatches++;
191 break;
194 break;
199 }
200 mismatchValue = MAX3(drp.fMaxMismatchR, drp.fMaxMismatchG,
201 drp.fMaxMismatchB);
202 if (mismatchValue > fMaxMismatchV) {
203 fMaxMismatchV = mismatchValue;
204 }
205 break;
209 drp.fBase.fFilename);
210 break;
212 SkDEBUGFAIL("adding uncategorized DiffRecord");
213 break;
214 default:
215 SkDEBUGFAIL("adding DiffRecord with unhandled fResult value");
216 break;
217 }
218
219 switch (drp.fResult) {
222 break;
223 default:
225 break;
226 }
227 }
228};
229
230/// Returns true if string contains any of these substrings.
231static bool string_contains_any_of(const SkString& string,
232 const StringArray& substrings) {
233 for (int i = 0; i < substrings.size(); i++) {
234 if (string.contains(substrings[i].c_str())) {
235 return true;
236 }
237 }
238 return false;
239}
240
241/// Internal (potentially recursive) implementation of get_file_list.
242static void get_file_list_subdir(const SkString& rootDir, const SkString& subDir,
243 const StringArray& matchSubstrings,
244 const StringArray& nomatchSubstrings,
245 bool recurseIntoSubdirs, FileArray *files) {
246 bool isSubDirEmpty = subDir.isEmpty();
247 SkString dir(rootDir);
248 if (!isSubDirEmpty) {
249 dir.append(PATH_DIV_STR);
250 dir.append(subDir);
251 }
252
253 // Iterate over files (not directories) within dir.
254 SkOSFile::Iter fileIterator(dir.c_str());
255 SkString fileName;
256 while (fileIterator.next(&fileName, false)) {
257 if (fileName.startsWith(".")) {
258 continue;
259 }
260 SkString pathRelativeToRootDir(subDir);
261 if (!isSubDirEmpty) {
262 pathRelativeToRootDir.append(PATH_DIV_STR);
263 }
264 pathRelativeToRootDir.append(fileName);
265 if (string_contains_any_of(pathRelativeToRootDir, matchSubstrings) &&
266 !string_contains_any_of(pathRelativeToRootDir, nomatchSubstrings)) {
267 files->push_back(std::move(pathRelativeToRootDir));
268 }
269 }
270
271 // Recurse into any non-ignored subdirectories.
272 if (recurseIntoSubdirs) {
273 SkOSFile::Iter dirIterator(dir.c_str());
274 SkString dirName;
275 while (dirIterator.next(&dirName, true)) {
276 if (dirName.startsWith(".")) {
277 continue;
278 }
279 SkString pathRelativeToRootDir(subDir);
280 if (!isSubDirEmpty) {
281 pathRelativeToRootDir.append(PATH_DIV_STR);
282 }
283 pathRelativeToRootDir.append(dirName);
284 if (!string_contains_any_of(pathRelativeToRootDir, nomatchSubstrings)) {
285 get_file_list_subdir(rootDir, pathRelativeToRootDir,
286 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
287 files);
288 }
289 }
290 }
291}
292
293/// Iterate over dir and get all files whose filename:
294/// - matches any of the substrings in matchSubstrings, but...
295/// - DOES NOT match any of the substrings in nomatchSubstrings
296/// - DOES NOT start with a dot (.)
297/// Adds the matching files to the list in *files.
298static void get_file_list(const SkString& dir,
299 const StringArray& matchSubstrings,
300 const StringArray& nomatchSubstrings,
301 bool recurseIntoSubdirs, FileArray *files) {
303 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
304 files);
305}
306
307/// Comparison routines for qsort, sort by file names.
309 return strcmp(lhs->c_str(), rhs->c_str());
310}
311
313public:
315 : fDrp(drp) {
316 SkASSERT(drp != nullptr);
317 }
319 fDrp->fBase.fBitmap.setPixelRef(nullptr, 0, 0);
320 fDrp->fComparison.fBitmap.setPixelRef(nullptr, 0, 0);
321 fDrp->fDifference.fBitmap.setPixelRef(nullptr, 0, 0);
322 fDrp->fWhite.fBitmap.setPixelRef(nullptr, 0, 0);
323 }
324
325private:
326 DiffRecord* fDrp;
327};
328
329static void get_bounds(DiffResource& resource, const char* name) {
330 if (resource.fBitmap.empty() && !DiffResource::isStatusFailed(resource.fStatus)) {
331 sk_sp<SkData> fileBits(read_file(resource.fFullPath.c_str()));
332 if (fileBits) {
333 get_bitmap(fileBits, resource, true, true);
334 } else {
335 SkDebugf("WARNING: couldn't read %s file <%s>\n", name, resource.fFullPath.c_str());
337 }
338 }
339}
340
341static void get_bounds(DiffRecord& drp) {
342 get_bounds(drp.fBase, "base");
343 get_bounds(drp.fComparison, "comparison");
344}
345
346#ifdef SK_OS_WIN
347#define ANSI_COLOR_RED ""
348#define ANSI_COLOR_GREEN ""
349#define ANSI_COLOR_YELLOW ""
350#define ANSI_COLOR_RESET ""
351#else
352#define ANSI_COLOR_RED "\x1b[31m"
353#define ANSI_COLOR_GREEN "\x1b[32m"
354#define ANSI_COLOR_YELLOW "\x1b[33m"
355#define ANSI_COLOR_RESET "\x1b[0m"
356#endif
357
358#define VERBOSE_STATUS(status,color,filename) if (verbose) printf( "[ " color " %10s " ANSI_COLOR_RESET " ] %s\n", status, filename.c_str())
359
360/// Creates difference images, returns the number that have a 0 metric.
361/// If outputDir.isEmpty(), don't write out diff files.
363 const int colorThreshold,
364 bool ignoreColorSpace,
365 RecordArray* differences,
366 const SkString& baseDir,
367 const SkString& comparisonDir,
368 const SkString& outputDir,
369 const StringArray& matchSubstrings,
370 const StringArray& nomatchSubstrings,
371 bool recurseIntoSubdirs,
372 bool getBounds,
373 bool verbose,
374 DiffSummary* summary) {
375 SkASSERT(!baseDir.isEmpty());
376 SkASSERT(!comparisonDir.isEmpty());
377
378 FileArray baseFiles;
379 FileArray comparisonFiles;
380
381 get_file_list(baseDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, &baseFiles);
382 get_file_list(comparisonDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
383 &comparisonFiles);
384
385 if (!baseFiles.empty()) {
386 qsort(baseFiles.begin(), baseFiles.size(), sizeof(SkString),
388 }
389 if (!comparisonFiles.empty()) {
390 qsort(comparisonFiles.begin(), comparisonFiles.size(), sizeof(SkString),
392 }
393
394 if (!outputDir.isEmpty()) {
395 sk_mkdir(outputDir.c_str());
396 }
397
398 int i = 0;
399 int j = 0;
400
401 while (i < baseFiles.size() &&
402 j < comparisonFiles.size()) {
403
404 SkString basePath(baseDir);
405 SkString comparisonPath(comparisonDir);
406
407 DiffRecord drp;
408 int v = strcmp(baseFiles[i].c_str(), comparisonFiles[j].c_str());
409
410 if (v < 0) {
411 // in baseDir, but not in comparisonDir
413
414 basePath.append(baseFiles[i]);
415 comparisonPath.append(baseFiles[i]);
416
417 drp.fBase.fFilename = baseFiles[i];
418 drp.fBase.fFullPath = basePath;
420
421 drp.fComparison.fFilename = baseFiles[i];
422 drp.fComparison.fFullPath = comparisonPath;
424
425 VERBOSE_STATUS("MISSING", ANSI_COLOR_YELLOW, baseFiles[i]);
426
427 ++i;
428 } else if (v > 0) {
429 // in comparisonDir, but not in baseDir
431
432 basePath.append(comparisonFiles[j]);
433 comparisonPath.append(comparisonFiles[j]);
434
435 drp.fBase.fFilename = comparisonFiles[j];
436 drp.fBase.fFullPath = basePath;
438
439 drp.fComparison.fFilename = comparisonFiles[j];
440 drp.fComparison.fFullPath = comparisonPath;
442
443 VERBOSE_STATUS("MISSING", ANSI_COLOR_YELLOW, comparisonFiles[j]);
444
445 ++j;
446 } else {
447 // Found the same filename in both baseDir and comparisonDir.
449
450 basePath.append(baseFiles[i]);
451 comparisonPath.append(comparisonFiles[j]);
452
453 drp.fBase.fFilename = baseFiles[i];
454 drp.fBase.fFullPath = basePath;
456
457 drp.fComparison.fFilename = comparisonFiles[j];
458 drp.fComparison.fFullPath = comparisonPath;
460
461 sk_sp<SkData> baseFileBits(read_file(drp.fBase.fFullPath.c_str()));
462 if (baseFileBits) {
464 }
465 sk_sp<SkData> comparisonFileBits(read_file(drp.fComparison.fFullPath.c_str()));
466 if (comparisonFileBits) {
468 }
469 if (nullptr == baseFileBits || nullptr == comparisonFileBits) {
470 if (nullptr == baseFileBits) {
472 VERBOSE_STATUS("READ FAIL", ANSI_COLOR_RED, baseFiles[i]);
473 }
474 if (nullptr == comparisonFileBits) {
476 VERBOSE_STATUS("READ FAIL", ANSI_COLOR_RED, comparisonFiles[j]);
477 }
479
480 } else if (are_buffers_equal(baseFileBits.get(), comparisonFileBits.get())) {
482 VERBOSE_STATUS("MATCH", ANSI_COLOR_GREEN, baseFiles[i]);
483 } else {
484 AutoReleasePixels arp(&drp);
485 get_bitmap(baseFileBits, drp.fBase, false, ignoreColorSpace);
486 get_bitmap(comparisonFileBits, drp.fComparison, false, ignoreColorSpace);
487 VERBOSE_STATUS("DIFFERENT", ANSI_COLOR_RED, baseFiles[i]);
490 create_and_write_diff_image(&drp, dmp, colorThreshold,
491 outputDir, drp.fBase.fFilename);
492 } else {
494 }
495 }
496
497 ++i;
498 ++j;
499 }
500
501 if (getBounds) {
502 get_bounds(drp);
503 }
505 summary->add(drp);
506 differences->push_back(std::move(drp));
507 }
508
509 for (; i < baseFiles.size(); ++i) {
510 // files only in baseDir
511 DiffRecord drp;
512 drp.fBase.fFilename = baseFiles[i];
513 drp.fBase.fFullPath = baseDir;
516
517 drp.fComparison.fFilename = baseFiles[i];
518 drp.fComparison.fFullPath = comparisonDir;
521
523 if (getBounds) {
524 get_bounds(drp);
525 }
526 summary->add(drp);
527 differences->push_back(std::move(drp));
528 }
529
530 for (; j < comparisonFiles.size(); ++j) {
531 // files only in comparisonDir
532 DiffRecord drp;
533 drp.fBase.fFilename = comparisonFiles[j];
534 drp.fBase.fFullPath = baseDir;
537
538 drp.fComparison.fFilename = comparisonFiles[j];
539 drp.fComparison.fFullPath = comparisonDir;
542
544 if (getBounds) {
545 get_bounds(drp);
546 }
547 summary->add(drp);
548 differences->push_back(std::move(drp));
549 }
550}
551
552static void usage (char * argv0) {
553 SkDebugf("Skia baseline image diff tool\n");
554 SkDebugf("\n"
555"Usage: \n"
556" %s <baseDir> <comparisonDir> [outputDir] \n", argv0);
557 SkDebugf(
558"\nArguments:"
559"\n --failonresult <result>: After comparing all file pairs, exit with nonzero"
560"\n return code (number of file pairs yielding this"
561"\n result) if any file pairs yielded this result."
562"\n This flag may be repeated, in which case the"
563"\n return code will be the number of fail pairs"
564"\n yielding ANY of these results."
565"\n --failonstatus <baseStatus> <comparisonStatus>: exit with nonzero return"
566"\n code if any file pairs yielded this status."
567"\n --help: display this info"
568"\n --listfilenames: list all filenames for each result type in stdout"
569"\n --match <substring>: compare files whose filenames contain this substring;"
570"\n if unspecified, compare ALL files."
571"\n this flag may be repeated."
572"\n --nocolorspace: Ignore color space of images."
573"\n --nodiffs: don't write out image diffs or index.html, just generate"
574"\n report on stdout"
575"\n --nomatch <substring>: regardless of --match, DO NOT compare files whose"
576"\n filenames contain this substring."
577"\n this flag may be repeated."
578"\n --noprintdirs: do not print the directories used."
579"\n --norecurse: do not recurse into subdirectories."
580"\n --sortbymaxmismatch: sort by worst color channel mismatch;"
581"\n break ties with -sortbymismatch"
582"\n --sortbymismatch: sort by average color channel mismatch"
583"\n --threshold <n>: only report differences > n (per color channel) [default 0]"
584"\n --weighted: sort by # pixels different weighted by color difference"
585"\n"
586"\n baseDir: directory to read baseline images from."
587"\n comparisonDir: directory to read comparison images from"
588"\n outputDir: directory to write difference images and index.html to;"
589"\n defaults to comparisonDir"
590"\n"
591"\nIf no sort is specified, it will sort by fraction of pixels mismatching."
592"\n");
593}
594
595const int kNoError = 0;
596const int kGenericError = -1;
597
598int main(int argc, char** argv) {
600 int (*sortProc)(const void*, const void*) = compare<CompareDiffMetrics>;
601
602 // Maximum error tolerated in any one color channel in any one pixel before
603 // a difference is reported.
604 int colorThreshold = 0;
605 SkString baseDir;
606 SkString comparisonDir;
607 SkString outputDir;
608
609 StringArray matchSubstrings;
610 StringArray nomatchSubstrings;
611
612 bool generateDiffs = true;
613 bool listFilenames = false;
614 bool printDirNames = true;
615 bool recurseIntoSubdirs = true;
616 bool verbose = false;
617 bool listFailingBase = false;
618 bool ignoreColorSpace = false;
619
620 RecordArray differences;
621 DiffSummary summary;
622
623 bool failOnResultType[DiffRecord::kResultCount];
624 for (int i = 0; i < DiffRecord::kResultCount; i++) {
625 failOnResultType[i] = false;
626 }
627
629 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
630 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
631 failOnStatusType[base][comparison] = false;
632 }
633 }
634
635 int numUnflaggedArguments = 0;
636 for (int i = 1; i < argc; i++) {
637 if (!strcmp(argv[i], "--failonresult")) {
638 if (argc == ++i) {
639 SkDebugf("failonresult expects one argument.\n");
640 continue;
641 }
644 failOnResultType[type] = true;
645 } else {
646 SkDebugf("ignoring unrecognized result <%s>\n", argv[i]);
647 }
648 continue;
649 }
650 if (!strcmp(argv[i], "--failonstatus")) {
651 if (argc == ++i) {
652 SkDebugf("failonstatus missing base status.\n");
653 continue;
654 }
655 bool baseStatuses[DiffResource::kStatusCount];
656 if (!DiffResource::getMatchingStatuses(argv[i], baseStatuses)) {
657 SkDebugf("unrecognized base status <%s>\n", argv[i]);
658 }
659
660 if (argc == ++i) {
661 SkDebugf("failonstatus missing comparison status.\n");
662 continue;
663 }
664 bool comparisonStatuses[DiffResource::kStatusCount];
665 if (!DiffResource::getMatchingStatuses(argv[i], comparisonStatuses)) {
666 SkDebugf("unrecognized comarison status <%s>\n", argv[i]);
667 }
668
669 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
670 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
671 failOnStatusType[base][comparison] |=
672 baseStatuses[base] && comparisonStatuses[comparison];
673 }
674 }
675 continue;
676 }
677 if (!strcmp(argv[i], "--help")) {
678 usage(argv[0]);
679 return kNoError;
680 }
681 if (!strcmp(argv[i], "--listfilenames")) {
682 listFilenames = true;
683 continue;
684 }
685 if (!strcmp(argv[i], "--verbose")) {
686 verbose = true;
687 continue;
688 }
689 if (!strcmp(argv[i], "--match")) {
690 matchSubstrings.emplace_back(argv[++i]);
691 continue;
692 }
693 if (!strcmp(argv[i], "--nocolorspace")) {
694 ignoreColorSpace = true;
695 continue;
696 }
697 if (!strcmp(argv[i], "--nodiffs")) {
698 generateDiffs = false;
699 continue;
700 }
701 if (!strcmp(argv[i], "--nomatch")) {
702 nomatchSubstrings.emplace_back(argv[++i]);
703 continue;
704 }
705 if (!strcmp(argv[i], "--noprintdirs")) {
706 printDirNames = false;
707 continue;
708 }
709 if (!strcmp(argv[i], "--norecurse")) {
710 recurseIntoSubdirs = false;
711 continue;
712 }
713 if (!strcmp(argv[i], "--sortbymaxmismatch")) {
714 sortProc = compare<CompareDiffMaxMismatches>;
715 continue;
716 }
717 if (!strcmp(argv[i], "--sortbymismatch")) {
718 sortProc = compare<CompareDiffMeanMismatches>;
719 continue;
720 }
721 if (!strcmp(argv[i], "--threshold")) {
722 colorThreshold = atoi(argv[++i]);
723 continue;
724 }
725 if (!strcmp(argv[i], "--weighted")) {
726 sortProc = compare<CompareDiffWeighted>;
727 continue;
728 }
729 if (argv[i][0] != '-') {
730 switch (numUnflaggedArguments++) {
731 case 0:
732 baseDir.set(argv[i]);
733 continue;
734 case 1:
735 comparisonDir.set(argv[i]);
736 continue;
737 case 2:
738 outputDir.set(argv[i]);
739 continue;
740 default:
741 SkDebugf("extra unflagged argument <%s>\n", argv[i]);
742 usage(argv[0]);
743 return kGenericError;
744 }
745 }
746 if (!strcmp(argv[i], "--listFailingBase")) {
747 listFailingBase = true;
748 continue;
749 }
750
751 SkDebugf("Unrecognized argument <%s>\n", argv[i]);
752 usage(argv[0]);
753 return kGenericError;
754 }
755
756 if (numUnflaggedArguments == 2) {
757 outputDir = comparisonDir;
758 } else if (numUnflaggedArguments != 3) {
759 usage(argv[0]);
760 return kGenericError;
761 }
762
763 if (!baseDir.endsWith(PATH_DIV_STR)) {
764 baseDir.append(PATH_DIV_STR);
765 }
766 if (printDirNames) {
767 printf("baseDir is [%s]\n", baseDir.c_str());
768 }
769
770 if (!comparisonDir.endsWith(PATH_DIV_STR)) {
771 comparisonDir.append(PATH_DIV_STR);
772 }
773 if (printDirNames) {
774 printf("comparisonDir is [%s]\n", comparisonDir.c_str());
775 }
776
777 if (!outputDir.endsWith(PATH_DIV_STR)) {
778 outputDir.append(PATH_DIV_STR);
779 }
780 if (generateDiffs) {
781 if (printDirNames) {
782 printf("writing diffs to outputDir is [%s]\n", outputDir.c_str());
783 }
784 } else {
785 if (printDirNames) {
786 printf("not writing any diffs to outputDir [%s]\n", outputDir.c_str());
787 }
788 outputDir.set("");
789 }
790
791 // If no matchSubstrings were specified, match ALL strings
792 // (except for whatever nomatchSubstrings were specified, if any).
793 if (matchSubstrings.empty()) {
794 matchSubstrings.emplace_back("");
795 }
796
797 create_diff_images(diffProc, colorThreshold, ignoreColorSpace, &differences,
798 baseDir, comparisonDir, outputDir,
799 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, generateDiffs,
800 verbose, &summary);
801 summary.print(listFilenames, failOnResultType, failOnStatusType);
802
803 if (listFailingBase) {
804 summary.printfFailingBaseNames("\n");
805 }
806
807 if (differences.size()) {
808 qsort(differences.begin(), differences.size(), sizeof(DiffRecord), sortProc);
809 }
810
811 if (generateDiffs) {
812 print_diff_page(summary.fNumMatches, colorThreshold, differences,
813 baseDir, comparisonDir, outputDir);
814 }
815
816 int num_failing_results = 0;
817 for (int i = 0; i < DiffRecord::kResultCount; i++) {
818 if (failOnResultType[i]) {
819 num_failing_results += summary.fResultsOfType[i].size();
820 }
821 }
822 if (!failOnResultType[DiffRecord::kCouldNotCompare_Result]) {
823 for (int base = 0; base < DiffResource::kStatusCount; ++base) {
824 for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
825 if (failOnStatusType[base][comparison]) {
826 num_failing_results += summary.fStatusOfType[base][comparison].size();
827 }
828 }
829 }
830 }
831
832 // On Linux (and maybe other platforms too), any results outside of the
833 // range [0...255] are wrapped (mod 256). Do the conversion ourselves, to
834 // make sure that we only return 0 when there were no failures.
835 return (num_failing_results > 255) ? 255 : num_failing_results;
836}
#define SkDEBUGFAIL(message)
Definition SkAssert.h:118
#define SkASSERT(cond)
Definition SkAssert.h:116
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
bool sk_mkdir(const char *path)
static bool contains(const SkRect &r, SkPoint p)
#define SkCastForQSort(compare)
Definition SkTSearch.h:130
Type::kYUV Type::kRGBA() int(0.7 *637)
AutoReleasePixels(DiffRecord *drp)
bool empty() const
Definition SkBitmap.h:210
void setPixelRef(sk_sp< SkPixelRef > pixelRef, int dx, int dy)
Definition SkBitmap.cpp:182
SK_SPI bool next(SkString *name, bool getDir=false)
static constexpr char SEPARATOR
Definition SkOSPath.h:21
bool startsWith(const char prefixStr[]) const
Definition SkString.h:140
void set(const SkString &src)
Definition SkString.h:186
bool equals(const SkString &) const
Definition SkString.cpp:324
bool isEmpty() const
Definition SkString.h:130
void append(const char text[])
Definition SkString.h:203
bool endsWith(const char suffixStr[]) const
Definition SkString.h:146
const char * c_str() const
Definition SkString.h:133
T * get() const
Definition SkRefCnt.h:303
bool empty() const
Definition SkTArray.h:194
int size() const
Definition SkTArray.h:416
T & emplace_back(Args &&... args)
Definition SkTArray.h:243
glong glong end
GAsyncResult * result
const char * name
Definition fuchsia.cc:50
char ** argv
Definition library.h:9
Definition main.py:1
#define MAX3(a, b, c)
Definition skdiff.h:26
SkPMColor(* DiffMetricProc)(SkPMColor, SkPMColor)
Parameterized routine to compute the color of a pixel in a difference image.
Definition skdiff.h:255
#define PATH_DIV_STR
Definition skdiff.h:21
static SkPMColor compute_diff_pmcolor(SkPMColor c0, SkPMColor c1)
Definition skdiff.h:258
void print_diff_page(const int matchCount, const int colorThreshold, const RecordArray &differences, const SkString &baseDir, const SkString &comparisonDir, const SkString &outputDir)
static bool string_contains_any_of(const SkString &string, const StringArray &substrings)
Returns true if string contains any of these substrings.
#define VERBOSE_STATUS(status, color, filename)
#define ANSI_COLOR_RED
StringArray FileArray
const int kNoError
#define ANSI_COLOR_YELLOW
static void add_unique_basename(StringArray *array, const SkString &filename)
static int compare_file_name_metrics(SkString *lhs, SkString *rhs)
Comparison routines for qsort, sort by file names.
static void get_file_list_subdir(const SkString &rootDir, const SkString &subDir, const StringArray &matchSubstrings, const StringArray &nomatchSubstrings, bool recurseIntoSubdirs, FileArray *files)
Internal (potentially recursive) implementation of get_file_list.
#define ANSI_COLOR_GREEN
static void usage(char *argv0)
TArray< SkString > StringArray
static void get_bounds(DiffResource &resource, const char *name)
const int kGenericError
static void get_file_list(const SkString &dir, const StringArray &matchSubstrings, const StringArray &nomatchSubstrings, bool recurseIntoSubdirs, FileArray *files)
static void create_diff_images(DiffMetricProc dmp, const int colorThreshold, bool ignoreColorSpace, RecordArray *differences, const SkString &baseDir, const SkString &comparisonDir, const SkString &outputDir, const StringArray &matchSubstrings, const StringArray &nomatchSubstrings, bool recurseIntoSubdirs, bool getBounds, bool verbose, DiffSummary *summary)
bool are_buffers_equal(SkData *skdata1, SkData *skdata2)
void create_and_write_diff_image(DiffRecord *drp, DiffMetricProc dmp, const int colorThreshold, const SkString &outputDir, const SkString &filename)
sk_sp< SkData > read_file(const char *file_path)
bool get_bitmap(sk_sp< SkData > fileBits, DiffResource &resource, bool sizeOnly, bool ignoreColorSpace)
DiffResource fDifference
Definition skdiff.h:132
static char const *const ResultNames[DiffRecord::kResultCount]
Definition skdiff.h:14
static Result getResultByName(const char *name)
Definition skdiff.cpp:23
DiffResource fComparison
Definition skdiff.h:131
Result fResult
Which category of diff result.
Definition skdiff.h:154
@ kUnknown_Result
Definition skdiff.h:97
@ kEqualBits_Result
Definition skdiff.h:92
@ kCouldNotCompare_Result
Definition skdiff.h:96
@ kResultCount
Definition skdiff.h:99
@ kDifferentSizes_Result
Definition skdiff.h:95
@ kDifferentPixels_Result
Definition skdiff.h:94
@ kEqualPixels_Result
Definition skdiff.h:93
DiffResource fBase
Definition skdiff.h:130
uint32_t fMaxMismatchB
Definition skdiff.h:151
uint32_t fMaxMismatchR
Definition skdiff.h:149
float fFractionDifference
Definition skdiff.h:138
uint32_t fMaxMismatchG
Definition skdiff.h:150
DiffResource fWhite
Definition skdiff.h:133
static const char * getResultDescription(Result result)
Definition skdiff.cpp:41
SkString fFullPath
Definition skdiff.h:81
SkBitmap fBitmap
Definition skdiff.h:83
static const char * getStatusDescription(Status status)
Definition skdiff.cpp:86
static bool isStatusFailed(Status status)
Definition skdiff.cpp:90
static bool getMatchingStatuses(char *selector, bool statuses[kStatusCount])
Definition skdiff.cpp:98
@ kRead_Status
Definition skdiff.h:37
@ kExists_Status
Definition skdiff.h:42
@ kCouldNotRead_Status
Definition skdiff.h:39
@ kDoesNotExist_Status
Definition skdiff.h:44
@ kStatusCount
Definition skdiff.h:55
@ kDecoded_Status
Definition skdiff.h:32
Status fStatus
Definition skdiff.h:84
SkString fFilename
Definition skdiff.h:79
uint32_t fNumMatches
void printfFailingBaseNames(const char separator[])
void printStatus(bool listFilenames, bool failOnStatusType[DiffResource::kStatusCount][DiffResource::kStatusCount])
float fMaxMismatchPercent
FileArray fStatusOfType[DiffResource::kStatusCount][DiffResource::kStatusCount]
uint32_t fNumMismatches
void printContents(const FileArray &fileArray, const char *baseStatus, const char *comparisonStatus, bool listFilenames)
uint32_t fMaxMismatchV
void add(const DiffRecord &drp)
StringArray fFailedBaseNames[DiffRecord::kResultCount]
void printContents(const FileArray &fileArray, const char *headerText, bool listFilenames)
FileArray fResultsOfType[DiffRecord::kResultCount]
void print(bool listFilenames, bool failOnResultType[DiffRecord::kResultCount], bool failOnStatusType[DiffResource::kStatusCount][DiffResource::kStatusCount])