rebaseline_server: use just skpdiff, not Python Image Library
BUG=skia:2414 R=djsollen@google.com, borenet@google.com Author: epoger@google.com Review URL: https://codereview.chromium.org/325413003
This commit is contained in:
parent
a887026421
commit
54f1ad8bb5
@ -10,7 +10,7 @@ Calulate differences between image pairs, and store them in a database.
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
import csv
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
@ -18,11 +18,6 @@ import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import urllib
|
||||
try:
|
||||
from PIL import Image, ImageChops
|
||||
except ImportError:
|
||||
raise ImportError('Requires PIL to be installed; see '
|
||||
+ 'http://www.pythonware.com/products/pil/')
|
||||
|
||||
# Set the PYTHONPATH to include the tools directory.
|
||||
sys.path.append(
|
||||
@ -38,11 +33,9 @@ DEFAULT_IMAGES_SUBDIR = 'images'
|
||||
|
||||
DISALLOWED_FILEPATH_CHAR_REGEX = re.compile('[^\w\-]')
|
||||
|
||||
DIFFS_SUBDIR = 'diffs'
|
||||
RGBDIFFS_SUBDIR = 'diffs'
|
||||
WHITEDIFFS_SUBDIR = 'whitediffs'
|
||||
|
||||
VALUES_PER_BAND = 256
|
||||
|
||||
# Keys used within DiffRecord dictionary representations.
|
||||
# NOTE: Keep these in sync with static/constants.js
|
||||
KEY__DIFFERENCES__MAX_DIFF_PER_CHANNEL = 'maxDiffPerChannel'
|
||||
@ -87,9 +80,8 @@ class DiffRecord(object):
|
||||
actual_image_locator = _sanitize_locator(actual_image_locator)
|
||||
|
||||
# Download the expected/actual images, if we don't have them already.
|
||||
# TODO(rmistry): Add a parameter that makes _download_and_open_image raise
|
||||
# an exception if images are not found locally (instead of trying to
|
||||
# download them).
|
||||
# TODO(rmistry): Add a parameter that just tries to use already-present
|
||||
# image files rather than downloading them.
|
||||
expected_image_file = os.path.join(
|
||||
storage_root, expected_images_subdir,
|
||||
str(expected_image_locator) + image_suffix)
|
||||
@ -97,80 +89,91 @@ class DiffRecord(object):
|
||||
storage_root, actual_images_subdir,
|
||||
str(actual_image_locator) + image_suffix)
|
||||
try:
|
||||
expected_image = _download_and_open_image(
|
||||
expected_image_file, expected_image_url)
|
||||
_download_file(expected_image_file, expected_image_url)
|
||||
except Exception:
|
||||
logging.exception('unable to download expected_image_url %s to file %s' %
|
||||
(expected_image_url, expected_image_file))
|
||||
raise
|
||||
try:
|
||||
actual_image = _download_and_open_image(
|
||||
actual_image_file, actual_image_url)
|
||||
_download_file(actual_image_file, actual_image_url)
|
||||
except Exception:
|
||||
logging.exception('unable to download actual_image_url %s to file %s' %
|
||||
(actual_image_url, actual_image_file))
|
||||
raise
|
||||
|
||||
# Generate the diff image (absolute diff at each pixel) and
|
||||
# max_diff_per_channel.
|
||||
diff_image = _generate_image_diff(actual_image, expected_image)
|
||||
diff_histogram = diff_image.histogram()
|
||||
(diff_width, diff_height) = diff_image.size
|
||||
self._max_diff_per_channel = _max_per_band(diff_histogram)
|
||||
|
||||
# Generate the whitediff image (any differing pixels show as white).
|
||||
# This is tricky, because when you convert color images to grayscale or
|
||||
# black & white in PIL, it has its own ideas about thresholds.
|
||||
# We have to force it: if a pixel has any color at all, it's a '1'.
|
||||
bands = diff_image.split()
|
||||
graydiff_image = ImageChops.lighter(ImageChops.lighter(
|
||||
bands[0], bands[1]), bands[2])
|
||||
whitediff_image = (graydiff_image.point(lambda p: p > 0 and VALUES_PER_BAND)
|
||||
.convert('1', dither=Image.NONE))
|
||||
|
||||
# Calculate the perceptual difference percentage.
|
||||
skpdiff_csv_dir = tempfile.mkdtemp()
|
||||
# Get all diff images and values from skpdiff binary.
|
||||
skpdiff_output_dir = tempfile.mkdtemp()
|
||||
try:
|
||||
skpdiff_csv_output = os.path.join(skpdiff_csv_dir, 'skpdiff-output.csv')
|
||||
skpdiff_summary_file = os.path.join(skpdiff_output_dir,
|
||||
'skpdiff-output.json')
|
||||
skpdiff_rgbdiff_dir = os.path.join(skpdiff_output_dir, 'rgbDiff')
|
||||
skpdiff_whitediff_dir = os.path.join(skpdiff_output_dir, 'whiteDiff')
|
||||
expected_img = os.path.join(storage_root, expected_images_subdir,
|
||||
str(expected_image_locator) + image_suffix)
|
||||
actual_img = os.path.join(storage_root, actual_images_subdir,
|
||||
str(actual_image_locator) + image_suffix)
|
||||
|
||||
# TODO: Call skpdiff ONCE for all image pairs, instead of calling it
|
||||
# repeatedly. This will allow us to parallelize a lot more work.
|
||||
find_run_binary.run_command(
|
||||
[SKPDIFF_BINARY, '-p', expected_img, actual_img,
|
||||
'--csv', skpdiff_csv_output, '-d', 'perceptual'])
|
||||
with contextlib.closing(open(skpdiff_csv_output)) as csv_file:
|
||||
for row in csv.DictReader(csv_file):
|
||||
perceptual_similarity = float(row[' perceptual'].strip())
|
||||
if not 0 <= perceptual_similarity <= 1:
|
||||
# skpdiff outputs -1 if the images are different sizes. Treat any
|
||||
# output that does not lie in [0, 1] as having 0% perceptual
|
||||
# similarity.
|
||||
perceptual_similarity = 0
|
||||
# skpdiff returns the perceptual similarity, convert it to get the
|
||||
# perceptual difference percentage.
|
||||
self._perceptual_difference = 100 - (perceptual_similarity * 100)
|
||||
'--jsonp', 'false',
|
||||
'--output', skpdiff_summary_file,
|
||||
'--differs', 'perceptual', 'different_pixels',
|
||||
'--rgbDiffDir', skpdiff_rgbdiff_dir,
|
||||
'--whiteDiffDir', skpdiff_whitediff_dir,
|
||||
])
|
||||
|
||||
# Get information out of the skpdiff_summary_file.
|
||||
with contextlib.closing(open(skpdiff_summary_file)) as fp:
|
||||
data = json.load(fp)
|
||||
|
||||
# For now, we can assume there is only one record in the output summary,
|
||||
# since we passed skpdiff only one pair of images.
|
||||
record = data['records'][0]
|
||||
self._width = record['width']
|
||||
self._height = record['height']
|
||||
# TODO: make max_diff_per_channel a tuple instead of a list, because the
|
||||
# structure is meaningful (first element is red, second is green, etc.)
|
||||
# See http://stackoverflow.com/a/626871
|
||||
self._max_diff_per_channel = [
|
||||
record['maxRedDiff'], record['maxGreenDiff'], record['maxBlueDiff']]
|
||||
rgb_diff_path = record['rgbDiffPath']
|
||||
white_diff_path = record['whiteDiffPath']
|
||||
per_differ_stats = record['diffs']
|
||||
for stats in per_differ_stats:
|
||||
differ_name = stats['differName']
|
||||
if differ_name == 'different_pixels':
|
||||
self._num_pixels_differing = stats['pointsOfInterest']
|
||||
elif differ_name == 'perceptual':
|
||||
perceptual_similarity = stats['result']
|
||||
|
||||
# skpdiff returns the perceptual similarity; convert it to get the
|
||||
# perceptual difference percentage.
|
||||
# skpdiff outputs -1 if the images are different sizes. Treat any
|
||||
# output that does not lie in [0, 1] as having 0% perceptual
|
||||
# similarity.
|
||||
if not 0 <= perceptual_similarity <= 1:
|
||||
perceptual_similarity = 0
|
||||
self._perceptual_difference = 100 - (perceptual_similarity * 100)
|
||||
|
||||
# Store the rgbdiff and whitediff images generated above.
|
||||
diff_image_locator = _get_difference_locator(
|
||||
expected_image_locator=expected_image_locator,
|
||||
actual_image_locator=actual_image_locator)
|
||||
basename = str(diff_image_locator) + image_suffix
|
||||
_mkdir_unless_exists(os.path.join(storage_root, RGBDIFFS_SUBDIR))
|
||||
_mkdir_unless_exists(os.path.join(storage_root, WHITEDIFFS_SUBDIR))
|
||||
# TODO: Modify skpdiff's behavior so we can tell it exactly where to
|
||||
# write the image files into, rather than having to move them around
|
||||
# after skpdiff writes them out.
|
||||
shutil.copyfile(rgb_diff_path,
|
||||
os.path.join(storage_root, RGBDIFFS_SUBDIR, basename))
|
||||
shutil.copyfile(white_diff_path,
|
||||
os.path.join(storage_root, WHITEDIFFS_SUBDIR, basename))
|
||||
|
||||
finally:
|
||||
shutil.rmtree(skpdiff_csv_dir)
|
||||
|
||||
# Final touches on diff_image: use whitediff_image as an alpha mask.
|
||||
# Unchanged pixels are transparent; differing pixels are opaque.
|
||||
diff_image.putalpha(whitediff_image)
|
||||
|
||||
# Store the diff and whitediff images generated above.
|
||||
diff_image_locator = _get_difference_locator(
|
||||
expected_image_locator=expected_image_locator,
|
||||
actual_image_locator=actual_image_locator)
|
||||
basename = str(diff_image_locator) + image_suffix
|
||||
_save_image(diff_image, os.path.join(
|
||||
storage_root, DIFFS_SUBDIR, basename))
|
||||
_save_image(whitediff_image, os.path.join(
|
||||
storage_root, WHITEDIFFS_SUBDIR, basename))
|
||||
|
||||
# Calculate difference metrics.
|
||||
(self._width, self._height) = diff_image.size
|
||||
self._num_pixels_differing = (
|
||||
whitediff_image.histogram()[VALUES_PER_BAND - 1])
|
||||
shutil.rmtree(skpdiff_output_dir)
|
||||
|
||||
def get_num_pixels_differing(self):
|
||||
"""Returns the absolute number of pixels that differ."""
|
||||
@ -278,102 +281,18 @@ class ImageDiffDB(object):
|
||||
|
||||
# Utility functions
|
||||
|
||||
def _max_per_band(histogram):
|
||||
"""Given the histogram of an image, return the maximum value of each band
|
||||
(a.k.a. "color channel", such as R/G/B) across the entire image.
|
||||
|
||||
Args:
|
||||
histogram: PIL histogram
|
||||
|
||||
Returns the maximum value of each band within the image histogram, as a list.
|
||||
"""
|
||||
max_per_band = []
|
||||
assert(len(histogram) % VALUES_PER_BAND == 0)
|
||||
num_bands = len(histogram) / VALUES_PER_BAND
|
||||
for band in xrange(num_bands):
|
||||
# Assuming that VALUES_PER_BAND is 256...
|
||||
# the 'R' band makes up indices 0-255 in the histogram,
|
||||
# the 'G' band makes up indices 256-511 in the histogram,
|
||||
# etc.
|
||||
min_index = band * VALUES_PER_BAND
|
||||
index = min_index + VALUES_PER_BAND
|
||||
while index > min_index:
|
||||
index -= 1
|
||||
if histogram[index] > 0:
|
||||
max_per_band.append(index - min_index)
|
||||
break
|
||||
return max_per_band
|
||||
|
||||
|
||||
def _generate_image_diff(image1, image2):
|
||||
"""Wrapper for ImageChops.difference(image1, image2) that will handle some
|
||||
errors automatically, or at least yield more useful error messages.
|
||||
|
||||
TODO(epoger): Currently, some of the images generated by the bots are RGBA
|
||||
and others are RGB. I'm not sure why that is. For now, to avoid confusion
|
||||
within the UI, convert all to RGB when diffing.
|
||||
|
||||
Args:
|
||||
image1: a PIL image object
|
||||
image2: a PIL image object
|
||||
|
||||
Returns: per-pixel diffs between image1 and image2, as a PIL image object
|
||||
"""
|
||||
try:
|
||||
return ImageChops.difference(image1.convert('RGB'), image2.convert('RGB'))
|
||||
except ValueError:
|
||||
logging.error('Error diffing image1 [%s] and image2 [%s].' % (
|
||||
repr(image1), repr(image2)))
|
||||
raise
|
||||
|
||||
|
||||
def _download_and_open_image(local_filepath, url):
|
||||
"""Open the image at local_filepath; if there is no file at that path,
|
||||
download it from url to that path and then open it.
|
||||
def _download_file(local_filepath, url):
|
||||
"""Download a file from url to local_filepath, unless it is already there.
|
||||
|
||||
Args:
|
||||
local_filepath: path on local disk where the image should be stored
|
||||
url: URL from which we can download the image if we don't have it yet
|
||||
|
||||
Returns: a PIL image object
|
||||
"""
|
||||
if not os.path.exists(local_filepath):
|
||||
_mkdir_unless_exists(os.path.dirname(local_filepath))
|
||||
with contextlib.closing(urllib.urlopen(url)) as url_handle:
|
||||
with open(local_filepath, 'wb') as file_handle:
|
||||
shutil.copyfileobj(fsrc=url_handle, fdst=file_handle)
|
||||
return _open_image(local_filepath)
|
||||
|
||||
|
||||
def _open_image(filepath):
|
||||
"""Wrapper for Image.open(filepath) that yields more useful error messages.
|
||||
|
||||
Args:
|
||||
filepath: path on local disk to load image from
|
||||
|
||||
Returns: a PIL image object
|
||||
"""
|
||||
try:
|
||||
return Image.open(filepath)
|
||||
except IOError:
|
||||
# If we are unable to load an image from the file, delete it from disk
|
||||
# and we will try to fetch it again next time. Fixes http://skbug.com/2247
|
||||
logging.error('IOError loading image file %s ; deleting it.' % filepath)
|
||||
os.remove(filepath)
|
||||
raise
|
||||
|
||||
|
||||
def _save_image(image, filepath, format='PNG'):
|
||||
"""Write an image to disk, creating any intermediate directories as needed.
|
||||
|
||||
Args:
|
||||
image: a PIL image object
|
||||
filepath: path on local disk to write image to
|
||||
format: one of the PIL image formats, listed at
|
||||
http://effbot.org/imagingbook/formats.htm
|
||||
"""
|
||||
_mkdir_unless_exists(os.path.dirname(filepath))
|
||||
image.save(filepath, format)
|
||||
|
||||
|
||||
def _mkdir_unless_exists(path):
|
||||
|
@ -19,7 +19,8 @@ import imagediffdb
|
||||
import imagepair
|
||||
|
||||
|
||||
IMG_URL_BASE = 'http://chromium-skia-gm.commondatastorage.googleapis.com/gm/bitmap-64bitMD5/'
|
||||
IMG_URL_BASE = ('http://chromium-skia-gm.commondatastorage.googleapis.com/'
|
||||
'gm/bitmap-64bitMD5/')
|
||||
|
||||
|
||||
class ImagePairTest(unittest.TestCase):
|
||||
@ -87,7 +88,7 @@ class ImagePairTest(unittest.TestCase):
|
||||
'maxDiffPerChannel': [255, 255, 247],
|
||||
'numDifferingPixels': 662,
|
||||
'percentDifferingPixels': 0.0662,
|
||||
'perceptualDifference': 0.06620000000000914,
|
||||
'perceptualDifference': 0.06620300000000157,
|
||||
},
|
||||
'imageAUrl': 'arcofzorro/16206093933823793653.png',
|
||||
'imageBUrl': 'arcofzorro/13786535001616823825.png',
|
||||
|
@ -14,7 +14,7 @@ import posixpath
|
||||
|
||||
# Local imports
|
||||
import column
|
||||
import imagepair
|
||||
import imagediffdb
|
||||
|
||||
# Keys used within dictionary representation of ImagePairSet.
|
||||
# NOTE: Keep these in sync with static/constants.js
|
||||
@ -157,12 +157,12 @@ class ImagePairSet(object):
|
||||
KEY__IMAGESETS__SET__DIFFS: {
|
||||
key_description: 'color difference per channel',
|
||||
key_base_url: posixpath.join(
|
||||
self._diff_base_url, 'diffs'),
|
||||
self._diff_base_url, imagediffdb.RGBDIFFS_SUBDIR),
|
||||
},
|
||||
KEY__IMAGESETS__SET__WHITEDIFFS: {
|
||||
key_description: 'differing pixels in white',
|
||||
key_base_url: posixpath.join(
|
||||
self._diff_base_url, 'whitediffs'),
|
||||
self._diff_base_url, imagediffdb.WHITEDIFFS_SUBDIR),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -94,7 +94,7 @@
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
"dataHash": "5496105477154010366",
|
||||
"dataHash": "-5829724510169924592",
|
||||
"isEditable": false,
|
||||
"isExported": true,
|
||||
"schemaVersion": 3,
|
||||
@ -189,7 +189,7 @@
|
||||
],
|
||||
"numDifferingPixels": 6081,
|
||||
"percentDifferingPixels": 2.4324,
|
||||
"perceptualDifference": 1.917199999999994
|
||||
"perceptualDifference": 1.9172010000000057
|
||||
},
|
||||
"extraColumns": {
|
||||
"builder": "Test-Builder-We-Have-No-Expectations-File-For",
|
||||
@ -210,7 +210,7 @@
|
||||
],
|
||||
"numDifferingPixels": 50097,
|
||||
"percentDifferingPixels": 30.5767822265625,
|
||||
"perceptualDifference": 3.3917
|
||||
"perceptualDifference": 3.391725000000008
|
||||
},
|
||||
"extraColumns": {
|
||||
"builder": "Test-Builder-We-Have-No-Expectations-File-For",
|
||||
@ -253,7 +253,7 @@
|
||||
],
|
||||
"numDifferingPixels": 6081,
|
||||
"percentDifferingPixels": 2.4324,
|
||||
"perceptualDifference": 1.917199999999994
|
||||
"perceptualDifference": 1.9172010000000057
|
||||
},
|
||||
"extraColumns": {
|
||||
"builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
|
||||
@ -274,7 +274,7 @@
|
||||
],
|
||||
"numDifferingPixels": 50097,
|
||||
"percentDifferingPixels": 30.5767822265625,
|
||||
"perceptualDifference": 3.3917
|
||||
"perceptualDifference": 3.391725000000008
|
||||
},
|
||||
"extraColumns": {
|
||||
"builder": "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
|
||||
|
@ -110,7 +110,7 @@
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
"dataHash": "7849962375815855931",
|
||||
"dataHash": "-7804718549064096650",
|
||||
"isEditable": false,
|
||||
"isExported": true,
|
||||
"schemaVersion": 3,
|
||||
@ -128,7 +128,7 @@
|
||||
],
|
||||
"numDifferingPixels": 120000,
|
||||
"percentDifferingPixels": 75.0,
|
||||
"perceptualDifference": 50.122499999999995
|
||||
"perceptualDifference": 50.122499
|
||||
},
|
||||
"expectations": {
|
||||
"bugs": null,
|
||||
@ -154,7 +154,7 @@
|
||||
],
|
||||
"numDifferingPixels": 765891,
|
||||
"percentDifferingPixels": 97.38807678222656,
|
||||
"perceptualDifference": 25.25699999999999
|
||||
"perceptualDifference": 25.256985
|
||||
},
|
||||
"expectations": {
|
||||
"bugs": [
|
||||
@ -182,7 +182,7 @@
|
||||
],
|
||||
"numDifferingPixels": 422432,
|
||||
"percentDifferingPixels": 53.715006510416664,
|
||||
"perceptualDifference": 25.120500000000007
|
||||
"perceptualDifference": 25.120543999999995
|
||||
},
|
||||
"expectations": {
|
||||
"bugs": [
|
||||
@ -202,16 +202,6 @@
|
||||
"isDifferent": true
|
||||
},
|
||||
{
|
||||
"differenceData": {
|
||||
"maxDiffPerChannel": [
|
||||
222,
|
||||
223,
|
||||
222
|
||||
],
|
||||
"numDifferingPixels": 53150,
|
||||
"percentDifferingPixels": 12.035778985507246,
|
||||
"perceptualDifference": 100
|
||||
},
|
||||
"expectations": {
|
||||
"bugs": [
|
||||
1578
|
||||
@ -230,16 +220,6 @@
|
||||
"isDifferent": true
|
||||
},
|
||||
{
|
||||
"differenceData": {
|
||||
"maxDiffPerChannel": [
|
||||
221,
|
||||
221,
|
||||
221
|
||||
],
|
||||
"numDifferingPixels": 53773,
|
||||
"percentDifferingPixels": 12.17685688405797,
|
||||
"perceptualDifference": 100
|
||||
},
|
||||
"expectations": {
|
||||
"bugs": [
|
||||
1578
|
||||
|
@ -9,11 +9,13 @@
|
||||
#include "SkImageDecoder.h"
|
||||
#include "SkOSFile.h"
|
||||
#include "SkRunnable.h"
|
||||
#include "SkSize.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkTDict.h"
|
||||
#include "SkThreadPool.h"
|
||||
|
||||
#include "SkDiffContext.h"
|
||||
#include "SkImageDiffer.h"
|
||||
#include "skpdiff_util.h"
|
||||
|
||||
SkDiffContext::SkDiffContext() {
|
||||
@ -28,9 +30,21 @@ SkDiffContext::~SkDiffContext() {
|
||||
}
|
||||
}
|
||||
|
||||
void SkDiffContext::setDifferenceDir(const SkString& path) {
|
||||
void SkDiffContext::setAlphaMaskDir(const SkString& path) {
|
||||
if (!path.isEmpty() && sk_mkdir(path.c_str())) {
|
||||
fDifferenceDir = path;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,13 +104,13 @@ void SkDiffContext::addDiff(const char* baselinePath, const char* testPath) {
|
||||
|
||||
newRecord->fBaselinePath = baselinePath;
|
||||
newRecord->fTestPath = testPath;
|
||||
newRecord->fSize = SkISize::Make(baselineBitmap.width(), baselineBitmap.height());
|
||||
|
||||
bool alphaMaskPending = false;
|
||||
|
||||
// only enable alpha masks if a difference dir has been provided
|
||||
if (!fDifferenceDir.isEmpty()) {
|
||||
alphaMaskPending = true;
|
||||
}
|
||||
// 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();
|
||||
|
||||
// Perform each diff
|
||||
for (int differIndex = 0; differIndex < fDifferCount; differIndex++) {
|
||||
@ -106,30 +120,69 @@ void SkDiffContext::addDiff(const char* baselinePath, const char* testPath) {
|
||||
DiffData& diffData = newRecord->fDiffs.push_back();
|
||||
diffData.fDiffName = differ->getName();
|
||||
|
||||
if (!differ->diff(&baselineBitmap, &testBitmap, alphaMaskPending, &diffData.fResult)) {
|
||||
// if the diff failed record -1 as the result
|
||||
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')
|
||||
diffData.fResult.result = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (alphaMaskPending
|
||||
if (bitmapsToCreate.alphaMask
|
||||
&& SkImageDiffer::RESULT_CORRECT != diffData.fResult.result
|
||||
&& !diffData.fResult.poiAlphaMask.empty()
|
||||
&& !newRecord->fCommonName.isEmpty()) {
|
||||
|
||||
newRecord->fDifferencePath = SkOSPath::SkPathJoin(fDifferenceDir.c_str(),
|
||||
newRecord->fCommonName.c_str());
|
||||
newRecord->fAlphaMaskPath = SkOSPath::SkPathJoin(fAlphaMaskDir.c_str(),
|
||||
newRecord->fCommonName.c_str());
|
||||
|
||||
// compute the image diff and output it
|
||||
SkBitmap copy;
|
||||
diffData.fResult.poiAlphaMask.copyTo(©, kN32_SkColorType);
|
||||
SkImageEncoder::EncodeFile(newRecord->fDifferencePath.c_str(), copy,
|
||||
SkImageEncoder::EncodeFile(newRecord->fAlphaMaskPath.c_str(), copy,
|
||||
SkImageEncoder::kPNG_Type, 100);
|
||||
|
||||
// cleanup the existing bitmap to free up resources;
|
||||
diffData.fResult.poiAlphaMask.reset();
|
||||
|
||||
alphaMaskPending = false;
|
||||
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;
|
||||
|
||||
newRecord->fRgbDiffPath = SkOSPath::SkPathJoin(fRgbDiffDir.c_str(),
|
||||
newRecord->fCommonName.c_str());
|
||||
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()) {
|
||||
newRecord->fWhiteDiffPath = SkOSPath::SkPathJoin(fWhiteDiffDir.c_str(),
|
||||
newRecord->fCommonName.c_str());
|
||||
SkImageEncoder::EncodeFile(newRecord->fWhiteDiffPath.c_str(),
|
||||
diffData.fResult.whiteDiffBitmap,
|
||||
SkImageEncoder::kPNG_Type, 100);
|
||||
diffData.fResult.whiteDiffBitmap.reset();
|
||||
bitmapsToCreate.whiteDiff = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -229,11 +282,15 @@ void SkDiffContext::outputRecords(SkWStream& stream, bool useJSONP) {
|
||||
} else {
|
||||
stream.writeText("{\n");
|
||||
}
|
||||
|
||||
// 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?')
|
||||
stream.writeText(" \"records\": [\n");
|
||||
while (NULL != currentRecord) {
|
||||
stream.writeText(" {\n");
|
||||
|
||||
SkString differenceAbsPath = get_absolute_path(currentRecord->fDifferencePath);
|
||||
SkString baselineAbsPath = get_absolute_path(currentRecord->fBaselinePath);
|
||||
SkString testAbsPath = get_absolute_path(currentRecord->fTestPath);
|
||||
|
||||
@ -242,7 +299,15 @@ void SkDiffContext::outputRecords(SkWStream& stream, bool useJSONP) {
|
||||
stream.writeText("\",\n");
|
||||
|
||||
stream.writeText(" \"differencePath\": \"");
|
||||
stream.writeText(differenceAbsPath.c_str());
|
||||
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());
|
||||
stream.writeText("\",\n");
|
||||
|
||||
stream.writeText(" \"baselinePath\": \"");
|
||||
@ -253,6 +318,23 @@ void SkDiffContext::outputRecords(SkWStream& stream, bool useJSONP) {
|
||||
stream.writeText(testAbsPath.c_str());
|
||||
stream.writeText("\",\n");
|
||||
|
||||
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");
|
||||
|
||||
stream.writeText(" \"diffs\": [\n");
|
||||
for (int diffIndex = 0; diffIndex < currentRecord->fDiffs.count(); diffIndex++) {
|
||||
DiffData& data = currentRecord->fDiffs[diffIndex];
|
||||
|
@ -28,10 +28,28 @@ public:
|
||||
void setThreadCount(int threadCount) { fThreadCount = threadCount; }
|
||||
|
||||
/**
|
||||
* Creates the directory if it does not exist and uses it to store differences
|
||||
* between images.
|
||||
* Sets the directory within which to store alphaMasks (images that
|
||||
* are transparent for each pixel that differs between baseline and test).
|
||||
*
|
||||
* If the directory does not exist yet, it will be created.
|
||||
*/
|
||||
void setDifferenceDir(const SkString& directory);
|
||||
void setAlphaMaskDir(const SkString& directory);
|
||||
|
||||
/**
|
||||
* Sets the directory within which to store rgbDiffs (images showing the
|
||||
* per-channel difference between baseline and test at each pixel).
|
||||
*
|
||||
* If the directory does not exist yet, it will be created.
|
||||
*/
|
||||
void setRgbDiffDir(const SkString& directory);
|
||||
|
||||
/**
|
||||
* Sets the directory within which to store whiteDiffs (images showing white
|
||||
* for each pixel that differs between baseline and test).
|
||||
*
|
||||
* If the directory does not exist yet, it will be created.
|
||||
*/
|
||||
void setWhiteDiffDir(const SkString& directory);
|
||||
|
||||
/**
|
||||
* Sets the differs to be used in each diff. Already started diffs will not retroactively use
|
||||
@ -74,6 +92,14 @@ public:
|
||||
* "differencePath" : (optional) string containing the path to an alpha
|
||||
* mask of the pixel difference between the baseline
|
||||
* and test images
|
||||
* TODO(epoger): consider renaming this "alphaMaskPath"
|
||||
* to distinguish from other difference types?
|
||||
* "rgbDiffPath" : (optional) string containing the path to a bitmap
|
||||
* showing per-channel differences between the
|
||||
* baseline and test images at each pixel
|
||||
* "whiteDiffPath" : (optional) string containing the path to a bitmap
|
||||
* showing every pixel that differs between the
|
||||
* baseline and test images as white
|
||||
*
|
||||
* They also have an array named "diffs" with each element being one diff record for the two
|
||||
* images indicated in the above field.
|
||||
@ -117,10 +143,21 @@ private:
|
||||
};
|
||||
|
||||
struct DiffRecord {
|
||||
// TODO(djsollen): Some of these fields are required, while others are optional
|
||||
// (e.g., fRgbDiffPath is only filled in if SkDifferentPixelsMetric
|
||||
// was run). Figure out a way to note that. See http://skbug.com/2712
|
||||
// ('allow skpdiff to report different sets of result fields for
|
||||
// different comparison algorithms')
|
||||
SkString fCommonName;
|
||||
SkString fDifferencePath;
|
||||
SkString fAlphaMaskPath;
|
||||
SkString fRgbDiffPath;
|
||||
SkString fWhiteDiffPath;
|
||||
SkString fBaselinePath;
|
||||
SkString fTestPath;
|
||||
SkISize fSize;
|
||||
int fMaxRedDiff;
|
||||
int fMaxGreenDiff;
|
||||
int fMaxBlueDiff;
|
||||
SkTArray<DiffData> fDiffs;
|
||||
};
|
||||
|
||||
@ -137,7 +174,9 @@ private:
|
||||
int fDifferCount;
|
||||
int fThreadCount;
|
||||
|
||||
SkString fDifferenceDir;
|
||||
SkString fAlphaMaskDir;
|
||||
SkString fRgbDiffDir;
|
||||
SkString fWhiteDiffDir;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -28,7 +28,8 @@ class SkDifferentPixelsMetric :
|
||||
#endif
|
||||
public:
|
||||
virtual const char* getName() const SK_OVERRIDE;
|
||||
virtual bool diff(SkBitmap* baseline, SkBitmap* test, bool computeMask,
|
||||
virtual bool diff(SkBitmap* baseline, SkBitmap* test,
|
||||
const BitmapsToCreate& bitmapsToCreate,
|
||||
Result* result) const SK_OVERRIDE;
|
||||
|
||||
protected:
|
||||
|
@ -14,7 +14,8 @@ const char* SkDifferentPixelsMetric::getName() const {
|
||||
return "different_pixels";
|
||||
}
|
||||
|
||||
bool SkDifferentPixelsMetric::diff(SkBitmap* baseline, SkBitmap* test, bool computeMask,
|
||||
bool SkDifferentPixelsMetric::diff(SkBitmap* baseline, SkBitmap* test,
|
||||
const BitmapsToCreate& bitmapsToCreate,
|
||||
Result* result) const {
|
||||
double startTime = get_seconds();
|
||||
|
||||
@ -22,17 +23,34 @@ bool SkDifferentPixelsMetric::diff(SkBitmap* baseline, SkBitmap* test, bool comp
|
||||
if (baseline->width() != test->width() || baseline->height() != test->height() ||
|
||||
baseline->width() <= 0 || baseline->height() <= 0 ||
|
||||
baseline->colorType() != test->colorType()) {
|
||||
SkASSERT(baseline->width() == test->width());
|
||||
SkASSERT(baseline->height() == test->height());
|
||||
SkASSERT(baseline->width() > 0);
|
||||
SkASSERT(baseline->height() > 0);
|
||||
SkASSERT(baseline->colorType() == test->colorType());
|
||||
return false;
|
||||
}
|
||||
|
||||
int width = baseline->width();
|
||||
int height = baseline->height();
|
||||
int maxRedDiff = 0;
|
||||
int maxGreenDiff = 0;
|
||||
int maxBlueDiff = 0;
|
||||
|
||||
// Prepare the POI alpha mask if needed
|
||||
if (computeMask) {
|
||||
// Prepare any bitmaps we will be filling in
|
||||
if (bitmapsToCreate.alphaMask) {
|
||||
result->poiAlphaMask.allocPixels(SkImageInfo::MakeA8(width, height));
|
||||
result->poiAlphaMask.eraseARGB(SK_AlphaOPAQUE, 0, 0, 0);
|
||||
}
|
||||
if (bitmapsToCreate.rgbDiff) {
|
||||
result->rgbDiffBitmap.allocPixels(SkImageInfo::Make(width, height, baseline->colorType(),
|
||||
kPremul_SkAlphaType));
|
||||
result->rgbDiffBitmap.eraseARGB(SK_AlphaTRANSPARENT, 0, 0, 0);
|
||||
}
|
||||
if (bitmapsToCreate.whiteDiff) {
|
||||
result->whiteDiffBitmap.allocPixels(SkImageInfo::MakeN32Premul(width, height));
|
||||
result->whiteDiffBitmap.eraseARGB(SK_AlphaOPAQUE, 0, 0, 0);
|
||||
}
|
||||
|
||||
// Prepare the pixels for comparison
|
||||
result->poiCount = 0;
|
||||
@ -40,24 +58,60 @@ bool SkDifferentPixelsMetric::diff(SkBitmap* baseline, SkBitmap* test, bool comp
|
||||
test->lockPixels();
|
||||
for (int y = 0; y < height; y++) {
|
||||
// Grab a row from each image for easy comparison
|
||||
unsigned char* baselineRow = (unsigned char*)baseline->getAddr(0, y);
|
||||
unsigned char* testRow = (unsigned char*)test->getAddr(0, y);
|
||||
// TODO(epoger): The code below already assumes 4 bytes per pixel, so I think
|
||||
// we could just call getAddr32() to save a little time.
|
||||
// OR, if we want to play it safe, call ComputeBytesPerPixel instead
|
||||
// of assuming 4 bytes per pixel.
|
||||
uint32_t* baselineRow = static_cast<uint32_t *>(baseline->getAddr(0, y));
|
||||
uint32_t* testRow = static_cast<uint32_t *>(test->getAddr(0, y));
|
||||
for (int x = 0; x < width; x++) {
|
||||
// Compare one pixel at a time so each differing pixel can be noted
|
||||
if (memcmp(&baselineRow[x * 4], &testRow[x * 4], 4) != 0) {
|
||||
// TODO(epoger): This loop looks like a good place to work on performance,
|
||||
// but we should run the code through a profiler to be sure.
|
||||
uint32_t baselinePixel = baselineRow[x];
|
||||
uint32_t testPixel = testRow[x];
|
||||
if (baselinePixel != testPixel) {
|
||||
result->poiCount++;
|
||||
if (computeMask) {
|
||||
|
||||
int redDiff = abs(static_cast<int>(SkColorGetR(baselinePixel) -
|
||||
SkColorGetR(testPixel)));
|
||||
if (redDiff > maxRedDiff) {maxRedDiff = redDiff;}
|
||||
int greenDiff = abs(static_cast<int>(SkColorGetG(baselinePixel) -
|
||||
SkColorGetG(testPixel)));
|
||||
if (greenDiff > maxGreenDiff) {maxGreenDiff = greenDiff;}
|
||||
int blueDiff = abs(static_cast<int>(SkColorGetB(baselinePixel) -
|
||||
SkColorGetB(testPixel)));
|
||||
if (blueDiff > maxBlueDiff) {maxBlueDiff = blueDiff;}
|
||||
|
||||
if (bitmapsToCreate.alphaMask) {
|
||||
*result->poiAlphaMask.getAddr8(x,y) = SK_AlphaTRANSPARENT;
|
||||
}
|
||||
if (bitmapsToCreate.rgbDiff) {
|
||||
*result->rgbDiffBitmap.getAddr32(x,y) =
|
||||
SkColorSetRGB(redDiff, greenDiff, blueDiff);
|
||||
}
|
||||
if (bitmapsToCreate.whiteDiff) {
|
||||
*result->whiteDiffBitmap.getAddr32(x,y) = SK_ColorWHITE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
test->unlockPixels();
|
||||
baseline->unlockPixels();
|
||||
|
||||
if (computeMask) {
|
||||
result->maxRedDiff = maxRedDiff;
|
||||
result->maxGreenDiff = maxGreenDiff;
|
||||
result->maxBlueDiff = maxBlueDiff;
|
||||
|
||||
if (bitmapsToCreate.alphaMask) {
|
||||
result->poiAlphaMask.unlockPixels();
|
||||
}
|
||||
if (bitmapsToCreate.rgbDiff) {
|
||||
result->rgbDiffBitmap.unlockPixels();
|
||||
}
|
||||
if (bitmapsToCreate.whiteDiff) {
|
||||
result->whiteDiffBitmap.unlockPixels();
|
||||
}
|
||||
|
||||
// Calculates the percentage of identical pixels
|
||||
result->result = 1.0 - ((double)result->poiCount / (width * height));
|
||||
|
@ -36,7 +36,8 @@ const char* SkDifferentPixelsMetric::getName() const {
|
||||
return "different_pixels";
|
||||
}
|
||||
|
||||
bool SkDifferentPixelsMetric::diff(SkBitmap* baseline, SkBitmap* test, bool computeMask,
|
||||
bool SkDifferentPixelsMetric::diff(SkBitmap* baseline, SkBitmap* test, bool computeAlphaMask,
|
||||
bool computeRgbDiff, bool computeWhiteDiff,
|
||||
Result* result) const {
|
||||
double startTime = get_seconds();
|
||||
|
||||
|
@ -24,10 +24,30 @@ public:
|
||||
struct Result {
|
||||
double result;
|
||||
int poiCount;
|
||||
// TODO(djsollen): Figure out a way that the differ can report which of the
|
||||
// optional fields it has filled in. See http://skbug.com/2712 ('allow
|
||||
// skpdiff to report different sets of result fields for different comparison algorithms')
|
||||
SkBitmap poiAlphaMask; // optional
|
||||
SkBitmap rgbDiffBitmap; // optional
|
||||
SkBitmap whiteDiffBitmap; // optional
|
||||
int maxRedDiff; // optional
|
||||
int maxGreenDiff; // optional
|
||||
int maxBlueDiff; // optional
|
||||
double timeElapsed; // optional
|
||||
};
|
||||
|
||||
// A bitfield indicating which bitmap types we want a differ to create.
|
||||
//
|
||||
// TODO(epoger): Remove whiteDiffBitmap, because alphaMask can provide
|
||||
// the same functionality and more.
|
||||
// It will be a little bit tricky, because the rebaseline_server client
|
||||
// and server side code will both need to change to use the alphaMask.
|
||||
struct BitmapsToCreate {
|
||||
bool alphaMask;
|
||||
bool rgbDiff;
|
||||
bool whiteDiff;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a unique and descriptive name of this differ
|
||||
* @return A statically allocated null terminated string that is the name of this differ
|
||||
@ -43,10 +63,10 @@ public:
|
||||
* diff on a pair of bitmaps.
|
||||
* @param baseline The correct bitmap
|
||||
* @param test The bitmap whose difference is being tested
|
||||
* @param computeMask true if the differ is to attempt to create poiAlphaMask
|
||||
* @param bitmapsToCreate Which bitmaps the differ should attempt to create
|
||||
* @return true on success, and false in the case of failure
|
||||
*/
|
||||
virtual bool diff(SkBitmap* baseline, SkBitmap* test, bool computeMask,
|
||||
virtual bool diff(SkBitmap* baseline, SkBitmap* test, const BitmapsToCreate& bitmapsToCreate,
|
||||
Result* result) const = 0;
|
||||
};
|
||||
|
||||
|
@ -442,7 +442,8 @@ static double pmetric(const ImageLAB* baselineLAB, const ImageLAB* testLAB, int*
|
||||
return 1.0 - (double)(*poiCount) / (width * height);
|
||||
}
|
||||
|
||||
bool SkPMetric::diff(SkBitmap* baseline, SkBitmap* test, bool computeMask, Result* result) const {
|
||||
bool SkPMetric::diff(SkBitmap* baseline, SkBitmap* test, const BitmapsToCreate& bitmapsToCreate,
|
||||
Result* result) const {
|
||||
double startTime = get_seconds();
|
||||
|
||||
// Ensure the images are comparable
|
||||
|
@ -19,7 +19,7 @@
|
||||
class SkPMetric : public SkImageDiffer {
|
||||
public:
|
||||
virtual const char* getName() const SK_OVERRIDE { return "perceptual"; }
|
||||
virtual bool diff(SkBitmap* baseline, SkBitmap* test, bool computeMask,
|
||||
virtual bool diff(SkBitmap* baseline, SkBitmap* test, const BitmapsToCreate& bitmapsToCreate,
|
||||
Result* result) const SK_OVERRIDE;
|
||||
|
||||
private:
|
||||
|
@ -5,6 +5,11 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
// TODO(djsollen): Rename this whole package (perhaps to "SkMultiDiffer").
|
||||
// It's not just for "pdiff" (perceptual diffs)--it's a harness that allows
|
||||
// the execution of an arbitrary set of difference algorithms.
|
||||
// See http://skbug.com/2711 ('rename skpdiff')
|
||||
|
||||
#if SK_SUPPORT_OPENCL
|
||||
|
||||
#define __NO_STD_VECTOR // Uses cl::vectpr instead of std::vectpr
|
||||
@ -37,10 +42,12 @@ DEFINE_bool2(list, l, false, "List out available differs");
|
||||
DEFINE_string2(differs, d, "", "The names of the differs to use or all of them by default");
|
||||
DEFINE_string2(folders, f, "", "Compare two folders with identical subfile names: <baseline folder> <test folder>");
|
||||
DEFINE_string2(patterns, p, "", "Use two patterns to compare images: <baseline> <test>");
|
||||
DEFINE_string2(output, o, "", "Writes the output of these diffs to output: <output>");
|
||||
DEFINE_string(alphaDir, "", "Writes the alpha mask of these diffs to output: <output>");
|
||||
DEFINE_string2(output, o, "", "Writes a JSON summary of these diffs to file: <filepath>");
|
||||
DEFINE_string(alphaDir, "", "If the differ can generate an alpha mask, write it into directory: <dirpath>");
|
||||
DEFINE_string(rgbDiffDir, "", "If the differ can generate an image showing the RGB diff at each pixel, write it into directory: <dirpath>");
|
||||
DEFINE_string(whiteDiffDir, "", "If the differ can generate an image showing every changed pixel in white, write it into directory: <dirpath>");
|
||||
DEFINE_bool(jsonp, true, "Output JSON with padding");
|
||||
DEFINE_string(csv, "", "Writes the output of these diffs to a csv file");
|
||||
DEFINE_string(csv, "", "Writes the output of these diffs to a csv file: <filepath>");
|
||||
DEFINE_int32(threads, -1, "run N threads in parallel [default is derived from CPUs available]");
|
||||
|
||||
#if SK_SUPPORT_OPENCL
|
||||
@ -193,12 +200,30 @@ int tool_main(int argc, char * argv[]) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (!FLAGS_rgbDiffDir.isEmpty()) {
|
||||
if (1 != FLAGS_rgbDiffDir.count()) {
|
||||
SkDebugf("rgbDiffDir flag expects one argument: <directory>\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (!FLAGS_whiteDiffDir.isEmpty()) {
|
||||
if (1 != FLAGS_whiteDiffDir.count()) {
|
||||
SkDebugf("whiteDiffDir flag expects one argument: <directory>\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
SkDiffContext ctx;
|
||||
ctx.setDiffers(chosenDiffers);
|
||||
|
||||
if (!FLAGS_alphaDir.isEmpty()) {
|
||||
ctx.setDifferenceDir(SkString(FLAGS_alphaDir[0]));
|
||||
ctx.setAlphaMaskDir(SkString(FLAGS_alphaDir[0]));
|
||||
}
|
||||
if (!FLAGS_rgbDiffDir.isEmpty()) {
|
||||
ctx.setRgbDiffDir(SkString(FLAGS_rgbDiffDir[0]));
|
||||
}
|
||||
if (!FLAGS_whiteDiffDir.isEmpty()) {
|
||||
ctx.setWhiteDiffDir(SkString(FLAGS_whiteDiffDir[0]));
|
||||
}
|
||||
|
||||
if (FLAGS_threads >= 0) {
|
||||
|
Loading…
Reference in New Issue
Block a user