From 1b6a04ca5504955c571d1c97504fb45ea0befee4 Mon Sep 17 00:00:00 2001 From: Valentin Popov Date: Mon, 8 Jan 2024 01:21:28 +0400 Subject: Initial vendor packages Signed-off-by: Valentin Popov --- vendor/image/src/buffer.rs | 1768 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1768 insertions(+) create mode 100644 vendor/image/src/buffer.rs (limited to 'vendor/image/src/buffer.rs') diff --git a/vendor/image/src/buffer.rs b/vendor/image/src/buffer.rs new file mode 100644 index 0000000..765a9de --- /dev/null +++ b/vendor/image/src/buffer.rs @@ -0,0 +1,1768 @@ +//! Contains the generic `ImageBuffer` struct. +use num_traits::Zero; +use std::fmt; +use std::marker::PhantomData; +use std::ops::{Deref, DerefMut, Index, IndexMut, Range}; +use std::path::Path; +use std::slice::{ChunksExact, ChunksExactMut}; + +use crate::color::{FromColor, Luma, LumaA, Rgb, Rgba}; +use crate::dynimage::{save_buffer, save_buffer_with_format, write_buffer_with_format}; +use crate::error::ImageResult; +use crate::flat::{FlatSamples, SampleLayout}; +use crate::image::{GenericImage, GenericImageView, ImageEncoder, ImageFormat, ImageOutputFormat}; +use crate::math::Rect; +use crate::traits::{EncodableLayout, Pixel, PixelWithColorType}; +use crate::utils::expand_packed; + +/// Iterate over pixel refs. +pub struct Pixels<'a, P: Pixel + 'a> +where + P::Subpixel: 'a, +{ + chunks: ChunksExact<'a, P::Subpixel>, +} + +impl<'a, P: Pixel + 'a> Iterator for Pixels<'a, P> +where + P::Subpixel: 'a, +{ + type Item = &'a P; + + #[inline(always)] + fn next(&mut self) -> Option<&'a P> { + self.chunks.next().map(|v|

::from_slice(v)) + } + + #[inline(always)] + fn size_hint(&self) -> (usize, Option) { + let len = self.len(); + (len, Some(len)) + } +} + +impl<'a, P: Pixel + 'a> ExactSizeIterator for Pixels<'a, P> +where + P::Subpixel: 'a, +{ + fn len(&self) -> usize { + self.chunks.len() + } +} + +impl<'a, P: Pixel + 'a> DoubleEndedIterator for Pixels<'a, P> +where + P::Subpixel: 'a, +{ + #[inline(always)] + fn next_back(&mut self) -> Option<&'a P> { + self.chunks.next_back().map(|v|

::from_slice(v)) + } +} + +impl Clone for Pixels<'_, P> { + fn clone(&self) -> Self { + Pixels { + chunks: self.chunks.clone(), + } + } +} + +impl fmt::Debug for Pixels<'_, P> +where + P::Subpixel: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Pixels") + .field("chunks", &self.chunks) + .finish() + } +} + +/// Iterate over mutable pixel refs. +pub struct PixelsMut<'a, P: Pixel + 'a> +where + P::Subpixel: 'a, +{ + chunks: ChunksExactMut<'a, P::Subpixel>, +} + +impl<'a, P: Pixel + 'a> Iterator for PixelsMut<'a, P> +where + P::Subpixel: 'a, +{ + type Item = &'a mut P; + + #[inline(always)] + fn next(&mut self) -> Option<&'a mut P> { + self.chunks.next().map(|v|

::from_slice_mut(v)) + } + + #[inline(always)] + fn size_hint(&self) -> (usize, Option) { + let len = self.len(); + (len, Some(len)) + } +} + +impl<'a, P: Pixel + 'a> ExactSizeIterator for PixelsMut<'a, P> +where + P::Subpixel: 'a, +{ + fn len(&self) -> usize { + self.chunks.len() + } +} + +impl<'a, P: Pixel + 'a> DoubleEndedIterator for PixelsMut<'a, P> +where + P::Subpixel: 'a, +{ + #[inline(always)] + fn next_back(&mut self) -> Option<&'a mut P> { + self.chunks + .next_back() + .map(|v|

::from_slice_mut(v)) + } +} + +impl fmt::Debug for PixelsMut<'_, P> +where + P::Subpixel: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("PixelsMut") + .field("chunks", &self.chunks) + .finish() + } +} + +/// Iterate over rows of an image +/// +/// This iterator is created with [`ImageBuffer::rows`]. See its document for details. +/// +/// [`ImageBuffer::rows`]: ../struct.ImageBuffer.html#method.rows +pub struct Rows<'a, P: Pixel + 'a> +where +

::Subpixel: 'a, +{ + pixels: ChunksExact<'a, P::Subpixel>, +} + +impl<'a, P: Pixel + 'a> Rows<'a, P> { + /// Construct the iterator from image pixels. This is not public since it has a (hidden) panic + /// condition. The `pixels` slice must be large enough so that all pixels are addressable. + fn with_image(pixels: &'a [P::Subpixel], width: u32, height: u32) -> Self { + let row_len = (width as usize) * usize::from(

::CHANNEL_COUNT); + if row_len == 0 { + Rows { + pixels: [].chunks_exact(1), + } + } else { + let pixels = pixels + .get(..row_len * height as usize) + .expect("Pixel buffer has too few subpixels"); + // Rows are physically present. In particular, height is smaller than `usize::MAX` as + // all subpixels can be indexed. + Rows { + pixels: pixels.chunks_exact(row_len), + } + } + } +} + +impl<'a, P: Pixel + 'a> Iterator for Rows<'a, P> +where + P::Subpixel: 'a, +{ + type Item = Pixels<'a, P>; + + #[inline(always)] + fn next(&mut self) -> Option> { + let row = self.pixels.next()?; + Some(Pixels { + // Note: this is not reached when CHANNEL_COUNT is 0. + chunks: row.chunks_exact(

::CHANNEL_COUNT as usize), + }) + } + + #[inline(always)] + fn size_hint(&self) -> (usize, Option) { + let len = self.len(); + (len, Some(len)) + } +} + +impl<'a, P: Pixel + 'a> ExactSizeIterator for Rows<'a, P> +where + P::Subpixel: 'a, +{ + fn len(&self) -> usize { + self.pixels.len() + } +} + +impl<'a, P: Pixel + 'a> DoubleEndedIterator for Rows<'a, P> +where + P::Subpixel: 'a, +{ + #[inline(always)] + fn next_back(&mut self) -> Option> { + let row = self.pixels.next_back()?; + Some(Pixels { + // Note: this is not reached when CHANNEL_COUNT is 0. + chunks: row.chunks_exact(

