1use std::collections::HashMap;
23use std::fmt::{self, Display};
24use std::io::{Cursor, Read, Seek, SeekFrom};
25
26use image::error::{
27 DecodingError, ImageFormatHint, LimitError, LimitErrorKind, UnsupportedError,
28 UnsupportedErrorKind,
29};
30use image::{ColorType, ImageDecoder, ImageError, ImageReader, ImageResult, LimitSupport, Limits};
31
32use icns::{Encoding, IconElement, IconType, OSType, PixelFormat};
33
34#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
36enum DecoderError {
37 ImageEndAfterEndOfStream,
39 NotICNS,
41 DuplicateEntry(IconType),
43 IncompleteEntry,
45 NoImageFound,
47 MissingMask(IconType),
49 BadEntryLength,
51 NotPNGorJP2(IconType),
53 BadPngSize(u32, u32, u32),
55 BadJp2Size(u32, u32, u32),
57}
58
59impl From<DecoderError> for ImageError {
60 fn from(e: DecoderError) -> ImageError {
61 ImageError::Decoding(DecodingError::new(icns_format_hint(), e))
62 }
63}
64
65impl Display for DecoderError {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 match self {
68 DecoderError::ImageEndAfterEndOfStream => {
69 f.write_str("The end of the image would have a stream position >u64::MAX")
70 }
71 DecoderError::NotICNS => f.write_str("Image file does not start with 'icns'"),
72 DecoderError::DuplicateEntry(t) => f.write_fmt(format_args!(
73 "Image file contains multiple entries of type {t:?}"
74 )),
75 DecoderError::MissingMask(t) => f.write_fmt(format_args!(
76 "Image file did not contain a mask entry for the entry of type {t:?}"
77 )),
78 DecoderError::IncompleteEntry => f.write_str(
79 "The last entry in the file would extend past the end of file indicated by the header"
80 ),
81 DecoderError::NoImageFound => f.write_str(
82 "No image entry that the decoder might support was found"
83 ),
84 DecoderError::BadEntryLength => f.write_str(
85 "Image file contained an entry with invalid length (less than 8)"
86 ),
87 DecoderError::NotPNGorJP2(t) => f.write_fmt(format_args!(
88 "Image file entry of type {t:?} contained neither PNG nor Jpeg 2000 data"
89 )),
90 DecoderError::BadPngSize(w,h,s) => f.write_fmt(format_args!(
91 "Image file entry with PNG data had size {w}x{h} instead of expected {s}x{s}"
92 )),
93 DecoderError::BadJp2Size(w,h,s) => f.write_fmt(format_args!(
94 "Image file entry with Jpeg 2000 data had size {w}x{h} instead of expected {s}x{s}"
95 )),
96 }
97 }
98}
99impl std::error::Error for DecoderError {}
100
101fn icns_format_hint() -> ImageFormatHint {
102 ImageFormatHint::Name("ICNS".into())
103}
104
105fn png_to_image_error(err: png::DecodingError) -> ImageError {
107 use png::DecodingError::*;
108 match err {
109 IoError(err) => ImageError::IoError(err),
110 err @ Format(_) => ImageError::Decoding(DecodingError::new(icns_format_hint(), err)),
112 Parameter(_) => unreachable!(),
113 LimitsExceeded => {
114 ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
115 }
116 }
117}
118
119fn jp2_to_image_error(err: ImageError) -> ImageError {
121 match err {
122 ImageError::Decoding(e) => ImageError::Decoding(DecodingError::new(icns_format_hint(), e)),
123 ImageError::Encoding(_) => {
124 unreachable!();
126 }
127 ImageError::Parameter(e) => ImageError::Parameter(e),
128 ImageError::Limits(e) => ImageError::Limits(e),
129 ImageError::Unsupported(e) => {
130 ImageError::Decoding(DecodingError::new(icns_format_hint(), e))
131 }
132 ImageError::IoError(e) => ImageError::IoError(e),
133 }
134}
135
136pub type SubformatDecodeFn = Box<dyn Fn(&[u8], u32, &mut [u8], u64) -> ImageResult<()>>;
144
145fn decode_png(data: &[u8], size: u32, buf: &mut [u8], allocation_limit: u64) -> ImageResult<()> {
150 let mut decoder = png::Decoder::new_with_limits(
151 Cursor::new(data),
152 png::Limits {
153 bytes: allocation_limit.try_into().unwrap_or(usize::MAX),
154 },
155 );
156 decoder.set_transformations(png::Transformations::STRIP_16 | png::Transformations::ALPHA);
159 let info = decoder.read_header_info().map_err(png_to_image_error)?;
160
161 if info.width != size || info.height != size {
162 return Err(DecoderError::BadPngSize(info.width, info.height, size).into());
163 }
164
165 let mut reader = decoder.read_info().map_err(png_to_image_error)?;
166 let (color_type, bits) = reader.output_color_type();
167
168 assert!(bits == png::BitDepth::Eight);
169 match color_type {
170 png::ColorType::GrayscaleAlpha => {
171 let mut tmp = vec![0u8; (size as usize) * (size as usize) * 2];
173 reader.next_frame(&mut tmp).map_err(png_to_image_error)?;
174
175 for (ga, rgba) in tmp.chunks_exact(2).zip(buf.chunks_exact_mut(4)) {
176 rgba.copy_from_slice(&[ga[0], ga[0], ga[0], ga[1]]);
177 }
178 }
179 png::ColorType::Rgba => {
180 reader.next_frame(buf).map_err(png_to_image_error)?;
181 }
182 _ => unreachable!(),
183 }
184
185 reader.finish().map_err(png_to_image_error)?;
186
187 Ok(())
188}
189
190pub fn decode_jpeg2000_using_hook(
195 data: &[u8],
196 size: u32,
197 buf: &mut [u8],
198 allocation_limit: u64,
199) -> ImageResult<()> {
200 let mut reader = ImageReader::new(Cursor::new(data));
204 reader = reader.with_guessed_format()?;
205 let mut limits = Limits::no_limits();
206 limits.max_alloc = Some(allocation_limit);
207 reader.limits(limits);
208
209 let image = reader.decode().map_err(jp2_to_image_error)?;
210 if image.width() != size || image.height() != size {
211 return Err(DecoderError::BadJp2Size(image.width(), image.height(), size).into());
212 }
213
214 buf.copy_from_slice(image.to_rgba8().as_flat_samples().samples);
215
216 Ok(())
217}
218
219pub fn unsupported_jpeg2000(
222 _data: &[u8],
223 _size: u32,
224 _buf: &mut [u8],
225 _allocation_limit: u64,
226) -> ImageResult<()> {
227 Err(ImageError::Unsupported(
228 UnsupportedError::from_format_and_kind(
229 icns_format_hint(),
230 UnsupportedErrorKind::GenericFeature("Jpeg 2000 subimage".to_string()),
231 ),
232 ))
233}
234
235#[derive(Clone, Copy)]
236struct IcnsEntry {
237 code: IconType,
238 stream_pos: u64,
239 length: u32,
240}
241
242impl IcnsEntry {
243 fn score(&self) -> (u32, u8, u64) {
248 let bit_depth = match self.code.encoding() {
249 Encoding::Mask8 => {
250 panic!("Entry with color data required, not Mask8");
251 }
252 Encoding::Mono => 1,
253 Encoding::MonoA => 2,
254 Encoding::Palette4 => 5,
256 Encoding::Palette8 => 9,
257 Encoding::RLE24 => 32,
259 Encoding::JP2PNG => 32,
260 };
261 (
262 self.code.pixel_width(),
263 bit_depth,
264 u64::MAX - self.stream_pos,
265 )
266 }
267}
268
269pub struct IcnsDecoder<R> {
271 reader: R,
272
273 main: IcnsEntry,
274 mask: Option<IcnsEntry>,
276
277 limits: Limits,
278 jp2: SubformatDecodeFn,
279}
280
281fn read_vec_at<R>(reader: &mut R, start: u64, len: u32) -> Result<Vec<u8>, std::io::Error>
283where
284 R: Read + Seek,
285{
286 assert!(start.checked_add(len as u64).is_some());
287 reader.seek(SeekFrom::Start(start))?;
288
289 let mut data = vec![0; len.try_into().unwrap()];
290 reader.read_exact(&mut data)?;
291 Ok(data)
292}
293
294impl<R> IcnsDecoder<R>
295where
296 R: Read + Seek,
297{
298 pub fn new(reader: R) -> Result<IcnsDecoder<R>, ImageError> {
306 Self::new_with_decode_func(reader, Box::new(unsupported_jpeg2000))
307 }
308
309 pub fn new_with_decode_func(
317 mut reader: R,
318 jp2: SubformatDecodeFn,
319 ) -> Result<IcnsDecoder<R>, ImageError> {
320 let mut header = [0u8; 8];
321 reader.read_exact(&mut header)?;
322 let (magic, file_len_field) = header.split_at(4);
323 if magic != b"icns" {
324 return Err(DecoderError::NotICNS.into());
325 }
326 let file_length = u32::from_be_bytes(file_len_field.try_into().unwrap());
327 let Some(remaining_len) = file_length.checked_sub(8) else {
328 return Err(DecoderError::BadEntryLength.into());
329 };
330
331 let mut first_entries: HashMap<IconType, IcnsEntry> = HashMap::new();
334
335 let base_pos = reader.stream_position()?;
336 let Some(end) = base_pos.checked_add(u64::from(remaining_len)) else {
337 return Err(DecoderError::ImageEndAfterEndOfStream.into());
338 };
339
340 let mut cur_pos = base_pos;
342 while cur_pos < end {
343 let image_start_pos = cur_pos;
344 if cur_pos > end.saturating_sub(8) {
345 return Err(DecoderError::IncompleteEntry.into());
346 }
347
348 let mut entry = [0u8; 8];
349 reader.read_exact(&mut entry)?;
350 let (ostype_field, entry_len_field) = entry.split_at(4);
351 let ostype = OSType(ostype_field.try_into().unwrap());
352 let entry_len = u32::from_be_bytes(entry_len_field.try_into().unwrap());
353
354 let Some(data_len) = entry_len.checked_sub(8) else {
355 return Err(DecoderError::BadEntryLength.into());
356 };
357 if cur_pos > end.saturating_sub(entry_len as u64) {
358 return Err(DecoderError::IncompleteEntry.into());
359 }
360 if cur_pos < end {
361 reader.seek_relative(data_len as i64)?;
362 }
363 cur_pos += entry_len as u64;
364
365 let Some(code) = IconType::from_ostype(ostype) else {
367 continue;
369 };
370
371 let entry = IcnsEntry {
372 code,
373 stream_pos: image_start_pos + 8,
374 length: data_len,
375 };
376 if first_entries.insert(code, entry).is_some() {
377 return Err(DecoderError::DuplicateEntry(code).into());
378 }
379 }
380
381 let main_entry = first_entries
382 .values()
383 .filter(|entry| entry.code.encoding() != Encoding::Mask8)
384 .max_by_key(|entry| entry.score());
385
386 let Some(main) = main_entry.copied() else {
387 return Err(DecoderError::NoImageFound.into());
388 };
389 let mut mask = None;
390 if let Some(mtype) = main.code.mask_type() {
391 mask = first_entries.get(&mtype).copied();
392 if mask.is_none() {
393 return Err(DecoderError::MissingMask(main.code).into());
394 }
395 }
396 Ok(IcnsDecoder {
397 reader,
398 main,
399 mask,
400 limits: Limits::no_limits(),
401 jp2,
402 })
403 }
404}
405
406impl<R: Read + Seek> ImageDecoder for IcnsDecoder<R> {
407 fn dimensions(&self) -> (u32, u32) {
408 (self.main.code.pixel_width(), self.main.code.pixel_height())
409 }
410
411 fn color_type(&self) -> ColorType {
412 match self.main.code.encoding() {
413 Encoding::Mask8 => unreachable!(),
414 Encoding::Mono => ColorType::L8,
415 Encoding::MonoA => ColorType::La8,
416 _ => ColorType::Rgba8,
417 }
418 }
419
420 fn set_limits(&mut self, mut limits: Limits) -> ImageResult<()> {
421 limits.check_support(&LimitSupport::default())?;
422 let (width, height) = self.dimensions();
423 limits.check_dimensions(width, height)?;
424
425 let icon_size = self.main.code.pixel_width();
426
427 let main_data = self.main.length;
428 let mask_data = self.mask.map(|x| x.length).unwrap_or_default();
429
430 assert!(icon_size <= 1024);
441 let icon_decode_space = icon_size * icon_size * 8;
442
443 let space_req = u64::from(icon_decode_space) + u64::from(main_data) + u64::from(mask_data);
444 limits.reserve(space_req)?;
445
446 self.limits = limits;
447
448 Ok(())
449 }
450
451 fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
452 assert!(self.total_bytes() == buf.len().try_into().unwrap());
453
454 let main_data = read_vec_at(&mut self.reader, self.main.stream_pos, self.main.length)?;
455
456 const PNG_SIGNATURE: &[u8] = &[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
457 const JP2_SIGNATURE: &[u8] = &[
458 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A,
459 ];
460
461 if self.main.code.encoding() == Encoding::JP2PNG {
462 if main_data.starts_with(PNG_SIGNATURE) {
466 decode_png(
467 &main_data,
468 self.main.code.pixel_width(),
469 buf,
470 self.limits.max_alloc.unwrap_or(u64::MAX),
471 )?;
472 } else if main_data.starts_with(JP2_SIGNATURE) {
473 (self.jp2)(
474 &main_data,
475 self.main.code.pixel_width(),
476 buf,
477 self.limits.max_alloc.unwrap_or(u64::MAX),
478 )?;
479 } else {
480 return Err(DecoderError::NotPNGorJP2(self.main.code).into());
481 }
482 } else {
483 let main = IconElement::new(self.main.code.ostype(), main_data);
484
485 let img = if let Some(mask_entry) = &self.mask {
486 let mask_data =
487 read_vec_at(&mut self.reader, mask_entry.stream_pos, mask_entry.length)?;
488 let mask = IconElement::new(mask_entry.code.ostype(), mask_data);
489
490 main.decode_image_with_mask(&mask)?
491 } else {
492 assert!(self.main.code.mask_type().is_none());
493 main.decode_image()?
494 };
495 assert!((img.width(), img.height()) == self.dimensions());
496 assert!(img.pixel_format() != PixelFormat::Alpha);
497
498 match (img.pixel_format(), self.color_type()) {
499 (PixelFormat::Gray, ColorType::L8) => {
500 buf.copy_from_slice(img.data());
501 }
502 (PixelFormat::GrayAlpha, ColorType::La8) => {
503 buf.copy_from_slice(img.data());
504 }
505 (PixelFormat::RGBA, ColorType::Rgba8)
506 | (PixelFormat::RGB, ColorType::Rgba8)
507 | (PixelFormat::Gray, ColorType::Rgba8)
508 | (PixelFormat::GrayAlpha, ColorType::Rgba8) => {
509 let converted = img.convert_to(PixelFormat::RGBA);
510 buf.copy_from_slice(converted.data());
511 }
512
513 _ => unreachable!(
515 "icns crate produced {:?}, not compatible with {:?}",
516 img.pixel_format(),
517 self.color_type()
518 ),
519 };
520 }
521
522 Ok(())
523 }
524
525 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
526 (*self).read_image(buf)
527 }
528}