#[derive(Clone, PartialEq, Eq)]
pub struct Image {
pub width: u32,
pub height: u32,
pub data: Vec<u8>,
}
impl Image {
#[must_use]
pub fn encode_png(&self) -> Vec<u8> {
assert_eq!(
self.data.len(),
self.width as usize * self.height as usize * 4,
"Image data must be width * height * 4 bytes"
);
let mut out = Vec::new();
let mut encoder = png::Encoder::new(&mut out, self.width, self.height);
encoder.set_color(png::ColorType::Rgba);
encoder.set_depth(png::BitDepth::Eight);
let mut writer = encoder.write_header().expect("in-memory PNG header");
writer
.write_image_data(&self.data)
.expect("in-memory PNG data");
writer.finish().expect("in-memory PNG");
out
}
#[must_use]
pub fn decode_png(bytes: &[u8]) -> Option<Image> {
let mut reader = png::Decoder::new(std::io::Cursor::new(bytes))
.read_info()
.ok()?;
let mut buf = vec![0; reader.output_buffer_size()?];
let info = reader.next_frame(&mut buf).ok()?;
if info.bit_depth != png::BitDepth::Eight {
return None;
}
let data = match info.color_type {
png::ColorType::Rgba => buf[..info.buffer_size()].to_vec(),
png::ColorType::Rgb => buf[..info.buffer_size()]
.chunks_exact(3)
.flat_map(|p| [p[0], p[1], p[2], 255])
.collect(),
_ => return None,
};
Some(Image {
width: info.width,
height: info.height,
data,
})
}
}
impl std::fmt::Debug for Image {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Image({}x{})", self.width, self.height)
}
}
pub trait FrameSource {
fn fps(&self) -> u32;
fn size(&self) -> (u32, u32);
fn total_frames(&self) -> u32;
fn render(&mut self, frame: u32, size: (u32, u32)) -> Image;
}