rlvgl_core/plugins/
gif.rs1use crate::widget::Color;
3use alloc::vec::Vec;
4pub use gif::DecodingError;
5use gif::{ColorOutput, DecodeOptions, Frame};
6use std::io::Cursor;
7
8#[derive(Debug, Clone)]
9pub struct GifFrame {
11 pub pixels: Vec<Color>,
13 pub delay: u16,
15}
16
17pub fn decode(data: &[u8]) -> Result<(Vec<GifFrame>, u16, u16), DecodingError> {
19 let mut options = DecodeOptions::new();
20 options.set_color_output(ColorOutput::RGBA);
21 let mut reader = options.read_info(Cursor::new(data))?;
22 let width = reader.width();
23 let height = reader.height();
24 let mut frames = Vec::new();
25 while let Some(frame) = reader.read_next_frame()? {
26 frames.push(convert_frame(frame, width, height));
27 }
28 Ok((frames, width, height))
29}
30
31fn convert_frame(frame: &Frame<'_>, width: u16, height: u16) -> GifFrame {
32 let mut pixels = Vec::with_capacity(width as usize * height as usize);
33 for chunk in frame.buffer.chunks_exact(4) {
34 pixels.push(Color(chunk[0], chunk[1], chunk[2], 255));
35 }
36 GifFrame {
37 pixels,
38 delay: frame.delay,
39 }
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45 use base64::Engine;
46
47 const RED_DOT_GIF: &str = "R0lGODdhAQABAPAAAP8AAP///yH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==";
48
49 #[test]
50 fn decode_red_dot() {
51 let data = base64::engine::general_purpose::STANDARD
52 .decode(RED_DOT_GIF)
53 .unwrap();
54 let (frames, w, h) = decode(&data).unwrap();
55 assert_eq!((w, h), (1, 1));
56 assert_eq!(frames.len(), 1);
57 assert_eq!(frames[0].pixels, vec![Color(255, 0, 0, 255)]);
58 }
59}