rlvgl_core/plugins/
apng.rs

1//! APNG decoder returning frames.
2use crate::widget::Color;
3use alloc::vec::Vec;
4use image::{AnimationDecoder, ImageDecoder, ImageError, codecs::png::PngDecoder};
5use std::io::Cursor;
6
7/// A single frame decoded from an APNG image.
8#[derive(Debug, Clone)]
9pub struct ApngFrame {
10    /// Pixel data for the frame in RGB format.
11    pub pixels: Vec<Color>,
12    /// Delay time for this frame in hundredths of a second.
13    pub delay: u16,
14}
15
16/// Decode an APNG byte stream and return the frames with image dimensions.
17pub fn decode(data: &[u8]) -> Result<(Vec<ApngFrame>, u32, u32), ImageError> {
18    let decoder = PngDecoder::new(Cursor::new(data))?;
19    let (width, height) = decoder.dimensions();
20    let apng = decoder.apng()?;
21    let mut frames_out = Vec::new();
22    for frame_res in apng.into_frames() {
23        let frame = frame_res?;
24        let (numer, denom) = frame.delay().numer_denom_ms();
25        let delay_ms = if denom == 0 { 0 } else { numer / denom };
26        let buffer = frame.into_buffer();
27        let mut pixels = Vec::with_capacity((width * height) as usize);
28        for chunk in buffer.into_raw().chunks_exact(4) {
29            pixels.push(Color(chunk[0], chunk[1], chunk[2], 255));
30        }
31        frames_out.push(ApngFrame {
32            pixels,
33            delay: (delay_ms / 10) as u16,
34        });
35    }
36    Ok((frames_out, width, height))
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42    use apng::{Encoder, Frame as ApngFrameInfo, PNGImage, create_config};
43    use png::{BitDepth, ColorType};
44
45    #[test]
46    fn decode_two_frames() {
47        let img1 = PNGImage {
48            width: 1,
49            height: 1,
50            data: vec![255, 0, 0, 255],
51            color_type: ColorType::Rgba,
52            bit_depth: BitDepth::Eight,
53        };
54        let img2 = PNGImage {
55            width: 1,
56            height: 1,
57            data: vec![0, 255, 0, 255],
58            color_type: ColorType::Rgba,
59            bit_depth: BitDepth::Eight,
60        };
61        let cfg = create_config(&vec![img1.clone(), img2.clone()], Some(1)).unwrap();
62        let mut data = Vec::new();
63        {
64            let mut enc = Encoder::new(&mut data, cfg).unwrap();
65            enc.write_frame(&img1, ApngFrameInfo::default()).unwrap();
66            enc.write_frame(&img2, ApngFrameInfo::default()).unwrap();
67            enc.finish_encode().unwrap();
68        }
69        let (frames, w, h) = decode(&data).unwrap();
70        assert_eq!((w, h), (1, 1));
71        assert_eq!(frames.len(), 2);
72        assert_eq!(frames[0].pixels[0], Color(255, 0, 0, 255));
73        assert_eq!(frames[1].pixels[0], Color(0, 255, 0, 255));
74    }
75}