#[derive(Debug, Clone, Copy)]
pub struct BitmapFont {
data: &'static [u8],
pub glyph_width: u8,
pub glyph_height: u8,
glyph_count: u16,
charset: Option<&'static [(char, u8)]>,
}
impl BitmapFont {
#[must_use]
pub const fn new(
data: &'static [u8],
glyph_width: u8,
glyph_height: u8,
glyph_count: u16,
) -> Self {
Self {
data,
glyph_width,
glyph_height,
glyph_count,
charset: None,
}
}
#[must_use]
pub const fn with_charset(
data: &'static [u8],
glyph_width: u8,
glyph_height: u8,
glyph_count: u16,
charset: &'static [(char, u8)],
) -> Self {
Self {
data,
glyph_width,
glyph_height,
glyph_count,
charset: Some(charset),
}
}
#[must_use]
pub fn rows(&self, index: u8) -> &[u8] {
assert!(
u16::from(index) < self.glyph_count,
"glyph index {index} out of range ({})",
self.glyph_count,
);
let h = usize::from(self.glyph_height);
let start = usize::from(index) * h;
&self.data[start..start + h]
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub fn glyph_pixels(&self, index: u8) -> impl Iterator<Item = (u8, u8)> + '_ {
let width = self.glyph_width;
self.rows(index)
.iter()
.enumerate()
.flat_map(move |(y, &row)| {
#[allow(clippy::cast_possible_truncation)]
let y = y as u8;
(0..width)
.filter_map(move |x| ((row >> (width - 1 - x)) & 1 == 1).then_some((x, y)))
})
}
#[must_use]
pub const fn glyph_count(&self) -> u16 {
self.glyph_count
}
#[must_use]
pub const fn glyph_index(&self, ch: char) -> Option<u8> {
if let Some(table) = self.charset {
let mut i = 0;
while i < table.len() {
let (table_ch, index) = table[i];
if table_ch == ch && (index as u16) < self.glyph_count {
return Some(index);
}
i += 1;
}
return None;
}
match try_unicode_to_cp437(ch) {
Some(index) if (index as u16) < self.glyph_count => Some(index),
_ => None,
}
}
}
impl PartialEq for BitmapFont {
fn eq(&self, other: &Self) -> bool {
core::ptr::eq(self.data.as_ptr(), other.data.as_ptr())
&& self.glyph_width == other.glyph_width
&& self.glyph_height == other.glyph_height
&& self.glyph_count == other.glyph_count
}
}
impl Eq for BitmapFont {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ResolvedGlyph {
font: BitmapFont,
font_index: usize,
index: u8,
notdef: bool,
}
impl ResolvedGlyph {
#[must_use]
pub const fn font(&self) -> &BitmapFont {
&self.font
}
#[must_use]
pub const fn font_index(&self) -> usize {
self.font_index
}
#[must_use]
pub const fn index(&self) -> u8 {
self.index
}
#[must_use]
pub const fn is_notdef(&self) -> bool {
self.notdef
}
#[must_use]
pub fn rows(&self) -> &[u8] {
self.font.rows(self.index)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FontChain<'a> {
primary: BitmapFont,
fallbacks: &'a [BitmapFont],
}
impl From<BitmapFont> for FontChain<'static> {
fn from(font: BitmapFont) -> Self {
Self::new(font, &[])
}
}
impl<'a> FontChain<'a> {
#[must_use]
pub const fn new(primary: BitmapFont, fallbacks: &'a [BitmapFont]) -> Self {
Self { primary, fallbacks }
}
pub fn fonts(&self) -> impl Iterator<Item = &BitmapFont> {
core::iter::once(&self.primary).chain(self.fallbacks.iter())
}
#[must_use]
pub const fn font_count(&self) -> usize {
1 + self.fallbacks.len()
}
#[must_use]
pub fn glyph_size(&self) -> Option<(u8, u8)> {
let size = (self.primary.glyph_width, self.primary.glyph_height);
self.fallbacks
.iter()
.all(|f| (f.glyph_width, f.glyph_height) == size)
.then_some(size)
}
#[must_use]
pub fn resolve(&self, ch: char) -> Option<ResolvedGlyph> {
self.lookup(ch, false).or_else(|| self.lookup(NOTDEF, true))
}
fn lookup(&self, ch: char, notdef: bool) -> Option<ResolvedGlyph> {
self.fonts()
.enumerate()
.find_map(|(font_index, font)| {
font.glyph_index(ch).map(|index| (font_index, font, index))
})
.map(|(font_index, font, index)| ResolvedGlyph {
font: *font,
font_index,
index,
notdef,
})
}
}
#[cfg(feature = "default-font")]
pub mod unscii16 {
use super::BitmapFont;
pub const FONT: BitmapFont = BitmapFont::new(&DATA, 8, 16, 256);
#[rustfmt::skip]
static DATA: [u8; 4096] = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x54, 0xfe, 0xfe, 0x54, 0x10, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0xfe, 0xfe, 0x38, 0x38, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xe7, 0xc3, 0xc3, 0xc3, 0xc3, 0xe7, 0xe7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x3c, 0x3c, 0x66, 0x66, 0x42, 0x42, 0x42, 0x42, 0x66, 0x66, 0x3c, 0x3c, 0x00, 0x00, 0xff, 0xff, 0xc3, 0xc3, 0x99, 0x99, 0xbd, 0xbd, 0xbd, 0xbd, 0x99, 0x99, 0xc3, 0xc3, 0xff, 0xff, 0x00, 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x00, 0x00, 0x00, 0x18, 0x1c, 0x1e, 0x1b, 0x18, 0x18, 0x78, 0xf8, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x18, 0xbd, 0x7e, 0x7e, 0xbd, 0x18, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xf0, 0xfc, 0xff, 0xfc, 0xf0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0f, 0x3f, 0xff, 0x3f, 0x0f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x7a, 0x7a, 0x7a, 0x7a, 0x3a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x30, 0x38, 0x6c, 0x66, 0x36, 0x1c, 0x0c, 0x06, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0xff, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0c, 0xfe, 0xfe, 0x0c, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x30, 0x7f, 0x7f, 0x30, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x3c, 0x7e, 0x7e, 0x7e, 0x7e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x7e, 0x7e, 0x7e, 0x3c, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x3c, 0x66, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x66, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x06, 0xc6, 0xcc, 0xcc, 0x18, 0x18, 0x30, 0x30, 0x66, 0x66, 0xc6, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x30, 0x7a, 0xde, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x18, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x03, 0x03, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x60, 0x60, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xce, 0xd6, 0xe6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x06, 0x06, 0x1c, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x60, 0x60, 0x60, 0x7c, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x76, 0x3c, 0x6e, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x06, 0x0c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x38, 0x18, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x06, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x60, 0x60, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x60, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x60, 0x60, 0x6e, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xcc, 0xcc, 0xd8, 0xf0, 0xd8, 0xcc, 0xcc, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xee, 0xee, 0xfe, 0xd6, 0xd6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xe6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xce, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xfe, 0xee, 0xee, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x30, 0x30, 0x60, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0xc0, 0xc0, 0x60, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x03, 0x03, 0x00, 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x18, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x7e, 0x60, 0x60, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x7c, 0x00, 0x00, 0x60, 0x60, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x78, 0x00, 0x00, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xfe, 0xd6, 0xd6, 0xd6, 0xd6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x60, 0x60, 0x3c, 0x06, 0x06, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0x7c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf0, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0xe0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x1e, 0x30, 0x30, 0x30, 0x30, 0x30, 0xe0, 0x00, 0x00, 0x00, 0x72, 0xd6, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x3c, 0x0c, 0x06, 0x1c, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x7e, 0x60, 0x60, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66, 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x3c, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x1c, 0x00, 0x18, 0x3c, 0x66, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x7e, 0x60, 0x60, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x7e, 0x60, 0x60, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x7e, 0x60, 0x60, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x3c, 0x00, 0x18, 0x3c, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x00, 0x7e, 0x60, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x1b, 0x1b, 0x7f, 0xd8, 0xd8, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x7c, 0xfc, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x3c, 0x66, 0x66, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x60, 0x60, 0xf0, 0x60, 0x60, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0xc6, 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x06, 0x06, 0x3e, 0x66, 0x66, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x30, 0x30, 0x60, 0x60, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xc6, 0x46, 0x4c, 0x4c, 0x18, 0x18, 0x30, 0x30, 0x6c, 0x62, 0xc4, 0xc8, 0x0e, 0x00, 0x00, 0x40, 0xc6, 0x46, 0x4c, 0x4c, 0x18, 0x18, 0x30, 0x30, 0x62, 0x66, 0xca, 0xcf, 0x02, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xec, 0xec, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xec, 0x0c, 0xec, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0c, 0xec, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xec, 0x0c, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6f, 0x6f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6f, 0x60, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x60, 0x6f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xef, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xef, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6f, 0x60, 0x6f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xef, 0x00, 0xef, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xff, 0xff, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xce, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xcc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x7c, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0x7c, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xee, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0x60, 0x30, 0x18, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdb, 0xdb, 0xdb, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc0, 0xdc, 0xd6, 0xd6, 0xd6, 0x7c, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x60, 0x60, 0x3c, 0x60, 0x60, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0xd6, 0x9c, 0x00, 0x72, 0xd6, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x06, 0x06, 0x06, 0x06, 0x06, 0xcc, 0xcc, 0x6c, 0x38, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x0c, 0x18, 0x30, 0x60, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
}
#[cfg(feature = "legacy-computing")]
pub mod legacy_computing {
pub mod blocks {
use crate::font::BitmapFont;
const QUADRANT_COUNT: usize = 10;
const SEXTANT_COUNT: usize = 60;
const TOTAL: usize = QUADRANT_COUNT + SEXTANT_COUNT;
#[allow(clippy::cast_possible_truncation)]
pub const FONT: BitmapFont = BitmapFont::with_charset(&DATA, 8, 16, TOTAL as u16, &CHARSET);
#[rustfmt::skip]
const QUADRANTS: [(u8, char); QUADRANT_COUNT] = [
(1, '▘'), (2, '▝'), (4, '▖'), (6, '▞'), (7, '▛'),
(8, '▗'), (9, '▚'), (11, '▜'), (13, '▙'), (14, '▟'),
];
const fn sextant_masks() -> [u8; SEXTANT_COUNT] {
let mut masks = [0u8; SEXTANT_COUNT];
let mut m: u16 = 1;
let mut i = 0;
while m <= 62 {
if m != 21 && m != 42 {
#[allow(clippy::cast_possible_truncation)]
{
masks[i] = m as u8;
}
i += 1;
}
m += 1;
}
masks
}
const fn sextant_codepoint(mask: u8) -> u32 {
let gaps_below = if mask > 21 { 1 } else { 0 } + if mask > 42 { 1 } else { 0 };
0x1_FB00 + (mask as u32 - 1) - gaps_below
}
const fn set_pixel(data: &mut [u8; TOTAL * 16], index: usize, x: u8, y: u8) {
let row = index * 16 + y as usize;
data[row] |= 1 << (7 - x);
}
const fn build_data() -> [u8; TOTAL * 16] {
let mut data = [0u8; TOTAL * 16];
let mut qi = 0;
while qi < QUADRANT_COUNT {
let (mask, _) = QUADRANTS[qi];
let mut y = 0u8;
while y < 16 {
let mut x = 0u8;
while x < 8 {
let bit = if x < 4 {
if y < 8 { 0 } else { 2 }
} else if y < 8 {
1
} else {
3
};
if (mask >> bit) & 1 == 1 {
set_pixel(&mut data, qi, x, y);
}
x += 1;
}
y += 1;
}
qi += 1;
}
let masks = sextant_masks();
let mut si = 0;
while si < SEXTANT_COUNT {
let mask = masks[si];
let index = QUADRANT_COUNT + si;
let mut y = 0u8;
while y < 16 {
let row = if y < 5 {
0
} else if y < 11 {
1
} else {
2
};
let mut x = 0u8;
while x < 8 {
let col: usize = if x >= 4 { 1 } else { 0 };
let bit = row * 2 + col;
if (mask >> bit) & 1 == 1 {
set_pixel(&mut data, index, x, y);
}
x += 1;
}
y += 1;
}
si += 1;
}
data
}
const fn build_charset() -> [(char, u8); TOTAL] {
let mut charset = [('\0', 0u8); TOTAL];
let mut qi = 0;
while qi < QUADRANT_COUNT {
let (_, ch) = QUADRANTS[qi];
#[allow(clippy::cast_possible_truncation)]
{
charset[qi] = (ch, qi as u8);
}
qi += 1;
}
let masks = sextant_masks();
let mut si = 0;
while si < SEXTANT_COUNT {
let cp = sextant_codepoint(masks[si]);
let Some(ch) = char::from_u32(cp) else {
panic!("sextant codepoint is not a valid char")
};
let index = QUADRANT_COUNT + si;
#[allow(clippy::cast_possible_truncation)]
{
charset[index] = (ch, index as u8);
}
si += 1;
}
charset
}
static DATA: [u8; TOTAL * 16] = build_data();
static CHARSET: [(char, u8); TOTAL] = build_charset();
#[cfg(test)]
mod tests {
use super::{CHARSET, FONT, SEXTANT_COUNT, TOTAL, sextant_codepoint, sextant_masks};
use crate::font::FontChain;
use std::collections::HashSet;
#[test]
fn total_glyph_count_matches_quadrants_plus_sextants() {
assert_eq!(TOTAL, 10 + 60);
assert_eq!(FONT.glyph_count(), u16::try_from(TOTAL).unwrap());
}
#[test]
fn no_charset_entry_duplicates_a_codepoint_cp437_already_serves() {
let already_cp437: HashSet<char> = [' ', '▀', '▄', '▌', '▐', '█', '░', '▒', '▓']
.into_iter()
.collect();
for &(ch, _) in &CHARSET {
assert!(
!already_cp437.contains(&ch),
"{ch:?} (U+{:04X}) duplicates existing CP437 coverage",
ch as u32
);
}
}
#[test]
fn every_charset_character_appears_exactly_once() {
let mut seen = HashSet::with_capacity(TOTAL);
for &(ch, _) in &CHARSET {
assert!(seen.insert(ch), "{ch:?} appears more than once in CHARSET");
}
assert_eq!(seen.len(), TOTAL);
}
#[test]
fn sextant_masks_skip_21_and_42() {
let masks = sextant_masks();
assert_eq!(masks.len(), SEXTANT_COUNT);
assert!(!masks.contains(&21));
assert!(!masks.contains(&42));
assert_eq!(masks[0], 1);
assert_eq!(masks[SEXTANT_COUNT - 1], 62);
}
#[test]
fn sextant_codepoint_shifts_down_after_each_gap() {
assert_eq!(sextant_codepoint(1), 0x1FB00);
assert_eq!(sextant_codepoint(22), 0x1FB00 + 21 - 1);
assert_eq!(sextant_codepoint(43), 0x1FB00 + 42 - 2);
}
#[test]
fn quadrant_top_left_mask_only_fills_the_top_left_quarter() {
let index = FONT.glyph_index('▘').expect("U+2598 is covered");
for (x, y) in FONT.glyph_pixels(index) {
assert!(x < 4 && y < 8, "({x}, {y}) outside the top-left quarter");
}
}
#[test]
fn font_chain_resolves_quadrant_via_blocks_fallback() {
static PRIMARY_DATA: [u8; 256 * 16] = [0; 256 * 16];
const PRIMARY: crate::font::BitmapFont =
crate::font::BitmapFont::new(&PRIMARY_DATA, 8, 16, 256);
static FALLBACKS: [crate::font::BitmapFont; 1] = [FONT];
let chain = FontChain::new(PRIMARY, &FALLBACKS);
let quadrant = chain
.resolve('▘')
.expect("covered by legacy_computing::blocks");
assert_eq!(quadrant.font_index(), 1);
assert!(!quadrant.is_notdef());
}
}
}
pub mod braille {
use crate::font::BitmapFont;
const TOTAL: usize = 256;
#[allow(clippy::cast_possible_truncation)]
pub const FONT: BitmapFont = BitmapFont::with_charset(&DATA, 8, 16, TOTAL as u16, &CHARSET);
const COL_X: [u8; 2] = [1, 4];
const ROW_Y: [u8; 4] = [1, 5, 9, 13];
const fn bit_index(col: usize, row: usize) -> u32 {
match (col, row) {
(0, 0) => 0,
(0, 1) => 1,
(0, 2) => 2,
(0, 3) => 6,
(1, 0) => 3,
(1, 1) => 4,
(1, 2) => 5,
(1, 3) => 7,
_ => panic!("braille dot position out of range"),
}
}
const fn set_pixel(data: &mut [u8; TOTAL * 16], index: usize, x: u8, y: u8) {
let row = index * 16 + y as usize;
data[row] |= 1 << (7 - x);
}
const fn build_data() -> [u8; TOTAL * 16] {
#[allow(clippy::cast_possible_truncation)]
const TOTAL_U32: u32 = TOTAL as u32;
let mut data = [0u8; TOTAL * 16];
let mut bits: u32 = 0;
while bits < TOTAL_U32 {
let index = bits as usize;
let mut col = 0usize;
while col < 2 {
let mut row = 0usize;
while row < 4 {
let bit = bit_index(col, row);
if (bits >> bit) & 1 == 1 {
let cx = COL_X[col];
let cy = ROW_Y[row];
let mut dy: i32 = -1;
while dy <= 1 {
let mut dx: i32 = -1;
while dx <= 1 {
let px = cx as i32 + dx;
let py = cy as i32 + dy;
if px >= 0 && px < 8 && py >= 0 && py < 16 {
#[allow(
clippy::cast_sign_loss,
clippy::cast_possible_truncation
)]
set_pixel(&mut data, index, px as u8, py as u8);
}
dx += 1;
}
dy += 1;
}
}
row += 1;
}
col += 1;
}
bits += 1;
}
data
}
const fn build_charset() -> [(char, u8); TOTAL] {
#[allow(clippy::cast_possible_truncation)]
const TOTAL_U32: u32 = TOTAL as u32;
let mut charset = [('\0', 0u8); TOTAL];
let mut bits: u32 = 0;
while bits < TOTAL_U32 {
let cp = 0x2800 + bits;
let Some(ch) = char::from_u32(cp) else {
panic!("braille codepoint is not a valid char")
};
let index = bits as usize;
#[allow(clippy::cast_possible_truncation)]
{
charset[index] = (ch, index as u8);
}
bits += 1;
}
charset
}
static DATA: [u8; TOTAL * 16] = build_data();
static CHARSET: [(char, u8); TOTAL] = build_charset();
#[cfg(test)]
mod tests {
use super::{CHARSET, FONT, TOTAL};
use crate::font::FontChain;
use std::collections::HashSet;
#[test]
fn total_glyph_count_is_256() {
assert_eq!(TOTAL, 256);
assert_eq!(FONT.glyph_count(), 256);
}
#[test]
fn every_charset_character_appears_exactly_once() {
let mut seen = HashSet::with_capacity(TOTAL);
for &(ch, _) in &CHARSET {
assert!(seen.insert(ch), "{ch:?} appears more than once in CHARSET");
}
assert_eq!(seen.len(), TOTAL);
}
#[test]
fn covers_the_full_u2800_block() {
for bits in 0u32..u32::try_from(TOTAL).unwrap() {
let ch = char::from_u32(0x2800 + bits).unwrap();
assert_eq!(CHARSET[bits as usize].0, ch);
assert_eq!(CHARSET[bits as usize].1, u8::try_from(bits).unwrap());
}
}
#[test]
fn blank_glyph_is_all_zero_bits() {
let index = FONT.glyph_index('\u{2800}').expect("U+2800 is covered");
assert!(FONT.rows(index).iter().all(|&b| b == 0));
}
#[test]
fn full_glyph_has_all_dot_positions_set() {
let index = FONT.glyph_index('\u{28FF}').expect("U+28FF is covered");
let pixel_count = FONT.glyph_pixels(index).count();
assert_eq!(pixel_count, 72);
}
#[test]
fn font_chain_resolves_via_braille_fallback() {
static PRIMARY_DATA: [u8; 256 * 16] = [0; 256 * 16];
const PRIMARY: crate::font::BitmapFont =
crate::font::BitmapFont::new(&PRIMARY_DATA, 8, 16, 256);
static FALLBACKS: [crate::font::BitmapFont; 1] = [FONT];
let chain = FontChain::new(PRIMARY, &FALLBACKS);
let braille = chain
.resolve('\u{2837}')
.expect("covered by legacy_computing::braille");
assert_eq!(braille.font_index(), 1);
assert!(!braille.is_notdef());
let full_block = chain.resolve('█').expect("CP437 coverage");
assert_eq!(*full_block.font(), PRIMARY);
assert!(!full_block.is_notdef());
}
}
}
}
const NOTDEF: char = '█';
#[allow(clippy::too_many_lines)]
const fn try_unicode_to_cp437(ch: char) -> Option<u8> {
let u = ch as u32;
if u < 0x80 {
#[allow(clippy::cast_possible_truncation)]
return Some(u as u8);
}
match ch {
'Ç' => Some(0x80),
'ü' => Some(0x81),
'é' => Some(0x82),
'â' => Some(0x83),
'ä' => Some(0x84),
'à' => Some(0x85),
'å' => Some(0x86),
'ç' => Some(0x87),
'ê' => Some(0x88),
'ë' => Some(0x89),
'è' => Some(0x8A),
'ï' => Some(0x8B),
'î' => Some(0x8C),
'ì' => Some(0x8D),
'Ä' => Some(0x8E),
'Å' => Some(0x8F),
'É' => Some(0x90),
'æ' => Some(0x91),
'Æ' => Some(0x92),
'ô' => Some(0x93),
'ö' => Some(0x94),
'ò' => Some(0x95),
'û' => Some(0x96),
'ù' => Some(0x97),
'ÿ' => Some(0x98),
'Ö' => Some(0x99),
'Ü' => Some(0x9A),
'¢' => Some(0x9B),
'£' => Some(0x9C),
'¥' => Some(0x9D),
'ƒ' => Some(0x9F),
'á' => Some(0xA0),
'í' => Some(0xA1),
'ó' => Some(0xA2),
'ú' => Some(0xA3),
'ñ' => Some(0xA4),
'Ñ' => Some(0xA5),
'ª' => Some(0xA6),
'º' => Some(0xA7),
'¿' => Some(0xA8),
'⌐' => Some(0xA9),
'½' => Some(0xAB),
'¼' => Some(0xAC),
'¡' => Some(0xAD),
'«' => Some(0xAE),
'»' => Some(0xAF),
'░' => Some(0xB0),
'▒' => Some(0xB1),
'▓' => Some(0xB2),
'│' => Some(0xB3),
'┤' => Some(0xB4),
'╡' => Some(0xB5),
'╢' => Some(0xB6),
'╖' => Some(0xB7),
'╕' => Some(0xB8),
'╣' => Some(0xB9),
'║' => Some(0xBA),
'╗' => Some(0xBB),
'╝' => Some(0xBC),
'╜' => Some(0xBD),
'╛' => Some(0xBE),
'┐' => Some(0xBF),
'└' => Some(0xC0),
'┴' => Some(0xC1),
'┬' => Some(0xC2),
'├' => Some(0xC3),
'─' => Some(0xC4),
'┼' => Some(0xC5),
'╞' => Some(0xC6),
'╟' => Some(0xC7),
'╚' => Some(0xC8),
'╔' => Some(0xC9),
'╩' => Some(0xCA),
'╦' => Some(0xCB),
'╠' => Some(0xCC),
'═' => Some(0xCD),
'╬' => Some(0xCE),
'╧' => Some(0xCF),
'╨' => Some(0xD0),
'╤' => Some(0xD1),
'╥' => Some(0xD2),
'╙' => Some(0xD3),
'╘' => Some(0xD4),
'╒' => Some(0xD5),
'╓' => Some(0xD6),
'╫' => Some(0xD7),
'╪' => Some(0xD8),
'┘' => Some(0xD9),
'┌' => Some(0xDA),
'█' => Some(0xDB),
'▄' => Some(0xDC),
'▌' => Some(0xDD),
'▐' => Some(0xDE),
'▀' => Some(0xDF),
'α' => Some(0xE0),
'ß' => Some(0xE1),
'Γ' => Some(0xE2),
'π' => Some(0xE3),
'Σ' => Some(0xE4),
'σ' => Some(0xE5),
'µ' | 'μ' => Some(0xE6),
'τ' => Some(0xE7),
'Φ' => Some(0xE8),
'Θ' => Some(0xE9),
'Ω' => Some(0xEA),
'δ' => Some(0xEB),
'∞' => Some(0xEC),
'φ' => Some(0xED),
'ε' => Some(0xEE),
'∩' => Some(0xEF),
'≡' => Some(0xF0),
'±' => Some(0xF1),
'≥' => Some(0xF2),
'≤' => Some(0xF3),
'⌠' => Some(0xF4),
'⌡' => Some(0xF5),
'÷' => Some(0xF6),
'≈' => Some(0xF7),
'°' => Some(0xF8),
'·' | '∙' => Some(0xF9),
'√' => Some(0xFB),
'ⁿ' => Some(0xFC),
'²' => Some(0xFD),
'■' => Some(0xFE),
'☺' => Some(0x01),
'•' => Some(0x07),
'☻' => Some(0x02),
'♥' => Some(0x03),
'♦' => Some(0x04),
'♣' => Some(0x05),
'♠' => Some(0x06),
'◘' => Some(0x08),
'○' => Some(0x09),
'◙' => Some(0x0A),
'♂' => Some(0x0B),
'♀' => Some(0x0C),
'♪' => Some(0x0D),
'♫' => Some(0x0E),
'☼' => Some(0x0F),
'►' => Some(0x10),
'◄' => Some(0x11),
'↕' => Some(0x12),
'‼' => Some(0x13),
'¶' => Some(0x14),
'§' => Some(0x15),
'▬' => Some(0x16),
'↨' => Some(0x17),
'↑' => Some(0x18),
'↓' => Some(0x19),
'→' => Some(0x1A),
'←' => Some(0x1B),
'∟' => Some(0x1C),
'↔' => Some(0x1D),
'▲' => Some(0x1E),
'▼' => Some(0x1F),
'⌂' => Some(0x7F),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::{BitmapFont, FontChain, try_unicode_to_cp437};
#[test]
fn patched_glyphs_are_reachable_by_char() {
assert_eq!(try_unicode_to_cp437('⌂'), Some(0x7F), "U+2302 HOUSE");
assert_eq!(
try_unicode_to_cp437('☼'),
Some(0x0F),
"U+263C WHITE SUN WITH RAYS"
);
assert_eq!(
try_unicode_to_cp437('⌐'),
Some(0xA9),
"U+2310 REVERSED NOT SIGN"
);
assert_eq!(
try_unicode_to_cp437('∙'),
Some(0xF9),
"U+2219 BULLET OPERATOR"
);
}
static PRIMARY_DATA: [u8; 128 * 16] = [0; 128 * 16];
const PRIMARY: BitmapFont = BitmapFont::new(&PRIMARY_DATA, 8, 16, 128);
static FALLBACK_DATA: [u8; 256 * 16] = [0; 256 * 16];
const FALLBACK_FONT: BitmapFont = BitmapFont::new(&FALLBACK_DATA, 8, 16, 256);
#[test]
fn chain_resolves_char_present_only_in_fallback_font() {
let chain = FontChain::new(PRIMARY, &[FALLBACK_FONT]);
let resolved = chain.resolve('Ç').expect("covered by the fallback font");
assert_eq!(*resolved.font(), FALLBACK_FONT);
assert_eq!(resolved.font_index(), 1);
assert_eq!(resolved.index(), 0x80);
assert!(!resolved.is_notdef());
}
#[test]
fn chain_falls_back_to_solid_block_when_every_font_misses() {
let chain = FontChain::new(PRIMARY, &[FALLBACK_FONT]);
let resolved = chain.resolve('あ').expect("solid block substitute");
assert_eq!(*resolved.font(), FALLBACK_FONT);
assert_eq!(resolved.index(), 0xDB);
assert!(resolved.is_notdef());
}
#[test]
fn chain_resolves_nothing_when_no_font_has_a_substitute() {
static DATA: [u8; 16] = [0; 16];
const CHARSET: [(char, u8); 1] = [('\u{2800}', 0)];
const BRAILLE: BitmapFont = BitmapFont::with_charset(&DATA, 8, 16, 1, &CHARSET);
let chain = FontChain::new(BRAILLE, &[]);
assert!(chain.resolve('\u{2800}').is_some());
assert!(chain.resolve('A').is_none());
}
#[test]
fn single_font_chain_resolves_that_font_directly() {
let chain = FontChain::from(FALLBACK_FONT);
assert_eq!(chain.font_count(), 1);
for ch in ['A', ' ', '█', '│', 'Ç', '☺'] {
let resolved = chain.resolve(ch).expect("CP437 coverage");
assert_eq!(*resolved.font(), FALLBACK_FONT);
assert_eq!(resolved.font_index(), 0);
assert_eq!(resolved.index(), FALLBACK_FONT.glyph_index(ch).unwrap());
}
}
#[test]
fn glyph_size_is_none_for_a_chain_of_mismatched_fonts() {
static DATA: [u8; 8] = [0; 8];
const SHORT: BitmapFont = BitmapFont::new(&DATA, 8, 8, 1);
assert_eq!(
FontChain::new(PRIMARY, &[FALLBACK_FONT]).glyph_size(),
Some((8, 16))
);
assert_eq!(FontChain::new(PRIMARY, &[SHORT]).glyph_size(), None);
}
#[test]
fn glyph_pixels_decodes_msb_first_row_major() {
static DATA: [u8; 4] = [0b1000_0001, 0b0000_0000, 0b1111_1111, 0b0000_1000];
let font = BitmapFont::new(&DATA, 8, 2, 2);
let g0: Vec<(u8, u8)> = font.glyph_pixels(0).collect();
assert_eq!(
g0,
[(0, 0), (7, 0)],
"MSB is the leftmost pixel; row 0 first"
);
let g1: Vec<(u8, u8)> = font.glyph_pixels(1).collect();
let mut expected: Vec<(u8, u8)> = (0..8).map(|x| (x, 0)).collect();
expected.push((4, 1)); assert_eq!(g1, expected);
}
#[test]
fn glyph_pixels_is_parameterized_by_width_not_hardcoded_to_8() {
static DATA: [u8; 1] = [0b0001_0001];
let font = BitmapFont::new(&DATA, 5, 1, 1);
let pixels: Vec<(u8, u8)> = font.glyph_pixels(0).collect();
assert_eq!(pixels, [(0, 0), (4, 0)]);
}
#[test]
fn chain_extends_past_cp437_via_charset_fallback_font() {
static BRAILLE_DATA: [u8; 16] = [0; 16];
const BRAILLE_CHARSET: [(char, u8); 1] = [('\u{2800}', 0)];
const BRAILLE_FONT: BitmapFont =
BitmapFont::with_charset(&BRAILLE_DATA, 8, 16, 1, &BRAILLE_CHARSET);
let primary = FALLBACK_FONT; let chain = FontChain::new(primary, &[BRAILLE_FONT]);
let braille = chain.resolve('\u{2800}').expect("charset coverage");
assert_eq!(*braille.font(), BRAILLE_FONT);
assert_eq!(braille.index(), 0);
let full_block = chain.resolve('\u{2588}').expect("CP437 coverage"); assert_eq!(*full_block.font(), primary);
assert_eq!(full_block.index(), 0xDB);
assert_ne!(braille.index(), full_block.index());
}
}