gpui_kit_material_color/
color.rs1#[derive(Clone, Copy, Debug, Default, PartialEq)]
3pub struct Color {
4 pub r: f32,
5 pub g: f32,
6 pub b: f32,
7 pub a: f32,
8}
9
10impl Color {
11 pub const fn hex(rgb: u32) -> Self {
13 Self::hexa((rgb << 8) | 0xff)
14 }
15
16 pub const fn hexa(rgba: u32) -> Self {
18 Self {
19 r: ((rgba >> 24) & 0xff) as f32 / 255.,
20 g: ((rgba >> 16) & 0xff) as f32 / 255.,
21 b: ((rgba >> 8) & 0xff) as f32 / 255.,
22 a: (rgba & 0xff) as f32 / 255.,
23 }
24 }
25
26 pub const fn alpha(self, alpha: f32) -> Self {
28 Self { a: alpha, ..self }
29 }
30
31 pub fn blend(self, to: Self, t: f32) -> Self {
37 if t >= 1. {
39 return to;
40 }
41 let t = t.max(0.);
42 Self {
43 r: self.r + (to.r - self.r) * t,
44 g: self.g + (to.g - self.g) * t,
45 b: self.b + (to.b - self.b) * t,
46 a: self.a + (to.a - self.a) * t,
47 }
48 }
49
50 pub fn luminance(self) -> f32 {
53 fn linear(channel: f32) -> f32 {
54 if channel <= 0.04045 {
55 channel / 12.92
56 } else {
57 ((channel + 0.055) / 1.055).powf(2.4)
58 }
59 }
60 0.2126 * linear(self.r) + 0.7152 * linear(self.g) + 0.0722 * linear(self.b)
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::Color;
67
68 #[test]
69 fn hex_and_blend() {
70 let black = Color::hex(0x000000);
71 let white = Color::hex(0xffffff);
72 assert_eq!(white.a, 1.);
73 assert_eq!(black.blend(white, 0.5).r, 0.5);
74 assert_eq!(black.blend(white, 2.), white, "t is clamped");
75 assert_eq!(Color::hexa(0x10141680).a, 128. / 255.);
76 assert!(white.luminance() > black.luminance());
77 }
78
79 #[test]
80 fn luminance_linearizes_srgb() {
81 assert_eq!(Color::hex(0xffffff).luminance(), 1.);
82 assert_eq!(Color::hex(0x000000).luminance(), 0.);
83 let gray = Color::hex(0x808080).luminance();
85 assert!((gray - 0.2159).abs() < 0.0005, "{gray}");
86 let dark = Color::hex(0x0a0a0a).luminance();
88 assert!((dark - 10. / 255. / 12.92).abs() < 1e-6, "{dark}");
89 assert!((Color::hex(0x00ff00).luminance() - 0.7152).abs() < 1e-6);
91 }
92}