imagesize/
lib.rs

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("Could not decode image"),
30            CorruptedImage => f.write_str("Hit end of file before finding size"),
31            IoError(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(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::open(path)?;
163    let reader = BufReader::new(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::new(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}