1include!(concat!(env!("OUT_DIR"), "/generated_ascii_3x5.rs"));
2
3pub trait Font: Send + Sync {
9 fn advance(&self) -> u32;
11
12 fn line_height(&self) -> u32;
14
15 fn draw_glyph(&self, ch: char, draw_pixel: &mut dyn FnMut(i32, i32));
18}
19
20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
21pub struct PackedFont {
22 pub first_char: u8,
23 pub advance: u8,
24 pub line_height: u8,
25 pub glyphs: &'static [[u8; 5]],
26}
27
28impl Font for PackedFont {
29 fn advance(&self) -> u32 {
30 self.advance as u32
31 }
32
33 fn line_height(&self) -> u32 {
34 self.line_height as u32
35 }
36
37 fn draw_glyph(&self, ch: char, draw_pixel: &mut dyn FnMut(i32, i32)) {
38 let code = ch as u32;
39 let rows = if code >= self.first_char as u32 {
40 let idx = (code as usize).saturating_sub(self.first_char as usize);
41 self.glyphs.get(idx).copied().unwrap_or([0; 5])
42 } else {
43 [0; 5]
44 };
45 for (row, bits) in rows.iter().enumerate() {
46 for col in 0..3 {
47 if bits & (1 << (2 - col)) != 0 {
48 draw_pixel(col, row as i32);
49 }
50 }
51 }
52 }
53}
54
55pub static ASCII_3X5_FONT: PackedFont = PackedFont {
56 first_char: 32,
57 advance: 4,
58 line_height: 6,
59 glyphs: &ASCII_3X5_GLYPHS,
60};
61
62pub static ASCII_4X7_FONT: PackedFont = PackedFont {
63 first_char: 32,
64 advance: 5,
65 line_height: 8,
66 glyphs: &ASCII_4X7_GLYPHS,
67};
68
69#[derive(Clone, Copy, Debug, PartialEq, Eq)]
71pub enum GlyphOp {
72 Pixel(i32, i32),
74 Span(i32, i32, u32),
76}
77
78#[derive(Clone, Copy, Debug, PartialEq, Eq)]
84pub struct BitmapFont {
85 pub width: u8,
87 pub height: u8,
89 pub advance: u8,
91 pub line_height: u8,
93 pub first_char: u8,
95 pub bytes_per_row: u8,
97 pub glyphs: &'static [u8],
99}
100
101impl BitmapFont {
102 pub const fn new_8x8(
104 first_char: u8,
105 advance: u8,
106 line_height: u8,
107 glyphs: &'static [u8],
108 ) -> Self {
109 Self {
110 width: 8,
111 height: 8,
112 advance,
113 line_height,
114 first_char,
115 bytes_per_row: 1,
116 glyphs,
117 }
118 }
119
120 pub const fn new_8x16(
122 first_char: u8,
123 advance: u8,
124 line_height: u8,
125 glyphs: &'static [u8],
126 ) -> Self {
127 Self {
128 width: 8,
129 height: 16,
130 advance,
131 line_height,
132 first_char,
133 bytes_per_row: 1,
134 glyphs,
135 }
136 }
137
138 pub fn glyph_bytes(&self, ch: char) -> Option<&'static [u8]> {
140 let code = ch as u32;
141 if code < self.first_char as u32 {
142 return None;
143 }
144 let idx = (code - self.first_char as u32) as usize;
145 let bytes_per_glyph = self.height as usize * self.bytes_per_row as usize;
146 let start = idx * bytes_per_glyph;
147 let end = start + bytes_per_glyph;
148 if end <= self.glyphs.len() {
149 Some(&self.glyphs[start..end])
150 } else {
151 None
152 }
153 }
154
155 pub fn draw_glyph_to<F>(&self, ch: char, mut emit: F)
157 where
158 F: FnMut(GlyphOp),
159 {
160 if let Some(data) = self.glyph_bytes(ch) {
161 let bpr = self.bytes_per_row as usize;
162 for row in 0..(self.height as usize) {
163 let row_data = &data[row * bpr..(row + 1) * bpr];
164 let mut span_start: Option<usize> = None;
165 let mut span_len = 0u32;
166
167 for col in 0..(self.width as usize) {
168 let byte_idx = col / 8;
169 let bit_idx = 7 - (col % 8);
170 let is_set =
171 byte_idx < row_data.len() && (row_data[byte_idx] & (1 << bit_idx)) != 0;
172
173 if is_set {
174 if span_start.is_none() {
175 span_start = Some(col);
176 span_len = 1;
177 } else {
178 span_len += 1;
179 }
180 } else if let Some(start) = span_start {
181 if span_len == 1 {
182 emit(GlyphOp::Pixel(start as i32, row as i32));
183 } else {
184 emit(GlyphOp::Span(start as i32, row as i32, span_len));
185 }
186 span_start = None;
187 span_len = 0;
188 }
189 }
190 if let Some(start) = span_start {
191 if span_len == 1 {
192 emit(GlyphOp::Pixel(start as i32, row as i32));
193 } else {
194 emit(GlyphOp::Span(start as i32, row as i32, span_len));
195 }
196 }
197 }
198 }
199 }
200}
201
202impl Font for BitmapFont {
203 fn advance(&self) -> u32 {
204 self.advance as u32
205 }
206
207 fn line_height(&self) -> u32 {
208 self.line_height as u32
209 }
210
211 fn draw_glyph(&self, ch: char, draw_pixel: &mut dyn FnMut(i32, i32)) {
212 self.draw_glyph_to(ch, |op| match op {
213 GlyphOp::Pixel(dx, dy) => draw_pixel(dx, dy),
214 GlyphOp::Span(dx, dy, len) => {
215 for col in 0..len {
216 draw_pixel(dx + col as i32, dy);
217 }
218 }
219 });
220 }
221}
222
223#[cfg(feature = "embedded-graphics")]
224impl Font for embedded_graphics::mono_font::MonoFont<'static> {
225 fn advance(&self) -> u32 {
226 self.character_size.width + self.character_spacing
227 }
228
229 fn line_height(&self) -> u32 {
230 self.character_size.height
231 }
232
233 fn draw_glyph(&self, ch: char, draw_pixel: &mut dyn FnMut(i32, i32)) {
234 use embedded_graphics::Drawable;
235 use embedded_graphics::draw_target::DrawTarget;
236 use embedded_graphics::geometry::{OriginDimensions, Point, Size};
237 use embedded_graphics::mono_font::MonoTextStyle;
238 use embedded_graphics::pixelcolor::BinaryColor;
239 use embedded_graphics::text::Text;
240
241 struct Collector<'a> {
242 f: &'a mut dyn FnMut(i32, i32),
243 }
244
245 impl OriginDimensions for Collector<'_> {
246 fn size(&self) -> Size {
247 Size::new(u32::MAX, u32::MAX)
248 }
249 }
250
251 impl DrawTarget for Collector<'_> {
252 type Color = BinaryColor;
253 type Error = core::convert::Infallible;
254
255 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
256 where
257 I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>,
258 {
259 for embedded_graphics::Pixel(pos, color) in pixels {
260 if color.is_on() {
261 (self.f)(pos.x, pos.y);
262 }
263 }
264 Ok(())
265 }
266 }
267
268 let text_style = MonoTextStyle::new(self, BinaryColor::On);
269 let mut buf = [0u8; 4];
270 let ch_str = ch.encode_utf8(&mut buf);
271 let mut collector = Collector { f: draw_pixel };
272 let _ = Text::new(ch_str, Point::zero(), text_style).draw(&mut collector);
273 }
274}
275
276#[derive(Clone, Copy)]
277pub enum FontId {
278 Tiny3x5,
279 Medium4x7,
280 Scaled6x10,
281 Vector(u8),
282 Custom(&'static PackedFont),
283 Bitmap(&'static BitmapFont),
284 Dynamic(&'static dyn Font),
285 #[cfg(feature = "embedded-graphics")]
286 MonoFont(&'static embedded_graphics::mono_font::MonoFont<'static>),
287}
288
289impl core::fmt::Debug for FontId {
290 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
291 match self {
292 Self::Tiny3x5 => write!(f, "Tiny3x5"),
293 Self::Medium4x7 => write!(f, "Medium4x7"),
294 Self::Scaled6x10 => write!(f, "Scaled6x10"),
295 Self::Vector(scale) => f.debug_tuple("Vector").field(scale).finish(),
296 Self::Custom(font) => f.debug_tuple("Custom").field(font).finish(),
297 Self::Bitmap(font) => f.debug_tuple("Bitmap").field(font).finish(),
298 Self::Dynamic(_) => f.write_str("Dynamic"),
299 #[cfg(feature = "embedded-graphics")]
300 Self::MonoFont(font) => f.debug_tuple("MonoFont").field(font).finish(),
301 }
302 }
303}
304
305impl PartialEq for FontId {
306 fn eq(&self, other: &Self) -> bool {
307 match (self, other) {
308 (Self::Tiny3x5, Self::Tiny3x5) => true,
309 (Self::Medium4x7, Self::Medium4x7) => true,
310 (Self::Scaled6x10, Self::Scaled6x10) => true,
311 (Self::Vector(a), Self::Vector(b)) => a == b,
312 (Self::Custom(a), Self::Custom(b)) => core::ptr::eq(*a, *b),
313 (Self::Bitmap(a), Self::Bitmap(b)) => core::ptr::eq(*a, *b),
314 (Self::Dynamic(a), Self::Dynamic(b)) => core::ptr::eq(
315 *a as *const dyn Font as *const (),
316 *b as *const dyn Font as *const (),
317 ),
318 #[cfg(feature = "embedded-graphics")]
319 (Self::MonoFont(a), Self::MonoFont(b)) => core::ptr::eq(*a, *b),
320 _ => false,
321 }
322 }
323}
324
325impl Eq for FontId {}
326
327impl FontId {
328 pub fn advance(self) -> u32 {
329 match self {
330 Self::Tiny3x5 => 4,
331 Self::Medium4x7 => 5,
332 Self::Scaled6x10 => 7,
333 Self::Vector(scale) => (8 * scale) as u32,
334 Self::Custom(font) => font.advance as u32,
335 Self::Bitmap(font) => font.advance as u32,
336 Self::Dynamic(font) => font.advance(),
337 #[cfg(feature = "embedded-graphics")]
338 Self::MonoFont(font) => font.character_size.width + font.character_spacing,
339 }
340 }
341
342 pub fn line_height(self) -> u32 {
343 match self {
344 Self::Tiny3x5 => 6,
345 Self::Medium4x7 => 8,
346 Self::Scaled6x10 => 11,
347 Self::Vector(scale) => (12 * scale) as u32,
348 Self::Custom(font) => font.line_height as u32,
349 Self::Bitmap(font) => font.line_height as u32,
350 Self::Dynamic(font) => font.line_height(),
351 #[cfg(feature = "embedded-graphics")]
352 Self::MonoFont(font) => font.character_size.height,
353 }
354 }
355}
356
357pub const fn packed_font(font: FontId) -> &'static PackedFont {
358 match font {
359 FontId::Tiny3x5 => &ASCII_3X5_FONT,
360 FontId::Medium4x7 => &ASCII_4X7_FONT,
361 FontId::Scaled6x10 => &ASCII_3X5_FONT,
362 FontId::Vector(_) => &ASCII_3X5_FONT,
363 FontId::Custom(font) => font,
364 FontId::Bitmap(_) => &ASCII_3X5_FONT,
365 FontId::Dynamic(_) => &ASCII_3X5_FONT,
366 #[cfg(feature = "embedded-graphics")]
367 FontId::MonoFont(_) => &ASCII_3X5_FONT,
368 }
369}
370
371impl From<&'static PackedFont> for FontId {
372 fn from(font: &'static PackedFont) -> Self {
373 FontId::Custom(font)
374 }
375}
376
377impl From<&'static BitmapFont> for FontId {
378 fn from(font: &'static BitmapFont) -> Self {
379 FontId::Bitmap(font)
380 }
381}
382
383impl From<&'static dyn Font> for FontId {
384 fn from(font: &'static dyn Font) -> Self {
385 FontId::Dynamic(font)
386 }
387}
388
389#[cfg(feature = "embedded-graphics")]
390impl From<&'static embedded_graphics::mono_font::MonoFont<'static>> for FontId {
391 fn from(font: &'static embedded_graphics::mono_font::MonoFont<'static>) -> Self {
392 FontId::MonoFont(font)
393 }
394}
395
396pub fn get_vector_glyph(ch: char) -> &'static [(u8, u8)] {
397 match ch {
398 ' ' => &[],
399 '0' => &[
400 (2, 0),
401 (6, 0),
402 (6, 10),
403 (2, 10),
404 (2, 0),
405 (0xFF, 0xFF),
406 (2, 10),
407 (6, 0),
408 ],
409 '1' => &[
410 (4, 0),
411 (4, 10),
412 (0xFF, 0xFF),
413 (2, 2),
414 (4, 0),
415 (0xFF, 0xFF),
416 (2, 10),
417 (6, 10),
418 ],
419 '2' => &[(2, 0), (6, 0), (6, 5), (2, 5), (2, 10), (6, 10)],
420 '3' => &[
421 (2, 0),
422 (6, 0),
423 (6, 10),
424 (2, 10),
425 (0xFF, 0xFF),
426 (2, 5),
427 (6, 5),
428 ],
429 '4' => &[(2, 0), (2, 5), (6, 5), (0xFF, 0xFF), (6, 0), (6, 10)],
430 '5' => &[(6, 0), (2, 0), (2, 5), (6, 5), (6, 10), (2, 10)],
431 '6' => &[(6, 0), (2, 0), (2, 10), (6, 10), (6, 5), (2, 5)],
432 '7' => &[(2, 0), (6, 0), (2, 10)],
433 '8' => &[
434 (2, 0),
435 (6, 0),
436 (6, 10),
437 (2, 10),
438 (2, 0),
439 (0xFF, 0xFF),
440 (2, 5),
441 (6, 5),
442 ],
443 '9' => &[(6, 5), (2, 5), (2, 0), (6, 0), (6, 10), (2, 10)],
444 'A' | 'a' => &[(2, 10), (4, 0), (6, 10), (0xFF, 0xFF), (3, 5), (5, 5)],
445 'B' | 'b' => &[
446 (2, 0),
447 (5, 0),
448 (6, 2),
449 (6, 4),
450 (5, 5),
451 (2, 5),
452 (5, 5),
453 (6, 6),
454 (6, 8),
455 (5, 10),
456 (2, 10),
457 (2, 0),
458 ],
459 'C' | 'c' => &[(6, 0), (2, 0), (2, 10), (6, 10)],
460 'D' | 'd' => &[(2, 0), (5, 0), (6, 3), (6, 7), (5, 10), (2, 10), (2, 0)],
461 'E' | 'e' => &[
462 (6, 0),
463 (2, 0),
464 (2, 10),
465 (6, 10),
466 (0xFF, 0xFF),
467 (2, 5),
468 (5, 5),
469 ],
470 'F' | 'f' => &[(6, 0), (2, 0), (2, 10), (0xFF, 0xFF), (2, 5), (5, 5)],
471 'G' | 'g' => &[(6, 2), (6, 0), (2, 0), (2, 10), (6, 10), (6, 5), (4, 5)],
472 'H' | 'h' => &[
473 (2, 0),
474 (2, 10),
475 (0xFF, 0xFF),
476 (6, 0),
477 (6, 10),
478 (0xFF, 0xFF),
479 (2, 5),
480 (6, 5),
481 ],
482 'I' | 'i' => &[
483 (4, 0),
484 (4, 10),
485 (0xFF, 0xFF),
486 (2, 0),
487 (6, 0),
488 (0xFF, 0xFF),
489 (2, 10),
490 (6, 10),
491 ],
492 'J' | 'j' => &[(6, 0), (6, 8), (4, 10), (2, 8)],
493 'K' | 'k' => &[(2, 0), (2, 10), (0xFF, 0xFF), (6, 0), (2, 5), (6, 10)],
494 'L' | 'l' => &[(2, 0), (2, 10), (6, 10)],
495 'M' | 'm' => &[(2, 10), (2, 0), (4, 5), (6, 0), (6, 10)],
496 'N' | 'n' => &[(2, 10), (2, 0), (6, 10), (6, 0)],
497 'O' | 'o' => &[(2, 0), (6, 0), (6, 10), (2, 10), (2, 0)],
498 'P' | 'p' => &[(2, 10), (2, 0), (6, 0), (6, 5), (2, 5)],
499 'Q' | 'q' => &[
500 (2, 0),
501 (6, 0),
502 (6, 10),
503 (2, 10),
504 (2, 0),
505 (0xFF, 0xFF),
506 (4, 7),
507 (7, 10),
508 ],
509 'R' | 'r' => &[
510 (2, 10),
511 (2, 0),
512 (6, 0),
513 (6, 5),
514 (2, 5),
515 (0xFF, 0xFF),
516 (4, 5),
517 (6, 10),
518 ],
519 'S' | 's' => &[(6, 0), (2, 0), (2, 5), (6, 5), (6, 10), (2, 10)],
520 'T' | 't' => &[(2, 0), (6, 0), (0xFF, 0xFF), (4, 0), (4, 10)],
521 'U' | 'u' => &[(2, 0), (2, 10), (6, 10), (6, 0)],
522 'V' | 'v' => &[(2, 0), (4, 10), (6, 0)],
523 'W' | 'w' => &[(2, 0), (2, 10), (4, 5), (6, 10), (6, 0)],
524 'X' | 'x' => &[(2, 0), (6, 10), (0xFF, 0xFF), (6, 0), (2, 10)],
525 'Y' | 'y' => &[(2, 0), (4, 5), (6, 0), (0xFF, 0xFF), (4, 5), (4, 10)],
526 'Z' | 'z' => &[(2, 0), (6, 0), (2, 10), (6, 10)],
527 '-' => &[(2, 5), (6, 5)],
528 '+' => &[(2, 5), (6, 5), (0xFF, 0xFF), (4, 2), (4, 8)],
529 '.' => &[(4, 9), (4, 10)],
530 ':' => &[(4, 2), (4, 3), (0xFF, 0xFF), (4, 7), (4, 8)],
531 '/' => &[(2, 10), (6, 0)],
532 _ => &[(2, 0), (6, 0), (6, 10), (2, 10), (2, 0), (2, 0), (6, 10)],
533 }
534}
535
536pub fn glyph_rows(font: FontId, ch: char) -> [u8; 5] {
537 let packed = packed_font(font);
538 let code = ch as u32;
539 if code >= packed.first_char as u32 {
540 let idx = (code as usize).saturating_sub(packed.first_char as usize);
541 if idx < packed.glyphs.len() {
542 return packed.glyphs[idx];
543 }
544 }
545 let fallback = b'?'.saturating_sub(packed.first_char) as usize;
546 packed.glyphs.get(fallback).copied().unwrap_or([0; 5])
547}
548
549#[cfg(test)]
550mod tests {
551 use super::*;
552 use std::vec::Vec;
553
554 static MY_CUSTOM_GLYPHS: [[u8; 5]; 2] = [
555 [0b111, 0b101, 0b111, 0b101, 0b101], [0b111, 0b111, 0b111, 0b111, 0b111],
557 ];
558
559 static MY_CUSTOM_FONT: PackedFont = PackedFont {
560 first_char: 32,
561 advance: 8,
562 line_height: 12,
563 glyphs: &MY_CUSTOM_GLYPHS,
564 };
565
566 static MY_BITMAP_GLYPHS: [u8; 16] = [
567 0b00111100, 0b01100110, 0b01100110, 0b01111110, 0b01100110, 0b01100110, 0b01100110,
569 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
570 0b00000000, 0b00000000,
571 ];
572
573 static MY_BITMAP_FONT: BitmapFont =
574 BitmapFont::new_8x16(65 , 8, 16, &MY_BITMAP_GLYPHS);
575
576 struct CustomFontImpl;
577 impl Font for CustomFontImpl {
578 fn advance(&self) -> u32 {
579 10
580 }
581 fn line_height(&self) -> u32 {
582 14
583 }
584 fn draw_glyph(&self, _ch: char, draw_pixel: &mut dyn FnMut(i32, i32)) {
585 draw_pixel(0, 0);
586 draw_pixel(1, 1);
587 }
588 }
589
590 static DYN_FONT_INSTANCE: CustomFontImpl = CustomFontImpl;
591
592 #[test]
593 fn test_custom_font_id() {
594 let font_id = FontId::Custom(&MY_CUSTOM_FONT);
595 assert_eq!(font_id.advance(), 8);
596 assert_eq!(font_id.line_height(), 12);
597 assert_eq!(packed_font(font_id).first_char, 32);
598 assert_eq!(
599 glyph_rows(font_id, ' '),
600 [0b111, 0b101, 0b111, 0b101, 0b101]
601 );
602 }
603
604 #[test]
605 fn test_bitmap_font() {
606 let font_id = FontId::from(&MY_BITMAP_FONT);
607 assert_eq!(font_id.advance(), 8);
608 assert_eq!(font_id.line_height(), 16);
609
610 let mut pixels = Vec::new();
611 MY_BITMAP_FONT.draw_glyph('A', &mut |x, y| pixels.push((x, y)));
612 assert!(!pixels.is_empty());
613 assert!(pixels.contains(&(2, 0))); }
615
616 #[test]
617 fn test_dynamic_font() {
618 let font_id = FontId::from(&DYN_FONT_INSTANCE as &'static dyn Font);
619 assert_eq!(font_id.advance(), 10);
620 assert_eq!(font_id.line_height(), 14);
621 }
622}