::CHANNEL_COUNT as usize), + }) + } +} + +impl Clone for Rows<'_, P> { + fn clone(&self) -> Self { + Rows { + pixels: self.pixels.clone(), + } + } +} + +impl fmt::Debug for Rows<'_, P> +where + P::Subpixel: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Rows") + .field("pixels", &self.pixels) + .finish() + } +} + +/// Iterate over mutable rows of an image +/// +/// This iterator is created with [`ImageBuffer::rows_mut`]. See its document for details. +/// +/// [`ImageBuffer::rows_mut`]: ../struct.ImageBuffer.html#method.rows_mut +pub struct RowsMut<'a, P: Pixel + 'a> +where +

::Subpixel: 'a, +{ + pixels: ChunksExactMut<'a, P::Subpixel>, +} + +impl<'a, P: Pixel + 'a> RowsMut<'a, P> { + /// Construct the iterator from image pixels. This is not public since it has a (hidden) panic + /// condition. The `pixels` slice must be large enough so that all pixels are addressable. + fn with_image(pixels: &'a mut [P::Subpixel], width: u32, height: u32) -> Self { + let row_len = (width as usize) * usize::from(

::CHANNEL_COUNT); + if row_len == 0 { + RowsMut { + pixels: [].chunks_exact_mut(1), + } + } else { + let pixels = pixels + .get_mut(..row_len * height as usize) + .expect("Pixel buffer has too few subpixels"); + // Rows are physically present. In particular, height is smaller than `usize::MAX` as + // all subpixels can be indexed. + RowsMut { + pixels: pixels.chunks_exact_mut(row_len), + } + } + } +} + +impl<'a, P: Pixel + 'a> Iterator for RowsMut<'a, P> +where + P::Subpixel: 'a, +{ + type Item = PixelsMut<'a, P>; + + #[inline(always)] + fn next(&mut self) -> Option> { + let row = self.pixels.next()?; + Some(PixelsMut { + // Note: this is not reached when CHANNEL_COUNT is 0. + chunks: row.chunks_exact_mut(

::CHANNEL_COUNT as usize), + }) + } + + #[inline(always)] + fn size_hint(&self) -> (usize, Option) { + let len = self.len(); + (len, Some(len)) + } +} + +impl<'a, P: Pixel + 'a> ExactSizeIterator for RowsMut<'a, P> +where + P::Subpixel: 'a, +{ + fn len(&self) -> usize { + self.pixels.len() + } +} + +impl<'a, P: Pixel + 'a> DoubleEndedIterator for RowsMut<'a, P> +where + P::Subpixel: 'a, +{ + #[inline(always)] + fn next_back(&mut self) -> Option> { + let row = self.pixels.next_back()?; + Some(PixelsMut { + // Note: this is not reached when CHANNEL_COUNT is 0. + chunks: row.chunks_exact_mut(

::CHANNEL_COUNT as usize), + }) + } +} + +impl fmt::Debug for RowsMut<'_, P> +where + P::Subpixel: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("RowsMut") + .field("pixels", &self.pixels) + .finish() + } +} + +/// Enumerate the pixels of an image. +pub struct EnumeratePixels<'a, P: Pixel + 'a> +where +

