aboutsummaryrefslogtreecommitdiff
path: root/vendor/image/src/io/reader.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/image/src/io/reader.rs')
-rw-r--r--vendor/image/src/io/reader.rs239
1 files changed, 239 insertions, 0 deletions
diff --git a/vendor/image/src/io/reader.rs b/vendor/image/src/io/reader.rs
new file mode 100644
index 0000000..660780f
--- /dev/null
+++ b/vendor/image/src/io/reader.rs
@@ -0,0 +1,239 @@
+use std::fs::File;
+use std::io::{self, BufRead, BufReader, Cursor, Read, Seek, SeekFrom};
+use std::path::Path;
+
+use crate::dynimage::DynamicImage;
+use crate::error::{ImageFormatHint, UnsupportedError, UnsupportedErrorKind};
+use crate::image::ImageFormat;
+use crate::{ImageError, ImageResult};
+
+use super::free_functions;
+
+/// A multi-format image reader.
+///
+/// Wraps an input reader to facilitate automatic detection of an image's format, appropriate
+/// decoding method, and dispatches into the set of supported [`ImageDecoder`] implementations.
+///
+/// ## Usage
+///
+/// Opening a file, deducing the format based on the file path automatically, and trying to decode
+/// the image contained can be performed by constructing the reader and immediately consuming it.
+///
+/// ```no_run
+/// # use image::ImageError;
+/// # use image::io::Reader;
+/// # fn main() -> Result<(), ImageError> {
+/// let image = Reader::open("path/to/image.png")?
+/// .decode()?;
+/// # Ok(()) }
+/// ```
+///
+/// It is also possible to make a guess based on the content. This is especially handy if the
+/// source is some blob in memory and you have constructed the reader in another way. Here is an
+/// example with a `pnm` black-and-white subformat that encodes its pixel matrix with ascii values.
+///
+/// ```
+/// # use image::ImageError;
+/// # use image::io::Reader;
+/// # fn main() -> Result<(), ImageError> {
+/// use std::io::Cursor;
+/// use image::ImageFormat;
+///
+/// let raw_data = b"P1 2 2\n\
+/// 0 1\n\
+/// 1 0\n";
+///
+/// let mut reader = Reader::new(Cursor::new(raw_data))
+/// .with_guessed_format()
+/// .expect("Cursor io never fails");
+/// assert_eq!(reader.format(), Some(ImageFormat::Pnm));
+///
+/// # #[cfg(feature = "pnm")]
+/// let image = reader.decode()?;
+/// # Ok(()) }
+/// ```
+///
+/// As a final fallback or if only a specific format must be used, the reader always allows manual
+/// specification of the supposed image format with [`set_format`].
+///
+/// [`set_format`]: #method.set_format
+/// [`ImageDecoder`]: ../trait.ImageDecoder.html
+pub struct Reader<R: Read> {
+ /// The reader. Should be buffered.
+ inner: R,
+ /// The format, if one has been set or deduced.
+ format: Option<ImageFormat>,
+ /// Decoding limits
+ limits: super::Limits,
+}
+
+impl<R: Read> Reader<R> {
+ /// Create a new image reader without a preset format.
+ ///
+ /// Assumes the reader is already buffered. For optimal performance,
+ /// consider wrapping the reader with a `BufReader::new()`.
+ ///
+ /// It is possible to guess the format based on the content of the read object with
+ /// [`with_guessed_format`], or to set the format directly with [`set_format`].
+ ///
+ /// [`with_guessed_format`]: #method.with_guessed_format
+ /// [`set_format`]: method.set_format
+ pub fn new(buffered_reader: R) -> Self {
+ Reader {
+ inner: buffered_reader,
+ format: None,
+ limits: super::Limits::default(),
+ }
+ }
+
+ /// Construct a reader with specified format.
+ ///
+ /// Assumes the reader is already buffered. For optimal performance,
+ /// consider wrapping the reader with a `BufReader::new()`.
+ pub fn with_format(buffered_reader: R, format: ImageFormat) -> Self {
+ Reader {
+ inner: buffered_reader,
+ format: Some(format),
+ limits: super::Limits::default(),
+ }
+ }
+
+ /// Get the currently determined format.
+ pub fn format(&self) -> Option<ImageFormat> {
+ self.format
+ }
+
+ /// Supply the format as which to interpret the read image.
+ pub fn set_format(&mut self, format: ImageFormat) {
+ self.format = Some(format);
+ }
+
+ /// Remove the current information on the image format.
+ ///
+ /// Note that many operations require format information to be present and will return e.g. an
+ /// `ImageError::Unsupported` when the image format has not been set.
+ pub fn clear_format(&mut self) {
+ self.format = None;
+ }
+
+ /// Disable all decoding limits.
+ pub fn no_limits(&mut self) {
+ self.limits = super::Limits::no_limits();
+ }
+
+ /// Set a custom set of decoding limits.
+ pub fn limits(&mut self, limits: super::Limits) {
+ self.limits = limits;
+ }
+
+ /// Unwrap the reader.
+ pub fn into_inner(self) -> R {
+ self.inner
+ }
+}
+
+impl Reader<BufReader<File>> {
+ /// Open a file to read, format will be guessed from path.
+ ///
+ /// This will not attempt any io operation on the opened file.
+ ///
+ /// If you want to inspect the content for a better guess on the format, which does not depend
+ /// on file extensions, follow this call with a call to [`with_guessed_format`].
+ ///
+ /// [`with_guessed_format`]: #method.with_guessed_format
+ pub fn open<P>(path: P) -> io::Result<Self>
+ where
+ P: AsRef<Path>,
+ {
+ Self::open_impl(path.as_ref())
+ }
+
+ fn open_impl(path: &Path) -> io::Result<Self> {
+ Ok(Reader {
+ inner: BufReader::new(File::open(path)?),
+ format: ImageFormat::from_path(path).ok(),
+ limits: super::Limits::default(),
+ })
+ }
+}
+
+impl<R: BufRead + Seek> Reader<R> {
+ /// Make a format guess based on the content, replacing it on success.
+ ///
+ /// Returns `Ok` with the guess if no io error occurs. Additionally, replaces the current
+ /// format if the guess was successful. If the guess was unable to determine a format then
+ /// the current format of the reader is unchanged.
+ ///
+ /// Returns an error if the underlying reader fails. The format is unchanged. The error is a
+ /// `std::io::Error` and not `ImageError` since the only error case is an error when the
+ /// underlying reader seeks.
+ ///
+ /// When an error occurs, the reader may not have been properly reset and it is potentially
+ /// hazardous to continue with more io.
+ ///
+ /// ## Usage
+ ///
+ /// This supplements the path based type deduction from [`open`](Reader::open) with content based deduction.
+ /// This is more common in Linux and UNIX operating systems and also helpful if the path can
+ /// not be directly controlled.
+ ///
+ /// ```no_run
+ /// # use image::ImageError;
+ /// # use image::io::Reader;
+ /// # fn main() -> Result<(), ImageError> {
+ /// let image = Reader::open("image.unknown")?
+ /// .with_guessed_format()?
+ /// .decode()?;
+ /// # Ok(()) }
+ /// ```
+ pub fn with_guessed_format(mut self) -> io::Result<Self> {
+ let format = self.guess_format()?;
+ // Replace format if found, keep current state if not.
+ self.format = format.or(self.format);
+ Ok(self)
+ }
+
+ fn guess_format(&mut self) -> io::Result<Option<ImageFormat>> {
+ let mut start = [0; 16];
+
+ // Save current offset, read start, restore offset.
+ let cur = self.inner.stream_position()?;
+ let len = io::copy(
+ // Accept shorter files but read at most 16 bytes.
+ &mut self.inner.by_ref().take(16),
+ &mut Cursor::new(&mut start[..]),
+ )?;
+ self.inner.seek(SeekFrom::Start(cur))?;
+
+ Ok(free_functions::guess_format_impl(&start[..len as usize]))
+ }
+
+ /// Read the image dimensions.
+ ///
+ /// Uses the current format to construct the correct reader for the format.
+ ///
+ /// If no format was determined, returns an `ImageError::Unsupported`.
+ pub fn into_dimensions(mut self) -> ImageResult<(u32, u32)> {
+ let format = self.require_format()?;
+ free_functions::image_dimensions_with_format_impl(self.inner, format)
+ }
+
+ /// Read the image (replaces `load`).
+ ///
+ /// Uses the current format to construct the correct reader for the format.
+ ///
+ /// If no format was determined, returns an `ImageError::Unsupported`.
+ pub fn decode(mut self) -> ImageResult<DynamicImage> {
+ let format = self.require_format()?;
+ free_functions::load_inner(self.inner, self.limits, format)
+ }
+
+ fn require_format(&mut self) -> ImageResult<ImageFormat> {
+ self.format.ok_or_else(|| {
+ ImageError::Unsupported(UnsupportedError::from_format_and_kind(
+ ImageFormatHint::Unknown,
+ UnsupportedErrorKind::Format(ImageFormatHint::Unknown),
+ ))
+ })
+ }
+}