diff --git a/src/Avalonia.Base/Media/Brush.cs b/src/Avalonia.Base/Media/Brush.cs index b9a560ad8f..accabce145 100644 --- a/src/Avalonia.Base/Media/Brush.cs +++ b/src/Avalonia.Base/Media/Brush.cs @@ -11,7 +11,7 @@ namespace Avalonia.Media /// Describes how an area is painted. /// [TypeConverter(typeof(BrushConverter))] - public abstract class Brush : Animatable + public abstract class Brush : Animatable, IBrush { /// /// Defines the property. diff --git a/src/Avalonia.Base/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs index 65575617d0..0ec7152359 100644 --- a/src/Avalonia.Base/Media/GlyphRun.cs +++ b/src/Avalonia.Base/Media/GlyphRun.cs @@ -13,14 +13,22 @@ namespace Avalonia.Media /// public sealed class GlyphRun : IDisposable { + private readonly static IPlatformRenderInterface s_renderInterface; + private IRef? _platformImpl; private double _fontRenderingEmSize; private int _biDiLevel; private GlyphRunMetrics? _glyphRunMetrics; private ReadOnlyMemory _characters; private IReadOnlyList _glyphInfos; + private Point? _baselineOrigin; private bool _hasOneCharPerCluster; // if true, character index and cluster are similar + static GlyphRun() + { + s_renderInterface = AvaloniaLocator.Current.GetRequiredService(); + } + /// /// Initializes a new instance of the class by specifying properties of the class. /// @@ -28,15 +36,17 @@ namespace Avalonia.Media /// The rendering em size. /// The characters. /// The glyph indices. + /// The baseline origin of the run. /// The bidi level. public GlyphRun( IGlyphTypeface glyphTypeface, double fontRenderingEmSize, ReadOnlyMemory characters, IReadOnlyList glyphIndices, + Point? baselineOrigin = null, int biDiLevel = 0) : this(glyphTypeface, fontRenderingEmSize, characters, - CreateGlyphInfos(glyphIndices, fontRenderingEmSize, glyphTypeface), biDiLevel) + CreateGlyphInfos(glyphIndices, fontRenderingEmSize, glyphTypeface), baselineOrigin, biDiLevel) { _hasOneCharPerCluster = true; } @@ -48,12 +58,14 @@ namespace Avalonia.Media /// The rendering em size. /// The characters. /// The list of glyphs used. + /// The baseline origin of the run. /// The bidi level. public GlyphRun( IGlyphTypeface glyphTypeface, double fontRenderingEmSize, ReadOnlyMemory characters, IReadOnlyList glyphInfos, + Point? baselineOrigin = null, int biDiLevel = 0) { GlyphTypeface = glyphTypeface; @@ -64,6 +76,8 @@ namespace Avalonia.Media _glyphInfos = glyphInfos; + _baselineOrigin = baselineOrigin; + _biDiLevel = biDiLevel; } @@ -72,6 +86,7 @@ namespace Avalonia.Media _glyphInfos = Array.Empty(); GlyphTypeface = Typeface.Default.GlyphTypeface; _platformImpl = platformImpl; + _baselineOrigin = platformImpl.Item.BaselineOrigin; } private static IReadOnlyList CreateGlyphInfos(IReadOnlyList glyphIndices, @@ -147,9 +162,13 @@ namespace Avalonia.Media => _glyphRunMetrics ??= CreateGlyphRunMetrics(); /// - /// Gets the baseline origin of the. + /// Gets or sets the baseline origin of the. /// - public Point BaselineOrigin => PlatformImpl.Item.BaselineOrigin; + public Point BaselineOrigin + { + get => _baselineOrigin ?? default; + set => Set(ref _baselineOrigin, value); + } /// /// Gets or sets the list of UTF16 code points that represent the Unicode content of the . @@ -204,9 +223,7 @@ namespace Avalonia.Media /// The geometry returned contains the combined geometry of all glyphs in the glyph run. public Geometry BuildGeometry() { - var platformRenderInterface = AvaloniaLocator.Current.GetRequiredService(); - - var geometryImpl = platformRenderInterface.BuildGlyphRunGeometry(this); + var geometryImpl = s_renderInterface.BuildGlyphRunGeometry(this); return new PlatformGeometry(geometryImpl); } @@ -802,9 +819,11 @@ namespace Avalonia.Media private IRef CreateGlyphRunImpl() { - var platformRenderInterface = AvaloniaLocator.Current.GetRequiredService(); - - var platformImpl = platformRenderInterface.CreateGlyphRun(GlyphTypeface, FontRenderingEmSize, GlyphInfos); + var platformImpl = s_renderInterface.CreateGlyphRun( + GlyphTypeface, + FontRenderingEmSize, + GlyphInfos, + _baselineOrigin ?? new Point(0, -GlyphTypeface.Metrics.Ascent * Scale)); _platformImpl = RefCountable.Create(platformImpl); diff --git a/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs index ac196bf7e0..7f23ac98b4 100644 --- a/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs +++ b/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs @@ -185,7 +185,7 @@ namespace Avalonia.Media.TextFormatting ShapedBuffer.FontRenderingEmSize, Text, ShapedBuffer, - BidiLevel); + biDiLevel: BidiLevel); } public void Dispose() diff --git a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs index e2160f21d2..41e792d58e 100644 --- a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs +++ b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs @@ -168,8 +168,9 @@ namespace Avalonia.Platform /// The glyph typeface. /// The font rendering em size. /// The list of glyphs. + /// The baseline origin of the run. Can be null. /// An . - IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos); + IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos, Point baselineOrigin); /// /// Creates a backend-specific object using a low-level API graphics context diff --git a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs index 572ff1c876..514d3b3e07 100644 --- a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs +++ b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs @@ -120,7 +120,11 @@ namespace Avalonia.Headless return new HeadlessGeometryStub(new Rect(glyphRun.Size)); } - public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos) + public IGlyphRunImpl CreateGlyphRun( + IGlyphTypeface glyphTypeface, + double fontRenderingEmSize, + IReadOnlyList glyphInfos, + Point baselineOrigin) { return new HeadlessGlyphRunStub(); } diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index 6630f0707e..b4297a7c33 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -201,7 +201,11 @@ namespace Avalonia.Skia return new WriteableBitmapImpl(size, dpi, format, alphaFormat); } - public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos) + public IGlyphRunImpl CreateGlyphRun( + IGlyphTypeface glyphTypeface, + double fontRenderingEmSize, + IReadOnlyList glyphInfos, + Point baselineOrigin) { if (glyphTypeface == null) { @@ -252,7 +256,6 @@ namespace Avalonia.Skia var scale = fontRenderingEmSize / glyphTypeface.Metrics.DesignEmHeight; var height = glyphTypeface.Metrics.LineSpacing * scale; - var baselineOrigin = new Point(0, -glyphTypeface.Metrics.Ascent * scale); return new GlyphRunImpl(builder.Build(), new Size(width, height), baselineOrigin); } diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs index 461950b728..fbf8097ece 100644 --- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs +++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs @@ -158,7 +158,8 @@ namespace Avalonia.Direct2D1 public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) => new GeometryGroupImpl(fillRule, children); public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2) => new CombinedGeometryImpl(combineMode, g1, g2); - public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos) + public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, + IReadOnlyList glyphInfos, Point baselineOrigin) { var glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface; @@ -207,7 +208,6 @@ namespace Avalonia.Direct2D1 var scale = fontRenderingEmSize / glyphTypeface.Metrics.DesignEmHeight; var height = glyphTypeface.Metrics.LineSpacing * scale; - var baselineOrigin = new Point(0, -glyphTypeface.Metrics.Ascent * scale); return new GlyphRunImpl(run, new Size(width, height), baselineOrigin); } @@ -257,7 +257,7 @@ namespace Avalonia.Direct2D1 sink.Close(); } - var (baselineOriginX, baselineOriginY) = glyphRun.BaselineOrigin; + var (baselineOriginX, baselineOriginY) = glyphRun.PlatformImpl.Item.BaselineOrigin; var transformedGeometry = new SharpDX.Direct2D1.TransformedGeometry( Direct2D1Factory, diff --git a/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs b/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs index a05bfbea4c..43feb75c08 100644 --- a/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs @@ -188,7 +188,7 @@ namespace Avalonia.Base.UnitTests.Media glyphInfos[i] = new GlyphInfo(0, glyphClusters[i], glyphAdvances[i]); } - return new GlyphRun(new MockGlyphTypeface(), 10, new string('a', count).AsMemory(), glyphInfos, bidiLevel); + return new GlyphRun(new MockGlyphTypeface(), 10, new string('a', count).AsMemory(), glyphInfos, biDiLevel: bidiLevel); } } } diff --git a/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs b/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs index ee501a86c1..76c7fe97fc 100644 --- a/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs +++ b/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs @@ -77,7 +77,8 @@ namespace Avalonia.Base.UnitTests.VisualTree throw new NotImplementedException(); } - public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos) + public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, + IReadOnlyList glyphInfos, Point baselineOrigin) { throw new NotImplementedException(); } diff --git a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs index 9b148c798b..37b79855db 100644 --- a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs +++ b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs @@ -123,7 +123,8 @@ namespace Avalonia.Benchmarks return new MockStreamGeometryImpl(); } - public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos) + public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, + IReadOnlyList glyphInfos, Point baselineOrigin) { return new MockGlyphRun(glyphInfos); } diff --git a/tests/Avalonia.Skia.UnitTests/Media/GlyphRunTests.cs b/tests/Avalonia.Skia.UnitTests/Media/GlyphRunTests.cs index 59c7ac3786..bfe03030c6 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/GlyphRunTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/GlyphRunTests.cs @@ -217,7 +217,7 @@ namespace Avalonia.Skia.UnitTests.Media shapedBuffer.FontRenderingEmSize, shapedBuffer.Text, shapedBuffer.GlyphInfos, - shapedBuffer.BidiLevel); + biDiLevel: shapedBuffer.BidiLevel); if(shapedBuffer.BidiLevel == 1) { diff --git a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs index 30f949ccb8..93073faefb 100644 --- a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs +++ b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs @@ -149,7 +149,8 @@ namespace Avalonia.UnitTests throw new NotImplementedException(); } - public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos) + public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, + IReadOnlyList glyphInfos, Point baselineOrigin) { return new MockGlyphRun(glyphInfos); }