use alloc::boxed::Box;
use zenpixels::{PixelDescriptor, PixelSliceMut};
pub type SinkError = Box<dyn core::error::Error + Send + Sync>;
pub trait DecodeRowSink {
fn begin(
&mut self,
_width: u32,
_height: u32,
_descriptor: PixelDescriptor,
) -> Result<(), SinkError> {
Ok(())
}
fn provide_next_buffer(
&mut self,
y: u32,
height: u32,
width: u32,
descriptor: PixelDescriptor,
) -> Result<PixelSliceMut<'_>, SinkError>;
fn finish(&mut self) -> Result<(), SinkError> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::format;
use alloc::string::ToString;
use alloc::vec::Vec;
#[test]
fn full_lifecycle() {
struct TestSink {
buf: Vec<u8>,
began: bool,
finished: bool,
strips: Vec<(u32, u32)>,
}
impl DecodeRowSink for TestSink {
fn begin(
&mut self,
_width: u32,
_height: u32,
_descriptor: PixelDescriptor,
) -> Result<(), SinkError> {
assert!(!self.began, "begin called twice");
self.began = true;
Ok(())
}
fn provide_next_buffer(
&mut self,
y: u32,
height: u32,
width: u32,
descriptor: PixelDescriptor,
) -> Result<PixelSliceMut<'_>, SinkError> {
assert!(self.began, "provide_next_buffer before begin");
self.strips.push((y, height));
let bpp = descriptor.bytes_per_pixel();
let stride = width as usize * bpp;
let needed = height as usize * stride;
self.buf.resize(needed, 0);
Ok(
PixelSliceMut::new(&mut self.buf, width, height, stride, descriptor)
.expect("valid buffer"),
)
}
fn finish(&mut self) -> Result<(), SinkError> {
assert!(self.began, "finish before begin");
assert!(!self.finished, "finish called twice");
self.finished = true;
Ok(())
}
}
let mut sink = TestSink {
buf: Vec::new(),
began: false,
finished: false,
strips: Vec::new(),
};
let width = 10u32;
let desc = PixelDescriptor::RGB8_SRGB;
let bpp = desc.bytes_per_pixel();
sink.begin(width, 24, desc).unwrap();
for strip in 0..3u32 {
let y = strip * 8;
let h = 8;
let mut ps = sink.provide_next_buffer(y, h, width, desc).unwrap();
assert_eq!(ps.stride(), 30);
for row in 0..h {
let row_data = ps.row_mut(row);
assert_eq!(row_data.len(), width as usize * bpp);
row_data.fill((strip + 1) as u8);
}
}
sink.finish().unwrap();
assert!(sink.began);
assert!(sink.finished);
assert_eq!(sink.strips.len(), 3);
assert_eq!(sink.strips[0], (0, 8));
assert_eq!(sink.strips[1], (8, 8));
assert_eq!(sink.strips[2], (16, 8));
}
#[test]
fn object_safe() {
struct SimpleSink {
buf: Vec<u8>,
}
impl DecodeRowSink for SimpleSink {
fn provide_next_buffer(
&mut self,
_y: u32,
height: u32,
width: u32,
descriptor: PixelDescriptor,
) -> Result<PixelSliceMut<'_>, SinkError> {
let bpp = descriptor.bytes_per_pixel();
let stride = width as usize * bpp;
let needed = height as usize * stride;
self.buf.resize(needed, 0);
Ok(
PixelSliceMut::new(&mut self.buf, width, height, stride, descriptor)
.expect("valid buffer"),
)
}
}
fn use_sink(sink: &mut dyn DecodeRowSink) {
sink.begin(10, 8, PixelDescriptor::RGB8_SRGB).unwrap();
let ps = sink
.provide_next_buffer(0, 8, 10, PixelDescriptor::RGB8_SRGB)
.unwrap();
assert_eq!(ps.stride(), 30);
assert_eq!(ps.width(), 10);
assert_eq!(ps.rows(), 8);
sink.finish().unwrap();
}
let mut sink = SimpleSink { buf: Vec::new() };
use_sink(&mut sink);
}
#[test]
fn lending_borrow_pattern() {
struct ReuseSink {
buf: Vec<u8>,
call_count: u32,
}
impl DecodeRowSink for ReuseSink {
fn provide_next_buffer(
&mut self,
_y: u32,
height: u32,
width: u32,
descriptor: PixelDescriptor,
) -> Result<PixelSliceMut<'_>, SinkError> {
self.call_count += 1;
let bpp = descriptor.bytes_per_pixel();
let stride = width as usize * bpp;
let needed = height as usize * stride;
self.buf.resize(needed, 0);
Ok(
PixelSliceMut::new(&mut self.buf, width, height, stride, descriptor)
.expect("valid buffer"),
)
}
}
let mut sink = ReuseSink {
buf: Vec::new(),
call_count: 0,
};
let desc = PixelDescriptor::GRAY8_SRGB;
{
let mut ps = sink.provide_next_buffer(0, 4, 10, desc).unwrap();
for row in 0..4 {
ps.row_mut(row).fill(1);
}
}
{
let mut ps = sink.provide_next_buffer(4, 4, 10, desc).unwrap();
for row in 0..4 {
ps.row_mut(row).fill(2);
}
}
{
let mut ps = sink.provide_next_buffer(8, 4, 10, desc).unwrap();
for row in 0..4 {
ps.row_mut(row).fill(3);
}
}
assert_eq!(sink.call_count, 3);
assert_eq!(sink.buf[0], 3);
}
#[test]
fn simd_aligned_sink() {
struct AlignedSink {
buf: Vec<u8>,
}
impl DecodeRowSink for AlignedSink {
fn provide_next_buffer(
&mut self,
_y: u32,
height: u32,
width: u32,
descriptor: PixelDescriptor,
) -> Result<PixelSliceMut<'_>, SinkError> {
let bpp = descriptor.bytes_per_pixel();
let row_bytes = width as usize * bpp;
let stride = (row_bytes + 63) & !63;
let needed = if height > 0 {
(height as usize - 1) * stride + row_bytes
} else {
0
};
self.buf.resize(needed, 0);
Ok(
PixelSliceMut::new(&mut self.buf, width, height, stride, descriptor)
.expect("valid buffer"),
)
}
}
let mut sink = AlignedSink { buf: Vec::new() };
let ps = sink
.provide_next_buffer(0, 4, 10, PixelDescriptor::RGBA8_SRGB)
.unwrap();
assert_eq!(ps.stride(), 64);
assert_eq!(ps.width(), 10);
assert_eq!(ps.rows(), 4);
assert_eq!(ps.descriptor(), PixelDescriptor::RGBA8_SRGB);
}
#[test]
fn provide_next_buffer_error() {
struct FailSink;
impl DecodeRowSink for FailSink {
fn provide_next_buffer(
&mut self,
_y: u32,
_height: u32,
_width: u32,
_descriptor: PixelDescriptor,
) -> Result<PixelSliceMut<'_>, SinkError> {
Err("sink cancelled".into())
}
}
let mut sink = FailSink;
let result = sink.provide_next_buffer(0, 8, 10, PixelDescriptor::RGB8_SRGB);
assert!(result.is_err());
assert_eq!(result.unwrap_err().to_string(), "sink cancelled");
}
#[test]
fn begin_rejects_format() {
struct Rgba8OnlySink {
buf: Vec<u8>,
}
impl DecodeRowSink for Rgba8OnlySink {
fn begin(
&mut self,
_width: u32,
_height: u32,
descriptor: PixelDescriptor,
) -> Result<(), SinkError> {
if descriptor != PixelDescriptor::RGBA8_SRGB {
return Err(format!("sink requires RGBA8, got {descriptor:?}").into());
}
Ok(())
}
fn provide_next_buffer(
&mut self,
_y: u32,
height: u32,
width: u32,
descriptor: PixelDescriptor,
) -> Result<PixelSliceMut<'_>, SinkError> {
let bpp = descriptor.bytes_per_pixel();
let stride = width as usize * bpp;
let needed = height as usize * stride;
self.buf.resize(needed, 0);
Ok(
PixelSliceMut::new(&mut self.buf, width, height, stride, descriptor)
.expect("valid buffer"),
)
}
}
let mut sink = Rgba8OnlySink { buf: Vec::new() };
let result = sink.begin(10, 8, PixelDescriptor::RGB8_SRGB);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("RGBA8"));
let mut sink2 = Rgba8OnlySink { buf: Vec::new() };
sink2.begin(10, 8, PixelDescriptor::RGBA8_SRGB).unwrap();
}
#[test]
fn finish_error() {
struct FinishFailSink;
impl DecodeRowSink for FinishFailSink {
fn provide_next_buffer(
&mut self,
_y: u32,
_height: u32,
_width: u32,
_descriptor: PixelDescriptor,
) -> Result<PixelSliceMut<'_>, SinkError> {
unreachable!("not called in this test")
}
fn finish(&mut self) -> Result<(), SinkError> {
Err("flush failed".into())
}
}
let mut sink = FinishFailSink;
let result = sink.finish();
assert!(result.is_err());
assert_eq!(result.unwrap_err().to_string(), "flush failed");
}
}