gba_agb_font_renderer 0.2.0

Bitmap font renderer for GBA/AGB
use crate::prelude::{FullFont, PrintableFont};

pub mod full;
pub mod printable;

/// 4bpp font for gba
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;

    /// Return the pixel data for the glyph corresponding to `c`.
    ///
    /// Panics in debug builds if `c` is outside the font's character range.
    /// In release builds, out-of-range values produce undefined pixel data.
    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())
        }
    }

    /// Size of text in pixels if rendered with this font.
    fn size_of(&self, text: &[u8], wrap_at: u8) -> (u8, u8) {
        let max_width = wrap_at as u32;
        if text.is_empty() {
            return (0, 0);
        }

        let mut max_encountered_width: u32 = 0;
        let mut current_line_width: u32 = 0;
        let mut total_height: u32 = self.glyph_height();

        for &c in text {
            if c == b'\n' {
                if current_line_width > max_encountered_width {
                    max_encountered_width = current_line_width;
                }
                current_line_width = 0;
                total_height += self.glyph_height();
            } else {
                let char_w = self.char_width(c) as u32;
                if current_line_width + char_w > max_width {
                    if current_line_width > max_encountered_width {
                        max_encountered_width = current_line_width;
                    }
                    current_line_width = char_w;
                    total_height += self.glyph_height();
                } else {
                    current_line_width += char_w;
                }
            }
        }

        if current_line_width > max_encountered_width {
            max_encountered_width = current_line_width;
        }

        (max_encountered_width as u8, total_height 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);