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
use crate::{DivoomAPIError, DivoomAPIResult};
use image::codecs::gif::GifDecoder;
use image::io::Reader as ImageReader;
use image::AnimationDecoder;
use std::fs::File;
use std::io::{BufReader, Cursor, Read};
use tiny_skia::Pixmap;

/// Load resources into a series of `tiny_skia::Pixmap`, so we can use them to build the animations.
pub struct DivoomAnimationResourceLoader {}

impl DivoomAnimationResourceLoader {
    /// Load image from file
    pub fn from_image_file(file_path: &str) -> DivoomAPIResult<Pixmap> {
        let image_file = File::open(file_path)?;
        DivoomAnimationResourceLoader::from_image(image_file)
    }

    /// Load image from Read trait
    pub fn from_image<R: Read>(reader: R) -> DivoomAPIResult<Pixmap> {
        let mut buf_reader = BufReader::new(reader);

        let mut image_buffer: Vec<u8> = Vec::new();
        buf_reader.read_to_end(&mut image_buffer)?;

        DivoomAnimationResourceLoader::from_image_buf(&image_buffer)
    }

    /// Load image from memory buffer
    pub fn from_image_buf(buf: &[u8]) -> DivoomAPIResult<Pixmap> {
        let image = ImageReader::new(Cursor::new(buf))
            .with_guessed_format()?
            .decode()?;
        let image_rgba8 = image.into_rgba8();

        let (width, height) = (image_rgba8.width(), image_rgba8.height());
        let mut frame = Pixmap::new(width as u32, height as u32).unwrap();
        frame.data_mut().copy_from_slice(&image_rgba8);

        Ok(frame)
    }

    /// Load gif resource from local file
    pub fn from_gif_file(file_path: &str) -> DivoomAPIResult<Vec<Pixmap>> {
        let input = File::open(file_path)?;
        DivoomAnimationResourceLoader::from_gif(input)
    }

    /// Load gif from a memory buffer
    pub fn from_gif_buf(buf: &[u8]) -> DivoomAPIResult<Vec<Pixmap>> {
        DivoomAnimationResourceLoader::from_gif(Cursor::new(buf))
    }

    /// Load gif resource from Read trait
    pub fn from_gif<R: Read>(reader: R) -> DivoomAPIResult<Vec<Pixmap>> {
        let mut frames = vec![];

        let decoder = GifDecoder::new(reader)?;
        for frame in decoder.into_frames().collect_frames()? {
            let mut frame_pixmap = Pixmap::new(
                frame.buffer().width() as u32,
                frame.buffer().height() as u32,
            )
            .unwrap();
            assert_eq!(frame_pixmap.data().len(), frame.buffer().len());
            frame_pixmap
                .data_mut()
                .copy_from_slice(frame.into_buffer().as_raw());
            frames.push(frame_pixmap);
        }

        Ok(frames)
    }
}

impl From<image::ImageError> for DivoomAPIError {
    fn from(err: image::ImageError) -> Self {
        DivoomAPIError::ResourceDecodeError(err.to_string())
    }
}