You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
260 lines
6.0 KiB
260 lines
6.0 KiB
// Copyright 2015 Yahoo! Inc.
|
|
// Copyrights licensed under the Mit License. See the accompanying LICENSE file for terms.
|
|
|
|
var PNGImage = require('pngjs-image'),
|
|
Config = require('./lib/configuration/config'),
|
|
Image = require('./lib/image'),
|
|
PixelComparator = require('./lib/pixelComparator'),
|
|
constants = require('./lib/constants'),
|
|
Base = require('preceptor-core').Base;
|
|
|
|
/**
|
|
* @class BlinkDiff
|
|
* @extends Base
|
|
* @module Compare
|
|
*
|
|
* @property {Config} _configuration
|
|
* @property {PNGImage} _outputImage
|
|
* @property {PNGImage} _highlightImage
|
|
*/
|
|
var BlinkDiff = Base.extend(
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @constructor
|
|
* @param {object} options
|
|
*/
|
|
function (options) {
|
|
options = options || {};
|
|
options.blinkDiff = this;
|
|
this._configuration = new Config(options);
|
|
},
|
|
|
|
{
|
|
/**
|
|
* Gets the configuration
|
|
*
|
|
* @method getConfig
|
|
* @return {Config}
|
|
*/
|
|
getConfig: function () {
|
|
return this._configuration;
|
|
},
|
|
|
|
/**
|
|
* Gets the output image
|
|
*
|
|
* @method getOutputImage
|
|
* @return {PNGImage}
|
|
*/
|
|
getOutputImage: function () {
|
|
return this._outputImage;
|
|
},
|
|
|
|
/**
|
|
* Gets the highlight image
|
|
*
|
|
* @method getHighlightImage
|
|
* @return {PNGImage}
|
|
*/
|
|
getHighlightImage: function () {
|
|
return this._highlightImage;
|
|
},
|
|
|
|
|
|
/**
|
|
* Logs events to the console
|
|
*
|
|
* @method log
|
|
* @param {string} text
|
|
*/
|
|
log: function (text) {
|
|
if (this._configuration.isVerboseMode()) {
|
|
console.log(text);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Clips the images to the lower resolution of both, if needed
|
|
*
|
|
* @private
|
|
* @method _clip
|
|
* @param {PNGImage} imageA Source image
|
|
* @param {PNGImage} imageB Destination image
|
|
*/
|
|
_clip: function (imageA, imageB) {
|
|
|
|
var minWidth, minHeight;
|
|
|
|
if ((imageA.getWidth() != imageB.getWidth()) || (imageA.getHeight() != imageB.getHeight())) {
|
|
|
|
minWidth = Math.min(imageA.getWidth(), imageB.getWidth());
|
|
minHeight = Math.min(imageA.getHeight(), imageB.getHeight());
|
|
|
|
this.log("Clipping to " + minWidth + " x " + minHeight);
|
|
|
|
imageA.clip(0, 0, minWidth, minHeight);
|
|
imageB.clip(0, 0, minWidth, minHeight);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Has comparison passed?
|
|
*
|
|
* @method hasPassed
|
|
* @param {int} result Comparison result-code
|
|
* @return {boolean}
|
|
*/
|
|
hasPassed: function (result) {
|
|
return ((result !== constants.RESULT_DIFFERENT) && (result !== constants.RESULT_UNKNOWN));
|
|
},
|
|
|
|
/**
|
|
* Runs the comparison synchronously
|
|
*
|
|
* @method process
|
|
* @return {Object} Result of comparison { code, differences, dimension, width, height }
|
|
*/
|
|
process: function () {
|
|
|
|
// Catch all image errors
|
|
PNGImage.log = function (text) {
|
|
this.log('ERROR: ' + text);
|
|
throw new Error('ERROR: ' + text);
|
|
}.bind(this);
|
|
|
|
var config = this.getConfig(),
|
|
|
|
dimension,
|
|
flagField,
|
|
imageA = config.getImageA().getProcessedImage(),
|
|
imageB = config.getImageB().getProcessedImage(),
|
|
compareImageA, compareImageB,
|
|
|
|
highlightImage,
|
|
outputImage,
|
|
differences = 0,
|
|
shifts = 0,
|
|
i, index, color,
|
|
exported = false,
|
|
code = constants.RESULT_UNKNOWN;
|
|
|
|
this._highlightImage = null;
|
|
this._outputImage = null;
|
|
|
|
this._clip(imageA, imageB);
|
|
|
|
dimension = imageA.getWidth() * imageB.getWidth();
|
|
flagField = new Buffer(dimension);
|
|
flagField.fill(0);
|
|
|
|
config.getComparisons().forEach(function (comparison, index) {
|
|
|
|
this.log('Apply comparison #' + index);
|
|
|
|
compareImageA = Image.processImage(PNGImage.copyImage(imageA), comparison);
|
|
compareImageB = Image.processImage(PNGImage.copyImage(imageB), comparison);
|
|
|
|
var pixelCompare = new PixelComparator(compareImageA, compareImageB, config);
|
|
pixelCompare.compare(comparison, flagField);
|
|
|
|
}.bind(this));
|
|
|
|
if (config.isDebugMode()) { // In debug-mode? Export comparison image
|
|
|
|
this.log('In debug-mode');
|
|
|
|
imageA = compareImageA || imageA;
|
|
imageB = compareImageB || imageB;
|
|
}
|
|
|
|
highlightImage = PNGImage.createImage(imageA.getWidth(), imageA.getHeight());
|
|
|
|
outputImage = PNGImage.createImage(imageA.getWidth(), imageA.getHeight());
|
|
config.getOutput().copyImage(imageA, imageB, outputImage);
|
|
|
|
// Draw and count flag-field
|
|
for(i = 0; i < dimension; i++) {
|
|
|
|
index = i * 4;
|
|
|
|
// Count
|
|
if (flagField[i] & 1 == 1) {
|
|
differences++;
|
|
}
|
|
if (flagField[i] & 2 == 2) {
|
|
shifts++;
|
|
}
|
|
|
|
// Draw
|
|
if (flagField[i] & 1 == 1) {
|
|
color = config.getDiffColor();
|
|
|
|
} else if (flagField[i] & 2 == 2) {
|
|
color = config.getIgnoreColor();
|
|
|
|
} else {
|
|
color = config.getBackgroundColor();
|
|
}
|
|
|
|
outputImage.setAtIndex(index, color.getColor(true, true));
|
|
highlightImage.setAtIndex(index, color.getColor(false, false));
|
|
}
|
|
|
|
// Create composition if requested
|
|
this._highlightImage = highlightImage;
|
|
this._outputImage = config.getOutput().createComposition(imageA, imageB, outputImage);
|
|
|
|
// Result
|
|
if (differences == 0) {
|
|
this.log("Images are identical or near identical");
|
|
code = constants.RESULT_IDENTICAL;
|
|
|
|
} else if (config.getThreshold().isAboveThreshold(differences, dimension)) {
|
|
this.log("Images are visibly different");
|
|
this.log(differences + " pixels are different");
|
|
code = constants.RESULT_DIFFERENT;
|
|
|
|
} else {
|
|
this.log("Images are similar");
|
|
this.log(differences + " pixels are different");
|
|
code = constants.RESULT_SIMILAR;
|
|
}
|
|
|
|
// Need to write to the filesystem?
|
|
if (config.getOutput().withinOutputLimit(code)) {
|
|
if (config.getOutput().writeImage(this._outputImage)) {
|
|
this.log("Wrote differences to " + config.getOutput().getImagePath());
|
|
exported = true;
|
|
}
|
|
}
|
|
|
|
return {
|
|
code: code,
|
|
differences: differences,
|
|
shifts: shifts,
|
|
dimension: dimension,
|
|
width: imageA.getWidth(),
|
|
height: imageA.getWidth(),
|
|
highlightImage: highlightImage,
|
|
outputImage: outputImage,
|
|
exported: exported
|
|
};
|
|
}
|
|
},
|
|
|
|
{
|
|
/**
|
|
* Version
|
|
*
|
|
* @static
|
|
* @property version
|
|
* @type {string}
|
|
*/
|
|
version: require('./package.json').version
|
|
}
|
|
);
|
|
|
|
module.exports = BlinkDiff;
|