Skip to main content

agg_rust/
glyph_raster_bin.rs

1//! Binary raster glyph generator.
2//!
3//! Port of `agg_glyph_raster_bin.h`.
4//! Extracts binary (1-bit per pixel) glyph bitmaps from embedded raster font data.
5
6use crate::basics::CoverType;
7
8// ============================================================================
9// GlyphRect — bounding box of a glyph
10// ============================================================================
11
12/// Bounding box and advance of a raster glyph.
13#[derive(Debug, Clone, Copy, Default)]
14pub struct GlyphRect {
15    pub x1: i32,
16    pub y1: i32,
17    pub x2: i32,
18    pub y2: i32,
19    pub dx: f64,
20    pub dy: f64,
21}
22
23// ============================================================================
24// GlyphRasterBin — extracts glyph bitmaps from embedded font data
25// ============================================================================
26
27/// Binary raster glyph generator.
28///
29/// Port of C++ `glyph_raster_bin<ColorT>`.
30/// Reads glyph bitmaps from embedded font data arrays (see `embedded_raster_fonts`).
31///
32/// Font data format:
33/// - Byte 0: height
34/// - Byte 1: baseline
35/// - Byte 2: start_char (first ASCII code)
36/// - Byte 3: num_chars
37/// - Bytes 4..4+num_chars*2: glyph offset table (little-endian u16)
38/// - Remaining bytes: glyph bitmap data (1 bit per pixel, packed 8 per byte)
39pub struct GlyphRasterBin<'a> {
40    font: &'a [u8],
41    span: Vec<CoverType>,
42    bits: &'a [u8],
43    glyph_width: u32,
44    glyph_byte_width: u32,
45}
46
47impl<'a> GlyphRasterBin<'a> {
48    pub fn new(font: &'a [u8]) -> Self {
49        Self {
50            font,
51            span: vec![0; 32],
52            bits: &[],
53            glyph_width: 0,
54            glyph_byte_width: 0,
55        }
56    }
57
58    pub fn font(&self) -> &'a [u8] {
59        self.font
60    }
61
62    pub fn set_font(&mut self, font: &'a [u8]) {
63        self.font = font;
64    }
65
66    /// Font height in pixels.
67    pub fn height(&self) -> f64 {
68        self.font[0] as f64
69    }
70
71    /// Baseline offset from top.
72    pub fn base_line(&self) -> f64 {
73        self.font[1] as f64
74    }
75
76    /// Calculate total width of a string.
77    pub fn width(&self, s: &str) -> f64 {
78        let start_char = self.font[2] as u32;
79        let num_chars = self.font[3] as u32;
80        let mut w = 0u32;
81        for ch in s.bytes() {
82            let glyph = ch as u32;
83            if glyph >= start_char && glyph < start_char + num_chars {
84                let offset = self.value(4 + (glyph - start_char) as usize * 2);
85                let bits_start = 4 + num_chars as usize * 2 + offset as usize;
86                w += self.font[bits_start] as u32;
87            }
88        }
89        w as f64
90    }
91
92    /// Prepare a glyph for rendering.
93    ///
94    /// Sets up internal state and fills `r` with the glyph's bounding box.
95    /// `glyph` is the ASCII character code. `flip` inverts Y direction.
96    pub fn prepare(&mut self, r: &mut GlyphRect, x: f64, y: f64, glyph: u32, flip: bool) {
97        let start_char = self.font[2] as u32;
98        let num_chars = self.font[3] as u32;
99
100        // Skip characters outside the font's range
101        if glyph < start_char || glyph >= start_char + num_chars {
102            r.x1 = 1;
103            r.x2 = 0; // x2 < x1 signals "no glyph" to the renderer
104            r.dx = 0.0;
105            r.dy = 0.0;
106            self.glyph_width = 0;
107            self.glyph_byte_width = 0;
108            self.bits = &[];
109            return;
110        }
111
112        let offset = self.value(4 + (glyph - start_char) as usize * 2);
113        let bits_start = 4 + num_chars as usize * 2 + offset as usize;
114
115        self.glyph_width = self.font[bits_start] as u32;
116        self.glyph_byte_width = (self.glyph_width + 7) >> 3;
117        self.bits = &self.font[bits_start + 1..];
118
119        // Ensure span buffer is large enough for this glyph
120        if self.span.len() < self.glyph_width as usize {
121            self.span.resize(self.glyph_width as usize, 0);
122        }
123
124        r.x1 = x as i32;
125        r.x2 = r.x1 + self.glyph_width as i32 - 1;
126        if flip {
127            r.y1 = y as i32 - self.font[0] as i32 + self.font[1] as i32;
128            r.y2 = r.y1 + self.font[0] as i32 - 1;
129        } else {
130            r.y1 = y as i32 - self.font[1] as i32 + 1;
131            r.y2 = r.y1 + self.font[0] as i32 - 1;
132        }
133        r.dx = self.glyph_width as f64;
134        r.dy = 0.0;
135    }
136
137    /// Get coverage data for scanline `i` (0 = top of glyph).
138    ///
139    /// Returns a slice of `CoverType` values (0 or 255) for each pixel.
140    pub fn span(&mut self, i: u32) -> &[CoverType] {
141        if self.glyph_width == 0 || self.bits.is_empty() {
142            return &self.span[..0];
143        }
144        // Font stores rows bottom-to-top, so invert
145        let row = self.font[0] as u32 - i - 1;
146        let row_start = (row * self.glyph_byte_width) as usize;
147        if row_start >= self.bits.len() {
148            // Glyph data is truncated — return empty coverage
149            for j in 0..self.glyph_width as usize {
150                self.span[j] = 0;
151            }
152            return &self.span[..self.glyph_width as usize];
153        }
154        let bits = &self.bits[row_start..];
155        let mut val = bits[0];
156        let mut nb = 0u32;
157        let mut bit_idx = 0usize;
158        for j in 0..self.glyph_width as usize {
159            self.span[j] = if (val & 0x80) != 0 { 255 } else { 0 };
160            val <<= 1;
161            nb += 1;
162            if nb >= 8 {
163                bit_idx += 1;
164                if bit_idx < bits.len() {
165                    val = bits[bit_idx];
166                }
167                nb = 0;
168            }
169        }
170        &self.span[..self.glyph_width as usize]
171    }
172
173    /// Read a little-endian u16 from font data at offset.
174    fn value(&self, offset: usize) -> u16 {
175        u16::from_le_bytes([self.font[offset], self.font[offset + 1]])
176    }
177}
178
179#[cfg(test)]
180mod tests {
181    use super::*;
182
183    // Minimal fake font: height=2, baseline=0, start_char=65('A'), num_chars=1
184    // Glyph offset for 'A': 0
185    // Glyph data: width=2, then 1 byte per row (2 rows)
186    // Row 0: 0b11000000 = both pixels on
187    // Row 1: 0b01000000 = only second pixel on
188    fn make_test_font() -> Vec<u8> {
189        let mut font = Vec::new();
190        // Header
191        font.push(2); // height
192        font.push(0); // baseline
193        font.push(65); // start_char = 'A'
194        font.push(1); // num_chars = 1
195
196        // Glyph offset table (1 entry, little-endian u16 = 0)
197        font.push(0);
198        font.push(0);
199
200        // Glyph data for 'A'
201        font.push(2); // glyph_width = 2
202        font.push(0b1100_0000); // row 0: both pixels on
203        font.push(0b0100_0000); // row 1: only pixel 1 on
204
205        font
206    }
207
208    #[test]
209    fn test_font_properties() {
210        let font = make_test_font();
211        let glyph = GlyphRasterBin::new(&font);
212        assert_eq!(glyph.height(), 2.0);
213        assert_eq!(glyph.base_line(), 0.0);
214    }
215
216    #[test]
217    fn test_prepare_glyph() {
218        let font = make_test_font();
219        let mut glyph = GlyphRasterBin::new(&font);
220        let mut r = GlyphRect::default();
221        glyph.prepare(&mut r, 10.0, 5.0, 65, false);
222        assert_eq!(r.x1, 10);
223        assert_eq!(r.x2, 11); // width 2
224        assert_eq!(r.dx, 2.0);
225    }
226
227    #[test]
228    fn test_span() {
229        let font = make_test_font();
230        let mut glyph = GlyphRasterBin::new(&font);
231        let mut r = GlyphRect::default();
232        glyph.prepare(&mut r, 0.0, 0.0, 65, false);
233
234        // Span 0 (top row) = row 1 of font data (inverted) = 0b01000000
235        let s0 = glyph.span(0);
236        assert_eq!(s0.len(), 2);
237        assert_eq!(s0[0], 0); // first pixel off
238        assert_eq!(s0[1], 255); // second pixel on
239
240        // Span 1 (bottom row) = row 0 of font data = 0b11000000
241        let s1 = glyph.span(1);
242        assert_eq!(s1[0], 255); // both on
243        assert_eq!(s1[1], 255);
244    }
245
246    #[test]
247    fn test_width_calculation() {
248        let font = make_test_font();
249        let glyph = GlyphRasterBin::new(&font);
250        assert_eq!(glyph.width("A"), 2.0);
251    }
252}