veridian_kernel/graphics/
texture_atlas.rs1#![allow(dead_code)]
10
11use alloc::vec::Vec;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub struct AtlasRegion {
20 pub x: u32,
22 pub y: u32,
24 pub width: u32,
26 pub height: u32,
28}
29
30impl AtlasRegion {
31 pub fn area(&self) -> u32 {
33 self.width * self.height
34 }
35
36 pub fn contains(&self, px: u32, py: u32) -> bool {
38 px >= self.x && px < self.x + self.width && py >= self.y && py < self.y + self.height
39 }
40}
41
42#[derive(Debug, Clone, Copy)]
48struct FreeSpan {
49 x: u32,
50 width: u32,
51}
52
53#[derive(Debug, Clone)]
59struct Shelf {
60 y: u32,
62 height: u32,
64 free_spans: Vec<FreeSpan>,
66}
67
68impl Shelf {
69 fn new(y: u32, height: u32, atlas_width: u32) -> Self {
71 Self {
72 y,
73 height,
74 free_spans: alloc::vec![FreeSpan {
75 x: 0,
76 width: atlas_width,
77 }],
78 }
79 }
80
81 fn allocate(&mut self, w: u32, h: u32) -> Option<AtlasRegion> {
84 if h > self.height {
85 return None;
86 }
87
88 for i in 0..self.free_spans.len() {
89 let span = self.free_spans[i];
90 if span.width >= w {
91 let region = AtlasRegion {
92 x: span.x,
93 y: self.y,
94 width: w,
95 height: h,
96 };
97
98 if span.width == w {
100 self.free_spans.remove(i);
101 } else {
102 self.free_spans[i] = FreeSpan {
103 x: span.x + w,
104 width: span.width - w,
105 };
106 }
107
108 return Some(region);
109 }
110 }
111
112 None
113 }
114
115 fn deallocate(&mut self, region: &AtlasRegion) {
117 if region.y != self.y {
118 return;
119 }
120
121 let new_span = FreeSpan {
123 x: region.x,
124 width: region.width,
125 };
126
127 let pos = self
129 .free_spans
130 .iter()
131 .position(|s| s.x > new_span.x)
132 .unwrap_or(self.free_spans.len());
133
134 self.free_spans.insert(pos, new_span);
135
136 self.merge_spans();
138 }
139
140 fn merge_spans(&mut self) {
142 let mut i = 0;
143 while i + 1 < self.free_spans.len() {
144 let a_end = self.free_spans[i].x + self.free_spans[i].width;
145 if a_end == self.free_spans[i + 1].x {
146 self.free_spans[i].width += self.free_spans[i + 1].width;
147 self.free_spans.remove(i + 1);
148 } else {
149 i += 1;
150 }
151 }
152 }
153}
154
155#[derive(Debug)]
164pub struct ShelfAllocator {
165 atlas_width: u32,
167 atlas_height: u32,
169 shelves: Vec<Shelf>,
171 next_shelf_y: u32,
173 allocation_count: u32,
175}
176
177impl ShelfAllocator {
178 pub fn new(width: u32, height: u32) -> Self {
180 Self {
181 atlas_width: width,
182 atlas_height: height,
183 shelves: Vec::new(),
184 next_shelf_y: 0,
185 allocation_count: 0,
186 }
187 }
188
189 pub fn width(&self) -> u32 {
191 self.atlas_width
192 }
193
194 pub fn height(&self) -> u32 {
196 self.atlas_height
197 }
198
199 pub fn allocation_count(&self) -> u32 {
201 self.allocation_count
202 }
203
204 pub fn allocate(&mut self, width: u32, height: u32) -> Option<AtlasRegion> {
208 if width == 0 || height == 0 || width > self.atlas_width {
209 return None;
210 }
211
212 let mut best_idx: Option<usize> = None;
214 let mut best_waste = u32::MAX;
215
216 for (i, shelf) in self.shelves.iter().enumerate() {
217 if shelf.height >= height {
218 let waste = shelf.height - height;
219 let has_space = shelf.free_spans.iter().any(|s| s.width >= width);
221 if has_space && waste < best_waste {
222 best_waste = waste;
223 best_idx = Some(i);
224 }
225 }
226 }
227
228 if let Some(idx) = best_idx {
229 let region = self.shelves[idx].allocate(width, height);
230 if region.is_some() {
231 self.allocation_count += 1;
232 }
233 return region;
234 }
235
236 if self.next_shelf_y + height > self.atlas_height {
238 return None; }
240
241 let mut shelf = Shelf::new(self.next_shelf_y, height, self.atlas_width);
242 let region = shelf.allocate(width, height);
243 self.next_shelf_y += height;
244 self.shelves.push(shelf);
245
246 if region.is_some() {
247 self.allocation_count += 1;
248 }
249 region
250 }
251
252 pub fn deallocate(&mut self, region: &AtlasRegion) {
256 for shelf in &mut self.shelves {
257 if shelf.y == region.y {
258 shelf.deallocate(region);
259 if self.allocation_count > 0 {
260 self.allocation_count -= 1;
261 }
262 return;
263 }
264 }
265 }
266
267 pub fn shelf_count(&self) -> usize {
269 self.shelves.len()
270 }
271
272 pub fn utilisation_percent(&self) -> u32 {
274 if self.atlas_height == 0 {
275 return 0;
276 }
277 (self.next_shelf_y * 100) / self.atlas_height
278 }
279
280 pub fn reset(&mut self) {
282 self.shelves.clear();
283 self.next_shelf_y = 0;
284 self.allocation_count = 0;
285 }
286}
287
288#[cfg(test)]
293mod tests {
294 use super::*;
295
296 #[test]
297 fn test_atlas_region_basics() {
298 let r = AtlasRegion {
299 x: 10,
300 y: 20,
301 width: 30,
302 height: 40,
303 };
304 assert_eq!(r.area(), 1200);
305 assert!(r.contains(10, 20));
306 assert!(r.contains(39, 59));
307 assert!(!r.contains(40, 20));
308 }
309
310 #[test]
311 fn test_allocate_single() {
312 let mut alloc = ShelfAllocator::new(256, 256);
313 let r = alloc.allocate(64, 32);
314 assert!(r.is_some());
315 let r = r.unwrap();
316 assert_eq!(r.x, 0);
317 assert_eq!(r.y, 0);
318 assert_eq!(r.width, 64);
319 assert_eq!(r.height, 32);
320 assert_eq!(alloc.allocation_count(), 1);
321 }
322
323 #[test]
324 fn test_allocate_multiple_same_height() {
325 let mut alloc = ShelfAllocator::new(256, 256);
326 let r1 = alloc.allocate(64, 32).unwrap();
327 let r2 = alloc.allocate(64, 32).unwrap();
328 assert_eq!(r1.y, r2.y);
330 assert_eq!(r2.x, 64);
331 assert_eq!(alloc.shelf_count(), 1);
332 }
333
334 #[test]
335 fn test_allocate_different_heights() {
336 let mut alloc = ShelfAllocator::new(256, 256);
337 let r1 = alloc.allocate(64, 32).unwrap();
338 let r2 = alloc.allocate(64, 64).unwrap();
339 assert_eq!(r1.y, 0);
341 assert_eq!(r2.y, 32);
342 assert_eq!(alloc.shelf_count(), 2);
343 }
344
345 #[test]
346 fn test_allocate_full() {
347 let mut alloc = ShelfAllocator::new(64, 64);
348 let _ = alloc.allocate(64, 64).unwrap();
349 let r = alloc.allocate(1, 1);
350 assert!(r.is_none());
351 }
352
353 #[test]
354 fn test_allocate_too_wide() {
355 let mut alloc = ShelfAllocator::new(64, 64);
356 let r = alloc.allocate(128, 32);
357 assert!(r.is_none());
358 }
359
360 #[test]
361 fn test_deallocate_reuse() {
362 let mut alloc = ShelfAllocator::new(128, 128);
363 let r1 = alloc.allocate(64, 32).unwrap();
364 let _ = alloc.allocate(64, 32).unwrap();
365 alloc.deallocate(&r1);
366 let r3 = alloc.allocate(64, 32).unwrap();
368 assert_eq!(r3.x, 0);
369 assert_eq!(r3.y, 0);
370 }
371
372 #[test]
373 fn test_zero_size() {
374 let mut alloc = ShelfAllocator::new(256, 256);
375 assert!(alloc.allocate(0, 32).is_none());
376 assert!(alloc.allocate(32, 0).is_none());
377 }
378
379 #[test]
380 fn test_utilisation() {
381 let mut alloc = ShelfAllocator::new(256, 100);
382 assert_eq!(alloc.utilisation_percent(), 0);
383 let _ = alloc.allocate(128, 50).unwrap();
384 assert_eq!(alloc.utilisation_percent(), 50);
385 }
386}