Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Implements ideas from the following papers:

- `threshold` — Matching threshold, ranges from `0` to `1`. Smaller values make the comparison more sensitive. `0.1` by default.
- `includeAA` — If `true`, disables detecting and ignoring anti-aliased pixels. `false` by default.
- `includeDiffOnDataEquality` - If `false`, disables writing a diff image on perfect match, meaning `Buffer` or `Array` equality. Useful for performance gains in CI pipelines. `true` by default.

Compares two images, writes the output diff and returns the number of mismatched pixels.

Expand Down
46 changes: 46 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,33 @@ function pixelmatch(img1, img2, output, width, height, options) {
if (!options) options = {};

var threshold = options.threshold === undefined ? 0.1 : options.threshold;
var includeDiffOnDataEquality = options.includeDiffOnDataEquality === undefined ? true : options.includeDiffOnDataEquality;

// maximum acceptable square distance between two colors;
// 35215 is the maximum possible value for the YIQ difference metric
var maxDelta = 35215 * threshold * threshold,
diff = 0;

// if in node.js environment, check if the buffers are equal
if (typeof Buffer === 'function') {
var img1Buffer = Buffer.isBuffer(img1) ? img1 : Buffer.from(img1);
var img2Buffer = Buffer.isBuffer(img2) ? img2 : Buffer.from(img2);

if (img1Buffer.equals(img2Buffer)) {
if (includeDiffOnDataEquality && output) {
// return grey image
drawGrayImage(img1, output, width, height);
}
return diff;
}
} else if (isEqual(img1, img2)) {
if (includeDiffOnDataEquality && output) {
drawGrayImage(img1, output, width, height);
}

return diff;
}

// compare each pixel of one image against the other one
for (var y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
Expand Down Expand Up @@ -158,3 +179,28 @@ function grayPixel(img, i) {
b = blend(img[i + 2], a);
return rgb2y(r, g, b);
}

function drawGrayImage(img1, output, width, height) {
for (var y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
var pos = (y * width + x) * 4;

var val = blend(grayPixel(img1, pos), 0.1);
drawPixel(output, pos, val, val, val);
}
}
}

function isEqual(arr1, arr2) {
if (arr1.length !== arr2.length) {
return false;
}

for (var x = 0; x < arr1.length; x++) {
if (arr1[x] !== arr2[x]) {
return false;
}
}

return true;
}
Binary file added test/fixtures/5a.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/fixtures/5diff.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
54 changes: 54 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,60 @@ test('throws error if image sizes do not match', function (t) {
t.end();
});

test('should not write a diff on exact match if includeDiffOnDataEquality=false', function (t) {
var diff = [];
var mismatch = match(Buffer.from([1, 2, 3, 4]), [1, 2, 3, 4], diff, 1, 1, {
includeDiffOnDataEquality: false
});

t.deepEqual(diff, []);
t.equal(mismatch, 0);
t.end();
});

test('should write a gray image on exact match', function (t) {
var diff = [];
var mismatch = match(Buffer.from([1, 2, 3, 4]), [1, 2, 3, 4], diff, 1, 1);

t.deepEqual(diff, [254.60284823051373, 254.60284823051373, 254.60284823051373, 255]);
t.equal(mismatch, 0);
t.end();
});

test('should write a gray image on data equality', function (t) {
var img1 = readImage('5a', function () {
var expectedDiff = readImage('5diff', function () {
var img2 = Buffer.alloc(img1.data.length);
img1.data.copy(img2);
var diff = new PNG({width: img1.width, height: img1.height});

var mismatch = match(img1.data, img2, diff.data, diff.width, diff.height);

t.deepEqual(diff.data, expectedDiff.data, 'diff image');
t.equal(mismatch, 0);

t.end();
});
});
});

test('should not write diff image on data equality if includeDiffOnDataEquality=false', function (t) {
var img1 = readImage('5a', function () {
var img2 = Buffer.alloc(img1.data.length);
img1.data.copy(img2);
var diff = [];

var mismatch = match(img1.data, img2, diff.data, diff.width, diff.height, {
includeDiffOnDataEquality: false
});

t.deepEqual(diff, [], 'diff image');
t.equal(mismatch, 0);

t.end();
});
});

function diffTest(imgPath1, imgPath2, diffPath, threshold, includeAA, expectedMismatch) {
var name = 'comparing ' + imgPath1 + ' to ' + imgPath2 +
', threshold: ' + threshold + ', includeAA: ' + includeAA;
Expand Down