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 container;
8mod formats;
9mod util;
10
11pub use container::heif::Compression;
12use {
13    container::heif::{self},
14    formats::*,
15};
16
17/// An Error type used in failure cases.
18#[derive(Debug)]
19pub 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
28impl Error for ImageError {}
29
30impl 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("Could not decode image"),
35            CorruptedImage => f.write_str("Hit end of file before finding size"),
36            IoError(error) => error.fmt(f),
37        }
38    }
39}
40
41impl From<std::io::Error> for ImageError {
42    fn from(err: std::io::Error) -> ImageError {
43        ImageError::IoError(err)
44    }
45}
46
47pub 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)]
52pub 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)]
102pub 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
109impl 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
115impl 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.
129pub fn image_type(header: &[u8]) -> ImageResult<ImageType> {
130    formats::image_type(&mut Cursor::new(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
167pub fn size<P: AsRef<Path>>(path: P) -> ImageResult<ImageSize> {
168    let file = File::open(path)?;
169    let reader = BufReader::new(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
202pub fn blob_size(data: &[u8]) -> ImageResult<ImageSize> {
203    let reader = Cursor::new(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
245pub 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
254fn 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}