lib/compatibility.js
// Copyright 2015 Yahoo! Inc.
// Copyrights licensed under the Mit License. See the accompanying LICENSE file for terms.
var Base = require('preceptor-core').Base;
var constants = require('./constants');
var assert = require('assert');
/**
* @class Compatibility
* @extends Base
* @module Compare
*
* @property {object} _options
*/
var Compatibility = Base.extend(
/**
* Constructor for the compatibility object
*
* @constructor
* @param {object} options
* @param {PNGImage|Buffer} options.imageA Image object of first image
* @param {string} options.imageAPath Path to first image
* @param {PNGImage|Buffer} options.imageB Image object of second image
* @param {string} options.imageBPath Path to second image
* @param {string} [options.imageOutputPath=undefined] Path to output image file
* @param {int} [options.imageOutputLimit=constants.OUTPUT_ALL] Determines when an image output is created
* @param {string} [options.thresholdType=constants.THRESHOLD_PIXEL] Defines the threshold of the comparison
* @param {int} [options.threshold=500] Threshold limit according to the comparison limit.
* @param {number} [options.delta=20] Distance between the color coordinates in the 4 dimensional color-space that will not trigger a difference.
* @param {int} [options.outputMaskRed=255] Value to set for red on difference pixel. 'Undefined' will not change the value.
* @param {int} [options.outputMaskGreen=0] Value to set for green on difference pixel. 'Undefined' will not change the value.
* @param {int} [options.outputMaskBlue=0] Value to set for blue on difference pixel. 'Undefined' will not change the value.
* @param {int} [options.outputMaskAlpha=255] Value to set for the alpha channel on difference pixel. 'Undefined' will not change the value.
* @param {float} [options.outputMaskOpacity=0.7] Strength of masking the pixel. 1.0 means that the full color will be used; anything less will mix-in the original pixel.
* @param {int} [options.outputShiftRed=255] Value to set for red on shifted pixel. 'Undefined' will not change the value.
* @param {int} [options.outputShiftGreen=165] Value to set for green on shifted pixel. 'Undefined' will not change the value.
* @param {int} [options.outputShiftBlue=0] Value to set for blue on shifted pixel. 'Undefined' will not change the value.
* @param {int} [options.outputShiftAlpha=255] Value to set for the alpha channel on shifted pixel. 'Undefined' will not change the value.
* @param {float} [options.outputShiftOpacity=0.7] Strength of masking the shifted pixel. 1.0 means that the full color will be used; anything less will mix-in the original pixel.
* @param {int} [options.outputBackgroundRed=0] Value to set for red as background. 'Undefined' will not change the value.
* @param {int} [options.outputBackgroundGreen=0] Value to set for green as background. 'Undefined' will not change the value.
* @param {int} [options.outputBackgroundBlue=0] Value to set for blue as background. 'Undefined' will not change the value.
* @param {int} [options.outputBackgroundAlpha=undefined] Value to set for the alpha channel as background. 'Undefined' will not change the value.
* @param {float} [options.outputBackgroundOpacity=0.6] Strength of masking the pixel. 1.0 means that the full color will be used; anything less will mix-in the original pixel.
* @param {object|object[]} [options.blockOut] Object or list of objects with coordinates of blocked-out areas.
* @param {int} [options.blockOutRed=0] Value to set for red on blocked-out pixel. 'Undefined' will not change the value.
* @param {int} [options.blockOutGreen=0] Value to set for green on blocked-out pixel. 'Undefined' will not change the value.
* @param {int} [options.blockOutBlue=0] Value to set for blue on blocked-out pixel. 'Undefined' will not change the value.
* @param {int} [options.blockOutAlpha=255] Value to set for the alpha channel on blocked-out pixel. 'Undefined' will not change the value.
* @param {float} [options.blockOutOpacity=1.0] Strength of masking the blocked-out pixel. 1.0 means that the full color will be used; anything less will mix-in the original pixel.
* @param {boolean} [options.copyImageAToOutput=true] Copies the first image to the output image before the comparison begins. This will make sure that the output image will highlight the differences on the first image.
* @param {boolean} [options.copyImageBToOutput=false] Copies the second image to the output image before the comparison begins. This will make sure that the output image will highlight the differences on the second image.
* @param {string[]} [options.filter=[]] Filters that will be applied before the comparison. Available filters are: blur, grayScale, lightness, luma, luminosity, sepia
* @param {boolean} [options.debug=false] When set, then the applied filters will be shown on the output image.
* @param {boolean} [options.composition=true] Should a composition be created to compare?
* @param {boolean} [options.composeLeftToRight=false] Create composition from left to right, otherwise let it decide on its own whats best
* @param {boolean} [options.composeTopToBottom=false] Create composition from top to bottom, otherwise let it decide on its own whats best
* @param {boolean} [options.hideShift=false] Hides shift highlighting by using the background color instead
* @param {int} [options.hShift=2] Horizontal shift for possible antialiasing
* @param {int} [options.vShift=2] Vertical shift for possible antialiasing
* @param {object} [options.cropImageA=null] Cropping for first image (default: no cropping)
* @param {int} [options.cropImageA.x=0] Coordinate for left corner of cropping region
* @param {int} [options.cropImageA.y=0] Coordinate for top corner of cropping region
* @param {int} [options.cropImageA.width] Width of cropping region (default: Width that is left)
* @param {int} [options.cropImageA.height] Height of cropping region (default: Height that is left)
* @param {object} [options.cropImageB=null] Cropping for second image (default: no cropping)
* @param {int} [options.cropImageB.x=0] Coordinate for left corner of cropping region
* @param {int} [options.cropImageB.y=0] Coordinate for top corner of cropping region
* @param {int} [options.cropImageB.width] Width of cropping region (default: Width that is left)
* @param {int} [options.cropImageB.height] Height of cropping region (default: Height that is left)
* @param {boolean} [options.perceptual=false] Turns perceptual comparison on
* @param {float} [options.gamma] Gamma correction for all colors
* @param {float} [options.gammaR] Gamma correction for red
* @param {float} [options.gammaG] Gamma correction for green
* @param {float} [options.gammaB] Gamma correction for blue
*/
function (options) {
this._options = options;
},
{
/**
* Generates a configuration object
*
* @method generate
* @return {object}
*/
generate: function () {
var options = this._options,
blockOuts,
comparison,
config;
assert.ok(options.imageAPath || options.imageA, "Image A not given.");
assert.ok(options.imageBPath || options.imageB, "Image B not given.");
options.blockOut = options.blockOut || [];
if (typeof options.blockOut != 'object' && (options.blockOut.length !== undefined)) {
options.blockOut = [options.blockOut];
}
blockOuts = [];
options.blockOut.forEach(function (blockOut) {
blockOuts.push({
visible: true,
area: {
left: blockOut.x,
top: blockout.y,
width: blockout.width,
height: blockout.height
},
color: {
red: options.blockOutRed || 0,
green: options.blockOutGreen || 0,
blue: options.blockOutBlue || 0,
alpha: options.blockOutAlpha || 255,
opacity: options.blockOutOpacity || 1.0
}
});
});
comparison = {
type: 'pixel',
colorDelta: options.delta || 20,
gamma: {
red: options.gamma || options.gammaR,
green: options.gamma || options.gammaG,
blue: options.gamma || options.gammaB
},
perceptual: !!options.perceptual,
filters: options.filter || [],
shift: {
active: !options.hideShift,
horizontal: options.hShift || 2,
vertical: options.vShift || 2
},
blockOuts: blockOuts,
areaImageA: {
left: 0,
top: 0,
width: null,
height: null
},
areaImageB: {
left: 0,
top: 0,
width: null,
height: null
}
};
config = {
debug: !!options.debug,
verbose: !!options.debug,
imageA: {
image: options.imageA || options.imageAPath,
crop: undefined
},
imageB: {
image: options.imageB || options.imageBPath,
crop: undefined
},
comparisons: [ comparison ],
threshold: {
type: options.thresholdType || constants.THRESHOLD_PIXEL,
value: options.threshold || 500
},
diffColor: {
red: options.outputMaskRed || 255,
green: options.outputMaskGreen || 0,
blue: options.outputMaskBlue || 0,
alpha: options.outputMaskAlpha || 255,
opacity: options.outputMaskOpacity || 0.7
},
backgroundColor: {
red: options.outputBackgroundRed || 0,
green: options.outputBackgroundGreen || 0,
blue: options.outputBackgroundBlue || 0,
alpha: options.outputBackgroundAlpha,
opacity: options.outputBackgroundOpacity || 0.6
},
ignoreColor: {
red: options.outputShiftRed || 200,
green: options.outputShiftGreen || 100,
blue: options.outputShiftBlue || 0,
alpha: options.outputShiftAlpha || 255,
opacity: options.outputShiftOpacity || 0.7
},
output: {
imagePath: options.imageOutputPath,
limit: options.imageOutputLimit || constants.OUTPUT_ALL,
composition: options.composeLeftToRight ? constants.COMPOSITION_LEFT_TO_RIGHT : (options.composeTopToBottom ? constants.COMPOSITION_TOP_TO_BOTTOM : constants.COMPOSITION_AUTO),
copyImage: options.copyImageBToOutput ? constants.COPY_IMAGE_B : constants.COPY_IMAGE_A
}
};
if (options.cropImageA) {
config.imageA.crop = {
left: options.cropImageA.x,
top: options.cropImageA.y,
width: options.cropImageA.width,
height: options.cropImageA.height
};
}
if (options.cropImageB) {
config.imageB.crop = {
left: options.cropImageB.x,
top: options.cropImageB.y,
width: options.cropImageB.width,
height: options.cropImageB.height
};
}
return config;
}
}
);
module.exports = Compatibility;