1 use byteorder::{LittleEndian, ReadBytesExt};
2 use std::io::{Read, Seek, SeekFrom};
3
4 use color::ColorType;
5 use image::{DecodingResult, ImageDecoder, ImageError, ImageResult};
6
7 use self::InnerDecoder::*;
8 use bmp::BMPDecoder;
9 use png::PNGDecoder;
10
11 // http://www.w3.org/TR/PNG-Structure.html
12 // The first eight bytes of a PNG file always contain the following (decimal) values:
13 const PNG_SIGNATURE: [u8; 8] = [137, 80, 78, 71, 13, 10, 26, 10];
14
15 /// An ico decoder
16 pub struct ICODecoder<R: Read> {
17 selected_entry: DirEntry,
18 inner_decoder: InnerDecoder<R>,
19 }
20
21 enum InnerDecoder<R: Read> {
22 BMP(BMPDecoder<R>),
23 PNG(PNGDecoder<R>),
24 }
25
26 #[derive(Clone, Copy, Default)]
27 struct DirEntry {
28 width: u8,
29 height: u8,
30 color_count: u8,
31 reserved: u8,
32
33 num_color_planes: u16,
34 bits_per_pixel: u16,
35
36 image_length: u32,
37 image_offset: u32,
38 }
39
40 impl<R: Read + Seek> ICODecoder<R> {
41 /// Create a new decoder that decodes from the stream ```r```
new(mut r: R) -> ImageResult<ICODecoder<R>>42 pub fn new(mut r: R) -> ImageResult<ICODecoder<R>> {
43 let entries = try!(read_entries(&mut r));
44 let entry = try!(best_entry(entries));
45 let decoder = try!(entry.decoder(r));
46
47 Ok(ICODecoder {
48 selected_entry: entry,
49 inner_decoder: decoder,
50 })
51 }
52 }
53
read_entries<R: Read>(r: &mut R) -> ImageResult<Vec<DirEntry>>54 fn read_entries<R: Read>(r: &mut R) -> ImageResult<Vec<DirEntry>> {
55 let _reserved = try!(r.read_u16::<LittleEndian>());
56 let _type = try!(r.read_u16::<LittleEndian>());
57 let count = try!(r.read_u16::<LittleEndian>());
58 (0..count).map(|_| read_entry(r)).collect()
59 }
60
read_entry<R: Read>(r: &mut R) -> ImageResult<DirEntry>61 fn read_entry<R: Read>(r: &mut R) -> ImageResult<DirEntry> {
62 let mut entry = DirEntry::default();
63
64 entry.width = try!(r.read_u8());
65 entry.height = try!(r.read_u8());
66 entry.color_count = try!(r.read_u8());
67 // Reserved value (not used)
68 entry.reserved = try!(r.read_u8());
69
70 // This may be either the number of color planes (0 or 1), or the horizontal coordinate
71 // of the hotspot for CUR files.
72 entry.num_color_planes = try!(r.read_u16::<LittleEndian>());
73 if entry.num_color_planes > 256 {
74 return Err(ImageError::FormatError(
75 "ICO image entry has a too large color planes/hotspot value".to_string(),
76 ));
77 }
78
79 // This may be either the bit depth (may be 0 meaning unspecified),
80 // or the vertical coordinate of the hotspot for CUR files.
81 entry.bits_per_pixel = try!(r.read_u16::<LittleEndian>());
82 if entry.bits_per_pixel > 256 {
83 return Err(ImageError::FormatError(
84 "ICO image entry has a too large bits per pixel/hotspot value".to_string(),
85 ));
86 }
87
88 entry.image_length = try!(r.read_u32::<LittleEndian>());
89 entry.image_offset = try!(r.read_u32::<LittleEndian>());
90
91 Ok(entry)
92 }
93
94 /// Find the entry with the highest (color depth, size).
best_entry(mut entries: Vec<DirEntry>) -> ImageResult<DirEntry>95 fn best_entry(mut entries: Vec<DirEntry>) -> ImageResult<DirEntry> {
96 let mut best = try!(entries.pop().ok_or(ImageError::ImageEnd));
97 let mut best_score = (
98 best.bits_per_pixel,
99 u32::from(best.real_width()) * u32::from(best.real_height()),
100 );
101
102 for entry in entries {
103 let score = (
104 entry.bits_per_pixel,
105 u32::from(entry.real_width()) * u32::from(entry.real_height()),
106 );
107 if score > best_score {
108 best = entry;
109 best_score = score;
110 }
111 }
112 Ok(best)
113 }
114
115 impl DirEntry {
real_width(&self) -> u16116 fn real_width(&self) -> u16 {
117 match self.width {
118 0 => 256,
119 w => u16::from(w),
120 }
121 }
122
real_height(&self) -> u16123 fn real_height(&self) -> u16 {
124 match self.height {
125 0 => 256,
126 h => u16::from(h),
127 }
128 }
129
matches_dimensions(&self, width: u32, height: u32) -> bool130 fn matches_dimensions(&self, width: u32, height: u32) -> bool {
131 u32::from(self.real_width()) == width && u32::from(self.real_height()) == height
132 }
133
seek_to_start<R: Read + Seek>(&self, r: &mut R) -> ImageResult<()>134 fn seek_to_start<R: Read + Seek>(&self, r: &mut R) -> ImageResult<()> {
135 try!(r.seek(SeekFrom::Start(u64::from(self.image_offset))));
136 Ok(())
137 }
138
is_png<R: Read + Seek>(&self, r: &mut R) -> ImageResult<bool>139 fn is_png<R: Read + Seek>(&self, r: &mut R) -> ImageResult<bool> {
140 try!(self.seek_to_start(r));
141
142 // Read the first 8 bytes to sniff the image.
143 let mut signature = [0u8; 8];
144 try!(r.read_exact(&mut signature));
145
146 Ok(signature == PNG_SIGNATURE)
147 }
148
decoder<R: Read + Seek>(&self, mut r: R) -> ImageResult<InnerDecoder<R>>149 fn decoder<R: Read + Seek>(&self, mut r: R) -> ImageResult<InnerDecoder<R>> {
150 let is_png = try!(self.is_png(&mut r));
151 try!(self.seek_to_start(&mut r));
152
153 if is_png {
154 Ok(PNG(PNGDecoder::new(r)))
155 } else {
156 let mut decoder = BMPDecoder::new(r);
157 try!(decoder.read_metadata_in_ico_format());
158 Ok(BMP(decoder))
159 }
160 }
161 }
162
163 impl<R: Read + Seek> ImageDecoder for ICODecoder<R> {
dimensions(&mut self) -> ImageResult<(u32, u32)>164 fn dimensions(&mut self) -> ImageResult<(u32, u32)> {
165 match self.inner_decoder {
166 BMP(ref mut decoder) => decoder.dimensions(),
167 PNG(ref mut decoder) => decoder.dimensions(),
168 }
169 }
170
colortype(&mut self) -> ImageResult<ColorType>171 fn colortype(&mut self) -> ImageResult<ColorType> {
172 match self.inner_decoder {
173 BMP(ref mut decoder) => decoder.colortype(),
174 PNG(ref mut decoder) => decoder.colortype(),
175 }
176 }
177
row_len(&mut self) -> ImageResult<usize>178 fn row_len(&mut self) -> ImageResult<usize> {
179 match self.inner_decoder {
180 BMP(ref mut decoder) => decoder.row_len(),
181 PNG(ref mut decoder) => decoder.row_len(),
182 }
183 }
184
read_scanline(&mut self, buf: &mut [u8]) -> ImageResult<u32>185 fn read_scanline(&mut self, buf: &mut [u8]) -> ImageResult<u32> {
186 match self.inner_decoder {
187 BMP(ref mut decoder) => decoder.read_scanline(buf),
188 PNG(ref mut decoder) => decoder.read_scanline(buf),
189 }
190 }
191
read_image(&mut self) -> ImageResult<DecodingResult>192 fn read_image(&mut self) -> ImageResult<DecodingResult> {
193 match self.inner_decoder {
194 PNG(ref mut decoder) => {
195 if self.selected_entry.image_length < PNG_SIGNATURE.len() as u32 {
196 return Err(ImageError::FormatError(
197 "Entry specified a length that is shorter than PNG header!".to_string(),
198 ));
199 }
200
201 // Check if the image dimensions match the ones in the image data.
202 let (width, height) = try!(decoder.dimensions());
203 if !self.selected_entry.matches_dimensions(width, height) {
204 return Err(ImageError::FormatError(
205 "Entry and PNG dimensions do not match!".to_string(),
206 ));
207 }
208
209 // Embedded PNG images can only be of the 32BPP RGBA format.
210 // https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473/
211 let color_type = try!(decoder.colortype());
212 if let ColorType::RGBA(8) = color_type {
213 } else {
214 return Err(ImageError::FormatError(
215 "The PNG is not in RGBA format!".to_string(),
216 ));
217 }
218
219 decoder.read_image()
220 }
221 BMP(ref mut decoder) => {
222 let (width, height) = try!(decoder.dimensions());
223 if !self.selected_entry.matches_dimensions(width, height) {
224 return Err(ImageError::FormatError(
225 "Entry({:?}) and BMP({:?}) dimensions do not match!".to_string(),
226 ));
227 }
228
229 // The ICO decoder needs an alpha channel to apply the AND mask.
230 if try!(decoder.colortype()) != ColorType::RGBA(8) {
231 return Err(ImageError::UnsupportedError(
232 "Unsupported color type".to_string(),
233 ));
234 }
235
236 let mut pixel_data = match try!(decoder.read_image()) {
237 DecodingResult::U8(v) => v,
238 _ => unreachable!(),
239 };
240
241 // If there's an AND mask following the image, read and apply it.
242 let r = decoder.reader();
243 let mask_start = try!(r.seek(SeekFrom::Current(0)));
244 let mask_end =
245 u64::from(self.selected_entry.image_offset + self.selected_entry.image_length);
246 let mask_length = mask_end - mask_start;
247
248 if mask_length > 0 {
249 // A mask row contains 1 bit per pixel, padded to 4 bytes.
250 let mask_row_bytes = ((width + 31) / 32) * 4;
251 let expected_length = u64::from(mask_row_bytes * height);
252 if mask_length < expected_length {
253 return Err(ImageError::ImageEnd);
254 }
255
256 for y in 0..height {
257 let mut x = 0;
258 for _ in 0..mask_row_bytes {
259 // Apply the bits of each byte until we reach the end of the row.
260 let mask_byte = try!(r.read_u8());
261 for bit in (0..8).rev() {
262 if x >= width {
263 break;
264 }
265 if mask_byte & (1 << bit) != 0 {
266 // Set alpha channel to transparent.
267 pixel_data[((height - y - 1) * width + x) as usize * 4 + 3] = 0;
268 }
269 x += 1;
270 }
271 }
272 }
273 }
274 Ok(DecodingResult::U8(pixel_data))
275 }
276 }
277 }
278 }
279