Browse Source

Merge remote-tracking branch 'origin/master' into gh-pages

gh-pages
james.cryer 7 years ago
parent
commit
7e579139f8
  1. 3
      .gitignore
  2. 85
      README.md
  3. 2
      bower.json
  4. 104
      chai-tests/main_spec.js
  5. 5
      chai-tests/test.html
  6. 13
      compareImages.js
  7. BIN
      demoassets/PeopleAlpha.png
  8. 51
      demoassets/main.js
  9. 98
      demoassets/resemble.css
  10. 15
      dockerfile
  11. 71
      index.html
  12. BIN
      nodejs-tests/PeopleComparedToPeople2.png
  13. 33
      nodejs-tests/compareImages.test.js
  14. 89
      nodejs-tests/resemble.test.js
  15. 2084
      package-lock.json
  16. 19
      package.json
  17. 256
      resemble.js
  18. 79
      tests/main_spec.js

3
.gitignore vendored

@ -1 +1,2 @@
.idea
.idea
node_modules

85
README.md

@ -1,7 +1,7 @@
Resemble.js
==========
Analyse and compare images with Javascript and HTML5. [Resemble.js Demo](http://huddle.github.com/Resemble.js/)
Analyse and compare images with Javascript and HTML5. [More info & Resemble.js Demo](http://huddle.github.com/Resemble.js/). Compatible with Node.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")
@ -13,7 +13,7 @@ Analyse and compare images with Javascript and HTML5. [Resemble.js Demo](http://
### Example
Retrieve basic analysis on image.
Retrieve basic analysis on an image:
```javascript
var api = resemble(fileData).onComplete(function(data){
@ -29,7 +29,7 @@ var api = resemble(fileData).onComplete(function(data){
});
```
Use resemble to compare two images.
Use resemble to compare two images:
```javascript
var diff = resemble(file).compareTo(file2).ignoreColors().onComplete(function(data){
@ -51,16 +51,17 @@ Scale second image to dimensions of the first one:
diff.scaleToSameSize();
```
You can also change the comparison method after the first analysis.
You can also change the comparison method after the first analysis:
```javascript
// diff.ignoreNothing();
// diff.ignoreColors();
// diff.ignoreAlpha();
diff.ignoreAntialiasing();
```
And change the output display style.
And change the output display style:
```javascript
resemble.outputSettings({
@ -71,14 +72,84 @@ resemble.outputSettings({
},
errorType: 'movement',
transparency: 0.3,
largeImageThreshold: 1200
largeImageThreshold: 1200,
useCrossOrigin: false,
outputDiff: true
});
// resembleControl.repaint();
```
It is possible to narrow down the area of comparison, by specifying a bounding box measured in pixels from the top left:
```javascript
resemble.outputSettings({
boundingBox: {
left: 100,
top: 200,
right: 200,
bottom: 600
}
});
// resembleControl.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.
You can switch this modify this behaviour by setting the `largeImageThreshold` option to a different value. Set it to **0** to switch it off completely.
You can modify this behaviour by setting the `largeImageThreshold` option to a different value. Set it to **0** to switch it off completely.
`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).
### Node.js
#### Installation
On Node, Resemble uses the `canvas` package instead of the native canvas support in the browser. To prevent browser users from being forced into installing Canvas, it's included as a peer dependency which means you have to install it alongside Resemble.
Canvas relies on some native image manipulation libraries to be install on the system. Please read the [Canvas installation instructions](https://www.npmjs.com/package/canvas) for OSX/Windows/Linux.
*Example commands for installation on OSX*
``` bash
npm install resemblejs
brew install pkg-config cairo libpng jpeg giflib
npm install canvas
```
#### 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:
``` js
const compareImage = require('resemblejs/compareImages');
// The parameters can be Node Buffers
// data is the same as usual with an additional getBuffer() function
const data = await compareImages(
fs.readFileSync('./demoassets/People.jpg'),
fs.readFileSync('./demoassets/People2.jpg')
);
fs.writeFileSync('./output.png', data.getBuffer());
```
#### Tests
To run the tests on Node (using Jest), type:
``` bash
npm run test
```
#### Dockerfile
For convenience I've added a simple Dockerfile to run the NodeJS tests in an Ubuntu container
``` bash
docker build -t huddle/resemble .
docker run huddle/resemble
```
--------------------------------------

2
bower.json

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

104
chai-tests/main_spec.js

File diff suppressed because one or more lines are too long

5
tests/test.html → chai-tests/test.html

@ -6,7 +6,8 @@
<body>
<p>Check the console for test results</p>
<script src="../resemble.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/chai/3.4.1/chai.js"></script>
<!--script src="//cdnjs.cloudflare.com/ajax/libs/chai/3.4.1/chai.js"></script-->
<script src="../node_modules/chai/chai.js"></script>
<script src="main_spec.js"></script>
</body>
</html>
</html>

13
compareImages.js

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

BIN
demoassets/PeopleAlpha.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

51
demoassets/main.js

@ -46,6 +46,8 @@ $(function(){
$('#blue').css('width',data.blue+'%');
$('#alpha').css('width',data.alpha+'%');
$('#brightness').css('width',data.brightness+'%');
$('#white').css('width',data.white+'%');
$('#black').css('width',data.black+'%');
});
});
@ -119,6 +121,10 @@ $(function(){
resembleControl.ignoreAntialiasing();
}
else
if($this.is('#alpha')){
resembleControl.ignoreAlpha();
}
else
if($this.is('#same-size')){
resembleControl.scaleToSameSize();
}
@ -177,6 +183,13 @@ $(function(){
resembleControl.repaint();
}
else
if($this.is('#diffOnly')){
resemble.outputSettings({
errorType: 'diffOnly'
});
resembleControl.repaint();
}
else
if($this.is('#opaque')){
resemble.outputSettings({
transparency: 1
@ -190,13 +203,28 @@ $(function(){
});
resembleControl.repaint();
}
else
if($this.is('#boundingBox')){
resemble.outputSettings({
boundingBox: {
left: $("#x1").val(),
top: $("#y1").val(),
right: $("#x2").val(),
bottom: $("#y2").val()
}
});
resembleControl.repaint();
$this.removeClass('active');
}
});
(function(){
var xhr = new XMLHttpRequest();
var xhr2 = new XMLHttpRequest();
var xhr3 = new XMLHttpRequest();
var done = $.Deferred();
var dtwo = $.Deferred();
var dthree = $.Deferred();
xhr.open('GET', 'demoassets/People.jpg', true);
xhr.responseType = 'blob';
@ -212,6 +240,13 @@ $(function(){
};
xhr2.send();
xhr3.open('GET', 'demoassets/PeopleAlpha.png', true);
xhr3.responseType = 'blob';
xhr3.onload = function(e) {
dthree.resolve(this.response);
};
xhr3.send();
$('#example-images').click(function(){
$('#dropzone1').html('<img src="demoassets/People.jpg"/>');
@ -228,6 +263,22 @@ $(function(){
return false;
});
$('#example-images-alpha').click(function(){
$('#dropzone1').html('<img src="demoassets/People.jpg"/>');
$('#dropzone2').html('<img src="demoassets/PeopleAlpha.png"/>');
$.when(done, dthree).done(function(file, file1){
if (typeof FileReader === 'undefined') {
resembleControl = resemble('demoassets/People.jpg').compareTo('demoassets/PeopleAlpha.png').onComplete(onComplete);
} else {
resembleControl = resemble(file).compareTo(file1).onComplete(onComplete);
}
});
return false;
});
}());
});

98
demoassets/resemble.css

@ -1,20 +1,19 @@
h1 {
text-align: center;
}
body {
padding-top: 60px;
padding-top: 60px;
}
footer {
margin-top: 45px;
padding: 35px 0 36px;
border-top: 1px solid #e5e5e5;
}
.drop-zone{
.drop-zone {
border: 10px dashed #ccc;
color: #ccc;
font-size: 32px;
@ -25,17 +24,19 @@ footer {
overflow: hidden;
position: relative;
}
.drop-zone.drag-over{
border: 10px dashed #0088CC;
}
.drop-zone img {
width: 400px;
position: absolute;
top: 0;
left: 0;
}
.small-drop-zone{
.drop-zone.drag-over {
border: 10px dashed #08c;
}
.drop-zone img {
width: 400px;
position: absolute;
top: 0;
left: 0;
}
.small-drop-zone {
margin-top: 20px;
margin-left: 90px;
border: 10px dashed #ccc;
@ -48,17 +49,58 @@ footer {
overflow: hidden;
position: relative;
}
.small-drop-zone.drag-over{
border: 10px dashed #0088CC;
}
.small-drop-zone img {
width: 330px;
position: absolute;
top: 0;
left: 0;
}
#image-diff {
margin-left: 0;
margin-top: 50px;
border-style: solid;
}
.small-drop-zone.drag-over {
border: 10px dashed #08c;
}
.small-drop-zone img {
width: 330px;
position: absolute;
top: 0;
left: 0;
}
#image-diff {
margin-left: 0;
margin-top: 25px;
border-style: solid;
}
.progress {
margin-bottom: 4px;
}
.progress.last {
margin-bottom: 8px;
}
.progress #alpha {
background-color: #eee;
background-image: linear-gradient(45deg, darkgrey 25%, transparent 25%, transparent 75%, darkgrey 75%, darkgrey),
linear-gradient(45deg, darkgrey 25%, lightgrey 25%, lightgrey 75%, darkgrey 75%, darkgrey);
background-size: 14px 14px;
background-position: 0 0, 7px 7px;
background-repeat: repeat;
}
.progress #white {
background-color: #fff;
background-image: -moz-linear-gradient(top, #fff, #ddd);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fff), to(#ddd));
background-image: -webkit-linear-gradient(top, #fff, #ddd);
background-image: -o-linear-gradient(top, #fff, #ddd);
background-image: linear-gradient(to bottom, #fff, #ddd);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffdddddd', GradientType=0)
}
.progress #black {
background-color: #000;
background-image: -moz-linear-gradient(top, #555, #000);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#555), to(#000));
background-image: -webkit-linear-gradient(top, #555, #000);
background-image: -o-linear-gradient(top, #555, #000);
background-image: linear-gradient(to bottom, #555, #000);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff555555', endColorstr='#ff000000', GradientType=0)
}

15
dockerfile

@ -0,0 +1,15 @@
FROM node:8
RUN apt-get update \
&& apt-get install -qq libcairo2-dev libjpeg-dev libpango1.0-dev libgif-dev build-essential g++
RUN mkdir -p /opt/node/js \
&& cd /opt/node \
&& npm install canvas color-convert
WORKDIR /opt/node/js
COPY . /opt/node/js
RUN npm install
CMD ["npm", "run", "test"]

71
index.html

@ -7,7 +7,7 @@
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="libs/twitter-bootstrap/bootstrap.min.css">
<link rel="stylesheet" href="demoassets/resemble.css?v1">
<link rel="stylesheet" href="demoassets/resemble.css?v1">
</head>
<body>
<div class="container">
@ -16,7 +16,7 @@
<h1>Resemble.js : Image analysis and comparison</h1>
</div>
</header>
<section role="main">
<div class="row">
<div class="span12">
@ -49,15 +49,20 @@
<div class="progress">
<div id="blue" class="bar" style="width: 0%;"></div>
</div>
<div class="progress">
<div class="progress last">
<div id="alpha" class="bar" style="width: 0%;"></div>
</div>
Black &amp; white
<div class="progress">
<div id="black" class="bar" style="width: 0%;"></div>
</div>
<div class="progress last">
<div id="white" class="bar" style="width: 0%;"></div>
</div>
Brightness
<div class="progress progress-warning">
<div id="brightness" class="bar" style="width: 0%;"></div>
</div>
</div>
</div>
</div>
@ -81,7 +86,13 @@
Drop two images on the boxes to the left. The box below will show a generated 'diff' image, pink areas show mismatch. This example best works with two very similar but slightly different images. Try for yourself!
</p>
<p>
Don't have any images to compare? <button class="btn" id="example-images">Use example images</button>
Don't have any images to compare?
<br />
<br />
<button class="btn" id="example-images">Use example images</button>
<br />
<br />
<button class="btn" id="example-images-alpha">Use example images with alpha</button>
</p>
<div id="image-diff" class="small-drop-zone">
Diff will appear here.
@ -93,6 +104,7 @@
<button class="btn active" id="less">Ignore less</button>
<button class="btn" id="colors">Ignore colors</button>
<button class="btn" id="antialising">Ignore antialiasing</button>
<button class="btn" id="alpha">Ignore alpha</button>
</div>
<br/>
@ -102,9 +114,6 @@
<button class="btn" id="same-size">Scale to same size</button>
</div>
<br/>
<br/>
<div class="btn-group buttons" style="display:none">
<button class="btn active" id="pink">Pink</button>
<button class="btn" id="yellow">Yellow</button>
@ -117,6 +126,7 @@
<button class="btn" id="movement">Movement</button>
<button class="btn" id="flatDifferenceIntensity">Flat with diff intensity</button>
<button class="btn" id="movementDifferenceIntensity">Movement with diff intensity</button>
<button class="btn" id="diffOnly">Diff portion from the input</button>
</div>
<br/>
<br/>
@ -125,7 +135,33 @@
<button class="btn active" id="opaque">Opaque</button>
<button class="btn" id="transparent">Transparent</button>
</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="x1" value="100" />
</div>
<div class="span1">
<label>Top</label>
<input type="number" class="input-mini" id="y1" value="100" />
</div>
<div class="span1">
<label>Right</label>
<input type="number" class="input-mini" id="x2" value="400" />
</div>
<div class="span1">
<label>Bottom</label>
<input type="number" class="input-mini" id="y2" value="300" />
</div>
<div class="span2">
<label>&nbsp;</label>
<button class="btn" id="boundingBox">Set bounding box</button>
</div>
</div>
</div>
<br/>
<br/>
<div id="diff-results" style="display:none;">
@ -137,7 +173,7 @@
Use the buttons above to change the comparison algorithm. Perhaps you don't care about color? Annoying antialiasing causing too much noise? Resemble.js offers multiple comparison options.
</p>
</div>
<p id="thesame" style="display:none;">
<strong>These images are the same!</strong>
</p>
@ -153,7 +189,7 @@
Resemble.js can be used for any image analysis and comparison requirement you might have in the browser. However, it has been designed and built for use by the PhantomJS powered visual regression library <a href="https://github.com/Huddle/PhantomCSS" target="_blank">PhantomCSS</a>. PhantomCSS needs to be able to ignore antialiasing as this would cause differences between screenshots derived from different machines.
</p>
<p>
Resemble.js uses the <a href="http://www.w3.org/TR/file-upload/" target="_blank">HTML5 File API</a> to parse image data, and canvas for rendering image diffs.
Resemble.js uses the <a href="http://www.w3.org/TR/file-upload/" target="_blank">HTML5 File API</a> to parse image data, and canvas for rendering image diffs.
</p>
<p>
<br/>
@ -168,10 +204,13 @@ var api = resemble(fileData).onComplete(function(data){
return data;
/*
{
red: 255,
green: 255,
blue: 255,
brightness: 255
red: 100,
green: 100,
blue: 100,
brightness: 100,
alpha: 100,
white: 100,
black: 100
}
*/
});</pre>
@ -191,7 +230,7 @@ resemble(file).compareTo(file2).onComplete(function(data){
</div>
</div>
</section>
<footer class="footer">
<p>
Created by <a href="https://github.com/jamescryer" target="_blank">James Cryer</a> and the Huddle development team.

BIN
nodejs-tests/PeopleComparedToPeople2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

33
nodejs-tests/compareImages.test.js

@ -0,0 +1,33 @@
const compareImages = require('../compareImages');
const fs = require('fs');
describe('compareImages', () => {
test('works with buffers', async () => {
const data = await compareImages(
fs.readFileSync('./demoassets/People.jpg'),
fs.readFileSync('./demoassets/People2.jpg')
);
expect(data.isSameDimensions).toBe(true);
expect(data.misMatchPercentage).toEqual('8.66');
const buffer = data.getBuffer();
expect(buffer).toBeInstanceOf(Buffer);
expect(buffer.length).toBe(91876);
const comparison = fs.readFileSync(
'./nodejs-tests/PeopleComparedToPeople2.png'
);
expect(buffer.equals(comparison)).toBe(true);
});
test('throws when failed', async () => {
const promise = compareImages(
fs.readFileSync('./demoassets/People.jpg'),
'bogus data'
);
await expect(promise).rejects.toMatch('Image load error.');
});
});

89
nodejs-tests/resemble.test.js

@ -0,0 +1,89 @@
const resemble = require('../resemble');
const fs = require('fs');
describe('resemble', () => {
test('base64', () => {
const people_src =
'data:image/jpeg;base64,' +
fs.readFileSync('./demoassets/People.jpg', 'base64');
const people2_src =
'data:image/jpeg;base64,' +
fs.readFileSync('./demoassets/People2.jpg', 'base64');
return new Promise(function(resolve, reject) {
resemble(people_src).compareTo(people2_src).onComplete(function(data) {
// console.info('Reached oncomplete for base64_string');
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.dimensionDifference.height).toEqual(0);
expect(data.dimensionDifference.width).toEqual(0);
expect(data.isSameDimensions).toBe(true);
expect(data.misMatchPercentage).toEqual('8.66');
resolve();
});
});
});
test('files', () => {
return new Promise(function(resolve, reject) {
resemble('./demoassets/People.jpg')
.compareTo('./demoassets/People2.jpg')
.onComplete(function(data) {
// console.info('Reached oncomplete for request_success');
expect(data.diffBounds).toEqual(
expect.objectContaining({
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.dimensionDifference.height).toEqual(0);
expect(data.dimensionDifference.width).toEqual(0);
expect(data.isSameDimensions).toBe(true);
expect(data.misMatchPercentage).toEqual('8.66');
resolve();
});
});
});
test('file not found', () => {
return new Promise(function(resolve, reject) {
resemble('../demoassets/People.jpg')
.compareTo('../demoassets/404-image.jpg')
.onComplete(function(data) {
// console.info('Reached oncomplete for request_404');
// console.log(data);
expect(data.error).toEqual('Image load error.');
resolve();
});
});
});
test('node buffers', () => {
const people = fs.readFileSync('./demoassets/People.jpg');
const people2 = fs.readFileSync('./demoassets/People2.jpg');
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(431);
expect(data.diffBounds.left).toEqual(22);
expect(data.diffBounds.right).toEqual(450);
expect(data.diffBounds.top).toEqual(58);
expect(data.dimensionDifference.height).toEqual(0);
expect(data.dimensionDifference.width).toEqual(0);
expect(data.isSameDimensions).toBe(true);
expect(data.misMatchPercentage).toEqual('8.66');
resolve();
});
});
});
});

2084
package-lock.json generated

File diff suppressed because it is too large Load Diff

19
package.json

@ -1,6 +1,6 @@
{
"name": "resemblejs",
"version": "2.2.1",
"version": "2.4.0",
"description": "Image analysis and comparison with HTML5",
"main": "resemble.js",
"repository": {
@ -20,5 +20,20 @@
"bugs": {
"url": "https://github.com/Huddle/Resemble.js/issues"
},
"homepage": "https://github.com/Huddle/Resemble.js"
"homepage": "https://github.com/Huddle/Resemble.js",
"scripts": {
"test": "jest ./nodejs-tests",
"test-watch": "jest --watch ./nodejs-tests"
},
"devDependencies": {
"chai": "^3.4.1",
"jest": "^20.0.4",
"jest-cli": "^20.0.4"
},
"peerDependencies": {
"canvas": "^1.6.5"
},
"jest": {
"testEnvironment": "node"
}
}

256
resemble.js

@ -1,5 +1,5 @@
/*
James Cryer / Huddle 2015
James Cryer / Huddle
URL: https://github.com/Huddle/Resemble.js
*/
@ -23,53 +23,70 @@ URL: https://github.com/Huddle/Resemble.js
alpha: 255
};
var targetPix = {r: 0, g: 0, b: 0, a: 0}; // isAntialiased
function colorsDistance(c1, c2){
return (Math.abs(c1.r - c2.r) + Math.abs(c1.g - c2.g) + Math.abs(c1.b - c2.b))/3;
}
function withinBoundingBox(x, y, width, height) {
if (!boundingBox) {
return true;
}
return x > (boundingBox.left || 0) &&
x < (boundingBox.right || width) &&
y > (boundingBox.top || 0) &&
y < (boundingBox.bottom || height);
}
var errorPixelTransform = {
flat : function (d1, d2){
return {
r: errorPixelColor.red,
g: errorPixelColor.green,
b: errorPixelColor.blue,
a: errorPixelColor.alpha
}
flat: function (px, offset, d1, d2) {
px[offset] = errorPixelColor.red;
px[offset + 1] = errorPixelColor.green;
px[offset + 2] = errorPixelColor.blue;
px[offset + 3] = 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
}
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 (d1, d2){
return {
r: errorPixelColor.red,
g: errorPixelColor.green,
b: errorPixelColor.blue,
a: colorsDistance(d1, d2)
}
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 (d1, d2){
var ratio = colorsDistance(d1, d2)/255 * 0.8;
return {
r: ((1-ratio)*(d2.r*(errorPixelColor.red/255)) + ratio*errorPixelColor.red),
g: ((1-ratio)*(d2.g*(errorPixelColor.green/255)) + ratio*errorPixelColor.green),
b: ((1-ratio)*(d2.b*(errorPixelColor.blue/255)) + ratio*errorPixelColor.blue),
a: d2.a
}
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 errorPixelTransformer = errorPixelTransform.flat;
var errorPixel = errorPixelTransform.flat;
var errorType;
var boundingBox;
var largeImageThreshold = 1200;
var httpRegex = /^https?:\/\//;
var document = typeof window != "undefined" ? window.document : {};
var documentDomainRegex = new RegExp('^https?://' + document.domain);
var useCrossOrigin = true;
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 ){
@ -100,12 +117,12 @@ URL: https://github.com/Huddle/Resemble.js
}
}
function loop(x, y, callback){
var i,j;
function loop(w, h, callback){
var x,y;
for (i=0;i<x;i++){
for (j=0;j<y;j++){
callback(i, j);
for (x=0;x<w;x++){
for (y=0;y<h;y++){
callback(x, y);
}
}
}
@ -118,8 +135,10 @@ URL: https://github.com/Huddle/Resemble.js
var blueTotal = 0;
var alphaTotal = 0;
var brightnessTotal = 0;
var whiteTotal = 0;
var blackTotal = 0;
loop(height, width, function(verticalPos, horizontalPos){
loop(width, height, function(horizontalPos, verticalPos){
var offset = (verticalPos*width + horizontalPos) * 4;
var red = sourceImageData[offset];
var green = sourceImageData[offset + 1];
@ -127,6 +146,14 @@ URL: https://github.com/Huddle/Resemble.js
var alpha = sourceImageData[offset + 3];
var brightness = getBrightness(red,green,blue);
if (red == green && red == blue && alpha) {
if (red == 0) {
blackTotal++
} else if (red == 255) {
whiteTotal++
}
}
pixelCount++;
redTotal += red / 255 * 100;
@ -141,20 +168,32 @@ URL: https://github.com/Huddle/Resemble.js
data.blue = Math.floor(blueTotal / pixelCount);
data.alpha = Math.floor(alphaTotal / pixelCount);
data.brightness = Math.floor(brightnessTotal / pixelCount);
data.white = Math.floor(whiteTotal / pixelCount * 100);
data.black = Math.floor(blackTotal / pixelCount * 100);
triggerDataUpdate();
}
function loadImageData( fileData, callback ){
var fileReader;
var hiddenImage = new Image();
var hiddenImage;
if (typeof Image !== 'undefined') {
hiddenImage = new Image();
} else {
var CanvasImage = require('canvas').Image;
hiddenImage = new CanvasImage();
hiddenImage.setAttribute = function setAttribute() { };
}
hiddenImage.setAttribute('crossorigin', 'anonymous');
hiddenImage.onerror = function () {
if(useCrossOrigin) {
hiddenImage.setAttribute('crossorigin', 'anonymous');
}
hiddenImage.onerror = function () {
hiddenImage.onerror = null; //fixes pollution between calls
images.push({ error : "Image load error."});
callback();
callback();
};
hiddenImage.onload = function() {
@ -163,16 +202,18 @@ URL: https://github.com/Huddle/Resemble.js
var hiddenCanvas = document.createElement('canvas');
var imageData;
if( scaleToSameSize && images.length == 1 ){
hiddenImage.width = images[0].width;
hiddenImage.height = images[0].height;
}
// don't assign to hiddenImage, see https://github.com/Huddle/Resemble.js/pull/87/commits/300d43352a2845aad289b254bfbdc7cd6a37e2d7
var width = hiddenImage.width;
var height = hiddenImage.height;
if( scaleToSameSize && images.length == 1 ){
width = images[0].width;
height = images[0].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);
@ -189,8 +230,13 @@ URL: https://github.com/Huddle/Resemble.js
} else if (typeof fileData.data !== 'undefined'
&& typeof fileData.width === 'number'
&& typeof fileData.height === 'number') {
images.push(fileData);
callback(fileData, fileData.width, fileData.height);
} else if (typeof Buffer !== 'undefined' && fileData instanceof Buffer){
// If we have Buffer, assume we're on Node+Canvas and its supported
hiddenImage.src = fileData;
} else {
fileReader = new FileReader();
fileReader.onload = function (event) {
@ -220,10 +266,6 @@ URL: https://github.com/Huddle/Resemble.js
}
}
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');
@ -280,7 +322,6 @@ URL: https://github.com/Huddle/Resemble.js
function isAntialiased(sourcePix, data, cacheSet, verticalPos, horizontalPos, width){
var offset;
var targetPix;
var distance = 1;
var i;
var j;
@ -298,9 +339,8 @@ URL: https://github.com/Huddle/Resemble.js
} else {
offset = ((verticalPos+j)*width + (horizontalPos+i)) * 4;
targetPix = getPixelInfo(data, offset, cacheSet);
if(targetPix === null){
if(!getPixelInfo(targetPix , data, offset, cacheSet)){
continue;
}
@ -333,15 +373,11 @@ URL: https://github.com/Huddle/Resemble.js
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){
if (errorType === 'diffOnly') {
return;
}
px[offset] = data.r; //r
px[offset + 1] = data.g; //g
px[offset + 2] = data.b; //b
@ -349,36 +385,27 @@ URL: https://github.com/Huddle/Resemble.js
}
function copyGrayScalePixel(px, offset, data){
if (errorType === 'diffOnly') {
return;
}
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
};
function getPixelInfo(dst, data, offset, cacheSet) {
if (data.length > offset) {
dst.r = data[offset];
dst.g = data[offset + 1];
dst.b = data[offset + 2];
dst.a = data[offset + 3];
return d;
} else {
return null;
return true;
}
return false;
}
function addBrightnessInfo(data){
@ -425,7 +452,10 @@ URL: https://github.com/Huddle/Resemble.js
skip = 6;
}
loop(height, width, function(verticalPos, horizontalPos){
var pixel1 = {r: 0, g: 0, b: 0, a: 0};
var pixel2 = { r: 0, g: 0, b: 0, a: 0 };
loop(width, height, function(horizontalPos, verticalPos){
if(skip){ // only skip if the image isn't small
if(verticalPos % skip === 0 || horizontalPos % skip === 0){
@ -434,10 +464,9 @@ URL: https://github.com/Huddle/Resemble.js
}
var offset = (verticalPos*width + horizontalPos) * 4;
var pixel1 = getPixelInfo(data1, offset, 1);
var pixel2 = getPixelInfo(data2, offset, 2);
var isWithinBoundingBox = withinBoundingBox(horizontalPos, verticalPos, width, height);
if(pixel1 === null || pixel2 === null){
if (!getPixelInfo(pixel1, data1, offset, 1) || !getPixelInfo(pixel2, data2, offset, 2)) {
return;
}
@ -446,7 +475,7 @@ URL: https://github.com/Huddle/Resemble.js
addBrightnessInfo(pixel1);
addBrightnessInfo(pixel2);
if( isPixelBrightnessSimilar(pixel1, pixel2) ){
if( isPixelBrightnessSimilar(pixel1, pixel2) || !isWithinBoundingBox ){
copyGrayScalePixel(targetPix, offset, pixel2);
} else {
errorPixel(targetPix, offset, pixel1, pixel2);
@ -456,7 +485,7 @@ URL: https://github.com/Huddle/Resemble.js
return;
}
if( isRGBSimilar(pixel1, pixel2) ){
if( isRGBSimilar(pixel1, pixel2) || !isWithinBoundingBox ){
copyPixel(targetPix, offset, pixel1, pixel2);
} else if( ignoreAntialiasing && (
@ -466,7 +495,7 @@ URL: https://github.com/Huddle/Resemble.js
isAntialiased(pixel2, data2, 2, verticalPos, horizontalPos, width)
)){
if( isPixelBrightnessSimilar(pixel1, pixel2) ){
if( isPixelBrightnessSimilar(pixel1, pixel2) || !isWithinBoundingBox ){
copyGrayScalePixel(targetPix, offset, pixel2);
} else {
errorPixel(targetPix, offset, pixel1, pixel2);
@ -497,6 +526,13 @@ URL: https://github.com/Huddle/Resemble.js
return hiddenCanvas.toDataURL("image/png");
};
if (hiddenCanvas.toBuffer) {
data.getBuffer = function() {
context.putImageData(imgd, 0, 0);
return hiddenCanvas.toBuffer();
}
}
}
function addLabel(text, context, hiddenCanvas){
@ -656,6 +692,21 @@ URL: https://github.com/Huddle/Resemble.js
if(hasMethod) { param(); }
return self;
},
ignoreAlpha: function() {
tolerance.red = 16;
tolerance.green = 16;
tolerance.blue = 16;
tolerance.alpha = 255;
tolerance.minBrightness = 16;
tolerance.maxBrightness = 240;
ignoreAntialiasing = false;
ignoreColors = false;
if(hasMethod) { param(); }
return self;
},
repaint: function(){
if(hasMethod) { param(); }
return self;
@ -702,7 +753,12 @@ URL: https://github.com/Huddle/Resemble.js
}
if(options.errorType && errorPixelTransform[options.errorType] ){
errorPixelTransformer = 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;
@ -711,6 +767,14 @@ URL: https://github.com/Huddle/Resemble.js
largeImageThreshold = options.largeImageThreshold;
}
if (options.useCrossOrigin !== undefined) {
useCrossOrigin = options.useCrossOrigin;
}
if (options.boundingBox !== undefined) {
boundingBox = options.boundingBox;
}
return this;
};

79
tests/main_spec.js

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save