2013-07-02 19:55:32 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2013 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "SkBitmap.h"
|
|
|
|
#include "SkImageDecoder.h"
|
|
|
|
#include "SkOSFile.h"
|
2013-10-16 18:36:49 +00:00
|
|
|
#include "SkRunnable.h"
|
2014-07-02 14:43:04 +00:00
|
|
|
#include "SkSize.h"
|
2013-07-02 19:55:32 +00:00
|
|
|
#include "SkStream.h"
|
2013-07-22 15:24:26 +00:00
|
|
|
#include "SkTDict.h"
|
SkThreadPool ~~> SkTaskGroup
SkTaskGroup is like SkThreadPool except the threads stay in
one global pool. Each SkTaskGroup itself is tiny (4 bytes)
and its wait() method applies only to tasks add()ed to that
instance, not the whole thread pool.
This means we don't need to bring up new thread pools when
tests themselves want to use multithreading (e.g. pathops,
quilt). We just create a new SkTaskGroup and wait for that
to complete. This should be more efficient, and allow us
to expand where we use threads to really latency sensitive
places. E.g. we can probably now use these in nanobench
for CPU .skp rendering.
Now that all threads are sharing the same pool, I think we
can remove most of the custom mechanism pathops tests use
to control threading. They'll just ride on the global pool
with all other tests now.
This (temporarily?) removes the GPU multithreading feature
from DM, which we don't use.
On my desktop, DM runs a little faster (57s -> 55s) in
Debug, and a lot faster in Release (36s -> 24s). The bots
show speedups of similar proportions, cutting more than a
minute off the N4/Release and Win7/Debug runtimes.
BUG=skia:
Committed: https://skia.googlesource.com/skia/+/9c7207b5dc71dc5a96a2eb107d401133333d5b6f
R=caryclark@google.com, bsalomon@google.com, bungeman@google.com, mtklein@google.com, reed@google.com
Author: mtklein@chromium.org
Review URL: https://codereview.chromium.org/531653002
2014-09-03 22:34:37 +00:00
|
|
|
#include "SkTaskGroup.h"
|
2013-07-02 19:55:32 +00:00
|
|
|
|
2014-08-13 17:36:06 +00:00
|
|
|
// from the tools directory for replace_char(...)
|
|
|
|
#include "picture_utils.h"
|
|
|
|
|
2013-07-02 19:55:32 +00:00
|
|
|
#include "SkDiffContext.h"
|
2014-07-02 14:43:04 +00:00
|
|
|
#include "SkImageDiffer.h"
|
2013-07-02 19:55:32 +00:00
|
|
|
#include "skpdiff_util.h"
|
|
|
|
|
|
|
|
SkDiffContext::SkDiffContext() {
|
2015-08-27 14:41:13 +00:00
|
|
|
fDiffers = nullptr;
|
2013-07-02 19:55:32 +00:00
|
|
|
fDifferCount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkDiffContext::~SkDiffContext() {
|
2014-09-05 20:34:00 +00:00
|
|
|
if (fDiffers) {
|
2015-08-26 20:07:48 +00:00
|
|
|
delete[] fDiffers;
|
2013-07-02 19:55:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-02 14:43:04 +00:00
|
|
|
void SkDiffContext::setAlphaMaskDir(const SkString& path) {
|
2013-11-07 19:24:06 +00:00
|
|
|
if (!path.isEmpty() && sk_mkdir(path.c_str())) {
|
2014-07-02 14:43:04 +00:00
|
|
|
fAlphaMaskDir = path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SkDiffContext::setRgbDiffDir(const SkString& path) {
|
|
|
|
if (!path.isEmpty() && sk_mkdir(path.c_str())) {
|
|
|
|
fRgbDiffDir = path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SkDiffContext::setWhiteDiffDir(const SkString& path) {
|
|
|
|
if (!path.isEmpty() && sk_mkdir(path.c_str())) {
|
|
|
|
fWhiteDiffDir = path;
|
2013-11-07 19:24:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-13 17:36:06 +00:00
|
|
|
void SkDiffContext::setLongNames(const bool useLongNames) {
|
|
|
|
longNames = useLongNames;
|
|
|
|
}
|
|
|
|
|
2013-07-02 19:55:32 +00:00
|
|
|
void SkDiffContext::setDiffers(const SkTDArray<SkImageDiffer*>& differs) {
|
|
|
|
// Delete whatever the last array of differs was
|
2014-09-05 20:34:00 +00:00
|
|
|
if (fDiffers) {
|
2015-08-26 20:07:48 +00:00
|
|
|
delete[] fDiffers;
|
2015-08-27 14:41:13 +00:00
|
|
|
fDiffers = nullptr;
|
2013-07-02 19:55:32 +00:00
|
|
|
fDifferCount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy over the new differs
|
|
|
|
fDifferCount = differs.count();
|
2015-08-26 20:07:48 +00:00
|
|
|
fDiffers = new SkImageDiffer* [fDifferCount];
|
2013-07-02 19:55:32 +00:00
|
|
|
differs.copy(fDiffers);
|
|
|
|
}
|
|
|
|
|
2013-11-07 19:24:06 +00:00
|
|
|
static SkString get_common_prefix(const SkString& a, const SkString& b) {
|
|
|
|
const size_t maxPrefixLength = SkTMin(a.size(), b.size());
|
|
|
|
SkASSERT(maxPrefixLength > 0);
|
|
|
|
for (size_t x = 0; x < maxPrefixLength; ++x) {
|
|
|
|
if (a[x] != b[x]) {
|
|
|
|
SkString result;
|
|
|
|
result.set(a.c_str(), x);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (a.size() > b.size()) {
|
|
|
|
return b;
|
|
|
|
} else {
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-13 17:36:06 +00:00
|
|
|
static SkString get_combined_name(const SkString& a, const SkString& b) {
|
SkThreadPool ~~> SkTaskGroup
SkTaskGroup is like SkThreadPool except the threads stay in
one global pool. Each SkTaskGroup itself is tiny (4 bytes)
and its wait() method applies only to tasks add()ed to that
instance, not the whole thread pool.
This means we don't need to bring up new thread pools when
tests themselves want to use multithreading (e.g. pathops,
quilt). We just create a new SkTaskGroup and wait for that
to complete. This should be more efficient, and allow us
to expand where we use threads to really latency sensitive
places. E.g. we can probably now use these in nanobench
for CPU .skp rendering.
Now that all threads are sharing the same pool, I think we
can remove most of the custom mechanism pathops tests use
to control threading. They'll just ride on the global pool
with all other tests now.
This (temporarily?) removes the GPU multithreading feature
from DM, which we don't use.
On my desktop, DM runs a little faster (57s -> 55s) in
Debug, and a lot faster in Release (36s -> 24s). The bots
show speedups of similar proportions, cutting more than a
minute off the N4/Release and Win7/Debug runtimes.
BUG=skia:
Committed: https://skia.googlesource.com/skia/+/9c7207b5dc71dc5a96a2eb107d401133333d5b6f
R=caryclark@google.com, bsalomon@google.com, bungeman@google.com, mtklein@google.com, reed@google.com
Author: mtklein@chromium.org
Review URL: https://codereview.chromium.org/531653002
2014-09-03 22:34:37 +00:00
|
|
|
// Note (stephana): We must keep this function in sync with
|
2014-08-13 17:36:06 +00:00
|
|
|
// getImageDiffRelativeUrl() in static/loader.js (under rebaseline_server).
|
|
|
|
SkString result = a;
|
|
|
|
result.append("-vs-");
|
|
|
|
result.append(b);
|
|
|
|
sk_tools::replace_char(&result, '.', '_');
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2013-07-02 19:55:32 +00:00
|
|
|
void SkDiffContext::addDiff(const char* baselinePath, const char* testPath) {
|
|
|
|
// Load the images at the paths
|
|
|
|
SkBitmap baselineBitmap;
|
|
|
|
SkBitmap testBitmap;
|
|
|
|
if (!SkImageDecoder::DecodeFile(baselinePath, &baselineBitmap)) {
|
|
|
|
SkDebugf("Failed to load bitmap \"%s\"\n", baselinePath);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!SkImageDecoder::DecodeFile(testPath, &testBitmap)) {
|
|
|
|
SkDebugf("Failed to load bitmap \"%s\"\n", testPath);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup a record for this diff
|
2013-11-12 18:29:17 +00:00
|
|
|
fRecordMutex.acquire();
|
|
|
|
DiffRecord* newRecord = fRecords.addToHead(DiffRecord());
|
|
|
|
fRecordMutex.release();
|
2013-07-02 19:55:32 +00:00
|
|
|
|
2013-11-07 19:24:06 +00:00
|
|
|
// compute the common name
|
2014-07-29 02:26:58 +00:00
|
|
|
SkString baseName = SkOSPath::Basename(baselinePath);
|
|
|
|
SkString testName = SkOSPath::Basename(testPath);
|
2014-08-13 17:36:06 +00:00
|
|
|
|
|
|
|
if (longNames) {
|
|
|
|
newRecord->fCommonName = get_combined_name(baseName, testName);
|
|
|
|
} else {
|
|
|
|
newRecord->fCommonName = get_common_prefix(baseName, testName);
|
|
|
|
}
|
|
|
|
newRecord->fCommonName.append(".png");
|
2013-11-07 19:24:06 +00:00
|
|
|
|
2013-11-12 18:29:17 +00:00
|
|
|
newRecord->fBaselinePath = baselinePath;
|
|
|
|
newRecord->fTestPath = testPath;
|
2014-07-02 14:43:04 +00:00
|
|
|
newRecord->fSize = SkISize::Make(baselineBitmap.width(), baselineBitmap.height());
|
2013-11-12 18:29:17 +00:00
|
|
|
|
2014-07-02 14:43:04 +00:00
|
|
|
// only generate diff images if we have a place to store them
|
|
|
|
SkImageDiffer::BitmapsToCreate bitmapsToCreate;
|
|
|
|
bitmapsToCreate.alphaMask = !fAlphaMaskDir.isEmpty();
|
|
|
|
bitmapsToCreate.rgbDiff = !fRgbDiffDir.isEmpty();
|
|
|
|
bitmapsToCreate.whiteDiff = !fWhiteDiffDir.isEmpty();
|
2013-11-07 19:24:06 +00:00
|
|
|
|
2013-07-02 19:55:32 +00:00
|
|
|
// Perform each diff
|
|
|
|
for (int differIndex = 0; differIndex < fDifferCount; differIndex++) {
|
|
|
|
SkImageDiffer* differ = fDiffers[differIndex];
|
2013-11-12 18:29:17 +00:00
|
|
|
|
|
|
|
// Copy the results into data for this record
|
|
|
|
DiffData& diffData = newRecord->fDiffs.push_back();
|
|
|
|
diffData.fDiffName = differ->getName();
|
|
|
|
|
2014-07-02 14:43:04 +00:00
|
|
|
if (!differ->diff(&baselineBitmap, &testBitmap, bitmapsToCreate, &diffData.fResult)) {
|
|
|
|
// if the diff failed, record -1 as the result
|
|
|
|
// TODO(djsollen): Record more detailed information about exactly what failed.
|
|
|
|
// (Image dimension mismatch? etc.) See http://skbug.com/2710 ('make skpdiff
|
|
|
|
// report more detail when it fails to compare two images')
|
2014-02-11 16:29:39 +00:00
|
|
|
diffData.fResult.result = -1;
|
2013-11-12 18:29:17 +00:00
|
|
|
continue;
|
2013-11-07 19:24:06 +00:00
|
|
|
}
|
|
|
|
|
2014-07-02 14:43:04 +00:00
|
|
|
if (bitmapsToCreate.alphaMask
|
2013-11-12 18:29:17 +00:00
|
|
|
&& SkImageDiffer::RESULT_CORRECT != diffData.fResult.result
|
|
|
|
&& !diffData.fResult.poiAlphaMask.empty()
|
|
|
|
&& !newRecord->fCommonName.isEmpty()) {
|
|
|
|
|
2014-07-29 02:26:58 +00:00
|
|
|
newRecord->fAlphaMaskPath = SkOSPath::Join(fAlphaMaskDir.c_str(),
|
|
|
|
newRecord->fCommonName.c_str());
|
2013-11-12 18:29:17 +00:00
|
|
|
|
|
|
|
// compute the image diff and output it
|
|
|
|
SkBitmap copy;
|
2014-04-11 17:15:40 +00:00
|
|
|
diffData.fResult.poiAlphaMask.copyTo(©, kN32_SkColorType);
|
2014-07-02 14:43:04 +00:00
|
|
|
SkImageEncoder::EncodeFile(newRecord->fAlphaMaskPath.c_str(), copy,
|
2013-11-12 18:29:17 +00:00
|
|
|
SkImageEncoder::kPNG_Type, 100);
|
2013-11-07 19:24:06 +00:00
|
|
|
|
2013-11-12 18:29:17 +00:00
|
|
|
// cleanup the existing bitmap to free up resources;
|
|
|
|
diffData.fResult.poiAlphaMask.reset();
|
|
|
|
|
2014-07-02 14:43:04 +00:00
|
|
|
bitmapsToCreate.alphaMask = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bitmapsToCreate.rgbDiff
|
|
|
|
&& SkImageDiffer::RESULT_CORRECT != diffData.fResult.result
|
|
|
|
&& !diffData.fResult.rgbDiffBitmap.empty()
|
|
|
|
&& !newRecord->fCommonName.isEmpty()) {
|
|
|
|
// TODO(djsollen): Rather than taking the max r/g/b diffs that come back from
|
|
|
|
// a particular differ and storing them as toplevel fields within
|
|
|
|
// newRecord, we should extend outputRecords() to report optional
|
|
|
|
// fields for each differ (not just "result" and "pointsOfInterest").
|
|
|
|
// See http://skbug.com/2712 ('allow skpdiff to report different sets
|
|
|
|
// of result fields for different comparison algorithms')
|
|
|
|
newRecord->fMaxRedDiff = diffData.fResult.maxRedDiff;
|
|
|
|
newRecord->fMaxGreenDiff = diffData.fResult.maxGreenDiff;
|
|
|
|
newRecord->fMaxBlueDiff = diffData.fResult.maxBlueDiff;
|
|
|
|
|
2014-07-29 02:26:58 +00:00
|
|
|
newRecord->fRgbDiffPath = SkOSPath::Join(fRgbDiffDir.c_str(),
|
|
|
|
newRecord->fCommonName.c_str());
|
2014-07-02 14:43:04 +00:00
|
|
|
SkImageEncoder::EncodeFile(newRecord->fRgbDiffPath.c_str(),
|
|
|
|
diffData.fResult.rgbDiffBitmap,
|
|
|
|
SkImageEncoder::kPNG_Type, 100);
|
|
|
|
diffData.fResult.rgbDiffBitmap.reset();
|
|
|
|
bitmapsToCreate.rgbDiff = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bitmapsToCreate.whiteDiff
|
|
|
|
&& SkImageDiffer::RESULT_CORRECT != diffData.fResult.result
|
|
|
|
&& !diffData.fResult.whiteDiffBitmap.empty()
|
|
|
|
&& !newRecord->fCommonName.isEmpty()) {
|
2014-07-29 02:26:58 +00:00
|
|
|
newRecord->fWhiteDiffPath = SkOSPath::Join(fWhiteDiffDir.c_str(),
|
|
|
|
newRecord->fCommonName.c_str());
|
2014-07-02 14:43:04 +00:00
|
|
|
SkImageEncoder::EncodeFile(newRecord->fWhiteDiffPath.c_str(),
|
|
|
|
diffData.fResult.whiteDiffBitmap,
|
|
|
|
SkImageEncoder::kPNG_Type, 100);
|
|
|
|
diffData.fResult.whiteDiffBitmap.reset();
|
|
|
|
bitmapsToCreate.whiteDiff = false;
|
2013-07-02 19:55:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SkDiffContext::diffDirectories(const char baselinePath[], const char testPath[]) {
|
|
|
|
// Get the files in the baseline, we will then look for those inside the test path
|
|
|
|
SkTArray<SkString> baselineEntries;
|
|
|
|
if (!get_directory(baselinePath, &baselineEntries)) {
|
|
|
|
SkDebugf("Unable to open path \"%s\"\n", baselinePath);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-06-17 22:26:15 +00:00
|
|
|
sk_parallel_for(baselineEntries.count(), [&](int i) {
|
|
|
|
const char* baseFilename = baselineEntries[i].c_str();
|
2013-07-02 19:55:32 +00:00
|
|
|
|
|
|
|
// Find the real location of each file to compare
|
2014-07-29 02:26:58 +00:00
|
|
|
SkString baselineFile = SkOSPath::Join(baselinePath, baseFilename);
|
|
|
|
SkString testFile = SkOSPath::Join(testPath, baseFilename);
|
2013-07-02 19:55:32 +00:00
|
|
|
|
|
|
|
// Check that the test file exists and is a file
|
|
|
|
if (sk_exists(testFile.c_str()) && !sk_isdir(testFile.c_str())) {
|
2015-06-17 22:26:15 +00:00
|
|
|
this->addDiff(baselineFile.c_str(), testFile.c_str());
|
2013-07-02 19:55:32 +00:00
|
|
|
} else {
|
|
|
|
SkDebugf("Baseline file \"%s\" has no corresponding test file\n", baselineFile.c_str());
|
|
|
|
}
|
2015-06-17 22:26:15 +00:00
|
|
|
});
|
2013-07-02 19:55:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SkDiffContext::diffPatterns(const char baselinePattern[], const char testPattern[]) {
|
|
|
|
// Get the files in the baseline and test patterns. Because they are in sorted order, it's easy
|
|
|
|
// to find corresponding images by matching entry indices.
|
|
|
|
|
|
|
|
SkTArray<SkString> baselineEntries;
|
|
|
|
if (!glob_files(baselinePattern, &baselineEntries)) {
|
|
|
|
SkDebugf("Unable to get pattern \"%s\"\n", baselinePattern);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkTArray<SkString> testEntries;
|
|
|
|
if (!glob_files(testPattern, &testEntries)) {
|
|
|
|
SkDebugf("Unable to get pattern \"%s\"\n", testPattern);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (baselineEntries.count() != testEntries.count()) {
|
|
|
|
SkDebugf("Baseline and test patterns do not yield corresponding number of files\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-06-17 22:26:15 +00:00
|
|
|
sk_parallel_for(baselineEntries.count(), [&](int i) {
|
|
|
|
this->addDiff(baselineEntries[i].c_str(), testEntries[i].c_str());
|
|
|
|
});
|
2013-07-02 19:55:32 +00:00
|
|
|
}
|
|
|
|
|
2013-07-08 15:04:45 +00:00
|
|
|
void SkDiffContext::outputRecords(SkWStream& stream, bool useJSONP) {
|
2013-11-12 18:29:17 +00:00
|
|
|
SkTLList<DiffRecord>::Iter iter(fRecords, SkTLList<DiffRecord>::Iter::kHead_IterStart);
|
|
|
|
DiffRecord* currentRecord = iter.get();
|
|
|
|
|
2013-07-08 15:04:45 +00:00
|
|
|
if (useJSONP) {
|
|
|
|
stream.writeText("var SkPDiffRecords = {\n");
|
2013-07-25 17:22:58 +00:00
|
|
|
} else {
|
2013-07-08 15:04:45 +00:00
|
|
|
stream.writeText("{\n");
|
|
|
|
}
|
2014-07-02 14:43:04 +00:00
|
|
|
|
|
|
|
// TODO(djsollen): Would it be better to use the jsoncpp library to write out the JSON?
|
|
|
|
// This manual approach is probably more efficient, but it sure is ugly.
|
|
|
|
// See http://skbug.com/2713 ('make skpdiff use jsoncpp library to write out
|
|
|
|
// JSON output, instead of manual writeText() calls?')
|
2013-07-02 19:55:32 +00:00
|
|
|
stream.writeText(" \"records\": [\n");
|
2014-09-05 20:34:00 +00:00
|
|
|
while (currentRecord) {
|
2013-07-02 19:55:32 +00:00
|
|
|
stream.writeText(" {\n");
|
|
|
|
|
2013-08-02 15:54:30 +00:00
|
|
|
SkString baselineAbsPath = get_absolute_path(currentRecord->fBaselinePath);
|
|
|
|
SkString testAbsPath = get_absolute_path(currentRecord->fTestPath);
|
|
|
|
|
2013-10-16 15:00:11 +00:00
|
|
|
stream.writeText(" \"commonName\": \"");
|
2013-11-07 19:24:06 +00:00
|
|
|
stream.writeText(currentRecord->fCommonName.c_str());
|
2013-10-16 15:00:11 +00:00
|
|
|
stream.writeText("\",\n");
|
|
|
|
|
2013-10-16 18:36:49 +00:00
|
|
|
stream.writeText(" \"differencePath\": \"");
|
2014-07-02 14:43:04 +00:00
|
|
|
stream.writeText(get_absolute_path(currentRecord->fAlphaMaskPath).c_str());
|
|
|
|
stream.writeText("\",\n");
|
|
|
|
|
|
|
|
stream.writeText(" \"rgbDiffPath\": \"");
|
|
|
|
stream.writeText(get_absolute_path(currentRecord->fRgbDiffPath).c_str());
|
|
|
|
stream.writeText("\",\n");
|
|
|
|
|
|
|
|
stream.writeText(" \"whiteDiffPath\": \"");
|
|
|
|
stream.writeText(get_absolute_path(currentRecord->fWhiteDiffPath).c_str());
|
2013-10-16 18:36:49 +00:00
|
|
|
stream.writeText("\",\n");
|
|
|
|
|
2013-07-02 19:55:32 +00:00
|
|
|
stream.writeText(" \"baselinePath\": \"");
|
2013-08-02 15:54:30 +00:00
|
|
|
stream.writeText(baselineAbsPath.c_str());
|
2013-07-02 19:55:32 +00:00
|
|
|
stream.writeText("\",\n");
|
|
|
|
|
|
|
|
stream.writeText(" \"testPath\": \"");
|
2013-08-02 15:54:30 +00:00
|
|
|
stream.writeText(testAbsPath.c_str());
|
2013-07-02 19:55:32 +00:00
|
|
|
stream.writeText("\",\n");
|
|
|
|
|
2014-07-02 14:43:04 +00:00
|
|
|
stream.writeText(" \"width\": ");
|
|
|
|
stream.writeDecAsText(currentRecord->fSize.width());
|
|
|
|
stream.writeText(",\n");
|
|
|
|
stream.writeText(" \"height\": ");
|
|
|
|
stream.writeDecAsText(currentRecord->fSize.height());
|
|
|
|
stream.writeText(",\n");
|
|
|
|
|
|
|
|
stream.writeText(" \"maxRedDiff\": ");
|
|
|
|
stream.writeDecAsText(currentRecord->fMaxRedDiff);
|
|
|
|
stream.writeText(",\n");
|
|
|
|
stream.writeText(" \"maxGreenDiff\": ");
|
|
|
|
stream.writeDecAsText(currentRecord->fMaxGreenDiff);
|
|
|
|
stream.writeText(",\n");
|
|
|
|
stream.writeText(" \"maxBlueDiff\": ");
|
|
|
|
stream.writeDecAsText(currentRecord->fMaxBlueDiff);
|
|
|
|
stream.writeText(",\n");
|
|
|
|
|
2013-07-02 19:55:32 +00:00
|
|
|
stream.writeText(" \"diffs\": [\n");
|
|
|
|
for (int diffIndex = 0; diffIndex < currentRecord->fDiffs.count(); diffIndex++) {
|
|
|
|
DiffData& data = currentRecord->fDiffs[diffIndex];
|
|
|
|
stream.writeText(" {\n");
|
|
|
|
|
|
|
|
stream.writeText(" \"differName\": \"");
|
|
|
|
stream.writeText(data.fDiffName);
|
|
|
|
stream.writeText("\",\n");
|
|
|
|
|
|
|
|
stream.writeText(" \"result\": ");
|
2013-11-12 18:29:17 +00:00
|
|
|
stream.writeScalarAsText((SkScalar)data.fResult.result);
|
2013-07-02 19:55:32 +00:00
|
|
|
stream.writeText(",\n");
|
|
|
|
|
2013-11-12 18:29:17 +00:00
|
|
|
stream.writeText(" \"pointsOfInterest\": ");
|
|
|
|
stream.writeDecAsText(data.fResult.poiCount);
|
|
|
|
stream.writeText("\n");
|
|
|
|
|
2013-07-02 19:55:32 +00:00
|
|
|
stream.writeText(" }");
|
|
|
|
|
|
|
|
// JSON does not allow trailing commas
|
2013-07-25 17:22:58 +00:00
|
|
|
if (diffIndex + 1 < currentRecord->fDiffs.count()) {
|
2013-07-02 19:55:32 +00:00
|
|
|
stream.writeText(",");
|
|
|
|
}
|
|
|
|
stream.writeText(" \n");
|
|
|
|
}
|
|
|
|
stream.writeText(" ]\n");
|
|
|
|
|
|
|
|
stream.writeText(" }");
|
|
|
|
|
2013-11-12 18:29:17 +00:00
|
|
|
currentRecord = iter.next();
|
|
|
|
|
2013-07-02 19:55:32 +00:00
|
|
|
// JSON does not allow trailing commas
|
2014-09-05 20:34:00 +00:00
|
|
|
if (currentRecord) {
|
2013-07-02 19:55:32 +00:00
|
|
|
stream.writeText(",");
|
|
|
|
}
|
|
|
|
stream.writeText("\n");
|
|
|
|
}
|
|
|
|
stream.writeText(" ]\n");
|
2013-07-08 15:04:45 +00:00
|
|
|
if (useJSONP) {
|
|
|
|
stream.writeText("};\n");
|
2013-07-25 17:22:58 +00:00
|
|
|
} else {
|
2013-07-08 15:04:45 +00:00
|
|
|
stream.writeText("}\n");
|
|
|
|
}
|
2013-07-02 19:55:32 +00:00
|
|
|
}
|
2013-07-22 15:24:26 +00:00
|
|
|
|
|
|
|
void SkDiffContext::outputCsv(SkWStream& stream) {
|
|
|
|
SkTDict<int> columns(2);
|
|
|
|
int cntColumns = 0;
|
|
|
|
|
|
|
|
stream.writeText("key");
|
|
|
|
|
2013-11-12 18:29:17 +00:00
|
|
|
SkTLList<DiffRecord>::Iter iter(fRecords, SkTLList<DiffRecord>::Iter::kHead_IterStart);
|
|
|
|
DiffRecord* currentRecord = iter.get();
|
2013-07-22 15:24:26 +00:00
|
|
|
|
|
|
|
// Write CSV header and create a dictionary of all columns.
|
2014-09-05 20:34:00 +00:00
|
|
|
while (currentRecord) {
|
2013-07-22 15:24:26 +00:00
|
|
|
for (int diffIndex = 0; diffIndex < currentRecord->fDiffs.count(); diffIndex++) {
|
|
|
|
DiffData& data = currentRecord->fDiffs[diffIndex];
|
|
|
|
if (!columns.find(data.fDiffName)) {
|
|
|
|
columns.set(data.fDiffName, cntColumns);
|
|
|
|
stream.writeText(", ");
|
|
|
|
stream.writeText(data.fDiffName);
|
|
|
|
cntColumns++;
|
|
|
|
}
|
|
|
|
}
|
2013-11-12 18:29:17 +00:00
|
|
|
currentRecord = iter.next();
|
2013-07-22 15:24:26 +00:00
|
|
|
}
|
|
|
|
stream.writeText("\n");
|
|
|
|
|
|
|
|
double values[100];
|
|
|
|
SkASSERT(cntColumns < 100); // Make the array larger, if we ever have so many diff types.
|
|
|
|
|
2013-11-12 18:29:17 +00:00
|
|
|
SkTLList<DiffRecord>::Iter iter2(fRecords, SkTLList<DiffRecord>::Iter::kHead_IterStart);
|
|
|
|
currentRecord = iter2.get();
|
2014-09-05 20:34:00 +00:00
|
|
|
while (currentRecord) {
|
2013-07-22 15:24:26 +00:00
|
|
|
for (int i = 0; i < cntColumns; i++) {
|
|
|
|
values[i] = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int diffIndex = 0; diffIndex < currentRecord->fDiffs.count(); diffIndex++) {
|
|
|
|
DiffData& data = currentRecord->fDiffs[diffIndex];
|
|
|
|
int index = -1;
|
|
|
|
SkAssertResult(columns.find(data.fDiffName, &index));
|
|
|
|
SkASSERT(index >= 0 && index < cntColumns);
|
2013-11-12 18:29:17 +00:00
|
|
|
values[index] = data.fResult.result;
|
2013-07-22 15:24:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const char* filename = currentRecord->fBaselinePath.c_str() +
|
|
|
|
strlen(currentRecord->fBaselinePath.c_str()) - 1;
|
|
|
|
while (filename > currentRecord->fBaselinePath.c_str() && *(filename - 1) != '/') {
|
|
|
|
filename--;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream.writeText(filename);
|
|
|
|
|
|
|
|
for (int i = 0; i < cntColumns; i++) {
|
|
|
|
SkString str;
|
|
|
|
str.printf(", %f", values[i]);
|
|
|
|
stream.writeText(str.c_str());
|
|
|
|
}
|
|
|
|
stream.writeText("\n");
|
|
|
|
|
2013-11-12 18:29:17 +00:00
|
|
|
currentRecord = iter2.next();
|
2013-07-22 15:24:26 +00:00
|
|
|
}
|
|
|
|
}
|