mirror of https://github.com/rkd77/elinks.git synced 2024-06-24 00:56:14 +00:00
2005-10-21 09:14:07 +02:00

300 lines
12 KiB

Color Models
The purpose of this document is to collect thoughts and ideas on how to mix and
adjust fore- and background colors to improve readability of pages.
ELinks already provides a mean to configure and adjust color combinations to
maximize readability. We would like to extend this capability to also include
the newly added 256 color mode.
For 16 colors, the adjustment is done by doing a few lookups in a hard coded
color table. This is fine when there are only 16 * 8 fore- and background
combinations but would probably be too painful for 256 * 256 combinations. Also,
the hard-coded table - although doing a good job - does not leave much up for the
users preferences. So the second goal is to also make the adjustment more
configurable so people with color deficiencies can tune the rendering to their
Partial sight, aging and congenital color deficits all produce changes in
perception that reduce the visual effectiveness of certain color combinations.
Two colors that contrast sharply to someone with normal vision may be far less
distinguishable to someone with a visual disorder. It is important to appreciate
that it is the contrast of colors one against another that makes them more or
less discernible rather than the individual colors themselves.
I.e., the current allow/forbid dark on black colors will be really simple and far
more universal. Zas is currently researching about rules to preserve page
readability by eliminating bad colors combinations (like dark red on dark red
or even primary red / primary blue, yellow on white, green on grey, ...). One
cool side effect is that we can adapt a color model to take in account some
user's visual deficiencies (e.g., rule like "do not use red colors at all", "forbid
orange on red", ...) Another color model may take in account screen environment:
"ensure very high lightness contrast".
Algorithms for determining bad color combinations
Zas has some ideas about that: there's no "algorithm" to determine bad color
combos, but many experiences, so we'll use common sense (i.e., low lightness
contrast, similar colors, white/bright yellow, and more...).
Algorithms for finding the nearest good color combination
This all comes down to applying what ever constraints the user desires:
1. given a back- and foreground color pair of RGB colors,
2. check if it satisfies all the constraints; if not, make a new couple obtained
by successive constraint satisfaction.
In some cases, one or more constraints can't be satisfied. For solving that, we
just need notions like "hard" / "strong" and "soft" / "weak" constraints.
hard constraint 1 = "no red"
hard constraint 2 = "lightness contrast > 50%"
hard constraint 3 = "saturation < 90%"
soft constraint 1 = "different hues for fg and bg"
soft constraint 2 = "foreground darker than background"
soft constraint 3 = "no green foreground on blue background"
Starting with (bg, fg) = (H=red L=70% S=100%; H=blue L=40% S=95%)
hc1 is not satisfied because fg is red, so we just need to move fg away from
1. hc2 is satisfied.
2. hc3 is not satisfied: red and blue are fully saturated.
3. sc1 is satisfied.
4. sc2 is not satisfied: fg is lighter than bg; so move fg away
from lightness, and move bg away from darkness.
5. sc3 is satisfied
So we have 2 constraints to satisfy, but while doing this we should not make
other unsatisfied.
Let's try:
Sort constraints related to hue, lightness and saturation:
hue: hc1, sc1, sc3
lightness: hc2, sc2
saturation: hc3
- Hue constraints satisfaction:
A) Nearest not red hue from fg is either green or blue (simpler for now), let
choose randomly one: green.
Now hc1 is satisfied, sc1 too, and sc3 is not.
B) Move fg away from green and red: fg is now blue.
Now hc1 is satisfied, sc1 is not, and sc3 is.
C) Since we can't satisfy all constraints, reiterate,
going back to A). Let move initial fg from red to blue instead of green.
Now hc1 is satisfied, sc1 is not, and sc3 too.
D) We cannot satisfy hc1 and sc1. Since sc1 is a soft constraint,
just ignore it and continue.
Finally we have (fg, bg) = (H=blue L=70% S=100%; H=blue L=40% S=95%)
- Lightness constraints satisfaction:
E) hc2 can be satisfied by either increasing lightness of fg or darkness of bg.
We choose to change fg (randomly):
(fg, bg) = (H=blue L=90% S=100%; H=blue L=40% S=95%)
F) sc2 can be satisfied by swapping fg an bg lightness:
(fg, bg) = (H=blue L=40% S=100%; H=blue L=90% S=95%)
- Saturation constraints:
G) hc3 can be satisfied by decreasing fg and bg saturation values:
(fg, bg) = (H=blue L=40% S=89%; H=blue L=90% S=89%)
This last couple satisfies all constraints, so we are done ;)
How to cache the result and integrate it with the current color system
In the previous example, we supposed we were using a true color palette, but in
the most cases, we have a limited number of usable colors.
We need to convert (fg, bg) to the nearest available color _before_ constraints
satisfaction and at each step of it.
When a valid transformation has been found we cache the initial (fg, bg) and the
result, and because we can't cache all combos we need to limit cache size to a
reasonable value and, sometimes, recalculate.
Maybe if the calculations will be really heavy, we can save/restore the combinations
to file (~/.elinks/colorhist ;) from session to session.
How to make the configuration easy but still powerful
User should be able to describe what he wants or not, so let's try to have our
own color-constraint interpreter:
One constraint is either hard or soft; it applies to fg, bg or both, and affects
either hue, lightness or saturation.
Hue unit is either a name (e.g., red) or an angle interval (e.g., for red, [0,30])
Lightness and saturation units are a percentage, 0-100%.
We'll use only integers values on the user side because it's simpler to handle.
Operators are either ! or no, < or lt, > or gt, = or eq, <= or lte, >= or
gte, != or ne, >< or hue differences (human sense).
Referring to hue, lighness and saturation is done by using these names or H, L,
Referring to fg or bg is possible by using fg or ^, bg or _.
Delta is expressed by / or delta
Conditional is ':'
hard constraint 1 = "no red" => "hue no red" == "H![0,30]"
hard constraint 2 = "lightness contrast > 50%" => "L delta gt 50"
hard constraint 3 = "saturation < 90%" => "saturation < 90"
soft constraint 1 = "different hues for fg and bg" => "hue fg >< bg"
soft constraint 2 = "foreground darker than background" => "L fg < bg"
soft constraint 3 = "no green foreground on blue background" =>
"H bg eq blue: fg ne green"
Hmmm, not perfect at all, but it's a start.
Maybe the option system can be made to handle it even though it might not be optimal.
[-]- Color model
| +-- Enable
| +-- Cache size
| [-]- Hard/Strong
| | [-]- Hue constraints
| | | +- Red (0 or 1 whether to allow this color)
| | | : (the basic colors of the color wheel)
| | [-]- Lightness constraints
| | | +- Minimum contrast
| | | +- Maximum contrast
| | [-]- Saturation constraints
| | +- Minimum saturation
| | +- Maximum saturation
| [+]- Soft/Weak
Rather hostile but quite usable for testing.
Including text attributes in the color model
Hmmm, since we use colors to render text attributes, I (zas) think we need to
integrate them in the process.
E.g., italics are rendered by color, so let the user tells which color to use.
A (bg, fg, type) triplet can be used for that. In constraint definition, we'll
have H, S, L and type (T) variables.
Type is (link|normal) and/or italic and/or bold and/or underline and/or
(subscript|supscript) etc...
Note link is not a text attribute (in the terminal sense of it); that's all the
Also note that the attribute enhancement should be fully optional.
In the solution we'll have (bg, fg, attributes), where bg and fg are colors.
Attributes are the ones supported by displaying device (i.e. normal bold underline
for now).
So constraints will take the form of:
link hue != blue : link hue = blue, link attr = bold
link hue >< italic hue
fg hue in !red
fg lightness > bg lightness
!(fg hue == yellow && bg hue == white)
fg hue >< bg hue
fg saturation < 90%
bg saturation < 90%
Hey, this language is better than the one before ;)
More about hues, or "what is red?":
Defining hues is not as easy it seems; the main problem here is to know what
we talk about when using the red name.
Where is the start and the end of red? Applying the KISS principle, let's say that in the HLS
system, S < H < E, where S is the starting hue and E the ending one.
For each hue we want to express let use a similar non-overlapping interval.
Take a circle and divide it in 3 parts for the primary hues, RGB:
Make R starts at 0 (hmm, in HLS it's not exactly the case, but NM).
Red is [0, 1*360/3[ (no, not a typo, 120 is not red)
Green is [1*360/3, 2*360/3[
Blue is [2*360/3, 0[
It will be convenient to have intermediate hues between red and green ;)
So just let divide by a value greater than 3, and name each part.
Using 12 subdivisions seems to be sufficient since we want to keep it simple.
Each subdivision should be equal but it may not reflect reality of human color
perception; using "uniform" color models instead of HLS may help here.
There is some sample code for RGB <-> HSL conversion posted to the elinks-dev
mailing list. It is expected to be merged to CVS when the color model itself
implementation draws nearer.
The color model idea sounds great. However, its usability is probably rather
limited and most users could live fine without it (that is not to dispute that
it can prove to be unique and invaluable for others). Thus, it should be fully
optional both at compile time and runtime.
It should still be possible to use only the original 16 colors fg_color xlat
table (which is so trivial that its inclusion probably need not be configurable
at compile time) because its value is indisputably greater by orders of
magnitude for the whole (or most of the) scale of users - the approximation to
16 colors is so imprecise that the result is too often far from what the page
author intended. Pasky believes that the 256 colors already provide much
preciser transformation of the RGB triplet and thus it is quite unlikely the
color combinations shown would differ dramatically from the intended look.
Jonas: I agree that the color adjustment should be optional like the current
one. Especially since the development of this will probably require quite a lot
of tuning before being really usable. In the future, with some kind of CSS
implementation, the user will have a further possibility to override any unreadable
styling. Whether this kind of fix-up will still be necessary only time will show.
Anyway since even 256 colors are pretty limited
Of course some people want to always force readability over the Web page's
author color choice (there is some really sick stuff color-wise around the Web,
and of course there are the "secret" black-on-black or white-on-white texts ;-),
and people with all sorts of color disabilities (which are quite frequent in the
population, AFAIK). This would be a killer feature for them and that is the
reason why Pasky thinks that the compile-time option for including of the color
models implementation should be by default enabled. Pasky is still not sure
whether the color models should be by default enabled or disabled at
runtime; pros and cons welcomed.
Further reading
Below are listed links to documents that have inspired this work.
Selecting harmonious colors for traditional window systems can be a
difficult and frustrating endeavor....
Conversion algorithms for these color spaces are originally from the
book Fundamentals of Interactive Computer Graphics by Foley and van Dam
(c 1982, Addison-Wesley).
Basic introduction to usage of HSL colors.
vim: textwidth=80