1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
use std::fmt::Debug;

#[cfg(feature = "image")]
use image::ImageBuffer;

#[allow(unused_imports)]
use crate::{ColorMode, Decoder, Error, PIXEL_BYTES};

/// An animation frame containing data and metadata produced by [`Decoder`]
///
/// Getting metadata:
/// ```rust
/// # use webp_animation::{Decoder, ColorMode};
/// #
/// # let buffer = std::fs::read("./data/animated.webp").unwrap();
/// # let decoder = Decoder::new(&buffer).unwrap();
/// # let frame = decoder.into_iter().next().unwrap();
/// #
/// assert_eq!(frame.dimensions(), (400, 400));
/// assert_eq!(frame.color_mode(), ColorMode::Rgba);
/// ```
///
/// Accessing frame data in raw [`ColorMode`] -encoded bytes:
/// ```rust
/// # use webp_animation::{Decoder, ColorMode};
/// #
/// # let buffer = std::fs::read("./data/animated.webp").unwrap();
/// # let decoder = Decoder::new(&buffer).unwrap();
/// # let frame = decoder.into_iter().next().unwrap();
/// #
/// assert_eq!(frame.data().len(), (400 * 400 * 4));
/// assert_eq!(frame.data()[0..4], [0, 0, 0, 255]);
/// ```
///
/// If `image` feature is enabled, frame can be converted into [`image::ImageBuffer`]:
/// ```rust
/// # use webp_animation::{Decoder, ColorMode};
/// #
/// # let buffer = std::fs::read("./data/animated.webp").unwrap();
/// # let decoder = Decoder::new(&buffer).unwrap();
/// # let frame = decoder.into_iter().next().unwrap();
/// #
/// ##[cfg(feature = "image")]
/// let image = frame.into_image().unwrap();
/// ##[cfg(feature = "image")]
/// assert_eq!(image.dimensions(), (400, 400));
/// ##[cfg(feature = "image")]
/// assert_eq!(image.height(), 400);
/// // image.save("frame.png");
/// ```
pub struct Frame {
    timestamp: i32,
    frame_data: Vec<u8>,

    #[allow(dead_code)]
    color_mode: ColorMode,
    dimensions: (u32, u32),
}

impl Frame {
    pub(crate) fn new_from_decoder(
        timestamp: i32,
        color_mode: ColorMode,
        frame_data: Vec<u8>,
        dimensions: (u32, u32),
    ) -> Self {
        Self {
            timestamp,
            color_mode,
            frame_data,
            dimensions,
        }
    }

    /// Get dimensions of the frame (`width`, `height`)
    pub fn dimensions(&self) -> (u32, u32) {
        self.dimensions
    }

    /// Get [`ColorMode`] of the frame (consistent accross frames)
    pub fn color_mode(&self) -> ColorMode {
        self.color_mode
    }

    /// Get timestamp of the frame in milliseconds
    pub fn timestamp(&self) -> i32 {
        self.timestamp
    }

    /// Get decoded frame data, size `width` * `height` * 4, pixels in [`ColorMode`] format
    pub fn data(&self) -> &[u8] {
        &self.frame_data
    }

    /// Convert the frame to [`image::ImageBuffer`] in `Rgba<u8>` format
    ///
    /// Must have [`ColorMode`] set to [`ColorMode::Rgba`] (default) when creating
    /// [`Decoder`]
    ///
    /// Requires feature `image` to be enabled
    ///
    /// ```
    /// # use webp_animation::{Decoder, DecoderOptions, ColorMode};
    /// #
    /// let buffer = std::fs::read("./data/animated.webp").unwrap();
    /// let decoder = Decoder::new(&buffer).unwrap();
    /// let frame = decoder.into_iter().next().unwrap();
    /// let _image = frame.into_image().unwrap();
    /// // _image.save("my_frame.jpg");
    /// ```
    #[cfg(feature = "image")]
    pub fn into_image(self) -> Result<ImageBuffer<image::Rgba<u8>, Vec<u8>>, Error> {
        self.into_rgba_image()
    }

    /// Convert the frame to [`image::ImageBuffer`] in `Rgba<u8>` format
    ///
    /// Must have [`ColorMode`] set to [`ColorMode::Rgba`] (default) when creating
    /// [`Decoder`]
    #[cfg(feature = "image")]
    pub fn into_rgba_image(self) -> Result<ImageBuffer<image::Rgba<u8>, Vec<u8>>, Error> {
        if self.color_mode != ColorMode::Rgba {
            return Err(Error::WrongColorMode(self.color_mode, ColorMode::Rgba));
        }

        Ok(ImageBuffer::from_vec(self.dimensions.0, self.dimensions.1, self.frame_data).unwrap())
    }

    /// Convert the frame to [`image::ImageBuffer`] in `Bgra<u8>` format
    ///
    /// Must have [`ColorMode`] set to [`ColorMode::Bgra`] when creating [`Decoder`]
    #[cfg(feature = "image")]
    pub fn into_bgra_image(self) -> Result<ImageBuffer<image::Bgra<u8>, Vec<u8>>, Error> {
        if self.color_mode != ColorMode::Bgra {
            return Err(Error::WrongColorMode(self.color_mode, ColorMode::Bgra));
        }
        Ok(ImageBuffer::from_vec(self.dimensions.0, self.dimensions.1, self.frame_data).unwrap())
    }
}

impl Debug for Frame {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Frame {{ timestamp: {}, frame_data: {}b }}",
            self.timestamp,
            self.frame_data.len()
        )
    }
}