1pub trait ColorType {
2 type ValueType;
3
4 fn channel(&self, c: usize) -> Option<Self::ValueType>;
5}
6
7#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
9pub struct Color {
10 pub r: u8,
11 pub g: u8,
12 pub b: u8,
13 pub a: u8,
14}
15
16pub enum ColorName {
18 Black,
19 White,
20 Red,
21}
22
23#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
25pub struct ColorI32 {
26 pub r: i32,
27 pub g: i32,
28 pub b: i32,
29}
30
31#[derive(Copy, Clone, Default, PartialEq, Debug)]
33pub struct ColorF64 {
34 pub r: f64,
35 pub g: f64,
36 pub b: f64,
37}
38
39#[derive(Copy, Clone, Default, PartialEq, Eq)]
41pub struct ColorSum {
42 pub r: u32,
43 pub g: u32,
44 pub b: u32,
45 pub a: u32,
46 pub counter: u32,
47}
48
49#[derive(Copy, Clone, PartialEq)]
51pub struct ColorHsv {
52 pub h: f64,
53 pub s: f64,
54 pub v: f64,
55}
56
57impl Color {
58 pub fn new(r: u8, g: u8, b: u8) -> Self {
59 Self::new_rgba(r, g, b, 255)
60 }
61
62 pub fn new_rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
63 Self { r, g, b, a }
64 }
65
66 pub fn color(name: &ColorName) -> Self {
67 match name {
68 ColorName::Black => Self::new(0, 0, 0),
69 ColorName::White => Self::new(255, 255, 255),
70 ColorName::Red => Self::new(255, 0, 0),
71 }
72 }
73
74 pub fn get_palette_color(i: usize) -> Self {
75 match i % 8 {
76 0 => Self::new(216, 51, 74), 1 => Self::new(255, 232, 96), 2 => Self::new(160, 212, 104), 3 => Self::new(72, 207, 173), 4 => Self::new(79, 193, 233), 5 => Self::new(93, 156, 236), 6 => Self::new(128, 103, 183), 7 => Self::new(172, 146, 236), _ => panic!("%"),
86 }
87 }
88
89 pub fn to_color_string(&self) -> String {
90 format!(
91 "rgba({},{},{},{})",
92 self.r,
93 self.g,
94 self.b,
95 self.a as f64 / 255.0
96 )
97 }
98
99 pub fn to_hex_string(&self) -> String {
100 format!("#{:02X}{:02X}{:02X}", self.r, self.g, self.b)
101 }
102
103 pub fn to_color_i32(&self) -> ColorI32 {
104 ColorI32::new(self)
105 }
106
107 #[allow(
108 clippy::many_single_char_names,
109 clippy::float_cmp
110 )]
111 pub fn to_hsv(&self) -> ColorHsv {
112 let r = self.r as f64 / 255.0;
121 let g = self.g as f64 / 255.0;
122 let b = self.b as f64 / 255.0;
123
124 let max = maxf64(r, maxf64(g, b));
125 let min = minf64(r, minf64(g, b));
126 let mut h;
127 let s;
128 let v = max;
129 let d = max - min;
130 s = if max == 0.0 { 0.0 } else { d / max };
131
132 if max == min {
133 h = 0.0; } else {
135 h = match max {
136 k if (k == r) => (g - b) / d + (if g < b { 6.0 } else { 0.0 }),
137 k if (k == g) => (b - r) / d + 2.0,
138 k if (k == b) => (r - g) / d + 4.0,
139 _ => unreachable!(),
140 };
141 h /= 6.0;
142 }
143 return ColorHsv::new(h, s, v);
144
145 fn maxf64(a: f64, b: f64) -> f64 {
146 if a > b {
147 a
148 } else {
149 b
150 }
151 }
152
153 fn minf64(a: f64, b: f64) -> f64 {
154 if a < b {
155 a
156 } else {
157 b
158 }
159 }
160 }
161}
162
163impl ColorType for Color {
164 type ValueType = u8;
165
166 fn channel(&self, c: usize) -> Option<Self::ValueType> {
167 match c {
168 0 => Some(self.r),
169 1 => Some(self.g),
170 2 => Some(self.b),
171 3 => Some(self.a),
172 _ => None,
173 }
174 }
175}
176
177impl ColorI32 {
178 pub fn new(color: &Color) -> Self {
179 Self {
180 r: color.r as i32,
181 g: color.g as i32,
182 b: color.b as i32,
183 }
184 }
185
186 pub fn add(&self, other: &Self) -> Self {
187 Self {
188 r: self.r + other.r,
189 g: self.g + other.g,
190 b: self.b + other.b,
191 }
192 }
193
194 pub fn diff(&self, other: &Self) -> Self {
195 Self {
196 r: self.r - other.r,
197 g: self.g - other.g,
198 b: self.b - other.b,
199 }
200 }
201
202 pub fn to_color(&self) -> Color {
203 assert!(0 <= self.r && self.r < 256);
204 assert!(0 <= self.g && self.g < 256);
205 assert!(0 <= self.b && self.b < 256);
206 Color::new(self.r as u8, self.g as u8, self.b as u8)
207 }
208
209 pub fn absolute(&self) -> i32 {
210 self.r.abs().max(self.g.abs().max(self.b.abs()))
211 }
212}
213
214impl ColorF64 {
215 pub fn new(color: &ColorI32) -> Self {
216 Self {
217 r: color.r as f64,
218 g: color.g as f64,
219 b: color.b as f64,
220 }
221 }
222
223 pub fn magnitude(&self) -> f64 {
224 (self.r * self.r + self.g * self.g + self.b * self.b).sqrt()
225 }
226}
227
228impl ColorHsv {
229 pub fn new(h: f64, s: f64, v: f64) -> Self {
230 Self { h, s, v }
231 }
232}
233
234impl ColorSum {
235 pub fn new() -> Self {
236 Default::default()
237 }
238
239 pub fn add(&mut self, color: &Color) {
240 self.r += color.r as u32;
241 self.g += color.g as u32;
242 self.b += color.b as u32;
243 self.a += color.a as u32;
244 self.counter += 1;
245 }
246
247 pub fn merge(&mut self, color: &ColorSum) {
248 self.r += color.r;
249 self.g += color.g;
250 self.b += color.b;
251 self.a += color.a;
252 self.counter += color.counter;
253 }
254
255 pub fn average(&self) -> Color {
256 Color::new_rgba(
257 (self.r / self.counter) as u8,
258 (self.g / self.counter) as u8,
259 (self.b / self.counter) as u8,
260 (self.a / self.counter) as u8,
261 )
262 }
263
264 pub fn clear(&mut self) {
265 self.r = 0;
266 self.g = 0;
267 self.b = 0;
268 self.a = 0;
269 self.counter = 0;
270 }
271}