Skip to main content

text_typeset/atlas/
allocator.rs

1use etagere::{AllocId, Allocation, BucketedAtlasAllocator, size2};
2
3const INITIAL_ATLAS_SIZE: i32 = 512;
4const MAX_ATLAS_SIZE: i32 = 4096;
5
6pub struct GlyphAtlas {
7    pub allocator: BucketedAtlasAllocator,
8    pub pixels: Vec<u8>,
9    pub width: u32,
10    pub height: u32,
11    pub dirty: bool,
12}
13
14impl Default for GlyphAtlas {
15    fn default() -> Self {
16        Self::new()
17    }
18}
19
20impl GlyphAtlas {
21    pub fn new() -> Self {
22        let size = INITIAL_ATLAS_SIZE;
23        let pixel_count = (size * size) as usize * 4;
24        Self {
25            allocator: BucketedAtlasAllocator::new(size2(size, size)),
26            pixels: vec![0u8; pixel_count],
27            width: size as u32,
28            height: size as u32,
29            dirty: false,
30        }
31    }
32
33    pub fn allocate(&mut self, width: u32, height: u32) -> Option<Allocation> {
34        let size = size2(width as i32, height as i32);
35        if let Some(alloc) = self.allocator.allocate(size) {
36            return Some(alloc);
37        }
38        // Try growing the atlas
39        let new_w = (self.width * 2).min(MAX_ATLAS_SIZE as u32) as i32;
40        let new_h = (self.height * 2).min(MAX_ATLAS_SIZE as u32) as i32;
41        if new_w as u32 == self.width && new_h as u32 == self.height {
42            return None; // Already at max size
43        }
44        self.grow(new_w as u32, new_h as u32);
45        self.allocator.allocate(size)
46    }
47
48    pub fn deallocate(&mut self, id: AllocId) {
49        self.allocator.deallocate(id);
50    }
51
52    fn grow(&mut self, new_width: u32, new_height: u32) {
53        let mut new_pixels = vec![0u8; (new_width * new_height) as usize * 4];
54        // Copy existing pixels row by row
55        for y in 0..self.height {
56            let src_start = (y * self.width) as usize * 4;
57            let src_end = src_start + self.width as usize * 4;
58            let dst_start = (y * new_width) as usize * 4;
59            let dst_end = dst_start + self.width as usize * 4;
60            new_pixels[dst_start..dst_end].copy_from_slice(&self.pixels[src_start..src_end]);
61        }
62        self.allocator
63            .grow(size2(new_width as i32, new_height as i32));
64        self.pixels = new_pixels;
65        self.width = new_width;
66        self.height = new_height;
67        self.dirty = true;
68    }
69
70    /// Blit RGBA pixel data into the atlas at the given position.
71    pub fn blit_rgba(&mut self, x: u32, y: u32, w: u32, h: u32, data: &[u8]) {
72        for row in 0..h {
73            let src_start = (row * w) as usize * 4;
74            let src_end = src_start + w as usize * 4;
75            let dst_start = ((y + row) * self.width + x) as usize * 4;
76            let dst_end = dst_start + w as usize * 4;
77            self.pixels[dst_start..dst_end].copy_from_slice(&data[src_start..src_end]);
78        }
79        self.dirty = true;
80    }
81
82    /// Blit a single-channel alpha mask into the atlas as white RGBA.
83    pub fn blit_mask(&mut self, x: u32, y: u32, w: u32, h: u32, data: &[u8]) {
84        for row in 0..h {
85            for col in 0..w {
86                let alpha = data[(row * w + col) as usize];
87                let dst = ((y + row) * self.width + x + col) as usize * 4;
88                self.pixels[dst] = 255;
89                self.pixels[dst + 1] = 255;
90                self.pixels[dst + 2] = 255;
91                self.pixels[dst + 3] = alpha;
92            }
93        }
94        self.dirty = true;
95    }
96}