Skip to content

Commit d4ecfac

Browse files
committed
Improve showing different dimension diffs
If two images were of different size but had overlapping content, we wouldn't display diffs properly. Instead of highlighting the "added" areas, we would simply ignore them and show a total/max diff of zero. It all boiled down to `colorDelta` not handling zero alpha values properly. I added a shortcut so that if one image has a fully transparent alpha and the other image doesn't, we show it as fully diffing. This will work for all web screenshots that we produce from Happo, but might look a little funky when providing own images through the api which are semi-transparent. For now I'm happpy to live with that limitation however, at least this is better than before (even for semi-transparent images).
1 parent 51c55de commit d4ecfac

File tree

9 files changed

+83
-15
lines changed

9 files changed

+83
-15
lines changed

src/__tests__/colorDelta-test.js

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
const colorDelta = require('../colorDelta');
22

33
it('is large when comparing black and white', () => {
4-
expect(colorDelta([0, 0, 0, 255], [255, 255, 255, 255]))
5-
.toBeGreaterThan(0.92);
4+
expect(colorDelta([0, 0, 0, 255], [255, 255, 255, 255])).toBeGreaterThan(
5+
0.92,
6+
);
67
});
78

89
it('is small when comparing black and very dark grey', () => {
9-
expect(colorDelta([0, 0, 0, 255], [10, 10, 10, 255]))
10-
.toBeLessThan(0.02);
10+
expect(colorDelta([0, 0, 0, 255], [10, 10, 10, 255])).toBeLessThan(0.02);
1111
});
1212

1313
it('is medium when comparing black and medium grey', () => {
@@ -23,11 +23,13 @@ it('is medium when comparing red and blue', () => {
2323
});
2424

2525
it('is zero when comparing transparent and white', () => {
26-
expect(colorDelta([0, 0, 0, 0], [255, 255, 255, 255]))
27-
.toEqual(0);
26+
expect(colorDelta([0, 0, 0, 0], [255, 255, 255, 255])).toEqual(0);
2827
});
2928

3029
it('is large when comparing transparent and black', () => {
31-
expect(colorDelta([0, 0, 0, 0], [0, 0, 0, 255]))
32-
.toBeGreaterThan(0.92);
30+
expect(colorDelta([0, 0, 0, 0], [0, 0, 0, 255])).toBeGreaterThan(0.92);
31+
});
32+
33+
it('is large when comparing white and transparent', () => {
34+
expect(colorDelta([255, 255, 255, 255], [0, 0, 0, 0])).toBeGreaterThan(0.92);
3335
});

src/__tests__/createDiffImage-test.js

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,8 @@ let image2;
1111
let subject;
1212

1313
beforeEach(async () => {
14-
image1 = (await Jimp.read(
15-
'http://localhost:5411/aa-ffffff.png',
16-
)).bitmap;
17-
image2 = (await Jimp.read(
18-
'http://localhost:5411/aa-f7f7f7.png',
19-
)).bitmap;
14+
image1 = (await Jimp.read('http://localhost:5411/aa-ffffff.png')).bitmap;
15+
image2 = (await Jimp.read('http://localhost:5411/aa-f7f7f7.png')).bitmap;
2016
subject = () =>
2117
createDiffImage(
2218
computeAndInjectDiffs({
@@ -31,3 +27,29 @@ it('has a total diff value and a max diff', async () => {
3127
expect(diff).toEqual(0.000013924627638992437);
3228
expect(maxDiff).toEqual(0.0009183359547574563);
3329
});
30+
31+
describe('when images are of different width', () => {
32+
beforeEach(async () => {
33+
image1 = (await Jimp.read('http://localhost:5411/alert-before.png')).bitmap;
34+
image2 = (await Jimp.read('http://localhost:5411/alert-after.png')).bitmap;
35+
});
36+
37+
it('has a total diff and a max diff', async () => {
38+
const { diff, maxDiff } = await subject();
39+
expect(diff).toEqual(0.20997431506849315);
40+
expect(maxDiff).toEqual(1);
41+
});
42+
});
43+
44+
describe('when images are of different height', () => {
45+
beforeEach(async () => {
46+
image1 = (await Jimp.read('http://localhost:5411/button-before.png')).bitmap;
47+
image2 = (await Jimp.read('http://localhost:5411/button-after.png')).bitmap;
48+
});
49+
50+
it('has a total diff and a max diff', async () => {
51+
const { diff, maxDiff } = await subject();
52+
expect(diff).toEqual(0.0078125);
53+
expect(maxDiff).toEqual(1);
54+
});
55+
});

src/__tests__/getDiffPixel-test.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
const getDiffPixel = require('../getDiffPixel');
2+
3+
let subject;
4+
let previousPixel;
5+
let currentPixel;
6+
7+
beforeEach(() => {
8+
previousPixel = [255, 255, 255, 255];
9+
currentPixel = [255, 255, 255, 255];
10+
subject = () => getDiffPixel(previousPixel, currentPixel);
11+
});
12+
13+
it('returns semi-opaque source if no diff', () => {
14+
expect(subject()).toEqual({ diff: 0, pixel: [255, 255, 255, 140] });
15+
});
16+
17+
it('returns magenta when diff', () => {
18+
currentPixel = [120, 120, 255, 255];
19+
expect(subject()).toEqual({
20+
diff: 0.23089126029146917,
21+
pixel: [179, 54, 130, 58.877271374324636],
22+
});
23+
});
24+
25+
it('returns diff when after alpha is zero', () => {
26+
currentPixel[3] = 0;
27+
expect(subject()).toEqual({
28+
diff: 1,
29+
pixel: [179, 54, 130, 255],
30+
});
31+
});
32+
33+
it('returns diff when before alpha is zero', () => {
34+
previousPixel[3] = 0;
35+
expect(subject()).toEqual({
36+
diff: 1,
37+
pixel: [179, 54, 130, 255],
38+
});
39+
});
2.51 KB
Loading
2.19 KB
Loading
7.87 KB
Loading
7.86 KB
Loading

src/colorDelta.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ module.exports = function colorDelta(previousPixel, currentPixel) {
3030
return 0;
3131
}
3232

33+
if ((a2 === 0 && a1 > 0) || (a1 === 0 && a2 > 0)) {
34+
return 1;
35+
}
36+
3337
if (a1 < 255) {
3438
a1 /= 255;
3539
r1 = blend(r1, a1);

src/createDiffImage.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ const GREEN = [106, 133, 0, 255];
55
const MAGENTA = [197, 39, 114, 255];
66

77
function getDataIndex(row, width, index) {
8-
return (width * row) + index;
8+
return width * row + index;
99
}
1010

1111
module.exports = function createDiffImage({ image1Data, image2Data }) {
12+
// Images have the same width and height here
1213
const width = image1Data[0].length;
1314
const height = image1Data.length;
1415

0 commit comments

Comments
 (0)