summaryrefslogtreecommitdiffstats
path: root/third_party/rust/image/src/io
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/image/src/io
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/image/src/io')
-rw-r--r--third_party/rust/image/src/io/free_functions.rs289
-rw-r--r--third_party/rust/image/src/io/mod.rs5
-rw-r--r--third_party/rust/image/src/io/reader.rs210
3 files changed, 504 insertions, 0 deletions
diff --git a/third_party/rust/image/src/io/free_functions.rs b/third_party/rust/image/src/io/free_functions.rs
new file mode 100644
index 0000000000..227864d77e
--- /dev/null
+++ b/third_party/rust/image/src/io/free_functions.rs
@@ -0,0 +1,289 @@
+use std::ffi::OsString;
+use std::fs::File;
+use std::io::{BufRead, BufReader, BufWriter, Seek};
+use std::path::Path;
+use std::u32;
+
+#[cfg(feature = "bmp")]
+use crate::bmp;
+#[cfg(feature = "gif")]
+use crate::gif;
+#[cfg(feature = "hdr")]
+use crate::hdr;
+#[cfg(feature = "ico")]
+use crate::ico;
+#[cfg(feature = "jpeg")]
+use crate::jpeg;
+#[cfg(feature = "png")]
+use crate::png;
+#[cfg(feature = "pnm")]
+use crate::pnm;
+#[cfg(feature = "tga")]
+use crate::tga;
+#[cfg(feature = "dds")]
+use crate::dds;
+#[cfg(feature = "tiff")]
+use crate::tiff;
+#[cfg(feature = "webp")]
+use crate::webp;
+
+use crate::color;
+use crate::image;
+use crate::dynimage::DynamicImage;
+use crate::error::{ImageError, ImageFormatHint, ImageResult};
+use crate::image::{ImageDecoder, ImageEncoder, ImageFormat};
+
+/// Internal error type for guessing format from path.
+pub(crate) enum PathError {
+ /// The extension did not fit a supported format.
+ UnknownExtension(OsString),
+ /// Extension could not be converted to `str`.
+ NoExtension,
+}
+
+pub(crate) fn open_impl(path: &Path) -> ImageResult<DynamicImage> {
+ let fin = match File::open(path) {
+ Ok(f) => f,
+ Err(err) => return Err(ImageError::IoError(err)),
+ };
+ let fin = BufReader::new(fin);
+
+ load(fin, ImageFormat::from_path(path)?)
+}
+
+/// Create a new image from a Reader
+///
+/// Try [`io::Reader`] for more advanced uses.
+///
+/// [`io::Reader`]: io/struct.Reader.html
+pub fn load<R: BufRead + Seek>(r: R, format: ImageFormat) -> ImageResult<DynamicImage> {
+ #[allow(deprecated, unreachable_patterns)]
+ // Default is unreachable if all features are supported.
+ match format {
+ #[cfg(feature = "png")]
+ image::ImageFormat::Png => DynamicImage::from_decoder(png::PngDecoder::new(r)?),
+ #[cfg(feature = "gif")]
+ image::ImageFormat::Gif => DynamicImage::from_decoder(gif::GifDecoder::new(r)?),
+ #[cfg(feature = "jpeg")]
+ image::ImageFormat::Jpeg => DynamicImage::from_decoder(jpeg::JpegDecoder::new(r)?),
+ #[cfg(feature = "webp")]
+ image::ImageFormat::WebP => DynamicImage::from_decoder(webp::WebPDecoder::new(r)?),
+ #[cfg(feature = "tiff")]
+ image::ImageFormat::Tiff => DynamicImage::from_decoder(tiff::TiffDecoder::new(r)?),
+ #[cfg(feature = "tga")]
+ image::ImageFormat::Tga => DynamicImage::from_decoder(tga::TgaDecoder::new(r)?),
+ #[cfg(feature = "dds")]
+ image::ImageFormat::Dds => DynamicImage::from_decoder(dds::DdsDecoder::new(r)?),
+ #[cfg(feature = "bmp")]
+ image::ImageFormat::Bmp => DynamicImage::from_decoder(bmp::BmpDecoder::new(r)?),
+ #[cfg(feature = "ico")]
+ image::ImageFormat::Ico => DynamicImage::from_decoder(ico::IcoDecoder::new(r)?),
+ #[cfg(feature = "hdr")]
+ image::ImageFormat::Hdr => DynamicImage::from_decoder(hdr::HDRAdapter::new(BufReader::new(r))?),
+ #[cfg(feature = "pnm")]
+ image::ImageFormat::Pnm => DynamicImage::from_decoder(pnm::PnmDecoder::new(BufReader::new(r))?),
+ _ => Err(ImageError::Unsupported(ImageFormatHint::Exact(format).into())),
+ }
+}
+
+pub(crate) fn image_dimensions_impl(path: &Path) -> ImageResult<(u32, u32)> {
+ let format = image::ImageFormat::from_path(path)?;
+
+ let fin = File::open(path)?;
+ let fin = BufReader::new(fin);
+
+ image_dimensions_with_format_impl(fin, format)
+}
+
+pub(crate) fn image_dimensions_with_format_impl<R: BufRead + Seek>(fin: R, format: ImageFormat)
+ -> ImageResult<(u32, u32)>
+{
+ #[allow(unreachable_patterns)]
+ // Default is unreachable if all features are supported.
+ Ok(match format {
+ #[cfg(feature = "jpeg")]
+ image::ImageFormat::Jpeg => jpeg::JpegDecoder::new(fin)?.dimensions(),
+ #[cfg(feature = "png")]
+ image::ImageFormat::Png => png::PngDecoder::new(fin)?.dimensions(),
+ #[cfg(feature = "gif")]
+ image::ImageFormat::Gif => gif::GifDecoder::new(fin)?.dimensions(),
+ #[cfg(feature = "webp")]
+ image::ImageFormat::WebP => webp::WebPDecoder::new(fin)?.dimensions(),
+ #[cfg(feature = "tiff")]
+ image::ImageFormat::Tiff => tiff::TiffDecoder::new(fin)?.dimensions(),
+ #[cfg(feature = "tga")]
+ image::ImageFormat::Tga => tga::TgaDecoder::new(fin)?.dimensions(),
+ #[cfg(feature = "dds")]
+ image::ImageFormat::Dds => dds::DdsDecoder::new(fin)?.dimensions(),
+ #[cfg(feature = "bmp")]
+ image::ImageFormat::Bmp => bmp::BmpDecoder::new(fin)?.dimensions(),
+ #[cfg(feature = "ico")]
+ image::ImageFormat::Ico => ico::IcoDecoder::new(fin)?.dimensions(),
+ #[cfg(feature = "hdr")]
+ image::ImageFormat::Hdr => hdr::HDRAdapter::new(fin)?.dimensions(),
+ #[cfg(feature = "pnm")]
+ image::ImageFormat::Pnm => {
+ pnm::PnmDecoder::new(fin)?.dimensions()
+ }
+ format => return Err(ImageError::Unsupported(ImageFormatHint::Exact(format).into())),
+ })
+}
+
+pub(crate) fn save_buffer_impl(
+ path: &Path,
+ buf: &[u8],
+ width: u32,
+ height: u32,
+ color: color::ColorType,
+) -> ImageResult<()> {
+ let fout = &mut BufWriter::new(File::create(path)?);
+ let ext = path.extension()
+ .and_then(|s| s.to_str())
+ .map_or("".to_string(), |s| s.to_ascii_lowercase());
+
+ match &*ext {
+ #[cfg(feature = "gif")]
+ "gif" => gif::Encoder::new(fout).encode(buf, width, height, color),
+ #[cfg(feature = "ico")]
+ "ico" => ico::ICOEncoder::new(fout).write_image(buf, width, height, color),
+ #[cfg(feature = "jpeg")]
+ "jpg" | "jpeg" => jpeg::JPEGEncoder::new(fout).write_image(buf, width, height, color),
+ #[cfg(feature = "png")]
+ "png" => png::PNGEncoder::new(fout).write_image(buf, width, height, color),
+ #[cfg(feature = "pnm")]
+ "pbm" => pnm::PNMEncoder::new(fout)
+ .with_subtype(pnm::PNMSubtype::Bitmap(pnm::SampleEncoding::Binary))
+ .write_image(buf, width, height, color),
+ #[cfg(feature = "pnm")]
+ "pgm" => pnm::PNMEncoder::new(fout)
+ .with_subtype(pnm::PNMSubtype::Graymap(pnm::SampleEncoding::Binary))
+ .write_image(buf, width, height, color),
+ #[cfg(feature = "pnm")]
+ "ppm" => pnm::PNMEncoder::new(fout)
+ .with_subtype(pnm::PNMSubtype::Pixmap(pnm::SampleEncoding::Binary))
+ .write_image(buf, width, height, color),
+ #[cfg(feature = "pnm")]
+ "pam" => pnm::PNMEncoder::new(fout).write_image(buf, width, height, color),
+ #[cfg(feature = "bmp")]
+ "bmp" => bmp::BMPEncoder::new(fout).write_image(buf, width, height, color),
+ #[cfg(feature = "tiff")]
+ "tif" | "tiff" => tiff::TiffEncoder::new(fout)
+ .write_image(buf, width, height, color),
+ _ => Err(ImageError::Unsupported(ImageFormatHint::from(path).into())),
+ }
+}
+
+pub(crate) fn save_buffer_with_format_impl(
+ path: &Path,
+ buf: &[u8],
+ width: u32,
+ height: u32,
+ color: color::ColorType,
+ format: ImageFormat,
+) -> ImageResult<()> {
+ let fout = &mut BufWriter::new(File::create(path)?);
+
+ match format {
+ #[cfg(feature = "gif")]
+ image::ImageFormat::Gif => gif::Encoder::new(fout).encode(buf, width, height, color),
+ #[cfg(feature = "ico")]
+ image::ImageFormat::Ico => ico::ICOEncoder::new(fout).write_image(buf, width, height, color),
+ #[cfg(feature = "jpeg")]
+ image::ImageFormat::Jpeg => jpeg::JPEGEncoder::new(fout).write_image(buf, width, height, color),
+ #[cfg(feature = "png")]
+ image::ImageFormat::Png => png::PNGEncoder::new(fout).write_image(buf, width, height, color),
+ #[cfg(feature = "bmp")]
+ image::ImageFormat::Bmp => bmp::BMPEncoder::new(fout).write_image(buf, width, height, color),
+ #[cfg(feature = "tiff")]
+ image::ImageFormat::Tiff => tiff::TiffEncoder::new(fout)
+ .write_image(buf, width, height, color),
+ format => return Err(ImageError::Unsupported(ImageFormatHint::Exact(format).into())),
+ }
+}
+
+/// Guess format from a path.
+///
+/// Returns `PathError::NoExtension` if the path has no extension or returns a
+/// `PathError::UnknownExtension` containing the extension if it can not be convert to a `str`.
+pub(crate) fn guess_format_from_path_impl(path: &Path) -> Result<ImageFormat, PathError> {
+ let exact_ext = path.extension();
+
+ let ext = exact_ext
+ .and_then(|s| s.to_str())
+ .map(str::to_ascii_lowercase);
+
+ let ext = ext.as_ref()
+ .map(String::as_str);
+
+ Ok(match ext {
+ Some("jpg") | Some("jpeg") => image::ImageFormat::Jpeg,
+ Some("png") => image::ImageFormat::Png,
+ Some("gif") => image::ImageFormat::Gif,
+ Some("webp") => image::ImageFormat::WebP,
+ Some("tif") | Some("tiff") => image::ImageFormat::Tiff,
+ Some("tga") => image::ImageFormat::Tga,
+ Some("dds") => image::ImageFormat::Dds,
+ Some("bmp") => image::ImageFormat::Bmp,
+ Some("ico") => image::ImageFormat::Ico,
+ Some("hdr") => image::ImageFormat::Hdr,
+ Some("pbm") | Some("pam") | Some("ppm") | Some("pgm") => image::ImageFormat::Pnm,
+ // The original extension is used, instead of _format
+ _ => return match exact_ext {
+ None => Err(PathError::NoExtension),
+ Some(os) => Err(PathError::UnknownExtension(os.to_owned())),
+ },
+ })
+}
+
+static MAGIC_BYTES: [(&'static [u8], ImageFormat); 18] = [
+ (b"\x89PNG\r\n\x1a\n", ImageFormat::Png),
+ (&[0xff, 0xd8, 0xff], ImageFormat::Jpeg),
+ (b"GIF89a", ImageFormat::Gif),
+ (b"GIF87a", ImageFormat::Gif),
+ (b"RIFF", ImageFormat::WebP), // TODO: better magic byte detection, see https://github.com/image-rs/image/issues/660
+ (b"MM\x00*", ImageFormat::Tiff),
+ (b"II*\x00", ImageFormat::Tiff),
+ (b"DDS ", ImageFormat::Dds),
+ (b"BM", ImageFormat::Bmp),
+ (&[0, 0, 1, 0], ImageFormat::Ico),
+ (b"#?RADIANCE", ImageFormat::Hdr),
+ (b"P1", ImageFormat::Pnm),
+ (b"P2", ImageFormat::Pnm),
+ (b"P3", ImageFormat::Pnm),
+ (b"P4", ImageFormat::Pnm),
+ (b"P5", ImageFormat::Pnm),
+ (b"P6", ImageFormat::Pnm),
+ (b"P7", ImageFormat::Pnm),
+];
+
+/// Guess image format from memory block
+///
+/// Makes an educated guess about the image format based on the Magic Bytes at the beginning.
+/// TGA is not supported by this function.
+/// This is not to be trusted on the validity of the whole memory block
+pub fn guess_format(buffer: &[u8]) -> ImageResult<ImageFormat> {
+ match guess_format_impl(buffer) {
+ Some(format) => Ok(format),
+ None => Err(ImageError::Unsupported(ImageFormatHint::Unknown.into())),
+ }
+}
+
+pub(crate) fn guess_format_impl(buffer: &[u8]) -> Option<ImageFormat> {
+ for &(signature, format) in &MAGIC_BYTES {
+ if buffer.starts_with(signature) {
+ return Some(format);
+ }
+ }
+
+ None
+}
+
+impl From<PathError> for ImageError {
+ fn from(path: PathError) -> Self {
+ let format_hint = match path {
+ PathError::NoExtension => ImageFormatHint::Unknown,
+ PathError::UnknownExtension(ext) => ImageFormatHint::PathExtension(ext.into()),
+ };
+ ImageError::Unsupported(format_hint.into())
+ }
+}
diff --git a/third_party/rust/image/src/io/mod.rs b/third_party/rust/image/src/io/mod.rs
new file mode 100644
index 0000000000..e7964c7259
--- /dev/null
+++ b/third_party/rust/image/src/io/mod.rs
@@ -0,0 +1,5 @@
+//! Input and output of images.
+mod reader;
+pub(crate) mod free_functions;
+
+pub use self::reader::Reader;
diff --git a/third_party/rust/image/src/io/reader.rs b/third_party/rust/image/src/io/reader.rs
new file mode 100644
index 0000000000..4da41f458b
--- /dev/null
+++ b/third_party/rust/image/src/io/reader.rs
@@ -0,0 +1,210 @@
+use std::fs::File;
+use std::io::{self, BufRead, BufReader, Cursor, Read, Seek, SeekFrom};
+use std::path::Path;
+
+use crate::dynimage::DynamicImage;
+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.
+///
+#[cfg_attr(feature = "pnm", doc = "```")]
+#[cfg_attr(not(feature = "pnm"), doc = "```no_run")]
+/// # 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));
+///
+/// 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.
+ inner: R,
+ /// The format, if one has been set or deduced.
+ format: Option<ImageFormat>,
+}
+
+impl<R: Read> Reader<R> {
+ /// Create a new image reader without a preset format.
+ ///
+ /// It is possible to guess the format based on the content of the read object with
+ /// [`guess_format`], or to set the format directly with [`set_format`].
+ ///
+ /// [`guess_format`]: #method.guess_format
+ /// [`set_format`]: method.set_format
+ pub fn new(reader: R) -> Self {
+ Reader {
+ inner: reader,
+ format: None,
+ }
+ }
+
+ /// Construct a reader with specified format.
+ pub fn with_format(reader: R, format: ImageFormat) -> Self {
+ Reader {
+ inner: reader,
+ format: Some(format),
+ }
+ }
+
+ /// 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::UnsupportedError` when the image format has not been set.
+ pub fn clear_format(&mut self) {
+ self.format = None;
+ }
+
+ /// 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 [`guess_format`].
+ ///
+ /// [`guess_format`]: #method.guess_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> {
+ let file = File::open(path)?;
+ Ok(Reader {
+ inner: BufReader::new(file),
+ format: ImageFormat::from_path(path).ok(),
+ })
+ }
+}
+
+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 not 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`] 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.seek(SeekFrom::Current(0))?;
+ 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::UnsupportedError`.
+ 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::UnsupportedError`.
+ pub fn decode(mut self) -> ImageResult<DynamicImage> {
+ let format = self.require_format()?;
+ free_functions::load(self.inner, format)
+ }
+
+ fn require_format(&mut self) -> ImageResult<ImageFormat> {
+ self.format.ok_or_else(||
+ ImageError::UnsupportedError("Unable to determine image format".into()))
+ }
+}