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