1 use byteorder::{LittleEndian, WriteBytesExt};
2 use std::io::{self, Write};
3
4 use crate::color::ColorType;
5 use crate::error::ImageResult;
6 use crate::image::ImageEncoder;
7
8 use crate::png::PngEncoder;
9
10 // Enum value indicating an ICO image (as opposed to a CUR image):
11 const ICO_IMAGE_TYPE: u16 = 1;
12 // The length of an ICO file ICONDIR structure, in bytes:
13 const ICO_ICONDIR_SIZE: u32 = 6;
14 // The length of an ICO file DIRENTRY structure, in bytes:
15 const ICO_DIRENTRY_SIZE: u32 = 16;
16
17 /// ICO encoder
18 pub struct IcoEncoder<W: Write> {
19 w: W,
20 }
21
22 /// ICO encoder
23 ///
24 /// An alias of [`IcoEncoder`].
25 ///
26 /// TODO: remove
27 ///
28 /// [`IcoEncoder`]: struct.IcoEncoder.html
29 #[allow(dead_code)]
30 #[deprecated(note = "Use `IcoEncoder` instead")]
31 pub type ICOEncoder<W> = IcoEncoder<W>;
32
33 impl<W: Write> IcoEncoder<W> {
34 /// Create a new encoder that writes its output to ```w```.
new(w: W) -> IcoEncoder<W>35 pub fn new(w: W) -> IcoEncoder<W> {
36 IcoEncoder { w }
37 }
38
39 /// Encodes the image ```image``` that has dimensions ```width``` and
40 /// ```height``` and ```ColorType``` ```c```. The dimensions of the image
41 /// must be between 1 and 256 (inclusive) or an error will be returned.
encode( mut self, data: &[u8], width: u32, height: u32, color: ColorType, ) -> ImageResult<()>42 pub fn encode(
43 mut self,
44 data: &[u8],
45 width: u32,
46 height: u32,
47 color: ColorType,
48 ) -> ImageResult<()> {
49 let mut image_data: Vec<u8> = Vec::new();
50 PngEncoder::new(&mut image_data).encode(data, width, height, color)?;
51
52 write_icondir(&mut self.w, 1)?;
53 write_direntry(
54 &mut self.w,
55 width,
56 height,
57 color,
58 ICO_ICONDIR_SIZE + ICO_DIRENTRY_SIZE,
59 image_data.len() as u32,
60 )?;
61 self.w.write_all(&image_data)?;
62 Ok(())
63 }
64 }
65
66 impl<W: Write> ImageEncoder for IcoEncoder<W> {
write_image( self, buf: &[u8], width: u32, height: u32, color_type: ColorType, ) -> ImageResult<()>67 fn write_image(
68 self,
69 buf: &[u8],
70 width: u32,
71 height: u32,
72 color_type: ColorType,
73 ) -> ImageResult<()> {
74 self.encode(buf, width, height, color_type)
75 }
76 }
77
write_icondir<W: Write>(w: &mut W, num_images: u16) -> io::Result<()>78 fn write_icondir<W: Write>(w: &mut W, num_images: u16) -> io::Result<()> {
79 // Reserved field (must be zero):
80 w.write_u16::<LittleEndian>(0)?;
81 // Image type (ICO or CUR):
82 w.write_u16::<LittleEndian>(ICO_IMAGE_TYPE)?;
83 // Number of images in the file:
84 w.write_u16::<LittleEndian>(num_images)?;
85 Ok(())
86 }
87
write_direntry<W: Write>( w: &mut W, width: u32, height: u32, color: ColorType, data_start: u32, data_size: u32, ) -> io::Result<()>88 fn write_direntry<W: Write>(
89 w: &mut W,
90 width: u32,
91 height: u32,
92 color: ColorType,
93 data_start: u32,
94 data_size: u32,
95 ) -> io::Result<()> {
96 // Image dimensions:
97 write_width_or_height(w, width)?;
98 write_width_or_height(w, height)?;
99 // Number of colors in palette (or zero for no palette):
100 w.write_u8(0)?;
101 // Reserved field (must be zero):
102 w.write_u8(0)?;
103 // Color planes:
104 w.write_u16::<LittleEndian>(0)?;
105 // Bits per pixel:
106 w.write_u16::<LittleEndian>(color.bits_per_pixel())?;
107 // Image data size, in bytes:
108 w.write_u32::<LittleEndian>(data_size)?;
109 // Image data offset, in bytes:
110 w.write_u32::<LittleEndian>(data_start)?;
111 Ok(())
112 }
113
114 /// Encode a width/height value as a single byte, where 0 means 256.
write_width_or_height<W: Write>(w: &mut W, value: u32) -> io::Result<()>115 fn write_width_or_height<W: Write>(w: &mut W, value: u32) -> io::Result<()> {
116 if value < 1 || value > 256 {
117 // TODO: this is not very idiomatic yet. Should return an EncodingError and be checked
118 // prior to encoding.
119 return Err(io::Error::new(
120 io::ErrorKind::InvalidData,
121 "Invalid ICO dimensions (width and \
122 height must be between 1 and 256)",
123 ));
124 }
125 w.write_u8(if value < 256 { value as u8 } else { 0 })
126 }
127