Skip to main content

agg_rust/
renderer_raster_text.rs

1//! Raster text renderer for embedded bitmap fonts.
2//!
3//! Port of `agg_renderer_raster_text.h`.
4//! Renders text using binary raster glyphs from `GlyphRasterBin`.
5
6use crate::glyph_raster_bin::{GlyphRasterBin, GlyphRect};
7use crate::pixfmt_rgba::PixelFormat;
8use crate::renderer_base::RendererBase;
9
10// ============================================================================
11// render_raster_htext_solid — render horizontal text with solid color
12// ============================================================================
13
14/// Render a horizontal text string using a raster glyph generator and solid color.
15///
16/// Port of C++ `renderer_raster_htext_solid::render_text()`.
17/// Renders each character's binary glyph as a sequence of `blend_solid_hspan` calls.
18pub fn render_raster_htext_solid<PF: PixelFormat>(
19    ren: &mut RendererBase<PF>,
20    glyph: &mut GlyphRasterBin,
21    x: f64,
22    y: f64,
23    text: &str,
24    color: &PF::ColorType,
25    flip: bool,
26) {
27    let mut x = x;
28    let mut y = y;
29    let mut r = GlyphRect::default();
30
31    for ch in text.bytes() {
32        glyph.prepare(&mut r, x, y, ch as u32, flip);
33        if r.x2 >= r.x1 {
34            if flip {
35                for i in r.y1..=r.y2 {
36                    let span = glyph.span((r.y2 - i) as u32);
37                    ren.blend_solid_hspan(r.x1, i, r.x2 - r.x1 + 1, color, span);
38                }
39            } else {
40                for i in r.y1..=r.y2 {
41                    let span = glyph.span((i - r.y1) as u32);
42                    ren.blend_solid_hspan(r.x1, i, r.x2 - r.x1 + 1, color, span);
43                }
44            }
45        }
46        x += r.dx;
47        y += r.dy;
48    }
49}
50
51/// Render a vertical text string using a raster glyph generator and solid color.
52///
53/// Port of C++ `renderer_raster_vtext_solid::render_text()`.
54pub fn render_raster_vtext_solid<PF: PixelFormat>(
55    ren: &mut RendererBase<PF>,
56    glyph: &mut GlyphRasterBin,
57    x: f64,
58    y: f64,
59    text: &str,
60    color: &PF::ColorType,
61    flip: bool,
62) {
63    let mut x = x;
64    let mut y = y;
65    let mut r = GlyphRect::default();
66
67    for ch in text.bytes() {
68        glyph.prepare(&mut r, x, y, ch as u32, !flip);
69        if r.x2 >= r.x1 {
70            if flip {
71                for i in r.y1..=r.y2 {
72                    let span = glyph.span((i - r.y1) as u32);
73                    ren.blend_solid_vspan(i, r.x1, r.x2 - r.x1 + 1, color, span);
74                }
75            } else {
76                for i in r.y1..=r.y2 {
77                    let span = glyph.span((r.y2 - i) as u32);
78                    ren.blend_solid_vspan(i, r.x1, r.x2 - r.x1 + 1, color, span);
79                }
80            }
81        }
82        x += r.dx;
83        y += r.dy;
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90    use crate::color::Rgba8;
91    use crate::pixfmt_rgba::PixfmtRgba32;
92    use crate::rendering_buffer::RowAccessor;
93
94    // Minimal test font: height=2, baseline=0, start_char=65('A'), num_chars=1
95    fn make_test_font() -> Vec<u8> {
96        let mut font = Vec::new();
97        font.push(2); // height
98        font.push(0); // baseline
99        font.push(65); // start_char = 'A'
100        font.push(1); // num_chars = 1
101        font.push(0); // glyph offset lo
102        font.push(0); // glyph offset hi
103        font.push(2); // glyph_width = 2
104        font.push(0b1100_0000); // row 0
105        font.push(0b1100_0000); // row 1
106        font
107    }
108
109    fn make_buffer(w: u32, h: u32) -> (Vec<u8>, RowAccessor) {
110        let stride = (w * 4) as i32;
111        let buf = vec![0u8; (h * w * 4) as usize];
112        let mut ra = RowAccessor::new();
113        unsafe {
114            ra.attach(buf.as_ptr() as *mut u8, w, h, stride);
115        }
116        (buf, ra)
117    }
118
119    #[test]
120    fn test_render_htext() {
121        let font_data = make_test_font();
122        let mut glyph = GlyphRasterBin::new(&font_data);
123        let (_buf, mut ra) = make_buffer(20, 20);
124        let pixf = PixfmtRgba32::new(&mut ra);
125        let mut ren = RendererBase::new(pixf);
126
127        let red = Rgba8::new(255, 0, 0, 255);
128        render_raster_htext_solid(&mut ren, &mut glyph, 5.0, 5.0, "A", &red, false);
129
130        // Check that some pixels were rendered.
131        // With baseline=0, height=2, non-flipped: y1 = 5 - 0 + 1 = 6, y2 = 7
132        let p = ren.ren().pixel(5, 6);
133        assert!(p.r > 0 || p.a > 0, "Expected rendered pixel at (5,6)");
134    }
135
136    #[test]
137    fn test_render_empty_string() {
138        let font_data = make_test_font();
139        let mut glyph = GlyphRasterBin::new(&font_data);
140        let (_buf, mut ra) = make_buffer(20, 20);
141        let pixf = PixfmtRgba32::new(&mut ra);
142        let mut ren = RendererBase::new(pixf);
143
144        let red = Rgba8::new(255, 0, 0, 255);
145        render_raster_htext_solid(&mut ren, &mut glyph, 5.0, 5.0, "", &red, false);
146        // Should not panic or crash
147    }
148}