1use std::collections::HashMap;
2use glam::{Vec2, Vec3};
3use fontdue::{Font, FontSettings};
4use crate::color::Color;
5use crate::geometry::{Vertex, Mesh};
6use crate::material::TextureData;
7
8#[derive(Debug, Clone)]
11pub struct GlyphInfo {
12 pub uv_min: Vec2,
14 pub uv_max: Vec2,
15 pub size: Vec2,
17 pub bearing: Vec2,
19 pub advance: f32,
21}
22
23impl GlyphInfo {
24 fn empty(advance: f32) -> Self {
25 Self { uv_min: Vec2::ZERO, uv_max: Vec2::ZERO, size: Vec2::ZERO, bearing: Vec2::ZERO, advance }
26 }
27}
28
29pub struct FontAtlas {
32 font: Font,
33 pub texture: TextureData,
34 glyphs: HashMap<(u32, u32), GlyphInfo>, cursor_x: usize,
36 cursor_y: usize,
37 row_height: usize,
38}
39
40impl FontAtlas {
41 pub fn from_bytes(font_data: &[u8], atlas_size: usize) -> Result<Self, String> {
43 let font = Font::from_bytes(font_data, FontSettings::default())
44 .map_err(|e| e.to_string())?;
45 let texture = TextureData::new(atlas_size, atlas_size);
46 Ok(Self {
47 font,
48 texture,
49 glyphs: HashMap::new(),
50 cursor_x: 1,
51 cursor_y: 1,
52 row_height: 0,
53 })
54 }
55
56 pub fn get_or_rasterize(&mut self, c: char, px: f32) -> GlyphInfo {
58 let key = (c as u32, px.to_bits());
59 if let Some(g) = self.glyphs.get(&key) { return g.clone(); }
60 self.rasterize_glyph(c, px);
61 self.glyphs.get(&key).cloned().unwrap_or_else(|| GlyphInfo::empty(px * 0.5))
62 }
63
64 fn rasterize_glyph(&mut self, c: char, px: f32) {
65 let (metrics, bitmap) = self.font.rasterize(c, px);
66
67 if metrics.width == 0 || metrics.height == 0 {
68 let key = (c as u32, px.to_bits());
69 self.glyphs.insert(key, GlyphInfo::empty(metrics.advance_width));
70 return;
71 }
72
73 let aw = self.texture.width;
74 let ah = self.texture.height;
75
76 if self.cursor_x + metrics.width + 1 > aw {
77 self.cursor_x = 1;
78 self.cursor_y += self.row_height + 1;
79 self.row_height = 0;
80 }
81
82 if self.cursor_y + metrics.height + 1 > ah {
83 let key = (c as u32, px.to_bits());
85 self.glyphs.insert(key, GlyphInfo::empty(metrics.advance_width));
86 return;
87 }
88
89 for gy in 0..metrics.height {
90 for gx in 0..metrics.width {
91 let alpha = bitmap[gy * metrics.width + gx];
92 let dx = self.cursor_x + gx;
93 let dy = self.cursor_y + gy;
94 let idx = (dy * aw + dx) * 4;
95 self.texture.data[idx] = 255;
96 self.texture.data[idx + 1] = 255;
97 self.texture.data[idx + 2] = 255;
98 self.texture.data[idx + 3] = alpha;
99 }
100 }
101
102 let uv_min = Vec2::new(
103 self.cursor_x as f32 / aw as f32,
104 self.cursor_y as f32 / ah as f32,
105 );
106 let uv_max = Vec2::new(
107 (self.cursor_x + metrics.width) as f32 / aw as f32,
108 (self.cursor_y + metrics.height) as f32 / ah as f32,
109 );
110
111 self.row_height = self.row_height.max(metrics.height);
112 self.cursor_x += metrics.width + 1;
113
114 let key = (c as u32, px.to_bits());
115 self.glyphs.insert(key, GlyphInfo {
116 uv_min,
117 uv_max,
118 size: Vec2::new(metrics.width as f32, metrics.height as f32),
119 bearing: Vec2::new(metrics.xmin as f32, metrics.ymin as f32),
120 advance: metrics.advance_width,
121 });
122 }
123}
124
125pub fn generate_text_mesh(atlas: &mut FontAtlas, text: &str, px: f32, color: Color) -> Mesh {
130 let mut vertices = Vec::new();
131 let mut indices = Vec::new();
132 let mut cursor_x = 0.0f32;
133
134 for ch in text.chars() {
135 let info = atlas.get_or_rasterize(ch, px);
136 if info.size.x > 0.0 && info.size.y > 0.0 {
137 let x0 = cursor_x + info.bearing.x;
138 let y0 = info.bearing.y;
139 let x1 = x0 + info.size.x;
140 let y1 = y0 + info.size.y;
141
142 let base = vertices.len() as u32;
143 vertices.push(Vertex { position: Vec3::new(x0, y0, 0.0), normal: Vec3::Z, uv: info.uv_min, color, tangent: Vec3::X });
144 vertices.push(Vertex { position: Vec3::new(x1, y0, 0.0), normal: Vec3::Z, uv: Vec2::new(info.uv_max.x, info.uv_min.y), color, tangent: Vec3::X });
145 vertices.push(Vertex { position: Vec3::new(x1, y1, 0.0), normal: Vec3::Z, uv: info.uv_max, color, tangent: Vec3::X });
146 vertices.push(Vertex { position: Vec3::new(x0, y1, 0.0), normal: Vec3::Z, uv: Vec2::new(info.uv_min.x, info.uv_max.y), color, tangent: Vec3::X });
147
148 indices.extend_from_slice(&[base, base+1, base+2, base, base+2, base+3]);
149 }
150 cursor_x += info.advance;
151 }
152
153 Mesh::new(vertices, indices)
154}
155
156pub fn measure_text(atlas: &mut FontAtlas, text: &str, px: f32) -> f32 {
158 text.chars().map(|c| atlas.get_or_rasterize(c, px).advance).sum()
159}
160
161pub struct GlyphFont {
170 font: Font,
171 cache: HashMap<(u32, u32), (fontdue::Metrics, Vec<u8>)>,
173}
174
175impl GlyphFont {
176 pub fn from_bytes(data: &[u8]) -> Result<Self, String> {
178 let font = Font::from_bytes(data, FontSettings::default()).map_err(|e| e.to_string())?;
179 Ok(Self { font, cache: HashMap::new() })
180 }
181
182 pub fn from_path(path: &str) -> Result<Self, String> {
184 let bytes = std::fs::read(path).map_err(|e| format!("{path}: {e}"))?;
185 Self::from_bytes(&bytes)
186 }
187
188 fn glyph(&mut self, c: char, px: f32) -> &(fontdue::Metrics, Vec<u8>) {
189 let key = (c as u32, px.to_bits());
190 self.cache.entry(key).or_insert_with(|| self.font.rasterize(c, px))
191 }
192
193 pub fn measure(&mut self, text: &str, px: f32) -> f32 {
195 text.chars().map(|c| self.font.metrics(c, px).advance_width).sum()
196 }
197
198 pub fn draw_text(
204 &mut self,
205 buf: &mut [u32], w: usize, h: usize,
206 x: f32, y: f32, px: f32, color: u32, text: &str,
207 ) {
208 if w == 0 || h == 0 || px <= 0.0 { return; }
209 let lm = self.font.horizontal_line_metrics(px);
210 let ascent = lm.map(|m| m.ascent).unwrap_or(px * 0.8);
211 let baseline = y + ascent;
212
213 let cr = ((color >> 16) & 0xFF) as f32;
214 let cg = ((color >> 8) & 0xFF) as f32;
215 let cb = (color & 0xFF) as f32;
216
217 let mut pen_x = x;
218 for c in text.chars() {
219 let (metrics, bitmap) = self.glyph(c, px).clone();
220 if metrics.width > 0 && metrics.height > 0 {
221 let gx0 = (pen_x + metrics.xmin as f32).round() as i32;
223 let gy0 = (baseline - metrics.ymin as f32 - metrics.height as f32).round() as i32;
224 for gy in 0..metrics.height {
225 let py = gy0 + gy as i32;
226 if py < 0 || py as usize >= h { continue; }
227 let row = py as usize * w;
228 for gx in 0..metrics.width {
229 let px_ = gx0 + gx as i32;
230 if px_ < 0 || px_ as usize >= w { continue; }
231 let a = bitmap[gy * metrics.width + gx] as f32 / 255.0;
232 if a <= 0.0 { continue; }
233 let idx = row + px_ as usize;
234 let dst = buf[idx];
235 let dr = ((dst >> 16) & 0xFF) as f32;
236 let dg = ((dst >> 8) & 0xFF) as f32;
237 let db = (dst & 0xFF) as f32;
238 let nr = (cr * a + dr * (1.0 - a)).min(255.0) as u32;
239 let ng = (cg * a + dg * (1.0 - a)).min(255.0) as u32;
240 let nb = (cb * a + db * (1.0 - a)).min(255.0) as u32;
241 buf[idx] = (nr << 16) | (ng << 8) | nb;
242 }
243 }
244 }
245 pen_x += metrics.advance_width;
246 }
247 }
248}