1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com/*
2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Copyright 2011 Google Inc.
3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com *
4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Use of this source code is governed by a BSD-style license that can be
5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * found in the LICENSE file.
6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com */
7e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com#include "skdiff.h"
8e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com#include "skdiff_html.h"
9e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com#include "skdiff_utils.h"
10e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com#include "SkBitmap.h"
1146256ea736868e6df1a6102672fc4bb41f99d29depoger@google.com#include "SkData.h"
124b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com#include "SkImageEncoder.h"
134b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com#include "SkOSFile.h"
14bf111d7bc9ba3857433e30eae27f0272c34ed0fbBen Wagner#include "SkOSPath.h"
154b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com#include "SkStream.h"
161b3387b5cfef150c3237c6a8700b2d6f3730e4ceHal Canary#include "SkPixelRef.h"
17a7e9f05119174848c6be5028568f46eb5e85398ebungeman#include "../private/SkTDArray.h"
1876a834a107af38fb2e85413bc7a46e555c444a85reed#include "../private/SkTSearch.h"
194b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com
2060e0fee6d4acff638ccc9670c4055aced529a7a0bungeman#include <stdlib.h>
2160e0fee6d4acff638ccc9670c4055aced529a7a0bungeman
224b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com/**
234b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com * skdiff
244b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com *
254b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com * Given three directory names, expects to find identically-named files in
264b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com * each of the first two; the first are treated as a set of baseline,
274b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com * the second a set of variant images, and a diff image is written into the
284b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com * third directory for each pair.
297d04280a68430d6b472ee67dc8f6092127625e73tomhudson@google.com * Creates an index.html in the current third directory to compare each
304b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com * pair that does not match exactly.
3171329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com * Recursively descends directories, unless run with --norecurse.
32be6188d64721850723c19d5a7a4f007a675300f4epoger@google.com *
33be6188d64721850723c19d5a7a4f007a675300f4epoger@google.com * Returns zero exit code if all images match across baseDir and comparisonDir.
344b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com */
354b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com
36a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.comtypedef SkTDArray<SkString*> StringArray;
37a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.comtypedef StringArray FileArray;
385fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com
393a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breedstatic void add_unique_basename(StringArray* array, const SkString& filename) {
403a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed    // trim off dirs
413a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed    const char* src = filename.c_str();
42bf111d7bc9ba3857433e30eae27f0272c34ed0fbBen Wagner    const char* trimmed = strrchr(src, SkOSPath::SEPARATOR);
433a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed    if (trimmed) {
443a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed        trimmed += 1;   // skip the separator
453a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed    } else {
463a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed        trimmed = src;
473a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed    }
483a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed    const char* end = strrchr(trimmed, '.');
493a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed    if (!end) {
503a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed        end = trimmed + strlen(trimmed);
513a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed    }
523a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed    SkString result(trimmed, end - trimmed);
533a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed
543a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed    // only add unique entries
553a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed    for (int i = 0; i < array->count(); ++i) {
563a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed        if (*array->getAt(i) == result) {
573a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed            return;
583a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed        }
593a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed    }
603a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed    *array->append() = new SkString(result);
613a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed}
623a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed
639dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.comstruct DiffSummary {
649dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com    DiffSummary ()
65e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        : fNumMatches(0)
66e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        , fNumMismatches(0)
67e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        , fMaxMismatchV(0)
68fe9172705791848ebfe5b5630973b7e67305f64cbungeman        , fMaxMismatchPercent(0) { }
699dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com
705fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com    ~DiffSummary() {
71e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        for (int i = 0; i < DiffRecord::kResultCount; ++i) {
7276222c0ef2c5e63d2d9875e1cdb89e52049c7754epoger@google.com            fResultsOfType[i].deleteAll();
7376222c0ef2c5e63d2d9875e1cdb89e52049c7754epoger@google.com        }
74e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        for (int base = 0; base < DiffResource::kStatusCount; ++base) {
75e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
76e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                fStatusOfType[base][comparison].deleteAll();
77e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            }
780264fb4543b0d8cebe00f1ee32433784f4ceb074skia.committer@gmail.com        }
795fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com    }
805fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com
819dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com    uint32_t fNumMatches;
829dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com    uint32_t fNumMismatches;
839dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com    uint32_t fMaxMismatchV;
849dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com    float fMaxMismatchPercent;
859dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com
86e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    FileArray fResultsOfType[DiffRecord::kResultCount];
87e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    FileArray fStatusOfType[DiffResource::kStatusCount][DiffResource::kStatusCount];
88e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
893a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed    StringArray fFailedBaseNames[DiffRecord::kResultCount];
903a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed
91e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    void printContents(const FileArray& fileArray,
92e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                       const char* baseStatus, const char* comparisonStatus,
93e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                       bool listFilenames) {
94e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        int n = fileArray.count();
95e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        printf("%d file pairs %s in baseDir and %s in comparisonDir",
96e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                n,            baseStatus,       comparisonStatus);
97e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        if (listFilenames) {
98e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            printf(": ");
99e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            for (int i = 0; i < n; ++i) {
100e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                printf("%s ", fileArray[i]->c_str());
101e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            }
102e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        }
103e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        printf("\n");
104e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    }
105e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
106e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    void printStatus(bool listFilenames,
107e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                     bool failOnStatusType[DiffResource::kStatusCount]
108e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                                          [DiffResource::kStatusCount]) {
109e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        typedef DiffResource::Status Status;
110e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
111e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        for (int base = 0; base < DiffResource::kStatusCount; ++base) {
112e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            Status baseStatus = static_cast<Status>(base);
113e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
114e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                Status comparisonStatus = static_cast<Status>(comparison);
115e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                const FileArray& fileArray = fStatusOfType[base][comparison];
116e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                if (fileArray.count() > 0) {
117e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                    if (failOnStatusType[base][comparison]) {
118e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                        printf("   [*] ");
119e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                    } else {
120e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                        printf("   [_] ");
121e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                    }
122e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                    printContents(fileArray,
123e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                                  DiffResource::getStatusDescription(baseStatus),
124e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                                  DiffResource::getStatusDescription(comparisonStatus),
125e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                                  listFilenames);
126e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                }
127e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            }
128e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        }
129e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    }
1305fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com
1313af4ff46a7461f78c8ab24d1e8213b187e8472e2epoger@google.com    // Print a line about the contents of this FileArray to stdout.
13246a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com    void printContents(const FileArray& fileArray, const char* headerText, bool listFilenames) {
13376222c0ef2c5e63d2d9875e1cdb89e52049c7754epoger@google.com        int n = fileArray.count();
1343af4ff46a7461f78c8ab24d1e8213b187e8472e2epoger@google.com        printf("%d file pairs %s", n, headerText);
1353af4ff46a7461f78c8ab24d1e8213b187e8472e2epoger@google.com        if (listFilenames) {
1363af4ff46a7461f78c8ab24d1e8213b187e8472e2epoger@google.com            printf(": ");
1373af4ff46a7461f78c8ab24d1e8213b187e8472e2epoger@google.com            for (int i = 0; i < n; ++i) {
1383af4ff46a7461f78c8ab24d1e8213b187e8472e2epoger@google.com                printf("%s ", fileArray[i]->c_str());
1395fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com            }
1405fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com        }
1413af4ff46a7461f78c8ab24d1e8213b187e8472e2epoger@google.com        printf("\n");
14276222c0ef2c5e63d2d9875e1cdb89e52049c7754epoger@google.com    }
14376222c0ef2c5e63d2d9875e1cdb89e52049c7754epoger@google.com
144e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    void print(bool listFilenames, bool failOnResultType[DiffRecord::kResultCount],
145e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com               bool failOnStatusType[DiffResource::kStatusCount]
146e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                                    [DiffResource::kStatusCount]) {
1473af4ff46a7461f78c8ab24d1e8213b187e8472e2epoger@google.com        printf("\ncompared %d file pairs:\n", fNumMatches + fNumMismatches);
148e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        for (int resultInt = 0; resultInt < DiffRecord::kResultCount; ++resultInt) {
149e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            DiffRecord::Result result = static_cast<DiffRecord::Result>(resultInt);
1503af4ff46a7461f78c8ab24d1e8213b187e8472e2epoger@google.com            if (failOnResultType[result]) {
1513af4ff46a7461f78c8ab24d1e8213b187e8472e2epoger@google.com                printf("[*] ");
1523af4ff46a7461f78c8ab24d1e8213b187e8472e2epoger@google.com            } else {
1533af4ff46a7461f78c8ab24d1e8213b187e8472e2epoger@google.com                printf("[_] ");
1543af4ff46a7461f78c8ab24d1e8213b187e8472e2epoger@google.com            }
155e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            printContents(fResultsOfType[result], DiffRecord::getResultDescription(result),
156e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                          listFilenames);
157e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            if (DiffRecord::kCouldNotCompare_Result == result) {
158e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                printStatus(listFilenames, failOnStatusType);
159e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            }
16046a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com        }
1613af4ff46a7461f78c8ab24d1e8213b187e8472e2epoger@google.com        printf("(results marked with [*] will cause nonzero return value)\n");
1623af4ff46a7461f78c8ab24d1e8213b187e8472e2epoger@google.com        printf("\nnumber of mismatching file pairs: %d\n", fNumMismatches);
1639dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com        if (fNumMismatches > 0) {
1649dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com            printf("Maximum pixel intensity mismatch %d\n", fMaxMismatchV);
16546a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com            printf("Largest area mismatch was %.2f%% of pixels\n",fMaxMismatchPercent);
1669dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com        }
1679dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com    }
1689dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com
1693a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed    void printfFailingBaseNames(const char separator[]) {
1703a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed        for (int resultInt = 0; resultInt < DiffRecord::kResultCount; ++resultInt) {
1713a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed            const StringArray& array = fFailedBaseNames[resultInt];
1723a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed            if (array.count()) {
1733a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed                printf("%s [%d]%s", DiffRecord::ResultNames[resultInt], array.count(), separator);
1743a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed                for (int j = 0; j < array.count(); ++j) {
1753a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed                    printf("%s%s", array[j]->c_str(), separator);
1763a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed                }
1773a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed                printf("\n");
1783a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed            }
1793a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed        }
1803a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed    }
1813a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed
1829dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com    void add (DiffRecord* drp) {
18346256ea736868e6df1a6102672fc4bb41f99d29depoger@google.com        uint32_t mismatchValue;
184292aff6aca8677d5159cc2f7fa2bb933a450f049epoger@google.com
185e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        if (drp->fBase.fFilename.equals(drp->fComparison.fFilename)) {
186e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            fResultsOfType[drp->fResult].push(new SkString(drp->fBase.fFilename));
187e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        } else {
188e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            SkString* blame = new SkString("(");
189e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            blame->append(drp->fBase.fFilename);
190e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            blame->append(", ");
191e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            blame->append(drp->fComparison.fFilename);
192e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            blame->append(")");
193e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            fResultsOfType[drp->fResult].push(blame);
194e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        }
195292aff6aca8677d5159cc2f7fa2bb933a450f049epoger@google.com        switch (drp->fResult) {
196e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com          case DiffRecord::kEqualBits_Result:
197292aff6aca8677d5159cc2f7fa2bb933a450f049epoger@google.com            fNumMatches++;
198292aff6aca8677d5159cc2f7fa2bb933a450f049epoger@google.com            break;
199e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com          case DiffRecord::kEqualPixels_Result:
20046256ea736868e6df1a6102672fc4bb41f99d29depoger@google.com            fNumMatches++;
201292aff6aca8677d5159cc2f7fa2bb933a450f049epoger@google.com            break;
202e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com          case DiffRecord::kDifferentSizes_Result:
2035fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com            fNumMismatches++;
204292aff6aca8677d5159cc2f7fa2bb933a450f049epoger@google.com            break;
205e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com          case DiffRecord::kDifferentPixels_Result:
2069dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com            fNumMismatches++;
2079dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com            if (drp->fFractionDifference * 100 > fMaxMismatchPercent) {
2089dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com                fMaxMismatchPercent = drp->fFractionDifference * 100;
2099dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com            }
21046256ea736868e6df1a6102672fc4bb41f99d29depoger@google.com            mismatchValue = MAX3(drp->fMaxMismatchR, drp->fMaxMismatchG,
21146256ea736868e6df1a6102672fc4bb41f99d29depoger@google.com                                 drp->fMaxMismatchB);
21246256ea736868e6df1a6102672fc4bb41f99d29depoger@google.com            if (mismatchValue > fMaxMismatchV) {
21346256ea736868e6df1a6102672fc4bb41f99d29depoger@google.com                fMaxMismatchV = mismatchValue;
2149dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com            }
215292aff6aca8677d5159cc2f7fa2bb933a450f049epoger@google.com            break;
216e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com          case DiffRecord::kCouldNotCompare_Result:
21746256ea736868e6df1a6102672fc4bb41f99d29depoger@google.com            fNumMismatches++;
218e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            fStatusOfType[drp->fBase.fStatus][drp->fComparison.fStatus].push(
219e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                    new SkString(drp->fBase.fFilename));
22046256ea736868e6df1a6102672fc4bb41f99d29depoger@google.com            break;
221e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com          case DiffRecord::kUnknown_Result:
22246256ea736868e6df1a6102672fc4bb41f99d29depoger@google.com            SkDEBUGFAIL("adding uncategorized DiffRecord");
22346256ea736868e6df1a6102672fc4bb41f99d29depoger@google.com            break;
22446256ea736868e6df1a6102672fc4bb41f99d29depoger@google.com          default:
22546256ea736868e6df1a6102672fc4bb41f99d29depoger@google.com            SkDEBUGFAIL("adding DiffRecord with unhandled fResult value");
22646256ea736868e6df1a6102672fc4bb41f99d29depoger@google.com            break;
2279dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com        }
2283a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed
2293a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed        switch (drp->fResult) {
2303a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed            case DiffRecord::kEqualBits_Result:
2313a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed            case DiffRecord::kEqualPixels_Result:
2323a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed                break;
2333a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed            default:
2343a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed                add_unique_basename(&fFailedBaseNames[drp->fResult], drp->fBase.fFilename);
2353a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed                break;
2363a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed        }
2379dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com    }
2389dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com};
2399dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com
240a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com/// Returns true if string contains any of these substrings.
241a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.comstatic bool string_contains_any_of(const SkString& string,
242a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com                                   const StringArray& substrings) {
243a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    for (int i = 0; i < substrings.count(); i++) {
244a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com        if (string.contains(substrings[i]->c_str())) {
245a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com            return true;
246a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com        }
247a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    }
248a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    return false;
249a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com}
250a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com
25171329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com/// Internal (potentially recursive) implementation of get_file_list.
25271329d809a42889af8d2cadc4e43c60488a739a1epoger@google.comstatic void get_file_list_subdir(const SkString& rootDir, const SkString& subDir,
25371329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com                                 const StringArray& matchSubstrings,
25471329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com                                 const StringArray& nomatchSubstrings,
25571329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com                                 bool recurseIntoSubdirs, FileArray *files) {
25671329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com    bool isSubDirEmpty = subDir.isEmpty();
25771329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com    SkString dir(rootDir);
25871329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com    if (!isSubDirEmpty) {
25971329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com        dir.append(PATH_DIV_STR);
26071329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com        dir.append(subDir);
26171329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com    }
26271329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com
26371329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com    // Iterate over files (not directories) within dir.
26471329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com    SkOSFile::Iter fileIterator(dir.c_str());
26571329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com    SkString fileName;
26671329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com    while (fileIterator.next(&fileName, false)) {
26771329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com        if (fileName.startsWith(".")) {
26871329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com            continue;
26971329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com        }
27071329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com        SkString pathRelativeToRootDir(subDir);
27171329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com        if (!isSubDirEmpty) {
27271329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com            pathRelativeToRootDir.append(PATH_DIV_STR);
27371329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com        }
27471329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com        pathRelativeToRootDir.append(fileName);
27571329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com        if (string_contains_any_of(pathRelativeToRootDir, matchSubstrings) &&
27671329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com            !string_contains_any_of(pathRelativeToRootDir, nomatchSubstrings)) {
27771329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com            files->push(new SkString(pathRelativeToRootDir));
27871329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com        }
27971329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com    }
28071329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com
28171329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com    // Recurse into any non-ignored subdirectories.
28271329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com    if (recurseIntoSubdirs) {
28371329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com        SkOSFile::Iter dirIterator(dir.c_str());
28471329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com        SkString dirName;
28571329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com        while (dirIterator.next(&dirName, true)) {
28671329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com            if (dirName.startsWith(".")) {
28771329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com                continue;
28871329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com            }
28971329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com            SkString pathRelativeToRootDir(subDir);
29071329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com            if (!isSubDirEmpty) {
29171329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com                pathRelativeToRootDir.append(PATH_DIV_STR);
29271329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com            }
29371329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com            pathRelativeToRootDir.append(dirName);
29471329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com            if (!string_contains_any_of(pathRelativeToRootDir, nomatchSubstrings)) {
29571329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com                get_file_list_subdir(rootDir, pathRelativeToRootDir,
29671329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com                                     matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
29771329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com                                     files);
29871329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com            }
29971329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com        }
30071329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com    }
30171329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com}
30271329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com
30371329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com/// Iterate over dir and get all files whose filename:
30471329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com///  - matches any of the substrings in matchSubstrings, but...
30571329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com///  - DOES NOT match any of the substrings in nomatchSubstrings
30671329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com///  - DOES NOT start with a dot (.)
30771329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com/// Adds the matching files to the list in *files.
308a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.comstatic void get_file_list(const SkString& dir,
309a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com                          const StringArray& matchSubstrings,
310a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com                          const StringArray& nomatchSubstrings,
31171329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com                          bool recurseIntoSubdirs, FileArray *files) {
31271329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com    get_file_list_subdir(dir, SkString(""),
31371329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com                         matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
31471329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com                         files);
3155fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com}
3165fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com
3175fd53858f83776025e63de44b2e0a1822b932c39epoger@google.comstatic void release_file_list(FileArray *files) {
3185fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com    files->deleteAll();
3195fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com}
3205fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com
3215fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com/// Comparison routines for qsort, sort by file names.
3225fd53858f83776025e63de44b2e0a1822b932c39epoger@google.comstatic int compare_file_name_metrics(SkString **lhs, SkString **rhs) {
3235fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com    return strcmp((*lhs)->c_str(), (*rhs)->c_str());
3245fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com}
3255fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com
326e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.comclass AutoReleasePixels {
327e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.compublic:
328e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    AutoReleasePixels(DiffRecord* drp)
329e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    : fDrp(drp) {
33096fcdcc219d2a0d3579719b84b28bede76efba64halcanary        SkASSERT(drp != nullptr);
331e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    }
332e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    ~AutoReleasePixels() {
3331b3387b5cfef150c3237c6a8700b2d6f3730e4ceHal Canary        fDrp->fBase.fBitmap.setPixelRef(nullptr, 0, 0);
3341b3387b5cfef150c3237c6a8700b2d6f3730e4ceHal Canary        fDrp->fComparison.fBitmap.setPixelRef(nullptr, 0, 0);
3351b3387b5cfef150c3237c6a8700b2d6f3730e4ceHal Canary        fDrp->fDifference.fBitmap.setPixelRef(nullptr, 0, 0);
3361b3387b5cfef150c3237c6a8700b2d6f3730e4ceHal Canary        fDrp->fWhite.fBitmap.setPixelRef(nullptr, 0, 0);
337e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    }
338e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
339e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.comprivate:
340e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    DiffRecord* fDrp;
341e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com};
342e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
343e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.comstatic void get_bounds(DiffResource& resource, const char* name) {
344e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    if (resource.fBitmap.empty() && !DiffResource::isStatusFailed(resource.fStatus)) {
34538d909ec2875f79952de08f36adfaac5680d2c53bungeman        sk_sp<SkData> fileBits(read_file(resource.fFullPath.c_str()));
34638d909ec2875f79952de08f36adfaac5680d2c53bungeman        if (fileBits) {
34742943c8aa9c611c18ad0f1a30a27669f3d82239creed            get_bitmap(fileBits, resource, true);
34838d909ec2875f79952de08f36adfaac5680d2c53bungeman        } else {
349e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            SkDebugf("WARNING: couldn't read %s file <%s>\n", name, resource.fFullPath.c_str());
350e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            resource.fStatus = DiffResource::kCouldNotRead_Status;
351e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        }
352e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    }
353e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com}
354e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
355e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.comstatic void get_bounds(DiffRecord& drp) {
356e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    get_bounds(drp.fBase, "base");
357e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    get_bounds(drp.fComparison, "comparison");
358e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com}
359e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
36093d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org#ifdef SK_OS_WIN
36193d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org#define ANSI_COLOR_RED     ""
36293d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org#define ANSI_COLOR_GREEN   ""
36393d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org#define ANSI_COLOR_YELLOW  ""
36493d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org#define ANSI_COLOR_RESET   ""
36593d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org#else
36693d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org#define ANSI_COLOR_RED     "\x1b[31m"
36793d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org#define ANSI_COLOR_GREEN   "\x1b[32m"
36893d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org#define ANSI_COLOR_YELLOW  "\x1b[33m"
36993d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org#define ANSI_COLOR_RESET   "\x1b[0m"
37093d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org#endif
37193d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org
37293d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org#define VERBOSE_STATUS(status,color,filename) if (verbose) printf( "[ " color " %10s " ANSI_COLOR_RESET " ] %s\n", status, filename->c_str())
37393d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org
3744b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com/// Creates difference images, returns the number that have a 0 metric.
375a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com/// If outputDir.isEmpty(), don't write out diff files.
3769dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.comstatic void create_diff_images (DiffMetricProc dmp,
3779dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com                                const int colorThreshold,
3789dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com                                RecordArray* differences,
3799dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com                                const SkString& baseDir,
3809dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com                                const SkString& comparisonDir,
3819dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com                                const SkString& outputDir,
382a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com                                const StringArray& matchSubstrings,
383a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com                                const StringArray& nomatchSubstrings,
38471329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com                                bool recurseIntoSubdirs,
385e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                                bool getBounds,
38693d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org                                bool verbose,
3879dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com                                DiffSummary* summary) {
388a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    SkASSERT(!baseDir.isEmpty());
389a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    SkASSERT(!comparisonDir.isEmpty());
3904b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com
3915fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com    FileArray baseFiles;
3925fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com    FileArray comparisonFiles;
3935fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com
39471329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com    get_file_list(baseDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, &baseFiles);
39571329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com    get_file_list(comparisonDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
396a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com                  &comparisonFiles);
3975fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com
398a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    if (!baseFiles.isEmpty()) {
399c7a67cb57e43f8e140c7bd21318b5ad3e2db6b2freed@google.com        qsort(baseFiles.begin(), baseFiles.count(), sizeof(SkString*),
400c7a67cb57e43f8e140c7bd21318b5ad3e2db6b2freed@google.com              SkCastForQSort(compare_file_name_metrics));
401a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    }
402a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    if (!comparisonFiles.isEmpty()) {
403c7a67cb57e43f8e140c7bd21318b5ad3e2db6b2freed@google.com        qsort(comparisonFiles.begin(), comparisonFiles.count(),
404c7a67cb57e43f8e140c7bd21318b5ad3e2db6b2freed@google.com              sizeof(SkString*), SkCastForQSort(compare_file_name_metrics));
405a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    }
40666008526673b55dcce69ba947186c5e9428a62e5epoger@google.com
407235cbf23c4836f87a4d53ac48345942ff605a739brianosman    if (!outputDir.isEmpty()) {
408235cbf23c4836f87a4d53ac48345942ff605a739brianosman        sk_mkdir(outputDir.c_str());
409235cbf23c4836f87a4d53ac48345942ff605a739brianosman    }
410235cbf23c4836f87a4d53ac48345942ff605a739brianosman
4115fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com    int i = 0;
4125fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com    int j = 0;
4135fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com
4145fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com    while (i < baseFiles.count() &&
4155fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com           j < comparisonFiles.count()) {
4164b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com
417e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        SkString basePath(baseDir);
418e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        SkString comparisonPath(comparisonDir);
4195fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com
420e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        DiffRecord *drp = new DiffRecord;
421e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        int v = strcmp(baseFiles[i]->c_str(), comparisonFiles[j]->c_str());
4225fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com
4235fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com        if (v < 0) {
4245fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com            // in baseDir, but not in comparisonDir
425e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            drp->fResult = DiffRecord::kCouldNotCompare_Result;
426e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
427e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            basePath.append(*baseFiles[i]);
428e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            comparisonPath.append(*baseFiles[i]);
429e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
430e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            drp->fBase.fFilename = *baseFiles[i];
431e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            drp->fBase.fFullPath = basePath;
432e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            drp->fBase.fStatus = DiffResource::kExists_Status;
433e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
434e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            drp->fComparison.fFilename = *baseFiles[i];
435e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            drp->fComparison.fFullPath = comparisonPath;
436e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            drp->fComparison.fStatus = DiffResource::kDoesNotExist_Status;
437e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
43893d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org            VERBOSE_STATUS("MISSING", ANSI_COLOR_YELLOW, baseFiles[i]);
43993d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org
4405fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com            ++i;
4415fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com        } else if (v > 0) {
4425fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com            // in comparisonDir, but not in baseDir
443e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            drp->fResult = DiffRecord::kCouldNotCompare_Result;
444e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
445e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            basePath.append(*comparisonFiles[j]);
446e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            comparisonPath.append(*comparisonFiles[j]);
447e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
448e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            drp->fBase.fFilename = *comparisonFiles[j];
449e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            drp->fBase.fFullPath = basePath;
450e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            drp->fBase.fStatus = DiffResource::kDoesNotExist_Status;
451e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
452e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            drp->fComparison.fFilename = *comparisonFiles[j];
453e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            drp->fComparison.fFullPath = comparisonPath;
454e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            drp->fComparison.fStatus = DiffResource::kExists_Status;
455e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
45693d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org            VERBOSE_STATUS("MISSING", ANSI_COLOR_YELLOW, comparisonFiles[j]);
45793d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org
4585fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com            ++j;
4595fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com        } else {
46046256ea736868e6df1a6102672fc4bb41f99d29depoger@google.com            // Found the same filename in both baseDir and comparisonDir.
461e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            SkASSERT(DiffRecord::kUnknown_Result == drp->fResult);
462e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
463e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            basePath.append(*baseFiles[i]);
464e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            comparisonPath.append(*comparisonFiles[j]);
465e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
466e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            drp->fBase.fFilename = *baseFiles[i];
467e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            drp->fBase.fFullPath = basePath;
468e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            drp->fBase.fStatus = DiffResource::kExists_Status;
469e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
470e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            drp->fComparison.fFilename = *comparisonFiles[j];
471e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            drp->fComparison.fFullPath = comparisonPath;
472e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            drp->fComparison.fStatus = DiffResource::kExists_Status;
473e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
47438d909ec2875f79952de08f36adfaac5680d2c53bungeman            sk_sp<SkData> baseFileBits(read_file(drp->fBase.fFullPath.c_str()));
47549f085dddff10473b6ebf832a974288300224e60bsalomon            if (baseFileBits) {
476e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                drp->fBase.fStatus = DiffResource::kRead_Status;
477e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            }
47838d909ec2875f79952de08f36adfaac5680d2c53bungeman            sk_sp<SkData> comparisonFileBits(read_file(drp->fComparison.fFullPath.c_str()));
47949f085dddff10473b6ebf832a974288300224e60bsalomon            if (comparisonFileBits) {
480e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                drp->fComparison.fStatus = DiffResource::kRead_Status;
481e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            }
48296fcdcc219d2a0d3579719b84b28bede76efba64halcanary            if (nullptr == baseFileBits || nullptr == comparisonFileBits) {
48396fcdcc219d2a0d3579719b84b28bede76efba64halcanary                if (nullptr == baseFileBits) {
484e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                    drp->fBase.fStatus = DiffResource::kCouldNotRead_Status;
48593d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org                    VERBOSE_STATUS("READ FAIL", ANSI_COLOR_RED, baseFiles[i]);
486e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                }
48796fcdcc219d2a0d3579719b84b28bede76efba64halcanary                if (nullptr == comparisonFileBits) {
488e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                    drp->fComparison.fStatus = DiffResource::kCouldNotRead_Status;
48993d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org                    VERBOSE_STATUS("READ FAIL", ANSI_COLOR_RED, comparisonFiles[j]);
490e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                }
491e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                drp->fResult = DiffRecord::kCouldNotCompare_Result;
492e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
49338d909ec2875f79952de08f36adfaac5680d2c53bungeman            } else if (are_buffers_equal(baseFileBits.get(), comparisonFileBits.get())) {
494e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                drp->fResult = DiffRecord::kEqualBits_Result;
49593d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org                VERBOSE_STATUS("MATCH", ANSI_COLOR_GREEN, baseFiles[i]);
49646256ea736868e6df1a6102672fc4bb41f99d29depoger@google.com            } else {
497e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                AutoReleasePixels arp(drp);
49842943c8aa9c611c18ad0f1a30a27669f3d82239creed                get_bitmap(baseFileBits, drp->fBase, false);
49942943c8aa9c611c18ad0f1a30a27669f3d82239creed                get_bitmap(comparisonFileBits, drp->fComparison, false);
50093d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org                VERBOSE_STATUS("DIFFERENT", ANSI_COLOR_RED, baseFiles[i]);
501e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                if (DiffResource::kDecoded_Status == drp->fBase.fStatus &&
502e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                    DiffResource::kDecoded_Status == drp->fComparison.fStatus) {
503e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                    create_and_write_diff_image(drp, dmp, colorThreshold,
504e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                                                outputDir, drp->fBase.fFilename);
50546256ea736868e6df1a6102672fc4bb41f99d29depoger@google.com                } else {
506e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                    drp->fResult = DiffRecord::kCouldNotCompare_Result;
50746256ea736868e6df1a6102672fc4bb41f99d29depoger@google.com                }
50846256ea736868e6df1a6102672fc4bb41f99d29depoger@google.com            }
509e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
5105fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com            ++i;
5115fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com            ++j;
5124b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com        }
513e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
514e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        if (getBounds) {
515e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            get_bounds(*drp);
516e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        }
517e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        SkASSERT(DiffRecord::kUnknown_Result != drp->fResult);
5185fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com        differences->push(drp);
5195fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com        summary->add(drp);
5205fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com    }
5215fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com
5225fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com    for (; i < baseFiles.count(); ++i) {
5235fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com        // files only in baseDir
524e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        DiffRecord *drp = new DiffRecord();
525e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        drp->fBase.fFilename = *baseFiles[i];
526e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        drp->fBase.fFullPath = baseDir;
527e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        drp->fBase.fFullPath.append(drp->fBase.fFilename);
528e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        drp->fBase.fStatus = DiffResource::kExists_Status;
529e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
530e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        drp->fComparison.fFilename = *baseFiles[i];
531e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        drp->fComparison.fFullPath = comparisonDir;
532e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        drp->fComparison.fFullPath.append(drp->fComparison.fFilename);
533e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        drp->fComparison.fStatus = DiffResource::kDoesNotExist_Status;
534e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
535e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        drp->fResult = DiffRecord::kCouldNotCompare_Result;
536e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        if (getBounds) {
537e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            get_bounds(*drp);
538e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        }
5395fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com        differences->push(drp);
5405fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com        summary->add(drp);
5415fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com    }
5424b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com
5435fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com    for (; j < comparisonFiles.count(); ++j) {
5445fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com        // files only in comparisonDir
545e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        DiffRecord *drp = new DiffRecord();
546e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        drp->fBase.fFilename = *comparisonFiles[j];
547e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        drp->fBase.fFullPath = baseDir;
548e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        drp->fBase.fFullPath.append(drp->fBase.fFilename);
549e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        drp->fBase.fStatus = DiffResource::kDoesNotExist_Status;
550e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
551e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        drp->fComparison.fFilename = *comparisonFiles[j];
552e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        drp->fComparison.fFullPath = comparisonDir;
553e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        drp->fComparison.fFullPath.append(drp->fComparison.fFilename);
554e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        drp->fComparison.fStatus = DiffResource::kExists_Status;
555e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
556e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        drp->fResult = DiffRecord::kCouldNotCompare_Result;
557e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        if (getBounds) {
558e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            get_bounds(*drp);
559e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        }
5604b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com        differences->push(drp);
5619dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com        summary->add(drp);
5624b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com    }
5635fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com
5645fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com    release_file_list(&baseFiles);
5655fd53858f83776025e63de44b2e0a1822b932c39epoger@google.com    release_file_list(&comparisonFiles);
5667d04280a68430d6b472ee67dc8f6092127625e73tomhudson@google.com}
5674b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com
5684b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.comstatic void usage (char * argv0) {
5694b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com    SkDebugf("Skia baseline image diff tool\n");
570a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    SkDebugf("\n"
571a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com"Usage: \n"
572e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com"    %s <baseDir> <comparisonDir> [outputDir] \n", argv0);
573a6318194df095b47165d475ada2581b992d0c821keyar@chromium.org    SkDebugf(
57446a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com"\nArguments:"
575dfbf24e5e7112e99e8c9bed1fdaadc136f42a0d3epoger@google.com"\n    --failonresult <result>: After comparing all file pairs, exit with nonzero"
576dfbf24e5e7112e99e8c9bed1fdaadc136f42a0d3epoger@google.com"\n                             return code (number of file pairs yielding this"
577dfbf24e5e7112e99e8c9bed1fdaadc136f42a0d3epoger@google.com"\n                             result) if any file pairs yielded this result."
578dfbf24e5e7112e99e8c9bed1fdaadc136f42a0d3epoger@google.com"\n                             This flag may be repeated, in which case the"
579dfbf24e5e7112e99e8c9bed1fdaadc136f42a0d3epoger@google.com"\n                             return code will be the number of fail pairs"
580dfbf24e5e7112e99e8c9bed1fdaadc136f42a0d3epoger@google.com"\n                             yielding ANY of these results."
581e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com"\n    --failonstatus <baseStatus> <comparisonStatus>: exit with nonzero return"
582e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com"\n                             code if any file pairs yielded this status."
58346a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com"\n    --help: display this info"
58446a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com"\n    --listfilenames: list all filenames for each result type in stdout"
585dfbf24e5e7112e99e8c9bed1fdaadc136f42a0d3epoger@google.com"\n    --match <substring>: compare files whose filenames contain this substring;"
586dfbf24e5e7112e99e8c9bed1fdaadc136f42a0d3epoger@google.com"\n                         if unspecified, compare ALL files."
587dfbf24e5e7112e99e8c9bed1fdaadc136f42a0d3epoger@google.com"\n                         this flag may be repeated."
58846a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com"\n    --nodiffs: don't write out image diffs or index.html, just generate"
58946a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com"\n               report on stdout"
590dfbf24e5e7112e99e8c9bed1fdaadc136f42a0d3epoger@google.com"\n    --nomatch <substring>: regardless of --match, DO NOT compare files whose"
591dfbf24e5e7112e99e8c9bed1fdaadc136f42a0d3epoger@google.com"\n                           filenames contain this substring."
592dfbf24e5e7112e99e8c9bed1fdaadc136f42a0d3epoger@google.com"\n                           this flag may be repeated."
59346a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com"\n    --noprintdirs: do not print the directories used."
59471329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com"\n    --norecurse: do not recurse into subdirectories."
59546a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com"\n    --sortbymaxmismatch: sort by worst color channel mismatch;"
59646a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com"\n                         break ties with -sortbymismatch"
59746a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com"\n    --sortbymismatch: sort by average color channel mismatch"
59846a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com"\n    --threshold <n>: only report differences > n (per color channel) [default 0]"
59946a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com"\n    --weighted: sort by # pixels different weighted by color difference"
60046a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com"\n"
60146a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com"\n    baseDir: directory to read baseline images from."
60246a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com"\n    comparisonDir: directory to read comparison images from"
60346a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com"\n    outputDir: directory to write difference images and index.html to;"
60446a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com"\n               defaults to comparisonDir"
60546a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com"\n"
60646a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com"\nIf no sort is specified, it will sort by fraction of pixels mismatching."
60746a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com"\n");
6084b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com}
6094b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com
61070044cc4964c32e0125e46c3c5f7f82446ddda85epoger@google.comconst int kNoError = 0;
61170044cc4964c32e0125e46c3c5f7f82446ddda85epoger@google.comconst int kGenericError = -1;
61246a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com
613be28ee2974474800323ce4fabf62a839018be591Mike Kleinint main(int argc, char** argv) {
6144b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com    DiffMetricProc diffProc = compute_diff_pmcolor;
61528060e7c940b07038bdaa3c5f8d1d87cf199e228epoger@google.com    int (*sortProc)(const void*, const void*) = compare<CompareDiffMetrics>;
6164b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com
6174b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com    // Maximum error tolerated in any one color channel in any one pixel before
6184b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com    // a difference is reported.
6194b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com    int colorThreshold = 0;
6204b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com    SkString baseDir;
6214b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com    SkString comparisonDir;
6224b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com    SkString outputDir;
623dfbf24e5e7112e99e8c9bed1fdaadc136f42a0d3epoger@google.com
624a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    StringArray matchSubstrings;
625a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    StringArray nomatchSubstrings;
6264b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com
627a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    bool generateDiffs = true;
62846a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com    bool listFilenames = false;
62971329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com    bool printDirNames = true;
63071329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com    bool recurseIntoSubdirs = true;
63193d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org    bool verbose = false;
6323a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed    bool listFailingBase = false;
6337d04280a68430d6b472ee67dc8f6092127625e73tomhudson@google.com
6344b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com    RecordArray differences;
6359dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com    DiffSummary summary;
6364b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com
637e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    bool failOnResultType[DiffRecord::kResultCount];
638e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    for (int i = 0; i < DiffRecord::kResultCount; i++) {
6393af4ff46a7461f78c8ab24d1e8213b187e8472e2epoger@google.com        failOnResultType[i] = false;
6403af4ff46a7461f78c8ab24d1e8213b187e8472e2epoger@google.com    }
6413af4ff46a7461f78c8ab24d1e8213b187e8472e2epoger@google.com
642e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    bool failOnStatusType[DiffResource::kStatusCount][DiffResource::kStatusCount];
643e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    for (int base = 0; base < DiffResource::kStatusCount; ++base) {
644e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
645e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            failOnStatusType[base][comparison] = false;
646e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        }
647e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    }
648e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
649a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    int i;
650a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    int numUnflaggedArguments = 0;
651a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    for (i = 1; i < argc; i++) {
652dfbf24e5e7112e99e8c9bed1fdaadc136f42a0d3epoger@google.com        if (!strcmp(argv[i], "--failonresult")) {
653e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            if (argc == ++i) {
654e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                SkDebugf("failonresult expects one argument.\n");
655e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                continue;
656e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            }
657e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            DiffRecord::Result type = DiffRecord::getResultByName(argv[i]);
658e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            if (type != DiffRecord::kResultCount) {
659e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                failOnResultType[type] = true;
660e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            } else {
661e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                SkDebugf("ignoring unrecognized result <%s>\n", argv[i]);
662e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            }
663e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            continue;
664e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        }
665e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        if (!strcmp(argv[i], "--failonstatus")) {
666e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            if (argc == ++i) {
667e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                SkDebugf("failonstatus missing base status.\n");
668e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                continue;
669e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            }
670e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            bool baseStatuses[DiffResource::kStatusCount];
671e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            if (!DiffResource::getMatchingStatuses(argv[i], baseStatuses)) {
672e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                SkDebugf("unrecognized base status <%s>\n", argv[i]);
673e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            }
674e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
675e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            if (argc == ++i) {
676e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                SkDebugf("failonstatus missing comparison status.\n");
677e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                continue;
678e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            }
679e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            bool comparisonStatuses[DiffResource::kStatusCount];
680e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            if (!DiffResource::getMatchingStatuses(argv[i], comparisonStatuses)) {
681e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                SkDebugf("unrecognized comarison status <%s>\n", argv[i]);
682e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            }
683e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
684e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            for (int base = 0; base < DiffResource::kStatusCount; ++base) {
685e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
686e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                    failOnStatusType[base][comparison] |=
687e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                        baseStatuses[base] && comparisonStatuses[comparison];
688e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                }
689e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            }
69046a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com            continue;
69146a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com        }
6927d04280a68430d6b472ee67dc8f6092127625e73tomhudson@google.com        if (!strcmp(argv[i], "--help")) {
6934b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com            usage(argv[0]);
69470044cc4964c32e0125e46c3c5f7f82446ddda85epoger@google.com            return kNoError;
695a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com        }
69646a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com        if (!strcmp(argv[i], "--listfilenames")) {
69746a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com            listFilenames = true;
698a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com            continue;
699a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com        }
70093d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org        if (!strcmp(argv[i], "--verbose")) {
70193d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org            verbose = true;
70293d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org            continue;
70393d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org        }
704a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com        if (!strcmp(argv[i], "--match")) {
705a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com            matchSubstrings.push(new SkString(argv[++i]));
706a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com            continue;
707a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com        }
70846a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com        if (!strcmp(argv[i], "--nodiffs")) {
70946a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com            generateDiffs = false;
71046a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com            continue;
71146a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com        }
712a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com        if (!strcmp(argv[i], "--nomatch")) {
713a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com            nomatchSubstrings.push(new SkString(argv[++i]));
714a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com            continue;
715a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com        }
71646a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com        if (!strcmp(argv[i], "--noprintdirs")) {
71771329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com            printDirNames = false;
71871329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com            continue;
71971329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com        }
72071329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com        if (!strcmp(argv[i], "--norecurse")) {
72171329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com            recurseIntoSubdirs = false;
7224b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com            continue;
7234b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com        }
7247d04280a68430d6b472ee67dc8f6092127625e73tomhudson@google.com        if (!strcmp(argv[i], "--sortbymaxmismatch")) {
72528060e7c940b07038bdaa3c5f8d1d87cf199e228epoger@google.com            sortProc = compare<CompareDiffMaxMismatches>;
7264b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com            continue;
7274b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com        }
72846a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com        if (!strcmp(argv[i], "--sortbymismatch")) {
72946a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com            sortProc = compare<CompareDiffMeanMismatches>;
7305b32529ab855764bf40f678f10d0e3045b990a94tomhudson@google.com            continue;
7315b32529ab855764bf40f678f10d0e3045b990a94tomhudson@google.com        }
73246a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com        if (!strcmp(argv[i], "--threshold")) {
73346a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com            colorThreshold = atoi(argv[++i]);
73446a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com            continue;
73546a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com        }
73646a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com        if (!strcmp(argv[i], "--weighted")) {
73746a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com            sortProc = compare<CompareDiffWeighted>;
738a6318194df095b47165d475ada2581b992d0c821keyar@chromium.org            continue;
739a6318194df095b47165d475ada2581b992d0c821keyar@chromium.org        }
7404b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com        if (argv[i][0] != '-') {
741a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com            switch (numUnflaggedArguments++) {
7424b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com                case 0:
7434b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com                    baseDir.set(argv[i]);
7444b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com                    continue;
7454b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com                case 1:
7464b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com                    comparisonDir.set(argv[i]);
7474b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com                    continue;
7484b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com                case 2:
7494b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com                    outputDir.set(argv[i]);
7504b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com                    continue;
7514b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com                default:
752a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com                    SkDebugf("extra unflagged argument <%s>\n", argv[i]);
7534b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com                    usage(argv[0]);
75470044cc4964c32e0125e46c3c5f7f82446ddda85epoger@google.com                    return kGenericError;
7554b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com            }
7564b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com        }
7573a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed        if (!strcmp(argv[i], "--listFailingBase")) {
7583a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed            listFailingBase = true;
7593a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed            continue;
7603a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed        }
7614b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com
7624b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com        SkDebugf("Unrecognized argument <%s>\n", argv[i]);
7634b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com        usage(argv[0]);
76470044cc4964c32e0125e46c3c5f7f82446ddda85epoger@google.com        return kGenericError;
7654b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com    }
766a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com
767a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    if (numUnflaggedArguments == 2) {
7689dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com        outputDir = comparisonDir;
769a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    } else if (numUnflaggedArguments != 3) {
7709dc527bebfaca7c87a39302510399bb0d0fe770dtomhudson@google.com        usage(argv[0]);
77170044cc4964c32e0125e46c3c5f7f82446ddda85epoger@google.com        return kGenericError;
7724b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com    }
7734b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com
7741a315fe57e393bbafa32d5482fed052e39ffdc2ebsalomon@google.com    if (!baseDir.endsWith(PATH_DIV_STR)) {
7751a315fe57e393bbafa32d5482fed052e39ffdc2ebsalomon@google.com        baseDir.append(PATH_DIV_STR);
7764b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com    }
77771329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com    if (printDirNames) {
778a6318194df095b47165d475ada2581b992d0c821keyar@chromium.org        printf("baseDir is [%s]\n", baseDir.c_str());
779a6318194df095b47165d475ada2581b992d0c821keyar@chromium.org    }
780a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com
7811a315fe57e393bbafa32d5482fed052e39ffdc2ebsalomon@google.com    if (!comparisonDir.endsWith(PATH_DIV_STR)) {
7821a315fe57e393bbafa32d5482fed052e39ffdc2ebsalomon@google.com        comparisonDir.append(PATH_DIV_STR);
7834b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com    }
78471329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com    if (printDirNames) {
785a6318194df095b47165d475ada2581b992d0c821keyar@chromium.org        printf("comparisonDir is [%s]\n", comparisonDir.c_str());
786a6318194df095b47165d475ada2581b992d0c821keyar@chromium.org    }
787a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com
7881a315fe57e393bbafa32d5482fed052e39ffdc2ebsalomon@google.com    if (!outputDir.endsWith(PATH_DIV_STR)) {
7891a315fe57e393bbafa32d5482fed052e39ffdc2ebsalomon@google.com        outputDir.append(PATH_DIV_STR);
7904b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com    }
791a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    if (generateDiffs) {
79271329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com        if (printDirNames) {
793a6318194df095b47165d475ada2581b992d0c821keyar@chromium.org            printf("writing diffs to outputDir is [%s]\n", outputDir.c_str());
794a6318194df095b47165d475ada2581b992d0c821keyar@chromium.org        }
795a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    } else {
79671329d809a42889af8d2cadc4e43c60488a739a1epoger@google.com        if (printDirNames) {
797a6318194df095b47165d475ada2581b992d0c821keyar@chromium.org            printf("not writing any diffs to outputDir [%s]\n", outputDir.c_str());
798a6318194df095b47165d475ada2581b992d0c821keyar@chromium.org        }
799a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com        outputDir.set("");
800a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    }
801a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com
802da4af24c9c52a80a0c9b4fbd2933f5939920c98cepoger@google.com    // If no matchSubstrings were specified, match ALL strings
803da4af24c9c52a80a0c9b4fbd2933f5939920c98cepoger@google.com    // (except for whatever nomatchSubstrings were specified, if any).
804a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    if (matchSubstrings.isEmpty()) {
805a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com        matchSubstrings.push(new SkString(""));
806a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    }
8074b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com
808a611c3ea53c02ef80baa32fbfb9cca33f999378depoger@google.com    create_diff_images(diffProc, colorThreshold, &differences,
809a611c3ea53c02ef80baa32fbfb9cca33f999378depoger@google.com                       baseDir, comparisonDir, outputDir,
810e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                       matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, generateDiffs,
81193d7bb6fc6e42d41e41ec9f38b540616d07925adcommit-bot@chromium.org                       verbose, &summary);
812e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    summary.print(listFilenames, failOnResultType, failOnStatusType);
8137d04280a68430d6b472ee67dc8f6092127625e73tomhudson@google.com
8143a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed    if (listFailingBase) {
8153a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed        summary.printfFailingBaseNames("\n");
8163a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed    }
8173a3baf6c4bde1d64a40446ca0fcd91274cdb2f8breed
8187d04280a68430d6b472ee67dc8f6092127625e73tomhudson@google.com    if (differences.count()) {
819c7a67cb57e43f8e140c7bd21318b5ad3e2db6b2freed@google.com        qsort(differences.begin(), differences.count(),
820c7a67cb57e43f8e140c7bd21318b5ad3e2db6b2freed@google.com              sizeof(DiffRecord*), sortProc);
8217d04280a68430d6b472ee67dc8f6092127625e73tomhudson@google.com    }
82266008526673b55dcce69ba947186c5e9428a62e5epoger@google.com
823a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    if (generateDiffs) {
824a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com        print_diff_page(summary.fNumMatches, colorThreshold, differences,
825a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com                        baseDir, comparisonDir, outputDir);
826a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    }
82776222c0ef2c5e63d2d9875e1cdb89e52049c7754epoger@google.com
8284b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com    for (i = 0; i < differences.count(); i++) {
8294b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com        delete differences[i];
8304b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com    }
831a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    matchSubstrings.deleteAll();
832a5f406e9984e09d084cc995895712eb3f8f1c3cfepoger@google.com    nomatchSubstrings.deleteAll();
833be6188d64721850723c19d5a7a4f007a675300f4epoger@google.com
834dfbf24e5e7112e99e8c9bed1fdaadc136f42a0d3epoger@google.com    int num_failing_results = 0;
835e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    for (int i = 0; i < DiffRecord::kResultCount; i++) {
8363af4ff46a7461f78c8ab24d1e8213b187e8472e2epoger@google.com        if (failOnResultType[i]) {
8373af4ff46a7461f78c8ab24d1e8213b187e8472e2epoger@google.com            num_failing_results += summary.fResultsOfType[i].count();
8383af4ff46a7461f78c8ab24d1e8213b187e8472e2epoger@google.com        }
83946a45963fa13082cbe0c800c92bccf2ac36c89b7epoger@google.com    }
840e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    if (!failOnResultType[DiffRecord::kCouldNotCompare_Result]) {
841e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        for (int base = 0; base < DiffResource::kStatusCount; ++base) {
842e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
843e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                if (failOnStatusType[base][comparison]) {
844e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                    num_failing_results += summary.fStatusOfType[base][comparison].count();
845e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                }
846e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            }
847e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        }
848e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    }
84928659883618d3c015defe91de504af96bc98afdbepoger@google.com
85028659883618d3c015defe91de504af96bc98afdbepoger@google.com    // On Linux (and maybe other platforms too), any results outside of the
85128659883618d3c015defe91de504af96bc98afdbepoger@google.com    // range [0...255] are wrapped (mod 256).  Do the conversion ourselves, to
85228659883618d3c015defe91de504af96bc98afdbepoger@google.com    // make sure that we only return 0 when there were no failures.
85328659883618d3c015defe91de504af96bc98afdbepoger@google.com    return (num_failing_results > 255) ? 255 : num_failing_results;
8544b33d28eb369622964d22d5cc4a6e3f0eb11a14ctomhudson@google.com}
855