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());
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");
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());
415 comparisonPath.
append(baseFiles[
i]);
432 basePath.
append(comparisonFiles[j]);
433 comparisonPath.
append(comparisonFiles[j]);
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."
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;
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;
650 if (!strcmp(
argv[
i],
"--failonstatus")) {
652 SkDebugf(
"failonstatus missing base status.\n");
661 SkDebugf(
"failonstatus missing comparison status.\n");
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) {
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 SkString resource(SkPDFResourceType type, int index)
#define SkCastForQSort(compare)
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)
std::string printf(const char *fmt,...) SK_PRINTF_LIKE(1
DEF_SWITCHES_START aot vmservice shared library name
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets dir
constexpr bool contains(std::string_view str, std::string_view needle)
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)
int main(int argc, char **argv)
#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])