goud_engine/rendering/text/
rasterizer.rs1#[derive(Debug, Clone, Copy, PartialEq)]
8pub struct GlyphMetrics {
9 pub advance_width: f32,
11 pub bearing_x: f32,
13 pub bearing_y: f32,
15 pub width: f32,
17 pub height: f32,
19}
20
21#[derive(Debug, Clone)]
27pub struct RasterizedGlyph {
28 pub bitmap: Vec<u8>,
30 pub width: u32,
32 pub height: u32,
34 pub metrics: GlyphMetrics,
36}
37
38pub fn rasterize_glyphs(
49 font: &fontdue::Font,
50 size_px: f32,
51 chars: &[char],
52) -> Vec<(char, RasterizedGlyph)> {
53 chars
54 .iter()
55 .map(|&ch| {
56 let (metrics, bitmap) = font.rasterize(ch, size_px);
57 let glyph = RasterizedGlyph {
58 bitmap,
59 width: metrics.width as u32,
60 height: metrics.height as u32,
61 metrics: GlyphMetrics {
62 advance_width: metrics.advance_width,
63 bearing_x: metrics.xmin as f32,
64 bearing_y: metrics.ymin as f32,
65 width: metrics.width as f32,
66 height: metrics.height as f32,
67 },
68 };
69 (ch, glyph)
70 })
71 .collect()
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 fn test_font() -> fontdue::Font {
80 let bytes = include_bytes!("../../../test_assets/fonts/test_font.ttf");
81 fontdue::Font::from_bytes(bytes as &[u8], fontdue::FontSettings::default())
82 .expect("test_font.ttf should parse")
83 }
84
85 #[test]
86 fn test_rasterize_glyph_a_has_nonzero_dimensions() {
87 let font = test_font();
88 let glyphs = rasterize_glyphs(&font, 32.0, &['A']);
89
90 assert_eq!(glyphs.len(), 1);
91 let (ch, glyph) = &glyphs[0];
92 assert_eq!(*ch, 'A');
93 assert!(glyph.width > 0, "glyph width should be > 0");
94 assert!(glyph.height > 0, "glyph height should be > 0");
95 }
96
97 #[test]
98 fn test_rasterize_glyph_a_bitmap_has_nonzero_pixels() {
99 let font = test_font();
100 let glyphs = rasterize_glyphs(&font, 32.0, &['A']);
101 let (_, glyph) = &glyphs[0];
102
103 let nonzero_count = glyph.bitmap.iter().filter(|&&b| b > 0).count();
104 assert!(nonzero_count > 0, "bitmap should have non-zero pixels");
105 }
106
107 #[test]
108 fn test_rasterize_glyph_bitmap_length_matches_dimensions() {
109 let font = test_font();
110 let glyphs = rasterize_glyphs(&font, 24.0, &['B']);
111 let (_, glyph) = &glyphs[0];
112
113 let expected_len = (glyph.width * glyph.height) as usize;
114 assert_eq!(glyph.bitmap.len(), expected_len);
115 }
116
117 #[test]
118 fn test_rasterize_multiple_chars_preserves_order() {
119 let font = test_font();
120 let chars = vec!['X', 'Y', 'Z'];
121 let glyphs = rasterize_glyphs(&font, 16.0, &chars);
122
123 assert_eq!(glyphs.len(), 3);
124 assert_eq!(glyphs[0].0, 'X');
125 assert_eq!(glyphs[1].0, 'Y');
126 assert_eq!(glyphs[2].0, 'Z');
127 }
128
129 #[test]
130 fn test_rasterize_space_glyph_has_zero_dimensions() {
131 let font = test_font();
132 let glyphs = rasterize_glyphs(&font, 32.0, &[' ']);
133 let (_, glyph) = &glyphs[0];
134
135 assert_eq!(glyph.width, 0);
137 assert_eq!(glyph.height, 0);
138 assert!(
139 glyph.metrics.advance_width > 0.0,
140 "space should have positive advance"
141 );
142 }
143
144 #[test]
145 fn test_rasterize_metrics_advance_width_positive_for_visible_glyph() {
146 let font = test_font();
147 let glyphs = rasterize_glyphs(&font, 20.0, &['M']);
148 let (_, glyph) = &glyphs[0];
149
150 assert!(
151 glyph.metrics.advance_width > 0.0,
152 "advance_width should be positive for 'M'"
153 );
154 }
155}