dreg_core/
style.rs

1#![allow(clippy::unreadable_literal)]
2
3
4
5use std::{fmt, str::FromStr};
6
7
8
9#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
10pub struct Style {
11    pub color_mode: ColorMode,
12    pub fg: Option<Color>,
13    pub bg: Option<Color>,
14    #[cfg(feature = "underline-color")]
15    pub underline_color: Option<Color>,
16    pub add_modifier: Modifier,
17    pub sub_modifier: Modifier,
18}
19
20impl Style {
21    pub const fn new() -> Self {
22        Self {
23            color_mode: ColorMode::overwrite(),
24            fg: None,
25            bg: None,
26            #[cfg(feature = "underline-color")]
27            underline_color: None,
28            add_modifier: Modifier::empty(),
29            sub_modifier: Modifier::empty(),
30        }
31    }
32
33    pub const fn fg(mut self, color: Color) -> Self {
34        self.fg = Some(color);
35        self
36    }
37
38    pub const fn bg(mut self, color: Color) -> Self {
39        self.bg = Some(color);
40        self
41    }
42
43    pub const fn color_mode(mut self, color_mode: ColorMode) -> Self {
44        self.color_mode = color_mode;
45        self
46    }
47
48    pub const fn add_modifier(mut self, modifier: Modifier) -> Self {
49        self.sub_modifier = self.sub_modifier.difference(modifier);
50        self.add_modifier = self.add_modifier.union(modifier);
51        self
52    }
53
54    pub const fn remove_modifier(mut self, modifier: Modifier) -> Self {
55        self.add_modifier = self.add_modifier.difference(modifier);
56        self.sub_modifier = self.sub_modifier.union(modifier);
57        self
58    }
59}
60
61impl Style {
62    pub fn patch<S: Into<Self>>(mut self, other: S) -> Self {
63        let other: Style = other.into();
64        match other.color_mode {
65            ColorMode::Overwrite => {
66                self.fg = other.fg.or(self.fg);
67                self.bg = other.bg.or(self.bg);
68            }
69            ColorMode::Additive => {
70                if let Some(other_fg) = other.fg {
71                    if let Some(self_fg) = self.fg {
72                        let other_rgb = other_fg.as_rgb();
73                        let self_rgb = self_fg.as_rgb();
74
75                        // let r = ((other_rgb.0 as u16) * (self_rgb.0 as u16)).div_ceil(255) as u8;
76                        // let g = ((other_rgb.1 as u16) * (self_rgb.1 as u16)).div_ceil(255) as u8;
77                        // let b = ((other_rgb.2 as u16) * (self_rgb.2 as u16)).div_ceil(255) as u8;
78                        let r = other_rgb.0.saturating_add(self_rgb.0);
79                        let g = other_rgb.1.saturating_add(self_rgb.1);
80                        let b = other_rgb.2.saturating_add(self_rgb.2);
81
82                        self.fg = Some(Color::Rgb(r, g, b));
83                    } else {
84                        self.fg = Some(other_fg);
85                    }
86                }
87                if let Some(other_bg) = other.bg {
88                    if let Some(self_bg) = self.bg {
89                        let other_rgb = other_bg.as_rgb();
90                        let self_rgb = self_bg.as_rgb();
91
92                        let r = other_rgb.0.saturating_add(self_rgb.0);
93                        let g = other_rgb.1.saturating_add(self_rgb.1);
94                        let b = other_rgb.2.saturating_add(self_rgb.2);
95                        // let r = ((other_rgb.0 as u16) * (self_rgb.0 as u16)).div_ceil(255) as u8;
96                        // let g = ((other_rgb.1 as u16) * (self_rgb.1 as u16)).div_ceil(255) as u8;
97                        // let b = ((other_rgb.2 as u16) * (self_rgb.2 as u16)).div_ceil(255) as u8;
98
99                        self.bg = Some(Color::Rgb(r, g, b));
100                    } else {
101                        self.bg = Some(other_bg);
102                    }
103                }
104            }
105            ColorMode::Subtractive => {
106                if let Some(other_fg) = other.fg {
107                    if let Some(self_fg) = self.fg {
108                        let other_rgb = other_fg.as_rgb();
109                        let self_rgb = self_fg.as_rgb();
110                        let r = other_rgb.0.saturating_sub(self_rgb.0);
111                        let g = other_rgb.1.saturating_sub(self_rgb.1);
112                        let b = other_rgb.2.saturating_sub(self_rgb.2);
113
114                        self.fg = Some(Color::Rgb(r, g, b));
115                    } else {
116                        self.fg = Some(other_fg);
117                    }
118                }
119                if let Some(other_bg) = other.bg {
120                    if let Some(self_bg) = self.bg {
121                        let other_rgb = other_bg.as_rgb();
122                        let self_rgb = self_bg.as_rgb();
123                        let r = other_rgb.0.saturating_sub(self_rgb.0);
124                        let g = other_rgb.1.saturating_sub(self_rgb.1);
125                        let b = other_rgb.2.saturating_sub(self_rgb.2);
126
127                        self.bg = Some(Color::Rgb(r, g, b));
128                    } else {
129                        self.bg = Some(other_bg);
130                    }
131                }
132            }
133            ColorMode::Blend => {
134                if let Some(other_fg) = other.fg {
135                    if let Some(self_fg) = self.fg {
136                        let other_rgb = other_fg.as_rgb();
137                        let self_rgb = self_fg.as_rgb();
138                        let r = other_rgb.0.saturating_add(self_rgb.0.saturating_sub(other_rgb.0));
139                        let g = other_rgb.1.saturating_add(self_rgb.1.saturating_sub(other_rgb.1));
140                        let b = other_rgb.2.saturating_add(self_rgb.2.saturating_sub(other_rgb.2));
141
142                        self.fg = Some(Color::Rgb(r, g, b));
143                    } else {
144                        self.fg = Some(other_fg);
145                    }
146                }
147                if let Some(other_bg) = other.bg {
148                    if let Some(self_bg) = self.bg {
149                        let other_rgb = other_bg.as_rgb();
150                        let self_rgb = self_bg.as_rgb();
151                        let r = other_rgb.0.saturating_add(self_rgb.0.saturating_sub(other_rgb.0));
152                        let g = other_rgb.1.saturating_add(self_rgb.1.saturating_sub(other_rgb.1));
153                        let b = other_rgb.2.saturating_add(self_rgb.2.saturating_sub(other_rgb.2));
154
155                        self.bg = Some(Color::Rgb(r, g, b));
156                    } else {
157                        self.bg = Some(other_bg);
158                    }
159                }
160            }
161            ColorMode::Mix => {
162                if let Some(other_fg) = other.fg {
163                    if let Some(self_fg) = self.fg {
164                        let other_rgb = other_fg.as_rgb();
165                        let self_rgb = self_fg.as_rgb();
166                        let r = self_rgb.0.saturating_add(other_rgb.0.saturating_sub(self_rgb.0));
167                        let g = self_rgb.1.saturating_add(other_rgb.1.saturating_sub(self_rgb.1));
168                        let b = self_rgb.2.saturating_add(other_rgb.2.saturating_sub(self_rgb.2));
169
170                        self.fg = Some(Color::Rgb(r, g, b));
171                    } else {
172                        self.fg = Some(other_fg);
173                    }
174                }
175                if let Some(other_bg) = other.bg {
176                    if let Some(self_bg) = self.bg {
177                        let other_rgb = other_bg.as_rgb();
178                        let self_rgb = self_bg.as_rgb();
179                        let r = self_rgb.0.saturating_add(other_rgb.0.saturating_sub(self_rgb.0));
180                        let g = self_rgb.1.saturating_add(other_rgb.1.saturating_sub(self_rgb.1));
181                        let b = self_rgb.2.saturating_add(other_rgb.2.saturating_sub(self_rgb.2));
182
183                        self.bg = Some(Color::Rgb(r, g, b));
184                    } else {
185                        self.bg = Some(other_bg);
186                    }
187                }
188            }
189        }
190
191        #[cfg(feature = "underline-color")]
192        {
193            self.underline_color = other.underline_color.or(self.underline_color);
194        }
195
196        self.add_modifier.remove(other.sub_modifier);
197        self.add_modifier.insert(other.add_modifier);
198        self.sub_modifier.remove(other.add_modifier);
199        self.sub_modifier.insert(other.sub_modifier);
200
201        self
202    }
203}
204
205
206
207// ================================================================================================
208
209
210
211/// The way in which an [`Element`] is rendered to the screen.
212#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
213pub enum ColorMode {
214    /// Ignore the buffer's current contents and overwrite cells with the colors provided to the
215    /// renderer.
216    #[default]
217    Overwrite,
218    /// Add the renderer's colors to the current cells in the buffer.
219    Additive,
220    Subtractive,
221    /// Blend the renderer's colors with the current cells in the buffer. This is absolutely
222    /// necessary for transparent images and overlays.
223    Blend,
224    Mix,
225}
226
227impl ColorMode {
228    pub const fn overwrite() -> Self {
229        Self::Overwrite
230    }
231
232    pub const fn additive() -> Self {
233        Self::Additive
234    }
235
236    pub const fn subtractive() -> Self {
237        Self::Subtractive
238    }
239
240    pub const fn blend() -> Self {
241        Self::Blend
242    }
243
244    pub const fn mix() -> Self {
245        Self::Mix
246    }
247}
248
249
250
251bitflags::bitflags! {
252    /// Modifier changes the way a piece of text is displayed.
253    ///
254    /// They are bitflags so they can easily be composed.
255    ///
256    /// `From<Modifier> for Style` is implemented so you can use `Modifier` anywhere that accepts
257    /// `Into<Style>`.
258    ///
259    /// ## Examples
260    ///
261    /// ```rust
262    /// use dreg::prelude::*;
263    ///
264    /// let m = Modifier::BOLD | Modifier::ITALIC;
265    /// ```
266    #[derive(Clone, Copy, Default, Eq, Hash, PartialEq)]
267    pub struct Modifier: u16 {
268        const BOLD              = 0b0000_0000_0001;
269        const DIM               = 0b0000_0000_0010;
270        const ITALIC            = 0b0000_0000_0100;
271        const UNDERLINED        = 0b0000_0000_1000;
272        const SLOW_BLINK        = 0b0000_0001_0000;
273        const RAPID_BLINK       = 0b0000_0010_0000;
274        const REVERSED          = 0b0000_0100_0000;
275        const HIDDEN            = 0b0000_1000_0000;
276        const CROSSED_OUT       = 0b0001_0000_0000;
277    }
278}
279
280/// Implement the `Debug` trait for `Modifier` manually.
281///
282/// This will avoid printing the empty modifier as 'Borders(0x0)' and instead print it as 'NONE'.
283impl fmt::Debug for Modifier {
284    /// Format the modifier as `NONE` if the modifier is empty or as a list of flags separated by
285    /// `|` otherwise.
286    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
287        if self.is_empty() {
288            return write!(f, "NONE");
289        }
290        write!(f, "{}", self.0)
291    }
292}
293
294
295
296/// ANSI Color
297///
298/// All colors from the [ANSI color table] are supported (though some names are not exactly the
299/// same).
300///
301/// | Color Name     | Color                   | Foreground | Background |
302/// |----------------|-------------------------|------------|------------|
303/// | `black`        | [`Color::Black`]        | 30         | 40         |
304/// | `red`          | [`Color::Red`]          | 31         | 41         |
305/// | `green`        | [`Color::Green`]        | 32         | 42         |
306/// | `yellow`       | [`Color::Yellow`]       | 33         | 43         |
307/// | `blue`         | [`Color::Blue`]         | 34         | 44         |
308/// | `magenta`      | [`Color::Magenta`]      | 35         | 45         |
309/// | `cyan`         | [`Color::Cyan`]         | 36         | 46         |
310/// | `gray`*        | [`Color::Gray`]         | 37         | 47         |
311/// | `darkgray`*    | [`Color::DarkGray`]     | 90         | 100        |
312/// | `lightred`     | [`Color::LightRed`]     | 91         | 101        |
313/// | `lightgreen`   | [`Color::LightGreen`]   | 92         | 102        |
314/// | `lightyellow`  | [`Color::LightYellow`]  | 93         | 103        |
315/// | `lightblue`    | [`Color::LightBlue`]    | 94         | 104        |
316/// | `lightmagenta` | [`Color::LightMagenta`] | 95         | 105        |
317/// | `lightcyan`    | [`Color::LightCyan`]    | 96         | 106        |
318/// | `white`*       | [`Color::White`]        | 97         | 107        |
319///
320/// - `gray` is sometimes called `white` - this is not supported as we use `white` for bright white
321/// - `gray` is sometimes called `silver` - this is supported
322/// - `darkgray` is sometimes called `light black` or `bright black` (both are supported)
323/// - `white` is sometimes called `light white` or `bright white` (both are supported)
324/// - we support `bright` and `light` prefixes for all colors
325/// - we support `-` and `_` and ` ` as separators for all colors
326/// - we support both `gray` and `grey` spellings
327///
328/// `From<Color> for Style` is implemented by creating a style with the foreground color set to the
329/// given color. This allows you to use colors anywhere that accepts `Into<Style>`.
330///
331/// # Example
332///
333/// ```
334/// use std::str::FromStr;
335///
336/// use ratatui::prelude::*;
337///
338/// assert_eq!(Color::from_str("red"), Ok(Color::Red));
339/// assert_eq!("red".parse(), Ok(Color::Red));
340/// assert_eq!("lightred".parse(), Ok(Color::LightRed));
341/// assert_eq!("light red".parse(), Ok(Color::LightRed));
342/// assert_eq!("light-red".parse(), Ok(Color::LightRed));
343/// assert_eq!("light_red".parse(), Ok(Color::LightRed));
344/// assert_eq!("lightRed".parse(), Ok(Color::LightRed));
345/// assert_eq!("bright red".parse(), Ok(Color::LightRed));
346/// assert_eq!("bright-red".parse(), Ok(Color::LightRed));
347/// assert_eq!("silver".parse(), Ok(Color::Gray));
348/// assert_eq!("dark-grey".parse(), Ok(Color::DarkGray));
349/// assert_eq!("dark gray".parse(), Ok(Color::DarkGray));
350/// assert_eq!("light-black".parse(), Ok(Color::DarkGray));
351/// assert_eq!("white".parse(), Ok(Color::White));
352/// assert_eq!("bright white".parse(), Ok(Color::White));
353/// ```
354///
355/// [ANSI color table]: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
356#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
357pub enum Color {
358    /// Resets the foreground or background color
359    #[default]
360    Reset,
361    /// ANSI Color: Black. Foreground: 30, Background: 40
362    Black,
363    /// ANSI Color: Red. Foreground: 31, Background: 41
364    Red,
365    /// ANSI Color: Green. Foreground: 32, Background: 42
366    Green,
367    /// ANSI Color: Yellow. Foreground: 33, Background: 43
368    Yellow,
369    /// ANSI Color: Blue. Foreground: 34, Background: 44
370    Blue,
371    /// ANSI Color: Magenta. Foreground: 35, Background: 45
372    Magenta,
373    /// ANSI Color: Cyan. Foreground: 36, Background: 46
374    Cyan,
375    /// ANSI Color: White. Foreground: 37, Background: 47
376    ///
377    /// Note that this is sometimes called `silver` or `white` but we use `white` for bright white
378    Gray,
379    /// ANSI Color: Bright Black. Foreground: 90, Background: 100
380    ///
381    /// Note that this is sometimes called `light black` or `bright black` but we use `dark gray`
382    DarkGray,
383    /// ANSI Color: Bright Red. Foreground: 91, Background: 101
384    LightRed,
385    /// ANSI Color: Bright Green. Foreground: 92, Background: 102
386    LightGreen,
387    /// ANSI Color: Bright Yellow. Foreground: 93, Background: 103
388    LightYellow,
389    /// ANSI Color: Bright Blue. Foreground: 94, Background: 104
390    LightBlue,
391    /// ANSI Color: Bright Magenta. Foreground: 95, Background: 105
392    LightMagenta,
393    /// ANSI Color: Bright Cyan. Foreground: 96, Background: 106
394    LightCyan,
395    /// ANSI Color: Bright White. Foreground: 97, Background: 107
396    /// Sometimes called `bright white` or `light white` in some terminals
397    White,
398    /// An RGB color.
399    ///
400    /// Note that only terminals that support 24-bit true color will display this correctly.
401    /// Notably versions of Windows Terminal prior to Windows 10 and macOS Terminal.app do not
402    /// support this.
403    ///
404    /// If the terminal does not support true color, code using the  [`TermwizBackend`] will
405    /// fallback to the default text color. Crossterm and Termion do not have this capability and
406    /// the display will be unpredictable (e.g. Terminal.app may display glitched blinking text).
407    /// See <https://github.com/ratatui-org/ratatui/issues/475> for an example of this problem.
408    ///
409    /// See also: <https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit>
410    ///
411    /// [`TermwizBackend`]: crate::backend::TermwizBackend
412    Rgb(u8, u8, u8),
413    /// An 8-bit 256 color.
414    ///
415    /// See also <https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit>
416    Indexed(u8),
417}
418
419impl Color {
420    /// Convert a u32 to a Color
421    ///
422    /// The u32 should be in the format 0x00RRGGBB.
423    pub const fn from_u32(u: u32) -> Self {
424        let r = (u >> 16) as u8;
425        let g = (u >> 8) as u8;
426        let b = u as u8;
427        Self::Rgb(r, g, b)
428    }
429
430    pub fn as_rgb(&self) -> (u8, u8, u8) {
431        match self {
432            Self::Rgb(r, g, b) => (*r, *g, *b),
433            _ => (0, 0, 0),
434        }
435    }
436}
437
438
439
440/// Error type indicating a failure to parse a color string.
441#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
442pub struct ParseColorError;
443
444impl fmt::Display for ParseColorError {
445    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
446        write!(f, "Failed to parse Colors")
447    }
448}
449
450impl std::error::Error for ParseColorError {}
451
452/// Converts a string representation to a `Color` instance.
453///
454/// The `from_str` function attempts to parse the given string and convert it to the corresponding
455/// `Color` variant. It supports named colors, RGB values, and indexed colors. If the string cannot
456/// be parsed, a `ParseColorError` is returned.
457///
458/// See the [`Color`] documentation for more information on the supported color names.
459impl FromStr for Color {
460    type Err = ParseColorError;
461
462    fn from_str(s: &str) -> Result<Self, Self::Err> {
463        Ok(
464            // There is a mix of different color names and formats in the wild.
465            // This is an attempt to support as many as possible.
466            match s
467                .to_lowercase()
468                .replace([' ', '-', '_'], "")
469                .replace("bright", "light")
470                .replace("grey", "gray")
471                .replace("silver", "gray")
472                .replace("lightblack", "darkgray")
473                .replace("lightwhite", "white")
474                .replace("lightgray", "white")
475                .as_ref()
476            {
477                "reset" => Self::Reset,
478                "black" => Self::Black,
479                "red" => Self::Red,
480                "green" => Self::Green,
481                "yellow" => Self::Yellow,
482                "blue" => Self::Blue,
483                "magenta" => Self::Magenta,
484                "cyan" => Self::Cyan,
485                "gray" => Self::Gray,
486                "darkgray" => Self::DarkGray,
487                "lightred" => Self::LightRed,
488                "lightgreen" => Self::LightGreen,
489                "lightyellow" => Self::LightYellow,
490                "lightblue" => Self::LightBlue,
491                "lightmagenta" => Self::LightMagenta,
492                "lightcyan" => Self::LightCyan,
493                "white" => Self::White,
494                _ => {
495                    if let Ok(index) = s.parse::<u8>() {
496                        Self::Indexed(index)
497                    } else if let Some((r, g, b)) = parse_hex_color(s) {
498                        Self::Rgb(r, g, b)
499                    } else {
500                        return Err(ParseColorError);
501                    }
502                }
503            },
504        )
505    }
506}
507
508fn parse_hex_color(input: &str) -> Option<(u8, u8, u8)> {
509    if !input.starts_with('#') || input.len() != 7 {
510        return None;
511    }
512    let r = u8::from_str_radix(input.get(1..3)?, 16).ok()?;
513    let g = u8::from_str_radix(input.get(3..5)?, 16).ok()?;
514    let b = u8::from_str_radix(input.get(5..7)?, 16).ok()?;
515    Some((r, g, b))
516}
517
518impl fmt::Display for Color {
519    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
520        match self {
521            Self::Reset => write!(f, "Reset"),
522            Self::Black => write!(f, "Black"),
523            Self::Red => write!(f, "Red"),
524            Self::Green => write!(f, "Green"),
525            Self::Yellow => write!(f, "Yellow"),
526            Self::Blue => write!(f, "Blue"),
527            Self::Magenta => write!(f, "Magenta"),
528            Self::Cyan => write!(f, "Cyan"),
529            Self::Gray => write!(f, "Gray"),
530            Self::DarkGray => write!(f, "DarkGray"),
531            Self::LightRed => write!(f, "LightRed"),
532            Self::LightGreen => write!(f, "LightGreen"),
533            Self::LightYellow => write!(f, "LightYellow"),
534            Self::LightBlue => write!(f, "LightBlue"),
535            Self::LightMagenta => write!(f, "LightMagenta"),
536            Self::LightCyan => write!(f, "LightCyan"),
537            Self::White => write!(f, "White"),
538            Self::Rgb(r, g, b) => write!(f, "#{r:02X}{g:02X}{b:02X}"),
539            Self::Indexed(i) => write!(f, "{i}"),
540        }
541    }
542}
543
544impl Color {
545    /// Converts a HSL representation to a `Color::Rgb` instance.
546    ///
547    /// The `from_hsl` function converts the Hue, Saturation and Lightness values to a
548    /// corresponding `Color` RGB equivalent.
549    ///
550    /// Hue values should be in the range [0, 360].
551    /// Saturation and L values should be in the range [0, 100].
552    /// Values that are not in the range are clamped to be within the range.
553    ///
554    /// # Examples
555    ///
556    /// ```
557    /// use ratatui::prelude::*;
558    ///
559    /// let color: Color = Color::from_hsl(360.0, 100.0, 100.0);
560    /// assert_eq!(color, Color::Rgb(255, 255, 255));
561    ///
562    /// let color: Color = Color::from_hsl(0.0, 0.0, 0.0);
563    /// assert_eq!(color, Color::Rgb(0, 0, 0));
564    /// ```
565    pub fn from_hsl(h: f64, s: f64, l: f64) -> Self {
566        // Clamp input values to valid ranges
567        let h = h.clamp(0.0, 360.0);
568        let s = s.clamp(0.0, 100.0);
569        let l = l.clamp(0.0, 100.0);
570
571        // Delegate to the function for normalized HSL to RGB conversion
572        normalized_hsl_to_rgb(h / 360.0, s / 100.0, l / 100.0)
573    }
574}
575
576/// Converts normalized HSL (Hue, Saturation, Lightness) values to RGB (Red, Green, Blue) color
577/// representation. H, S, and L values should be in the range [0, 1].
578///
579/// Based on <https://github.com/killercup/hsl-rs/blob/b8a30e11afd75f262e0550725333293805f4ead0/src/lib.rs>
580fn normalized_hsl_to_rgb(hue: f64, saturation: f64, lightness: f64) -> Color {
581    // This function can be made into `const` in the future.
582    // This comment contains the relevant information for making it `const`.
583    //
584    // If it is `const` and made public, users can write the following:
585    //
586    // ```rust
587    // const SLATE_50: Color = normalized_hsl_to_rgb(0.210, 0.40, 0.98);
588    // ```
589    //
590    // For it to be const now, we need `#![feature(const_fn_floating_point_arithmetic)]`
591    // Tracking issue: https://github.com/rust-lang/rust/issues/57241
592    //
593    // We would also need to remove the use of `.round()` in this function, i.e.:
594    //
595    // ```rust
596    // Color::Rgb((r * 255.0) as u8, (g * 255.0) as u8, (b * 255.0) as u8)
597    // ```
598
599    // Initialize RGB components
600    let red: f64;
601    let green: f64;
602    let blue: f64;
603
604    // Check if the color is achromatic (grayscale)
605    if saturation == 0.0 {
606        red = lightness;
607        green = lightness;
608        blue = lightness;
609    } else {
610        // Calculate RGB components for colored cases
611        let q = if lightness < 0.5 {
612            lightness * (1.0 + saturation)
613        } else {
614            lightness + saturation - lightness * saturation
615        };
616        let p = 2.0 * lightness - q;
617        red = hue_to_rgb(p, q, hue + 1.0 / 3.0);
618        green = hue_to_rgb(p, q, hue);
619        blue = hue_to_rgb(p, q, hue - 1.0 / 3.0);
620    }
621
622    // Scale RGB components to the range [0, 255] and create a Color::Rgb instance
623    Color::Rgb(
624        (red * 255.0).round() as u8,
625        (green * 255.0).round() as u8,
626        (blue * 255.0).round() as u8,
627    )
628}
629
630/// Helper function to calculate RGB component for a specific hue value.
631fn hue_to_rgb(p: f64, q: f64, t: f64) -> f64 {
632    // Adjust the hue value to be within the valid range [0, 1]
633    let mut t = t;
634    if t < 0.0 {
635        t += 1.0;
636    }
637    if t > 1.0 {
638        t -= 1.0;
639    }
640
641    // Calculate the RGB component based on the hue value
642    if t < 1.0 / 6.0 {
643        p + (q - p) * 6.0 * t
644    } else if t < 1.0 / 2.0 {
645        q
646    } else if t < 2.0 / 3.0 {
647        p + (q - p) * (2.0 / 3.0 - t) * 6.0
648    } else {
649        p
650    }
651}