Skip to content

Commit 59a59e5

Browse files
committed
Handle almost-transparent and transparent pixels better
I came across a case where one pixel is almost transparent and the other is fully transparent. Consider these pixels: [ 1, 46, 250, 0 ] [ 1, 42, 250, 4 ] With our old logic, there was a color-delta of `1` between these pixels. If we blend them both with white, we instead get a very low color-delta value. At first I was going to always blend with white, but I realized we are using fully transparent as a signal that there is a gap/missing pixels in either the before or after image. So I instead changed it to "intentional transparency" where something like [255, 255, 255, 0] [0, 0, 0, 0] [100, 100, 100, 0] would be considered "intentional transparency". Of course, this assumption can't always be made. But for us I think we can use it to get rid of the edge-case that I present here. There's still a risk that we'll encounter "wild" transparency with r===g===b but at least this is better than what we have.
1 parent 809bb90 commit 59a59e5

File tree

2 files changed

+22
-1
lines changed

2 files changed

+22
-1
lines changed

src/__tests__/colorDelta-test.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,11 @@ it('is large when comparing transparent and black', () => {
3333
it('is large when comparing white and transparent', () => {
3434
expect(colorDelta([255, 255, 255, 255], [0, 0, 0, 0])).toBeGreaterThan(0.92);
3535
});
36+
37+
it('is one when comparing intentionally transparent and some other color', () => {
38+
expect(colorDelta([0, 0, 0, 0], [33, 33, 33, 10])).toEqual(1);
39+
});
40+
41+
it('is small when comparing unintentional transparent and similar color', () => {
42+
expect(colorDelta([ 1, 46, 250, 0 ], [ 1, 42, 250, 4 ])).toBeLessThan(0.05);
43+
});

src/colorDelta.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ function blend(color, alpha) {
1717
return 255 + (color - 255) * alpha;
1818
}
1919

20+
function isIntentionalTransparent([r, g, b, a]) {
21+
if (a !== 0) {
22+
return false;
23+
}
24+
if (r === g && g === b && (r === 255 || r === 0)) {
25+
return true;
26+
}
27+
return false;
28+
}
29+
2030
// calculate color difference according to the paper "Measuring perceived color
2131
// difference using YIQ NTSC transmission color space in mobile applications" by
2232
// Y. Kotsarenko and F. Ramos
@@ -30,7 +40,10 @@ module.exports = function colorDelta(previousPixel, currentPixel) {
3040
return 0;
3141
}
3242

33-
if ((a2 === 0 && a1 > 0) || (a1 === 0 && a2 > 0)) {
43+
if (
44+
(isIntentionalTransparent(currentPixel) && a1 > 0) ||
45+
(isIntentionalTransparent(previousPixel) && a2 > 0)
46+
) {
3447
return 1;
3548
}
3649

0 commit comments

Comments
 (0)