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 0x0020..0x00FF,
67 ]
68 }
69
70 pub fn korean_char_set() -> &'static [Range<u32>] {
71 &[
72 0x0020..0x00FF,
74 0x3131..0x3163,
76 0xAC00..0xD7A3,
78 0xFFFD..0xFFFD,
80 ]
81 }
82
83 pub fn chinese_full_char_set() -> &'static [Range<u32>] {
84 &[
85 0x0020..0x00FF,
87 0x2000..0x206F,
89 0x3000..0x30FF,
91 0x31F0..0x31FF,
93 0xFF00..0xFFEF,
95 0xFFFD..0xFFFD,
97 0x4e00..0x9FAF,
99 ]
100 }
101
102 pub fn cyrillic_char_set() -> &'static [Range<u32>] {
103 &[
104 0x0020..0x00FF,
106 0x0400..0x052F,
108 0x2DE0..0x2DFF,
110 0xA640..0xA69F,
112 ]
113 }
114
115 pub fn thai_char_set() -> &'static [Range<u32>] {
116 &[
117 0x0020..0x00FF,
119 0x2010..0x205E,
121 0x0E00..0x0E7F,
123 ]
124 }
125
126 pub fn vietnamese_char_set() -> &'static [Range<u32>] {
127 &[
128 0x0020..0x00FF,
130 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 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}