//! 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;
}
}