1use crate::errors::*;
2use serde::de::{self, Deserializer, Visitor};
3use serde::{Deserialize, Serialize, Serializer};
4use smart_default::SmartDefault;
5use std::fmt;
6use std::ops::Add;
7use std::str::FromStr;
8
9#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
11pub struct Rgba {
12 pub r: u8,
13 pub g: u8,
14 pub b: u8,
15 pub a: u8,
16}
17
18impl Rgba {
19 pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
29 Self { r, g, b, a }
30 }
31
32 pub fn from_hex(hex: u32) -> Self {
36 let [r, g, b, a] = hex.to_be_bytes();
37 Self { r, g, b, a }
38 }
39}
40
41impl Add for Rgba {
42 type Output = Self;
43 fn add(self, rhs: Self) -> Self::Output {
44 Rgba::new(
45 self.r.saturating_add(rhs.r),
46 self.g.saturating_add(rhs.g),
47 self.b.saturating_add(rhs.b),
48 self.a.saturating_add(rhs.a),
49 )
50 }
51}
52
53#[derive(Copy, Clone, Debug, Default)]
55pub struct Hsva {
56 pub h: f64,
57 pub s: f64,
58 pub v: f64,
59 pub a: u8,
60}
61
62impl Hsva {
63 pub fn new(h: f64, s: f64, v: f64, a: u8) -> Self {
73 Self { h, s, v, a }
74 }
75}
76
77impl PartialEq for Hsva {
78 fn eq(&self, other: &Self) -> bool {
79 approx(self.h, other.h)
80 && approx(self.s, other.s)
81 && approx(self.v, other.v)
82 && self.a == other.a
83 }
84}
85
86impl From<Rgba> for Hsva {
87 fn from(rgba: Rgba) -> Self {
88 let r = rgba.r as f64 / 255.0;
89 let g = rgba.g as f64 / 255.0;
90 let b = rgba.b as f64 / 255.0;
91
92 let min = r.min(g.min(b));
93 let max = r.max(g.max(b));
94 let delta = max - min;
95
96 let v = max;
97 let s = match max > 1e-3 {
98 true => delta / max,
99 false => 0.0,
100 };
101 let h = match delta == 0.0 {
102 true => 0.0,
103 false => {
104 if r == max {
105 (g - b) / delta
106 } else if g == max {
107 2.0 + (b - r) / delta
108 } else {
109 4.0 + (r - g) / delta
110 }
111 }
112 };
113 let h2 = ((h * 60.0) + 360.0) % 360.0;
114
115 Self::new(h2, s, v, rgba.a)
116 }
117}
118
119impl From<Hsva> for Rgba {
120 fn from(hsva: Hsva) -> Self {
121 let range = (hsva.h / 60.0) as u8;
122 let c = hsva.v * hsva.s;
123 let x = c * (1.0 - (((hsva.h / 60.0) % 2.0) - 1.0).abs());
124 let m = hsva.v - c;
125
126 let cm_scaled = ((c + m) * 255.0) as u8;
127 let xm_scaled = ((x + m) * 255.0) as u8;
128 let m_scaled = (m * 255.0) as u8;
129
130 match range {
131 0 => Self::new(cm_scaled, xm_scaled, m_scaled, hsva.a),
132 1 => Self::new(xm_scaled, cm_scaled, m_scaled, hsva.a),
133 2 => Self::new(m_scaled, cm_scaled, xm_scaled, hsva.a),
134 3 => Self::new(m_scaled, xm_scaled, cm_scaled, hsva.a),
135 4 => Self::new(xm_scaled, m_scaled, cm_scaled, hsva.a),
136 _ => Self::new(cm_scaled, m_scaled, xm_scaled, hsva.a),
137 }
138 }
139}
140
141impl Add for Hsva {
142 type Output = Self;
143 fn add(self, rhs: Self) -> Self::Output {
144 Hsva::new(
145 (self.h + rhs.h) % 360.,
146 (self.s + rhs.s).clamp(0., 1.),
147 (self.v + rhs.v).clamp(0., 1.),
148 self.a.saturating_add(rhs.a),
149 )
150 }
151}
152
153pub fn approx(a: f64, b: f64) -> bool {
154 if a == b {
155 return true;
156 }
157 let eps = 1e-2;
158 let abs_a = a.abs();
159 let abs_b = b.abs();
160 let diff = (abs_a - abs_b).abs();
161 if a == 0.0 || b == 0.0 || abs_a + abs_b < f64::EPSILON {
162 diff < eps * f64::EPSILON
163 } else {
164 diff / (abs_a + abs_b).min(f64::MAX) < eps
165 }
166}
167
168#[derive(Debug, Clone, Copy, PartialEq, SmartDefault)]
169pub enum Color {
170 #[default]
171 None,
172 Auto,
173 Rgba(Rgba),
174 Hsva(Hsva),
175}
176
177impl Color {
178 pub fn skip_ser(&self) -> bool {
179 matches!(self, Self::None | Self::Auto)
180 }
181}
182
183impl Add for Color {
184 type Output = Color;
185 fn add(self, rhs: Self) -> Self::Output {
186 match (self, rhs) {
187 (x, Self::None | Self::Auto) | (Self::None | Self::Auto, x) => x,
189 (Color::Hsva(hsva1), Color::Hsva(hsva2)) => Color::Hsva(hsva1 + hsva2),
191 (Color::Rgba(rgba1), Color::Rgba(rgba2)) => Color::Rgba(rgba1 + rgba2),
193 (Color::Hsva(hsva), Color::Rgba(rgba)) | (Color::Rgba(rgba), Color::Hsva(hsva)) => {
196 Color::Hsva(hsva + rgba.into())
197 }
198 }
199 }
200}
201
202impl FromStr for Color {
203 type Err = Error;
204
205 fn from_str(color: &str) -> Result<Self, Self::Err> {
206 Ok(if color == "none" || color.is_empty() {
207 Color::None
208 } else if color == "auto" {
209 Color::Auto
210 } else if color.starts_with("hsv:") {
211 let err_msg = || format!("'{color}' is not a valid HSVA color");
212 let color = color.split_at(4).1;
213 let mut components = color.split(':').map(|x| x.parse::<f64>().or_error(err_msg));
214 let h = components.next().or_error(err_msg)??;
215 let s = components.next().or_error(err_msg)??;
216 let v = components.next().or_error(err_msg)??;
217 let a = components.next().unwrap_or(Ok(100.))?;
218 Color::Hsva(Hsva::new(h, s / 100., v / 100., (a / 100. * 255.) as u8))
219 } else if color.starts_with("x:") {
220 let name = color.split_at(2).1;
221 super::xresources::get_color(name)?
222 .or_error(|| format!("color '{name}' not defined in ~/.Xresources"))?
223 .parse()
224 .or_error(|| format!("invalid color definition '{name}'"))?
225 } else {
226 let err_msg = || format!("'{color}' is not a valid RGBA color");
227 let rgb = color.get(1..7).or_error(err_msg)?;
228 let a = color.get(7..9).unwrap_or("FF");
229 Color::Rgba(Rgba::from_hex(
230 (u32::from_str_radix(rgb, 16).or_error(err_msg)? << 8)
231 + u32::from_str_radix(a, 16).or_error(err_msg)?,
232 ))
233 })
234 }
235}
236
237impl Serialize for Color {
238 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
239 where
240 S: Serializer,
241 {
242 let format_rgba =
243 |rgba: Rgba| format!("#{:02X}{:02X}{:02X}{:02X}", rgba.r, rgba.g, rgba.b, rgba.a);
244 match *self {
245 Self::None | Self::Auto => serializer.serialize_none(),
246 Self::Rgba(rgba) => serializer.serialize_str(&format_rgba(rgba)),
247 Self::Hsva(hsva) => serializer.serialize_str(&format_rgba(hsva.into())),
248 }
249 }
250}
251
252impl<'de> Deserialize<'de> for Color {
253 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
254 where
255 D: Deserializer<'de>,
256 {
257 struct ColorVisitor;
258
259 impl Visitor<'_> for ColorVisitor {
260 type Value = Color;
261
262 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
263 formatter.write_str("color")
264 }
265
266 fn visit_str<E>(self, s: &str) -> Result<Color, E>
267 where
268 E: de::Error,
269 {
270 s.parse().serde_error()
271 }
272 }
273
274 deserializer.deserialize_any(ColorVisitor)
275 }
276}