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