1use std::fs::File;
27use std::io::{Read, Seek, SeekFrom, BufReader};
28
29#[non_exhaustive]
30#[derive(Debug, Clone, Copy, PartialEq)]
31pub enum ImFormat {
32 GIF = 1,
34
35 PNG = 2,
37
38 BMP = 3,
41
42 JPEG = 4,
44
45 WEBP = 5,
47
48 QOI = 6,
50
51 PSD = 7,
53
54 XCF = 8,
56
57 ICO = 9,
60
61 AVIF = 10,
63
64 TIFF = 11,
66
67 OpenEXR = 12,
69
70 PCX = 13,
72
73 TGA = 14,
80
81 DDS = 15,
83
84 HEIF = 16,
87
88 JP2K = 17,
90
91 DIB = 18,
93
94 VTF = 19,
96
97 ILBM = 20,
99}
100
101impl ImFormat {
102 pub const fn name(&self) -> &'static str {
103 match self {
104 Self::GIF => "GIF",
105 Self::PNG => "PNG",
106 Self::BMP => "BMP",
107 Self::JPEG => "JPEG",
108 Self::WEBP => "WebP",
109 Self::QOI => "QOI",
110 Self::PSD => "PSD",
111 Self::XCF => "XCF",
112 Self::ICO => "ICO",
113 Self::AVIF => "AVIF",
114 Self::TIFF => "TIFF",
115 Self::OpenEXR => "OpenEXR",
116 Self::PCX => "PCX",
117 Self::TGA => "TGA",
118 Self::DDS => "DDS",
119 Self::HEIF => "HEIF",
120 Self::JP2K => "JPEG 2000",
121 Self::DIB => "DIB",
122 Self::VTF => "VTF",
123 Self::ILBM => "ILBM",
124 }
125 }
126}
127
128impl std::fmt::Display for ImFormat {
129 #[inline]
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 self.name().fmt(f)
132 }
133}
134
135#[derive(Debug, Clone)]
137pub struct ImInfo {
138 pub width: u64,
139 pub height: u64,
140 pub format: ImFormat,
141}
142
143#[derive(Debug)]
144pub enum ImError {
145 IO(std::io::Error),
147
148 UnknownFormat,
150
151 ParserError(ImFormat),
154}
155
156impl std::fmt::Display for ImError {
157 #[inline]
158 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159 match self {
160 Self::IO(error) => error.fmt(f),
161 Self::UnknownFormat => "Unknown Format".fmt(f),
162 Self::ParserError(format) => write!(f, "Error parsing {format} image")
163 }
164 }
165}
166
167impl std::error::Error for ImError {
168 #[inline]
169 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
170 match self {
171 Self::IO(error) => Some(error),
172 _ => None
173 }
174 }
175}
176
177impl From<std::io::Error> for ImError {
178 #[inline]
179 fn from(error: std::io::Error) -> Self {
180 ImError::IO(error)
181 }
182}
183
184pub type ImResult<T> = std::result::Result<T, ImError>;
185
186trait Ratio<T: Sized> {
187 fn value<R>(&self) -> R::Output
188 where R: Sized, R: std::ops::Div, R: From<T>;
189}
190
191impl Ratio<u32> for (u32, u32) {
192 #[inline]
193 fn value<R>(&self) -> R::Output
194 where R: Sized, R: std::ops::Div, R: From<u32> {
195 let (a, b) = *self;
196 let x: R = a.into();
197 let y: R = b.into();
198 x / y
199 }
200}
201
202impl Ratio<i32> for (i32, i32) {
203 #[inline]
204 fn value<R>(&self) -> R::Output
205 where R: Sized, R: std::ops::Div, R: From<i32> {
206 let (a, b) = *self;
207 let x: R = a.into();
208 let y: R = b.into();
209 x / y
210 }
211}
212
213trait BinaryReader {
214 #[inline]
215 fn read_u8(reader: &mut impl Read) -> std::io::Result<u8> {
216 let mut buf = [0u8];
217 reader.read_exact(&mut buf)?;
218 return Ok(buf[0]);
219 }
220
221 #[inline]
222 fn read_uchar(reader: &mut impl Read) -> std::io::Result<u8> {
223 let mut buf = [0u8];
224 reader.read_exact(&mut buf)?;
225 return Ok(buf[0]);
226 }
227
228 #[inline]
229 fn read_i8(reader: &mut impl Read) -> std::io::Result<i8> {
230 let mut buf = [0u8];
231 reader.read_exact(&mut buf)?;
232 return Ok(buf[0] as i8);
233 }
234
235 #[inline]
236 fn read_ichar(reader: &mut impl Read) -> std::io::Result<i8> {
237 let mut buf = [0u8];
238 reader.read_exact(&mut buf)?;
239 return Ok(buf[0] as i8);
240 }
241
242 fn get_u32(buf: [u8; 4]) -> u32;
243
244 fn read_u16(reader: &mut impl Read) -> std::io::Result<u16>;
245 fn read_u32(reader: &mut impl Read) -> std::io::Result<u32>;
246 fn read_uratio(reader: &mut impl Read) -> std::io::Result<(u32, u32)>;
247
248 fn read_i16(reader: &mut impl Read) -> std::io::Result<i16>;
249 fn read_i32(reader: &mut impl Read) -> std::io::Result<i32>;
250 fn read_iratio(reader: &mut impl Read) -> std::io::Result<(i32, i32)>;
251
252 fn read_f32(reader: &mut impl Read) -> std::io::Result<f32>;
253 fn read_f64(reader: &mut impl Read) -> std::io::Result<f64>;
254}
255
256struct LittleEndianReader;
257struct BigEndianReader;
258
259impl BinaryReader for LittleEndianReader {
260 #[inline]
261 fn get_u32(buf: [u8; 4]) -> u32 {
262 return u32::from_le_bytes(buf);
263 }
264
265 #[inline]
266 fn read_u16(reader: &mut impl Read) -> std::io::Result<u16> {
267 let mut buf = [0u8; 2];
268 reader.read_exact(&mut buf)?;
269 return Ok(u16::from_le_bytes(buf));
270 }
271
272 #[inline]
273 fn read_u32(reader: &mut impl Read) -> std::io::Result<u32> {
274 let mut buf = [0u8; 4];
275 reader.read_exact(&mut buf)?;
276 return Ok(u32::from_le_bytes(buf));
277 }
278
279 #[inline]
280 fn read_uratio(reader: &mut impl Read) -> std::io::Result<(u32, u32)> {
281 let mut buf = [0u8; 8];
282 reader.read_exact(&mut buf)?;
283 return Ok((
284 u32::from_le_bytes([ buf[0], buf[1], buf[2], buf[3] ]),
285 u32::from_le_bytes([ buf[4], buf[5], buf[6], buf[7] ]),
286 ));
287 }
288
289 #[inline]
290 fn read_i16(reader: &mut impl Read) -> std::io::Result<i16> {
291 let mut buf = [0u8; 2];
292 reader.read_exact(&mut buf)?;
293 return Ok(i16::from_le_bytes(buf));
294 }
295
296 #[inline]
297 fn read_i32(reader: &mut impl Read) -> std::io::Result<i32> {
298 let mut buf = [0u8; 4];
299 reader.read_exact(&mut buf)?;
300 return Ok(i32::from_le_bytes(buf));
301 }
302
303 #[inline]
304 fn read_iratio(reader: &mut impl Read) -> std::io::Result<(i32, i32)> {
305 let mut buf = [0u8; 8];
306 reader.read_exact(&mut buf)?;
307 return Ok((
308 i32::from_le_bytes([ buf[0], buf[1], buf[2], buf[3] ]),
309 i32::from_le_bytes([ buf[4], buf[5], buf[6], buf[7] ]),
310 ));
311 }
312
313 #[inline]
314 fn read_f32(reader: &mut impl Read) -> std::io::Result<f32> {
315 let mut buf = [0u8; 4];
316 reader.read_exact(&mut buf)?;
317 return Ok(f32::from_le_bytes(buf));
318 }
319
320 #[inline]
321 fn read_f64(reader: &mut impl Read) -> std::io::Result<f64> {
322 let mut buf = [0u8; 8];
323 reader.read_exact(&mut buf)?;
324 return Ok(f64::from_le_bytes(buf));
325 }
326}
327
328impl BinaryReader for BigEndianReader {
329 #[inline]
330 fn get_u32(buf: [u8; 4]) -> u32 {
331 return u32::from_be_bytes(buf);
332 }
333
334 #[inline]
335 fn read_u16(reader: &mut impl Read) -> std::io::Result<u16> {
336 let mut buf = [0u8; 2];
337 reader.read_exact(&mut buf)?;
338 return Ok(u16::from_be_bytes(buf));
339 }
340
341 #[inline]
342 fn read_u32(reader: &mut impl Read) -> std::io::Result<u32> {
343 let mut buf = [0u8; 4];
344 reader.read_exact(&mut buf)?;
345 return Ok(u32::from_be_bytes(buf));
346 }
347
348 #[inline]
349 fn read_uratio(reader: &mut impl Read) -> std::io::Result<(u32, u32)> {
350 let mut buf = [0u8; 8];
351 reader.read_exact(&mut buf)?;
352 return Ok((
353 u32::from_be_bytes([ buf[0], buf[1], buf[2], buf[3] ]),
354 u32::from_be_bytes([ buf[4], buf[5], buf[6], buf[7] ]),
355 ));
356 }
357
358 #[inline]
359 fn read_i16(reader: &mut impl Read) -> std::io::Result<i16> {
360 let mut buf = [0u8; 2];
361 reader.read_exact(&mut buf)?;
362 return Ok(i16::from_be_bytes(buf));
363 }
364
365 #[inline]
366 fn read_i32(reader: &mut impl Read) -> std::io::Result<i32> {
367 let mut buf = [0u8; 4];
368 reader.read_exact(&mut buf)?;
369 return Ok(i32::from_be_bytes(buf));
370 }
371
372 #[inline]
373 fn read_iratio(reader: &mut impl Read) -> std::io::Result<(i32, i32)> {
374 let mut buf = [0u8; 8];
375 reader.read_exact(&mut buf)?;
376 return Ok((
377 i32::from_be_bytes([ buf[0], buf[1], buf[2], buf[3] ]),
378 i32::from_be_bytes([ buf[4], buf[5], buf[6], buf[7] ]),
379 ));
380 }
381
382 #[inline]
383 fn read_f32(reader: &mut impl Read) -> std::io::Result<f32> {
384 let mut buf = [0u8; 4];
385 reader.read_exact(&mut buf)?;
386 return Ok(f32::from_be_bytes(buf));
387 }
388
389 #[inline]
390 fn read_f64(reader: &mut impl Read) -> std::io::Result<f64> {
391 let mut buf = [0u8; 8];
392 reader.read_exact(&mut buf)?;
393 return Ok(f64::from_be_bytes(buf));
394 }
395}
396
397macro_rules! array2 {
398 ($data:expr, $offset:expr) => {
399 [ $data[$offset], $data[$offset + 1] ]
400 };
401}
402
403macro_rules! array4 {
404 ($data:expr, $offset:expr) => {
405 [ $data[$offset], $data[$offset + 1], $data[$offset + 2], $data[$offset + 3] ]
406 };
407}
408
409macro_rules! map_err {
410 ($fmt:expr, $expr:expr) => {
411 if let Err(_) = $expr {
412 return Err(ImError::ParserError($fmt));
413 }
414 };
415
416 ($fmt:ident $expr:expr) => {
417 map_err!(ImFormat::$fmt, $expr);
418 };
419}
420
421macro_rules! map_expr {
422 ($fmt:ident $expr:expr) => {
423 match $expr {
424 Err(_) => return Err(ImError::ParserError(ImFormat::$fmt)),
425 Ok(value) => value
426 }
427 };
428}
429
430fn find_riff_chunk<R>(reader: &mut R, name: &[u8; 4], chunk_size: u64, format: ImFormat) -> ImResult<u64>
431where R: Read, R: Seek {
432 let mut sub_chunk_size;
433 let mut buf = [0u8; 8];
434 let mut offset = 0;
435
436 loop {
437 if offset > chunk_size {
438 return Err(ImError::ParserError(format));
439 }
440 if let Err(_) = reader.read_exact(&mut buf) {
441 return Err(ImError::ParserError(format));
442 }
443 sub_chunk_size = u32::from_be_bytes(array4!(&buf, 0)) as u64;
444 if sub_chunk_size < 8 {
445 return Err(ImError::ParserError(format));
446 }
447 if &buf[4..8] == name {
448 break;
449 }
450 offset += sub_chunk_size;
451 if let Err(_) = reader.seek(SeekFrom::Current(sub_chunk_size as i64 - 8)) {
452 return Err(ImError::ParserError(format));
453 }
454 }
455
456 return Ok(sub_chunk_size);
457}
458
459fn parse_tiff<BR, R>(reader: &mut R, preamble: &[u8]) -> ImResult<ImInfo>
460where BR: BinaryReader, R: Read, R: Seek {
461 let ifd_offset = BR::get_u32(array4!(preamble, 4));
462 map_err!(TIFF reader.seek(SeekFrom::Start(ifd_offset as u64)));
463
464 let ifd_entry_count = map_expr!(TIFF BR::read_u16(reader)) as u32;
465 let mut width: Option<u64> = None;
468 let mut height: Option<u64> = None;
469
470 for index in 0..ifd_entry_count {
471 let entry_offset = ifd_offset + 2 + index * 12;
473 map_err!(TIFF reader.seek(SeekFrom::Start(entry_offset as u64)));
474 let tag = map_expr!(TIFF BR::read_u16(reader));
475
476 if tag == 256 || tag == 257 {
479 let ftype = map_expr!(TIFF BR::read_u16(reader));
482 map_err!(TIFF reader.seek(SeekFrom::Start(entry_offset as u64 + 8)));
483 let value: u64 = match ftype {
484 1 => map_expr!(TIFF BR::read_u8(reader)).into(),
485 2 => map_expr!(TIFF BR::read_uchar(reader)).into(),
486 3 => map_expr!(TIFF BR::read_u16(reader)).into(),
487 4 => map_expr!(TIFF BR::read_u32(reader)).into(),
488 5 => map_expr!(TIFF BR::read_uratio(reader)).value::<u64>(),
489 6 => map_expr!(TIFF BR::read_i8(reader)).max(0) as u64,
490 7 => map_expr!(TIFF BR::read_ichar(reader)).max(0) as u64,
491 8 => map_expr!(TIFF BR::read_i16(reader)).max(0) as u64,
492 9 => map_expr!(TIFF BR::read_i32(reader)).max(0) as u64,
493 10 => map_expr!(TIFF BR::read_iratio(reader)).value::<i64>().max(0) as u64,
494 11 => map_expr!(TIFF BR::read_f32(reader)) as u64,
495 12 => map_expr!(TIFF BR::read_f64(reader)) as u64,
496 _ => return Err(ImError::ParserError(ImFormat::TIFF))
497 };
498
499 if tag == 256 {
500 if let Some(height) = height {
501 return Ok(ImInfo {
502 format: ImFormat::TIFF,
503 width: value,
504 height,
505 });
506 }
507 width = Some(value);
508 } else {
509 if let Some(width) = width {
510 return Ok(ImInfo {
511 format: ImFormat::TIFF,
512 width,
513 height: value,
514 });
515 }
516 height = Some(value);
517 }
518 }
519 }
520
521 return Err(ImError::ParserError(ImFormat::TIFF));
522}
523
524#[inline]
525fn is_tga<R>(file: &mut R) -> std::io::Result<bool>
526where R: Read, R: Seek {
527 file.seek(SeekFrom::End(-18))?;
528 let mut buf = [0u8; 18];
529 file.read_exact(&mut buf)?;
530 return Ok(&buf == b"TRUEVISION-XFILE.\0");
531}
532
533pub trait Imsz {
535 fn imsz(self) -> ImResult<ImInfo>;
536}
537
538impl Imsz for &str {
539 #[inline]
540 fn imsz(self) -> ImResult<ImInfo> {
541 return imsz_from_path(self);
542 }
543}
544
545impl Imsz for &String {
546 #[inline]
547 fn imsz(self) -> ImResult<ImInfo> {
548 return imsz_from_path(self.as_str());
549 }
550}
551
552impl Imsz for String {
553 #[inline]
554 fn imsz(self) -> ImResult<ImInfo> {
555 return imsz_from_path(self.as_str());
556 }
557}
558
559impl Imsz for &std::ffi::OsStr {
560 #[inline]
561 fn imsz(self) -> ImResult<ImInfo> {
562 return imsz_from_path(self);
563 }
564}
565
566impl Imsz for &std::ffi::OsString {
567 #[inline]
568 fn imsz(self) -> ImResult<ImInfo> {
569 return imsz_from_path(self.as_os_str());
570 }
571}
572
573impl Imsz for std::ffi::OsString {
574 #[inline]
575 fn imsz(self) -> ImResult<ImInfo> {
576 return imsz_from_path(self.as_os_str());
577 }
578}
579
580impl Imsz for &std::path::Path {
581 #[inline]
582 fn imsz(self) -> ImResult<ImInfo> {
583 return imsz_from_path(self);
584 }
585}
586
587impl Imsz for &std::path::PathBuf {
588 #[inline]
589 fn imsz(self) -> ImResult<ImInfo> {
590 return imsz_from_path(self.as_path());
591 }
592}
593
594impl Imsz for std::path::PathBuf {
595 #[inline]
596 fn imsz(self) -> ImResult<ImInfo> {
597 return imsz_from_path(self.as_path());
598 }
599}
600
601impl Imsz for &[u8] {
602 #[inline]
603 fn imsz(self) -> ImResult<ImInfo> {
604 return imsz_from_reader(&mut std::io::Cursor::new(self));
605 }
606}
607
608impl<const LEN: usize> Imsz for [u8; LEN] {
609 #[inline]
610 fn imsz(self) -> ImResult<ImInfo> {
611 return imsz_from_reader(&mut std::io::Cursor::new(&self[..]));
612 }
613}
614
615impl<const LEN: usize> Imsz for &[u8; LEN] {
616 #[inline]
617 fn imsz(self) -> ImResult<ImInfo> {
618 return imsz_from_reader(&mut std::io::Cursor::new(&self[..]));
619 }
620}
621
622impl Imsz for &mut std::fs::File {
623 #[inline]
624 fn imsz(self) -> ImResult<ImInfo> {
625 return imsz_from_reader(&mut BufReader::new(self));
626 }
627}
628
629impl Imsz for std::fs::File {
630 #[inline]
631 fn imsz(mut self) -> ImResult<ImInfo> {
632 return imsz_from_reader(&mut BufReader::new(&mut self));
633 }
634}
635
636impl Imsz for std::io::Stdin {
637 #[inline]
638 fn imsz(self) -> ImResult<ImInfo> {
639 return (&self).imsz();
640 }
641}
642
643#[cfg(any(target_family="unix", target_family="windows"))]
644impl Imsz for &std::io::Stdin {
645 #[inline]
647 fn imsz(self) -> ImResult<ImInfo> {
648 let lock = self.lock();
649
650 #[cfg(target_family="unix")]
651 let mut seekable_stdin = unsafe {
652 use std::os::unix::io::{AsRawFd, FromRawFd};
653 std::fs::File::from_raw_fd(lock.as_raw_fd())
654 };
655
656 #[cfg(target_family="windows")]
657 let mut seekable_stdin = unsafe {
658 use std::os::windows::io::{AsRawHandle, FromRawHandle};
659 std::fs::File::from_raw_handle(lock.as_raw_handle())
660 };
661
662 let result = imsz_from_reader(&mut BufReader::new(&mut seekable_stdin));
663
664 drop(lock);
666
667 return result;
668 }
669}
670
671impl Imsz for &mut std::io::Cursor<&[u8]> {
672 #[inline]
673 fn imsz(self) -> ImResult<ImInfo> {
674 return imsz_from_reader(self);
675 }
676}
677
678impl Imsz for std::io::Cursor<&[u8]> {
679 #[inline]
680 fn imsz(mut self) -> ImResult<ImInfo> {
681 return imsz_from_reader(&mut self);
682 }
683}
684
685impl<const LEN: usize> Imsz for std::io::Cursor<&[u8; LEN]> {
686 #[inline]
687 fn imsz(mut self) -> ImResult<ImInfo> {
688 return imsz_from_reader(&mut self);
689 }
690}
691
692impl<const LEN: usize> Imsz for std::io::Cursor<[u8; LEN]> {
693 #[inline]
694 fn imsz(mut self) -> ImResult<ImInfo> {
695 return imsz_from_reader(&mut self);
696 }
697}
698
699impl<R> Imsz for &mut std::io::BufReader<R> where R: Read, R: Seek {
700 #[inline]
701 fn imsz(self) -> ImResult<ImInfo> {
702 return imsz_from_reader(self);
703 }
704}
705
706impl<R> Imsz for std::io::BufReader<R> where R: Read, R: Seek {
707 #[inline]
708 fn imsz(mut self) -> ImResult<ImInfo> {
709 return imsz_from_reader(&mut self);
710 }
711}
712
713#[inline]
717pub fn imsz(input: impl Imsz) -> ImResult<ImInfo> {
718 return input.imsz();
719}
720
721#[inline]
723pub fn imsz_from_path(path: impl AsRef<std::path::Path>) -> ImResult<ImInfo> {
724 let mut reader = BufReader::new(File::open(path)?);
725 return imsz_from_reader(&mut reader);
726}
727
728pub fn imsz_from_reader<R>(file: &mut R) -> ImResult<ImInfo>
733where R: Read, R: Seek {
734 let mut preamble = [0u8; 30];
735
736 let size = file.read(&mut preamble)?;
737
738 if size >= 6 && (&preamble[..6] == b"GIF87a" || &preamble[..6] == b"GIF89a") {
739 if size < 10 {
741 return Err(ImError::ParserError(ImFormat::GIF));
742 }
743 let w = u16::from_le_bytes(array2!(preamble, 6));
744 let h = u16::from_le_bytes(array2!(preamble, 8));
745
746 return Ok(ImInfo {
747 format: ImFormat::GIF,
748 width: w as u64,
749 height: h as u64,
750 });
751 } else if size >= 8 && preamble.starts_with(b"\x89PNG\r\n\x1a\n") {
752 if size < 24 {
754 return Err(ImError::ParserError(ImFormat::PNG));
755 }
756
757 let chunk_size = u32::from_be_bytes(array4!(preamble, 8));
758 if chunk_size < 8 || &preamble[12..16] != b"IHDR" {
759 return Err(ImError::ParserError(ImFormat::PNG));
760 }
761
762 let w = u32::from_be_bytes(array4!(preamble, 16));
763 let h = u32::from_be_bytes(array4!(preamble, 20));
764
765 return Ok(ImInfo {
766 format: ImFormat::PNG,
767 width: w as u64,
768 height: h as u64,
769 });
770 } else if size >= 10 && preamble.starts_with(b"BM") && &preamble[6..10] == b"\0\0\0\0" {
771 let file_size = u32::from_le_bytes(array4!(preamble, 2));
773 let min_size = (file_size as usize).min(size);
774 if min_size < 22 {
775 return Err(ImError::ParserError(ImFormat::BMP));
776 }
777
778 let header_size = u32::from_le_bytes(array4!(preamble, 14));
779 if header_size == 12 {
780 let w = i16::from_le_bytes(array2!(preamble, 18));
782 let h = i16::from_le_bytes(array2!(preamble, 20));
783
784 return Ok(ImInfo {
785 format: ImFormat::BMP,
786 width: w as u64,
787 height: h.abs() as u64,
789 });
790 } else {
791 if min_size < 26 || header_size <= 12 {
792 return Err(ImError::ParserError(ImFormat::BMP));
793 }
794 let w = i32::from_le_bytes(array4!(preamble, 18));
795 let h = i32::from_le_bytes(array4!(preamble, 22));
796
797 return Ok(ImInfo {
798 format: ImFormat::BMP,
799 width: w as u64,
800 height: h.abs() as u64
802 });
803 }
804 } else if size >= 3 && &preamble[..2] == b"\xff\xd8" {
805 map_err!(JPEG file.seek(SeekFrom::Start(3)));
807 let mut buf1: [u8; 1] = [ preamble[2] ];
808 let mut buf2: [u8; 2] = [0; 2];
809 let mut buf4: [u8; 4] = [0; 4];
810 while buf1[0] != b'\xda' && buf1[0] != 0 {
811 while buf1[0] != b'\xff' {
812 map_err!(JPEG file.read_exact(&mut buf1));
813 }
814 while buf1[0] == b'\xff' {
815 map_err!(JPEG file.read_exact(&mut buf1));
816 }
817 if buf1[0] >= 0xc0 && buf1[0] <= 0xc3 {
818 map_err!(JPEG file.seek(SeekFrom::Current(3)));
819 map_err!(JPEG file.read_exact(&mut buf4));
820 let h = u16::from_be_bytes(array2!(buf4, 0));
821 let w = u16::from_be_bytes(array2!(buf4, 2));
822
823 return Ok(ImInfo {
824 format: ImFormat::JPEG,
825 width: w as u64,
826 height: h as u64,
827 });
828 }
829 map_err!(JPEG file.read_exact(&mut buf2));
830 let b = u16::from_be_bytes(buf2);
831 let offset = (b - 2) as i64;
832 map_err!(JPEG file.seek(SeekFrom::Current(offset)));
833 map_err!(JPEG file.read_exact(&mut buf1));
834 }
835 return Err(ImError::ParserError(ImFormat::JPEG));
836 } else if size >= 30 && preamble.starts_with(b"RIFF") && &preamble[8..12] == b"WEBP" {
837 let hdr = &preamble[12..16];
839 if hdr == b"VP8L" {
840 let b0 = preamble[21];
841 let b1 = preamble[22];
842 let b2 = preamble[23];
843 let b3 = preamble[24];
844
845 let w = 1u32 + ((((b1 & 0x3F) as u32) << 8) | b0 as u32);
846 let h = 1u32 + ((((b3 & 0xF) as u32) << 10) | ((b2 as u32) << 2) | ((b1 & 0xC0) as u32 >> 6));
847
848 return Ok(ImInfo {
849 format: ImFormat::WEBP,
850 width: w as u64,
851 height: h as u64,
852 });
853 } else if hdr == b"VP8 " {
854 let b0 = preamble[23];
855 let b1 = preamble[24];
856 let b2 = preamble[25];
857 if b0 != 0x9d || b1 != 0x01 || b2 != 0x2a {
858 return Err(ImError::ParserError(ImFormat::WEBP));
859 }
860 let w = u16::from_le_bytes(array2!(preamble, 26));
861 let h = u16::from_le_bytes(array2!(preamble, 28));
862 return Ok(ImInfo {
863 format: ImFormat::WEBP,
864 width: w as u64 & 0x3ffff,
865 height: h as u64 & 0x3ffff,
866 });
867 } else if hdr == b"VP8X" {
868 let w1 = preamble[24] as u32;
869 let w2 = preamble[25] as u32;
870 let w3 = preamble[26] as u32;
871 let h1 = preamble[27] as u32;
872 let h2 = preamble[28] as u32;
873 let h3 = preamble[29] as u32;
874
875 let width = (w1 | w2 << 8 | w3 << 16) + 1;
876 let height = (h1 | h2 << 8 | h3 << 16) + 1;
877
878 return Ok(ImInfo {
879 format: ImFormat::WEBP,
880 width: width as u64,
881 height: height as u64,
882 });
883 }
884 return Err(ImError::ParserError(ImFormat::WEBP));
885 } else if size >= 12 && (&preamble[4..12] == b"ftypavif" || &preamble[4..12] == b"ftypheic") {
886 let format = if &preamble[8..12] == b"avif" {
888 ImFormat::AVIF
889 } else {
890 ImFormat::HEIF
891 };
892
893 let ftype_size = u32::from_be_bytes(array4!(preamble, 0));
894 if ftype_size < 12 {
895 return Err(ImError::ParserError(format));
896 }
897 map_err!(format, file.seek(SeekFrom::Start(ftype_size as u64)));
898
899 let chunk_size = find_riff_chunk(file, b"meta", u64::MAX, format)?;
901 if chunk_size < 12 {
902 return Err(ImError::ParserError(format));
903 }
904 map_err!(format, file.seek(SeekFrom::Current(4)));
905 let chunk_size = find_riff_chunk(file, b"iprp", chunk_size - 12, format)?;
906 let chunk_size = find_riff_chunk(file, b"ipco", chunk_size - 8, format)?;
907 let chunk_size = find_riff_chunk(file, b"ispe", chunk_size - 8, format)?;
908
909 if chunk_size < 12 {
910 return Err(ImError::ParserError(format));
911 }
912
913 let mut buf = [0u8; 12];
914 map_err!(format, file.read_exact(&mut buf));
915
916 let w = u32::from_be_bytes(array4!(buf, 4));
917 let h = u32::from_be_bytes(array4!(buf, 8));
918
919 return Ok(ImInfo {
920 format,
921 width: w as u64,
922 height: h as u64,
923 });
924 } else if size >= 24 && preamble.starts_with(b"\0\0\0\x0CjP ") && &preamble[16..24] == b"ftypjp2 " {
925 let chunk_size = u32::from_be_bytes(array4!(preamble, 12));
927 map_err!(JP2K file.seek(SeekFrom::Start(12 + chunk_size as u64)));
928 let chunk_size = find_riff_chunk(file, b"jp2h", u64::MAX, ImFormat::JP2K)?;
929 let chunk_size = find_riff_chunk(file, b"ihdr", chunk_size, ImFormat::JP2K)?;
930
931 if chunk_size < 8 {
932 return Err(ImError::ParserError(ImFormat::JP2K));
933 }
934
935 let mut buf = [0u8; 8];
936 map_err!(JP2K file.read_exact(&mut buf));
937
938 let h = u32::from_be_bytes(array4!(buf, 0));
939 let w = u32::from_be_bytes(array4!(buf, 4));
940
941 return Ok(ImInfo {
942 format: ImFormat::JP2K,
943 width: w as u64,
944 height: h as u64,
945 });
946 } else if size >= 8 && (preamble.starts_with(b"II*\0") || preamble.starts_with(b"MM\0*")) {
947 if preamble.starts_with(b"MM") {
949 return parse_tiff::<BigEndianReader, R>(file, &preamble[..size]);
951 } else {
952 return parse_tiff::<LittleEndianReader, R>(file, &preamble[..size]);
954 }
955 } else if size >= 14 && preamble.starts_with(b"qoif") {
956 let w = u32::from_be_bytes(array4!(preamble, 4));
958 let h = u32::from_be_bytes(array4!(preamble, 8));
959
960 return Ok(ImInfo {
961 format: ImFormat::QOI,
962 width: w as u64,
963 height: h as u64,
964 });
965 } else if size >= 22 && preamble.starts_with(b"8BPS\0\x01\0\0\0\0\0\0") {
966 let h = u32::from_be_bytes(array4!(preamble, 14));
968 let w = u32::from_be_bytes(array4!(preamble, 18));
969
970 return Ok(ImInfo {
971 format: ImFormat::PSD,
972 width: w as u64,
973 height: h as u64,
974 });
975 } else if size >= 22 && preamble.starts_with(b"gimp xcf ") && preamble[13] == 0 {
976 let w = u32::from_be_bytes(array4!(preamble, 14));
978 let h = u32::from_be_bytes(array4!(preamble, 18));
979
980 return Ok(ImInfo {
981 format: ImFormat::XCF,
982 width: w as u64,
983 height: h as u64,
984 });
985 } else if size >= 6 && preamble.starts_with(b"\0\0\x01\0") {
986 let count = u16::from_le_bytes(array2!(preamble, 4));
988 map_err!(ICO file.seek(SeekFrom::Start(6)));
989
990 let mut buf = [0u8; 16];
991 let mut width: u32 = 0;
992 let mut height: u32 = 0;
993 for _ in 0..count {
994 map_err!(ICO file.read_exact(&mut buf));
995 let w = buf[0] as u32;
996 let h = buf[1] as u32;
997 if w >= width && h >= height {
998 width = w;
999 height = h;
1000 }
1001 }
1002
1003 return Ok(ImInfo {
1004 format: ImFormat::ICO,
1005 width: width as u64,
1006 height: height as u64,
1007 });
1008 } else if size > 8 && preamble.starts_with(b"\x76\x2f\x31\x01") && (preamble[4] == 0x01 || preamble[4] == 0x02) {
1009 map_err!(OpenEXR file.seek(SeekFrom::Start(8)));
1012
1013 let mut name_buf = Vec::new();
1014 let mut type_buf = Vec::new();
1015 let mut buf1 = [0u8];
1016 let mut buf4 = [0u8; 4];
1017
1018 loop {
1019 name_buf.clear();
1020 loop {
1021 map_err!(OpenEXR file.read_exact(&mut buf1));
1022 let byte = buf1[0];
1023 if byte == 0 {
1024 break;
1025 }
1026 name_buf.push(byte);
1027 }
1028
1029 if name_buf.is_empty() {
1030 break;
1031 }
1032
1033 type_buf.clear();
1034 loop {
1035 map_err!(OpenEXR file.read_exact(&mut buf1));
1036 let byte = buf1[0];
1037 if byte == 0 {
1038 break;
1039 }
1040 type_buf.push(byte);
1041 }
1042
1043 map_err!(OpenEXR file.read_exact(&mut buf4));
1044 let size = u32::from_le_bytes(buf4);
1045
1046 if &name_buf == b"displayWindow" {
1047 if &type_buf != b"box2i" || size != 16 {
1048 return Err(ImError::ParserError(ImFormat::OpenEXR));
1049 }
1050
1051 let mut box_buf = [0u8; 16];
1052 map_err!(OpenEXR file.read_exact(&mut box_buf));
1053
1054 let x1 = i32::from_le_bytes(array4!(box_buf, 0)) as i64;
1055 let y1 = i32::from_le_bytes(array4!(box_buf, 4)) as i64;
1056 let x2 = i32::from_le_bytes(array4!(box_buf, 8)) as i64;
1057 let y2 = i32::from_le_bytes(array4!(box_buf, 12)) as i64;
1058
1059 let width = x2 - x1 + 1;
1060 let height = y2 - y1 + 1;
1061
1062 if width <= 0 || height <= 0 {
1063 return Err(ImError::ParserError(ImFormat::OpenEXR));
1064 }
1065
1066 return Ok(ImInfo {
1067 format: ImFormat::OpenEXR,
1068 width: width as u64,
1069 height: height as u64,
1070 });
1071 } else {
1072 map_err!(OpenEXR file.seek(SeekFrom::Current(size as i64)));
1073 }
1074 }
1075
1076 return Err(ImError::ParserError(ImFormat::OpenEXR));
1077 } else if size >= 30 && preamble[0] == 0x0A && preamble[1] < 6 && (preamble[3] == 1 || preamble[3] == 2 || preamble[3] == 4 || preamble[3] == 8) {
1078 let x1 = u16::from_le_bytes(array2!(preamble, 4)) as i64;
1080 let y1 = u16::from_le_bytes(array2!(preamble, 6)) as i64;
1081 let x2 = u16::from_le_bytes(array2!(preamble, 8)) as i64;
1082 let y2 = u16::from_le_bytes(array2!(preamble, 10)) as i64;
1083
1084 let width = x2 - x1 + 1;
1085 let height = y2 - y1 + 1;
1086
1087 if width <= 0 || height <= 0 {
1088 return Err(ImError::ParserError(ImFormat::PCX));
1089 }
1090
1091 return Ok(ImInfo {
1092 format: ImFormat::PCX,
1093 width: width as u64,
1094 height: height as u64,
1095 });
1096 } else if size >= 30 && preamble.starts_with(b"DDS \x7C\0\0\0") && (u32::from_le_bytes(array4!(preamble, 8)) & 0x1007) != 0 {
1097 let h = u32::from_le_bytes(array4!(preamble, 12));
1102 let w = u32::from_le_bytes(array4!(preamble, 16));
1103
1104 return Ok(ImInfo {
1105 format: ImFormat::DDS,
1106 width: w as u64,
1107 height: h as u64,
1108 });
1109 } else if size >= 14 && preamble.starts_with(b"\x28\0\0\0") && &preamble[12..14] == b"\x01\0" && preamble[15] == 0 {
1110 let w = i32::from_le_bytes(array4!(preamble, 4));
1112 let h = i32::from_le_bytes(array4!(preamble, 8));
1113
1114 return Ok(ImInfo {
1115 format: ImFormat::DIB,
1116 width: w as u64,
1117 height: h.abs() as u64,
1118 });
1119 } else if size >= 20 && preamble.starts_with(b"VTF\0") {
1120 let header_size = u32::from_le_bytes(array4!(preamble, 12));
1122 let w = u16::from_le_bytes(array2!(preamble, 16));
1123 let h = u16::from_le_bytes(array2!(preamble, 18));
1124 if header_size < 10 {
1125 return Err(ImError::ParserError(ImFormat::VTF));
1126 }
1127
1128 return Ok(ImInfo {
1129 format: ImFormat::VTF,
1130 width: w as u64,
1131 height: h as u64,
1132 });
1133 } else if size >= 24 && preamble.starts_with(b"FORM") && matches!(&preamble[8..12], b"ILBM"|b"PBM ") && &preamble[12..16] == b"BMHD" {
1134 let chunk_len = u32::from_be_bytes(array4!(preamble, 4));
1135 if chunk_len < 32 {
1136 return Err(ImError::ParserError(ImFormat::ILBM));
1138 }
1139
1140 let bmhd_chunk_len = u32::from_be_bytes(array4!(preamble, 16));
1141 if bmhd_chunk_len < 20 {
1142 return Err(ImError::ParserError(ImFormat::ILBM));
1144 }
1145
1146 let w = u16::from_be_bytes(array2!(preamble, 20));
1147 let h = u16::from_be_bytes(array2!(preamble, 22));
1148
1149 return Ok(ImInfo {
1150 format: ImFormat::ILBM,
1151 width: w as u64,
1152 height: h as u64,
1153 })
1154 } else if size >= 30 && preamble[1] < 2 && preamble[2] < 12 && is_tga(file)? {
1155 let w = u16::from_le_bytes(array2!(preamble, 12));
1157 let h = u16::from_le_bytes(array2!(preamble, 14));
1158
1159 return Ok(ImInfo {
1160 format: ImFormat::TGA,
1161 width: w as u64,
1162 height: h as u64,
1163 });
1164 }
1165 return Err(ImError::UnknownFormat);
1166}