Skip to main content

imagesize/
lib.rs

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