From 1bce702bd42ca784cca7e9ef81a52f4b4950edb0 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 23 Jan 2026 12:22:27 +0100 Subject: [PATCH] Introduce TextOptions (#20107) * Introduce TextOptions API * Store BaselinePixelAlignment as byte. Move to a dedicated file. * Update API suppressions --------- Co-authored-by: Julien Lebosquain --- api/Avalonia.nupkg.xml | 1302 ++++------------- samples/TextTestApp/InteractiveLineControl.cs | 2 +- .../Media/BaselinePixelAlignment.cs | 26 + src/Avalonia.Base/Media/DrawingContext.cs | 20 +- src/Avalonia.Base/Media/DrawingGroup.cs | 13 + .../Media/PlatformDrawingContext.cs | 4 + src/Avalonia.Base/Media/RenderOptions.cs | 45 +- src/Avalonia.Base/Media/TextHintingMode.cs | 31 + src/Avalonia.Base/Media/TextOptions.cs | 136 ++ src/Avalonia.Base/Media/TextRenderingMode.cs | 27 +- .../Platform/IDrawingContextImpl.cs | 11 + .../Drawing/Nodes/RenderDataNodes.cs | 15 + .../Drawing/RenderDataDrawingContext.cs | 6 + .../DrawingContextProxy.PendingCommands.cs | 4 + .../Composition/Server/DrawingContextProxy.cs | 21 + .../Server/ServerCompositionVisual.cs | 10 +- .../Rendering/ImmediateRenderer.cs | 1 + src/Avalonia.Base/Visual.Composition.cs | 3 +- src/Avalonia.Base/Visual.cs | 64 +- src/Avalonia.Base/composition-schema.xml | 1 + .../HeadlessPlatformRenderInterface.cs | 14 +- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 45 +- src/Skia/Avalonia.Skia/GlyphRunImpl.cs | 92 +- src/Skia/Avalonia.Skia/TwoLevelCache.cs | 157 ++ .../Shapes/ShapeTests.cs | 8 + .../Controls/TextBlockTests.cs | 38 + .../Media/GlyphRunTests.cs | 2 +- .../Media/GlyphRunTests.cs | 2 +- .../TwoLevelCacheTests.cs | 351 +++++ ..._With_TextOptions_Alias_Light.expected.png | Bin 0 -> 890 bytes ...k_With_TextOptions_Alias_None.expected.png | Bin 0 -> 885 bytes ...h_TextOptions_Antialias_Light.expected.png | Bin 0 -> 2734 bytes ...th_TextOptions_Antialias_None.expected.png | Bin 0 -> 2848 bytes 33 files changed, 1389 insertions(+), 1062 deletions(-) create mode 100644 src/Avalonia.Base/Media/BaselinePixelAlignment.cs create mode 100644 src/Avalonia.Base/Media/TextHintingMode.cs create mode 100644 src/Avalonia.Base/Media/TextOptions.cs create mode 100644 src/Skia/Avalonia.Skia/TwoLevelCache.cs create mode 100644 tests/Avalonia.Skia.UnitTests/TwoLevelCacheTests.cs create mode 100644 tests/TestFiles/Skia/Controls/TextBlock/Should_Render_TextBlock_With_TextOptions_Alias_Light.expected.png create mode 100644 tests/TestFiles/Skia/Controls/TextBlock/Should_Render_TextBlock_With_TextOptions_Alias_None.expected.png create mode 100644 tests/TestFiles/Skia/Controls/TextBlock/Should_Render_TextBlock_With_TextOptions_Antialias_Light.expected.png create mode 100644 tests/TestFiles/Skia/Controls/TextBlock/Should_Render_TextBlock_With_TextOptions_Antialias_None.expected.png diff --git a/api/Avalonia.nupkg.xml b/api/Avalonia.nupkg.xml index dd1ea2ac88..5b4d58be5b 100644 --- a/api/Avalonia.nupkg.xml +++ b/api/Avalonia.nupkg.xml @@ -3,7 +3,13 @@ CP0001 - T:Avalonia.Media.IGlyphTypeface + T:Avalonia.Controls.Primitives.IScrollable + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + + + CP0001 + T:Avalonia.Media.Fonts.FontFamilyLoader baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll @@ -15,19 +21,19 @@ CP0001 - T:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings.CompiledBindingPath - baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll - current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll + T:Avalonia.Media.Fonts.FontFamilyLoader + baseline/Avalonia/lib/net6.0/Avalonia.Base.dll + current/Avalonia/lib/net6.0/Avalonia.Base.dll CP0001 - T:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings.CompiledBindingPathBuilder - baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll - current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll + T:Avalonia.Controls.Primitives.IScrollable + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll CP0001 - T:Avalonia.Media.IGlyphTypeface + T:Avalonia.Media.Fonts.FontFamilyLoader baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll @@ -39,1023 +45,417 @@ CP0001 - T:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings.CompiledBindingPath - baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll - current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll - - - CP0001 - T:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings.CompiledBindingPathBuilder - baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll - current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll + T:Avalonia.Media.Fonts.FontFamilyLoader + baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll CP0002 - M:Avalonia.Media.FontManager.TryGetGlyphTypeface(Avalonia.Media.Typeface,Avalonia.Media.IGlyphTypeface@) + F:Avalonia.Media.DrawingImage.ViewboxProperty baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll CP0002 - M:Avalonia.Media.FontMetrics.get_DesignEmHeight + F:Avalonia.Media.Fonts.FontCollectionBase._glyphTypefaceCache baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll CP0002 - M:Avalonia.Media.Fonts.FontCollectionBase.TryAddGlyphTypeface(Avalonia.Media.IGlyphTypeface) + M:Avalonia.Media.DrawingImage.get_Viewbox baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll CP0002 - M:Avalonia.Media.Fonts.FontCollectionBase.TryAddGlyphTypeface(System.IO.Stream,Avalonia.Media.IGlyphTypeface@) + M:Avalonia.Media.DrawingImage.set_Viewbox(System.Nullable{Avalonia.Rect}) baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll CP0002 - M:Avalonia.Media.Fonts.FontCollectionBase.TryAddGlyphTypeface(System.String,Avalonia.Media.Fonts.FontCollectionKey,Avalonia.Media.IGlyphTypeface) + M:Avalonia.Media.Fonts.FontCollectionBase.Initialize(Avalonia.Platform.IFontManagerImpl) baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll CP0002 - M:Avalonia.Media.Fonts.FontCollectionBase.TryCreateSyntheticGlyphTypeface(Avalonia.Media.IGlyphTypeface,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IGlyphTypeface@) + M:Avalonia.Media.Fonts.IFontCollection.Initialize(Avalonia.Platform.IFontManagerImpl) baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll CP0002 - M:Avalonia.Media.Fonts.FontCollectionBase.TryGetGlyphTypeface(System.String,Avalonia.Media.Fonts.FontCollectionKey,Avalonia.Media.IGlyphTypeface@) + M:Avalonia.Platform.IDrawingContextImplWithEffects.PushEffect(Avalonia.Media.IEffect) baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll CP0002 - M:Avalonia.Media.Fonts.FontCollectionBase.TryGetGlyphTypeface(System.String,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IGlyphTypeface@) + M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.Globalization.CultureInfo,Avalonia.Media.Typeface@) baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll CP0002 - M:Avalonia.Media.Fonts.FontCollectionBase.TryGetNearestMatch(System.Collections.Generic.IDictionary{Avalonia.Media.Fonts.FontCollectionKey,Avalonia.Media.IGlyphTypeface},Avalonia.Media.Fonts.FontCollectionKey,Avalonia.Media.IGlyphTypeface@) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + F:Avalonia.Controls.TextBlock.LetterSpacingProperty + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Media.Fonts.FontCollectionBase.TryGetNearestMatch(System.String,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IGlyphTypeface@) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + F:Avalonia.Controls.TextBox.LetterSpacingProperty + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Media.Fonts.IFontCollection.TryGetGlyphTypeface(System.String,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IGlyphTypeface@) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + M:Avalonia.Controls.Design.CreatePreviewWithControl(System.Object) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Media.GlyphMetrics.get_Height - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + M:Avalonia.Controls.Design.GetDataContext(Avalonia.Controls.Templates.IDataTemplate) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Media.GlyphMetrics.get_Width - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + M:Avalonia.Controls.Design.GetPreviewWith(Avalonia.Controls.Templates.IDataTemplate) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Media.GlyphRun.#ctor(Avalonia.Media.IGlyphTypeface,System.Double,System.ReadOnlyMemory{System.Char},System.Collections.Generic.IReadOnlyList{Avalonia.Media.TextFormatting.GlyphInfo},System.Nullable{Avalonia.Point},System.Int32) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + M:Avalonia.Controls.Design.GetPreviewWith(Avalonia.Styling.IStyle) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Media.GlyphRun.#ctor(Avalonia.Media.IGlyphTypeface,System.Double,System.ReadOnlyMemory{System.Char},System.Collections.Generic.IReadOnlyList{System.UInt16},System.Nullable{Avalonia.Point},System.Int32) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + M:Avalonia.Controls.Design.SetDataContext(Avalonia.Controls.Templates.IDataTemplate,System.Object) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Media.GlyphRun.get_GlyphTypeface - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.AvaloniaObject,Avalonia.Controls.ITemplate{Avalonia.Controls.Control}) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Media.IGlyphTypeface.GetGlyph(System.UInt32) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Controls.ResourceDictionary,Avalonia.Controls.ITemplate{Avalonia.Controls.Control}) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Media.IGlyphTypeface.GetGlyphAdvance(System.UInt16) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Controls.Templates.IDataTemplate,Avalonia.Controls.Control) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Media.IGlyphTypeface.GetGlyphAdvances(System.ReadOnlySpan{System.UInt16}) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Controls.Templates.IDataTemplate,Avalonia.Controls.ITemplate{Avalonia.Controls.Control}) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Media.IGlyphTypeface.GetGlyphs(System.ReadOnlySpan{System.UInt32}) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Styling.IStyle,Avalonia.Controls.Control) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Media.IGlyphTypeface.TryGetGlyph(System.UInt32,System.UInt16@) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Styling.IStyle,Avalonia.Controls.ITemplate{Avalonia.Controls.Control}) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Media.IGlyphTypeface.TryGetTable(System.UInt32,System.Byte[]@) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + F:Avalonia.Media.Fonts.FontCollectionBase._glyphTypefaceCache + baseline/Avalonia/lib/net6.0/Avalonia.Base.dll + current/Avalonia/lib/net6.0/Avalonia.Base.dll CP0002 - M:Avalonia.Media.TextFormatting.ShapedBuffer.#ctor(System.ReadOnlyMemory{System.Char},System.Int32,Avalonia.Media.IGlyphTypeface,System.Double,System.SByte) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + M:Avalonia.Media.Fonts.FontCollectionBase.Initialize(Avalonia.Platform.IFontManagerImpl) + baseline/Avalonia/lib/net6.0/Avalonia.Base.dll + current/Avalonia/lib/net6.0/Avalonia.Base.dll CP0002 - M:Avalonia.Media.TextFormatting.ShapedBuffer.get_GlyphTypeface - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + M:Avalonia.Media.Fonts.IFontCollection.Initialize(Avalonia.Platform.IFontManagerImpl) + baseline/Avalonia/lib/net6.0/Avalonia.Base.dll + current/Avalonia/lib/net6.0/Avalonia.Base.dll CP0002 - M:Avalonia.Media.TextFormatting.TextMetrics.#ctor(Avalonia.Media.IGlyphTypeface,System.Double) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.Globalization.CultureInfo,Avalonia.Media.Typeface@) + baseline/Avalonia/lib/net6.0/Avalonia.Base.dll + current/Avalonia/lib/net6.0/Avalonia.Base.dll CP0002 - M:Avalonia.Media.TextFormatting.TextShaperOptions.#ctor(Avalonia.Media.IGlyphTypeface,System.Collections.Generic.IReadOnlyList{Avalonia.Media.FontFeature},System.Double,System.SByte,System.Globalization.CultureInfo,System.Double,System.Double) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + M:Avalonia.Dialogs.Internal.ManagedFileChooserFilterViewModel.#ctor(Avalonia.Platform.Storage.FilePickerFileType) + baseline/Avalonia/lib/net6.0/Avalonia.Dialogs.dll + current/Avalonia/lib/net6.0/Avalonia.Dialogs.dll CP0002 - M:Avalonia.Media.TextFormatting.TextShaperOptions.#ctor(Avalonia.Media.IGlyphTypeface,System.Double,System.SByte,System.Globalization.CultureInfo,System.Double,System.Double) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + F:Avalonia.Media.DrawingImage.ViewboxProperty + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll CP0002 - M:Avalonia.Media.TextFormatting.TextShaperOptions.get_Typeface - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + F:Avalonia.Media.Fonts.FontCollectionBase._glyphTypefaceCache + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll CP0002 - M:Avalonia.Media.Typeface.get_GlyphTypeface - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + M:Avalonia.Media.DrawingImage.get_Viewbox + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll CP0002 - M:Avalonia.Platform.IFontManagerImpl.TryCreateGlyphTypeface(System.IO.Stream,Avalonia.Media.FontSimulations,Avalonia.Media.IGlyphTypeface@) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + M:Avalonia.Media.DrawingImage.set_Viewbox(System.Nullable{Avalonia.Rect}) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll CP0002 - M:Avalonia.Platform.IFontManagerImpl.TryCreateGlyphTypeface(System.String,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IGlyphTypeface@) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + M:Avalonia.Media.Fonts.FontCollectionBase.Initialize(Avalonia.Platform.IFontManagerImpl) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll CP0002 - M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.String,System.Globalization.CultureInfo,Avalonia.Media.Typeface@) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + M:Avalonia.Media.Fonts.IFontCollection.Initialize(Avalonia.Platform.IFontManagerImpl) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll CP0002 - M:Avalonia.Platform.IGlyphRunImpl.get_GlyphTypeface - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + M:Avalonia.Platform.IDrawingContextImplWithEffects.PushEffect(Avalonia.Media.IEffect) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll CP0002 - M:Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.IGlyphTypeface,System.Double,System.Collections.Generic.IReadOnlyList{Avalonia.Media.TextFormatting.GlyphInfo},Avalonia.Point) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll + M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.Globalization.CultureInfo,Avalonia.Media.Typeface@) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll CP0002 - F:Avalonia.Controls.Documents.TextElement.LetterSpacingProperty - baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll - current/Avalonia/lib/net10.0/Avalonia.Controls.dll + F:Avalonia.Controls.TextBlock.LetterSpacingProperty + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll CP0002 - F:Avalonia.Controls.Presenters.ContentPresenter.LetterSpacingProperty - baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll - current/Avalonia/lib/net10.0/Avalonia.Controls.dll + F:Avalonia.Controls.TextBox.LetterSpacingProperty + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll CP0002 - F:Avalonia.Controls.Primitives.TemplatedControl.LetterSpacingProperty - baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll - current/Avalonia/lib/net10.0/Avalonia.Controls.dll + M:Avalonia.Controls.Design.CreatePreviewWithControl(System.Object) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll CP0002 - F:Avalonia.Controls.TextBlock.LetterSpacingProperty - baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll - current/Avalonia/lib/net10.0/Avalonia.Controls.dll + M:Avalonia.Controls.Design.GetDataContext(Avalonia.Controls.Templates.IDataTemplate) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Controls.Documents.TextElement.get_LetterSpacing - baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll - current/Avalonia/lib/net10.0/Avalonia.Controls.dll + M:Avalonia.Controls.Design.GetPreviewWith(Avalonia.Controls.Templates.IDataTemplate) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Controls.Documents.TextElement.GetLetterSpacing(Avalonia.Controls.Control) - baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll - current/Avalonia/lib/net10.0/Avalonia.Controls.dll + M:Avalonia.Controls.Design.GetPreviewWith(Avalonia.Styling.IStyle) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Controls.Documents.TextElement.set_LetterSpacing(System.Double) - baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll - current/Avalonia/lib/net10.0/Avalonia.Controls.dll + M:Avalonia.Controls.Design.SetDataContext(Avalonia.Controls.Templates.IDataTemplate,System.Object) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Controls.Documents.TextElement.SetLetterSpacing(Avalonia.Controls.Control,System.Double) - baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll - current/Avalonia/lib/net10.0/Avalonia.Controls.dll + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.AvaloniaObject,Avalonia.Controls.ITemplate{Avalonia.Controls.Control}) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Controls.Presenters.ContentPresenter.get_LetterSpacing - baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll - current/Avalonia/lib/net10.0/Avalonia.Controls.dll + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Controls.ResourceDictionary,Avalonia.Controls.ITemplate{Avalonia.Controls.Control}) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Controls.Presenters.ContentPresenter.set_LetterSpacing(System.Double) - baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll - current/Avalonia/lib/net10.0/Avalonia.Controls.dll + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Controls.Templates.IDataTemplate,Avalonia.Controls.Control) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Controls.Primitives.TemplatedControl.get_LetterSpacing - baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll - current/Avalonia/lib/net10.0/Avalonia.Controls.dll + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Controls.Templates.IDataTemplate,Avalonia.Controls.ITemplate{Avalonia.Controls.Control}) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Controls.Primitives.TemplatedControl.set_LetterSpacing(System.Double) - baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll - current/Avalonia/lib/net10.0/Avalonia.Controls.dll + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Styling.IStyle,Avalonia.Controls.Control) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindingExtension.#ctor(Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings.CompiledBindingPath) - baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll - current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Styling.IStyle,Avalonia.Controls.ITemplate{Avalonia.Controls.Control}) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindingExtension.get_Path - baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll - current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll + M:Avalonia.Dialogs.Internal.ManagedFileChooserFilterViewModel.#ctor(Avalonia.Platform.Storage.FilePickerFileType) + baseline/Avalonia/lib/net8.0/Avalonia.Dialogs.dll + current/Avalonia/lib/net8.0/Avalonia.Dialogs.dll CP0002 - M:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindingExtension.ProvideValue(System.IServiceProvider) - baseline/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll - current/Avalonia/lib/net10.0/Avalonia.Markup.Xaml.dll + F:Avalonia.Media.Fonts.FontCollectionBase._glyphTypefaceCache + baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll CP0002 - M:Avalonia.Media.GlyphMetrics.get_Height - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll + M:Avalonia.Media.Fonts.FontCollectionBase.Initialize(Avalonia.Platform.IFontManagerImpl) + baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll CP0002 - M:Avalonia.Media.GlyphMetrics.get_Width - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll + M:Avalonia.Media.Fonts.IFontCollection.Initialize(Avalonia.Platform.IFontManagerImpl) + baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll CP0002 - M:Avalonia.Media.IGlyphTypeface.get_GlyphCount - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll + M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.Globalization.CultureInfo,Avalonia.Media.Typeface@) + baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll CP0002 - M:Avalonia.Media.IGlyphTypeface.GetGlyph(System.UInt32) - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll + M:Avalonia.Dialogs.Internal.ManagedFileChooserFilterViewModel.#ctor(Avalonia.Platform.Storage.FilePickerFileType) + baseline/Avalonia/lib/netstandard2.0/Avalonia.Dialogs.dll + current/Avalonia/lib/netstandard2.0/Avalonia.Dialogs.dll CP0002 - M:Avalonia.Media.IGlyphTypeface.GetGlyphAdvance(System.UInt16) - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll + M:Avalonia.Platform.IDrawingContextImplWithEffects.PopEffect + baseline/netstandard2.0/Avalonia.Base.dll + target/netstandard2.0/Avalonia.Base.dll CP0002 - M:Avalonia.Media.IGlyphTypeface.GetGlyphAdvances(System.ReadOnlySpan{System.UInt16}) - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll + M:Avalonia.Platform.IDrawingContextImplWithEffects.PushEffect(Avalonia.Media.IEffect) + baseline/netstandard2.0/Avalonia.Base.dll + target/netstandard2.0/Avalonia.Base.dll - CP0002 - M:Avalonia.Media.IGlyphTypeface.GetGlyphs(System.ReadOnlySpan{System.UInt32}) + CP0006 + M:Avalonia.Platform.IDrawingContextImpl.PopTextOptions + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + + + CP0006 + M:Avalonia.Platform.IDrawingContextImpl.PushTextOptions(Avalonia.Media.TextOptions) + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + + + CP0006 + M:Avalonia.Platform.IDrawingContextImplWithEffects.PushEffect(System.Nullable{Avalonia.Rect},Avalonia.Media.IEffect) + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + + + CP0006 + M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.String,System.Globalization.CultureInfo,Avalonia.Media.Typeface@) + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + + + CP0006 + M:Avalonia.Input.Platform.IClipboard.SetDataAsync(Avalonia.Input.IAsyncDataTransfer) baseline/Avalonia/lib/net6.0/Avalonia.Base.dll current/Avalonia/lib/net6.0/Avalonia.Base.dll - CP0002 - M:Avalonia.Media.IGlyphTypeface.TryGetGlyph(System.UInt32,System.UInt16@) + CP0006 + M:Avalonia.Input.Platform.IClipboard.TryGetDataAsync baseline/Avalonia/lib/net6.0/Avalonia.Base.dll current/Avalonia/lib/net6.0/Avalonia.Base.dll - CP0002 - M:Avalonia.Media.IGlyphTypeface.TryGetTable(System.UInt32,System.Byte[]@) + CP0006 + M:Avalonia.Input.Platform.IClipboard.TryGetInProcessDataAsync baseline/Avalonia/lib/net6.0/Avalonia.Base.dll current/Avalonia/lib/net6.0/Avalonia.Base.dll - CP0002 - M:Avalonia.Platform.IFontManagerImpl.TryCreateGlyphTypeface(System.IO.Stream,Avalonia.Media.FontSimulations,Avalonia.Media.IGlyphTypeface@) + CP0006 + M:Avalonia.Input.Platform.IPlatformDragSource.DoDragDropAsync(Avalonia.Input.PointerEventArgs,Avalonia.Input.IDataTransfer,Avalonia.Input.DragDropEffects) baseline/Avalonia/lib/net6.0/Avalonia.Base.dll current/Avalonia/lib/net6.0/Avalonia.Base.dll - CP0002 - M:Avalonia.Platform.IFontManagerImpl.TryCreateGlyphTypeface(System.String,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IGlyphTypeface@) + CP0006 + M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.String,System.Globalization.CultureInfo,Avalonia.Media.Typeface@) baseline/Avalonia/lib/net6.0/Avalonia.Base.dll current/Avalonia/lib/net6.0/Avalonia.Base.dll - CP0002 - M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.Globalization.CultureInfo,Avalonia.Media.Typeface@) + CP0006 + M:Avalonia.Platform.IPlatformRenderInterfaceImportedImage.SnapshotWithTimelineSemaphores(Avalonia.Platform.IPlatformRenderInterfaceImportedSemaphore,System.UInt64,Avalonia.Platform.IPlatformRenderInterfaceImportedSemaphore,System.UInt64) baseline/Avalonia/lib/net6.0/Avalonia.Base.dll current/Avalonia/lib/net6.0/Avalonia.Base.dll - CP0002 - M:Avalonia.Platform.IGlyphRunImpl.get_GlyphTypeface + CP0006 + M:Avalonia.Platform.Storage.IStorageProvider.SaveFilePickerWithResultAsync(Avalonia.Platform.Storage.FilePickerSaveOptions) baseline/Avalonia/lib/net6.0/Avalonia.Base.dll current/Avalonia/lib/net6.0/Avalonia.Base.dll - CP0002 - M:Avalonia.Media.FontManager.TryGetGlyphTypeface(Avalonia.Media.Typeface,Avalonia.Media.IGlyphTypeface@) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll + CP0006 + M:Avalonia.OpenGL.IGlExternalSemaphore.SignalTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64) + baseline/Avalonia/lib/net6.0/Avalonia.OpenGL.dll + current/Avalonia/lib/net6.0/Avalonia.OpenGL.dll - CP0002 - M:Avalonia.Media.FontMetrics.get_DesignEmHeight - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.Fonts.FontCollectionBase.TryAddGlyphTypeface(Avalonia.Media.IGlyphTypeface) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.Fonts.FontCollectionBase.TryAddGlyphTypeface(System.IO.Stream,Avalonia.Media.IGlyphTypeface@) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.Fonts.FontCollectionBase.TryAddGlyphTypeface(System.String,Avalonia.Media.Fonts.FontCollectionKey,Avalonia.Media.IGlyphTypeface) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.Fonts.FontCollectionBase.TryCreateSyntheticGlyphTypeface(Avalonia.Media.IGlyphTypeface,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IGlyphTypeface@) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.Fonts.FontCollectionBase.TryGetGlyphTypeface(System.String,Avalonia.Media.Fonts.FontCollectionKey,Avalonia.Media.IGlyphTypeface@) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.Fonts.FontCollectionBase.TryGetGlyphTypeface(System.String,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IGlyphTypeface@) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.Fonts.FontCollectionBase.TryGetNearestMatch(System.Collections.Generic.IDictionary{Avalonia.Media.Fonts.FontCollectionKey,Avalonia.Media.IGlyphTypeface},Avalonia.Media.Fonts.FontCollectionKey,Avalonia.Media.IGlyphTypeface@) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.Fonts.FontCollectionBase.TryGetNearestMatch(System.String,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IGlyphTypeface@) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.Fonts.IFontCollection.TryGetGlyphTypeface(System.String,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IGlyphTypeface@) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.GlyphMetrics.get_Height - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.GlyphMetrics.get_Width - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.GlyphRun.#ctor(Avalonia.Media.IGlyphTypeface,System.Double,System.ReadOnlyMemory{System.Char},System.Collections.Generic.IReadOnlyList{Avalonia.Media.TextFormatting.GlyphInfo},System.Nullable{Avalonia.Point},System.Int32) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.GlyphRun.#ctor(Avalonia.Media.IGlyphTypeface,System.Double,System.ReadOnlyMemory{System.Char},System.Collections.Generic.IReadOnlyList{System.UInt16},System.Nullable{Avalonia.Point},System.Int32) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.GlyphRun.get_GlyphTypeface - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.IGlyphTypeface.get_GlyphCount - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.IGlyphTypeface.GetGlyph(System.UInt32) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.IGlyphTypeface.GetGlyphAdvance(System.UInt16) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.IGlyphTypeface.GetGlyphAdvances(System.ReadOnlySpan{System.UInt16}) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.IGlyphTypeface.GetGlyphs(System.ReadOnlySpan{System.UInt32}) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.IGlyphTypeface.TryGetGlyph(System.UInt32,System.UInt16@) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.IGlyphTypeface.TryGetTable(System.UInt32,System.Byte[]@) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.TextFormatting.ShapedBuffer.#ctor(System.ReadOnlyMemory{System.Char},System.Int32,Avalonia.Media.IGlyphTypeface,System.Double,System.SByte) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.TextFormatting.ShapedBuffer.get_GlyphTypeface - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.TextFormatting.TextMetrics.#ctor(Avalonia.Media.IGlyphTypeface,System.Double) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.TextFormatting.TextShaperOptions.#ctor(Avalonia.Media.IGlyphTypeface,System.Collections.Generic.IReadOnlyList{Avalonia.Media.FontFeature},System.Double,System.SByte,System.Globalization.CultureInfo,System.Double,System.Double) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.TextFormatting.TextShaperOptions.#ctor(Avalonia.Media.IGlyphTypeface,System.Double,System.SByte,System.Globalization.CultureInfo,System.Double,System.Double) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.TextFormatting.TextShaperOptions.get_Typeface - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.Typeface.get_GlyphTypeface - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Platform.IFontManagerImpl.TryCreateGlyphTypeface(System.IO.Stream,Avalonia.Media.FontSimulations,Avalonia.Media.IGlyphTypeface@) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Platform.IFontManagerImpl.TryCreateGlyphTypeface(System.String,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IGlyphTypeface@) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.Globalization.CultureInfo,Avalonia.Media.Typeface@) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.String,System.Globalization.CultureInfo,Avalonia.Media.Typeface@) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Platform.IGlyphRunImpl.get_GlyphTypeface - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.IGlyphTypeface,System.Double,System.Collections.Generic.IReadOnlyList{Avalonia.Media.TextFormatting.GlyphInfo},Avalonia.Point) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0002 - F:Avalonia.Controls.Documents.TextElement.LetterSpacingProperty - baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll - current/Avalonia/lib/net8.0/Avalonia.Controls.dll - - - CP0002 - F:Avalonia.Controls.Presenters.ContentPresenter.LetterSpacingProperty - baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll - current/Avalonia/lib/net8.0/Avalonia.Controls.dll - - - CP0002 - F:Avalonia.Controls.Primitives.TemplatedControl.LetterSpacingProperty - baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll - current/Avalonia/lib/net8.0/Avalonia.Controls.dll - - - CP0002 - F:Avalonia.Controls.TextBlock.LetterSpacingProperty - baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll - current/Avalonia/lib/net8.0/Avalonia.Controls.dll - - - CP0002 - M:Avalonia.Controls.Documents.TextElement.get_LetterSpacing - baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll - current/Avalonia/lib/net8.0/Avalonia.Controls.dll - - - CP0002 - M:Avalonia.Controls.Documents.TextElement.GetLetterSpacing(Avalonia.Controls.Control) - baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll - current/Avalonia/lib/net8.0/Avalonia.Controls.dll - - - CP0002 - M:Avalonia.Controls.Documents.TextElement.set_LetterSpacing(System.Double) - baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll - current/Avalonia/lib/net8.0/Avalonia.Controls.dll - - - CP0002 - M:Avalonia.Controls.Documents.TextElement.SetLetterSpacing(Avalonia.Controls.Control,System.Double) - baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll - current/Avalonia/lib/net8.0/Avalonia.Controls.dll - - - CP0002 - M:Avalonia.Controls.Presenters.ContentPresenter.get_LetterSpacing - baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll - current/Avalonia/lib/net8.0/Avalonia.Controls.dll - - - CP0002 - M:Avalonia.Controls.Presenters.ContentPresenter.set_LetterSpacing(System.Double) - baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll - current/Avalonia/lib/net8.0/Avalonia.Controls.dll - - - CP0002 - M:Avalonia.Controls.Primitives.TemplatedControl.get_LetterSpacing - baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll - current/Avalonia/lib/net8.0/Avalonia.Controls.dll - - - CP0002 - M:Avalonia.Controls.Primitives.TemplatedControl.set_LetterSpacing(System.Double) - baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll - current/Avalonia/lib/net8.0/Avalonia.Controls.dll - - - CP0002 - M:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindingExtension.#ctor(Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings.CompiledBindingPath) - baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll - current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll - - - CP0002 - M:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindingExtension.get_Path - baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll - current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll - - - CP0002 - M:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindingExtension.ProvideValue(System.IServiceProvider) - baseline/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll - current/Avalonia/lib/net8.0/Avalonia.Markup.Xaml.dll - - - CP0002 - M:Avalonia.Media.GlyphMetrics.get_Height - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.GlyphMetrics.get_Width - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.IGlyphTypeface.get_GlyphCount - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.IGlyphTypeface.GetGlyph(System.UInt32) - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.IGlyphTypeface.GetGlyphAdvance(System.UInt16) - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.IGlyphTypeface.GetGlyphAdvances(System.ReadOnlySpan{System.UInt16}) - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.IGlyphTypeface.GetGlyphs(System.ReadOnlySpan{System.UInt32}) - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.IGlyphTypeface.TryGetGlyph(System.UInt32,System.UInt16@) - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Media.IGlyphTypeface.TryGetTable(System.UInt32,System.Byte[]@) - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Platform.IFontManagerImpl.TryCreateGlyphTypeface(System.IO.Stream,Avalonia.Media.FontSimulations,Avalonia.Media.IGlyphTypeface@) - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Platform.IFontManagerImpl.TryCreateGlyphTypeface(System.String,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IGlyphTypeface@) - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.Globalization.CultureInfo,Avalonia.Media.Typeface@) - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Platform.IGlyphRunImpl.get_GlyphTypeface - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Media.Fonts.IFontCollection.TryCreateSyntheticGlyphTypeface(Avalonia.Media.GlyphTypeface,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.GlyphTypeface@) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Media.Fonts.IFontCollection.TryCreateSyntheticGlyphTypeface(Avalonia.Media.IGlyphTypeface,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IGlyphTypeface@) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Media.Fonts.IFontCollection.TryGetFamilyTypefaces(System.String,System.Collections.Generic.IReadOnlyList{Avalonia.Media.Typeface}@) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Media.Fonts.IFontCollection.TryGetGlyphTypeface(System.String,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.GlyphTypeface@) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Media.Fonts.IFontCollection.TryGetNearestMatch(System.String,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.GlyphTypeface@) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Media.Fonts.IFontCollection.TryGetNearestMatch(System.String,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IGlyphTypeface@) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Media.IGlyphTypeface.GetGlyphAdvance(System.UInt16) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Platform.IFontManagerImpl.TryCreateGlyphTypeface(System.IO.Stream,Avalonia.Media.FontSimulations,Avalonia.Media.IPlatformTypeface@) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Platform.IFontManagerImpl.TryCreateGlyphTypeface(System.String,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IPlatformTypeface@) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Platform.IFontManagerImpl.TryGetFamilyTypefaces(System.String,System.Collections.Generic.IReadOnlyList{Avalonia.Media.Typeface}@) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.String,System.Globalization.CultureInfo,Avalonia.Media.IPlatformTypeface@) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.GlyphTypeface,System.Double,System.Collections.Generic.IReadOnlyList{Avalonia.Media.TextFormatting.GlyphInfo},Avalonia.Point) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Platform.ITextShaperImpl.CreateTypeface(Avalonia.Media.GlyphTypeface) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Platform.ITextShaperImpl.CreateTypeface(Avalonia.Media.IGlyphTypeface) - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.CharacterToGlyphMap - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.FaceNames - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.FamilyNames - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.PlatformTypeface - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.SupportedFeatures - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.TextShaperTypeface - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.TypographicFamilyName - baseline/Avalonia/lib/net10.0/Avalonia.Base.dll - current/Avalonia/lib/net10.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Input.Platform.IClipboard.SetDataAsync(Avalonia.Input.IAsyncDataTransfer) - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Input.Platform.IClipboard.TryGetDataAsync - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Input.Platform.IClipboard.TryGetInProcessDataAsync - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Input.Platform.IPlatformDragSource.DoDragDropAsync(Avalonia.Input.PointerEventArgs,Avalonia.Input.IDataTransfer,Avalonia.Input.DragDropEffects) - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Media.IGlyphTypeface.GetGlyphAdvance(System.UInt16) - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Platform.IFontManagerImpl.TryCreateGlyphTypeface(System.IO.Stream,Avalonia.Media.FontSimulations,Avalonia.Media.IPlatformTypeface@) - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Platform.IFontManagerImpl.TryCreateGlyphTypeface(System.String,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IPlatformTypeface@) - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Platform.IFontManagerImpl.TryGetFamilyTypefaces(System.String,System.Collections.Generic.IReadOnlyList{Avalonia.Media.Typeface}@) - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.Globalization.CultureInfo,Avalonia.Media.IPlatformTypeface@) - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Platform.IPlatformRenderInterfaceImportedImage.SnapshotWithTimelineSemaphores(Avalonia.Platform.IPlatformRenderInterfaceImportedSemaphore,System.UInt64,Avalonia.Platform.IPlatformRenderInterfaceImportedSemaphore,System.UInt64) - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Platform.ITextShaperImpl.CreateTypeface(Avalonia.Media.IGlyphTypeface) - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.CharacterToGlyphMap - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.FaceNames - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.FamilyNames - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.GlyphCount - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.PlatformTypeface - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.SupportedFeatures - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.TextShaperTypeface - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.TypographicFamilyName - baseline/Avalonia/lib/net6.0/Avalonia.Base.dll - current/Avalonia/lib/net6.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.OpenGL.IGlExternalSemaphore.SignalTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64) - baseline/Avalonia/lib/net6.0/Avalonia.OpenGL.dll - current/Avalonia/lib/net6.0/Avalonia.OpenGL.dll - - - CP0006 - M:Avalonia.OpenGL.IGlExternalSemaphore.WaitTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64) - baseline/Avalonia/lib/net6.0/Avalonia.OpenGL.dll - current/Avalonia/lib/net6.0/Avalonia.OpenGL.dll + CP0006 + M:Avalonia.OpenGL.IGlExternalSemaphore.WaitTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64) + baseline/Avalonia/lib/net6.0/Avalonia.OpenGL.dll + current/Avalonia/lib/net6.0/Avalonia.OpenGL.dll CP0006 @@ -1089,79 +489,25 @@ CP0006 - M:Avalonia.Media.Fonts.IFontCollection.TryCreateSyntheticGlyphTypeface(Avalonia.Media.GlyphTypeface,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.GlyphTypeface@) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Media.Fonts.IFontCollection.TryCreateSyntheticGlyphTypeface(Avalonia.Media.IGlyphTypeface,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IGlyphTypeface@) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Media.Fonts.IFontCollection.TryGetFamilyTypefaces(System.String,System.Collections.Generic.IReadOnlyList{Avalonia.Media.Typeface}@) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Media.Fonts.IFontCollection.TryGetGlyphTypeface(System.String,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.GlyphTypeface@) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Media.Fonts.IFontCollection.TryGetNearestMatch(System.String,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.GlyphTypeface@) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Media.Fonts.IFontCollection.TryGetNearestMatch(System.String,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IGlyphTypeface@) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Media.IGlyphTypeface.GetGlyphAdvance(System.UInt16) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Platform.IFontManagerImpl.TryCreateGlyphTypeface(System.IO.Stream,Avalonia.Media.FontSimulations,Avalonia.Media.IPlatformTypeface@) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Platform.IFontManagerImpl.TryCreateGlyphTypeface(System.String,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IPlatformTypeface@) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Platform.IFontManagerImpl.TryGetFamilyTypefaces(System.String,System.Collections.Generic.IReadOnlyList{Avalonia.Media.Typeface}@) + M:Avalonia.Platform.IDrawingContextImpl.PopTextOptions baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll CP0006 - M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.Globalization.CultureInfo,Avalonia.Media.IPlatformTypeface@) + M:Avalonia.Platform.IDrawingContextImpl.PushTextOptions(Avalonia.Media.TextOptions) baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll CP0006 - M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.String,System.Globalization.CultureInfo,Avalonia.Media.IPlatformTypeface@) + M:Avalonia.Platform.IDrawingContextImplWithEffects.PushEffect(System.Nullable{Avalonia.Rect},Avalonia.Media.IEffect) baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll CP0006 - M:Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.GlyphTypeface,System.Double,System.Collections.Generic.IReadOnlyList{Avalonia.Media.TextFormatting.GlyphInfo},Avalonia.Point) + M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.String,System.Globalization.CultureInfo,Avalonia.Media.Typeface@) baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll @@ -1173,61 +519,7 @@ CP0006 - M:Avalonia.Platform.ITextShaperImpl.CreateTypeface(Avalonia.Media.GlyphTypeface) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Platform.ITextShaperImpl.CreateTypeface(Avalonia.Media.IGlyphTypeface) - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.CharacterToGlyphMap - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.FaceNames - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.FamilyNames - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.GlyphCount - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.PlatformTypeface - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.SupportedFeatures - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.TextShaperTypeface - baseline/Avalonia/lib/net8.0/Avalonia.Base.dll - current/Avalonia/lib/net8.0/Avalonia.Base.dll - - - CP0006 - P:Avalonia.Media.IGlyphTypeface.TypographicFamilyName + M:Avalonia.Platform.Storage.IStorageProvider.SaveFilePickerWithResultAsync(Avalonia.Platform.Storage.FilePickerSaveOptions) baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll @@ -1275,110 +567,158 @@ CP0006 - M:Avalonia.Media.IGlyphTypeface.GetGlyphAdvance(System.UInt16) + M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.String,System.Globalization.CultureInfo,Avalonia.Media.Typeface@) baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll CP0006 - M:Avalonia.Platform.IFontManagerImpl.TryCreateGlyphTypeface(System.IO.Stream,Avalonia.Media.FontSimulations,Avalonia.Media.IPlatformTypeface@) + M:Avalonia.Platform.IPlatformRenderInterfaceImportedImage.SnapshotWithTimelineSemaphores(Avalonia.Platform.IPlatformRenderInterfaceImportedSemaphore,System.UInt64,Avalonia.Platform.IPlatformRenderInterfaceImportedSemaphore,System.UInt64) baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll CP0006 - M:Avalonia.Platform.IFontManagerImpl.TryCreateGlyphTypeface(System.String,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,Avalonia.Media.IPlatformTypeface@) + M:Avalonia.Platform.Storage.IStorageProvider.SaveFilePickerWithResultAsync(Avalonia.Platform.Storage.FilePickerSaveOptions) baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll CP0006 - M:Avalonia.Platform.IFontManagerImpl.TryGetFamilyTypefaces(System.String,System.Collections.Generic.IReadOnlyList{Avalonia.Media.Typeface}@) - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + M:Avalonia.OpenGL.IGlExternalSemaphore.SignalTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64) + baseline/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll + current/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll CP0006 - M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.Globalization.CultureInfo,Avalonia.Media.IPlatformTypeface@) - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + M:Avalonia.OpenGL.IGlExternalSemaphore.WaitTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64) + baseline/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll + current/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll CP0006 - M:Avalonia.Platform.IPlatformRenderInterfaceImportedImage.SnapshotWithTimelineSemaphores(Avalonia.Platform.IPlatformRenderInterfaceImportedSemaphore,System.UInt64,Avalonia.Platform.IPlatformRenderInterfaceImportedSemaphore,System.UInt64) - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + P:Avalonia.OpenGL.IGlExternalImageTexture.TextureType + baseline/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll + current/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll - CP0006 - M:Avalonia.Platform.ITextShaperImpl.CreateTypeface(Avalonia.Media.IGlyphTypeface) - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + CP0012 + M:Avalonia.Media.Fonts.FontCollectionBase.get_Count + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll - CP0006 - P:Avalonia.Media.IGlyphTypeface.CharacterToGlyphMap - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + CP0012 + M:Avalonia.Media.Fonts.FontCollectionBase.get_Item(System.Int32) + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll - CP0006 - P:Avalonia.Media.IGlyphTypeface.FaceNames - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + CP0012 + M:Avalonia.Media.Fonts.FontCollectionBase.GetEnumerator + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll - CP0006 - P:Avalonia.Media.IGlyphTypeface.FamilyNames - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + CP0012 + P:Avalonia.Media.Fonts.FontCollectionBase.Count + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll - CP0006 - P:Avalonia.Media.IGlyphTypeface.GlyphCount - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + CP0012 + P:Avalonia.Media.Fonts.FontCollectionBase.Item(System.Int32) + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll - CP0006 - P:Avalonia.Media.IGlyphTypeface.PlatformTypeface - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + CP0012 + M:Avalonia.Media.Fonts.FontCollectionBase.get_Count + baseline/Avalonia/lib/net6.0/Avalonia.Base.dll + current/Avalonia/lib/net6.0/Avalonia.Base.dll - CP0006 - P:Avalonia.Media.IGlyphTypeface.SupportedFeatures - baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + CP0012 + M:Avalonia.Media.Fonts.FontCollectionBase.get_Item(System.Int32) + baseline/Avalonia/lib/net6.0/Avalonia.Base.dll + current/Avalonia/lib/net6.0/Avalonia.Base.dll - CP0006 - P:Avalonia.Media.IGlyphTypeface.TextShaperTypeface + CP0012 + M:Avalonia.Media.Fonts.FontCollectionBase.GetEnumerator + baseline/Avalonia/lib/net6.0/Avalonia.Base.dll + current/Avalonia/lib/net6.0/Avalonia.Base.dll + + + CP0012 + P:Avalonia.Media.Fonts.FontCollectionBase.Count + baseline/Avalonia/lib/net6.0/Avalonia.Base.dll + current/Avalonia/lib/net6.0/Avalonia.Base.dll + + + CP0012 + P:Avalonia.Media.Fonts.FontCollectionBase.Item(System.Int32) + baseline/Avalonia/lib/net6.0/Avalonia.Base.dll + current/Avalonia/lib/net6.0/Avalonia.Base.dll + + + CP0012 + M:Avalonia.Media.Fonts.FontCollectionBase.get_Count + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0012 + M:Avalonia.Media.Fonts.FontCollectionBase.get_Item(System.Int32) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0012 + M:Avalonia.Media.Fonts.FontCollectionBase.GetEnumerator + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0012 + P:Avalonia.Media.Fonts.FontCollectionBase.Count + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0012 + P:Avalonia.Media.Fonts.FontCollectionBase.Item(System.Int32) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0012 + M:Avalonia.Media.Fonts.FontCollectionBase.get_Count baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - CP0006 - P:Avalonia.Media.IGlyphTypeface.TypographicFamilyName + CP0012 + M:Avalonia.Media.Fonts.FontCollectionBase.get_Item(System.Int32) baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - CP0006 - M:Avalonia.OpenGL.IGlExternalSemaphore.SignalTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64) - baseline/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll - current/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll + CP0012 + M:Avalonia.Media.Fonts.FontCollectionBase.GetEnumerator + baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - CP0006 - M:Avalonia.OpenGL.IGlExternalSemaphore.WaitTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64) - baseline/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll - current/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll + CP0012 + P:Avalonia.Media.Fonts.FontCollectionBase.Count + baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - CP0006 - P:Avalonia.OpenGL.IGlExternalImageTexture.TextureType - baseline/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll - current/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll + CP0012 + P:Avalonia.Media.Fonts.FontCollectionBase.Item(System.Int32) + baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll diff --git a/samples/TextTestApp/InteractiveLineControl.cs b/samples/TextTestApp/InteractiveLineControl.cs index 362be52ac3..7af977f9a1 100644 --- a/samples/TextTestApp/InteractiveLineControl.cs +++ b/samples/TextTestApp/InteractiveLineControl.cs @@ -336,7 +336,7 @@ namespace TextTestApp _textSource = new TextSource(this); RenderOptions.SetEdgeMode(this, EdgeMode.Aliased); - RenderOptions.SetTextRenderingMode(this, TextRenderingMode.SubpixelAntialias); + TextOptions.SetTextRenderingMode(this, TextRenderingMode.SubpixelAntialias); } private void InvalidateTextRunProperties() diff --git a/src/Avalonia.Base/Media/BaselinePixelAlignment.cs b/src/Avalonia.Base/Media/BaselinePixelAlignment.cs new file mode 100644 index 0000000000..37bd48c480 --- /dev/null +++ b/src/Avalonia.Base/Media/BaselinePixelAlignment.cs @@ -0,0 +1,26 @@ +namespace Avalonia.Media +{ + /// + /// Specifies the baseline pixel alignment options for rendering text or graphics. + /// + /// Use this enumeration to control whether the baseline of rendered content is aligned to the + /// pixel grid, which can affect visual crispness and positioning. The value may influence rendering quality, + /// especially at small font sizes or when precise alignment is required. + public enum BaselinePixelAlignment : byte + { + /// + /// The baseline pixel alignment is unspecified. + /// + Unspecified, + + /// + /// The baseline is aligned to the pixel grid. + /// + Aligned, + + /// + /// The baseline is not aligned to the pixel grid. + /// + Unaligned + } +} diff --git a/src/Avalonia.Base/Media/DrawingContext.cs b/src/Avalonia.Base/Media/DrawingContext.cs index bd2c43878d..0f73e4af98 100644 --- a/src/Avalonia.Base/Media/DrawingContext.cs +++ b/src/Avalonia.Base/Media/DrawingContext.cs @@ -284,7 +284,8 @@ namespace Avalonia.Media Clip, GeometryClip, OpacityMask, - RenderOptions + RenderOptions, + TextOptions } public RestoreState(DrawingContext context, PushedStateType type) @@ -311,6 +312,8 @@ namespace Avalonia.Media _context.PopOpacityMaskCore(); else if (_type == PushedStateType.RenderOptions) _context.PopRenderOptionsCore(); + else if (_type == PushedStateType.TextOptions) + _context.PopTextOptionsCore(); } } @@ -417,6 +420,20 @@ namespace Avalonia.Media } protected abstract void PushRenderOptionsCore(RenderOptions renderOptions); + /// + /// Pushes text options for the drawing context. + /// + /// The text options. + /// A disposable to undo the text options. + public PushedState PushTextOptions(TextOptions textOptions) + { + PushTextOptionsCore(textOptions); + _states ??= StateStackPool.Get(); + _states.Push(new RestoreState(this, RestoreState.PushedStateType.TextOptions)); + return new PushedState(this); + } + protected abstract void PushTextOptionsCore(TextOptions textOptions); + [Obsolete("Use PushTransform"), EditorBrowsable(EditorBrowsableState.Never)] public PushedState PushPreTransform(Matrix matrix) => PushTransform(matrix); [Obsolete("Use PushTransform"), EditorBrowsable(EditorBrowsableState.Never)] @@ -433,6 +450,7 @@ namespace Avalonia.Media protected abstract void PopOpacityMaskCore(); protected abstract void PopTransformCore(); protected abstract void PopRenderOptionsCore(); + protected abstract void PopTextOptionsCore(); private static bool PenIsVisible(IPen? pen) { diff --git a/src/Avalonia.Base/Media/DrawingGroup.cs b/src/Avalonia.Base/Media/DrawingGroup.cs index 7299bff850..75921196c0 100644 --- a/src/Avalonia.Base/Media/DrawingGroup.cs +++ b/src/Avalonia.Base/Media/DrawingGroup.cs @@ -54,6 +54,7 @@ namespace Avalonia.Media } internal RenderOptions? RenderOptions { get; set; } + internal TextOptions? TextOptions { get; set; } /// /// Gets or sets the collection that contains the child geometries. @@ -78,6 +79,7 @@ namespace Avalonia.Media using (ClipGeometry != null ? context.PushGeometryClip(ClipGeometry) : default) using (OpacityMask != null ? context.PushOpacityMask(OpacityMask, bounds) : default) using (RenderOptions != null ? context.PushRenderOptions(RenderOptions.Value) : default) + using (TextOptions != null ? context.PushTextOptions(TextOptions.Value) : default) { foreach (var drawing in Children) { @@ -325,6 +327,15 @@ namespace Avalonia.Media drawingGroup.RenderOptions = renderOptions; } + protected override void PushTextOptionsCore(TextOptions textOptions) + { + // Instantiate a new drawing group and set it as the _currentDrawingGroup + var drawingGroup = PushNewDrawingGroup(); + + // Set the text options on the new DrawingGroup + drawingGroup.TextOptions = textOptions; + } + protected override void PopClipCore() => Pop(); protected override void PopGeometryClipCore() => Pop(); @@ -337,6 +348,8 @@ namespace Avalonia.Media protected override void PopRenderOptionsCore() => Pop(); + protected override void PopTextOptionsCore() => Pop(); + /// /// Creates a new DrawingGroup for a Push* call by setting the /// _currentDrawingGroup to a newly instantiated DrawingGroup, diff --git a/src/Avalonia.Base/Media/PlatformDrawingContext.cs b/src/Avalonia.Base/Media/PlatformDrawingContext.cs index 312cae2c52..12eef33a6d 100644 --- a/src/Avalonia.Base/Media/PlatformDrawingContext.cs +++ b/src/Avalonia.Base/Media/PlatformDrawingContext.cs @@ -85,6 +85,8 @@ internal sealed class PlatformDrawingContext : DrawingContext } protected override void PushRenderOptionsCore(RenderOptions renderOptions) => _impl.PushRenderOptions(renderOptions); + + protected override void PushTextOptionsCore(TextOptions textOptions) => _impl.PushTextOptions(textOptions); protected override void PopClipCore() => _impl.PopClip(); @@ -99,6 +101,8 @@ internal sealed class PlatformDrawingContext : DrawingContext (_transforms ?? throw new ObjectDisposedException(nameof(PlatformDrawingContext))).Pop(); protected override void PopRenderOptionsCore() => _impl.PopRenderOptions(); + + protected override void PopTextOptionsCore() => _impl.PopTextOptions(); protected override void DisposeCore() { diff --git a/src/Avalonia.Base/Media/RenderOptions.cs b/src/Avalonia.Base/Media/RenderOptions.cs index 1ac2520919..0bde05418c 100644 --- a/src/Avalonia.Base/Media/RenderOptions.cs +++ b/src/Avalonia.Base/Media/RenderOptions.cs @@ -1,13 +1,48 @@ -using Avalonia.Media.Imaging; +using System; +using Avalonia.Media.Imaging; namespace Avalonia.Media { + /// + /// Provides a set of options that control rendering behavior for visuals, including text rendering, bitmap + /// interpolation, edge rendering, blending, and opacity handling. + /// + /// Use this structure to specify rendering preferences for visual elements. Each property + /// corresponds to a specific aspect of rendering, allowing fine-grained control over how content is displayed. + /// These options can be applied to visuals to influence quality, performance, and visual effects. When merging two + /// instances, unspecified values are inherited from the other instance, enabling layered configuration. public readonly record struct RenderOptions { + /// + /// Gets the text rendering mode used to control how text glyphs are rendered. + /// + [Obsolete("TextRenderingMode is obsolete. Use TextOptions.TextRenderingMode instead.")] + public TextRenderingMode TextRenderingMode { get; init; } + + /// + /// Gets the interpolation mode used when rendering bitmap images. + /// + /// The interpolation mode determines how bitmap images are scaled or transformed during + /// rendering. Selecting an appropriate mode can affect image quality and performance. + /// public BitmapInterpolationMode BitmapInterpolationMode { get; init; } + + /// + /// Gets the edge rendering mode used for drawing operations. + /// public EdgeMode EdgeMode { get; init; } - public TextRenderingMode TextRenderingMode { get; init; } + + /// + /// Gets the blending mode used when rendering bitmap images. + /// + /// The blending mode determines how bitmap pixels are composited with the background or + /// other images. Select an appropriate mode based on the desired visual effect, such as transparency or + /// additive blending. public BitmapBlendingMode BitmapBlendingMode { get; init; } + + /// + /// Gets a value indicating whether full opacity handling is required for the associated content. + /// public bool? RequiresFullOpacityHandling { get; init; } /// @@ -75,6 +110,7 @@ namespace Avalonia.Media /// /// The control. /// The value. + [Obsolete("TextRenderingMode is obsolete. Use TextOptions.TextRenderingMode instead.")] public static TextRenderingMode GetTextRenderingMode(Visual visual) { return visual.RenderOptions.TextRenderingMode; @@ -85,6 +121,7 @@ namespace Avalonia.Media /// /// The control. /// The value. + [Obsolete("TextRenderingMode is obsolete. Use TextOptions.TextRenderingMode instead.")] public static void SetTextRenderingMode(Visual visual, TextRenderingMode value) { visual.RenderOptions = visual.RenderOptions with { TextRenderingMode = value }; @@ -126,11 +163,15 @@ namespace Avalonia.Media edgeMode = other.EdgeMode; } +#pragma warning disable CS0618 var textRenderingMode = TextRenderingMode; +#pragma warning restore CS0618 if (textRenderingMode == TextRenderingMode.Unspecified) { +#pragma warning disable CS0618 textRenderingMode = other.TextRenderingMode; +#pragma warning disable CS0618 } var bitmapBlendingMode = BitmapBlendingMode; diff --git a/src/Avalonia.Base/Media/TextHintingMode.cs b/src/Avalonia.Base/Media/TextHintingMode.cs new file mode 100644 index 0000000000..0095bf04e9 --- /dev/null +++ b/src/Avalonia.Base/Media/TextHintingMode.cs @@ -0,0 +1,31 @@ +namespace Avalonia.Media +{ + /// + /// Specifies the level of hinting applied to text glyphs during rendering. + /// Text hinting adjusts glyph outlines to improve readability and crispness, + /// especially at small font sizes or low DPI. This enum controls the amount + /// of grid-fitting and outline adjustment performed. + /// + public enum TextHintingMode : byte + { + /// + /// Hinting mode is not explicitly specified. The default will be used. + /// + Unspecified, + + /// + /// No hinting, outlines are scaled only. + /// + None, + + /// + /// Minimal hinting, preserves glyph shape. + /// + Light, + + /// + /// Aggressive grid-fitting, maximum crispness at low DPI. + /// + Strong + } +} diff --git a/src/Avalonia.Base/Media/TextOptions.cs b/src/Avalonia.Base/Media/TextOptions.cs new file mode 100644 index 0000000000..d8d33b5f16 --- /dev/null +++ b/src/Avalonia.Base/Media/TextOptions.cs @@ -0,0 +1,136 @@ +namespace Avalonia.Media +{ + /// + /// Provides options for controlling text rendering behavior, including rendering mode, hinting mode, and baseline + /// pixel alignment. Used to configure how text appears within visual elements. + /// + /// TextOptions encapsulates settings that influence the clarity, sharpness, and positioning of + /// rendered text. These options can be applied to visual elements to customize text appearance for different + /// display scenarios, such as optimizing for readability at small font sizes or ensuring pixel-perfect alignment. + /// The struct supports merging with other instances to inherit unspecified values, and exposes attached properties + /// for use with visuals. + public readonly record struct TextOptions + { + /// + /// Gets the text rendering mode used to control how text glyphs are rendered. + /// + public TextRenderingMode TextRenderingMode { get; init; } + + /// + /// Gets the text rendering hinting mode used to optimize the display of text. + /// + /// The hinting mode determines how text is rendered to improve clarity and readability, + /// especially at small font sizes. Changing this value may affect the appearance of text depending on the + /// rendering engine and display device. + public TextHintingMode TextHintingMode { get; init; } + + /// + /// Gets a value indicating whether the text baseline should be aligned to the pixel grid. + /// + /// + /// When enabled, the vertical position of the text baseline is snapped to whole pixel boundaries. + /// This ensures consistent sharpness and reduces blurriness caused by fractional positioning, + /// particularly at small font sizes or low DPI settings. + /// + public BaselinePixelAlignment BaselinePixelAlignment { get; init; } + + /// + /// Merges this instance with using inheritance semantics: unspecified values on this + /// instance are taken from . + /// + public TextOptions MergeWith(TextOptions other) + { + var textRenderingMode = TextRenderingMode; + + if (textRenderingMode == TextRenderingMode.Unspecified) + { + textRenderingMode = other.TextRenderingMode; + } + + var textHintingMode = TextHintingMode; + + if (textHintingMode == TextHintingMode.Unspecified) + { + textHintingMode = other.TextHintingMode; + } + + var baselinePixelAlignment = BaselinePixelAlignment; + + if (baselinePixelAlignment == BaselinePixelAlignment.Unspecified) + { + baselinePixelAlignment = other.BaselinePixelAlignment; + } + + return new TextOptions + { + TextRenderingMode = textRenderingMode, + TextHintingMode = textHintingMode, + BaselinePixelAlignment = baselinePixelAlignment + }; + } + + /// + /// Gets the TextOptions attached value for a visual. + /// + public static TextOptions GetTextOptions(Visual visual) + { + return visual.TextOptions; + } + + /// + /// Sets the TextOptions attached value for a visual. + /// + public static void SetTextOptions(Visual visual, TextOptions value) + { + visual.TextOptions = value; + } + + /// + /// Gets the TextRenderingMode attached property for a visual. + /// + public static TextRenderingMode GetTextRenderingMode(Visual visual) + { + return visual.TextOptions.TextRenderingMode; + } + + /// + /// Sets the TextRenderingMode attached property for a visual. + /// + public static void SetTextRenderingMode(Visual visual, TextRenderingMode value) + { + visual.TextOptions = visual.TextOptions with { TextRenderingMode = value }; + } + + /// + /// Gets the TextHintingMode attached property for a visual. + /// + public static TextHintingMode GetTextHintingMode(Visual visual) + { + return visual.TextOptions.TextHintingMode; + } + + /// + /// Sets the TextHintingMode attached property for a visual. + /// + public static void SetTextHintingMode(Visual visual, TextHintingMode value) + { + visual.TextOptions = visual.TextOptions with { TextHintingMode = value }; + } + + /// + /// Gets the BaselinePixelAlignment attached property for a visual. + /// + public static BaselinePixelAlignment GetBaselinePixelAlignment(Visual visual) + { + return visual.TextOptions.BaselinePixelAlignment; + } + + /// + /// Sets the BaselinePixelAlignment attached property for a visual. + /// + public static void SetBaselinePixelAlignment(Visual visual, BaselinePixelAlignment value) + { + visual.TextOptions = visual.TextOptions with { BaselinePixelAlignment = value }; + } + } +} diff --git a/src/Avalonia.Base/Media/TextRenderingMode.cs b/src/Avalonia.Base/Media/TextRenderingMode.cs index 927d2bce73..dfc084720f 100644 --- a/src/Avalonia.Base/Media/TextRenderingMode.cs +++ b/src/Avalonia.Base/Media/TextRenderingMode.cs @@ -1,11 +1,36 @@ namespace Avalonia.Media { + /// + /// Specifies how text glyphs are rendered in Avalonia. + /// Controls the smoothing and antialiasing applied during text rasterization. + /// public enum TextRenderingMode : byte { + /// + /// Rendering mode is not explicitly specified. + /// The system or platform default will be used. + /// Unspecified, - SubpixelAntialias, + /// + /// Glyphs are rendered with subpixel antialiasing. + /// This provides higher apparent resolution on LCD screens + /// by using the individual red, green, and blue subpixels. + /// + SubpixelAntialias, + + /// + /// Glyphs are rendered with standard grayscale antialiasing. + /// This smooths edges without using subpixel information, + /// preserving shape fidelity across different display types. + /// Antialias, + + /// + /// Glyphs are rendered without antialiasing. + /// This produces sharp, aliased edges and may be useful + /// for pixel-art aesthetics or low-DPI environments. + /// Alias } } diff --git a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs index 1a813f7bbe..848620dae2 100644 --- a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs +++ b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs @@ -195,6 +195,17 @@ namespace Avalonia.Platform /// void PopRenderOptions(); + /// + /// Pushes text options for the drawing context. + /// + /// The text options. + void PushTextOptions(TextOptions textOptions); + + /// + /// Pops the latest text options. + /// + void PopTextOptions(); + /// /// Attempts to get an optional feature from the drawing context implementation. /// diff --git a/src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataNodes.cs b/src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataNodes.cs index ac0a5c4cb7..c7d10f69a1 100644 --- a/src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataNodes.cs +++ b/src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataNodes.cs @@ -232,3 +232,18 @@ class RenderDataRenderOptionsNode : RenderDataPushNode context.Context.PopRenderOptions(); } } + +class RenderDataTextOptionsNode : RenderDataPushNode +{ + public TextOptions TextOptions { get; set; } + + public override void Push(ref RenderDataNodeRenderContext context) + { + context.Context.PushTextOptions(TextOptions); + } + + public override void Pop(ref RenderDataNodeRenderContext context) + { + context.Context.PopTextOptions(); + } +} diff --git a/src/Avalonia.Base/Rendering/Composition/Drawing/RenderDataDrawingContext.cs b/src/Avalonia.Base/Rendering/Composition/Drawing/RenderDataDrawingContext.cs index 870a084d31..ead6a3f730 100644 --- a/src/Avalonia.Base/Rendering/Composition/Drawing/RenderDataDrawingContext.cs +++ b/src/Avalonia.Base/Rendering/Composition/Drawing/RenderDataDrawingContext.cs @@ -264,6 +264,10 @@ internal class RenderDataDrawingContext : DrawingContext RenderOptions = renderOptions }); + protected override void PushTextOptionsCore(TextOptions textOptions) => Push(new RenderDataTextOptionsNode() + { + TextOptions = textOptions + }); protected override void PopClipCore() => Pop(); @@ -277,6 +281,8 @@ internal class RenderDataDrawingContext : DrawingContext protected override void PopRenderOptionsCore() => Pop(); + protected override void PopTextOptionsCore() => Pop(); + internal override void DrawBitmap(IRef? source, double opacity, Rect sourceRect, Rect destRect) { if (source == null || sourceRect.IsEmpty() || destRect.IsEmpty()) diff --git a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.PendingCommands.cs b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.PendingCommands.cs index ee0447629a..d26aec0d69 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.PendingCommands.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.PendingCommands.cs @@ -22,6 +22,7 @@ internal partial class CompositorDrawingContextProxy PushOpacityMask, PushGeometryClip, PushRenderOptions, + PushTextOptions, PushEffect } @@ -43,6 +44,7 @@ internal partial class CompositorDrawingContextProxy [FieldOffset(0)] public Matrix Transform; [FieldOffset(0)] public RenderOptions RenderOptions; + [FieldOffset(0)] public TextOptions TextOptions; // PushClip/PushOpacityMask [FieldOffset(0)] public bool IsRoundRect; @@ -148,6 +150,8 @@ internal partial class CompositorDrawingContextProxy } else if (cmd.Type == PendingCommandType.PushRenderOptions) _impl.PushRenderOptions(cmd.DataUnion.RenderOptions); + else if (cmd.Type == PendingCommandType.PushTextOptions) + _impl.PushTextOptions(cmd.DataUnion.TextOptions); else Debug.Assert(false); } diff --git a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs index 6b4982c490..81041a659d 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs @@ -260,6 +260,18 @@ internal partial class CompositorDrawingContextProxy : IDrawingContextImpl, }); } + public void PushTextOptions(TextOptions textOptions) + { + AddCommand(new() + { + Type = PendingCommandType.PushTextOptions, + DataUnion = + { + TextOptions = textOptions + } + }); + } + public void PopRenderOptions() { if (!TryDiscardOrFlush(PendingCommandType.PushRenderOptions)) @@ -269,6 +281,15 @@ internal partial class CompositorDrawingContextProxy : IDrawingContextImpl, } } + public void PopTextOptions() + { + if (!TryDiscardOrFlush(PendingCommandType.PushTextOptions)) + { + _impl.PopTextOptions(); + RestoreTransform(); + } + } + public object? GetFeature(Type t) { Flush(); diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs index c2d43f5667..9225dd6ac6 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs @@ -62,6 +62,12 @@ namespace Avalonia.Rendering.Composition.Server if (applyRenderOptions) canvas.PushRenderOptions(RenderOptions); + + var applyTextOptions = TextOptions != default; + + if (applyTextOptions) + canvas.PushTextOptions(TextOptions); + var needPopEffect = PushEffect(canvas); if (Opacity != 1) @@ -88,7 +94,9 @@ namespace Avalonia.Rendering.Composition.Server if (needPopEffect) canvas.PopEffect(); - if(applyRenderOptions) + if (applyTextOptions) + canvas.PopTextOptions(); + if (applyRenderOptions) canvas.PopRenderOptions(); } diff --git a/src/Avalonia.Base/Rendering/ImmediateRenderer.cs b/src/Avalonia.Base/Rendering/ImmediateRenderer.cs index d308f532da..f054e77db8 100644 --- a/src/Avalonia.Base/Rendering/ImmediateRenderer.cs +++ b/src/Avalonia.Base/Rendering/ImmediateRenderer.cs @@ -50,6 +50,7 @@ internal class ImmediateRenderer transform = Matrix.CreateTranslation(bounds.Position); } + using (visual.TextOptions != default ? context.PushTextOptions(visual.TextOptions) : default(DrawingContext.PushedState?)) using (visual.RenderOptions != default ? context.PushRenderOptions(visual.RenderOptions) : default(DrawingContext.PushedState?)) using (context.PushTransform(transform)) using (visual.HasMirrorTransform ? context.PushTransform(new Matrix(-1.0, 0.0, 0.0, 1.0, visual.Bounds.Width, 0)) : default(DrawingContext.PushedState?)) diff --git a/src/Avalonia.Base/Visual.Composition.cs b/src/Avalonia.Base/Visual.Composition.cs index f521f2a3f2..a1f90a5407 100644 --- a/src/Avalonia.Base/Visual.Composition.cs +++ b/src/Avalonia.Base/Visual.Composition.cs @@ -148,6 +148,7 @@ public partial class Visual comp.Effect = Effect?.ToImmutable(); comp.RenderOptions = RenderOptions; + comp.TextOptions = TextOptions; var renderTransform = Matrix.Identity; @@ -163,4 +164,4 @@ public partial class Visual comp.TransformMatrix = renderTransform; } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Visual.cs b/src/Avalonia.Base/Visual.cs index 6b10fa228c..ee960cee1e 100644 --- a/src/Avalonia.Base/Visual.cs +++ b/src/Avalonia.Base/Visual.cs @@ -1,5 +1,3 @@ - - #nullable enable using System; @@ -39,7 +37,7 @@ namespace Avalonia /// public static readonly DirectProperty BoundsProperty = AvaloniaProperty.RegisterDirect(nameof(Bounds), o => o.Bounds); - + /// /// Defines the property. /// @@ -51,7 +49,7 @@ namespace Avalonia /// public static readonly StyledProperty ClipProperty = AvaloniaProperty.Register(nameof(Clip)); - + /// /// Defines the property. /// @@ -69,7 +67,7 @@ namespace Avalonia /// public static readonly StyledProperty OpacityMaskProperty = AvaloniaProperty.Register(nameof(OpacityMask)); - + /// /// Defines the property. /// @@ -113,7 +111,7 @@ namespace Avalonia /// public static readonly StyledProperty ZIndexProperty = AvaloniaProperty.Register(nameof(ZIndex)); - + private static readonly WeakEvent InvalidatedWeakEvent = WeakEvent.Register( (s, h) => s.Invalidated += h, @@ -124,6 +122,8 @@ namespace Avalonia private Visual? _visualParent; private bool _hasMirrorTransform; private TargetWeakEventSubscriber? _affectsRenderWeakSubscriber; + private RenderOptions _renderOptions; + private TextOptions _textOptions; /// /// Initializes static members of the class. @@ -201,7 +201,7 @@ namespace Avalonia /// Gets a value indicating whether this control and all its parents are visible. /// public bool IsEffectivelyVisible { get; private set; } = true; - + /// /// Updates the property based on the parent's /// . @@ -218,7 +218,7 @@ namespace Avalonia // PERF-SENSITIVE: This is called on entire hierarchy and using foreach or LINQ // will cause extra allocations and overhead. - + var children = VisualChildren; // ReSharper disable once ForCanBeConvertedToForeach @@ -255,7 +255,7 @@ namespace Avalonia get { return GetValue(OpacityMaskProperty); } set { SetValue(OpacityMaskProperty, value); } } - + /// /// Gets or sets the effect of the control. /// @@ -269,8 +269,8 @@ namespace Avalonia /// /// Gets or sets a value indicating whether to apply mirror transform on this control. /// - public bool HasMirrorTransform - { + public bool HasMirrorTransform + { get { return _hasMirrorTransform; } protected set { SetAndRaise(HasMirrorTransformProperty, ref _hasMirrorTransform, value); } } @@ -326,7 +326,25 @@ namespace Avalonia /// protected internal IRenderRoot? VisualRoot => _visualRoot; - internal RenderOptions RenderOptions { get; set; } + internal RenderOptions RenderOptions + { + get => _renderOptions; + set + { + _renderOptions = value; + InvalidateVisual(); + } + } + + internal TextOptions TextOptions + { + get => _textOptions; + set + { + _textOptions = value; + InvalidateVisual(); + } + } internal bool HasNonUniformZIndexChildren { get; private set; } @@ -413,8 +431,8 @@ namespace Avalonia sender.InvalidateVisual(); } }); - - + + var invalidateAndSubscribeObserver = new AnonymousObserver( static e => { @@ -466,7 +484,7 @@ namespace Avalonia if (change.Property == IsVisibleProperty) { UpdateIsEffectivelyVisible(VisualParent?.IsEffectivelyVisible ?? true); - } + } else if (change.Property == FlowDirectionProperty) { InvalidateMirrorTransform(); @@ -477,7 +495,7 @@ namespace Avalonia } } } - + protected override void LogicalChildrenCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { base.LogicalChildrenCollectionChanged(sender, e); @@ -515,12 +533,12 @@ namespace Avalonia OnAttachedToVisualTree(e); AttachedToVisualTree?.Invoke(this, e); InvalidateVisual(); - + _visualRoot.Renderer.RecalculateChildren(_visualParent); - + if (ZIndex != 0) _visualParent.HasNonUniformZIndexChildren = true; - + var visualChildren = VisualChildren; var visualChildrenCount = visualChildren.Count; @@ -617,7 +635,7 @@ namespace Avalonia { newTransform.Changed += sender.RenderTransformChanged; } - + sender.InvalidateVisual(); } } @@ -651,7 +669,7 @@ namespace Avalonia var parent = sender?.VisualParent; if (sender?.ZIndex != 0 && parent is Visual parentVisual) parentVisual.HasNonUniformZIndexChildren = true; - + sender?.InvalidateVisual(); parent?.VisualRoot?.Renderer.RecalculateChildren(parent); } @@ -721,7 +739,7 @@ namespace Avalonia break; } } - + private static void SetVisualParent(IList children, Visual? parent) { var count = children.Count; @@ -729,7 +747,7 @@ namespace Avalonia for (var i = 0; i < count; i++) { var visual = (Visual) children[i]!; - + visual.SetVisualParent(parent); } } diff --git a/src/Avalonia.Base/composition-schema.xml b/src/Avalonia.Base/composition-schema.xml index ce989296b1..dd1d284cd5 100644 --- a/src/Avalonia.Base/composition-schema.xml +++ b/src/Avalonia.Base/composition-schema.xml @@ -34,6 +34,7 @@ + diff --git a/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs index e2c406998a..4bcc81846c 100644 --- a/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs +++ b/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs @@ -559,12 +559,22 @@ namespace Avalonia.Headless public void PushRenderOptions(RenderOptions renderOptions) { - + } public void PopRenderOptions() { - + + } + + public void PushTextOptions(TextOptions textOptions) + { + // No-op in headless stub + } + + public void PopTextOptions() + { + // No-op in headless stub } } diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index e122c21242..42448ed631 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -27,6 +27,7 @@ namespace Avalonia.Skia private readonly Stack<(SKMatrix matrix, PaintWrapper paint)> _maskStack = new(); private readonly Stack _opacityStack = new(); private readonly Stack _renderOptionsStack = new(); + private readonly Stack _textOptionsStack = new(); private readonly Matrix? _postTransform; private double _currentOpacity = 1.0f; private readonly bool _disableSubpixelTextRendering; @@ -223,6 +224,7 @@ namespace Avalonia.Skia public SKSurface? Surface { get; } public RenderOptions RenderOptions { get; set; } + public TextOptions TextOptions { get; set; } private void CheckLease() { @@ -604,23 +606,32 @@ namespace Avalonia.Skia { var glyphRunImpl = (GlyphRunImpl)glyphRun; - var textRenderOptions = RenderOptions; + // Determine effective TextOptions for text rendering. Start with current pushed TextOptions. + var effectiveTextOptions = TextOptions; + // If subpixel rendering is disabled globally, map subpixel modes to grayscale. if (_disableSubpixelTextRendering) { - switch (textRenderOptions.TextRenderingMode) + var mode = effectiveTextOptions.TextRenderingMode; + + if (mode == TextRenderingMode.SubpixelAntialias || + (mode == TextRenderingMode.Unspecified && (RenderOptions.EdgeMode == EdgeMode.Antialias || RenderOptions.EdgeMode == EdgeMode.Unspecified))) { - case TextRenderingMode.Unspecified - when textRenderOptions.EdgeMode == EdgeMode.Antialias || textRenderOptions.EdgeMode == EdgeMode.Unspecified: - case TextRenderingMode.SubpixelAntialias: - { - textRenderOptions = textRenderOptions with { TextRenderingMode = TextRenderingMode.Antialias }; - break; - } + effectiveTextOptions = effectiveTextOptions with { TextRenderingMode = TextRenderingMode.Antialias }; } } - var textBlob = glyphRunImpl.GetTextBlob(textRenderOptions); + var renderOptions = RenderOptions; + + // If TextRenderingMode is unspecified in TextOptions, use the one from RenderOptions. +#pragma warning disable CS0618 + if (effectiveTextOptions.TextRenderingMode == TextRenderingMode.Unspecified && renderOptions.TextRenderingMode != TextRenderingMode.Unspecified) + { + effectiveTextOptions = effectiveTextOptions with { TextRenderingMode = renderOptions.TextRenderingMode }; + } +#pragma warning restore CS0618 + + var textBlob = glyphRunImpl.GetTextBlob(effectiveTextOptions, RenderOptions); Canvas.DrawText(textBlob, (float)glyphRun.BaselineOrigin.X, (float)glyphRun.BaselineOrigin.Y, paintWrapper.Paint); @@ -755,11 +766,25 @@ namespace Avalonia.Skia RenderOptions = RenderOptions.MergeWith(renderOptions); } + public void PushTextOptions(TextOptions textOptions) + { + CheckLease(); + + _textOptionsStack.Push(TextOptions); + + TextOptions = TextOptions.MergeWith(textOptions); + } + public void PopRenderOptions() { RenderOptions = _renderOptionsStack.Pop(); } + public void PopTextOptions() + { + TextOptions = _textOptionsStack.Pop(); + } + /// public virtual void Dispose() { diff --git a/src/Skia/Avalonia.Skia/GlyphRunImpl.cs b/src/Skia/Avalonia.Skia/GlyphRunImpl.cs index 30f6da1dc2..e7f772190d 100644 --- a/src/Skia/Avalonia.Skia/GlyphRunImpl.cs +++ b/src/Skia/Avalonia.Skia/GlyphRunImpl.cs @@ -15,13 +15,9 @@ namespace Avalonia.Skia private readonly ushort[] _glyphIndices; private readonly SKPoint[] _glyphPositions; - // We use an array as opposed to a ConcurrentDictionary to prevent a large amount of lock object allocations. - // This is possible because the SKFontEdging enum has consecutive integer elements 0, 1, 2, etc. and thus - // can be mapped directly to array indices. - // - // Should Skia update the enum with more elements, then the size of this array should be updated appropriately. - private const int FontEdgingsCount = (int)SKFontEdging.SubpixelAntialias + 1; - private readonly SKTextBlob?[] _textBlobCache = new SKTextBlob?[FontEdgingsCount]; + // A two level cache optimized for single-entry read. Uses TextOptions as a key. + private readonly TwoLevelCache _textBlobCache = + new TwoLevelCache(secondarySize: 3, evictionAction: b => b?.Dispose()); public GlyphRunImpl(GlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos, Point baselineOrigin) @@ -62,7 +58,14 @@ namespace Avalonia.Skia // But the bounds depends on the edging: for now, always use SubpixelAntialias so we have consistent values. // The resulting bounds may be shifted by 1px on some fonts: // "F" text with Inter size 14 has a 0px left bound with SubpixelAntialias but 1px with Antialias. - using var font = CreateFont(SKFontEdging.SubpixelAntialias); + var defaultTextOptions = default(TextOptions) with + { + TextRenderingMode = TextRenderingMode.SubpixelAntialias, + TextHintingMode = TextHintingMode.Strong, + BaselinePixelAlignment = BaselinePixelAlignment.Unaligned + }; + + using var font = CreateFont(defaultTextOptions); var runBounds = new Rect(); var glyphBounds = ArrayPool.Shared.Rent(count); @@ -92,26 +95,19 @@ namespace Avalonia.Skia public Rect Bounds { get; } - public SKTextBlob GetTextBlob(RenderOptions renderOptions) + public SKTextBlob GetTextBlob(TextOptions textOptions, RenderOptions renderOptions) { - var edging = SKFontEdging.SubpixelAntialias; - - switch (renderOptions.TextRenderingMode) + if (textOptions.TextRenderingMode == TextRenderingMode.Unspecified) { - case TextRenderingMode.Alias: - edging = SKFontEdging.Alias; - break; - case TextRenderingMode.Antialias: - edging = SKFontEdging.Antialias; - break; - case TextRenderingMode.Unspecified: - edging = renderOptions.EdgeMode == EdgeMode.Aliased ? SKFontEdging.Alias : SKFontEdging.SubpixelAntialias; - break; + textOptions = textOptions with + { + TextRenderingMode = renderOptions.EdgeMode == EdgeMode.Aliased ? TextRenderingMode.Alias : TextRenderingMode.SubpixelAntialias + }; } - if (_textBlobCache[(int)edging] is null) + return _textBlobCache.GetOrAdd(textOptions, k => { - using var font = CreateFont(edging); + using var font = CreateFont(textOptions); var builder = SKTextBlobBuilderCache.Shared.Get(); @@ -121,37 +117,59 @@ namespace Avalonia.Skia runBuffer.SetGlyphs(_glyphIndices); var textBlob = builder.Build()!; - SKTextBlobBuilderCache.Shared.Return(builder); - - Interlocked.CompareExchange(ref _textBlobCache[(int)edging], textBlob, null); - } - - return _textBlobCache[(int)edging]!; + return textBlob; + }); } - private SKFont CreateFont(SKFontEdging edging) + private SKFont CreateFont(TextOptions textOptions) { + // Determine edging from TextRenderingMode + var edging = textOptions.TextRenderingMode switch + { + TextRenderingMode.Alias => SKFontEdging.Alias, + TextRenderingMode.Antialias => SKFontEdging.Antialias, + TextRenderingMode.SubpixelAntialias => SKFontEdging.SubpixelAntialias, + _ => SKFontEdging.SubpixelAntialias + }; + + // Determine hinting + var hinting = textOptions.TextHintingMode switch + { + TextHintingMode.None => SKFontHinting.None, + TextHintingMode.Light => SKFontHinting.Slight, + TextHintingMode.Strong => SKFontHinting.Full, + _ => SKFontHinting.Full, + }; + + // Force auto-hinting for "Slight" mode (prefer autohinter over bytecode hints), otherwise default. + var forceAutoHinting = textOptions.TextHintingMode == TextHintingMode.Light; + + // Subpixel rendering enabled when edging is not alias. + var subpixel = edging != SKFontEdging.Alias; + + // Baseline snap defaults to true unless explicitly disabled. + var baselineSnap = textOptions.BaselinePixelAlignment != BaselinePixelAlignment.Unaligned; + var font = _glyphTypefaceImpl.CreateSKFont((float)FontRenderingEmSize); - font.Hinting = SKFontHinting.Full; - font.Subpixel = edging != SKFontEdging.Alias; + font.ForceAutoHinting = forceAutoHinting; + font.Hinting = hinting; + font.Subpixel = subpixel; font.Edging = edging; + font.BaselineSnap = baselineSnap; return font; } public void Dispose() { - foreach (var textBlob in _textBlobCache) - { - textBlob?.Dispose(); - } + _textBlobCache.ClearAndDispose(); } public IReadOnlyList GetIntersections(float lowerLimit, float upperLimit) { - var textBlob = GetTextBlob(default); + var textBlob = GetTextBlob(default, default); return textBlob.GetIntercepts(lowerLimit, upperLimit); } diff --git a/src/Skia/Avalonia.Skia/TwoLevelCache.cs b/src/Skia/Avalonia.Skia/TwoLevelCache.cs new file mode 100644 index 0000000000..cfc14fae4c --- /dev/null +++ b/src/Skia/Avalonia.Skia/TwoLevelCache.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; + +namespace Avalonia.Skia +{ + /// + /// Provides a lightweight two-level cache for storing key-value pairs, supporting fast retrieval and optional + /// eviction handling. + /// + /// The cache maintains a primary entry for the most recently added item and a secondary array + /// for additional items, with a configurable capacity. When the cache exceeds its capacity, evicted values can be + /// processed using an optional eviction action. This class is intended for internal use and is not + /// thread-safe. + /// The type of keys used to identify cached values. Must be non-nullable. + /// The type of values to be stored in the cache. Must be a reference type. + internal class TwoLevelCache + where TKey : notnull + where TValue : class + { + private readonly int _secondarySize; + private TKey? _primaryKey; + private TValue? _primaryValue; + private KeyValuePair[]? _secondary; + private int _secondaryCount; + private readonly Action? _evictionAction; + private readonly IEqualityComparer _comparer; + + public TwoLevelCache(int secondarySize = 3, Action? evictionAction = null, IEqualityComparer? comparer = null) + { + if (secondarySize < 0) + { + throw new ArgumentOutOfRangeException(nameof(secondarySize)); + } + + _secondarySize = secondarySize; + _evictionAction = evictionAction; + _comparer = comparer ?? EqualityComparer.Default; + } + + public bool TryGet(TKey key, out TValue? value) + { + if (_primaryValue != null && _comparer.Equals(_primaryKey!, key)) + { + value = _primaryValue; + return true; + } + + var sec = _secondary; + if (sec != null) + { + for (int i = 0; i < _secondaryCount; i++) + { + if (_comparer.Equals(sec[i].Key, key)) + { + value = sec[i].Value; + return true; + } + } + } + + value = null; + return false; + } + + public TValue GetOrAdd(TKey key, Func factory) + { + // Check if key already exists + if (TryGet(key, out var existing) && existing != null) + { + return existing; + } + + // Key doesn't exist, create new value + var value = factory(key); + + // Primary is empty - store in primary + if (_primaryValue == null) + { + _primaryKey = key; + _primaryValue = value; + return value; + } + + // No secondary cache configured - replace primary + if (_secondarySize == 0) + { + _evictionAction?.Invoke(_primaryValue); + _primaryKey = key; + _primaryValue = value; + return value; + } + + // Secondary not yet initialized - create it + if (_secondary == null) + { + _secondary = new KeyValuePair[_secondarySize]; + _secondaryCount = 0; + } + + // Shift existing entries right and insert new one at front + // This maintains insertion order and evicts the oldest (last) entry when full + TValue? evicted = default; + bool shouldEvict = _secondaryCount == _secondarySize; + + if (shouldEvict) + { + // Cache is full, last entry will be evicted + evicted = _secondary[_secondarySize - 1].Value; + } + + // Shift existing entries to make room at index 0 + int shiftCount = shouldEvict ? _secondarySize - 1 : _secondaryCount; + for (int i = shiftCount; i > 0; i--) + { + _secondary[i] = _secondary[i - 1]; + } + + // Insert new entry at front + _secondary[0] = new KeyValuePair(key, value); + + // Update count (capped at size) + if (_secondaryCount < _secondarySize) + { + _secondaryCount++; + } + + // Invoke eviction action if we evicted an entry + if (shouldEvict) + { + _evictionAction?.Invoke(evicted); + } + + return value; + } + + public void ClearAndDispose() + { + if (_primaryValue != null) + { + _evictionAction?.Invoke(_primaryValue); + _primaryValue = null; + _primaryKey = default; + } + + if (_secondary != null) + { + for (int i = 0; i < _secondaryCount; i++) + { + _evictionAction?.Invoke(_secondary[i].Value); + } + + _secondary = null; + _secondaryCount = 0; + } + } + } +} diff --git a/tests/Avalonia.Controls.UnitTests/Shapes/ShapeTests.cs b/tests/Avalonia.Controls.UnitTests/Shapes/ShapeTests.cs index ac5dded8fc..b7b5604135 100644 --- a/tests/Avalonia.Controls.UnitTests/Shapes/ShapeTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Shapes/ShapeTests.cs @@ -180,6 +180,10 @@ public class ShapeTests : ScopedTestBase protected override void PushRenderOptionsCore(RenderOptions renderOptions) { + } + + protected override void PushTextOptionsCore(TextOptions textOptions) + { } protected override void PushTransformCore(Matrix matrix) @@ -210,6 +214,10 @@ public class ShapeTests : ScopedTestBase { } + protected override void PopTextOptionsCore() + { + } + protected override void DisposeCore() { } diff --git a/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs b/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs index 45b8c5d2ce..deecc9eae3 100644 --- a/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs +++ b/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs @@ -385,6 +385,44 @@ namespace Avalonia.Skia.RenderTests FontFamily = new FontFamily(symbolsFont) }; } + + [InlineData(TextRenderingMode.Antialias, TextHintingMode.None)] + [InlineData(TextRenderingMode.Alias, TextHintingMode.None)] + [InlineData(TextRenderingMode.Antialias, TextHintingMode.Light)] + [InlineData(TextRenderingMode.Alias, TextHintingMode.Light)] + [Win32Theory("Depends on the backend")] + public async Task Should_Render_TextBlock_With_TextOptions( + TextRenderingMode textRenderingMode, + TextHintingMode textHintingMode) + { + var textBlock = new TextBlock + { + FontFamily = TestFontFamily, + FontSize = 24, + Foreground = Brushes.Black, + Text = "TextOptions", + Background = Brushes.LightGray, + Padding = new Thickness(10) + }; + + TextOptions.SetTextOptions(textBlock, new TextOptions + { + TextRenderingMode = textRenderingMode, + TextHintingMode = textHintingMode + }); + + var target = new Border + { + Width = 300, + Height = 100, + Background = Brushes.White, + Child = textBlock + }; + + var testName = $"Should_Render_TextBlock_With_TextOptions_{textRenderingMode}_{textHintingMode}"; + await RenderToFile(target, testName); + CompareImages(testName); + } } } diff --git a/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs b/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs index b28762d08d..ad0cf67d2f 100644 --- a/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs +++ b/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs @@ -115,7 +115,7 @@ namespace Avalonia.Skia.RenderTests [TextElement.ForegroundProperty] = new SolidColorBrush { Color = Colors.Black } }; - RenderOptions.SetTextRenderingMode(control, TextRenderingMode.Alias); + TextOptions.SetTextRenderingMode(control, TextRenderingMode.Alias); Decorator target = new Decorator { diff --git a/tests/Avalonia.Skia.UnitTests/Media/GlyphRunTests.cs b/tests/Avalonia.Skia.UnitTests/Media/GlyphRunTests.cs index 1882ef9ccb..8f74f0779f 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/GlyphRunTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/GlyphRunTests.cs @@ -169,7 +169,7 @@ namespace Avalonia.Skia.UnitTests.Media var glyphRun1 = CreateGlyphRun(shapedBuffer); var bounds1 = glyphRun1.InkBounds; - ((GlyphRunImpl)glyphRun1.PlatformImpl.Item).GetTextBlob(new RenderOptions { TextRenderingMode = TextRenderingMode.SubpixelAntialias }); + ((GlyphRunImpl)glyphRun1.PlatformImpl.Item).GetTextBlob(new TextOptions { TextRenderingMode = TextRenderingMode.SubpixelAntialias }, default); var bounds2 = CreateGlyphRun(shapedBuffer).InkBounds; diff --git a/tests/Avalonia.Skia.UnitTests/TwoLevelCacheTests.cs b/tests/Avalonia.Skia.UnitTests/TwoLevelCacheTests.cs new file mode 100644 index 0000000000..6946c7ec2b --- /dev/null +++ b/tests/Avalonia.Skia.UnitTests/TwoLevelCacheTests.cs @@ -0,0 +1,351 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using Avalonia.Skia; +using Xunit; + +namespace Avalonia.Skia.UnitTests +{ + public class TwoLevelCacheTests + { + [Fact] + public void Constructor_WithNegativeSecondarySize_ThrowsArgumentOutOfRangeException() + { + Assert.Throws(() => new TwoLevelCache(-1)); + } + + [Fact] + public void Constructor_WithZeroSecondarySize_DoesNotThrow() + { + var cache = new TwoLevelCache(0); + Assert.NotNull(cache); + } + + [Fact] + public void TryGet_EmptyCache_ReturnsFalse() + { + var cache = new TwoLevelCache(); + + var result = cache.TryGet("key", out var value); + + Assert.False(result); + Assert.Null(value); + } + + [Fact] + public void GetOrAdd_FirstItem_StoresInPrimary() + { + var cache = new TwoLevelCache(); + var value = new object(); + + var result = cache.GetOrAdd("key1", _ => value); + + Assert.Same(value, result); + Assert.True(cache.TryGet("key1", out var retrieved)); + Assert.Same(value, retrieved); + } + + [Fact] + public void GetOrAdd_SameKey_ReturnsExistingValue() + { + var cache = new TwoLevelCache(); + var value1 = new object(); + var value2 = new object(); + + cache.GetOrAdd("key", _ => value1); + var result = cache.GetOrAdd("key", _ => value2); + + Assert.Same(value1, result); + } + + [Fact] + public void GetOrAdd_SecondItem_StoresInSecondary() + { + var cache = new TwoLevelCache(secondarySize: 3); + var value1 = new object(); + var value2 = new object(); + + cache.GetOrAdd("key1", _ => value1); + cache.GetOrAdd("key2", _ => value2); + + Assert.True(cache.TryGet("key1", out var retrieved1)); + Assert.Same(value1, retrieved1); + Assert.True(cache.TryGet("key2", out var retrieved2)); + Assert.Same(value2, retrieved2); + } + + [Fact] + public void GetOrAdd_MultipleItems_StoresCorrectly() + { + var cache = new TwoLevelCache(secondarySize: 3); + var values = new object[4]; + for (int i = 0; i < 4; i++) + { + values[i] = new object(); + cache.GetOrAdd($"key{i}", _ => values[i]); + } + + // All should be retrievable + for (int i = 0; i < 4; i++) + { + Assert.True(cache.TryGet($"key{i}", out var retrieved)); + Assert.Same(values[i], retrieved); + } + } + + [Fact] + public void GetOrAdd_ExceedsCapacity_CallsEvictionAction() + { + var evictedValues = new List(); + var cache = new TwoLevelCache( + secondarySize: 2, + evictionAction: v => evictedValues.Add(v)); + + var value1 = new object(); + var value2 = new object(); + var value3 = new object(); + var value4 = new object(); + + cache.GetOrAdd("key1", _ => value1); + cache.GetOrAdd("key2", _ => value2); + cache.GetOrAdd("key3", _ => value3); + + // No evictions yet + Assert.Empty(evictedValues); + + // This should cause eviction + cache.GetOrAdd("key4", _ => value4); + + Assert.Single(evictedValues); + Assert.Same(value2, evictedValues[0]); + } + + [Fact] + public void GetOrAdd_ZeroSecondarySize_EvictsPrimaryImmediately() + { + var evictedValues = new List(); + var cache = new TwoLevelCache( + secondarySize: 0, + evictionAction: v => evictedValues.Add(v)); + + var value1 = new object(); + var value2 = new object(); + + cache.GetOrAdd("key1", _ => value1); + cache.GetOrAdd("key2", _ => value2); + + Assert.Single(evictedValues); + Assert.Same(value1, evictedValues[0]); + + // Only the latest value should be retrievable + Assert.False(cache.TryGet("key1", out _)); + Assert.True(cache.TryGet("key2", out var retrieved)); + Assert.Same(value2, retrieved); + } + + [Fact] + public void GetOrAdd_DuplicateKey_ReturnsExistingWithoutCallingFactory() + { + var cache = new TwoLevelCache(); + var value1 = new object(); + var factoryCalled = false; + + // Add initial value + cache.GetOrAdd("key", _ => value1); + + // Try to add again - factory should not be called + var result = cache.GetOrAdd("key", _ => + { + factoryCalled = true; + return new object(); + }); + + // Should return first value without calling factory + Assert.Same(value1, result); + Assert.False(factoryCalled); + } + + [Fact] + public void GetOrAdd_DuplicateKeyInSecondary_ReturnsExistingWithoutCallingFactory() + { + var cache = new TwoLevelCache(secondarySize: 2); + var value1 = new object(); + var value2 = new object(); + var factoryCalled = false; + + cache.GetOrAdd("key1", _ => value1); + cache.GetOrAdd("key2", _ => value2); + + // Try to add key2 again - factory should not be called + var result = cache.GetOrAdd("key2", _ => + { + factoryCalled = true; + return new object(); + }); + + Assert.Same(value2, result); + Assert.False(factoryCalled); + } + + [Fact] + public void ClearAndDispose_EmptyCache_DoesNotThrow() + { + var cache = new TwoLevelCache(); + cache.ClearAndDispose(); + } + + [Fact] + public void ClearAndDispose_WithValues_CallsEvictionActionForAll() + { + var evictedValues = new List(); + var cache = new TwoLevelCache( + secondarySize: 2, + evictionAction: v => evictedValues.Add(v)); + + var value1 = new object(); + var value2 = new object(); + var value3 = new object(); + + cache.GetOrAdd("key1", _ => value1); + cache.GetOrAdd("key2", _ => value2); + cache.GetOrAdd("key3", _ => value3); + + cache.ClearAndDispose(); + + Assert.Equal(3, evictedValues.Count); + Assert.Contains(value1, evictedValues); + Assert.Contains(value2, evictedValues); + Assert.Contains(value3, evictedValues); + } + + [Fact] + public void ClearAndDispose_ClearsAllEntries() + { + var cache = new TwoLevelCache(secondarySize: 2); + + cache.GetOrAdd("key1", _ => new object()); + cache.GetOrAdd("key2", _ => new object()); + cache.ClearAndDispose(); + + Assert.False(cache.TryGet("key1", out _)); + Assert.False(cache.TryGet("key2", out _)); + } + + [Fact] + public void GetOrAdd_WithCustomComparer_UsesComparer() + { + var comparer = StringComparer.OrdinalIgnoreCase; + var cache = new TwoLevelCache(comparer: comparer); + + var value = new object(); + cache.GetOrAdd("KEY", _ => value); + + Assert.True(cache.TryGet("key", out var retrieved)); + Assert.Same(value, retrieved); + } + + [Fact] + public void TryGet_WithCustomComparer_UsesComparer() + { + var comparer = StringComparer.OrdinalIgnoreCase; + var cache = new TwoLevelCache( + secondarySize: 2, + comparer: comparer); + + var value1 = new object(); + var value2 = new object(); + + cache.GetOrAdd("PRIMARY", _ => value1); + cache.GetOrAdd("SECONDARY", _ => value2); + + Assert.True(cache.TryGet("primary", out var retrieved1)); + Assert.Same(value1, retrieved1); + Assert.True(cache.TryGet("secondary", out var retrieved2)); + Assert.Same(value2, retrieved2); + } + + [Fact] + public void GetOrAdd_IntKeys_WorksCorrectly() + { + var cache = new TwoLevelCache(secondarySize: 2); + + var value1 = new object(); + var value2 = new object(); + var value3 = new object(); + + cache.GetOrAdd(1, _ => value1); + cache.GetOrAdd(2, _ => value2); + cache.GetOrAdd(3, _ => value3); + + Assert.True(cache.TryGet(1, out var retrieved1)); + Assert.Same(value1, retrieved1); + Assert.True(cache.TryGet(2, out var retrieved2)); + Assert.Same(value2, retrieved2); + Assert.True(cache.TryGet(3, out var retrieved3)); + Assert.Same(value3, retrieved3); + } + + [Fact] + public void GetOrAdd_RotatesSecondaryCorrectly() + { + var evictedValues = new List(); + var cache = new TwoLevelCache( + secondarySize: 2, + evictionAction: v => evictedValues.Add(v)); + + var values = new object[5]; + for (int i = 0; i < 5; i++) + { + values[i] = new object(); + cache.GetOrAdd(i, _ => values[i]); + } + + // Primary: 0, Secondary: [1, 2] + // After adding 3: Primary: 0, Secondary: [3, 1] (evicts 2) + // After adding 4: Primary: 0, Secondary: [4, 3] (evicts 1) + + Assert.Equal(2, evictedValues.Count); + Assert.Contains(values[2], evictedValues); + Assert.Contains(values[1], evictedValues); + + // These should still be in cache + Assert.True(cache.TryGet(0, out _)); + Assert.True(cache.TryGet(3, out _)); + Assert.True(cache.TryGet(4, out _)); + + // These should be evicted + Assert.False(cache.TryGet(1, out _)); + Assert.False(cache.TryGet(2, out _)); + } + + [Fact] + public void FactoryFunction_ReceivesCorrectKey() + { + var cache = new TwoLevelCache(); + string? capturedKey = null; + + cache.GetOrAdd("testKey", key => + { + capturedKey = key; + return "value"; + }); + + Assert.Equal("testKey", capturedKey); + } + + [Fact] + public void GetOrAdd_NullEvictionAction_DoesNotThrow() + { + var cache = new TwoLevelCache( + secondarySize: 1, + evictionAction: null); + + cache.GetOrAdd("key1", _ => new object()); + cache.GetOrAdd("key2", _ => new object()); + cache.GetOrAdd("key3", _ => new object()); // Should evict without error + + cache.ClearAndDispose(); // Should also not throw + } + } +} diff --git a/tests/TestFiles/Skia/Controls/TextBlock/Should_Render_TextBlock_With_TextOptions_Alias_Light.expected.png b/tests/TestFiles/Skia/Controls/TextBlock/Should_Render_TextBlock_With_TextOptions_Alias_Light.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..0610d12ae77976ad661ca571b4945118a672ef82 GIT binary patch literal 890 zcmeAS@N?(olHy`uVBq!ia0y~yVAKJ!Q#jawqzxnQ0U*Uv?Bp530R%N1DIE+9%!ZyW zjv*Cu-rk9wG|NGvEl_>x+YSF)H!PUBdZiKD8~It&!q4wK^MZqMUd^@{>q}$z=QA+= z+f&8Nki{t`;Jl$hqKWm0fQ~|20;3@dcZXtx1J6OGBo0)my$e$#^GJtrMY+KoxXYNdHD@}k1v73Vc7-W6RlpQW~D?a4>q5AFJOrBEfT|M$1w zn^PnGZoXfa~V)Y8(}6(&dD{;hdkniaP@^-$%UO>*0x zek`B*ZDDD2wOsV)JC{CJf8YFNmg)JY#lCYl%v<~Sip!#Ji}p3`t+maruGasQeml6@ zCkm*fdVQ5o<=#x;Ynx3M-oD$p|GU_l5~1n$c70v5)wVk4z1iEh&oAcgwVhe^ttV=E zc4gkFS?bqKYI(EYyXeN|MBDq@{akP2+7q|j+eR*BhR~XeiJm(QH;Ft8Uu~&oDtucq z-!<|471!^A^4GWPy(`lFetg3gABO%%dD%PcH_m-B^IGZMnA=9Bfkvf=qy%RiH(g`A z`0JbRyLa^6{$`ZZezQ3$3M1Dvw9gz_Lt5bU#BH45!`Mkv-5sV+pOeC ze>8R8XZ~urT757t*2Y#*<|FG1!HX+953Jd7c1}$Hy%nd@e@9*a`ul`_#?SB_&syI6 zY$|Gxw7CMnm4D{n77^4YB>cGWl2#8)k$Rod638*Ux{cF%I(*XO3?_3}Fv zH>XBd@4psOHP`si#j5KzcdTs^KkC1>E10*!pR@728cO8r|J0v!f2KeCLmM>$$pcTy e8H7@Q7{aeb%KiVxK_<75*!Zmhg-*xTGf351>cFiR>pJBOM-qx=+MUm0vKc;;y34VX_ zO7{B5wt4#!P88haSY4_fW3}hU&x-T+k9ae_?fhJ_q{HfET=%sLg&faRTo$D6?vf4P z^=HC|75oLS-ij2rZx4T!D|zXj`#V9?LsE6M8){D&PYqi2{Cic0M(ysJmaXPS`_CuN zm0Y{|`Tw%5+^pLyix;^?Y<2F~lK(Vr+V<@lIXh3C{O!W}I{9?(yy|E1o4=b@bGx0r z`q1Ns+MU;%65h%RUiI4)|B>;2)#j{A8j3I5V(zV-J^xwFn!TJKi?6QzddK~>e?(o} z+1tO=)4u)7oTXfUtN5DLx~FYH-*$YNu%WCpelF{r5}E*p8+%g# literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Controls/TextBlock/Should_Render_TextBlock_With_TextOptions_Antialias_Light.expected.png b/tests/TestFiles/Skia/Controls/TextBlock/Should_Render_TextBlock_With_TextOptions_Antialias_Light.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..d9634a250aacf4134c33a055a5bb5b8b1edb0828 GIT binary patch literal 2734 zcmd^>Yc!N=8^<3KlViwf9AnyzA-iNVq#>IeVj@P-7CGfGXd7oULPVKDPSG%!5GkiI z%4vttIL~I!&@hMwdra64$}$b;r;$T>t4_OtZUutUe|M7zyJTf?>nNcB}Ad3 z002nX+E`uz00CX_+ZQ4XzD)%r-hz`5&cfCO0zP<%Z#n>o`q^4uaET%DryT-hXQ9ID zOLyKVYlW7J9dAW6%NV#DRVH`Ul=hm8J&;#P(U?fjO_c9Cx>9aH(`azCyr1e0Z`z=w zo^iLLLY;I(vm1y^MEWnw7H!hwpMHeu<8J-qXZGiW4|x{h@Du%9*@SBC(Y zZTI(Y`}5mz3RF&ZwsfsqlRvtSTvG!iZftCrakoz*Qcs-i{?kCB%n@RiaPWk&L!nS6 z+oFFiXFOMOeV=jxhdbNa+M0!~Qh3I!uGSX1dx4mlIlA)wF|3Op<-?|hoTz&kaAr6t zC6YU&L^8(pMa}d*R8BI(8sv?(MX&j{hamDSz)b*+qma4v`5x|bY(&JdnVA{KV~6GB zkbUXI`Wu~iJ&};8H;CSZ7l+n zc66vZJ3F7Ki!_|Kqvjr*oh93p@F9cCCnTh#w6bl0LT!5o2Z^Z9(?`;JZn9BonOet0 zyOuWR4|X?=TdKwbNhkvJzNKJsaq;uIIuZTx8WAbAxrP4BKlp8K!I!JXCtD+RfYO$h z7Oy&lisY{t%+}UcDUBu%oIZ0Vsju&%ke~pSO4U%yWbd3k3Mnls6VNNrS=#uxf$Zz+ z%d-C|#CZ(MBvAD^X%_pKp?B|uKF_qJoxEIP&We0J6N;VfRE^(RrN`f)6c~eT%+tAQ zfx$G5-kNS1OMqUycyaw>uNXUC%T@-VqtoCwC?8{5GRt(hG+JAkvYBUBar^HH?M?OT z`F=oRIouH=_ZW6uMUrh%-Px(;izim_}8x+T7Y@J z-{#%pwlc1@*KWp(^ENB4Xm5r$NDXs`pO)1iZnVeT6waULvFeuB)~pSnsW96tKP9@a zOv5#Ca6BkqG~Jn;R0K*}TcJQH&jgQBXTI_XW{3V&gNfy>tgZ?H_#fX@839zUnJJ=4 zwvy98Bau$GZDM+tokyV_B_#Z+qF_^sF z(b_<0IRic&YLwhn%17xH3^K#c3J$J)QBaM<@BY)RKQP zad^qJLkCr zE(odYOb?sw%zLAGP797ld3G_XBcJ8iRfKbLB$N$@_F_Hs6yoCIj*1IQi>4;pk3M)9 zRdm1caQ=3>SJZfXrHZ6TP*BhicNH|YOYT~Lu4kvpdm_-tBDpDLm=I8&^k8_^YGs1) z1e5u3W1%0^2qR#Exg{keD6WTij!pU$)mFya3wz4xeRf4#TwMIgo#^OjHng*%-hUXn z8E#UnhunKoQX(wBa>&yzCnqQ4pLTSWY{DPvX_BhRm4UA#dGan8E6r?pobTg&TH|&- zk<F0Fx3t@Lom2Ij3dy!`p|jjScjjl5I!ME}k8^>q^TC9BTUh!yk~i?uM< zbvFw}$8z!?^vs)U90dS&z(|m{cQ+|N^aE9U&2TsW9PpN6oe2P-ihp+M#Vtgb46k6z zyb^X+m9h%^3KrfMGct8)xAVcg59hOZpvG-rGRr*PV7y&MWuT&x555JMl zQFxZ>%uJ>9xg&h!ni+{Lufj)eROO+ohPPco@1!118T`}@c2inh`1hUFDN^qfR}gal zoG+gS3;fr#L0#(>MZEcKna{Vv;hNln4c+oHO1}Pf^=caE*tq7_?cArUe;rIk@js+0 z8-wwJy6Tn9RxkCKkR8Y$k_|m zm)+~@^}G1qkrPyq$Je*#1@GOvx4uZ1W$Qn}S8){uvK%m&%?zM$Ji4)`pSSIO`}UY= z!I+|euwYX1lFUed(jk%hxa}Y|6o@#DOqQyW47b`gfB{ z+$Fzs8@#x#ri}2RjP#VQx0;$aPuHj0D4N>YSa#1r{v-BZmi@nnHq1Jm`TAYbJ)z(Q O6tJ~ITfVaJO8ysf{s}1n literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Controls/TextBlock/Should_Render_TextBlock_With_TextOptions_Antialias_None.expected.png b/tests/TestFiles/Skia/Controls/TextBlock/Should_Render_TextBlock_With_TextOptions_Antialias_None.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..f5b987243fdf8c953d1911c4acb36f201ac4d867 GIT binary patch literal 2848 zcmeHJ=U3C&7X5`z3{nO#)F8tHL}m;Kg7hjx34)l=1(Y5sf^-5nAYBkLSO7(WFig}C zfdm1OCIpF82cmS03`mTT5_);R`SRY!_Yb`L;oiH>TKAl__t|%^ljG!QCkB;=0stU} zvbVVe01$2P?k^$)?j{hi4)73&wnAMN0avn!e>MO>nJ62}%kkx$sf)oMz9WQxY;MxI zS21^+)1722pGhs26Su^V{ABX}j^f`_9lA>ba`Eu@s%|ApLean31k^S_jo{5o>k@~5 zRT^?E)uDuN|H1Eb?L=W$@zB`4lv8N?`;F|z*rfG+qp+}1lXv&xDi#SE+d=X1TRsw! zqI0PY(|{^5S%;Es4~J>1N(u4jWoLADsH*xQ|95DIUoP|3Wi9>50Tr!RheQ7J{<@;w z$ki(|Y+e^@WzV7YZ&)|-u7kSHnKPNAqnETjX|OAGakR1$7Z;bGZhF%>lTGt$+kvfT zZ3ItObu4o@w@IXgQ|9*_;iuyRrHBR*Gd4*{tt_5}N?LDk@2tF6jtyVlu~X+zsCW6y zu^1%)siUJ4RABCW{JkaxOZLjKP`SHOD(73TeQmz$wj6h$dS+~E%aHGu#mw=<`7S{l zRY?^kr+|Qf2j%59K0b)cn4aNbiI&#ZR4u2PYjB}Z9B$;(r<~H#Qn|W1l9sxRZ z=wSz8#1YM_FCXw))KDL(xMO=M=1DnUNVT_09Mo@o>62V)IoduOG1i@#nJIBJ>o`N# zvD~mo(u2q2*{ewn>xoS^MZ}dqHwd4y(3Z7Uk?4!(vYB;oIXOsL{=~#hDlI2Rl)dPs zfwr|3)I)do^<`I~L1^CI-cA^qTNZ$VLHOG&am14lc|CMGiBvF@av+(v zyZ1&_S67ocqPML;sHc4uY8JcqtSpS40&`$pFawEXl=(JR9ATD0)V!;9f)x?+J&4w! zm-W{M?|m%0r9kE+7D?&DjPNm#uyUgqXbZXO*|Q@hB_(p)hlg*?D#SZ|hZBcbD|0{Q zyWt3gouOiHsXp#GK}EUHQ^VoHg$unKudhU$!=IS-C_AgTw!43JGic^@g2m34$LBxj zZSAL+;CJ+@G$S&t?d*g>@*hk7zCn8oat#(Dk+itP*8K+y3U2)T{NpReNgLODF+;w{ zY=Xwc&%>40?&)^U(s~94thvtg@i*{HzuPgXsqNR=;!VPpDozIt8WvcZDAPx)FRX={ z@8ff^-MMrY_F|th);_B^q3cBM=LhTsUgF5a1Qak#JurcFQ>>1rRk>nCT$0YXJTz6> zMfQ_lz8twBr~`*}aW}1*F>hHbe6)?lbsi4_czAj$x#%UEA>>xm)vT1)U z^Zi>;#yUnuq4;P<{WqgABHS!x@4?#s^)`Wqpuq96z&!V7gn+Yb-3`3GI-y{GI#=eT z7n3nI$xCbt#^J1!l8n3Nk>JP0U}QGFeLB6!ReAEswXs9DeV{?Ca&>+s-pSQ92#>Y4 zw(gt=Dm70vyZfv1;wpnKtED=J4gN9R>n+sjv-27Q8$ATH7nOnTpuO^u* z=k|9=olmp3M#=g4)dj{lx5)mj^Q(PKV5ciH2A)~OYiLj2<^$X)zLuRdZwHXdiZRY^ zve{O$nWAANF!b2eT2G!zc4+ao{g;&v&MlDh*;UfK;M~Zztd;! z_2eA`(qh=-&2K6=A)*8Vp+ix`s6psHJC}$@q%b32Jbfyludn|qeub!iocY4-V#ZaW zd+yf@&sCXI4+hE%DnsP`-Or)XV#{py{N}D?jtvrtbfUy2nkeHQ#2p^snE_Hc8T#7O z6u)$~Zp6&%8~FWq1PzOVCnsg=m^k!wUu@6ll(74~yeOYwJl@pzBtim>`KzN?P4uN3 z%+%DB%mA&DNzf>=f6~^bRIKJc+ofD6OfN(T1C5Q14oP|W2~m!&+QMo0-3`ds89L}` zN?|6Tn1LrRUi^Z)(bm+&ydikq*B1cu6{`ibc|d^+_q0J7;df&vXa~71Ek-KSRSze| z{dIGq7Y8ANWSL&p%5zttfjpfL1GlQ$T}MLQz>+~jk9&%KwtJ!pC63hUJJ=-h)zpND z2=K}Aj&vggTVad0ztc{!9BhAPgm&6BPC^G{Lmq2@2Y^niXx4aebdgUXD- zN5++!P}L%lH>rmO_4T?cO1@5w#nFpM5lWk%_v_f0>qu_WFWEm)*S-sXIJxtQHkgs%<&fI-A>`|3*;vQU zhgX8Kk}`unYbM|{vh{`POV@IBH1 literal 0 HcmV?d00001