use std::fmt;
use std::ops::Index;
const BIT_OFFSETS: [(usize, usize); 8] = [
(1, 3), (0, 3), (1, 2), (1, 1), (1, 0), (0, 2), (0, 1), (0, 0), ];
const CHAR_WIDTH: usize = 2;
const CHAR_HEIGHT: usize = 4;
const CHARS: [char; 256] = [
'⠀', '⠁', '⠂', '⠃', '⠄', '⠅', '⠆', '⠇', '⠈', '⠉', '⠊', '⠋', '⠌', '⠍', '⠎', '⠏', '⠐', '⠑', '⠒',
'⠓', '⠔', '⠕', '⠖', '⠗', '⠘', '⠙', '⠚', '⠛', '⠜', '⠝', '⠞', '⠟', '⠠', '⠡', '⠢', '⠣', '⠤', '⠥',
'⠦', '⠧', '⠨', '⠩', '⠪', '⠫', '⠬', '⠭', '⠮', '⠯', '⠰', '⠱', '⠲', '⠳', '⠴', '⠵', '⠶', '⠷', '⠸',
'⠹', '⠺', '⠻', '⠼', '⠽', '⠾', '⠿', '⡀', '⡁', '⡂', '⡃', '⡄', '⡅', '⡆', '⡇', '⡈', '⡉', '⡊', '⡋',
'⡌', '⡍', '⡎', '⡏', '⡐', '⡑', '⡒', '⡓', '⡔', '⡕', '⡖', '⡗', '⡘', '⡙', '⡚', '⡛', '⡜', '⡝', '⡞',
'⡟', '⡠', '⡡', '⡢', '⡣', '⡤', '⡥', '⡦', '⡧', '⡨', '⡩', '⡪', '⡫', '⡬', '⡭', '⡮', '⡯', '⡰', '⡱',
'⡲', '⡳', '⡴', '⡵', '⡶', '⡷', '⡸', '⡹', '⡺', '⡻', '⡼', '⡽', '⡾', '⡿', '⢀', '⢁', '⢂', '⢃', '⢄',
'⢅', '⢆', '⢇', '⢈', '⢉', '⢊', '⢋', '⢌', '⢍', '⢎', '⢏', '⢐', '⢑', '⢒', '⢓', '⢔', '⢕', '⢖', '⢗',
'⢘', '⢙', '⢚', '⢛', '⢜', '⢝', '⢞', '⢟', '⢠', '⢡', '⢢', '⢣', '⢤', '⢥', '⢦', '⢧', '⢨', '⢩', '⢪',
'⢫', '⢬', '⢭', '⢮', '⢯', '⢰', '⢱', '⢲', '⢳', '⢴', '⢵', '⢶', '⢷', '⢸', '⢹', '⢺', '⢻', '⢼', '⢽',
'⢾', '⢿', '⣀', '⣁', '⣂', '⣃', '⣄', '⣅', '⣆', '⣇', '⣈', '⣉', '⣊', '⣋', '⣌', '⣍', '⣎', '⣏', '⣐',
'⣑', '⣒', '⣓', '⣔', '⣕', '⣖', '⣗', '⣘', '⣙', '⣚', '⣛', '⣜', '⣝', '⣞', '⣟', '⣠', '⣡', '⣢', '⣣',
'⣤', '⣥', '⣦', '⣧', '⣨', '⣩', '⣪', '⣫', '⣬', '⣭', '⣮', '⣯', '⣰', '⣱', '⣲', '⣳', '⣴', '⣵', '⣶',
'⣷', '⣸', '⣹', '⣺', '⣻', '⣼', '⣽', '⣾', '⣿',
];
#[derive(Debug, Copy, Clone)]
pub struct Framebuffer<'a> {
framebuffer: &'a [bool],
width: usize,
height: usize,
x_chars_count: usize,
y_chars_count: usize,
}
impl<'a> Framebuffer<'a> {
pub fn new(framebuffer: &'a [bool], width: usize, height: usize) -> Self {
assert_eq!(
framebuffer.len(),
width * height,
"supplied slice does not match width * height"
);
let x_chars_count = (round_up(width, CHAR_WIDTH) / CHAR_WIDTH) + 1; let y_chars_count = round_up(height, CHAR_HEIGHT) / CHAR_HEIGHT;
Self {
framebuffer,
width,
height,
x_chars_count,
y_chars_count,
}
}
pub fn get(&self, index: usize) -> Option<char> {
self.get_inner(index).copied()
}
fn get_inner(&self, index: usize) -> Option<&'static char> {
match self.offsets(index) {
Offsets::Char(x_offset, y_offset) => Some(get_char(
self.framebuffer,
x_offset,
y_offset,
self.width,
self.height,
)),
Offsets::Linebreak => Some(&'\n'),
Offsets::End => None,
}
}
pub fn x_chars_count(&self) -> usize {
self.x_chars_count
}
pub fn y_chars_count(&self) -> usize {
self.y_chars_count
}
pub fn len(&self) -> usize {
self.y_chars_count * self.x_chars_count
}
pub fn is_empty(&self) -> bool {
self.framebuffer.is_empty()
}
fn offsets(&self, index: usize) -> Offsets {
if index > 0 && (index + 1) % self.x_chars_count == 0 {
return Offsets::Linebreak;
}
let rows = index / self.x_chars_count;
let y_offset = rows * CHAR_HEIGHT;
if y_offset >= self.height {
return Offsets::End;
}
let cols = index % self.x_chars_count;
let x_offset = cols * CHAR_WIDTH;
Offsets::Char(x_offset, y_offset)
}
}
impl fmt::Display for Framebuffer<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for c in self {
write!(f, "{}", c)?;
}
Ok(())
}
}
impl Index<usize> for Framebuffer<'_> {
type Output = char;
fn index(&self, index: usize) -> &Self::Output {
self.get_inner(index).unwrap_or_else(|| {
panic!(
"index out of bounds: the len is {} but the index is {}",
self.len(),
index
)
})
}
}
impl<'a, 'f> IntoIterator for &'a Framebuffer<'f> {
type Item = char;
type IntoIter = Iter<'a, 'f>;
fn into_iter(self) -> Self::IntoIter {
Iter {
inner: self,
index: 0,
}
}
}
#[derive(Debug, PartialEq)]
enum Offsets {
Char(usize, usize),
Linebreak,
End,
}
pub struct Iter<'a, 'i> {
inner: &'a Framebuffer<'i>,
index: usize,
}
impl<'a, 'i> Iterator for Iter<'a, 'i> {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
match self.inner.offsets(self.index) {
Offsets::Char(x_offset, y_offset) => {
self.index += 1;
Some(*get_char(
self.inner.framebuffer,
x_offset,
y_offset,
self.inner.width,
self.inner.height,
))
}
Offsets::Linebreak => {
self.index += 1;
Some('\n')
}
Offsets::End => None,
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let size = self.inner.len();
(size, Some(size))
}
}
fn round_up(input: usize, multiple: usize) -> usize {
((input + multiple - 1) / multiple) * multiple
}
fn get_char(
framebuffer: &[bool],
x_offset: usize,
y_offset: usize,
width: usize,
height: usize,
) -> &'static char {
let mut n: u8 = 0;
for (x, y) in BIT_OFFSETS {
n <<= 1;
let xx = x_offset + x;
let yy = y_offset + y;
if xx >= width || yy >= height {
continue;
}
n |= framebuffer[xx + yy * width] as u8;
}
&CHARS[n as usize]
}
#[cfg(test)]
mod tests {
use super::{get_char, Framebuffer, Offsets};
macro_rules! framebuffer {
(#) => {true};
(.) => {false};
($($c:tt)+) => {vec![
$(framebuffer!($c)),+
]}
}
#[test]
fn single_chars() {
let framebuffer = framebuffer![
# .
# #
. .
. .
];
let f = Framebuffer::new(&framebuffer, 2, 4);
assert_eq!(Some('⠓'), f.get(0));
assert_eq!(Some('\n'), f.get(1));
assert_eq!(None, f.get(2));
let framebuffer = framebuffer![
# .
# .
# .
# #
];
let f = Framebuffer::new(&framebuffer, 2, 4);
assert_eq!(Some('⣇'), f.get(0));
assert_eq!(Some('\n'), f.get(1));
assert_eq!(None, f.get(2));
}
#[test]
fn multiple_chars() {
let framebuffer = framebuffer![
# . # #
# . . #
# . # #
# # . .
# # . #
# # . #
. . # #
# . # .
];
let f = Framebuffer::new(&framebuffer, 4, 8);
assert_eq!(Some('⣇'), f.get(0));
assert_eq!(Some('⠽'), f.get(1));
assert_eq!(Some('\n'), f.get(2));
assert_eq!(Some('⡛'), f.get(3));
assert_eq!(Some('⡼'), f.get(4));
}
#[test]
fn len() {
let framebuffer = vec![false; 2 * 4];
let f = Framebuffer::new(&framebuffer, 2, 4);
assert_eq!(2, f.len());
let framebuffer = vec![false; 4 * 8];
let f = Framebuffer::new(&framebuffer, 4, 8);
assert_eq!(6, f.len());
let framebuffer = vec![false; 3 * 5];
let f = Framebuffer::new(&framebuffer, 3, 5);
assert_eq!(6, f.len());
}
#[test]
fn padding() {
let framebuffer = framebuffer![
# . #
# . .
# . #
. . .
# # #
];
let f = Framebuffer::new(&framebuffer, 3, 5);
assert_eq!(Some('⠇'), f.get(0));
assert_eq!(Some('⠅'), f.get(1));
assert_eq!(Some('\n'), f.get(2));
assert_eq!(Some('⠉'), f.get(3));
assert_eq!(Some('⠁'), f.get(4));
assert_eq!(Some('\n'), f.get(5));
}
#[test]
fn test_get_char() {
let framebuffer = framebuffer![
# . #
# . .
# . #
. . .
# # #
];
assert_eq!(&'⠇', get_char(&framebuffer, 0, 0, 3, 5));
assert_eq!(&'⠅', get_char(&framebuffer, 2, 0, 3, 5));
assert_eq!(&'⠉', get_char(&framebuffer, 0, 4, 3, 5));
assert_eq!(&'⠁', get_char(&framebuffer, 2, 4, 3, 5));
}
#[test]
fn offsets() {
fn test(f: Framebuffer) {
assert_eq!(Offsets::Char(0, 0), f.offsets(0));
assert_eq!(Offsets::Char(2, 0), f.offsets(1));
assert_eq!(Offsets::Linebreak, f.offsets(2));
assert_eq!(Offsets::Char(0, 4), f.offsets(3));
assert_eq!(Offsets::Char(2, 4), f.offsets(4));
assert_eq!(Offsets::Linebreak, f.offsets(5));
assert_eq!(Offsets::Char(0, 8), f.offsets(6));
assert_eq!(Offsets::Char(2, 8), f.offsets(7));
assert_eq!(Offsets::Linebreak, f.offsets(8));
assert_eq!(Offsets::End, f.offsets(9));
}
let framebuffer = vec![false; 4 * 12];
let f = Framebuffer::new(&framebuffer, 4, 12);
test(f);
let framebuffer = vec![false; 3 * 11];
let f = Framebuffer::new(&framebuffer, 3, 11);
test(f);
}
#[test]
fn chars() {
let chars = (0..256)
.map(|i| char::from_u32(0x2800 + i as u32).unwrap())
.collect::<Vec<_>>();
assert_eq!(super::CHARS, &chars[..]);
}
}