Browse Source

Merge branch 'master' into gh-pages

gh-pages
james.cryer 7 years ago
parent
commit
8e3920bbf3
  1. 7
      .dockerignore
  2. 6
      .npmignore
  3. 105
      README.md
  4. 1
      bower.json
  5. 10
      compareImages.js
  6. BIN
      demoassets/ghost1.png
  7. BIN
      demoassets/ghost2.png
  8. 87
      demoassets/main.js
  9. 37
      index.html
  10. BIN
      nodejs-tests/PeopleComparedToPeople2WithOriginal.png
  11. 8
      nodejs-tests/compareImages.test.js
  12. 108
      nodejs-tests/resemble.test.js
  13. 1339
      package-lock.json
  14. 15
      package.json
  15. 304
      resemble.js

7
.dockerignore

@ -0,0 +1,7 @@
.idea
**/node_modules
npm-debug.log
**/libs
index.html
bower.json
README.md

6
.npmignore

@ -3,4 +3,8 @@ npm-debug.log
libs libs
index.html index.html
bower.json bower.json
README.md README.md
chai-tests
nodejs-tests
.dockerignore
dockerfile

105
README.md

@ -75,8 +75,8 @@ resemble.outputSettings({
largeImageThreshold: 1200, largeImageThreshold: 1200,
useCrossOrigin: false, useCrossOrigin: false,
outputDiff: true outputDiff: true
}); })
// resembleControl.repaint(); // .repaint();
``` ```
It is possible to narrow down the area of comparison, by specifying a bounding box measured in pixels from the top left: It is possible to narrow down the area of comparison, by specifying a bounding box measured in pixels from the top left:
@ -89,8 +89,22 @@ resemble.outputSettings({
right: 200, right: 200,
bottom: 600 bottom: 600
} }
}); })
// resembleControl.repaint(); // .repaint();
```
You can also exclude part of the image from comparison, by specifying the excluded area in pixels from the top left:
```javascript
resemble.outputSettings({
ignoredBox: {
left: 100,
top: 200,
right: 200,
bottom: 600
}
})
// .repaint();
``` ```
By default, the comparison algorithm skips pixels when the image width or height is larger than 1200 pixels. This is there to mitigate performance issues. By default, the comparison algorithm skips pixels when the image width or height is larger than 1200 pixels. This is there to mitigate performance issues.
@ -99,6 +113,52 @@ You can modify this behaviour by setting the `largeImageThreshold` option to a d
`useCrossOrigin` is true by default, you might need to set it to false if you're using [Data URIs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs). `useCrossOrigin` is true by default, you might need to set it to false if you're using [Data URIs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs).
### Single callback api
The resemble.compare API provides a convenience function that is used as follows:
``` js
const compare = require('resemblejs').compare;
function getDiff(){
const options = {
output: {
errorColor: {
red: 255,
green: 0,
blue: 255
},
errorType: 'movement',
transparency: 0.3,
largeImageThreshold: 1200,
useCrossOrigin: false,
outputDiff: true
},
scaleToSameSize: true,
ignore: ['nothing', 'less', 'antialiasing', 'colors', 'alpha'],
};
// The parameters can be Node Buffers
// data is the same as usual with an additional getBuffer() function
compare(image1, image2, options, function (err, data) {
if (err) {
console.log('An error!')
} else {
console.log(data);
/*
{
misMatchPercentage : 100, // %
isSameDimensions: true, // or false
dimensionDifference: { width: 0, height: -1 }, // defined if dimensions are not the same
getImageDataUrl: function(){}
}
*/
}
});
}
```
### Node.js ### Node.js
#### Installation #### Installation
@ -117,19 +177,44 @@ npm install canvas
#### Usage #### Usage
The API under Node is the same as on the browser with one addition, a promise based `compareImage` convenience function that is used as follows: The API under Node is the same as on the `resemble.compare` but promise based:
``` js ``` js
const compareImage = require('resemblejs/compareImages'); const compareImages = require('resemblejs/compareImages');
const fs = require("mz/fs");
async function getDiff(){
const options = {
output: {
errorColor: {
red: 255,
green: 0,
blue: 255
},
errorType: 'movement',
transparency: 0.3,
largeImageThreshold: 1200,
useCrossOrigin: false,
outputDiff: true
},
scaleToSameSize: true,
ignore: ['nothing', 'less', 'antialiasing', 'colors', 'alpha'],
};
// The parameters can be Node Buffers // The parameters can be Node Buffers
// data is the same as usual with an additional getBuffer() function // data is the same as usual with an additional getBuffer() function
const data = await compareImages( const data = await compareImages(
fs.readFileSync('./demoassets/People.jpg'), await fs.readFile('./demoassets/People.jpg'),
fs.readFileSync('./demoassets/People2.jpg') await fs.readFile('./demoassets/People2.jpg'),
options
); );
fs.writeFileSync('./output.png', data.getBuffer()); await fs.writeFile('./output.png', data.getBuffer());
}
getDiff();
``` ```
#### Tests #### Tests

