diff --git a/samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs b/samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs index 938c45a4e2..db74be7c08 100644 --- a/samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs +++ b/samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs @@ -16,10 +16,10 @@ namespace ControlCatalog.Pages private Point _cursorPoint; - public StyledProperty ScaleProperty = AvaloniaProperty.Register(nameof(Scale), 1.0d); + public static readonly StyledProperty ScaleProperty = AvaloniaProperty.Register(nameof(Scale), 1.0d); public double Scale { get => GetValue(ScaleProperty); set => SetValue(ScaleProperty, value); } - public StyledProperty RotationProperty = AvaloniaProperty.Register(nameof(Rotation)); + public static readonly StyledProperty RotationProperty = AvaloniaProperty.Register(nameof(Rotation)); /// /// Rotation, measured in Radians! /// @@ -33,10 +33,10 @@ namespace ControlCatalog.Pages } } - public StyledProperty ViewportCenterYProperty = AvaloniaProperty.Register(nameof(ViewportCenterY), 0.0d); + public static readonly StyledProperty ViewportCenterYProperty = AvaloniaProperty.Register(nameof(ViewportCenterY), 0.0d); public double ViewportCenterY { get => GetValue(ViewportCenterYProperty); set => SetValue(ViewportCenterYProperty, value); } - public StyledProperty ViewportCenterXProperty = AvaloniaProperty.Register(nameof(ViewportCenterX), 0.0d); + public static readonly StyledProperty ViewportCenterXProperty = AvaloniaProperty.Register(nameof(ViewportCenterX), 0.0d); public double ViewportCenterX { get => GetValue(ViewportCenterXProperty); set => SetValue(ViewportCenterXProperty, value); } private IPen _pen; diff --git a/samples/ControlCatalog/Pages/PointerCanvas.cs b/samples/ControlCatalog/Pages/PointerCanvas.cs index da1ff5442d..653590fe64 100644 --- a/samples/ControlCatalog/Pages/PointerCanvas.cs +++ b/samples/ControlCatalog/Pages/PointerCanvas.cs @@ -93,7 +93,7 @@ public class PointerCanvas : Control } private int _threadSleep; - public static DirectProperty ThreadSleepProperty = + public static readonly DirectProperty ThreadSleepProperty = AvaloniaProperty.RegisterDirect(nameof(ThreadSleep), c => c.ThreadSleep, (c, v) => c.ThreadSleep = v); public int ThreadSleep @@ -103,7 +103,7 @@ public class PointerCanvas : Control } private bool _drawOnlyPoints; - public static DirectProperty DrawOnlyPointsProperty = + public static readonly DirectProperty DrawOnlyPointsProperty = AvaloniaProperty.RegisterDirect(nameof(DrawOnlyPoints), c => c.DrawOnlyPoints, (c, v) => c.DrawOnlyPoints = v); public bool DrawOnlyPoints @@ -113,7 +113,7 @@ public class PointerCanvas : Control } private string? _status; - public static DirectProperty StatusProperty = + public static readonly DirectProperty StatusProperty = AvaloniaProperty.RegisterDirect(nameof(DrawOnlyPoints), c => c.Status, (c, v) => c.Status = v, defaultBindingMode: Avalonia.Data.BindingMode.TwoWay); diff --git a/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs b/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs index c1b9b77401..75249ff7e7 100644 --- a/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs +++ b/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs @@ -89,13 +89,13 @@ namespace Avalonia.Media.TextFormatting var offset = Math.Max(0, currentPosition - glyphRun.Metrics.FirstCluster); var glyphIndex = glyphRun.FindGlyphIndex(characterIndex - offset); - var glyphInfo = shapedBuffer.GlyphInfos[glyphIndex]; + var glyphInfo = shapedBuffer[glyphIndex]; - shapedBuffer.GlyphInfos[glyphIndex] = new GlyphInfo(glyphInfo.GlyphIndex, + shapedBuffer[glyphIndex] = new GlyphInfo(glyphInfo.GlyphIndex, glyphInfo.GlyphCluster, glyphInfo.GlyphAdvance + spacing); } - glyphRun.GlyphInfos = shapedBuffer.GlyphInfos; + glyphRun.GlyphInfos = shapedBuffer; } currentPosition += textRun.Length; diff --git a/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs index f29bdd4459..3f26d081b0 100644 --- a/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs +++ b/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs @@ -2,6 +2,7 @@ using System.Buffers; using System.Collections; using System.Collections.Generic; +using System.Runtime.CompilerServices; using Avalonia.Utilities; namespace Avalonia.Media.TextFormatting @@ -9,12 +10,13 @@ namespace Avalonia.Media.TextFormatting public sealed class ShapedBuffer : IReadOnlyList, IDisposable { private GlyphInfo[]? _rentedBuffer; + private ArraySlice _glyphInfos; public ShapedBuffer(ReadOnlyMemory text, int bufferLength, IGlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel) { - _rentedBuffer = ArrayPool.Shared.Rent(bufferLength); Text = text; - GlyphInfos = new ArraySlice(_rentedBuffer, 0, bufferLength); + _rentedBuffer = ArrayPool.Shared.Rent(bufferLength); + _glyphInfos = new ArraySlice(_rentedBuffer, 0, bufferLength); GlyphTypeface = glyphTypeface; FontRenderingEmSize = fontRenderingEmSize; BidiLevel = bidiLevel; @@ -23,27 +25,70 @@ namespace Avalonia.Media.TextFormatting internal ShapedBuffer(ReadOnlyMemory text, ArraySlice glyphInfos, IGlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel) { Text = text; - GlyphInfos = glyphInfos; + _glyphInfos = glyphInfos; GlyphTypeface = glyphTypeface; FontRenderingEmSize = fontRenderingEmSize; BidiLevel = bidiLevel; } - internal ArraySlice GlyphInfos { get; private set; } - - public int Length - => GlyphInfos.Length; + /// + /// The buffer's length. + /// + public int Length => _glyphInfos.Length; + /// + /// The buffer's glyph typeface. + /// public IGlyphTypeface GlyphTypeface { get; } + /// + /// The buffers font rendering em size. + /// public double FontRenderingEmSize { get; } + /// + /// The buffer's bidi level. + /// public sbyte BidiLevel { get; } + /// + /// The buffer's reading direction. + /// public bool IsLeftToRight => (BidiLevel & 1) == 0; + /// + /// The text that is represended by this buffer. + /// public ReadOnlyMemory Text { get; } + /// + /// Reverses the buffer. + /// + public void Reverse() + { + _glyphInfos.Span.Reverse(); + } + + public void Dispose() + { + if (_rentedBuffer is not null) + { + ArrayPool.Shared.Return(_rentedBuffer); + _rentedBuffer = null; + _glyphInfos = ArraySlice.Empty; // ensure we don't misuse the returned array + } + } + + public GlyphInfo this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _glyphInfos[index]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => _glyphInfos[index] = value; + } + + public IEnumerator GetEnumerator() => _glyphInfos.GetEnumerator(); + /// /// Finds a glyph index for given character index. /// @@ -53,20 +98,19 @@ namespace Avalonia.Media.TextFormatting /// private int FindGlyphIndex(int characterIndex) { - if (characterIndex < GlyphInfos[0].GlyphCluster) + if (characterIndex < _glyphInfos[0].GlyphCluster) { return 0; } - if (characterIndex > GlyphInfos[GlyphInfos.Length - 1].GlyphCluster) + if (characterIndex > _glyphInfos[_glyphInfos.Length - 1].GlyphCluster) { - return GlyphInfos.Length - 1; + return _glyphInfos.Length - 1; } - var comparer = GlyphInfo.ClusterAscendingComparer; - var glyphInfos = GlyphInfos.Span; + var glyphInfos = _glyphInfos.Span; var searchValue = new GlyphInfo(default, characterIndex, default); @@ -109,42 +153,24 @@ namespace Avalonia.Media.TextFormatting return new SplitResult(this, null); } - var firstCluster = GlyphInfos[0].GlyphCluster; - var lastCluster = GlyphInfos[GlyphInfos.Length - 1].GlyphCluster; + var firstCluster = _glyphInfos[0].GlyphCluster; + var lastCluster = _glyphInfos[_glyphInfos.Length - 1].GlyphCluster; var start = firstCluster < lastCluster ? firstCluster : lastCluster; var glyphCount = FindGlyphIndex(start + length); var first = new ShapedBuffer(Text.Slice(0, length), - GlyphInfos.Take(glyphCount), GlyphTypeface, FontRenderingEmSize, BidiLevel); + _glyphInfos.Take(glyphCount), GlyphTypeface, FontRenderingEmSize, BidiLevel); var second = new ShapedBuffer(Text.Slice(length), - GlyphInfos.Skip(glyphCount), GlyphTypeface, FontRenderingEmSize, BidiLevel); + _glyphInfos.Skip(glyphCount), GlyphTypeface, FontRenderingEmSize, BidiLevel); return new SplitResult(first, second); } - int IReadOnlyCollection.Count => GlyphInfos.Length; - - public GlyphInfo this[int index] - { - get => GlyphInfos[index]; - set => GlyphInfos[index] = value; - } - - public IEnumerator GetEnumerator() => GlyphInfos.GetEnumerator(); + int IReadOnlyCollection.Count => _glyphInfos.Length; - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - public void Dispose() - { - if (_rentedBuffer is not null) - { - ArrayPool.Shared.Return(_rentedBuffer); - _rentedBuffer = null; - GlyphInfos = ArraySlice.Empty; // ensure we don't misuse the returned array - } - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } diff --git a/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs index 568148e15c..c5dd30b620 100644 --- a/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs +++ b/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs @@ -85,7 +85,7 @@ namespace Avalonia.Media.TextFormatting { _glyphRun = null; - ShapedBuffer.GlyphInfos.Span.Reverse(); + ShapedBuffer.Reverse(); IsReversed = !IsReversed; } @@ -106,7 +106,7 @@ namespace Avalonia.Media.TextFormatting for (var i = 0; i < ShapedBuffer.Length; i++) { - var advance = ShapedBuffer.GlyphInfos[i].GlyphAdvance; + var advance = ShapedBuffer[i].GlyphAdvance; if (currentWidth + advance > availableWidth) { @@ -130,7 +130,7 @@ namespace Avalonia.Media.TextFormatting for (var i = ShapedBuffer.Length - 1; i >= 0; i--) { - var advance = ShapedBuffer.GlyphInfos[i].GlyphAdvance; + var advance = ShapedBuffer[i].GlyphAdvance; if (width + advance > availableWidth) { diff --git a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs index 7f74f49982..12efb3c383 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs @@ -583,7 +583,7 @@ namespace Avalonia.Media.TextFormatting { if (shapedTextCharacters.ShapedBuffer.Length > 0) { - var firstCluster = shapedTextCharacters.ShapedBuffer.GlyphInfos[0].GlyphCluster; + var firstCluster = shapedTextCharacters.ShapedBuffer[0].GlyphCluster; var lastCluster = firstCluster; for (var j = 0; j < shapedTextCharacters.ShapedBuffer.Length; j++) diff --git a/src/Avalonia.Controls.DataGrid/Themes/Simple.xaml b/src/Avalonia.Controls.DataGrid/Themes/Simple.xaml index 76e44e4daf..464a12770e 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Simple.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Simple.xaml @@ -202,7 +202,7 @@ @@ -129,7 +129,7 @@ - +