1use std::collections::HashMap;
14
15const MAX_RASTER_CACHE: usize = 2048;
17
18pub const MAX_CACHED_SIZE: f32 = 48.0;
21
22#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
24pub struct RasterGlyphKey {
25 pub font_id: usize,
27 pub glyph_id: u16,
29 pub size_q: u32,
31}
32
33#[derive(Debug, Clone)]
35pub struct RasterGlyph {
36 pub width: u32,
38 pub height: u32,
40 pub bearing_x: i32,
42 pub bearing_y: i32,
44 pub alpha: Vec<u8>,
46}
47
48#[derive(Debug)]
53pub struct RasterizedGlyphCache {
54 cache: HashMap<RasterGlyphKey, Option<RasterGlyph>>,
55}
56
57impl Default for RasterizedGlyphCache {
58 fn default() -> Self {
59 Self::new()
60 }
61}
62
63impl RasterizedGlyphCache {
64 pub fn new() -> Self {
66 Self {
67 cache: HashMap::new(),
68 }
69 }
70
71 pub fn quantize_size(size: f32) -> u32 {
73 (size * 2.0).round() as u32
74 }
75
76 pub fn get(&self, key: &RasterGlyphKey) -> Option<&Option<RasterGlyph>> {
81 self.cache.get(key)
82 }
83
84 pub fn insert(&mut self, key: RasterGlyphKey, glyph: Option<RasterGlyph>) {
88 if self.cache.len() >= MAX_RASTER_CACHE {
89 self.evict();
90 }
91 self.cache.insert(key, glyph);
92 }
93
94 pub fn len(&self) -> usize {
96 self.cache.len()
97 }
98
99 pub fn is_empty(&self) -> bool {
101 self.cache.is_empty()
102 }
103
104 fn evict(&mut self) {
106 let target = self.cache.len() / 4;
107 let keys: Vec<RasterGlyphKey> = self.cache.keys().take(target).copied().collect();
108 for key in keys {
109 self.cache.remove(&key);
110 }
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn test_quantize_size_rounds() {
120 assert_eq!(RasterizedGlyphCache::quantize_size(12.0), 24);
121 assert_eq!(RasterizedGlyphCache::quantize_size(12.5), 25);
122 assert_eq!(RasterizedGlyphCache::quantize_size(12.25), 25); assert_eq!(RasterizedGlyphCache::quantize_size(12.74), 25);
124 assert_eq!(RasterizedGlyphCache::quantize_size(0.0), 0);
125 }
126
127 #[test]
128 fn test_cache_miss_returns_none() {
129 let cache = RasterizedGlyphCache::new();
130 let key = RasterGlyphKey {
131 font_id: 1,
132 glyph_id: 42,
133 size_q: 24,
134 };
135 assert!(cache.get(&key).is_none());
136 }
137
138 #[test]
139 fn test_cache_insert_and_hit() {
140 let mut cache = RasterizedGlyphCache::new();
141 let key = RasterGlyphKey {
142 font_id: 1,
143 glyph_id: 42,
144 size_q: 24,
145 };
146 let glyph = RasterGlyph {
147 width: 10,
148 height: 12,
149 bearing_x: 1,
150 bearing_y: 11,
151 alpha: vec![128; 120],
152 };
153 cache.insert(key, Some(glyph));
154 assert_eq!(cache.len(), 1);
155 let result = cache.get(&key);
156 assert!(result.is_some());
157 let glyph = result.unwrap().as_ref().unwrap();
158 assert_eq!(glyph.width, 10);
159 assert_eq!(glyph.height, 12);
160 }
161
162 #[test]
163 fn test_negative_cache_entry() {
164 let mut cache = RasterizedGlyphCache::new();
165 let key = RasterGlyphKey {
166 font_id: 1,
167 glyph_id: 999,
168 size_q: 24,
169 };
170 cache.insert(key, None);
171 assert_eq!(cache.len(), 1);
172 let result = cache.get(&key);
173 assert!(result.is_some());
174 assert!(result.unwrap().is_none());
175 }
176
177 #[test]
178 fn test_cache_eviction() {
179 let mut cache = RasterizedGlyphCache::new();
180 for i in 0..MAX_RASTER_CACHE + 10 {
182 let key = RasterGlyphKey {
183 font_id: 1,
184 glyph_id: i as u16,
185 size_q: 24,
186 };
187 cache.insert(key, None);
188 }
189 assert!(cache.len() <= MAX_RASTER_CACHE);
191 }
192
193 #[test]
194 fn test_cache_empty_and_len() {
195 let mut cache = RasterizedGlyphCache::new();
196 assert!(cache.is_empty());
197 assert_eq!(cache.len(), 0);
198 let key = RasterGlyphKey {
199 font_id: 1,
200 glyph_id: 1,
201 size_q: 24,
202 };
203 cache.insert(key, None);
204 assert!(!cache.is_empty());
205 assert_eq!(cache.len(), 1);
206 }
207
208 #[test]
209 fn test_different_sizes_different_keys() {
210 let mut cache = RasterizedGlyphCache::new();
211 let key1 = RasterGlyphKey {
212 font_id: 1,
213 glyph_id: 42,
214 size_q: 24, };
216 let key2 = RasterGlyphKey {
217 font_id: 1,
218 glyph_id: 42,
219 size_q: 48, };
221 cache.insert(
222 key1,
223 Some(RasterGlyph {
224 width: 10,
225 height: 12,
226 bearing_x: 1,
227 bearing_y: 11,
228 alpha: vec![],
229 }),
230 );
231 cache.insert(
232 key2,
233 Some(RasterGlyph {
234 width: 20,
235 height: 24,
236 bearing_x: 2,
237 bearing_y: 22,
238 alpha: vec![],
239 }),
240 );
241 assert_eq!(cache.len(), 2);
242 assert_eq!(cache.get(&key1).unwrap().as_ref().unwrap().width, 10);
243 assert_eq!(cache.get(&key2).unwrap().as_ref().unwrap().width, 20);
244 }
245
246 #[test]
247 fn test_raster_glyph_key_eq_and_hash() {
248 let key1 = RasterGlyphKey {
249 font_id: 1,
250 glyph_id: 42,
251 size_q: 24,
252 };
253 let key2 = RasterGlyphKey {
254 font_id: 1,
255 glyph_id: 42,
256 size_q: 24,
257 };
258 assert_eq!(key1, key2);
259
260 let mut set = std::collections::HashSet::new();
261 set.insert(key1);
262 assert!(set.contains(&key2));
263 }
264
265 #[test]
266 fn test_max_cached_size_constant() {
267 assert_eq!(MAX_CACHED_SIZE, 48.0);
268 }
269}