os_terminal/
graphic.rs

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