webp_animation/frame.rs
1use std::fmt::Debug;
2
3#[cfg(feature = "image")]
4use image::ImageBuffer;
5
6#[allow(unused_imports)]
7use crate::{ColorMode, Decoder, Error};
8
9/// An animation frame containing data and metadata produced by [`Decoder`]
10///
11/// Getting metadata:
12/// ```rust
13/// # use webp_animation::{Decoder, ColorMode};
14/// #
15/// # let buffer = std::fs::read("./data/animated.webp").unwrap();
16/// # let decoder = Decoder::new(&buffer).unwrap();
17/// # let frame = decoder.into_iter().next().unwrap();
18/// #
19/// assert_eq!(frame.dimensions(), (400, 400));
20/// assert_eq!(frame.color_mode(), ColorMode::Rgba);
21/// ```
22///
23/// Accessing frame data in raw [`ColorMode`] -encoded bytes:
24/// ```rust
25/// # use webp_animation::{Decoder, ColorMode};
26/// #
27/// # let buffer = std::fs::read("./data/animated.webp").unwrap();
28/// # let decoder = Decoder::new(&buffer).unwrap();
29/// # let frame = decoder.into_iter().next().unwrap();
30/// #
31/// assert_eq!(frame.data().len(), (400 * 400 * 4));
32/// assert_eq!(frame.data()[0..4], [0, 0, 0, 255]);
33/// ```
34///
35/// If `image` feature is enabled, frame can be converted into [`image::ImageBuffer`]:
36/// ```rust
37/// # use webp_animation::{Decoder, ColorMode};
38/// #
39/// # let buffer = std::fs::read("./data/animated.webp").unwrap();
40/// # let decoder = Decoder::new(&buffer).unwrap();
41/// # let frame = decoder.into_iter().next().unwrap();
42/// #
43/// ##[cfg(feature = "image")]
44/// let image = frame.into_image().unwrap();
45/// ##[cfg(feature = "image")]
46/// assert_eq!(image.dimensions(), (400, 400));
47/// ##[cfg(feature = "image")]
48/// assert_eq!(image.height(), 400);
49/// // image.save("frame.png");
50/// ```
51pub struct Frame {
52 timestamp: i32,
53 frame_data: Vec<u8>,
54
55 #[allow(dead_code)]
56 color_mode: ColorMode,
57 dimensions: (u32, u32),
58}
59
60impl Frame {
61 pub(crate) fn new_from_decoder(
62 timestamp: i32,
63 color_mode: ColorMode,
64 frame_data: Vec<u8>,
65 dimensions: (u32, u32),
66 ) -> Self {
67 Self {
68 timestamp,
69 color_mode,
70 frame_data,
71 dimensions,
72 }
73 }
74
75 /// Get dimensions of the frame (`width`, `height`)
76 pub fn dimensions(&self) -> (u32, u32) {
77 self.dimensions
78 }
79
80 /// Get [`ColorMode`] of the frame (consistent accross frames)
81 pub fn color_mode(&self) -> ColorMode {
82 self.color_mode
83 }
84
85 /// Get timestamp of the frame in milliseconds
86 pub fn timestamp(&self) -> i32 {
87 self.timestamp
88 }
89
90 /// Get decoded frame data, size `width` * `height` * 4, pixels in [`ColorMode`] format
91 pub fn data(&self) -> &[u8] {
92 &self.frame_data
93 }
94
95 /// Convert the frame to [`image::ImageBuffer`] in `Rgba<u8>` format
96 ///
97 /// Must have [`ColorMode`] set to [`ColorMode::Rgba`] (default) when creating
98 /// [`Decoder`]
99 ///
100 /// Requires feature `image` to be enabled
101 ///
102 /// ```
103 /// # use webp_animation::{Decoder, DecoderOptions, ColorMode};
104 /// #
105 /// let buffer = std::fs::read("./data/animated.webp").unwrap();
106 /// let decoder = Decoder::new(&buffer).unwrap();
107 /// let frame = decoder.into_iter().next().unwrap();
108 /// let _image = frame.into_image().unwrap();
109 /// // _image.save("my_frame.jpg");
110 /// ```
111 #[cfg(feature = "image")]
112 pub fn into_image(self) -> Result<ImageBuffer<image::Rgba<u8>, Vec<u8>>, Error> {
113 self.into_rgba_image()
114 }
115
116 /// Convert the frame to [`image::ImageBuffer`] in `Rgba<u8>` format
117 ///
118 /// Must have [`ColorMode`] set to [`ColorMode::Rgba`] (default) when creating
119 /// [`Decoder`]
120 #[cfg(feature = "image")]
121 pub fn into_rgba_image(self) -> Result<ImageBuffer<image::Rgba<u8>, Vec<u8>>, Error> {
122 if self.color_mode != ColorMode::Rgba {
123 return Err(Error::WrongColorMode(self.color_mode, ColorMode::Rgba));
124 }
125
126 Ok(ImageBuffer::from_vec(self.dimensions.0, self.dimensions.1, self.frame_data).unwrap())
127 }
128}
129
130impl Debug for Frame {
131 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132 write!(
133 f,
134 "Frame {{ timestamp: {}, frame_data: {}b }}",
135 self.timestamp,
136 self.frame_data.len()
137 )
138 }
139}