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::image::ImageFormat; 7 use crate::error::{ImageFormatHint, UnsupportedError, UnsupportedErrorKind}; 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 #[cfg_attr(feature = "pnm", doc = "```")] 36 #[cfg_attr(not(feature = "pnm"), doc = "```no_run")] 37 /// # use image::ImageError; 38 /// # use image::io::Reader; 39 /// # fn main() -> Result<(), ImageError> { 40 /// use std::io::Cursor; 41 /// use image::ImageFormat; 42 /// 43 /// let raw_data = b"P1 2 2\n\ 44 /// 0 1\n\ 45 /// 1 0\n"; 46 /// 47 /// let mut reader = Reader::new(Cursor::new(raw_data)) 48 /// .with_guessed_format() 49 /// .expect("Cursor io never fails"); 50 /// assert_eq!(reader.format(), Some(ImageFormat::Pnm)); 51 /// 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. 63 inner: R, 64 /// The format, if one has been set or deduced. 65 format: Option<ImageFormat>, 66 } 67 68 impl<R: Read> Reader<R> { 69 /// Create a new image reader without a preset format. 70 /// 71 /// It is possible to guess the format based on the content of the read object with 72 /// [`with_guessed_format`], or to set the format directly with [`set_format`]. 73 /// 74 /// [`with_guessed_format`]: #method.with_guessed_format 75 /// [`set_format`]: method.set_format new(reader: R) -> Self76 pub fn new(reader: R) -> Self { 77 Reader { 78 inner: reader, 79 format: None, 80 } 81 } 82 83 /// Construct a reader with specified format. with_format(reader: R, format: ImageFormat) -> Self84 pub fn with_format(reader: R, format: ImageFormat) -> Self { 85 Reader { 86 inner: reader, 87 format: Some(format), 88 } 89 } 90 91 /// Get the currently determined format. format(&self) -> Option<ImageFormat>92 pub fn format(&self) -> Option<ImageFormat> { 93 self.format 94 } 95 96 /// Supply the format as which to interpret the read image. set_format(&mut self, format: ImageFormat)97 pub fn set_format(&mut self, format: ImageFormat) { 98 self.format = Some(format); 99 } 100 101 /// Remove the current information on the image format. 102 /// 103 /// Note that many operations require format information to be present and will return e.g. an 104 /// `ImageError::Unsupported` when the image format has not been set. clear_format(&mut self)105 pub fn clear_format(&mut self) { 106 self.format = None; 107 } 108 109 /// Unwrap the reader. into_inner(self) -> R110 pub fn into_inner(self) -> R { 111 self.inner 112 } 113 } 114 115 impl Reader<BufReader<File>> { 116 /// Open a file to read, format will be guessed from path. 117 /// 118 /// This will not attempt any io operation on the opened file. 119 /// 120 /// If you want to inspect the content for a better guess on the format, which does not depend 121 /// on file extensions, follow this call with a call to [`with_guessed_format`]. 122 /// 123 /// [`with_guessed_format`]: #method.with_guessed_format open<P>(path: P) -> io::Result<Self> where P: AsRef<Path>124 pub fn open<P>(path: P) -> io::Result<Self> where P: AsRef<Path> { 125 Self::open_impl(path.as_ref()) 126 } 127 open_impl(path: &Path) -> io::Result<Self>128 fn open_impl(path: &Path) -> io::Result<Self> { 129 let file = File::open(path)?; 130 Ok(Reader { 131 inner: BufReader::new(file), 132 format: ImageFormat::from_path(path).ok(), 133 }) 134 } 135 } 136 137 impl<R: BufRead + Seek> Reader<R> { 138 /// Make a format guess based on the content, replacing it on success. 139 /// 140 /// Returns `Ok` with the guess if no io error occurs. Additionally, replaces the current 141 /// format if the guess was successful. If the guess was unable to determine a format then 142 /// the current format of the reader is unchanged. 143 /// 144 /// Returns an error if the underlying reader fails. The format is unchanged. The error is a 145 /// `std::io::Error` and not `ImageError` since the only error case is an error when the 146 /// underlying reader seeks. 147 /// 148 /// When an error occurs, the reader may not have been properly reset and it is potentially 149 /// hazardous to continue with more io. 150 /// 151 /// ## Usage 152 /// 153 /// This supplements the path based type deduction from [`open`](Reader::open) with content based deduction. 154 /// This is more common in Linux and UNIX operating systems and also helpful if the path can 155 /// not be directly controlled. 156 /// 157 /// ```no_run 158 /// # use image::ImageError; 159 /// # use image::io::Reader; 160 /// # fn main() -> Result<(), ImageError> { 161 /// let image = Reader::open("image.unknown")? 162 /// .with_guessed_format()? 163 /// .decode()?; 164 /// # Ok(()) } 165 /// ``` with_guessed_format(mut self) -> io::Result<Self>166 pub fn with_guessed_format(mut self) -> io::Result<Self> { 167 let format = self.guess_format()?; 168 // Replace format if found, keep current state if not. 169 self.format = format.or(self.format); 170 Ok(self) 171 } 172 guess_format(&mut self) -> io::Result<Option<ImageFormat>>173 fn guess_format(&mut self) -> io::Result<Option<ImageFormat>> { 174 let mut start = [0; 16]; 175 176 // Save current offset, read start, restore offset. 177 let cur = self.inner.seek(SeekFrom::Current(0))?; 178 let len = io::copy( 179 // Accept shorter files but read at most 16 bytes. 180 &mut self.inner.by_ref().take(16), 181 &mut Cursor::new(&mut start[..]))?; 182 self.inner.seek(SeekFrom::Start(cur))?; 183 184 Ok(free_functions::guess_format_impl(&start[..len as usize])) 185 } 186 187 /// Read the image dimensions. 188 /// 189 /// Uses the current format to construct the correct reader for the format. 190 /// 191 /// If no format was determined, returns an `ImageError::Unsupported`. into_dimensions(mut self) -> ImageResult<(u32, u32)>192 pub fn into_dimensions(mut self) -> ImageResult<(u32, u32)> { 193 let format = self.require_format()?; 194 free_functions::image_dimensions_with_format_impl(self.inner, format) 195 } 196 197 /// Read the image (replaces `load`). 198 /// 199 /// Uses the current format to construct the correct reader for the format. 200 /// 201 /// If no format was determined, returns an `ImageError::Unsupported`. decode(mut self) -> ImageResult<DynamicImage>202 pub fn decode(mut self) -> ImageResult<DynamicImage> { 203 let format = self.require_format()?; 204 free_functions::load(self.inner, format) 205 } 206 require_format(&mut self) -> ImageResult<ImageFormat>207 fn require_format(&mut self) -> ImageResult<ImageFormat> { 208 self.format.ok_or_else(|| 209 ImageError::Unsupported(UnsupportedError::from_format_and_kind( 210 ImageFormatHint::Unknown, 211 UnsupportedErrorKind::Format(ImageFormatHint::Unknown)))) 212 } 213 } 214