1 | pub mod aesprite; |
2 | pub mod avif; |
3 | pub mod bmp; |
4 | pub mod dds; |
5 | pub mod exr; |
6 | pub mod farbfeld; |
7 | pub mod gif; |
8 | pub mod hdr; |
9 | pub mod heif; |
10 | pub mod ico; |
11 | pub mod jpeg; |
12 | pub mod jxl; |
13 | pub mod ktx2; |
14 | pub mod png; |
15 | pub mod pnm; |
16 | pub mod psd; |
17 | pub mod qoi; |
18 | pub mod tga; |
19 | pub mod tiff; |
20 | pub mod vtf; |
21 | pub mod webp; |
22 | |
23 | use crate::{ImageError, ImageResult, ImageType}; |
24 | use std::io::{BufRead, Seek}; |
25 | |
26 | pub fn image_type<R: BufRead + Seek>(reader: &mut R) -> ImageResult<ImageType> { |
27 | let mut header = [0; 12]; |
28 | reader.read_exact(&mut header)?; |
29 | |
30 | // Currently there are no formats where 1 byte is enough to determine format |
31 | if header.len() < 2 { |
32 | return Err( |
33 | std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "Not enough data" ).into(), |
34 | ); |
35 | } |
36 | |
37 | // This is vaguely organized in what I assume are the most commonly used formats. |
38 | // I don't know how much this matters for actual execution time. |
39 | if jpeg::matches(&header) { |
40 | return Ok(ImageType::Jpeg); |
41 | } |
42 | |
43 | if png::matches(&header) { |
44 | return Ok(ImageType::Png); |
45 | } |
46 | |
47 | if gif::matches(&header) { |
48 | return Ok(ImageType::Gif); |
49 | } |
50 | |
51 | if tiff::matches(&header) { |
52 | return Ok(ImageType::Tiff); |
53 | } |
54 | |
55 | if webp::matches(&header) { |
56 | return Ok(ImageType::Webp); |
57 | } |
58 | |
59 | if heif::matches(&header) { |
60 | return Ok(ImageType::Heif); |
61 | } |
62 | |
63 | if avif::matches(&header) { |
64 | return Ok(ImageType::Avif); |
65 | } |
66 | |
67 | if jxl::matches(&header) { |
68 | return Ok(ImageType::Jxl); |
69 | } |
70 | |
71 | if bmp::matches(&header) { |
72 | return Ok(ImageType::Bmp); |
73 | } |
74 | |
75 | if psd::matches(&header) { |
76 | return Ok(ImageType::Psd); |
77 | } |
78 | |
79 | if ico::matches(&header) { |
80 | return Ok(ImageType::Ico); |
81 | } |
82 | |
83 | if aesprite::matches(&header) { |
84 | return Ok(ImageType::Aseprite); |
85 | } |
86 | |
87 | if exr::matches(&header) { |
88 | return Ok(ImageType::Exr); |
89 | } |
90 | |
91 | if hdr::matches(&header) { |
92 | return Ok(ImageType::Hdr); |
93 | } |
94 | |
95 | if dds::matches(&header) { |
96 | return Ok(ImageType::Dds); |
97 | } |
98 | |
99 | if ktx2::matches(&header) { |
100 | return Ok(ImageType::Ktx2); |
101 | } |
102 | |
103 | if qoi::matches(&header) { |
104 | return Ok(ImageType::Qoi); |
105 | } |
106 | |
107 | if farbfeld::matches(&header) { |
108 | return Ok(ImageType::Farbfeld); |
109 | } |
110 | |
111 | if pnm::matches(&header) { |
112 | return Ok(ImageType::Pnm); |
113 | } |
114 | |
115 | if vtf::matches(&header) { |
116 | return Ok(ImageType::Vtf); |
117 | } |
118 | |
119 | // Keep TGA last because it has the highest probability of false positives |
120 | if tga::matches(&header, reader) { |
121 | return Ok(ImageType::Tga); |
122 | } |
123 | |
124 | Err(ImageError::NotSupported) |
125 | } |
126 | |