1 use byteorder::{LittleEndian, ReadBytesExt};
2 use std::convert::TryFrom;
3 use std::io::{self, Cursor, Read, Seek, SeekFrom};
4 use std::marker::PhantomData;
5 use std::{error, fmt, mem};
6 
7 use crate::color::ColorType;
8 use crate::error::{DecodingError, ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind};
9 use crate::image::{self, ImageDecoder, ImageFormat};
10 
11 use self::InnerDecoder::*;
12 use crate::bmp::BmpDecoder;
13 use crate::png::PngDecoder;
14 
15 // http://www.w3.org/TR/PNG-Structure.html
16 // The first eight bytes of a PNG file always contain the following (decimal) values:
17 const PNG_SIGNATURE: [u8; 8] = [137, 80, 78, 71, 13, 10, 26, 10];
18 
19 /// Errors that can occur during decoding and parsing an ICO image or one of its enclosed images.
20 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
21 enum DecoderError {
22     /// The ICO directory is empty
23     NoEntries,
24     /// The number of color planes (0 or 1), or the horizontal coordinate of the hotspot for CUR files too big.
25     IcoEntryTooManyPlanesOrHotspot,
26     /// The bit depth (may be 0 meaning unspecified), or the vertical coordinate of the hotspot for CUR files too big.
27     IcoEntryTooManyBitsPerPixelOrHotspot,
28 
29     /// The entry is in PNG format and specified a length that is shorter than PNG header.
30     PngShorterThanHeader,
31     /// The enclosed PNG is not in RGBA, which is invalid: https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473/.
32     PngNotRgba,
33 
34     /// The entry is in BMP format and specified a data size that is not correct for the image and optional mask data.
35     InvalidDataSize,
36 
37     /// The dimensions specified by the entry does not match the dimensions in the header of the enclosed image.
38     ImageEntryDimensionMismatch {
39         /// The mismatched subimage's type
40         format: IcoEntryImageFormat,
41         /// The dimensions specified by the entry
42         entry: (u16, u16),
43         /// The dimensions of the image itself
44         image: (u32, u32)
45     },
46 }
47 
48 impl fmt::Display for DecoderError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result49     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50         match self {
51             DecoderError::NoEntries =>
52                 f.write_str("ICO directory contains no image"),
53             DecoderError::IcoEntryTooManyPlanesOrHotspot =>
54                 f.write_str("ICO image entry has too many color planes or too large hotspot value"),
55             DecoderError::IcoEntryTooManyBitsPerPixelOrHotspot =>
56                 f.write_str("ICO image entry has too many bits per pixel or too large hotspot value"),
57             DecoderError::PngShorterThanHeader =>
58                 f.write_str("Entry specified a length that is shorter than PNG header!"),
59             DecoderError::PngNotRgba =>
60                 f.write_str("The PNG is not in RGBA format!"),
61             DecoderError::InvalidDataSize =>
62                 f.write_str("ICO image data size did not match expected size"),
63             DecoderError::ImageEntryDimensionMismatch { format, entry, image } =>
64                 f.write_fmt(format_args!("Entry{:?} and {}{:?} dimensions do not match!", entry, format, image)),
65         }
66     }
67 }
68 
69 impl From<DecoderError> for ImageError {
from(e: DecoderError) -> ImageError70     fn from(e: DecoderError) -> ImageError {
71         ImageError::Decoding(DecodingError::new(ImageFormat::Ico.into(), e))
72     }
73 }
74 
75 impl error::Error for DecoderError {}
76 
77 /// The image formats an ICO may contain
78 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
79 enum IcoEntryImageFormat {
80     /// PNG in ARGB
81     Png,
82     /// BMP with optional alpha mask
83     Bmp,
84 }
85 
86 impl fmt::Display for IcoEntryImageFormat {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result87     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88         f.write_str(match self {
89             IcoEntryImageFormat::Png => "PNG",
90             IcoEntryImageFormat::Bmp => "BMP",
91         })
92     }
93 }
94 
95 impl Into<ImageFormat> for IcoEntryImageFormat {
into(self) -> ImageFormat96     fn into(self) -> ImageFormat {
97         match self {
98             IcoEntryImageFormat::Png => ImageFormat::Png,
99             IcoEntryImageFormat::Bmp => ImageFormat::Bmp,
100         }
101     }
102 }
103 
104 /// An ico decoder
105 pub struct IcoDecoder<R: Read> {
106     selected_entry: DirEntry,
107     inner_decoder: InnerDecoder<R>,
108 }
109 
110 enum InnerDecoder<R: Read> {
111     BMP(BmpDecoder<R>),
112     PNG(PngDecoder<R>),
113 }
114 
115 #[derive(Clone, Copy, Default)]
116 struct DirEntry {
117     width: u8,
118     height: u8,
119     // We ignore some header fields as they will be replicated in the PNG, BMP and they are not
120     // necessary for determining the best_entry.
121     #[allow(unused)]
122     color_count: u8,
123     // Wikipedia has this to say:
124     // Although Microsoft's technical documentation states that this value must be zero, the icon
125     // encoder built into .NET (System.Drawing.Icon.Save) sets this value to 255. It appears that
126     // the operating system ignores this value altogether.
127     #[allow(unused)]
128     reserved: u8,
129 
130     // We ignore some header fields as they will be replicated in the PNG, BMP and they are not
131     // necessary for determining the best_entry.
132     #[allow(unused)]
133     num_color_planes: u16,
134     bits_per_pixel: u16,
135 
136     image_length: u32,
137     image_offset: u32,
138 }
139 
140 impl<R: Read + Seek> IcoDecoder<R> {
141     /// Create a new decoder that decodes from the stream ```r```
new(mut r: R) -> ImageResult<IcoDecoder<R>>142     pub fn new(mut r: R) -> ImageResult<IcoDecoder<R>> {
143         let entries = read_entries(&mut r)?;
144         let entry = best_entry(entries)?;
145         let decoder = entry.decoder(r)?;
146 
147         Ok(IcoDecoder {
148             selected_entry: entry,
149             inner_decoder: decoder,
150         })
151     }
152 }
153 
read_entries<R: Read>(r: &mut R) -> ImageResult<Vec<DirEntry>>154 fn read_entries<R: Read>(r: &mut R) -> ImageResult<Vec<DirEntry>> {
155     let _reserved = r.read_u16::<LittleEndian>()?;
156     let _type = r.read_u16::<LittleEndian>()?;
157     let count = r.read_u16::<LittleEndian>()?;
158     (0..count).map(|_| read_entry(r)).collect()
159 }
160 
read_entry<R: Read>(r: &mut R) -> ImageResult<DirEntry>161 fn read_entry<R: Read>(r: &mut R) -> ImageResult<DirEntry> {
162     Ok(DirEntry {
163         width: r.read_u8()?,
164         height: r.read_u8()?,
165         color_count: r.read_u8()?,
166         reserved: r.read_u8()?,
167         num_color_planes: {
168             // This may be either the number of color planes (0 or 1), or the horizontal coordinate
169             // of the hotspot for CUR files.
170             let num = r.read_u16::<LittleEndian>()?;
171             if num > 256 {
172                 return Err(DecoderError::IcoEntryTooManyPlanesOrHotspot.into());
173             }
174             num
175         },
176         bits_per_pixel: {
177             // This may be either the bit depth (may be 0 meaning unspecified),
178             // or the vertical coordinate of the hotspot for CUR files.
179             let num = r.read_u16::<LittleEndian>()?;
180             if num > 256 {
181                 return Err(DecoderError::IcoEntryTooManyBitsPerPixelOrHotspot.into());
182             }
183             num
184         },
185         image_length: r.read_u32::<LittleEndian>()?,
186         image_offset: r.read_u32::<LittleEndian>()?,
187     })
188 }
189 
190 /// Find the entry with the highest (color depth, size).
best_entry(mut entries: Vec<DirEntry>) -> ImageResult<DirEntry>191 fn best_entry(mut entries: Vec<DirEntry>) -> ImageResult<DirEntry> {
192     let mut best = entries.pop().ok_or(DecoderError::NoEntries)?;
193 
194     let mut best_score = (
195         best.bits_per_pixel,
196         u32::from(best.real_width()) * u32::from(best.real_height()),
197     );
198 
199     for entry in entries {
200         let score = (
201             entry.bits_per_pixel,
202             u32::from(entry.real_width()) * u32::from(entry.real_height()),
203         );
204         if score > best_score {
205             best = entry;
206             best_score = score;
207         }
208     }
209     Ok(best)
210 }
211 
212 impl DirEntry {
real_width(&self) -> u16213     fn real_width(&self) -> u16 {
214         match self.width {
215             0 => 256,
216             w => u16::from(w),
217         }
218     }
219 
real_height(&self) -> u16220     fn real_height(&self) -> u16 {
221         match self.height {
222             0 => 256,
223             h => u16::from(h),
224         }
225     }
226 
matches_dimensions(&self, width: u32, height: u32) -> bool227     fn matches_dimensions(&self, width: u32, height: u32) -> bool {
228         u32::from(self.real_width()) == width && u32::from(self.real_height()) == height
229     }
230 
seek_to_start<R: Read + Seek>(&self, r: &mut R) -> ImageResult<()>231     fn seek_to_start<R: Read + Seek>(&self, r: &mut R) -> ImageResult<()> {
232         r.seek(SeekFrom::Start(u64::from(self.image_offset)))?;
233         Ok(())
234     }
235 
is_png<R: Read + Seek>(&self, r: &mut R) -> ImageResult<bool>236     fn is_png<R: Read + Seek>(&self, r: &mut R) -> ImageResult<bool> {
237         self.seek_to_start(r)?;
238 
239         // Read the first 8 bytes to sniff the image.
240         let mut signature = [0u8; 8];
241         r.read_exact(&mut signature)?;
242 
243         Ok(signature == PNG_SIGNATURE)
244     }
245 
decoder<R: Read + Seek>(&self, mut r: R) -> ImageResult<InnerDecoder<R>>246     fn decoder<R: Read + Seek>(&self, mut r: R) -> ImageResult<InnerDecoder<R>> {
247         let is_png = self.is_png(&mut r)?;
248         self.seek_to_start(&mut r)?;
249 
250         if is_png {
251             Ok(PNG(PngDecoder::new(r)?))
252         } else {
253             Ok(BMP(BmpDecoder::new_with_ico_format(r)?))
254         }
255     }
256 }
257 
258 /// Wrapper struct around a `Cursor<Vec<u8>>`
259 pub struct IcoReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
260 impl<R> Read for IcoReader<R> {
read(&mut self, buf: &mut [u8]) -> io::Result<usize>261     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
262         self.0.read(buf)
263     }
read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize>264     fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
265         if self.0.position() == 0 && buf.is_empty() {
266             mem::swap(buf, self.0.get_mut());
267             Ok(buf.len())
268         } else {
269             self.0.read_to_end(buf)
270         }
271     }
272 }
273 
274 impl<'a, R: 'a + Read + Seek> ImageDecoder<'a> for IcoDecoder<R> {
275     type Reader = IcoReader<R>;
276 
dimensions(&self) -> (u32, u32)277     fn dimensions(&self) -> (u32, u32) {
278         match self.inner_decoder {
279             BMP(ref decoder) => decoder.dimensions(),
280             PNG(ref decoder) => decoder.dimensions(),
281         }
282     }
283 
color_type(&self) -> ColorType284     fn color_type(&self) -> ColorType {
285         match self.inner_decoder {
286             BMP(ref decoder) => decoder.color_type(),
287             PNG(ref decoder) => decoder.color_type(),
288         }
289     }
290 
into_reader(self) -> ImageResult<Self::Reader>291     fn into_reader(self) -> ImageResult<Self::Reader> {
292         Ok(IcoReader(Cursor::new(image::decoder_to_vec(self)?), PhantomData))
293     }
294 
read_image(self, buf: &mut [u8]) -> ImageResult<()>295     fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
296         assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
297         match self.inner_decoder {
298             PNG(decoder) => {
299                 if self.selected_entry.image_length < PNG_SIGNATURE.len() as u32 {
300                     return Err(DecoderError::PngShorterThanHeader.into());
301                 }
302 
303                 // Check if the image dimensions match the ones in the image data.
304                 let (width, height) = decoder.dimensions();
305                 if !self.selected_entry.matches_dimensions(width, height) {
306                     return Err(DecoderError::ImageEntryDimensionMismatch {
307                         format: IcoEntryImageFormat::Png,
308                         entry: (self.selected_entry.real_width(), self.selected_entry.real_height()),
309                         image: (width, height)
310                     }.into());
311                 }
312 
313                 // Embedded PNG images can only be of the 32BPP RGBA format.
314                 // https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473/
315                 if decoder.color_type() != ColorType::Rgba8 {
316                     return Err(DecoderError::PngNotRgba.into());
317                 }
318 
319                 decoder.read_image(buf)
320             }
321             BMP(mut decoder) => {
322                 let (width, height) = decoder.dimensions();
323                 if !self.selected_entry.matches_dimensions(width, height) {
324                     return Err(DecoderError::ImageEntryDimensionMismatch {
325                         format: IcoEntryImageFormat::Bmp,
326                         entry: (self.selected_entry.real_width(), self.selected_entry.real_height()),
327                         image: (width, height)
328                     }.into());
329                 }
330 
331                 // The ICO decoder needs an alpha channel to apply the AND mask.
332                 if decoder.color_type() != ColorType::Rgba8 {
333                     return Err(ImageError::Unsupported(UnsupportedError::from_format_and_kind(
334                         ImageFormat::Bmp.into(),
335                         UnsupportedErrorKind::Color(decoder.color_type().into()),
336                     )));
337                 }
338 
339                 decoder.read_image_data(buf)?;
340 
341                 let r = decoder.reader();
342                 let image_end = r.seek(SeekFrom::Current(0))?;
343                 let data_end =
344                     u64::from(self.selected_entry.image_offset + self.selected_entry.image_length);
345 
346                 let mask_row_bytes = ((width + 31) / 32) * 4;
347                 let mask_length = u64::from(mask_row_bytes) * u64::from(height);
348 
349                 // data_end should be image_end + the mask length (mask_row_bytes * height).
350                 // According to
351                 // https://devblogs.microsoft.com/oldnewthing/20101021-00/?p=12483
352                 // the mask is required, but according to Wikipedia
353                 // https://en.wikipedia.org/wiki/ICO_(file_format)
354                 // the mask is not required. Unfortunately, Wikipedia does not have a citation
355                 // for that claim, so we can't be sure which is correct.
356                 if data_end >= image_end + mask_length {
357                     // If there's an AND mask following the image, read and apply it.
358                     for y in 0..height {
359                         let mut x = 0;
360                         for _ in 0..mask_row_bytes {
361                             // Apply the bits of each byte until we reach the end of the row.
362                             let mask_byte = r.read_u8()?;
363                             for bit in (0..8).rev() {
364                                 if x >= width {
365                                     break;
366                                 }
367                                 if mask_byte & (1 << bit) != 0 {
368                                     // Set alpha channel to transparent.
369                                     buf[((height - y - 1) * width + x) as usize * 4 + 3] = 0;
370                                 }
371                                 x += 1;
372                             }
373                         }
374                     }
375 
376                     Ok(())
377                 } else if data_end == image_end {
378                     // accept images with no mask data
379                     Ok(())
380                 } else {
381                     Err(DecoderError::InvalidDataSize.into())
382                 }
383             }
384         }
385     }
386 }
387