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:
parent
24ef4e2fd9
commit
ae96bf26d1
1 changed files with 75 additions and 181 deletions
214
src/shape.rs
214
src/shape.rs
|
|
@ -205,11 +205,7 @@ fn shape_run(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log::trace!(
|
log::trace!(" Run {:?}: '{}'", &scripts, &line[start_run..end_run],);
|
||||||
" Run {:?}: '{}'",
|
|
||||||
&scratch.scripts,
|
|
||||||
&line[start_run..end_run],
|
|
||||||
);
|
|
||||||
|
|
||||||
let attrs = attrs_list.get_span(start_run);
|
let attrs = attrs_list.get_span(start_run);
|
||||||
|
|
||||||
|
|
@ -1154,8 +1150,6 @@ impl ShapeLine {
|
||||||
|
|
||||||
// Create the LayoutLines using the ranges inside visual lines
|
// Create the LayoutLines using the ranges inside visual lines
|
||||||
let start_x = if self.rtl { line_width } else { 0.0 };
|
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();
|
let number_of_visual_lines = visual_lines.len();
|
||||||
for (index, visual_line) in visual_lines.iter().enumerate() {
|
for (index, visual_line) in visual_lines.iter().enumerate() {
|
||||||
|
|
@ -1166,8 +1160,8 @@ impl ShapeLine {
|
||||||
let mut glyphs = Vec::with_capacity(1);
|
let mut glyphs = Vec::with_capacity(1);
|
||||||
let mut x = start_x;
|
let mut x = start_x;
|
||||||
let mut y = 0.;
|
let mut y = 0.;
|
||||||
max_ascent = 0.;
|
let mut max_ascent: f32 = 0.;
|
||||||
max_descent = 0.;
|
let mut max_descent: f32 = 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.,
|
||||||
|
|
@ -1175,8 +1169,17 @@ impl ShapeLine {
|
||||||
(Align::Right, false) => line_width - visual_line.w,
|
(Align::Right, false) => line_width - visual_line.w,
|
||||||
(Align::Center, _) => (line_width - visual_line.w) / 2.0,
|
(Align::Center, _) => (line_width - visual_line.w) / 2.0,
|
||||||
(Align::End, _) => line_width - visual_line.w,
|
(Align::End, _) => line_width - visual_line.w,
|
||||||
(Align::Justified, _) => {
|
(Align::Justified, _) => 0.,
|
||||||
// TODO: Only certain `is_whitespace` chars are typically expanded.
|
};
|
||||||
|
|
||||||
|
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
|
// https://www.unicode.org/reports/tr14/#Introduction
|
||||||
// > When expanding or compressing interword space according to common
|
// > When expanding or compressing interword space according to common
|
||||||
|
|
@ -1189,82 +1192,44 @@ impl ShapeLine {
|
||||||
// (also some spaces aren't followed by potential linebreaks but they could
|
// (also some spaces aren't followed by potential linebreaks but they could
|
||||||
// still be expanded)
|
// 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.
|
// Don't justify the last line in a paragraph.
|
||||||
if visual_line.spaces > 0 && index != number_of_visual_lines - 1 {
|
&& index != number_of_visual_lines - 1
|
||||||
|
{
|
||||||
(line_width - visual_line.w) / visual_line.spaces as f32
|
(line_width - visual_line.w) / visual_line.spaces as f32
|
||||||
} else {
|
} else {
|
||||||
0.
|
0.
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
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;
|
let mut process_range = |range: Range<usize>| {
|
||||||
for glyph in &word.glyphs[g1..g2] {
|
for &(span_index, (starting_word, starting_glyph), (ending_word, ending_glyph)) in
|
||||||
let x_advance = font_size * glyph.x_advance;
|
visual_line.ranges[range.clone()].iter()
|
||||||
let y_advance = font_size * glyph.y_advance;
|
{
|
||||||
x -= x_advance;
|
let span = &self.spans[span_index];
|
||||||
if word_blank && align == Align::Justified {
|
// If ending_glyph is not 0 we need to include glyphs from the ending_word
|
||||||
x -= alignment_correction;
|
for i in starting_word..ending_word + usize::from(ending_glyph != 0) {
|
||||||
glyphs.push(glyph.layout(
|
let word = &span.words[i];
|
||||||
font_size,
|
let included_glyphs = match (i == starting_word, i == ending_word) {
|
||||||
x,
|
(false, false) => &word.glyphs[..],
|
||||||
y,
|
(true, false) => &word.glyphs[starting_glyph..],
|
||||||
x_advance + alignment_correction,
|
(false, true) => &word.glyphs[..ending_glyph],
|
||||||
span.level,
|
(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 {
|
} else {
|
||||||
glyphs
|
0.0
|
||||||
.push(glyph.layout(
|
};
|
||||||
font_size, x, y, x_advance, span.level,
|
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;
|
y += y_advance;
|
||||||
max_ascent = max_ascent.max(glyph.ascent);
|
max_ascent = max_ascent.max(glyph.ascent);
|
||||||
|
|
@ -1272,87 +1237,16 @@ impl ShapeLine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
if self.rtl {
|
||||||
|
for range in new_order.into_iter().rev() {
|
||||||
|
process_range(range);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* LTR */
|
/* LTR */
|
||||||
if align != Align::Justified {
|
|
||||||
x += alignment_correction;
|
|
||||||
}
|
|
||||||
for range in new_order {
|
for range in new_order {
|
||||||
for (
|
process_range(range);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1368,7 +1262,7 @@ impl ShapeLine {
|
||||||
},
|
},
|
||||||
max_ascent: max_ascent * font_size,
|
max_ascent: max_ascent * font_size,
|
||||||
max_descent: max_descent * font_size,
|
max_descent: max_descent * font_size,
|
||||||
glyphs: mem::take(&mut glyphs),
|
glyphs,
|
||||||
});
|
});
|
||||||
push_line = false;
|
push_line = false;
|
||||||
}
|
}
|
||||||
|
|
@ -1376,8 +1270,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_ascent: 0.0,
|
||||||
max_descent: max_descent * font_size,
|
max_descent: 0.0,
|
||||||
glyphs: Default::default(),
|
glyphs: Default::default(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue