kobo_core/rendering/text_render/
blit.rs1use rustybuzz::UnicodeBuffer;
4
5use super::detect_script;
6use super::fonts::{fallback_for_char, font_for, glyph_cache, TextStyle};
7
8pub fn blit_rgb565(
9 buf: &mut [u8],
10 buf_stride: usize,
11 text: &str,
12 px_size: f32,
13 ox: usize,
14 oy: usize,
15 max_w: usize,
16 max_h: usize,
17) {
18 blit_rgb565_color(buf, buf_stride, text, px_size, ox, oy, 0x0000, max_w, max_h);
19}
20
21const OBLIQUE_SHEAR: f32 = 0.21; fn bold_smear(px_size: f32) -> usize {
36 ((px_size / 22.0).round() as usize).max(1)
37}
38
39pub fn blit_rgb565_color(
40 buf: &mut [u8],
41 buf_stride: usize,
42 text: &str,
43 px_size: f32,
44 ox: usize,
45 oy: usize,
46 color: u16,
47 max_w: usize,
48 max_h: usize,
49) {
50 blit_rgb565_styled(
51 buf,
52 buf_stride,
53 text,
54 px_size,
55 ox,
56 oy,
57 color,
58 TextStyle::PLAIN,
59 max_w,
60 max_h,
61 );
62}
63
64#[allow(clippy::too_many_arguments)]
66pub fn blit_rgb565_styled(
67 buf: &mut [u8],
68 buf_stride: usize,
69 text: &str,
70 px_size: f32,
71 ox: usize,
72 oy: usize,
73 color: u16,
74 style: TextStyle,
75 max_w: usize,
76 max_h: usize,
77) {
78 let cr = ((color >> 11) & 0x1f) as u32;
79 let cg = ((color >> 5) & 0x3f) as u32;
80 let cb = (color & 0x1f) as u32;
81 let script = detect_script(text);
82 let fd = font_for(script, style);
83 let smear = if style.bold { bold_smear(px_size) } else { 0 };
84 let scale = px_size / fd.face.units_per_em() as f32;
85 let lm = fd.body.horizontal_line_metrics(px_size);
86 let baseline = match lm {
87 Some(m) => m.ascent as i32,
88 None => px_size as i32,
89 };
90
91 let mut ub = UnicodeBuffer::new();
92 ub.push_str(text);
93 let dir = if script.is_rtl() {
94 rustybuzz::Direction::RightToLeft
95 } else {
96 rustybuzz::Direction::LeftToRight
97 };
98 ub.set_direction(dir);
99
100 let gb = rustybuzz::shape(&fd.face, &[], ub);
101 let glyphs = gb.glyph_infos();
102 let positions = gb.glyph_positions();
103
104 let mut cursor_x = 0.0;
105 let gc = glyph_cache();
106 for (info, pos) in glyphs.iter().zip(positions) {
107 let advance = pos.x_advance as f32 * scale;
108 let gid = info.glyph_id as u16;
109 let px_key = (px_size * 100.0) as u32;
110 let (rfont, rgid) = if gid != 0 {
111 (fd, gid)
112 } else {
113 let ch = text
114 .get(info.cluster as usize..)
115 .and_then(|s| s.chars().next());
116 match ch.and_then(fallback_for_char) {
117 Some(fb) => fb,
118 None => (fd, gid),
119 }
120 };
121 let key = (rfont.id, rgid, px_key);
122 let (metrics, bitmap) = if let Ok(cache) = gc.lock() {
123 cache.get(&key).cloned().unwrap_or_else(|| {
124 drop(cache);
125 let entry = rfont.body.rasterize_indexed(rgid, px_size);
126 let _ = gc.lock().map(|mut c| {
128 c.insert(key, entry.clone());
129 });
130 entry
131 })
132 } else {
133 rfont.body.rasterize_indexed(rgid, px_size)
134 };
135 let gw = metrics.width;
136 let gh = metrics.height;
137 if gw > 0 && gh > 0 {
138 let pen_x = cursor_x + pos.x_offset as f32 * scale;
139 let gx0 = ox as i32 + pen_x.round() as i32 + metrics.xmin;
140 let gy0 = oy as i32 + baseline
141 - (pos.y_offset as f32 * scale).round() as i32
142 - metrics.ymin
143 - gh as i32;
144 for ry in 0..gh {
145 let shear_dx = if style.italic {
149 let above_baseline = (gh as i32 - ry as i32) + metrics.ymin;
150 (above_baseline as f32 * OBLIQUE_SHEAR).round() as i32
151 } else {
152 0
153 };
154 for rx in 0..gw + smear {
155 let lo = rx.saturating_sub(smear);
159 let hi = rx.min(gw - 1);
160 let cov = (lo..=hi)
161 .map(|sx| bitmap[ry * gw + sx] as u32)
162 .max()
163 .unwrap_or(0);
164 if cov == 0 {
165 continue;
166 }
167 let px = gx0 + rx as i32 + shear_dx;
168 let py = gy0 + ry as i32;
169 if px < 0 || (px as usize) >= max_w || py < 0 || (py as usize) >= max_h {
170 continue;
171 }
172 let off = (py as usize * buf_stride + px as usize) * 2;
173 if off + 2 > buf.len() {
174 continue;
175 }
176 let old = (buf[off] as u16) | ((buf[off + 1] as u16) << 8);
177 let inv = 255 - cov;
178 let nr = (cr * cov + (((old >> 11) & 0x1f) as u32) * inv) / 255;
179 let ng = (cg * cov + (((old >> 5) & 0x3f) as u32) * inv) / 255;
180 let nb = (cb * cov + ((old & 0x1f) as u32) * inv) / 255;
181 let v = ((nr << 11) | (ng << 5) | nb) as u16;
182 buf[off] = (v & 0xff) as u8;
183 buf[off + 1] = (v >> 8) as u8;
184 }
185 }
186 }
187 cursor_x += advance;
188 }
189}