1const FONT_DATA: [u8; 768] = [
10 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
12 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00,
14 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00,
16 0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0x6C, 0x6C, 0x00,
18 0x18, 0x3E, 0x60, 0x3C, 0x06, 0x7C, 0x18, 0x00,
20 0x00, 0xC6, 0xCC, 0x18, 0x30, 0x66, 0xC6, 0x00,
22 0x38, 0x6C, 0x38, 0x76, 0xDC, 0xCC, 0x76, 0x00,
24 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
26 0x0C, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00,
28 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00,
30 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00,
32 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00,
34 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30,
36 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00,
38 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
40 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00,
42 0x7C, 0xCE, 0xDE, 0xF6, 0xE6, 0xC6, 0x7C, 0x00,
44 0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00,
46 0x7C, 0xC6, 0x06, 0x7C, 0xC0, 0xC0, 0xFE, 0x00,
48 0x7C, 0xC6, 0x06, 0x3C, 0x06, 0xC6, 0x7C, 0x00,
50 0x0C, 0x2C, 0x4C, 0x8C, 0xFE, 0x0C, 0x0C, 0x00,
52 0xFE, 0xC0, 0xFC, 0x06, 0x06, 0xC6, 0x7C, 0x00,
54 0x7C, 0xC0, 0xC0, 0xFC, 0xC6, 0xC6, 0x7C, 0x00,
56 0xFE, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00,
58 0x7C, 0xC6, 0xC6, 0x7C, 0xC6, 0xC6, 0x7C, 0x00,
60 0x7C, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0x7C, 0x00,
62 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00,
64 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x30, 0x00,
66 0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C, 0x00,
68 0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00,
70 0x30, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x30, 0x00,
72 0x7C, 0xC6, 0x0C, 0x18, 0x18, 0x00, 0x18, 0x00,
74 0x7C, 0xC6, 0xDE, 0xDE, 0xDC, 0xC0, 0x7C, 0x00,
76 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0x00,
78 0xFC, 0xC6, 0xC6, 0xFC, 0xC6, 0xC6, 0xFC, 0x00,
80 0x7C, 0xC6, 0xC0, 0xC0, 0xC0, 0xC6, 0x7C, 0x00,
82 0xF8, 0xCC, 0xC6, 0xC6, 0xC6, 0xCC, 0xF8, 0x00,
84 0xFE, 0xC0, 0xC0, 0xFC, 0xC0, 0xC0, 0xFE, 0x00,
86 0xFE, 0xC0, 0xC0, 0xFC, 0xC0, 0xC0, 0xC0, 0x00,
88 0x7C, 0xC6, 0xC0, 0xCE, 0xC6, 0xC6, 0x7E, 0x00,
90 0xC6, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0x00,
92 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00,
94 0x06, 0x06, 0x06, 0x06, 0xC6, 0xC6, 0x7C, 0x00,
96 0xC6, 0xCC, 0xD8, 0xF0, 0xD8, 0xCC, 0xC6, 0x00,
98 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFE, 0x00,
100 0xC6, 0xEE, 0xFE, 0xD6, 0xC6, 0xC6, 0xC6, 0x00,
102 0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0xC6, 0x00,
104 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00,
106 0xFC, 0xC6, 0xC6, 0xFC, 0xC0, 0xC0, 0xC0, 0x00,
108 0x7C, 0xC6, 0xC6, 0xC6, 0xD6, 0xCC, 0x76, 0x00,
110 0xFC, 0xC6, 0xC6, 0xFC, 0xD8, 0xCC, 0xC6, 0x00,
112 0x7C, 0xC6, 0xC0, 0x7C, 0x06, 0xC6, 0x7C, 0x00,
114 0xFE, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
116 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00,
118 0xC6, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x10, 0x00,
120 0xC6, 0xC6, 0xC6, 0xD6, 0xFE, 0xEE, 0xC6, 0x00,
122 0xC6, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0xC6, 0x00,
124 0xC6, 0xC6, 0x6C, 0x38, 0x18, 0x18, 0x18, 0x00,
126 0xFE, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xFE, 0x00,
128 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00,
130 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x02, 0x00,
132 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00,
134 0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00,
136 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00,
138 0x18, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00,
140 0x00, 0x00, 0x7C, 0x06, 0x7E, 0xC6, 0x7E, 0x00,
142 0xC0, 0xC0, 0xFC, 0xC6, 0xC6, 0xC6, 0xFC, 0x00,
144 0x00, 0x00, 0x7C, 0xC6, 0xC0, 0xC6, 0x7C, 0x00,
146 0x06, 0x06, 0x7E, 0xC6, 0xC6, 0xC6, 0x7E, 0x00,
148 0x00, 0x00, 0x7C, 0xC6, 0xFE, 0xC0, 0x7C, 0x00,
150 0x1C, 0x36, 0x30, 0x7C, 0x30, 0x30, 0x30, 0x00,
152 0x00, 0x00, 0x7E, 0xC6, 0xC6, 0x7E, 0x06, 0x7C,
154 0xC0, 0xC0, 0xFC, 0xC6, 0xC6, 0xC6, 0xC6, 0x00,
156 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3C, 0x00,
158 0x06, 0x00, 0x06, 0x06, 0x06, 0xC6, 0xC6, 0x7C,
160 0xC0, 0xC0, 0xCC, 0xD8, 0xF0, 0xD8, 0xCC, 0x00,
162 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00,
164 0x00, 0x00, 0xCC, 0xFE, 0xD6, 0xC6, 0xC6, 0x00,
166 0x00, 0x00, 0xFC, 0xC6, 0xC6, 0xC6, 0xC6, 0x00,
168 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0x00,
170 0x00, 0x00, 0xFC, 0xC6, 0xC6, 0xFC, 0xC0, 0xC0,
172 0x00, 0x00, 0x7E, 0xC6, 0xC6, 0x7E, 0x06, 0x06,
174 0x00, 0x00, 0xDC, 0xE6, 0xC0, 0xC0, 0xC0, 0x00,
176 0x00, 0x00, 0x7E, 0xC0, 0x7C, 0x06, 0xFC, 0x00,
178 0x30, 0x30, 0x7C, 0x30, 0x30, 0x36, 0x1C, 0x00,
180 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x00,
182 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x00,
184 0x00, 0x00, 0xC6, 0xC6, 0xD6, 0xFE, 0x6C, 0x00,
186 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00,
188 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x7C,
190 0x00, 0x00, 0xFE, 0x0C, 0x38, 0x60, 0xFE, 0x00,
192 0x0E, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0E, 0x00,
194 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
196 0x70, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x70, 0x00,
198 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
200 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0x00,
202];
203
204const ATLAS_COLUMNS: u32 = 16;
206const ATLAS_ROWS: u32 = 6;
207const GLYPH_W: u32 = 8;
208const GLYPH_H: u32 = 8;
209
210pub fn generate_builtin_font() -> (Vec<u8>, u32, u32) {
216 let width = ATLAS_COLUMNS * GLYPH_W;
217 let height = ATLAS_ROWS * GLYPH_H;
218 let mut pixels = vec![0u8; (width * height * 4) as usize];
219
220 for glyph_idx in 0..96u32 {
221 let col = glyph_idx % ATLAS_COLUMNS;
222 let row = glyph_idx / ATLAS_COLUMNS;
223 let base_x = col * GLYPH_W;
224 let base_y = row * GLYPH_H;
225
226 for py in 0..GLYPH_H {
227 let byte = FONT_DATA[(glyph_idx * 8 + py) as usize];
228 for px in 0..GLYPH_W {
229 let bit = (byte >> (7 - px)) & 1;
230 let x = base_x + px;
231 let y = base_y + py;
232 let offset = ((y * width + x) * 4) as usize;
233 if bit == 1 {
234 pixels[offset] = 255; pixels[offset + 1] = 255; pixels[offset + 2] = 255; pixels[offset + 3] = 255; } }
240 }
241 }
242
243 (pixels, width, height)
244}
245
246#[cfg(test)]
247mod tests {
248 use super::*;
249
250 #[test]
251 fn font_texture_dimensions() {
252 let (pixels, w, h) = generate_builtin_font();
253 assert_eq!(w, 128);
254 assert_eq!(h, 48);
255 assert_eq!(pixels.len(), (128 * 48 * 4) as usize);
256 }
257
258 #[test]
259 fn space_is_empty() {
260 let (pixels, w, _) = generate_builtin_font();
261 for py in 0..8 {
263 for px in 0..8 {
264 let offset = ((py * w + px) * 4) as usize;
265 assert_eq!(pixels[offset + 3], 0, "space pixel ({px},{py}) should be transparent");
266 }
267 }
268 }
269
270 #[test]
271 fn letter_a_has_pixels() {
272 let (pixels, w, _) = generate_builtin_font();
273 let base_x: u32 = 1 * 8;
275 let base_y: u32 = 2 * 8;
276 let mut has_opaque = false;
277 for py in 0..8u32 {
278 for px in 0..8u32 {
279 let offset = (((base_y + py) * w + (base_x + px)) * 4) as usize;
280 if pixels[offset + 3] == 255 {
281 has_opaque = true;
282 }
283 }
284 }
285 assert!(has_opaque, "'A' glyph should have opaque pixels");
286 }
287}