1use alacritty_terminal::{ansi::Color as AnsiColor, term::color::Rgb as AlacRgb};
2use gpui::color::Color;
3use theme::TerminalStyle;
4
5///Converts a 2, 8, or 24 bit color ANSI color to the GPUI equivalent
6pub fn convert_color(alac_color: &AnsiColor, style: &TerminalStyle) -> Color {
7 match alac_color {
8 //Named and theme defined colors
9 alacritty_terminal::ansi::Color::Named(n) => match n {
10 alacritty_terminal::ansi::NamedColor::Black => style.black,
11 alacritty_terminal::ansi::NamedColor::Red => style.red,
12 alacritty_terminal::ansi::NamedColor::Green => style.green,
13 alacritty_terminal::ansi::NamedColor::Yellow => style.yellow,
14 alacritty_terminal::ansi::NamedColor::Blue => style.blue,
15 alacritty_terminal::ansi::NamedColor::Magenta => style.magenta,
16 alacritty_terminal::ansi::NamedColor::Cyan => style.cyan,
17 alacritty_terminal::ansi::NamedColor::White => style.white,
18 alacritty_terminal::ansi::NamedColor::BrightBlack => style.bright_black,
19 alacritty_terminal::ansi::NamedColor::BrightRed => style.bright_red,
20 alacritty_terminal::ansi::NamedColor::BrightGreen => style.bright_green,
21 alacritty_terminal::ansi::NamedColor::BrightYellow => style.bright_yellow,
22 alacritty_terminal::ansi::NamedColor::BrightBlue => style.bright_blue,
23 alacritty_terminal::ansi::NamedColor::BrightMagenta => style.bright_magenta,
24 alacritty_terminal::ansi::NamedColor::BrightCyan => style.bright_cyan,
25 alacritty_terminal::ansi::NamedColor::BrightWhite => style.bright_white,
26 alacritty_terminal::ansi::NamedColor::Foreground => style.foreground,
27 alacritty_terminal::ansi::NamedColor::Background => style.background,
28 alacritty_terminal::ansi::NamedColor::Cursor => style.cursor,
29 alacritty_terminal::ansi::NamedColor::DimBlack => style.dim_black,
30 alacritty_terminal::ansi::NamedColor::DimRed => style.dim_red,
31 alacritty_terminal::ansi::NamedColor::DimGreen => style.dim_green,
32 alacritty_terminal::ansi::NamedColor::DimYellow => style.dim_yellow,
33 alacritty_terminal::ansi::NamedColor::DimBlue => style.dim_blue,
34 alacritty_terminal::ansi::NamedColor::DimMagenta => style.dim_magenta,
35 alacritty_terminal::ansi::NamedColor::DimCyan => style.dim_cyan,
36 alacritty_terminal::ansi::NamedColor::DimWhite => style.dim_white,
37 alacritty_terminal::ansi::NamedColor::BrightForeground => style.bright_foreground,
38 alacritty_terminal::ansi::NamedColor::DimForeground => style.dim_foreground,
39 },
40 //'True' colors
41 alacritty_terminal::ansi::Color::Spec(rgb) => Color::new(rgb.r, rgb.g, rgb.b, u8::MAX),
42 //8 bit, indexed colors
43 alacritty_terminal::ansi::Color::Indexed(i) => get_color_at_index(&(*i as usize), style),
44 }
45}
46
47///Converts an 8 bit ANSI color to it's GPUI equivalent.
48///Accepts usize for compatibility with the alacritty::Colors interface,
49///Other than that use case, should only be called with values in the [0,255] range
50pub fn get_color_at_index(index: &usize, style: &TerminalStyle) -> Color {
51 match index {
52 //0-15 are the same as the named colors above
53 0 => style.black,
54 1 => style.red,
55 2 => style.green,
56 3 => style.yellow,
57 4 => style.blue,
58 5 => style.magenta,
59 6 => style.cyan,
60 7 => style.white,
61 8 => style.bright_black,
62 9 => style.bright_red,
63 10 => style.bright_green,
64 11 => style.bright_yellow,
65 12 => style.bright_blue,
66 13 => style.bright_magenta,
67 14 => style.bright_cyan,
68 15 => style.bright_white,
69 //16-231 are mapped to their RGB colors on a 0-5 range per channel
70 16..=231 => {
71 let (r, g, b) = rgb_for_index(&(*index as u8)); //Split the index into it's ANSI-RGB components
72 let step = (u8::MAX as f32 / 5.).floor() as u8; //Split the RGB range into 5 chunks, with floor so no overflow
73 Color::new(r * step, g * step, b * step, u8::MAX) //Map the ANSI-RGB components to an RGB color
74 }
75 //232-255 are a 24 step grayscale from black to white
76 232..=255 => {
77 let i = *index as u8 - 232; //Align index to 0..24
78 let step = (u8::MAX as f32 / 24.).floor() as u8; //Split the RGB grayscale values into 24 chunks
79 Color::new(i * step, i * step, i * step, u8::MAX) //Map the ANSI-grayscale components to the RGB-grayscale
80 }
81 //For compatibility with the alacritty::Colors interface
82 256 => style.foreground,
83 257 => style.background,
84 258 => style.cursor,
85 259 => style.dim_black,
86 260 => style.dim_red,
87 261 => style.dim_green,
88 262 => style.dim_yellow,
89 263 => style.dim_blue,
90 264 => style.dim_magenta,
91 265 => style.dim_cyan,
92 266 => style.dim_white,
93 267 => style.bright_foreground,
94 268 => style.black, //'Dim Background', non-standard color
95 _ => Color::new(0, 0, 0, 255),
96 }
97}
98///Generates the rgb channels in [0, 5] for a given index into the 6x6x6 ANSI color cube
99///See: [8 bit ansi color](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit).
100///
101///Wikipedia gives a formula for calculating the index for a given color:
102///
103///index = 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
104///
105///This function does the reverse, calculating the r, g, and b components from a given index.
106fn rgb_for_index(i: &u8) -> (u8, u8, u8) {
107 debug_assert!((&16..=&231).contains(&i));
108 let i = i - 16;
109 let r = (i - (i % 36)) / 36;
110 let g = ((i % 36) - (i % 6)) / 6;
111 let b = (i % 36) % 6;
112 (r, g, b)
113}
114
115//Convenience method to convert from a GPUI color to an alacritty Rgb
116pub fn to_alac_rgb(color: Color) -> AlacRgb {
117 AlacRgb {
118 r: color.r,
119 g: color.g,
120 b: color.g,
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 #[test]
127 fn test_rgb_for_index() {
128 //Test every possible value in the color cube
129 for i in 16..=231 {
130 let (r, g, b) = crate::mappings::colors::rgb_for_index(&(i as u8));
131 assert_eq!(i, 16 + 36 * r + 6 * g + b);
132 }
133 }
134}