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