::Subpixel: 'a, +{ + pixels: Pixels<'a, P>, + x: u32, + y: u32, + width: u32, +} + +impl<'a, P: Pixel + 'a> Iterator for EnumeratePixels<'a, P> +where + P::Subpixel: 'a, +{ + type Item = (u32, u32, &'a P); + + #[inline(always)] + fn next(&mut self) -> Option<(u32, u32, &'a P)> { + if self.x >= self.width { + self.x = 0; + self.y += 1; + } + let (x, y) = (self.x, self.y); + self.x += 1; + self.pixels.next().map(|p| (x, y, p)) + } + + #[inline(always)] + fn size_hint(&self) -> (usize, Option) { + let len = self.len(); + (len, Some(len)) + } +} + +impl<'a, P: Pixel + 'a> ExactSizeIterator for EnumeratePixels<'a, P> +where + P::Subpixel: 'a, +{ + fn len(&self) -> usize { + self.pixels.len() + } +} + +impl Clone for EnumeratePixels<'_, P> { + fn clone(&self) -> Self { + EnumeratePixels { + pixels: self.pixels.clone(), + ..*self + } + } +} + +impl fmt::Debug for EnumeratePixels<'_, P> +where + P::Subpixel: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("EnumeratePixels") + .field("pixels", &self.pixels) + .field("x", &self.x) + .field("y", &self.y) + .field("width", &self.width) + .finish() + } +} + +/// Enumerate the rows of an image. +pub struct EnumerateRows<'a, P: Pixel + 'a> +where +

::Subpixel: 'a, +{ + rows: Rows<'a, P>, + y: u32, + width: u32, +} + +impl<'a, P: Pixel + 'a> Iterator for EnumerateRows<'a, P> +where + P::Subpixel: 'a, +{ + type Item = (u32, EnumeratePixels<'a, P>); + + #[inline(always)] + fn next(&mut self) -> Option<(u32, EnumeratePixels<'a, P>)> { + let y = self.y; + self.y += 1; + self.rows.next().map(|r| { + ( + y, + EnumeratePixels { + x: 0, + y, + width: self.width, + pixels: r, + }, + ) + }) + } + + #[inline(always)] + fn size_hint(&self) -> (usize, Option) { + let len = self.len(); + (len, Some(len)) + } +} + +impl<'a, P: Pixel + 'a> ExactSizeIterator for EnumerateRows<'a, P> +where + P::Subpixel: 'a, +{ + fn len(&self) -> usize { + self.rows.len() + } +} + +impl Clone for EnumerateRows<'_, P> { + fn clone(&self) -> Self { + EnumerateRows { + rows: self.rows.clone(), + ..*self + } + } +} + +impl fmt::Debug for EnumerateRows<'_, P> +where + P::Subpixel: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("EnumerateRows") + .field("rows", &self.rows) + .field("y", &self.y) + .field("width", &self.width) + .finish() + } +} + +/// Enumerate the pixels of an image. +pub struct EnumeratePixelsMut<'a, P: Pixel + 'a> +where +

::Subpixel: 'a, +{ + pixels: PixelsMut<'a, P>, + x: u32, + y: u32, + width: u32, +} + +impl<'a, P: Pixel + 'a> Iterator for EnumeratePixelsMut<'a, P> +where + P::Subpixel: 'a, +{ + type Item = (u32, u32, &'a mut P); + + #[inline(always)] + fn next(&mut self) -> Option<(u32, u32, &'a mut P)> { + if self.x >= self.width { + self.x = 0; + self.y += 1; + } + let (x, y) = (self.x, self.y); + self.x += 1; + self.pixels.next().map(|p| (x, y, p)) + } + + #[inline(always)] + fn size_hint(&self) -> (usize, Option) { + let len = self.len(); + (len, Some(len)) + } +} + +impl<'a, P: Pixel + 'a> ExactSizeIterator for EnumeratePixelsMut<'a, P> +where + P::Subpixel: 'a, +{ + fn len(&self) -> usize { + self.pixels.len() + } +} + +impl fmt::Debug for EnumeratePixelsMut<'_, P> +where + P::Subpixel: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("EnumeratePixelsMut") + .field("pixels", &self.pixels) + .field("x", &self.x) + .field("y", &self.y) + .field("width", &self.width) + .finish() + } +} + +/// Enumerate the rows of an image. +pub struct EnumerateRowsMut<'a, P: Pixel + 'a> +where +

::Subpixel: 'a, +{ + rows: RowsMut<'a, P>, + y: u32, + width: u32, +} + +impl<'a, P: Pixel + 'a> Iterator for EnumerateRowsMut<'a, P> +where + P::Subpixel: 'a, +{ + type Item = (u32, EnumeratePixelsMut<'a, P>); + + #[inline(always)] + fn next(&mut self) -> Option<(u32, EnumeratePixelsMut<'a, P>)> { + let y = self.y; + self.y += 1; + self.rows.next().map(|r| { + ( + y, + EnumeratePixelsMut { + x: 0, + y, + width: self.width, + pixels: r, + }, + ) + }) + } + + #[inline(always)] + fn size_hint(&self) -> (usize, Option) { + let len = self.len(); + (len, Some(len)) + } +} + +impl<'a, P: Pixel + 'a> ExactSizeIterator for EnumerateRowsMut<'a, P> +where + P::Subpixel: 'a, +{ + fn len(&self) -> usize { + self.rows.len() + } +} + +impl fmt::Debug for EnumerateRowsMut<'_, P> +where + P::Subpixel: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("EnumerateRowsMut") + .field("rows", &self.rows) + .field("y", &self.y) + .field("width", &self.width) + .finish() + } +} + +/// Generic image buffer +/// +/// This is an image parameterised by its Pixel types, represented by a width and height and a +/// container of channel data. It provides direct access to its pixels and implements the +/// [`GenericImageView`] and [`GenericImage`] traits. In many ways, this is the standard buffer +/// implementing those traits. Using this concrete type instead of a generic type parameter has +/// been shown to improve performance. +/// +/// The crate defines a few type aliases with regularly used pixel types for your convenience, such +/// as [`RgbImage`], [`GrayImage`] etc. +/// +/// [`GenericImage`]: trait.GenericImage.html +/// [`GenericImageView`]: trait.GenericImageView.html +/// [`RgbImage`]: type.RgbImage.html +/// [`GrayImage`]: type.GrayImage.html +/// +/// To convert between images of different Pixel types use [`DynamicImage`]. +/// +/// You can retrieve a complete description of the buffer's layout and contents through +/// [`as_flat_samples`] and [`as_flat_samples_mut`]. This can be handy to also use the contents in +/// a foreign language, map it as a GPU host buffer or other similar tasks. +/// +/// [`DynamicImage`]: enum.DynamicImage.html +/// [`as_flat_samples`]: #method.as_flat_samples +/// [`as_flat_samples_mut`]: #method.as_flat_samples_mut +/// +/// ## Examples +/// +/// Create a simple canvas and paint a small cross. +/// +/// ``` +/// use image::{RgbImage, Rgb}; +/// +/// let mut img = RgbImage::new(32, 32); +/// +/// for x in 15..=17 { +/// for y in 8..24 { +/// img.put_pixel(x, y, Rgb([255, 0, 0])); +/// img.put_pixel(y, x, Rgb([255, 0, 0])); +/// } +/// } +/// ``` +/// +/// Overlays an image on top of a larger background raster. +/// +/// ```no_run +/// use image::{GenericImage, GenericImageView, ImageBuffer, open}; +/// +/// let on_top = open("path/to/some.png").unwrap().into_rgb8(); +/// let mut img = ImageBuffer::from_fn(512, 512, |x, y| { +/// if (x + y) % 2 == 0 { +/// image::Rgb([0, 0, 0]) +/// } else { +/// image::Rgb([255, 255, 255]) +/// } +/// }); +/// +/// image::imageops::overlay(&mut img, &on_top, 128, 128); +/// ``` +/// +/// Convert an RgbaImage to a GrayImage. +/// +/// ```no_run +/// use image::{open, DynamicImage}; +/// +/// let rgba = open("path/to/some.png").unwrap().into_rgba8(); +/// let gray = DynamicImage::ImageRgba8(rgba).into_luma8(); +/// ``` +#[derive(Debug, Hash, PartialEq, Eq)] +pub struct ImageBuffer { + width: u32, + height: u32, + _phantom: PhantomData

, + data: Container, +} + +// generic implementation, shared along all image buffers +impl ImageBuffer +where + P: Pixel, + Container: Deref, +{ + /// Constructs a buffer from a generic container + /// (for example a `Vec` or a slice) + /// + /// Returns `None` if the container is not big enough (including when the image dimensions + /// necessitate an allocation of more bytes than supported by the container). + pub fn from_raw(width: u32, height: u32, buf: Container) -> Option> { + if Self::check_image_fits(width, height, buf.len()) { + Some(ImageBuffer { + data: buf, + width, + height, + _phantom: PhantomData, + }) + } else { + None + } + } + + /// Returns the underlying raw buffer + pub fn into_raw(self) -> Container { + self.data + } + + /// Returns the underlying raw buffer + pub fn as_raw(&self) -> &Container { + &self.data + } + + /// The width and height of this image. + pub fn dimensions(&self) -> (u32, u32) { + (self.width, self.height) + } + + /// The width of this image. + pub fn width(&self) -> u32 { + self.width + } + + /// The height of this image. + pub fn height(&self) -> u32 { + self.height + } + + // TODO: choose name under which to expose. + pub(crate) fn inner_pixels(&self) -> &[P::Subpixel] { + let len = Self::image_buffer_len(self.width, self.height).unwrap(); + &self.data[..len] + } + + /// Returns an iterator over the pixels of this image. + /// The iteration order is x = 0 to width then y = 0 to height + pub fn pixels(&self) -> Pixels

