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