use crate::char_offset::{CHARS_PER_ROW, char_offset_impl};
#[derive(Clone, Copy)]
pub struct FbFont {
data: &'static [u8],
char_width: usize,
char_height: usize,
}
impl FbFont {
#[cfg(feature = "regular8x8")]
pub const fn regular_8x8() -> Self {
Self {
data: crate::IBM437_8X8_REGULAR_DATA,
char_width: 8,
char_height: 8,
}
}
#[cfg(feature = "bold8x8")]
pub const fn bold_8x8() -> Self {
Self {
data: crate::IBM437_8X8_BOLD_DATA,
char_width: 8,
char_height: 8,
}
}
#[cfg(feature = "regular9x14")]
pub const fn regular_9x14() -> Self {
Self {
data: crate::IBM437_9X14_REGULAR_DATA,
char_width: 9,
char_height: 14,
}
}
#[inline]
pub const fn char_width(&self) -> usize {
self.char_width
}
#[inline]
pub const fn char_height(&self) -> usize {
self.char_height
}
pub fn draw_char(
&self,
buffer: &mut [u32],
buf_width: usize,
x: usize,
y: usize,
c: char,
fg: u32,
bg: Option<u32>,
) {
let idx = char_offset_impl(c);
let glyph_col = idx % CHARS_PER_ROW;
let glyph_row = idx / CHARS_PER_ROW;
let row_bits = CHARS_PER_ROW * self.char_width;
let buf_height = if buf_width > 0 {
buffer.len() / buf_width
} else {
return;
};
for gy in 0..self.char_height {
let py = y + gy;
if py >= buf_height {
break;
}
for gx in 0..self.char_width {
let px = x + gx;
if px >= buf_width {
break;
}
let bit_index = (glyph_row * self.char_height + gy) * row_bits
+ glyph_col * self.char_width
+ gx;
let byte = self.data[bit_index / 8];
let is_set = (byte >> (7 - (bit_index % 8))) & 1 != 0;
if is_set {
buffer[py * buf_width + px] = fg;
} else if let Some(bg_color) = bg {
buffer[py * buf_width + px] = bg_color;
}
}
}
}
pub fn draw_str(
&self,
buffer: &mut [u32],
buf_width: usize,
x: usize,
y: usize,
text: &str,
fg: u32,
bg: Option<u32>,
) -> (usize, usize) {
let mut cx = x;
let mut cy = y;
for c in text.chars() {
if c == '\n' {
cx = x;
cy += self.char_height;
continue;
}
self.draw_char(buffer, buf_width, cx, cy, c, fg, bg);
cx += self.char_width;
}
(cx, cy)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn draw_char_a_matches_expected_pattern() {
let font = FbFont::regular_8x8();
let w = 8;
let h = 8;
let mut buf = vec![0u32; w * h];
font.draw_char(&mut buf, w, 0, 0, 'a', 1, Some(0));
let pattern: Vec<Vec<u8>> = (0..h)
.map(|y| (0..w).map(|x| buf[y * w + x] as u8).collect())
.collect();
assert_eq!(pattern[0], [0, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(pattern[1], [0, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(pattern[2], [0, 0, 1, 1, 1, 1, 0, 0]);
assert_eq!(pattern[3], [0, 0, 0, 0, 0, 0, 1, 0]);
assert_eq!(pattern[4], [0, 0, 1, 1, 1, 1, 1, 0]);
assert_eq!(pattern[5], [0, 1, 0, 0, 0, 0, 1, 0]);
assert_eq!(pattern[6], [0, 0, 1, 1, 1, 1, 1, 1]);
assert_eq!(pattern[7], [0, 0, 0, 0, 0, 0, 0, 0]);
}
#[test]
fn draw_str_newline_resets_column() {
let font = FbFont::regular_8x8();
let w = 80;
let h = 24;
let mut buf = vec![0u32; w * h];
let (cx, cy) = font.draw_str(&mut buf, w, 0, 0, "AB\nC", 1, None);
assert_eq!(cx, 8); assert_eq!(cy, 8); }
#[test]
fn clipping_does_not_panic() {
let font = FbFont::regular_8x8();
let mut buf = vec![0u32; 4 * 4]; font.draw_char(&mut buf, 4, 2, 2, 'X', 0x00FFFFFF, None);
font.draw_str(&mut buf, 4, 0, 0, "Hello, World!", 0x00FFFFFF, None);
}
#[test]
fn transparent_background_does_not_overwrite() {
let font = FbFont::regular_8x8();
let w = 8;
let h = 8;
let sentinel = 0xDEAD_BEEF;
let mut buf = vec![sentinel; w * h];
font.draw_char(&mut buf, w, 0, 0, ' ', 0x00FFFFFF, None);
for &px in &buf {
assert_eq!(px, sentinel);
}
}
}