use tiny_skia::{FillRule, FilterQuality, Paint, Pixmap, PixmapPaint, Stroke, Transform};
use zenith_core::FontProvider;
use zenith_scene::SceneCommand;
use super::super::commands::DrawCtx;
use super::super::paths::{GlyphOutlinePen, clip_mask};
pub(in crate::tiny_skia) fn draw_glyph_run(
target: &mut Pixmap,
ctx: DrawCtx,
cmd: &SceneCommand,
fonts: &dyn FontProvider,
) {
let SceneCommand::DrawGlyphRun {
x,
y,
font_id,
font_size,
color,
stroke_color,
stroke_width,
link: _,
selectable: _,
glyphs,
} = cmd
else {
return;
};
let font_data = match fonts.by_id(font_id) {
Some(fd) => fd,
None => {
return;
}
};
let face = match ttf_parser::Face::parse(&font_data.bytes, font_data.index) {
Ok(f) => f,
Err(_) => return, };
let units_per_em = face.units_per_em();
if units_per_em == 0 {
return; }
let scale = font_size / f32::from(units_per_em);
let mut paint = Paint::default();
paint.set_color_rgba8(color.r, color.g, color.b, color.a);
paint.anti_alias = true;
let effective_clip = ctx.effective_clip;
let mask = match clip_mask(effective_clip, ctx.width, ctx.height) {
None => return, Some(m) => m,
};
for glyph in glyphs {
let origin_x = *x as f32 + glyph.dx;
let baseline_y = *y as f32 + glyph.dy;
if let Some(img) =
face.glyph_raster_image(ttf_parser::GlyphId(glyph.glyph_id), *font_size as u16)
&& img.format == ttf_parser::RasterImageFormat::PNG
&& img.pixels_per_em > 0
&& let Ok(decoded) = Pixmap::decode_png(img.data)
{
let s = *font_size / f32::from(img.pixels_per_em);
let draw_x = origin_x + f32::from(img.x) * s;
let draw_y = baseline_y - (f32::from(img.y) + f32::from(img.height)) * s;
let emoji_fit = Transform::from_row(s, 0.0, 0.0, s, draw_x, draw_y);
let emoji_ts = ctx.current_ts.pre_concat(emoji_fit);
let emoji_paint = PixmapPaint {
quality: FilterQuality::Bilinear,
..Default::default()
};
target.draw_pixmap(
0,
0,
decoded.as_ref(),
&emoji_paint,
emoji_ts,
mask.as_ref(),
);
continue;
}
let mut pen = GlyphOutlinePen::new(origin_x, baseline_y, scale);
if face
.outline_glyph(ttf_parser::GlyphId(glyph.glyph_id), &mut pen)
.is_none()
{
continue;
}
let path = match pen.builder.finish() {
Some(p) => p,
None => continue,
};
target.fill_path(
&path,
&paint,
FillRule::Winding,
ctx.current_ts,
mask.as_ref(),
);
if let (Some(sc), Some(sw)) = (stroke_color, stroke_width)
&& *sw > 0.0
{
let mut spaint = Paint::default();
spaint.set_color_rgba8(sc.r, sc.g, sc.b, sc.a);
spaint.anti_alias = true;
let sstroke = Stroke {
width: *sw as f32,
..Default::default()
};
target.stroke_path(&path, &spaint, &sstroke, ctx.current_ts, mask.as_ref());
}
}
}