Skip to main content

embedded_gui/
font.rs

1include!(concat!(env!("OUT_DIR"), "/generated_ascii_3x5.rs"));
2
3#[derive(Clone, Copy, Debug, PartialEq, Eq)]
4pub struct PackedFont {
5    pub first_char: u8,
6    pub advance: u8,
7    pub line_height: u8,
8    pub glyphs: &'static [[u8; 5]],
9}
10
11pub static ASCII_3X5_FONT: PackedFont = PackedFont {
12    first_char: 32,
13    advance: 4,
14    line_height: 6,
15    glyphs: &ASCII_3X5_GLYPHS,
16};
17
18pub static ASCII_4X7_FONT: PackedFont = PackedFont {
19    first_char: 32,
20    advance: 5,
21    line_height: 8,
22    glyphs: &ASCII_4X7_GLYPHS,
23};
24
25#[derive(Clone, Copy, Debug, PartialEq)]
26pub enum FontId {
27    Tiny3x5,
28    Medium4x7,
29    Scaled6x10,
30    Vector(u8),
31    Custom(&'static PackedFont),
32    #[cfg(feature = "embedded-graphics")]
33    MonoFont(&'static embedded_graphics::mono_font::MonoFont<'static>),
34}
35
36impl Eq for FontId {}
37
38impl FontId {
39    pub const fn advance(self) -> u32 {
40        match self {
41            Self::Tiny3x5 => 4,
42            Self::Medium4x7 => 5,
43            Self::Scaled6x10 => 7,
44            Self::Vector(scale) => (8 * scale) as u32,
45            Self::Custom(font) => font.advance as u32,
46            #[cfg(feature = "embedded-graphics")]
47            Self::MonoFont(font) => font.character_size.width + font.character_spacing,
48        }
49    }
50
51    pub const fn line_height(self) -> u32 {
52        match self {
53            Self::Tiny3x5 => 6,
54            Self::Medium4x7 => 8,
55            Self::Scaled6x10 => 11,
56            Self::Vector(scale) => (12 * scale) as u32,
57            Self::Custom(font) => font.line_height as u32,
58            #[cfg(feature = "embedded-graphics")]
59            Self::MonoFont(font) => font.character_size.height,
60        }
61    }
62}
63
64pub const fn packed_font(font: FontId) -> &'static PackedFont {
65    match font {
66        FontId::Tiny3x5 => &ASCII_3X5_FONT,
67        FontId::Medium4x7 => &ASCII_4X7_FONT,
68        FontId::Scaled6x10 => &ASCII_3X5_FONT,
69        FontId::Vector(_) => &ASCII_3X5_FONT,
70        FontId::Custom(font) => font,
71        #[cfg(feature = "embedded-graphics")]
72        FontId::MonoFont(_) => &ASCII_3X5_FONT,
73    }
74}
75
76impl From<&'static PackedFont> for FontId {
77    fn from(font: &'static PackedFont) -> Self {
78        FontId::Custom(font)
79    }
80}
81
82#[cfg(feature = "embedded-graphics")]
83impl From<&'static embedded_graphics::mono_font::MonoFont<'static>> for FontId {
84    fn from(font: &'static embedded_graphics::mono_font::MonoFont<'static>) -> Self {
85        FontId::MonoFont(font)
86    }
87}
88
89pub fn get_vector_glyph(ch: char) -> &'static [(u8, u8)] {
90    match ch {
91        ' ' => &[],
92        '0' => &[
93            (2, 0),
94            (6, 0),
95            (6, 10),
96            (2, 10),
97            (2, 0),
98            (0xFF, 0xFF),
99            (2, 10),
100            (6, 0),
101        ],
102        '1' => &[
103            (4, 0),
104            (4, 10),
105            (0xFF, 0xFF),
106            (2, 2),
107            (4, 0),
108            (0xFF, 0xFF),
109            (2, 10),
110            (6, 10),
111        ],
112        '2' => &[(2, 0), (6, 0), (6, 5), (2, 5), (2, 10), (6, 10)],
113        '3' => &[
114            (2, 0),
115            (6, 0),
116            (6, 10),
117            (2, 10),
118            (0xFF, 0xFF),
119            (2, 5),
120            (6, 5),
121        ],
122        '4' => &[(2, 0), (2, 5), (6, 5), (0xFF, 0xFF), (6, 0), (6, 10)],
123        '5' => &[(6, 0), (2, 0), (2, 5), (6, 5), (6, 10), (2, 10)],
124        '6' => &[(6, 0), (2, 0), (2, 10), (6, 10), (6, 5), (2, 5)],
125        '7' => &[(2, 0), (6, 0), (2, 10)],
126        '8' => &[
127            (2, 0),
128            (6, 0),
129            (6, 10),
130            (2, 10),
131            (2, 0),
132            (0xFF, 0xFF),
133            (2, 5),
134            (6, 5),
135        ],
136        '9' => &[(6, 5), (2, 5), (2, 0), (6, 0), (6, 10), (2, 10)],
137        'A' | 'a' => &[(2, 10), (4, 0), (6, 10), (0xFF, 0xFF), (3, 5), (5, 5)],
138        'B' | 'b' => &[
139            (2, 0),
140            (5, 0),
141            (6, 2),
142            (6, 4),
143            (5, 5),
144            (2, 5),
145            (5, 5),
146            (6, 6),
147            (6, 8),
148            (5, 10),
149            (2, 10),
150            (2, 0),
151        ],
152        'C' | 'c' => &[(6, 0), (2, 0), (2, 10), (6, 10)],
153        'D' | 'd' => &[(2, 0), (5, 0), (6, 3), (6, 7), (5, 10), (2, 10), (2, 0)],
154        'E' | 'e' => &[
155            (6, 0),
156            (2, 0),
157            (2, 10),
158            (6, 10),
159            (0xFF, 0xFF),
160            (2, 5),
161            (5, 5),
162        ],
163        'F' | 'f' => &[(6, 0), (2, 0), (2, 10), (0xFF, 0xFF), (2, 5), (5, 5)],
164        'G' | 'g' => &[(6, 2), (6, 0), (2, 0), (2, 10), (6, 10), (6, 5), (4, 5)],
165        'H' | 'h' => &[
166            (2, 0),
167            (2, 10),
168            (0xFF, 0xFF),
169            (6, 0),
170            (6, 10),
171            (0xFF, 0xFF),
172            (2, 5),
173            (6, 5),
174        ],
175        'I' | 'i' => &[
176            (4, 0),
177            (4, 10),
178            (0xFF, 0xFF),
179            (2, 0),
180            (6, 0),
181            (0xFF, 0xFF),
182            (2, 10),
183            (6, 10),
184        ],
185        'J' | 'j' => &[(6, 0), (6, 8), (4, 10), (2, 8)],
186        'K' | 'k' => &[(2, 0), (2, 10), (0xFF, 0xFF), (6, 0), (2, 5), (6, 10)],
187        'L' | 'l' => &[(2, 0), (2, 10), (6, 10)],
188        'M' | 'm' => &[(2, 10), (2, 0), (4, 5), (6, 0), (6, 10)],
189        'N' | 'n' => &[(2, 10), (2, 0), (6, 10), (6, 0)],
190        'O' | 'o' => &[(2, 0), (6, 0), (6, 10), (2, 10), (2, 0)],
191        'P' | 'p' => &[(2, 10), (2, 0), (6, 0), (6, 5), (2, 5)],
192        'Q' | 'q' => &[
193            (2, 0),
194            (6, 0),
195            (6, 10),
196            (2, 10),
197            (2, 0),
198            (0xFF, 0xFF),
199            (4, 7),
200            (7, 10),
201        ],
202        'R' | 'r' => &[
203            (2, 10),
204            (2, 0),
205            (6, 0),
206            (6, 5),
207            (2, 5),
208            (0xFF, 0xFF),
209            (4, 5),
210            (6, 10),
211        ],
212        'S' | 's' => &[(6, 0), (2, 0), (2, 5), (6, 5), (6, 10), (2, 10)],
213        'T' | 't' => &[(2, 0), (6, 0), (0xFF, 0xFF), (4, 0), (4, 10)],
214        'U' | 'u' => &[(2, 0), (2, 10), (6, 10), (6, 0)],
215        'V' | 'v' => &[(2, 0), (4, 10), (6, 0)],
216        'W' | 'w' => &[(2, 0), (2, 10), (4, 5), (6, 10), (6, 0)],
217        'X' | 'x' => &[(2, 0), (6, 10), (0xFF, 0xFF), (6, 0), (2, 10)],
218        'Y' | 'y' => &[(2, 0), (4, 5), (6, 0), (0xFF, 0xFF), (4, 5), (4, 10)],
219        'Z' | 'z' => &[(2, 0), (6, 0), (2, 10), (6, 10)],
220        '-' => &[(2, 5), (6, 5)],
221        '+' => &[(2, 5), (6, 5), (0xFF, 0xFF), (4, 2), (4, 8)],
222        '.' => &[(4, 9), (4, 10)],
223        ':' => &[(4, 2), (4, 3), (0xFF, 0xFF), (4, 7), (4, 8)],
224        '/' => &[(2, 10), (6, 0)],
225        _ => &[(2, 0), (6, 0), (6, 10), (2, 10), (2, 0), (2, 0), (6, 10)],
226    }
227}
228
229pub fn glyph_rows(font: FontId, ch: char) -> [u8; 5] {
230    let packed = packed_font(font);
231    let code = ch as u32;
232    if code >= packed.first_char as u32 {
233        let idx = (code as usize).saturating_sub(packed.first_char as usize);
234        if idx < packed.glyphs.len() {
235            return packed.glyphs[idx];
236        }
237    }
238    let fallback = b'?'.saturating_sub(packed.first_char) as usize;
239    packed.glyphs.get(fallback).copied().unwrap_or([0; 5])
240}
241
242#[cfg(test)]
243mod tests {
244    use super::*;
245
246    static MY_CUSTOM_GLYPHS: [[u8; 5]; 2] = [
247        [0b111, 0b101, 0b111, 0b101, 0b101], // Space / 'A'
248        [0b111, 0b111, 0b111, 0b111, 0b111],
249    ];
250
251    static MY_CUSTOM_FONT: PackedFont = PackedFont {
252        first_char: 32,
253        advance: 8,
254        line_height: 12,
255        glyphs: &MY_CUSTOM_GLYPHS,
256    };
257
258    #[test]
259    fn test_custom_font_id() {
260        let font_id = FontId::Custom(&MY_CUSTOM_FONT);
261        assert_eq!(font_id.advance(), 8);
262        assert_eq!(font_id.line_height(), 12);
263        assert_eq!(packed_font(font_id).first_char, 32);
264        assert_eq!(
265            glyph_rows(font_id, ' '),
266            [0b111, 0b101, 0b111, 0b101, 0b101]
267        );
268    }
269}