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}