baremetal_unifont/
lib.rs

1#![no_std]
2
3use core::fmt::Debug;
4
5pub const BIN_WIDTH: &'static [u8] = include_bytes!("unifont.width.bin");
6pub const BIN_BYTES: &'static [u8] = include_bytes!("unifont.bin");
7#[cfg(feature = "otf")]
8pub const OTF_BYTES: &'static [u8] = include_bytes!("unifont.otf");
9#[cfg(feature = "hex")]
10pub const HEX_BYTES: &'static [u8] = include_bytes!("unifont.hex");
11
12pub fn char_width(c: char) -> Option<usize> {
13    let code = c as u32;
14    if code < 65536 {
15        let width = BIN_WIDTH[code as usize] as usize;
16        if width == 0 { None } else { Some(width) }
17    } else {
18        None
19    }
20}
21
22pub fn get_glyph(c: char) -> Option<Unifont> {
23    let code = c as u32;
24    if code < 65536 {
25        let width = BIN_WIDTH[code as usize] as usize;
26        if width == 0 {
27            None
28        } else {
29            let data = &BIN_BYTES[code as usize * 32..code as usize * 32 + 32];
30            if width == 1 {
31                Some(Unifont {
32                    inner: UnifontInner::HalfWidth(data),
33                })
34            } else {
35                Some(Unifont {
36                    inner: UnifontInner::FullWidth(data),
37                })
38            }
39        }
40    } else {
41        None
42    }
43}
44
45enum UnifontInner {
46    HalfWidth(&'static [u8]),
47    FullWidth(&'static [u8]),
48}
49
50impl Debug for UnifontInner {
51    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
52        let (width, data) = match self {
53            UnifontInner::HalfWidth(data) => (8, data),
54            UnifontInner::FullWidth(data) => (16, data),
55        };
56        match self {
57            UnifontInner::HalfWidth(_) => writeln!(f, "Unifont(HalfWidth(")?,
58            UnifontInner::FullWidth(_) => writeln!(f, "Unifont(FullWidth(")?,
59        }
60        for y in 0..16 {
61            write!(f, "    ")?;
62            for x in 0..width {
63                let c = match (data[y * 2 + x / 8] >> (7 - x % 8)) & 1 {
64                    1 => '#',
65                    _ => '.',
66                };
67                write!(f, "{}", c)?;
68            }
69            writeln!(f)?;
70        }
71        writeln!(f, "))")?;
72        Ok(())
73    }
74}
75
76pub struct Unifont {
77    inner: UnifontInner,
78}
79
80impl Debug for Unifont {
81    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
82        self.inner.fmt(f)
83    }
84}
85
86impl Default for Unifont {
87    fn default() -> Self {
88        Unifont {
89            inner: UnifontInner::HalfWidth(&BIN_BYTES[32 * 32..33 * 32]),
90        }
91    }
92}
93
94impl Unifont {
95    pub fn width(&self) -> usize {
96        match self.inner {
97            UnifontInner::HalfWidth(_) => 8,
98            UnifontInner::FullWidth(_) => 16,
99        }
100    }
101
102    pub fn height(&self) -> usize {
103        16
104    }
105
106    pub fn get(&self, x: usize, y: usize) -> bool {
107        match self.inner {
108            UnifontInner::HalfWidth(data) => {
109                if x >= 8 || y >= 16 {
110                    false
111                } else {
112                    (data[y * 2 + x / 8] >> (7 - x % 8)) & 1 == 1
113                }
114            }
115            UnifontInner::FullWidth(data) => {
116                if x >= 16 || y >= 16 {
117                    false
118                } else {
119                    (data[y * 2 + x / 8] >> (7 - x % 8)) & 1 == 1
120                }
121            }
122        }
123    }
124
125    pub unsafe fn get_unchecked(&self, x: usize, y: usize) -> bool {
126        unsafe {
127            match self.inner {
128                UnifontInner::HalfWidth(data) | UnifontInner::FullWidth(data) => {
129                    (data.get_unchecked(y * 2 + x / 8) >> (7 - x % 8)) & 1 == 1
130                }
131            }
132        }
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139
140    #[test]
141    fn test_char_width() {
142        assert_eq!(char_width('A'), Some(1));
143        assert_eq!(char_width('あ'), Some(2));
144        assert_eq!(char_width('\u{E000}'), None);
145        assert_eq!(char_width('\u{10FFFF}'), None);
146    }
147
148    #[test]
149    fn test_get_glyph() {
150        let glyph_a = get_glyph('A').unwrap();
151        assert_eq!(glyph_a.width(), 8);
152        assert_eq!(glyph_a.height(), 16);
153
154        let glyph_あ = get_glyph('あ').unwrap();
155        assert_eq!(glyph_あ.width(), 16);
156        assert_eq!(glyph_あ.height(), 16);
157
158        assert!(get_glyph('\u{E000}').is_none());
159        assert!(get_glyph('\u{10FFFF}').is_none());
160    }
161}