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