diff --git a/index.js b/index.js index 7ecfbd8..3315037 100644 --- a/index.js +++ b/index.js @@ -4,6 +4,8 @@ module.exports = pixelmatch; const defaultOptions = { threshold: 0.1, // matching threshold (0 to 1); smaller is more sensitive + horizontalShiftPixels: 0, // Check matches within X many pixels of current pixel + verticalShiftPixels: 0, // Check matches within Y many pixels of current pixel includeAA: false, // whether to skip anti-aliasing detection alpha: 0.1, // opacity of original image in diff output aaColor: [255, 255, 0], // color of anti-aliased pixels in diff output @@ -52,7 +54,32 @@ function pixelmatch(img1, img2, output, width, height, options) { const pos = (y * width + x) * 4; // squared YUV distance between colors at this pixel position, negative if the img2 pixel is darker - const delta = colorDelta(img1, img2, pos, pos); + let delta = colorDelta(img1, img2, pos, pos); + if ((delta > maxDelta || delta < -1 * maxDelta) && (options.horizontalShiftPixels > 0 || options.verticalShiftPixels > 0)) { + let minAbsDelta = 9999; + let minOtherDelta = 9999; + for (let hShift = -1 * options.horizontalShiftPixels; hShift <= options.horizontalShiftPixels; ++hShift) { + for (let vShift = -1 * options.verticalShiftPixels; vShift <= options.verticalShiftPixels; ++vShift) { + if (x + hShift < 0 || x + hShift > width || y + vShift < 0 || y + vShift > height) { + //Ignore shifts of pixels outside the image + continue; + } + const currDelta = colorDelta(img1, img2, pos, pos + ((width * vShift) + hShift) * 4); + if (Math.abs(currDelta) < Math.abs(minAbsDelta)) { + minAbsDelta = currDelta; + } + const otherDelta = colorDelta(img1, img2, pos + ((width * vShift) + hShift) * 4, pos); + if (Math.abs(otherDelta) < Math.abs(minOtherDelta)) { + minOtherDelta = otherDelta; + } + } + } + if (Math.abs(minAbsDelta) > Math.abs(minOtherDelta)) { + delta = minAbsDelta; + } else { + delta = minOtherDelta; + } + } // the color difference is above the threshold if (Math.abs(delta) > maxDelta) { diff --git a/test/fixtures/8a.png b/test/fixtures/8a.png new file mode 100644 index 0000000..e6568fd Binary files /dev/null and b/test/fixtures/8a.png differ diff --git a/test/fixtures/8b.png b/test/fixtures/8b.png new file mode 100644 index 0000000..e759bf8 Binary files /dev/null and b/test/fixtures/8b.png differ diff --git a/test/fixtures/8diff.png b/test/fixtures/8diff.png new file mode 100644 index 0000000..9dd1585 Binary files /dev/null and b/test/fixtures/8diff.png differ diff --git a/test/fixtures/8empty.png b/test/fixtures/8empty.png new file mode 100644 index 0000000..9ee0d3d Binary files /dev/null and b/test/fixtures/8empty.png differ diff --git a/test/test.js b/test/test.js index cf52a1d..3dbe329 100644 --- a/test/test.js +++ b/test/test.js @@ -23,6 +23,8 @@ diffTest('5a', '5b', '5diff', options, 0); diffTest('6a', '6b', '6diff', options, 51); diffTest('6a', '6a', '6empty', {threshold: 0}, 0); diffTest('7a', '7b', '7diff', {diffColorAlt: [0, 255, 0]}, 2448); +diffTest('8a', '8b', '8diff', {threshold: 0.1}, 2944); +diffTest('8a', '8b', '8empty', {horizontalShiftPixels: 7, verticalShiftPixels: 6, threshold: 0.1}, 0); test('throws error if image sizes do not match', (t) => { t.throws(() => match(new Uint8Array(8), new Uint8Array(9), null, 2, 1), 'Image sizes do not match');