1363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger/*
2363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger * Copyright 2012 Google Inc.
3363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger *
4363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger * Use of this source code is governed by a BSD-style license that can be
5363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger * found in the LICENSE file.
6363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger */
7363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "skdiff.h"
8363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "skdiff_utils.h"
9363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkBitmap.h"
10363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkData.h"
11363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkImageDecoder.h"
12363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkImageEncoder.h"
13363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkOSFile.h"
14363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkTDArray.h"
15363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkTemplates.h"
16363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkTypes.h"
17363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
180a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger#include <stdio.h>
190a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger
20363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger/// If outputDir.isEmpty(), don't write out diff files.
21363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerstatic void create_diff_images (DiffMetricProc dmp,
22363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                                const int colorThreshold,
23363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                                const SkString& baseFile,
24363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                                const SkString& comparisonFile,
25363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                                const SkString& outputDir,
26363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                                const SkString& outputFilename,
27363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                                DiffRecord* drp) {
28363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    SkASSERT(!baseFile.isEmpty());
29363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    SkASSERT(!comparisonFile.isEmpty());
30363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
31363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    drp->fBase.fFilename = baseFile;
32363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    drp->fBase.fFullPath = baseFile;
33363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    drp->fBase.fStatus = DiffResource::kSpecified_Status;
34363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
35363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    drp->fComparison.fFilename = comparisonFile;
36363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    drp->fComparison.fFullPath = comparisonFile;
37363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    drp->fComparison.fStatus = DiffResource::kSpecified_Status;
38363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
39363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    SkAutoDataUnref baseFileBits(read_file(drp->fBase.fFullPath.c_str()));
40363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (NULL != baseFileBits) {
41363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fBase.fStatus = DiffResource::kRead_Status;
42363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
43363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    SkAutoDataUnref comparisonFileBits(read_file(drp->fComparison.fFullPath.c_str()));
44363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (NULL != comparisonFileBits) {
45363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fComparison.fStatus = DiffResource::kRead_Status;
46363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
47363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (NULL == baseFileBits || NULL == comparisonFileBits) {
48363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (NULL == baseFileBits) {
49363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            drp->fBase.fStatus = DiffResource::kCouldNotRead_Status;
50363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
51363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (NULL == comparisonFileBits) {
52363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            drp->fComparison.fStatus = DiffResource::kCouldNotRead_Status;
53363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
54363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fResult = DiffRecord::kCouldNotCompare_Result;
55363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        return;
56363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
57363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
58363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (are_buffers_equal(baseFileBits, comparisonFileBits)) {
59363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fResult = DiffRecord::kEqualBits_Result;
60363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        return;
61363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
62363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
63363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    get_bitmap(baseFileBits, drp->fBase, SkImageDecoder::kDecodePixels_Mode);
64363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    get_bitmap(comparisonFileBits, drp->fComparison, SkImageDecoder::kDecodePixels_Mode);
65363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (DiffResource::kDecoded_Status != drp->fBase.fStatus ||
66363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        DiffResource::kDecoded_Status != drp->fComparison.fStatus)
67363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    {
68363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fResult = DiffRecord::kCouldNotCompare_Result;
69363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        return;
70363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
71363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
72363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    create_and_write_diff_image(drp, dmp, colorThreshold, outputDir, outputFilename);
73363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //TODO: copy fBase.fFilename and fComparison.fFilename to outputDir
74363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //      svn and git often present tmp files to diff tools which are promptly deleted
75363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
76363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //TODO: serialize drp to outputDir
77363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //      write a tool to deserialize them and call print_diff_page
78363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
79363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    SkASSERT(DiffRecord::kUnknown_Result != drp->fResult);
80363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger}
81363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
82363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerstatic void usage (char * argv0) {
83363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    SkDebugf("Skia image diff tool\n");
84363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    SkDebugf("\n"
85363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"Usage: \n"
86363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"    %s <baseFile> <comparisonFile>\n" , argv0);
87363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    SkDebugf(
88363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\nArguments:"
89363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n    --failonresult <result>: After comparing all file pairs, exit with nonzero"
90363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n                             return code (number of file pairs yielding this"
91363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n                             result) if any file pairs yielded this result."
92363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n                             This flag may be repeated, in which case the"
93363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n                             return code will be the number of fail pairs"
94363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n                             yielding ANY of these results."
95363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n    --failonstatus <baseStatus> <comparisonStatus>: exit with nonzero return"
96363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n                             code if any file pairs yeilded this status."
97363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n    --help: display this info"
98363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n    --listfilenames: list all filenames for each result type in stdout"
99363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n    --nodiffs: don't write out image diffs, just generate report on stdout"
100363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n    --outputdir: directory to write difference images"
101363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n    --threshold <n>: only report differences > n (per color channel) [default 0]"
102363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n    -u: ignored. Recognized for compatibility with svn diff."
103363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n    -L: first occurrence label for base, second occurrence label for comparison."
104363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n        Labels must be of the form \"<filename>(\t<specifier>)?\"."
105363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n        The base <filename> will be used to create files in outputdir."
106363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n"
107363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n    baseFile: baseline image file."
108363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n    comparisonFile: comparison image file"
109363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n"
110363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\nIf no sort is specified, it will sort by fraction of pixels mismatching."
111363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n");
112363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger}
113363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
114363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerconst int kNoError = 0;
115363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerconst int kGenericError = -1;
116363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
117363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerint tool_main(int argc, char** argv);
118363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerint tool_main(int argc, char** argv) {
119363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    DiffMetricProc diffProc = compute_diff_pmcolor;
120363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
121363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    // Maximum error tolerated in any one color channel in any one pixel before
122363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    // a difference is reported.
123363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    int colorThreshold = 0;
124363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    SkString baseFile;
125363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    SkString baseLabel;
126363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    SkString comparisonFile;
127363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    SkString comparisonLabel;
128363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    SkString outputDir;
129363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
130363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    bool listFilenames = false;
131363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
132363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    bool failOnResultType[DiffRecord::kResultCount];
133363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    for (int i = 0; i < DiffRecord::kResultCount; i++) {
134363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        failOnResultType[i] = false;
135363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
136363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
137363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    bool failOnStatusType[DiffResource::kStatusCount][DiffResource::kStatusCount];
138363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    for (int base = 0; base < DiffResource::kStatusCount; ++base) {
139363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
140363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            failOnStatusType[base][comparison] = false;
141363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
142363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
143363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
144363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    int i;
145363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    int numUnflaggedArguments = 0;
146363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    int numLabelArguments = 0;
147363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    for (i = 1; i < argc; i++) {
148363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (!strcmp(argv[i], "--failonresult")) {
149363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            if (argc == ++i) {
150363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                SkDebugf("failonresult expects one argument.\n");
151363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                continue;
152363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
153363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            DiffRecord::Result type = DiffRecord::getResultByName(argv[i]);
154363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            if (type != DiffRecord::kResultCount) {
155363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                failOnResultType[type] = true;
156363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            } else {
157363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                SkDebugf("ignoring unrecognized result <%s>\n", argv[i]);
158363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
159363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            continue;
160363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
161363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (!strcmp(argv[i], "--failonstatus")) {
162363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            if (argc == ++i) {
163363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                SkDebugf("failonstatus missing base status.\n");
164363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                continue;
165363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
166363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            bool baseStatuses[DiffResource::kStatusCount];
167363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            if (!DiffResource::getMatchingStatuses(argv[i], baseStatuses)) {
168363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                SkDebugf("unrecognized base status <%s>\n", argv[i]);
169363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
170363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
171363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            if (argc == ++i) {
172363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                SkDebugf("failonstatus missing comparison status.\n");
173363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                continue;
174363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
175363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            bool comparisonStatuses[DiffResource::kStatusCount];
176363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            if (!DiffResource::getMatchingStatuses(argv[i], comparisonStatuses)) {
177363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                SkDebugf("unrecognized comarison status <%s>\n", argv[i]);
178363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
179363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
180363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            for (int base = 0; base < DiffResource::kStatusCount; ++base) {
181363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
182363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    failOnStatusType[base][comparison] |=
183363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                        baseStatuses[base] && comparisonStatuses[comparison];
184363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                }
185363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
186363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            continue;
187363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
188363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (!strcmp(argv[i], "--help")) {
189363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            usage(argv[0]);
190363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            return kNoError;
191363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
192363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (!strcmp(argv[i], "--listfilenames")) {
193363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            listFilenames = true;
194363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            continue;
195363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
196363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (!strcmp(argv[i], "--outputdir")) {
197363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            if (argc == ++i) {
198363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                SkDebugf("outputdir expects one argument.\n");
199363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                continue;
200363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
201363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            outputDir.set(argv[i]);
202363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            continue;
203363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
204363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (!strcmp(argv[i], "--threshold")) {
205363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            colorThreshold = atoi(argv[++i]);
206363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            continue;
207363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
208363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (!strcmp(argv[i], "-u")) {
209363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            //we don't produce unified diffs, ignore parameter to work with svn diff
210363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            continue;
211363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
212363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (!strcmp(argv[i], "-L")) {
213363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            if (argc == ++i) {
214363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                SkDebugf("label expects one argument.\n");
215363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                continue;
216363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
217363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            switch (numLabelArguments++) {
218363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                case 0:
219363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    baseLabel.set(argv[i]);
220363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    continue;
221363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                case 1:
222363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    comparisonLabel.set(argv[i]);
223363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    continue;
224363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                default:
225363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    SkDebugf("extra label argument <%s>\n", argv[i]);
226363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    usage(argv[0]);
227363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    return kGenericError;
228363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
229363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            continue;
230363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
231363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (argv[i][0] != '-') {
232363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            switch (numUnflaggedArguments++) {
233363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                case 0:
234363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    baseFile.set(argv[i]);
235363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    continue;
236363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                case 1:
237363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    comparisonFile.set(argv[i]);
238363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    continue;
239363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                default:
240363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    SkDebugf("extra unflagged argument <%s>\n", argv[i]);
241363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    usage(argv[0]);
242363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    return kGenericError;
243363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
244363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
245363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
246363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        SkDebugf("Unrecognized argument <%s>\n", argv[i]);
247363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        usage(argv[0]);
248363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        return kGenericError;
249363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
250363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
251363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (numUnflaggedArguments != 2) {
252363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        usage(argv[0]);
253363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        return kGenericError;
254363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
255363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
256363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (listFilenames) {
257363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        printf("Base file is [%s]\n", baseFile.c_str());
258363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
259363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
260363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (listFilenames) {
261363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        printf("Comparison file is [%s]\n", comparisonFile.c_str());
262363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
263363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
264363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (outputDir.isEmpty()) {
265363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (listFilenames) {
266363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            printf("Not writing any diffs. No output dir specified.\n");
267363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
268363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    } else {
269363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (!outputDir.endsWith(PATH_DIV_STR)) {
270363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            outputDir.append(PATH_DIV_STR);
271363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
272363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (listFilenames) {
273363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            printf("Writing diffs. Output dir is [%s]\n", outputDir.c_str());
274363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
275363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
276363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
277363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    // Some obscure documentation about diff/patch labels:
278363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //
279363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    // Posix says the format is: <filename><tab><date>
280363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //     It also states that if a filename contains <tab> or <newline>
281363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //     the result is implementation defined
282363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //
283363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    // Svn diff --diff-cmd provides labels of the form: <filename><tab><revision>
284363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //
285363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    // Git diff --ext-diff does not supply arguments compatible with diff.
286363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //     However, it does provide the filename directly.
287363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //     skimagediff_git.sh: skimagediff %2 %5 -L "%1\t(%3)" -L "%1\t(%6)"
288363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //
289363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    // Git difftool sets $LOCAL, $REMOTE, $MERGED, and $BASE instead of command line parameters.
290363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //     difftool.<>.cmd: skimagediff $LOCAL $REMOTE -L "$MERGED\t(local)" -L "$MERGED\t(remote)"
291363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //
292363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    // Diff will write any specified label verbatim. Without a specified label diff will write
293363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //     <filename><tab><date>
294363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //     However, diff will encode the filename as a cstring if the filename contains
295363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //         Any of <space> or <double quote>
296363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //         A char less than 32
297096defe64d408e54474fe19f418c95bf1a554fc7Derek Sollenberger    //         Any escapable character \\, \a, \b, \t, \n, \v, \f, \r
298363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //
299363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    // Patch decodes:
300363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //     If first <non-white-space> is <double quote>, parse filename from cstring.
301363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //     If there is a <tab> after the first <non-white-space>, filename is
302363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //         [first <non-white-space>, the next run of <white-space> with an embedded <tab>).
303363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //     Otherwise the filename is [first <non-space>, the next <white-space>).
304363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //
305363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    // The filename /dev/null means the file does not exist (used in adds and deletes).
306363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
307363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    // Considering the above, skimagediff will consider the contents of a -L parameter as
308363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //     <filename>(\t<specifier>)?
309363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    SkString outputFile;
310363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
311363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (baseLabel.isEmpty()) {
312363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        baseLabel.set(baseFile);
313363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        outputFile = baseLabel;
314363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    } else {
315363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        const char* baseLabelCstr = baseLabel.c_str();
316363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        const char* tab = strchr(baseLabelCstr, '\t');
317363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (NULL == tab) {
318363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            outputFile = baseLabel;
319363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        } else {
320363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            outputFile.set(baseLabelCstr, tab - baseLabelCstr);
321363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
322363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
323363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (comparisonLabel.isEmpty()) {
324363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        comparisonLabel.set(comparisonFile);
325363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
326363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    printf("Base:       %s\n", baseLabel.c_str());
327363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    printf("Comparison: %s\n", comparisonLabel.c_str());
328363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
329363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    DiffRecord dr;
330363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    create_diff_images(diffProc, colorThreshold, baseFile, comparisonFile, outputDir, outputFile,
331363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                       &dr);
332363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
333363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (DiffResource::isStatusFailed(dr.fBase.fStatus)) {
334363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        printf("Base %s.\n", DiffResource::getStatusDescription(dr.fBase.fStatus));
335363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
336363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (DiffResource::isStatusFailed(dr.fComparison.fStatus)) {
337363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        printf("Comparison %s.\n", DiffResource::getStatusDescription(dr.fComparison.fStatus));
338363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
339363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    printf("Base and Comparison %s.\n", DiffRecord::getResultDescription(dr.fResult));
340363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
341363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (DiffRecord::kDifferentPixels_Result == dr.fResult) {
342363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        printf("%.4f%% of pixels differ", 100 * dr.fFractionDifference);
343363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        printf(" (%.4f%%  weighted)", 100 * dr.fWeightedFraction);
344363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (dr.fFractionDifference < 0.01) {
345363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            printf(" %d pixels", static_cast<int>(dr.fFractionDifference *
346363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                                                  dr.fBase.fBitmap.width() *
347363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                                                  dr.fBase.fBitmap.height()));
348363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
349363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
350363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        printf("\nAverage color mismatch: ");
351363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        printf("%d", static_cast<int>(MAX3(dr.fAverageMismatchR,
352363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                                           dr.fAverageMismatchG,
353363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                                           dr.fAverageMismatchB)));
354363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        printf("\nMax color mismatch: ");
355363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        printf("%d", MAX3(dr.fMaxMismatchR,
356363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                          dr.fMaxMismatchG,
357363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                          dr.fMaxMismatchB));
358363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        printf("\n");
359363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
360363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    printf("\n");
361363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
362363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    int num_failing_results = 0;
363363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (failOnResultType[dr.fResult]) {
364363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        ++num_failing_results;
365363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
366363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (failOnStatusType[dr.fBase.fStatus][dr.fComparison.fStatus]) {
367363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        ++num_failing_results;
368363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
369363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
370363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    return num_failing_results;
371363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger}
372363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
373363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#if !defined SK_BUILD_FOR_IOS
374363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerint main(int argc, char * const argv[]) {
375363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    return tool_main(argc, (char**) argv);
376363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger}
377363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#endif
378