diff options
Diffstat (limited to 'vendor/image/src/image.rs')
-rw-r--r-- | vendor/image/src/image.rs | 1915 |
1 files changed, 0 insertions, 1915 deletions
diff --git a/vendor/image/src/image.rs b/vendor/image/src/image.rs deleted file mode 100644 index d131b98..0000000 --- a/vendor/image/src/image.rs +++ /dev/null @@ -1,1915 +0,0 @@ -#![allow(clippy::too_many_arguments)] -use std::convert::TryFrom; -use std::ffi::OsStr; -use std::io; -use std::io::Read; -use std::ops::{Deref, DerefMut}; -use std::path::Path; -use std::usize; - -use crate::color::{ColorType, ExtendedColorType}; -use crate::error::{ - ImageError, ImageFormatHint, ImageResult, LimitError, LimitErrorKind, ParameterError, - ParameterErrorKind, -}; -use crate::math::Rect; -use crate::traits::Pixel; -use crate::ImageBuffer; - -use crate::animation::Frames; - -#[cfg(feature = "pnm")] -use crate::codecs::pnm::PnmSubtype; - -/// An enumeration of supported image formats. -/// Not all formats support both encoding and decoding. -#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] -#[non_exhaustive] -pub enum ImageFormat { - /// An Image in PNG Format - Png, - - /// An Image in JPEG Format - Jpeg, - - /// An Image in GIF Format - Gif, - - /// An Image in WEBP Format - WebP, - - /// An Image in general PNM Format - Pnm, - - /// An Image in TIFF Format - Tiff, - - /// An Image in TGA Format - Tga, - - /// An Image in DDS Format - Dds, - - /// An Image in BMP Format - Bmp, - - /// An Image in ICO Format - Ico, - - /// An Image in Radiance HDR Format - Hdr, - - /// An Image in OpenEXR Format - OpenExr, - - /// An Image in farbfeld Format - Farbfeld, - - /// An Image in AVIF format. - Avif, - - /// An Image in QOI format. - Qoi, -} - -impl ImageFormat { - /// Return the image format specified by a path's file extension. - /// - /// # Example - /// - /// ``` - /// use image::ImageFormat; - /// - /// let format = ImageFormat::from_extension("jpg"); - /// assert_eq!(format, Some(ImageFormat::Jpeg)); - /// ``` - #[inline] - pub fn from_extension<S>(ext: S) -> Option<Self> - where - S: AsRef<OsStr>, - { - // thin wrapper function to strip generics - fn inner(ext: &OsStr) -> Option<ImageFormat> { - let ext = ext.to_str()?.to_ascii_lowercase(); - - Some(match ext.as_str() { - "avif" => ImageFormat::Avif, - "jpg" | "jpeg" => ImageFormat::Jpeg, - "png" => ImageFormat::Png, - "gif" => ImageFormat::Gif, - "webp" => ImageFormat::WebP, - "tif" | "tiff" => ImageFormat::Tiff, - "tga" => ImageFormat::Tga, - "dds" => ImageFormat::Dds, - "bmp" => ImageFormat::Bmp, - "ico" => ImageFormat::Ico, - "hdr" => ImageFormat::Hdr, - "exr" => ImageFormat::OpenExr, - "pbm" | "pam" | "ppm" | "pgm" => ImageFormat::Pnm, - "ff" | "farbfeld" => ImageFormat::Farbfeld, - "qoi" => ImageFormat::Qoi, - _ => return None, - }) - } - - inner(ext.as_ref()) - } - - /// Return the image format specified by the path's file extension. - /// - /// # Example - /// - /// ``` - /// use image::ImageFormat; - /// - /// let format = ImageFormat::from_path("images/ferris.png")?; - /// assert_eq!(format, ImageFormat::Png); - /// - /// # Ok::<(), image::error::ImageError>(()) - /// ``` - #[inline] - pub fn from_path<P>(path: P) -> ImageResult<Self> - where - P: AsRef<Path>, - { - // thin wrapper function to strip generics - fn inner(path: &Path) -> ImageResult<ImageFormat> { - let exact_ext = path.extension(); - exact_ext - .and_then(ImageFormat::from_extension) - .ok_or_else(|| { - let format_hint = match exact_ext { - None => ImageFormatHint::Unknown, - Some(os) => ImageFormatHint::PathExtension(os.into()), - }; - ImageError::Unsupported(format_hint.into()) - }) - } - - inner(path.as_ref()) - } - - /// Return the image format specified by a MIME type. - /// - /// # Example - /// - /// ``` - /// use image::ImageFormat; - /// - /// let format = ImageFormat::from_mime_type("image/png").unwrap(); - /// assert_eq!(format, ImageFormat::Png); - /// ``` - pub fn from_mime_type<M>(mime_type: M) -> Option<Self> - where - M: AsRef<str>, - { - match mime_type.as_ref() { - "image/avif" => Some(ImageFormat::Avif), - "image/jpeg" => Some(ImageFormat::Jpeg), - "image/png" => Some(ImageFormat::Png), - "image/gif" => Some(ImageFormat::Gif), - "image/webp" => Some(ImageFormat::WebP), - "image/tiff" => Some(ImageFormat::Tiff), - "image/x-targa" | "image/x-tga" => Some(ImageFormat::Tga), - "image/vnd-ms.dds" => Some(ImageFormat::Dds), - "image/bmp" => Some(ImageFormat::Bmp), - "image/x-icon" => Some(ImageFormat::Ico), - "image/vnd.radiance" => Some(ImageFormat::Hdr), - "image/x-exr" => Some(ImageFormat::OpenExr), - "image/x-portable-bitmap" - | "image/x-portable-graymap" - | "image/x-portable-pixmap" - | "image/x-portable-anymap" => Some(ImageFormat::Pnm), - // Qoi's MIME type is being worked on. - // See: https://github.com/phoboslab/qoi/issues/167 - "image/x-qoi" => Some(ImageFormat::Qoi), - _ => None, - } - } - - /// Return the MIME type for this image format or "application/octet-stream" if no MIME type - /// exists for the format. - /// - /// Some notes on a few of the MIME types: - /// - /// - The portable anymap format has a separate MIME type for the pixmap, graymap and bitmap - /// formats, but this method returns the general "image/x-portable-anymap" MIME type. - /// - The Targa format has two common MIME types, "image/x-targa" and "image/x-tga"; this - /// method returns "image/x-targa" for that format. - /// - The QOI MIME type is still a work in progress. This method returns "image/x-qoi" for - /// that format. - /// - /// # Example - /// - /// ``` - /// use image::ImageFormat; - /// - /// let mime_type = ImageFormat::Png.to_mime_type(); - /// assert_eq!(mime_type, "image/png"); - /// ``` - pub fn to_mime_type(&self) -> &'static str { - match self { - ImageFormat::Avif => "image/avif", - ImageFormat::Jpeg => "image/jpeg", - ImageFormat::Png => "image/png", - ImageFormat::Gif => "image/gif", - ImageFormat::WebP => "image/webp", - ImageFormat::Tiff => "image/tiff", - // the targa MIME type has two options, but this one seems to be used more - ImageFormat::Tga => "image/x-targa", - ImageFormat::Dds => "image/vnd-ms.dds", - ImageFormat::Bmp => "image/bmp", - ImageFormat::Ico => "image/x-icon", - ImageFormat::Hdr => "image/vnd.radiance", - ImageFormat::OpenExr => "image/x-exr", - // return the most general MIME type - ImageFormat::Pnm => "image/x-portable-anymap", - // Qoi's MIME type is being worked on. - // See: https://github.com/phoboslab/qoi/issues/167 - ImageFormat::Qoi => "image/x-qoi", - // farbfield's MIME type taken from https://www.wikidata.org/wiki/Q28206109 - ImageFormat::Farbfeld => "application/octet-stream", - } - } - - /// Return if the ImageFormat can be decoded by the lib. - #[inline] - pub fn can_read(&self) -> bool { - // Needs to be updated once a new variant's decoder is added to free_functions.rs::load - match self { - ImageFormat::Png => true, - ImageFormat::Gif => true, - ImageFormat::Jpeg => true, - ImageFormat::WebP => true, - ImageFormat::Tiff => true, - ImageFormat::Tga => true, - ImageFormat::Dds => false, - ImageFormat::Bmp => true, - ImageFormat::Ico => true, - ImageFormat::Hdr => true, - ImageFormat::OpenExr => true, - ImageFormat::Pnm => true, - ImageFormat::Farbfeld => true, - ImageFormat::Avif => true, - ImageFormat::Qoi => true, - } - } - - /// Return if the ImageFormat can be encoded by the lib. - #[inline] - pub fn can_write(&self) -> bool { - // Needs to be updated once a new variant's encoder is added to free_functions.rs::save_buffer_with_format_impl - match self { - ImageFormat::Gif => true, - ImageFormat::Ico => true, - ImageFormat::Jpeg => true, - ImageFormat::Png => true, - ImageFormat::Bmp => true, - ImageFormat::Tiff => true, - ImageFormat::Tga => true, - ImageFormat::Pnm => true, - ImageFormat::Farbfeld => true, - ImageFormat::Avif => true, - ImageFormat::WebP => true, - ImageFormat::Hdr => false, - ImageFormat::OpenExr => true, - ImageFormat::Dds => false, - ImageFormat::Qoi => true, - } - } - - /// Return a list of applicable extensions for this format. - /// - /// All currently recognized image formats specify at least on extension but for future - /// compatibility you should not rely on this fact. The list may be empty if the format has no - /// recognized file representation, for example in case it is used as a purely transient memory - /// format. - /// - /// The method name `extensions` remains reserved for introducing another method in the future - /// that yields a slice of `OsStr` which is blocked by several features of const evaluation. - pub fn extensions_str(self) -> &'static [&'static str] { - match self { - ImageFormat::Png => &["png"], - ImageFormat::Jpeg => &["jpg", "jpeg"], - ImageFormat::Gif => &["gif"], - ImageFormat::WebP => &["webp"], - ImageFormat::Pnm => &["pbm", "pam", "ppm", "pgm"], - ImageFormat::Tiff => &["tiff", "tif"], - ImageFormat::Tga => &["tga"], - ImageFormat::Dds => &["dds"], - ImageFormat::Bmp => &["bmp"], - ImageFormat::Ico => &["ico"], - ImageFormat::Hdr => &["hdr"], - ImageFormat::OpenExr => &["exr"], - ImageFormat::Farbfeld => &["ff"], - // According to: https://aomediacodec.github.io/av1-avif/#mime-registration - ImageFormat::Avif => &["avif"], - ImageFormat::Qoi => &["qoi"], - } - } -} - -/// An enumeration of supported image formats for encoding. -#[derive(Clone, PartialEq, Eq, Debug)] -#[non_exhaustive] -pub enum ImageOutputFormat { - #[cfg(feature = "png")] - /// An Image in PNG Format - Png, - - #[cfg(feature = "jpeg")] - /// An Image in JPEG Format with specified quality, up to 100 - Jpeg(u8), - - #[cfg(feature = "pnm")] - /// An Image in one of the PNM Formats - Pnm(PnmSubtype), - - #[cfg(feature = "gif")] - /// An Image in GIF Format - Gif, - - #[cfg(feature = "ico")] - /// An Image in ICO Format - Ico, - - #[cfg(feature = "bmp")] - /// An Image in BMP Format - Bmp, - - #[cfg(feature = "farbfeld")] - /// An Image in farbfeld Format - Farbfeld, - - #[cfg(feature = "tga")] - /// An Image in TGA Format - Tga, - - #[cfg(feature = "exr")] - /// An Image in OpenEXR Format - OpenExr, - - #[cfg(feature = "tiff")] - /// An Image in TIFF Format - Tiff, - - #[cfg(feature = "avif-encoder")] - /// An image in AVIF Format - Avif, - - #[cfg(feature = "qoi")] - /// An image in QOI Format - Qoi, - - #[cfg(feature = "webp-encoder")] - /// An image in WebP Format. - WebP, - - /// A value for signalling an error: An unsupported format was requested - // Note: When TryFrom is stabilized, this value should not be needed, and - // a TryInto<ImageOutputFormat> should be used instead of an Into<ImageOutputFormat>. - Unsupported(String), -} - -impl From<ImageFormat> for ImageOutputFormat { - fn from(fmt: ImageFormat) -> Self { - match fmt { - #[cfg(feature = "png")] - ImageFormat::Png => ImageOutputFormat::Png, - #[cfg(feature = "jpeg")] - ImageFormat::Jpeg => ImageOutputFormat::Jpeg(75), - #[cfg(feature = "pnm")] - ImageFormat::Pnm => ImageOutputFormat::Pnm(PnmSubtype::ArbitraryMap), - #[cfg(feature = "gif")] - ImageFormat::Gif => ImageOutputFormat::Gif, - #[cfg(feature = "ico")] - ImageFormat::Ico => ImageOutputFormat::Ico, - #[cfg(feature = "bmp")] - ImageFormat::Bmp => ImageOutputFormat::Bmp, - #[cfg(feature = "farbfeld")] - ImageFormat::Farbfeld => ImageOutputFormat::Farbfeld, - #[cfg(feature = "tga")] - ImageFormat::Tga => ImageOutputFormat::Tga, - #[cfg(feature = "exr")] - ImageFormat::OpenExr => ImageOutputFormat::OpenExr, - #[cfg(feature = "tiff")] - ImageFormat::Tiff => ImageOutputFormat::Tiff, - - #[cfg(feature = "avif-encoder")] - ImageFormat::Avif => ImageOutputFormat::Avif, - #[cfg(feature = "webp-encoder")] - ImageFormat::WebP => ImageOutputFormat::WebP, - - #[cfg(feature = "qoi")] - ImageFormat::Qoi => ImageOutputFormat::Qoi, - - f => ImageOutputFormat::Unsupported(format!("{:?}", f)), - } - } -} - -// This struct manages buffering associated with implementing `Read` and `Seek` on decoders that can -// must decode ranges of bytes at a time. -#[allow(dead_code)] -// When no image formats that use it are enabled -pub(crate) struct ImageReadBuffer { - scanline_bytes: usize, - buffer: Vec<u8>, - consumed: usize, - - total_bytes: u64, - offset: u64, -} -impl ImageReadBuffer { - /// Create a new ImageReadBuffer. - /// - /// Panics if scanline_bytes doesn't fit into a usize, because that would mean reading anything - /// from the image would take more RAM than the entire virtual address space. In other words, - /// actually using this struct would instantly OOM so just get it out of the way now. - #[allow(dead_code)] - // When no image formats that use it are enabled - pub(crate) fn new(scanline_bytes: u64, total_bytes: u64) -> Self { - Self { - scanline_bytes: usize::try_from(scanline_bytes).unwrap(), - buffer: Vec::new(), - consumed: 0, - total_bytes, - offset: 0, - } - } - - #[allow(dead_code)] - // When no image formats that use it are enabled - pub(crate) fn read<F>(&mut self, buf: &mut [u8], mut read_scanline: F) -> io::Result<usize> - where - F: FnMut(&mut [u8]) -> io::Result<usize>, - { - if self.buffer.len() == self.consumed { - if self.offset == self.total_bytes { - return Ok(0); - } else if buf.len() >= self.scanline_bytes { - // If there is nothing buffered and the user requested a full scanline worth of - // data, skip buffering. - let bytes_read = read_scanline(&mut buf[..self.scanline_bytes])?; - self.offset += u64::try_from(bytes_read).unwrap(); - return Ok(bytes_read); - } else { - // Lazily allocate buffer the first time that read is called with a buffer smaller - // than the scanline size. - if self.buffer.is_empty() { - self.buffer.resize(self.scanline_bytes, 0); - } - - self.consumed = 0; - let bytes_read = read_scanline(&mut self.buffer[..])?; - self.buffer.resize(bytes_read, 0); - self.offset += u64::try_from(bytes_read).unwrap(); - - assert!(bytes_read == self.scanline_bytes || self.offset == self.total_bytes); - } - } - - // Finally, copy bytes into output buffer. - let bytes_buffered = self.buffer.len() - self.consumed; - if bytes_buffered > buf.len() { - buf.copy_from_slice(&self.buffer[self.consumed..][..buf.len()]); - self.consumed += buf.len(); - Ok(buf.len()) - } else { - buf[..bytes_buffered].copy_from_slice(&self.buffer[self.consumed..][..bytes_buffered]); - self.consumed = self.buffer.len(); - Ok(bytes_buffered) - } - } -} - -/// Decodes a specific region of the image, represented by the rectangle -/// starting from ```x``` and ```y``` and having ```length``` and ```width``` -#[allow(dead_code)] -// When no image formats that use it are enabled -pub(crate) fn load_rect<'a, D, F, F1, F2, E>( - x: u32, - y: u32, - width: u32, - height: u32, - buf: &mut [u8], - progress_callback: F, - decoder: &mut D, - mut seek_scanline: F1, - mut read_scanline: F2, -) -> ImageResult<()> -where - D: ImageDecoder<'a>, - F: Fn(Progress), - F1: FnMut(&mut D, u64) -> io::Result<()>, - F2: FnMut(&mut D, &mut [u8]) -> Result<(), E>, - ImageError: From<E>, -{ - let (x, y, width, height) = ( - u64::from(x), - u64::from(y), - u64::from(width), - u64::from(height), - ); - let dimensions = decoder.dimensions(); - let bytes_per_pixel = u64::from(decoder.color_type().bytes_per_pixel()); - let row_bytes = bytes_per_pixel * u64::from(dimensions.0); - let scanline_bytes = decoder.scanline_bytes(); - let total_bytes = width * height * bytes_per_pixel; - - if buf.len() < usize::try_from(total_bytes).unwrap_or(usize::max_value()) { - panic!( - "output buffer too short\n expected `{}`, provided `{}`", - total_bytes, - buf.len() - ); - } - - let mut bytes_read = 0u64; - let mut current_scanline = 0; - let mut tmp = Vec::new(); - let mut tmp_scanline = None; - - { - // Read a range of the image starting from byte number `start` and continuing until byte - // number `end`. Updates `current_scanline` and `bytes_read` appropriately. - let mut read_image_range = |mut start: u64, end: u64| -> ImageResult<()> { - // If the first scanline we need is already stored in the temporary buffer, then handle - // it first. - let target_scanline = start / scanline_bytes; - if tmp_scanline == Some(target_scanline) { - let position = target_scanline * scanline_bytes; - let offset = start.saturating_sub(position); - let len = (end - start) - .min(scanline_bytes - offset) - .min(end - position); - - buf[(bytes_read as usize)..][..len as usize] - .copy_from_slice(&tmp[offset as usize..][..len as usize]); - bytes_read += len; - start += len; - - progress_callback(Progress { - current: bytes_read, - total: total_bytes, - }); - - if start == end { - return Ok(()); - } - } - - let target_scanline = start / scanline_bytes; - if target_scanline != current_scanline { - seek_scanline(decoder, target_scanline)?; - current_scanline = target_scanline; - } - - let mut position = current_scanline * scanline_bytes; - while position < end { - if position >= start && end - position >= scanline_bytes { - read_scanline( - decoder, - &mut buf[(bytes_read as usize)..][..(scanline_bytes as usize)], - )?; - bytes_read += scanline_bytes; - } else { - tmp.resize(scanline_bytes as usize, 0u8); - read_scanline(decoder, &mut tmp)?; - tmp_scanline = Some(current_scanline); - - let offset = start.saturating_sub(position); - let len = (end - start) - .min(scanline_bytes - offset) - .min(end - position); - - buf[(bytes_read as usize)..][..len as usize] - .copy_from_slice(&tmp[offset as usize..][..len as usize]); - bytes_read += len; - } - - current_scanline += 1; - position += scanline_bytes; - progress_callback(Progress { - current: bytes_read, - total: total_bytes, - }); - } - Ok(()) - }; - - if x + width > u64::from(dimensions.0) - || y + height > u64::from(dimensions.1) - || width == 0 - || height == 0 - { - return Err(ImageError::Parameter(ParameterError::from_kind( - ParameterErrorKind::DimensionMismatch, - ))); - } - if scanline_bytes > usize::max_value() as u64 { - return Err(ImageError::Limits(LimitError::from_kind( - LimitErrorKind::InsufficientMemory, - ))); - } - - progress_callback(Progress { - current: 0, - total: total_bytes, - }); - if x == 0 && width == u64::from(dimensions.0) { - let start = x * bytes_per_pixel + y * row_bytes; - let end = (x + width) * bytes_per_pixel + (y + height - 1) * row_bytes; - read_image_range(start, end)?; - } else { - for row in y..(y + height) { - let start = x * bytes_per_pixel + row * row_bytes; - let end = (x + width) * bytes_per_pixel + row * row_bytes; - read_image_range(start, end)?; - } - } - } - - // Seek back to the start - Ok(seek_scanline(decoder, 0)?) -} - -/// Reads all of the bytes of a decoder into a Vec<T>. No particular alignment -/// of the output buffer is guaranteed. -/// -/// Panics if there isn't enough memory to decode the image. -pub(crate) fn decoder_to_vec<'a, T>(decoder: impl ImageDecoder<'a>) -> ImageResult<Vec<T>> -where - T: crate::traits::Primitive + bytemuck::Pod, -{ - let total_bytes = usize::try_from(decoder.total_bytes()); - if total_bytes.is_err() || total_bytes.unwrap() > isize::max_value() as usize { - return Err(ImageError::Limits(LimitError::from_kind( - LimitErrorKind::InsufficientMemory, - ))); - } - - let mut buf = vec![num_traits::Zero::zero(); total_bytes.unwrap() / std::mem::size_of::<T>()]; - decoder.read_image(bytemuck::cast_slice_mut(buf.as_mut_slice()))?; - Ok(buf) -} - -/// Represents the progress of an image operation. -/// -/// Note that this is not necessarily accurate and no change to the values passed to the progress -/// function during decoding will be considered breaking. A decoder could in theory report the -/// progress `(0, 0)` if progress is unknown, without violating the interface contract of the type. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Progress { - current: u64, - total: u64, -} - -impl Progress { - /// Create Progress. Result in invalid progress if you provide a greater `current` than `total`. - pub(crate) fn new(current: u64, total: u64) -> Self { - Self { current, total } - } - - /// A measure of completed decoding. - pub fn current(self) -> u64 { - self.current - } - - /// A measure of all necessary decoding work. - /// - /// This is in general greater or equal than `current`. - pub fn total(self) -> u64 { - self.total - } - - /// Calculate a measure for remaining decoding work. - pub fn remaining(self) -> u64 { - self.total.max(self.current) - self.current - } -} - -/// The trait that all decoders implement -pub trait ImageDecoder<'a>: Sized { - /// The type of reader produced by `into_reader`. - type Reader: Read + 'a; - - /// Returns a tuple containing the width and height of the image - fn dimensions(&self) -> (u32, u32); - - /// Returns the color type of the image data produced by this decoder - fn color_type(&self) -> ColorType; - - /// Returns the color type of the image file before decoding - fn original_color_type(&self) -> ExtendedColorType { - self.color_type().into() - } - - /// Returns the ICC color profile embedded in the image - /// - /// For formats that don't support embedded profiles this function will always return `None`. - /// This feature is currently only supported for the JPEG, PNG, and AVIF formats. - fn icc_profile(&mut self) -> Option<Vec<u8>> { - None - } - - /// Returns a reader that can be used to obtain the bytes of the image. For the best - /// performance, always try to read at least `scanline_bytes` from the reader at a time. Reading - /// fewer bytes will cause the reader to perform internal buffering. - fn into_reader(self) -> ImageResult<Self::Reader>; - - /// Returns the total number of bytes in the decoded image. - /// - /// This is the size of the buffer that must be passed to `read_image` or - /// `read_image_with_progress`. The returned value may exceed usize::MAX, in - /// which case it isn't actually possible to construct a buffer to decode all the image data - /// into. If, however, the size does not fit in a u64 then u64::MAX is returned. - fn total_bytes(&self) -> u64 { - let dimensions = self.dimensions(); - let total_pixels = u64::from(dimensions.0) * u64::from(dimensions.1); - let bytes_per_pixel = u64::from(self.color_type().bytes_per_pixel()); - total_pixels.saturating_mul(bytes_per_pixel) - } - - /// Returns the minimum number of bytes that can be efficiently read from this decoder. This may - /// be as few as 1 or as many as `total_bytes()`. - fn scanline_bytes(&self) -> u64 { - self.total_bytes() - } - - /// Returns all the bytes in the image. - /// - /// This function takes a slice of bytes and writes the pixel data of the image into it. - /// Although not required, for certain color types callers may want to pass buffers which are - /// aligned to 2 or 4 byte boundaries to the slice can be cast to a [u16] or [u32]. To accommodate - /// such casts, the returned contents will always be in native endian. - /// - /// # Panics - /// - /// This function panics if buf.len() != self.total_bytes(). - /// - /// # Examples - /// - /// ```no_build - /// use zerocopy::{AsBytes, FromBytes}; - /// fn read_16bit_image(decoder: impl ImageDecoder) -> Vec<16> { - /// let mut buf: Vec<u16> = vec![0; decoder.total_bytes()/2]; - /// decoder.read_image(buf.as_bytes()); - /// buf - /// } - /// ``` - fn read_image(self, buf: &mut [u8]) -> ImageResult<()> { - self.read_image_with_progress(buf, |_| {}) - } - - /// Same as `read_image` but periodically calls the provided callback to give updates on loading - /// progress. - fn read_image_with_progress<F: Fn(Progress)>( - self, - buf: &mut [u8], - progress_callback: F, - ) -> ImageResult<()> { - assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes())); - - let total_bytes = self.total_bytes() as usize; - let scanline_bytes = self.scanline_bytes() as usize; - let target_read_size = if scanline_bytes < 4096 { - (4096 / scanline_bytes) * scanline_bytes - } else { - scanline_bytes - }; - - let mut reader = self.into_reader()?; - - let mut bytes_read = 0; - while bytes_read < total_bytes { - let read_size = target_read_size.min(total_bytes - bytes_read); - reader.read_exact(&mut buf[bytes_read..][..read_size])?; - bytes_read += read_size; - - progress_callback(Progress { - current: bytes_read as u64, - total: total_bytes as u64, - }); - } - - Ok(()) - } - - /// Set decoding limits for this decoder. See [`Limits`] for the different kinds of - /// limits that is possible to set. - /// - /// Note to implementors: make sure you call [`Limits::check_support`] so that - /// decoding fails if any unsupported strict limits are set. Also make sure - /// you call [`Limits::check_dimensions`] to check the `max_image_width` and - /// `max_image_height` limits. - /// - /// [`Limits`]: ./io/struct.Limits.html - /// [`Limits::check_support`]: ./io/struct.Limits.html#method.check_support - /// [`Limits::check_dimensions`]: ./io/struct.Limits.html#method.check_dimensions - fn set_limits(&mut self, limits: crate::io::Limits) -> ImageResult<()> { - limits.check_support(&crate::io::LimitSupport::default())?; - - let (width, height) = self.dimensions(); - limits.check_dimensions(width, height)?; - - Ok(()) - } -} - -/// Specialized image decoding not be supported by all formats -pub trait ImageDecoderRect<'a>: ImageDecoder<'a> + Sized { - /// Decode a rectangular section of the image; see [`read_rect_with_progress()`](#fn.read_rect_with_progress). - fn read_rect( - &mut self, - x: u32, - y: u32, - width: u32, - height: u32, - buf: &mut [u8], - ) -> ImageResult<()> { - self.read_rect_with_progress(x, y, width, height, buf, |_| {}) - } - - /// Decode a rectangular section of the image, periodically reporting progress. - /// - /// The output buffer will be filled with fields specified by - /// [`ImageDecoder::color_type()`](trait.ImageDecoder.html#fn.color_type), - /// in that order, each field represented in native-endian. - /// - /// The progress callback will be called at least once at the start and the end of decoding, - /// implementations are encouraged to call this more often, - /// with a frequency meaningful for display to the end-user. - /// - /// This function will panic if the output buffer isn't at least - /// `color_type().bytes_per_pixel() * color_type().channel_count() * width * height` bytes long. - fn read_rect_with_progress<F: Fn(Progress)>( - &mut self, - x: u32, - y: u32, - width: u32, - height: u32, - buf: &mut [u8], - progress_callback: F, - ) -> ImageResult<()>; -} - -/// AnimationDecoder trait -pub trait AnimationDecoder<'a> { - /// Consume the decoder producing a series of frames. - fn into_frames(self) -> Frames<'a>; -} - -/// The trait all encoders implement -pub trait ImageEncoder { - /// Writes all the bytes in an image to the encoder. - /// - /// This function takes a slice of bytes of the pixel data of the image - /// and encodes them. Unlike particular format encoders inherent impl encode - /// methods where endianness is not specified, here image data bytes should - /// always be in native endian. The implementor will reorder the endianness - /// as necessary for the target encoding format. - /// - /// See also `ImageDecoder::read_image` which reads byte buffers into - /// native endian. - fn write_image( - self, - buf: &[u8], - width: u32, - height: u32, - color_type: ColorType, - ) -> ImageResult<()>; -} - -/// Immutable pixel iterator -#[derive(Debug)] -pub struct Pixels<'a, I: ?Sized + 'a> { - image: &'a I, - x: u32, - y: u32, - width: u32, - height: u32, -} - -impl<'a, I: GenericImageView> Iterator for Pixels<'a, I> { - type Item = (u32, u32, I::Pixel); - - fn next(&mut self) -> Option<(u32, u32, I::Pixel)> { - if self.x >= self.width { - self.x = 0; - self.y += 1; - } - - if self.y >= self.height { - None - } else { - let pixel = self.image.get_pixel(self.x, self.y); - let p = (self.x, self.y, pixel); - - self.x += 1; - - Some(p) - } - } -} - -impl<I: ?Sized> Clone for Pixels<'_, I> { - fn clone(&self) -> Self { - Pixels { ..*self } - } -} - -/// Trait to inspect an image. -/// -/// ``` -/// use image::{GenericImageView, Rgb, RgbImage}; -/// -/// let buffer = RgbImage::new(10, 10); -/// let image: &dyn GenericImageView<Pixel=Rgb<u8>> = &buffer; -/// ``` -pub trait GenericImageView { - /// The type of pixel. - type Pixel: Pixel; - - /// The width and height of this image. - fn dimensions(&self) -> (u32, u32); - - /// The width of this image. - fn width(&self) -> u32 { - let (w, _) = self.dimensions(); - w - } - - /// The height of this image. - fn height(&self) -> u32 { - let (_, h) = self.dimensions(); - h - } - - /// The bounding rectangle of this image. - fn bounds(&self) -> (u32, u32, u32, u32); - - /// Returns true if this x, y coordinate is contained inside the image. - fn in_bounds(&self, x: u32, y: u32) -> bool { - let (ix, iy, iw, ih) = self.bounds(); - x >= ix && x < ix + iw && y >= iy && y < iy + ih - } - - /// Returns the pixel located at (x, y). Indexed from top left. - /// - /// # Panics - /// - /// Panics if `(x, y)` is out of bounds. - fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel; - - /// Returns the pixel located at (x, y). Indexed from top left. - /// - /// This function can be implemented in a way that ignores bounds checking. - /// # Safety - /// - /// The coordinates must be [`in_bounds`] of the image. - /// - /// [`in_bounds`]: #method.in_bounds - unsafe fn unsafe_get_pixel(&self, x: u32, y: u32) -> Self::Pixel { - self.get_pixel(x, y) - } - - /// Returns an Iterator over the pixels of this image. - /// The iterator yields the coordinates of each pixel - /// along with their value - fn pixels(&self) -> Pixels<Self> - where - Self: Sized, - { - let (width, height) = self.dimensions(); - - Pixels { - image: self, - x: 0, - y: 0, - width, - height, - } - } - - /// Returns a subimage that is an immutable view into this image. - /// You can use [`GenericImage::sub_image`] if you need a mutable view instead. - /// The coordinates set the position of the top left corner of the view. - fn view(&self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&Self> - where - Self: Sized, - { - assert!(x as u64 + width as u64 <= self.width() as u64); - assert!(y as u64 + height as u64 <= self.height() as u64); - SubImage::new(self, x, y, width, height) - } -} - -/// A trait for manipulating images. -pub trait GenericImage: GenericImageView { - /// Gets a reference to the mutable pixel at location `(x, y)`. Indexed from top left. - /// - /// # Panics - /// - /// Panics if `(x, y)` is out of bounds. - /// - /// Panics for dynamic images (this method is deprecated and will be removed). - /// - /// ## Known issues - /// - /// This requires the buffer to contain a unique set of continuous channels in the exact order - /// and byte representation that the pixel type requires. This is somewhat restrictive. - /// - /// TODO: Maybe use some kind of entry API? this would allow pixel type conversion on the fly - /// while still doing only one array lookup: - /// - /// ```ignore - /// let px = image.pixel_entry_at(x,y); - /// px.set_from_rgba(rgba) - /// ``` - #[deprecated(since = "0.24.0", note = "Use `get_pixel` and `put_pixel` instead.")] - fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut Self::Pixel; - - /// Put a pixel at location (x, y). Indexed from top left. - /// - /// # Panics - /// - /// Panics if `(x, y)` is out of bounds. - fn put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel); - - /// Puts a pixel at location (x, y). Indexed from top left. - /// - /// This function can be implemented in a way that ignores bounds checking. - /// # Safety - /// - /// The coordinates must be [`in_bounds`] of the image. - /// - /// [`in_bounds`]: traits.GenericImageView.html#method.in_bounds - unsafe fn unsafe_put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) { - self.put_pixel(x, y, pixel); - } - - /// Put a pixel at location (x, y), taking into account alpha channels - #[deprecated( - since = "0.24.0", - note = "Use iterator `pixels_mut` to blend the pixels directly" - )] - fn blend_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel); - - /// Copies all of the pixels from another image into this image. - /// - /// The other image is copied with the top-left corner of the - /// other image placed at (x, y). - /// - /// In order to copy only a piece of the other image, use [`GenericImageView::view`]. - /// - /// You can use [`FlatSamples`] to source pixels from an arbitrary regular raster of channel - /// values, for example from a foreign interface or a fixed image. - /// - /// # Returns - /// Returns an error if the image is too large to be copied at the given position - /// - /// [`GenericImageView::view`]: trait.GenericImageView.html#method.view - /// [`FlatSamples`]: flat/struct.FlatSamples.html - fn copy_from<O>(&mut self, other: &O, x: u32, y: u32) -> ImageResult<()> - where - O: GenericImageView<Pixel = Self::Pixel>, - { - // Do bounds checking here so we can use the non-bounds-checking - // functions to copy pixels. - if self.width() < other.width() + x || self.height() < other.height() + y { - return Err(ImageError::Parameter(ParameterError::from_kind( - ParameterErrorKind::DimensionMismatch, - ))); - } - - for k in 0..other.height() { - for i in 0..other.width() { - let p = other.get_pixel(i, k); - self.put_pixel(i + x, k + y, p); - } - } - Ok(()) - } - - /// Copies all of the pixels from one part of this image to another part of this image. - /// - /// The destination rectangle of the copy is specified with the top-left corner placed at (x, y). - /// - /// # Returns - /// `true` if the copy was successful, `false` if the image could not - /// be copied due to size constraints. - fn copy_within(&mut self, source: Rect, x: u32, y: u32) -> bool { - let Rect { - x: sx, - y: sy, - width, - height, - } = source; - let dx = x; - let dy = y; - assert!(sx < self.width() && dx < self.width()); - assert!(sy < self.height() && dy < self.height()); - if self.width() - dx.max(sx) < width || self.height() - dy.max(sy) < height { - return false; - } - // since `.rev()` creates a new dype we would either have to go with dynamic dispatch for the ranges - // or have quite a lot of code bloat. A macro gives us static dispatch with less visible bloat. - macro_rules! copy_within_impl_ { - ($xiter:expr, $yiter:expr) => { - for y in $yiter { - let sy = sy + y; - let dy = dy + y; - for x in $xiter { - let sx = sx + x; - let dx = dx + x; - let pixel = self.get_pixel(sx, sy); - self.put_pixel(dx, dy, pixel); - } - } - }; - } - // check how target and source rectangles relate to each other so we dont overwrite data before we copied it. - match (sx < dx, sy < dy) { - (true, true) => copy_within_impl_!((0..width).rev(), (0..height).rev()), - (true, false) => copy_within_impl_!((0..width).rev(), 0..height), - (false, true) => copy_within_impl_!(0..width, (0..height).rev()), - (false, false) => copy_within_impl_!(0..width, 0..height), - } - true - } - - /// Returns a mutable subimage that is a view into this image. - /// If you want an immutable subimage instead, use [`GenericImageView::view`] - /// The coordinates set the position of the top left corner of the SubImage. - fn sub_image(&mut self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&mut Self> - where - Self: Sized, - { - assert!(x as u64 + width as u64 <= self.width() as u64); - assert!(y as u64 + height as u64 <= self.height() as u64); - SubImage::new(self, x, y, width, height) - } -} - -/// A View into another image -/// -/// Instances of this struct can be created using: -/// - [`GenericImage::sub_image`] to create a mutable view, -/// - [`GenericImageView::view`] to create an immutable view, -/// - [`SubImage::new`] to instantiate the struct directly. -/// -/// Note that this does _not_ implement `GenericImage`, but it dereferences to one which allows you -/// to use it as if it did. See [Design Considerations](#Design-Considerations) below for details. -/// -/// # Design Considerations -/// -/// For reasons relating to coherence, this is not itself a `GenericImage` or a `GenericImageView`. -/// In short, we want to reserve the ability of adding traits implemented for _all_ generic images -/// but in a different manner for `SubImage`. This may be required to ensure that stacking -/// sub-images comes at no double indirect cost. -/// -/// If, ultimately, this is not needed then a directly implementation of `GenericImage` can and -/// will get added. This inconvenience may alternatively get resolved if Rust allows some forms of -/// specialization, which might make this trick unnecessary and thus also allows for a direct -/// implementation. -#[derive(Copy, Clone)] -pub struct SubImage<I> { - inner: SubImageInner<I>, -} - -/// The inner type of `SubImage` that implements `GenericImage{,View}`. -/// -/// This type is _nominally_ `pub` but it is not exported from the crate. It should be regarded as -/// an existential type in any case. -#[derive(Copy, Clone)] -pub struct SubImageInner<I> { - image: I, - xoffset: u32, - yoffset: u32, - xstride: u32, - ystride: u32, -} - -/// Alias to access Pixel behind a reference -type DerefPixel<I> = <<I as Deref>::Target as GenericImageView>::Pixel; - -/// Alias to access Subpixel behind a reference -type DerefSubpixel<I> = <DerefPixel<I> as Pixel>::Subpixel; - -impl<I> SubImage<I> { - /// Construct a new subimage - /// The coordinates set the position of the top left corner of the SubImage. - pub fn new(image: I, x: u32, y: u32, width: u32, height: u32) -> SubImage<I> { - SubImage { - inner: SubImageInner { - image, - xoffset: x, - yoffset: y, - xstride: width, - ystride: height, - }, - } - } - - /// Change the coordinates of this subimage. - pub fn change_bounds(&mut self, x: u32, y: u32, width: u32, height: u32) { - self.inner.xoffset = x; - self.inner.yoffset = y; - self.inner.xstride = width; - self.inner.ystride = height; - } - - /// Convert this subimage to an ImageBuffer - pub fn to_image(&self) -> ImageBuffer<DerefPixel<I>, Vec<DerefSubpixel<I>>> - where - I: Deref, - I::Target: GenericImageView + 'static, - { - let mut out = ImageBuffer::new(self.inner.xstride, self.inner.ystride); - let borrowed = self.inner.image.deref(); - - for y in 0..self.inner.ystride { - for x in 0..self.inner.xstride { - let p = borrowed.get_pixel(x + self.inner.xoffset, y + self.inner.yoffset); - out.put_pixel(x, y, p); - } - } - - out - } -} - -/// Methods for readable images. -impl<I> SubImage<I> -where - I: Deref, - I::Target: GenericImageView, -{ - /// Create a sub-view of the image. - /// - /// The coordinates given are relative to the current view on the underlying image. - /// - /// Note that this method is preferred to the one from `GenericImageView`. This is accessible - /// with the explicit method call syntax but it should rarely be needed due to causing an - /// extra level of indirection. - /// - /// ``` - /// use image::{GenericImageView, RgbImage, SubImage}; - /// let buffer = RgbImage::new(10, 10); - /// - /// let subimage: SubImage<&RgbImage> = buffer.view(0, 0, 10, 10); - /// let subview: SubImage<&RgbImage> = subimage.view(0, 0, 10, 10); - /// - /// // Less efficient and NOT &RgbImage - /// let _: SubImage<&_> = GenericImageView::view(&*subimage, 0, 0, 10, 10); - /// ``` - pub fn view(&self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&I::Target> { - use crate::GenericImageView as _; - assert!(x as u64 + width as u64 <= self.inner.width() as u64); - assert!(y as u64 + height as u64 <= self.inner.height() as u64); - let x = self.inner.xoffset + x; - let y = self.inner.yoffset + y; - SubImage::new(&*self.inner.image, x, y, width, height) - } - - /// Get a reference to the underlying image. - pub fn inner(&self) -> &I::Target { - &self.inner.image - } -} - -impl<I> SubImage<I> -where - I: DerefMut, - I::Target: GenericImage, -{ - /// Create a mutable sub-view of the image. - /// - /// The coordinates given are relative to the current view on the underlying image. - pub fn sub_image( - &mut self, - x: u32, - y: u32, - width: u32, - height: u32, - ) -> SubImage<&mut I::Target> { - assert!(x as u64 + width as u64 <= self.inner.width() as u64); - assert!(y as u64 + height as u64 <= self.inner.height() as u64); - let x = self.inner.xoffset + x; - let y = self.inner.yoffset + y; - SubImage::new(&mut *self.inner.image, x, y, width, height) - } - - /// Get a mutable reference to the underlying image. - pub fn inner_mut(&mut self) -> &mut I::Target { - &mut self.inner.image - } -} - -impl<I> Deref for SubImage<I> -where - I: Deref, -{ - type Target = SubImageInner<I>; - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -impl<I> DerefMut for SubImage<I> -where - I: DerefMut, -{ - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner - } -} - -#[allow(deprecated)] -impl<I> GenericImageView for SubImageInner<I> -where - I: Deref, - I::Target: GenericImageView, -{ - type Pixel = DerefPixel<I>; - - fn dimensions(&self) -> (u32, u32) { - (self.xstride, self.ystride) - } - - fn bounds(&self) -> (u32, u32, u32, u32) { - (self.xoffset, self.yoffset, self.xstride, self.ystride) - } - - fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel { - self.image.get_pixel(x + self.xoffset, y + self.yoffset) - } -} - -#[allow(deprecated)] -impl<I> GenericImage for SubImageInner<I> -where - I: DerefMut, - I::Target: GenericImage + Sized, -{ - fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut Self::Pixel { - self.image.get_pixel_mut(x + self.xoffset, y + self.yoffset) - } - - fn put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) { - self.image - .put_pixel(x + self.xoffset, y + self.yoffset, pixel) - } - - /// DEPRECATED: This method will be removed. Blend the pixel directly instead. - fn blend_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) { - self.image - .blend_pixel(x + self.xoffset, y + self.yoffset, pixel) - } -} - -#[cfg(test)] -mod tests { - use std::io; - use std::path::Path; - - use super::{ - load_rect, ColorType, GenericImage, GenericImageView, ImageDecoder, ImageFormat, - ImageResult, - }; - use crate::color::Rgba; - use crate::math::Rect; - use crate::{GrayImage, ImageBuffer}; - - #[test] - #[allow(deprecated)] - /// Test that alpha blending works as expected - fn test_image_alpha_blending() { - let mut target = ImageBuffer::new(1, 1); - target.put_pixel(0, 0, Rgba([255u8, 0, 0, 255])); - assert!(*target.get_pixel(0, 0) == Rgba([255, 0, 0, 255])); - target.blend_pixel(0, 0, Rgba([0, 255, 0, 255])); - assert!(*target.get_pixel(0, 0) == Rgba([0, 255, 0, 255])); - - // Blending an alpha channel onto a solid background - target.blend_pixel(0, 0, Rgba([255, 0, 0, 127])); - assert!(*target.get_pixel(0, 0) == Rgba([127, 127, 0, 255])); - - // Blending two alpha channels - target.put_pixel(0, 0, Rgba([0, 255, 0, 127])); - target.blend_pixel(0, 0, Rgba([255, 0, 0, 127])); - assert!(*target.get_pixel(0, 0) == Rgba([169, 85, 0, 190])); - } - - #[test] - fn test_in_bounds() { - let mut target = ImageBuffer::new(2, 2); - target.put_pixel(0, 0, Rgba([255u8, 0, 0, 255])); - - assert!(target.in_bounds(0, 0)); - assert!(target.in_bounds(1, 0)); - assert!(target.in_bounds(0, 1)); - assert!(target.in_bounds(1, 1)); - - assert!(!target.in_bounds(2, 0)); - assert!(!target.in_bounds(0, 2)); - assert!(!target.in_bounds(2, 2)); - } - - #[test] - fn test_can_subimage_clone_nonmut() { - let mut source = ImageBuffer::new(3, 3); - source.put_pixel(1, 1, Rgba([255u8, 0, 0, 255])); - - // A non-mutable copy of the source image - let source = source.clone(); - - // Clone a view into non-mutable to a separate buffer - let cloned = source.view(1, 1, 1, 1).to_image(); - - assert!(cloned.get_pixel(0, 0) == source.get_pixel(1, 1)); - } - - #[test] - fn test_can_nest_views() { - let mut source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); - - { - let mut sub1 = source.sub_image(0, 0, 2, 2); - let mut sub2 = sub1.sub_image(1, 1, 1, 1); - sub2.put_pixel(0, 0, Rgba([0, 0, 0, 0])); - } - - assert_eq!(*source.get_pixel(1, 1), Rgba([0, 0, 0, 0])); - - let view1 = source.view(0, 0, 2, 2); - assert_eq!(*source.get_pixel(1, 1), view1.get_pixel(1, 1)); - - let view2 = view1.view(1, 1, 1, 1); - assert_eq!(*source.get_pixel(1, 1), view2.get_pixel(0, 0)); - } - - #[test] - #[should_panic] - fn test_view_out_of_bounds() { - let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); - source.view(1, 1, 3, 3); - } - - #[test] - #[should_panic] - fn test_view_coordinates_out_of_bounds() { - let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); - source.view(3, 3, 3, 3); - } - - #[test] - #[should_panic] - fn test_view_width_out_of_bounds() { - let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); - source.view(1, 1, 3, 2); - } - - #[test] - #[should_panic] - fn test_view_height_out_of_bounds() { - let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); - source.view(1, 1, 2, 3); - } - - #[test] - #[should_panic] - fn test_view_x_out_of_bounds() { - let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); - source.view(3, 1, 3, 3); - } - - #[test] - #[should_panic] - fn test_view_y_out_of_bounds() { - let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); - source.view(1, 3, 3, 3); - } - - #[test] - fn test_view_in_bounds() { - let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); - source.view(0, 0, 3, 3); - source.view(1, 1, 2, 2); - source.view(2, 2, 0, 0); - } - - #[test] - fn test_copy_sub_image() { - let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); - let view = source.view(0, 0, 3, 3); - let mut views = Vec::new(); - views.push(view); - view.to_image(); - } - - #[test] - fn test_load_rect() { - struct MockDecoder { - scanline_number: u64, - scanline_bytes: u64, - } - impl<'a> ImageDecoder<'a> for MockDecoder { - type Reader = Box<dyn io::Read>; - fn dimensions(&self) -> (u32, u32) { - (5, 5) - } - fn color_type(&self) -> ColorType { - ColorType::L8 - } - fn into_reader(self) -> ImageResult<Self::Reader> { - unimplemented!() - } - fn scanline_bytes(&self) -> u64 { - self.scanline_bytes - } - } - - const DATA: [u8; 25] = [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, - ]; - - fn seek_scanline(m: &mut MockDecoder, n: u64) -> io::Result<()> { - m.scanline_number = n; - Ok(()) - } - fn read_scanline(m: &mut MockDecoder, buf: &mut [u8]) -> io::Result<()> { - let bytes_read = m.scanline_number * m.scanline_bytes; - if bytes_read >= 25 { - return Ok(()); - } - - let len = m.scanline_bytes.min(25 - bytes_read); - buf[..(len as usize)].copy_from_slice(&DATA[(bytes_read as usize)..][..(len as usize)]); - m.scanline_number += 1; - Ok(()) - } - - for scanline_bytes in 1..30 { - let mut output = [0u8; 26]; - - load_rect( - 0, - 0, - 5, - 5, - &mut output, - |_| {}, - &mut MockDecoder { - scanline_number: 0, - scanline_bytes, - }, - seek_scanline, - read_scanline, - ) - .unwrap(); - assert_eq!(output[0..25], DATA); - assert_eq!(output[25], 0); - - output = [0u8; 26]; - load_rect( - 3, - 2, - 1, - 1, - &mut output, - |_| {}, - &mut MockDecoder { - scanline_number: 0, - scanline_bytes, - }, - seek_scanline, - read_scanline, - ) - .unwrap(); - assert_eq!(output[0..2], [13, 0]); - - output = [0u8; 26]; - load_rect( - 3, - 2, - 2, - 2, - &mut output, - |_| {}, - &mut MockDecoder { - scanline_number: 0, - scanline_bytes, - }, - seek_scanline, - read_scanline, - ) - .unwrap(); - assert_eq!(output[0..5], [13, 14, 18, 19, 0]); - - output = [0u8; 26]; - load_rect( - 1, - 1, - 2, - 4, - &mut output, - |_| {}, - &mut MockDecoder { - scanline_number: 0, - scanline_bytes, - }, - seek_scanline, - read_scanline, - ) - .unwrap(); - assert_eq!(output[0..9], [6, 7, 11, 12, 16, 17, 21, 22, 0]); - } - } - - #[test] - fn test_load_rect_single_scanline() { - const DATA: [u8; 25] = [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, - ]; - - struct MockDecoder; - impl<'a> ImageDecoder<'a> for MockDecoder { - type Reader = Box<dyn io::Read>; - fn dimensions(&self) -> (u32, u32) { - (5, 5) - } - fn color_type(&self) -> ColorType { - ColorType::L8 - } - fn into_reader(self) -> ImageResult<Self::Reader> { - unimplemented!() - } - fn scanline_bytes(&self) -> u64 { - 25 - } - } - - // Ensure that seek scanline is called only once. - let mut seeks = 0; - let seek_scanline = |_d: &mut MockDecoder, n: u64| -> io::Result<()> { - seeks += 1; - assert_eq!(n, 0); - assert_eq!(seeks, 1); - Ok(()) - }; - - fn read_scanline(_m: &mut MockDecoder, buf: &mut [u8]) -> io::Result<()> { - buf.copy_from_slice(&DATA); - Ok(()) - } - - let mut output = [0; 26]; - load_rect( - 1, - 1, - 2, - 4, - &mut output, - |_| {}, - &mut MockDecoder, - seek_scanline, - read_scanline, - ) - .unwrap(); - assert_eq!(output[0..9], [6, 7, 11, 12, 16, 17, 21, 22, 0]); - } - - #[test] - fn test_image_format_from_path() { - fn from_path(s: &str) -> ImageResult<ImageFormat> { - ImageFormat::from_path(Path::new(s)) - } - assert_eq!(from_path("./a.jpg").unwrap(), ImageFormat::Jpeg); - assert_eq!(from_path("./a.jpeg").unwrap(), ImageFormat::Jpeg); - assert_eq!(from_path("./a.JPEG").unwrap(), ImageFormat::Jpeg); - assert_eq!(from_path("./a.pNg").unwrap(), ImageFormat::Png); - assert_eq!(from_path("./a.gif").unwrap(), ImageFormat::Gif); - assert_eq!(from_path("./a.webp").unwrap(), ImageFormat::WebP); - assert_eq!(from_path("./a.tiFF").unwrap(), ImageFormat::Tiff); - assert_eq!(from_path("./a.tif").unwrap(), ImageFormat::Tiff); - assert_eq!(from_path("./a.tga").unwrap(), ImageFormat::Tga); - assert_eq!(from_path("./a.dds").unwrap(), ImageFormat::Dds); - assert_eq!(from_path("./a.bmp").unwrap(), ImageFormat::Bmp); - assert_eq!(from_path("./a.Ico").unwrap(), ImageFormat::Ico); - assert_eq!(from_path("./a.hdr").unwrap(), ImageFormat::Hdr); - assert_eq!(from_path("./a.exr").unwrap(), ImageFormat::OpenExr); - assert_eq!(from_path("./a.pbm").unwrap(), ImageFormat::Pnm); - assert_eq!(from_path("./a.pAM").unwrap(), ImageFormat::Pnm); - assert_eq!(from_path("./a.Ppm").unwrap(), ImageFormat::Pnm); - assert_eq!(from_path("./a.pgm").unwrap(), ImageFormat::Pnm); - assert_eq!(from_path("./a.AViF").unwrap(), ImageFormat::Avif); - assert!(from_path("./a.txt").is_err()); - assert!(from_path("./a").is_err()); - } - - #[test] - fn test_generic_image_copy_within_oob() { - let mut image: GrayImage = ImageBuffer::from_raw(4, 4, vec![0u8; 16]).unwrap(); - assert!(!image.sub_image(0, 0, 4, 4).copy_within( - Rect { - x: 0, - y: 0, - width: 5, - height: 4 - }, - 0, - 0 - )); - assert!(!image.sub_image(0, 0, 4, 4).copy_within( - Rect { - x: 0, - y: 0, - width: 4, - height: 5 - }, - 0, - 0 - )); - assert!(!image.sub_image(0, 0, 4, 4).copy_within( - Rect { - x: 1, - y: 0, - width: 4, - height: 4 - }, - 0, - 0 - )); - assert!(!image.sub_image(0, 0, 4, 4).copy_within( - Rect { - x: 0, - y: 0, - width: 4, - height: 4 - }, - 1, - 0 - )); - assert!(!image.sub_image(0, 0, 4, 4).copy_within( - Rect { - x: 0, - y: 1, - width: 4, - height: 4 - }, - 0, - 0 - )); - assert!(!image.sub_image(0, 0, 4, 4).copy_within( - Rect { - x: 0, - y: 0, - width: 4, - height: 4 - }, - 0, - 1 - )); - assert!(!image.sub_image(0, 0, 4, 4).copy_within( - Rect { - x: 1, - y: 1, - width: 4, - height: 4 - }, - 0, - 0 - )); - } - - #[test] - fn test_generic_image_copy_within_tl() { - let data = &[ - 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, - ]; - let expected = [ - 00, 01, 02, 03, 04, 00, 01, 02, 08, 04, 05, 06, 12, 08, 09, 10, - ]; - let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap(); - assert!(image.sub_image(0, 0, 4, 4).copy_within( - Rect { - x: 0, - y: 0, - width: 3, - height: 3 - }, - 1, - 1 - )); - assert_eq!(&image.into_raw(), &expected); - } - - #[test] - fn test_generic_image_copy_within_tr() { - let data = &[ - 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, - ]; - let expected = [ - 00, 01, 02, 03, 01, 02, 03, 07, 05, 06, 07, 11, 09, 10, 11, 15, - ]; - let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap(); - assert!(image.sub_image(0, 0, 4, 4).copy_within( - Rect { - x: 1, - y: 0, - width: 3, - height: 3 - }, - 0, - 1 - )); - assert_eq!(&image.into_raw(), &expected); - } - - #[test] - fn test_generic_image_copy_within_bl() { - let data = &[ - 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, - ]; - let expected = [ - 00, 04, 05, 06, 04, 08, 09, 10, 08, 12, 13, 14, 12, 13, 14, 15, - ]; - let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap(); - assert!(image.sub_image(0, 0, 4, 4).copy_within( - Rect { - x: 0, - y: 1, - width: 3, - height: 3 - }, - 1, - 0 - )); - assert_eq!(&image.into_raw(), &expected); - } - - #[test] - fn test_generic_image_copy_within_br() { - let data = &[ - 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, - ]; - let expected = [ - 05, 06, 07, 03, 09, 10, 11, 07, 13, 14, 15, 11, 12, 13, 14, 15, - ]; - let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap(); - assert!(image.sub_image(0, 0, 4, 4).copy_within( - Rect { - x: 1, - y: 1, - width: 3, - height: 3 - }, - 0, - 0 - )); - assert_eq!(&image.into_raw(), &expected); - } - - #[test] - fn image_formats_are_recognized() { - use ImageFormat::*; - const ALL_FORMATS: &'static [ImageFormat] = &[ - Avif, Png, Jpeg, Gif, WebP, Pnm, Tiff, Tga, Dds, Bmp, Ico, Hdr, Farbfeld, OpenExr, - ]; - for &format in ALL_FORMATS { - let mut file = Path::new("file.nothing").to_owned(); - for ext in format.extensions_str() { - assert!(file.set_extension(ext)); - match ImageFormat::from_path(&file) { - Err(_) => panic!("Path {} not recognized as {:?}", file.display(), format), - Ok(result) => assert_eq!(format, result), - } - } - } - } - - #[test] - fn total_bytes_overflow() { - struct D; - impl<'a> ImageDecoder<'a> for D { - type Reader = std::io::Cursor<Vec<u8>>; - fn color_type(&self) -> ColorType { - ColorType::Rgb8 - } - fn dimensions(&self) -> (u32, u32) { - (0xffffffff, 0xffffffff) - } - fn into_reader(self) -> ImageResult<Self::Reader> { - unreachable!() - } - } - assert_eq!(D.total_bytes(), u64::max_value()); - - let v: ImageResult<Vec<u8>> = super::decoder_to_vec(D); - assert!(v.is_err()); - } -} |