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 optional mask row, containing 1 bit per pixel, padded to 4 bytes, was too short for this image.
35     BmpIcoMaskTooShortForImage,
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::BmpIcoMaskTooShortForImage =>
62                 f.write_str("ICO mask too short for the image"),
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     color_count: u8,
120     reserved: u8,
121 
122     num_color_planes: u16,
123     bits_per_pixel: u16,
124 
125     image_length: u32,
126     image_offset: u32,
127 }
128 
129 impl<R: Read + Seek> IcoDecoder<R> {
130     /// Create a new decoder that decodes from the stream ```r```
new(mut r: R) -> ImageResult<IcoDecoder<R>>131     pub fn new(mut r: R) -> ImageResult<IcoDecoder<R>> {
132         let entries = read_entries(&mut r)?;
133         let entry = best_entry(entries)?;
134         let decoder = entry.decoder(r)?;
135 
136         Ok(IcoDecoder {
137             selected_entry: entry,
138             inner_decoder: decoder,
139         })
140     }
141 }
142 
read_entries<R: Read>(r: &mut R) -> ImageResult<Vec<DirEntry>>143 fn read_entries<R: Read>(r: &mut R) -> ImageResult<Vec<DirEntry>> {
144     let _reserved = r.read_u16::<LittleEndian>()?;
145     let _type = r.read_u16::<LittleEndian>()?;
146     let count = r.read_u16::<LittleEndian>()?;
147     (0..count).map(|_| read_entry(r)).collect()
148 }
149 
read_entry<R: Read>(r: &mut R) -> ImageResult<DirEntry>150 fn read_entry<R: Read>(r: &mut R) -> ImageResult<DirEntry> {
151     let mut entry = DirEntry::default();
152 
153     entry.width = r.read_u8()?;
154     entry.height = r.read_u8()?;
155     entry.color_count = r.read_u8()?;
156     // Reserved value (not used)
157     entry.reserved = r.read_u8()?;
158 
159     // This may be either the number of color planes (0 or 1), or the horizontal coordinate
160     // of the hotspot for CUR files.
161     entry.num_color_planes = r.read_u16::<LittleEndian>()?;
162     if entry.num_color_planes > 256 {
163         return Err(DecoderError::IcoEntryTooManyPlanesOrHotspot.into());
164     }
165 
166     // This may be either the bit depth (may be 0 meaning unspecified),
167     // or the vertical coordinate of the hotspot for CUR files.
168     entry.bits_per_pixel = r.read_u16::<LittleEndian>()?;
169     if entry.bits_per_pixel > 256 {
170         return Err(DecoderError::IcoEntryTooManyBitsPerPixelOrHotspot.into());
171     }
172 
173     entry.image_length = r.read_u32::<LittleEndian>()?;
174     entry.image_offset = r.read_u32::<LittleEndian>()?;
175 
176     Ok(entry)
177 }
178 
179 /// Find the entry with the highest (color depth, size).
best_entry(mut entries: Vec<DirEntry>) -> ImageResult<DirEntry>180 fn best_entry(mut entries: Vec<DirEntry>) -> ImageResult<DirEntry> {
181     let mut best = entries.pop().ok_or_else(|| DecoderError::NoEntries)?;
182 
183     let mut best_score = (
184         best.bits_per_pixel,
185         u32::from(best.real_width()) * u32::from(best.real_height()),
186     );
187 
188     for entry in entries {
189         let score = (
190             entry.bits_per_pixel,
191             u32::from(entry.real_width()) * u32::from(entry.real_height()),
192         );
193         if score > best_score {
194             best = entry;
195             best_score = score;
196         }
197     }
198     Ok(best)
199 }
200 
201 impl DirEntry {
real_width(&self) -> u16202     fn real_width(&self) -> u16 {
203         match self.width {
204             0 => 256,
205             w => u16::from(w),
206         }
207     }
208 
real_height(&self) -> u16209     fn real_height(&self) -> u16 {
210         match self.height {
211             0 => 256,
212             h => u16::from(h),
213         }
214     }
215 
matches_dimensions(&self, width: u32, height: u32) -> bool216     fn matches_dimensions(&self, width: u32, height: u32) -> bool {
217         u32::from(self.real_width()) == width && u32::from(self.real_height()) == height
218     }
219 
seek_to_start<R: Read + Seek>(&self, r: &mut R) -> ImageResult<()>220     fn seek_to_start<R: Read + Seek>(&self, r: &mut R) -> ImageResult<()> {
221         r.seek(SeekFrom::Start(u64::from(self.image_offset)))?;
222         Ok(())
223     }
224 
is_png<R: Read + Seek>(&self, r: &mut R) -> ImageResult<bool>225     fn is_png<R: Read + Seek>(&self, r: &mut R) -> ImageResult<bool> {
226         self.seek_to_start(r)?;
227 
228         // Read the first 8 bytes to sniff the image.
229         let mut signature = [0u8; 8];
230         r.read_exact(&mut signature)?;
231 
232         Ok(signature == PNG_SIGNATURE)
233     }
234 
decoder<R: Read + Seek>(&self, mut r: R) -> ImageResult<InnerDecoder<R>>235     fn decoder<R: Read + Seek>(&self, mut r: R) -> ImageResult<InnerDecoder<R>> {
236         let is_png = self.is_png(&mut r)?;
237         self.seek_to_start(&mut r)?;
238 
239         if is_png {
240             Ok(PNG(PngDecoder::new(r)?))
241         } else {
242             Ok(BMP(BmpDecoder::new_with_ico_format(r)?))
243         }
244     }
245 }
246 
247 /// Wrapper struct around a `Cursor<Vec<u8>>`
248 pub struct IcoReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
249 impl<R> Read for IcoReader<R> {
read(&mut self, buf: &mut [u8]) -> io::Result<usize>250     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
251         self.0.read(buf)
252     }
read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize>253     fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
254         if self.0.position() == 0 && buf.is_empty() {
255             mem::swap(buf, self.0.get_mut());
256             Ok(buf.len())
257         } else {
258             self.0.read_to_end(buf)
259         }
260     }
261 }
262 
263 impl<'a, R: 'a + Read + Seek> ImageDecoder<'a> for IcoDecoder<R> {
264     type Reader = IcoReader<R>;
265 
dimensions(&self) -> (u32, u32)266     fn dimensions(&self) -> (u32, u32) {
267         match self.inner_decoder {
268             BMP(ref decoder) => decoder.dimensions(),
269             PNG(ref decoder) => decoder.dimensions(),
270         }
271     }
272 
color_type(&self) -> ColorType273     fn color_type(&self) -> ColorType {
274         match self.inner_decoder {
275             BMP(ref decoder) => decoder.color_type(),
276             PNG(ref decoder) => decoder.color_type(),
277         }
278     }
279 
into_reader(self) -> ImageResult<Self::Reader>280     fn into_reader(self) -> ImageResult<Self::Reader> {
281         Ok(IcoReader(Cursor::new(image::decoder_to_vec(self)?), PhantomData))
282     }
283 
read_image(self, buf: &mut [u8]) -> ImageResult<()>284     fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
285         assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
286         match self.inner_decoder {
287             PNG(decoder) => {
288                 if self.selected_entry.image_length < PNG_SIGNATURE.len() as u32 {
289                     return Err(DecoderError::PngShorterThanHeader.into());
290                 }
291 
292                 // Check if the image dimensions match the ones in the image data.
293                 let (width, height) = decoder.dimensions();
294                 if !self.selected_entry.matches_dimensions(width, height) {
295                     return Err(DecoderError::ImageEntryDimensionMismatch {
296                         format: IcoEntryImageFormat::Png,
297                         entry: (self.selected_entry.real_width(), self.selected_entry.real_height()),
298                         image: (width, height)
299                     }.into());
300                 }
301 
302                 // Embedded PNG images can only be of the 32BPP RGBA format.
303                 // https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473/
304                 if decoder.color_type() != ColorType::Rgba8 {
305                     return Err(DecoderError::PngNotRgba.into());
306                 }
307 
308                 decoder.read_image(buf)
309             }
310             BMP(mut decoder) => {
311                 let (width, height) = decoder.dimensions();
312                 if !self.selected_entry.matches_dimensions(width, height) {
313                     return Err(DecoderError::ImageEntryDimensionMismatch {
314                         format: IcoEntryImageFormat::Bmp,
315                         entry: (self.selected_entry.real_width(), self.selected_entry.real_height()),
316                         image: (width, height)
317                     }.into());
318                 }
319 
320                 // The ICO decoder needs an alpha channel to apply the AND mask.
321                 if decoder.color_type() != ColorType::Rgba8 {
322                     return Err(ImageError::Unsupported(UnsupportedError::from_format_and_kind(
323                         ImageFormat::Bmp.into(),
324                         UnsupportedErrorKind::Color(decoder.color_type().into()),
325                     )));
326                 }
327 
328                 decoder.read_image_data(buf)?;
329 
330                 // If there's an AND mask following the image, read and apply it.
331                 let r = decoder.reader();
332                 let mask_start = r.seek(SeekFrom::Current(0))?;
333                 let mask_end =
334                     u64::from(self.selected_entry.image_offset + self.selected_entry.image_length);
335                 let mask_length = mask_end - mask_start;
336 
337                 if mask_length > 0 {
338                     // A mask row contains 1 bit per pixel, padded to 4 bytes.
339                     let mask_row_bytes = ((width + 31) / 32) * 4;
340                     let expected_length = u64::from(mask_row_bytes) * u64::from(height);
341                     if mask_length < expected_length {
342                         return Err(DecoderError::BmpIcoMaskTooShortForImage.into());
343                     }
344 
345                     for y in 0..height {
346                         let mut x = 0;
347                         for _ in 0..mask_row_bytes {
348                             // Apply the bits of each byte until we reach the end of the row.
349                             let mask_byte = r.read_u8()?;
350                             for bit in (0..8).rev() {
351                                 if x >= width {
352                                     break;
353                                 }
354                                 if mask_byte & (1 << bit) != 0 {
355                                     // Set alpha channel to transparent.
356                                     buf[((height - y - 1) * width + x) as usize * 4 + 3] = 0;
357                                 }
358                                 x += 1;
359                             }
360                         }
361                     }
362                 }
363                 Ok(())
364             }
365         }
366     }
367 }
368