diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/image/src/image.rs | |
parent | Initial commit. (diff) | |
download | firefox-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/image.rs')
-rw-r--r-- | third_party/rust/image/src/image.rs | 1088 |
1 files changed, 1088 insertions, 0 deletions
diff --git a/third_party/rust/image/src/image.rs b/third_party/rust/image/src/image.rs new file mode 100644 index 0000000000..f761902b2a --- /dev/null +++ b/third_party/rust/image/src/image.rs @@ -0,0 +1,1088 @@ +#![allow(clippy::too_many_arguments)] +use std::convert::TryFrom; +use std::io; +use std::io::Read; +use std::ops::{Deref, DerefMut}; +use std::path::Path; + +use crate::buffer::{ImageBuffer, Pixel}; +use crate::color::{ColorType, ExtendedColorType}; +use crate::error::{ImageError, ImageResult}; +use crate::math::Rect; + +use crate::animation::Frames; + +#[cfg(feature = "pnm")] +use crate::pnm::PNMSubtype; + +/// An enumeration of supported image formats. +/// Not all formats support both encoding and decoding. +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +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, + + #[doc(hidden)] + __NonExhaustive(crate::utils::NonExhaustiveMarker), +} + +impl ImageFormat { + /// Return the image format specified by the path's file extension. + pub fn from_path<P>(path: P) -> ImageResult<Self> where P : AsRef<Path> { + // thin wrapper function to strip generics before calling from_path_impl + crate::io::free_functions::guess_format_from_path_impl(path.as_ref()) + .map_err(Into::into) + } +} + +/// An enumeration of supported image formats for encoding. +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum ImageOutputFormat { + #[cfg(feature = "png")] + /// An Image in PNG Format + Png, + + #[cfg(feature = "jpeg")] + /// An Image in JPEG Format with specified quality + 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, + + /// 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), + + #[doc(hidden)] + __NonExhaustive(crate::utils::NonExhaustiveMarker), +} + +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, + + f => ImageOutputFormat::Unsupported(format!( + "Image format {:?} not supported for encoding.", + f + )), + } + } +} + +// This struct manages buffering associated with implementing `Read` and `Seek` on decoders that can +// must decode ranges of bytes at a time. +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. + 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, + } + } + + 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() { + crate::copy_memory(&self.buffer[self.consumed..][..buf.len()], &mut buf[..]); + self.consumed += buf.len(); + Ok(buf.len()) + } else { + crate::copy_memory(&self.buffer[self.consumed..], &mut buf[..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``` +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<usize, 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; + + let mut bytes_read = 0u64; + let mut current_scanline = 0; + let mut tmp = Vec::new(); + + { + // Read a range of the image starting from byte number `start` and continuing until byte + // number `end`. Updates `current_scanline` and `bytes_read` appropiately. + let mut read_image_range = |start: u64, end: u64| -> ImageResult<()> { + 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)?; + + 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.0) + || width == 0 || height == 0 { + return Err(ImageError::DimensionError); + } + if scanline_bytes > usize::max_value() as u64 { + return Err(ImageError::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 mut buf = vec![num_traits::Zero::zero(); usize::try_from(decoder.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 { + /// 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; + + /// Retuns the color type of the image file before decoding + fn original_color_type(&self) -> ExtendedColorType { + self.color_type().into() + } + + /// 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. + fn total_bytes(&self) -> u64 { + let dimensions = self.dimensions(); + u64::from(dimensions.0) * u64::from(dimensions.1) * u64::from(self.color_type().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(()) + } +} + +/// ImageDecoderExt trait +pub trait ImageDecoderExt<'a>: ImageDecoder<'a> + Sized { + /// Read a rectangular section of the image. + 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, |_|{}) + } + + /// Read a rectangular section of the image, periodically reporting progress. + 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 endianess + /// 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 +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) + } + } +} + +/// Trait to inspect an image. +pub trait GenericImageView { + /// The type of pixel. + type Pixel: Pixel; + + /// Underlying image type. This is mainly used by SubImages in order to + /// always have a reference to the original image. This allows for less + /// indirections and it eases the use of nested SubImages. + type InnerImageView: GenericImageView<Pixel = Self::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) + /// + /// # Panics + /// + /// Panics if `(x, y)` is out of bounds. + /// + /// TODO: change this signature to &P + fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel; + + /// Returns the pixel located at (x, y) + /// + /// This function can be implemented in a way that ignores bounds checking. + 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> { + let (width, height) = self.dimensions(); + + Pixels { + image: self, + x: 0, + y: 0, + width, + height, + } + } + + /// Returns a reference to the underlying image. + fn inner(&self) -> &Self::InnerImageView; + + /// Returns an subimage that is an immutable view into this image. + /// You can use [`GenericImage::sub_image`] if you need a mutable view instead. + fn view(&self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&Self::InnerImageView> { + SubImage::new(self.inner(), x, y, width, height) + } +} + +/// A trait for manipulating images. +pub trait GenericImage: GenericImageView { + /// Underlying image type. This is mainly used by SubImages in order to + /// always have a reference to the original image. This allows for less + /// indirections and it eases the use of nested SubImages. + type InnerImage: GenericImage<Pixel = Self::Pixel>; + + /// Gets a reference to the mutable pixel at location `(x, y)` + /// + /// # Panics + /// + /// Panics if `(x, y)` is out of bounds. + fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut Self::Pixel; + + /// Put a pixel at location (x, y) + /// + /// # 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) + /// + /// This function can be implemented in a way that ignores bounds checking. + 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: This method will be removed. Blend the pixel directly instead. + 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`]. + /// + /// # Returns + /// Returns an error if the image is too large to be copied at the given position + 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::DimensionError); + } + + for i in 0..other.width() { + for k in 0..other.height() { + 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 reference to the underlying image. + fn inner_mut(&mut self) -> &mut Self::InnerImage; + + /// Returns a mutable subimage that is a view into this image. + /// If you want an immutable subimage instead, use [`GenericImageView::view`] + fn sub_image( + &mut self, + x: u32, + y: u32, + width: u32, + height: u32, + ) -> SubImage<&mut Self::InnerImage> { + SubImage::new(self.inner_mut(), 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. +pub struct SubImage<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 + pub fn new(image: I, x: u32, y: u32, width: u32, height: u32) -> SubImage<I> { + SubImage { + 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.xoffset = x; + self.yoffset = y; + self.xstride = width; + self.ystride = height; + } + + /// Convert this subimage to an ImageBuffer + pub fn to_image(&self) -> ImageBuffer<DerefPixel<I>, Vec<DerefSubpixel<I>>> + where + I: Deref, + I::Target: GenericImage + 'static, + { + let mut out = ImageBuffer::new(self.xstride, self.ystride); + let borrowed = self.image.deref(); + + for y in 0..self.ystride { + for x in 0..self.xstride { + let p = borrowed.get_pixel(x + self.xoffset, y + self.yoffset); + out.put_pixel(x, y, p); + } + } + + out + } +} + +#[allow(deprecated)] +impl<I> GenericImageView for SubImage<I> +where + I: Deref, + I::Target: GenericImageView + Sized, +{ + type Pixel = DerefPixel<I>; + type InnerImageView = I::Target; + + 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) + } + + fn view(&self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&Self::InnerImageView> { + let x = self.xoffset + x; + let y = self.yoffset + y; + SubImage::new(self.inner(), x, y, width, height) + } + + fn inner(&self) -> &Self::InnerImageView { + &self.image + } +} + +#[allow(deprecated)] +impl<I> GenericImage for SubImage<I> +where + I: DerefMut, + I::Target: GenericImage + Sized, +{ + type InnerImage = I::Target; + + 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) + } + + fn sub_image( + &mut self, + x: u32, + y: u32, + width: u32, + height: u32, + ) -> SubImage<&mut Self::InnerImage> { + let x = self.xoffset + x; + let y = self.yoffset + y; + SubImage::new(self.inner_mut(), x, y, width, height) + } + + fn inner_mut(&mut self) -> &mut Self::InnerImage { + &mut self.image + } +} + +#[cfg(test)] +mod tests { + use std::io; + use std::path::Path; + + use super::{ColorType, ImageDecoder, ImageResult, GenericImage, GenericImageView, load_rect, ImageFormat}; + use crate::buffer::{GrayImage, ImageBuffer}; + use crate::color::Rgba; + use crate::math::Rect; + + #[test] + /// 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] + 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<usize> { + let bytes_read = m.scanline_number * m.scanline_bytes; + if bytes_read >= 25 { + return Ok(0); + } + + 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(len as usize) + } + + 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_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.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!(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); + } +} |