1#[cfg(not(feature = "std"))]
2use alloc::{format, vec, vec::Vec};
3
4#[derive(Debug, Clone, Default, PartialEq, Eq)]
10pub struct Bitmap {
11 pub width: u32,
12 pub height: u32,
13 pub data: Vec<u8>,
15}
16
17impl Bitmap {
18 pub fn new(width: u32, height: u32) -> Self {
20 let stride = Self::compute_row_stride(width);
21 Bitmap {
22 width,
23 height,
24 data: vec![0u8; stride * height as usize],
25 }
26 }
27
28 #[inline]
30 pub fn row_stride(&self) -> usize {
31 Self::compute_row_stride(self.width)
32 }
33
34 fn compute_row_stride(width: u32) -> usize {
35 (width as usize).div_ceil(8)
36 }
37
38 #[inline]
41 pub fn get(&self, x: u32, y: u32) -> bool {
42 debug_assert!(x < self.width && y < self.height);
43 let stride = self.row_stride();
44 let byte_idx = y as usize * stride + (x as usize / 8);
45 let bit_idx = 7 - (x % 8);
46 (self.data[byte_idx] >> bit_idx) & 1 == 1
47 }
48
49 pub fn set(&mut self, x: u32, y: u32, val: bool) {
52 debug_assert!(x < self.width && y < self.height);
53 let stride = self.row_stride();
54 let byte_idx = y as usize * stride + (x as usize / 8);
55 let bit_idx = 7 - (x % 8);
56 if val {
57 self.data[byte_idx] |= 1 << bit_idx;
58 } else {
59 self.data[byte_idx] &= !(1 << bit_idx);
60 }
61 }
62
63 pub fn set_black(&mut self, x: u32, y: u32) {
65 let stride = self.row_stride();
66 let byte_idx = y as usize * stride + (x as usize / 8);
67 let bit_idx = 7 - (x % 8);
68 self.data[byte_idx] |= 1 << bit_idx;
69 }
70
71 pub fn dilate(&self) -> Bitmap {
75 let mut out = self.clone();
76 for y in 0..self.height {
77 for x in 0..self.width {
78 if self.get(x, y) {
79 if x > 0 {
80 out.set_black(x - 1, y);
81 }
82 if x + 1 < self.width {
83 out.set_black(x + 1, y);
84 }
85 if y > 0 {
86 out.set_black(x, y - 1);
87 }
88 if y + 1 < self.height {
89 out.set_black(x, y + 1);
90 }
91 }
92 }
93 }
94 out
95 }
96
97 pub fn to_pbm(&self) -> Vec<u8> {
100 let header = format!("P4\n{} {}\n", self.width, self.height);
101 let stride = self.row_stride();
102 let mut out = Vec::with_capacity(header.len() + stride * self.height as usize);
103 out.extend_from_slice(header.as_bytes());
104 for y in 0..self.height as usize {
106 let row_start = y * stride;
107 out.extend_from_slice(&self.data[row_start..row_start + stride]);
108 }
109 out
110 }
111
112 pub fn dilate_n(self, passes: u32) -> Self {
122 if passes == 0 {
123 return self;
124 }
125 let width = self.width;
126 let height = self.height;
127 let stride = self.row_stride();
128 let h = height as usize;
129
130 let mut src = self.data;
131 let mut dst = vec![0u8; stride * h];
132
133 for _ in 0..passes {
134 for b in dst.iter_mut() {
135 *b = 0;
136 }
137
138 for y in 0..h {
139 let row = y * stride;
140
141 for i in 0..stride {
142 let v = src[row + i];
143 if v == 0 {
144 continue;
145 }
146
147 dst[row + i] |= v; dst[row + i] |= v >> 1; dst[row + i] |= v << 1; if i + 1 < stride {
153 dst[row + i + 1] |= (v & 0x01) << 7;
154 }
155 if i > 0 {
157 dst[row + i - 1] |= (v & 0x80) >> 7;
158 }
159
160 if y + 1 < h {
162 dst[(y + 1) * stride + i] |= v;
163 }
164 if y > 0 {
166 dst[(y - 1) * stride + i] |= v;
167 }
168 }
169 }
170
171 core::mem::swap(&mut src, &mut dst);
172 }
173
174 Bitmap {
175 width,
176 height,
177 data: src,
178 }
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185
186 #[test]
187 fn new_bitmap_is_all_white() {
188 let bm = Bitmap::new(10, 5);
189 for y in 0..5 {
190 for x in 0..10 {
191 assert!(!bm.get(x, y));
192 }
193 }
194 }
195
196 #[test]
197 fn set_and_get_roundtrip() {
198 let mut bm = Bitmap::new(16, 8);
199 bm.set(0, 0, true);
200 bm.set(15, 0, true);
201 bm.set(7, 3, true);
202 bm.set(0, 7, true);
203 bm.set(15, 7, true);
204
205 assert!(bm.get(0, 0));
206 assert!(bm.get(15, 0));
207 assert!(bm.get(7, 3));
208 assert!(bm.get(0, 7));
209 assert!(bm.get(15, 7));
210
211 assert!(!bm.get(1, 0));
213 assert!(!bm.get(14, 0));
214 assert!(!bm.get(6, 3));
215 assert!(!bm.get(8, 3));
216 }
217
218 #[test]
219 fn set_and_clear() {
220 let mut bm = Bitmap::new(8, 1);
221 bm.set(3, 0, true);
222 assert!(bm.get(3, 0));
223 bm.set(3, 0, false);
224 assert!(!bm.get(3, 0));
225 }
226
227 #[test]
228 fn non_byte_aligned_width() {
229 let mut bm = Bitmap::new(10, 2);
231 assert_eq!(bm.row_stride(), 2);
232 bm.set(9, 0, true);
233 bm.set(9, 1, true);
234 assert!(bm.get(9, 0));
235 assert!(bm.get(9, 1));
236 }
237
238 #[test]
239 fn to_pbm_format() {
240 let mut bm = Bitmap::new(8, 2);
241 bm.set(0, 0, true);
243 bm.set(7, 0, true);
244 for x in 0..8 {
246 bm.set(x, 1, true);
247 }
248 let pbm = bm.to_pbm();
249 let hdr = b"P4\n8 2\n";
250 assert_eq!(&pbm[..hdr.len()], hdr);
251 assert_eq!(pbm[hdr.len()], 0x81);
252 assert_eq!(pbm[hdr.len() + 1], 0xFF);
253 assert_eq!(pbm.len(), hdr.len() + 2);
254 }
255
256 #[test]
257 fn to_pbm_non_byte_aligned() {
258 let mut bm = Bitmap::new(3, 1);
260 bm.set(0, 0, true);
261 bm.set(2, 0, true);
262 let pbm = bm.to_pbm();
263 let hdr = b"P4\n3 1\n";
264 assert_eq!(&pbm[..hdr.len()], hdr);
265 assert_eq!(pbm[hdr.len()], 0xA0);
267 }
268
269 fn set_pixels(bm: &Bitmap) -> Vec<(u32, u32)> {
273 let mut out = Vec::new();
274 for y in 0..bm.height {
275 for x in 0..bm.width {
276 if bm.get(x, y) {
277 out.push((x, y));
278 }
279 }
280 }
281 out
282 }
283
284 #[test]
285 fn dilate_n_zero_passes_is_identity() {
286 let mut bm = Bitmap::new(8, 8);
287 bm.set(3, 3, true);
288 let orig = set_pixels(&bm);
289 let result = set_pixels(&bm.clone().dilate_n(0));
290 assert_eq!(orig, result);
291 }
292
293 #[test]
294 fn dilate_n_one_pass_matches_dilate() {
295 let mut bm = Bitmap::new(16, 16);
296 bm.set(7, 7, true);
297 bm.set(0, 0, true);
298 bm.set(15, 15, true);
299 let expected = set_pixels(&bm.dilate());
300 let got = set_pixels(&bm.clone().dilate_n(1));
301 assert_eq!(expected, got);
302 }
303
304 #[test]
305 fn dilate_n_two_passes_matches_dilate_twice() {
306 let mut bm = Bitmap::new(20, 20);
307 bm.set(10, 10, true);
308 bm.set(1, 1, true);
309 let expected = set_pixels(&bm.dilate().dilate());
310 let got = set_pixels(&bm.clone().dilate_n(2));
311 assert_eq!(expected, got);
312 }
313
314 #[test]
315 fn dilate_n_three_passes_matches_dilate_three_times() {
316 let mut bm = Bitmap::new(20, 20);
317 bm.set(10, 10, true);
318 let expected = set_pixels(&bm.dilate().dilate().dilate());
319 let got = set_pixels(&bm.clone().dilate_n(3));
320 assert_eq!(expected, got);
321 }
322
323 #[test]
324 fn dilate_n_cross_byte_boundary() {
325 let mut bm = Bitmap::new(16, 1);
327 bm.set(7, 0, true);
328 let result = bm.clone().dilate_n(1);
329 assert!(result.get(6, 0), "x-1 neighbour");
330 assert!(result.get(7, 0), "original");
331 assert!(result.get(8, 0), "x+1 neighbour (cross-byte)");
332 let mut bm2 = Bitmap::new(16, 1);
334 bm2.set(8, 0, true);
335 let result2 = bm2.clone().dilate_n(1);
336 assert!(result2.get(7, 0), "x-1 neighbour (cross-byte)");
337 assert!(result2.get(8, 0), "original");
338 assert!(result2.get(9, 0), "x+1 neighbour");
339 }
340
341 #[test]
342 fn dilate_n_boundary_pixels_dont_wrap() {
343 let mut bm = Bitmap::new(16, 16);
345 bm.set(0, 0, true);
346 let result = bm.dilate_n(1);
347 assert!(result.get(0, 0));
348 assert!(result.get(1, 0));
349 assert!(result.get(0, 1));
350 assert!(!result.get(15, 15));
352 }
353}