1 | use std::error::Error; |
2 | use std::fmt; |
3 | use std::fs::File; |
4 | use std::io::{BufRead, BufReader, Cursor, Seek}; |
5 | use std::path::Path; |
6 | |
7 | mod container; |
8 | mod formats; |
9 | mod util; |
10 | |
11 | pub use container::heif::Compression; |
12 | use { |
13 | container::heif::{self}, |
14 | formats::*, |
15 | }; |
16 | |
17 | /// An Error type used in failure cases. |
18 | #[derive (Debug)] |
19 | pub enum ImageError { |
20 | /// Used when the given data is not a supported format. |
21 | NotSupported, |
22 | /// Used when the image has an invalid format. |
23 | CorruptedImage, |
24 | /// Used when an IoError occurs when trying to read the given data. |
25 | IoError(std::io::Error), |
26 | } |
27 | |
28 | impl Error for ImageError {} |
29 | |
30 | impl fmt::Display for ImageError { |
31 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
32 | use self::ImageError::*; |
33 | match self { |
34 | NotSupported => f.write_str(data:"Could not decode image" ), |
35 | CorruptedImage => f.write_str(data:"Hit end of file before finding size" ), |
36 | IoError(error: &Error) => error.fmt(f), |
37 | } |
38 | } |
39 | } |
40 | |
41 | impl From<std::io::Error> for ImageError { |
42 | fn from(err: std::io::Error) -> ImageError { |
43 | ImageError::IoError(err) |
44 | } |
45 | } |
46 | |
47 | pub type ImageResult<T> = Result<T, ImageError>; |
48 | |
49 | /// Types of image formats that this crate can identify. |
50 | #[non_exhaustive ] |
51 | #[derive (Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] |
52 | pub enum ImageType { |
53 | /// Animated sprite image format |
54 | /// <https://github.com/aseprite/aseprite> |
55 | Aseprite, |
56 | /// Standard Bitmap |
57 | Bmp, |
58 | /// DirectDraw Surface |
59 | Dds, |
60 | /// OpenEXR |
61 | Exr, |
62 | /// Farbfeld |
63 | /// <https://tools.suckless.org/farbfeld/> |
64 | Farbfeld, |
65 | /// Standard GIF |
66 | Gif, |
67 | /// Radiance HDR |
68 | Hdr, |
69 | /// Image Container Format |
70 | Heif(Compression), |
71 | /// Icon file |
72 | Ico, |
73 | /// Interleaved Bitmap |
74 | Ilbm, |
75 | /// Standard JPEG |
76 | Jpeg, |
77 | /// JPEG XL |
78 | Jxl, |
79 | /// Khronos Texture Container |
80 | Ktx2, |
81 | /// Standard PNG |
82 | Png, |
83 | /// Portable Any Map |
84 | Pnm, |
85 | /// Photoshop Document |
86 | Psd, |
87 | /// Quite OK Image Format |
88 | /// <https://qoiformat.org/> |
89 | Qoi, |
90 | /// Truevision Graphics Adapter |
91 | Tga, |
92 | /// Standard TIFF |
93 | Tiff, |
94 | /// Valve Texture Format |
95 | Vtf, |
96 | /// Standard Webp |
97 | Webp, |
98 | } |
99 | |
100 | /// Holds the size information of an image. |
101 | #[derive (Clone, Copy, Debug, PartialEq, Eq, Hash)] |
102 | pub struct ImageSize { |
103 | /// Width of an image in pixels. |
104 | pub width: usize, |
105 | /// Height of an image in pixels. |
106 | pub height: usize, |
107 | } |
108 | |
109 | impl Ord for ImageSize { |
110 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { |
111 | (self.width * self.height).cmp(&(other.width * other.height)) |
112 | } |
113 | } |
114 | |
115 | impl PartialOrd for ImageSize { |
116 | fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { |
117 | Some(self.cmp(other)) |
118 | } |
119 | } |
120 | |
121 | /// Get the image type from a header |
122 | /// |
123 | /// # Arguments |
124 | /// * `header` - The header of the file. |
125 | /// |
126 | /// # Remarks |
127 | /// |
128 | /// This will check the header to determine what image type the data is. |
129 | pub fn image_type(header: &[u8]) -> ImageResult<ImageType> { |
130 | formats::image_type(&mut Cursor::new(inner:header)) |
131 | } |
132 | |
133 | /// Get the image size from a local file |
134 | /// |
135 | /// # Arguments |
136 | /// * `path` - A local path to the file to parse. |
137 | /// |
138 | /// # Remarks |
139 | /// |
140 | /// Will try to read as little of the file as possible in order to get the |
141 | /// proper size information. |
142 | /// |
143 | /// # Error |
144 | /// |
145 | /// This method will return an [`ImageError`] under the following conditions: |
146 | /// |
147 | /// * The header isn't recognized as a supported image format |
148 | /// * The data isn't long enough to find the size for the given format |
149 | /// |
150 | /// The minimum data required is 12 bytes. Anything shorter will return [`ImageError::IoError`]. |
151 | /// |
152 | /// # Examples |
153 | /// |
154 | /// ``` |
155 | /// use imagesize::size; |
156 | /// |
157 | /// match size("test/test.webp" ) { |
158 | /// Ok(dim) => { |
159 | /// assert_eq!(dim.width, 716); |
160 | /// assert_eq!(dim.height, 716); |
161 | /// } |
162 | /// Err(why) => println!("Error getting size: {:?}" , why) |
163 | /// } |
164 | /// ``` |
165 | /// |
166 | /// [`ImageError`]: enum.ImageError.html |
167 | pub fn size<P: AsRef<Path>>(path: P) -> ImageResult<ImageSize> { |
168 | let file: File = File::open(path)?; |
169 | let reader: BufReader = BufReader::new(inner:file); |
170 | reader_size(reader) |
171 | } |
172 | |
173 | /// Get the image size from a block of raw data. |
174 | /// |
175 | /// # Arguments |
176 | /// * `data` - A Vec containing the data to parse for image size. |
177 | /// |
178 | /// # Error |
179 | /// |
180 | /// This method will return an [`ImageError`] under the following conditions: |
181 | /// |
182 | /// * The header isn't recognized as a supported image format |
183 | /// * The data isn't long enough to find the size for the given format |
184 | /// |
185 | /// The minimum data required is 12 bytes. Anything shorter will return [`ImageError::IoError`]. |
186 | /// |
187 | /// # Examples |
188 | /// |
189 | /// ``` |
190 | /// use imagesize::blob_size; |
191 | /// |
192 | /// // First few bytes of arbitrary data. |
193 | /// let data = vec![0x89, 0x89, 0x89, 0x89, 0x0D, 0x0A, 0x1A, 0x0A, |
194 | /// 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, |
195 | /// 0x00, 0x00, 0x00, 0x7B, 0x01, 0x00, 0x01, 0x41, |
196 | /// 0x08, 0x06, 0x00, 0x00, 0x00, 0x9A, 0x38, 0xC4]; |
197 | /// |
198 | /// assert_eq!(blob_size(&data).is_err(), true); |
199 | /// ``` |
200 | /// |
201 | /// [`ImageError`]: enum.ImageError.html |
202 | pub fn blob_size(data: &[u8]) -> ImageResult<ImageSize> { |
203 | let reader: Cursor<&[u8]> = Cursor::new(inner:data); |
204 | reader_size(reader) |
205 | } |
206 | |
207 | /// Get the image size from a reader |
208 | /// |
209 | /// # Arguments |
210 | /// * `reader` - A reader for the data |
211 | /// |
212 | /// # Error |
213 | /// |
214 | /// This method will return an [`ImageError`] under the following conditions: |
215 | /// |
216 | /// * The header isn't recognized as a supported image format |
217 | /// * The data isn't long enough to find the size for the given format |
218 | /// |
219 | /// The minimum data required is 12 bytes. Anything shorter will return [`ImageError::IoError`]. |
220 | /// |
221 | /// # Examples |
222 | /// |
223 | /// ``` |
224 | /// use std::io::Cursor; |
225 | /// use imagesize::reader_size; |
226 | /// |
227 | /// // PNG Header with size 123x321 |
228 | /// let reader = Cursor::new([ |
229 | /// 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, |
230 | /// 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, |
231 | /// 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x01, 0x41, |
232 | /// 0x08, 0x06, 0x00, 0x00, 0x00, 0x9A, 0x38, 0xC4 |
233 | /// ]); |
234 | /// |
235 | /// match reader_size(reader) { |
236 | /// Ok(dim) => { |
237 | /// assert_eq!(dim.width, 123); |
238 | /// assert_eq!(dim.height, 321); |
239 | /// } |
240 | /// Err(why) => println!("Error getting reader size: {:?}" , why) |
241 | /// } |
242 | /// ``` |
243 | /// |
244 | /// [`ImageError`]: enum.ImageError.html |
245 | pub fn reader_size<R: BufRead + Seek>(mut reader: R) -> ImageResult<ImageSize> { |
246 | dispatch_header(&mut reader) |
247 | } |
248 | |
249 | /// Calls the correct image size method based on the image type |
250 | /// |
251 | /// # Arguments |
252 | /// * `reader` - A reader for the data |
253 | /// * `header` - The header of the file |
254 | fn dispatch_header<R: BufRead + Seek>(reader: &mut R) -> ImageResult<ImageSize> { |
255 | match formats::image_type(reader)? { |
256 | ImageType::Aseprite => aesprite::size(reader), |
257 | ImageType::Bmp => bmp::size(reader), |
258 | ImageType::Dds => dds::size(reader), |
259 | ImageType::Exr => exr::size(reader), |
260 | ImageType::Farbfeld => farbfeld::size(reader), |
261 | ImageType::Gif => gif::size(reader), |
262 | ImageType::Hdr => hdr::size(reader), |
263 | ImageType::Ico => ico::size(reader), |
264 | ImageType::Ilbm => ilbm::size(reader), |
265 | ImageType::Jpeg => jpeg::size(reader), |
266 | ImageType::Jxl => jxl::size(reader), |
267 | ImageType::Ktx2 => ktx2::size(reader), |
268 | ImageType::Png => png::size(reader), |
269 | ImageType::Pnm => pnm::size(reader), |
270 | ImageType::Psd => psd::size(reader), |
271 | ImageType::Qoi => qoi::size(reader), |
272 | ImageType::Tga => tga::size(reader), |
273 | ImageType::Tiff => tiff::size(reader), |
274 | ImageType::Vtf => vtf::size(reader), |
275 | ImageType::Webp => webp::size(reader), |
276 | |
277 | ImageType::Heif(..) => heif::size(reader), |
278 | } |
279 | } |
280 | |