git.lucas.co / cce-core
GUI-free half of the cce toolkit: config, input, IPC, spec parsers
git clone https://git.lucas.co/cce-core.git

src/color.rs (2K)

 1 //! sRGB/linear conversion and the DE's hex colour strings — the pure half of
 2 //! `cce_ui::color`, which re-exports every item here.
 3 
 4 pub fn srgb_to_linear(c: f32) -> f32 {
 5     if c <= 0.04045 {
 6         c / 12.92
 7     } else {
 8         ((c + 0.055) / 1.055).powf(2.4)
 9     }
10 }
11 
12 /// Parse a hex color string into raw RGBA bytes.
13 ///
14 /// Accepts `#RRGGBB` or `#RRGGBBAA`, tolerating surrounding quotes/whitespace and
15 /// an optional leading `#`. 6-digit input yields alpha `255`. Returns `None` for
16 /// any shorter/invalid input. This is the primitive the `[f32;_]` parsers build on.
17 pub fn parse_hex_bytes(s: &str) -> Option<[u8; 4]> {
18     let hex = s
19         .trim_matches(|c| c == '"' || c == '\'' || c == ' ')
20         .trim_start_matches('#');
21     let b = |i: usize| u8::from_str_radix(hex.get(i * 2..i * 2 + 2)?, 16).ok();
22     if hex.len() >= 8 {
23         Some([b(0)?, b(1)?, b(2)?, b(3)?])
24     } else if hex.len() >= 6 {
25         Some([b(0)?, b(1)?, b(2)?, 255])
26     } else {
27         None
28     }
29 }
30 
31 /// Parse a hex color string into raw sRGB RGBA in `[0,1]` (no gamma conversion).
32 /// See [`parse_hex_bytes`] for the accepted formats. Apply [`srgb_to_linear`] to
33 /// the RGB channels yourself if your render target expects linear color.
34 pub fn parse_hex_rgba(s: &str) -> Option<[f32; 4]> {
35     parse_hex_bytes(s).map(|[r, g, b, a]| {
36         [r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0, a as f32 / 255.0]
37     })
38 }
39 
40 /// Like [`parse_hex_rgba`] but drops alpha, returning raw sRGB RGB in `[0,1]`.
41 pub fn parse_hex_rgb(s: &str) -> Option<[f32; 3]> {
42     parse_hex_rgba(s).map(|[r, g, b, _]| [r, g, b])
43 }
44 
45 /// Like [`parse_hex_rgba`] but converts RGB from sRGB to linear (alpha kept as-is).
46 /// Use when your render target samples colors in linear space.
47 pub fn parse_hex_rgba_linear(s: &str) -> Option<[f32; 4]> {
48     parse_hex_rgba(s).map(|[r, g, b, a]| {
49         [srgb_to_linear(r), srgb_to_linear(g), srgb_to_linear(b), a]
50     })
51 }
52 
53 pub fn linear_to_srgb(c: f32) -> f32 {
54     if c <= 0.0031308 {
55         c * 12.92
56     } else {
57         1.055 * c.powf(1.0 / 2.4) - 0.055
58     }
59 }