image/io/
reader.rs

1use std::fs::File;
2use std::io::{self, BufRead, BufReader, Cursor, Read, Seek, SeekFrom};
3use std::path::Path;
4
5use crate::dynimage::DynamicImage;
6use crate::error::{ImageFormatHint, UnsupportedError, UnsupportedErrorKind};
7use crate::image::ImageFormat;
8use crate::{ImageError, ImageResult};
9
10use super::free_functions;
11
12/// A multi-format image reader.
13///
14/// Wraps an input reader to facilitate automatic detection of an image's format, appropriate
15/// decoding method, and dispatches into the set of supported [`ImageDecoder`] implementations.
16///
17/// ## Usage
18///
19/// Opening a file, deducing the format based on the file path automatically, and trying to decode
20/// the image contained can be performed by constructing the reader and immediately consuming it.
21///
22/// ```no_run
23/// # use image::ImageError;
24/// # use image::io::Reader;
25/// # fn main() -> Result<(), ImageError> {
26/// let image = Reader::open("path/to/image.png")?
27///     .decode()?;
28/// # Ok(()) }
29/// ```
30///
31/// It is also possible to make a guess based on the content. This is especially handy if the
32/// source is some blob in memory and you have constructed the reader in another way. Here is an
33/// example with a `pnm` black-and-white subformat that encodes its pixel matrix with ascii values.
34///
35/// ```
36/// # use image::ImageError;
37/// # use image::io::Reader;
38/// # fn main() -> Result<(), ImageError> {
39/// use std::io::Cursor;
40/// use image::ImageFormat;
41///
42/// let raw_data = b"P1 2 2\n\
43///     0 1\n\
44///     1 0\n";
45///
46/// let mut reader = Reader::new(Cursor::new(raw_data))
47///     .with_guessed_format()
48///     .expect("Cursor io never fails");
49/// assert_eq!(reader.format(), Some(ImageFormat::Pnm));
50///
51/// # #[cfg(feature = "pnm")]
52/// let image = reader.decode()?;
53/// # Ok(()) }
54/// ```
55///
56/// As a final fallback or if only a specific format must be used, the reader always allows manual
57/// specification of the supposed image format with [`set_format`].
58///
59/// [`set_format`]: #method.set_format
60/// [`ImageDecoder`]: ../trait.ImageDecoder.html
61pub struct Reader<R: Read> {
62    /// The reader. Should be buffered.
63    inner: R,
64    /// The format, if one has been set or deduced.
65    format: Option<ImageFormat>,
66    /// Decoding limits
67    limits: super::Limits,
68}
69
70impl<R: Read> Reader<R> {
71    /// Create a new image reader without a preset format.
72    ///
73    /// Assumes the reader is already buffered. For optimal performance,
74    /// consider wrapping the reader with a `BufReader::new()`.
75    ///
76    /// It is possible to guess the format based on the content of the read object with
77    /// [`with_guessed_format`], or to set the format directly with [`set_format`].
78    ///
79    /// [`with_guessed_format`]: #method.with_guessed_format
80    /// [`set_format`]: method.set_format
81    pub fn new(buffered_reader: R) -> Self {
82        Reader {
83            inner: buffered_reader,
84            format: None,
85            limits: super::Limits::default(),
86        }
87    }
88
89    /// Construct a reader with specified format.
90    ///
91    /// Assumes the reader is already buffered. For optimal performance,
92    /// consider wrapping the reader with a `BufReader::new()`.
93    pub fn with_format(buffered_reader: R, format: ImageFormat) -> Self {
94        Reader {
95            inner: buffered_reader,
96            format: Some(format),
97            limits: super::Limits::default(),
98        }
99    }
100
101    /// Get the currently determined format.
102    pub fn format(&self) -> Option<ImageFormat> {
103        self.format
104    }
105
106    /// Supply the format as which to interpret the read image.
107    pub fn set_format(&mut self, format: ImageFormat) {
108        self.format = Some(format);
109    }
110
111    /// Remove the current information on the image format.
112    ///
113    /// Note that many operations require format information to be present and will return e.g. an
114    /// `ImageError::Unsupported` when the image format has not been set.
115    pub fn clear_format(&mut self) {
116        self.format = None;
117    }
118
119    /// Disable all decoding limits.
120    pub fn no_limits(&mut self) {
121        self.limits = super::Limits::no_limits();
122    }
123
124    /// Set a custom set of decoding limits.
125    pub fn limits(&mut self, limits: super::Limits) {
126        self.limits = limits;
127    }
128
129    /// Unwrap the reader.
130    pub fn into_inner(self) -> R {
131        self.inner
132    }
133}
134
135impl Reader<BufReader<File>> {
136    /// Open a file to read, format will be guessed from path.
137    ///
138    /// This will not attempt any io operation on the opened file.
139    ///
140    /// If you want to inspect the content for a better guess on the format, which does not depend
141    /// on file extensions, follow this call with a call to [`with_guessed_format`].
142    ///
143    /// [`with_guessed_format`]: #method.with_guessed_format
144    pub fn open<P>(path: P) -> io::Result<Self>
145    where
146        P: AsRef<Path>,
147    {
148        Self::open_impl(path.as_ref())
149    }
150
151    fn open_impl(path: &Path) -> io::Result<Self> {
152        Ok(Reader {
153            inner: BufReader::new(File::open(path)?),
154            format: ImageFormat::from_path(path).ok(),
155            limits: super::Limits::default(),
156        })
157    }
158}
159
160impl<R: BufRead + Seek> Reader<R> {
161    /// Make a format guess based on the content, replacing it on success.
162    ///
163    /// Returns `Ok` with the guess if no io error occurs. Additionally, replaces the current
164    /// format if the guess was successful. If the guess was unable to determine a format then
165    /// the current format of the reader is unchanged.
166    ///
167    /// Returns an error if the underlying reader fails. The format is unchanged. The error is a
168    /// `std::io::Error` and not `ImageError` since the only error case is an error when the
169    /// underlying reader seeks.
170    ///
171    /// When an error occurs, the reader may not have been properly reset and it is potentially
172    /// hazardous to continue with more io.
173    ///
174    /// ## Usage
175    ///
176    /// This supplements the path based type deduction from [`open`](Reader::open) with content based deduction.
177    /// This is more common in Linux and UNIX operating systems and also helpful if the path can
178    /// not be directly controlled.
179    ///
180    /// ```no_run
181    /// # use image::ImageError;
182    /// # use image::io::Reader;
183    /// # fn main() -> Result<(), ImageError> {
184    /// let image = Reader::open("image.unknown")?
185    ///     .with_guessed_format()?
186    ///     .decode()?;
187    /// # Ok(()) }
188    /// ```
189    pub fn with_guessed_format(mut self) -> io::Result<Self> {
190        let format = self.guess_format()?;
191        // Replace format if found, keep current state if not.
192        self.format = format.or(self.format);
193        Ok(self)
194    }
195
196    fn guess_format(&mut self) -> io::Result<Option<ImageFormat>> {
197        let mut start = [0; 16];
198
199        // Save current offset, read start, restore offset.
200        let cur = self.inner.stream_position()?;
201        let len = io::copy(
202            // Accept shorter files but read at most 16 bytes.
203            &mut self.inner.by_ref().take(16),
204            &mut Cursor::new(&mut start[..]),
205        )?;
206        self.inner.seek(SeekFrom::Start(cur))?;
207
208        Ok(free_functions::guess_format_impl(&start[..len as usize]))
209    }
210
211    /// Read the image dimensions.
212    ///
213    /// Uses the current format to construct the correct reader for the format.
214    ///
215    /// If no format was determined, returns an `ImageError::Unsupported`.
216    pub fn into_dimensions(mut self) -> ImageResult<(u32, u32)> {
217        let format = self.require_format()?;
218        free_functions::image_dimensions_with_format_impl(self.inner, format)
219    }
220
221    /// Read the image (replaces `load`).
222    ///
223    /// Uses the current format to construct the correct reader for the format.
224    ///
225    /// If no format was determined, returns an `ImageError::Unsupported`.
226    pub fn decode(mut self) -> ImageResult<DynamicImage> {
227        let format = self.require_format()?;
228        free_functions::load_inner(self.inner, self.limits, format)
229    }
230
231    fn require_format(&mut self) -> ImageResult<ImageFormat> {
232        self.format.ok_or_else(|| {
233            ImageError::Unsupported(UnsupportedError::from_format_and_kind(
234                ImageFormatHint::Unknown,
235                UnsupportedErrorKind::Format(ImageFormatHint::Unknown),
236            ))
237        })
238    }
239}