os_terminal/
graphic.rs

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