kobo_core/rendering/text_render/
blit.rs1use 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}