1 | use std::fs::File; |
2 | use std::io::{self, BufRead, BufReader, Cursor, Read, Seek, SeekFrom}; |
3 | use std::path::Path; |
4 | |
5 | use crate::dynimage::DynamicImage; |
6 | use crate::error::{ImageFormatHint, UnsupportedError, UnsupportedErrorKind}; |
7 | use crate::image::ImageFormat; |
8 | use crate::{ImageError, ImageResult}; |
9 | |
10 | use 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 |
61 | pub 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 | |
70 | impl<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 | |
135 | impl 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(inner:File::open(path)?), |
154 | format: ImageFormat::from_path(path).ok(), |
155 | limits: super::Limits::default(), |
156 | }) |
157 | } |
158 | } |
159 | |
160 | impl<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 | } |
240 | |