{ + Pixels { + chunks: self + .inner_pixels() + .chunks_exact(

::CHANNEL_COUNT as usize), + } + } + + /// Returns an iterator over the rows of this image. + /// + /// Only non-empty rows can be iterated in this manner. In particular the iterator will not + /// yield any item when the width of the image is `0` or a pixel type without any channels is + /// used. This ensures that its length can always be represented by `usize`. + pub fn rows(&self) -> Rows

{ + Rows::with_image(&self.data, self.width, self.height) + } + + /// Enumerates over the pixels of the image. + /// The iterator yields the coordinates of each pixel + /// along with a reference to them. + /// The iteration order is x = 0 to width then y = 0 to height + /// Starting from the top left. + pub fn enumerate_pixels(&self) -> EnumeratePixels

{ + EnumeratePixels { + pixels: self.pixels(), + x: 0, + y: 0, + width: self.width, + } + } + + /// Enumerates over the rows of the image. + /// The iterator yields the y-coordinate of each row + /// along with a reference to them. + pub fn enumerate_rows(&self) -> EnumerateRows

{ + EnumerateRows { + rows: self.rows(), + y: 0, + width: self.width, + } + } + + /// Gets a reference to the pixel at location `(x, y)` + /// + /// # Panics + /// + /// Panics if `(x, y)` is out of the bounds `(width, height)`. + #[inline] + #[track_caller] + pub fn get_pixel(&self, x: u32, y: u32) -> &P { + match self.pixel_indices(x, y) { + None => panic!( + "Image index {:?} out of bounds {:?}", + (x, y), + (self.width, self.height) + ), + Some(pixel_indices) =>

::from_slice(&self.data[pixel_indices]), + } + } + + /// Gets a reference to the pixel at location `(x, y)` or returns `None` if + /// the index is out of the bounds `(width, height)`. + pub fn get_pixel_checked(&self, x: u32, y: u32) -> Option<&P> { + if x >= self.width { + return None; + } + let num_channels =

::CHANNEL_COUNT as usize; + let i = (y as usize) + .saturating_mul(self.width as usize) + .saturating_add(x as usize) + .saturating_mul(num_channels); + + self.data + .get(i..i + num_channels) + .map(|pixel_indices|

::from_slice(pixel_indices)) + } + + /// Test that the image fits inside the buffer. + /// + /// Verifies that the maximum image of pixels inside the bounds is smaller than the provided + /// length. Note that as a corrolary we also have that the index calculation of pixels inside + /// the bounds will not overflow. + fn check_image_fits(width: u32, height: u32, len: usize) -> bool { + let checked_len = Self::image_buffer_len(width, height); + checked_len.map(|min_len| min_len <= len).unwrap_or(false) + } + + fn image_buffer_len(width: u32, height: u32) -> Option { + Some(

::CHANNEL_COUNT as usize) + .and_then(|size| size.checked_mul(width as usize)) + .and_then(|size| size.checked_mul(height as usize)) + } + + #[inline(always)] + fn pixel_indices(&self, x: u32, y: u32) -> Option> { + if x >= self.width || y >= self.height { + return None; + } + + Some(self.pixel_indices_unchecked(x, y)) + } + + #[inline(always)] + fn pixel_indices_unchecked(&self, x: u32, y: u32) -> Range { + let no_channels =

::CHANNEL_COUNT as usize; + // If in bounds, this can't overflow as we have tested that at construction! + let min_index = (y as usize * self.width as usize + x as usize) * no_channels; + min_index..min_index + no_channels + } + + /// Get the format of the buffer when viewed as a matrix of samples. + pub fn sample_layout(&self) -> SampleLayout { + // None of these can overflow, as all our memory is addressable. + SampleLayout::row_major_packed(

::CHANNEL_COUNT, self.width, self.height) + } + + /// Return the raw sample buffer with its stride an dimension information. + /// + /// The returned buffer is guaranteed to be well formed in all cases. It is laid out by + /// colors, width then height, meaning `channel_stride <= width_stride <= height_stride`. All + /// strides are in numbers of elements but those are mostly `u8` in which case the strides are + /// also byte strides. + pub fn into_flat_samples(self) -> FlatSamples + where + Container: AsRef<[P::Subpixel]>, + { + // None of these can overflow, as all our memory is addressable. + let layout = self.sample_layout(); + FlatSamples { + samples: self.data, + layout, + color_hint: None, // TODO: the pixel type might contain P::COLOR_TYPE if it satisfies PixelWithColorType + } + } + + /// Return a view on the raw sample buffer. + /// + /// See [`into_flat_samples`](#method.into_flat_samples) for more details. + pub fn as_flat_samples(&self) -> FlatSamples<&[P::Subpixel]> + where + Container: AsRef<[P::Subpixel]>, + { + let layout = self.sample_layout(); + FlatSamples { + samples: self.data.as_ref(), + layout, + color_hint: None, // TODO: the pixel type might contain P::COLOR_TYPE if it satisfies PixelWithColorType + } + } + + /// Return a mutable view on the raw sample buffer. + /// + /// See [`into_flat_samples`](#method.into_flat_samples) for more details. + pub fn as_flat_samples_mut(&mut self) -> FlatSamples<&mut [P::Subpixel]> + where + Container: AsMut<[P::Subpixel]>, + { + let layout = self.sample_layout(); + FlatSamples { + samples: self.data.as_mut(), + layout, + color_hint: None, // TODO: the pixel type might contain P::COLOR_TYPE if it satisfies PixelWithColorType + } + } +} + +impl ImageBuffer +where + P: Pixel, + Container: Deref + DerefMut, +{ + // TODO: choose name under which to expose. + fn inner_pixels_mut(&mut self) -> &mut [P::Subpixel] { + let len = Self::image_buffer_len(self.width, self.height).unwrap(); + &mut self.data[..len] + } + + /// Returns an iterator over the mutable pixels of this image. + pub fn pixels_mut(&mut self) -> PixelsMut

