pix_engine/
color.rs

1//! [Color] functions for drawing.
2//!
3//! Each [Color] can be constructed with a [Mode]. The default mode and internal
4//! representation is [Rgb] with values ranging from `0..=255` for red, green, blue, and alpha
5//! transparency. [Hsb] and [Hsl] values range from `0.0..=360.0` for hue, `0.0..=100.0` for
6//! saturation and brightness/lightness and `0.0..=1.0` for alpha transparency.
7//!
8//! There are convience macros for flexible construction: [color!], [rgb!], [hsb!] and [hsl!] that
9//! take 1-4 parameters. The number of parameters provided alter how they are interpreted:
10//!
11//! - Providing a single parameter constructs a grayscale color.
12//! - Two parameters constructs a grayscale color with alpha transparency.
13//! - Three parameters are used as `RGB` or `HSB`/`HSL` values.
14//! - Four parameters are used as `RGBB` or `HSb`/`HSL` values with alpha transparency.
15//!
16//! If you're not picky about color, there are the [random](Color::random) and
17//! [`random_alpha`](Color::random_alpha) methods.
18//!
19//! [Color] also implements [`FromStr`](std::str::FromStr) allowing conversion from a 3, 4, 6, or
20//! 8-digit [hexadecimal](https://en.wikipedia.org/wiki/Web_colors) string.
21//!
22//! The [Color] instance stores which [Mode] it was created with, modifying how manipulation
23//! methods are interprted such as [`set_alpha`](Color::set_alpha) taking a range of `0.0..=255.0` or
24//! `0.0..=1.0`. The [Mode] can be changed any time to alter this behavior using
25//! [`set_mode`](Color::set_mode).
26//!
27//! There are also several named color [constants] available in the
28//! [prelude](crate::prelude) matching the [SVG 1.0 Color
29//! Keywords](https://www.w3.org/TR/SVG11/types.html#ColorKeywords).
30//!
31//! [color!]: crate::prelude::color
32//! [rgb!]: crate::prelude::rgb
33//! [hsb!]: crate::prelude::hsb
34//! [hsl!]: crate::prelude::hsl
35//!
36//! # Examples
37//!
38//! Rgb values range from `0..=255` for red, green, blue and alpha transparency.
39//!
40//! ```
41//! use pix_engine::prelude::*;
42//!
43//! let c = rgb!(55); // Grayscale
44//! assert_eq!(c.channels(), [55, 55, 55, 255]);
45//!
46//! let c = rgb!(55, 128); // Grayscale with alpha
47//! assert_eq!(c.channels(), [55, 55, 55, 128]);
48//!
49//! let c = rgb!(128, 0, 55); // Red, Green, Blue
50//! assert_eq!(c.channels(), [128, 0, 55, 255]);
51//!
52//! let c = rgb!(128, 0, 55, 128); // Red, Green, Blue, and Alpha
53//! assert_eq!(c.channels(), [128, 0, 55, 128]);
54//! ```
55//!
56//! `Hsb`/`Hsl` values range from `0.0..=360.0` for hue, `0.0..=100.0` for saturation and
57//! brightness/lightness and `0.0..=1.0` for alpha transparency.
58//!
59//! ```
60//! use pix_engine::prelude::*;
61//!
62//! let c = hsb!(50.0); // Gray
63//! assert_eq!(c.channels(), [128, 128, 128, 255]);
64//!
65//! let c = hsb!(50.0, 0.5); // Gray with alpha
66//! assert_eq!(c.channels(), [128, 128, 128, 128]);
67//!
68//! let c = hsb!(342.0, 100.0, 80.0); // Hue, Saturation, Brightness
69//! assert_eq!(c.channels(), [204, 0, 61, 255]);
70//!
71//! let c = hsb!(342.0, 100.0, 80.0, 0.5); // Hue, Saturation, Brightness, Alpha
72//! assert_eq!(c.channels(), [204, 0, 61, 128]);
73//! ```
74//!
75//! # SVG 1.0 Named color constants
76//!
77//! ```
78//! use pix_engine::prelude::*;
79//!
80//! let c = Color::ALICE_BLUE;
81//! assert_eq!(c.channels(), [240, 248, 255, 255]);
82//!
83//! let c = Color::DARK_ORCHID;
84//! assert_eq!(c.channels(), [153, 50, 204, 255]);
85//! ```
86//!
87//! # From a hexadecimal string
88//!
89//! ```
90//! use pix_engine::prelude::*;
91//! use std::str::FromStr;
92//!
93//! let c = Color::from_str("#F0F")?; // 3-digit Hex string
94//! assert_eq!(c.channels(), [255, 0, 255, 255]);
95//!
96//! let c = Color::from_str("#F0F5")?; // 4-digit Hex string
97//! assert_eq!(c.channels(), [255, 0, 255, 85]);
98//!
99//! let c = Color::from_str("#F0F5BF")?; // 6-digit Hex string
100//! assert_eq!(c.channels(), [240, 245, 191, 255]);
101//!
102//! let c = Color::from_str("#F0F5BF5F")?; // 8-digit Hex string
103//! assert_eq!(c.channels(), [240, 245, 191, 95]);
104//! # Ok::<(), PixError>(())
105//! ```
106
107use crate::random;
108use conversion::{calculate_channels, clamp_levels, convert_levels, maxes};
109#[cfg(feature = "serde")]
110use serde::{Deserialize, Serialize};
111use std::fmt;
112
113pub mod constants;
114pub mod conversion;
115pub mod ops;
116
117/// [Color] mode indicating level interpretation.
118#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
119#[must_use]
120#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
121pub enum Mode {
122    /// Red, Green, Blue, and Alpha
123    Rgb,
124    /// Hue, Saturation, Brightness, and Alpha
125    Hsb,
126    /// Hue, Saturation, Lightness, and Alpha
127    Hsl,
128}
129
130use Mode::{Hsb, Hsl, Rgb};
131
132/// A color represented with a [Mode].
133#[derive(Debug, Copy, Clone)]
134#[must_use]
135#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
136pub struct Color {
137    /// `Color` mode.
138    mode: Mode,
139    /// RGB values ranging from `0..=255`.
140    channels: [u8; 4],
141}
142
143/// Constructs a [Color] with `red`, `green`, `blue` and optional `alpha`.
144///
145/// # Examples
146///
147/// ```
148/// # use pix_engine::prelude::*;
149/// let c = color!(128); // Gray
150/// assert_eq!(c.channels(), [128, 128, 128, 255]);
151///
152/// let c = color!(128, 64); // Gray with alpha
153/// assert_eq!(c.channels(), [128, 128, 128, 64]);
154///
155/// let c = color!(128, 64, 0); // Red, Green, Blue
156/// assert_eq!(c.channels(), [128, 64, 0, 255]);
157///
158/// let c = color!(128, 64, 128, 128); // Red, Green, Blue, Alpha
159/// assert_eq!(c.channels(), [128, 64, 128, 128]);
160/// ```
161#[macro_export]
162macro_rules! color {
163    ($gray:expr) => {
164        rgb!($gray)
165    };
166    ($gray:expr, $a:expr$(,)?) => {
167        rgb!($gray, $a)
168    };
169    ($r:expr, $g:expr, $b:expr$(,)?) => {
170        rgb!($r, $g, $b)
171    };
172    ($r:expr, $g:expr, $b:expr, $a:expr$(,)?) => {
173        rgb!($r, $g, $b, $a)
174    };
175}
176
177/// Constructs a [Color] with `red`, `green`, `blue` and optional `alpha`.
178///
179/// Alias for [color!].
180///
181/// [color!]: crate::prelude::color
182///
183/// # Examples
184///
185/// ```
186/// # use pix_engine::prelude::*;
187/// let c = rgb!(128); // Gray
188/// assert_eq!(c.channels(), [128, 128, 128, 255]);
189///
190/// let c = rgb!(128, 64); // Gray with alpha
191/// assert_eq!(c.channels(), [128, 128, 128, 64]);
192///
193/// let c = rgb!(128, 64, 0); // Red, Green, Blue
194/// assert_eq!(c.channels(), [128, 64, 0, 255]);
195///
196/// let c = rgb!(128, 64, 128, 128); // Red, Green, Blue, Alpha
197/// assert_eq!(c.channels(), [128, 64, 128, 128]);
198/// ```
199#[doc(alias = "color")]
200#[macro_export]
201macro_rules! rgb {
202    ($gray:expr) => {
203        rgb!($gray, $gray, $gray)
204    };
205    ($gray:expr, $a:expr$(,)?) => {
206        rgb!($gray, $gray, $gray, $a)
207    };
208    ($r:expr, $g:expr, $b:expr$(,)?) => {
209        rgb!($r, $g, $b, 255)
210    };
211    ($r:expr, $g:expr, $b:expr, $a:expr$(,)?) => {
212        $crate::prelude::Color::rgba($r, $g, $b, $a)
213    };
214}
215
216/// Constructs a [Color] with `hue`, `saturation`, `brightness` and optional `alpha`.
217///
218/// # Examples
219///
220/// ```
221/// # use pix_engine::prelude::*;
222/// let c = hsb!(50.0); // Gray
223/// assert_eq!(c.channels(), [128, 128, 128, 255]);
224///
225/// let c = hsb!(50.0, 0.5); // Gray with alpha
226/// assert_eq!(c.channels(), [128, 128, 128, 128]);
227///
228/// let c = hsb!(342.0, 100.0, 80.0); // Hue, Saturation, Brightness
229/// assert_eq!(c.channels(), [204, 0, 61, 255]);
230///
231/// let c = hsb!(342.0, 100.0, 80.0, 0.5); // Hue, Saturation, Brightness, Alpha
232/// assert_eq!(c.channels(), [204, 0, 61, 128]);
233/// ```
234#[macro_export]
235macro_rules! hsb {
236    ($gray:expr) => {
237        hsb!(0.0, 0.0, $gray)
238    };
239    ($gray:expr, $a:expr$(,)?) => {
240        hsb!(0.0, 0.0, $gray, $a)
241    };
242    ($h:expr, $s:expr, $b:expr$(,)?) => {
243        hsb!($h, $s, $b, 1.0)
244    };
245    ($h:expr, $s:expr, $b:expr, $a:expr$(,)?) => {
246        $crate::prelude::Color::hsba($h, $s, $b, $a)
247    };
248}
249
250/// Constructs a [Color] with `hue`, `saturation`, `lightness` and optional `alpha`.
251///
252/// # Examples
253///
254/// ```
255/// # use pix_engine::prelude::*;
256/// let c = hsl!(50.0); // Gray
257/// assert_eq!(c.channels(), [128, 128, 128, 255]);
258///
259/// let c = hsl!(50.0, 0.5); // Gray with alpha
260/// assert_eq!(c.channels(), [128, 128, 128, 128]);
261///
262/// let c = hsl!(342.0, 100.0, 80.0); // Hue, Saturation, Lightness
263/// assert_eq!(c.channels(), [255, 153, 184, 255]);
264///
265/// let c = hsl!(342.0, 100.0, 80.0, 0.5); // Hue, Saturation, Lightness, Alpha
266/// assert_eq!(c.channels(), [255, 153, 184, 128]);
267/// ```
268#[macro_export]
269macro_rules! hsl {
270    ($gray:expr) => {
271        hsl!(0.0, 0.0, $gray)
272    };
273    ($gray:expr, $a:expr$(,)?) => {
274        hsl!(0.0, 0.0, $gray, $a)
275    };
276    ($h:expr, $s:expr, $l:expr$(,)?) => {
277        hsl!($h, $s, $l, 1.0)
278    };
279    ($h:expr, $s:expr, $l:expr, $a:expr$(,)?) => {
280        $crate::prelude::Color::hsla($h, $s, $l, $a)
281    };
282}
283
284impl Color {
285    /// Constructs a `Color` with `red`, `green`, `blue` and max `alpha`.
286    ///
287    /// # Example
288    ///
289    /// ```
290    /// # use pix_engine::prelude::*;
291    /// let c = Color::new(0, 0, 128);
292    /// assert_eq!(c.channels(), [0, 0, 128, 255]);
293    /// ```
294    #[inline]
295    pub const fn new(r: u8, g: u8, b: u8) -> Self {
296        Self::rgb(r, g, b)
297    }
298
299    /// Constructs a `Color` with `red`, `green`, `blue` and `alpha`.
300    ///
301    /// # Example
302    ///
303    /// ```
304    /// # use pix_engine::prelude::*;
305    /// let c = Color::new_alpha(0, 0, 128, 50);
306    /// assert_eq!(c.channels(), [0, 0, 128, 50]);
307    /// ```
308    #[inline]
309    pub const fn new_alpha(r: u8, g: u8, b: u8, a: u8) -> Self {
310        Self::rgba(r, g, b, a)
311    }
312
313    /// Constructs a `Color` with the given [Mode] and max alpha.
314    ///
315    /// # Examples
316    ///
317    /// ```
318    /// # use pix_engine::prelude::*;
319    /// let c = Color::with_mode(ColorMode::Rgb, 0, 0, 128);
320    /// assert_eq!(c.channels(), [0, 0, 128, 255]);
321    ///
322    /// let c = Color::with_mode(ColorMode::Hsb, 126.0, 50.0, 100.0);
323    /// assert_eq!(c.channels(), [128, 255, 140, 255]);
324    /// ```
325    pub fn with_mode<T: Into<f64>>(mode: Mode, v1: T, v2: T, v3: T) -> Self {
326        let [_, _, _, alpha_max] = maxes(mode);
327        Self::with_mode_alpha(mode, v1.into(), v2.into(), v3.into(), alpha_max)
328    }
329
330    /// Constructs a `Color` with the given [Mode] and alpha.
331    ///
332    /// # Examples
333    ///
334    /// ```
335    /// # use pix_engine::prelude::*;
336    /// let c = Color::with_mode_alpha(ColorMode::Rgb, 0.0, 0.0, 128.0, 50.0);
337    /// assert_eq!(c.channels(), [0, 0, 128, 50]);
338    ///
339    /// let c = Color::with_mode_alpha(ColorMode::Hsb, 126.0, 50.0, 100.0, 0.8);
340    /// assert_eq!(c.channels(), [128, 255, 140, 204]);
341    /// ```
342    pub fn with_mode_alpha<T: Into<f64>>(mode: Mode, v1: T, v2: T, v3: T, alpha: T) -> Self {
343        let [v1, v2, v3, alpha] = [v1.into(), v2.into(), v3.into(), alpha.into()];
344        let channels = if mode == Rgb {
345            [v1 as u8, v2 as u8, v3 as u8, alpha as u8]
346        } else {
347            // Normalize channels
348            let [v1_max, v2_max, v3_max, alpha_max] = maxes(mode);
349            let levels = clamp_levels([v1 / v1_max, v2 / v2_max, v3 / v3_max, alpha / alpha_max]);
350            // Convert to Rgb
351            let levels = convert_levels(levels, mode, Rgb);
352            calculate_channels(levels)
353        };
354
355        Self { mode, channels }
356    }
357
358    /// Constructs a `Color` with `red`, `green`, `blue` and max `alpha`.
359    ///
360    /// Alias for [Color::new].
361    ///
362    /// # Example
363    ///
364    /// ```
365    /// # use pix_engine::prelude::*;
366    /// let c = Color::rgb(128, 64, 0);
367    /// assert_eq!(c.channels(), [128, 64, 0, 255]);
368    /// ```
369    #[doc(alias = "new")]
370    #[inline]
371    pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
372        Self {
373            mode: Rgb,
374            channels: [r, g, b, 255],
375        }
376    }
377
378    /// Constructs a `Color` with `red`, `green`, `blue` and `alpha`.
379    ///
380    /// Alias for [Color::new_alpha].
381    ///
382    /// # Example
383    ///
384    /// ```
385    /// # use pix_engine::prelude::*;
386    /// let c = Color::rgba(128, 64, 128, 128);
387    /// assert_eq!(c.channels(), [128, 64, 128, 128]);
388    /// ```
389    #[doc(alias = "new_alpha")]
390    #[inline]
391    pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
392        Self {
393            mode: Rgb,
394            channels: [r, g, b, a],
395        }
396    }
397
398    /// Constructs a `Color` with `hue`, `saturation`, `brightness` and max `alpha`.
399    ///
400    /// # Example
401    ///
402    /// ```
403    /// # use pix_engine::prelude::*;
404    /// let c = Color::hsb(126.0, 80.0, 50.0);
405    /// assert_eq!(c.channels(), [25, 128, 36, 255]);
406    /// ```
407    #[inline]
408    pub fn hsb<T: Into<f64>>(h: T, s: T, b: T) -> Self {
409        Self::with_mode(Hsb, h, s, b)
410    }
411
412    /// Constructs a `Color` with `hue`, `saturation`, `brightness` and `alpha`.
413    ///
414    /// # Example
415    ///
416    /// ```
417    /// # use pix_engine::prelude::*;
418    /// let c = Color::hsba(126.0, 80.0, 50.0, 0.5);
419    /// assert_eq!(c.channels(), [25, 128, 36, 128]);
420    /// ```
421    #[inline]
422    pub fn hsba<T: Into<f64>>(h: T, s: T, b: T, a: T) -> Self {
423        Self::with_mode_alpha(Hsb, h, s, b, a)
424    }
425
426    /// Constructs a `Color` with `hue`, `saturation`, `lightness` and max `alpha`.
427    ///
428    /// # Example
429    ///
430    /// ```
431    /// # use pix_engine::prelude::*;
432    /// let c = Color::hsl(126.0, 80.0, 50.0);
433    /// assert_eq!(c.channels(), [25, 230, 46, 255]);
434    /// ```
435    #[inline]
436    pub fn hsl<T: Into<f64>>(h: T, s: T, l: T) -> Self {
437        Self::with_mode(Hsl, h, s, l)
438    }
439
440    /// Constructs a `Color` with `hue`, `saturation`, `lightness` and `alpha`.
441    ///
442    /// # Example
443    ///
444    /// ```
445    /// # use pix_engine::prelude::*;
446    /// let c = Color::hsla(126.0, 80.0, 50.0, 0.5);
447    /// assert_eq!(c.channels(), [25, 230, 46, 128]);
448    /// ```
449    #[inline]
450    pub fn hsla<T: Into<f64>>(h: T, s: T, l: T, a: T) -> Self {
451        Self::with_mode_alpha(Hsl, h, s, l, a)
452    }
453
454    /// Constructs a `Color` with the given [Mode] and alpha using levels ranging from `0.0..=1.0`.
455    ///
456    /// # Example
457    ///
458    /// ```
459    /// # use pix_engine::prelude::*;
460    /// let c = Color::from_levels(ColorMode::Rgb, 0.4, 0.5, 1.0, 0.8);
461    /// assert_eq!(c.channels(), [102, 128, 255, 204]);
462    /// ```
463    pub fn from_levels<T: Into<f64>>(mode: Mode, v1: T, v2: T, v3: T, alpha: T) -> Self {
464        let mut levels = [v1.into(), v2.into(), v3.into(), alpha.into()];
465        for v in &mut levels {
466            *v = (*v).clamp(0.0, 1.0);
467        }
468        Self {
469            mode,
470            channels: calculate_channels(levels),
471        }
472    }
473
474    /// Constructs a random `Color` with `red`, `green`, `blue` and max alpha.
475    ///
476    /// # Example
477    ///
478    /// ```
479    /// # use pix_engine::prelude::*;
480    /// let c = Color::random();
481    /// // `c.channels()` will return something like:
482    /// // [207, 12, 217, 255]
483    /// ```
484    #[inline]
485    pub fn random() -> Self {
486        Self::rgb(random!(255), random!(255), random!(255))
487    }
488
489    /// Constructs a random `Color` with `red`, `green`, `blue` and alpha.
490    ///
491    /// # Example
492    ///
493    /// ```
494    /// # use pix_engine::prelude::*;
495    /// let c = Color::random_alpha();
496    /// // `c.channels()` will return something like:
497    /// // [132, 159, 233, 76]
498    /// ```
499    #[inline]
500    pub fn random_alpha() -> Self {
501        Self::rgba(random!(255), random!(255), random!(255), random!(255))
502    }
503
504    /// Returns the [u32] RGB hexadecimal value of a `Color`.
505    ///
506    /// # Examples
507    ///
508    /// ```
509    /// # use pix_engine::prelude::*;
510    /// let c = Color::rgb(240, 255, 0);
511    /// assert_eq!(c.as_hex(), 0xF0FF00);
512    /// ```
513    #[inline]
514    #[must_use]
515    pub const fn as_hex(&self) -> u32 {
516        let [r, g, b, _] = self.channels();
517        u32::from_be_bytes([0, r, g, b])
518    }
519
520    /// Returns the [u32] RGBA hexadecimal value of a `Color`.
521    ///
522    /// # Examples
523    ///
524    /// ```
525    /// # use pix_engine::prelude::*;
526    /// let c = Color::rgb(240, 255, 0);
527    /// assert_eq!(c.as_hex_alpha(), 0xF0FF00FF);
528    ///
529    /// let c = Color::rgba(240, 255, 0, 128);
530    /// assert_eq!(c.as_hex_alpha(), 0xF0FF0080);
531    /// ```
532    #[inline]
533    #[must_use]
534    pub const fn as_hex_alpha(&self) -> u32 {
535        u32::from_be_bytes(self.channels())
536    }
537
538    /// Returns a list of max values for each color channel based on [Mode].
539    ///
540    /// # Examples
541    ///
542    /// ```
543    /// # use pix_engine::prelude::*;
544    /// let c = Color::rgb(0, 0, 0);
545    /// assert_eq!(c.maxes(), [255.0, 255.0, 255.0, 255.0]);
546    ///
547    /// let c = Color::hsb(0.0, 0.0, 0.0);
548    /// assert_eq!(c.maxes(), [360.0, 100.0, 100.0, 1.0]);
549    ///
550    /// let c = Color::hsl(0.0, 0.0, 0.0);
551    /// assert_eq!(c.maxes(), [360.0, 100.0, 100.0, 1.0]);
552    /// ```
553    #[inline]
554    #[must_use]
555    pub const fn maxes(&self) -> [f64; 4] {
556        maxes(self.mode)
557    }
558
559    /// Returns the `Color` levels for the given [Mode] which range from `0.0..=1.0`.
560    #[inline]
561    #[must_use]
562    pub fn levels(&self) -> [f64; 4] {
563        let [r, g, b, a] = self.channels;
564        let [r_max, g_max, b_max, a_max] = maxes(Rgb);
565        let levels = clamp_levels([
566            f64::from(r) / r_max,
567            f64::from(g) / g_max,
568            f64::from(b) / b_max,
569            f64::from(a) / a_max,
570        ]);
571        // Convert to current mode
572        convert_levels(levels, Rgb, self.mode)
573    }
574
575    /// Set the `Color` levels ranging from `0.0..=1.0` using the current [Mode].
576    ///
577    /// # Example
578    ///
579    /// ```
580    /// # use pix_engine::prelude::*;
581    /// let mut c = Color::rgba(128, 64, 128, 128);
582    /// c.set_levels([1.0, 0.5, 0.4, 1.0]);
583    /// assert_eq!(c.channels(), [255, 128, 102, 255]);
584    /// ```
585    #[inline]
586    pub fn set_levels(&mut self, levels: [f64; 4]) {
587        let levels = clamp_levels(levels);
588        self.update_channels(levels, self.mode);
589    }
590
591    /// Returns the `Color` channels as `[red, green, blue, alpha]` which range from `0..=255`.
592    ///
593    /// # Example
594    ///
595    /// ```
596    /// # use pix_engine::prelude::*;
597    /// let c = Color::rgba(128, 64, 128, 128);
598    /// assert_eq!(c.channels(), [128, 64, 128, 128]);
599    /// ```
600    #[inline]
601    #[must_use]
602    pub const fn channels(&self) -> [u8; 4] {
603        self.channels
604    }
605
606    /// Returns the current color [Mode].
607    ///
608    /// # Examples
609    ///
610    /// ```
611    /// # use pix_engine::prelude::*;
612    /// let c = Color::rgb(100, 0, 0);
613    /// assert_eq!(c.mode(), ColorMode::Rgb);
614    ///
615    /// let c = Color::hsb(100.0, 0.0, 0.0);
616    /// assert_eq!(c.mode(), ColorMode::Hsb);
617    /// ```
618    #[inline]
619    pub const fn mode(&self) -> Mode {
620        self.mode
621    }
622
623    /// Set the color [Mode].
624    ///
625    /// # Example
626    ///
627    /// ```
628    /// # use pix_engine::prelude::*;
629    /// let mut c = Color::rgb(100, 0, 0);
630    /// c.set_mode(ColorMode::Hsb);
631    /// assert_eq!(c.mode(), ColorMode::Hsb);
632    /// ```
633    #[inline]
634    pub fn set_mode(&mut self, mode: Mode) {
635        self.mode = mode;
636    }
637
638    /// Returns the red `Color` channel ranging from `0..=255`.
639    ///
640    /// # Example
641    ///
642    /// ```
643    /// # use pix_engine::prelude::*;
644    /// let c = Color::rgb(100, 0, 0);
645    /// assert_eq!(c.red(), 100);
646    /// ```
647    #[inline]
648    #[must_use]
649    pub const fn red(&self) -> u8 {
650        self.channels[0]
651    }
652
653    /// Set the red `Color` channel ranging from `0..=255`.
654    ///
655    /// # Example
656    ///
657    /// ```
658    /// # use pix_engine::prelude::*;
659    /// let mut c = Color::default();
660    /// assert_eq!(c.channels(), [0, 0, 0, 255]);
661    /// c.set_red(100);
662    /// assert_eq!(c.channels(), [100, 0, 0, 255]);
663    /// ```
664    #[inline]
665    pub fn set_red(&mut self, r: u8) {
666        self.channels[0] = r;
667    }
668
669    /// Returns the green `Color` channel ranging from `0..=255`.
670    ///
671    /// # Example
672    ///
673    /// ```
674    /// # use pix_engine::prelude::*;
675    /// let c = Color::rgb(0, 100, 0);
676    /// assert_eq!(c.green(), 100);
677    /// ```
678    #[inline]
679    #[must_use]
680    pub const fn green(&self) -> u8 {
681        self.channels[1]
682    }
683
684    /// Set the green `Color` channel ranging from `0..=255`.
685    ///
686    /// # Example
687    ///
688    /// ```
689    /// # use pix_engine::prelude::*;
690    /// let mut c = Color::default();
691    /// assert_eq!(c.channels(), [0, 0, 0, 255]);
692    /// c.set_green(100);
693    /// assert_eq!(c.channels(), [0, 100, 0, 255]);
694    /// ```
695    #[inline]
696    pub fn set_green(&mut self, g: u8) {
697        self.channels[1] = g;
698    }
699
700    /// Returns the blue `Color` channel ranging from `0..=255`.
701    ///
702    /// # Example
703    ///
704    /// ```
705    /// # use pix_engine::prelude::*;
706    /// let c = Color::rgb(0, 0, 100);
707    /// assert_eq!(c.blue(), 100);
708    /// ```
709    #[inline]
710    #[must_use]
711    pub const fn blue(&self) -> u8 {
712        self.channels[2]
713    }
714
715    /// Set the blue `Color` channel ranging from `0..=255`.
716    ///
717    /// # Example
718    ///
719    /// ```
720    /// # use pix_engine::prelude::*;
721    /// let mut c = Color::default();
722    /// assert_eq!(c.channels(), [0, 0, 0, 255]);
723    /// c.set_blue(100);
724    /// assert_eq!(c.channels(), [0, 0, 100, 255]);
725    /// ```
726    #[inline]
727    pub fn set_blue(&mut self, b: u8) {
728        self.channels[2] = b;
729    }
730
731    /// Returns the alpha `Color` channel ranging from `0..=255`.
732    ///
733    /// # Examples
734    ///
735    /// ```
736    /// # use pix_engine::prelude::*;
737    /// let c = Color::rgba(0, 0, 0, 100);
738    /// assert_eq!(c.alpha(), 100);
739    /// ```
740    #[inline]
741    #[must_use]
742    pub const fn alpha(&self) -> u8 {
743        self.channels[3]
744    }
745
746    /// Set the alpha `Color` channel ranging from `0..=255`.
747    ///
748    /// # Examples
749    ///
750    /// ```
751    /// # use pix_engine::prelude::*;
752    /// let mut c = Color::default();
753    /// assert_eq!(c.channels(), [0, 0, 0, 255]);
754    /// c.set_alpha(100);
755    /// assert_eq!(c.channels(), [0, 0, 0, 100]);
756    /// ```
757    #[inline]
758    pub fn set_alpha(&mut self, a: u8) {
759        self.channels[3] = a;
760    }
761
762    /// Returns the hue ranging from `0.0..=360.0`.
763    ///
764    /// # Example
765    ///
766    /// ```
767    /// # use pix_engine::prelude::*;
768    /// let c = Color::rgb(0, 100, 0);
769    /// assert_eq!(c.hue(), 120.0);
770    /// ```
771    #[inline]
772    #[must_use]
773    pub fn hue(&self) -> f64 {
774        let maxes = maxes(Hsb);
775        let levels = convert_levels(self.levels(), self.mode, Hsb);
776        levels[0] * maxes[0]
777    }
778
779    /// Set the hue ranging from `0.0..=360.0`.
780    ///
781    /// # Example
782    ///
783    /// ```
784    /// # use pix_engine::prelude::*;
785    /// let mut c = Color::rgb(128, 0, 0);
786    /// assert_eq!(c.channels(), [128, 0, 0, 255]);
787    /// c.set_hue(100.0);
788    /// assert_eq!(c.channels(), [43, 128, 0, 255]);
789    /// ```
790    #[inline]
791    pub fn set_hue<H: Into<f64>>(&mut self, h: H) {
792        let maxes = maxes(Hsb);
793        let mut levels = convert_levels(self.levels(), self.mode, Hsb);
794        levels[0] = h.into() / maxes[0];
795        self.update_channels(levels, Hsb);
796    }
797
798    /// Returns the saturation ranging from `0.0..=100.0`.
799    ///
800    /// # Example
801    ///
802    /// ```
803    /// # use pix_engine::prelude::*;
804    /// let c = Color::rgb(0, 100, 0);
805    /// assert_eq!(c.saturation(), 100.0);
806    /// ```
807    #[inline]
808    #[must_use]
809    pub fn saturation(&self) -> f64 {
810        let maxes = maxes(Hsb);
811        let levels = convert_levels(self.levels(), self.mode, Hsb);
812        levels[1] * maxes[1]
813    }
814
815    /// Set the saturation ranging from `0.0..=100.0`. Defaults to [Hsb] if the
816    /// current mode is not [Hsb] or [Hsl] already.
817    ///
818    /// # Examples
819    ///
820    /// ```
821    /// # use pix_engine::prelude::*;
822    /// let mut c = Color::rgb(128, 0, 0);
823    /// assert_eq!(c.channels(), [128, 0, 0, 255]);
824    /// c.set_saturation(50.0);
825    /// assert_eq!(c.channels(), [128, 64, 64, 255]);
826    ///
827    /// let mut c = Color::rgb(128, 0, 0);
828    /// c.set_mode(ColorMode::Hsl);
829    /// assert_eq!(c.channels(), [128, 0, 0, 255]);
830    /// c.set_saturation(50.0);
831    /// assert_eq!(c.channels(), [96, 32, 32, 255]);
832    /// ```
833    #[inline]
834    pub fn set_saturation<S: Into<f64>>(&mut self, s: S) {
835        let mode = match self.mode {
836            Hsb | Hsl => self.mode,
837            Rgb => Hsb,
838        };
839        let maxes = maxes(mode);
840        let mut levels = convert_levels(self.levels(), self.mode, mode);
841        levels[1] = s.into() / maxes[1];
842        self.update_channels(levels, mode);
843    }
844
845    /// Returns the brightness ranging from `0.0..=100.0`.
846    ///
847    /// # Example
848    ///
849    /// ```
850    /// # use pix_engine::prelude::*;
851    /// let c = Color::rgb(0, 102, 0);
852    /// assert_eq!(c.brightness(), 40.0);
853    /// ```
854    #[inline]
855    #[must_use]
856    pub fn brightness(&self) -> f64 {
857        let maxes = maxes(Hsb);
858        let levels = convert_levels(self.levels(), self.mode, Hsb);
859        levels[2] * maxes[2]
860    }
861
862    /// Set the brightness ranging from `0.0..=100.0`.
863    ///
864    /// # Example
865    ///
866    /// ```
867    /// # use pix_engine::prelude::*;
868    /// let mut c = Color::rgb(128, 0, 0);
869    /// assert_eq!(c.channels(), [128, 0, 0, 255]);
870    /// c.set_brightness(90.0);
871    /// assert_eq!(c.channels(), [230, 0, 0, 255]);
872    /// ```
873    #[inline]
874    pub fn set_brightness<B: Into<f64>>(&mut self, b: B) {
875        let maxes = maxes(Hsb);
876        let mut levels = convert_levels(self.levels(), self.mode, Hsb);
877        levels[2] = b.into() / maxes[2];
878        self.update_channels(levels, Hsb);
879    }
880
881    /// Returns the lightness ranging from `0.0..=100.0`.
882    ///
883    /// # Example
884    ///
885    /// ```
886    /// # use pix_engine::prelude::*;
887    /// let c = Color::rgb(0, 102, 0);
888    /// assert_eq!(c.lightness(), 20.0);
889    /// ```
890    #[inline]
891    #[must_use]
892    pub fn lightness(&self) -> f64 {
893        let maxes = maxes(Hsl);
894        let levels = convert_levels(self.levels(), self.mode, Hsl);
895        levels[2] * maxes[2]
896    }
897
898    /// Set the lightness ranging from `0.0..=100.0`.
899    ///
900    /// # Example
901    ///
902    /// ```
903    /// # use pix_engine::prelude::*;
904    /// let mut c = Color::rgb(128, 0, 0);
905    /// assert_eq!(c.channels(), [128, 0, 0, 255]);
906    /// c.set_lightness(90.0);
907    /// assert_eq!(c.channels(), [255, 204, 204, 255]);
908    /// ```
909    #[inline]
910    pub fn set_lightness<L: Into<f64>>(&mut self, l: L) {
911        let maxes = maxes(Hsl);
912        let mut levels = convert_levels(self.levels(), self.mode, Hsl);
913        levels[2] = l.into() / maxes[2];
914        self.update_channels(levels, Hsl);
915    }
916
917    /// Returns `Color` as a [Vec] of `[red, green, blue, alpha]`.
918    ///
919    /// # Example
920    ///
921    /// ```
922    /// # use pix_engine::prelude::*;
923    /// let c = color!(100, 200, 50);
924    /// assert_eq!(c.to_vec(), vec![100, 200, 50, 255]);
925    /// ```
926    #[inline]
927    #[must_use]
928    pub fn to_vec(self) -> Vec<u8> {
929        self.channels.to_vec()
930    }
931}
932
933impl Default for Color {
934    fn default() -> Self {
935        Self::rgb(0, 0, 0)
936    }
937}
938
939/// Display [Color] as "[r, g, b, a]".
940impl fmt::Display for Color {
941    #[allow(clippy::many_single_char_names)]
942    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
943        let [r, g, b, a] = self.channels();
944        write!(f, "[{r}, {g}, {b}, {a}]")
945    }
946}
947
948#[cfg(test)]
949mod tests {
950    use super::*;
951
952    #[test]
953    fn test_constructors() {
954        let expected = |mode| Color {
955            mode,
956            channels: [0, 0, 0, 255],
957        };
958        assert_eq!(Color::with_mode(Rgb, 0.0, 0.0, 0.0), expected(Rgb));
959        assert_eq!(
960            Color::with_mode_alpha(Rgb, 0.0, 0.0, 0.0, 255.0),
961            expected(Rgb)
962        );
963        assert_eq!(Color::with_mode(Hsb, 0.0, 0.0, 0.0), expected(Hsb));
964        assert_eq!(
965            Color::with_mode_alpha(Hsb, 0.0, 0.0, 0.0, 1.0),
966            expected(Hsb)
967        );
968        assert_eq!(Color::with_mode(Hsl, 0.0, 0.0, 0.0), expected(Hsl));
969        assert_eq!(
970            Color::with_mode_alpha(Hsl, 0.0, 0.0, 0.0, 1.0),
971            expected(Hsl)
972        );
973    }
974}