rg3d_ui/
ttf.rs

1use crate::{
2    core::{algebra::Vector2, io, rectpack::RectPacker},
3    draw::SharedTexture,
4};
5use fxhash::FxHashMap;
6use std::{
7    fmt::{Debug, Formatter},
8    ops::{Deref, Range},
9    path::Path,
10    sync::{Arc, Mutex},
11};
12
13#[derive(Debug)]
14pub struct FontGlyph {
15    pub top: f32,
16    pub left: f32,
17    pub advance: f32,
18    pub tex_coords: [Vector2<f32>; 4],
19    pub bitmap_width: usize,
20    pub bitmap_height: usize,
21    pub pixels: Vec<u8>,
22}
23
24pub struct Font {
25    height: f32,
26    glyphs: Vec<FontGlyph>,
27    ascender: f32,
28    descender: f32,
29    char_map: FxHashMap<u32, usize>,
30    atlas: Vec<u8>,
31    atlas_size: usize,
32    pub texture: Option<SharedTexture>,
33}
34
35#[derive(Debug, Clone)]
36pub struct SharedFont(pub Arc<Mutex<Font>>);
37
38impl SharedFont {
39    pub fn new(font: Font) -> Self {
40        Self(Arc::new(Mutex::new(font)))
41    }
42}
43
44impl From<Arc<Mutex<Font>>> for SharedFont {
45    fn from(arc: Arc<Mutex<Font>>) -> Self {
46        SharedFont(arc)
47    }
48}
49
50impl PartialEq for SharedFont {
51    fn eq(&self, other: &Self) -> bool {
52        std::ptr::eq(self.0.deref(), other.0.deref())
53    }
54}
55
56impl Debug for Font {
57    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
58        writeln!(f, "Font")
59    }
60}
61
62impl Font {
63    pub fn default_char_set() -> &'static [Range<u32>] {
64        &[
65            // Basic Latin + Latin Supplement
66            0x0020..0x00FF,
67        ]
68    }
69
70    pub fn korean_char_set() -> &'static [Range<u32>] {
71        &[
72            // Basic Latin + Latin Supplement
73            0x0020..0x00FF,
74            // Korean alphabets
75            0x3131..0x3163,
76            // Korean characters
77            0xAC00..0xD7A3,
78            // Invalid
79            0xFFFD..0xFFFD,
80        ]
81    }
82
83    pub fn chinese_full_char_set() -> &'static [Range<u32>] {
84        &[
85            // Basic Latin + Latin Supplement
86            0x0020..0x00FF,
87            // General Punctuation
88            0x2000..0x206F,
89            // CJK Symbols and Punctuations, Hiragana, Katakana
90            0x3000..0x30FF,
91            // Katakana Phonetic Extensions
92            0x31F0..0x31FF,
93            // Half-width characters
94            0xFF00..0xFFEF,
95            // Invalid
96            0xFFFD..0xFFFD,
97            // CJK Ideograms
98            0x4e00..0x9FAF,
99        ]
100    }
101
102    pub fn cyrillic_char_set() -> &'static [Range<u32>] {
103        &[
104            // Basic Latin + Latin Supplement
105            0x0020..0x00FF,
106            // Cyrillic + Cyrillic Supplement
107            0x0400..0x052F,
108            // Cyrillic Extended-A
109            0x2DE0..0x2DFF,
110            // Cyrillic Extended-B
111            0xA640..0xA69F,
112        ]
113    }
114
115    pub fn thai_char_set() -> &'static [Range<u32>] {
116        &[
117            // Basic Latin + Latin Supplement
118            0x0020..0x00FF,
119            // Punctuations
120            0x2010..0x205E,
121            // Thai
122            0x0E00..0x0E7F,
123        ]
124    }
125
126    pub fn vietnamese_char_set() -> &'static [Range<u32>] {
127        &[
128            // Basic Latin + Latin Supplement
129            0x0020..0x00FF,
130            // Vietnamese
131            0x0102..0x0103,
132            0x0110..0x0111,
133            0x0128..0x0129,
134            0x0168..0x0169,
135            0x01A0..0x01A1,
136            0x01AF..0x01B0,
137            0x1EA0..0x1EF9,
138        ]
139    }
140
141    pub fn from_memory(
142        data: Vec<u8>,
143        height: f32,
144        char_set: &[Range<u32>],
145    ) -> Result<Self, &'static str> {
146        let fontdue_font = fontdue::Font::from_bytes(data, fontdue::FontSettings::default())?;
147        let font_metrics = fontdue_font.horizontal_line_metrics(height).unwrap();
148
149        let mut font = Font {
150            height,
151            glyphs: Vec::new(),
152            ascender: font_metrics.ascent,
153            descender: font_metrics.descent,
154            char_map: FxHashMap::default(),
155            atlas: Vec::new(),
156            atlas_size: 0,
157            texture: None,
158        };
159
160        let mut index = 0;
161        for range in char_set {
162            for unicode in range.start..range.end {
163                if let Some(character) = std::char::from_u32(unicode) {
164                    let (metrics, bitmap) = fontdue_font.rasterize(character, height);
165
166                    font.glyphs.push(FontGlyph {
167                        left: metrics.xmin as f32,
168                        top: metrics.ymin as f32,
169                        pixels: bitmap,
170                        advance: metrics.advance_width,
171                        tex_coords: Default::default(),
172                        bitmap_width: metrics.width,
173                        bitmap_height: metrics.height,
174                    });
175
176                    font.char_map.insert(unicode, index);
177                    index += 1;
178                }
179            }
180        }
181
182        font.pack();
183
184        Ok(font)
185    }
186
187    pub async fn from_file<P: AsRef<Path>>(
188        path: P,
189        height: f32,
190        char_set: &[Range<u32>],
191    ) -> Result<Self, &'static str> {
192        if let Ok(file_content) = io::load_file(path).await {
193            Self::from_memory(file_content, height, char_set)
194        } else {
195            Err("Unable to read file")
196        }
197    }
198
199    #[inline]
200    pub fn glyph(&self, unicode: u32) -> Option<&FontGlyph> {
201        match self.char_map.get(&unicode) {
202            Some(glyph_index) => self.glyphs.get(*glyph_index),
203            None => None,
204        }
205    }
206
207    #[inline]
208    pub fn glyph_index(&self, unicode: u32) -> Option<usize> {
209        self.char_map.get(&unicode).cloned()
210    }
211
212    #[inline]
213    pub fn glyphs(&self) -> &[FontGlyph] {
214        &self.glyphs
215    }
216
217    #[inline]
218    pub fn height(&self) -> f32 {
219        self.height
220    }
221
222    #[inline]
223    pub fn ascender(&self) -> f32 {
224        self.ascender
225    }
226
227    #[inline]
228    pub fn descender(&self) -> f32 {
229        self.descender
230    }
231
232    #[inline]
233    pub fn atlas_pixels(&self) -> &[u8] {
234        self.atlas.as_slice()
235    }
236
237    #[inline]
238    pub fn atlas_size(&self) -> usize {
239        self.atlas_size
240    }
241
242    #[inline]
243    pub fn glyph_advance(&self, c: u32) -> f32 {
244        self.glyph(c).map_or(self.height(), |glyph| glyph.advance)
245    }
246
247    #[inline]
248    fn compute_atlas_size(&self, border: usize) -> usize {
249        let mut area = 0.0;
250        for glyph in self.glyphs.iter() {
251            area += (glyph.bitmap_width + border) as f32 * (glyph.bitmap_height + border) as f32;
252        }
253        (1.3 * area.sqrt()) as usize
254    }
255
256    fn pack(&mut self) {
257        let border = 2;
258        self.atlas_size = self.compute_atlas_size(border);
259        self.atlas = vec![0; (self.atlas_size * self.atlas_size) as usize];
260        let k = 1.0 / self.atlas_size as f32;
261        let mut rect_packer = RectPacker::new(self.atlas_size, self.atlas_size);
262        for glyph in self.glyphs.iter_mut() {
263            if let Some(bounds) =
264                rect_packer.find_free(glyph.bitmap_width + border, glyph.bitmap_height + border)
265            {
266                let bw = (bounds.w() - border) as usize;
267                let bh = (bounds.h() - border) as usize;
268                let bx = (bounds.x() + border / 2) as usize;
269                let by = (bounds.y() + border / 2) as usize;
270
271                let tw = bw as f32 * k;
272                let th = bh as f32 * k;
273                let tx = bx as f32 * k;
274                let ty = by as f32 * k;
275
276                glyph.tex_coords[0] = Vector2::new(tx, ty);
277                glyph.tex_coords[1] = Vector2::new(tx + tw, ty);
278                glyph.tex_coords[2] = Vector2::new(tx + tw, ty + th);
279                glyph.tex_coords[3] = Vector2::new(tx, ty + th);
280
281                let row_end = by + bh;
282                let col_end = bx + bw;
283
284                // Copy glyph pixels to atlas pixels
285                for (src_row, row) in (by..row_end).enumerate() {
286                    for (src_col, col) in (bx..col_end).enumerate() {
287                        self.atlas[row * self.atlas_size + col] =
288                            glyph.pixels[src_row * bw + src_col];
289                    }
290                }
291            } else {
292                println!("Insufficient atlas size!");
293            }
294        }
295    }
296}