Functional Weave
Code in Python

charts.color@1.0.0

README.md

1,606 bytes · view raw

# charts.color

The small kit every colour calculation in `charts.*` starts from: hex text to
three 8-bit channels and back (`parseHex`, `toHex`), and each channel to
linear light and back (`srgbToLinear`, `linearToSrgb`). A group, because the
four only make sense together and `charts.interpolate-color` and
`charts.contrast` use them all.

**Hex.** `parseHex` accepts `#rrggbb` and the CSS short form `#rgb` (each digit
doubled, so `#f80` is `#ff8800`), in either case. A missing `#`, an alpha
channel, `rgb()` syntax or a colour name is an error, not a guess. `toHex`
always writes lowercase `#rrggbb`.

**Linear light.** A stored channel is gamma-encoded: 128 is only 21.6% of the
light of 255, and averaging stored values gives muddy, too-dark mixes.
`srgbToLinear` applies the sRGB transfer function of IEC 61966-2-1:
c / 12.92 when c = channel / 255 is at most 0.04045, otherwise
((c + 0.055) / 1.055)^2.4. It returns linear light from 0 to 1, rounded to 12
decimal places. `linearToSrgb` inverts it (12.92 v up to 0.0031308, otherwise
1.055 v^(1/2.4) - 0.055), clipping to 0..1 first because a mix computed in
another colour space can land a hair out of gamut, and rounds the channel half
away from zero. Every 8-bit value survives the round trip.

The powers come from `math.pow`, not `Math.pow` or `**`, so all three languages
return the same bits, and the rounding from `math.round-float`.

Sources: IEC 61966-2-1:1999, "Default RGB colour space - sRGB"; W3C, CSS Color
Module Level 4, section 10.2 "Predefined sRGB" (the same transfer function)
and section 5.2 "The RGB hexadecimal notations".