180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru/*
280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru * Copyright 2011 Google Inc.
380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru *
480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru * Use of this source code is governed by a BSD-style license that can be
580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru * found in the LICENSE file.
680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru */
7363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "skdiff.h"
8363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "skdiff_html.h"
9363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "skdiff_utils.h"
10363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkBitmap.h"
1180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#include "SkData.h"
1258190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger#include "SkForceLinking.h"
1380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#include "SkImageDecoder.h"
1480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#include "SkImageEncoder.h"
1580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#include "SkOSFile.h"
1680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#include "SkStream.h"
1780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#include "SkTDArray.h"
1880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#include "SkTemplates.h"
1980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#include "SkTSearch.h"
2080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#include "SkTypes.h"
2180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
2258190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger__SK_FORCE_IMAGE_DECODER_LINKING;
2358190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger
2480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru/**
2580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru * skdiff
2680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru *
2780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru * Given three directory names, expects to find identically-named files in
2880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru * each of the first two; the first are treated as a set of baseline,
2980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru * the second a set of variant images, and a diff image is written into the
3080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru * third directory for each pair.
3180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru * Creates an index.html in the current third directory to compare each
3280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru * pair that does not match exactly.
3380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru * Recursively descends directories, unless run with --norecurse.
3480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru *
3580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru * Returns zero exit code if all images match across baseDir and comparisonDir.
3680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru */
3780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
3880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querutypedef SkTDArray<SkString*> StringArray;
3980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querutypedef StringArray FileArray;
4080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
4180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querustruct DiffSummary {
4280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    DiffSummary ()
43363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        : fNumMatches(0)
44363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        , fNumMismatches(0)
45363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        , fMaxMismatchV(0)
46363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        , fMaxMismatchPercent(0) { };
4780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
4880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    ~DiffSummary() {
49363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        for (int i = 0; i < DiffRecord::kResultCount; ++i) {
5080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            fResultsOfType[i].deleteAll();
5180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
52363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        for (int base = 0; base < DiffResource::kStatusCount; ++base) {
53363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
54363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                fStatusOfType[base][comparison].deleteAll();
55363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
56363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
5780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
5880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
5980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    uint32_t fNumMatches;
6080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    uint32_t fNumMismatches;
6180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    uint32_t fMaxMismatchV;
6280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    float fMaxMismatchPercent;
6380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
64363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    FileArray fResultsOfType[DiffRecord::kResultCount];
65363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    FileArray fStatusOfType[DiffResource::kStatusCount][DiffResource::kStatusCount];
66363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
67363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    void printContents(const FileArray& fileArray,
68363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                       const char* baseStatus, const char* comparisonStatus,
69363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                       bool listFilenames) {
70363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        int n = fileArray.count();
71363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        printf("%d file pairs %s in baseDir and %s in comparisonDir",
72363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                n,            baseStatus,       comparisonStatus);
73363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (listFilenames) {
74363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            printf(": ");
75363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            for (int i = 0; i < n; ++i) {
76363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                printf("%s ", fileArray[i]->c_str());
77363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
78363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
79363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        printf("\n");
80363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
81363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
82363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    void printStatus(bool listFilenames,
83363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                     bool failOnStatusType[DiffResource::kStatusCount]
84363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                                          [DiffResource::kStatusCount]) {
85363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        typedef DiffResource::Status Status;
86363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
87363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        for (int base = 0; base < DiffResource::kStatusCount; ++base) {
88363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            Status baseStatus = static_cast<Status>(base);
89363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
90363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                Status comparisonStatus = static_cast<Status>(comparison);
91363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                const FileArray& fileArray = fStatusOfType[base][comparison];
92363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                if (fileArray.count() > 0) {
93363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    if (failOnStatusType[base][comparison]) {
94363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                        printf("   [*] ");
95363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    } else {
96363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                        printf("   [_] ");
97363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    }
98363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    printContents(fileArray,
99363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                                  DiffResource::getStatusDescription(baseStatus),
100363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                                  DiffResource::getStatusDescription(comparisonStatus),
101363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                                  listFilenames);
102363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                }
103363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
104363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
105363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
10680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
10780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    // Print a line about the contents of this FileArray to stdout.
10880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    void printContents(const FileArray& fileArray, const char* headerText, bool listFilenames) {
10980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        int n = fileArray.count();
11080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        printf("%d file pairs %s", n, headerText);
11180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (listFilenames) {
11280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            printf(": ");
11380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            for (int i = 0; i < n; ++i) {
11480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                printf("%s ", fileArray[i]->c_str());
11580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            }
11680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
11780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        printf("\n");
11880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
11980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
120363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    void print(bool listFilenames, bool failOnResultType[DiffRecord::kResultCount],
121363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger               bool failOnStatusType[DiffResource::kStatusCount]
122363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                                    [DiffResource::kStatusCount]) {
12380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        printf("\ncompared %d file pairs:\n", fNumMatches + fNumMismatches);
124363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        for (int resultInt = 0; resultInt < DiffRecord::kResultCount; ++resultInt) {
125363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            DiffRecord::Result result = static_cast<DiffRecord::Result>(resultInt);
12680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            if (failOnResultType[result]) {
12780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                printf("[*] ");
12880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            } else {
12980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                printf("[_] ");
13080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            }
131363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            printContents(fResultsOfType[result], DiffRecord::getResultDescription(result),
132363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                          listFilenames);
133363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            if (DiffRecord::kCouldNotCompare_Result == result) {
134363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                printStatus(listFilenames, failOnStatusType);
135363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
13680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
13780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        printf("(results marked with [*] will cause nonzero return value)\n");
13880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        printf("\nnumber of mismatching file pairs: %d\n", fNumMismatches);
13980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (fNumMismatches > 0) {
14080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            printf("Maximum pixel intensity mismatch %d\n", fMaxMismatchV);
14180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            printf("Largest area mismatch was %.2f%% of pixels\n",fMaxMismatchPercent);
14280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
14380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
14480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
14580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    void add (DiffRecord* drp) {
14680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        uint32_t mismatchValue;
14780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
148363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (drp->fBase.fFilename.equals(drp->fComparison.fFilename)) {
149363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            fResultsOfType[drp->fResult].push(new SkString(drp->fBase.fFilename));
150363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        } else {
151363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            SkString* blame = new SkString("(");
152363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            blame->append(drp->fBase.fFilename);
153363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            blame->append(", ");
154363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            blame->append(drp->fComparison.fFilename);
155363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            blame->append(")");
156363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            fResultsOfType[drp->fResult].push(blame);
157363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
15880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        switch (drp->fResult) {
159363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger          case DiffRecord::kEqualBits_Result:
16080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            fNumMatches++;
16180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            break;
162363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger          case DiffRecord::kEqualPixels_Result:
16380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            fNumMatches++;
16480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            break;
165363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger          case DiffRecord::kDifferentSizes_Result:
16680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            fNumMismatches++;
16780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            break;
168363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger          case DiffRecord::kDifferentPixels_Result:
16980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            fNumMismatches++;
17080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            if (drp->fFractionDifference * 100 > fMaxMismatchPercent) {
17180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                fMaxMismatchPercent = drp->fFractionDifference * 100;
17280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            }
17380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            mismatchValue = MAX3(drp->fMaxMismatchR, drp->fMaxMismatchG,
17480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                                 drp->fMaxMismatchB);
17580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            if (mismatchValue > fMaxMismatchV) {
17680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                fMaxMismatchV = mismatchValue;
17780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            }
17880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            break;
179363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger          case DiffRecord::kCouldNotCompare_Result:
18080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            fNumMismatches++;
181363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            fStatusOfType[drp->fBase.fStatus][drp->fComparison.fStatus].push(
182363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    new SkString(drp->fBase.fFilename));
18380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            break;
184363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger          case DiffRecord::kUnknown_Result:
18580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            SkDEBUGFAIL("adding uncategorized DiffRecord");
18680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            break;
18780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru          default:
18880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            SkDEBUGFAIL("adding DiffRecord with unhandled fResult value");
18980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            break;
19080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
19180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
19280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru};
19380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
19480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru/// Returns true if string contains any of these substrings.
19580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querustatic bool string_contains_any_of(const SkString& string,
19680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                                   const StringArray& substrings) {
19780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    for (int i = 0; i < substrings.count(); i++) {
19880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (string.contains(substrings[i]->c_str())) {
19980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            return true;
20080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
20180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
20280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    return false;
20380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru}
20480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
20580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru/// Internal (potentially recursive) implementation of get_file_list.
20680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querustatic void get_file_list_subdir(const SkString& rootDir, const SkString& subDir,
20780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                                 const StringArray& matchSubstrings,
20880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                                 const StringArray& nomatchSubstrings,
20980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                                 bool recurseIntoSubdirs, FileArray *files) {
21080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    bool isSubDirEmpty = subDir.isEmpty();
21180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    SkString dir(rootDir);
21280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    if (!isSubDirEmpty) {
21380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        dir.append(PATH_DIV_STR);
21480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        dir.append(subDir);
21580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
21680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
21780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    // Iterate over files (not directories) within dir.
21880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    SkOSFile::Iter fileIterator(dir.c_str());
21980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    SkString fileName;
22080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    while (fileIterator.next(&fileName, false)) {
22180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (fileName.startsWith(".")) {
22280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            continue;
22380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
22480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        SkString pathRelativeToRootDir(subDir);
22580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (!isSubDirEmpty) {
22680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            pathRelativeToRootDir.append(PATH_DIV_STR);
22780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
22880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        pathRelativeToRootDir.append(fileName);
22980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (string_contains_any_of(pathRelativeToRootDir, matchSubstrings) &&
23080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            !string_contains_any_of(pathRelativeToRootDir, nomatchSubstrings)) {
23180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            files->push(new SkString(pathRelativeToRootDir));
23280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
23380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
23480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
23580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    // Recurse into any non-ignored subdirectories.
23680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    if (recurseIntoSubdirs) {
23780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        SkOSFile::Iter dirIterator(dir.c_str());
23880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        SkString dirName;
23980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        while (dirIterator.next(&dirName, true)) {
24080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            if (dirName.startsWith(".")) {
24180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                continue;
24280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            }
24380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            SkString pathRelativeToRootDir(subDir);
24480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            if (!isSubDirEmpty) {
24580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                pathRelativeToRootDir.append(PATH_DIV_STR);
24680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            }
24780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            pathRelativeToRootDir.append(dirName);
24880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            if (!string_contains_any_of(pathRelativeToRootDir, nomatchSubstrings)) {
24980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                get_file_list_subdir(rootDir, pathRelativeToRootDir,
25080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                                     matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
25180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                                     files);
25280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            }
25380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
25480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
25580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru}
25680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
25780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru/// Iterate over dir and get all files whose filename:
25880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru///  - matches any of the substrings in matchSubstrings, but...
25980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru///  - DOES NOT match any of the substrings in nomatchSubstrings
26080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru///  - DOES NOT start with a dot (.)
26180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru/// Adds the matching files to the list in *files.
26280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querustatic void get_file_list(const SkString& dir,
26380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                          const StringArray& matchSubstrings,
26480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                          const StringArray& nomatchSubstrings,
26580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                          bool recurseIntoSubdirs, FileArray *files) {
26680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    get_file_list_subdir(dir, SkString(""),
26780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                         matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
26880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                         files);
26980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru}
27080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
27180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querustatic void release_file_list(FileArray *files) {
27280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    files->deleteAll();
27380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru}
27480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
27580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru/// Comparison routines for qsort, sort by file names.
27680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querustatic int compare_file_name_metrics(SkString **lhs, SkString **rhs) {
27780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    return strcmp((*lhs)->c_str(), (*rhs)->c_str());
27880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru}
27980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
280363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerclass AutoReleasePixels {
281363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerpublic:
282363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    AutoReleasePixels(DiffRecord* drp)
283363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    : fDrp(drp) {
284363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        SkASSERT(drp != NULL);
285363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
286363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    ~AutoReleasePixels() {
287363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        fDrp->fBase.fBitmap.setPixelRef(NULL);
288363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        fDrp->fComparison.fBitmap.setPixelRef(NULL);
289363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        fDrp->fDifference.fBitmap.setPixelRef(NULL);
290363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        fDrp->fWhite.fBitmap.setPixelRef(NULL);
291363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
292363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
293363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerprivate:
294363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    DiffRecord* fDrp;
295363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger};
296363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
297363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerstatic void get_bounds(DiffResource& resource, const char* name) {
298363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (resource.fBitmap.empty() && !DiffResource::isStatusFailed(resource.fStatus)) {
299363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        SkAutoDataUnref fileBits(read_file(resource.fFullPath.c_str()));
300363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (NULL == fileBits) {
301363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            SkDebugf("WARNING: couldn't read %s file <%s>\n", name, resource.fFullPath.c_str());
302363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            resource.fStatus = DiffResource::kCouldNotRead_Status;
303363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        } else {
304363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            get_bitmap(fileBits, resource, SkImageDecoder::kDecodeBounds_Mode);
305363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
306363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
307363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger}
308363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
309363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerstatic void get_bounds(DiffRecord& drp) {
310363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    get_bounds(drp.fBase, "base");
311363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    get_bounds(drp.fComparison, "comparison");
312363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger}
313363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
31480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru/// Creates difference images, returns the number that have a 0 metric.
31580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru/// If outputDir.isEmpty(), don't write out diff files.
31680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querustatic void create_diff_images (DiffMetricProc dmp,
31780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                                const int colorThreshold,
31880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                                RecordArray* differences,
31980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                                const SkString& baseDir,
32080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                                const SkString& comparisonDir,
32180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                                const SkString& outputDir,
32280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                                const StringArray& matchSubstrings,
32380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                                const StringArray& nomatchSubstrings,
32480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                                bool recurseIntoSubdirs,
325363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                                bool getBounds,
32680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                                DiffSummary* summary) {
32780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    SkASSERT(!baseDir.isEmpty());
32880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    SkASSERT(!comparisonDir.isEmpty());
32980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
33080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    FileArray baseFiles;
33180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    FileArray comparisonFiles;
33280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
33380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    get_file_list(baseDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, &baseFiles);
33480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    get_file_list(comparisonDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
33580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                  &comparisonFiles);
33680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
33780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    if (!baseFiles.isEmpty()) {
33880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        qsort(baseFiles.begin(), baseFiles.count(), sizeof(SkString*),
33980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru              SkCastForQSort(compare_file_name_metrics));
34080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
34180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    if (!comparisonFiles.isEmpty()) {
34280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        qsort(comparisonFiles.begin(), comparisonFiles.count(),
34380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru              sizeof(SkString*), SkCastForQSort(compare_file_name_metrics));
34480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
34580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
34680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    int i = 0;
34780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    int j = 0;
34880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
34980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    while (i < baseFiles.count() &&
35080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru           j < comparisonFiles.count()) {
35180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
352363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        SkString basePath(baseDir);
353363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        SkString comparisonPath(comparisonDir);
35480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
355363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        DiffRecord *drp = new DiffRecord;
356363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        int v = strcmp(baseFiles[i]->c_str(), comparisonFiles[j]->c_str());
35780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
35880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (v < 0) {
35980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            // in baseDir, but not in comparisonDir
360363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            drp->fResult = DiffRecord::kCouldNotCompare_Result;
361363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
362363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            basePath.append(*baseFiles[i]);
363363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            comparisonPath.append(*baseFiles[i]);
364363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
365363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            drp->fBase.fFilename = *baseFiles[i];
366363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            drp->fBase.fFullPath = basePath;
367363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            drp->fBase.fStatus = DiffResource::kExists_Status;
368363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
369363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            drp->fComparison.fFilename = *baseFiles[i];
370363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            drp->fComparison.fFullPath = comparisonPath;
371363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            drp->fComparison.fStatus = DiffResource::kDoesNotExist_Status;
372363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
37380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            ++i;
37480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        } else if (v > 0) {
37580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            // in comparisonDir, but not in baseDir
376363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            drp->fResult = DiffRecord::kCouldNotCompare_Result;
377363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
378363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            basePath.append(*comparisonFiles[j]);
379363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            comparisonPath.append(*comparisonFiles[j]);
380363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
381363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            drp->fBase.fFilename = *comparisonFiles[j];
382363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            drp->fBase.fFullPath = basePath;
383363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            drp->fBase.fStatus = DiffResource::kDoesNotExist_Status;
384363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
385363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            drp->fComparison.fFilename = *comparisonFiles[j];
386363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            drp->fComparison.fFullPath = comparisonPath;
387363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            drp->fComparison.fStatus = DiffResource::kExists_Status;
388363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
38980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            ++j;
39080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        } else {
39180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            // Found the same filename in both baseDir and comparisonDir.
392363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            SkASSERT(DiffRecord::kUnknown_Result == drp->fResult);
393363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
394363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            basePath.append(*baseFiles[i]);
395363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            comparisonPath.append(*comparisonFiles[j]);
396363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
397363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            drp->fBase.fFilename = *baseFiles[i];
398363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            drp->fBase.fFullPath = basePath;
399363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            drp->fBase.fStatus = DiffResource::kExists_Status;
400363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
401363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            drp->fComparison.fFilename = *comparisonFiles[j];
402363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            drp->fComparison.fFullPath = comparisonPath;
403363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            drp->fComparison.fStatus = DiffResource::kExists_Status;
404363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
405363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            SkAutoDataUnref baseFileBits(read_file(drp->fBase.fFullPath.c_str()));
406363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            if (NULL != baseFileBits) {
407363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                drp->fBase.fStatus = DiffResource::kRead_Status;
408363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
409363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            SkAutoDataUnref comparisonFileBits(read_file(drp->fComparison.fFullPath.c_str()));
410363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            if (NULL != comparisonFileBits) {
411363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                drp->fComparison.fStatus = DiffResource::kRead_Status;
412363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
413363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            if (NULL == baseFileBits || NULL == comparisonFileBits) {
414363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                if (NULL == baseFileBits) {
415363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    drp->fBase.fStatus = DiffResource::kCouldNotRead_Status;
416363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                }
417363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                if (NULL == comparisonFileBits) {
418363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    drp->fComparison.fStatus = DiffResource::kCouldNotRead_Status;
419363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                }
420363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                drp->fResult = DiffRecord::kCouldNotCompare_Result;
421363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
422363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            } else if (are_buffers_equal(baseFileBits, comparisonFileBits)) {
423363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                drp->fResult = DiffRecord::kEqualBits_Result;
424363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
42580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            } else {
426363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                AutoReleasePixels arp(drp);
427363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                get_bitmap(baseFileBits, drp->fBase, SkImageDecoder::kDecodePixels_Mode);
428363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                get_bitmap(comparisonFileBits, drp->fComparison,
429363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                           SkImageDecoder::kDecodePixels_Mode);
430363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                if (DiffResource::kDecoded_Status == drp->fBase.fStatus &&
431363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    DiffResource::kDecoded_Status == drp->fComparison.fStatus) {
43280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                    create_and_write_diff_image(drp, dmp, colorThreshold,
433363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                                                outputDir, drp->fBase.fFilename);
43480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                } else {
435363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    drp->fResult = DiffRecord::kCouldNotCompare_Result;
43680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                }
43780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            }
438363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
43980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            ++i;
44080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            ++j;
44180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
442363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
443363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (getBounds) {
444363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            get_bounds(*drp);
445363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
446363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        SkASSERT(DiffRecord::kUnknown_Result != drp->fResult);
44780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        differences->push(drp);
44880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        summary->add(drp);
44980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
45080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
45180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    for (; i < baseFiles.count(); ++i) {
45280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        // files only in baseDir
453363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        DiffRecord *drp = new DiffRecord();
454363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fBase.fFilename = *baseFiles[i];
455363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fBase.fFullPath = baseDir;
456363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fBase.fFullPath.append(drp->fBase.fFilename);
457363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fBase.fStatus = DiffResource::kExists_Status;
458363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
459363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fComparison.fFilename = *baseFiles[i];
460363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fComparison.fFullPath = comparisonDir;
461363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fComparison.fFullPath.append(drp->fComparison.fFilename);
462363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fComparison.fStatus = DiffResource::kDoesNotExist_Status;
463363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
464363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fResult = DiffRecord::kCouldNotCompare_Result;
465363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (getBounds) {
466363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            get_bounds(*drp);
467363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
46880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        differences->push(drp);
46980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        summary->add(drp);
47080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
47180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
47280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    for (; j < comparisonFiles.count(); ++j) {
47380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        // files only in comparisonDir
474363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        DiffRecord *drp = new DiffRecord();
475363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fBase.fFilename = *comparisonFiles[j];
476363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fBase.fFullPath = baseDir;
477363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fBase.fFullPath.append(drp->fBase.fFilename);
478363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fBase.fStatus = DiffResource::kDoesNotExist_Status;
479363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
480363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fComparison.fFilename = *comparisonFiles[j];
481363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fComparison.fFullPath = comparisonDir;
482363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fComparison.fFullPath.append(drp->fComparison.fFilename);
483363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fComparison.fStatus = DiffResource::kExists_Status;
484363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
485363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        drp->fResult = DiffRecord::kCouldNotCompare_Result;
486363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (getBounds) {
487363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            get_bounds(*drp);
488363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
48980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        differences->push(drp);
49080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        summary->add(drp);
49180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
49280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
49380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    release_file_list(&baseFiles);
49480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    release_file_list(&comparisonFiles);
49580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru}
49680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
49780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querustatic void usage (char * argv0) {
49880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    SkDebugf("Skia baseline image diff tool\n");
49980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    SkDebugf("\n"
50080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"Usage: \n"
501363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"    %s <baseDir> <comparisonDir> [outputDir] \n", argv0);
50280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    SkDebugf(
50380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\nArguments:"
50480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n    --failonresult <result>: After comparing all file pairs, exit with nonzero"
50580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n                             return code (number of file pairs yielding this"
50680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n                             result) if any file pairs yielded this result."
50780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n                             This flag may be repeated, in which case the"
50880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n                             return code will be the number of fail pairs"
50980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n                             yielding ANY of these results."
510363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n    --failonstatus <baseStatus> <comparisonStatus>: exit with nonzero return"
511363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n                             code if any file pairs yielded this status."
51280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n    --help: display this info"
51380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n    --listfilenames: list all filenames for each result type in stdout"
51480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n    --match <substring>: compare files whose filenames contain this substring;"
51580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n                         if unspecified, compare ALL files."
51680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n                         this flag may be repeated."
51780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n    --nodiffs: don't write out image diffs or index.html, just generate"
51880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n               report on stdout"
51980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n    --nomatch <substring>: regardless of --match, DO NOT compare files whose"
52080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n                           filenames contain this substring."
52180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n                           this flag may be repeated."
52280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n    --noprintdirs: do not print the directories used."
52380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n    --norecurse: do not recurse into subdirectories."
52480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n    --sortbymaxmismatch: sort by worst color channel mismatch;"
52580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n                         break ties with -sortbymismatch"
52680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n    --sortbymismatch: sort by average color channel mismatch"
52780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n    --threshold <n>: only report differences > n (per color channel) [default 0]"
52880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n    --weighted: sort by # pixels different weighted by color difference"
52980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n"
53080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n    baseDir: directory to read baseline images from."
53180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n    comparisonDir: directory to read comparison images from"
53280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n    outputDir: directory to write difference images and index.html to;"
53380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n               defaults to comparisonDir"
53480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n"
53580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\nIf no sort is specified, it will sort by fraction of pixels mismatching."
53680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru"\n");
53780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru}
53880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
53980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queruconst int kNoError = 0;
54080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queruconst int kGenericError = -1;
54180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
54280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queruint tool_main(int argc, char** argv);
54380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queruint tool_main(int argc, char** argv) {
54480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    DiffMetricProc diffProc = compute_diff_pmcolor;
54580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    int (*sortProc)(const void*, const void*) = compare<CompareDiffMetrics>;
54680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
54780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    // Maximum error tolerated in any one color channel in any one pixel before
54880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    // a difference is reported.
54980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    int colorThreshold = 0;
55080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    SkString baseDir;
55180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    SkString comparisonDir;
55280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    SkString outputDir;
55380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
55480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    StringArray matchSubstrings;
55580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    StringArray nomatchSubstrings;
55680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
55780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    bool generateDiffs = true;
55880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    bool listFilenames = false;
55980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    bool printDirNames = true;
56080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    bool recurseIntoSubdirs = true;
56180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
56280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    RecordArray differences;
56380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    DiffSummary summary;
56480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
565363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    bool failOnResultType[DiffRecord::kResultCount];
566363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    for (int i = 0; i < DiffRecord::kResultCount; i++) {
56780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        failOnResultType[i] = false;
56880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
56980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
570363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    bool failOnStatusType[DiffResource::kStatusCount][DiffResource::kStatusCount];
571363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    for (int base = 0; base < DiffResource::kStatusCount; ++base) {
572363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
573363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            failOnStatusType[base][comparison] = false;
574363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
575363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
576363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
57780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    int i;
57880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    int numUnflaggedArguments = 0;
57980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    for (i = 1; i < argc; i++) {
58080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (!strcmp(argv[i], "--failonresult")) {
581363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            if (argc == ++i) {
582363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                SkDebugf("failonresult expects one argument.\n");
583363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                continue;
584363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
585363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            DiffRecord::Result type = DiffRecord::getResultByName(argv[i]);
586363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            if (type != DiffRecord::kResultCount) {
587363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                failOnResultType[type] = true;
588363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            } else {
589363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                SkDebugf("ignoring unrecognized result <%s>\n", argv[i]);
590363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
591363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            continue;
592363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
593363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (!strcmp(argv[i], "--failonstatus")) {
594363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            if (argc == ++i) {
595363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                SkDebugf("failonstatus missing base status.\n");
596363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                continue;
597363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
598363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            bool baseStatuses[DiffResource::kStatusCount];
599363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            if (!DiffResource::getMatchingStatuses(argv[i], baseStatuses)) {
600363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                SkDebugf("unrecognized base status <%s>\n", argv[i]);
601363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
602363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
603363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            if (argc == ++i) {
604363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                SkDebugf("failonstatus missing comparison status.\n");
605363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                continue;
606363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
607363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            bool comparisonStatuses[DiffResource::kStatusCount];
608363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            if (!DiffResource::getMatchingStatuses(argv[i], comparisonStatuses)) {
609363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                SkDebugf("unrecognized comarison status <%s>\n", argv[i]);
610363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
611363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
612363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            for (int base = 0; base < DiffResource::kStatusCount; ++base) {
613363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
614363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    failOnStatusType[base][comparison] |=
615363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                        baseStatuses[base] && comparisonStatuses[comparison];
616363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                }
617363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
61880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            continue;
61980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
62080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (!strcmp(argv[i], "--help")) {
62180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            usage(argv[0]);
62280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            return kNoError;
62380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
62480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (!strcmp(argv[i], "--listfilenames")) {
62580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            listFilenames = true;
62680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            continue;
62780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
62880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (!strcmp(argv[i], "--match")) {
62980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            matchSubstrings.push(new SkString(argv[++i]));
63080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            continue;
63180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
63280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (!strcmp(argv[i], "--nodiffs")) {
63380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            generateDiffs = false;
63480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            continue;
63580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
63680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (!strcmp(argv[i], "--nomatch")) {
63780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            nomatchSubstrings.push(new SkString(argv[++i]));
63880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            continue;
63980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
64080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (!strcmp(argv[i], "--noprintdirs")) {
64180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            printDirNames = false;
64280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            continue;
64380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
64480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (!strcmp(argv[i], "--norecurse")) {
64580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            recurseIntoSubdirs = false;
64680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            continue;
64780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
64880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (!strcmp(argv[i], "--sortbymaxmismatch")) {
64980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            sortProc = compare<CompareDiffMaxMismatches>;
65080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            continue;
65180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
65280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (!strcmp(argv[i], "--sortbymismatch")) {
65380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            sortProc = compare<CompareDiffMeanMismatches>;
65480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            continue;
65580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
65680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (!strcmp(argv[i], "--threshold")) {
65780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            colorThreshold = atoi(argv[++i]);
65880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            continue;
65980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
66080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (!strcmp(argv[i], "--weighted")) {
66180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            sortProc = compare<CompareDiffWeighted>;
66280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            continue;
66380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
66480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (argv[i][0] != '-') {
66580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            switch (numUnflaggedArguments++) {
66680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                case 0:
66780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                    baseDir.set(argv[i]);
66880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                    continue;
66980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                case 1:
67080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                    comparisonDir.set(argv[i]);
67180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                    continue;
67280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                case 2:
67380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                    outputDir.set(argv[i]);
67480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                    continue;
67580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                default:
67680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                    SkDebugf("extra unflagged argument <%s>\n", argv[i]);
67780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                    usage(argv[0]);
67880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                    return kGenericError;
67980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            }
68080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
68180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
68280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        SkDebugf("Unrecognized argument <%s>\n", argv[i]);
68380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        usage(argv[0]);
68480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        return kGenericError;
68580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
68680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
68780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    if (numUnflaggedArguments == 2) {
68880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        outputDir = comparisonDir;
68980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    } else if (numUnflaggedArguments != 3) {
69080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        usage(argv[0]);
69180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        return kGenericError;
69280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
69380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
69480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    if (!baseDir.endsWith(PATH_DIV_STR)) {
69580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        baseDir.append(PATH_DIV_STR);
69680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
69780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    if (printDirNames) {
69880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        printf("baseDir is [%s]\n", baseDir.c_str());
69980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
70080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
70180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    if (!comparisonDir.endsWith(PATH_DIV_STR)) {
70280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        comparisonDir.append(PATH_DIV_STR);
70380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
70480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    if (printDirNames) {
70580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        printf("comparisonDir is [%s]\n", comparisonDir.c_str());
70680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
70780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
70880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    if (!outputDir.endsWith(PATH_DIV_STR)) {
70980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        outputDir.append(PATH_DIV_STR);
71080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
71180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    if (generateDiffs) {
71280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (printDirNames) {
71380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            printf("writing diffs to outputDir is [%s]\n", outputDir.c_str());
71480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
71580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    } else {
71680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (printDirNames) {
71780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            printf("not writing any diffs to outputDir [%s]\n", outputDir.c_str());
71880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
71980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        outputDir.set("");
72080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
72180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
72280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    // If no matchSubstrings were specified, match ALL strings
72380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    // (except for whatever nomatchSubstrings were specified, if any).
72480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    if (matchSubstrings.isEmpty()) {
72580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        matchSubstrings.push(new SkString(""));
72680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
72780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
72880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    create_diff_images(diffProc, colorThreshold, &differences,
72980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                       baseDir, comparisonDir, outputDir,
730363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                       matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, generateDiffs,
731363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                       &summary);
732363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    summary.print(listFilenames, failOnResultType, failOnStatusType);
73380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
73480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    if (differences.count()) {
73580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        qsort(differences.begin(), differences.count(),
73680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru              sizeof(DiffRecord*), sortProc);
73780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
73880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
73980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    if (generateDiffs) {
74080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        print_diff_page(summary.fNumMatches, colorThreshold, differences,
74180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru                        baseDir, comparisonDir, outputDir);
74280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
74380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
74480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    for (i = 0; i < differences.count(); i++) {
74580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        delete differences[i];
74680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
74780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    matchSubstrings.deleteAll();
74880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    nomatchSubstrings.deleteAll();
74980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
75080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    int num_failing_results = 0;
751363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    for (int i = 0; i < DiffRecord::kResultCount; i++) {
75280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        if (failOnResultType[i]) {
75380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru            num_failing_results += summary.fResultsOfType[i].count();
75480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru        }
75580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    }
756363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (!failOnResultType[DiffRecord::kCouldNotCompare_Result]) {
757363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        for (int base = 0; base < DiffResource::kStatusCount; ++base) {
758363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
759363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                if (failOnStatusType[base][comparison]) {
760363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    num_failing_results += summary.fStatusOfType[base][comparison].count();
761363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                }
762363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
763363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
764363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
76580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
76680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    // On Linux (and maybe other platforms too), any results outside of the
76780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    // range [0...255] are wrapped (mod 256).  Do the conversion ourselves, to
76880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    // make sure that we only return 0 when there were no failures.
76980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    return (num_failing_results > 255) ? 255 : num_failing_results;
77080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru}
77180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru
77280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#if !defined SK_BUILD_FOR_IOS
77380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queruint main(int argc, char * const argv[]) {
77480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru    return tool_main(argc, (char**) argv);
77580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru}
77680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#endif
777