bug-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

bug#41544: 26.3; Possible incorrect results from color-distance


From: Mattias Engdegård
Subject: bug#41544: 26.3; Possible incorrect results from color-distance
Date: Mon, 8 Jun 2020 15:11:36 +0200

6 juni 2020 kl. 20.27 skrev Eli Zaretskii <eliz@gnu.org>:

> What practical problem is being solved here?

The current predicates for determining whether a colour is light or dark are 
just bad. We can and should do better, and that's what this is all about.

Let's consider the three saturated colours #ff0000 (red), #00ff00 (green) and 
#0000ff (blue). Black text looks terrible on blue, as does white on green; 
black on red isn't good either. This comes as no surprise: the human eye is 
more sensitive to brightness levels of green than blue, with red somewhere 
in-between.

(These three colours just serve as illustrative examples; large chunks of the 
RGB space will share the same properties.)

How do the existing predicates do? Let's call them MAX, AVG and DIST.

MAX: dark iff max(r,g,b) < 0.5

This classifies saturated red and blue as light, which is clearly terrible.

AVG: dark iff (r+g+b) / 3 < 0.6

This classifies saturated green as dark, which is also wrong.

DIST: dark iff (color-distance c "black") ≤ 292485

This one also thinks saturated green is dark. While the approach here looks 
reasonable at first blush, it's really not: color-distance uses a simplified 
expression for how dissimilar two colours are, but doesn't do a very good job 
at determining brightness. For instance, its heavy blue weight makes no sense 
when used in this way:

 (color-distance "#ff0000" "black") => 162308
 (color-distance "#00ff00" "black") => 260100
 (color-distance "#0000ff" "black") => 194820

This means that we cannot fix DIST by tweaking its cut-off value; it's 
fundamentally unfit for this purpose.

For a proper solution, we have theory to guide us: determine the perceived 
brightness of a colour, and classify everything below a cut-off value as dark, 
others as light. The patch uses a standard expression for relative luminance: Y 
= 0.2126R + 0.7152G + 0.0722B, where R, G and B are linear colour components. 
We assume a gamma of 2.2; it is nearly identical to the sRGB gamma curve and 
the results are almost the same.

With a cut-off of 0.6, this predicate turns out to be quite good: much better 
than MAX, AVG or DIST at any rate. While not perfect, it's good enough in the 
sense that for colours where it seems to make the wrong decision, it's a fairly 
close call, and the colour is quite readable with both black and write as 
contrast.

I have tested it on several platforms and monitors, including 80x25 VGA 
hardware text mode, and have yet to find a case where it fails, which 
definitely cannot be said about the old predicates.

We can still tweak it: mainly the cut-off value (which is just experimentally 
determined) but also the coefficients and the gamma correction. Although 
technically valid, they aren't holy.

> This code's algorithm and rationale should be explained in the
> comments before we can discuss whether it's an improvement and why.

Thanks, I have improved this in the new patch.

This patch should also use the right coefficients (see above); I think I got 
them wrong the first time.

Attachment: 0001-Improved-light-dark-colour-predicate-bug-41544.patch
Description: Binary data


reply via email to

[Prev in Thread] Current Thread [Next in Thread]