Skip to content
Merged
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
13 changes: 10 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* @param {boolean} [options.diffMask=false] Draw the diff over a transparent background (a mask).
* @param {boolean} [options.checkerboard=true] Whether to blend semi-transparent pixels against a checkerboard pattern (true) or plain white (false) when comparing.
* @param {number} [options.windowSize=Infinity] If finite, return the maximum number of diff pixels found in any N×N sliding window instead of the total diff count.
* @param {Uint8Array | Uint8ClampedArray | void} [options.ignoreMask] Image data mask where any non-zero color value skips the corresponding img1 and img2 pixels being checked.
*
* @return {number} The number of mismatched pixels (or the maximum per-window count if windowSize is finite).
*/
Expand All @@ -28,7 +29,8 @@ export default function pixelmatch(img1, img2, output, width, height, options =
diffColor = [255, 0, 0],
checkerboard = true,
windowSize = Infinity,
includeAA, diffColorAlt, diffMask
includeAA, diffColorAlt, diffMask,
ignoreMask
} = options;

if (!isPixelData(img1) || !isPixelData(img2) || (output && !isPixelData(output)))
Expand All @@ -39,14 +41,19 @@ export default function pixelmatch(img1, img2, output, width, height, options =

if (img1.length !== width * height * 4) throw new Error(`Image data size does not match width/height. Expecting ${width * height * 4}. Got ${img1.length}`);

// check if ignored mask is identical
if (ignoreMask && ignoreMask.length !== width * height)
throw new Error(`Ignore mask size does not match image width/height. Expecting ${width * height}. Got ${ignoreMask.length}`);

// check if images are identical
const len = width * height;
const a32 = new Uint32Array(img1.buffer, img1.byteOffset, len);
const b32 = new Uint32Array(img2.buffer, img2.byteOffset, len);

let identical = true;

for (let i = 0; i < len; i++) {
if (a32[i] !== b32[i]) { identical = false; break; }
if (a32[i] !== b32[i] && (!ignoreMask || ignoreMask[i] <= 0)) { identical = false; break; }
}
if (identical) { // fast path if identical
if (output && !diffMask) {
Expand Down Expand Up @@ -75,7 +82,7 @@ export default function pixelmatch(img1, img2, output, width, height, options =
// compare each pixel of one image against the other one
for (let i = 0, pos = 0; i < len; i++, pos += 4) {
// whether the HyAB OKLab distance exceeds the threshold: 0 if not, ±1 if yes (negative if img2 pixel is darker)
const delta = a32[i] === b32[i] ? 0 : colorDelta(img1, img2, pos, pos, checkerboard, maxDelta);
const delta = (a32[i] === b32[i] || (ignoreMask && ignoreMask[i] > 0)) ? 0 : colorDelta(img1, img2, pos, pos, checkerboard, maxDelta);

// the color difference is above the threshold
if (delta) {
Expand Down
Binary file added test/fixtures/10a.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/10b.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/10diff.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/9a.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/9b.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/9diff.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ diffTest('6a', '6b', '6diff', options, 51);
diffTest('6a', '6a', '6empty', {threshold: 0}, 0);
diffTest('7a', '7b', '7diff', {diffColorAlt: [0, 255, 0]}, 2440);
diffTest('8a', '5b', '8diff', options, 32896);
// Test diff for parts not within an ignore mask
diffTest('9a', '9b', '9diff', {
threshold: 0.05,
ignoreMask: true
}, 12422);
// Test fast path with a diff within an ignore mask
diffTest('10a', '10b', '10diff', {
threshold: 0.05,
ignoreMask: true
}, 0);

test('OKLab metric uses Lr toe correction for near-black differences', () => {
const pixel = gray => new Uint8Array([gray, gray, gray, 255]);
Expand Down Expand Up @@ -164,6 +174,18 @@ function diffTest(imgPath1, imgPath2, diffPath, options, expectedMismatch) {
const {width, height} = img1;
const diff = new PNG({width, height});

if (options?.ignoreMask) {
const ignoreMask = new Uint8Array(width * height);
for (let index = 0; index < ignoreMask.length; index++) {
if (index > ignoreMask.length / 2) {
ignoreMask[index] = 0;
} else {
ignoreMask[index] = 1;
}
}
options.ignoreMask = ignoreMask;
}

const mismatch = match(img1.data, img2.data, diff.data, width, height, options);
const mismatch2 = match(img1.data, img2.data, null, width, height, options);

Expand Down
Loading