orbtk_render/raqote/
font.rs

1use crate::utils::{Color, Rectangle};
2
3#[derive(Debug, Clone)]
4pub struct Font {
5    inner: rusttype::Font<'static>,
6}
7
8impl Font {
9    pub fn from_bytes(bytes: &'static [u8]) -> Result<Self, rusttype::Error> {
10        rusttype::Font::from_bytes(bytes).map(|font| Font { inner: font })
11    }
12
13    pub fn measure_text(&self, text: &str, size: f64) -> (f64, f64) {
14        let scale = rusttype::Scale::uniform(size as f32);
15        let v_metrics = self.inner.v_metrics(scale);
16        let offset = rusttype::point(0.0, v_metrics.ascent);
17
18        let pixel_height = size.ceil();
19
20        // Glyphs to draw for "RustType". Feel free to try other strings.
21        let glyphs: Vec<rusttype::PositionedGlyph> =
22            self.inner.layout(text, scale, offset).collect();
23
24        let width = glyphs
25            .iter()
26            .rev()
27            .map(|g| g.position().x as f32 + g.unpositioned().h_metrics().advance_width)
28            .next()
29            .unwrap_or(0.0)
30            .ceil() as f64;
31
32        (width, pixel_height)
33    }
34
35    pub fn render_text(
36        &self,
37        text: &str,
38        data: &mut [u32],
39        width: f64,
40        // size, color, alpha
41        config: (f64, Color, f32),
42        position: (f64, f64),
43    ) {
44        self.render_text_clipped(
45            text,
46            data,
47            width,
48            config,
49            position,
50            Rectangle::new((0.0, 0.0), (width, std::f64::MAX)),
51        );
52    }
53
54    pub fn render_text_clipped(
55        &self,
56        text: &str,
57        data: &mut [u32],
58        width: f64,
59        // size, color, alpha
60        config: (f64, Color, f32),
61        position: (f64, f64),
62        clip: Rectangle,
63    ) {
64        let scale = rusttype::Scale::uniform(config.0 as f32);
65
66        // The origin of a line of text is at the baseline (roughly where non-descending letters sit).
67        // We don't want to clip the text, so we shift it down with an offset when laying it out.
68        // v_metrics.ascent is the distance between the baseline and the highest edge of any glyph in
69        // the font. That's enough to guarantee that there's no clipping.
70        let v_metrics = self.inner.v_metrics(scale);
71        let offset = rusttype::point(0.0, v_metrics.ascent);
72
73        // Glyphs to draw for "RustType". Feel free to try other strings.
74        let glyphs: Vec<rusttype::PositionedGlyph> =
75            self.inner.layout(text, scale, offset).collect();
76
77        let pixel_width = glyphs
78            .iter()
79            .rev()
80            .map(|g| g.position().x as f32 + g.unpositioned().h_metrics().advance_width)
81            .next()
82            .unwrap_or(0.0)
83            .ceil() as i32;
84
85        let pixel_height = config.0.ceil() as i32;
86
87        for g in glyphs.iter() {
88            if let Some(bb) = g.pixel_bounding_box() {
89                g.draw(|off_x, off_y, v| {
90                    let off_x = off_x as i32 + bb.min.x;
91                    let off_y = off_y as i32 + bb.min.y;
92
93                    if off_x >= 0
94                        && off_x < pixel_width
95                        && off_y >= 0
96                        && off_y < pixel_height
97                        && position.0 + off_x as f64 >= clip.x()
98                        && position.0 + off_x as f64 <= clip.x() + clip.width()
99                        && position.1 + off_y as f64 >= clip.y()
100                        && position.1 + off_y as f64 <= clip.y() + clip.height()
101                    {
102                        // Alpha blending from orbclient
103                        let alpha = (config.2 * v * 255.0) as u32;
104                        let new = (alpha << 24) | (config.1.data & 0x00FF_FFFF);
105
106                        let index = ((position.1 as i32 + off_y) * width as i32
107                            + position.0 as i32
108                            + off_x) as usize;
109                        if index >= data.len() {
110                            return;
111                        }
112                        let old = &mut data[index];
113                        if alpha >= 255 {
114                            *old = new;
115                        } else if alpha > 0 {
116                            let n_alpha = 255 - alpha;
117                            let rb = ((n_alpha * (*old & 0x00FF_00FF))
118                                + (alpha * (new & 0x00FF_00FF)))
119                                >> 8;
120                            let ag = (n_alpha * ((*old & 0xFF00_FF00) >> 8))
121                                + (alpha * (0x0100_0000 | ((new & 0x0000_FF00) >> 8)));
122
123                            *old = (rb & 0x00FF_00FF) | (ag & 0xFF00_FF00);
124                        }
125                    }
126                });
127            }
128        }
129    }
130}