# Re: [dev] [st] Colors slightly different hue than in xterm

From: Alexander S. <alex0player_AT_gmail.com>
Date: Mon, 26 May 2014 06:25:12 +0400

2014-05-22 1:57 GMT+04:00 Roberto E. Vargas Caballero <k0ga_AT_shike2.com>:
>>
>> We need to generally re-think the algorithm used here. I hate the idea
>> of being conformant to this or that terminal emulator and instead
>> prefer an implementation according to how it's defined (!).
>
> I like this idea, but I don't know how it could be implemented.

The obvious idea is to convert a colour to HSV/HSL, increase
brightness, and convert it back
(One cannot help but notice that such a calculation is a matter of
initialization, since appropriate bright colours may be calculated for
all built-in colours in advance. Therefore, one possible solution may
be to modify dc.col (also xloadcols and xsetcolorname) to have
structures, containing normal and bold variants for a colour.)
After this point, dragons lie.

In HSV model, there are notions of Value, which is equal to maximum
among colors, and Saturation, which is 1 - Max/Min. Thus, Max = V, Min
= V (1 - S).
Then,
R = r * Max + (1 - r) * Min = V * (r + (1 - r) * (1 - S)),
G = g * Max + (1 - g) * Min = ..., and so on, and r, g, b depend only
on Hue. It is then trivial to see that a change in Brightness is a
trivial rescale: R_{new} = V_{new} * R_{old}/V_{old}. (In linear
space, not gamma space.) It's a simple calculation indeed, but, as
XTerm cube contains a lot of colours close to pure, it may not look
appropriate.

In HSL model, there are notions of Saturation and Lightness.
Saturation is calculated as (Max - Min)/(1 - |1 - (Max + Min)|), and
Lightness as (Max + Min)/2. Thus, reverse calculations of Max and Min
are a little more involved, but the general truth remains the same:
all colour components are convex combinations between Max and Min,
whose coefficients depend only on Hue.
Thus, for R coordinate for example, R_{new} = (R_{old} - Min_{old}) /
(Max_{old} - Min_{old}) * (Max_{new} - Min_{new}) + Min_{new}. It's
the same rescaling but with two points instead of one.

(I cannot emphanise enough that we DO NOT need luminance or perceived
brightness here. These define the brightness of all colours to come
common reference point, whereas lightness/value define brightness
relative to a reference color of the same hue. Which is exactly what
we need. Default colors are of different brightnesses, and this is not
our concern.)

Let us now estimate how precise should we be in our tables of sRGB <->
linear conversion. Let's say that, for the simplicity, we use the
evenly-spaced table and linear interpolation. (Dreadful.) The error is
estimated thus by h^2 * 1/8 max(f''(x)). for sRGB->linear, the
constant is 3.429/8 = 0.428. Let's say we want the precision of 2e-16,
which is totally reasonable considering that we use 16-bit color
model. And I might say, is ain't so bad! We merely need ~170 data
points. Let's go big and make it 257. (Then, the index for a 16-bit
integer is just number>>8.)

As for the reverse transofrmation, there we experience a big uprise
around zero. In fact, the second derivative peaks at ~-2300, which is
indeed true: the function curves a lot there, and interpolating it is
the world of trouble, requiring ~4300 points, which is distressingly
only slightly more than 4096. Makes things even worse.
Fortunately, we happen to know the reverse function for this one,
which is not only pretty smooth - but we also have a table for it! So
the task results in binary search in aforementioned table (of size
257, I might remind you, so it's more or less instant) and doing a
linear interpolation afterwards. Slower, but saves us a lot of
trouble!

Thus, the proposal requires addition of:
- the table of 16-bit integers of size 257;
- the function of interpolating y given x1, x2, y1, y2 and x;
- the function of increasing a given color's brightness;
- possibly changes to dc.col, depending on how costly it will be to do
such calculations on the fly vs in the initialization.

```--
Best regards,
Alexander Sedov.
```
Received on Mon May 26 2014 - 04:25:12 CEST

This archive was generated by hypermail 2.3.0 : Mon May 26 2014 - 04:36:07 CEST