1pub fn ico(images: &[(u32, Vec<u8>)]) -> Vec<u8> {
15 let mut out = Vec::new();
16 out.extend_from_slice(&0u16.to_le_bytes()); out.extend_from_slice(&1u16.to_le_bytes()); out.extend_from_slice(&(images.len() as u16).to_le_bytes());
19
20 let mut offset = 6 + 16 * images.len() as u32;
22 for (size, png) in images {
23 assert!(
24 (1..=256).contains(size),
25 "an ico entry is at most 256 pixels, not {size}"
26 );
27 let dimension = if *size == 256 { 0u8 } else { *size as u8 };
29 out.push(dimension);
30 out.push(dimension);
31 out.push(0); out.push(0); out.extend_from_slice(&1u16.to_le_bytes()); out.extend_from_slice(&32u16.to_le_bytes()); out.extend_from_slice(&(png.len() as u32).to_le_bytes());
36 out.extend_from_slice(&offset.to_le_bytes());
37 offset += png.len() as u32;
38 }
39 for (_, png) in images {
40 out.extend_from_slice(png);
41 }
42 out
43}
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48
49 #[test]
50 fn every_entry_points_at_its_own_payload() {
51 let images = vec![(16u32, vec![1u8; 30]), (32, vec![2u8; 40])];
52 let bytes = ico(&images);
53 assert_eq!(&bytes[0..6], &[0, 0, 1, 0, 2, 0]);
54 for (index, (size, png)) in images.iter().enumerate() {
55 let entry = 6 + 16 * index;
56 assert_eq!(u32::from(bytes[entry]), *size);
57 let length =
58 u32::from_le_bytes(bytes[entry + 8..entry + 12].try_into().unwrap()) as usize;
59 let offset =
60 u32::from_le_bytes(bytes[entry + 12..entry + 16].try_into().unwrap()) as usize;
61 assert_eq!(&bytes[offset..offset + length], png.as_slice());
62 }
63 }
64
65 #[test]
66 fn a_256_pixel_entry_writes_its_size_as_zero() {
67 let bytes = ico(&[(256, vec![0u8; 8])]);
68 assert_eq!(bytes[6], 0);
69 assert_eq!(bytes[7], 0);
70 }
71}