color.rs

  1#![allow(dead_code)]
  2
  3use serde::de::{self, Deserialize, Deserializer, Visitor};
  4use std::fmt;
  5use std::num::ParseIntError;
  6
  7pub fn rgb<C: From<Rgba>>(hex: u32) -> C {
  8    let r = ((hex >> 16) & 0xFF) as f32 / 255.0;
  9    let g = ((hex >> 8) & 0xFF) as f32 / 255.0;
 10    let b = (hex & 0xFF) as f32 / 255.0;
 11    Rgba { r, g, b, a: 1.0 }.into()
 12}
 13
 14pub fn rgba(hex: u32) -> Rgba {
 15    let r = ((hex >> 24) & 0xFF) as f32 / 255.0;
 16    let g = ((hex >> 16) & 0xFF) as f32 / 255.0;
 17    let b = ((hex >> 8) & 0xFF) as f32 / 255.0;
 18    let a = (hex & 0xFF) as f32 / 255.0;
 19    Rgba { r, g, b, a }
 20}
 21
 22#[derive(Clone, Copy, Default, Debug)]
 23pub struct Rgba {
 24    pub r: f32,
 25    pub g: f32,
 26    pub b: f32,
 27    pub a: f32,
 28}
 29
 30impl Rgba {
 31    pub fn blend(&self, other: Rgba) -> Self {
 32        if other.a >= 1.0 {
 33            return other;
 34        } else if other.a <= 0.0 {
 35            return *self;
 36        } else {
 37            return Rgba {
 38                r: (self.r * (1.0 - other.a)) + (other.r * other.a),
 39                g: (self.g * (1.0 - other.a)) + (other.g * other.a),
 40                b: (self.b * (1.0 - other.a)) + (other.b * other.a),
 41                a: self.a,
 42            };
 43        }
 44    }
 45
 46    pub fn to_hex(&self) -> String {
 47        let r = (self.r * 255.0) as u32;
 48        let g = (self.g * 255.0) as u32;
 49        let b = (self.b * 255.0) as u32;
 50        let a = (self.a * 255.0) as u32;
 51        format!("rgba(0x{:02x}{:02x}{:02x}{:02x}).into()", r, g, b, a)
 52    }
 53}
 54
 55struct RgbaVisitor;
 56
 57impl<'de> Visitor<'de> for RgbaVisitor {
 58    type Value = Rgba;
 59
 60    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
 61        formatter.write_str("a string in the format #rrggbb or #rrggbbaa")
 62    }
 63
 64    fn visit_str<E: de::Error>(self, value: &str) -> Result<Rgba, E> {
 65        if value.len() == 7 || value.len() == 9 {
 66            let r = u8::from_str_radix(&value[1..3], 16).unwrap() as f32 / 255.0;
 67            let g = u8::from_str_radix(&value[3..5], 16).unwrap() as f32 / 255.0;
 68            let b = u8::from_str_radix(&value[5..7], 16).unwrap() as f32 / 255.0;
 69            let a = if value.len() == 9 {
 70                u8::from_str_radix(&value[7..9], 16).unwrap() as f32 / 255.0
 71            } else {
 72                1.0
 73            };
 74            Ok(Rgba { r, g, b, a })
 75        } else {
 76            Err(E::custom(
 77                "Bad format for RGBA. Expected #rrggbb or #rrggbbaa.",
 78            ))
 79        }
 80    }
 81}
 82
 83impl<'de> Deserialize<'de> for Rgba {
 84    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
 85        deserializer.deserialize_str(RgbaVisitor)
 86    }
 87}
 88
 89impl From<Hsla> for Rgba {
 90    fn from(color: Hsla) -> Self {
 91        let h = color.h;
 92        let s = color.s;
 93        let l = color.l;
 94
 95        let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
 96        let x = c * (1.0 - ((h * 6.0) % 2.0 - 1.0).abs());
 97        let m = l - c / 2.0;
 98        let cm = c + m;
 99        let xm = x + m;
100
101        let (r, g, b) = match (h * 6.0).floor() as i32 {
102            0 | 6 => (cm, xm, m),
103            1 => (xm, cm, m),
104            2 => (m, cm, xm),
105            3 => (m, xm, cm),
106            4 => (xm, m, cm),
107            _ => (cm, m, xm),
108        };
109
110        Rgba {
111            r,
112            g,
113            b,
114            a: color.a,
115        }
116    }
117}
118
119impl TryFrom<&'_ str> for Rgba {
120    type Error = ParseIntError;
121
122    fn try_from(value: &'_ str) -> Result<Self, Self::Error> {
123        let r = u8::from_str_radix(&value[1..3], 16)? as f32 / 255.0;
124        let g = u8::from_str_radix(&value[3..5], 16)? as f32 / 255.0;
125        let b = u8::from_str_radix(&value[5..7], 16)? as f32 / 255.0;
126        let a = if value.len() > 7 {
127            u8::from_str_radix(&value[7..9], 16)? as f32 / 255.0
128        } else {
129            1.0
130        };
131
132        Ok(Rgba { r, g, b, a })
133    }
134}
135
136#[derive(Default, Copy, Clone, Debug, PartialEq)]
137#[repr(C)]
138pub struct Hsla {
139    pub h: f32,
140    pub s: f32,
141    pub l: f32,
142    pub a: f32,
143}
144
145impl Hsla {
146    pub fn to_rgb(self) -> Rgba {
147        self.into()
148    }
149}
150
151
152impl Eq for Hsla {}
153
154
155pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Hsla {
156    Hsla {
157        h: h.clamp(0., 1.),
158        s: s.clamp(0., 1.),
159        l: l.clamp(0., 1.),
160        a: a.clamp(0., 1.),
161    }
162}
163
164pub fn black() -> Hsla {
165    Hsla {
166        h: 0.,
167        s: 0.,
168        l: 0.,
169        a: 1.,
170    }
171}
172
173pub fn white() -> Hsla {
174    Hsla {
175        h: 0.,
176        s: 0.,
177        l: 1.,
178        a: 1.,
179    }
180}
181
182pub fn red() -> Hsla {
183    Hsla {
184        h: 0.,
185        s: 1.,
186        l: 0.5,
187        a: 1.,
188    }
189}
190
191impl Hsla {
192    /// Returns true if the HSLA color is fully transparent, false otherwise.
193    pub fn is_transparent(&self) -> bool {
194        self.a == 0.0
195    }
196
197    /// Blends `other` on top of `self` based on `other`'s alpha value. The resulting color is a combination of `self`'s and `other`'s colors.
198    ///
199    /// If `other`'s alpha value is 1.0 or greater, `other` color is fully opaque, thus `other` is returned as the output color.
200    /// If `other`'s alpha value is 0.0 or less, `other` color is fully transparent, thus `self` is returned as the output color.
201    /// Else, the output color is calculated as a blend of `self` and `other` based on their weighted alpha values.
202    ///
203    /// Assumptions:
204    /// - Alpha values are contained in the range [0, 1], with 1 as fully opaque and 0 as fully transparent.
205    /// - The relative contributions of `self` and `other` is based on `self`'s alpha value (`self.a`) and `other`'s  alpha value (`other.a`), `self` contributing `self.a * (1.0 - other.a)` and `other` contributing it's own alpha value.
206    /// - RGB color components are contained in the range [0, 1].
207    /// - If `self` and `other` colors are out of the valid range, the blend operation's output and behavior is undefined.
208    pub fn blend(self, other: Hsla) -> Hsla {
209        let alpha = other.a;
210
211        if alpha >= 1.0 {
212            return other;
213        } else if alpha <= 0.0 {
214            return self;
215        } else {
216            let converted_self = Rgba::from(self);
217            let converted_other = Rgba::from(other);
218            let blended_rgb = converted_self.blend(converted_other);
219            return Hsla::from(blended_rgb);
220        }
221    }
222
223    /// Fade out the color by a given factor. This factor should be between 0.0 and 1.0.
224    /// Where 0.0 will leave the color unchanged, and 1.0 will completely fade out the color.
225    pub fn fade_out(&mut self, factor: f32) {
226        self.a *= 1.0 - factor.clamp(0., 1.);
227    }
228}
229
230// impl From<Hsla> for Rgba {
231//     fn from(value: Hsla) -> Self {
232//         let h = value.h;
233//         let s = value.s;
234//         let l = value.l;
235
236//         let c = (1 - |2L - 1|) X s
237//     }
238// }
239
240impl From<Rgba> for Hsla {
241    fn from(color: Rgba) -> Self {
242        let r = color.r;
243        let g = color.g;
244        let b = color.b;
245
246        let max = r.max(g.max(b));
247        let min = r.min(g.min(b));
248        let delta = max - min;
249
250        let l = (max + min) / 2.0;
251        let s = if l == 0.0 || l == 1.0 {
252            0.0
253        } else if l < 0.5 {
254            delta / (2.0 * l)
255        } else {
256            delta / (2.0 - 2.0 * l)
257        };
258
259        let h = if delta == 0.0 {
260            0.0
261        } else if max == r {
262            ((g - b) / delta).rem_euclid(6.0) / 6.0
263        } else if max == g {
264            ((b - r) / delta + 2.0) / 6.0
265        } else {
266            ((r - g) / delta + 4.0) / 6.0
267        };
268
269        Hsla {
270            h,
271            s,
272            l,
273            a: color.a,
274        }
275    }
276}
277
278impl<'de> Deserialize<'de> for Hsla {
279    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
280    where
281        D: Deserializer<'de>,
282    {
283        // First, deserialize it into Rgba
284        let rgba = Rgba::deserialize(deserializer)?;
285
286        // Then, use the From<Rgba> for Hsla implementation to convert it
287        Ok(Hsla::from(rgba))
288    }
289}