Repository navigation
Conversation
tomcur
commented
Oct 25, 2026
Comment on lines
+958
to
+966
| if whitespace_hangs(last.whitespace, &data.styles[last.style_index as usize]) { | ||
| // As a shortcut, we don't check if reshaping hanging whitespace causes overflow. | ||
| // The hanging whitespace itself doesn't need to fit, and reshaping would only | ||
| // change how far it hangs, except in the rare instance where a font's lookups reach | ||
| // across the trailing whitespace. Doing this fully correctly would entail walking | ||
| // the reshaped end's clusters to figure out if any part of the change in advance is | ||
| // in the non-hanging part. | ||
| return false; | ||
| } |
Member
Author
There was a problem hiding this comment.
Blink applies this same shortcut.
tomcur
commented
Oct 25, 2026
Comment on lines
127
to
+131
| @@ -141,18 +124,18 @@ | |||
| /// The amount of additional spacing to apply per justification opportunity. | |||
| pub(crate) amount_per_opportunity: f32, | |||
|
|
|||
| /// One past the line's logically last shaped cluster eligible for justification. Shaped | |||
| /// clusters at or beyond this index are not justified, as they hang past the line's end, or are | |||
| /// part of the last atom that doesn't fully hang (e.g., an NBSP doesn't hang, but if it closes | |||
| /// the line, it shouldn't get justified). | |||
| pub(crate) justification_end_cluster: u32, | |||
| /// One past the line's logically last character eligible for justification. Atoms with | |||
| /// characters at or beyond this index are not justified, as they hang past the line's end, or | |||
| /// are the last atom that doesn't fully hang (e.g., an NBSP doesn't hang, but if it closes the | |||
| /// line, it shouldn't get justified). | |||
| pub(crate) justification_end_char: u32, | |||
Member
Author
There was a problem hiding this comment.
This is changed to a character index, because a shaped cluster index could now either mean one of the original shaped clusters, or one of the reshaped ones.
tomcur
commented
Oct 25, 2026
| /// [word separator](`is_word_separator`), i.e., a justification opportunity. | ||
| #[inline] | ||
| #[inline(always)] | ||
| fn append_atom_to_line<B: Brush>( |
Member
Author
There was a problem hiding this comment.
Technically orthogonal, but the code changes were causing the compiler to sometimes stop inlining, which measurably slows down line breaking.
This wires up reshaping in Parley. Breaks we take at positions that are not safe to break are now reshaped. Breaks already safe to break are unchanged. Note this doesn't introduce new opportunities yet. The breaker still walks shaped clusters, so we don't yet add opportunities inside ligatures. That would now of course absolutely be possible, and is something we should do separately.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
LLM Contributions: Initial implementation, review, added tests.
On top of #908, which implements reshaping in
parley_engine.This wires up reshaping in Parley. Breaks we take at positions that are not safe to break are now reshaped. Breaks already safe to break are unchanged.
When starting on a new line, the breaker measures the effect that reshaping the start of that line would have, and takes it into account for measuring fit. If the end also needs to be reshaped, and the line overflows after reshaping, we do one more pass over the line with the max advance bumped down so we end one break sooner. (I first just saved the earlier break opportunity alongside the previous opportunity, but that slows down the general case by up to ten percent, whereas needing to step back is relatively rare.)
For now, the breaker still walks shaped clusters, so we don't yet have break opportunities inside ligatures. That would now of course absolutely be possible, but I think it's something we should do separately.
Note that like Blink, we choose not to reshape for the min-content width calculations. A single reshape would take about as much time as measuring an entire paragraph! That does mean the calculations can be slightly off. (I suppose that should be documented somewhere...)
Performance
This is neutral for text that doesn't reshape (including Roboto with kerning turned off). The speed up on wider lines seems to be a codegen effect.
For Roboto with kerning, the reshaping increases line breaking times by about 15-20% on narrow lines; less on wider lines. That's the cost of reshaping. Glyph iteration is about 2% slower. Some parts of querying got faster, others slower; possibly just a codegen effect, but I'll take another look tomorrow.
Changelog