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 util; |
8 | |
9 | mod formats; |
10 | use formats::*; |
11 | |
12 | /// An Error type used in failure cases. |
13 | #[derive (Debug)] |
14 | pub 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 | |
23 | impl Error for ImageError {} |
24 | |
25 | impl 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 | |
36 | impl From<std::io::Error> for ImageError { |
37 | fn from(err: std::io::Error) -> ImageError { |
38 | ImageError::IoError(err) |
39 | } |
40 | } |
41 | |
42 | pub 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)] |
46 | pub 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)] |
96 | pub 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 | |
103 | impl 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 | |
109 | impl 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. |
123 | pub 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 |
161 | pub 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 |
196 | pub 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 |
239 | pub 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 |
248 | fn 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 | |