42 const char* src = filename.
c_str();
49 const char*
end = strrchr(trimmed,
'.');
51 end = trimmed + strlen(trimmed);
56 for (
int i = 0; i < array->
size(); ++i) {
82 const char* baseStatus,
const char* comparisonStatus,
84 int n = fileArray.
size();
85 printf(
"%d file pairs %s in baseDir and %s in comparisonDir",
86 n, baseStatus, comparisonStatus);
89 for (
int i = 0; i < n; ++i) {
90 printf(
"%s ", fileArray[i].c_str());
102 Status baseStatus =
static_cast<Status
>(
base);
104 Status comparisonStatus =
static_cast<Status
>(comparison);
106 if (fileArray.
size() > 0) {
107 if (failOnStatusType[
base][comparison]) {
123 int n = fileArray.
size();
124 printf(
"%d file pairs %s", n, headerText);
127 for (
int i = 0; i < n; ++i) {
128 printf(
"%s ", fileArray[i].c_str());
140 if (failOnResultType[
result]) {
151 printf(
"(results marked with [*] will cause nonzero return value)\n");
152 printf(
"\nnumber of mismatching file pairs: %u\n",
fNumMismatches);
154 printf(
"Maximum pixel intensity mismatch %u\n",
fMaxMismatchV);
164 for (
int j = 0; j < array.
size(); ++j) {
165 printf(
"%s%s", array[j].c_str(), separator);
173 uint32_t mismatchValue;
215 SkDEBUGFAIL(
"adding DiffRecord with unhandled fResult value");
233 for (
int i = 0; i < substrings.
size(); i++) {
234 if (
string.
contains(substrings[i].c_str())) {
245 bool recurseIntoSubdirs,
FileArray *files) {
246 bool isSubDirEmpty = subDir.
isEmpty();
248 if (!isSubDirEmpty) {
256 while (fileIterator.
next(&fileName,
false)) {
260 SkString pathRelativeToRootDir(subDir);
261 if (!isSubDirEmpty) {
264 pathRelativeToRootDir.
append(fileName);
267 files->
push_back(std::move(pathRelativeToRootDir));
272 if (recurseIntoSubdirs) {
275 while (dirIterator.
next(&dirName,
true)) {
279 SkString pathRelativeToRootDir(subDir);
280 if (!isSubDirEmpty) {
283 pathRelativeToRootDir.
append(dirName);
286 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
301 bool recurseIntoSubdirs,
FileArray *files) {
303 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
347#define ANSI_COLOR_RED ""
348#define ANSI_COLOR_GREEN ""
349#define ANSI_COLOR_YELLOW ""
350#define ANSI_COLOR_RESET ""
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"
358#define VERBOSE_STATUS(status,color,filename) if (verbose) printf( "[ " color " %10s " ANSI_COLOR_RESET " ] %s\n", status, filename.c_str())
363 const int colorThreshold,
364 bool ignoreColorSpace,
371 bool recurseIntoSubdirs,
381 get_file_list(baseDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, &baseFiles);
382 get_file_list(comparisonDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
385 if (!baseFiles.
empty()) {
389 if (!comparisonFiles.
empty()) {
401 while (i < baseFiles.
size() &&
402 j < comparisonFiles.
size()) {
405 SkString comparisonPath(comparisonDir);
408 int v = strcmp(baseFiles[i].c_str(), comparisonFiles[j].c_str());
414 basePath.
append(baseFiles[i]);
415 comparisonPath.
append(baseFiles[i]);
432 basePath.
append(comparisonFiles[j]);
433 comparisonPath.
append(comparisonFiles[j]);
450 basePath.
append(baseFiles[i]);
451 comparisonPath.
append(comparisonFiles[j]);
466 if (comparisonFileBits) {
469 if (
nullptr == baseFileBits ||
nullptr == comparisonFileBits) {
470 if (
nullptr == baseFileBits) {
474 if (
nullptr == comparisonFileBits) {
509 for (; i < baseFiles.
size(); ++i) {
530 for (; j < comparisonFiles.
size(); ++j) {
553 SkDebugf(
"Skia baseline image diff tool\n");
556" %s <baseDir> <comparisonDir> [outputDir] \n", argv0);
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"
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"
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"
591"\nIf no sort is specified, it will sort by fraction of pixels mismatching."
598int main(
int argc,
char** argv) {
600 int (*sortProc)(
const void*,
const void*) = compare<CompareDiffMetrics>;
604 int colorThreshold = 0;
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;
625 failOnResultType[i] =
false;
631 failOnStatusType[
base][comparison] =
false;
635 int numUnflaggedArguments = 0;
636 for (
int i = 1; i < argc; i++) {
637 if (!strcmp(
argv[i],
"--failonresult")) {
639 SkDebugf(
"failonresult expects one argument.\n");
644 failOnResultType[
type] =
true;
646 SkDebugf(
"ignoring unrecognized result <%s>\n",
argv[i]);
650 if (!strcmp(
argv[i],
"--failonstatus")) {
652 SkDebugf(
"failonstatus missing base status.\n");
661 SkDebugf(
"failonstatus missing comparison status.\n");
666 SkDebugf(
"unrecognized comarison status <%s>\n",
argv[i]);
671 failOnStatusType[
base][comparison] |=
672 baseStatuses[
base] && comparisonStatuses[comparison];
677 if (!strcmp(
argv[i],
"--help")) {
681 if (!strcmp(
argv[i],
"--listfilenames")) {
682 listFilenames =
true;
685 if (!strcmp(
argv[i],
"--verbose")) {
689 if (!strcmp(
argv[i],
"--match")) {
693 if (!strcmp(
argv[i],
"--nocolorspace")) {
694 ignoreColorSpace =
true;
697 if (!strcmp(
argv[i],
"--nodiffs")) {
698 generateDiffs =
false;
701 if (!strcmp(
argv[i],
"--nomatch")) {
705 if (!strcmp(
argv[i],
"--noprintdirs")) {
706 printDirNames =
false;
709 if (!strcmp(
argv[i],
"--norecurse")) {
710 recurseIntoSubdirs =
false;
713 if (!strcmp(
argv[i],
"--sortbymaxmismatch")) {
714 sortProc = compare<CompareDiffMaxMismatches>;
717 if (!strcmp(
argv[i],
"--sortbymismatch")) {
718 sortProc = compare<CompareDiffMeanMismatches>;
721 if (!strcmp(
argv[i],
"--threshold")) {
722 colorThreshold = atoi(
argv[++i]);
725 if (!strcmp(
argv[i],
"--weighted")) {
726 sortProc = compare<CompareDiffWeighted>;
729 if (
argv[i][0] !=
'-') {
730 switch (numUnflaggedArguments++) {
746 if (!strcmp(
argv[i],
"--listFailingBase")) {
747 listFailingBase =
true;
756 if (numUnflaggedArguments == 2) {
757 outputDir = comparisonDir;
758 }
else if (numUnflaggedArguments != 3) {
767 printf(
"baseDir is [%s]\n", baseDir.
c_str());
774 printf(
"comparisonDir is [%s]\n", comparisonDir.
c_str());
782 printf(
"writing diffs to outputDir is [%s]\n", outputDir.
c_str());
786 printf(
"not writing any diffs to outputDir [%s]\n", outputDir.
c_str());
793 if (matchSubstrings.
empty()) {
798 baseDir, comparisonDir, outputDir,
799 matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, generateDiffs,
801 summary.
print(listFilenames, failOnResultType, failOnStatusType);
803 if (listFailingBase) {
807 if (differences.
size()) {
813 baseDir, comparisonDir, outputDir);
816 int num_failing_results = 0;
818 if (failOnResultType[i]) {
825 if (failOnStatusType[
base][comparison]) {
835 return (num_failing_results > 255) ? 255 : num_failing_results;
#define SkDEBUGFAIL(message)
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)
Type::kYUV Type::kRGBA() int(0.7 *637)
AutoReleasePixels(DiffRecord *drp)
void setPixelRef(sk_sp< SkPixelRef > pixelRef, int dx, int dy)
SK_SPI bool next(SkString *name, bool getDir=false)
static constexpr char SEPARATOR
bool startsWith(const char prefixStr[]) const
void set(const SkString &src)
bool equals(const SkString &) const
void append(const char text[])
bool endsWith(const char suffixStr[]) const
const char * c_str() const
T & emplace_back(Args &&... args)
SkPMColor(* DiffMetricProc)(SkPMColor, SkPMColor)
Parameterized routine to compute the color of a pixel in a difference image.
static SkPMColor compute_diff_pmcolor(SkPMColor c0, SkPMColor c1)
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_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.
static void usage(char *argv0)
TArray< SkString > StringArray
static void get_bounds(DiffResource &resource, const char *name)
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)
static char const *const ResultNames[DiffRecord::kResultCount]
static Result getResultByName(const char *name)
Result fResult
Which category of diff result.
@ kCouldNotCompare_Result
@ kDifferentPixels_Result
float fFractionDifference
static const char * getResultDescription(Result result)
static const char * getStatusDescription(Status status)
static bool isStatusFailed(Status status)
static bool getMatchingStatuses(char *selector, bool statuses[kStatusCount])
void printfFailingBaseNames(const char separator[])
void printStatus(bool listFilenames, bool failOnStatusType[DiffResource::kStatusCount][DiffResource::kStatusCount])
float fMaxMismatchPercent
FileArray fStatusOfType[DiffResource::kStatusCount][DiffResource::kStatusCount]
void printContents(const FileArray &fileArray, const char *baseStatus, const char *comparisonStatus, bool listFilenames)
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])