@ -4,6 +4,7 @@ URL: https://github.com/Huddle/Resemble.js
* /
* /
( function ( root , factory ) {
( function ( root , factory ) {
'use strict' ;
if ( typeof define === 'function' && define . amd ) {
if ( typeof define === 'function' && define . amd ) {
define ( [ ] , factory ) ;
define ( [ ] , factory ) ;
} else if ( typeof module === 'object' && module . exports ) {
} else if ( typeof module === 'object' && module . exports ) {
@ -14,82 +15,103 @@ URL: https://github.com/Huddle/Resemble.js
} ( this , function ( ) {
} ( this , function ( ) {
'use strict' ;
'use strict' ;
var pixelTransparency = 1 ;
var document = typeof window != "undefined" ? window . document : {
createElement : function ( ) {
var errorPixelColor = { // Color for Error Pixels. Between 0 and 255.
// This will work as long as only createElement is used on window.document
red : 255 ,
var Canvas = require ( 'canvas-prebuilt' ) ;
green : 0 ,
return new Canvas ( ) ;
blue : 255 ,
}
alpha : 255
} ;
} ;
var targetPix = { r : 0 , g : 0 , b : 0 , a : 0 } ; // isAntialiased
var oldGlobalSettings = { } ;
var globalOutputSettings = oldGlobalSettings ;
function colorsDistance ( c1 , c2 ) {
function setGlobalOutputSettings ( settings ) {
return ( Math . abs ( c1 . r - c2 . r ) + Math . abs ( c1 . g - c2 . g ) + Math . abs ( c1 . b - c2 . b ) ) / 3 ;
var msg = 'warning resemble.outputSettings mutates global state, and ' +
'will be removed in 3.0.0' ;
console . warn ( msg ) ;
globalOutputSettings = settings ;
return this
}
}
function withinBoundingBox ( x , y , width , height ) {
var resemble = function ( fileData ) {
if ( ! boundingBox ) {
var pixelTransparency = 1 ;
return true ;
}
return x > ( boundingBox . left || 0 ) &&
var errorPixelColor = { // Color for Error Pixels. Between 0 and 255.
x < ( boundingBox . right || width ) &&
red : 255 ,
y > ( boundingBox . top || 0 ) &&
green : 0 ,
y < ( boundingBox . bottom || height ) ;
blue : 255 ,
}
alpha : 255
} ;
var errorPixelTransform = {
var targetPix = { r : 0 , g : 0 , b : 0 , a : 0 } ; // isAntialiased
flat : function ( px , offset , d1 , d2 ) {
px [ offset ] = errorPixelColor . red ;
function colorsDistance ( c1 , c2 ) {
px [ offset + 1 ] = errorPixelColor . green ;
return ( Math . abs ( c1 . r - c2 . r ) + Math . abs ( c1 . g - c2 . g ) + Math . abs ( c1 . b - c2 . b ) ) / 3 ;
px [ offset + 2 ] = errorPixelColor . blue ;
px [ offset + 3 ] = errorPixelColor . alpha ;
} ,
movement : function ( px , offset , d1 , d2 ) {
px [ offset ] = ( ( d2 . r * ( errorPixelColor . red / 255 ) ) + errorPixelColor . red ) / 2 ;
px [ offset + 1 ] = ( ( d2 . g * ( errorPixelColor . green / 255 ) ) + errorPixelColor . green ) / 2 ;
px [ offset + 2 ] = ( ( d2 . b * ( errorPixelColor . blue / 255 ) ) + errorPixelColor . blue ) / 2 ;
px [ offset + 3 ] = d2 . a ;
} ,
flatDifferenceIntensity : function ( px , offset , d1 , d2 ) {
px [ offset ] = errorPixelColor . red ;
px [ offset + 1 ] = errorPixelColor . green ;
px [ offset + 2 ] = errorPixelColor . blue ;
px [ offset + 3 ] = colorsDistance ( d1 , d2 ) ;
} ,
movementDifferenceIntensity : function ( px , offset , d1 , d2 ) {
var ratio = colorsDistance ( d1 , d2 ) / 255 * 0.8 ;
px [ offset ] = ( ( 1 - ratio ) * ( d2 . r * ( errorPixelColor . red / 255 ) ) + ratio * errorPixelColor . red ) ;
px [ offset + 1 ] = ( ( 1 - ratio ) * ( d2 . g * ( errorPixelColor . green / 255 ) ) + ratio * errorPixelColor . green ) ;
px [ offset + 2 ] = ( ( 1 - ratio ) * ( d2 . b * ( errorPixelColor . blue / 255 ) ) + ratio * errorPixelColor . blue ) ;
px [ offset + 3 ] = d2 . a ;
} ,
diffOnly : function ( px , offset , d1 , d2 ) {
px [ offset ] = d2 . r ;
px [ offset + 1 ] = d2 . g ;
px [ offset + 2 ] = d2 . b ;
px [ offset + 3 ] = d2 . a ;
}
}
} ;
var errorPixel = errorPixelTransform . flat ;
function withinBoundingBox ( x , y , width , height , box ) {
var errorType ;
return x > ( box . left || 0 ) &&
var boundingBox ;
x < ( box . right || width ) &&
var largeImageThreshold = 1200 ;
y > ( box . top || 0 ) &&
var useCrossOrigin = true ;
y < ( box . bottom || height ) ;
var document = typeof window != "undefined" ? window . document : {
createElement : function ( ) {
// This will work as long as only createElement is used on window.document
var Canvas = require ( 'canvas' ) ;
return new Canvas ( ) ;
}
}
} ;
var resemble = function ( fileData ) {
function withinComparedArea ( x , y , width , height ) {
var isIncluded = true ;
if ( boundingBox !== undefined && ! withinBoundingBox ( x , y , width , height , boundingBox ) ) {
isIncluded = false ;
}
if ( ignoredBox !== undefined && withinBoundingBox ( x , y , width , height , ignoredBox ) ) {
isIncluded = false ;
}
return isIncluded ;
}
var errorPixelTransform = {
flat : function ( px , offset ) {
px [ offset ] = errorPixelColor . red ;
px [ offset + 1 ] = errorPixelColor . green ;
px [ offset + 2 ] = errorPixelColor . blue ;
px [ offset + 3 ] = errorPixelColor . alpha ;
} ,
movement : function ( px , offset , d1 , d2 ) {
px [ offset ] = ( ( d2 . r * ( errorPixelColor . red / 255 ) ) + errorPixelColor . red ) / 2 ;
px [ offset + 1 ] = ( ( d2 . g * ( errorPixelColor . green / 255 ) ) + errorPixelColor . green ) / 2 ;
px [ offset + 2 ] = ( ( d2 . b * ( errorPixelColor . blue / 255 ) ) + errorPixelColor . blue ) / 2 ;
px [ offset + 3 ] = d2 . a ;
} ,
flatDifferenceIntensity : function ( px , offset , d1 , d2 ) {
px [ offset ] = errorPixelColor . red ;
px [ offset + 1 ] = errorPixelColor . green ;
px [ offset + 2 ] = errorPixelColor . blue ;
px [ offset + 3 ] = colorsDistance ( d1 , d2 ) ;
} ,
movementDifferenceIntensity : function ( px , offset , d1 , d2 ) {
var ratio = colorsDistance ( d1 , d2 ) / 255 * 0.8 ;
px [ offset ] = ( ( 1 - ratio ) * ( d2 . r * ( errorPixelColor . red / 255 ) ) + ratio * errorPixelColor . red ) ;
px [ offset + 1 ] = ( ( 1 - ratio ) * ( d2 . g * ( errorPixelColor . green / 255 ) ) + ratio * errorPixelColor . green ) ;
px [ offset + 2 ] = ( ( 1 - ratio ) * ( d2 . b * ( errorPixelColor . blue / 255 ) ) + ratio * errorPixelColor . blue ) ;
px [ offset + 3 ] = d2 . a ;
} ,
diffOnly : function ( px , offset , d1 , d2 ) {
px [ offset ] = d2 . r ;
px [ offset + 1 ] = d2 . g ;
px [ offset + 2 ] = d2 . b ;
px [ offset + 3 ] = d2 . a ;
}
} ;
var errorPixel = errorPixelTransform . flat ;
var errorType ;
var boundingBox ;
var ignoredBox ;
var largeImageThreshold = 1200 ;
var useCrossOrigin = true ;
var data = { } ;
var data = { } ;
var images = [ ] ;
var images = [ ] ;
var updateCallbackArray = [ ] ;
var updateCallbackArray = [ ] ;
@ -180,19 +202,18 @@ URL: https://github.com/Huddle/Resemble.js
if ( typeof Image !== 'undefined' ) {
if ( typeof Image !== 'undefined' ) {
hiddenImage = new Image ( ) ;
hiddenImage = new Image ( ) ;
} else {
} else {
var CanvasImage = require ( 'canvas' ) . Image ;
var CanvasImage = require ( 'canvas-prebuilt ' ) . Image ;
hiddenImage = new CanvasImage ( ) ;
hiddenImage = new CanvasImage ( ) ;
hiddenImage . setAttribute = function setAttribute ( ) { } ;
hiddenImage . setAttribute = function setAttribute ( ) { } ;
}
}
if ( useCrossOrigin ) {
if ( useCrossOrigin ) {
hiddenImage . setAttribute ( 'crossorigin' , 'anonymous' ) ;
hiddenImage . setAttribute ( 'crossorigin' , 'anonymous' ) ;
}
}
hiddenImage . onerror = function ( ) {
hiddenImage . onerror = function ( err ) {
hiddenImage . onerror = null ; //fixes pollution between calls
hiddenImage . onerror = null ; //fixes pollution between calls
images . push ( { error : "Image load error."} ) ;
images . push ( { error : err ? err + "" : "Image load error." } ) ;
callback ( ) ;
callback ( ) ;
} ;
} ;
@ -395,7 +416,7 @@ URL: https://github.com/Huddle/Resemble.js
px [ offset + 3 ] = data . a * pixelTransparency ; //a
px [ offset + 3 ] = data . a * pixelTransparency ; //a
}
}
function getPixelInfo ( dst , data , offset , cacheSet ) {
function getPixelInfo ( dst , data , offset ) {
if ( data . length > offset ) {
if ( data . length > offset ) {
dst . r = data [ offset ] ;
dst . r = data [ offset ] ;
dst . g = data [ offset + 1 ] ;
dst . g = data [ offset + 1 ] ;
@ -464,7 +485,7 @@ URL: https://github.com/Huddle/Resemble.js
}
}
var offset = ( verticalPos * width + horizontalPos ) * 4 ;
var offset = ( verticalPos * width + horizontalPos ) * 4 ;
var isWithinBoundingBox = withinBoundingBox ( horizontalPos , verticalPos , width , height ) ;
var isWithinComparedArea = withinComparedArea ( horizontalPos , verticalPos , width , height ) ;
if ( ! getPixelInfo ( pixel1 , data1 , offset , 1 ) || ! getPixelInfo ( pixel2 , data2 , offset , 2 ) ) {
if ( ! getPixelInfo ( pixel1 , data1 , offset , 1 ) || ! getPixelInfo ( pixel2 , data2 , offset , 2 ) ) {
return ;
return ;
@ -475,7 +496,7 @@ URL: https://github.com/Huddle/Resemble.js
addBrightnessInfo ( pixel1 ) ;
addBrightnessInfo ( pixel1 ) ;
addBrightnessInfo ( pixel2 ) ;
addBrightnessInfo ( pixel2 ) ;
if ( isPixelBrightnessSimilar ( pixel1 , pixel2 ) || ! isWithinBoundingBox ) {
if ( isPixelBrightnessSimilar ( pixel1 , pixel2 ) || ! isWithinComparedArea ) {
copyGrayScalePixel ( targetPix , offset , pixel2 ) ;
copyGrayScalePixel ( targetPix , offset , pixel2 ) ;
} else {
} else {
errorPixel ( targetPix , offset , pixel1 , pixel2 ) ;
errorPixel ( targetPix , offset , pixel1 , pixel2 ) ;
@ -485,8 +506,8 @@ URL: https://github.com/Huddle/Resemble.js
return ;
return ;
}
}
if ( isRGBSimilar ( pixel1 , pixel2 ) || ! isWithinBoundingBox ) {
if ( isRGBSimilar ( pixel1 , pixel2 ) || ! isWithinComparedArea ) {
copyPixel ( targetPix , offset , pixel1 , pixel2 ) ;
copyPixel ( targetPix , offset , pixel1 ) ;
} else if ( ignoreAntialiasing && (
} else if ( ignoreAntialiasing && (
addBrightnessInfo ( pixel1 ) , // jit pixel info augmentation looks a little weird, sorry.
addBrightnessInfo ( pixel1 ) , // jit pixel info augmentation looks a little weird, sorry.
@ -495,7 +516,7 @@ URL: https://github.com/Huddle/Resemble.js
isAntialiased ( pixel2 , data2 , 2 , verticalPos , horizontalPos , width )
isAntialiased ( pixel2 , data2 , 2 , verticalPos , horizontalPos , width )
) ) {
) ) {
if ( isPixelBrightnessSimilar ( pixel1 , pixel2 ) || ! isWithinBoundingBox ) {
if ( isPixelBrightnessSimilar ( pixel1 , pixel2 ) || ! isWithinComparedArea ) {
copyGrayScalePixel ( targetPix , offset , pixel2 ) ;
copyGrayScalePixel ( targetPix , offset , pixel2 ) ;
} else {
} else {
errorPixel ( targetPix , offset , pixel1 , pixel2 ) ;
errorPixel ( targetPix , offset , pixel1 , pixel2 ) ;
@ -528,8 +549,16 @@ URL: https://github.com/Huddle/Resemble.js
} ;
} ;
if ( hiddenCanvas . toBuffer ) {
if ( hiddenCanvas . toBuffer ) {
data . getBuffer = function ( ) {
data . getBuffer = function ( includeOriginal ) {
context . putImageData ( imgd , 0 , 0 ) ;
if ( includeOriginal ) {
var imageWidth = hiddenCanvas . width + 2 ;
hiddenCanvas . width = imageWidth * 3 ;
context . putImageData ( img1 , 0 , 0 ) ;
context . putImageData ( img2 , imageWidth , 0 ) ;
context . putImageData ( imgd , imageWidth * 2 , 0 ) ;
} else {
context . putImageData ( imgd , 0 , 0 ) ;
}
return hiddenCanvas . toBuffer ( ) ;
return hiddenCanvas . toBuffer ( ) ;
}
}
}
}
@ -578,7 +607,49 @@ URL: https://github.com/Huddle/Resemble.js
return img ;
return img ;
}
}
function outputSettings ( options ) {
var key ;
var undefined ;
if ( options . errorColor ) {
for ( key in options . errorColor ) {
errorPixelColor [ key ] = options . errorColor [ key ] === undefined ? errorPixelColor [ key ] : options . errorColor [ key ] ;
}
}
if ( options . errorType && errorPixelTransform [ options . errorType ] ) {
errorPixel = errorPixelTransform [ options . errorType ] ;
errorType = options . errorType ;
}
if ( options . errorPixel && typeof options . errorPixel === "function" ) {
errorPixel = options . errorPixel ;
}
pixelTransparency = isNaN ( Number ( options . transparency ) ) ? pixelTransparency : options . transparency ;
if ( options . largeImageThreshold !== undefined ) {
largeImageThreshold = options . largeImageThreshold ;
}
if ( options . useCrossOrigin !== undefined ) {
useCrossOrigin = options . useCrossOrigin ;
}
if ( options . boundingBox !== undefined ) {
boundingBox = options . boundingBox ;
}
if ( options . ignoredBox !== undefined ) {
ignoredBox = options . ignoredBox ;
}
}
function compare ( one , two ) {
function compare ( one , two ) {
if ( globalOutputSettings !== oldGlobalSettings ) {
outputSettings ( globalOutputSettings ) ;
}
function onceWeHaveBoth ( ) {
function onceWeHaveBoth ( ) {
var width ;
var width ;
@ -711,6 +782,10 @@ URL: https://github.com/Huddle/Resemble.js
if ( hasMethod ) { param ( ) ; }
if ( hasMethod ) { param ( ) ; }
return self ;
return self ;
} ,
} ,
outputSettings : function ( options ) {
outputSettings ( options ) ;
return self ;
} ,
onComplete : function ( callback ) {
onComplete : function ( callback ) {
updateCallbackArray . push ( callback ) ;
updateCallbackArray . push ( callback ) ;
@ -728,7 +803,7 @@ URL: https://github.com/Huddle/Resemble.js
return self ;
return self ;
}
}
return {
var rootSelf = {
onComplete : function ( callback ) {
onComplete : function ( callback ) {
updateCallbackArray . push ( callback ) ;
updateCallbackArray . push ( callback ) ;
loadImageData ( fileData , function ( imageData , width , height ) {
loadImageData ( fileData , function ( imageData , width , height ) {
@ -737,46 +812,73 @@ URL: https://github.com/Huddle/Resemble.js
} ,
} ,
compareTo : function ( secondFileData ) {
compareTo : function ( secondFileData ) {
return getCompareApi ( secondFileData ) ;
return getCompareApi ( secondFileData ) ;
} ,
outputSettings : function ( options ) {
outputSettings ( options ) ;
return rootSelf ;
}
}
} ;
} ;
return rootSelf ;
} ;
} ;
resemble . outputSettings = function ( options ) {
function applyIgnore ( api , ignore ) {
var key ;
switch ( ignore ) {
var undefined ;
case 'nothing' :
api . ignoreNothing ( ) ;
if ( options . errorColor ) {
break ;
for ( key in options . errorColor ) {
case 'less' :
errorPixelColor [ key ] = options . errorColor [ key ] === undefined ? errorPixelColor [ key ] : options . errorColor [ key ] ;
api . ignoreLess ( ) ;
}
break ;
}
case 'antialiasing' :
api . ignoreAntialiasing ( ) ;
if ( options . errorType && errorPixelTransform [ options . errorType ] ) {
break ;
errorPixel = errorPixelTransform [ options . errorType ] ;
case 'colors' :
errorType = options . errorType ;
api . ignoreColors ( ) ;
break ;
case 'alpha' :
api . ignoreAlpha ( ) ;
break ;
default :
throw new Error ( 'Invalid ignore: ' + ignore ) ;
break ;
}
}
}
if ( options . errorPixel && typeof options . errorPixel === "function" ) {
resemble . compare = function ( image1 , image2 , options , callback ) {
errorPixel = options . errorPixel ;
if ( typeof options === 'function' ) {
callback = options ;
options = undefined ;
}
}
pixelTransparency = isNaN ( Number ( options . transparency ) ) ? pixelTransparency : options . transparency ;
var res = resemble ( image1 ) , opt = options || { } , compare ;
if ( options . largeImageThreshold !== undefined ) {
if ( opt . output ) {
largeImageThreshold = options . largeImageThreshold ;
res . outputSettings ( opt . output ) ;
}
}
if ( options . useCrossOrigin !== undefined ) {
compare = res . compareTo ( image2 ) ;
useCrossOrigin = options . useCrossOrigin ;
if ( opt . scaleToSameSize ) {
compare . scaleToSameSize ( ) ;
}
}
if ( options . boundingBox !== undefined ) {
if ( typeof opt . ignore === 'string' ) {
boundingBox = options . boundingBox ;
applyIgnore ( compare , opt . ignore ) ;
} else if ( opt . ignore && opt . ignore . forEach ) {
opt . ignore . forEach ( function ( v ) {
applyIgnore ( compare , v ) ;
} ) ;
}
}
return this ;
compare . onComplete ( function ( data ) {
if ( data . error ) {
callback ( data . error ) ;
} else {
callback ( null , data ) ;
}
} ) ;
} ;
} ;
resemble . outputSettings = setGlobalOutputSettings ;
return resemble ;
return resemble ;
} ) ) ;
} ) ) ;