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}