{ + PixelsMut { + chunks: self + .inner_pixels_mut() + .chunks_exact_mut(

::CHANNEL_COUNT as usize), + } + } + + /// Returns an iterator over the mutable rows of this image. + /// + /// Only non-empty rows can be iterated in this manner. In particular the iterator will not + /// yield any item when the width of the image is `0` or a pixel type without any channels is + /// used. This ensures that its length can always be represented by `usize`. + pub fn rows_mut(&mut self) -> RowsMut

{ + RowsMut::with_image(&mut self.data, self.width, self.height) + } + + /// Enumerates over the pixels of the image. + /// The iterator yields the coordinates of each pixel + /// along with a mutable reference to them. + pub fn enumerate_pixels_mut(&mut self) -> EnumeratePixelsMut

{ + let width = self.width; + EnumeratePixelsMut { + pixels: self.pixels_mut(), + x: 0, + y: 0, + width, + } + } + + /// Enumerates over the rows of the image. + /// The iterator yields the y-coordinate of each row + /// along with a mutable reference to them. + pub fn enumerate_rows_mut(&mut self) -> EnumerateRowsMut

{ + let width = self.width; + EnumerateRowsMut { + rows: self.rows_mut(), + y: 0, + width, + } + } + + /// Gets a reference to the mutable pixel at location `(x, y)` + /// + /// # Panics + /// + /// Panics if `(x, y)` is out of the bounds `(width, height)`. + #[inline] + #[track_caller] + pub fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut P { + match self.pixel_indices(x, y) { + None => panic!( + "Image index {:?} out of bounds {:?}", + (x, y), + (self.width, self.height) + ), + Some(pixel_indices) =>

::from_slice_mut(&mut self.data[pixel_indices]), + } + } + + /// Gets a reference to the mutable pixel at location `(x, y)` or returns + /// `None` if the index is out of the bounds `(width, height)`. + pub fn get_pixel_mut_checked(&mut self, x: u32, y: u32) -> Option<&mut P> { + if x >= self.width { + return None; + } + let num_channels =

::CHANNEL_COUNT as usize; + let i = (y as usize) + .saturating_mul(self.width as usize) + .saturating_add(x as usize) + .saturating_mul(num_channels); + + self.data + .get_mut(i..i + num_channels) + .map(|pixel_indices|

::from_slice_mut(pixel_indices)) + } + + /// Puts a pixel at location `(x, y)` + /// + /// # Panics + /// + /// Panics if `(x, y)` is out of the bounds `(width, height)`. + #[inline] + #[track_caller] + pub fn put_pixel(&mut self, x: u32, y: u32, pixel: P) { + *self.get_pixel_mut(x, y) = pixel + } +} + +impl ImageBuffer +where + P: Pixel, + [P::Subpixel]: EncodableLayout, + Container: Deref, +{ + /// Saves the buffer to a file at the path specified. + /// + /// The image format is derived from the file extension. + pub fn save(&self, path: Q) -> ImageResult<()> + where + Q: AsRef, + P: PixelWithColorType, + { + save_buffer( + path, + self.inner_pixels().as_bytes(), + self.width(), + self.height(), +

