Skip to main content

ai_image/images/
buffer_par.rs

1use alloc::vec::Vec;
2use core::fmt;
3use core::ops::{Deref, DerefMut};
4use rayon::iter::plumbing::*;
5use rayon::iter::{IndexedParallelIterator, ParallelIterator};
6use rayon::slice::{ChunksExact, ChunksExactMut, ParallelSlice, ParallelSliceMut};
7
8use crate::traits::Pixel;
9use crate::ImageBuffer;
10
11/// Parallel iterator over pixel refs.
12#[derive(Clone)]
13pub struct PixelsPar<'a, P>
14where
15    P: Pixel + Sync + 'a,
16    P::Subpixel: Sync + 'a,
17{
18    chunks: ChunksExact<'a, P::Subpixel>,
19}
20
21impl<'a, P> ParallelIterator for PixelsPar<'a, P>
22where
23    P: Pixel + Sync + 'a,
24    P::Subpixel: Sync + 'a,
25{
26    type Item = &'a P;
27
28    fn drive_unindexed<C>(self, consumer: C) -> C::Result
29    where
30        C: UnindexedConsumer<Self::Item>,
31    {
32        self.chunks
33            .map(|v| <P as Pixel>::from_slice(v))
34            .drive_unindexed(consumer)
35    }
36
37    fn opt_len(&self) -> Option<usize> {
38        Some(self.len())
39    }
40}
41
42impl<'a, P> IndexedParallelIterator for PixelsPar<'a, P>
43where
44    P: Pixel + Sync + 'a,
45    P::Subpixel: Sync + 'a,
46{
47    fn drive<C: Consumer<Self::Item>>(self, consumer: C) -> C::Result {
48        self.chunks
49            .map(|v| <P as Pixel>::from_slice(v))
50            .drive(consumer)
51    }
52
53    fn len(&self) -> usize {
54        self.chunks.len()
55    }
56
57    fn with_producer<CB: ProducerCallback<Self::Item>>(self, callback: CB) -> CB::Output {
58        self.chunks
59            .map(|v| <P as Pixel>::from_slice(v))
60            .with_producer(callback)
61    }
62}
63
64impl<P> fmt::Debug for PixelsPar<'_, P>
65where
66    P: Pixel + Sync,
67    P::Subpixel: Sync + fmt::Debug,
68{
69    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
70        f.debug_struct("PixelsPar")
71            .field("chunks", &self.chunks)
72            .finish()
73    }
74}
75
76/// Parallel iterator over mutable pixel refs.
77pub struct PixelsMutPar<'a, P>
78where
79    P: Pixel + Send + Sync + 'a,
80    P::Subpixel: Send + Sync + 'a,
81{
82    chunks: ChunksExactMut<'a, P::Subpixel>,
83}
84
85impl<'a, P> ParallelIterator for PixelsMutPar<'a, P>
86where
87    P: Pixel + Send + Sync + 'a,
88    P::Subpixel: Send + Sync + 'a,
89{
90    type Item = &'a mut P;
91
92    fn drive_unindexed<C>(self, consumer: C) -> C::Result
93    where
94        C: UnindexedConsumer<Self::Item>,
95    {
96        self.chunks
97            .map(|v| <P as Pixel>::from_slice_mut(v))
98            .drive_unindexed(consumer)
99    }
100
101    fn opt_len(&self) -> Option<usize> {
102        Some(self.len())
103    }
104}
105
106impl<'a, P> IndexedParallelIterator for PixelsMutPar<'a, P>
107where
108    P: Pixel + Send + Sync + 'a,
109    P::Subpixel: Send + Sync + 'a,
110{
111    fn drive<C: Consumer<Self::Item>>(self, consumer: C) -> C::Result {
112        self.chunks
113            .map(|v| <P as Pixel>::from_slice_mut(v))
114            .drive(consumer)
115    }
116
117    fn len(&self) -> usize {
118        self.chunks.len()
119    }
120
121    fn with_producer<CB: ProducerCallback<Self::Item>>(self, callback: CB) -> CB::Output {
122        self.chunks
123            .map(|v| <P as Pixel>::from_slice_mut(v))
124            .with_producer(callback)
125    }
126}
127
128impl<P> fmt::Debug for PixelsMutPar<'_, P>
129where
130    P: Pixel + Send + Sync,
131    P::Subpixel: Send + Sync + fmt::Debug,
132{
133    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
134        f.debug_struct("PixelsMutPar")
135            .field("chunks", &self.chunks)
136            .finish()
137    }
138}
139
140/// Parallel iterator over pixel refs and their coordinates.
141#[derive(Clone)]
142pub struct EnumeratePixelsPar<'a, P>
143where
144    P: Pixel + Sync + 'a,
145    P::Subpixel: Sync + 'a,
146{
147    pixels: PixelsPar<'a, P>,
148    width: u32,
149}
150
151impl<'a, P> ParallelIterator for EnumeratePixelsPar<'a, P>
152where
153    P: Pixel + Sync + 'a,
154    P::Subpixel: Sync + 'a,
155{
156    type Item = (u32, u32, &'a P);
157
158    fn drive_unindexed<C>(self, consumer: C) -> C::Result
159    where
160        C: UnindexedConsumer<Self::Item>,
161    {
162        self.pixels
163            .enumerate()
164            .map(|(i, p)| {
165                (
166                    (i % self.width as usize) as u32,
167                    (i / self.width as usize) as u32,
168                    p,
169                )
170            })
171            .drive_unindexed(consumer)
172    }
173
174    fn opt_len(&self) -> Option<usize> {
175        Some(self.len())
176    }
177}
178
179impl<'a, P> IndexedParallelIterator for EnumeratePixelsPar<'a, P>
180where
181    P: Pixel + Sync + 'a,
182    P::Subpixel: Sync + 'a,
183{
184    fn drive<C: Consumer<Self::Item>>(self, consumer: C) -> C::Result {
185        self.pixels
186            .enumerate()
187            .map(|(i, p)| {
188                (
189                    (i % self.width as usize) as u32,
190                    (i / self.width as usize) as u32,
191                    p,
192                )
193            })
194            .drive(consumer)
195    }
196
197    fn len(&self) -> usize {
198        self.pixels.len()
199    }
200
201    fn with_producer<CB: ProducerCallback<Self::Item>>(self, callback: CB) -> CB::Output {
202        self.pixels
203            .enumerate()
204            .map(|(i, p)| {
205                (
206                    (i % self.width as usize) as u32,
207                    (i / self.width as usize) as u32,
208                    p,
209                )
210            })
211            .with_producer(callback)
212    }
213}
214
215impl<P> fmt::Debug for EnumeratePixelsPar<'_, P>
216where
217    P: Pixel + Sync,
218    P::Subpixel: Sync + fmt::Debug,
219{
220    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
221        f.debug_struct("EnumeratePixelsPar")
222            .field("pixels", &self.pixels)
223            .field("width", &self.width)
224            .finish()
225    }
226}
227
228/// Parallel iterator over mutable pixel refs and their coordinates.
229pub struct EnumeratePixelsMutPar<'a, P>
230where
231    P: Pixel + Send + Sync + 'a,
232    P::Subpixel: Send + Sync + 'a,
233{
234    pixels: PixelsMutPar<'a, P>,
235    width: u32,
236}
237
238impl<'a, P> ParallelIterator for EnumeratePixelsMutPar<'a, P>
239where
240    P: Pixel + Send + Sync + 'a,
241    P::Subpixel: Send + Sync + 'a,
242{
243    type Item = (u32, u32, &'a mut P);
244
245    fn drive_unindexed<C>(self, consumer: C) -> C::Result
246    where
247        C: UnindexedConsumer<Self::Item>,
248    {
249        self.pixels
250            .enumerate()
251            .map(|(i, p)| {
252                (
253                    (i % self.width as usize) as u32,
254                    (i / self.width as usize) as u32,
255                    p,
256                )
257            })
258            .drive_unindexed(consumer)
259    }
260
261    fn opt_len(&self) -> Option<usize> {
262        Some(self.len())
263    }
264}
265
266impl<'a, P> IndexedParallelIterator for EnumeratePixelsMutPar<'a, P>
267where
268    P: Pixel + Send + Sync + 'a,
269    P::Subpixel: Send + Sync + 'a,
270{
271    fn drive<C: Consumer<Self::Item>>(self, consumer: C) -> C::Result {
272        self.pixels
273            .enumerate()
274            .map(|(i, p)| {
275                (
276                    (i % self.width as usize) as u32,
277                    (i / self.width as usize) as u32,
278                    p,
279                )
280            })
281            .drive(consumer)
282    }
283
284    fn len(&self) -> usize {
285        self.pixels.len()
286    }
287
288    fn with_producer<CB: ProducerCallback<Self::Item>>(self, callback: CB) -> CB::Output {
289        self.pixels
290            .enumerate()
291            .map(|(i, p)| {
292                (
293                    (i % self.width as usize) as u32,
294                    (i / self.width as usize) as u32,
295                    p,
296                )
297            })
298            .with_producer(callback)
299    }
300}
301
302impl<P> fmt::Debug for EnumeratePixelsMutPar<'_, P>
303where
304    P: Pixel + Send + Sync,
305    P::Subpixel: Send + Sync + fmt::Debug,
306{
307    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
308        f.debug_struct("EnumeratePixelsMutPar")
309            .field("pixels", &self.pixels)
310            .field("width", &self.width)
311            .finish()
312    }
313}
314
315impl<P, Container> ImageBuffer<P, Container>
316where
317    P: Pixel + Sync,
318    P::Subpixel: Sync,
319    Container: Deref<Target = [P::Subpixel]>,
320{
321    /// Returns a parallel iterator over the pixels of this image, usable with `rayon`.
322    /// See [`pixels`] for more information.
323    ///
324    /// [`pixels`]: #method.pixels
325    pub fn par_pixels(&self) -> PixelsPar<'_, P> {
326        PixelsPar {
327            chunks: self
328                .inner_pixels()
329                .par_chunks_exact(<P as Pixel>::CHANNEL_COUNT as usize),
330        }
331    }
332
333    /// Returns a parallel iterator over the pixels of this image and their coordinates, usable with `rayon`.
334    /// See [`enumerate_pixels`] for more information.
335    ///
336    /// [`enumerate_pixels`]: #method.enumerate_pixels
337    pub fn par_enumerate_pixels(&self) -> EnumeratePixelsPar<'_, P> {
338        EnumeratePixelsPar {
339            pixels: self.par_pixels(),
340            width: self.width(),
341        }
342    }
343}
344
345impl<P, Container> ImageBuffer<P, Container>
346where
347    P: Pixel + Send + Sync,
348    P::Subpixel: Send + Sync,
349    Container: Deref<Target = [P::Subpixel]> + DerefMut,
350{
351    /// Returns a parallel iterator over the mutable pixels of this image, usable with `rayon`.
352    /// See [`pixels_mut`] for more information.
353    ///
354    /// [`pixels_mut`]: #method.pixels_mut
355    pub fn par_pixels_mut(&mut self) -> PixelsMutPar<'_, P> {
356        PixelsMutPar {
357            chunks: self
358                .inner_pixels_mut()
359                .par_chunks_exact_mut(<P as Pixel>::CHANNEL_COUNT as usize),
360        }
361    }
362
363    /// Returns a parallel iterator over the mutable pixels of this image and their coordinates, usable with `rayon`.
364    /// See [`enumerate_pixels_mut`] for more information.
365    ///
366    /// [`enumerate_pixels_mut`]: #method.enumerate_pixels_mut
367    pub fn par_enumerate_pixels_mut(&mut self) -> EnumeratePixelsMutPar<'_, P> {
368        let width = self.width();
369        EnumeratePixelsMutPar {
370            pixels: self.par_pixels_mut(),
371            width,
372        }
373    }
374}
375
376impl<P> ImageBuffer<P, Vec<P::Subpixel>>
377where
378    P: Pixel + Send + Sync,
379    P::Subpixel: Send + Sync,
380{
381    /// Constructs a new `ImageBuffer` by repeated application of the supplied function,
382    /// utilizing multi-threading via `rayon`.
383    ///
384    /// The arguments to the function are the pixel's x and y coordinates.
385    ///
386    /// # Panics
387    ///
388    /// Panics when the resulting image is larger than the maximum size of a vector.
389    pub fn from_par_fn<F>(width: u32, height: u32, f: F) -> ImageBuffer<P, Vec<P::Subpixel>>
390    where
391        F: Fn(u32, u32) -> P + Send + Sync,
392    {
393        let mut buf = ImageBuffer::new(width, height);
394        buf.par_enumerate_pixels_mut().for_each(|(x, y, p)| {
395            *p = f(x, y);
396        });
397
398        buf
399    }
400}
401
402#[cfg(test)]
403mod test {
404    use crate::{Rgb, RgbImage};
405    use rayon::iter::{IndexedParallelIterator, ParallelIterator};
406
407    fn test_width_height(width: u32, height: u32, len: usize) {
408        let mut image = RgbImage::new(width, height);
409
410        assert_eq!(image.par_enumerate_pixels_mut().len(), len);
411        assert_eq!(image.par_enumerate_pixels().len(), len);
412        assert_eq!(image.par_pixels_mut().len(), len);
413        assert_eq!(image.par_pixels().len(), len);
414    }
415
416    #[test]
417    fn zero_width_zero_height() {
418        test_width_height(0, 0, 0);
419    }
420
421    #[test]
422    fn zero_width_nonzero_height() {
423        test_width_height(0, 2, 0);
424    }
425
426    #[test]
427    fn nonzero_width_zero_height() {
428        test_width_height(2, 0, 0);
429    }
430
431    #[test]
432    fn iter_parity() {
433        let mut image1 = RgbImage::from_fn(17, 29, |x, y| {
434            Rgb(core::array::from_fn(|i| {
435                ((x + y * 98 + i as u32 * 27) % 255) as u8
436            }))
437        });
438        let mut image2 = image1.clone();
439
440        assert_eq!(
441            image1.enumerate_pixels_mut().collect::<Vec<_>>(),
442            image2.par_enumerate_pixels_mut().collect::<Vec<_>>()
443        );
444        assert_eq!(
445            image1.enumerate_pixels().collect::<Vec<_>>(),
446            image2.par_enumerate_pixels().collect::<Vec<_>>()
447        );
448        assert_eq!(
449            image1.pixels_mut().collect::<Vec<_>>(),
450            image2.par_pixels_mut().collect::<Vec<_>>()
451        );
452        assert_eq!(
453            image1.pixels().collect::<Vec<_>>(),
454            image2.par_pixels().collect::<Vec<_>>()
455        );
456    }
457}
458
459#[cfg(test)]
460#[cfg(feature = "benchmarks")]
461mod benchmarks {
462    use crate::{Rgb, RgbImage};
463
464    const S: u32 = 1024;
465
466    #[bench]
467    fn creation(b: &mut test::Bencher) {
468        let mut bytes = 0;
469        b.iter(|| {
470            let img = RgbImage::from_fn(S, S, |_, _| test::black_box(pixel_func()));
471
472            bytes += img.as_raw().len() as u64;
473        });
474
475        b.bytes = bytes;
476    }
477
478    #[bench]
479    fn creation_par(b: &mut test::Bencher) {
480        let mut bytes = 0;
481        b.iter(|| {
482            let img = RgbImage::from_par_fn(S, S, |_, _| test::black_box(pixel_func()));
483
484            bytes += img.as_raw().len() as u64;
485        });
486
487        b.bytes = bytes;
488    }
489
490    fn pixel_func() -> Rgb<u8> {
491        use std::collections::hash_map::RandomState;
492        use std::hash::{BuildHasher, Hasher};
493        Rgb(core::array::from_fn(|_| {
494            RandomState::new().build_hasher().finish() as u8
495        }))
496    }
497}