1use alacritty_terminal::{
2 term::color::Colors as AlacrittyColors,
3 vte::ansi::{Color, NamedColor, Rgb as AlacrittyRgb},
4};
5
6use std::collections::HashMap;
7
8use crate::{Rgb, Screen};
9
10pub(crate) struct Colors {
11 colors: AlacrittyColors,
12}
13
14impl Colors {
15 pub fn to_rgb(&self, color: Color) -> Rgb {
16 let AlacrittyRgb { r, g, b } = match color {
17 Color::Named(named_color) => {
18 self.colors[named_color as usize].expect("all colors should be defined")
19 }
20 Color::Indexed(idx) => {
21 self.colors[usize::from(idx)].expect("all colors should be defined")
22 }
23 Color::Spec(rgb) => rgb,
24 };
25
26 Rgb { r, g, b }
27 }
28}
29
30impl Default for Colors {
31 fn default() -> Colors {
33 let mut colors = AlacrittyColors::default();
34
35 fill_named(&mut colors);
36 fill_cube(&mut colors);
37 fill_gray_ramp(&mut colors);
38
39 Colors { colors }
40 }
41}
42
43fn fill_named(colors: &mut AlacrittyColors) {
45 colors[NamedColor::Black as usize] = Some("#073642".parse().unwrap());
46 colors[NamedColor::Black] = Some("#073642".parse().unwrap());
47 colors[NamedColor::Red] = Some("#dc322f".parse().unwrap());
48 colors[NamedColor::Green] = Some("#859900".parse().unwrap());
49 colors[NamedColor::Yellow] = Some("#b58900".parse().unwrap());
50 colors[NamedColor::Blue] = Some("#268bd2".parse().unwrap());
51 colors[NamedColor::Magenta] = Some("#d33682".parse().unwrap());
52 colors[NamedColor::Cyan] = Some("#2aa198".parse().unwrap());
53 colors[NamedColor::White] = Some("#eee8d5".parse().unwrap());
54 colors[NamedColor::BrightBlack] = Some("#002b36".parse().unwrap());
55 colors[NamedColor::BrightRed] = Some("#cb4b16".parse().unwrap());
56 colors[NamedColor::BrightGreen] = Some("#586e75".parse().unwrap());
57 colors[NamedColor::BrightYellow] = Some("#657b83".parse().unwrap());
58 colors[NamedColor::BrightBlue] = Some("#839496".parse().unwrap());
59 colors[NamedColor::BrightMagenta] = Some("#6c71c4".parse().unwrap());
60 colors[NamedColor::BrightCyan] = Some("#93a1a1".parse().unwrap());
61 colors[NamedColor::BrightWhite] = Some("#fdf6e3".parse().unwrap());
62 colors[NamedColor::Foreground] = Some("#839496".parse().unwrap());
63 colors[NamedColor::Background] = Some("#002b36".parse().unwrap());
64 colors[NamedColor::Cursor] = Some("#839496".parse().unwrap());
65 colors[NamedColor::DimBlack] = Some("#073642".parse().unwrap());
66 colors[NamedColor::DimRed] = Some("#dc322f".parse().unwrap());
67 colors[NamedColor::DimGreen] = Some("#859900".parse().unwrap());
68 colors[NamedColor::DimYellow] = Some("#b58900".parse().unwrap());
69 colors[NamedColor::DimBlue] = Some("#268bd2".parse().unwrap());
70 colors[NamedColor::DimMagenta] = Some("#d33682".parse().unwrap());
71 colors[NamedColor::DimCyan] = Some("#2aa198".parse().unwrap());
72 colors[NamedColor::DimWhite] = Some("#eee8d5".parse().unwrap());
73 colors[NamedColor::DimForeground] = Some("#839496".parse().unwrap());
74 colors[NamedColor::BrightForeground] = Some("#839496".parse().unwrap());
75}
76
77fn fill_cube(colors: &mut AlacrittyColors) {
78 let mut index = 16usize;
80
81 for r in 0..6 {
83 for g in 0..6 {
84 for b in 0..6 {
85 colors[index] = Some(AlacrittyRgb {
87 r: if r == 0 { 0 } else { r * 40 + 55 },
88 g: if g == 0 { 0 } else { g * 40 + 55 },
89 b: if b == 0 { 0 } else { b * 40 + 55 },
90 });
91 index += 1;
92 }
93 }
94 }
95
96 debug_assert!(index == 232);
97}
98
99fn fill_gray_ramp(colors: &mut AlacrittyColors) {
100 let mut index: usize = 232;
102
103 for i in 0..24 {
105 let value = i * 10 + 8;
106 colors[index] = Some(AlacrittyRgb {
107 r: value,
108 g: value,
109 b: value,
110 });
111 index += 1;
112 }
113
114 debug_assert!(index == 256);
115}
116
117pub(crate) fn most_common_color(screen: &Screen) -> Rgb {
118 use std::hash::{Hash, Hasher};
119
120 #[derive(PartialEq, Eq, Copy, Clone)]
121 struct Rgb_(Rgb);
122
123 impl Hash for Rgb_ {
124 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
125 state.write_u32(
126 (u32::from(self.0.r) << 16) + (u32::from(self.0.g) << 8) + u32::from(self.0.b),
127 );
128 }
129 }
130
131 #[derive(Default)]
132 struct NoHashHasher(u64);
133
134 impl Hasher for NoHashHasher {
135 fn finish(&self) -> u64 {
136 self.0
137 }
138
139 fn write(&mut self, bytes: &[u8]) {
140 for byte in bytes {
141 self.0 <<= 8;
142 self.0 += u64::from(*byte);
143 }
144 }
145 }
146
147 let mut counts = HashMap::<Rgb_, u32, _>::with_capacity_and_hasher(
148 16,
149 std::hash::BuildHasherDefault::<NoHashHasher>::default(),
150 );
151
152 for idx in 0..screen.lines() * screen.columns() {
153 let cell = &screen.cells[usize::from(idx)];
154 let bg = &cell.bg;
155
156 *counts.entry(Rgb_(*bg)).or_insert(0) += 1;
157 }
158
159 counts
160 .iter()
161 .max_by_key(|(_, count)| *count)
162 .map(|(k, _)| k.0)
163 .unwrap_or(Rgb { r: 0, g: 0, b: 0 })
165}