oxframe_core/frame.rs
1//! Frames as pixels, and the one interface through which the editor, the
2//! CLI and the exporter get them: [`FrameSource`]. None of them needs to
3//! know what draws the frames (2D nodes today, other modules later).
4
5/// 8-bit sRGB RGBA, straight alpha, row-major, no padding.
6#[derive(Clone, PartialEq, Eq)]
7pub struct Image {
8 /// Width in pixels.
9 pub width: u32,
10 /// Height in pixels.
11 pub height: u32,
12 /// `width * height * 4` bytes.
13 pub data: Vec<u8>,
14}
15
16impl Image {
17 /// Encodes the image as PNG (8-bit RGBA, straight alpha).
18 ///
19 /// # Panics
20 ///
21 /// If `data` is not `width * height * 4` bytes — a bug in whoever
22 /// built the image.
23 #[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
43impl std::fmt::Debug for Image {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 write!(f, "Image({}x{})", self.width, self.height)
46 }
47}
48
49/// A timeline that can draw any of its frames: "give me frame `n`".
50///
51/// Frames are addressed by index (time `n / fps`) so that every caller
52/// gets exactly the same pixels for the same frame, in any order.
53pub trait FrameSource {
54 /// Frames per second.
55 fn fps(&self) -> u32;
56 /// Native size in pixels.
57 fn size(&self) -> (u32, u32);
58 /// Length in frames.
59 fn total_frames(&self) -> u32;
60 /// Draws `frame` at `size` pixels (scaled to fit and centered).
61 fn render(&mut self, frame: u32, size: (u32, u32)) -> Image;
62}