Deduplicate / reorganize / clarify code for creating LayoutLines

* max_ascent and max_descent declarations moved into loop since they are
  reset each iteration and the one spot where they are used outside the
  loop for pushing an empty line is if all items are empty (so they
  would always be 0.0 there).
* For `Align::Justified`, instead of repurposing `alignment_correction`
  variable for expanding blank spaces, there is a new
  `justification_expansion` variable. This helps clarify the code.
* Common code for processing ranges factored out section where ranges
  are iterated in opposite orders for RTL vs LTR.
* We don't need to use `take_mut` on `glyphs` since the variable is not
  used afterwards (i.e. we can just  move out of `glyphs`).
* Fix bug where `scratch.scripts` was being used for logging info
  instead of `scripts`.
This commit is contained in:
Imbris 2023-08-14 00:18:15 -04:00
parent 24ef4e2fd9
commit ae96bf26d1

View file

@ -205,11 +205,7 @@ fn shape_run(
}
}
log::trace!(
" Run {:?}: '{}'",
&scratch.scripts,
&line[start_run..end_run],
);
log::trace!(" Run {:?}: '{}'", &scripts, &line[start_run..end_run],);
let attrs = attrs_list.get_span(start_run);
@ -1154,8 +1150,6 @@ impl ShapeLine {
// Create the LayoutLines using the ranges inside visual lines
let start_x = if self.rtl { line_width } else { 0.0 };
let mut max_ascent: f32 = 0.;
let mut max_descent: f32 = 0.;
let number_of_visual_lines = visual_lines.len();
for (index, visual_line) in visual_lines.iter().enumerate() {
@ -1166,8 +1160,8 @@ impl ShapeLine {
let mut glyphs = Vec::with_capacity(1);
let mut x = start_x;
let mut y = 0.;
max_ascent = 0.;
max_descent = 0.;
let mut max_ascent: f32 = 0.;
let mut max_descent: f32 = 0.;
let alignment_correction = match (align, self.rtl) {
(Align::Left, true) => line_width - visual_line.w,
(Align::Left, false) => 0.,
@ -1175,184 +1169,84 @@ impl ShapeLine {
(Align::Right, false) => line_width - visual_line.w,
(Align::Center, _) => (line_width - visual_line.w) / 2.0,
(Align::End, _) => line_width - visual_line.w,
(Align::Justified, _) => {
// TODO: Only certain `is_whitespace` chars are typically expanded.
//
// https://www.unicode.org/reports/tr14/#Introduction
// > When expanding or compressing interword space according to common
// > typographical practice, only the spaces marked by U+0020 SPACE and U+00A0
// > NO-BREAK SPACE are subject to compression, and only spaces marked by U+0020
// > SPACE, U+00A0 NO-BREAK SPACE, and occasionally spaces marked by U+2009 THIN
// > SPACE are subject to expansion. All other space characters normally have
// > fixed width.
//
// (also some spaces aren't followed by potential linebreaks but they could
// still be expanded)
(Align::Justified, _) => 0.,
};
// Don't justify the last line in a paragraph.
if visual_line.spaces > 0 && index != number_of_visual_lines - 1 {
(line_width - visual_line.w) / visual_line.spaces as f32
} else {
0.
if self.rtl {
x -= alignment_correction;
} else {
x += alignment_correction;
}
// TODO: Only certain `is_whitespace` chars are typically expanded but this is what is
// currently used to compute `visual_line.spaces`.
//
// https://www.unicode.org/reports/tr14/#Introduction
// > When expanding or compressing interword space according to common
// > typographical practice, only the spaces marked by U+0020 SPACE and U+00A0
// > NO-BREAK SPACE are subject to compression, and only spaces marked by U+0020
// > SPACE, U+00A0 NO-BREAK SPACE, and occasionally spaces marked by U+2009 THIN
// > SPACE are subject to expansion. All other space characters normally have
// > fixed width.
//
// (also some spaces aren't followed by potential linebreaks but they could
// still be expanded)
// Amount of extra width added to each blank space within a line.
let justification_expansion = if matches!(align, Align::Justified)
&& visual_line.spaces > 0
// Don't justify the last line in a paragraph.
&& index != number_of_visual_lines - 1
{
(line_width - visual_line.w) / visual_line.spaces as f32
} else {
0.
};
let mut process_range = |range: Range<usize>| {
for &(span_index, (starting_word, starting_glyph), (ending_word, ending_glyph)) in
visual_line.ranges[range.clone()].iter()
{
let span = &self.spans[span_index];
// If ending_glyph is not 0 we need to include glyphs from the ending_word
for i in starting_word..ending_word + usize::from(ending_glyph != 0) {
let word = &span.words[i];
let included_glyphs = match (i == starting_word, i == ending_word) {
(false, false) => &word.glyphs[..],
(true, false) => &word.glyphs[starting_glyph..],
(false, true) => &word.glyphs[..ending_glyph],
(true, true) => &word.glyphs[starting_glyph..ending_glyph],
};
for glyph in included_glyphs {
let x_advance = font_size * glyph.x_advance
+ if word.blank {
justification_expansion
} else {
0.0
};
let y_advance = font_size * glyph.y_advance;
glyphs.push(glyph.layout(font_size, x, y, x_advance, span.level));
if self.rtl {
x -= x_advance;
} else {
x += x_advance;
}
y += y_advance;
max_ascent = max_ascent.max(glyph.ascent);
max_descent = max_descent.max(glyph.descent);
}
}
}
};
if self.rtl {
if align != Align::Justified {
x -= alignment_correction;
}
for range in new_order.iter().rev() {
for (
span_index,
(starting_word, starting_glyph),
(ending_word, ending_glyph),
) in visual_line.ranges[range.clone()].iter()
{
let span = &self.spans[*span_index];
if starting_word == ending_word {
let word_blank = span.words[*starting_word].blank;
for glyph in span.words[*starting_word].glyphs
[*starting_glyph..*ending_glyph]
.iter()
{
let x_advance = font_size * glyph.x_advance;
let y_advance = font_size * glyph.y_advance;
x -= x_advance;
if word_blank && align == Align::Justified {
x -= alignment_correction;
glyphs.push(glyph.layout(
font_size,
x,
y,
x_advance + alignment_correction,
span.level,
));
} else {
glyphs
.push(glyph.layout(font_size, x, y, x_advance, span.level));
}
y += y_advance;
max_ascent = max_ascent.max(glyph.ascent);
max_descent = max_descent.max(glyph.descent);
}
} else {
for i in *starting_word..*ending_word + 1 {
if let Some(word) = span.words.get(i) {
let (g1, g2) = if i == *starting_word {
(*starting_glyph, word.glyphs.len())
} else if i == *ending_word {
(0, *ending_glyph)
} else {
(0, word.glyphs.len())
};
let word_blank = word.blank;
for glyph in &word.glyphs[g1..g2] {
let x_advance = font_size * glyph.x_advance;
let y_advance = font_size * glyph.y_advance;
x -= x_advance;
if word_blank && align == Align::Justified {
x -= alignment_correction;
glyphs.push(glyph.layout(
font_size,
x,
y,
x_advance + alignment_correction,
span.level,
));
} else {
glyphs
.push(glyph.layout(
font_size, x, y, x_advance, span.level,
));
}
y += y_advance;
max_ascent = max_ascent.max(glyph.ascent);
max_descent = max_descent.max(glyph.descent);
}
}
}
}
}
if self.rtl {
for range in new_order.into_iter().rev() {
process_range(range);
}
} else {
/* LTR */
if align != Align::Justified {
x += alignment_correction;
}
for range in new_order {
for (
span_index,
(starting_word, starting_glyph),
(ending_word, ending_glyph),
) in visual_line.ranges[range.clone()].iter()
{
let span = &self.spans[*span_index];
if starting_word == ending_word {
let word_blank = span.words[*starting_word].blank;
for glyph in span.words[*starting_word].glyphs
[*starting_glyph..*ending_glyph]
.iter()
{
let x_advance = font_size * glyph.x_advance;
let y_advance = font_size * glyph.y_advance;
if word_blank && align == Align::Justified {
glyphs.push(glyph.layout(
font_size,
x,
y,
x_advance + alignment_correction,
span.level,
));
x += alignment_correction;
} else {
glyphs
.push(glyph.layout(font_size, x, y, x_advance, span.level));
}
x += x_advance;
y += y_advance;
max_ascent = max_ascent.max(glyph.ascent);
max_descent = max_descent.max(glyph.descent);
}
} else {
for i in *starting_word..*ending_word + 1 {
if let Some(word) = span.words.get(i) {
let (g1, g2) = if i == *starting_word {
(*starting_glyph, word.glyphs.len())
} else if i == *ending_word {
(0, *ending_glyph)
} else {
(0, word.glyphs.len())
};
let word_blank = word.blank;
for glyph in &word.glyphs[g1..g2] {
let x_advance = font_size * glyph.x_advance;
let y_advance = font_size * glyph.y_advance;
if word_blank && align == Align::Justified {
glyphs.push(glyph.layout(
font_size,
x,
y,
x_advance + alignment_correction,
span.level,
));
x += alignment_correction;
} else {
glyphs
.push(glyph.layout(
font_size, x, y, x_advance, span.level,
));
}
x += x_advance;
y += y_advance;
max_ascent = max_ascent.max(glyph.ascent);
max_descent = max_descent.max(glyph.descent);
}
}
}
}
}
process_range(range);
}
}
@ -1368,7 +1262,7 @@ impl ShapeLine {
},
max_ascent: max_ascent * font_size,
max_descent: max_descent * font_size,
glyphs: mem::take(&mut glyphs),
glyphs,
});
push_line = false;
}
@ -1376,8 +1270,8 @@ impl ShapeLine {
if push_line {
layout_lines.push(LayoutLine {
w: 0.0,
max_ascent: max_ascent * font_size,
max_descent: max_descent * font_size,
max_ascent: 0.0,
max_descent: 0.0,
glyphs: Default::default(),
});
}