riff_ani/
lib.rs

1use byteorder::{WriteBytesExt, LE};
2use ico::IconDir;
3use riff::{ChunkContents, ChunkId, LIST_ID, RIFF_ID};
4use std::io::{Seek, Write};
5use thiserror::Error;
6
7pub use ico;
8
9#[derive(Error, Debug)]
10pub enum Error {
11    #[error("I/O error or system error")]
12    Io(#[from] std::io::Error),
13}
14
15pub type Result<T> = std::result::Result<T, Error>;
16
17pub struct Ani {
18    pub header: AniHeader,
19    pub frames: Vec<IconDir>,
20}
21
22pub struct AniHeader {
23    /// The number of stored frames in this animation.
24    pub num_frames: u32,
25    /// The number of steps in this animation. Since the `seq` chunk is not implemented, it should
26    /// be equal to `num_frames`.
27    pub num_steps: u32,
28    /// The width.
29    pub width: u32,
30    /// The height.
31    pub height: u32,
32    /// The number of jiffies (1/60 sec) that each frame displays.
33    pub frame_rate: u32,
34}
35
36impl Ani {
37    pub fn encode<T: Seek + Write>(&self, mut writer: T) -> Result<u64> {
38        const fn chunk_id(value: &[u8; 4]) -> ChunkId {
39            ChunkId { value: *value }
40        }
41
42        let contents = ChunkContents::Children(
43            RIFF_ID.clone(),
44            chunk_id(b"ACON"),
45            vec![
46                ChunkContents::Data(chunk_id(b"anih"), self.encode_header()?),
47                ChunkContents::Children(LIST_ID.clone(), chunk_id(b"fram"), {
48                    let mut chunks = Vec::new();
49                    for cur in &self.frames {
50                        let mut data = Vec::new();
51                        cur.write(&mut data)?;
52                        chunks.push(ChunkContents::Data(chunk_id(b"icon"), data));
53                    }
54                    chunks
55                }),
56            ],
57        );
58
59        contents.write(&mut writer).map_err(From::from)
60    }
61
62    fn encode_header(&self) -> Result<Vec<u8>> {
63        // 4 (header size) + 32 (the rest)
64        let mut data = Vec::with_capacity(36);
65
66        data.write_u32::<LE>(36)?; // Header size
67
68        data.write_u32::<LE>(self.header.num_frames)?;
69        data.write_u32::<LE>(self.header.num_steps)?;
70        data.write_u32::<LE>(self.header.width)?;
71        data.write_u32::<LE>(self.header.height)?;
72        data.write_u32::<LE>(32)?; // Color depth
73        data.write_u32::<LE>(1)?; // Number of planes
74        data.write_u32::<LE>(self.header.frame_rate)?;
75        data.write_u32::<LE>(0b01)?; // Flags
76
77        Ok(data)
78    }
79}