Skip to main content

gpui_xterm/
colors.rs

1use alacritty_terminal::term::color::Colors;
2use alacritty_terminal::vte::ansi::{Color, NamedColor, Rgb};
3use gpui::Hsla;
4
5#[derive(Debug, Clone)]
6pub struct ColorPalette {
7    ansi_colors: [Hsla; 16],
8
9    extended_colors: [Hsla; 256],
10
11    foreground: Hsla,
12
13    background: Hsla,
14
15    cursor: Hsla,
16}
17
18impl Default for ColorPalette {
19    fn default() -> Self {
20        let ansi_colors = [
21            rgb_to_hsla(Rgb {
22                r: 0x00,
23                g: 0x00,
24                b: 0x00,
25            }),
26            rgb_to_hsla(Rgb {
27                r: 0xcc,
28                g: 0x00,
29                b: 0x00,
30            }),
31            rgb_to_hsla(Rgb {
32                r: 0x4e,
33                g: 0x9a,
34                b: 0x06,
35            }),
36            rgb_to_hsla(Rgb {
37                r: 0xc4,
38                g: 0xa0,
39                b: 0x00,
40            }),
41            rgb_to_hsla(Rgb {
42                r: 0x34,
43                g: 0x65,
44                b: 0xa4,
45            }),
46            rgb_to_hsla(Rgb {
47                r: 0x75,
48                g: 0x50,
49                b: 0x7b,
50            }),
51            rgb_to_hsla(Rgb {
52                r: 0x06,
53                g: 0x98,
54                b: 0x9a,
55            }),
56            rgb_to_hsla(Rgb {
57                r: 0xd3,
58                g: 0xd7,
59                b: 0xcf,
60            }),
61            rgb_to_hsla(Rgb {
62                r: 0x55,
63                g: 0x57,
64                b: 0x53,
65            }),
66            rgb_to_hsla(Rgb {
67                r: 0xef,
68                g: 0x29,
69                b: 0x29,
70            }),
71            rgb_to_hsla(Rgb {
72                r: 0x8a,
73                g: 0xe2,
74                b: 0x34,
75            }),
76            rgb_to_hsla(Rgb {
77                r: 0xfc,
78                g: 0xe9,
79                b: 0x4f,
80            }),
81            rgb_to_hsla(Rgb {
82                r: 0x72,
83                g: 0x9f,
84                b: 0xcf,
85            }),
86            rgb_to_hsla(Rgb {
87                r: 0xad,
88                g: 0x7f,
89                b: 0xa8,
90            }),
91            rgb_to_hsla(Rgb {
92                r: 0x34,
93                g: 0xe2,
94                b: 0xe2,
95            }),
96            rgb_to_hsla(Rgb {
97                r: 0xee,
98                g: 0xee,
99                b: 0xec,
100            }),
101        ];
102
103        let mut extended_colors = [Hsla::default(); 256];
104
105        extended_colors[0..16].copy_from_slice(&ansi_colors);
106
107        let mut idx = 16;
108        for r in 0..6 {
109            for g in 0..6 {
110                for b in 0..6 {
111                    let rgb = Rgb {
112                        r: if r == 0 { 0 } else { 55 + r * 40 },
113                        g: if g == 0 { 0 } else { 55 + g * 40 },
114                        b: if b == 0 { 0 } else { 55 + b * 40 },
115                    };
116                    extended_colors[idx] = rgb_to_hsla(rgb);
117                    idx += 1;
118                }
119            }
120        }
121
122        for i in 0..24 {
123            let gray = (8 + i * 10) as u8;
124            extended_colors[232 + i] = rgb_to_hsla(Rgb {
125                r: gray,
126                g: gray,
127                b: gray,
128            });
129        }
130
131        let foreground = rgb_to_hsla(Rgb {
132            r: 0xd4,
133            g: 0xd4,
134            b: 0xd4,
135        });
136        let background = rgb_to_hsla(Rgb {
137            r: 0x1e,
138            g: 0x1e,
139            b: 0x1e,
140        });
141        let cursor = rgb_to_hsla(Rgb {
142            r: 0xff,
143            g: 0xff,
144            b: 0xff,
145        });
146
147        Self {
148            ansi_colors,
149            extended_colors,
150            foreground,
151            background,
152            cursor,
153        }
154    }
155}
156
157impl ColorPalette {
158    pub fn new() -> Self {
159        Self::default()
160    }
161
162    pub fn builder() -> ColorPaletteBuilder {
163        ColorPaletteBuilder::new()
164    }
165
166    pub fn resolve(&self, color: Color, colors: &Colors) -> Hsla {
167        match color {
168            Color::Named(named) => {
169                if let Some(rgb) = colors[named] {
170                    return rgb_to_hsla(rgb);
171                }
172
173                let idx = named as usize;
174                if idx < 16 {
175                    self.ansi_colors[idx]
176                } else {
177                    match named {
178                        NamedColor::Foreground => self.foreground,
179                        NamedColor::Background => self.background,
180                        NamedColor::Cursor => self.cursor,
181                        NamedColor::DimForeground => {
182                            let mut dim = self.foreground;
183                            dim.l *= 0.7;
184                            dim
185                        }
186                        NamedColor::BrightForeground => {
187                            let mut bright = self.foreground;
188                            bright.l = (bright.l * 1.2).min(1.0);
189                            bright
190                        }
191                        NamedColor::DimBlack
192                        | NamedColor::DimRed
193                        | NamedColor::DimGreen
194                        | NamedColor::DimYellow
195                        | NamedColor::DimBlue
196                        | NamedColor::DimMagenta
197                        | NamedColor::DimCyan
198                        | NamedColor::DimWhite => {
199                            let base_idx = match named {
200                                NamedColor::DimBlack => 0,
201                                NamedColor::DimRed => 1,
202                                NamedColor::DimGreen => 2,
203                                NamedColor::DimYellow => 3,
204                                NamedColor::DimBlue => 4,
205                                NamedColor::DimMagenta => 5,
206                                NamedColor::DimCyan => 6,
207                                NamedColor::DimWhite => 7,
208                                _ => 7,
209                            };
210                            let mut dim = self.ansi_colors[base_idx];
211                            dim.l *= 0.7;
212                            dim
213                        }
214                        _ => self.foreground,
215                    }
216                }
217            }
218            Color::Spec(rgb) => rgb_to_hsla(rgb),
219            Color::Indexed(idx) => self.extended_colors[idx as usize],
220        }
221    }
222
223    pub fn ansi_colors(&self) -> &[Hsla; 16] {
224        &self.ansi_colors
225    }
226
227    pub fn extended_colors(&self) -> &[Hsla; 256] {
228        &self.extended_colors
229    }
230
231    pub fn foreground(&self) -> Hsla {
232        self.foreground
233    }
234
235    pub fn background(&self) -> Hsla {
236        self.background
237    }
238
239    pub fn cursor(&self) -> Hsla {
240        self.cursor
241    }
242}
243
244fn rgb_to_hsla(rgb: Rgb) -> Hsla {
245    let r = rgb.r as f32 / 255.0;
246    let g = rgb.g as f32 / 255.0;
247    let b = rgb.b as f32 / 255.0;
248
249    let max = r.max(g).max(b);
250    let min = r.min(g).min(b);
251    let delta = max - min;
252
253    let l = (max + min) / 2.0;
254
255    let s = if delta == 0.0 {
256        0.0
257    } else {
258        delta / (1.0 - (2.0 * l - 1.0).abs())
259    };
260
261    let h = if delta == 0.0 {
262        0.0
263    } else if max == r {
264        60.0 * (((g - b) / delta) % 6.0)
265    } else if max == g {
266        60.0 * (((b - r) / delta) + 2.0)
267    } else {
268        60.0 * (((r - g) / delta) + 4.0)
269    };
270
271    let h = if h < 0.0 { h + 360.0 } else { h } / 360.0;
272
273    Hsla { h, s, l, a: 1.0 }
274}
275
276#[derive(Debug, Clone)]
277pub struct ColorPaletteBuilder {
278    palette: ColorPalette,
279}
280
281impl Default for ColorPaletteBuilder {
282    fn default() -> Self {
283        Self::new()
284    }
285}
286
287impl ColorPaletteBuilder {
288    pub fn new() -> Self {
289        Self {
290            palette: ColorPalette::default(),
291        }
292    }
293
294    pub fn background(mut self, r: u8, g: u8, b: u8) -> Self {
295        self.palette.background = rgb_to_hsla(Rgb { r, g, b });
296        self
297    }
298
299    pub fn foreground(mut self, r: u8, g: u8, b: u8) -> Self {
300        self.palette.foreground = rgb_to_hsla(Rgb { r, g, b });
301        self
302    }
303
304    pub fn cursor(mut self, r: u8, g: u8, b: u8) -> Self {
305        self.palette.cursor = rgb_to_hsla(Rgb { r, g, b });
306        self
307    }
308
309    pub fn black(mut self, r: u8, g: u8, b: u8) -> Self {
310        self.set_ansi_color(0, r, g, b);
311        self
312    }
313
314    pub fn red(mut self, r: u8, g: u8, b: u8) -> Self {
315        self.set_ansi_color(1, r, g, b);
316        self
317    }
318
319    pub fn green(mut self, r: u8, g: u8, b: u8) -> Self {
320        self.set_ansi_color(2, r, g, b);
321        self
322    }
323
324    pub fn yellow(mut self, r: u8, g: u8, b: u8) -> Self {
325        self.set_ansi_color(3, r, g, b);
326        self
327    }
328
329    pub fn blue(mut self, r: u8, g: u8, b: u8) -> Self {
330        self.set_ansi_color(4, r, g, b);
331        self
332    }
333
334    pub fn magenta(mut self, r: u8, g: u8, b: u8) -> Self {
335        self.set_ansi_color(5, r, g, b);
336        self
337    }
338
339    pub fn cyan(mut self, r: u8, g: u8, b: u8) -> Self {
340        self.set_ansi_color(6, r, g, b);
341        self
342    }
343
344    pub fn white(mut self, r: u8, g: u8, b: u8) -> Self {
345        self.set_ansi_color(7, r, g, b);
346        self
347    }
348
349    pub fn bright_black(mut self, r: u8, g: u8, b: u8) -> Self {
350        self.set_ansi_color(8, r, g, b);
351        self
352    }
353
354    pub fn bright_red(mut self, r: u8, g: u8, b: u8) -> Self {
355        self.set_ansi_color(9, r, g, b);
356        self
357    }
358
359    pub fn bright_green(mut self, r: u8, g: u8, b: u8) -> Self {
360        self.set_ansi_color(10, r, g, b);
361        self
362    }
363
364    pub fn bright_yellow(mut self, r: u8, g: u8, b: u8) -> Self {
365        self.set_ansi_color(11, r, g, b);
366        self
367    }
368
369    pub fn bright_blue(mut self, r: u8, g: u8, b: u8) -> Self {
370        self.set_ansi_color(12, r, g, b);
371        self
372    }
373
374    pub fn bright_magenta(mut self, r: u8, g: u8, b: u8) -> Self {
375        self.set_ansi_color(13, r, g, b);
376        self
377    }
378
379    pub fn bright_cyan(mut self, r: u8, g: u8, b: u8) -> Self {
380        self.set_ansi_color(14, r, g, b);
381        self
382    }
383
384    pub fn bright_white(mut self, r: u8, g: u8, b: u8) -> Self {
385        self.set_ansi_color(15, r, g, b);
386        self
387    }
388
389    fn set_ansi_color(&mut self, idx: usize, r: u8, g: u8, b: u8) {
390        let color = rgb_to_hsla(Rgb { r, g, b });
391        self.palette.ansi_colors[idx] = color;
392        self.palette.extended_colors[idx] = color;
393    }
394
395    pub fn build(self) -> ColorPalette {
396        self.palette
397    }
398}
399
400#[cfg(test)]
401mod tests {
402    use super::*;
403
404    #[test]
405    fn test_rgb_to_hsla_black() {
406        let rgb = Rgb { r: 0, g: 0, b: 0 };
407        let hsla = rgb_to_hsla(rgb);
408        assert_eq!(hsla.l, 0.0);
409        assert_eq!(hsla.s, 0.0);
410        assert_eq!(hsla.a, 1.0);
411    }
412
413    #[test]
414    fn test_rgb_to_hsla_white() {
415        let rgb = Rgb {
416            r: 255,
417            g: 255,
418            b: 255,
419        };
420        let hsla = rgb_to_hsla(rgb);
421        assert_eq!(hsla.l, 1.0);
422        assert_eq!(hsla.s, 0.0);
423        assert_eq!(hsla.a, 1.0);
424    }
425
426    #[test]
427    fn test_rgb_to_hsla_red() {
428        let rgb = Rgb { r: 255, g: 0, b: 0 };
429        let hsla = rgb_to_hsla(rgb);
430        assert_eq!(hsla.h, 0.0);
431        assert_eq!(hsla.s, 1.0);
432        assert_eq!(hsla.a, 1.0);
433    }
434
435    #[test]
436    fn test_color_palette_default() {
437        let palette = ColorPalette::default();
438        assert_eq!(palette.ansi_colors.len(), 16);
439        assert_eq!(palette.extended_colors.len(), 256);
440    }
441
442    #[test]
443    fn test_resolve_named_color() {
444        use alacritty_terminal::vte::ansi::NamedColor;
445
446        let palette = ColorPalette::new();
447        let colors = Colors::default();
448        let hsla = palette.resolve(Color::Named(NamedColor::Red), &colors);
449        assert!(hsla.a > 0.0);
450    }
451
452    #[test]
453    fn test_resolve_indexed_color() {
454        let palette = ColorPalette::new();
455        let colors = Colors::default();
456        let hsla = palette.resolve(Color::Indexed(42), &colors);
457        assert_eq!(hsla.a, 1.0);
458    }
459
460    #[test]
461    fn test_resolve_spec_color() {
462        let palette = ColorPalette::new();
463        let colors = Colors::default();
464        let rgb = Rgb {
465            r: 128,
466            g: 64,
467            b: 192,
468        };
469        let hsla = palette.resolve(Color::Spec(rgb), &colors);
470        assert_eq!(hsla.a, 1.0);
471    }
472}