pixelpwnr_render/
pixmap.rs

1use lazy_static::lazy_static;
2use std::ptr;
3
4use crate::color::Color;
5
6lazy_static! {
7    /// The default color value for each pixel
8    static ref DEFAULT_PIXEL: u32 = Color::black().to_raw();
9}
10
11/// A struct representing a pixelmap for pixelflut.
12///
13/// This struct holds the data for each pixel, and can be concidered a bitmap.
14/// For each pixel, a u32 (DWORD) value is used containing 4 bytes that define
15/// the value for each of the 4 color channels.
16///
17/// This data structure is focussed on performance and multithreaded use with
18/// multiple readers and writers.  This structure does not use any kind of
19/// locks. Instead, it is assumed that the operations done on the internal map
20/// are atomic (on a pixel basis).  This is perfectly fine for what this
21/// pixelmap is used for.
22///
23/// Because this structure is aligned to 4 bytes in memory, each raw color
24/// value (u32) is also aligned to 4 bytes. This makes direct reads and writes
25/// on these values on most CPUs (but not all!). The fact that this may not be
26/// atomic in some cases is accepted for this structure. The speed of not using
27/// locks is preferred over the minor side effect of seldom rendering artifact
28/// on some systems.
29///
30/// More info: https://stackoverflow.com/a/5002256/1000145
31///
32/// Important: this data structure is considered unsafe, but is perfectly
33/// usable for pixelflut applications.
34#[repr(align(4))]
35pub struct Pixmap {
36    /// A map with a raw color value for each pixel in the map, where each
37    /// pixel consists of 4 bytes in a single u32 for each color channel.
38    map: Vec<u32>,
39
40    /// Pixelmap dimensions, width and height
41    dimensions: (usize, usize),
42}
43
44impl Pixmap {
45    /// Construct a new
46    pub fn new(width: usize, height: usize) -> Self {
47        Pixmap {
48            // Build a pixel map, with the default value and the proper sizeto
49            // fit each pixel
50            map: vec![*DEFAULT_PIXEL; width * height],
51
52            // Set the dimensions
53            dimensions: (width, height),
54        }
55    }
56
57    /// Get the width of the pixel map.
58    pub fn width(&self) -> usize {
59        self.dimensions.0
60    }
61
62    /// Get the height of the pixel map.
63    pub fn height(&self) -> usize {
64        self.dimensions.1
65    }
66
67    /// Get the dimensions of the pixel map.
68    #[allow(dead_code)]
69    pub fn dimensions(&self) -> (usize, usize) {
70        self.dimensions
71    }
72
73    /// Get the pixel at the given coordinate, as color.
74    #[allow(dead_code)]
75    pub fn pixel(&self, x: usize, y: usize) -> Result<Color, PixmapErr> {
76        Ok(Color::new(self.pixel_raw(x, y)?))
77    }
78
79    /// Get the pixel at the given coordinate, as raw color value.
80    pub fn pixel_raw(&self, x: usize, y: usize) -> Result<u32, PixmapErr> {
81        Ok(self.map[self.pixel_index(x, y)?])
82    }
83
84    /// Set the pixel at the given coordinate, to the given color.
85    pub fn set_pixel(&self, x: usize, y: usize, color: Color) -> Result<(), PixmapErr> {
86        let mut current_color = self.pixel(x, y)?;
87        current_color.blend(color);
88        self.set_pixel_raw(x, y, current_color.to_raw())
89    }
90
91    /// Set the pixel at the given coordinate, to the given raw color value.
92    pub fn set_pixel_raw(&self, x: usize, y: usize, raw: u32) -> Result<(), PixmapErr> {
93        // Determine the pixel index
94        let index = self.pixel_index(x, y)?;
95
96        // Write the pixel data
97        unsafe {
98            Pixmap::write_pixel_raw(&self.map, index, raw);
99        }
100
101        Ok(())
102    }
103
104    /// Write raw pixel data to the given pixel `map`.
105    ///
106    /// Note: this function writes raw pixel data on the pixel map at the
107    /// given index, even though the map itself is immutable.
108    /// This allows multiple writes from multiple threads at the same time.
109    /// This operation is considered safe however, as the writen set of bytes
110    /// is aligned.
111    /// See the description of this struct for more information.
112    ///
113    /// Note: this method does not check for pixel index bounds, as it's only
114    /// used in this structure internally.
115    unsafe fn write_pixel_raw(map: &Vec<u32>, i: usize, raw: u32) {
116        // Create a mutable pointer, to the pixel data on the immutable pixel map
117        let pixel_ptr: *mut u32 = (&map[i] as *const u32) as *mut u32;
118
119        // Write the new raw value to the pointer
120        ptr::write(pixel_ptr, raw);
121    }
122
123    /// Get the index a pixel is at, for the given coordinate.
124    fn pixel_index(&self, x: usize, y: usize) -> Result<usize, PixmapErr> {
125        // Check pixel bounds
126        if x >= self.dimensions.0 {
127            return Err(PixmapErr::OutOfBound("x coordinate out of bound"));
128        } else if y >= self.dimensions.1 {
129            return Err(PixmapErr::OutOfBound("y coordinate out of bound"));
130        }
131
132        // Determine the index and return
133        Ok(y * self.dimensions.0 + x)
134    }
135
136    /// Get the pixelmap data, as slice with the raw color value of each
137    /// pixel.
138    ///
139    /// Note: this method returns a single u32 for each pixel, instead of 4
140    /// u8 bytes for each pixel as the `as_bytes()` method does.
141    pub fn as_slice(&self) -> &[u32] {
142        self.map.as_slice()
143    }
144
145    /// Get the pixelmap data, as a slice of bytes.
146    ///
147    /// Each pixel consumes a sequence of 4 bytes, each defining the value of
148    /// a different color channel.
149    ///
150    /// This data may be used to send to the GPU, as raw texture buffer, for
151    /// rendering.
152    pub fn as_bytes(&self) -> &[u8] {
153        // The following code transmutes the raw slice bytes from the
154        // `[u32; size]` type into `[u8; size * 4]`. Cloning the data array
155        // and casting each raw value to 4 u8 bytes if a very expensive
156        // operation to do each frame for such a big array of pixels.
157        // Transmuting is considered unsafe, but usually is about a 1000 times
158        // faster resulting in insane performance gains. This unsafe bit of
159        // code is desirable over safe code that is enormously slower.
160        // The implementation below is memory safe.
161
162        let slice_u32 = self.as_slice();
163        let len = slice_u32.len() * 4;
164
165        unsafe { core::slice::from_raw_parts(slice_u32.as_ptr() as _, len) }
166    }
167}
168
169unsafe impl Send for Pixmap {}
170unsafe impl Sync for Pixmap {}
171
172/// An error representation for pixel map operations.
173#[derive(Debug)]
174pub enum PixmapErr<'a> {
175    /// The given pixel coordinate or index is out of bound.
176    OutOfBound(&'a str),
177}