image_merger/
cell.rs

1use super::core::Image;
2use image::Pixel;
3use std::{
4    cell::UnsafeCell,
5    marker::{Send, Sync},
6    ops::Deref,
7};
8
9/// A struct that allows multible mutable references to an underlying image's data buffer. This is an
10/// unsafe struct and should only be used when no two items are trying to change the same place in the underlying
11/// image's data buffer. This struct is used to allow multible threads to write to the same image at the same time.
12pub struct ImageCell<P: Pixel, U: image::GenericImage<Pixel = P>> {
13    underlying: UnsafeCell<Image<P, U>>,
14}
15
16/// Represents a handout of an image cell. This struct is used to write to the image cell's underlying
17/// image's data buffer without a mutable reference to the underlying image.
18pub struct Handout<'a, P: Pixel, U: image::GenericImage<Pixel = P>> {
19    ic: &'a ImageCell<P, U>,
20    x: u32,
21    y: u32,
22}
23
24impl<P: Pixel, U: image::GenericImage<Pixel = P>> ImageCell<P, U> {
25    pub fn new(image: Image<P, U>) -> Self {
26        Self {
27            underlying: UnsafeCell::new(image),
28        }
29    }
30
31    /// Returns the underlying image.
32    pub fn into_inner(self) -> Image<P, U> {
33        self.underlying.into_inner()
34    }
35
36    #[allow(clippy::mut_from_ref)]
37    pub(crate) fn get_image_mut(&self) -> &mut Image<P, U> {
38        unsafe { &mut *self.underlying.get() }
39    }
40
41    /// Requests a handout at the given coordinates of the undelrying image. Can be be used to write
42    /// to an underlying image buffer across threads without a mutable reference to the underlying image.
43    /// # Safety
44    /// This function is unsafe because it does not implement any thread safety via locks or anything else. It is up to the caller to ensure that
45    /// no two threads are trying to write to the same place in the underlying image's data buffer.
46    ///
47    /// # Arguments
48    /// * `x` - The x coordinate of the pixel to request a handout for.
49    /// * `y` - The y coordinate of the pixel to request a handout for.
50    /// # Returns
51    /// A handout that can be used to write to the underlying image's data buffer.
52    /// # Example
53    /// ```
54    /// use image_merger::{Rgb, raw::ImageCell, Image};
55    /// use image::ImageBuffer;
56    ///
57    /// let buf: ImageBuffer<Rgb<u8>, Vec<u8>> = ImageBuffer::new(100, 100);
58    /// let cell = ImageCell::new(Image::from(buf));
59    /// let mut handout = unsafe { cell.request_handout(0, 0) };
60    /// handout.put_pixel(Rgb([255, 255, 255]));
61    /// ```
62    pub unsafe fn request_handout(&self, x: u32, y: u32) -> Handout<P, U> {
63        Handout { ic: self, x, y }
64    }
65}
66
67impl<P: Pixel, U: image::GenericImage<Pixel = P>> Deref for ImageCell<P, U> {
68    type Target = Image<P, U>;
69
70    fn deref(&self) -> &Self::Target {
71        unsafe { &*self.underlying.get() }
72    }
73}
74
75unsafe impl<P: Pixel, U: image::GenericImage<Pixel = P>> Sync for ImageCell<P, U> {}
76unsafe impl<P: Pixel, U: image::GenericImage<Pixel = P>> Send for ImageCell<P, U> {}
77
78impl<'a, P: Pixel, U: image::GenericImage<Pixel = P>> Handout<'a, P, U> {
79    /// Puts a pixel at the handout's coordinates.
80    /// # Arguments
81    /// * `pixel` - The pixel to place.
82    pub fn put_pixel(&mut self, pixel: P) {
83        let image = self.ic.get_image_mut();
84        image.put_pixel(self.x, self.y, pixel);
85    }
86
87    /// Same as `put_pixel` but does not check bounds.
88    /// # Safety
89    /// This function is unsafe because it does not check bounds wen placing the pixel.
90    /// # Arguments
91    /// * `pixel` - The pixel to place.
92    pub unsafe fn unsafe_put_pixel(&mut self, pixel: P) {
93        let image = self.ic.get_image_mut();
94        image.unsafe_put_pixel(self.x, self.y, pixel);
95    }
96}