mirror of
https://github.com/rkd77/elinks.git
synced 2024-08-22 21:14:32 -04:00
300 lines
12 KiB
Plaintext
300 lines
12 KiB
Plaintext
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.
|
|
|
|
Motivation
|
|
----------
|
|
|
|
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
|
|
needs.
|
|
|
|
Ideas
|
|
-----
|
|
|
|
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.
|
|
|
|
Example
|
|
-------
|
|
|
|
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
|
|
red.
|
|
|
|
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,
|
|
S.
|
|
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
|
|
magic.
|
|
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.
|
|
|
|
Implementation
|
|
--------------
|
|
|
|
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.
|
|
|
|
Availability
|
|
------------
|
|
|
|
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.
|
|
|
|
http://citeseer.nj.nec.com/macintyre91constraintbased.html
|
|
|
|
Selecting harmonious colors for traditional window systems can be a
|
|
difficult and frustrating endeavor....
|
|
|
|
http://130.113.54.154/~monger/hsl-rgb.html
|
|
|
|
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).
|
|
|
|
http://www.lighthouse.org/color_contrast.htm
|
|
|
|
Basic introduction to usage of HSL colors.
|
|
|
|
|
|
|
|
vim: textwidth=80
|