rlvgl_core/plugins/
apng.rs1use crate::widget::Color;
3use alloc::vec::Vec;
4use image::{AnimationDecoder, ImageDecoder, ImageError, codecs::png::PngDecoder};
5use std::io::Cursor;
6
7#[derive(Debug, Clone)]
9pub struct ApngFrame {
10 pub pixels: Vec<Color>,
12 pub delay: u16,
14}
15
16pub 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}