use std::fmt;
use std::ops;
use crate::color;
use crate::glyph;
use crate::mat;
pub trait Buffer: ops::IndexMut<(usize, usize)> {
fn new(size: mat::Size) -> Self;
fn render(&mut self) -> &str;
fn preallocate(&mut self);
fn size(&self) -> mat::Size;
fn glyphs(&self) -> glyph::GlyphsRef;
fn glyphs_mut(&mut self) -> glyph::GlyphsRefMut;
}
pub struct SimpleBuffer {
glyphs: glyph::Glyphs,
string: String,
}
impl ops::Index<(usize, usize)> for SimpleBuffer {
type Output = glyph::Glyph;
#[inline]
fn index(&self, index: (usize, usize)) -> &Self::Output {
self.glyphs.index(index)
}
}
impl ops::IndexMut<(usize, usize)> for SimpleBuffer {
#[inline]
fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output {
self.glyphs.index_mut(index)
}
}
impl Buffer for SimpleBuffer {
#[inline]
fn new(size: mat::Size) -> SimpleBuffer {
SimpleBuffer {
glyphs: glyph::Glyphs::new(size),
string: String::new(),
}
}
#[inline]
fn render(&mut self) -> &str {
self.string.clear();
render(&mut self.string, &self.glyphs)
.expect("<String as std::fmt::Write> should not fail");
self.string.as_str()
}
#[inline]
fn preallocate(&mut self) {
ensure_size(&mut self.string, self.glyphs.size);
}
#[inline]
fn size(&self) -> mat::Size {
self.glyphs.size
}
#[inline]
fn glyphs(&self) -> glyph::GlyphsRef {
self.glyphs.to_ref()
}
#[inline]
fn glyphs_mut(&mut self) -> glyph::GlyphsRefMut {
self.glyphs.to_mut()
}
}
pub struct DoubleBuffer {
previous: glyph::Glyphs,
current: glyph::Glyphs,
string: String,
}
impl ops::Index<(usize, usize)> for DoubleBuffer {
type Output = glyph::Glyph;
#[inline]
fn index(&self, index: (usize, usize)) -> &Self::Output {
self.current.index(index)
}
}
impl ops::IndexMut<(usize, usize)> for DoubleBuffer {
#[inline]
fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output {
self.current.index_mut(index)
}
}
impl Buffer for DoubleBuffer {
#[inline]
fn new(size: mat::Size) -> DoubleBuffer {
DoubleBuffer {
previous: glyph::Glyphs::new(size),
current: glyph::Glyphs::new(size),
string: String::new(),
}
}
#[inline]
fn render(&mut self) -> &str {
debug_assert_eq!(self.previous.size, self.current.size);
self.string.clear();
render_diff(&mut self.string, &self.previous, &self.current)
.expect("<String as std::fmt::Write> should not fail");
std::mem::swap(&mut self.previous, &mut self.current);
self.string.as_str()
}
#[inline]
fn preallocate(&mut self) {
debug_assert_eq!(self.previous.size, self.current.size);
ensure_size(&mut self.string, self.previous.size);
}
#[inline]
fn size(&self) -> mat::Size {
debug_assert_eq!(self.previous.size, self.current.size);
self.previous.size
}
#[inline]
fn glyphs(&self) -> glyph::GlyphsRef {
self.current.to_ref()
}
#[inline]
fn glyphs_mut(&mut self) -> glyph::GlyphsRefMut {
self.current.to_mut()
}
}
const NUMBERS: [&str; 256] = [
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16",
"17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32",
"33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48",
"49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63", "64",
"65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "80",
"81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96",
"97", "98", "99", "100", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110",
"111", "112", "113", "114", "115", "116", "117", "118", "119", "120", "121", "122", "123",
"124", "125", "126", "127", "128", "129", "130", "131", "132", "133", "134", "135", "136",
"137", "138", "139", "140", "141", "142", "143", "144", "145", "146", "147", "148", "149",
"150", "151", "152", "153", "154", "155", "156", "157", "158", "159", "160", "161", "162",
"163", "164", "165", "166", "167", "168", "169", "170", "171", "172", "173", "174", "175",
"176", "177", "178", "179", "180", "181", "182", "183", "184", "185", "186", "187", "188",
"189", "190", "191", "192", "193", "194", "195", "196", "197", "198", "199", "200", "201",
"202", "203", "204", "205", "206", "207", "208", "209", "210", "211", "212", "213", "214",
"215", "216", "217", "218", "219", "220", "221", "222", "223", "224", "225", "226", "227",
"228", "229", "230", "231", "232", "233", "234", "235", "236", "237", "238", "239", "240",
"241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253",
"254", "255",
];
fn render_g<W>(buf: &mut W, color: color::Rgb) -> fmt::Result
where
W: fmt::Write,
{
buf.write_str(NUMBERS[color.r as usize])?;
buf.write_char(';')?;
buf.write_str(NUMBERS[color.g as usize])?;
buf.write_char(';')?;
buf.write_str(NUMBERS[color.b as usize])?;
buf.write_char('m')
}
fn render_fg<W>(buf: &mut W, color: color::Rgb) -> fmt::Result
where
W: fmt::Write,
{
buf.write_str("\x1b[38;2;")?;
render_g(buf, color)
}
fn render_bg<W>(buf: &mut W, color: color::Rgb) -> fmt::Result
where
W: fmt::Write,
{
buf.write_str("\x1b[48;2;")?;
render_g(buf, color)
}
const RESET: &str = "\x1b[0m";
const LINE_END: &str = "\x1b[0m\r\n";
fn ensure_size(string: &mut String, size: mat::Size) {
let bytes_per_glyph = 42;
let bytes_per_line_ending = 5;
let required_size = (bytes_per_glyph * size.width + bytes_per_line_ending) * size.height;
string.reserve(required_size);
}
#[inline]
pub fn render<W, S>(write: &mut W, glyphs: &mat::Matrix<S>) -> fmt::Result
where
W: fmt::Write,
S: mat::Sequence<Item = glyph::Glyph>,
{
let mut fg_last = None;
let mut bg_last = None;
let rows = glyphs.rows();
for row in rows {
for glyph in row {
if fg_last != glyph.foreground {
if let Some(fg) = glyph.foreground {
render_fg(write, fg)?;
fg_last = glyph.foreground;
}
}
if bg_last != glyph.background {
if let Some(bg) = glyph.background {
render_bg(write, bg)?;
bg_last = glyph.background;
}
}
write.write_char(glyph.char)?;
}
write.write_str(LINE_END)?;
fg_last = None;
bg_last = None;
}
Ok(())
}
#[derive(Clone, Copy, Default, PartialEq)]
struct Cursor {
row: usize,
col: usize,
}
#[inline]
pub fn render_diff<W, S>(write: &mut W, prev: &mat::Matrix<S>, next: &mat::Matrix<S>) -> fmt::Result
where
W: fmt::Write,
S: mat::Sequence<Item = glyph::Glyph>,
{
assert_eq!(prev.size, next.size);
let width = next.size.width;
let height = next.size.height;
let mut fg_last = None;
let mut bg_last = None;
let mut cursor = Cursor::default();
let mut terminal_cursor = Cursor::default();
for (rowp, rown) in prev.rows().zip(next.rows()) {
for (glyphp, glyphn) in rowp.iter().zip(rown.iter()) {
if glyphp != glyphn {
if cursor.row > terminal_cursor.row {
write!(write, "\x1b[{}B", cursor.row - terminal_cursor.row)?;
}
if cursor.col > terminal_cursor.col {
write!(write, "\x1b[{}C", cursor.col - terminal_cursor.col)?;
} else if cursor.col < terminal_cursor.col {
write!(write, "\x1b[{}D", terminal_cursor.col - cursor.col)?;
}
if fg_last != glyphn.foreground {
if let Some(fg) = glyphn.foreground {
render_fg(write, fg)?;
fg_last = glyphn.foreground;
}
}
if bg_last != glyphn.background {
if let Some(bg) = glyphn.background {
render_bg(write, bg)?;
bg_last = glyphn.background;
}
}
write.write_char(glyphn.char)?;
terminal_cursor = cursor;
terminal_cursor.col += 1;
}
cursor.col += 1;
}
if cursor == terminal_cursor {
write.write_str(RESET)?;
if cursor.row + 1 < height {
write!(write, "\x1b[{}D\x1b[B", width)?;
terminal_cursor.col = 0;
terminal_cursor.row += 1;
}
fg_last = None;
bg_last = None;
}
cursor.col = 0;
cursor.row += 1;
}
if cursor.row > terminal_cursor.row + 1 {
write!(write, "\x1b[{}B", cursor.row - terminal_cursor.row - 1)?;
}
if width > terminal_cursor.col {
write!(write, "\x1b[{}C", width - terminal_cursor.col)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn buffer_preallocate() {
let mut buffer = SimpleBuffer::new((10, 10).into());
assert_eq!(0, buffer.string.capacity());
buffer.preallocate();
assert_eq!(4250, buffer.string.capacity());
}
#[test]
fn double_buffer_preallocate() {
let mut buffer = DoubleBuffer::new((10, 10).into());
assert_eq!(0, buffer.string.capacity());
buffer.preallocate();
assert_eq!(4250, buffer.string.capacity());
}
}