https://bugs.documentfoundation.org/show_bug.cgi?id=161766

--- Comment #9 from Myndex <[email protected]> ---
I get the impression here that the issue is not with the threshold, but some
other issue in how an automatic text color is chosen?

I do recall from #156182 that only the threshold was changed, as a compromise
solution. It was a compromise as still the incorrect coefficients (and lack of
linearization) were being used to calculate lightness. As I understand it, the
calculation is still:

aLum = (Red(aColor) * 76 + Green(aColor) * 151 + Blue(aColor) * 29) / 256

Which is incorrect but has the advantage of some computational simplicity. The
coefficients 76,151,29 relate to the obsolete NTSC system, which has been dead
for about two decades. These are still used for some "standard def" video for
legacy reasons, but don't relate to modern displays.

The incorrectness here is most visible in saturated green. An immediate
improvement can be had by swapping out the coefficients with those more related
to modern sRGB displays:

aLum = (Red(aColor) * 56 + Green(aColor) * 182 + Blue(aColor) * 18) / 256

Threshold = 154



And if saving cycles is important, this can be simplified to remove the
division:


aLum = (Red(aColor) * 2 + Green(aColor) * 7 + Blue(aColor))

Threshold = 1540


MORE ACCURATE METHODS AVAILABLE

We've done a lot of research here and it's not necessary to re-invent anything,
but real improvements make it necessary to incorporate more correct color
science into this project. If you want a finer and more accurate way to set
text either black or white, we have a "most simple but correct" version of a
text color flipper at the MaxContrast repo:

https://github.com/Myndex/max-contrast

This addresses lightness contrast only, and does not address the font weight
(which is  also important, but hey, baby steps...)

A key factor missing in the LibreOffice calculation is that gamma encoded
colors need to be linearized before applying the coefficients. If linearization
is not performed, then you'll experiences the kinds of errors noted in this
bug.


-----

@Regina: the calculation rules listed in WCAG 2.x are incorrect, and fail badly
for dark color pairs. WCAG 2.x contrast math should not be used, and the
results from WCAG 2.x math have been shown to be harmful to readability for
those with color vision deficiencies.



-----


SUMMARY OF THE COLOR SCIENCE

#00ff00 should calculate so that it is paired with black. In fact, from about
#0b0 up to #0f0 should pair with black. Darker than #0b0 can pair with white.

All red-only #n00 or blue-only #00n should pair with white.

Reason: the majority of luminance (lightness) is "green" and comes from the
green primary on a display. Blue makes up 7% at most, and red about 22%.

Luminance/lightness contrast is what is needed to view text. Contrast middle is
about #a2a2a2, though there are a number of factors at play, and the actual
perceptual middle can vary between about #999 and #b0b0b0.

#a2a2a2 is a reasonable flip point. For saturated colors, the equivalent
luminance is 0.36 Y, and this requires that the appropriate coefficients are
used to weight the R,G, and B primaries.

This was discussed in the original bug:
https://bugs.documentfoundation.org/show_bug.cgi?id=156182


----

The JS code from the Max Contrast repo https://github.com/Myndex/max-contrast.
Send it RGB ints, it returns a string (or however you want to define
black/white)


function maxContrast (sR = 0xa4, sG = 0xa4, sB = 0xa4) {

  const trc = 2.4, sRco = 0.2126729, sGco = 0.7151522, sBco = 0.0721750; 
  const flipY = 0.342; // based on APCA™ 0.98G-4g middle BG color

  let screenY = (sR/255.0)**trc*sRco + (sG/255.0)**trc*sGco +
(sB/255.0)**trc*sBco; 

  return screenY < flipY ? 'white' : 'black'
}

Alternately, of course:

   return screenY < flipY ? 1 : 0  // return 1 for white 0 for black
or
   return screenY < flipY ? 0xffffffff : 0xff000000    // return ARGB hex value


And the ** is the JS exponent operator, so for other languages:

  screenY = pow((sR/255.0),trc) * sRco + (pow((sG/255.0),trc) * sGco +
pow((sB/255.0),trc) * sBco; 



The MaxContrast function is the simplest but accurate, color-only model.
Further accuracy can be had by also incorporating spatial contexts (such as
font weight) and surrounding contexts, but that is a much bigger fish to fry.


Thank you for reading,

Andy


Andrew Somers
Director of Research
Inclusive Reading Technologies, Inc.

https://readtech.org/ARC/

-- 
You are receiving this mail because:
You are the assignee for the bug.

Reply via email to