1use byteorder_lite::{LittleEndian, ReadBytesExt};
2use std::io::{BufRead, Read, Seek, SeekFrom};
3use std::{error, fmt};
4
5use crate::color::ColorType;
6use crate::error::{
7 DecodingError, ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind,
8};
9use crate::image::{ImageDecoder, ImageFormat};
10
11use self::InnerDecoder::*;
12use crate::codecs::bmp::BmpDecoder;
13use crate::codecs::png::{PngDecoder, PNG_SIGNATURE};
14
15#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
17enum DecoderError {
18 NoEntries,
20 IcoEntryTooManyPlanesOrHotspot,
22 IcoEntryTooManyBitsPerPixelOrHotspot,
24
25 PngShorterThanHeader,
27 PngNotRgba,
29
30 InvalidDataSize,
32
33 ImageEntryDimensionMismatch {
35 format: IcoEntryImageFormat,
37 entry: (u16, u16),
39 image: (u32, u32),
41 },
42}
43
44impl fmt::Display for DecoderError {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 match self {
47 DecoderError::NoEntries => f.write_str("ICO directory contains no image"),
48 DecoderError::IcoEntryTooManyPlanesOrHotspot => {
49 f.write_str("ICO image entry has too many color planes or too large hotspot value")
50 }
51 DecoderError::IcoEntryTooManyBitsPerPixelOrHotspot => f.write_str(
52 "ICO image entry has too many bits per pixel or too large hotspot value",
53 ),
54 DecoderError::PngShorterThanHeader => {
55 f.write_str("Entry specified a length that is shorter than PNG header!")
56 }
57 DecoderError::PngNotRgba => f.write_str("The PNG is not in RGBA format!"),
58 DecoderError::InvalidDataSize => {
59 f.write_str("ICO image data size did not match expected size")
60 }
61 DecoderError::ImageEntryDimensionMismatch {
62 format,
63 entry,
64 image,
65 } => f.write_fmt(format_args!(
66 "Entry{entry:?} and {format}{image:?} dimensions do not match!"
67 )),
68 }
69 }
70}
71
72impl From<DecoderError> for ImageError {
73 fn from(e: DecoderError) -> ImageError {
74 ImageError::Decoding(DecodingError::new(ImageFormat::Ico.into(), e))
75 }
76}
77
78impl error::Error for DecoderError {}
79
80#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
82enum IcoEntryImageFormat {
83 Png,
85 Bmp,
87}
88
89impl fmt::Display for IcoEntryImageFormat {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 f.write_str(match self {
92 IcoEntryImageFormat::Png => "PNG",
93 IcoEntryImageFormat::Bmp => "BMP",
94 })
95 }
96}
97
98impl From<IcoEntryImageFormat> for ImageFormat {
99 fn from(val: IcoEntryImageFormat) -> Self {
100 match val {
101 IcoEntryImageFormat::Png => ImageFormat::Png,
102 IcoEntryImageFormat::Bmp => ImageFormat::Bmp,
103 }
104 }
105}
106
107pub struct IcoDecoder<R: BufRead + Seek> {
109 selected_entry: DirEntry,
110 inner_decoder: InnerDecoder<R>,
111}
112
113enum InnerDecoder<R: BufRead + Seek> {
114 Bmp(BmpDecoder<R>),
115 Png(Box<PngDecoder<R>>),
116}
117
118#[derive(Clone, Copy, Default)]
119struct DirEntry {
120 width: u8,
121 height: u8,
122 #[allow(unused)]
125 color_count: u8,
126 #[allow(unused)]
131 reserved: u8,
132
133 #[allow(unused)]
136 num_color_planes: u16,
137 bits_per_pixel: u16,
138
139 image_length: u32,
140 image_offset: u32,
141}
142
143impl<R: BufRead + Seek> IcoDecoder<R> {
144 pub fn new(mut r: R) -> ImageResult<IcoDecoder<R>> {
146 let entries = read_entries(&mut r)?;
147 let entry = best_entry(entries)?;
148 let decoder = entry.decoder(r)?;
149
150 Ok(IcoDecoder {
151 selected_entry: entry,
152 inner_decoder: decoder,
153 })
154 }
155}
156
157fn read_entries<R: Read>(r: &mut R) -> ImageResult<Vec<DirEntry>> {
158 let _reserved = r.read_u16::<LittleEndian>()?;
159 let _type = r.read_u16::<LittleEndian>()?;
160 let count = r.read_u16::<LittleEndian>()?;
161 (0..count).map(|_| read_entry(r)).collect()
162}
163
164fn read_entry<R: Read>(r: &mut R) -> ImageResult<DirEntry> {
165 Ok(DirEntry {
166 width: r.read_u8()?,
167 height: r.read_u8()?,
168 color_count: r.read_u8()?,
169 reserved: r.read_u8()?,
170 num_color_planes: {
171 let num = r.read_u16::<LittleEndian>()?;
174 if num > 256 {
175 return Err(DecoderError::IcoEntryTooManyPlanesOrHotspot.into());
176 }
177 num
178 },
179 bits_per_pixel: {
180 let num = r.read_u16::<LittleEndian>()?;
183 if num > 256 {
184 return Err(DecoderError::IcoEntryTooManyBitsPerPixelOrHotspot.into());
185 }
186 num
187 },
188 image_length: r.read_u32::<LittleEndian>()?,
189 image_offset: r.read_u32::<LittleEndian>()?,
190 })
191}
192
193fn best_entry(mut entries: Vec<DirEntry>) -> ImageResult<DirEntry> {
195 let mut best = entries.pop().ok_or(DecoderError::NoEntries)?;
196
197 let mut best_score = (
198 best.bits_per_pixel,
199 u32::from(best.real_width()) * u32::from(best.real_height()),
200 );
201
202 for entry in entries {
203 let score = (
204 entry.bits_per_pixel,
205 u32::from(entry.real_width()) * u32::from(entry.real_height()),
206 );
207 if score > best_score {
208 best = entry;
209 best_score = score;
210 }
211 }
212 Ok(best)
213}
214
215impl DirEntry {
216 fn real_width(&self) -> u16 {
217 match self.width {
218 0 => 256,
219 w => u16::from(w),
220 }
221 }
222
223 fn real_height(&self) -> u16 {
224 match self.height {
225 0 => 256,
226 h => u16::from(h),
227 }
228 }
229
230 fn matches_dimensions(&self, width: u32, height: u32) -> bool {
231 u32::from(self.real_width()) == width.min(256)
232 && u32::from(self.real_height()) == height.min(256)
233 }
234
235 fn seek_to_start<R: Read + Seek>(&self, r: &mut R) -> ImageResult<()> {
236 r.seek(SeekFrom::Start(u64::from(self.image_offset)))?;
237 Ok(())
238 }
239
240 fn is_png<R: Read + Seek>(&self, r: &mut R) -> ImageResult<bool> {
241 self.seek_to_start(r)?;
242
243 let mut signature = [0u8; 8];
245 r.read_exact(&mut signature)?;
246
247 Ok(signature == PNG_SIGNATURE)
248 }
249
250 fn decoder<R: BufRead + Seek>(&self, mut r: R) -> ImageResult<InnerDecoder<R>> {
251 let is_png = self.is_png(&mut r)?;
252 self.seek_to_start(&mut r)?;
253
254 if is_png {
255 Ok(Png(Box::new(PngDecoder::new(r)?)))
256 } else {
257 Ok(Bmp(BmpDecoder::new_with_ico_format(r)?))
258 }
259 }
260}
261
262impl<R: BufRead + Seek> ImageDecoder for IcoDecoder<R> {
263 fn dimensions(&self) -> (u32, u32) {
264 match self.inner_decoder {
265 Bmp(ref decoder) => decoder.dimensions(),
266 Png(ref decoder) => decoder.dimensions(),
267 }
268 }
269
270 fn color_type(&self) -> ColorType {
271 match self.inner_decoder {
272 Bmp(ref decoder) => decoder.color_type(),
273 Png(ref decoder) => decoder.color_type(),
274 }
275 }
276
277 fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
278 assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
279 match self.inner_decoder {
280 Png(decoder) => {
281 if self.selected_entry.image_length < PNG_SIGNATURE.len() as u32 {
282 return Err(DecoderError::PngShorterThanHeader.into());
283 }
284
285 let (width, height) = decoder.dimensions();
287 if !self.selected_entry.matches_dimensions(width, height) {
288 return Err(DecoderError::ImageEntryDimensionMismatch {
289 format: IcoEntryImageFormat::Png,
290 entry: (
291 self.selected_entry.real_width(),
292 self.selected_entry.real_height(),
293 ),
294 image: (width, height),
295 }
296 .into());
297 }
298
299 if decoder.color_type() != ColorType::Rgba8 {
302 return Err(DecoderError::PngNotRgba.into());
303 }
304
305 decoder.read_image(buf)
306 }
307 Bmp(mut decoder) => {
308 let (width, height) = decoder.dimensions();
309 if !self.selected_entry.matches_dimensions(width, height) {
310 return Err(DecoderError::ImageEntryDimensionMismatch {
311 format: IcoEntryImageFormat::Bmp,
312 entry: (
313 self.selected_entry.real_width(),
314 self.selected_entry.real_height(),
315 ),
316 image: (width, height),
317 }
318 .into());
319 }
320
321 if decoder.color_type() != ColorType::Rgba8 {
323 return Err(ImageError::Unsupported(
324 UnsupportedError::from_format_and_kind(
325 ImageFormat::Bmp.into(),
326 UnsupportedErrorKind::Color(decoder.color_type().into()),
327 ),
328 ));
329 }
330
331 decoder.read_image_data(buf)?;
332
333 let r = decoder.reader();
334 let image_end = r.stream_position()?;
335 let data_end = u64::from(self.selected_entry.image_offset)
336 + u64::from(self.selected_entry.image_length);
337
338 let mask_row_bytes = ((width + 31) / 32) * 4;
339 let mask_length = u64::from(mask_row_bytes) * u64::from(height);
340
341 if data_end >= image_end + mask_length {
349 for y in 0..height {
351 let mut x = 0;
352 for _ in 0..mask_row_bytes {
353 let mask_byte = r.read_u8()?;
355 for bit in (0..8).rev() {
356 if x >= width {
357 break;
358 }
359 if mask_byte & (1 << bit) != 0 {
360 buf[((height - y - 1) * width + x) as usize * 4 + 3] = 0;
362 }
363 x += 1;
364 }
365 }
366 }
367
368 Ok(())
369 } else if data_end == image_end {
370 Ok(())
372 } else {
373 Err(DecoderError::InvalidDataSize.into())
374 }
375 }
376 }
377 }
378
379 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
380 (*self).read_image(buf)
381 }
382}
383
384#[cfg(test)]
385mod test {
386 use super::*;
387
388 #[test]
391 fn bmp_16_with_missing_alpha_channel() {
392 let data = vec![
393 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0e, 0x04, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00,
394 0x7c, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0x01, 0x00,
395 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
396 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
397 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
398 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
399 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
400 0x00, 0x00, 0x00, 0x8f, 0xf6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
401 0x20, 0x66, 0x74, 0x83, 0x70, 0x61, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
402 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xeb, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00,
403 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x47, 0x0d,
404 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x62, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00,
405 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0c,
406 0x00, 0x00, 0x00, 0xc3, 0x3f, 0x94, 0x61, 0xaa, 0x17, 0x4d, 0x8d, 0x79, 0x1d, 0x8b,
407 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x2e, 0x28, 0x40, 0xe5, 0x9f,
408 0x4b, 0x4d, 0xe9, 0x87, 0xd3, 0xda, 0xd6, 0x89, 0x81, 0xc5, 0xa4, 0xa1, 0x60, 0x98,
409 0x31, 0xc7, 0x1d, 0xb6, 0x8f, 0x20, 0xc8, 0x3e, 0xee, 0xd8, 0xe4, 0x8f, 0xee, 0x7b,
410 0x48, 0x9b, 0x88, 0x25, 0x13, 0xda, 0xa4, 0x13, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x40,
411 0x16, 0x01, 0xff, 0xff, 0xff, 0xff, 0xe9, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
412 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
413 0x00, 0x00, 0xa3, 0x66, 0x64, 0x41, 0x54, 0xa3, 0xa3, 0x00, 0x00, 0x00, 0xb8, 0x00,
414 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
415 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa3, 0x66, 0x64, 0x41, 0x54, 0xa3, 0xa3,
416 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
417 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
418 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0xf6, 0xff, 0xff,
419 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x66, 0x74, 0x83, 0x70, 0x61, 0x76,
420 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
421 0xeb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
422 0x00, 0x00, 0x00, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x62, 0x49,
423 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
424 0x00, 0x00, 0x00, 0xff, 0xff, 0x94, 0xc8, 0x00, 0x02, 0x0c, 0x00, 0xff, 0xff, 0xc6,
425 0x84, 0x00, 0x2a, 0x75, 0x03, 0xa3, 0x05, 0xfb, 0xe1, 0x6e, 0xe8, 0x27, 0xd6, 0xd3,
426 0x96, 0xc1, 0xe4, 0x30, 0x0c, 0x05, 0xb9, 0xa3, 0x8b, 0x29, 0xda, 0xa4, 0xf1, 0x4d,
427 0xf3, 0xb2, 0x98, 0x2b, 0xe6, 0x93, 0x07, 0xf9, 0xca, 0x2b, 0xc2, 0x39, 0x20, 0xba,
428 0x7c, 0xa0, 0xb1, 0x43, 0xe6, 0xf9, 0xdc, 0xd1, 0xc2, 0x52, 0xdc, 0x41, 0xc1, 0x2f,
429 0x29, 0xf7, 0x46, 0x32, 0xda, 0x1b, 0x72, 0x8c, 0xe6, 0x2b, 0x01, 0xe5, 0x49, 0x21,
430 0x89, 0x89, 0xe4, 0x3d, 0xa1, 0xdb, 0x3b, 0x4a, 0x0b, 0x52, 0x86, 0x52, 0x33, 0x9d,
431 0xb2, 0xcf, 0x4a, 0x86, 0x53, 0xd7, 0xa9, 0x4b, 0xaf, 0x62, 0x06, 0x49, 0x53, 0x00,
432 0xc3, 0x3f, 0x94, 0x61, 0xaa, 0x17, 0x4d, 0x8d, 0x79, 0x1d, 0x8b, 0x10, 0x00, 0x00,
433 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x2e, 0x28, 0x40, 0xe5, 0x9f, 0x4b, 0x4d, 0xe9,
434 0x87, 0xd3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe7, 0xc5, 0x00,
435 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x50, 0x31, 0x00, 0x00, 0x00, 0x00,
436 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x76, 0x76, 0x01, 0x00, 0x00, 0x00, 0x76, 0x00,
437 0x00, 0x23, 0x3f, 0x52, 0x41, 0x44, 0x49, 0x41, 0x4e, 0x43, 0x45, 0x61, 0x50, 0x35,
438 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x4d, 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x05,
439 0x50, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x37, 0x61,
440 ];
441
442 let decoder = IcoDecoder::new(std::io::Cursor::new(&data)).unwrap();
443 let mut buf = vec![0; usize::try_from(decoder.total_bytes()).unwrap()];
444 assert!(decoder.read_image(&mut buf).is_err());
445 }
446}