Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
7529d7f
Introduce more tests and benchmarks for the UnicodeData
Gillibald May 19, 2026
887ccb3
Pair some rules to pay a lesser read cost
Gillibald May 19, 2026
af76220
Cache next/previous class for a faster lookup
Gillibald May 20, 2026
a367621
Add some profiling for text layout
Gillibald May 20, 2026
86d2836
Cache glyph cluster Metrics
Gillibald May 20, 2026
a12b0ba
remove planning document
Gillibald May 20, 2026
8f8e29e
Use a foreach loop for ExecuteRules to get a better JIT optimization
Gillibald May 20, 2026
bfe9650
use canonical foreach pattern over T[]
Gillibald May 20, 2026
30301d0
Fix SplitTextRuns can drop the current run when the split point falls…
Gillibald May 21, 2026
8e6200c
Fix LogicalTextRunEnumerator returning the first run on every iteration
Gillibald May 21, 2026
3023242
Fix TextLeadingPrefixCharacterEllipsis on bidi and multi-run lines
Gillibald May 21, 2026
a679624
Fix TextPathSegmentEllipsis crashing in fallback path when whole run …
Gillibald May 21, 2026
eb07dff
Add TextCollapsingProperties bidi characterization tests
Gillibald May 21, 2026
5fdc61d
Document logical-order contract on TextCollapsingProperties.Collapse
Gillibald May 21, 2026
f608dae
Fix TextPathSegmentEllipsis.MeasureSegmentWidth on RTL buffers
Gillibald May 21, 2026
0bf2f08
Speed up TextPathSegmentEllipsis.MeasureSegmentWidth
Gillibald May 21, 2026
9e9a56d
Fix TryMeasureCharacters/Backwards for RTL runs
Gillibald May 21, 2026
4e5e89b
Refcount ShapedBuffer pool array so Split/WithBidiLevel views can out…
Gillibald May 21, 2026
70dc37e
Replace reflection in ShapedBufferLifetimeTests with internal test hook
Gillibald May 21, 2026
5072002
Adjust xml comment
Gillibald May 21, 2026
c3a057f
Fix comment in test
Gillibald May 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -225,3 +225,5 @@ src/Browser/Avalonia.Browser/staticwebassets
# Claude agent worktrees
.claude/worktrees/
/.claude/settings.local.json
/planning
BenchmarkDotNet.Artifacts.*
19 changes: 14 additions & 5 deletions src/Avalonia.Base/Media/GlyphRun.cs
Original file line number Diff line number Diff line change
Expand Up @@ -684,15 +684,24 @@ private GlyphRunMetrics CreateGlyphRunMetrics()
}

var height = GlyphTypeface.Metrics.LineSpacing * Scale;
var widthIncludingTrailingWhitespace = 0d;

var trailingWhitespaceLength = GetTrailingWhitespaceLength(isReversed, out var newLineLength, out var glyphCount);

for (var index = 0; index < _glyphInfos.Count; index++)
// A2: when our glyph source is a ShapedBuffer (the common case — every
// shape result flows through one), it already maintains a cluster-width
// prefix sum we can read in O(1) instead of summing all advances here.
double widthIncludingTrailingWhitespace;
if (_glyphInfos is TextFormatting.ShapedBuffer shapedBuffer)
{
var advance = _glyphInfos[index].GlyphAdvance;

widthIncludingTrailingWhitespace += advance;
widthIncludingTrailingWhitespace = shapedBuffer.TotalGlyphAdvance;
}
else
{
widthIncludingTrailingWhitespace = 0d;
for (var index = 0; index < _glyphInfos.Count; index++)
{
widthIncludingTrailingWhitespace += _glyphInfos[index].GlyphAdvance;
}
}

var width = widthIncludingTrailingWhitespace;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,21 @@

namespace Avalonia.Media.TextFormatting;

/// <summary>
/// Walks the runs of a <see cref="TextLine"/> in <b>logical</b> (source-text)
/// order. This is the order that splits and length-based offsets are defined
/// in, and is what every <see cref="TextCollapsingProperties.Collapse"/>
/// implementation needs to see — unlike <see cref="TextLine.TextRuns"/>,
/// which exposes the post-BiDi <i>visual</i> ordering used for rendering.
/// </summary>
/// <remarks>
/// When the line has been finalized (the normal case after
/// <c>TextLineImpl.FinalizeLine</c>), the enumerator iterates over
/// <c>_indexedTextRuns</c> — a level-resolved table that maps each run back
/// to its original logical position. If the line hasn't been finalized (or
/// the line is not a <c>TextLineImpl</c>), it falls back to the raw
/// <see cref="TextLine.TextRuns"/> list.
/// </remarks>
internal ref struct LogicalTextRunEnumerator
{
private readonly IReadOnlyList<TextRun>? _textRuns;
Expand Down Expand Up @@ -61,7 +76,7 @@ public bool MoveNext([MaybeNullWhen(false)] out TextRun run)
}
else if (_textRuns != null)
{
run = _textRuns[0];
run = _textRuns[_index];
}
else
{
Expand Down
Loading