use rustybuzz::UnicodeBuffer;
use super::detect_script;
use super::fonts::{fallback_for_char, font_for_script, glyph_cache};
pub fn blit_rgb565(
buf: &mut [u8],
buf_stride: usize,
text: &str,
px_size: f32,
ox: usize,
oy: usize,
max_w: usize,
max_h: usize,
) {
blit_rgb565_color(buf, buf_stride, text, px_size, ox, oy, 0x0000, max_w, max_h);
}
pub fn blit_rgb565_color(
buf: &mut [u8],
buf_stride: usize,
text: &str,
px_size: f32,
ox: usize,
oy: usize,
color: u16,
max_w: usize,
max_h: usize,
) {
let cr = ((color >> 11) & 0x1f) as u32;
let cg = ((color >> 5) & 0x3f) as u32;
let cb = (color & 0x1f) as u32;
let script = detect_script(text);
let fd = font_for_script(script);
let scale = px_size / fd.face.units_per_em() as f32;
let lm = fd.body.horizontal_line_metrics(px_size);
let baseline = match lm {
Some(m) => m.ascent as i32,
None => px_size as i32,
};
let mut ub = UnicodeBuffer::new();
ub.push_str(text);
let dir = if script.is_rtl() {
rustybuzz::Direction::RightToLeft
} else {
rustybuzz::Direction::LeftToRight
};
ub.set_direction(dir);
let gb = rustybuzz::shape(&fd.face, &[], ub);
let glyphs = gb.glyph_infos();
let positions = gb.glyph_positions();
let mut cursor_x = 0.0;
let gc = glyph_cache();
for (info, pos) in glyphs.iter().zip(positions) {
let advance = pos.x_advance as f32 * scale;
let gid = info.glyph_id as u16;
let px_key = (px_size * 100.0) as u32;
let (rfont, rgid) = if gid != 0 {
(fd, gid)
} else {
let ch = text
.get(info.cluster as usize..)
.and_then(|s| s.chars().next());
match ch.and_then(fallback_for_char) {
Some(fb) => fb,
None => (fd, gid),
}
};
let key = (rfont.id, rgid, px_key);
let (metrics, bitmap) = if let Ok(cache) = gc.lock() {
cache.get(&key).cloned().unwrap_or_else(|| {
drop(cache);
let entry = rfont.body.rasterize_indexed(rgid, px_size);
let _ = gc.lock().map(|mut c| {
c.insert(key, entry.clone());
});
entry
})
} else {
rfont.body.rasterize_indexed(rgid, px_size)
};
let gw = metrics.width;
let gh = metrics.height;
if gw > 0 && gh > 0 {
let pen_x = cursor_x + pos.x_offset as f32 * scale;
let gx0 = ox as i32 + pen_x.round() as i32 + metrics.xmin;
let gy0 = oy as i32 + baseline
- (pos.y_offset as f32 * scale).round() as i32
- metrics.ymin
- gh as i32;
for ry in 0..gh {
for rx in 0..gw {
let cov = bitmap[ry * gw + rx] as u32;
if cov == 0 {
continue;
}
let px = gx0 + rx as i32;
let py = gy0 + ry as i32;
if px < 0 || (px as usize) >= max_w || py < 0 || (py as usize) >= max_h {
continue;
}
let off = (py as usize * buf_stride + px as usize) * 2;
if off + 2 > buf.len() {
continue;
}
let old = (buf[off] as u16) | ((buf[off + 1] as u16) << 8);
let inv = 255 - cov;
let nr = (cr * cov + (((old >> 11) & 0x1f) as u32) * inv) / 255;
let ng = (cg * cov + (((old >> 5) & 0x3f) as u32) * inv) / 255;
let nb = (cb * cov + ((old & 0x1f) as u32) * inv) / 255;
let v = ((nr << 11) | (ng << 5) | nb) as u16;
buf[off] = (v & 0xff) as u8;
buf[off + 1] = (v >> 8) as u8;
}
}
}
cursor_x += advance;
}
}