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}