Skip to main content

ai_imagesize/
lib.rs

1#![no_std]
2#![allow(dead_code)]
3
4extern crate alloc;
5
6use core::fmt;
7use no_std_io::io::{BufRead, Cursor, Seek};
8
9#[cfg(feature = "std")]
10extern crate std;
11#[cfg(feature = "std")]
12use std::fs::File;
13#[cfg(feature = "std")]
14use std::path::Path;
15
16mod container;
17mod formats;
18mod util;
19
20pub use container::{
21    atc::AtcCompression, dds::DdsCompression, heif::Compression, pkm::PkmCompression,
22    pvrtc::PvrtcCompression,
23};
24
25/// Groups related compression algorithms regardless of their container format
26#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub enum CompressionFamily {
28    /// Block Compression family (BC1-7, also known as DXT1-5, ATI1-2)
29    BlockCompression,
30    /// Ericsson Texture Compression family (ETC1, ETC2 variants)
31    Etc,
32    /// Ericsson Alpha Compression (EAC R11, RG11)
33    Eac,
34    /// PowerVR Texture Compression
35    Pvrtc,
36    /// Adaptive Scalable Texture Compression
37    Astc,
38    /// Adaptive Texture Compression (Qualcomm Adreno)
39    Atc,
40    /// Uncompressed formats
41    Uncompressed,
42}
43
44#[allow(unused_imports)]
45use {
46    container::heif::{self},
47    formats::*,
48};
49
50/// An Error type used in failure cases.
51#[derive(Debug)]
52pub enum ImageError {
53    /// Used when the given data is not a supported format.
54    NotSupported,
55    /// Used when the image has an invalid format.
56    CorruptedImage,
57    /// Used when an IoError occurs when trying to read the given data.
58    IoError(no_std_io::io::Error),
59}
60
61impl core::error::Error for ImageError {}
62
63impl fmt::Display for ImageError {
64    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65        use self::ImageError::*;
66        match self {
67            NotSupported => f.write_str("Could not decode image"),
68            CorruptedImage => f.write_str("Hit end of file before finding size"),
69            IoError(error) => error.fmt(f),
70        }
71    }
72}
73
74impl From<no_std_io::io::Error> for ImageError {
75    fn from(err: no_std_io::io::Error) -> ImageError {
76        ImageError::IoError(err)
77    }
78}
79
80pub type ImageResult<T> = Result<T, ImageError>;
81
82/// Types of image formats that this crate can identify.
83///
84/// Many container formats support multiple inner compression formats. For these formats,
85/// the enum contains the inner compression type to provide more detailed information:
86///
87/// - `Dds(DdsCompression)` - DirectDraw Surface with various BC compression formats
88/// - `Etc2(PkmCompression)` - ETC/PKM container with ETC1, ETC2, EAC variants
89/// - `Eac(PkmCompression)` - EAC formats (unified with ETC2 detection)
90/// - `Atc(AtcCompression)` - Adaptive Texture Compression variants
91/// - `Pvrtc(PvrtcCompression)` - PowerVR texture compression with 2bpp/4bpp variants
92///
93/// # Helper Methods
94///
95/// The `ImageType` provides several helper methods to query compression information
96/// across different container formats:
97///
98/// - [`compression_family()`](ImageType::compression_family) - Groups related compression algorithms
99/// - [`is_block_compressed()`](ImageType::is_block_compressed) - Checks for BC/DXT compression
100/// - [`container_format()`](ImageType::container_format) - Returns container format name
101/// - [`is_multi_compression_container()`](ImageType::is_multi_compression_container) - Checks if container supports multiple compression types
102///
103/// # Examples
104///
105/// ## Basic Format Detection
106///
107/// ```rust
108/// use ai_imagesize::{image_type, ImageType, PkmCompression};
109///
110/// // Create a PKM header for ETC2 format
111/// let mut header = vec![b'P', b'K', b'M', b' ', b'2', b'0'];
112/// header.extend_from_slice(&0x0001u16.to_be_bytes()); // ETC2 RGB
113/// header.extend_from_slice(&[0x00, 0x40, 0x00, 0x40]); // Extended dimensions
114/// header.extend_from_slice(&[0x00, 0x40, 0x00, 0x40]); // Original dimensions
115///
116/// match image_type(&header).unwrap() {
117///     ImageType::Etc2(PkmCompression::Etc2) => println!("This is ETC2 RGB format"),
118///     ImageType::Etc2(compression) => println!("This is ETC2 format: {:?}", compression),
119///     other => println!("Other format: {:?}", other),
120/// }
121/// ```
122///
123/// ## Using Helper Methods for Cross-Container Queries
124///
125/// ```rust
126/// use ai_imagesize::{ImageType, CompressionFamily, DdsCompression, PvrtcCompression};
127///
128/// // Query compression families across different containers
129/// let dds_bc1 = ImageType::Dds(DdsCompression::Bc1);
130/// let pvr_etc2 = ImageType::Pvrtc(PvrtcCompression::Etc2Rgb);
131/// let png = ImageType::Png;
132///
133/// // Group related compression algorithms
134/// assert_eq!(dds_bc1.compression_family(), Some(CompressionFamily::BlockCompression));
135/// assert_eq!(pvr_etc2.compression_family(), Some(CompressionFamily::Etc));
136/// assert_eq!(png.compression_family(), None); // Simple formats don't have compression
137///
138/// // Check for specific compression types
139/// assert!(dds_bc1.is_block_compressed());
140/// assert!(!pvr_etc2.is_block_compressed());
141///
142/// // Identify container formats
143/// assert_eq!(dds_bc1.container_format(), Some("DDS"));
144/// assert_eq!(pvr_etc2.container_format(), Some("PowerVR"));
145/// assert_eq!(png.container_format(), None);
146///
147/// // Check multi-compression support
148/// assert!(dds_bc1.is_multi_compression_container()); // DDS supports BC1-7, RGBA, etc.
149/// assert!(pvr_etc2.is_multi_compression_container()); // PowerVR supports PVRTC, ETC2, EAC
150/// ```
151#[non_exhaustive]
152#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
153pub enum ImageType {
154    /// Animated sprite image format
155    /// <https://github.com/aseprite/aseprite>
156    #[cfg(feature = "aesprite")]
157    Aseprite,
158    /// Adaptive Scalable Texture Compression
159    #[cfg(feature = "astc")]
160    Astc,
161    /// Adaptive Texture Compression
162    #[cfg(feature = "atc")]
163    Atc(AtcCompression),
164    /// Standard Bitmap
165    #[cfg(feature = "bmp")]
166    Bmp,
167    /// DirectDraw Surface
168    #[cfg(feature = "dds")]
169    Dds(DdsCompression),
170    /// Ericsson Texture Compression - Alpha Channel (now unified with ETC2)
171    #[cfg(feature = "eac")]
172    Eac(PkmCompression),
173    /// Ericsson Texture Compression 2 (includes ETC1, ETC2 variants)
174    #[cfg(feature = "etc2")]
175    Etc2(PkmCompression),
176    /// OpenEXR
177    #[cfg(feature = "exr")]
178    Exr,
179    /// Farbfeld
180    /// <https://tools.suckless.org/farbfeld/>
181    #[cfg(feature = "farbfeld")]
182    Farbfeld,
183    /// Standard GIF
184    #[cfg(feature = "gif")]
185    Gif,
186    /// Radiance HDR
187    #[cfg(feature = "hdr")]
188    Hdr,
189    /// Image Container Format
190    #[cfg(feature = "heif")]
191    Heif(Compression),
192    /// Icon file
193    #[cfg(feature = "ico")]
194    Ico,
195    /// Interleaved Bitmap
196    #[cfg(feature = "ilbm")]
197    Ilbm,
198    /// Standard JPEG
199    #[cfg(feature = "jpeg")]
200    Jpeg,
201    /// JPEG XL
202    #[cfg(feature = "jxl")]
203    Jxl,
204    /// Khronos Texture Container
205    #[cfg(feature = "ktx2")]
206    Ktx2,
207    /// Standard PNG
208    #[cfg(feature = "png")]
209    Png,
210    /// Portable Any Map
211    #[cfg(feature = "pnm")]
212    Pnm,
213    /// PowerVR Texture Compression
214    #[cfg(feature = "pvrtc")]
215    Pvrtc(PvrtcCompression),
216    /// Photoshop Document
217    #[cfg(feature = "psd")]
218    Psd,
219    /// Quite OK Image Format
220    /// <https://qoiformat.org/>
221    #[cfg(feature = "qoi")]
222    Qoi,
223    /// Truevision Graphics Adapter
224    #[cfg(feature = "tga")]
225    Tga,
226    /// Standard TIFF
227    #[cfg(feature = "tiff")]
228    Tiff,
229    /// Valve Texture Format
230    #[cfg(feature = "vtf")]
231    Vtf,
232    /// Standard Webp
233    #[cfg(feature = "webp")]
234    Webp,
235}
236
237impl ImageType {
238    /// Returns the compression family for texture formats
239    ///
240    /// Groups related compression algorithms regardless of their container format.
241    /// Returns None for simple image formats like PNG, JPEG, etc.
242    ///
243    /// # Examples
244    ///
245    /// ```rust
246    /// use ai_imagesize::{ImageType, CompressionFamily, DdsCompression, PvrtcCompression};
247    ///
248    /// let dds_type = ImageType::Dds(DdsCompression::Bc1);
249    /// assert_eq!(dds_type.compression_family(), Some(CompressionFamily::BlockCompression));
250    ///
251    /// let pvrtc_etc2_type = ImageType::Pvrtc(PvrtcCompression::Etc2Rgb);
252    /// assert_eq!(pvrtc_etc2_type.compression_family(), Some(CompressionFamily::Etc));
253    ///
254    /// let png_type = ImageType::Png;
255    /// assert_eq!(png_type.compression_family(), None);
256    /// ```
257    pub fn compression_family(&self) -> Option<CompressionFamily> {
258        match self {
259            #[cfg(feature = "dds")]
260            ImageType::Dds(compression) => match compression {
261                DdsCompression::Bc1
262                | DdsCompression::Bc2
263                | DdsCompression::Bc3
264                | DdsCompression::Bc4
265                | DdsCompression::Bc5
266                | DdsCompression::Bc6h
267                | DdsCompression::Bc7 => Some(CompressionFamily::BlockCompression),
268                DdsCompression::Rgba32 | DdsCompression::Rgb24 => {
269                    Some(CompressionFamily::Uncompressed)
270                }
271                DdsCompression::Unknown => None,
272            },
273
274            #[cfg(feature = "etc2")]
275            ImageType::Etc2(compression) => match compression {
276                PkmCompression::Etc1
277                | PkmCompression::Etc2
278                | PkmCompression::Etc2A1
279                | PkmCompression::Etc2A8 => Some(CompressionFamily::Etc),
280                PkmCompression::EacR
281                | PkmCompression::EacRg
282                | PkmCompression::EacRSigned
283                | PkmCompression::EacRgSigned => Some(CompressionFamily::Eac),
284                PkmCompression::Unknown => None,
285            },
286
287            #[cfg(feature = "eac")]
288            ImageType::Eac(compression) => match compression {
289                PkmCompression::EacR
290                | PkmCompression::EacRg
291                | PkmCompression::EacRSigned
292                | PkmCompression::EacRgSigned => Some(CompressionFamily::Eac),
293                PkmCompression::Etc1
294                | PkmCompression::Etc2
295                | PkmCompression::Etc2A1
296                | PkmCompression::Etc2A8 => Some(CompressionFamily::Etc),
297                PkmCompression::Unknown => None,
298            },
299
300            #[cfg(feature = "pvrtc")]
301            ImageType::Pvrtc(compression) => match compression {
302                PvrtcCompression::Pvrtc2BppRgb
303                | PvrtcCompression::Pvrtc2BppRgba
304                | PvrtcCompression::Pvrtc4BppRgb
305                | PvrtcCompression::Pvrtc4BppRgba => Some(CompressionFamily::Pvrtc),
306                PvrtcCompression::Etc2Rgb
307                | PvrtcCompression::Etc2Rgba
308                | PvrtcCompression::Etc2RgbA1 => Some(CompressionFamily::Etc),
309                PvrtcCompression::EacR11 | PvrtcCompression::EacRg11 => {
310                    Some(CompressionFamily::Eac)
311                }
312                PvrtcCompression::Unknown => None,
313            },
314
315            #[cfg(feature = "atc")]
316            ImageType::Atc(_) => Some(CompressionFamily::Atc),
317
318            #[cfg(feature = "astc")]
319            ImageType::Astc => Some(CompressionFamily::Astc),
320
321            // Simple formats don't have compression families
322            _ => None,
323        }
324    }
325
326    /// Returns true if the image uses block compression (BC/DXT family)
327    ///
328    /// Block compression includes BC1-7 formats (also known as DXT1-5, ATI1-2).
329    ///
330    /// # Examples
331    ///
332    /// ```rust
333    /// use ai_imagesize::{ImageType, DdsCompression};
334    ///
335    /// let bc1_type = ImageType::Dds(DdsCompression::Bc1);
336    /// assert!(bc1_type.is_block_compressed());
337    ///
338    /// let png_type = ImageType::Png;
339    /// assert!(!png_type.is_block_compressed());
340    /// ```
341    pub fn is_block_compressed(&self) -> bool {
342        matches!(
343            self.compression_family(),
344            Some(CompressionFamily::BlockCompression)
345        )
346    }
347
348    /// Returns the container format name for texture formats
349    ///
350    /// Returns a human-readable string identifying the container format.
351    /// Returns None for simple image formats.
352    ///
353    /// # Examples
354    ///
355    /// ```rust
356    /// use ai_imagesize::{ImageType, DdsCompression, PvrtcCompression};
357    ///
358    /// let dds_type = ImageType::Dds(DdsCompression::Bc1);
359    /// assert_eq!(dds_type.container_format(), Some("DDS"));
360    ///
361    /// let pvr_type = ImageType::Pvrtc(PvrtcCompression::Pvrtc2BppRgb);
362    /// assert_eq!(pvr_type.container_format(), Some("PowerVR"));
363    ///
364    /// let png_type = ImageType::Png;
365    /// assert_eq!(png_type.container_format(), None);
366    /// ```
367    pub fn container_format(&self) -> Option<&'static str> {
368        match self {
369            #[cfg(feature = "dds")]
370            ImageType::Dds(_) => Some("DDS"),
371
372            #[cfg(feature = "etc2")]
373            ImageType::Etc2(_) => Some("PKM"),
374
375            #[cfg(feature = "eac")]
376            ImageType::Eac(_) => Some("PKM"),
377
378            #[cfg(feature = "pvrtc")]
379            ImageType::Pvrtc(_) => Some("PowerVR"),
380
381            #[cfg(feature = "atc")]
382            ImageType::Atc(_) => Some("PKM"), // ATC typically uses PKM containers
383
384            #[cfg(feature = "astc")]
385            ImageType::Astc => Some("ASTC"), // Direct ASTC format
386
387            #[cfg(feature = "heif")]
388            ImageType::Heif(_) => Some("HEIF"),
389
390            #[cfg(feature = "ktx2")]
391            ImageType::Ktx2 => Some("KTX2"),
392
393            // Simple formats don't have containers
394            _ => None,
395        }
396    }
397
398    /// Returns true if the image format supports multiple compression types within the same container
399    ///
400    /// Some container formats like PowerVR can store different compression algorithms.
401    ///
402    /// # Examples
403    ///
404    /// ```rust
405    /// use ai_imagesize::{ImageType, PvrtcCompression, DdsCompression};
406    ///
407    /// let pvr_type = ImageType::Pvrtc(PvrtcCompression::Etc2Rgb);
408    /// assert!(pvr_type.is_multi_compression_container());
409    ///
410    /// let dds_type = ImageType::Dds(DdsCompression::Bc1);
411    /// assert!(dds_type.is_multi_compression_container());
412    ///
413    /// let png_type = ImageType::Png;
414    /// assert!(!png_type.is_multi_compression_container());
415    /// ```
416    pub fn is_multi_compression_container(&self) -> bool {
417        match self {
418            #[cfg(feature = "dds")]
419            ImageType::Dds(_) => true, // DDS supports BC1-7, RGBA, etc.
420
421            #[cfg(feature = "pvrtc")]
422            ImageType::Pvrtc(_) => true, // PowerVR supports PVRTC, ETC2, EAC
423
424            #[cfg(feature = "ktx2")]
425            ImageType::Ktx2 => true, // KTX2 supports many formats
426
427            _ => false,
428        }
429    }
430
431    /// Calls the correct image size method based on the image type
432    ///
433    /// # Arguments
434    /// * `reader` - A reader for the data
435    pub fn reader_size<R: BufRead + Seek>(&self, reader: &mut R) -> ImageResult<ImageSize> {
436        match self {
437            #[cfg(feature = "aesprite")]
438            ImageType::Aseprite => aesprite::size(reader),
439            #[cfg(feature = "astc")]
440            ImageType::Astc => astc::size(reader),
441            #[cfg(feature = "atc")]
442            ImageType::Atc(..) => container::atc::size(reader),
443            #[cfg(feature = "bmp")]
444            ImageType::Bmp => bmp::size(reader),
445            #[cfg(feature = "dds")]
446            ImageType::Dds(..) => container::dds::size(reader),
447            #[cfg(feature = "eac")]
448            ImageType::Eac(..) => container::pkm::size(reader),
449            #[cfg(feature = "etc2")]
450            ImageType::Etc2(..) => container::pkm::size(reader),
451            #[cfg(feature = "exr")]
452            ImageType::Exr => exr::size(reader),
453            #[cfg(feature = "farbfeld")]
454            ImageType::Farbfeld => farbfeld::size(reader),
455            #[cfg(feature = "gif")]
456            ImageType::Gif => gif::size(reader),
457            #[cfg(feature = "hdr")]
458            ImageType::Hdr => hdr::size(reader),
459            #[cfg(feature = "ico")]
460            ImageType::Ico => ico::size(reader),
461            #[cfg(feature = "ilbm")]
462            ImageType::Ilbm => ilbm::size(reader),
463            #[cfg(feature = "jpeg")]
464            ImageType::Jpeg => jpeg::size(reader),
465            #[cfg(feature = "jxl")]
466            ImageType::Jxl => jxl::size(reader),
467            #[cfg(feature = "ktx2")]
468            ImageType::Ktx2 => ktx2::size(reader),
469            #[cfg(feature = "png")]
470            ImageType::Png => png::size(reader),
471            #[cfg(feature = "pnm")]
472            ImageType::Pnm => pnm::size(reader),
473            #[cfg(feature = "pvrtc")]
474            ImageType::Pvrtc(..) => container::pvrtc::size(reader),
475            #[cfg(feature = "psd")]
476            ImageType::Psd => psd::size(reader),
477            #[cfg(feature = "qoi")]
478            ImageType::Qoi => qoi::size(reader),
479            #[cfg(feature = "tga")]
480            ImageType::Tga => tga::size(reader),
481            #[cfg(feature = "tiff")]
482            ImageType::Tiff => tiff::size(reader),
483            #[cfg(feature = "vtf")]
484            ImageType::Vtf => vtf::size(reader),
485            #[cfg(feature = "webp")]
486            ImageType::Webp => webp::size(reader),
487
488            #[cfg(feature = "heif")]
489            ImageType::Heif(..) => heif::size(reader),
490
491            // Required for when no format features are enabled
492            #[allow(unreachable_patterns)]
493            _ => { let _ = reader; Err(ImageError::NotSupported) }
494        }
495    }
496}
497
498/// Holds the size information of an image.
499#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
500pub struct ImageSize {
501    /// Width of an image in pixels.
502    pub width: usize,
503    /// Height of an image in pixels.
504    pub height: usize,
505}
506
507impl Ord for ImageSize {
508    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
509        (self.width * self.height).cmp(&(other.width * other.height))
510    }
511}
512
513impl PartialOrd for ImageSize {
514    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
515        Some(self.cmp(other))
516    }
517}
518
519/// Get the image type from a header
520///
521/// # Arguments
522/// * `header` - The header of the file.
523///
524/// # Remarks
525///
526/// This will check the header to determine what image type the data is.
527pub fn image_type(header: &[u8]) -> ImageResult<ImageType> {
528    formats::image_type(&mut Cursor::new(header))
529}
530
531/// Get the image size from a local file
532///
533/// # Arguments
534/// * `path` - A local path to the file to parse.
535///
536/// # Remarks
537///
538/// Will try to read as little of the file as possible in order to get the
539/// proper size information.
540///
541/// # Error
542///
543/// This method will return an [`ImageError`] under the following conditions:
544///
545/// * The header isn't recognized as a supported image format
546/// * The data isn't long enough to find the size for the given format
547///
548/// The minimum data required is 12 bytes. Anything shorter will return [`ImageError::IoError`].
549///
550/// # Examples
551///
552/// ```
553/// use ai_imagesize::size;
554///
555/// match size("test/test.webp") {
556///     Ok(dim) => {
557///         assert_eq!(dim.width, 716);
558///         assert_eq!(dim.height, 716);
559///     }
560///     Err(why) => println!("Error getting size: {:?}", why)
561/// }
562/// ```
563///
564/// [`ImageError`]: enum.ImageError.html
565#[cfg(feature = "std")]
566pub fn size<P: AsRef<Path>>(path: P) -> ImageResult<ImageSize> {
567    let file = File::open(path)?;
568    let reader = std::io::BufReader::new(file);
569    reader_size(reader)
570}
571
572/// Get the image size from a block of raw data.
573///
574/// # Arguments
575/// * `data` - A Vec containing the data to parse for image size.
576///
577/// # Error
578///
579/// This method will return an [`ImageError`] under the following conditions:
580///
581/// * The header isn't recognized as a supported image format
582/// * The data isn't long enough to find the size for the given format
583///
584/// The minimum data required is 12 bytes. Anything shorter will return [`ImageError::IoError`].
585///
586/// # Examples
587///
588/// ```
589/// use ai_imagesize::blob_size;
590///
591/// // First few bytes of arbitrary data.
592/// let data = vec![0x89, 0x89, 0x89, 0x89, 0x0D, 0x0A, 0x1A, 0x0A,
593///                 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52,
594///                 0x00, 0x00, 0x00, 0x7B, 0x01, 0x00, 0x01, 0x41,
595///                 0x08, 0x06, 0x00, 0x00, 0x00, 0x9A, 0x38, 0xC4];
596///
597/// assert_eq!(blob_size(&data).is_err(), true);
598/// ```
599///
600/// [`ImageError`]: enum.ImageError.html
601pub fn blob_size(data: &[u8]) -> ImageResult<ImageSize> {
602    let reader = Cursor::new(data);
603    reader_size(reader)
604}
605
606/// Get the image size from a reader
607///
608/// # Arguments
609/// * `reader` - A reader for the data
610///
611/// # Error
612///
613/// This method will return an [`ImageError`] under the following conditions:
614///
615/// * The header isn't recognized as a supported image format
616/// * The data isn't long enough to find the size for the given format
617///
618/// The minimum data required is 12 bytes. Anything shorter will return [`ImageError::IoError`].
619///
620/// # Examples
621///
622/// ```
623/// use no_std_io::io::Cursor; // or std::io::Cursor with the "std" feature
624/// use ai_imagesize::reader_size;
625///
626/// // PNG Header with size 123x321
627/// let reader = Cursor::new([
628///     0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
629///     0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52,
630///     0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x01, 0x41,
631///     0x08, 0x06, 0x00, 0x00, 0x00, 0x9A, 0x38, 0xC4
632/// ]);
633///
634/// match reader_size(reader) {
635///     Ok(dim) => {
636///         assert_eq!(dim.width, 123);
637///         assert_eq!(dim.height, 321);
638///     }
639///     Err(why) => println!("Error getting reader size: {:?}", why)
640/// }
641/// ```
642///
643/// [`ImageError`]: enum.ImageError.html
644pub fn reader_size<R: BufRead + Seek>(mut reader: R) -> ImageResult<ImageSize> {
645    reader_type(&mut reader)?.reader_size(&mut reader)
646}
647
648/// Get the image type from a reader
649///
650/// # Arguments
651/// * `reader` - A reader for the data
652///
653/// # Remarks
654///
655/// This will check the header to determine what image type the data is.
656pub fn reader_type<R: BufRead + Seek>(mut reader: R) -> ImageResult<ImageType> {
657    formats::image_type(&mut reader)
658}