use super::{Cell, Rect};
mod border;
mod composite;
mod diff;
mod write;
#[cfg(test)]
mod tests;
pub use border::{
BorderCell, BorderContribution, BorderDirState, BorderSide, DIR_E, DIR_N, DIR_S, DIR_W,
QUAD_BL, QUAD_BR, QUAD_TL, QUAD_TR,
};
#[derive(Debug, Clone)]
pub struct Buffer {
pub area: Rect,
pub content: Vec<Cell>,
pub border_dirs: Vec<BorderCell>,
pub half_block_quads: Vec<u8>,
}
impl PartialEq for Buffer {
fn eq(&self, other: &Self) -> bool {
self.area == other.area && self.content == other.content
}
}
impl Buffer {
pub fn empty(area: Rect) -> Self {
Self::filled(area, Cell::EMPTY)
}
pub fn filled(area: Rect, cell: Cell) -> Self {
let len = area.area() as usize;
Buffer {
area,
content: vec![cell; len],
border_dirs: vec![BorderCell::default(); len],
half_block_quads: vec![0u8; len],
}
}
pub fn with_cells(area: Rect, cells: Vec<Cell>) -> Self {
assert_eq!(
cells.len(),
area.area() as usize,
"Buffer::with_cells: cell count {} != area.area() {}",
cells.len(),
area.area()
);
let len = area.area() as usize;
Buffer {
area,
content: cells,
border_dirs: vec![BorderCell::default(); len],
half_block_quads: vec![0u8; len],
}
}
pub fn index_of(&self, x: u16, y: u16) -> Option<usize> {
if x < self.area.x || y < self.area.y || x >= self.area.right() || y >= self.area.bottom() {
return None;
}
let dx = (x - self.area.x) as usize;
let dy = (y - self.area.y) as usize;
Some(dy * self.area.width as usize + dx)
}
pub fn cell(&self, x: u16, y: u16) -> Option<&Cell> {
self.index_of(x, y).map(|i| &self.content[i])
}
pub fn cell_mut(&mut self, x: u16, y: u16) -> Option<&mut Cell> {
let i = self.index_of(x, y)?;
Some(&mut self.content[i])
}
pub fn clear(&mut self) {
self.content.fill(Cell::EMPTY);
for dir in &mut self.border_dirs {
*dir = BorderCell::default();
}
self.half_block_quads.fill(0);
}
pub fn fill(&mut self, area: Rect, cell: Cell) {
let clip = self.area.intersection(area);
if clip.is_empty() {
return;
}
for y in clip.y..clip.bottom() {
for x in clip.x..clip.right() {
if let Some(i) = self.index_of(x, y) {
self.content[i] = cell.clone();
}
}
}
}
pub fn resize(&mut self, new_area: Rect) -> bool {
if new_area == self.area {
return false;
}
let mut next = Self::empty(new_area);
let overlap = self.area.intersection(new_area);
if !overlap.is_empty() {
for y in overlap.y..overlap.bottom() {
for x in overlap.x..overlap.right() {
if let (Some(src_i), Some(dst_i)) = (self.index_of(x, y), next.index_of(x, y)) {
next.content[dst_i] = self.content[src_i].clone();
}
}
}
}
*self = next;
true
}
pub(crate) fn copy_region(&self, area: Rect) -> Buffer {
let region = self.area.intersection(area);
let len = region.area() as usize;
let mut out = Buffer {
area: region,
content: Vec::with_capacity(len),
border_dirs: Vec::with_capacity(len),
half_block_quads: Vec::with_capacity(len),
};
for y in region.y..region.bottom() {
let (Some(a), Some(b)) = (
self.index_of(region.x, y),
self.index_of(region.right() - 1, y),
) else {
continue;
};
out.content.extend_from_slice(&self.content[a..=b]);
out.border_dirs.extend_from_slice(&self.border_dirs[a..=b]);
out.half_block_quads
.extend_from_slice(&self.half_block_quads[a..=b]);
}
out
}
pub fn merge(&mut self, other: &Buffer) {
let overlap = self.area.intersection(other.area);
if overlap.is_empty() {
return;
}
for y in overlap.y..overlap.bottom() {
for x in overlap.x..overlap.right() {
if let (Some(src_i), Some(dst_i)) = (other.index_of(x, y), self.index_of(x, y)) {
self.content[dst_i] = other.content[src_i].clone();
}
}
}
}
}