breakwater_parser/framebuffer/
simple.rs1use core::slice;
2
3use super::FrameBuffer;
4
5pub struct SimpleFrameBuffer {
6 width: usize,
7 height: usize,
8 buffer: Vec<u32>,
9}
10
11impl SimpleFrameBuffer {
12 pub fn new(width: usize, height: usize) -> Self {
13 let mut buffer = Vec::with_capacity(width * height);
14 buffer.resize_with(width * height, || 0);
15 Self {
16 width,
17 height,
18 buffer,
19 }
20 }
21}
22
23impl FrameBuffer for SimpleFrameBuffer {
24 #[inline(always)]
25 fn get_width(&self) -> usize {
26 self.width
27 }
28
29 #[inline(always)]
30 fn get_height(&self) -> usize {
31 self.height
32 }
33
34 #[inline(always)]
35 unsafe fn get_unchecked(&self, x: usize, y: usize) -> u32 {
36 *self.buffer.get_unchecked(x + y * self.width)
37 }
38
39 #[inline(always)]
40 fn set(&self, x: usize, y: usize, rgba: u32) {
41 if x < self.width && y < self.height {
47 unsafe {
48 let ptr = self.buffer.as_ptr().add(x + y * self.width) as *mut u32;
49 *ptr = rgba;
50 }
51 }
52 }
53
54 #[inline(always)]
55 fn set_multi_from_start_index(&self, starting_index: usize, pixels: &[u8]) -> usize {
56 let num_pixels = pixels.len() / 4;
57
58 if starting_index + num_pixels > self.buffer.len() {
59 dbg!(
60 "Ignoring invalid set_multi call, which would exceed the screen",
61 starting_index,
62 num_pixels,
63 self.buffer.len()
64 );
65 return 0;
67 }
68
69 let starting_ptr = unsafe { self.buffer.as_ptr().add(starting_index) };
70 let target_slice =
71 unsafe { slice::from_raw_parts_mut(starting_ptr as *mut u8, pixels.len()) };
72 target_slice.copy_from_slice(pixels);
73
74 num_pixels
75 }
76
77 #[inline(always)]
78 fn as_bytes(&self) -> &[u8] {
79 let len = 4 * self.buffer.len();
80 let ptr = self.buffer.as_ptr() as *const u8;
81 unsafe { std::slice::from_raw_parts(ptr, len) }
82 }
83
84 #[inline(always)]
85 fn as_pixels(&self) -> &[u32] {
86 &self.buffer
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 use rstest::{fixture, rstest};
95
96 #[fixture]
97 fn fb() -> SimpleFrameBuffer {
98 SimpleFrameBuffer::new(640, 480)
100 }
101
102 #[rstest]
103 #[case(0, 0, 0)]
104 #[case(0, 0, 0xff0000)]
105 #[case(0, 0, 0x0000ff)]
106 #[case(0, 0, 0x12345678)]
107 pub fn test_roundtrip(
108 fb: SimpleFrameBuffer,
109 #[case] x: usize,
110 #[case] y: usize,
111 #[case] rgba: u32,
112 ) {
113 fb.set(x, y, rgba);
114 assert_eq!(fb.get(x, y), Some(rgba));
115 }
116
117 #[rstest]
118 pub fn test_out_of_bounds(fb: SimpleFrameBuffer) {
119 assert_eq!(fb.get(usize::MAX, usize::MAX), None);
120 assert_eq!(fb.get(usize::MAX, usize::MAX), None);
121 }
122
123 #[rstest]
124 pub fn test_set_multi_from_beginning(fb: SimpleFrameBuffer) {
125 let pixels = (0..10_u32).collect::<Vec<_>>();
126 let pixel_bytes: Vec<u8> = pixels.iter().flat_map(|p| p.to_le_bytes()).collect();
127
128 let (current_x, current_y) = fb.set_multi(0, 0, &pixel_bytes);
129
130 assert_eq!(current_x, 10);
131 assert_eq!(current_y, 0);
132
133 for x in 0..10 {
134 assert_eq!(fb.get(x as usize, 0), Some(x), "Checking pixel {x}");
135 }
136
137 assert_eq!(fb.get(11, 0), Some(0));
139 }
140
141 #[rstest]
142 pub fn test_set_multi_in_the_middle(fb: SimpleFrameBuffer) {
143 let mut x = 10;
144 let mut y = 100;
145
146 let pixels = (0..3 * fb.width as u32 + 42).collect::<Vec<_>>();
148 let pixel_bytes: Vec<u8> = pixels.iter().flat_map(|p| p.to_le_bytes()).collect();
149 let (current_x, current_y) = fb.set_multi(x, y, &pixel_bytes);
150
151 assert_eq!(current_x, 52);
152 assert_eq!(current_y, 103);
153
154 for rgba in 0..3 * fb.width as u32 + 42 {
156 assert_eq!(fb.get(x, y), Some(rgba));
157
158 x += 1;
159 if x >= fb.width {
160 x = 0;
161 y += 1;
162 }
163 }
164
165 for _ in 0..10 * fb.width as u32 {
167 assert_eq!(fb.get(x, y), Some(0));
168
169 x += 1;
170 if x >= fb.width {
171 x = 0;
172 y += 1;
173 }
174 }
175 }
176
177 #[rstest]
178 pub fn test_set_multi_does_nothing_when_too_long(fb: SimpleFrameBuffer) {
179 let mut too_long = Vec::with_capacity(fb.width * fb.height * 4 );
180 too_long.fill_with(|| 42_u8);
181 let (current_x, current_y) = fb.set_multi(1, 0, &too_long);
182
183 assert_eq!(current_x, 1);
185 assert_eq!(current_y, 0);
186
187 for x in 0..fb.width {
188 for y in 0..fb.height {
189 assert_eq!(fb.get(x, y), Some(0));
190 }
191 }
192 }
193}