anstyle_lossy/
palette.rs

1//! Popular color palettes for [`anstyle::AnsiColor`]
2//!
3//! Based on [wikipedia](https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit)
4use anstyle::RgbColor as Rgb;
5
6/// A color palette for rendering 4-bit [`anstyle::AnsiColor`]
7#[allow(clippy::exhaustive_structs)]
8#[derive(Copy, Clone, PartialEq, Eq, Debug)]
9pub struct Palette(pub RawPalette);
10type RawPalette = [Rgb; 16];
11
12impl Palette {
13    /// Look up the [`anstyle::RgbColor`] in the palette
14    pub const fn get(&self, color: anstyle::AnsiColor) -> Rgb {
15        let color = anstyle::Ansi256Color::from_ansi(color);
16        *self.get_ansi256_ref(color)
17    }
18    const fn get_ansi256_ref(&self, color: anstyle::Ansi256Color) -> &Rgb {
19        let index = color.index() as usize;
20        &self.0[index]
21    }
22
23    pub(crate) const fn rgb_from_ansi(&self, color: anstyle::AnsiColor) -> anstyle::RgbColor {
24        self.get(color)
25    }
26
27    pub(crate) const fn rgb_from_index(&self, index: u8) -> Option<anstyle::RgbColor> {
28        let index = index as usize;
29        if index < self.0.len() {
30            Some(self.0[index])
31        } else {
32            None
33        }
34    }
35
36    pub(crate) const fn find_match(&self, color: anstyle::RgbColor) -> anstyle::AnsiColor {
37        let mut best_index = 0;
38        let mut best_distance = crate::distance(color, self.0[best_index]);
39
40        let mut index = best_index + 1;
41        while index < self.0.len() {
42            let distance = crate::distance(color, self.0[index]);
43            if distance < best_distance {
44                best_index = index;
45                best_distance = distance;
46            }
47
48            index += 1;
49        }
50
51        if let Some(color) = anstyle::Ansi256Color(best_index as u8).into_ansi() {
52            color
53        } else {
54            // Panic
55            #[allow(clippy::no_effect)]
56            ["best_index is out of bounds"][best_index];
57            // Make compiler happy
58            anstyle::AnsiColor::Black
59        }
60    }
61}
62
63impl Default for Palette {
64    fn default() -> Self {
65        DEFAULT
66    }
67}
68
69impl std::ops::Index<anstyle::AnsiColor> for Palette {
70    type Output = Rgb;
71
72    #[inline]
73    fn index(&self, color: anstyle::AnsiColor) -> &Rgb {
74        let color = anstyle::Ansi256Color::from_ansi(color);
75        self.get_ansi256_ref(color)
76    }
77}
78
79impl From<RawPalette> for Palette {
80    fn from(raw: RawPalette) -> Self {
81        Self(raw)
82    }
83}
84
85/// Platform-specific default
86#[cfg(not(windows))]
87pub use VGA as DEFAULT;
88
89/// Platform-specific default
90#[cfg(windows)]
91pub use WIN10_CONSOLE as DEFAULT;
92
93/// Typical colors that are used when booting PCs and leaving them in text mode
94pub const VGA: Palette = Palette([
95    Rgb(0, 0, 0),
96    Rgb(170, 0, 0),
97    Rgb(0, 170, 0),
98    Rgb(170, 85, 0),
99    Rgb(0, 0, 170),
100    Rgb(170, 0, 170),
101    Rgb(0, 170, 170),
102    Rgb(170, 170, 170),
103    Rgb(85, 85, 85),
104    Rgb(255, 85, 85),
105    Rgb(85, 255, 85),
106    Rgb(255, 255, 85),
107    Rgb(85, 85, 255),
108    Rgb(255, 85, 255),
109    Rgb(85, 255, 255),
110    Rgb(255, 255, 255),
111]);
112
113/// Campbell theme, used as of Windows 10 version 1709.
114pub const WIN10_CONSOLE: Palette = Palette([
115    Rgb(12, 12, 12),
116    Rgb(197, 15, 31),
117    Rgb(19, 161, 14),
118    Rgb(193, 156, 0),
119    Rgb(0, 55, 218),
120    Rgb(136, 23, 152),
121    Rgb(58, 150, 221),
122    Rgb(204, 204, 204),
123    Rgb(118, 118, 118),
124    Rgb(231, 72, 86),
125    Rgb(22, 198, 12),
126    Rgb(249, 241, 165),
127    Rgb(59, 120, 255),
128    Rgb(180, 0, 158),
129    Rgb(97, 214, 214),
130    Rgb(242, 242, 242),
131]);