diff options
author | Thomas Steur <thomas.steur@googlemail.com> | 2014-05-28 06:49:10 +0400 |
---|---|---|
committer | Thomas Steur <thomas.steur@googlemail.com> | 2014-05-28 06:49:10 +0400 |
commit | ab31aea98e870f7675227c7c9d87e0b08d825613 (patch) | |
tree | fe1cc36b0ac158830875fbeb8697f143d76ef0a7 /tests | |
parent | 7b62ecf324df7c01d1fc1cb9b0dc34832c3c3a12 (diff) |
added missing resemblejs lib
Diffstat (limited to 'tests')
-rw-r--r-- | tests/lib/resemblejs/LICENSE | 18 | ||||
-rw-r--r-- | tests/lib/resemblejs/README.md | 73 | ||||
-rw-r--r-- | tests/lib/resemblejs/resemble.js | 600 |
3 files changed, 691 insertions, 0 deletions
diff --git a/tests/lib/resemblejs/LICENSE b/tests/lib/resemblejs/LICENSE new file mode 100644 index 0000000000..df12acc478 --- /dev/null +++ b/tests/lib/resemblejs/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) Copyright © 2013 Huddle + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the “Software”), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file diff --git a/tests/lib/resemblejs/README.md b/tests/lib/resemblejs/README.md new file mode 100644 index 0000000000..62e1041104 --- /dev/null +++ b/tests/lib/resemblejs/README.md @@ -0,0 +1,73 @@ +Resemble.js +========== + +Analyse and compare images with Javascript and HTML5. [Resemble.js Demo](http://huddle.github.com/Resemble.js/) + +![Two image diff examples side-by-side, one pink, one yellow.](https://raw.github.com/Huddle/Resemble.js/master/demoassets/readmeimage.jpg "Visual image comparison") + +### Get it + +`npm install resemblejs` + +`bower install resemblejs` + +### Example + +Retrieve basic analysis on image. + +```javascript +var api = resemble(fileData).onComplete(function(data){ + console.log(data); + /* + { + red: 255, + green: 255, + blue: 255, + brightness: 255 + } + */ +}); +``` + +Use resemble to compare two images. + +```javascript +var diff = resemble(file).compareTo(file2).ignoreColors().onComplete(function(data){ + console.log(data); + /* + { + misMatchPercentage : 100, // % + isSameDimensions: true, // or false + dimensionDifference: { width: 0, height: -1 }, // defined if dimensions are not the same + getImageDataUrl: function(){} + } + */ +}); +``` + +You can also change the comparison method after the first analysis. + +```javascript +// diff.ignoreNothing(); +// diff.ignoreColors(); +diff.ignoreAntialiasing(); +``` + +And change the output display style. + +```javascript +resemble.outputSettings({ + errorColor: { + red: 255, + green: 0, + blue: 255 + }, + errorType: 'movement', + transparency: 0.3 +}); +// resembleControl.repaint(); +``` + +-------------------------------------- + +Created by [James Cryer](http://github.com/jamescryer) and the Huddle development team.
\ No newline at end of file diff --git a/tests/lib/resemblejs/resemble.js b/tests/lib/resemblejs/resemble.js new file mode 100644 index 0000000000..dbb927a7cd --- /dev/null +++ b/tests/lib/resemblejs/resemble.js @@ -0,0 +1,600 @@ +/* + James Cryer / Huddle 2014 + URL: https://github.com/Huddle/Resemble.js + */ + +(function(_this){ + 'use strict'; + + var pixelTransparency = 1; + + var errorPixelColor = { // Color for Error Pixels. Between 0 and 255. + red: 255, + green: 0, + blue: 255, + alpha: 255 + }; + + var errorPixelTransform = { + flat : function (d1, d2){ + return { + r: errorPixelColor.red, + g: errorPixelColor.green, + b: errorPixelColor.blue, + a: errorPixelColor.alpha + } + }, + movement: function (d1, d2){ + return { + r: ((d2.r*(errorPixelColor.red/255)) + errorPixelColor.red)/2, + g: ((d2.g*(errorPixelColor.green/255)) + errorPixelColor.green)/2, + b: ((d2.b*(errorPixelColor.blue/255)) + errorPixelColor.blue)/2, + a: d2.a + } + } + }; + + var errorPixelTransformer = errorPixelTransform.flat; + + _this['resemble'] = function( fileData ){ + + var data = {}; + var images = []; + var updateCallbackArray = []; + + var tolerance = { // between 0 and 255 + red: 16, + green: 16, + blue: 16, + alpha: 16, + minBrightness: 16, + maxBrightness: 240 + }; + + var ignoreAntialiasing = false; + var ignoreColors = false; + + function triggerDataUpdate(){ + var len = updateCallbackArray.length; + var i; + for(i=0;i<len;i++){ + if (typeof updateCallbackArray[i] === 'function'){ + updateCallbackArray[i](data); + } + } + } + + function loop(x, y, callback){ + var i,j; + + for (i=0;i<x;i++){ + for (j=0;j<y;j++){ + callback(i, j); + } + } + } + + function parseImage(sourceImageData, width, height){ + + var pixleCount = 0; + var redTotal = 0; + var greenTotal = 0; + var blueTotal = 0; + var brightnessTotal = 0; + + loop(height, width, function(verticalPos, horizontalPos){ + var offset = (verticalPos*width + horizontalPos) * 4; + var red = sourceImageData[offset]; + var green = sourceImageData[offset + 1]; + var blue = sourceImageData[offset + 2]; + var brightness = getBrightness(red,green,blue); + + pixleCount++; + + redTotal += red / 255 * 100; + greenTotal += green / 255 * 100; + blueTotal += blue / 255 * 100; + brightnessTotal += brightness / 255 * 100; + }); + + data.red = Math.floor(redTotal / pixleCount); + data.green = Math.floor(greenTotal / pixleCount); + data.blue = Math.floor(blueTotal / pixleCount); + data.brightness = Math.floor(brightnessTotal / pixleCount); + + triggerDataUpdate(); + } + + function loadImageData( fileData, callback ){ + var fileReader; + var hiddenImage = new Image(); + + hiddenImage.onload = function() { + + var hiddenCanvas = document.createElement('canvas'); + var imageData; + var width = hiddenImage.width; + var height = hiddenImage.height; + + hiddenCanvas.width = width; + hiddenCanvas.height = height; + hiddenCanvas.getContext('2d').drawImage(hiddenImage, 0, 0, width, height); + imageData = hiddenCanvas.getContext('2d').getImageData(0, 0, width, height); + + images.push(imageData); + + callback(imageData, width, height); + }; + + if (typeof fileData === 'string') { + hiddenImage.src = fileData; + } else { + fileReader = new FileReader(); + fileReader.onload = function (event) { + hiddenImage.src = event.target.result; + }; + fileReader.readAsDataURL(fileData); + } + } + + function isColorSimilar(a, b, color){ + + var absDiff = Math.abs(a - b); + + if(typeof a === 'undefined'){ + return false; + } + if(typeof b === 'undefined'){ + return false; + } + + if(a === b){ + return true; + } else if ( absDiff < tolerance[color] ) { + return true; + } else { + return false; + } + } + + function isNumber(n) { + return !isNaN(parseFloat(n)); + } + + function isPixelBrightnessSimilar(d1, d2){ + var alpha = isColorSimilar(d1.a, d2.a, 'alpha'); + var brightness = isColorSimilar(d1.brightness, d2.brightness, 'minBrightness'); + return brightness && alpha; + } + + function getBrightness(r,g,b){ + return 0.3*r + 0.59*g + 0.11*b; + } + + function isRGBSame(d1,d2){ + var red = d1.r === d2.r; + var green = d1.g === d2.g; + var blue = d1.b === d2.b; + return red && green && blue; + } + + function isRGBSimilar(d1, d2){ + var red = isColorSimilar(d1.r,d2.r,'red'); + var green = isColorSimilar(d1.g,d2.g,'green'); + var blue = isColorSimilar(d1.b,d2.b,'blue'); + var alpha = isColorSimilar(d1.a, d2.a, 'alpha'); + + return red && green && blue && alpha; + } + + function isContrasting(d1, d2){ + return Math.abs(d1.brightness - d2.brightness) > tolerance.maxBrightness; + } + + function getHue(r,g,b){ + + r = r / 255; + g = g / 255; + b = b / 255; + var max = Math.max(r, g, b), min = Math.min(r, g, b); + var h; + var d; + + if (max == min){ + h = 0; // achromatic + } else{ + d = max - min; + switch(max){ + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + h /= 6; + } + + return h; + } + + function isAntialiased(sourcePix, data, cacheSet, verticalPos, horizontalPos, width){ + var offset; + var targetPix; + var distance = 1; + var i; + var j; + var hasHighContrastSibling = 0; + var hasSiblingWithDifferentHue = 0; + var hasEquivilantSibling = 0; + + addHueInfo(sourcePix); + + for (i = distance*-1; i <= distance; i++){ + for (j = distance*-1; j <= distance; j++){ + + if(i===0 && j===0){ + // ignore source pixel + } else { + + offset = ((verticalPos+j)*width + (horizontalPos+i)) * 4; + targetPix = getPixelInfo(data, offset, cacheSet); + + if(targetPix === null){ + continue; + } + + addBrightnessInfo(targetPix); + addHueInfo(targetPix); + + if( isContrasting(sourcePix, targetPix) ){ + hasHighContrastSibling++; + } + + if( isRGBSame(sourcePix,targetPix) ){ + hasEquivilantSibling++; + } + + if( Math.abs(targetPix.h - sourcePix.h) > 0.3 ){ + hasSiblingWithDifferentHue++; + } + + if( hasSiblingWithDifferentHue > 1 || hasHighContrastSibling > 1){ + return true; + } + } + } + } + + if(hasEquivilantSibling < 2){ + return true; + } + + return false; + } + + function errorPixel(px, offset, data1, data2){ + var data = errorPixelTransformer(data1, data2); + px[offset] = data.r; + px[offset + 1] = data.g; + px[offset + 2] = data.b; + px[offset + 3] = data.a; + } + + function copyPixel(px, offset, data){ + px[offset] = data.r; //r + px[offset + 1] = data.g; //g + px[offset + 2] = data.b; //b + px[offset + 3] = data.a * pixelTransparency; //a + } + + function copyGrayScalePixel(px, offset, data){ + px[offset] = data.brightness; //r + px[offset + 1] = data.brightness; //g + px[offset + 2] = data.brightness; //b + px[offset + 3] = data.a * pixelTransparency; //a + } + + function getPixelInfo(data, offset, cacheSet){ + var r; + var g; + var b; + var d; + var a; + + r = data[offset]; + + if(typeof r !== 'undefined'){ + g = data[offset+1]; + b = data[offset+2]; + a = data[offset+3]; + d = { + r: r, + g: g, + b: b, + a: a + }; + + return d; + } else { + return null; + } + } + + function addBrightnessInfo(data){ + data.brightness = getBrightness(data.r,data.g,data.b); // 'corrected' lightness + } + + function addHueInfo(data){ + data.h = getHue(data.r,data.g,data.b); + } + + function analyseImages(img1, img2, width, height){ + + var hiddenCanvas = document.createElement('canvas'); + + var data1 = img1.data; + var data2 = img2.data; + + hiddenCanvas.width = width; + hiddenCanvas.height = height; + + var context = hiddenCanvas.getContext('2d'); + var imgd = context.createImageData(width,height); + var targetPix = imgd.data; + + var mismatchCount = 0; + + var time = Date.now(); + + var skip; + + if( (width > 1200 || height > 1200) && ignoreAntialiasing){ + skip = 6; + } + + loop(height, width, function(verticalPos, horizontalPos){ + + if(skip){ // only skip if the image isn't small + if(verticalPos % skip === 0 || horizontalPos % skip === 0){ + return; + } + } + + var offset = (verticalPos*width + horizontalPos) * 4; + var pixel1 = getPixelInfo(data1, offset, 1); + var pixel2 = getPixelInfo(data2, offset, 2); + + if(pixel1 === null || pixel2 === null){ + return; + } + + if (ignoreColors){ + + addBrightnessInfo(pixel1); + addBrightnessInfo(pixel2); + + if( isPixelBrightnessSimilar(pixel1, pixel2) ){ + copyGrayScalePixel(targetPix, offset, pixel2); + } else { + errorPixel(targetPix, offset, pixel1, pixel2); + mismatchCount++; + } + return; + } + + if( isRGBSimilar(pixel1, pixel2) ){ + copyPixel(targetPix, offset, pixel1, pixel2); + + } else if( ignoreAntialiasing && ( + addBrightnessInfo(pixel1), // jit pixel info augmentation looks a little weird, sorry. + addBrightnessInfo(pixel2), + isAntialiased(pixel1, data1, 1, verticalPos, horizontalPos, width) || + isAntialiased(pixel2, data2, 2, verticalPos, horizontalPos, width) + )){ + + if( isPixelBrightnessSimilar(pixel1, pixel2) ){ + copyGrayScalePixel(targetPix, offset, pixel2); + } else { + errorPixel(targetPix, offset, pixel1, pixel2); + mismatchCount++; + } + } else { + errorPixel(targetPix, offset, pixel1, pixel2); + mismatchCount++; + } + + }); + + data.misMatchPercentage = (mismatchCount / (height*width) * 100).toFixed(2); + data.analysisTime = Date.now() - time; + + data.getImageDataUrl = function(text){ + var barHeight = 0; + + if(text){ + barHeight = addLabel(text,context,hiddenCanvas); + } + + context.putImageData(imgd, 0, barHeight); + + return hiddenCanvas.toDataURL("image/png"); + }; + } + + function addLabel(text, context, hiddenCanvas){ + var textPadding = 2; + + context.font = '12px sans-serif'; + + var textWidth = context.measureText(text).width + textPadding*2; + var barHeight = 22; + + if(textWidth > hiddenCanvas.width){ + hiddenCanvas.width = textWidth; + } + + hiddenCanvas.height += barHeight; + + context.fillStyle = "#666"; + context.fillRect(0,0,hiddenCanvas.width,barHeight -4); + context.fillStyle = "#fff"; + context.fillRect(0,barHeight -4,hiddenCanvas.width, 4); + + context.fillStyle = "#fff"; + context.textBaseline = "top"; + context.font = '12px sans-serif'; + context.fillText(text, textPadding, 1); + + return barHeight; + } + + function normalise(img, w, h){ + var c; + var context; + + if(img.height < h || img.width < w){ + c = document.createElement('canvas'); + c.width = w; + c.height = h; + context = c.getContext('2d'); + context.putImageData(img, 0, 0); + return context.getImageData(0, 0, w, h); + } + + return img; + } + + function compare(one, two){ + + function onceWeHaveBoth(){ + var width; + var height; + if(images.length === 2){ + width = images[0].width > images[1].width ? images[0].width : images[1].width; + height = images[0].height > images[1].height ? images[0].height : images[1].height; + + if( (images[0].width === images[1].width) && (images[0].height === images[1].height) ){ + data.isSameDimensions = true; + } else { + data.isSameDimensions = false; + } + + data.dimensionDifference = { width: images[0].width - images[1].width, height: images[0].height - images[1].height }; + + analyseImages( normalise(images[0],width, height), normalise(images[1],width, height), width, height); + + triggerDataUpdate(); + } + } + + images = []; + loadImageData(one, onceWeHaveBoth); + loadImageData(two, onceWeHaveBoth); + } + + function getCompareApi(param){ + + var secondFileData, + hasMethod = typeof param === 'function'; + + if( !hasMethod ){ + // assume it's file data + secondFileData = param; + } + + var self = { + ignoreNothing: function(){ + + tolerance.red = 16; + tolerance.green = 16; + tolerance.blue = 16; + tolerance.alpha = 16; + tolerance.minBrightness = 16; + tolerance.maxBrightness = 240; + + ignoreAntialiasing = false; + ignoreColors = false; + + if(hasMethod) { param(); } + return self; + }, + ignoreAntialiasing: function(){ + + tolerance.red = 32; + tolerance.green = 32; + tolerance.blue = 32; + tolerance.alpha = 32; + tolerance.minBrightness = 64; + tolerance.maxBrightness = 96; + + ignoreAntialiasing = true; + ignoreColors = false; + + if(hasMethod) { param(); } + return self; + }, + ignoreColors: function(){ + + tolerance.alpha = 16; + tolerance.minBrightness = 16; + tolerance.maxBrightness = 240; + + ignoreAntialiasing = false; + ignoreColors = true; + + if(hasMethod) { param(); } + return self; + }, + repaint: function(){ + if(hasMethod) { param(); } + return self; + }, + onComplete: function( callback ){ + + updateCallbackArray.push(callback); + + var wrapper = function(){ + compare(fileData, secondFileData); + }; + + wrapper(); + + return getCompareApi(wrapper); + } + }; + + return self; + } + + return { + onComplete: function( callback ){ + updateCallbackArray.push(callback); + loadImageData(fileData, function(imageData, width, height){ + parseImage(imageData.data, width, height); + }); + }, + compareTo: function(secondFileData){ + return getCompareApi(secondFileData); + } + }; + + }; + + _this['resemble'].outputSettings = function(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] ){ + errorPixelTransformer = errorPixelTransform[options.errorType]; + } + + pixelTransparency = options.transparency || pixelTransparency; + + return this; + }; + +}(this));
\ No newline at end of file |