ass_renderer/utils/
caches.rs1use ahash::AHashMap;
4
5#[cfg(feature = "nostd")]
6use alloc::{string::String, vec, vec::Vec};
7#[cfg(not(feature = "nostd"))]
8use std::{string::String, vec::Vec};
9
10pub struct GlyphCache {
12 cache: AHashMap<GlyphKey, CachedGlyph>,
13 max_entries: usize,
14}
15
16#[derive(Debug, Clone, Hash, PartialEq, Eq)]
18pub struct GlyphKey {
19 pub font_family: String,
21 pub glyph_id: u16,
23 pub size: u32,
25 pub style_flags: u32,
27}
28
29pub struct CachedGlyph {
31 pub bitmap: Vec<u8>,
33 pub width: u32,
35 pub height: u32,
37 pub advance: f32,
39 pub bearing_x: f32,
41 pub bearing_y: f32,
43}
44
45impl GlyphCache {
46 pub fn new(max_entries: usize) -> Self {
48 Self {
49 cache: AHashMap::new(),
50 max_entries,
51 }
52 }
53
54 pub fn get(&self, key: &GlyphKey) -> Option<&CachedGlyph> {
56 self.cache.get(key)
57 }
58
59 pub fn insert(&mut self, key: GlyphKey, glyph: CachedGlyph) {
61 if self.cache.len() >= self.max_entries {
62 if let Some(first_key) = self.cache.keys().next().cloned() {
64 self.cache.remove(&first_key);
65 }
66 }
67 self.cache.insert(key, glyph);
68 }
69
70 pub fn clear(&mut self) {
72 self.cache.clear();
73 }
74
75 pub fn len(&self) -> usize {
77 self.cache.len()
78 }
79
80 pub fn is_empty(&self) -> bool {
82 self.cache.is_empty()
83 }
84}
85
86pub struct TextureAtlas {
88 width: u32,
89 height: u32,
90 data: Vec<u8>,
91 allocations: Vec<AtlasRegion>,
92 next_x: u32,
93 next_y: u32,
94 row_height: u32,
95}
96
97#[derive(Debug, Clone)]
99pub struct AtlasRegion {
100 pub id: u32,
102 pub x: u32,
104 pub y: u32,
106 pub width: u32,
108 pub height: u32,
110}
111
112impl TextureAtlas {
113 pub fn new(width: u32, height: u32) -> Self {
115 Self {
116 width,
117 height,
118 data: vec![0; (width * height * 4) as usize],
119 allocations: Vec::new(),
120 next_x: 0,
121 next_y: 0,
122 row_height: 0,
123 }
124 }
125
126 pub fn allocate(&mut self, width: u32, height: u32) -> Option<AtlasRegion> {
128 if self.next_x + width > self.width {
130 self.next_x = 0;
132 self.next_y += self.row_height;
133 self.row_height = 0;
134 }
135
136 if self.next_y + height > self.height {
137 return None;
139 }
140
141 let region = AtlasRegion {
142 id: self.allocations.len() as u32,
143 x: self.next_x,
144 y: self.next_y,
145 width,
146 height,
147 };
148
149 self.next_x += width;
150 self.row_height = self.row_height.max(height);
151 self.allocations.push(region.clone());
152
153 Some(region)
154 }
155
156 pub fn write_region(&mut self, region: &AtlasRegion, data: &[u8]) {
158 let stride = self.width * 4;
159 for y in 0..region.height {
160 let src_offset = (y * region.width * 4) as usize;
161 let dst_offset = ((region.y + y) * stride + region.x * 4) as usize;
162 let src_end = src_offset + (region.width * 4) as usize;
163 let dst_end = dst_offset + (region.width * 4) as usize;
164
165 if src_end <= data.len() && dst_end <= self.data.len() {
166 self.data[dst_offset..dst_end].copy_from_slice(&data[src_offset..src_end]);
167 }
168 }
169 }
170
171 pub fn data(&self) -> &[u8] {
173 &self.data
174 }
175
176 pub fn clear(&mut self) {
178 self.data.fill(0);
179 self.allocations.clear();
180 self.next_x = 0;
181 self.next_y = 0;
182 self.row_height = 0;
183 }
184}