Skip to main content

renderling/
color.rs

1//! Color utils.
2
3use glam::Vec4;
4#[cfg(target_arch = "spirv")]
5use spirv_std::num_traits::Float;
6
7/// Applies a linear transfer function to an 8-bit unsigned integer color
8/// component.
9///
10/// This function simulates the gamma correction process by raising the
11/// component to the power of 2.2.
12///
13/// Converts from sRGB to linear color space.
14pub fn linear_xfer_u8(c: &mut u8) {
15    *c = ((*c as f32 / 255.0).powf(2.2) * 255.0) as u8;
16}
17
18/// Applies an optical transfer function to an 8-bit unsigned integer color
19/// component.
20///
21/// This function simulates the inverse gamma correction process by raising the
22/// component to the power of 1/2.2.
23///
24/// Converts from linear to sRGB color space.
25pub fn opto_xfer_u8(c: &mut u8) {
26    *c = ((*c as f32 / 255.0).powf(1.0 / 2.2) * 255.0) as u8;
27}
28
29/// Applies a linear transfer function to a 16-bit unsigned integer color
30/// component.
31///
32/// This function simulates the gamma correction process by raising the
33/// component to the power of 2.2.
34///
35/// Converts from sRGB to linear color space.
36pub fn linear_xfer_u16(c: &mut u16) {
37    *c = ((*c as f32 / 65535.0).powf(2.2) * 65535.0) as u16;
38}
39
40#[cfg(not(target_arch = "spirv"))]
41mod cpu {
42    use super::*;
43    use glam::Vec3;
44
45    /// Applies a linear transfer function to a 16-bit floating-point color
46    /// component.
47    ///
48    /// This function simulates the gamma correction process by raising the
49    /// component to the power of 2.2.
50    ///
51    /// Converts from sRGB to linear color space.
52    pub fn linear_xfer_f16(c: &mut u16) {
53        let mut f = half::f16::from_bits(*c).to_f32();
54        super::linear_xfer_f32(&mut f);
55        *c = half::f16::from_f32(f).to_bits();
56    }
57
58    pub fn u16_to_u8(c: u16) -> u8 {
59        ((c as f32 / 65535.0) * 255.0) as u8
60    }
61
62    pub fn f32_to_u8(c: f32) -> u8 {
63        (c / 255.0) as u8
64    }
65
66    pub fn u8_to_f32(c: u8) -> f32 {
67        c as f32 / 255.0
68    }
69
70    /// Converts a CSS style sRGB color into a `Vec4`.
71    ///
72    /// This applies the linear transfer function to the input color,
73    /// returning a color in linear color space.
74    pub fn css_srgb_color_to_linear(r: u8, g: u8, b: u8) -> Vec4 {
75        let rgb = [r, g, b].map(u8_to_f32);
76        let mut color = Vec3::from_array(rgb).extend(1.0);
77        linear_xfer_vec4(&mut color);
78        color
79    }
80}
81#[cfg(not(target_arch = "spirv"))]
82pub use cpu::*;
83
84/// Applies a linear transfer function to a 32-bit floating-point color
85/// component.
86///
87/// This function simulates the gamma correction process by raising the
88/// component to the power of 2.2.
89///
90/// Converts from sRGB to linear color space.
91pub fn linear_xfer_f32(c: &mut f32) {
92    *c = c.powf(2.2);
93}
94
95/// Applies a linear transfer function to each component of a `Vec4`.
96///
97/// This function simulates the gamma correction process by raising each
98/// component to the power of 2.2.
99///
100/// Converts from sRGB to linear color space for each component.
101pub fn linear_xfer_vec4(v: &mut Vec4) {
102    linear_xfer_f32(&mut v.x);
103    linear_xfer_f32(&mut v.y);
104    linear_xfer_f32(&mut v.z);
105}
106
107/// Converts an RGB hex color.
108///
109/// Converts a hex code like `0x6DC5D1` to a Vec4 with
110/// RGB components in the range `0.0` to `1.0` and an alpha of `1.0`.
111///
112/// ## Note
113/// This does **not** apply the linear transfer.
114pub fn rgb_hex_color(hex: u32) -> Vec4 {
115    let r = ((hex >> 16) & 0xFF) as f32 / 255.0;
116    let g = ((hex >> 8) & 0xFF) as f32 / 255.0;
117    let b = (hex & 0xFF) as f32 / 255.0;
118    Vec4::new(r, g, b, 1.0)
119}
120
121#[cfg(test)]
122mod test {
123    use super::rgb_hex_color;
124
125    #[test]
126    fn can_rgb_hex_color() {
127        let hex = 0x6dc5d1;
128        let color = rgb_hex_color(hex);
129        let r = (color.x * 255.0) as u8;
130        let g = (color.y * 255.0) as u8;
131        let b = (color.z * 255.0) as u8;
132        assert_eq!(109, 0x6d);
133        assert_eq!([109, 197, 209], [r, g, b]);
134    }
135}