est_render/font/
mod.rs

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