const FONT_DATA: [u8; 768] = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00,
0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00,
0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0x6C, 0x6C, 0x00,
0x18, 0x3E, 0x60, 0x3C, 0x06, 0x7C, 0x18, 0x00,
0x00, 0xC6, 0xCC, 0x18, 0x30, 0x66, 0xC6, 0x00,
0x38, 0x6C, 0x38, 0x76, 0xDC, 0xCC, 0x76, 0x00,
0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0C, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00,
0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00,
0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00,
0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30,
0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00,
0x7C, 0xCE, 0xDE, 0xF6, 0xE6, 0xC6, 0x7C, 0x00,
0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00,
0x7C, 0xC6, 0x06, 0x7C, 0xC0, 0xC0, 0xFE, 0x00,
0x7C, 0xC6, 0x06, 0x3C, 0x06, 0xC6, 0x7C, 0x00,
0x0C, 0x2C, 0x4C, 0x8C, 0xFE, 0x0C, 0x0C, 0x00,
0xFE, 0xC0, 0xFC, 0x06, 0x06, 0xC6, 0x7C, 0x00,
0x7C, 0xC0, 0xC0, 0xFC, 0xC6, 0xC6, 0x7C, 0x00,
0xFE, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00,
0x7C, 0xC6, 0xC6, 0x7C, 0xC6, 0xC6, 0x7C, 0x00,
0x7C, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0x7C, 0x00,
0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00,
0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x30, 0x00,
0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C, 0x00,
0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00,
0x30, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x30, 0x00,
0x7C, 0xC6, 0x0C, 0x18, 0x18, 0x00, 0x18, 0x00,
0x7C, 0xC6, 0xDE, 0xDE, 0xDC, 0xC0, 0x7C, 0x00,
0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0x00,
0xFC, 0xC6, 0xC6, 0xFC, 0xC6, 0xC6, 0xFC, 0x00,
0x7C, 0xC6, 0xC0, 0xC0, 0xC0, 0xC6, 0x7C, 0x00,
0xF8, 0xCC, 0xC6, 0xC6, 0xC6, 0xCC, 0xF8, 0x00,
0xFE, 0xC0, 0xC0, 0xFC, 0xC0, 0xC0, 0xFE, 0x00,
0xFE, 0xC0, 0xC0, 0xFC, 0xC0, 0xC0, 0xC0, 0x00,
0x7C, 0xC6, 0xC0, 0xCE, 0xC6, 0xC6, 0x7E, 0x00,
0xC6, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0x00,
0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00,
0x06, 0x06, 0x06, 0x06, 0xC6, 0xC6, 0x7C, 0x00,
0xC6, 0xCC, 0xD8, 0xF0, 0xD8, 0xCC, 0xC6, 0x00,
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFE, 0x00,
0xC6, 0xEE, 0xFE, 0xD6, 0xC6, 0xC6, 0xC6, 0x00,
0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0xC6, 0x00,
0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00,
0xFC, 0xC6, 0xC6, 0xFC, 0xC0, 0xC0, 0xC0, 0x00,
0x7C, 0xC6, 0xC6, 0xC6, 0xD6, 0xCC, 0x76, 0x00,
0xFC, 0xC6, 0xC6, 0xFC, 0xD8, 0xCC, 0xC6, 0x00,
0x7C, 0xC6, 0xC0, 0x7C, 0x06, 0xC6, 0x7C, 0x00,
0xFE, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00,
0xC6, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x10, 0x00,
0xC6, 0xC6, 0xC6, 0xD6, 0xFE, 0xEE, 0xC6, 0x00,
0xC6, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0xC6, 0x00,
0xC6, 0xC6, 0x6C, 0x38, 0x18, 0x18, 0x18, 0x00,
0xFE, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xFE, 0x00,
0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00,
0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x02, 0x00,
0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00,
0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00,
0x18, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7C, 0x06, 0x7E, 0xC6, 0x7E, 0x00,
0xC0, 0xC0, 0xFC, 0xC6, 0xC6, 0xC6, 0xFC, 0x00,
0x00, 0x00, 0x7C, 0xC6, 0xC0, 0xC6, 0x7C, 0x00,
0x06, 0x06, 0x7E, 0xC6, 0xC6, 0xC6, 0x7E, 0x00,
0x00, 0x00, 0x7C, 0xC6, 0xFE, 0xC0, 0x7C, 0x00,
0x1C, 0x36, 0x30, 0x7C, 0x30, 0x30, 0x30, 0x00,
0x00, 0x00, 0x7E, 0xC6, 0xC6, 0x7E, 0x06, 0x7C,
0xC0, 0xC0, 0xFC, 0xC6, 0xC6, 0xC6, 0xC6, 0x00,
0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3C, 0x00,
0x06, 0x00, 0x06, 0x06, 0x06, 0xC6, 0xC6, 0x7C,
0xC0, 0xC0, 0xCC, 0xD8, 0xF0, 0xD8, 0xCC, 0x00,
0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00,
0x00, 0x00, 0xCC, 0xFE, 0xD6, 0xC6, 0xC6, 0x00,
0x00, 0x00, 0xFC, 0xC6, 0xC6, 0xC6, 0xC6, 0x00,
0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0x00,
0x00, 0x00, 0xFC, 0xC6, 0xC6, 0xFC, 0xC0, 0xC0,
0x00, 0x00, 0x7E, 0xC6, 0xC6, 0x7E, 0x06, 0x06,
0x00, 0x00, 0xDC, 0xE6, 0xC0, 0xC0, 0xC0, 0x00,
0x00, 0x00, 0x7E, 0xC0, 0x7C, 0x06, 0xFC, 0x00,
0x30, 0x30, 0x7C, 0x30, 0x30, 0x36, 0x1C, 0x00,
0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x00,
0x00, 0x00, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x00,
0x00, 0x00, 0xC6, 0xC6, 0xD6, 0xFE, 0x6C, 0x00,
0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00,
0x00, 0x00, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x7C,
0x00, 0x00, 0xFE, 0x0C, 0x38, 0x60, 0xFE, 0x00,
0x0E, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0E, 0x00,
0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
0x70, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x70, 0x00,
0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0x00,
];
const ATLAS_COLUMNS: u32 = 16;
const ATLAS_ROWS: u32 = 6;
const GLYPH_W: u32 = 8;
const GLYPH_H: u32 = 8;
pub fn generate_builtin_font() -> (Vec<u8>, u32, u32) {
let width = ATLAS_COLUMNS * GLYPH_W;
let height = ATLAS_ROWS * GLYPH_H;
let mut pixels = vec![0u8; (width * height * 4) as usize];
for glyph_idx in 0..96u32 {
let col = glyph_idx % ATLAS_COLUMNS;
let row = glyph_idx / ATLAS_COLUMNS;
let base_x = col * GLYPH_W;
let base_y = row * GLYPH_H;
for py in 0..GLYPH_H {
let byte = FONT_DATA[(glyph_idx * 8 + py) as usize];
for px in 0..GLYPH_W {
let bit = (byte >> (7 - px)) & 1;
let x = base_x + px;
let y = base_y + py;
let offset = ((y * width + x) * 4) as usize;
if bit == 1 {
pixels[offset] = 255; pixels[offset + 1] = 255; pixels[offset + 2] = 255; pixels[offset + 3] = 255; } }
}
}
(pixels, width, height)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn font_texture_dimensions() {
let (pixels, w, h) = generate_builtin_font();
assert_eq!(w, 128);
assert_eq!(h, 48);
assert_eq!(pixels.len(), (128 * 48 * 4) as usize);
}
#[test]
fn space_is_empty() {
let (pixels, w, _) = generate_builtin_font();
for py in 0..8 {
for px in 0..8 {
let offset = ((py * w + px) * 4) as usize;
assert_eq!(pixels[offset + 3], 0, "space pixel ({px},{py}) should be transparent");
}
}
}
#[test]
fn letter_a_has_pixels() {
let (pixels, w, _) = generate_builtin_font();
let base_x: u32 = 1 * 8;
let base_y: u32 = 2 * 8;
let mut has_opaque = false;
for py in 0..8u32 {
for px in 0..8u32 {
let offset = (((base_y + py) * w + (base_x + px)) * 4) as usize;
if pixels[offset + 3] == 255 {
has_opaque = true;
}
}
}
assert!(has_opaque, "'A' glyph should have opaque pixels");
}
}