use crate::{Color, Image};
pub(crate) const GLYPH_WIDTH: u32 = 3;
pub(crate) const ADVANCE: u32 = 4;
pub(crate) const HEIGHT: u32 = 5;
#[rustfmt::skip]
const GLYPHS: &[(char, [&str; 5])] = &[
('A', [".#.", "#.#", "###", "#.#", "#.#"]),
('B', ["##.", "#.#", "##.", "#.#", "##."]),
('C', [".##", "#..", "#..", "#..", ".##"]),
('D', ["##.", "#.#", "#.#", "#.#", "##."]),
('E', ["###", "#..", "##.", "#..", "###"]),
('F', ["###", "#..", "##.", "#..", "#.."]),
('G', [".##", "#..", "#.#", "#.#", ".##"]),
('H', ["#.#", "#.#", "###", "#.#", "#.#"]),
('I', ["###", ".#.", ".#.", ".#.", "###"]),
('J', ["..#", "..#", "..#", "#.#", ".#."]),
('K', ["#.#", "#.#", "##.", "#.#", "#.#"]),
('L', ["#..", "#..", "#..", "#..", "###"]),
('M', ["#.#", "###", "###", "#.#", "#.#"]),
('N', ["##.", "#.#", "#.#", "#.#", "#.#"]),
('O', [".#.", "#.#", "#.#", "#.#", ".#."]),
('P', ["##.", "#.#", "##.", "#..", "#.."]),
('Q', [".#.", "#.#", "#.#", "##.", ".##"]),
('R', ["##.", "#.#", "##.", "#.#", "#.#"]),
('S', [".##", "#..", ".#.", "..#", "##."]),
('T', ["###", ".#.", ".#.", ".#.", ".#."]),
('U', ["#.#", "#.#", "#.#", "#.#", "###"]),
('V', ["#.#", "#.#", "#.#", "#.#", ".#."]),
('W', ["#.#", "#.#", "###", "###", "#.#"]),
('X', ["#.#", "#.#", ".#.", "#.#", "#.#"]),
('Y', ["#.#", "#.#", ".#.", ".#.", ".#."]),
('Z', ["###", "..#", ".#.", "#..", "###"]),
('0', ["###", "#.#", "#.#", "#.#", "###"]),
('1', [".#.", "##.", ".#.", ".#.", "###"]),
('2', ["##.", "..#", ".#.", "#..", "###"]),
('3', ["##.", "..#", ".#.", "..#", "##."]),
('4', ["#.#", "#.#", "###", "..#", "..#"]),
('5', ["###", "#..", "##.", "..#", "##."]),
('6', [".##", "#..", "###", "#.#", "###"]),
('7', ["###", "..#", ".#.", ".#.", ".#."]),
('8', ["###", "#.#", "###", "#.#", "###"]),
('9', ["###", "#.#", "###", "..#", "##."]),
(' ', ["...", "...", "...", "...", "..."]),
('_', ["...", "...", "...", "...", "###"]),
('-', ["...", "...", "###", "...", "..."]),
(':', ["...", ".#.", "...", ".#.", "..."]),
('.', ["...", "...", "...", "...", ".#."]),
(',', ["...", "...", "...", ".#.", "#.."]),
('(', [".#.", "#..", "#..", "#..", ".#."]),
(')', [".#.", "..#", "..#", "..#", ".#."]),
('[', ["##.", "#..", "#..", "#..", "##."]),
(']', [".##", "..#", "..#", "..#", ".##"]),
('<', ["..#", ".#.", "#..", ".#.", "..#"]),
('>', ["#..", ".#.", "..#", ".#.", "#.."]),
('=', ["...", "###", "...", "###", "..."]),
('+', ["...", ".#.", "###", ".#.", "..."]),
('/', ["..#", "..#", ".#.", "#..", "#.."]),
('#', ["#.#", "###", "#.#", "###", "#.#"]),
('*', ["#.#", ".#.", "#.#", "...", "..."]),
('\'', [".#.", ".#.", "...", "...", "..."]),
('!', [".#.", ".#.", ".#.", "...", ".#."]),
('?', ["##.", "..#", ".#.", "...", ".#."]),
];
fn glyph(c: char) -> &'static [&'static str; 5] {
let c = c.to_ascii_uppercase();
GLYPHS
.iter()
.find(|(g, _)| *g == c)
.or_else(|| GLYPHS.iter().find(|(g, _)| *g == '?'))
.map(|(_, rows)| rows)
.unwrap_or(&GLYPHS[0].1)
}
pub(crate) fn width(text: &str, scale: u32) -> u32 {
let n = text.chars().count() as u32;
if n == 0 {
0
} else {
(n * ADVANCE - (ADVANCE - GLYPH_WIDTH)) * scale
}
}
pub(crate) fn draw(image: &mut Image, text: &str, x: i64, y: i64, scale: u32, color: Color) {
for (i, c) in text.chars().enumerate() {
let left = x + (i as u32 * ADVANCE * scale) as i64;
for (row, line) in glyph(c).iter().enumerate() {
for (col, pixel) in line.bytes().enumerate() {
if pixel == b'#' {
image.fill_rect(
left + (col as u32 * scale) as i64,
y + (row as u32 * scale) as i64,
scale,
scale,
color,
);
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn glyphs_are_well_formed_and_distinct() {
for (c, rows) in GLYPHS {
assert!(
rows.iter()
.all(|r| r.len() == 3 && r.bytes().all(|b| b == b'#' || b == b'.')),
"{c}"
);
}
for (i, (a, ra)) in GLYPHS.iter().enumerate() {
for (b, rb) in &GLYPHS[i + 1..] {
assert!(a != b && ra != rb, "{a} and {b}");
}
}
assert_eq!(glyph('a'), glyph('A'));
assert_eq!(glyph('é'), glyph('?'));
assert_eq!(width("ab", 2), 14);
}
}