image/image_reader/image_reader_type.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::{ImageDecoder, 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::ImageReader;
25/// # fn main() -> Result<(), ImageError> {
26/// let image = ImageReader::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::ImageReader;
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 = ImageReader::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 ImageReader<R: Read + Seek> {
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<'a, R: 'a + BufRead + Seek> ImageReader<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 ImageReader {
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 ImageReader {
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 /// Makes a decoder.
135 ///
136 /// For all formats except PNG, the limits are ignored and can be set with
137 /// `ImageDecoder::set_limits` after calling this function. PNG is handled specially because that
138 /// decoder has a different API which does not allow setting limits after construction.
139 fn make_decoder(
140 format: ImageFormat,
141 reader: R,
142 limits_for_png: super::Limits,
143 ) -> ImageResult<Box<dyn ImageDecoder + 'a>> {
144 #[allow(unused)]
145 use crate::codecs::*;
146
147 #[allow(unreachable_patterns)]
148 // Default is unreachable if all features are supported.
149 Ok(match format {
150 #[cfg(feature = "avif-native")]
151 ImageFormat::Avif => Box::new(avif::AvifDecoder::new(reader)?),
152 #[cfg(feature = "png")]
153 ImageFormat::Png => Box::new(png::PngDecoder::with_limits(reader, limits_for_png)?),
154 #[cfg(feature = "gif")]
155 ImageFormat::Gif => Box::new(gif::GifDecoder::new(reader)?),
156 #[cfg(feature = "jpeg")]
157 ImageFormat::Jpeg => Box::new(jpeg::JpegDecoder::new(reader)?),
158 #[cfg(feature = "webp")]
159 ImageFormat::WebP => Box::new(webp::WebPDecoder::new(reader)?),
160 #[cfg(feature = "tiff")]
161 ImageFormat::Tiff => Box::new(tiff::TiffDecoder::new(reader)?),
162 #[cfg(feature = "tga")]
163 ImageFormat::Tga => Box::new(tga::TgaDecoder::new(reader)?),
164 #[cfg(feature = "dds")]
165 ImageFormat::Dds => Box::new(dds::DdsDecoder::new(reader)?),
166 #[cfg(feature = "bmp")]
167 ImageFormat::Bmp => Box::new(bmp::BmpDecoder::new(reader)?),
168 #[cfg(feature = "ico")]
169 ImageFormat::Ico => Box::new(ico::IcoDecoder::new(reader)?),
170 #[cfg(feature = "hdr")]
171 ImageFormat::Hdr => Box::new(hdr::HdrDecoder::new(reader)?),
172 #[cfg(feature = "exr")]
173 ImageFormat::OpenExr => Box::new(openexr::OpenExrDecoder::new(reader)?),
174 #[cfg(feature = "pnm")]
175 ImageFormat::Pnm => Box::new(pnm::PnmDecoder::new(reader)?),
176 #[cfg(feature = "ff")]
177 ImageFormat::Farbfeld => Box::new(farbfeld::FarbfeldDecoder::new(reader)?),
178 #[cfg(feature = "qoi")]
179 ImageFormat::Qoi => Box::new(qoi::QoiDecoder::new(reader)?),
180 #[cfg(feature = "pcx")]
181 ImageFormat::Pcx => Box::new(pcx::PCXDecoder::new(reader)?),
182 format => {
183 return Err(ImageError::Unsupported(
184 ImageFormatHint::Exact(format).into(),
185 ))
186 }
187 })
188 }
189
190 /// Convert the reader into a decoder.
191 pub fn into_decoder(mut self) -> ImageResult<impl ImageDecoder + 'a> {
192 let mut decoder =
193 Self::make_decoder(self.require_format()?, self.inner, self.limits.clone())?;
194 decoder.set_limits(self.limits)?;
195 Ok(decoder)
196 }
197
198 /// Make a format guess based on the content, replacing it on success.
199 ///
200 /// Returns `Ok` with the guess if no io error occurs. Additionally, replaces the current
201 /// format if the guess was successful. If the guess was unable to determine a format then
202 /// the current format of the reader is unchanged.
203 ///
204 /// Returns an error if the underlying reader fails. The format is unchanged. The error is a
205 /// `std::io::Error` and not `ImageError` since the only error case is an error when the
206 /// underlying reader seeks.
207 ///
208 /// When an error occurs, the reader may not have been properly reset and it is potentially
209 /// hazardous to continue with more io.
210 ///
211 /// ## Usage
212 ///
213 /// This supplements the path based type deduction from [`ImageReader::open()`] with content based deduction.
214 /// This is more common in Linux and UNIX operating systems and also helpful if the path can
215 /// not be directly controlled.
216 ///
217 /// ```no_run
218 /// # use image::ImageError;
219 /// # use image::ImageReader;
220 /// # fn main() -> Result<(), ImageError> {
221 /// let image = ImageReader::open("image.unknown")?
222 /// .with_guessed_format()?
223 /// .decode()?;
224 /// # Ok(()) }
225 /// ```
226 pub fn with_guessed_format(mut self) -> io::Result<Self> {
227 let format = self.guess_format()?;
228 // Replace format if found, keep current state if not.
229 self.format = format.or(self.format);
230 Ok(self)
231 }
232
233 fn guess_format(&mut self) -> io::Result<Option<ImageFormat>> {
234 let mut start = [0; 16];
235
236 // Save current offset, read start, restore offset.
237 let cur = self.inner.stream_position()?;
238 let len = io::copy(
239 // Accept shorter files but read at most 16 bytes.
240 &mut self.inner.by_ref().take(16),
241 &mut Cursor::new(&mut start[..]),
242 )?;
243 self.inner.seek(SeekFrom::Start(cur))?;
244
245 Ok(free_functions::guess_format_impl(&start[..len as usize]))
246 }
247
248 /// Read the image dimensions.
249 ///
250 /// Uses the current format to construct the correct reader for the format.
251 ///
252 /// If no format was determined, returns an `ImageError::Unsupported`.
253 pub fn into_dimensions(self) -> ImageResult<(u32, u32)> {
254 self.into_decoder().map(|d| d.dimensions())
255 }
256
257 /// Read the image (replaces `load`).
258 ///
259 /// Uses the current format to construct the correct reader for the format.
260 ///
261 /// If no format was determined, returns an `ImageError::Unsupported`.
262 pub fn decode(mut self) -> ImageResult<DynamicImage> {
263 let format = self.require_format()?;
264
265 let mut limits = self.limits;
266 let mut decoder = Self::make_decoder(format, self.inner, limits.clone())?;
267
268 // Check that we do not allocate a bigger buffer than we are allowed to
269 // FIXME: should this rather go in `DynamicImage::from_decoder` somehow?
270 limits.reserve(decoder.total_bytes())?;
271 decoder.set_limits(limits)?;
272
273 DynamicImage::from_decoder(decoder)
274 }
275
276 fn require_format(&mut self) -> ImageResult<ImageFormat> {
277 self.format.ok_or_else(|| {
278 ImageError::Unsupported(UnsupportedError::from_format_and_kind(
279 ImageFormatHint::Unknown,
280 UnsupportedErrorKind::Format(ImageFormatHint::Unknown),
281 ))
282 })
283 }
284}
285
286impl ImageReader<BufReader<File>> {
287 /// Open a file to read, format will be guessed from path.
288 ///
289 /// This will not attempt any io operation on the opened file.
290 ///
291 /// If you want to inspect the content for a better guess on the format, which does not depend
292 /// on file extensions, follow this call with a call to [`with_guessed_format`].
293 ///
294 /// [`with_guessed_format`]: #method.with_guessed_format
295 pub fn open<P>(path: P) -> io::Result<Self>
296 where
297 P: AsRef<Path>,
298 {
299 Self::open_impl(path.as_ref())
300 }
301
302 fn open_impl(path: &Path) -> io::Result<Self> {
303 Ok(ImageReader {
304 inner: BufReader::new(File::open(path)?),
305 format: ImageFormat::from_path(path).ok(),
306 limits: super::Limits::default(),
307 })
308 }
309}