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 pub num_frames: u32,
25 pub num_steps: u32,
28 pub width: u32,
30 pub height: u32,
32 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 let mut data = Vec::with_capacity(36);
65
66 data.write_u32::<LE>(36)?; 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)?; data.write_u32::<LE>(1)?; data.write_u32::<LE>(self.header.frame_rate)?;
75 data.write_u32::<LE>(0b01)?; Ok(data)
78 }
79}