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];