use rayon::iter::plumbing::*;
use rayon::iter::{IndexedParallelIterator, ParallelIterator};
use rayon::slice::{ChunksExact, ChunksExactMut, ParallelSlice, ParallelSliceMut};
use std::fmt;
use std::ops::{Deref, DerefMut};
use crate::traits::Pixel;
use crate::ImageBuffer;
#[derive(Clone)]
pub struct PixelsPar<'a, P>
where
P: Pixel + Sync + 'a,
P::Subpixel: Sync + 'a,
{
chunks: ChunksExact<'a, P::Subpixel>,
}
impl<'a, P> ParallelIterator for PixelsPar<'a, P>
where
P: Pixel + Sync + 'a,
P::Subpixel: Sync + 'a,
{
type Item = &'a P;
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
self.chunks
.map(|v| <P as Pixel>::from_slice(v))
.drive_unindexed(consumer)
}
fn opt_len(&self) -> Option<usize> {
Some(self.len())
}
}
impl<'a, P> IndexedParallelIterator for PixelsPar<'a, P>
where
P: Pixel + Sync + 'a,
P::Subpixel: Sync + 'a,
{
fn drive<C: Consumer<Self::Item>>(self, consumer: C) -> C::Result {
self.chunks
.map(|v| <P as Pixel>::from_slice(v))
.drive(consumer)
}
fn len(&self) -> usize {
self.chunks.len()
}
fn with_producer<CB: ProducerCallback<Self::Item>>(self, callback: CB) -> CB::Output {
self.chunks
.map(|v| <P as Pixel>::from_slice(v))
.with_producer(callback)
}
}
impl<P> fmt::Debug for PixelsPar<'_, P>
where
P: Pixel + Sync,
P::Subpixel: Sync + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("PixelsPar")
.field("chunks", &self.chunks)
.finish()
}
}
pub struct PixelsMutPar<'a, P>
where
P: Pixel + Send + Sync + 'a,
P::Subpixel: Send + Sync + 'a,
{
chunks: ChunksExactMut<'a, P::Subpixel>,
}
impl<'a, P> ParallelIterator for PixelsMutPar<'a, P>
where
P: Pixel + Send + Sync + 'a,
P::Subpixel: Send + Sync + 'a,
{
type Item = &'a mut P;
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
self.chunks
.map(|v| <P as Pixel>::from_slice_mut(v))
.drive_unindexed(consumer)
}
fn opt_len(&self) -> Option<usize> {
Some(self.len())
}
}
impl<'a, P> IndexedParallelIterator for PixelsMutPar<'a, P>
where
P: Pixel + Send + Sync + 'a,
P::Subpixel: Send + Sync + 'a,
{
fn drive<C: Consumer<Self::Item>>(self, consumer: C) -> C::Result {
self.chunks
.map(|v| <P as Pixel>::from_slice_mut(v))
.drive(consumer)
}
fn len(&self) -> usize {
self.chunks.len()
}
fn with_producer<CB: ProducerCallback<Self::Item>>(self, callback: CB) -> CB::Output {
self.chunks
.map(|v| <P as Pixel>::from_slice_mut(v))
.with_producer(callback)
}
}
impl<P> fmt::Debug for PixelsMutPar<'_, P>
where
P: Pixel + Send + Sync,
P::Subpixel: Send + Sync + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("PixelsMutPar")
.field("chunks", &self.chunks)
.finish()
}
}
#[derive(Clone)]
pub struct EnumeratePixelsPar<'a, P>
where
P: Pixel + Sync + 'a,
P::Subpixel: Sync + 'a,
{
pixels: PixelsPar<'a, P>,
width: u32,
}
impl<'a, P> ParallelIterator for EnumeratePixelsPar<'a, P>
where
P: Pixel + Sync + 'a,
P::Subpixel: Sync + 'a,
{
type Item = (u32, u32, &'a P);
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
self.pixels
.enumerate()
.map(|(i, p)| {
(
(i % self.width as usize) as u32,
(i / self.width as usize) as u32,
p,
)
})
.drive_unindexed(consumer)
}
fn opt_len(&self) -> Option<usize> {
Some(self.len())
}
}
impl<'a, P> IndexedParallelIterator for EnumeratePixelsPar<'a, P>
where
P: Pixel + Sync + 'a,
P::Subpixel: Sync + 'a,
{
fn drive<C: Consumer<Self::Item>>(self, consumer: C) -> C::Result {
self.pixels
.enumerate()
.map(|(i, p)| {
(
(i % self.width as usize) as u32,
(i / self.width as usize) as u32,
p,
)
})
.drive(consumer)
}
fn len(&self) -> usize {
self.pixels.len()
}
fn with_producer<CB: ProducerCallback<Self::Item>>(self, callback: CB) -> CB::Output {
self.pixels
.enumerate()
.map(|(i, p)| {
(
(i % self.width as usize) as u32,
(i / self.width as usize) as u32,
p,
)
})
.with_producer(callback)
}
}
impl<P> fmt::Debug for EnumeratePixelsPar<'_, P>
where
P: Pixel + Sync,
P::Subpixel: Sync + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("EnumeratePixelsPar")
.field("pixels", &self.pixels)
.field("width", &self.width)
.finish()
}
}
pub struct EnumeratePixelsMutPar<'a, P>
where
P: Pixel + Send + Sync + 'a,
P::Subpixel: Send + Sync + 'a,
{
pixels: PixelsMutPar<'a, P>,
width: u32,
}
impl<'a, P> ParallelIterator for EnumeratePixelsMutPar<'a, P>
where
P: Pixel + Send + Sync + 'a,
P::Subpixel: Send + Sync + 'a,
{
type Item = (u32, u32, &'a mut P);
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
self.pixels
.enumerate()
.map(|(i, p)| {
(
(i % self.width as usize) as u32,
(i / self.width as usize) as u32,
p,
)
})
.drive_unindexed(consumer)
}
fn opt_len(&self) -> Option<usize> {
Some(self.len())
}
}
impl<'a, P> IndexedParallelIterator for EnumeratePixelsMutPar<'a, P>
where
P: Pixel + Send + Sync + 'a,
P::Subpixel: Send + Sync + 'a,
{
fn drive<C: Consumer<Self::Item>>(self, consumer: C) -> C::Result {
self.pixels
.enumerate()
.map(|(i, p)| {
(
(i % self.width as usize) as u32,
(i / self.width as usize) as u32,
p,
)
})
.drive(consumer)
}
fn len(&self) -> usize {
self.pixels.len()
}
fn with_producer<CB: ProducerCallback<Self::Item>>(self, callback: CB) -> CB::Output {
self.pixels
.enumerate()
.map(|(i, p)| {
(
(i % self.width as usize) as u32,
(i / self.width as usize) as u32,
p,
)
})
.with_producer(callback)
}
}
impl<P> fmt::Debug for EnumeratePixelsMutPar<'_, P>
where
P: Pixel + Send + Sync,
P::Subpixel: Send + Sync + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("EnumeratePixelsMutPar")
.field("pixels", &self.pixels)
.field("width", &self.width)
.finish()
}
}
impl<P, Container> ImageBuffer<P, Container>
where
P: Pixel + Sync,
P::Subpixel: Sync,
Container: Deref<Target = [P::Subpixel]>,
{
pub fn par_pixels(&self) -> PixelsPar<P> {
PixelsPar {
chunks: self
.inner_pixels()
.par_chunks_exact(<P as Pixel>::CHANNEL_COUNT as usize),
}
}
pub fn par_enumerate_pixels(&self) -> EnumeratePixelsPar<P> {
EnumeratePixelsPar {
pixels: self.par_pixels(),
width: self.width(),
}
}
}
impl<P, Container> ImageBuffer<P, Container>
where
P: Pixel + Send + Sync,
P::Subpixel: Send + Sync,
Container: Deref<Target = [P::Subpixel]> + DerefMut,
{
pub fn par_pixels_mut(&mut self) -> PixelsMutPar<P> {
PixelsMutPar {
chunks: self
.inner_pixels_mut()
.par_chunks_exact_mut(<P as Pixel>::CHANNEL_COUNT as usize),
}
}
pub fn par_enumerate_pixels_mut(&mut self) -> EnumeratePixelsMutPar<P> {
let width = self.width();
EnumeratePixelsMutPar {
pixels: self.par_pixels_mut(),
width,
}
}
}
impl<P> ImageBuffer<P, Vec<P::Subpixel>>
where
P: Pixel + Send + Sync,
P::Subpixel: Send + Sync,
{
pub fn from_par_fn<F>(width: u32, height: u32, f: F) -> ImageBuffer<P, Vec<P::Subpixel>>
where
F: Fn(u32, u32) -> P + Send + Sync,
{
let mut buf = ImageBuffer::new(width, height);
buf.par_enumerate_pixels_mut().for_each(|(x, y, p)| {
*p = f(x, y);
});
buf
}
}
#[cfg(test)]
mod test {
use crate::{Rgb, RgbImage};
use rayon::iter::{IndexedParallelIterator, ParallelIterator};
fn test_width_height(width: u32, height: u32, len: usize) {
let mut image = RgbImage::new(width, height);
assert_eq!(image.par_enumerate_pixels_mut().len(), len);
assert_eq!(image.par_enumerate_pixels().len(), len);
assert_eq!(image.par_pixels_mut().len(), len);
assert_eq!(image.par_pixels().len(), len);
}
#[test]
fn zero_width_zero_height() {
test_width_height(0, 0, 0);
}
#[test]
fn zero_width_nonzero_height() {
test_width_height(0, 2, 0);
}
#[test]
fn nonzero_width_zero_height() {
test_width_height(2, 0, 0);
}
#[test]
fn iter_parity() {
let mut image1 = RgbImage::from_fn(17, 29, |x, y| {
Rgb(std::array::from_fn(|i| {
((x + y * 98 + i as u32 * 27) % 255) as u8
}))
});
let mut image2 = image1.clone();
assert_eq!(
image1.enumerate_pixels_mut().collect::<Vec<_>>(),
image2.par_enumerate_pixels_mut().collect::<Vec<_>>()
);
assert_eq!(
image1.enumerate_pixels().collect::<Vec<_>>(),
image2.par_enumerate_pixels().collect::<Vec<_>>()
);
assert_eq!(
image1.pixels_mut().collect::<Vec<_>>(),
image2.par_pixels_mut().collect::<Vec<_>>()
);
assert_eq!(
image1.pixels().collect::<Vec<_>>(),
image2.par_pixels().collect::<Vec<_>>()
);
}
}
#[cfg(test)]
#[cfg(feature = "benchmarks")]
mod benchmarks {
use crate::{Rgb, RgbImage};
const S: u32 = 1024;
#[bench]
fn creation(b: &mut test::Bencher) {
let mut bytes = 0;
b.iter(|| {
let img = RgbImage::from_fn(S, S, |_, _| test::black_box(pixel_func()));
bytes += img.as_raw().len() as u64;
});
b.bytes = bytes;
}
#[bench]
fn creation_par(b: &mut test::Bencher) {
let mut bytes = 0;
b.iter(|| {
let img = RgbImage::from_par_fn(S, S, |_, _| test::black_box(pixel_func()));
bytes += img.as_raw().len() as u64;
});
b.bytes = bytes;
}
fn pixel_func() -> Rgb<u8> {
use std::collections::hash_map::RandomState;
use std::hash::{BuildHasher, Hasher};
Rgb(std::array::from_fn(|_| {
RandomState::new().build_hasher().finish() as u8
}))
}
}