Skip to main content

packed_font/
lib.rs

1#![no_std]
2
3mod blend;
4mod textrenderer;
5mod unpack;
6
7pub mod twocolor;
8
9use bytemuck::from_bytes;
10use embedded_graphics::{
11    draw_target::DrawTarget,
12    geometry::{Point, Size},
13    pixelcolor::PixelColor,
14    primitives::Rectangle,
15};
16
17use self::unpack::Unpacker;
18
19pub use packed_font_derive::packed_font;
20pub use packed_font_structs::{AaColor, FontMetrics, Metrics, map_character};
21
22pub use textrenderer::CharacterStyle;
23
24pub trait UnpackStyle {
25    type Color: PixelColor;
26    fn map_color(&self, grade: AaColor) -> Self::Color;
27    fn background_color(&self) -> Option<Self::Color>;
28}
29
30#[derive(Debug)]
31pub struct PackedFont {
32    pub metrics: FontMetrics,
33    pub dict: &'static [u16],
34    pub data: &'static [u8],
35}
36
37impl PackedFont {
38    pub fn get_metrics_and_data(&self, character: char) -> Option<(&Metrics, &[u8])> {
39        let idx = map_character(character)? as usize;
40        let offset = self.dict.get(idx).map(|x| (*x) as usize)?;
41        let end_offset = self
42            .dict
43            .get(idx + 1)
44            .map(|x| (*x) as usize)
45            .unwrap_or(self.data.len());
46        let raw = &self.data[offset..end_offset];
47        let (metrics, packed) = raw.split_at(size_of::<Metrics>());
48        let metrics: &Metrics = from_bytes(metrics);
49        Some((metrics, packed))
50    }
51
52    pub fn render<S, D>(
53        &self,
54        character: char,
55        style: &S,
56        origin: Point,
57        target: &mut D,
58    ) -> Result<Option<(&Metrics, u32)>, D::Error>
59    where
60        S: UnpackStyle,
61        D: DrawTarget<Color = S::Color>,
62    {
63        let Some((metrics, packed)) = self.get_metrics_and_data(character) else {
64            return Ok(None);
65        };
66
67        let w = metrics.width as u32;
68        let height = if w > 0 {
69            let h = (self.metrics.ascent as i32 - self.metrics.descent as i32) as u32;
70            let lsb = metrics.left_bearing as i32;
71            let tsb = metrics.top_bearing as i32;
72            let origin = Point::new(origin.x + lsb, origin.y - tsb);
73
74            let rect = Rectangle::new(origin, Size::new(w, h));
75
76            let pixels = Unpacker::new(packed.iter().cloned()).map(|a| style.map_color(a));
77            let mut pixel_count = 0;
78            let pixels = pixels.inspect(|_| pixel_count += 1);
79
80            target.fill_contiguous(&rect, pixels)?;
81
82            pixel_count / w
83        } else {
84            0
85        };
86
87        Ok(Some((metrics, height)))
88    }
89}