::COLOR_TYPE, + ) + } +} + +impl ImageBuffer +where + P: Pixel, + [P::Subpixel]: EncodableLayout, + Container: Deref, +{ + /// Saves the buffer to a file at the specified path in + /// the specified format. + /// + /// See [`save_buffer_with_format`](fn.save_buffer_with_format.html) for + /// supported types. + pub fn save_with_format(&self, path: Q, format: ImageFormat) -> ImageResult<()> + where + Q: AsRef, + P: PixelWithColorType, + { + // This is valid as the subpixel is u8. + save_buffer_with_format( + path, + self.inner_pixels().as_bytes(), + self.width(), + self.height(), +

::COLOR_TYPE, + format, + ) + } +} + +impl ImageBuffer +where + P: Pixel, + [P::Subpixel]: EncodableLayout, + Container: Deref, +{ + /// Writes the buffer to a writer in the specified format. + /// + /// Assumes the writer is buffered. In most cases, + /// you should wrap your writer in a `BufWriter` for best performance. + /// + /// See [`ImageOutputFormat`](enum.ImageOutputFormat.html) for + /// supported types. + pub fn write_to(&self, writer: &mut W, format: F) -> ImageResult<()> + where + W: std::io::Write + std::io::Seek, + F: Into, + P: PixelWithColorType, + { + // This is valid as the subpixel is u8. + write_buffer_with_format( + writer, + self.inner_pixels().as_bytes(), + self.width(), + self.height(), +

::COLOR_TYPE, + format, + ) + } +} + +impl ImageBuffer +where + P: Pixel, + [P::Subpixel]: EncodableLayout, + Container: Deref, +{ + /// Writes the buffer with the given encoder. + pub fn write_with_encoder(&self, encoder: E) -> ImageResult<()> + where + E: ImageEncoder, + P: PixelWithColorType, + { + // This is valid as the subpixel is u8. + encoder.write_image( + self.inner_pixels().as_bytes(), + self.width(), + self.height(), +

::COLOR_TYPE, + ) + } +} + +impl Default for ImageBuffer +where + P: Pixel, + Container: Default, +{ + fn default() -> Self { + Self { + width: 0, + height: 0, + _phantom: PhantomData, + data: Default::default(), + } + } +} + +impl Deref for ImageBuffer +where + P: Pixel, + Container: Deref, +{ + type Target = [P::Subpixel]; + + fn deref(&self) -> &::Target { + &self.data + } +} + +impl DerefMut for ImageBuffer +where + P: Pixel, + Container: Deref + DerefMut, +{ + fn deref_mut(&mut self) -> &mut ::Target { + &mut self.data + } +} + +impl Index<(u32, u32)> for ImageBuffer +where + P: Pixel, + Container: Deref, +{ + type Output = P; + + fn index(&self, (x, y): (u32, u32)) -> &P { + self.get_pixel(x, y) + } +} + +impl IndexMut<(u32, u32)> for ImageBuffer +where + P: Pixel, + Container: Deref + DerefMut, +{ + fn index_mut(&mut self, (x, y): (u32, u32)) -> &mut P { + self.get_pixel_mut(x, y) + } +} + +impl Clone for ImageBuffer +where + P: Pixel, + Container: Deref + Clone, +{ + fn clone(&self) -> ImageBuffer { + ImageBuffer { + data: self.data.clone(), + width: self.width, + height: self.height, + _phantom: PhantomData, + } + } +} + +impl GenericImageView for ImageBuffer +where + P: Pixel, + Container: Deref + Deref, +{ + type Pixel = P; + + fn dimensions(&self) -> (u32, u32) { + self.dimensions() + } + + fn bounds(&self) -> (u32, u32, u32, u32) { + (0, 0, self.width, self.height) + } + + fn get_pixel(&self, x: u32, y: u32) -> P { + *self.get_pixel(x, y) + } + + /// Returns the pixel located at (x, y), ignoring bounds checking. + #[inline(always)] + unsafe fn unsafe_get_pixel(&self, x: u32, y: u32) -> P { + let indices = self.pixel_indices_unchecked(x, y); + *

::from_slice(self.data.get_unchecked(indices)) + } +} + +impl GenericImage for ImageBuffer +where + P: Pixel, + Container: Deref + DerefMut, +{ + fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut P { + self.get_pixel_mut(x, y) + } + + fn put_pixel(&mut self, x: u32, y: u32, pixel: P) { + *self.get_pixel_mut(x, y) = pixel + } + + /// Puts a pixel at location (x, y), ignoring bounds checking. + #[inline(always)] + unsafe fn unsafe_put_pixel(&mut self, x: u32, y: u32, pixel: P) { + let indices = self.pixel_indices_unchecked(x, y); + let p =

::from_slice_mut(self.data.get_unchecked_mut(indices)); + *p = 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, p: P) { + self.get_pixel_mut(x, y).blend(&p) + } + + 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; + } + + if sy < dy { + for y in (0..height).rev() { + let sy = sy + y; + let dy = dy + y; + let Range { start, .. } = self.pixel_indices_unchecked(sx, sy); + let Range { end, .. } = self.pixel_indices_unchecked(sx + width - 1, sy); + let dst = self.pixel_indices_unchecked(dx, dy).start; + self.data.copy_within(start..end, dst); + } + } else { + for y in 0..height { + let sy = sy + y; + let dy = dy + y; + let Range { start, .. } = self.pixel_indices_unchecked(sx, sy); + let Range { end, .. } = self.pixel_indices_unchecked(sx + width - 1, sy); + let dst = self.pixel_indices_unchecked(dx, dy).start; + self.data.copy_within(start..end, dst); + } + } + true + } +} + +// concrete implementation for `Vec`-backed buffers +// TODO: I think that rustc does not "see" this impl any more: the impl with +// Container meets the same requirements. At least, I got compile errors that +// there is no such function as `into_vec`, whereas `into_raw` did work, and +// `into_vec` is redundant anyway, because `into_raw` will give you the vector, +// and it is more generic. +impl ImageBuffer> { + /// Creates a new image buffer based on a `Vec`. + /// + /// # Panics + /// + /// Panics when the resulting image is larger the the maximum size of a vector. + pub fn new(width: u32, height: u32) -> ImageBuffer> { + let size = Self::image_buffer_len(width, height) + .expect("Buffer length in `ImageBuffer::new` overflows usize"); + ImageBuffer { + data: vec![Zero::zero(); size], + width, + height, + _phantom: PhantomData, + } + } + + /// Constructs a new ImageBuffer by copying a pixel + /// + /// # Panics + /// + /// Panics when the resulting image is larger the the maximum size of a vector. + pub fn from_pixel(width: u32, height: u32, pixel: P) -> ImageBuffer> { + let mut buf = ImageBuffer::new(width, height); + for p in buf.pixels_mut() { + *p = pixel + } + buf + } + + /// Constructs a new ImageBuffer by repeated application of the supplied function. + /// + /// The arguments to the function are the pixel's x and y coordinates. + /// + /// # Panics + /// + /// Panics when the resulting image is larger the the maximum size of a vector. + pub fn from_fn(width: u32, height: u32, mut f: F) -> ImageBuffer> + where + F: FnMut(u32, u32) -> P, + { + let mut buf = ImageBuffer::new(width, height); + for (x, y, p) in buf.enumerate_pixels_mut() { + *p = f(x, y) + } + buf + } + + /// Creates an image buffer out of an existing buffer. + /// Returns None if the buffer is not big enough. + pub fn from_vec( + width: u32, + height: u32, + buf: Vec, + ) -> Option>> { + ImageBuffer::from_raw(width, height, buf) + } + + /// Consumes the image buffer and returns the underlying data + /// as an owned buffer + pub fn into_vec(self) -> Vec { + self.into_raw() + } +} + +/// Provides color conversions for whole image buffers. +pub trait ConvertBuffer { + /// Converts `self` to a buffer of type T + /// + /// A generic implementation is provided to convert any image buffer to a image buffer + /// based on a `Vec`. + fn convert(&self) -> T; +} + +// concrete implementation Luma -> Rgba +impl GrayImage { + /// Expands a color palette by re-using the existing buffer. + /// Assumes 8 bit per pixel. Uses an optionally transparent index to + /// adjust it's alpha value accordingly. + pub fn expand_palette( + self, + palette: &[(u8, u8, u8)], + transparent_idx: Option, + ) -> RgbaImage { + let (width, height) = self.dimensions(); + let mut data = self.into_raw(); + let entries = data.len(); + data.resize(entries.checked_mul(4).unwrap(), 0); + let mut buffer = ImageBuffer::from_vec(width, height, data).unwrap(); + expand_packed(&mut buffer, 4, 8, |idx, pixel| { + let (r, g, b) = palette[idx as usize]; + let a = if let Some(t_idx) = transparent_idx { + if t_idx == idx { + 0 + } else { + 255 + } + } else { + 255 + }; + pixel[0] = r; + pixel[1] = g; + pixel[2] = b; + pixel[3] = a; + }); + buffer + } +} + +// TODO: Equality constraints are not yet supported in where clauses, when they +// are, the T parameter should be removed in favor of ToType::Subpixel, which +// will then be FromType::Subpixel. +impl + ConvertBuffer>> for ImageBuffer +where + Container: Deref, + ToType: FromColor, +{ + /// # Examples + /// Convert RGB image to gray image. + /// ```no_run + /// use image::buffer::ConvertBuffer; + /// use image::GrayImage; + /// + /// let image_path = "examples/fractal.png"; + /// let image = image::open(&image_path) + /// .expect("Open file failed") + /// .to_rgba8(); + /// + /// let gray_image: GrayImage = image.convert(); + /// ``` + fn convert(&self) -> ImageBuffer> { + let mut buffer: ImageBuffer> = + ImageBuffer::new(self.width, self.height); + for (to, from) in buffer.pixels_mut().zip(self.pixels()) { + to.from_color(from) + } + buffer + } +} + +/// Sendable Rgb image buffer +pub type RgbImage = ImageBuffer, Vec>; +/// Sendable Rgb + alpha channel image buffer +pub type RgbaImage = ImageBuffer, Vec>; +/// Sendable grayscale image buffer +pub type GrayImage = ImageBuffer, Vec>; +/// Sendable grayscale + alpha channel image buffer +pub type GrayAlphaImage = ImageBuffer, Vec>; +/// Sendable 16-bit Rgb image buffer +pub(crate) type Rgb16Image = ImageBuffer, Vec>; +/// Sendable 16-bit Rgb + alpha channel image buffer +pub(crate) type Rgba16Image = ImageBuffer, Vec>; +/// Sendable 16-bit grayscale image buffer +pub(crate) type Gray16Image = ImageBuffer, Vec>; +/// Sendable 16-bit grayscale + alpha channel image buffer +pub(crate) type GrayAlpha16Image = ImageBuffer, Vec>; + +/// An image buffer for 32-bit float RGB pixels, +/// where the backing container is a flattened vector of floats. +pub type Rgb32FImage = ImageBuffer, Vec>; + +/// An image buffer for 32-bit float RGBA pixels, +/// where the backing container is a flattened vector of floats. +pub type Rgba32FImage = ImageBuffer, Vec>; + +#[cfg(test)] +mod test { + use super::{GrayImage, ImageBuffer, ImageOutputFormat, RgbImage}; + use crate::math::Rect; + use crate::GenericImage as _; + use crate::{color, Rgb}; + + #[test] + /// Tests if image buffers from slices work + fn slice_buffer() { + let data = [0; 9]; + let buf: ImageBuffer, _> = ImageBuffer::from_raw(3, 3, &data[..]).unwrap(); + assert_eq!(&*buf, &data[..]) + } + + #[test] + fn get_pixel() { + let mut a: RgbImage = ImageBuffer::new(10, 10); + { + let b = a.get_mut(3 * 10).unwrap(); + *b = 255; + } + assert_eq!(a.get_pixel(0, 1)[0], 255) + } + + #[test] + fn get_pixel_checked() { + let mut a: RgbImage = ImageBuffer::new(10, 10); + a.get_pixel_mut_checked(0, 1).map(|b| b[0] = 255); + + assert_eq!(a.get_pixel_checked(0, 1), Some(&Rgb([255, 0, 0]))); + assert_eq!(a.get_pixel_checked(0, 1).unwrap(), a.get_pixel(0, 1)); + assert_eq!(a.get_pixel_checked(10, 0), None); + assert_eq!(a.get_pixel_checked(0, 10), None); + assert_eq!(a.get_pixel_mut_checked(10, 0), None); + assert_eq!(a.get_pixel_mut_checked(0, 10), None); + + // From image/issues/1672 + const WHITE: Rgb = Rgb([255_u8, 255, 255]); + let mut a = RgbImage::new(2, 1); + a.put_pixel(1, 0, WHITE); + + assert_eq!(a.get_pixel_checked(1, 0), Some(&WHITE)); + assert_eq!(a.get_pixel_checked(1, 0).unwrap(), a.get_pixel(1, 0)); + } + + #[test] + fn mut_iter() { + let mut a: RgbImage = ImageBuffer::new(10, 10); + { + let val = a.pixels_mut().next().unwrap(); + *val = Rgb([42, 0, 0]); + } + assert_eq!(a.data[0], 42) + } + + #[test] + fn zero_width_zero_height() { + let mut image = RgbImage::new(0, 0); + + assert_eq!(image.rows_mut().count(), 0); + assert_eq!(image.pixels_mut().count(), 0); + assert_eq!(image.rows().count(), 0); + assert_eq!(image.pixels().count(), 0); + } + + #[test] + fn zero_width_nonzero_height() { + let mut image = RgbImage::new(0, 2); + + assert_eq!(image.rows_mut().count(), 0); + assert_eq!(image.pixels_mut().count(), 0); + assert_eq!(image.rows().count(), 0); + assert_eq!(image.pixels().count(), 0); + } + + #[test] + fn nonzero_width_zero_height() { + let mut image = RgbImage::new(2, 0); + + assert_eq!(image.rows_mut().count(), 0); + assert_eq!(image.pixels_mut().count(), 0); + assert_eq!(image.rows().count(), 0); + assert_eq!(image.pixels().count(), 0); + } + + #[test] + fn pixels_on_large_buffer() { + let mut image = RgbImage::from_raw(1, 1, vec![0; 6]).unwrap(); + + assert_eq!(image.pixels().count(), 1); + assert_eq!(image.enumerate_pixels().count(), 1); + assert_eq!(image.pixels_mut().count(), 1); + assert_eq!(image.enumerate_pixels_mut().count(), 1); + + assert_eq!(image.rows().count(), 1); + assert_eq!(image.rows_mut().count(), 1); + } + + #[test] + fn default() { + let image = ImageBuffer::, Vec>::default(); + assert_eq!(image.dimensions(), (0, 0)); + } + + #[test] + #[rustfmt::skip] + fn test_image_buffer_copy_within_oob() { + let mut image: GrayImage = ImageBuffer::from_raw(4, 4, vec![0u8; 16]).unwrap(); + assert!(!image.copy_within(Rect { x: 0, y: 0, width: 5, height: 4 }, 0, 0)); + assert!(!image.copy_within(Rect { x: 0, y: 0, width: 4, height: 5 }, 0, 0)); + assert!(!image.copy_within(Rect { x: 1, y: 0, width: 4, height: 4 }, 0, 0)); + assert!(!image.copy_within(Rect { x: 0, y: 0, width: 4, height: 4 }, 1, 0)); + assert!(!image.copy_within(Rect { x: 0, y: 1, width: 4, height: 4 }, 0, 0)); + assert!(!image.copy_within(Rect { x: 0, y: 0, width: 4, height: 4 }, 0, 1)); + assert!(!image.copy_within(Rect { x: 1, y: 1, width: 4, height: 4 }, 0, 0)); + } + + #[test] + fn test_image_buffer_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.copy_within( + Rect { + x: 0, + y: 0, + width: 3, + height: 3 + }, + 1, + 1 + )); + assert_eq!(&image.into_raw(), &expected); + } + + #[test] + fn test_image_buffer_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.copy_within( + Rect { + x: 1, + y: 0, + width: 3, + height: 3 + }, + 0, + 1 + )); + assert_eq!(&image.into_raw(), &expected); + } + + #[test] + fn test_image_buffer_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.copy_within( + Rect { + x: 0, + y: 1, + width: 3, + height: 3 + }, + 1, + 0 + )); + assert_eq!(&image.into_raw(), &expected); + } + + #[test] + fn test_image_buffer_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.copy_within( + Rect { + x: 1, + y: 1, + width: 3, + height: 3 + }, + 0, + 0 + )); + assert_eq!(&image.into_raw(), &expected); + } + + #[test] + #[cfg(feature = "png")] + fn write_to_with_large_buffer() { + // A buffer of 1 pixel, padded to 4 bytes as would be common in, e.g. BMP. + let img: GrayImage = ImageBuffer::from_raw(1, 1, vec![0u8; 4]).unwrap(); + let mut buffer = std::io::Cursor::new(vec![]); + assert!(img.write_to(&mut buffer, ImageOutputFormat::Png).is_ok()); + } + + #[test] + fn exact_size_iter_size_hint() { + // The docs for `std::iter::ExactSizeIterator` requires that the implementation of + // `size_hint` on the iterator returns the same value as the `len` implementation. + + // This test should work for any size image. + const N: u32 = 10; + + let mut image = RgbImage::from_raw(N, N, vec![0; (N * N * 3) as usize]).unwrap(); + + let iter = image.pixels(); + let exact_len = ExactSizeIterator::len(&iter); + assert_eq!(iter.size_hint(), (exact_len, Some(exact_len))); + + let iter = image.pixels_mut(); + let exact_len = ExactSizeIterator::len(&iter); + assert_eq!(iter.size_hint(), (exact_len, Some(exact_len))); + + let iter = image.rows(); + let exact_len = ExactSizeIterator::len(&iter); + assert_eq!(iter.size_hint(), (exact_len, Some(exact_len))); + + let iter = image.rows_mut(); + let exact_len = ExactSizeIterator::len(&iter); + assert_eq!(iter.size_hint(), (exact_len, Some(exact_len))); + + let iter = image.enumerate_pixels(); + let exact_len = ExactSizeIterator::len(&iter); + assert_eq!(iter.size_hint(), (exact_len, Some(exact_len))); + + let iter = image.enumerate_rows(); + let exact_len = ExactSizeIterator::len(&iter); + assert_eq!(iter.size_hint(), (exact_len, Some(exact_len))); + + let iter = image.enumerate_pixels_mut(); + let exact_len = ExactSizeIterator::len(&iter); + assert_eq!(iter.size_hint(), (exact_len, Some(exact_len))); + + let iter = image.enumerate_rows_mut(); + let exact_len = ExactSizeIterator::len(&iter); + assert_eq!(iter.size_hint(), (exact_len, Some(exact_len))); + } +} + +#[cfg(test)] +#[cfg(feature = "benchmarks")] +mod benchmarks { + use super::{ConvertBuffer, GrayImage, ImageBuffer, Pixel, RgbImage}; + use crate::GenericImage; + use test; + + #[bench] + fn conversion(b: &mut test::Bencher) { + let mut a: RgbImage = ImageBuffer::new(1000, 1000); + for p in a.pixels_mut() { + let rgb = p.channels_mut(); + rgb[0] = 255; + rgb[1] = 23; + rgb[2] = 42; + } + assert!(a.data[0] != 0); + b.iter(|| { + let b: GrayImage = a.convert(); + assert!(0 != b.data[0]); + assert!(a.data[0] != b.data[0]); + test::black_box(b); + }); + b.bytes = 1000 * 1000 * 3 + } + + #[bench] + fn image_access_row_by_row(b: &mut test::Bencher) { + let mut a: RgbImage = ImageBuffer::new(1000, 1000); + for p in a.pixels_mut() { + let rgb = p.channels_mut(); + rgb[0] = 255; + rgb[1] = 23; + rgb[2] = 42; + } + + b.iter(move || { + let image: &RgbImage = test::black_box(&a); + let mut sum: usize = 0; + for y in 0..1000 { + for x in 0..1000 { + let pixel = image.get_pixel(x, y); + sum = sum.wrapping_add(pixel[0] as usize); + sum = sum.wrapping_add(pixel[1] as usize); + sum = sum.wrapping_add(pixel[2] as usize); + } + } + test::black_box(sum) + }); + + b.bytes = 1000 * 1000 * 3; + } + + #[bench] + fn image_access_col_by_col(b: &mut test::Bencher) { + let mut a: RgbImage = ImageBuffer::new(1000, 1000); + for p in a.pixels_mut() { + let rgb = p.channels_mut(); + rgb[0] = 255; + rgb[1] = 23; + rgb[2] = 42; + } + + b.iter(move || { + let image: &RgbImage = test::black_box(&a); + let mut sum: usize = 0; + for x in 0..1000 { + for y in 0..1000 { + let pixel = image.get_pixel(x, y); + sum = sum.wrapping_add(pixel[0] as usize); + sum = sum.wrapping_add(pixel[1] as usize); + sum = sum.wrapping_add(pixel[2] as usize); + } + } + test::black_box(sum) + }); + + b.bytes = 1000 * 1000 * 3; + } +} -- cgit v1.2.3