1#[derive(Clone, PartialEq, Eq)]
7pub struct Image {
8 pub width: u32,
10 pub height: u32,
12 pub data: Vec<u8>,
14}
15
16impl Image {
17 #[must_use]
24 pub fn encode_png(&self) -> Vec<u8> {
25 assert_eq!(
26 self.data.len(),
27 self.width as usize * self.height as usize * 4,
28 "Image data must be width * height * 4 bytes"
29 );
30 let mut out = Vec::new();
31 let mut encoder = png::Encoder::new(&mut out, self.width, self.height);
32 encoder.set_color(png::ColorType::Rgba);
33 encoder.set_depth(png::BitDepth::Eight);
34 let mut writer = encoder.write_header().expect("in-memory PNG header");
35 writer
36 .write_image_data(&self.data)
37 .expect("in-memory PNG data");
38 writer.finish().expect("in-memory PNG");
39 out
40 }
41
42 #[must_use]
45 pub fn decode_png(bytes: &[u8]) -> Option<Image> {
46 let mut reader = png::Decoder::new(std::io::Cursor::new(bytes))
47 .read_info()
48 .ok()?;
49 let mut buf = vec![0; reader.output_buffer_size()?];
50 let info = reader.next_frame(&mut buf).ok()?;
51 if info.bit_depth != png::BitDepth::Eight {
52 return None;
53 }
54 let data = match info.color_type {
55 png::ColorType::Rgba => buf[..info.buffer_size()].to_vec(),
56 png::ColorType::Rgb => buf[..info.buffer_size()]
57 .chunks_exact(3)
58 .flat_map(|p| [p[0], p[1], p[2], 255])
59 .collect(),
60 _ => return None,
61 };
62 Some(Image {
63 width: info.width,
64 height: info.height,
65 data,
66 })
67 }
68}
69
70impl std::fmt::Debug for Image {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 write!(f, "Image({}x{})", self.width, self.height)
73 }
74}
75
76pub trait FrameSource {
81 fn fps(&self) -> u32;
83 fn size(&self) -> (u32, u32);
85 fn total_frames(&self) -> u32;
87 fn render(&mut self, frame: u32, size: (u32, u32)) -> Image;
89}