terminal_colorsaurus/
color.rs1#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
4#[non_exhaustive]
5pub struct Color {
6 pub r: u16,
8 pub g: u16,
10 pub b: u16,
12 }
15
16impl Color {
17 pub fn rgb(r: u16, g: u16, b: u16) -> Self {
19 Self { r, g, b }
20 }
21
22 pub fn perceived_lightness(&self) -> f32 {
32 let color = xterm_color::Color::rgb(self.r, self.g, self.b);
33 color.perceived_lightness()
34 }
35
36 pub fn scale_to_8bit(&self) -> (u8, u8, u8) {
47 (
48 scale_to_u8(self.r),
49 scale_to_u8(self.g),
50 scale_to_u8(self.b),
51 )
52 }
53}
54
55fn scale_to_u8(channel: u16) -> u8 {
56 (channel as u32 * (u8::MAX as u32) / (u16::MAX as u32)) as u8
57}
58
59#[cfg(feature = "rgb")]
60impl From<Color> for rgb::RGB16 {
61 fn from(value: Color) -> Self {
62 rgb::RGB16 {
63 r: value.r,
64 g: value.g,
65 b: value.b,
66 }
67 }
68}
69
70#[cfg(feature = "rgb")]
71impl From<Color> for rgb::RGB8 {
72 fn from(value: Color) -> Self {
73 let (r, g, b) = value.scale_to_8bit();
74 rgb::RGB8 { r, g, b }
75 }
76}
77
78#[cfg(feature = "rgb")]
79impl From<rgb::RGB16> for Color {
80 fn from(value: rgb::RGB16) -> Self {
81 Color {
82 r: value.r,
83 g: value.g,
84 b: value.b,
85 }
86 }
87}
88
89#[cfg(feature = "anstyle")]
90impl From<Color> for anstyle::RgbColor {
91 fn from(value: Color) -> Self {
92 let (r, g, b) = value.scale_to_8bit();
93 anstyle::RgbColor(r, g, b)
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn black_has_perceived_lightness_zero() {
103 let black = Color::default();
104 assert_eq!(0.0, black.perceived_lightness())
105 }
106
107 #[test]
108 fn white_has_perceived_lightness_100() {
109 let white = Color {
110 r: u16::MAX,
111 g: u16::MAX,
112 b: u16::MAX,
113 };
114 assert_eq!(1.0, white.perceived_lightness())
115 }
116}