use std::{
cell::{Ref, RefCell},
ops::{Index, IndexMut},
rc::Rc,
};
use vector2d::Vector2D;
use crate::common::*;
#[derive(Debug, PartialEq)]
pub(crate) struct Frame {
buffer: Vec<Glyph>,
size: TPoint,
}
impl Frame {
fn new(size: TPoint) -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self {
buffer: vec![Glyph::default(); (size.x * size.y) as usize],
size: Vector2D::new(size.x, size.y),
}))
}
#[cfg(test)]
pub(crate) fn index(&self, x: TSize, y: TSize) -> usize {
x as usize + y as usize * self.size.x as usize
}
fn resize(&mut self, size: TPoint) {
self.buffer
.resize((size.x * size.y) as usize, Glyph::default());
self.size = Vector2D::new(size.x, size.y);
}
pub(crate) fn size(&self) -> TPoint {
self.size
}
}
impl Index<usize> for Frame {
type Output = Glyph;
fn index(&self, index: usize) -> &Self::Output {
&self.buffer[index]
}
}
impl IndexMut<usize> for Frame {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.buffer[index]
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq)]
#[repr(usize)]
enum BufferSide {
#[default]
Primary = 0,
Secondary,
}
impl BufferSide {
const SWAP_MAP: [BufferSide; Self::variant_count()] =
[BufferSide::Secondary, BufferSide::Primary];
pub const fn variant_count() -> usize {
BufferSide::Secondary as usize + 1
}
pub const fn swap(&self) -> Self {
Self::SWAP_MAP[*self as usize]
}
}
pub(crate) struct Framebuffer {
buffers: [Rc<RefCell<Frame>>; BufferSide::variant_count()],
active: BufferSide,
force_redraw: bool,
}
impl Framebuffer {
pub(crate) fn new(size: TPoint) -> Self {
Self {
buffers: [Frame::new(size), Frame::new(size)],
active: BufferSide::default(),
force_redraw: true,
}
}
pub(crate) fn resize(&mut self, size: TPoint) {
for idx in 0..BufferSide::variant_count() {
self.reset_buffer(idx);
self.buffers[idx].borrow_mut().resize(size);
}
self.force_redraw = true;
}
#[inline(always)]
fn reset_buffer(&mut self, index: usize) {
let glyph = Glyph::default();
self.buffers[index].borrow_mut().buffer.fill(glyph);
}
pub(crate) fn size(&self) -> TPoint {
self.buffers[BufferSide::Primary as usize].borrow().size()
}
pub(crate) fn prepare_next_frame(&mut self) -> Rc<RefCell<Frame>> {
let next_frame = self.active.swap() as usize;
self.reset_buffer(next_frame);
self.buffers[next_frame].clone()
}
pub(crate) fn get_line_iterator(&mut self) -> GlyphLineIterator<'_> {
self.active = self.active.swap();
let iter = GlyphLineIterator {
size: self.size(),
active: self.active,
line: TSize::MIN,
force_redraw: self.force_redraw,
buffers: [
self.buffers[BufferSide::Primary as usize].borrow(),
self.buffers[BufferSide::Secondary as usize].borrow(),
],
};
self.force_redraw = false;
iter
}
}
#[derive(Debug, PartialEq)]
pub(crate) enum LineContent<'a> {
Line { index: TSize, glyphs: &'a [Glyph] },
Skip,
}
pub(crate) struct GlyphLineIterator<'a> {
size: TPoint,
active: BufferSide,
line: TSize,
force_redraw: bool,
buffers: [Ref<'a, Frame>; BufferSide::variant_count()],
}
impl GlyphLineIterator<'_> {
pub(crate) fn next(&mut self) -> Option<LineContent<'_>> {
let cur_line = self.line;
let prev_buffer = &self.buffers[self.active.swap() as usize].buffer;
let cur_buffer = &self.buffers[self.active as usize].buffer;
let range =
(self.line * self.size.x) as usize..(self.size.x + self.line * self.size.x) as usize;
self.line += 1;
if cur_line < self.size.y {
return match self.force_redraw
|| cur_buffer[range.start..range.end] != prev_buffer[range.start..range.end]
{
true => Some(LineContent::Line {
index: cur_line,
glyphs: &cur_buffer[range],
}),
false => Some(LineContent::Skip),
};
}
None
}
#[cfg(test)]
fn count_redraws(mut self) -> usize {
let mut n = 0;
while let Some(v) = self.next() {
if v != LineContent::Skip {
n += 1;
}
}
n
}
}
#[cfg(test)]
mod test {
use super::*;
use arrayvec::ArrayString;
#[test]
fn check_frame() {
let frame = Frame::new(Vector2D::new(16, 16));
let default = Glyph::default();
assert!(frame.borrow().buffer.iter().all(|x| *x == default));
let dot = ArrayString::from(".").unwrap();
frame
.borrow_mut()
.buffer
.iter_mut()
.for_each(|x| x.grapheme = dot);
assert!(!frame.borrow().buffer.iter().all(|x| *x == default));
frame.borrow_mut().resize(Vector2D::new(32, 32));
assert_eq!(frame.borrow().size(), Vector2D::new(32, 32));
assert!(frame.borrow().buffer.iter().any(|x| *x == default));
assert!(frame.borrow().buffer.iter().any(|x| *x.grapheme == dot));
}
#[test]
fn check_framebuffer() {
let dot = ArrayString::from(".").unwrap();
let mut fb = Framebuffer::new(Vector2D::new(16, 16));
let mut iter = fb.get_line_iterator();
assert_eq!(iter.count_redraws(), 16);
iter = fb.get_line_iterator();
assert_eq!(iter.count_redraws(), 0);
assert_eq!(fb.buffers[0], fb.buffers[1]);
let frame = fb.prepare_next_frame();
let line4 = frame.borrow().index(4, 0);
frame.borrow_mut().buffer[line4].grapheme = dot;
iter = fb.get_line_iterator();
assert_eq!(iter.count_redraws(), 1);
assert_ne!(fb.buffers[0], fb.buffers[1]);
let size = Vector2D::new(32, 32);
fb.resize(size);
assert_eq!(fb.size(), size);
}
}