use crate::prelude::{FullFont, PrintableFont};
pub mod full;
pub mod printable;
pub trait AgbFont {
fn char_widths(&self) -> &[u8];
#[inline]
fn char_width(&self, c: u8) -> u8 {
self.char_widths()[c as usize - self.char_offset()]
}
fn char_offset(&self) -> usize;
fn data(&self) -> &[u32];
fn glyph_height(&self) -> u32;
fn glyph_size(&self) -> usize;
fn row_u32s(&self) -> usize;
fn glyph(&self, c: u8) -> &[u32] {
if cfg!(debug_assertions) && self.char_offset() != 0 && !(32..=126).contains(&c) {
panic!("glyph {c} out of printable bounds");
}
let idx = c as usize - self.char_offset();
let offset = idx * self.glyph_size();
unsafe {
self.data()
.get_unchecked(offset..offset + self.glyph_size())
}
}
fn measure_line(&self, text: &[u8], wrap_at: Option<u32>) -> (u32, usize) {
let mut width: u32 = 0;
for (i, &c) in text.iter().enumerate() {
if c == b'\n' {
return (width, i + 1);
}
let char_w = self.char_width(c) as u32;
if let Some(max) = wrap_at
&& i > 0
&& width + char_w > max
{
return (width, i);
}
width += char_w;
}
(width, text.len())
}
fn size_of(&self, text: &[u8], wrap_at: Option<u8>) -> (u8, u8) {
if text.is_empty() {
return (0, 0);
}
let max_width = wrap_at.map(|v| v as u32);
let mut max_w: u32 = 0;
let mut total_h: u32 = 0;
let mut remaining = text;
loop {
let (line_w, consumed) = self.measure_line(remaining, max_width);
if line_w > max_w {
max_w = line_w;
}
total_h += self.glyph_height();
remaining = &remaining[consumed..];
if remaining.is_empty() {
break;
}
}
(max_w as u8, total_h as u8)
}
}
macro_rules! impl_agb_font {
($font_class:ident, $offset: expr) => {
impl AgbFont for $font_class {
#[inline]
fn char_widths(&self) -> &[u8] {
&self.char_widths
}
#[inline]
fn char_offset(&self) -> usize {
$offset
}
#[inline]
fn data(&self) -> &[u32] {
&self.data
}
#[inline]
fn glyph_height(&self) -> u32 {
self.glyph_height
}
#[inline]
fn glyph_size(&self) -> usize {
self.glyph_size
}
#[inline]
fn row_u32s(&self) -> usize {
self.row_u32s
}
}
};
}
impl_agb_font!(PrintableFont, 32);
impl_agb_font!(FullFont, 0);