1use std::{
2 collections::HashMap,
3 hash::{Hash, Hasher},
4 io::{Read, Write},
5};
6
7use byteorder_lite::{LittleEndian, ReadBytesExt, WriteBytesExt};
8use flate2::bufread::ZlibDecoder;
9
10use crate::{
11 gpu::{
12 GPU,
13 GPUInner,
14 texture::{Texture, TextureBuilder, TextureError, TextureFormat, TextureUsage},
15 },
16 math::{Point2, Vector2},
17 utils::ArcRef,
18};
19
20
21pub fn new() -> FontManager {
25 FontManager::new()
26}
27
28mod system;
29
30#[derive(Clone, Copy, Debug)]
31pub struct FontStyle(u8);
32
33bitflags::bitflags! {
34 impl FontStyle: u8 {
35 const BOLD = 0b00000001;
37 const ITALIC = 0b00000010;
39 }
40}
41
42#[derive(Clone, Debug)]
43pub struct FontInfo {
44 pub name: String,
45 pub path: std::path::PathBuf,
46 pub style: FontStyle,
47}
48
49#[derive(Clone, Debug)]
50pub struct FontInner {
51 pub info: FontInfo,
52 pub glyphs: HashMap<u32, Glyph>,
53 pub texture_buffer: Vec<u8>,
54 pub texture_width: u32,
55 pub texture_height: u32,
56 pub ascender: f32,
57 pub descender: f32,
58 pub line_height: f32,
59 pub space_width: f32,
60}
61
62#[derive(Clone, Debug)]
63pub struct Font {
64 pub(crate) inner: ArcRef<FontInner>,
65}
66
67const FONT_CACHE_MAGIC: [u8; 5] = *b"eFONT";
68const MAX_ATLAS_SIZE: usize = 2048; fn power_of_two(n: usize) -> usize {
71 let mut power = 1;
72 while power < n {
73 power *= 2;
74 }
75 power
76}
77
78pub enum FontBakeFormat {
79 GrayScale,
80 Rgba,
81}
82
83pub enum FontError {
84 InvalidFontData(String),
85 GlyphNotFound(u32),
86 IoError(std::io::Error),
87}
88
89impl Font {
90 pub(crate) fn new(info: FontInfo, size: f32, glyph_range: &[(u32, u32)]) -> Self {
91 let data = std::fs::read(&info.path).expect("Failed to read font file");
92 let font = fontdue::Font::from_bytes(data, fontdue::FontSettings::default())
93 .expect("Failed to parse font file");
94
95 let line_metrics = font.horizontal_line_metrics(size);
96 let pixel_gap = 2usize; #[cfg(any(debug_assertions, feature = "enable-release-validation"))]
99 if line_metrics.is_none() {
100 panic!(
101 "Failed to get line metrics for font: {}",
102 info.path.display()
103 );
104 }
105
106 let line_metrics = line_metrics.unwrap();
107
108 let ascender = line_metrics.ascent;
109 let descender = line_metrics.descent;
110 let line_height = line_metrics.ascent - line_metrics.descent + line_metrics.line_gap;
111 let space_metrics = font.metrics(' ', size);
112
113 let tex_width = {
116 let mut total_area = 0;
117
118 for &(start, end) in glyph_range {
119 for codepoint in start..=end {
120 let codepoint_char = std::char::from_u32(codepoint).unwrap_or_default();
121 let metrics = font.metrics(codepoint_char, size);
122
123 total_area += ((metrics.width + pixel_gap) * (metrics.height + pixel_gap)) as usize;
124 }
125 }
126
127 power_of_two((total_area as f32).sqrt().ceil() as usize) as i32
128 };
129
130 if tex_width > MAX_ATLAS_SIZE as i32 {
131 panic!(
132 "Calculated texture area {} exceeds maximum atlas size {}",
133 tex_width, MAX_ATLAS_SIZE
134 );
135 }
136
137 let rect_config = rect_packer::Config {
138 width: tex_width,
139 height: tex_width,
140 border_padding: 0,
141 rectangle_padding: pixel_gap as i32,
142 };
143
144 let mut packer = rect_packer::Packer::new(rect_config);
145 let mut raw_glyphs = Vec::new();
146 let mut max_size = Point2::new(0, 0);
147
148 for &(start, end) in glyph_range {
149 for codepoint in start..=end {
150 let codepoint_char = std::char::from_u32(codepoint).unwrap_or_default();
151 let (metrics, bitmap) = font.rasterize(codepoint_char, size);
152 if bitmap.is_empty() {
153 continue;
154 }
155
156 if let Some(rect) = packer.pack(metrics.width as i32, metrics.height as i32, false) {
157 raw_glyphs.push(
158 (rect, codepoint, metrics, bitmap)
159 );
160
161 max_size.x = max_size.x.max(rect.x + rect.width);
162 max_size.y = max_size.y.max(rect.y + rect.height);
163 } else {
164 #[cfg(any(debug_assertions, feature = "enable-release-validation"))]
165 panic!(
166 "Failed to pack glyph: {} ({}x{}) with atlas size {}x{}",
167 codepoint_char,
168 metrics.width,
169 metrics.height,
170 tex_width,
171 tex_width
172 );
173 }
174 }
175 }
176
177 let mut texture_buffer = vec![0; (max_size.x * max_size.y) as usize];
178 let mut glyphs = HashMap::new();
179
180 for (rect, codepoint, metrics, bitmap) in raw_glyphs {
181 let advance = metrics.advance_width as f32;
182 let glyph_width = metrics.width as usize;
183 let glyph_height = metrics.height as usize;
184
185 for j in 0..glyph_height {
186 for i in 0..glyph_width {
187 let src_index = j * glyph_width + i;
188 let dest_x = rect.x as usize + i;
189 let dest_y = rect.y as usize + j;
190 let dest_index = dest_y * max_size.x as usize + dest_x;
191
192 if dest_index < texture_buffer.len() && src_index < bitmap.len() {
193 texture_buffer[dest_index] = bitmap[src_index];
194 }
195 }
196 }
197
198 let start_offset = Vector2::new(rect.x as f32, rect.y as f32);
199 let end_offset = Vector2::new(
200 rect.x + glyph_width as i32,
201 rect.y + glyph_height as i32,
202 );
203
204 let glyph = Glyph {
205 codepoint,
206 advance,
207 atlas_start_offset: start_offset,
208 atlas_end_offset: end_offset,
209
210 width: glyph_width as f32,
211 height: glyph_height as f32,
212 bearing_x: metrics.xmin as f32,
213 bearing_y: metrics.ymin as f32,
214 advance_x: metrics.advance_width as f32,
215 advance_y: metrics.advance_height as f32,
216 ascender: -metrics.bounds.ymin.max(0.0) as f32,
217 descender: (metrics.bounds.ymin + metrics.bounds.height) as f32,
218 };
219
220 glyphs.insert(codepoint, glyph);
221 }
222
223 let inner = FontInner {
224 info,
225 glyphs,
226 texture_buffer,
227 texture_width: max_size.x as u32,
228 texture_height: max_size.y as u32,
229 ascender,
230 descender,
231 line_height,
232 space_width: space_metrics.advance_width as f32,
233 };
234
235 let inner = ArcRef::new(inner);
236
237 Font {
238 inner,
239 }
240 }
241
242 pub fn line_height(&self) -> f32 {
243 self.inner.borrow().line_height
244 }
245
246 pub fn ascender(&self) -> f32 {
247 self.inner.borrow().ascender
248 }
249
250 pub fn descender(&self) -> f32 {
251 self.inner.borrow().descender
252 }
253
254 pub fn space_width(&self) -> f32 {
255 self.inner.borrow().space_width
256 }
257
258 pub fn texture_size(&self) -> Point2 {
259 let inner = self.inner.borrow();
260 Point2::new(inner.texture_width as i32, inner.texture_height as i32)
261 }
262
263 pub fn calculate_text_size(&self, text: &str) -> Vector2 {
264 let inner = self.inner.borrow();
265
266 let mut width = 0.0f32;
267 let mut height = inner.line_height;
268
269 let mut pen_x = 0.0;
270
271 for c in text.chars() {
272 let codepoint = c as u32;
273 if codepoint == '\n' as u32 {
274 width = width.max(pen_x);
275 pen_x = 0.0;
276 height += inner.line_height;
277 continue;
278 }
279
280 if codepoint == ' ' as u32 {
281 pen_x += inner.space_width;
282 continue;
283 }
284
285 if let Some(glyph) = inner.glyphs.get(&codepoint) {
286 pen_x += glyph.advance_x;
287 }
288 }
289
290 width = width.max(pen_x);
291
292 Vector2::new(width, height)
293 }
294
295 pub fn create_baked_text_raw(
299 &self,
300 text: &str,
301 format: FontBakeFormat,
302 ) -> Result<(Vec<u8>, u32, u32), String> {
303 let inner = self.inner.borrow();
304
305 let mut pen = Vector2::new(0.0, 0.0);
306
307 let mut min_x = f32::MAX;
309 let mut min_y = f32::MAX;
310 let mut max_x = f32::MIN;
311 let mut max_y = f32::MIN;
312
313 for c in text.chars() {
316 let codepoint = c as u32;
317 if codepoint == '\n' as u32 {
318 pen.x = 0.0;
319 pen.y += inner.line_height as f32;
320 continue;
321 }
322
323 if codepoint == ' ' as u32 {
324 pen.x += inner.space_width;
325 continue;
326 }
327
328 if let Some(glyph) = inner.glyphs.get(&codepoint) {
329 let x0 = pen.x + glyph.bearing_x;
330 let y0 = pen.y + inner.ascender - (glyph.height + glyph.bearing_y);
331 let x1 = x0 + glyph.width;
332 let y1 = y0 + glyph.height;
333
334 min_x = min_x.min(x0);
335 min_y = min_y.min(y0);
336 max_x = max_x.max(x1);
337 max_y = max_y.max(y1);
338
339 pen.x += glyph.advance_x;
340 }
341 }
342
343 if min_x == f32::MAX || min_y == f32::MAX {
345 return Err("No glyphs found".to_string());
346 }
347
348 let width = (max_x - min_x).ceil().max(1.0) as usize;
349 let height = (max_y - min_y).ceil().max(1.0) as usize;
350 let mut buffer = vec![0; width * height];
351
352 let mut pen2 = Vector2::new(0.0, 0.0);
353
354 for c in text.chars() {
355 let codepoint = c as u32;
356 if codepoint == '\n' as u32 {
357 pen2.x = 0.0;
358 pen2.y += inner.line_height as f32;
359 continue;
360 }
361
362 if codepoint == ' ' as u32 {
363 pen2.x += inner.space_width;
364 continue;
365 }
366
367 if let Some(glyph) = inner.glyphs.get(&codepoint) {
368 let x0 = pen2.x + glyph.bearing_x - min_x;
369 let y0 = pen2.y + inner.ascender - (glyph.height + glyph.bearing_y) - min_y;
370
371 let atlas_offset_x = glyph.atlas_start_offset.x as usize;
372 let atlas_offset_y = glyph.atlas_start_offset.y as usize;
373 let atlas_width = inner.texture_width as usize;
374 let atlas_height = inner.texture_height as usize;
375
376 for y in 0..glyph.height as usize {
377 let src_start = (atlas_offset_y + y) * atlas_width + atlas_offset_x;
378 let dest_start = (y0 as usize + y) * width + x0 as usize;
379
380 for x in 0..glyph.width as usize {
381 let src_index = src_start + x;
382 let dest_index = dest_start + x;
383
384 if src_index < atlas_width * atlas_height && dest_index < buffer.len() {
385 buffer[dest_index] = inner.texture_buffer[src_index];
386 }
387 }
388 }
389
390 pen2.x += glyph.advance_x;
391 }
392 }
393
394 match format {
395 FontBakeFormat::GrayScale => Ok((buffer, width as u32, height as u32)),
396 FontBakeFormat::Rgba => {
397 let mut rgba_buffer = Vec::with_capacity(width * height * 4);
398 for byte in buffer.iter() {
399 let is_transparent = *byte == 0;
400
401 rgba_buffer.push(*byte);
402 rgba_buffer.push(*byte);
403 rgba_buffer.push(*byte);
404 rgba_buffer.push(if is_transparent { 0 } else { 255 });
405 }
406
407 Ok((rgba_buffer, width as u32, height as u32))
408 }
409 }
410 }
411
412 pub(crate) fn new_cached(path: &str) -> Result<Self, std::io::Error> {
413 let data = std::fs::read(path)?;
414 let mut reader = std::io::Cursor::new(data);
415
416 let mut magic = [0; 5];
417 reader.read_exact(&mut magic)?;
418 if magic != FONT_CACHE_MAGIC {
419 return Err(std::io::Error::new(
420 std::io::ErrorKind::InvalidData,
421 "Invalid font cache file",
422 ));
423 }
424
425 let compressed_size = reader.read_u32::<LittleEndian>()?;
426 let uncompressed_size = reader.read_u32::<LittleEndian>()?;
427
428 let mut compressed_data = vec![0; compressed_size as usize];
429 reader.read_exact(&mut compressed_data)?;
430
431 let mut decoder = ZlibDecoder::new(&compressed_data[..]);
432 let mut decompressed_data = Vec::with_capacity(uncompressed_size as usize);
433 decoder.read_to_end(&mut decompressed_data)?;
434
435 let mut reader = std::io::Cursor::new(decompressed_data);
436
437 let font_family_name_len = reader.read_u32::<LittleEndian>()?;
438 let mut font_family_name = vec![0; font_family_name_len as usize];
439 reader.read_exact(&mut font_family_name)?;
440 let font_family_name = String::from_utf8(font_family_name).map_err(|_| {
441 std::io::Error::new(
442 std::io::ErrorKind::InvalidData,
443 "Invalid UTF-8 in font family name",
444 )
445 })?;
446 let font_style = reader.read_u8()?;
447 let font_style = FontStyle::from_bits(font_style).ok_or_else(|| {
448 std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid font style")
449 })?;
450
451 let info = FontInfo {
452 name: font_family_name,
453 path: std::path::PathBuf::from(path),
454 style: font_style,
455 };
456
457 let num_glyphs = reader.read_u32::<LittleEndian>()?;
458 let mut glyphs = HashMap::new();
459 for _ in 0..num_glyphs {
460 let codepoint = reader.read_u32::<LittleEndian>()?;
461 let advance = reader.read_f32::<LittleEndian>()?;
462 let atlas_start_offset = Vector2::new(
463 reader.read_f32::<LittleEndian>()?,
464 reader.read_f32::<LittleEndian>()?,
465 );
466 let atlas_end_offset = Vector2::new(
467 reader.read_f32::<LittleEndian>()?,
468 reader.read_f32::<LittleEndian>()?,
469 );
470 let width = reader.read_f32::<LittleEndian>()?;
471 let height = reader.read_f32::<LittleEndian>()?;
472 let bearing_x = reader.read_f32::<LittleEndian>()?;
473 let bearing_y = reader.read_f32::<LittleEndian>()?;
474 let advance_x = reader.read_f32::<LittleEndian>()?;
475 let advance_y = reader.read_f32::<LittleEndian>()?;
476 let ascender = reader.read_f32::<LittleEndian>()?;
477 let descender = reader.read_f32::<LittleEndian>()?;
478
479 let glyph = Glyph {
480 codepoint,
481 advance,
482 atlas_start_offset,
483 atlas_end_offset,
484 width,
485 height,
486 bearing_x,
487 bearing_y,
488 advance_x,
489 advance_y,
490 ascender,
491 descender,
492 };
493
494 glyphs.insert(codepoint, glyph);
495 }
496
497 let texture_buffer_width = reader.read_u32::<LittleEndian>()?;
498 let texture_buffer_height = reader.read_u32::<LittleEndian>()?;
499
500 if texture_buffer_width > MAX_ATLAS_SIZE as u32
501 || texture_buffer_height > MAX_ATLAS_SIZE as u32
502 {
503 return Err(std::io::Error::new(
504 std::io::ErrorKind::InvalidData,
505 "Invalid texture buffer size",
506 ));
507 }
508
509 let mut texture_buffer = vec![0; (texture_buffer_width * texture_buffer_height) as usize];
510 reader.read_exact(&mut texture_buffer)?;
511
512 let ascender = reader.read_f32::<LittleEndian>()?;
513 let descender = reader.read_f32::<LittleEndian>()?;
514 let line_height = reader.read_f32::<LittleEndian>()?;
515 let space_width = reader.read_f32::<LittleEndian>()?;
516
517 let inner = FontInner {
518 info,
519 glyphs,
520 texture_buffer,
521 texture_width: texture_buffer_width,
522 texture_height: texture_buffer_height,
523 ascender,
524 descender,
525 line_height,
526 space_width,
527 };
528
529 let inner = ArcRef::new(inner);
530
531 Ok(Font {
532 inner: ArcRef::clone(&inner),
533 })
534 }
535
536 pub fn save_font_cache(&self, path: &str) -> Result<(), std::io::Error> {
540 let mut writer = std::fs::File::create(path)?;
541 writer.write_all(&FONT_CACHE_MAGIC)?;
542
543 let inner = self.inner.borrow();
544
545 let mut writer2 = std::io::Cursor::new(Vec::<u8>::new());
546
547 writer2.write_u32::<LittleEndian>(inner.info.name.len() as u32)?;
548 writer2.write_all(inner.info.name.as_bytes())?;
549 writer2.write_u8(inner.info.style.bits())?;
550
551 writer2.write_u32::<LittleEndian>(inner.glyphs.len() as u32)?;
552 for (_index, glyph) in inner.glyphs.iter() {
553 writer2.write_u32::<LittleEndian>(glyph.codepoint)?;
554 writer2.write_f32::<LittleEndian>(glyph.advance)?;
555 writer2.write_f32::<LittleEndian>(glyph.atlas_start_offset.x)?;
556 writer2.write_f32::<LittleEndian>(glyph.atlas_start_offset.y)?;
557 writer2.write_f32::<LittleEndian>(glyph.atlas_end_offset.x)?;
558 writer2.write_f32::<LittleEndian>(glyph.atlas_end_offset.y)?;
559 writer2.write_f32::<LittleEndian>(glyph.width)?;
560 writer2.write_f32::<LittleEndian>(glyph.height)?;
561 writer2.write_f32::<LittleEndian>(glyph.bearing_x)?;
562 writer2.write_f32::<LittleEndian>(glyph.bearing_y)?;
563 writer2.write_f32::<LittleEndian>(glyph.advance_x)?;
564 writer2.write_f32::<LittleEndian>(glyph.advance_y)?;
565 writer2.write_f32::<LittleEndian>(glyph.ascender)?;
566 writer2.write_f32::<LittleEndian>(glyph.descender)?;
567 }
568
569 writer2.write_u32::<LittleEndian>(inner.texture_width)?;
570 writer2.write_u32::<LittleEndian>(inner.texture_height)?;
571 writer2.write_all(&inner.texture_buffer)?;
572
573 writer2.write_f32::<LittleEndian>(inner.ascender)?;
574 writer2.write_f32::<LittleEndian>(inner.descender)?;
575 writer2.write_f32::<LittleEndian>(inner.line_height)?;
576 writer2.write_f32::<LittleEndian>(inner.space_width)?;
577
578 let uncompressed_data: Vec<u8> = writer2.into_inner();
579 let uncompressed_size = uncompressed_data.len() as u32;
580
581 let mut compressed_data =
582 flate2::write::ZlibEncoder::new(Vec::new(), flate2::Compression::default());
583 compressed_data.write_all(&uncompressed_data)?;
584
585 let compressed_data = compressed_data.finish()?;
586
587 writer.write_u32::<LittleEndian>(compressed_data.len() as u32)?;
588 writer.write_u32::<LittleEndian>(uncompressed_size as u32)?;
589 writer.write_all(&compressed_data)?;
590
591 Ok(())
592 }
593
594 pub fn get_image_data(&self) -> (Vec<u8>, u32, u32) {
596 let inner = self.inner.borrow();
597 (
598 inner.texture_buffer.clone(),
599 inner.texture_width,
600 inner.texture_height,
601 )
602 }
603
604 pub fn get_glyph(&self, codepoint: u32) -> Result<Glyph, FontError> {
606 let inner = self.inner.borrow();
607
608 inner
609 .glyphs
610 .get(&codepoint)
611 .cloned()
612 .ok_or(FontError::GlyphNotFound(codepoint))
613 }
614
615 pub fn create_baked_text(
619 &self,
620 gpu: &mut GPU,
621 text: &str,
622 ) -> Result<Texture, TextureError> {
623 let (image_data, width, height) = self.create_baked_text_raw(text, FontBakeFormat::Rgba)
624 .map_err(|_| TextureError::InvalidTextureData)?;
625
626 let format = {
627 let gpu_inner = gpu.inner.borrow();
628
629 if gpu_inner.is_srgb() {
630 TextureFormat::Bgra8UnormSrgb
631 } else {
632 TextureFormat::Bgra8Unorm
633 }
634 };
635
636 let texture = gpu
637 .create_texture()
638 .set_raw_image(&image_data, Point2::new(width as i32, height as i32), format)
639 .set_usage(TextureUsage::Sampler)
640 .build()?;
641
642 Ok(texture)
643 }
644
645 pub fn create_texture(&self, gpu: &mut GPU) -> Result<Texture, TextureError> {
647 let gpu_inner = &gpu.inner;
648
649 self.create_texture_inner(&gpu_inner)
650 }
651
652 pub(crate) fn create_texture_inner(
653 &self,
654 gpu: &ArcRef<GPUInner>,
655 ) -> Result<Texture, TextureError> {
656 let (image_data, width, height) = self.get_image_data();
657
658 let format = {
659 let gpu_inner = gpu.borrow();
660
661 if gpu_inner.is_srgb() {
662 TextureFormat::Bgra8UnormSrgb
663 } else {
664 TextureFormat::Bgra8Unorm
665 }
666 };
667
668 let image_data = {
669 let mut data = Vec::with_capacity(image_data.len() * 4);
670 for &pixel in &image_data {
671 let is_transparent_pixel = pixel == 0;
672 data.push(pixel);
673 data.push(pixel);
674 data.push(pixel);
675 data.push(if is_transparent_pixel { 0 } else { 255 });
676 }
677
678 data
679 };
680
681 let texture = TextureBuilder::new(ArcRef::clone(gpu))
682 .set_raw_image(
683 &image_data,
684 Point2::new(width as i32, height as i32),
685 format,
686 )
687 .set_usage(TextureUsage::Sampler)
688 .build()?;
689
690 Ok(texture)
691 }
692}
693
694#[derive(Clone, Debug)]
695pub struct Glyph {
696 pub codepoint: u32,
697 pub advance: f32,
698 pub atlas_start_offset: Vector2,
699 pub atlas_end_offset: Vector2,
700
701 pub width: f32,
703 pub height: f32,
704 pub bearing_x: f32,
705 pub bearing_y: f32,
706 pub advance_x: f32,
707 pub advance_y: f32,
708 pub ascender: f32,
709 pub descender: f32,
710}
711
712impl Eq for Glyph {}
713
714impl PartialEq for Glyph {
715 fn eq(&self, other: &Self) -> bool {
716 self.codepoint == other.codepoint
717 }
718}
719
720#[derive(Clone, Debug)]
721pub struct FontManager {
722 fonts: Vec<FontInfo>,
723 cached_font: HashMap<u64, Font>,
724}
725
726const DEFAULT_GLYPH_RANGE: [(u32, u32); 1] = [(0x20, 0x7E)]; impl FontManager {
729 pub fn new() -> Self {
734 let fonts = system::search_system_font();
735 FontManager {
736 fonts,
737 cached_font: HashMap::new(),
738 }
739 }
740
741 pub fn load_font(
746 &mut self,
747 font_name: &str,
748 glyph_range: Option<&[(u32, u32)]>,
749 size: f32,
750 ) -> Option<Font> {
751 let glyph_range = glyph_range.unwrap_or(&DEFAULT_GLYPH_RANGE);
752
753 let hashed_name = {
754 let mut hasher = std::collections::hash_map::DefaultHasher::new();
755 font_name.hash(&mut hasher);
756 for (start, end) in glyph_range {
757 start.hash(&mut hasher);
758 end.hash(&mut hasher);
759 }
760 size.to_bits().hash(&mut hasher);
761 hasher.finish()
762 };
763
764 if self.cached_font.contains_key(&hashed_name) {
765 return self.cached_font.get(&hashed_name).cloned();
766 }
767
768 if std::path::Path::new(font_name).exists() {
769 let path = std::path::Path::new(font_name);
770
771 let font_info = system::get_font_info(path);
772 if font_info.is_none() {
773 return None;
774 }
775
776 let font_info = font_info.unwrap();
777 let font = Font::new(font_info, size, glyph_range);
778
779 self.cached_font.insert(hashed_name, font.clone());
780
781 return Some(font);
782 } else {
783 for font in &self.fonts {
784 if font.name == font_name {
785 let font = Font::new(font.clone(), size, glyph_range);
786 self.cached_font.insert(hashed_name, font.clone());
787 return Some(font);
788 }
789 }
790 }
791
792 None
793 }
794
795 pub fn load_font_cached(&mut self, path: &str) -> Option<Font> {
800 let hash_id = {
801 let mut hasher = std::collections::hash_map::DefaultHasher::new();
802 path.hash(&mut hasher);
803 hasher.finish()
804 };
805
806 if self.cached_font.contains_key(&hash_id) {
807 return self.cached_font.get(&hash_id).cloned();
808 }
809
810 match Font::new_cached(path) {
811 Ok(font) => {
812 self.cached_font.insert(hash_id, font.clone());
813 Some(font)
814 }
815 Err(_) => None,
816 }
817 }
818}