pub(crate) const GLYPH_W: u32 = 5;
pub(crate) const GLYPH_H: u32 = 7;
pub(crate) const ADVANCE: u32 = GLYPH_W + 1;
#[cfg(any(feature = "png", feature = "svg"))]
mod bitmap {
use super::{GLYPH_H, GLYPH_W};
type Glyph = [u8; GLYPH_H as usize];
#[rustfmt::skip]
const DIGITS: [Glyph; 10] = [
[0b01110, 0b10001, 0b10011, 0b10101, 0b11001, 0b10001, 0b01110], [0b00100, 0b01100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110], [0b01110, 0b10001, 0b00001, 0b00010, 0b00100, 0b01000, 0b11111], [0b11111, 0b00010, 0b00100, 0b00010, 0b00001, 0b10001, 0b01110], [0b00010, 0b00110, 0b01010, 0b10010, 0b11111, 0b00010, 0b00010], [0b11111, 0b10000, 0b11110, 0b00001, 0b00001, 0b10001, 0b01110], [0b00110, 0b01000, 0b10000, 0b11110, 0b10001, 0b10001, 0b01110], [0b11111, 0b10001, 0b00001, 0b00010, 0b00100, 0b00100, 0b00100], [0b01110, 0b10001, 0b10001, 0b01110, 0b10001, 0b10001, 0b01110], [0b01110, 0b10001, 0b10001, 0b01111, 0b00001, 0b00010, 0b01100], ];
#[rustfmt::skip]
const LETTERS: [Glyph; 26] = [
[0b00100, 0b01010, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001], [0b11110, 0b10001, 0b10001, 0b11110, 0b10001, 0b10001, 0b11110], [0b01110, 0b10001, 0b10000, 0b10000, 0b10000, 0b10001, 0b01110], [0b11100, 0b10010, 0b10001, 0b10001, 0b10001, 0b10010, 0b11100], [0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b11111], [0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b10000], [0b01110, 0b10001, 0b10000, 0b10111, 0b10001, 0b10001, 0b01111], [0b10001, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b10001], [0b01110, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110], [0b00111, 0b00010, 0b00010, 0b00010, 0b00010, 0b10010, 0b01100], [0b10001, 0b10010, 0b10100, 0b11000, 0b10100, 0b10010, 0b10001], [0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b11111], [0b10001, 0b11011, 0b10101, 0b10101, 0b10001, 0b10001, 0b10001], [0b10001, 0b10001, 0b11001, 0b10101, 0b10011, 0b10001, 0b10001], [0b01110, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110], [0b11110, 0b10001, 0b10001, 0b11110, 0b10000, 0b10000, 0b10000], [0b01110, 0b10001, 0b10001, 0b10001, 0b10101, 0b10010, 0b01101], [0b11110, 0b10001, 0b10001, 0b11110, 0b10100, 0b10010, 0b10001], [0b01111, 0b10000, 0b10000, 0b01110, 0b00001, 0b00001, 0b11110], [0b11111, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100], [0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110], [0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01010, 0b00100], [0b10001, 0b10001, 0b10001, 0b10101, 0b10101, 0b11011, 0b10001], [0b10001, 0b10001, 0b01010, 0b00100, 0b01010, 0b10001, 0b10001], [0b10001, 0b10001, 0b01010, 0b00100, 0b00100, 0b00100, 0b00100], [0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b10000, 0b11111], ];
#[rustfmt::skip]
const SPACE: Glyph = [0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000];
#[rustfmt::skip]
const HYPHEN: Glyph = [0b00000, 0b00000, 0b00000, 0b11111, 0b00000, 0b00000, 0b00000];
#[rustfmt::skip]
const PERIOD: Glyph = [0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b01100, 0b01100];
#[rustfmt::skip]
const SLASH: Glyph = [0b00001, 0b00001, 0b00010, 0b00100, 0b01000, 0b10000, 0b10000];
#[rustfmt::skip]
const STAR: Glyph = [0b00000, 0b00000, 0b10101, 0b01110, 0b10101, 0b00000, 0b00000];
#[rustfmt::skip]
const PLUS: Glyph = [0b00000, 0b00100, 0b00100, 0b11111, 0b00100, 0b00100, 0b00000];
#[rustfmt::skip]
const DOLLAR: Glyph = [0b00100, 0b01111, 0b10100, 0b01110, 0b00101, 0b11110, 0b00100];
#[rustfmt::skip]
const PERCENT:Glyph = [0b11001, 0b11010, 0b00010, 0b00100, 0b01000, 0b01011, 0b10011];
#[rustfmt::skip]
const COLON: Glyph = [0b00000, 0b00000, 0b01100, 0b01100, 0b00000, 0b01100, 0b01100];
#[rustfmt::skip]
pub(crate) const TOFU: Glyph = [0b11111, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b11111];
pub(crate) fn glyph(c: char) -> &'static Glyph {
match c {
'0'..='9' => &DIGITS[c as usize - '0' as usize],
'A'..='Z' => &LETTERS[c as usize - 'A' as usize],
' ' => &SPACE,
'-' => &HYPHEN,
'.' => &PERIOD,
'/' => &SLASH,
'*' => &STAR,
'+' => &PLUS,
'$' => &DOLLAR,
'%' => &PERCENT,
':' => &COLON,
_ => &TOFU,
}
}
pub(crate) fn pixel(glyph: &Glyph, x: u32, y: u32) -> bool {
if x >= GLYPH_W || y >= GLYPH_H {
return false;
}
let shift = GLYPH_W - 1 - x;
(glyph[y as usize] >> shift) & 1 == 1
}
}
#[cfg(any(feature = "png", feature = "svg"))]
pub(crate) use bitmap::{glyph, pixel};
pub(crate) fn is_supported(c: char) -> bool {
matches!(c, '0'..='9' | 'A'..='Z' | ' ' | '-' | '.' | '/' | '*' | '+' | '$' | '%' | ':')
}
pub(crate) fn text_width(char_count: u32) -> u32 {
if char_count == 0 {
0
} else {
char_count * ADVANCE - 1
}
}
#[cfg(all(test, any(feature = "png", feature = "svg")))]
mod tests {
use super::*;
use bitmap::TOFU;
#[test]
fn every_glyph_fits_five_columns() {
for c in ('0'..='9').chain('A'..='Z').chain(" -./*+$%:".chars()) {
for row in glyph(c) {
assert_eq!(row & !0b11111, 0, "glyph `{c}` has bits outside 5 columns");
}
}
}
#[test]
fn distinct_characters_have_distinct_glyphs() {
let chars: alloc::vec::Vec<char> = ('0'..='9').chain('A'..='Z').collect();
for (i, a) in chars.iter().enumerate() {
for b in &chars[i + 1..] {
assert_ne!(glyph(*a), glyph(*b), "`{a}` and `{b}` share a glyph");
}
}
}
#[test]
fn no_alphanumeric_glyph_is_blank() {
for c in ('0'..='9').chain('A'..='Z') {
assert!(glyph(c).iter().any(|r| *r != 0), "glyph `{c}` is blank");
}
}
#[test]
fn unsupported_characters_fall_back_to_the_box() {
assert_eq!(glyph('a'), &TOFU);
assert_eq!(glyph('\u{4e2d}'), &TOFU);
assert!(!is_supported('a'));
assert!(is_supported('Z'));
}
#[test]
fn pixels_are_addressed_left_to_right() {
let one = glyph('1');
assert!(!pixel(one, 0, 0));
assert!(pixel(one, 2, 0));
assert!(!pixel(one, 9, 0));
}
#[test]
fn text_width_accounts_for_inter_character_gaps() {
assert_eq!(text_width(0), 0);
assert_eq!(text_width(1), 5);
assert_eq!(text_width(3), 17); }
}