os_terminal/
graphic.rs

1use alloc::boxed::Box;
2use alloc::collections::btree_map::BTreeMap;
3use core::mem::swap;
4use core::ops::{Deref, DerefMut};
5use vte::ansi::Color;
6
7use crate::cell::{Cell, Flags};
8use crate::color::{ColorScheme, Rgb};
9use crate::font::{ContentInfo, FontManager, Rasterized};
10
11pub trait DrawTarget {
12    fn size(&self) -> (usize, usize);
13    fn draw_pixel(&mut self, x: usize, y: usize, color: Rgb);
14}
15
16pub struct Graphic<D: DrawTarget> {
17    graphic: D,
18    pub(crate) color_scheme: ColorScheme,
19    pub(crate) font_manager: Option<Box<dyn FontManager>>,
20    color_cache: BTreeMap<(Rgb, Rgb), ColorCache>,
21}
22
23impl<D: DrawTarget> Deref for Graphic<D> {
24    type Target = D;
25
26    fn deref(&self) -> &Self::Target {
27        &self.graphic
28    }
29}
30
31impl<D: DrawTarget> DerefMut for Graphic<D> {
32    fn deref_mut(&mut self) -> &mut Self::Target {
33        &mut self.graphic
34    }
35}
36
37impl<D: DrawTarget> Graphic<D> {
38    pub fn new(graphic: D) -> Self {
39        Self {
40            graphic,
41            color_scheme: ColorScheme::default(),
42            font_manager: None,
43            color_cache: BTreeMap::new(),
44        }
45    }
46
47    pub fn clear(&mut self, cell: Cell) {
48        let color = self.color_to_rgb(cell.background);
49
50        for y in 0..self.graphic.size().1 {
51            for x in 0..self.graphic.size().0 {
52                self.graphic.draw_pixel(x, y, color);
53            }
54        }
55    }
56
57    pub fn color_to_rgb(&self, color: Color) -> Rgb {
58        match color {
59            Color::Spec(rgb) => (rgb.r, rgb.g, rgb.b),
60            Color::Named(color) => match color as usize {
61                256 => self.color_scheme.foreground,
62                257 => self.color_scheme.background,
63                index => self.color_scheme.ansi_colors[index],
64            },
65            Color::Indexed(index) => {
66                let color_scheme = &self.color_scheme;
67                color_scheme.ansi_colors[index as usize]
68            }
69        }
70    }
71}
72
73impl<D: DrawTarget> Graphic<D> {
74    pub fn write(&mut self, row: usize, col: usize, cell: Cell) {
75        if cell.placeholder {
76            return;
77        }
78
79        let mut foreground = self.color_to_rgb(cell.foreground);
80        let mut background = self.color_to_rgb(cell.background);
81
82        if cell.flags.intersects(Flags::INVERSE | Flags::CURSOR_BLOCK) {
83            swap(&mut foreground, &mut background);
84        }
85
86        if cell.flags.contains(Flags::HIDDEN) {
87            foreground = background;
88        }
89
90        if let Some(font_manager) = self.font_manager.as_mut() {
91            let (font_width, font_height) = font_manager.size();
92            let (x_start, y_start) = (col * font_width, row * font_height);
93
94            let color_cache = self
95                .color_cache
96                .entry((foreground, background))
97                .or_insert_with(|| ColorCache::new(foreground, background));
98
99            let content_info = ContentInfo {
100                content: cell.content,
101                bold: cell.flags.contains(Flags::BOLD),
102                italic: cell.flags.contains(Flags::ITALIC),
103                wide: cell.wide,
104            };
105
106            macro_rules! draw_raster {
107                ($raster:ident) => {
108                    for (y, lines) in $raster.iter().enumerate() {
109                        for (x, &intensity) in lines.iter().enumerate() {
110                            let (r, g, b) = color_cache.0[intensity as usize];
111                            self.graphic.draw_pixel(x_start + x, y_start + y, (r, g, b));
112                        }
113                    }
114                };
115            }
116
117            match font_manager.rasterize(content_info) {
118                Rasterized::Slice(raster) => draw_raster!(raster),
119                Rasterized::Vec(raster) => draw_raster!(raster),
120                Rasterized::Owned(raster) => draw_raster!(raster),
121            }
122
123            if cell.flags.contains(Flags::CURSOR_BEAM) {
124                let (r, g, b) = color_cache.0[0xff];
125                (0..font_height)
126                    .for_each(|y| self.graphic.draw_pixel(x_start, y_start + y, (r, g, b)));
127            }
128
129            if cell
130                .flags
131                .intersects(Flags::UNDERLINE | Flags::CURSOR_UNDERLINE)
132            {
133                let (r, g, b) = color_cache.0[0xff];
134                let y_base = y_start + font_height - 1;
135                (0..font_width)
136                    .for_each(|x| self.graphic.draw_pixel(x_start + x, y_base, (r, g, b)));
137            }
138        }
139    }
140}
141
142struct ColorCache([Rgb; 256]);
143
144impl ColorCache {
145    fn new(foreground: Rgb, background: Rgb) -> Self {
146        let (r_diff, g_diff, b_diff) = (
147            foreground.0 as i32 - background.0 as i32,
148            foreground.1 as i32 - background.1 as i32,
149            foreground.2 as i32 - background.2 as i32,
150        );
151
152        let colors = core::array::from_fn(|intensity| {
153            let weight = intensity as i32;
154            (
155                ((background.0 as i32 + (r_diff * weight / 0xff)).clamp(0, 255)) as u8,
156                ((background.1 as i32 + (g_diff * weight / 0xff)).clamp(0, 255)) as u8,
157                ((background.2 as i32 + (b_diff * weight / 0xff)).clamp(0, 255)) as u8,
158            )
159        });
160
161        Self(colors)
162    }
163}