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