use crate::console::{CharsXY, SizeInPixels};
use crate::gfx::lcd::LcdSize;
use std::collections::HashMap;
use std::io;
mod font_5x8;
pub use font_5x8::FONT_5X8;
mod font_16x16;
pub use font_16x16::FONT_16X16;
mod font_vga8x16;
pub use font_vga8x16::FONT_VGA8X16;
pub struct Font {
pub name: &'static str,
pub glyph_size: LcdSize,
pub stride: usize,
pub data: &'static [u8],
}
impl Font {
pub(crate) fn glyph(&self, mut ch: char) -> &'static [u8] {
if !(' '..='~').contains(&ch) {
ch = '?';
}
let height = self.glyph_size.height * self.stride;
let offset = ((ch as usize) - (' ' as usize)) * height;
debug_assert!(offset < (self.data.len() + height));
&self.data[offset..offset + height]
}
pub fn chars_in_area(&self, area: SizeInPixels) -> CharsXY {
CharsXY::new(
area.width
.checked_div(u16::try_from(self.glyph_size.width).expect("Must fit"))
.expect("Glyph size tested for non-zero during init"),
area.height
.checked_div(u16::try_from(self.glyph_size.height).expect("Must fit"))
.expect("Glyph size tested for non-zero during init"),
)
}
}
pub struct Fonts(HashMap<&'static str, &'static Font>);
impl Fonts {
pub fn all() -> Self {
let mut fonts = HashMap::default();
fonts.insert(FONT_5X8.name, &FONT_5X8);
fonts.insert(FONT_16X16.name, &FONT_16X16);
fonts.insert(FONT_VGA8X16.name, &FONT_VGA8X16);
Self(fonts)
}
pub fn get(&self, name: &str) -> io::Result<&'static Font> {
match self.0.get(name) {
Some(font) => Ok(*font),
None => {
let mut valid = self.0.keys().copied().collect::<Vec<&'static str>>();
valid.sort();
Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Unknown font: {}; valid names are: {}", name, valid.join(", ")),
))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_font_glyph_printable() {
let font = &FONT_5X8;
let offset = (usize::from(b'a') - usize::from(b' ')) * 8;
let expected = &font.data[offset..offset + 8];
let data = font.glyph('a');
assert_eq!(expected, data);
}
#[test]
fn test_font_glyph_non_printable() {
let font = &FONT_5X8;
let offset = (usize::from(b'?') - usize::from(b' ')) * 8;
let expected = &font.data[offset..offset + 8];
let data = font.glyph(char::from(30));
assert_eq!(expected, data);
}
}