diff options
Diffstat (limited to 'vendor/image/src/io/reader.rs')
-rw-r--r-- | vendor/image/src/io/reader.rs | 239 |
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), + )) + }) + } +} |