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: Ansi256Color = 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]);
132