1
bower.json

@ -1,7 +1,6 @@
{ {
"name": "resemblejs", "name": "resemblejs",
"main": "resemble.js", "main": "resemble.js",
"version": "2.4.0",
"homepage": "https://github.com/Huddle/Resemble.js", "homepage": "https://github.com/Huddle/Resemble.js",
"authors": [ "authors": [
"James Cryer <james.cryer@huddle.com>" "James Cryer <james.cryer@huddle.com>"

10
compareImages.js

@ -1,10 +1,10 @@
const resemble = require('./resemble'); var resemble = require('./resemble');
module.exports = async function(image1, image2) { module.exports = function(image1, image2, options) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
resemble(image1).compareTo(image2).onComplete(function(data) { resemble.compare(image1, image2, options, function(err, data) {
if (data.error) { if (err) {
reject(data.error); reject(err);
} else { } else {
resolve(data); resolve(data);
} }

BIN
demoassets/ghost1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
demoassets/ghost2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

87
demoassets/main.js

@ -60,7 +60,23 @@ $(function(){
$('#image-diff').html(diffImage); $('#image-diff').html(diffImage);
$(diffImage).click(function(){ $(diffImage).click(function(){
window.open(diffImage.src, '_blank'); var w = window.open("about:blank", "_blank");
var html = w.document.documentElement;
var body = w.document.body;
html.style.margin = 0;
html.style.padding = 0;
body.style.margin = 0;
body.style.padding = 0;
var img = w.document.createElement("img");
img.src = diffImage.src;
img.alt = "image diff";
img.style.maxWidth = "100%";
img.addEventListener("click", function() {
this.style.maxWidth = this.style.maxWidth === "100%" ? "" : "100%";
});
body.appendChild(img);
}); });
$('.buttons').show(); $('.buttons').show();
@ -134,86 +150,87 @@ $(function(){
} }
else else
if($this.is('#pink')){ if($this.is('#pink')){
resemble.outputSettings({ resembleControl.outputSettings({
errorColor: { errorColor: {
red: 255, red: 255,
green: 0, green: 0,
blue: 255 blue: 255
} }
}); }).repaint();
resembleControl.repaint();
} }
else else
if($this.is('#yellow')){ if($this.is('#yellow')){
resemble.outputSettings({ resembleControl.outputSettings({
errorColor: { errorColor: {
red: 255, red: 255,
green: 255, green: 255,
blue: 0 blue: 0
} }
}); }).repaint();
resembleControl.repaint();
} }
else else
if($this.is('#flat')){ if($this.is('#flat')){
resemble.outputSettings({ resembleControl.outputSettings({
errorType: 'flat' errorType: 'flat'
}); }).repaint();
resembleControl.repaint();
} }
else else
if($this.is('#movement')){ if($this.is('#movement')){
resemble.outputSettings({ resembleControl.outputSettings({
errorType: 'movement' errorType: 'movement'
}); }).repaint();
resembleControl.repaint();
} }
else else
if($this.is('#flatDifferenceIntensity')){ if($this.is('#flatDifferenceIntensity')){
resemble.outputSettings({ resembleControl.outputSettings({
errorType: 'flatDifferenceIntensity' errorType: 'flatDifferenceIntensity'
}); }).repaint();
resembleControl.repaint();
} }
else else
if($this.is('#movementDifferenceIntensity')){ if($this.is('#movementDifferenceIntensity')){
resemble.outputSettings({ resembleControl.outputSettings({
errorType: 'movementDifferenceIntensity' errorType: 'movementDifferenceIntensity'
}); }).repaint();
resembleControl.repaint();
} }
else else
if($this.is('#diffOnly')){ if($this.is('#diffOnly')){
resemble.outputSettings({ resembleControl.outputSettings({
errorType: 'diffOnly' errorType: 'diffOnly'
}); }).repaint();
resembleControl.repaint();
} }
else else
if($this.is('#opaque')){ if($this.is('#opaque')){
resemble.outputSettings({ resembleControl.outputSettings({
transparency: 1 transparency: 1
}); }).repaint();
resembleControl.repaint();
} }
else else
if($this.is('#transparent')){ if($this.is('#transparent')){
resemble.outputSettings({ resembleControl.outputSettings({
transparency: 0.3 transparency: 0.3
}); }).repaint();
resembleControl.repaint();
} }
else else
if($this.is('#boundingBox')){ if($this.is('#boundingBox')){
resemble.outputSettings({ resembleControl.outputSettings({
boundingBox: { boundingBox: {
left: $("#x1").val(), left: $("#bounding-box-x1").val(),
top: $("#y1").val(), top: $("#bounding-box-y1").val(),
right: $("#x2").val(), right: $("#bounding-box-x2").val(),
bottom: $("#y2").val() bottom: $("#bounding-box-y2").val()
} }
}); }).repaint();
resembleControl.repaint(); $this.removeClass('active');
}
if($this.is('#ignoredBox')){
resembleControl.outputSettings({
ignoredBox: {
left: $("#ignored-box-x1").val(),
top: $("#ignored-box-y1").val(),
right: $("#ignored-box-x2").val(),
bottom: $("#ignored-box-y2").val()
}
}).repaint();
$this.removeClass('active'); $this.removeClass('active');
} }
}); });

37
index.html

@ -142,19 +142,19 @@
<div class="row"> <div class="row">
<div class="span1"> <div class="span1">
<label>Left</label> <label>Left</label>
<input type="number" class="input-mini" id="x1" value="100" /> <input type="number" class="input-mini" id="bounding-box-x1" value="100" />
</div> </div>
<div class="span1"> <div class="span1">
<label>Top</label> <label>Top</label>
<input type="number" class="input-mini" id="y1" value="100" /> <input type="number" class="input-mini" id="bounding-box-y1" value="100" />
</div> </div>
<div class="span1"> <div class="span1">
<label>Right</label> <label>Right</label>
<input type="number" class="input-mini" id="x2" value="400" /> <input type="number" class="input-mini" id="bounding-box-x2" value="400" />
</div> </div>
<div class="span1"> <div class="span1">
<label>Bottom</label> <label>Bottom</label>
<input type="number" class="input-mini" id="y2" value="300" /> <input type="number" class="input-mini" id="bounding-box-y2" value="300" />
</div> </div>
<div class="span2"> <div class="span2">
<label>&nbsp;</label> <label>&nbsp;</label>
@ -162,6 +162,35 @@
</div> </div>
</div> </div>
</div> </div>
<br/>
<br/>
<div class="btn-group buttons" style="display:none">
<div class="row">
<div class="span1">
<label>Left</label>
<input type="number" class="input-mini" id="ignored-box-x1" value="120" />
</div>
<div class="span1">
<label>Top</label>
<input type="number" class="input-mini" id="ignored-box-y1" value="200" />
</div>
<div class="span1">
<label>Right</label>
<input type="number" class="input-mini" id="ignored-box-x2" value="400" />
</div>
<div class="span1">
<label>Bottom</label>
<input type="number" class="input-mini" id="ignored-box-y2" value="250" />
</div>
<div class="span2">
<label>&nbsp;</label>
<button class="btn" id="ignoredBox">Set ignored box</button>
</div>
</div>
</div>
<br/> <br/>
<br/> <br/>
<div id="diff-results" style="display:none;"> <div id="diff-results" style="display:none;">

BIN
nodejs-tests/PeopleComparedToPeople2WithOriginal.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

8
nodejs-tests/compareImages.test.js

@ -21,6 +21,12 @@ describe('compareImages', () => {
); );
expect(buffer.equals(comparison)).toBe(true); expect(buffer.equals(comparison)).toBe(true);
const buffer2 = data.getBuffer(true);
const comparison2 = fs.readFileSync(
'./nodejs-tests/PeopleComparedToPeople2WithOriginal.png'
);
expect(buffer2.equals(comparison2)).toBe(true);
}); });
test('throws when failed', async () => { test('throws when failed', async () => {
@ -28,6 +34,6 @@ describe('compareImages', () => {
fs.readFileSync('./demoassets/People.jpg'), fs.readFileSync('./demoassets/People.jpg'),
'bogus data' 'bogus data'
); );
await expect(promise).rejects.toMatch('Image load error.'); await expect(promise).rejects.toMatch('Error: error while reading from input stream');
}); });
}); });

