1use serde::{Deserialize, Serialize};
14use substring::Substring;
15
16#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Debug, Default)]
17pub struct Color {
18 pub r: u8,
19 pub g: u8,
20 pub b: u8,
21 pub a: u8,
22}
23
24impl Color {
25 pub fn hex(&self) -> String {
27 format!("#{:02X}{:02X}{:02X}", self.r, self.g, self.b)
28 }
29 pub fn rgba(&self) -> String {
31 let fa = (self.a as f32) / 255.0;
32 format!("rgba({},{},{},{:.1})", self.r, self.g, self.b, fa)
33 }
34 pub fn opacity(&self) -> f32 {
36 let a = self.a as f32;
37 a / 255.0
38 }
39 pub fn is_zero(&self) -> bool {
41 self.r == 0 && self.g == 0 && self.b == 0 && self.a == 0
42 }
43 pub fn is_transparent(&self) -> bool {
45 self.a == 0
46 }
47 pub fn is_nontransparent(&self) -> bool {
49 self.a == 255
50 }
51 pub fn white() -> Color {
53 (255, 255, 255).into()
54 }
55 pub fn black() -> Color {
57 (0, 0, 0).into()
58 }
59 pub fn transparent() -> Color {
60 (0, 0, 0, 0).into()
61 }
62 pub fn with_alpha(&self, a: u8) -> Color {
64 let mut c = *self;
65 c.a = a;
66 c
67 }
68 pub fn is_light(&self) -> bool {
70 let mut r = self.r as f64;
71 let mut g = self.g as f64;
72 let mut b = self.b as f64;
73 r = r * r * 0.299;
74 g = g * g * 0.587;
75 b = b * b * 0.114;
76 (r + g + b).sqrt() > 127.5
77 }
78}
79
80impl From<(u8, u8, u8)> for Color {
81 fn from(values: (u8, u8, u8)) -> Self {
82 Color {
83 r: values.0,
84 g: values.1,
85 b: values.2,
86 a: 255,
87 }
88 }
89}
90impl From<(u8, u8, u8, u8)> for Color {
91 fn from(values: (u8, u8, u8, u8)) -> Self {
92 Color {
93 r: values.0,
94 g: values.1,
95 b: values.2,
96 a: values.3,
97 }
98 }
99}
100
101fn parse_hex(hex: &str) -> u8 {
102 u8::from_str_radix(hex, 16).unwrap_or_default()
103}
104
105impl From<&str> for Color {
106 fn from(value: &str) -> Self {
107 let mut c = Color::default();
108 if !value.starts_with('#') {
109 return c;
110 }
111 let hex = value.substring(1, value.len());
112 if hex.len() == 3 {
113 c.r = parse_hex(&hex.substring(0, 1).repeat(2));
114 c.g = parse_hex(&hex.substring(1, 2).repeat(2));
115 c.b = parse_hex(&hex.substring(2, 3).repeat(2));
116 } else {
117 c.r = parse_hex(hex.substring(0, 2));
118 c.g = parse_hex(hex.substring(2, 4));
119 c.b = parse_hex(hex.substring(4, 6));
120 }
121 c.a = 255;
122 c
123 }
124}
125
126pub(crate) fn get_color(colors: &[Color], index: usize) -> Color {
127 let i = index % colors.len();
128 *colors.get(i).unwrap_or_else(|| &colors[0])
129}
130
131#[cfg(test)]
132mod tests {
133 use super::Color;
134 use pretty_assertions::assert_eq;
135 #[test]
136 fn color_hex() {
137 let mut c: Color = (200, 200, 200).into();
138 assert_eq!("#C8C8C8", c.hex());
139
140 c = (51, 51, 51).into();
141 assert_eq!("#333333", c.hex());
142 }
143 #[test]
144 fn color_rgba() {
145 let mut c: Color = (200, 200, 200).into();
146 assert_eq!("rgba(200,200,200,1.0)", c.rgba());
147 c = (51, 51, 51, 51).into();
148 assert_eq!("rgba(51,51,51,0.2)", c.rgba());
149 }
150 #[test]
151 fn color_opacity() {
152 let mut c: Color = (200, 200, 200).into();
153 assert_eq!(1.0, c.opacity());
154 c = (51, 51, 51, 51).into();
155 assert_eq!(0.2, c.opacity());
156 }
157 #[test]
158 fn color_is_zero() {
159 let mut c: Color = (200, 200, 200).into();
160 assert!(!c.is_zero());
161 c = (0, 0, 0, 0).into();
162 assert!(c.is_zero());
163 }
164 #[test]
165 fn color_is_transparent() {
166 let mut c: Color = (200, 200, 200).into();
167 assert!(!c.is_transparent());
168 assert!(c.is_nontransparent());
169 c = (200, 200, 200, 0).into();
170 assert!(c.is_transparent());
171 c = (200, 200, 200, 100).into();
172 assert!(!c.is_nontransparent());
173 }
174 #[test]
175 fn color_static() {
176 assert_eq!("rgba(255,255,255,1.0)", Color::white().rgba());
177 assert_eq!("rgba(0,0,0,1.0)", Color::black().rgba());
178 }
179
180 #[test]
181 fn color_with_alpha() {
182 let mut c = Color::white();
183 assert_eq!("rgba(255,255,255,1.0)", c.rgba());
184 c = c.with_alpha(51);
185 assert_eq!("rgba(255,255,255,0.2)", c.rgba());
186 }
187}