Skip to main content

kobo_core/rendering/text_render/
blit.rs

1use rustybuzz::UnicodeBuffer;
2
3use super::detect_script;
4use super::fonts::{fallback_for_char, font_for_script, glyph_cache};
5
6pub fn blit_rgb565(
7    buf: &mut [u8],
8    buf_stride: usize,
9    text: &str,
10    px_size: f32,
11    ox: usize,
12    oy: usize,
13    max_w: usize,
14    max_h: usize,
15) {
16    blit_rgb565_color(buf, buf_stride, text, px_size, ox, oy, 0x0000, max_w, max_h);
17}
18
19pub fn blit_rgb565_color(
20    buf: &mut [u8],
21    buf_stride: usize,
22    text: &str,
23    px_size: f32,
24    ox: usize,
25    oy: usize,
26    color: u16,
27    max_w: usize,
28    max_h: usize,
29) {
30    let cr = ((color >> 11) & 0x1f) as u32;
31    let cg = ((color >> 5) & 0x3f) as u32;
32    let cb = (color & 0x1f) as u32;
33    let script = detect_script(text);
34    let fd = font_for_script(script);
35    let scale = px_size / fd.face.units_per_em() as f32;
36    let lm = fd.body.horizontal_line_metrics(px_size);
37    let baseline = match lm {
38        Some(m) => m.ascent as i32,
39        None => px_size as i32,
40    };
41
42    let mut ub = UnicodeBuffer::new();
43    ub.push_str(text);
44    let dir = if script.is_rtl() {
45        rustybuzz::Direction::RightToLeft
46    } else {
47        rustybuzz::Direction::LeftToRight
48    };
49    ub.set_direction(dir);
50
51    let gb = rustybuzz::shape(&fd.face, &[], ub);
52    let glyphs = gb.glyph_infos();
53    let positions = gb.glyph_positions();
54
55    let mut cursor_x = 0.0;
56    let gc = glyph_cache();
57    for (info, pos) in glyphs.iter().zip(positions) {
58        let advance = pos.x_advance as f32 * scale;
59        let gid = info.glyph_id as u16;
60        let px_key = (px_size * 100.0) as u32;
61        let (rfont, rgid) = if gid != 0 {
62            (fd, gid)
63        } else {
64            let ch = text
65                .get(info.cluster as usize..)
66                .and_then(|s| s.chars().next());
67            match ch.and_then(fallback_for_char) {
68                Some(fb) => fb,
69                None => (fd, gid),
70            }
71        };
72        let key = (rfont.id, rgid, px_key);
73        let (metrics, bitmap) = if let Ok(cache) = gc.lock() {
74            cache.get(&key).cloned().unwrap_or_else(|| {
75                drop(cache);
76                let entry = rfont.body.rasterize_indexed(rgid, px_size);
77                let _ = gc.lock().map(|mut c| {
78                    c.insert(key, entry.clone());
79                });
80                entry
81            })
82        } else {
83            rfont.body.rasterize_indexed(rgid, px_size)
84        };
85        let gw = metrics.width;
86        let gh = metrics.height;
87        if gw > 0 && gh > 0 {
88            let pen_x = cursor_x + pos.x_offset as f32 * scale;
89            let gx0 = ox as i32 + pen_x.round() as i32 + metrics.xmin;
90            let gy0 = oy as i32 + baseline
91                - (pos.y_offset as f32 * scale).round() as i32
92                - metrics.ymin
93                - gh as i32;
94            for ry in 0..gh {
95                for rx in 0..gw {
96                    let cov = bitmap[ry * gw + rx] as u32;
97                    if cov == 0 {
98                        continue;
99                    }
100                    let px = gx0 + rx as i32;
101                    let py = gy0 + ry as i32;
102                    if px < 0 || (px as usize) >= max_w || py < 0 || (py as usize) >= max_h {
103                        continue;
104                    }
105                    let off = (py as usize * buf_stride + px as usize) * 2;
106                    if off + 2 > buf.len() {
107                        continue;
108                    }
109                    let old = (buf[off] as u16) | ((buf[off + 1] as u16) << 8);
110                    let inv = 255 - cov;
111                    let nr = (cr * cov + (((old >> 11) & 0x1f) as u32) * inv) / 255;
112                    let ng = (cg * cov + (((old >> 5) & 0x3f) as u32) * inv) / 255;
113                    let nb = (cb * cov + ((old & 0x1f) as u32) * inv) / 255;
114                    let v = ((nr << 11) | (ng << 5) | nb) as u16;
115                    buf[off] = (v & 0xff) as u8;
116                    buf[off + 1] = (v >> 8) as u8;
117                }
118            }
119        }
120        cursor_x += advance;
121    }
122}