1945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com/*
2945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com * Copyright 2013 Google Inc.
3945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com *
4945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com * Use of this source code is governed by a BSD-style license that can be
5945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com * found in the LICENSE file.
6945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com */
7945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
8945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com#include "SkBitmap.h"
9945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com#include "SkImageDecoder.h"
10945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com#include "SkOSFile.h"
11cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com#include "SkRunnable.h"
1254f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger#include "SkSize.h"
13945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com#include "SkStream.h"
14c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com#include "SkTDict.h"
15406654be7a930b484159f5bca107d3b11d8a9edemtklein#include "SkTaskGroup.h"
16945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
1721b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephana// from the tools directory for replace_char(...)
1821b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephana#include "picture_utils.h"
1921b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephana
20945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com#include "SkDiffContext.h"
2154f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger#include "SkImageDiffer.h"
22945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com#include "skpdiff_util.h"
23945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
24945708ad9494322e2bc26776ccb741776205b4b8zachr@google.comSkDiffContext::SkDiffContext() {
25945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    fDiffers = NULL;
26945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    fDifferCount = 0;
27945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com}
28945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
29945708ad9494322e2bc26776ccb741776205b4b8zachr@google.comSkDiffContext::~SkDiffContext() {
3049f085dddff10473b6ebf832a974288300224e60bsalomon    if (fDiffers) {
31945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        SkDELETE_ARRAY(fDiffers);
32945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    }
33945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com}
34945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
3554f1ad8bb5bdd2ac2ea7981427abeb193383d449epogervoid SkDiffContext::setAlphaMaskDir(const SkString& path) {
36513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com    if (!path.isEmpty() && sk_mkdir(path.c_str())) {
3754f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger        fAlphaMaskDir = path;
3854f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger    }
3954f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger}
4054f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger
4154f1ad8bb5bdd2ac2ea7981427abeb193383d449epogervoid SkDiffContext::setRgbDiffDir(const SkString& path) {
4254f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger    if (!path.isEmpty() && sk_mkdir(path.c_str())) {
4354f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger        fRgbDiffDir = path;
4454f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger    }
4554f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger}
4654f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger
4754f1ad8bb5bdd2ac2ea7981427abeb193383d449epogervoid SkDiffContext::setWhiteDiffDir(const SkString& path) {
4854f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger    if (!path.isEmpty() && sk_mkdir(path.c_str())) {
4954f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger        fWhiteDiffDir = path;
50513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com    }
51513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com}
52513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com
5321b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephanavoid SkDiffContext::setLongNames(const bool useLongNames) {
5421b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephana    longNames = useLongNames;
5521b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephana}
5621b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephana
57945708ad9494322e2bc26776ccb741776205b4b8zachr@google.comvoid SkDiffContext::setDiffers(const SkTDArray<SkImageDiffer*>& differs) {
58945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    // Delete whatever the last array of differs was
5949f085dddff10473b6ebf832a974288300224e60bsalomon    if (fDiffers) {
60945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        SkDELETE_ARRAY(fDiffers);
61945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        fDiffers = NULL;
62945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        fDifferCount = 0;
63945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    }
64945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
65945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    // Copy over the new differs
66945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    fDifferCount = differs.count();
67945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    fDiffers = SkNEW_ARRAY(SkImageDiffer*, fDifferCount);
68945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    differs.copy(fDiffers);
69945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com}
70945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
71513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.comstatic SkString get_common_prefix(const SkString& a, const SkString& b) {
72513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com    const size_t maxPrefixLength = SkTMin(a.size(), b.size());
73513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com    SkASSERT(maxPrefixLength > 0);
74513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com    for (size_t x = 0; x < maxPrefixLength; ++x) {
75513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com        if (a[x] != b[x]) {
76513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com            SkString result;
77513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com            result.set(a.c_str(), x);
78513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com            return result;
79513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com        }
80513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com    }
81513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com    if (a.size() > b.size()) {
82513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com        return b;
83513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com    } else {
84513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com        return a;
85513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com    }
86513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com}
87513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com
8821b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephanastatic SkString get_combined_name(const SkString& a, const SkString& b) {
89406654be7a930b484159f5bca107d3b11d8a9edemtklein    // Note (stephana): We must keep this function in sync with
9021b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephana    // getImageDiffRelativeUrl() in static/loader.js (under rebaseline_server).
9121b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephana    SkString result = a;
9221b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephana    result.append("-vs-");
9321b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephana    result.append(b);
9421b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephana    sk_tools::replace_char(&result, '.', '_');
9521b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephana    return result;
9621b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephana}
9721b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephana
98945708ad9494322e2bc26776ccb741776205b4b8zachr@google.comvoid SkDiffContext::addDiff(const char* baselinePath, const char* testPath) {
99945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    // Load the images at the paths
100945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    SkBitmap baselineBitmap;
101945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    SkBitmap testBitmap;
102945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    if (!SkImageDecoder::DecodeFile(baselinePath, &baselineBitmap)) {
103945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        SkDebugf("Failed to load bitmap \"%s\"\n", baselinePath);
104945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        return;
105945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    }
106945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    if (!SkImageDecoder::DecodeFile(testPath, &testBitmap)) {
107945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        SkDebugf("Failed to load bitmap \"%s\"\n", testPath);
108945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        return;
109945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    }
110945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
111945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    // Setup a record for this diff
112efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com    fRecordMutex.acquire();
113efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com    DiffRecord* newRecord = fRecords.addToHead(DiffRecord());
114efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com    fRecordMutex.release();
115945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
116513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com    // compute the common name
117a8e2e1504b9af6ba791637f228debaa23953064atfarina    SkString baseName = SkOSPath::Basename(baselinePath);
118a8e2e1504b9af6ba791637f228debaa23953064atfarina    SkString testName = SkOSPath::Basename(testPath);
11921b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephana
12021b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephana    if (longNames) {
12121b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephana        newRecord->fCommonName = get_combined_name(baseName, testName);
12221b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephana    } else {
12321b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephana        newRecord->fCommonName = get_common_prefix(baseName, testName);
12421b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephana    }
12521b342d19c71fa5abe7e4aa5cfb518fd04cb9d67stephana    newRecord->fCommonName.append(".png");
126513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com
127efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com    newRecord->fBaselinePath = baselinePath;
128efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com    newRecord->fTestPath = testPath;
12954f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger    newRecord->fSize = SkISize::Make(baselineBitmap.width(), baselineBitmap.height());
130efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com
13154f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger    // only generate diff images if we have a place to store them
13254f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger    SkImageDiffer::BitmapsToCreate bitmapsToCreate;
13354f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger    bitmapsToCreate.alphaMask = !fAlphaMaskDir.isEmpty();
13454f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger    bitmapsToCreate.rgbDiff = !fRgbDiffDir.isEmpty();
13554f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger    bitmapsToCreate.whiteDiff = !fWhiteDiffDir.isEmpty();
136513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com
137945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    // Perform each diff
138945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    for (int differIndex = 0; differIndex < fDifferCount; differIndex++) {
139945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        SkImageDiffer* differ = fDiffers[differIndex];
140efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com
141efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com        // Copy the results into data for this record
142efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com        DiffData& diffData = newRecord->fDiffs.push_back();
143efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com        diffData.fDiffName = differ->getName();
144efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com
14554f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger        if (!differ->diff(&baselineBitmap, &testBitmap, bitmapsToCreate, &diffData.fResult)) {
14654f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            // if the diff failed, record -1 as the result
14754f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            // TODO(djsollen): Record more detailed information about exactly what failed.
14854f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            // (Image dimension mismatch? etc.)  See http://skbug.com/2710 ('make skpdiff
14954f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            // report more detail when it fails to compare two images')
15027d7ede2e64bddeacce4a0d83bc055bf966cce2fdjsollen@google.com            diffData.fResult.result = -1;
151efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com            continue;
152513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com        }
153513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com
15454f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger        if (bitmapsToCreate.alphaMask
155efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com                && SkImageDiffer::RESULT_CORRECT != diffData.fResult.result
156efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com                && !diffData.fResult.poiAlphaMask.empty()
157efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com                && !newRecord->fCommonName.isEmpty()) {
158efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com
159a8e2e1504b9af6ba791637f228debaa23953064atfarina            newRecord->fAlphaMaskPath = SkOSPath::Join(fAlphaMaskDir.c_str(),
160a8e2e1504b9af6ba791637f228debaa23953064atfarina                                                       newRecord->fCommonName.c_str());
161efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com
162efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com            // compute the image diff and output it
163efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com            SkBitmap copy;
16428fcae2ec77eb16a79e155f8d788b20457f1c951commit-bot@chromium.org            diffData.fResult.poiAlphaMask.copyTo(&copy, kN32_SkColorType);
16554f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            SkImageEncoder::EncodeFile(newRecord->fAlphaMaskPath.c_str(), copy,
166efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com                                       SkImageEncoder::kPNG_Type, 100);
167513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com
168efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com            // cleanup the existing bitmap to free up resources;
169efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com            diffData.fResult.poiAlphaMask.reset();
170efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com
17154f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            bitmapsToCreate.alphaMask = false;
17254f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger        }
17354f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger
17454f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger        if (bitmapsToCreate.rgbDiff
17554f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger                && SkImageDiffer::RESULT_CORRECT != diffData.fResult.result
17654f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger                && !diffData.fResult.rgbDiffBitmap.empty()
17754f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger                && !newRecord->fCommonName.isEmpty()) {
17854f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            // TODO(djsollen): Rather than taking the max r/g/b diffs that come back from
17954f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            // a particular differ and storing them as toplevel fields within
18054f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            // newRecord, we should extend outputRecords() to report optional
18154f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            // fields for each differ (not just "result" and "pointsOfInterest").
18254f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            // See http://skbug.com/2712 ('allow skpdiff to report different sets
18354f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            // of result fields for different comparison algorithms')
18454f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            newRecord->fMaxRedDiff = diffData.fResult.maxRedDiff;
18554f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            newRecord->fMaxGreenDiff = diffData.fResult.maxGreenDiff;
18654f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            newRecord->fMaxBlueDiff = diffData.fResult.maxBlueDiff;
18754f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger
188a8e2e1504b9af6ba791637f228debaa23953064atfarina            newRecord->fRgbDiffPath = SkOSPath::Join(fRgbDiffDir.c_str(),
189a8e2e1504b9af6ba791637f228debaa23953064atfarina                                                     newRecord->fCommonName.c_str());
19054f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            SkImageEncoder::EncodeFile(newRecord->fRgbDiffPath.c_str(),
19154f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger                                       diffData.fResult.rgbDiffBitmap,
19254f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger                                       SkImageEncoder::kPNG_Type, 100);
19354f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            diffData.fResult.rgbDiffBitmap.reset();
19454f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            bitmapsToCreate.rgbDiff = false;
19554f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger        }
19654f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger
19754f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger        if (bitmapsToCreate.whiteDiff
19854f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger                && SkImageDiffer::RESULT_CORRECT != diffData.fResult.result
19954f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger                && !diffData.fResult.whiteDiffBitmap.empty()
20054f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger                && !newRecord->fCommonName.isEmpty()) {
201a8e2e1504b9af6ba791637f228debaa23953064atfarina            newRecord->fWhiteDiffPath = SkOSPath::Join(fWhiteDiffDir.c_str(),
202a8e2e1504b9af6ba791637f228debaa23953064atfarina                                                       newRecord->fCommonName.c_str());
20354f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            SkImageEncoder::EncodeFile(newRecord->fWhiteDiffPath.c_str(),
20454f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger                                       diffData.fResult.whiteDiffBitmap,
20554f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger                                       SkImageEncoder::kPNG_Type, 100);
20654f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            diffData.fResult.whiteDiffBitmap.reset();
20754f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            bitmapsToCreate.whiteDiff = false;
208945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        }
209945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    }
210945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com}
211945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
212cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.comclass SkThreadedDiff : public SkRunnable {
213cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.compublic:
214cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com    SkThreadedDiff() : fDiffContext(NULL) { }
215cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com
216cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com    void setup(SkDiffContext* diffContext, const SkString& baselinePath, const SkString& testPath) {
217cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com        fDiffContext = diffContext;
218cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com        fBaselinePath = baselinePath;
219cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com        fTestPath = testPath;
220cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com    }
221cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com
22236352bf5e38f45a70ee4f4fc132a38048d38206dmtklein    void run() override {
223cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com        fDiffContext->addDiff(fBaselinePath.c_str(), fTestPath.c_str());
224cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com    }
225cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com
226cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.comprivate:
227cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com    SkDiffContext* fDiffContext;
228cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com    SkString fBaselinePath;
229cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com    SkString fTestPath;
230cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com};
231945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
232945708ad9494322e2bc26776ccb741776205b4b8zachr@google.comvoid SkDiffContext::diffDirectories(const char baselinePath[], const char testPath[]) {
233945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    // Get the files in the baseline, we will then look for those inside the test path
234945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    SkTArray<SkString> baselineEntries;
235945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    if (!get_directory(baselinePath, &baselineEntries)) {
236945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        SkDebugf("Unable to open path \"%s\"\n", baselinePath);
237945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        return;
238945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    }
239945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
240406654be7a930b484159f5bca107d3b11d8a9edemtklein    SkTaskGroup tg;
241cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com    SkTArray<SkThreadedDiff> runnableDiffs;
242cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com    runnableDiffs.reset(baselineEntries.count());
243cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com
244cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com    for (int x = 0; x < baselineEntries.count(); x++) {
245cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com        const char* baseFilename = baselineEntries[x].c_str();
246945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
247945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        // Find the real location of each file to compare
248a8e2e1504b9af6ba791637f228debaa23953064atfarina        SkString baselineFile = SkOSPath::Join(baselinePath, baseFilename);
249a8e2e1504b9af6ba791637f228debaa23953064atfarina        SkString testFile = SkOSPath::Join(testPath, baseFilename);
250945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
251945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        // Check that the test file exists and is a file
252945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        if (sk_exists(testFile.c_str()) && !sk_isdir(testFile.c_str())) {
253945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com            // Queue up the comparison with the differ
254cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com            runnableDiffs[x].setup(this, baselineFile, testFile);
255406654be7a930b484159f5bca107d3b11d8a9edemtklein            tg.add(&runnableDiffs[x]);
256945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        } else {
257945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com            SkDebugf("Baseline file \"%s\" has no corresponding test file\n", baselineFile.c_str());
258945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        }
259945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    }
260945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com}
261945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
262945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
263945708ad9494322e2bc26776ccb741776205b4b8zachr@google.comvoid SkDiffContext::diffPatterns(const char baselinePattern[], const char testPattern[]) {
264945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    // Get the files in the baseline and test patterns. Because they are in sorted order, it's easy
265945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    // to find corresponding images by matching entry indices.
266945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
267945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    SkTArray<SkString> baselineEntries;
268945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    if (!glob_files(baselinePattern, &baselineEntries)) {
269945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        SkDebugf("Unable to get pattern \"%s\"\n", baselinePattern);
270945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        return;
271945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    }
272945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
273945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    SkTArray<SkString> testEntries;
274945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    if (!glob_files(testPattern, &testEntries)) {
275945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        SkDebugf("Unable to get pattern \"%s\"\n", testPattern);
276945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        return;
277945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    }
278945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
279945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    if (baselineEntries.count() != testEntries.count()) {
280945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        SkDebugf("Baseline and test patterns do not yield corresponding number of files\n");
281945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        return;
282945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    }
283945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
284406654be7a930b484159f5bca107d3b11d8a9edemtklein    SkTaskGroup tg;
285cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com    SkTArray<SkThreadedDiff> runnableDiffs;
286cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com    runnableDiffs.reset(baselineEntries.count());
287945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
288cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com    for (int x = 0; x < baselineEntries.count(); x++) {
289cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com        runnableDiffs[x].setup(this, baselineEntries[x], testEntries[x]);
290406654be7a930b484159f5bca107d3b11d8a9edemtklein        tg.add(&runnableDiffs[x]);
291945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    }
2927260d7292bde966f038b8e166f3fe41ddd8f2099stephana    tg.wait();
293945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com}
294945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
295a95959c3fb4c502b45bc78f15b65cda1f21620e6zachr@google.comvoid SkDiffContext::outputRecords(SkWStream& stream, bool useJSONP) {
296efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com    SkTLList<DiffRecord>::Iter iter(fRecords, SkTLList<DiffRecord>::Iter::kHead_IterStart);
297efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com    DiffRecord* currentRecord = iter.get();
298efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com
299a95959c3fb4c502b45bc78f15b65cda1f21620e6zachr@google.com    if (useJSONP) {
300a95959c3fb4c502b45bc78f15b65cda1f21620e6zachr@google.com        stream.writeText("var SkPDiffRecords = {\n");
30155173f2ecc8103b7675655f4a35990ac98050205zachr@google.com    } else {
302a95959c3fb4c502b45bc78f15b65cda1f21620e6zachr@google.com        stream.writeText("{\n");
303a95959c3fb4c502b45bc78f15b65cda1f21620e6zachr@google.com    }
30454f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger
30554f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger    // TODO(djsollen): Would it be better to use the jsoncpp library to write out the JSON?
30654f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger    // This manual approach is probably more efficient, but it sure is ugly.
30754f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger    // See http://skbug.com/2713 ('make skpdiff use jsoncpp library to write out
30854f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger    // JSON output, instead of manual writeText() calls?')
309945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    stream.writeText("    \"records\": [\n");
31049f085dddff10473b6ebf832a974288300224e60bsalomon    while (currentRecord) {
311945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        stream.writeText("        {\n");
312945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
313a479aa1366734f80360a00e3020f5b148cb3c060zachr@google.com            SkString baselineAbsPath = get_absolute_path(currentRecord->fBaselinePath);
314a479aa1366734f80360a00e3020f5b148cb3c060zachr@google.com            SkString testAbsPath = get_absolute_path(currentRecord->fTestPath);
315a479aa1366734f80360a00e3020f5b148cb3c060zachr@google.com
3161e391b587085587d8b95038dac20063b40d47dd0djsollen@google.com            stream.writeText("            \"commonName\": \"");
317513a7bffd344a2bba6e014ec08838ea0bbb8aa68djsollen@google.com            stream.writeText(currentRecord->fCommonName.c_str());
3181e391b587085587d8b95038dac20063b40d47dd0djsollen@google.com            stream.writeText("\",\n");
3191e391b587085587d8b95038dac20063b40d47dd0djsollen@google.com
320cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com            stream.writeText("            \"differencePath\": \"");
32154f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            stream.writeText(get_absolute_path(currentRecord->fAlphaMaskPath).c_str());
32254f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            stream.writeText("\",\n");
32354f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger
32454f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            stream.writeText("            \"rgbDiffPath\": \"");
32554f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            stream.writeText(get_absolute_path(currentRecord->fRgbDiffPath).c_str());
32654f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            stream.writeText("\",\n");
32754f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger
32854f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            stream.writeText("            \"whiteDiffPath\": \"");
32954f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            stream.writeText(get_absolute_path(currentRecord->fWhiteDiffPath).c_str());
330cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com            stream.writeText("\",\n");
331cbbf1ca304d35e3acd944609cf7a1c5127d0ca56djsollen@google.com
332945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com            stream.writeText("            \"baselinePath\": \"");
333a479aa1366734f80360a00e3020f5b148cb3c060zachr@google.com            stream.writeText(baselineAbsPath.c_str());
334945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com            stream.writeText("\",\n");
335945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
336945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com            stream.writeText("            \"testPath\": \"");
337a479aa1366734f80360a00e3020f5b148cb3c060zachr@google.com            stream.writeText(testAbsPath.c_str());
338945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com            stream.writeText("\",\n");
339945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
34054f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            stream.writeText("            \"width\": ");
34154f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            stream.writeDecAsText(currentRecord->fSize.width());
34254f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            stream.writeText(",\n");
34354f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            stream.writeText("            \"height\": ");
34454f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            stream.writeDecAsText(currentRecord->fSize.height());
34554f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            stream.writeText(",\n");
34654f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger
34754f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            stream.writeText("            \"maxRedDiff\": ");
34854f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            stream.writeDecAsText(currentRecord->fMaxRedDiff);
34954f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            stream.writeText(",\n");
35054f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            stream.writeText("            \"maxGreenDiff\": ");
35154f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            stream.writeDecAsText(currentRecord->fMaxGreenDiff);
35254f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            stream.writeText(",\n");
35354f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            stream.writeText("            \"maxBlueDiff\": ");
35454f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            stream.writeDecAsText(currentRecord->fMaxBlueDiff);
35554f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger            stream.writeText(",\n");
35654f1ad8bb5bdd2ac2ea7981427abeb193383d449epoger
357945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com            stream.writeText("            \"diffs\": [\n");
358945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com            for (int diffIndex = 0; diffIndex < currentRecord->fDiffs.count(); diffIndex++) {
359945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com                DiffData& data = currentRecord->fDiffs[diffIndex];
360945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com                stream.writeText("                {\n");
361945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
362945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com                    stream.writeText("                    \"differName\": \"");
363945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com                    stream.writeText(data.fDiffName);
364945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com                    stream.writeText("\",\n");
365945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
366945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com                    stream.writeText("                    \"result\": ");
367efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com                    stream.writeScalarAsText((SkScalar)data.fResult.result);
368945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com                    stream.writeText(",\n");
369945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
370efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com                    stream.writeText("                    \"pointsOfInterest\": ");
371efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com                    stream.writeDecAsText(data.fResult.poiCount);
372efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com                    stream.writeText("\n");
373efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com
374945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com                stream.writeText("                }");
375945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
376945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com                // JSON does not allow trailing commas
37755173f2ecc8103b7675655f4a35990ac98050205zachr@google.com                if (diffIndex + 1 < currentRecord->fDiffs.count()) {
378945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com                    stream.writeText(",");
379945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com                }
380945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com                stream.writeText("                \n");
381945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com            }
382945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com            stream.writeText("            ]\n");
383945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
384945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        stream.writeText("        }");
385945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com
386efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com        currentRecord = iter.next();
387efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com
388945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        // JSON does not allow trailing commas
38949f085dddff10473b6ebf832a974288300224e60bsalomon        if (currentRecord) {
390945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com            stream.writeText(",");
391945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        }
392945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com        stream.writeText("\n");
393945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    }
394945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com    stream.writeText("    ]\n");
395a95959c3fb4c502b45bc78f15b65cda1f21620e6zachr@google.com    if (useJSONP) {
396a95959c3fb4c502b45bc78f15b65cda1f21620e6zachr@google.com        stream.writeText("};\n");
39755173f2ecc8103b7675655f4a35990ac98050205zachr@google.com    } else {
398a95959c3fb4c502b45bc78f15b65cda1f21620e6zachr@google.com        stream.writeText("}\n");
399a95959c3fb4c502b45bc78f15b65cda1f21620e6zachr@google.com    }
400945708ad9494322e2bc26776ccb741776205b4b8zachr@google.com}
401c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com
402c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.comvoid SkDiffContext::outputCsv(SkWStream& stream) {
403c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com    SkTDict<int> columns(2);
404c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com    int cntColumns = 0;
405c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com
406c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com    stream.writeText("key");
407c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com
408efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com    SkTLList<DiffRecord>::Iter iter(fRecords, SkTLList<DiffRecord>::Iter::kHead_IterStart);
409efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com    DiffRecord* currentRecord = iter.get();
410c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com
411c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com    // Write CSV header and create a dictionary of all columns.
41249f085dddff10473b6ebf832a974288300224e60bsalomon    while (currentRecord) {
413c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com        for (int diffIndex = 0; diffIndex < currentRecord->fDiffs.count(); diffIndex++) {
414c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com            DiffData& data = currentRecord->fDiffs[diffIndex];
415c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com            if (!columns.find(data.fDiffName)) {
416c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com                columns.set(data.fDiffName, cntColumns);
417c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com                stream.writeText(", ");
418c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com                stream.writeText(data.fDiffName);
419c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com                cntColumns++;
420c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com            }
421c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com        }
422efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com        currentRecord = iter.next();
423c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com    }
424c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com    stream.writeText("\n");
425c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com
426c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com    double values[100];
427c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com    SkASSERT(cntColumns < 100);  // Make the array larger, if we ever have so many diff types.
428c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com
429efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com    SkTLList<DiffRecord>::Iter iter2(fRecords, SkTLList<DiffRecord>::Iter::kHead_IterStart);
430efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com    currentRecord = iter2.get();
43149f085dddff10473b6ebf832a974288300224e60bsalomon    while (currentRecord) {
432c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com        for (int i = 0; i < cntColumns; i++) {
433c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com            values[i] = -1;
434c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com        }
435c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com
436c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com        for (int diffIndex = 0; diffIndex < currentRecord->fDiffs.count(); diffIndex++) {
437c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com            DiffData& data = currentRecord->fDiffs[diffIndex];
438c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com            int index = -1;
439c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com            SkAssertResult(columns.find(data.fDiffName, &index));
440c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com            SkASSERT(index >= 0 && index < cntColumns);
441efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com            values[index] = data.fResult.result;
442c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com        }
443c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com
444c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com        const char* filename = currentRecord->fBaselinePath.c_str() +
445c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com                strlen(currentRecord->fBaselinePath.c_str()) - 1;
446c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com        while (filename > currentRecord->fBaselinePath.c_str() && *(filename - 1) != '/') {
447c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com            filename--;
448c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com        }
449c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com
450c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com        stream.writeText(filename);
451c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com
452c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com        for (int i = 0; i < cntColumns; i++) {
453c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com            SkString str;
454c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com            str.printf(", %f", values[i]);
455c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com            stream.writeText(str.c_str());
456c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com        }
457c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com        stream.writeText("\n");
458c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com
459efc51b79a22348e3c2596e872609a7a4b018e531djsollen@google.com        currentRecord = iter2.next();
460c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com    }
461c93c8ac625a89c14f392d46620abaf6edfabe02eedisonn@google.com}
462