vsvg/
color.rs

1use std::fmt;
2
3#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
4pub struct Color {
5    pub r: u8,
6    pub g: u8,
7    pub b: u8,
8    pub a: u8,
9}
10
11impl Color {
12    pub const BLACK: Self = Self::rgb(0, 0, 0);
13    pub const DARK_GRAY: Self = Self::rgb(96, 96, 96);
14    pub const GRAY: Self = Self::rgb(160, 160, 160);
15    pub const LIGHT_GRAY: Self = Self::rgb(220, 220, 220);
16    pub const WHITE: Self = Self::rgb(255, 255, 255);
17    pub const BROWN: Self = Self::rgb(165, 42, 42);
18    pub const DARK_RED: Self = Self::rgb(0x8B, 0, 0);
19    pub const RED: Self = Self::rgb(255, 0, 0);
20    pub const LIGHT_RED: Self = Self::rgb(255, 128, 128);
21    pub const YELLOW: Self = Self::rgb(255, 255, 0);
22    pub const LIGHT_YELLOW: Self = Self::rgb(255, 255, 0xE0);
23    pub const KHAKI: Self = Self::rgb(240, 230, 140);
24    pub const DARK_GREEN: Self = Self::rgb(0, 0x64, 0);
25    pub const GREEN: Self = Self::rgb(0, 255, 0);
26    pub const LIGHT_GREEN: Self = Self::rgb(0x90, 0xEE, 0x90);
27    pub const DARK_BLUE: Self = Self::rgb(0, 0, 0x8B);
28    pub const BLUE: Self = Self::rgb(0, 0, 255);
29    pub const LIGHT_BLUE: Self = Self::rgb(0xAD, 0xD8, 0xE6);
30    pub const GOLD: Self = Self::rgb(255, 215, 0);
31
32    #[must_use]
33    pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
34        Self { r, g, b, a }
35    }
36
37    #[must_use]
38    pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
39        Self { r, g, b, a: 0xFF }
40    }
41
42    #[must_use]
43    pub const fn gray(v: u8) -> Self {
44        Self {
45            r: v,
46            g: v,
47            b: v,
48            a: 0xFF,
49        }
50    }
51
52    #[allow(clippy::cast_possible_truncation)]
53    #[allow(clippy::cast_sign_loss)]
54    #[must_use]
55    pub fn with_opacity(&self, opacity: f32) -> Self {
56        Self {
57            r: self.r,
58            g: self.g,
59            b: self.b,
60            a: (opacity * 255.0) as u8,
61        }
62    }
63
64    #[must_use]
65    pub const fn to_rgba(&self) -> u32 {
66        self.r as u32 | (self.g as u32) << 8 | (self.b as u32) << 16 | (self.a as u32) << 24
67    }
68
69    #[must_use]
70    pub fn to_rgb_string(&self) -> String {
71        format!("#{:02x}{:02x}{:02x}", self.r, self.g, self.b)
72    }
73
74    #[must_use]
75    pub fn opacity(&self) -> f32 {
76        f32::from(self.a) / 255.0
77    }
78}
79
80impl Default for Color {
81    fn default() -> Self {
82        Self {
83            r: 0,
84            g: 0,
85            b: 0,
86            a: 255,
87        }
88    }
89}
90
91impl fmt::Display for Color {
92    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93        write!(
94            f,
95            "#{:02x}{:02x}{:02x}{:02x}",
96            self.r, self.g, self.b, self.a
97        )
98    }
99}
100
101impl From<[f32; 4]> for Color {
102    #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
103    fn from(value: [f32; 4]) -> Self {
104        Self {
105            r: (value[0].clamp(0., 1.) * 255.0) as u8,
106            g: (value[1].clamp(0., 1.) * 255.0) as u8,
107            b: (value[2].clamp(0., 1.) * 255.0) as u8,
108            a: (value[3].clamp(0., 1.) * 255.0) as u8,
109        }
110    }
111}
112
113impl From<Color> for [f32; 4] {
114    fn from(value: Color) -> Self {
115        [
116            f32::from(value.r) / 255.0,
117            f32::from(value.g) / 255.0,
118            f32::from(value.b) / 255.0,
119            f32::from(value.a) / 255.0,
120        ]
121    }
122}
123
124pub const COLORS: [Color; 19] = [
125    Color::BLACK,
126    Color::DARK_GRAY,
127    Color::GRAY,
128    Color::LIGHT_GRAY,
129    Color::WHITE,
130    Color::BROWN,
131    Color::DARK_RED,
132    Color::RED,
133    Color::LIGHT_RED,
134    Color::YELLOW,
135    Color::LIGHT_YELLOW,
136    Color::KHAKI,
137    Color::DARK_GREEN,
138    Color::GREEN,
139    Color::LIGHT_GREEN,
140    Color::DARK_BLUE,
141    Color::BLUE,
142    Color::LIGHT_BLUE,
143    Color::GOLD,
144];