use image::{DynamicImage, GrayImage, Luma};
pub fn render_text_bitmap(
text: &str,
image_height: u32,
background_color: Luma<u8>,
text_color: Luma<u8>,
padding: u32,
) -> DynamicImage {
let glyph_width = 5u32;
let glyph_height = 7u32;
let scale =
(image_height.saturating_sub(padding * 2) as f32 / glyph_height as f32).max(1.0) as u32;
let scaled_glyph_w = glyph_width * scale;
let scaled_glyph_h = glyph_height * scale;
let text_width = text.len() as u32 * scaled_glyph_w;
let img_width = text_width + padding * 2;
let img_height = image_height;
let mut image = GrayImage::from_pixel(img_width, img_height, background_color);
let baseline_y = (img_height.saturating_sub(scaled_glyph_h)) / 2;
for (char_idx, ch) in text.chars().enumerate() {
let x_offset = padding + char_idx as u32 * scaled_glyph_w;
if let Some(rows) = glyph_5x7_rows(ch) {
for (row_idx, row) in rows.iter().enumerate() {
for (col_idx, pixel) in row.chars().enumerate() {
if pixel == '1' {
for dy in 0..scale {
for dx in 0..scale {
let px = x_offset + col_idx as u32 * scale + dx;
let py = baseline_y + row_idx as u32 * scale + dy;
if px < img_width && py < img_height {
image.put_pixel(px, py, text_color);
}
}
}
}
}
}
}
}
DynamicImage::ImageLuma8(image)
}
pub fn glyph_5x7_rows(ch: char) -> Option<[&'static str; 7]> {
match ch {
'A' => Some([
"01110", "10001", "10001", "11111", "10001", "10001", "10001",
]),
'B' => Some([
"11110", "10001", "10001", "11110", "10001", "10001", "11110",
]),
'C' => Some([
"01111", "10000", "10000", "10000", "10000", "10000", "01111",
]),
'D' => Some([
"11110", "10001", "10001", "10001", "10001", "10001", "11110",
]),
'E' => Some([
"11111", "10000", "10000", "11110", "10000", "10000", "11111",
]),
'F' => Some([
"11111", "10000", "10000", "11110", "10000", "10000", "10000",
]),
'G' => Some([
"01111", "10000", "10000", "10111", "10001", "10001", "01111",
]),
'H' => Some([
"10001", "10001", "10001", "11111", "10001", "10001", "10001",
]),
'I' => Some([
"11111", "00100", "00100", "00100", "00100", "00100", "11111",
]),
'J' => Some([
"00111", "00010", "00010", "00010", "00010", "10010", "01100",
]),
'K' => Some([
"10001", "10010", "10100", "11000", "10100", "10010", "10001",
]),
'L' => Some([
"10000", "10000", "10000", "10000", "10000", "10000", "11111",
]),
'M' => Some([
"10001", "11011", "10101", "10101", "10001", "10001", "10001",
]),
'N' => Some([
"10001", "11001", "10101", "10011", "10001", "10001", "10001",
]),
'O' => Some([
"01110", "10001", "10001", "10001", "10001", "10001", "01110",
]),
'P' => Some([
"11110", "10001", "10001", "11110", "10000", "10000", "10000",
]),
'Q' => Some([
"01110", "10001", "10001", "10001", "10101", "10010", "01101",
]),
'R' => Some([
"11110", "10001", "10001", "11110", "10100", "10010", "10001",
]),
'S' => Some([
"01111", "10000", "10000", "01110", "00001", "00001", "11110",
]),
'T' => Some([
"11111", "00100", "00100", "00100", "00100", "00100", "00100",
]),
'U' => Some([
"10001", "10001", "10001", "10001", "10001", "10001", "01110",
]),
'V' => Some([
"10001", "10001", "10001", "10001", "10001", "01010", "00100",
]),
'W' => Some([
"10001", "10001", "10001", "10101", "10101", "10101", "01010",
]),
'X' => Some([
"10001", "10001", "01010", "00100", "01010", "10001", "10001",
]),
'Y' => Some([
"10001", "10001", "01010", "00100", "00100", "00100", "00100",
]),
'Z' => Some([
"11111", "00001", "00010", "00100", "01000", "10000", "11111",
]),
'0' => Some([
"01110", "10001", "10011", "10101", "11001", "10001", "01110",
]),
'1' => Some([
"00100", "01100", "00100", "00100", "00100", "00100", "01110",
]),
'2' => Some([
"01110", "10001", "00001", "00010", "00100", "01000", "11111",
]),
'3' => Some([
"11110", "00001", "00001", "01110", "00001", "00001", "11110",
]),
'4' => Some([
"00010", "00110", "01010", "10010", "11111", "00010", "00010",
]),
'5' => Some([
"11111", "10000", "10000", "11110", "00001", "00001", "11110",
]),
'6' => Some([
"01110", "10000", "10000", "11110", "10001", "10001", "01110",
]),
'7' => Some([
"11111", "00001", "00010", "00100", "01000", "01000", "01000",
]),
'8' => Some([
"01110", "10001", "10001", "01110", "10001", "10001", "01110",
]),
'9' => Some([
"01110", "10001", "10001", "01111", "00001", "00001", "01110",
]),
'a' => Some([
"00000", "00000", "01110", "00001", "01111", "10001", "01111",
]),
'b' => Some([
"10000", "10000", "11110", "10001", "10001", "10001", "11110",
]),
'c' => Some([
"00000", "00000", "01111", "10000", "10000", "10000", "01111",
]),
'd' => Some([
"00001", "00001", "01111", "10001", "10001", "10001", "01111",
]),
'e' => Some([
"00000", "00000", "01110", "10001", "11111", "10000", "01111",
]),
'f' => Some([
"00111", "01000", "01000", "11110", "01000", "01000", "01000",
]),
'g' => Some([
"00000", "00000", "01111", "10001", "10001", "01111", "00001",
]),
'h' => Some([
"10000", "10000", "11110", "10001", "10001", "10001", "10001",
]),
'i' => Some([
"00100", "00000", "00100", "00100", "00100", "00100", "00100",
]),
'j' => Some([
"00010", "00000", "00010", "00010", "00010", "10010", "01100",
]),
'k' => Some([
"10000", "10000", "10010", "10100", "11000", "10100", "10010",
]),
'l' => Some([
"01000", "01000", "01000", "01000", "01000", "01000", "01110",
]),
'm' => Some([
"00000", "00000", "11110", "10101", "10101", "10101", "10101",
]),
'n' => Some([
"00000", "00000", "11110", "10001", "10001", "10001", "10001",
]),
'o' => Some([
"00000", "00000", "01110", "10001", "10001", "10001", "01110",
]),
'p' => Some([
"00000", "00000", "11110", "10001", "10001", "11110", "10000",
]),
'q' => Some([
"00000", "00000", "01111", "10001", "10001", "01111", "00001",
]),
'r' => Some([
"00000", "00000", "11010", "10001", "10000", "10000", "10000",
]),
's' => Some([
"00000", "00000", "01111", "10000", "01110", "00001", "11110",
]),
't' => Some([
"01000", "01000", "11110", "01000", "01000", "01001", "00110",
]),
'u' => Some([
"00000", "00000", "10001", "10001", "10001", "10001", "01110",
]),
'v' => Some([
"00000", "00000", "10001", "10001", "10001", "01010", "00100",
]),
'w' => Some([
"00000", "00000", "10001", "10001", "10101", "10101", "01010",
]),
'x' => Some([
"00000", "00000", "10001", "01010", "00100", "01010", "10001",
]),
'y' => Some([
"00000", "00000", "10001", "10001", "10001", "01111", "00001",
]),
'z' => Some([
"00000", "00000", "11111", "00010", "00100", "01000", "11111",
]),
' ' => Some([
"00000", "00000", "00000", "00000", "00000", "00000", "00000",
]),
'.' => Some([
"00000", "00000", "00000", "00000", "00000", "00100", "00100",
]),
'-' => Some([
"00000", "00000", "00000", "11111", "00000", "00000", "00000",
]),
':' => Some([
"00000", "00100", "00100", "00000", "00100", "00100", "00000",
]),
'/' => Some([
"00001", "00010", "00100", "01000", "10000", "00000", "00000",
]),
',' => Some([
"00000", "00000", "00000", "00000", "00000", "00100", "01000",
]),
'!' => Some([
"00100", "00100", "00100", "00100", "00100", "00000", "00100",
]),
'?' => Some([
"01110", "10001", "00001", "00010", "00100", "00000", "00100",
]),
';' => Some([
"00000", "00100", "00100", "00000", "00100", "01000", "00000",
]),
'(' => Some([
"00010", "00100", "01000", "01000", "01000", "00100", "00010",
]),
')' => Some([
"01000", "00100", "00010", "00010", "00010", "00100", "01000",
]),
'[' => Some([
"01110", "01000", "01000", "01000", "01000", "01000", "01110",
]),
']' => Some([
"01110", "00010", "00010", "00010", "00010", "00010", "01110",
]),
'{' => Some([
"00010", "00100", "00100", "01000", "00100", "00100", "00010",
]),
'}' => Some([
"01000", "00100", "00100", "00010", "00100", "00100", "01000",
]),
'"' => Some([
"01010", "01010", "00000", "00000", "00000", "00000", "00000",
]),
'\'' => Some([
"00100", "00100", "00000", "00000", "00000", "00000", "00000",
]),
'#' => Some([
"01010", "01010", "11111", "01010", "11111", "01010", "01010",
]),
'$' => Some([
"00100", "01111", "10100", "01110", "00101", "11110", "00100",
]),
'%' => Some([
"11001", "11010", "00010", "00100", "01000", "01011", "10011",
]),
'&' => Some([
"01100", "10010", "10100", "01000", "10101", "10010", "01101",
]),
'*' => Some([
"00000", "01010", "10101", "01110", "10101", "01010", "00000",
]),
'+' => Some([
"00000", "00100", "00100", "11111", "00100", "00100", "00000",
]),
'<' => Some([
"00001", "00010", "00100", "01000", "00100", "00010", "00001",
]),
'=' => Some([
"00000", "11111", "00000", "11111", "00000", "00000", "00000",
]),
'>' => Some([
"10000", "01000", "00100", "00010", "00100", "01000", "10000",
]),
'@' => Some([
"01110", "10001", "10111", "10101", "10111", "10000", "01110",
]),
'\\' => Some([
"10000", "01000", "00100", "00010", "00001", "00000", "00000",
]),
'^' => Some([
"00100", "01010", "10001", "00000", "00000", "00000", "00000",
]),
'_' => Some([
"00000", "00000", "00000", "00000", "00000", "00000", "11111",
]),
'`' => Some([
"01000", "00100", "00010", "00000", "00000", "00000", "00000",
]),
'|' => Some([
"00100", "00100", "00100", "00100", "00100", "00100", "00100",
]),
'~' => Some([
"00000", "00000", "01010", "10101", "00000", "00000", "00000",
]),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_render_bitmap_text() {
let img = render_text_bitmap("AB", 64, Luma([255]), Luma([0]), 8);
assert!(img.width() > 0);
assert_eq!(img.height(), 64);
}
#[test]
fn test_all_ascii_glyphs_exist() {
let chars: Vec<char> = (' '..='~').collect();
let mut missing = Vec::new();
for ch in chars {
if glyph_5x7_rows(ch).is_none() {
missing.push(ch);
}
}
assert!(missing.is_empty(), "Missing glyphs for: {:?}", missing);
}
}