1use byteorder::{LittleEndian, ReadBytesExt};
2use std::io::{self, Cursor, Read, Seek, SeekFrom};
3use std::marker::PhantomData;
4use std::{error, fmt, mem};
5
6use crate::color::ColorType;
7use crate::error::{
8 DecodingError, ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind,
9};
10use crate::image::{self, ImageDecoder, ImageFormat};
11
12use self::InnerDecoder::*;
13use crate::codecs::bmp::BmpDecoder;
14use crate::codecs::png::{PngDecoder, PNG_SIGNATURE};
15
16/// Errors that can occur during decoding and parsing an ICO image or one of its enclosed images.
17#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
18enum DecoderError {
19 /// The ICO directory is empty
20 NoEntries,
21 /// The number of color planes (0 or 1), or the horizontal coordinate of the hotspot for CUR files too big.
22 IcoEntryTooManyPlanesOrHotspot,
23 /// The bit depth (may be 0 meaning unspecified), or the vertical coordinate of the hotspot for CUR files too big.
24 IcoEntryTooManyBitsPerPixelOrHotspot,
25
26 /// The entry is in PNG format and specified a length that is shorter than PNG header.
27 PngShorterThanHeader,
28 /// The enclosed PNG is not in RGBA, which is invalid: https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473/.
29 PngNotRgba,
30
31 /// The entry is in BMP format and specified a data size that is not correct for the image and optional mask data.
32 InvalidDataSize,
33
34 /// The dimensions specified by the entry does not match the dimensions in the header of the enclosed image.
35 ImageEntryDimensionMismatch {
36 /// The mismatched subimage's type
37 format: IcoEntryImageFormat,
38 /// The dimensions specified by the entry
39 entry: (u16, u16),
40 /// The dimensions of the image itself
41 image: (u32, u32),
42 },
43}
44
45impl fmt::Display for DecoderError {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 match self {
48 DecoderError::NoEntries => f.write_str("ICO directory contains no image"),
49 DecoderError::IcoEntryTooManyPlanesOrHotspot => {
50 f.write_str("ICO image entry has too many color planes or too large hotspot value")
51 }
52 DecoderError::IcoEntryTooManyBitsPerPixelOrHotspot => f.write_str(
53 "ICO image entry has too many bits per pixel or too large hotspot value",
54 ),
55 DecoderError::PngShorterThanHeader => {
56 f.write_str("Entry specified a length that is shorter than PNG header!")
57 }
58 DecoderError::PngNotRgba => f.write_str("The PNG is not in RGBA format!"),
59 DecoderError::InvalidDataSize => {
60 f.write_str("ICO image data size did not match expected size")
61 }
62 DecoderError::ImageEntryDimensionMismatch {
63 format,
64 entry,
65 image,
66 } => f.write_fmt(format_args!(
67 "Entry{:?} and {}{:?} dimensions do not match!",
68 entry, format, image
69 )),
70 }
71 }
72}
73
74impl From<DecoderError> for ImageError {
75 fn from(e: DecoderError) -> ImageError {
76 ImageError::Decoding(DecodingError::new(format:ImageFormat::Ico.into(), err:e))
77 }
78}
79
80impl error::Error for DecoderError {}
81
82/// The image formats an ICO may contain
83#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
84enum IcoEntryImageFormat {
85 /// PNG in ARGB
86 Png,
87 /// BMP with optional alpha mask
88 Bmp,
89}
90
91impl fmt::Display for IcoEntryImageFormat {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 f.write_str(data:match self {
94 IcoEntryImageFormat::Png => "PNG",
95 IcoEntryImageFormat::Bmp => "BMP",
96 })
97 }
98}
99
100impl From<IcoEntryImageFormat> for ImageFormat {
101 fn from(val: IcoEntryImageFormat) -> Self {
102 match val {
103 IcoEntryImageFormat::Png => ImageFormat::Png,
104 IcoEntryImageFormat::Bmp => ImageFormat::Bmp,
105 }
106 }
107}
108
109/// An ico decoder
110pub struct IcoDecoder<R: Read> {
111 selected_entry: DirEntry,
112 inner_decoder: InnerDecoder<R>,
113}
114
115enum InnerDecoder<R: Read> {
116 Bmp(BmpDecoder<R>),
117 Png(Box<PngDecoder<R>>),
118}
119
120#[derive(Clone, Copy, Default)]
121struct DirEntry {
122 width: u8,
123 height: u8,
124 // We ignore some header fields as they will be replicated in the PNG, BMP and they are not
125 // necessary for determining the best_entry.
126 #[allow(unused)]
127 color_count: u8,
128 // Wikipedia has this to say:
129 // Although Microsoft's technical documentation states that this value must be zero, the icon
130 // encoder built into .NET (System.Drawing.Icon.Save) sets this value to 255. It appears that
131 // the operating system ignores this value altogether.
132 #[allow(unused)]
133 reserved: u8,
134
135 // We ignore some header fields as they will be replicated in the PNG, BMP and they are not
136 // necessary for determining the best_entry.
137 #[allow(unused)]
138 num_color_planes: u16,
139 bits_per_pixel: u16,
140
141 image_length: u32,
142 image_offset: u32,
143}
144
145impl<R: Read + Seek> IcoDecoder<R> {
146 /// Create a new decoder that decodes from the stream ```r```
147 pub fn new(mut r: R) -> ImageResult<IcoDecoder<R>> {
148 let entries: Vec = read_entries(&mut r)?;
149 let entry: DirEntry = best_entry(entries)?;
150 let decoder: InnerDecoder = entry.decoder(r)?;
151
152 Ok(IcoDecoder {
153 selected_entry: entry,
154 inner_decoder: decoder,
155 })
156 }
157}
158
159fn read_entries<R: Read>(r: &mut R) -> ImageResult<Vec<DirEntry>> {
160 let _reserved: u16 = r.read_u16::<LittleEndian>()?;
161 let _type: u16 = r.read_u16::<LittleEndian>()?;
162 let count: u16 = r.read_u16::<LittleEndian>()?;
163 (0..count).map(|_| read_entry(r)).collect()
164}
165
166fn read_entry<R: Read>(r: &mut R) -> ImageResult<DirEntry> {
167 Ok(DirEntry {
168 width: r.read_u8()?,
169 height: r.read_u8()?,
170 color_count: r.read_u8()?,
171 reserved: r.read_u8()?,
172 num_color_planes: {
173 // This may be either the number of color planes (0 or 1), or the horizontal coordinate
174 // of the hotspot for CUR files.
175 let num = r.read_u16::<LittleEndian>()?;
176 if num > 256 {
177 return Err(DecoderError::IcoEntryTooManyPlanesOrHotspot.into());
178 }
179 num
180 },
181 bits_per_pixel: {
182 // This may be either the bit depth (may be 0 meaning unspecified),
183 // or the vertical coordinate of the hotspot for CUR files.
184 let num = r.read_u16::<LittleEndian>()?;
185 if num > 256 {
186 return Err(DecoderError::IcoEntryTooManyBitsPerPixelOrHotspot.into());
187 }
188 num
189 },
190 image_length: r.read_u32::<LittleEndian>()?,
191 image_offset: r.read_u32::<LittleEndian>()?,
192 })
193}
194
195/// Find the entry with the highest (color depth, size).
196fn best_entry(mut entries: Vec<DirEntry>) -> ImageResult<DirEntry> {
197 let mut best: DirEntry = entries.pop().ok_or(err:DecoderError::NoEntries)?;
198
199 let mut best_score: (u16, u32) = (
200 best.bits_per_pixel,
201 u32::from(best.real_width()) * u32::from(best.real_height()),
202 );
203
204 for entry: DirEntry in entries {
205 let score: (u16, u32) = (
206 entry.bits_per_pixel,
207 u32::from(entry.real_width()) * u32::from(entry.real_height()),
208 );
209 if score > best_score {
210 best = entry;
211 best_score = score;
212 }
213 }
214 Ok(best)
215}
216
217impl DirEntry {
218 fn real_width(&self) -> u16 {
219 match self.width {
220 0 => 256,
221 w => u16::from(w),
222 }
223 }
224
225 fn real_height(&self) -> u16 {
226 match self.height {
227 0 => 256,
228 h => u16::from(h),
229 }
230 }
231
232 fn matches_dimensions(&self, width: u32, height: u32) -> bool {
233 u32::from(self.real_width()) == width.min(256)
234 && u32::from(self.real_height()) == height.min(256)
235 }
236
237 fn seek_to_start<R: Read + Seek>(&self, r: &mut R) -> ImageResult<()> {
238 r.seek(SeekFrom::Start(u64::from(self.image_offset)))?;
239 Ok(())
240 }
241
242 fn is_png<R: Read + Seek>(&self, r: &mut R) -> ImageResult<bool> {
243 self.seek_to_start(r)?;
244
245 // Read the first 8 bytes to sniff the image.
246 let mut signature = [0u8; 8];
247 r.read_exact(&mut signature)?;
248
249 Ok(signature == PNG_SIGNATURE)
250 }
251
252 fn decoder<R: Read + Seek>(&self, mut r: R) -> ImageResult<InnerDecoder<R>> {
253 let is_png = self.is_png(&mut r)?;
254 self.seek_to_start(&mut r)?;
255
256 if is_png {
257 Ok(Png(Box::new(PngDecoder::new(r)?)))
258 } else {
259 Ok(Bmp(BmpDecoder::new_with_ico_format(r)?))
260 }
261 }
262}
263
264/// Wrapper struct around a `Cursor<Vec<u8>>`
265pub struct IcoReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
266impl<R> Read for IcoReader<R> {
267 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
268 self.0.read(buf)
269 }
270 fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
271 if self.0.position() == 0 && buf.is_empty() {
272 mem::swap(x:buf, self.0.get_mut());
273 Ok(buf.len())
274 } else {
275 self.0.read_to_end(buf)
276 }
277 }
278}
279
280impl<'a, R: 'a + Read + Seek> ImageDecoder<'a> for IcoDecoder<R> {
281 type Reader = IcoReader<R>;
282
283 fn dimensions(&self) -> (u32, u32) {
284 match self.inner_decoder {
285 Bmp(ref decoder) => decoder.dimensions(),
286 Png(ref decoder) => decoder.dimensions(),
287 }
288 }
289
290 fn color_type(&self) -> ColorType {
291 match self.inner_decoder {
292 Bmp(ref decoder) => decoder.color_type(),
293 Png(ref decoder) => decoder.color_type(),
294 }
295 }
296
297 fn into_reader(self) -> ImageResult<Self::Reader> {
298 Ok(IcoReader(
299 Cursor::new(image::decoder_to_vec(self)?),
300 PhantomData,
301 ))
302 }
303
304 fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
305 assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
306 match self.inner_decoder {
307 Png(decoder) => {
308 if self.selected_entry.image_length < PNG_SIGNATURE.len() as u32 {
309 return Err(DecoderError::PngShorterThanHeader.into());
310 }
311
312 // Check if the image dimensions match the ones in the image data.
313 let (width, height) = decoder.dimensions();
314 if !self.selected_entry.matches_dimensions(width, height) {
315 return Err(DecoderError::ImageEntryDimensionMismatch {
316 format: IcoEntryImageFormat::Png,
317 entry: (
318 self.selected_entry.real_width(),
319 self.selected_entry.real_height(),
320 ),
321 image: (width, height),
322 }
323 .into());
324 }
325
326 // Embedded PNG images can only be of the 32BPP RGBA format.
327 // https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473/
328 if decoder.color_type() != ColorType::Rgba8 {
329 return Err(DecoderError::PngNotRgba.into());
330 }
331
332 decoder.read_image(buf)
333 }
334 Bmp(mut decoder) => {
335 let (width, height) = decoder.dimensions();
336 if !self.selected_entry.matches_dimensions(width, height) {
337 return Err(DecoderError::ImageEntryDimensionMismatch {
338 format: IcoEntryImageFormat::Bmp,
339 entry: (
340 self.selected_entry.real_width(),
341 self.selected_entry.real_height(),
342 ),
343 image: (width, height),
344 }
345 .into());
346 }
347
348 // The ICO decoder needs an alpha channel to apply the AND mask.
349 if decoder.color_type() != ColorType::Rgba8 {
350 return Err(ImageError::Unsupported(
351 UnsupportedError::from_format_and_kind(
352 ImageFormat::Bmp.into(),
353 UnsupportedErrorKind::Color(decoder.color_type().into()),
354 ),
355 ));
356 }
357
358 decoder.read_image_data(buf)?;
359
360 let r = decoder.reader();
361 let image_end = r.stream_position()?;
362 let data_end = u64::from(self.selected_entry.image_offset)
363 + u64::from(self.selected_entry.image_length);
364
365 let mask_row_bytes = ((width + 31) / 32) * 4;
366 let mask_length = u64::from(mask_row_bytes) * u64::from(height);
367
368 // data_end should be image_end + the mask length (mask_row_bytes * height).
369 // According to
370 // https://devblogs.microsoft.com/oldnewthing/20101021-00/?p=12483
371 // the mask is required, but according to Wikipedia
372 // https://en.wikipedia.org/wiki/ICO_(file_format)
373 // the mask is not required. Unfortunately, Wikipedia does not have a citation
374 // for that claim, so we can't be sure which is correct.
375 if data_end >= image_end + mask_length {
376 // If there's an AND mask following the image, read and apply it.
377 for y in 0..height {
378 let mut x = 0;
379 for _ in 0..mask_row_bytes {
380 // Apply the bits of each byte until we reach the end of the row.
381 let mask_byte = r.read_u8()?;
382 for bit in (0..8).rev() {
383 if x >= width {
384 break;
385 }
386 if mask_byte & (1 << bit) != 0 {
387 // Set alpha channel to transparent.
388 buf[((height - y - 1) * width + x) as usize * 4 + 3] = 0;
389 }
390 x += 1;
391 }
392 }
393 }
394
395 Ok(())
396 } else if data_end == image_end {
397 // accept images with no mask data
398 Ok(())
399 } else {
400 Err(DecoderError::InvalidDataSize.into())
401 }
402 }
403 }
404 }
405}
406
407#[cfg(test)]
408mod test {
409 use super::*;
410
411 // Test if BMP images without alpha channel inside ICOs don't panic.
412 // Because the test data is invalid decoding should produce an error.
413 #[test]
414 fn bmp_16_with_missing_alpha_channel() {
415 let data = vec![
416 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0e, 0x04, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00,
417 0x7c, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0x01, 0x00,
418 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
419 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
420 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
421 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
422 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
423 0x00, 0x00, 0x00, 0x8f, 0xf6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
424 0x20, 0x66, 0x74, 0x83, 0x70, 0x61, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
425 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xeb, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00,
426 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x47, 0x0d,
427 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x62, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00,
428 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0c,
429 0x00, 0x00, 0x00, 0xc3, 0x3f, 0x94, 0x61, 0xaa, 0x17, 0x4d, 0x8d, 0x79, 0x1d, 0x8b,
430 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x2e, 0x28, 0x40, 0xe5, 0x9f,
431 0x4b, 0x4d, 0xe9, 0x87, 0xd3, 0xda, 0xd6, 0x89, 0x81, 0xc5, 0xa4, 0xa1, 0x60, 0x98,
432 0x31, 0xc7, 0x1d, 0xb6, 0x8f, 0x20, 0xc8, 0x3e, 0xee, 0xd8, 0xe4, 0x8f, 0xee, 0x7b,
433 0x48, 0x9b, 0x88, 0x25, 0x13, 0xda, 0xa4, 0x13, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x40,
434 0x16, 0x01, 0xff, 0xff, 0xff, 0xff, 0xe9, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
435 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
436 0x00, 0x00, 0xa3, 0x66, 0x64, 0x41, 0x54, 0xa3, 0xa3, 0x00, 0x00, 0x00, 0xb8, 0x00,
437 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
438 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa3, 0x66, 0x64, 0x41, 0x54, 0xa3, 0xa3,
439 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
440 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
441 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0xf6, 0xff, 0xff,
442 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x66, 0x74, 0x83, 0x70, 0x61, 0x76,
443 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
444 0xeb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
445 0x00, 0x00, 0x00, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x62, 0x49,
446 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
447 0x00, 0x00, 0x00, 0xff, 0xff, 0x94, 0xc8, 0x00, 0x02, 0x0c, 0x00, 0xff, 0xff, 0xc6,
448 0x84, 0x00, 0x2a, 0x75, 0x03, 0xa3, 0x05, 0xfb, 0xe1, 0x6e, 0xe8, 0x27, 0xd6, 0xd3,
449 0x96, 0xc1, 0xe4, 0x30, 0x0c, 0x05, 0xb9, 0xa3, 0x8b, 0x29, 0xda, 0xa4, 0xf1, 0x4d,
450 0xf3, 0xb2, 0x98, 0x2b, 0xe6, 0x93, 0x07, 0xf9, 0xca, 0x2b, 0xc2, 0x39, 0x20, 0xba,
451 0x7c, 0xa0, 0xb1, 0x43, 0xe6, 0xf9, 0xdc, 0xd1, 0xc2, 0x52, 0xdc, 0x41, 0xc1, 0x2f,
452 0x29, 0xf7, 0x46, 0x32, 0xda, 0x1b, 0x72, 0x8c, 0xe6, 0x2b, 0x01, 0xe5, 0x49, 0x21,
453 0x89, 0x89, 0xe4, 0x3d, 0xa1, 0xdb, 0x3b, 0x4a, 0x0b, 0x52, 0x86, 0x52, 0x33, 0x9d,
454 0xb2, 0xcf, 0x4a, 0x86, 0x53, 0xd7, 0xa9, 0x4b, 0xaf, 0x62, 0x06, 0x49, 0x53, 0x00,
455 0xc3, 0x3f, 0x94, 0x61, 0xaa, 0x17, 0x4d, 0x8d, 0x79, 0x1d, 0x8b, 0x10, 0x00, 0x00,
456 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x2e, 0x28, 0x40, 0xe5, 0x9f, 0x4b, 0x4d, 0xe9,
457 0x87, 0xd3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe7, 0xc5, 0x00,
458 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x50, 0x31, 0x00, 0x00, 0x00, 0x00,
459 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x76, 0x76, 0x01, 0x00, 0x00, 0x00, 0x76, 0x00,
460 0x00, 0x23, 0x3f, 0x52, 0x41, 0x44, 0x49, 0x41, 0x4e, 0x43, 0x45, 0x61, 0x50, 0x35,
461 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x4d, 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x05,
462 0x50, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x37, 0x61,
463 ];
464
465 let decoder = IcoDecoder::new(Cursor::new(&data)).unwrap();
466 let mut buf = vec![0; usize::try_from(decoder.total_bytes()).unwrap()];
467 assert!(decoder.read_image(&mut buf).is_err());
468 }
469}
470