1
use std::fmt::Debug;
2
use std::sync::atomic::{AtomicU64, Ordering};
3
use std::sync::Arc;
4

            
5
use easygpu::prelude::*;
6
use figures::Figure;
7
use lazy_static::lazy_static;
8
use rusttype::{gpu_cache, Scale};
9
use ttf_parser::name_id;
10

            
11
use crate::math::Pixels;
12

            
13
lazy_static! {
14
    static ref GLOBAL_ID_CELL: AtomicU64 = AtomicU64::new(0);
15
}
16

            
17
/// Embeds a font into your executable.
18
#[macro_export]
19
macro_rules! include_font {
20
    ($path:expr) => {{
21
        let bytes = std::include_bytes!($path);
22
        Font::try_from_bytes(bytes as &[u8]).expect("Error loading bundled font")
23
    }};
24
}
25

            
26
/// Font provides TrueType Font rendering
27
#[derive(Clone, Debug)]
28
pub struct Font {
29
    pub(crate) handle: Arc<FontData>,
30
}
31

            
32
impl Font {
33
    /// Try to load a font from `bytes`.
34
    #[must_use]
35
    pub fn try_from_bytes(bytes: &'static [u8]) -> Option<Self> {
36
        let font = rusttype::Font::try_from_bytes(bytes)?;
37
        let id = GLOBAL_ID_CELL.fetch_add(1, Ordering::SeqCst);
38
        Some(Self {
39
            handle: Arc::new(FontData { id, font }),
40
        })
41
    }
42

            
43
    /// Returns the unique ID of the font. This ID depends on load order, and is
44
    /// not from the font data.
45
    #[must_use]
46
    pub fn id(&self) -> u64 {
47
        self.handle.id
48
    }
49

            
50
    /// Measures the vertical metrics for a given size.
51
    #[must_use]
52
    pub fn metrics(&self, size: Figure<f32, Pixels>) -> rusttype::VMetrics {
53
        self.handle
54
            .font
55
            .v_metrics(rusttype::Scale::uniform(size.get()))
56
    }
57

            
58
    /// Returns the name of the font's family, if available.
59
    #[must_use]
60
    pub fn family(&self) -> Option<String> {
61
        match &self.handle.font {
62
            rusttype::Font::Ref(f) => f
63
                .names()
64
                .get(name_id::FAMILY)
65
                .and_then(|name| name.to_string()),
66
            rusttype::Font::Owned(_) => None,
67
        }
68
    }
69

            
70
    #[must_use]
71
    pub(crate) fn glyph(&self, c: char) -> rusttype::Glyph<'static> {
72
        self.handle.font.glyph(c)
73
    }
74

            
75
    #[must_use]
76
    pub(crate) fn pair_kerning(
77
        &self,
78
        size: f32,
79
        a: rusttype::GlyphId,
80
        b: rusttype::GlyphId,
81
    ) -> f32 {
82
        self.handle.font.pair_kerning(Scale::uniform(size), a, b)
83
    }
84
}
85

            
86
#[derive(Debug)]
87
pub struct FontData {
88
    pub(crate) id: u64,
89
    pub(crate) font: rusttype::Font<'static>,
90
}
91

            
92
pub struct LoadedFont {
93
    pub font: Font,
94
    pub cache: gpu_cache::Cache<'static>,
95
    pub(crate) binding: Option<BindingGroup>,
96
    pub(crate) texture: Option<Texture>,
97
}
98

            
99
impl Debug for LoadedFont {
100
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101
        f.debug_struct("LoadedFont")
102
            .field("font", &self.font)
103
            .field("binding", &self.binding)
104
            .field("texture", &self.texture)
105
            .finish()
106
    }
107
}
108

            
109
impl LoadedFont {
110
    pub fn new(font: &Font) -> Self {
111
        Self {
112
            font: font.clone(),
113
            cache: gpu_cache::Cache::builder().dimensions(512, 512).build(),
114
            binding: None,
115
            texture: None,
116
        }
117
    }
118
}