108
nodejs-tests/resemble.test.js

@ -26,33 +26,36 @@ describe('resemble', () => {
}); });
}); });
test('files', () => { // this doesn't work on windows
return new Promise(function(resolve, reject) {
resemble('./demoassets/People.jpg') // test('files', () => {
.compareTo('./demoassets/People2.jpg') // return new Promise(function(resolve, reject) {
.onComplete(function(data) { // console.log('hello there how are you');
// console.info('Reached oncomplete for request_success'); // resemble('demoassets/People.jpg')
expect(data.diffBounds).toEqual( // .compareTo('demoassets/People2.jpg')
expect.objectContaining({ // .onComplete(function(data) {
bottom: expect.any(Number), // // console.info('Reached oncomplete for request_success');
left: expect.any(Number), // expect(data.diffBounds).toEqual(
top: expect.any(Number), // expect.objectContaining({
right: expect.any(Number) // bottom: expect.any(Number),
}) // left: expect.any(Number),
); // top: expect.any(Number),
// right: expect.any(Number)
expect(data.diffBounds.bottom).toEqual(431); // })
expect(data.diffBounds.left).toEqual(22); // );
expect(data.diffBounds.right).toEqual(450); //
expect(data.diffBounds.top).toEqual(58); // expect(data.diffBounds.bottom).toEqual(431);
expect(data.dimensionDifference.height).toEqual(0); // expect(data.diffBounds.left).toEqual(22);
expect(data.dimensionDifference.width).toEqual(0); // expect(data.diffBounds.right).toEqual(450);
expect(data.isSameDimensions).toBe(true); // expect(data.diffBounds.top).toEqual(58);
expect(data.misMatchPercentage).toEqual('8.66'); // expect(data.dimensionDifference.height).toEqual(0);
resolve(); // expect(data.dimensionDifference.width).toEqual(0);
}); // expect(data.isSameDimensions).toBe(true);
}); // expect(data.misMatchPercentage).toEqual('8.66');
}); // resolve();
// });
// });
// });
test('file not found', () => { test('file not found', () => {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
@ -61,13 +64,13 @@ describe('resemble', () => {
.onComplete(function(data) { .onComplete(function(data) {
// console.info('Reached oncomplete for request_404'); // console.info('Reached oncomplete for request_404');
// console.log(data); // console.log(data);
expect(data.error).toEqual('Image load error.'); expect(data.error).toEqual('Error: error while reading from input stream');
resolve(); resolve();
}); });
}); });
}); });
test('node buffers', () => { test('node buffers jpg', () => {
const people = fs.readFileSync('./demoassets/People.jpg'); const people = fs.readFileSync('./demoassets/People.jpg');
const people2 = fs.readFileSync('./demoassets/People2.jpg'); const people2 = fs.readFileSync('./demoassets/People2.jpg');
@ -86,4 +89,51 @@ describe('resemble', () => {
}); });
}); });
}); });
test('node buffers png', () => {
const people = fs.readFileSync('./demoassets/ghost1.png');
const people2 = fs.readFileSync('./demoassets/ghost2.png');
return new Promise(function(resolve, reject) {
resemble(people).compareTo(people2).onComplete(function(data) {
// console.info('Reached oncomplete for base64_string');
expect(data.diffBounds.bottom).toEqual(138);
expect(data.diffBounds.left).toEqual(90);
expect(data.diffBounds.right).toEqual(157);
expect(data.diffBounds.top).toEqual(107);
expect(data.dimensionDifference.height).toEqual(0);
expect(data.dimensionDifference.width).toEqual(0);
expect(data.isSameDimensions).toBe(true);
expect(data.misMatchPercentage).toEqual('0.27');
resolve();
});
});
});
test('test partial diff with bounding box', () => {
const people = fs.readFileSync('./demoassets/ghost1.png');
const people2 = fs.readFileSync('./demoassets/ghost2.png');
return new Promise(function(resolve, reject) {
resemble.outputSettings({
boundingBox: {
left: 80,
top: 80,
right: 130,
bottom: 130
}
});
resemble(people).compareTo(people2).onComplete(function(data) {
expect(data.misMatchPercentage).toEqual('0.04');
resolve();
});
});
});
}); });

