Align glyphs in a LayoutRun to baseline

This commit is contained in:
Héctor Ramón Jiménez 2023-06-16 01:47:35 +02:00
parent 3640b5e1ef
commit c2bef6a345
No known key found for this signature in database
GPG key ID: 140CC052C94F138E
3 changed files with 37 additions and 11 deletions

View file

@ -119,9 +119,9 @@ pub struct LayoutRun<'a> {
pub rtl: bool, pub rtl: bool,
/// The array of layout glyphs to draw /// The array of layout glyphs to draw
pub glyphs: &'a [LayoutGlyph], pub glyphs: &'a [LayoutGlyph],
/// Y offset of line /// Y offset to baseline of line
pub line_y: f32, pub line_y: f32,
/// width of line /// Width of line
pub line_w: f32, pub line_w: f32,
} }
@ -187,7 +187,6 @@ pub struct LayoutRunIter<'b> {
line_i: usize, line_i: usize,
layout_i: usize, layout_i: usize,
remaining_len: usize, remaining_len: usize,
line_y: f32,
total_layout: i32, total_layout: i32,
} }
@ -222,7 +221,6 @@ impl<'b> LayoutRunIter<'b> {
line_i: 0, line_i: 0,
layout_i: 0, layout_i: 0,
remaining_len: bottom_cropped_layout_lines, remaining_len: bottom_cropped_layout_lines,
line_y: buffer.metrics.y_offset(),
total_layout: 0, total_layout: 0,
} }
} }
@ -248,11 +246,15 @@ impl<'b> Iterator for LayoutRunIter<'b> {
continue; continue;
} }
self.line_y += self.buffer.metrics.line_height; let line_y = self.line_i as f32 * self.buffer.metrics.line_height;
if self.line_y - self.buffer.metrics.y_offset() > self.buffer.height {
if line_y > self.buffer.height {
return None; return None;
} }
let glyph_height = layout_line.max_ascent + layout_line.max_descent;
let centering_offset = (self.buffer.metrics.line_height - glyph_height) / 2.0;
return self.remaining_len.checked_sub(1).map(|num| { return self.remaining_len.checked_sub(1).map(|num| {
self.remaining_len = num; self.remaining_len = num;
LayoutRun { LayoutRun {
@ -260,7 +262,7 @@ impl<'b> Iterator for LayoutRunIter<'b> {
text: line.text(), text: line.text(),
rtl: shape.rtl, rtl: shape.rtl,
glyphs: &layout_line.glyphs, glyphs: &layout_line.glyphs,
line_y: self.line_y, line_y: line_y + centering_offset + layout_line.max_ascent,
line_w: layout_line.w, line_w: layout_line.w,
} }
}); });
@ -298,10 +300,6 @@ impl Metrics {
line_height: self.line_height * scale, line_height: self.line_height * scale,
} }
} }
fn y_offset(&self) -> f32 {
self.font_size - self.line_height
}
} }
impl fmt::Display for Metrics { impl fmt::Display for Metrics {

View file

@ -56,6 +56,10 @@ pub struct LayoutGlyph {
pub struct LayoutLine { pub struct LayoutLine {
/// Width of the line /// Width of the line
pub w: f32, pub w: f32,
/// Maximum ascent of the glyphs in line
pub max_ascent: f32,
/// Maximum descent of the glyphs in line
pub max_descent: f32,
/// Glyphs in line /// Glyphs in line
pub glyphs: Vec<LayoutGlyph>, pub glyphs: Vec<LayoutGlyph>,
} }

View file

@ -62,6 +62,7 @@ fn shape_fallback(
let run = &line[start_run..end_run]; let run = &line[start_run..end_run];
let font_scale = font.rustybuzz().units_per_em() as f32; let font_scale = font.rustybuzz().units_per_em() as f32;
let metrics = font.as_swash().metrics(&[]);
let mut buffer = rustybuzz::UnicodeBuffer::new(); let mut buffer = rustybuzz::UnicodeBuffer::new();
buffer.set_direction(if span_rtl { buffer.set_direction(if span_rtl {
@ -101,6 +102,8 @@ fn shape_fallback(
y_advance, y_advance,
x_offset, x_offset,
y_offset, y_offset,
ascent: metrics.ascent / f32::from(metrics.units_per_em),
descent: metrics.descent / f32::from(metrics.units_per_em),
font_id: font.id(), font_id: font.id(),
glyph_id: info.glyph_id.try_into().expect("failed to cast glyph ID"), glyph_id: info.glyph_id.try_into().expect("failed to cast glyph ID"),
//TODO: color should not be related to shaping //TODO: color should not be related to shaping
@ -272,6 +275,7 @@ fn shape_skip(
let font = font.as_swash(); let font = font.as_swash();
let charmap = font.charmap(); let charmap = font.charmap();
let metrics = font.metrics(&[]);
let glyph_metrics = font.glyph_metrics(&[]).scale(1.0); let glyph_metrics = font.glyph_metrics(&[]).scale(1.0);
line[start_run..end_run] line[start_run..end_run]
@ -288,6 +292,8 @@ fn shape_skip(
y_advance: 0.0, y_advance: 0.0,
x_offset: 0.0, x_offset: 0.0,
y_offset: 0.0, y_offset: 0.0,
ascent: metrics.ascent / f32::from(metrics.units_per_em),
descent: metrics.descent / f32::from(metrics.units_per_em),
font_id, font_id,
glyph_id, glyph_id,
color_opt: attrs.color_opt, color_opt: attrs.color_opt,
@ -305,6 +311,8 @@ pub struct ShapeGlyph {
pub y_advance: f32, pub y_advance: f32,
pub x_offset: f32, pub x_offset: f32,
pub y_offset: f32, pub y_offset: f32,
pub ascent: f32,
pub descent: f32,
pub font_id: fontdb::ID, pub font_id: fontdb::ID,
pub glyph_id: u16, pub glyph_id: u16,
pub color_opt: Option<Color>, pub color_opt: Option<Color>,
@ -749,6 +757,8 @@ impl ShapeLine {
let start_x = if self.rtl { line_width } else { 0.0 }; let start_x = if self.rtl { line_width } else { 0.0 };
let mut x; let mut x;
let mut y; let mut y;
let mut max_ascent: f32 = 0.;
let mut max_descent: f32 = 0.;
// This would keep the maximum number of spans that would fit on a visual line // This would keep the maximum number of spans that would fit on a visual line
// If one span is too large, this variable will hold the range of words inside that span // If one span is too large, this variable will hold the range of words inside that span
@ -987,6 +997,8 @@ impl ShapeLine {
let mut glyphs = Vec::with_capacity(1); let mut glyphs = Vec::with_capacity(1);
x = start_x; x = start_x;
y = 0.; y = 0.;
max_ascent = 0.;
max_descent = 0.;
let alignment_correction = match (align, self.rtl) { let alignment_correction = match (align, self.rtl) {
(Align::Left, true) => line_width - visual_line.w, (Align::Left, true) => line_width - visual_line.w,
(Align::Left, false) => 0., (Align::Left, false) => 0.,
@ -1037,6 +1049,8 @@ impl ShapeLine {
.push(glyph.layout(font_size, x, y, x_advance, span.level)); .push(glyph.layout(font_size, x, y, x_advance, span.level));
} }
y += y_advance; y += y_advance;
max_ascent = max_ascent.max(glyph.ascent);
max_descent = max_descent.max(glyph.descent);
} }
} else { } else {
for i in *starting_word..*ending_word + 1 { for i in *starting_word..*ending_word + 1 {
@ -1070,6 +1084,8 @@ impl ShapeLine {
)); ));
} }
y += y_advance; y += y_advance;
max_ascent = max_ascent.max(glyph.ascent);
max_descent = max_descent.max(glyph.descent);
} }
} }
} }
@ -1112,6 +1128,8 @@ impl ShapeLine {
} }
x += x_advance; x += x_advance;
y += y_advance; y += y_advance;
max_ascent = max_ascent.max(glyph.ascent);
max_descent = max_descent.max(glyph.descent);
} }
} else { } else {
for i in *starting_word..*ending_word + 1 { for i in *starting_word..*ending_word + 1 {
@ -1145,6 +1163,8 @@ impl ShapeLine {
} }
x += x_advance; x += x_advance;
y += y_advance; y += y_advance;
max_ascent = max_ascent.max(glyph.ascent);
max_descent = max_descent.max(glyph.descent);
} }
} }
} }
@ -1156,6 +1176,8 @@ impl ShapeLine {
mem::swap(&mut glyphs, &mut glyphs_swap); mem::swap(&mut glyphs, &mut glyphs_swap);
layout_lines.push(LayoutLine { layout_lines.push(LayoutLine {
w: if self.rtl { start_x - x } else { x }, w: if self.rtl { start_x - x } else { x },
max_ascent: max_ascent * font_size,
max_descent: max_descent * font_size,
glyphs: glyphs_swap, glyphs: glyphs_swap,
}); });
push_line = false; push_line = false;
@ -1164,6 +1186,8 @@ impl ShapeLine {
if push_line { if push_line {
layout_lines.push(LayoutLine { layout_lines.push(LayoutLine {
w: 0.0, w: 0.0,
max_ascent: max_ascent * font_size,
max_descent: max_descent * font_size,
glyphs: Default::default(), glyphs: Default::default(),
}); });
} }