use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use ratatui::style::Color;
pub fn paint_halfblocks_partial(
scratch: &Buffer,
full_rect: Rect,
src_y_offset: u16,
dst_rect: Rect,
buf: &mut Buffer,
bg: Color,
) {
if full_rect.width == 0 || full_rect.height == 0 || dst_rect.width == 0 || dst_rect.height == 0
{
return;
}
for dy in 0..dst_rect.height {
let src_y = src_y_offset.saturating_add(dy);
if src_y >= full_rect.height {
break;
}
for dx in 0..dst_rect.width {
if dx >= full_rect.width {
break;
}
let Some(src_cell) = scratch.cell((dx, src_y)) else {
continue;
};
let mut copied = src_cell.clone();
if copied.bg == Color::Reset {
copied.bg = bg;
}
if let Some(dst_cell) = buf.cell_mut((dst_rect.x + dx, dst_rect.y + dy)) {
*dst_cell = copied;
}
}
}
}
#[cfg(test)]
#[allow(deprecated)]
mod tests {
use super::*;
use image::DynamicImage;
use ratatui::widgets::StatefulWidget;
use ratatui_image::picker::Picker;
use ratatui_image::{Resize, StatefulImage};
fn halfblocks_scratch(rect: Rect) -> Buffer {
let mut picker = Picker::from_fontsize((1, 2).into());
picker.set_protocol_type(ratatui_image::picker::ProtocolType::Halfblocks);
let img = DynamicImage::ImageRgba8(image::RgbaImage::from_pixel(
u32::from(rect.width) * 2,
u32::from(rect.height) * 4,
image::Rgba([40, 80, 120, 255]),
));
let mut protocol = picker.new_resize_protocol(img);
let mut buf = Buffer::empty(rect);
StatefulImage::default()
.resize(Resize::Fit(None))
.render(rect, &mut buf, &mut protocol);
buf
}
#[test]
fn positive_src_offset_shifts_source_rows() {
let full = Rect::new(0, 0, 8, 6);
let scratch = halfblocks_scratch(full);
let mut dst_buf = Buffer::empty(Rect::new(0, 0, 8, 3));
paint_halfblocks_partial(
&scratch,
full,
2,
Rect::new(0, 0, 8, 3),
&mut dst_buf,
Color::Reset,
);
for dy in 0..3u16 {
for dx in 0..8u16 {
let dst_cell = dst_buf.cell((dx, dy)).unwrap();
let src_cell = scratch.cell((dx, 2 + dy)).unwrap();
assert_eq!(dst_cell.symbol(), src_cell.symbol());
assert_eq!(dst_cell.style(), src_cell.style());
}
}
}
#[test]
fn destination_clipping_silently_drops_out_of_bounds_cells() {
let full = Rect::new(0, 0, 8, 4);
let scratch = halfblocks_scratch(full);
let mut buf = Buffer::empty(Rect::new(0, 0, 4, 2));
paint_halfblocks_partial(
&scratch,
full,
0,
Rect::new(0, 0, 6, 3),
&mut buf,
Color::Reset,
);
}
#[test]
fn src_offset_past_image_leaves_dst_default() {
let full = Rect::new(0, 0, 8, 4);
let scratch = halfblocks_scratch(full);
let mut buf = Buffer::empty(Rect::new(0, 0, 8, 6));
paint_halfblocks_partial(
&scratch,
full,
5,
Rect::new(0, 0, 8, 6),
&mut buf,
Color::Reset,
);
for y in 0..6u16 {
for x in 0..8u16 {
let c = buf.cell((x, y)).unwrap();
assert_eq!(c.symbol(), " ");
}
}
}
#[test]
fn zero_area_is_noop() {
let scratch = halfblocks_scratch(Rect::new(0, 0, 4, 4));
let mut buf = Buffer::empty(Rect::new(0, 0, 4, 4));
paint_halfblocks_partial(
&scratch,
Rect::new(0, 0, 0, 4),
0,
Rect::new(0, 0, 4, 4),
&mut buf,
Color::Reset,
);
paint_halfblocks_partial(
&scratch,
Rect::new(0, 0, 4, 4),
0,
Rect::new(0, 0, 0, 4),
&mut buf,
Color::Reset,
);
for y in 0..4u16 {
for x in 0..4u16 {
assert_eq!(buf.cell((x, y)).unwrap().symbol(), " ");
}
}
}
}