1339
package-lock.json generated

File diff suppressed because it is too large Load Diff

15
package.json

@ -1,6 +1,6 @@
{ {
"name": "resemblejs", "name": "resemblejs",
"version": "2.4.0", "version": "2.9.0",
"description": "Image analysis and comparison with HTML5", "description": "Image analysis and comparison with HTML5",
"main": "resemble.js", "main": "resemble.js",
"repository": { "repository": {
@ -22,16 +22,17 @@
}, },
"homepage": "https://github.com/Huddle/Resemble.js", "homepage": "https://github.com/Huddle/Resemble.js",
"scripts": { "scripts": {
"test": "jest ./nodejs-tests", "test": "jest nodejs-tests",
"test-watch": "jest --watch ./nodejs-tests" "test-watch": "jest --watch nodejs-tests"
},
"dependencies": {
"canvas-prebuilt": "^1.6.5-prerelease.1"
}, },
"devDependencies": { "devDependencies": {
"chai": "^3.4.1", "chai": "^3.4.1",
"jest": "^20.0.4", "jest": "^20.0.4",
"jest-cli": "^20.0.4" "jest-cli": "^20.0.4",
}, "color-convert": "^1.9.0"
"peerDependencies": {
"canvas": "^1.6.5"
}, },
"jest": { "jest": {
"testEnvironment": "node" "testEnvironment": "node"

304
resemble.js

@ -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;
})); }));

Loading…
Cancel
Save