1#![warn(
23 missing_docs,
24 clippy::all,
25 clippy::correctness,
26 clippy::suspicious,
27 clippy::style,
28 clippy::complexity,
29 clippy::perf,
30 clippy::pedantic,
31 clippy::cargo,
32 clippy::nursery
33)]
34#![allow(
35 clippy::suboptimal_flops )]
37
38#[cfg(feature = "tiny-skia")]
39mod skia;
40
41#[cfg(feature = "rgb")]
42mod rgb;
43
44use std::fmt::{Display, LowerHex, UpperHex};
45
46#[derive(Debug, Clone, Copy, PartialEq, Default)]
50pub struct Color([f32; 3]); impl Color {
53 #[must_use]
58 pub const fn new(r: f32, g: f32, b: f32) -> Self {
59 Self([r, g, b])
60 }
61
62 #[must_use]
64 pub const fn r(&self) -> f32 {
65 self.0[0]
66 }
67
68 #[must_use]
70 pub const fn g(&self) -> f32 {
71 self.0[1]
72 }
73
74 #[must_use]
76 pub const fn b(&self) -> f32 {
77 self.0[2]
78 }
79
80 pub fn iter(&self) -> impl Iterator<Item = &f32> {
82 self.0.iter()
83 }
84
85 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut f32> {
90 self.0.iter_mut()
91 }
92
93 #[must_use]
98 pub fn map(self, f: impl FnMut(f32) -> f32) -> Self {
99 Self(self.0.map(f))
100 }
101
102 #[must_use]
104 pub fn as_rgb8(&self) -> (u8, u8, u8) {
105 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] let [r, g, b] = self.0.map(|c| (c * 256.0) as u8);
107 (r, g, b)
108 }
109
110 #[must_use]
112 pub fn as_rgb16(&self) -> (u16, u16, u16) {
113 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] let [r, g, b] = self.0.map(|c| (c * 65536.0) as u16);
115 (r, g, b)
116 }
117
118 #[must_use]
121 pub fn from_rgb8((r, g, b): (u8, u8, u8)) -> Self {
122 [r, g, b].map(|c| f32::from(c) / 255.0).into()
123 }
124
125 #[must_use]
128 pub fn from_rgb16((r, g, b): (u16, u16, u16)) -> Self {
129 [r, g, b].map(|c| f32::from(c) / 65535.0).into()
130 }
131
132 #[must_use]
134 pub const fn as_slice(&self) -> &[f32] {
135 &self.0
136 }
137
138 #[must_use]
140 pub fn as_mut_slice(&mut self) -> &mut [f32] {
141 &mut self.0
142 }
143}
144
145impl IntoIterator for Color {
146 type Item = f32;
147 type IntoIter = std::array::IntoIter<f32, 3>;
148
149 fn into_iter(self) -> Self::IntoIter {
150 self.0.into_iter()
151 }
152}
153
154impl AsMut<[f32; 3]> for Color {
155 fn as_mut(&mut self) -> &mut [f32; 3] {
156 &mut self.0
157 }
158}
159
160impl AsMut<[f32]> for Color {
161 fn as_mut(&mut self) -> &mut [f32] {
162 &mut self.0
163 }
164}
165
166impl AsRef<[f32; 3]> for Color {
167 fn as_ref(&self) -> &[f32; 3] {
168 &self.0
169 }
170}
171
172impl AsRef<[f32]> for Color {
173 fn as_ref(&self) -> &[f32] {
174 &self.0
175 }
176}
177
178impl Display for Color {
179 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180 let [r, g, b] = self.0.map(|c| (c * 1e3).round() / 1e3);
181 write!(f, "rgb({r},{g},{b})")
182 }
183}
184
185impl LowerHex for Color {
186 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187 let prefix = if f.alternate() { "0x" } else { "#" };
188 let (r, g, b) = self.as_rgb8();
189 write!(f, "{prefix}{r:02x}{g:02x}{b:02x}")
190 }
191}
192
193impl UpperHex for Color {
194 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
195 let prefix = if f.alternate() { "0x" } else { "#" };
196 let (r, g, b) = self.as_rgb8();
197 write!(f, "{prefix}{r:02X}{g:02X}{b:02X}")
198 }
199}
200
201impl From<[f32; 3]> for Color {
202 fn from(value: [f32; 3]) -> Self {
203 Self(value)
204 }
205}
206
207impl From<(f32, f32, f32)> for Color {
208 fn from((r, g, b): (f32, f32, f32)) -> Self {
209 Self::new(r, g, b)
210 }
211}
212
213impl From<Color> for [f32; 3] {
214 fn from(value: Color) -> Self {
215 value.0
216 }
217}
218
219impl From<Color> for (f32, f32, f32) {
220 fn from(value: Color) -> Self {
221 (value.r(), value.g(), value.b())
222 }
223}
224
225impl Color {
226 #[must_use]
231 pub fn lighter(self, val: f32) -> Self {
232 self.map(|c| val + c * (1.0 - val))
233 }
234
235 #[must_use]
240 pub fn darker(self, val: f32) -> Self {
241 self.map(|c| c * (1.0 - val))
242 }
243
244 #[must_use]
252 pub fn highlight(self, val: f32) -> Self {
253 let (c_max, c_min) = self
254 .0
255 .into_iter()
256 .fold((f32::NEG_INFINITY, f32::INFINITY), |(max, min), c| {
257 (max.max(c), min.min(c))
258 });
259 let lum_x2 = c_max + c_min;
260
261 if lum_x2 > 1.0 {
263 self.darker(val)
264 } else {
265 self.lighter(val)
266 }
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use assert_approx_eq::assert_approx_eq;
273
274 use super::*;
275
276 #[test]
277 fn new() {
278 let color = Color::new(0.2, 0.4, 0.6);
279 assert_eq!(color.0[0], 0.2);
280 assert_eq!(color.0[1], 0.4);
281 assert_eq!(color.0[2], 0.6);
282 }
283
284 #[test]
285 fn r() {
286 let color = Color::new(0.2, 0.4, 0.6);
287 assert_eq!(color.r(), 0.2);
288 }
289
290 #[test]
291 fn g() {
292 let color = Color::new(0.2, 0.4, 0.6);
293 assert_eq!(color.g(), 0.4);
294 }
295
296 #[test]
297 fn b() {
298 let color = Color::new(0.2, 0.4, 0.6);
299 assert_eq!(color.b(), 0.6);
300 }
301
302 #[test]
303 fn iter() {
304 let color = Color::new(0.2, 0.4, 0.6);
305 let mut iter = color.iter();
306
307 assert_eq!(iter.next(), Some(&0.2));
308 assert_eq!(iter.next(), Some(&0.4));
309 assert_eq!(iter.next(), Some(&0.6));
310 assert_eq!(iter.next(), None);
311 }
312
313 #[test]
314 fn iter_mut() {
315 let mut color = Color::new(0.2, 0.4, 0.6);
316 let iter = color.iter_mut();
317 iter.for_each(|c| *c = 1.0 - *c);
318
319 assert_approx_eq!(color.0[0], 0.8);
320 assert_approx_eq!(color.0[1], 0.6);
321 assert_approx_eq!(color.0[2], 0.4);
322 }
323
324 #[test]
325 fn map() {
326 let color = Color::new(0.2, 0.4, 0.6).map(|c| 1.0 - c);
327
328 assert_approx_eq!(color.0[0], 0.8);
329 assert_approx_eq!(color.0[1], 0.6);
330 assert_approx_eq!(color.0[2], 0.4);
331 }
332
333 #[test]
334 fn as_rgb8() {
335 let color = Color::new(0.2, 0.4, 0.6);
336 let (r, g, b) = color.as_rgb8();
337
338 assert_eq!(r, 0x33);
339 assert_eq!(g, 0x66);
340 assert_eq!(b, 0x99);
341 }
342
343 #[test]
344 fn as_rgb16() {
345 let color = Color::new(0.2, 0.4, 0.6);
346 let (r, g, b) = color.as_rgb16();
347
348 assert_eq!(r, 0x3333);
349 assert_eq!(g, 0x6666);
350 assert_eq!(b, 0x9999);
351 }
352
353 #[test]
354 fn from_rgb8() {
355 let rgb = (0x33, 0x66, 0x99);
356 let color = Color::from_rgb8(rgb);
357
358 assert_approx_eq!(color.0[0], 0.2);
359 assert_approx_eq!(color.0[1], 0.4);
360 assert_approx_eq!(color.0[2], 0.6);
361 }
362
363 #[test]
364 fn from_rgb16() {
365 let rgb = (0x3333, 0x6666, 0x9999);
366 let color = Color::from_rgb16(rgb);
367
368 assert_approx_eq!(color.0[0], 0.2);
369 assert_approx_eq!(color.0[1], 0.4);
370 assert_approx_eq!(color.0[2], 0.6);
371 }
372
373 #[test]
374 fn as_slice() {
375 let color = Color::new(0.2, 0.4, 0.6);
376 let slice = color.as_slice();
377
378 assert_eq!(slice.len(), 3);
379 assert_eq!(slice[0], 0.2);
380 assert_eq!(slice[1], 0.4);
381 assert_eq!(slice[2], 0.6);
382 }
383
384 #[test]
385 fn as_mut_slice() {
386 let mut color = Color::new(0.2, 0.4, 0.6);
387 let slice = color.as_mut_slice();
388
389 assert_eq!(slice.len(), 3);
390
391 slice[0] = 0.8;
392 slice[1] = 0.6;
393 slice[2] = 0.4;
394
395 assert_eq!(color.0[0], 0.8);
396 assert_eq!(color.0[1], 0.6);
397 assert_eq!(color.0[2], 0.4);
398 }
399
400 #[test]
401 fn into_iter() {
402 let color = Color::new(0.2, 0.4, 0.6);
403 let iter = color.into_iter();
404 let vec: Vec<_> = iter.collect();
405
406 assert_eq!(vec.len(), 3);
407 assert_eq!(vec[0], 0.2);
408 assert_eq!(vec[1], 0.4);
409 assert_eq!(vec[2], 0.6);
410 }
411
412 #[test]
413 fn as_mut() {
414 let mut color = Color::new(0.2, 0.4, 0.6);
415
416 {
417 let array: &mut [f32; 3] = color.as_mut();
418 assert_eq!(array.len(), 3);
419 array[0] = 0.8;
420 array[1] = 0.6;
421 array[2] = 0.4;
422 }
423
424 assert_eq!(color.0[0], 0.8);
425 assert_eq!(color.0[1], 0.6);
426 assert_eq!(color.0[2], 0.4);
427
428 {
429 let slice: &mut [f32] = color.as_mut();
430 assert_eq!(slice.len(), 3);
431 slice[0] = 0.2;
432 slice[1] = 0.4;
433 slice[2] = 0.6;
434 }
435
436 assert_eq!(color.0[0], 0.2);
437 assert_eq!(color.0[1], 0.4);
438 assert_eq!(color.0[2], 0.6);
439 }
440
441 #[test]
442 fn as_ref() {
443 let color = Color::new(0.2, 0.4, 0.6);
444
445 let array: &[f32; 3] = color.as_ref();
446
447 assert_eq!(array.len(), 3);
448 assert_eq!(array[0], 0.2);
449 assert_eq!(array[1], 0.4);
450 assert_eq!(array[2], 0.6);
451
452 let slice: &[f32] = color.as_ref();
453
454 assert_eq!(slice.len(), 3);
455 assert_eq!(slice[0], 0.2);
456 assert_eq!(slice[1], 0.4);
457 assert_eq!(slice[2], 0.6);
458 }
459
460 #[test]
461 fn fmt() {
462 let color = Color::new(0.6, 0.8, 1.0);
463
464 assert_eq!(format!("{color}"), "rgb(0.6,0.8,1)");
465 assert_eq!(format!("{color:x}"), "#99ccff");
466 assert_eq!(format!("{color:X}"), "#99CCFF");
467 assert_eq!(format!("{color:#x}"), "0x99ccff");
468 assert_eq!(format!("{color:#X}"), "0x99CCFF");
469 }
470
471 #[test]
472 fn from_array() {
473 let array = [0.2, 0.4, 0.6];
474 let color = Color::from(array);
475
476 assert_eq!(color.0[0], 0.2);
477 assert_eq!(color.0[1], 0.4);
478 assert_eq!(color.0[2], 0.6);
479 }
480
481 #[test]
482 fn from_tuple() {
483 let tuple = (0.2, 0.4, 0.6);
484 let color = Color::from(tuple);
485
486 assert_eq!(color.0[0], 0.2);
487 assert_eq!(color.0[1], 0.4);
488 assert_eq!(color.0[2], 0.6);
489 }
490
491 #[test]
492 fn into_array() {
493 let color = Color::new(0.2, 0.4, 0.6);
494 let array = <[f32; 3]>::from(color);
495
496 assert_eq!(array.len(), 3);
497 assert_eq!(array[0], 0.2);
498 assert_eq!(array[1], 0.4);
499 assert_eq!(array[2], 0.6);
500 }
501
502 #[test]
503 fn into_tuple() {
504 let color = Color::new(0.2, 0.4, 0.6);
505 let tuple = <(f32, f32, f32)>::from(color);
506
507 assert_eq!(tuple.0, 0.2);
508 assert_eq!(tuple.1, 0.4);
509 assert_eq!(tuple.2, 0.6);
510 }
511
512 #[test]
513 fn lighter() {
514 let color = Color::new(0.2, 0.4, 0.6).lighter(0.5);
515
516 assert_approx_eq!(color.0[0], 0.6);
517 assert_approx_eq!(color.0[1], 0.7);
518 assert_approx_eq!(color.0[2], 0.8);
519 }
520
521 #[test]
522 fn darker() {
523 let color = Color::new(0.6, 0.8, 1.0).darker(0.5);
524
525 assert_approx_eq!(color.0[0], 0.3);
526 assert_approx_eq!(color.0[1], 0.4);
527 assert_approx_eq!(color.0[2], 0.5);
528 }
529
530 #[test]
531 fn highlight() {
532 let color = Color::new(0.6, 0.8, 1.0).highlight(0.5);
533
534 assert_approx_eq!(color.0[0], 0.3);
535 assert_approx_eq!(color.0[1], 0.4);
536 assert_approx_eq!(color.0[2], 0.5);
537
538 let color = Color::new(0.2, 0.4, 0.6).highlight(0.5);
539
540 assert_approx_eq!(color.0[0], 0.6);
541 assert_approx_eq!(color.0[1], 0.7);
542 assert_approx_eq!(color.0[2], 0.8);
543 }
544}