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}