From 74fafe3b09dbe75b810eb0a590ed588791422a39 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 11 Oct 2022 20:16:19 +0200 Subject: [PATCH 1/5] Optimize GlyphRun allocations --- src/Avalonia.Base/Media/GlyphRun.cs | 81 +++++++- src/Avalonia.Base/Media/GlyphTypeface.cs | 5 + src/Avalonia.Base/Platform/IGlyphRunBuffer.cs | 22 +++ .../Platform/IGlyphTypefaceImpl.cs | 5 + .../Platform/IPlatformRenderInterface.cs | 37 +++- .../HeadlessPlatformRenderInterface.cs | 38 +++- .../HeadlessPlatformStubs.cs | 2 + src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs | 4 + .../Avalonia.Skia/PlatformRenderInterface.cs | 182 ++++++++---------- .../Avalonia.Direct2D1/Direct2D1Platform.cs | 89 +++++---- .../Media/GlyphTypefaceImpl.cs | 2 + .../VisualTree/MockRenderInterface.cs | 15 ++ .../NullRenderingPlatform.cs | 16 +- .../HarfBuzzGlyphTypefaceImpl.cs | 4 + tests/Avalonia.UnitTests/MockGlyphTypeface.cs | 1 + .../MockPlatformRenderInterface.cs | 15 ++ 16 files changed, 354 insertions(+), 164 deletions(-) create mode 100644 src/Avalonia.Base/Platform/IGlyphRunBuffer.cs diff --git a/src/Avalonia.Base/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs index 2289f98228..ce436ee017 100644 --- a/src/Avalonia.Base/Media/GlyphRun.cs +++ b/src/Avalonia.Base/Media/GlyphRun.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Drawing; using Avalonia.Media.TextFormatting.Unicode; using Avalonia.Platform; using Avalonia.Utilities; @@ -854,9 +855,87 @@ namespace Avalonia.Media throw new InvalidOperationException(); } + _glyphRunImpl = CreateGlyphRunImpl(); + } + + private IGlyphRunImpl CreateGlyphRunImpl() + { + IGlyphRunImpl glyphRunImpl; + var platformRenderInterface = AvaloniaLocator.Current.GetRequiredService(); + var count = GlyphIndices.Count; + var scale = (float)(FontRenderingEmSize / GlyphTypeface.DesignEmHeight); + + if (GlyphOffsets == null) + { + if (GlyphTypeface.IsFixedPitch) + { + var buffer = platformRenderInterface.AllocateGlyphRun(GlyphTypeface, (float)FontRenderingEmSize, count); + + var glyphs = buffer.GlyphIndices; + + for (int i = 0; i < glyphs.Length; i++) + { + glyphs[i] = GlyphIndices[i]; + } + + glyphRunImpl = buffer.Build(); + } + else + { + var buffer = platformRenderInterface.AllocateHorizontalGlyphRun(GlyphTypeface, (float)FontRenderingEmSize, count); + var glyphs = buffer.GlyphIndices; + var positions = buffer.GlyphPositions; + var width = 0d; + + for (var i = 0; i < count; i++) + { + positions[i] = (float)width; + + if (GlyphAdvances == null) + { + width += GlyphTypeface.GetGlyphAdvance(GlyphIndices[i]) * scale; + } + else + { + width += GlyphAdvances[i]; + } + + glyphs[i] = GlyphIndices[i]; + } + + glyphRunImpl = buffer.Build(); + } + } + else + { + var buffer = platformRenderInterface.AllocatePositionedGlyphRun(GlyphTypeface, (float)FontRenderingEmSize, count); + var glyphs = buffer.GlyphIndices; + var glyphPositions = buffer.GlyphPositions; + var currentX = 0.0; + + for (var i = 0; i < count; i++) + { + var glyphOffset = GlyphOffsets[i]; + + glyphPositions[i] = new PointF((float)(currentX + glyphOffset.X), (float)glyphOffset.Y); + + if (GlyphAdvances == null) + { + currentX += GlyphTypeface.GetGlyphAdvance(GlyphIndices[i]) * scale; + } + else + { + currentX += GlyphAdvances[i]; + } + + glyphs[i] = GlyphIndices[i]; + } + + glyphRunImpl = buffer.Build(); + } - _glyphRunImpl = platformRenderInterface.CreateGlyphRun(this); + return glyphRunImpl; } void IDisposable.Dispose() diff --git a/src/Avalonia.Base/Media/GlyphTypeface.cs b/src/Avalonia.Base/Media/GlyphTypeface.cs index 45ef04e77f..36b6f3a5f8 100644 --- a/src/Avalonia.Base/Media/GlyphTypeface.cs +++ b/src/Avalonia.Base/Media/GlyphTypeface.cs @@ -67,6 +67,11 @@ namespace Avalonia.Media /// public bool IsFixedPitch => PlatformImpl.IsFixedPitch; + /// + /// Gets the number of glyphs held by this glyph typeface. + /// + public int GlyphCount => PlatformImpl.GlyphCount; + /// /// Returns an glyph index for the specified codepoint. /// diff --git a/src/Avalonia.Base/Platform/IGlyphRunBuffer.cs b/src/Avalonia.Base/Platform/IGlyphRunBuffer.cs new file mode 100644 index 0000000000..c1fc7a5967 --- /dev/null +++ b/src/Avalonia.Base/Platform/IGlyphRunBuffer.cs @@ -0,0 +1,22 @@ +using System; +using System.Drawing; + +namespace Avalonia.Platform +{ + public interface IGlyphRunBuffer + { + Span GlyphIndices { get; } + + IGlyphRunImpl Build(); + } + + public interface IHorizontalGlyphRunBuffer : IGlyphRunBuffer + { + Span GlyphPositions { get; } + } + + public interface IPositionedGlyphRunBuffer : IGlyphRunBuffer + { + Span GlyphPositions { get; } + } +} diff --git a/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs b/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs index 415f34fb29..5a38e133ff 100644 --- a/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs +++ b/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs @@ -51,6 +51,11 @@ namespace Avalonia.Platform /// bool IsFixedPitch { get; } + /// + /// Gets the number of glyphs held by this glyph typeface. + /// + int GlyphCount { get; } + /// /// Returns an glyph index for the specified codepoint. /// diff --git a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs index e39a4e23df..93105b21a4 100644 --- a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs +++ b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs @@ -171,11 +171,40 @@ namespace Avalonia.Platform IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride); /// - /// Creates a platform implementation of a glyph run. + /// Allocates a platform glyph run buffer. /// - /// The glyph run. - /// - IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun); + /// The glyph typeface. + /// The font rendering em size. + /// The length. + /// An . + /// + /// This buffer only holds glyph indices. + /// + IGlyphRunBuffer AllocateGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length); + + /// + /// Allocates a horizontal platform glyph run buffer. + /// + /// The glyph typeface. + /// The font rendering em size. + /// The length. + /// An . + /// + /// This buffer holds glyph indices and glyph advances. + /// + IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length); + + /// + /// Allocates a positioned platform glyph run buffer. + /// + /// The glyph typeface. + /// The font rendering em size. + /// The length. + /// An . + /// + /// This buffer holds glyph indices, glyph advances and glyph positions. + /// + IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length); /// /// Gets a value indicating whether the platform directly supports rectangles with rounded corners. diff --git a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs index cb23c6c336..12a9a016f3 100644 --- a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs +++ b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs @@ -110,14 +110,24 @@ namespace Avalonia.Headless return new HeadlessBitmapStub(destinationSize, new Vector(96, 96)); } - public IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun) + public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun) { - return new HeadlessGlyphRunStub(); + return new HeadlessGeometryStub(new Rect(glyphRun.Size)); } - public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun) + public IGlyphRunBuffer AllocateGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) { - return new HeadlessGeometryStub(new Rect(glyphRun.Size)); + return new HeadlessGlyphRunBufferStub(); + } + + public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + { + return new HeadlessHorizontalGlyphRunBufferStub(); + } + + public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + { + return new HeadlessPositionedGlyphRunBufferStub(); } class HeadlessGeometryStub : IGeometryImpl @@ -203,6 +213,26 @@ namespace Avalonia.Headless public Matrix Transform { get; } } + class HeadlessGlyphRunBufferStub : IGlyphRunBuffer + { + public Span GlyphIndices => Span.Empty; + + public IGlyphRunImpl Build() + { + return new HeadlessGlyphRunStub(); + } + } + + class HeadlessHorizontalGlyphRunBufferStub : HeadlessGlyphRunBufferStub, IHorizontalGlyphRunBuffer + { + public Span GlyphPositions => Span.Empty; + } + + class HeadlessPositionedGlyphRunBufferStub : HeadlessGlyphRunBufferStub, IPositionedGlyphRunBuffer + { + public Span GlyphPositions => Span.Empty; + } + class HeadlessGlyphRunStub : IGlyphRunImpl { public void Dispose() diff --git a/src/Avalonia.Headless/HeadlessPlatformStubs.cs b/src/Avalonia.Headless/HeadlessPlatformStubs.cs index ed51529a96..b3bf92f06b 100644 --- a/src/Avalonia.Headless/HeadlessPlatformStubs.cs +++ b/src/Avalonia.Headless/HeadlessPlatformStubs.cs @@ -95,6 +95,8 @@ namespace Avalonia.Headless public bool IsFixedPitch => true; + public int GlyphCount => 1337; + public void Dispose() { } diff --git a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs index dcb4eac7ca..a3174010a1 100644 --- a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs +++ b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs @@ -55,6 +55,8 @@ namespace Avalonia.Skia IsFixedPitch = Typeface.IsFixedPitch; + GlyphCount = Typeface.GlyphCount; + IsFakeBold = isFakeBold; IsFakeItalic = isFakeItalic; @@ -94,6 +96,8 @@ namespace Avalonia.Skia /// public bool IsFixedPitch { get; } + + public int GlyphCount { get; } public bool IsFakeBold { get; } diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index 91fe4fc085..aa9225ba64 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -1,7 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; -using System.IO; +using System.IO; using System.Linq; using System.Threading; @@ -12,6 +12,8 @@ using Avalonia.OpenGL.Imaging; using Avalonia.Platform; using Avalonia.Media.Imaging; using SkiaSharp; +using System.Runtime.InteropServices; +using System.Drawing; namespace Avalonia.Skia { @@ -33,13 +35,17 @@ namespace Avalonia.Skia } var gl = AvaloniaLocator.Current.GetService(); - if (gl != null) + if (gl != null) _skiaGpu = new GlSkiaGpu(gl, maxResourceBytes); - - //TODO: SKFont crashes when disposed in finalizer so we keep it alive - GC.SuppressFinalize(s_font); } + + public bool SupportsIndividualRoundRects => true; + + public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul; + + public PixelFormat DefaultPixelFormat { get; } + public IGeometryImpl CreateEllipseGeometry(Rect rect) => new EllipseGeometryImpl(rect); public IGeometryImpl CreateLineGeometry(Point p1, Point p2) => new LineGeometryImpl(p1, p2); @@ -228,133 +234,95 @@ namespace Avalonia.Skia return new WriteableBitmapImpl(size, dpi, format, alphaFormat); } - private static readonly SKFont s_font = new SKFont - { - Subpixel = true, - Edging = SKFontEdging.SubpixelAntialias, - Hinting = SKFontHinting.Full, - LinearMetrics = true - }; - - private static readonly ThreadLocal s_textBlobBuilderThreadLocal = new ThreadLocal(() => new SKTextBlobBuilder()); - - /// - public IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun) + public IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi) { - var count = glyphRun.GlyphIndices.Count; - var textBlobBuilder = s_textBlobBuilderThreadLocal.Value; - - var glyphTypeface = (GlyphTypefaceImpl)glyphRun.GlyphTypeface.PlatformImpl; + if (_skiaGpu is IOpenGlAwareSkiaGpu glAware) + return glAware.CreateOpenGlBitmap(size, dpi); + if (_skiaGpu == null) + throw new PlatformNotSupportedException("GPU acceleration is not available"); + throw new PlatformNotSupportedException( + "Current GPU acceleration backend does not support OpenGL integration"); + } - var typeface = glyphTypeface.Typeface; + public IGlyphRunBuffer AllocateGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + => new SKGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length); - s_font.Size = (float)glyphRun.FontRenderingEmSize; - s_font.Typeface = typeface; - s_font.Embolden = glyphTypeface.IsFakeBold; - s_font.SkewX = glyphTypeface.IsFakeItalic ? -0.2f : 0; + public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + => new SKHorizontalGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length); - SKTextBlob textBlob; + public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + => new SKPositionedGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length); - var scale = (float)(glyphRun.FontRenderingEmSize / glyphTypeface.DesignEmHeight); + private abstract class SKGlyphRunBufferBase : IGlyphRunBuffer + { + protected readonly SKTextBlobBuilder _builder; + protected readonly SKFont _font; - if (glyphRun.GlyphOffsets == null) + public SKGlyphRunBufferBase(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) { - if (glyphTypeface.IsFixedPitch) - { - var buffer = textBlobBuilder.AllocateRun(s_font, glyphRun.GlyphIndices.Count, 0, 0); - - var glyphs = buffer.GetGlyphSpan(); + _builder = new SKTextBlobBuilder(); - for (int i = 0; i < glyphs.Length; i++) - { - glyphs[i] = glyphRun.GlyphIndices[i]; - } + var glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface.PlatformImpl; - textBlob = textBlobBuilder.Build(); - } - else + _font = new SKFont { - var buffer = textBlobBuilder.AllocateHorizontalRun(s_font, count, 0); - - var positions = buffer.GetPositionSpan(); - - var width = 0d; - - for (var i = 0; i < count; i++) - { - positions[i] = (float)width; - - if (glyphRun.GlyphAdvances == null) - { - width += glyphTypeface.GetGlyphAdvance(glyphRun.GlyphIndices[i]) * scale; - } - else - { - width += glyphRun.GlyphAdvances[i]; - } - } - - var glyphs = buffer.GetGlyphSpan(); + Subpixel = true, + Edging = SKFontEdging.SubpixelAntialias, + Hinting = SKFontHinting.Full, + LinearMetrics = true, + Size = fontRenderingEmSize, + Typeface = glyphTypefaceImpl.Typeface, + Embolden = glyphTypefaceImpl.IsFakeBold, + SkewX = glyphTypefaceImpl.IsFakeItalic ? -0.2f : 0 + }; + } - for (int i = 0; i < glyphs.Length; i++) - { - glyphs[i] = glyphRun.GlyphIndices[i]; - } + public abstract Span GlyphIndices { get; } - textBlob = textBlobBuilder.Build(); - } - } - else + public IGlyphRunImpl Build() { - var buffer = textBlobBuilder.AllocatePositionedRun(s_font, count); - - var glyphPositions = buffer.GetPositionSpan(); + return new GlyphRunImpl(_builder.Build()); + } + } - var currentX = 0.0; + private sealed class SKGlyphRunBuffer : SKGlyphRunBufferBase + { + private readonly SKRunBuffer _buffer; - for (var i = 0; i < count; i++) - { - var glyphOffset = glyphRun.GlyphOffsets[i]; - - glyphPositions[i] = new SKPoint((float)(currentX + glyphOffset.X), (float)glyphOffset.Y); - - if (glyphRun.GlyphAdvances == null) - { - currentX += glyphTypeface.GetGlyphAdvance(glyphRun.GlyphIndices[i]) * scale; - } - else - { - currentX += glyphRun.GlyphAdvances[i]; - } - } + public SKGlyphRunBuffer(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) : base(glyphTypeface, fontRenderingEmSize, length) + { + _buffer = _builder.AllocateRun(_font, length, 0, 0); + } - var glyphs = buffer.GetGlyphSpan(); + public override Span GlyphIndices => _buffer.GetGlyphSpan(); + } - for (int i = 0; i < glyphs.Length; i++) - { - glyphs[i] = glyphRun.GlyphIndices[i]; - } + private sealed class SKHorizontalGlyphRunBuffer : SKGlyphRunBufferBase, IHorizontalGlyphRunBuffer + { + private readonly SKHorizontalRunBuffer _buffer; - textBlob = textBlobBuilder.Build(); + public SKHorizontalGlyphRunBuffer(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) : base(glyphTypeface, fontRenderingEmSize, length) + { + _buffer = _builder.AllocateHorizontalRun(_font, length, 0); } - return new GlyphRunImpl(textBlob); + public override Span GlyphIndices => _buffer.GetGlyphSpan(); + + public Span GlyphPositions => _buffer.GetPositionSpan(); } - public IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi) + private sealed class SKPositionedGlyphRunBuffer : SKGlyphRunBufferBase, IPositionedGlyphRunBuffer { - if (_skiaGpu is IOpenGlAwareSkiaGpu glAware) - return glAware.CreateOpenGlBitmap(size, dpi); - if (_skiaGpu == null) - throw new PlatformNotSupportedException("GPU acceleration is not available"); - throw new PlatformNotSupportedException( - "Current GPU acceleration backend does not support OpenGL integration"); - } + private readonly SKPositionedRunBuffer _buffer; - public bool SupportsIndividualRoundRects => true; + public SKPositionedGlyphRunBuffer(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) : base(glyphTypeface, fontRenderingEmSize, length) + { + _buffer = _builder.AllocatePositionedRun(_font, length); + } - public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul; + public override Span GlyphIndices => _buffer.GetGlyphSpan(); - public PixelFormat DefaultPixelFormat { get; } + public Span GlyphPositions => MemoryMarshal.Cast(_buffer.GetPositionSpan()); + } } } diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs index 81fa8c4bce..45744e6efa 100644 --- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs +++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs @@ -12,6 +12,8 @@ using SharpDX.DirectWrite; using GlyphRun = Avalonia.Media.GlyphRun; using TextAlignment = Avalonia.Media.TextAlignment; using SharpDX.Mathematics.Interop; +using System.Runtime.InteropServices; +using System.Drawing; namespace Avalonia { @@ -258,69 +260,66 @@ namespace Avalonia.Direct2D1 return new WicBitmapImpl(format, alphaFormat, data, size, dpi, stride); } - public IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun) + private class DWGlyphRunBuffer : IGlyphRunBuffer { - var glyphTypeface = (GlyphTypefaceImpl)glyphRun.GlyphTypeface.PlatformImpl; + protected readonly SharpDX.DirectWrite.GlyphRun _dwRun; - var glyphCount = glyphRun.GlyphIndices.Count; - - var run = new SharpDX.DirectWrite.GlyphRun + public DWGlyphRunBuffer(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) { - FontFace = glyphTypeface.FontFace, - FontSize = (float)glyphRun.FontRenderingEmSize - }; - - var indices = new short[glyphCount]; + var glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface.PlatformImpl; - for (var i = 0; i < glyphCount; i++) - { - indices[i] = (short)glyphRun.GlyphIndices[i]; + _dwRun = new SharpDX.DirectWrite.GlyphRun + { + FontFace = glyphTypefaceImpl.FontFace, + FontSize = fontRenderingEmSize, + Indices = new short[length] + }; } - run.Indices = indices; - - run.Advances = new float[glyphCount]; + public Span GlyphIndices => MemoryMarshal.Cast(_dwRun.Indices.AsSpan()); - var scale = (float)(glyphRun.FontRenderingEmSize / glyphTypeface.DesignEmHeight); - - if (glyphRun.GlyphAdvances == null) + public IGlyphRunImpl Build() { - for (var i = 0; i < glyphCount; i++) - { - var advance = glyphTypeface.GetGlyphAdvance(glyphRun.GlyphIndices[i]) * scale; - - run.Advances[i] = advance; - } + return new GlyphRunImpl(_dwRun); } - else - { - for (var i = 0; i < glyphCount; i++) - { - var advance = (float)glyphRun.GlyphAdvances[i]; + } - run.Advances[i] = advance; - } + private class DWHorizontalGlyphRunBuffer : DWGlyphRunBuffer, IHorizontalGlyphRunBuffer + { + public DWHorizontalGlyphRunBuffer(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + : base(glyphTypeface, fontRenderingEmSize, length) + { + _dwRun.Advances = new float[length]; } - if (glyphRun.GlyphOffsets == null) + public Span GlyphPositions => _dwRun.Advances.AsSpan(); + } + + private class DWPositionedGlyphRunBuffer : DWGlyphRunBuffer, IPositionedGlyphRunBuffer + { + public DWPositionedGlyphRunBuffer(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + : base(glyphTypeface, fontRenderingEmSize, length) { - return new GlyphRunImpl(run); + _dwRun.Advances = new float[length]; + _dwRun.Offsets = new GlyphOffset[length]; } - run.Offsets = new GlyphOffset[glyphCount]; + public Span GlyphPositions => MemoryMarshal.Cast(_dwRun.Offsets.AsSpan()); + } - for (var i = 0; i < glyphCount; i++) - { - var (x, y) = glyphRun.GlyphOffsets[i]; + public IGlyphRunBuffer AllocateGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + { + return new DWGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length); + } - run.Offsets[i] = new GlyphOffset - { - AdvanceOffset = (float)x, - AscenderOffset = (float)y - }; - } + public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + { + return new DWHorizontalGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length); + } - return new GlyphRunImpl(run); + public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + { + return new DWPositionedGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length); } public bool SupportsIndividualRoundRects => false; diff --git a/src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs index 4154b44702..443bb8a207 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs @@ -117,6 +117,8 @@ namespace Avalonia.Direct2D1.Media /// public bool IsFixedPitch { get; } + public int GlyphCount { get; set; } + /// public ushort GetGlyph(uint codepoint) { diff --git a/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs b/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs index 1f0b82b465..d21879f349 100644 --- a/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs +++ b/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs @@ -126,6 +126,21 @@ namespace Avalonia.Base.UnitTests.VisualTree throw new NotImplementedException(); } + public IGlyphRunBuffer AllocateGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + { + throw new NotImplementedException(); + } + + public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + { + throw new NotImplementedException(); + } + + public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + { + throw new NotImplementedException(); + } + class MockStreamGeometry : IStreamGeometryImpl { private MockStreamGeometryContext _impl = new MockStreamGeometryContext(); diff --git a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs index 0193f5d772..a2c2936a4d 100644 --- a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs +++ b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs @@ -112,12 +112,22 @@ namespace Avalonia.Benchmarks return new MockFontManagerImpl(); } - public IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun) + public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun) { - return new NullGlyphRun(); + return new MockStreamGeometryImpl(); } - public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun) + public IGlyphRunBuffer AllocateGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + { + throw new NotImplementedException(); + } + + public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + { + throw new NotImplementedException(); + } + + public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) { throw new NotImplementedException(); } diff --git a/tests/Avalonia.UnitTests/HarfBuzzGlyphTypefaceImpl.cs b/tests/Avalonia.UnitTests/HarfBuzzGlyphTypefaceImpl.cs index 32e0434cd4..04f316e5b4 100644 --- a/tests/Avalonia.UnitTests/HarfBuzzGlyphTypefaceImpl.cs +++ b/tests/Avalonia.UnitTests/HarfBuzzGlyphTypefaceImpl.cs @@ -44,6 +44,8 @@ namespace Avalonia.UnitTests IsFixedPitch = GetGlyphAdvance(GetGlyph('a')) == GetGlyphAdvance(GetGlyph('b')); + GlyphCount = Face.GlyphCount; + IsFakeBold = isFakeBold; IsFakeItalic = isFakeItalic; @@ -79,6 +81,8 @@ namespace Avalonia.UnitTests /// public bool IsFixedPitch { get; } + + public int GlyphCount { get; set; } public bool IsFakeBold { get; } diff --git a/tests/Avalonia.UnitTests/MockGlyphTypeface.cs b/tests/Avalonia.UnitTests/MockGlyphTypeface.cs index c9c59a6e32..0b7beeb4fb 100644 --- a/tests/Avalonia.UnitTests/MockGlyphTypeface.cs +++ b/tests/Avalonia.UnitTests/MockGlyphTypeface.cs @@ -14,6 +14,7 @@ namespace Avalonia.UnitTests public int StrikethroughPosition { get; } public int StrikethroughThickness { get; } public bool IsFixedPitch { get; } + public int GlyphCount => 1337; public ushort GetGlyph(uint codepoint) { diff --git a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs index f6a127b573..314cc95041 100644 --- a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs +++ b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs @@ -152,6 +152,21 @@ namespace Avalonia.UnitTests return Mock.Of(); } + public IGlyphRunBuffer AllocateGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + { + return Mock.Of(); + } + + public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + { + return Mock.Of(); + } + + public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + { + return Mock.Of(); + } + public bool SupportsIndividualRoundRects { get; set; } public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul; From 5a5080b1a5698456961624de63af0c0e4ba203ec Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Wed, 12 Oct 2022 14:14:46 +0200 Subject: [PATCH 2/5] Rework GlyphTypeface --- samples/RenderDemo/Pages/GlyphRunPage.xaml.cs | 4 +- src/Avalonia.Base/Media/FontManager.cs | 12 +- src/Avalonia.Base/Media/FontMetrics.cs | 63 +++++++++ src/Avalonia.Base/Media/GlyphRun.cs | 18 +-- src/Avalonia.Base/Media/GlyphTypeface.cs | 130 ------------------ .../IGlyphTypeface.cs} | 73 ++++------ src/Avalonia.Base/Media/TextDecoration.cs | 16 +-- .../Media/TextFormatting/ShapedBuffer.cs | 6 +- .../TextFormatting/ShapedTextCharacters.cs | 9 +- .../Media/TextFormatting/TextLineImpl.cs | 26 ++-- .../{FontMetrics.cs => TextMetrics.cs} | 24 ++-- .../Media/TextFormatting/TextShaperOptions.cs | 4 +- .../Media/TextFormatting/Unicode/Codepoint.cs | 5 +- src/Avalonia.Base/Media/Typeface.cs | 2 +- .../Platform/IFontManagerImpl.cs | 2 +- .../Platform/IPlatformRenderInterface.cs | 6 +- .../Composition/Server/FpsCounter.cs | 2 +- src/Avalonia.Base/Utilities/ReadOnlySlice.cs | 2 + .../HeadlessPlatformStubs.cs | 22 ++- src/Skia/Avalonia.Skia/FontManagerImpl.cs | 2 +- src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs | 92 +++++-------- .../Avalonia.Skia/PlatformRenderInterface.cs | 18 +-- .../Media/FontManagerImpl.cs | 2 +- .../Media/GlyphTypefaceImpl.cs | 105 ++++++-------- .../Media/CustomFontManagerImpl.cs | 2 +- .../TextFormatting/TextFormatterTests.cs | 2 +- .../Media/TextFormatting/TextLayoutTests.cs | 4 +- .../HarfBuzzFontManagerImpl.cs | 2 +- .../HarfBuzzGlyphTypefaceImpl.cs | 96 ++++++------- .../Avalonia.UnitTests/MockFontManagerImpl.cs | 2 +- tests/Avalonia.UnitTests/MockGlyphTypeface.cs | 34 +++-- 31 files changed, 352 insertions(+), 435 deletions(-) create mode 100644 src/Avalonia.Base/Media/FontMetrics.cs delete mode 100644 src/Avalonia.Base/Media/GlyphTypeface.cs rename src/Avalonia.Base/{Platform/IGlyphTypefaceImpl.cs => Media/IGlyphTypeface.cs} (53%) rename src/Avalonia.Base/Media/TextFormatting/{FontMetrics.cs => TextMetrics.cs} (69%) diff --git a/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs b/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs index 674ed8e61f..5c31e138e0 100644 --- a/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs +++ b/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs @@ -22,7 +22,7 @@ namespace RenderDemo.Pages public class GlyphRunControl : Control { - private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface; + private IGlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface; private readonly Random _rand = new Random(); private ushort[] _glyphIndices = new ushort[1]; private char[] _characters = new char[1]; @@ -81,7 +81,7 @@ namespace RenderDemo.Pages public class GlyphRunGeometryControl : Control { - private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface; + private IGlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface; private readonly Random _rand = new Random(); private ushort[] _glyphIndices = new ushort[1]; private char[] _characters = new char[1]; diff --git a/src/Avalonia.Base/Media/FontManager.cs b/src/Avalonia.Base/Media/FontManager.cs index 37091b82e3..dcb29ffebf 100644 --- a/src/Avalonia.Base/Media/FontManager.cs +++ b/src/Avalonia.Base/Media/FontManager.cs @@ -13,8 +13,8 @@ namespace Avalonia.Media /// public sealed class FontManager { - private readonly ConcurrentDictionary _glyphTypefaceCache = - new ConcurrentDictionary(); + private readonly ConcurrentDictionary _glyphTypefaceCache = + new ConcurrentDictionary(); private readonly FontFamily _defaultFontFamily; private readonly IReadOnlyList? _fontFallbacks; @@ -81,13 +81,13 @@ namespace Avalonia.Media PlatformImpl.GetInstalledFontFamilyNames(checkForUpdates); /// - /// Returns a new , or an existing one if a matching exists. + /// Returns a new , or an existing one if a matching exists. /// /// The typeface. /// - /// The . + /// The . /// - public GlyphTypeface GetOrAddGlyphTypeface(Typeface typeface) + public IGlyphTypeface GetOrAddGlyphTypeface(Typeface typeface) { while (true) { @@ -96,7 +96,7 @@ namespace Avalonia.Media return glyphTypeface; } - glyphTypeface = new GlyphTypeface(typeface); + glyphTypeface = PlatformImpl.CreateGlyphTypeface(typeface); if (_glyphTypefaceCache.TryAdd(typeface, glyphTypeface)) { diff --git a/src/Avalonia.Base/Media/FontMetrics.cs b/src/Avalonia.Base/Media/FontMetrics.cs new file mode 100644 index 0000000000..3924119a3e --- /dev/null +++ b/src/Avalonia.Base/Media/FontMetrics.cs @@ -0,0 +1,63 @@ +namespace Avalonia.Media +{ + /// + /// The font metrics is holding information about a font's ascent, descent, etc. in design em units. + /// + public readonly struct FontMetrics + { + /// + /// Gets the font design units per em. + /// + public short DesignEmHeight { get; init; } + + /// + /// A value indicating whether all glyphs in the font have the same advancement. + /// + public bool IsFixedPitch { get; init; } + + /// + /// Gets the recommended distance above the baseline in design em size. + /// + public int Ascent { get; init; } + + /// + /// Gets the recommended distance under the baseline in design em size. + /// + public int Descent { get; init; } + + /// + /// Gets the recommended additional space between two lines of text in design em size. + /// + public int LineGap { get; init; } + + /// + /// Gets the recommended line spacing of a formed text line. + /// + public int LineSpacing => Descent - Ascent + LineGap; + + /// + /// Gets a value that indicates the distance of the underline from the baseline in design em size. + /// + public int UnderlinePosition { get; init; } + + /// + /// Gets a value that indicates the thickness of the underline in design em size. + /// + public int UnderlineThickness { get; init; } + + /// + /// Gets a value that indicates the distance of the strikethrough from the baseline in design em size. + /// + public int StrikethroughPosition { get; init; } + + /// + /// Gets a value that indicates the thickness of the underline in design em size. + /// + public int StrikethroughThickness { get; init; } + } +} + +namespace System.Runtime.CompilerServices +{ + public class IsExternalInit { } +} diff --git a/src/Avalonia.Base/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs index ce436ee017..a1cb00e209 100644 --- a/src/Avalonia.Base/Media/GlyphRun.cs +++ b/src/Avalonia.Base/Media/GlyphRun.cs @@ -16,7 +16,7 @@ namespace Avalonia.Media private static readonly IComparer s_descendingComparer = new ReverseComparer(); private IGlyphRunImpl? _glyphRunImpl; - private GlyphTypeface _glyphTypeface; + private IGlyphTypeface _glyphTypeface; private double _fontRenderingEmSize; private int _biDiLevel; private Point? _baselineOrigin; @@ -43,7 +43,7 @@ namespace Avalonia.Media /// The glyph clusters. /// The bidi level. public GlyphRun( - GlyphTypeface glyphTypeface, + IGlyphTypeface glyphTypeface, double fontRenderingEmSize, ReadOnlySlice characters, IReadOnlyList glyphIndices, @@ -70,9 +70,9 @@ namespace Avalonia.Media } /// - /// Gets the for the . + /// Gets the for the . /// - public GlyphTypeface GlyphTypeface => _glyphTypeface; + public IGlyphTypeface GlyphTypeface => _glyphTypeface; /// /// Gets or sets the em size used for rendering the . @@ -172,7 +172,7 @@ namespace Avalonia.Media /// /// Gets the scale of the current /// - internal double Scale => FontRenderingEmSize / GlyphTypeface.DesignEmHeight; + internal double Scale => FontRenderingEmSize / GlyphTypeface.Metrics.DesignEmHeight; /// /// Returns true if the text direction is left-to-right. Otherwise, returns false. @@ -613,7 +613,7 @@ namespace Avalonia.Media /// The baseline origin. private Point CalculateBaselineOrigin() { - return new Point(0, -GlyphTypeface.Ascent * Scale); + return new Point(0, -GlyphTypeface.Metrics.Ascent * Scale); } private GlyphRunMetrics CreateGlyphRunMetrics() @@ -637,7 +637,7 @@ namespace Avalonia.Media } var isReversed = firstCluster > lastCluster; - var height = (GlyphTypeface.Descent - GlyphTypeface.Ascent + GlyphTypeface.LineGap) * Scale; + var height = GlyphTypeface.Metrics.LineSpacing * Scale; var widthIncludingTrailingWhitespace = 0d; var trailingWhitespaceLength = GetTrailingWhitespaceLength(isReversed, out var newLineLength, out var glyphCount); @@ -864,11 +864,11 @@ namespace Avalonia.Media var platformRenderInterface = AvaloniaLocator.Current.GetRequiredService(); var count = GlyphIndices.Count; - var scale = (float)(FontRenderingEmSize / GlyphTypeface.DesignEmHeight); + var scale = (float)(FontRenderingEmSize / GlyphTypeface.Metrics.DesignEmHeight); if (GlyphOffsets == null) { - if (GlyphTypeface.IsFixedPitch) + if (GlyphTypeface.Metrics.IsFixedPitch) { var buffer = platformRenderInterface.AllocateGlyphRun(GlyphTypeface, (float)FontRenderingEmSize, count); diff --git a/src/Avalonia.Base/Media/GlyphTypeface.cs b/src/Avalonia.Base/Media/GlyphTypeface.cs deleted file mode 100644 index 36b6f3a5f8..0000000000 --- a/src/Avalonia.Base/Media/GlyphTypeface.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System; -using Avalonia.Platform; - -namespace Avalonia.Media -{ - public sealed class GlyphTypeface : IDisposable - { - public GlyphTypeface(Typeface typeface) - : this(FontManager.Current.PlatformImpl.CreateGlyphTypeface(typeface)) - { - } - - public GlyphTypeface(IGlyphTypefaceImpl platformImpl) - { - PlatformImpl = platformImpl; - } - - public IGlyphTypefaceImpl PlatformImpl { get; } - - /// - /// Gets the font design units per em. - /// - public short DesignEmHeight => PlatformImpl.DesignEmHeight; - - /// - /// Gets the recommended distance above the baseline in design em size. - /// - public int Ascent => PlatformImpl.Ascent; - - /// - /// Gets the recommended distance under the baseline in design em size. - /// - public int Descent => PlatformImpl.Descent; - - /// - /// Gets the recommended additional space between two lines of text in design em size. - /// - public int LineGap => PlatformImpl.LineGap; - - /// - /// Gets the recommended line height. - /// - public int LineHeight => Descent - Ascent + LineGap; - - /// - /// Gets a value that indicates the distance of the underline from the baseline in design em size. - /// - public int UnderlinePosition => PlatformImpl.UnderlinePosition; - - /// - /// Gets a value that indicates the thickness of the underline in design em size. - /// - public int UnderlineThickness => PlatformImpl.UnderlineThickness; - - /// - /// Gets a value that indicates the distance of the strikethrough from the baseline in design em size. - /// - public int StrikethroughPosition => PlatformImpl.StrikethroughPosition; - - /// - /// Gets a value that indicates the thickness of the underline in design em size. - /// - public int StrikethroughThickness => PlatformImpl.StrikethroughThickness; - - /// - /// A value indicating whether all glyphs in the font have the same advancement. - /// - public bool IsFixedPitch => PlatformImpl.IsFixedPitch; - - /// - /// Gets the number of glyphs held by this glyph typeface. - /// - public int GlyphCount => PlatformImpl.GlyphCount; - - /// - /// Returns an glyph index for the specified codepoint. - /// - /// - /// Returns a replacement glyph if a glyph isn't found. - /// - /// The codepoint. - /// - /// A glyph index. - /// - public ushort GetGlyph(uint codepoint) => PlatformImpl.GetGlyph(codepoint); - - /// - /// Tries to get an glyph index for specified codepoint. - /// - /// The codepoint. - /// A glyph index. - /// - /// true if an glyph index was found, false otherwise. - /// - public bool TryGetGlyph(uint codepoint, out ushort glyph) - { - glyph = PlatformImpl.GetGlyph(codepoint); - - return glyph != 0; - } - - /// - /// Returns an array of glyph indices. Codepoints that are not represented by the font are returned as 0. - /// - /// The codepoints to map. - /// - public ushort[] GetGlyphs(ReadOnlySpan codepoints) => PlatformImpl.GetGlyphs(codepoints); - - /// - /// Returns the glyph advance for the specified glyph. - /// - /// The glyph. - /// - /// The advance. - /// - public int GetGlyphAdvance(ushort glyph) => PlatformImpl.GetGlyphAdvance(glyph); - - /// - /// Returns an array of glyph advances in design em size. - /// - /// The glyph indices. - /// - public int[] GetGlyphAdvances(ReadOnlySpan glyphs) => PlatformImpl.GetGlyphAdvances(glyphs); - - void IDisposable.Dispose() - { - PlatformImpl?.Dispose(); - } - } -} diff --git a/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs b/src/Avalonia.Base/Media/IGlyphTypeface.cs similarity index 53% rename from src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs rename to src/Avalonia.Base/Media/IGlyphTypeface.cs index 5a38e133ff..de2a2309ee 100644 --- a/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs +++ b/src/Avalonia.Base/Media/IGlyphTypeface.cs @@ -1,60 +1,23 @@ using System; using Avalonia.Metadata; -namespace Avalonia.Platform +namespace Avalonia.Media { [Unstable] - public interface IGlyphTypefaceImpl : IDisposable + public interface IGlyphTypeface : IDisposable { /// - /// Gets the font design units per em. - /// - short DesignEmHeight { get; } - - /// - /// Gets the recommended distance above the baseline in design em size. - /// - int Ascent { get; } - - /// - /// Gets the recommended distance under the baseline in design em size. - /// - int Descent { get; } - - /// - /// Gets the recommended additional space between two lines of text in design em size. - /// - int LineGap { get; } - - /// - /// Gets a value that indicates the distance of the underline from the baseline in design em size. - /// - int UnderlinePosition { get; } - - /// - /// Gets a value that indicates the thickness of the underline in design em size. - /// - int UnderlineThickness { get; } - - /// - /// Gets a value that indicates the distance of the strikethrough from the baseline in design em size. - /// - int StrikethroughPosition { get; } - - /// - /// Gets a value that indicates the thickness of the underline in design em size. - /// - int StrikethroughThickness { get; } - - /// - /// A value indicating whether all glyphs in the font have the same advancement. + /// Gets the number of glyphs held by this glyph typeface. /// - bool IsFixedPitch { get; } + int GlyphCount { get; } /// - /// Gets the number of glyphs held by this glyph typeface. + /// Gets the font metrics. /// - int GlyphCount { get; } + /// + /// The font metrics. + /// + FontMetrics Metrics { get; } /// /// Returns an glyph index for the specified codepoint. @@ -68,6 +31,16 @@ namespace Avalonia.Platform /// ushort GetGlyph(uint codepoint); + /// + /// Tries to get an glyph index for specified codepoint. + /// + /// The codepoint. + /// A glyph index. + /// + /// true if an glyph index was found, false otherwise. + /// + bool TryGetGlyph(uint codepoint, out ushort glyph); + /// /// Returns an array of glyph indices. Codepoints that are not represented by the font are returned as 0. /// @@ -94,5 +67,13 @@ namespace Avalonia.Platform /// An array of glyph advances. /// int[] GetGlyphAdvances(ReadOnlySpan glyphs); + + /// + /// Returns the contents of the table data for the specified tag. + /// + /// The table tag to get the data for. + /// The contents of the table data for the specified tag. + /// Returns true if the content exists, otherwise false. + bool TryGetTable(uint tag, out byte[] table); } } diff --git a/src/Avalonia.Base/Media/TextDecoration.cs b/src/Avalonia.Base/Media/TextDecoration.cs index 4c9764af96..0a7328125a 100644 --- a/src/Avalonia.Base/Media/TextDecoration.cs +++ b/src/Avalonia.Base/Media/TextDecoration.cs @@ -155,9 +155,9 @@ namespace Avalonia.Media /// /// The drawing context. /// The decorated run. - /// The font metrics of the decorated run. + /// The font metrics of the decorated run. /// The default brush that is used to draw the decoration. - internal void Draw(DrawingContext drawingContext, GlyphRun glyphRun, FontMetrics fontMetrics, IBrush defaultBrush) + internal void Draw(DrawingContext drawingContext, GlyphRun glyphRun, TextMetrics textMetrics, IBrush defaultBrush) { var baselineOrigin = glyphRun.BaselineOrigin; var thickness = StrokeThickness; @@ -168,16 +168,16 @@ namespace Avalonia.Media switch (Location) { case TextDecorationLocation.Underline: - thickness = fontMetrics.UnderlineThickness; + thickness = textMetrics.UnderlineThickness; break; case TextDecorationLocation.Strikethrough: - thickness = fontMetrics.StrikethroughThickness; + thickness = textMetrics.StrikethroughThickness; break; } break; case TextDecorationUnit.FontRenderingEmSize: - thickness = fontMetrics.FontRenderingEmSize * thickness; + thickness = textMetrics.FontRenderingEmSize * thickness; break; } @@ -189,17 +189,17 @@ namespace Avalonia.Media origin += glyphRun.BaselineOrigin; break; case TextDecorationLocation.Strikethrough: - origin += new Point(baselineOrigin.X, baselineOrigin.Y + fontMetrics.StrikethroughPosition); + origin += new Point(baselineOrigin.X, baselineOrigin.Y + textMetrics.StrikethroughPosition); break; case TextDecorationLocation.Underline: - origin += new Point(baselineOrigin.X, baselineOrigin.Y + fontMetrics.UnderlinePosition); + origin += new Point(baselineOrigin.X, baselineOrigin.Y + textMetrics.UnderlinePosition); break; } switch (StrokeOffsetUnit) { case TextDecorationUnit.FontRenderingEmSize: - origin += new Point(0, StrokeOffset * fontMetrics.FontRenderingEmSize); + origin += new Point(0, StrokeOffset * textMetrics.FontRenderingEmSize); break; case TextDecorationUnit.Pixel: origin += new Point(0, StrokeOffset); diff --git a/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs index 47a6334e39..85924a3d32 100644 --- a/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs +++ b/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs @@ -8,13 +8,13 @@ namespace Avalonia.Media.TextFormatting { private static readonly IComparer s_clusterComparer = new CompareClusters(); - public ShapedBuffer(ReadOnlySlice text, int length, GlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel) + public ShapedBuffer(ReadOnlySlice text, int length, IGlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel) : this(text, new GlyphInfo[length], glyphTypeface, fontRenderingEmSize, bidiLevel) { } - internal ShapedBuffer(ReadOnlySlice text, ArraySlice glyphInfos, GlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel) + internal ShapedBuffer(ReadOnlySlice text, ArraySlice glyphInfos, IGlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel) { Text = text; GlyphInfos = glyphInfos; @@ -29,7 +29,7 @@ namespace Avalonia.Media.TextFormatting public int Length => GlyphInfos.Length; - public GlyphTypeface GlyphTypeface { get; } + public IGlyphTypeface GlyphTypeface { get; } public double FontRenderingEmSize { get; } diff --git a/src/Avalonia.Base/Media/TextFormatting/ShapedTextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedTextCharacters.cs index 53287a264d..21101f462c 100644 --- a/src/Avalonia.Base/Media/TextFormatting/ShapedTextCharacters.cs +++ b/src/Avalonia.Base/Media/TextFormatting/ShapedTextCharacters.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using Avalonia.Media.TextFormatting.Unicode; using Avalonia.Utilities; @@ -18,7 +17,7 @@ namespace Avalonia.Media.TextFormatting Text = shapedBuffer.Text; Properties = properties; TextSourceLength = Text.Length; - FontMetrics = new FontMetrics(properties.Typeface, properties.FontRenderingEmSize); + TextMetrics = new TextMetrics(properties.Typeface, properties.FontRenderingEmSize); } public bool IsReversed { get; private set; } @@ -36,9 +35,9 @@ namespace Avalonia.Media.TextFormatting /// public override int TextSourceLength { get; } - public FontMetrics FontMetrics { get; } + public TextMetrics TextMetrics { get; } - public override double Baseline => -FontMetrics.Ascent; + public override double Baseline => -TextMetrics.Ascent; public override Size Size => GlyphRun.Size; @@ -89,7 +88,7 @@ namespace Avalonia.Media.TextFormatting foreach (var textDecoration in Properties.TextDecorations) { - textDecoration.Draw(drawingContext, GlyphRun, FontMetrics, Properties.ForegroundBrush); + textDecoration.Draw(drawingContext, GlyphRun, TextMetrics, Properties.ForegroundBrush); } } } diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index aba8008fb9..96f88d1f44 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -1378,17 +1378,17 @@ namespace Avalonia.Media.TextFormatting private TextLineMetrics CreateLineMetrics() { - var glyphTypeface = _paragraphProperties.DefaultTextRunProperties.Typeface.GlyphTypeface; + var fontMetrics = _paragraphProperties.DefaultTextRunProperties.Typeface.GlyphTypeface.Metrics; var fontRenderingEmSize = _paragraphProperties.DefaultTextRunProperties.FontRenderingEmSize; - var scale = fontRenderingEmSize / glyphTypeface.DesignEmHeight; + var scale = fontRenderingEmSize / fontMetrics.DesignEmHeight; var width = 0d; var widthIncludingWhitespace = 0d; var trailingWhitespaceLength = 0; var newLineLength = 0; - var ascent = glyphTypeface.Ascent * scale; - var descent = glyphTypeface.Descent * scale; - var lineGap = glyphTypeface.LineGap * scale; + var ascent = fontMetrics.Ascent * scale; + var descent = fontMetrics.Descent * scale; + var lineGap = fontMetrics.LineGap * scale; var height = descent - ascent + lineGap; @@ -1400,26 +1400,26 @@ namespace Avalonia.Media.TextFormatting { case ShapedTextCharacters textRun: { - var fontMetrics = - new FontMetrics(textRun.Properties.Typeface, textRun.Properties.FontRenderingEmSize); + var textMetrics = + new TextMetrics(textRun.Properties.Typeface, textRun.Properties.FontRenderingEmSize); if (fontRenderingEmSize < textRun.Properties.FontRenderingEmSize) { fontRenderingEmSize = textRun.Properties.FontRenderingEmSize; - if (ascent > fontMetrics.Ascent) + if (ascent > textMetrics.Ascent) { - ascent = fontMetrics.Ascent; + ascent = textMetrics.Ascent; } - if (descent < fontMetrics.Descent) + if (descent < textMetrics.Descent) { - descent = fontMetrics.Descent; + descent = textMetrics.Descent; } - if (lineGap < fontMetrics.LineGap) + if (lineGap < textMetrics.LineGap) { - lineGap = fontMetrics.LineGap; + lineGap = textMetrics.LineGap; } if (descent - ascent + lineGap > height) diff --git a/src/Avalonia.Base/Media/TextFormatting/FontMetrics.cs b/src/Avalonia.Base/Media/TextFormatting/TextMetrics.cs similarity index 69% rename from src/Avalonia.Base/Media/TextFormatting/FontMetrics.cs rename to src/Avalonia.Base/Media/TextFormatting/TextMetrics.cs index e01bba00a4..0382e66b5a 100644 --- a/src/Avalonia.Base/Media/TextFormatting/FontMetrics.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextMetrics.cs @@ -1,33 +1,33 @@ namespace Avalonia.Media.TextFormatting { /// - /// A metric that holds information about font specific measurements. + /// A metric that holds information about text specific measurements. /// - public readonly struct FontMetrics + public readonly struct TextMetrics { - public FontMetrics(Typeface typeface, double fontRenderingEmSize) + public TextMetrics(Typeface typeface, double fontRenderingEmSize) { - var glyphTypeface = typeface.GlyphTypeface; + var fontMetrics = typeface.GlyphTypeface.Metrics; - var scale = fontRenderingEmSize / glyphTypeface.DesignEmHeight; + var scale = fontRenderingEmSize / fontMetrics.DesignEmHeight; FontRenderingEmSize = fontRenderingEmSize; - Ascent = glyphTypeface.Ascent * scale; + Ascent = fontMetrics.Ascent * scale; - Descent = glyphTypeface.Descent * scale; + Descent = fontMetrics.Descent * scale; - LineGap = glyphTypeface.LineGap * scale; + LineGap = fontMetrics.LineGap * scale; LineHeight = Descent - Ascent + LineGap; - UnderlineThickness = glyphTypeface.UnderlineThickness * scale; + UnderlineThickness = fontMetrics.UnderlineThickness * scale; - UnderlinePosition = glyphTypeface.UnderlinePosition * scale; + UnderlinePosition = fontMetrics.UnderlinePosition * scale; - StrikethroughThickness = glyphTypeface.StrikethroughThickness * scale; + StrikethroughThickness = fontMetrics.StrikethroughThickness * scale; - StrikethroughPosition = glyphTypeface.StrikethroughPosition * scale; + StrikethroughPosition = fontMetrics.StrikethroughPosition * scale; } /// diff --git a/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs b/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs index 4e75bb921e..0d00bed51e 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs @@ -8,7 +8,7 @@ namespace Avalonia.Media.TextFormatting public readonly struct TextShaperOptions { public TextShaperOptions( - GlyphTypeface typeface, + IGlyphTypeface typeface, double fontRenderingEmSize = 12, sbyte bidiLevel = 0, CultureInfo? culture = null, @@ -24,7 +24,7 @@ namespace Avalonia.Media.TextFormatting /// /// Get the typeface. /// - public GlyphTypeface Typeface { get; } + public IGlyphTypeface Typeface { get; } /// /// Get the font rendering em size. /// diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs index ce9cdde044..de40839853 100644 --- a/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs +++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs @@ -1,4 +1,5 @@ -using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; using Avalonia.Utilities; namespace Avalonia.Media.TextFormatting.Unicode @@ -165,7 +166,7 @@ namespace Avalonia.Media.TextFormatting.Unicode /// The index to read at. /// The count of character that were read. /// - public static Codepoint ReadAt(ReadOnlySlice text, int index, out int count) + public static Codepoint ReadAt(ReadOnlySpan text, int index, out int count) { count = 1; diff --git a/src/Avalonia.Base/Media/Typeface.cs b/src/Avalonia.Base/Media/Typeface.cs index f0daa841d9..e6047bf96c 100644 --- a/src/Avalonia.Base/Media/Typeface.cs +++ b/src/Avalonia.Base/Media/Typeface.cs @@ -81,7 +81,7 @@ namespace Avalonia.Media /// /// The glyph typeface. /// - public GlyphTypeface GlyphTypeface => FontManager.Current.GetOrAddGlyphTypeface(this); + public IGlyphTypeface GlyphTypeface => FontManager.Current.GetOrAddGlyphTypeface(this); public static bool operator !=(Typeface a, Typeface b) { diff --git a/src/Avalonia.Base/Platform/IFontManagerImpl.cs b/src/Avalonia.Base/Platform/IFontManagerImpl.cs index 932249bd52..cd6e64abaf 100644 --- a/src/Avalonia.Base/Platform/IFontManagerImpl.cs +++ b/src/Avalonia.Base/Platform/IFontManagerImpl.cs @@ -43,6 +43,6 @@ namespace Avalonia.Platform /// 0 /// The created glyph typeface. Can be Null if it was not possible to create a glyph typeface. /// - IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface); + IGlyphTypeface CreateGlyphTypeface(Typeface typeface); } } diff --git a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs index 93105b21a4..9d0d7974b4 100644 --- a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs +++ b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs @@ -180,7 +180,7 @@ namespace Avalonia.Platform /// /// This buffer only holds glyph indices. /// - IGlyphRunBuffer AllocateGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length); + IGlyphRunBuffer AllocateGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length); /// /// Allocates a horizontal platform glyph run buffer. @@ -192,7 +192,7 @@ namespace Avalonia.Platform /// /// This buffer holds glyph indices and glyph advances. /// - IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length); + IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length); /// /// Allocates a positioned platform glyph run buffer. @@ -204,7 +204,7 @@ namespace Avalonia.Platform /// /// This buffer holds glyph indices, glyph advances and glyph positions. /// - IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length); + IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length); /// /// Gets a value indicating whether the platform directly supports rectangles with rounded corners. diff --git a/src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs b/src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs index b0b3982ed5..06fb526736 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs @@ -25,7 +25,7 @@ internal class FpsCounter // ASCII chars private GlyphRun[] _runs = new GlyphRun[LastChar - FirstChar + 1]; - public FpsCounter(GlyphTypeface typeface) + public FpsCounter(IGlyphTypeface typeface) { for (var c = FirstChar; c <= LastChar; c++) { diff --git a/src/Avalonia.Base/Utilities/ReadOnlySlice.cs b/src/Avalonia.Base/Utilities/ReadOnlySlice.cs index ad545b2923..583a3139b9 100644 --- a/src/Avalonia.Base/Utilities/ReadOnlySlice.cs +++ b/src/Avalonia.Base/Utilities/ReadOnlySlice.cs @@ -214,6 +214,8 @@ namespace Avalonia.Utilities return new ReadOnlySlice(memory); } + public static implicit operator ReadOnlySpan(ReadOnlySlice slice) => slice.Span; + internal class ReadOnlySliceDebugView { private readonly ReadOnlySlice _readOnlySlice; diff --git a/src/Avalonia.Headless/HeadlessPlatformStubs.cs b/src/Avalonia.Headless/HeadlessPlatformStubs.cs index b3bf92f06b..c8ac947c16 100644 --- a/src/Avalonia.Headless/HeadlessPlatformStubs.cs +++ b/src/Avalonia.Headless/HeadlessPlatformStubs.cs @@ -75,8 +75,13 @@ namespace Avalonia.Headless public TimeSpan TouchDoubleClickTime => DoubleClickTime; } - class HeadlessGlyphTypefaceImpl : IGlyphTypefaceImpl + class HeadlessGlyphTypefaceImpl : IGlyphTypeface { + public FontMetrics Metrics => new FontMetrics + { + + }; + public short DesignEmHeight => 10; public int Ascent => 5; @@ -106,6 +111,13 @@ namespace Avalonia.Headless return 1; } + public bool TryGetGlyph(uint codepoint, out ushort glyph) + { + glyph = 1; + + return true; + } + public int GetGlyphAdvance(ushort glyph) { return 1; @@ -120,6 +132,12 @@ namespace Avalonia.Headless { return codepoints.ToArray().Select(x => (ushort)x).ToArray(); } + + public bool TryGetTable(uint tag, out byte[] table) + { + table = null; + return false; + } } class HeadlessTextShaperStub : ITextShaperImpl @@ -136,7 +154,7 @@ namespace Avalonia.Headless class HeadlessFontManagerStub : IFontManagerImpl { - public IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface) + public IGlyphTypeface CreateGlyphTypeface(Typeface typeface) { return new HeadlessGlyphTypefaceImpl(); } diff --git a/src/Skia/Avalonia.Skia/FontManagerImpl.cs b/src/Skia/Avalonia.Skia/FontManagerImpl.cs index 125dd0e455..6b5e0b3db5 100644 --- a/src/Skia/Avalonia.Skia/FontManagerImpl.cs +++ b/src/Skia/Avalonia.Skia/FontManagerImpl.cs @@ -102,7 +102,7 @@ namespace Avalonia.Skia return false; } - public IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface) + public IGlyphTypeface CreateGlyphTypeface(Typeface typeface) { SKTypeface skTypeface = null; diff --git a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs index a3174010a1..7f3faf251f 100644 --- a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs +++ b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs @@ -1,14 +1,14 @@ using System; using System.Runtime.InteropServices; +using Avalonia.Media; using Avalonia.Metadata; -using Avalonia.Platform; using HarfBuzzSharp; using SkiaSharp; namespace Avalonia.Skia { [Unstable] - public class GlyphTypefaceImpl : IGlyphTypefaceImpl + public class GlyphTypefaceImpl : IGlyphTypeface { private bool _isDisposed; @@ -25,35 +25,30 @@ namespace Avalonia.Skia Font.SetFunctionsOpenType(); - DesignEmHeight = (short)Typeface.UnitsPerEm; - var metrics = Typeface.ToFont().Metrics; const double defaultFontRenderingEmSize = 12.0; - Ascent = (int)(metrics.Ascent / defaultFontRenderingEmSize * Typeface.UnitsPerEm); - - Descent = (int)(metrics.Descent / defaultFontRenderingEmSize * Typeface.UnitsPerEm); - - LineGap = (int)(metrics.Leading / defaultFontRenderingEmSize * Typeface.UnitsPerEm); - - UnderlinePosition = metrics.UnderlinePosition != null ? + Metrics = new FontMetrics + { + DesignEmHeight = (short)Typeface.UnitsPerEm, + Ascent = (int)(metrics.Ascent / defaultFontRenderingEmSize * Typeface.UnitsPerEm), + Descent = (int)(metrics.Descent / defaultFontRenderingEmSize * Typeface.UnitsPerEm), + LineGap = (int)(metrics.Leading / defaultFontRenderingEmSize * Typeface.UnitsPerEm), + UnderlinePosition = metrics.UnderlinePosition != null ? (int)(metrics.UnderlinePosition / defaultFontRenderingEmSize * Typeface.UnitsPerEm) : - 0; - - UnderlineThickness = metrics.UnderlineThickness != null ? + 0, + UnderlineThickness = metrics.UnderlineThickness != null ? (int)(metrics.UnderlineThickness / defaultFontRenderingEmSize * Typeface.UnitsPerEm) : - 0; - - StrikethroughPosition = metrics.StrikeoutPosition != null ? + 0, + StrikethroughPosition = metrics.StrikeoutPosition != null ? (int)(metrics.StrikeoutPosition / defaultFontRenderingEmSize * Typeface.UnitsPerEm) : - 0; - - StrikethroughThickness = metrics.StrikeoutThickness != null ? + 0, + StrikethroughThickness = metrics.StrikeoutThickness != null ? (int)(metrics.StrikeoutThickness / defaultFontRenderingEmSize * Typeface.UnitsPerEm) : - 0; - - IsFixedPitch = Typeface.IsFixedPitch; + 0, + IsFixedPitch = Typeface.IsFixedPitch + }; GlyphCount = Typeface.GlyphCount; @@ -69,41 +64,16 @@ namespace Avalonia.Skia public SKTypeface Typeface { get; } public int ReplacementCodepoint { get; } - - /// - public short DesignEmHeight { get; } - - /// - public int Ascent { get; } - - /// - public int Descent { get; } - - /// - public int LineGap { get; } - /// - public int UnderlinePosition { get; } - - /// - public int UnderlineThickness { get; } - - /// - public int StrikethroughPosition { get; } - - /// - public int StrikethroughThickness { get; } - - /// - public bool IsFixedPitch { get; } + public FontMetrics Metrics { get; } public int GlyphCount { get; } - + public bool IsFakeBold { get; } - + public bool IsFakeItalic { get; } - /// + /// public ushort GetGlyph(uint codepoint) { if (Font.TryGetGlyph(codepoint, out var glyph)) @@ -114,7 +84,14 @@ namespace Avalonia.Skia return 0; } - /// + public bool TryGetGlyph(uint codepoint, out ushort glyph) + { + glyph = GetGlyph(codepoint); + + return glyph != 0; + } + + /// public ushort[] GetGlyphs(ReadOnlySpan codepoints) { var glyphs = new ushort[codepoints.Length]; @@ -130,13 +107,13 @@ namespace Avalonia.Skia return glyphs; } - /// + /// public int GetGlyphAdvance(ushort glyph) { return Font.GetHorizontalGlyphAdvance(glyph); } - /// + /// public int[] GetGlyphAdvances(ReadOnlySpan glyphs) { var glyphIndices = new uint[glyphs.Length]; @@ -184,5 +161,10 @@ namespace Avalonia.Skia Dispose(true); GC.SuppressFinalize(this); } + + public bool TryGetTable(uint tag, out byte[] table) + { + return Typeface.TryGetTableData(tag, out table); + } } } diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index aa9225ba64..a9696efbd4 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -70,7 +70,7 @@ namespace Avalonia.Skia public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun) { - if (glyphRun.GlyphTypeface.PlatformImpl is not GlyphTypefaceImpl glyphTypeface) + if (glyphRun.GlyphTypeface is not GlyphTypefaceImpl glyphTypeface) { throw new InvalidOperationException("PlatformImpl can't be null."); } @@ -244,13 +244,13 @@ namespace Avalonia.Skia "Current GPU acceleration backend does not support OpenGL integration"); } - public IGlyphRunBuffer AllocateGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + public IGlyphRunBuffer AllocateGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) => new SKGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length); - public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) => new SKHorizontalGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length); - public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) => new SKPositionedGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length); private abstract class SKGlyphRunBufferBase : IGlyphRunBuffer @@ -258,11 +258,11 @@ namespace Avalonia.Skia protected readonly SKTextBlobBuilder _builder; protected readonly SKFont _font; - public SKGlyphRunBufferBase(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + public SKGlyphRunBufferBase(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) { _builder = new SKTextBlobBuilder(); - var glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface.PlatformImpl; + var glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface; _font = new SKFont { @@ -289,7 +289,7 @@ namespace Avalonia.Skia { private readonly SKRunBuffer _buffer; - public SKGlyphRunBuffer(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) : base(glyphTypeface, fontRenderingEmSize, length) + public SKGlyphRunBuffer(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) : base(glyphTypeface, fontRenderingEmSize, length) { _buffer = _builder.AllocateRun(_font, length, 0, 0); } @@ -301,7 +301,7 @@ namespace Avalonia.Skia { private readonly SKHorizontalRunBuffer _buffer; - public SKHorizontalGlyphRunBuffer(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) : base(glyphTypeface, fontRenderingEmSize, length) + public SKHorizontalGlyphRunBuffer(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) : base(glyphTypeface, fontRenderingEmSize, length) { _buffer = _builder.AllocateHorizontalRun(_font, length, 0); } @@ -315,7 +315,7 @@ namespace Avalonia.Skia { private readonly SKPositionedRunBuffer _buffer; - public SKPositionedGlyphRunBuffer(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) : base(glyphTypeface, fontRenderingEmSize, length) + public SKPositionedGlyphRunBuffer(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) : base(glyphTypeface, fontRenderingEmSize, length) { _buffer = _builder.AllocatePositionedRun(_font, length); } diff --git a/src/Windows/Avalonia.Direct2D1/Media/FontManagerImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/FontManagerImpl.cs index c996337520..b98ed3ffe6 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/FontManagerImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/FontManagerImpl.cs @@ -62,7 +62,7 @@ namespace Avalonia.Direct2D1.Media return false; } - public IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface) + public IGlyphTypeface CreateGlyphTypeface(Typeface typeface) { return new GlyphTypefaceImpl(typeface); } diff --git a/src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs index 443bb8a207..77d0e58d3d 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs @@ -1,14 +1,15 @@ using System; +using System.Drawing.Drawing2D; using Avalonia.Media; using Avalonia.Metadata; -using Avalonia.Platform; using HarfBuzzSharp; using SharpDX.DirectWrite; +using FontMetrics = Avalonia.Media.FontMetrics; namespace Avalonia.Direct2D1.Media { [Unstable] - public class GlyphTypefaceImpl : IGlyphTypefaceImpl + public class GlyphTypefaceImpl : IGlyphTypeface { private bool _isDisposed; @@ -26,40 +27,28 @@ namespace Avalonia.Direct2D1.Media Font.GetScale(out var xScale, out _); - DesignEmHeight = (short)xScale; - if (!Font.TryGetHorizontalFontExtents(out var fontExtents)) { Font.TryGetVerticalFontExtents(out fontExtents); } - Ascent = -fontExtents.Ascender; - - Descent = -fontExtents.Descender; - - LineGap = fontExtents.LineGap; - - if (Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.UnderlineOffset, out var underlinePosition)) - { - UnderlinePosition = underlinePosition; - } - - if (Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.UnderlineSize, out var underlineThickness)) - { - UnderlineThickness = underlineThickness; - } + Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.UnderlineOffset, out var underlinePosition); + Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.UnderlineSize, out var underlineThickness); + Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.StrikeoutOffset, out var strikethroughPosition); + Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.StrikeoutSize, out var strikethroughThickness); - if (Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.StrikeoutOffset, out var strikethroughPosition)) + Metrics = new FontMetrics { - StrikethroughPosition = strikethroughPosition; - } - - if (Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.StrikeoutSize, out var strikethroughThickness)) - { - StrikethroughThickness = strikethroughThickness; - } - - IsFixedPitch = FontFace.IsMonospacedFont; + DesignEmHeight = (short)xScale, + Ascent = -fontExtents.Ascender, + Descent = -fontExtents.Descender, + LineGap = fontExtents.LineGap, + UnderlinePosition = underlinePosition, + UnderlineThickness = underlineThickness, + StrikethroughPosition = strikethroughPosition, + StrikethroughThickness = strikethroughThickness, + IsFixedPitch = FontFace.IsMonospacedFont + }; } private Blob GetTable(Face face, Tag tag) @@ -89,37 +78,11 @@ namespace Avalonia.Direct2D1.Media public HarfBuzzSharp.Font Font { get; } - /// - public short DesignEmHeight { get; } - - /// - public int Ascent { get; } - - /// - public int Descent { get; } - - /// - public int LineGap { get; } - - //ToDo: Read font table for these values - /// - public int UnderlinePosition { get; } - - /// - public int UnderlineThickness { get; } - - /// - public int StrikethroughPosition { get; } - - /// - public int StrikethroughThickness { get; } - - /// - public bool IsFixedPitch { get; } + public FontMetrics Metrics { get; } public int GlyphCount { get; set; } - /// + /// public ushort GetGlyph(uint codepoint) { if (Font.TryGetGlyph(codepoint, out var glyph)) @@ -130,7 +93,14 @@ namespace Avalonia.Direct2D1.Media return 0; } - /// + public bool TryGetGlyph(uint codepoint, out ushort glyph) + { + glyph = GetGlyph(codepoint); + + return glyph != 0; + } + + /// public ushort[] GetGlyphs(ReadOnlySpan codepoints) { var glyphs = new ushort[codepoints.Length]; @@ -146,13 +116,13 @@ namespace Avalonia.Direct2D1.Media return glyphs; } - /// + /// public int GetGlyphAdvance(ushort glyph) { return Font.GetHorizontalGlyphAdvance(glyph); } - /// + /// public int[] GetGlyphAdvances(ReadOnlySpan glyphs) { var glyphIndices = new uint[glyphs.Length]; @@ -189,6 +159,21 @@ namespace Avalonia.Direct2D1.Media Dispose(true); GC.SuppressFinalize(this); } + + public bool TryGetTable(uint tag, out byte[] table) + { + table = null; + var blob = Face.ReferenceTable(tag); + + if (blob.Length > 0) + { + table = blob.AsSpan().ToArray(); + + return true; + } + + return false; + } } } diff --git a/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs b/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs index 7d0cf05b0b..7998c95877 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs @@ -64,7 +64,7 @@ namespace Avalonia.Skia.UnitTests.Media return true; } - public IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface) + public IGlyphTypeface CreateGlyphTypeface(Typeface typeface) { SKTypeface skTypeface; diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs index 960c409058..a315158e1b 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs @@ -237,7 +237,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var glyph = typeface.GlyphTypeface.GetGlyph('a'); var advance = typeface.GlyphTypeface.GetGlyphAdvance(glyph) * - (12.0 / typeface.GlyphTypeface.DesignEmHeight); + (12.0 / typeface.GlyphTypeface.Metrics.DesignEmHeight); var paragraphWidth = advance * numberOfCharactersPerLine; diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs index e21986d3a2..d6da2c77c4 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs @@ -578,9 +578,9 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting { var glyphTypeface = Typeface.Default.GlyphTypeface; - var emHeight = glyphTypeface.DesignEmHeight; + var emHeight = glyphTypeface.Metrics.DesignEmHeight; - var lineHeight = (glyphTypeface.Descent - glyphTypeface.Ascent) * (12.0 / emHeight); + var lineHeight = glyphTypeface.Metrics.LineSpacing * (12.0 / emHeight); var layout = new TextLayout( text, diff --git a/tests/Avalonia.UnitTests/HarfBuzzFontManagerImpl.cs b/tests/Avalonia.UnitTests/HarfBuzzFontManagerImpl.cs index 9f958bdcdd..2b1685f358 100644 --- a/tests/Avalonia.UnitTests/HarfBuzzFontManagerImpl.cs +++ b/tests/Avalonia.UnitTests/HarfBuzzFontManagerImpl.cs @@ -58,7 +58,7 @@ namespace Avalonia.UnitTests return false; } - public IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface) + public IGlyphTypeface CreateGlyphTypeface(Typeface typeface) { var fontFamily = typeface.FontFamily; diff --git a/tests/Avalonia.UnitTests/HarfBuzzGlyphTypefaceImpl.cs b/tests/Avalonia.UnitTests/HarfBuzzGlyphTypefaceImpl.cs index 04f316e5b4..3bcbc2efbd 100644 --- a/tests/Avalonia.UnitTests/HarfBuzzGlyphTypefaceImpl.cs +++ b/tests/Avalonia.UnitTests/HarfBuzzGlyphTypefaceImpl.cs @@ -1,11 +1,11 @@ using System; using System.IO; -using Avalonia.Platform; +using Avalonia.Media; using HarfBuzzSharp; namespace Avalonia.UnitTests { - public class HarfBuzzGlyphTypefaceImpl : IGlyphTypefaceImpl + public class HarfBuzzGlyphTypefaceImpl : IGlyphTypeface { private bool _isDisposed; private Blob _blob; @@ -21,28 +21,28 @@ namespace Avalonia.UnitTests Font.SetFunctionsOpenType(); Font.GetScale(out var scale, out _); - - DesignEmHeight = (short)scale; - - var metrics = Font.OpenTypeMetrics; const double defaultFontRenderingEmSize = 12.0; - Ascent = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalAscender) / defaultFontRenderingEmSize * DesignEmHeight); - - Descent = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalDescender) / defaultFontRenderingEmSize * DesignEmHeight); + var metrics = Font.OpenTypeMetrics; - LineGap = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalLineGap) / defaultFontRenderingEmSize * DesignEmHeight); + Metrics = new FontMetrics + { + DesignEmHeight = (short)scale, + Ascent = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalAscender) / defaultFontRenderingEmSize * scale), + Descent = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalDescender) / defaultFontRenderingEmSize * scale), + LineGap = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalLineGap) / defaultFontRenderingEmSize * scale), - UnderlinePosition = (int)(metrics.GetXVariation(OpenTypeMetricsTag.UnderlineOffset) / defaultFontRenderingEmSize * DesignEmHeight); + UnderlinePosition = (int)(metrics.GetXVariation(OpenTypeMetricsTag.UnderlineOffset) / defaultFontRenderingEmSize * scale), - UnderlineThickness = (int)(metrics.GetXVariation(OpenTypeMetricsTag.UnderlineSize) / defaultFontRenderingEmSize * DesignEmHeight); + UnderlineThickness = (int)(metrics.GetXVariation(OpenTypeMetricsTag.UnderlineSize) / defaultFontRenderingEmSize * scale), - StrikethroughPosition = (int)(metrics.GetXVariation(OpenTypeMetricsTag.StrikeoutOffset) / defaultFontRenderingEmSize * DesignEmHeight); + StrikethroughPosition = (int)(metrics.GetXVariation(OpenTypeMetricsTag.StrikeoutOffset) / defaultFontRenderingEmSize * scale), - StrikethroughThickness = (int)(metrics.GetXVariation(OpenTypeMetricsTag.StrikeoutSize) / defaultFontRenderingEmSize * DesignEmHeight); + StrikethroughThickness = (int)(metrics.GetXVariation(OpenTypeMetricsTag.StrikeoutSize) / defaultFontRenderingEmSize * scale), - IsFixedPitch = GetGlyphAdvance(GetGlyph('a')) == GetGlyphAdvance(GetGlyph('b')); + IsFixedPitch = GetGlyphAdvance(GetGlyph('a')) == GetGlyphAdvance(GetGlyph('b')) + }; GlyphCount = Face.GlyphCount; @@ -51,44 +51,19 @@ namespace Avalonia.UnitTests IsFakeItalic = isFakeItalic; } + public FontMetrics Metrics { get; } + public Face Face { get; } public Font Font { get; } - /// - public short DesignEmHeight { get; } - - /// - public int Ascent { get; } - - /// - public int Descent { get; } - - /// - public int LineGap { get; } - - /// - public int UnderlinePosition { get; } - - /// - public int UnderlineThickness { get; } - - /// - public int StrikethroughPosition { get; } - - /// - public int StrikethroughThickness { get; } - - /// - public bool IsFixedPitch { get; } - public int GlyphCount { get; set; } public bool IsFakeBold { get; } public bool IsFakeItalic { get; } - /// + /// public ushort GetGlyph(uint codepoint) { if (Font.TryGetGlyph(codepoint, out var glyph)) @@ -99,7 +74,21 @@ namespace Avalonia.UnitTests return 0; } - /// + public bool TryGetGlyph(uint codepoint,out ushort glyph) + { + glyph = 0; + + if (Font.TryGetGlyph(codepoint, out var glyphId)) + { + glyph = (ushort)glyphId; + + return true; + } + + return false; + } + + /// public ushort[] GetGlyphs(ReadOnlySpan codepoints) { var glyphs = new ushort[codepoints.Length]; @@ -115,13 +104,13 @@ namespace Avalonia.UnitTests return glyphs; } - /// + /// public int GetGlyphAdvance(ushort glyph) { return Font.GetHorizontalGlyphAdvance(glyph); } - /// + /// public int[] GetGlyphAdvances(ReadOnlySpan glyphs) { var glyphIndices = new uint[glyphs.Length]; @@ -134,6 +123,21 @@ namespace Avalonia.UnitTests return Font.GetHorizontalGlyphAdvances(glyphIndices); } + public bool TryGetTable(uint tag, out byte[] table) + { + table = null; + var blob = Face.ReferenceTable(tag); + + if (blob.Length > 0) + { + table = blob.AsSpan().ToArray(); + + return true; + } + + return false; + } + private void Dispose(bool disposing) { if (_isDisposed) diff --git a/tests/Avalonia.UnitTests/MockFontManagerImpl.cs b/tests/Avalonia.UnitTests/MockFontManagerImpl.cs index e2678394df..e9b923a367 100644 --- a/tests/Avalonia.UnitTests/MockFontManagerImpl.cs +++ b/tests/Avalonia.UnitTests/MockFontManagerImpl.cs @@ -33,7 +33,7 @@ namespace Avalonia.UnitTests return false; } - public IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface) + public IGlyphTypeface CreateGlyphTypeface(Typeface typeface) { return new MockGlyphTypeface(); } diff --git a/tests/Avalonia.UnitTests/MockGlyphTypeface.cs b/tests/Avalonia.UnitTests/MockGlyphTypeface.cs index 0b7beeb4fb..a1c492a7f1 100644 --- a/tests/Avalonia.UnitTests/MockGlyphTypeface.cs +++ b/tests/Avalonia.UnitTests/MockGlyphTypeface.cs @@ -1,19 +1,18 @@ using System; -using Avalonia.Platform; +using Avalonia.Media; namespace Avalonia.UnitTests { - public class MockGlyphTypeface : IGlyphTypefaceImpl + public class MockGlyphTypeface : IGlyphTypeface { - public short DesignEmHeight => 10; - public int Ascent => 2; - public int Descent => 10; - public int LineGap { get; } - public int UnderlinePosition { get; } - public int UnderlineThickness { get; } - public int StrikethroughPosition { get; } - public int StrikethroughThickness { get; } - public bool IsFixedPitch { get; } + public FontMetrics Metrics => new FontMetrics + { + DesignEmHeight = 10, + Ascent = 2, + Descent = 10, + IsFixedPitch = true + }; + public int GlyphCount => 1337; public ushort GetGlyph(uint codepoint) @@ -31,6 +30,13 @@ namespace Avalonia.UnitTests return 8; } + public bool TryGetGlyph(uint codepoint, out ushort glyph) + { + glyph = 8; + + return true; + } + public int[] GetGlyphAdvances(ReadOnlySpan glyphs) { var advances = new int[glyphs.Length]; @@ -44,5 +50,11 @@ namespace Avalonia.UnitTests } public void Dispose() { } + + public bool TryGetTable(uint tag, out byte[] table) + { + table = null; + return false; + } } } From a4a24c2fa56c54753102df216acb76ff0e285c88 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Wed, 12 Oct 2022 15:41:13 +0200 Subject: [PATCH 3/5] Remove GlyphTypeface.PlatformImpl --- src/Avalonia.Base/Media/FontManager.cs | 2 +- .../HeadlessPlatformRenderInterface.cs | 6 +++--- src/Skia/Avalonia.Skia/TextShaperImpl.cs | 2 +- .../Avalonia.Direct2D1/Direct2D1Platform.cs | 16 ++++++++-------- .../Avalonia.Direct2D1/Media/TextShaperImpl.cs | 2 +- .../Media/GlyphRunTests.cs | 2 +- .../VisualTree/MockRenderInterface.cs | 6 +++--- .../Avalonia.Benchmarks/NullRenderingPlatform.cs | 6 +++--- .../Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs | 2 +- .../MockPlatformRenderInterface.cs | 6 +++--- 10 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/Avalonia.Base/Media/FontManager.cs b/src/Avalonia.Base/Media/FontManager.cs index dcb29ffebf..d92d003c2a 100644 --- a/src/Avalonia.Base/Media/FontManager.cs +++ b/src/Avalonia.Base/Media/FontManager.cs @@ -81,7 +81,7 @@ namespace Avalonia.Media PlatformImpl.GetInstalledFontFamilyNames(checkForUpdates); /// - /// Returns a new , or an existing one if a matching exists. + /// Returns a new , or an existing one if a matching exists. /// /// The typeface. /// diff --git a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs index 12a9a016f3..fcd7f1e31f 100644 --- a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs +++ b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs @@ -115,17 +115,17 @@ namespace Avalonia.Headless return new HeadlessGeometryStub(new Rect(glyphRun.Size)); } - public IGlyphRunBuffer AllocateGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + public IGlyphRunBuffer AllocateGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) { return new HeadlessGlyphRunBufferStub(); } - public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) { return new HeadlessHorizontalGlyphRunBufferStub(); } - public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) { return new HeadlessPositionedGlyphRunBufferStub(); } diff --git a/src/Skia/Avalonia.Skia/TextShaperImpl.cs b/src/Skia/Avalonia.Skia/TextShaperImpl.cs index 6da8a20f6a..b07deb1f4d 100644 --- a/src/Skia/Avalonia.Skia/TextShaperImpl.cs +++ b/src/Skia/Avalonia.Skia/TextShaperImpl.cs @@ -31,7 +31,7 @@ namespace Avalonia.Skia buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture); - var font = ((GlyphTypefaceImpl)typeface.PlatformImpl).Font; + var font = ((GlyphTypefaceImpl)typeface).Font; font.Shape(buffer); diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs index 45744e6efa..8103f89dad 100644 --- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs +++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs @@ -162,7 +162,7 @@ namespace Avalonia.Direct2D1 public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun) { - if (glyphRun.GlyphTypeface.PlatformImpl is not GlyphTypefaceImpl glyphTypeface) + if (glyphRun.GlyphTypeface is not GlyphTypefaceImpl glyphTypeface) { throw new InvalidOperationException("PlatformImpl can't be null."); } @@ -264,9 +264,9 @@ namespace Avalonia.Direct2D1 { protected readonly SharpDX.DirectWrite.GlyphRun _dwRun; - public DWGlyphRunBuffer(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + public DWGlyphRunBuffer(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) { - var glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface.PlatformImpl; + var glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface; _dwRun = new SharpDX.DirectWrite.GlyphRun { @@ -286,7 +286,7 @@ namespace Avalonia.Direct2D1 private class DWHorizontalGlyphRunBuffer : DWGlyphRunBuffer, IHorizontalGlyphRunBuffer { - public DWHorizontalGlyphRunBuffer(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + public DWHorizontalGlyphRunBuffer(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) : base(glyphTypeface, fontRenderingEmSize, length) { _dwRun.Advances = new float[length]; @@ -297,7 +297,7 @@ namespace Avalonia.Direct2D1 private class DWPositionedGlyphRunBuffer : DWGlyphRunBuffer, IPositionedGlyphRunBuffer { - public DWPositionedGlyphRunBuffer(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + public DWPositionedGlyphRunBuffer(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) : base(glyphTypeface, fontRenderingEmSize, length) { _dwRun.Advances = new float[length]; @@ -307,17 +307,17 @@ namespace Avalonia.Direct2D1 public Span GlyphPositions => MemoryMarshal.Cast(_dwRun.Offsets.AsSpan()); } - public IGlyphRunBuffer AllocateGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + public IGlyphRunBuffer AllocateGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) { return new DWGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length); } - public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) { return new DWHorizontalGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length); } - public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) { return new DWPositionedGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length); } diff --git a/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs index bda36e6c8c..064320f809 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs @@ -31,7 +31,7 @@ namespace Avalonia.Direct2D1.Media buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture); - var font = ((GlyphTypefaceImpl)typeface.PlatformImpl).Font; + var font = ((GlyphTypefaceImpl)typeface).Font; font.Shape(buffer); diff --git a/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs b/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs index cd0586233f..df16c1b34f 100644 --- a/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs @@ -185,7 +185,7 @@ namespace Avalonia.Base.UnitTests.Media var characters = new ReadOnlySlice(Enumerable.Repeat('a', count).ToArray(), start, count); - return new GlyphRun(new GlyphTypeface(new MockGlyphTypeface()), 10, characters, glyphIndices, glyphAdvances, + return new GlyphRun(new MockGlyphTypeface(), 10, characters, glyphIndices, glyphAdvances, glyphClusters: glyphClusters, biDiLevel: bidiLevel); } } diff --git a/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs b/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs index d21879f349..a7a55653b7 100644 --- a/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs +++ b/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs @@ -126,17 +126,17 @@ namespace Avalonia.Base.UnitTests.VisualTree throw new NotImplementedException(); } - public IGlyphRunBuffer AllocateGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + public IGlyphRunBuffer AllocateGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) { throw new NotImplementedException(); } - public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) { throw new NotImplementedException(); } - public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) { throw new NotImplementedException(); } diff --git a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs index a2c2936a4d..34f0dfef11 100644 --- a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs +++ b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs @@ -117,17 +117,17 @@ namespace Avalonia.Benchmarks return new MockStreamGeometryImpl(); } - public IGlyphRunBuffer AllocateGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + public IGlyphRunBuffer AllocateGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) { throw new NotImplementedException(); } - public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) { throw new NotImplementedException(); } - public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) { throw new NotImplementedException(); } diff --git a/tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs b/tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs index 459ae3a549..7b7488bd5a 100644 --- a/tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs +++ b/tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs @@ -30,7 +30,7 @@ namespace Avalonia.UnitTests buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture); - var font = ((HarfBuzzGlyphTypefaceImpl)typeface.PlatformImpl).Font; + var font = ((HarfBuzzGlyphTypefaceImpl)typeface).Font; font.Shape(buffer); diff --git a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs index 314cc95041..586436ef7f 100644 --- a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs +++ b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs @@ -152,17 +152,17 @@ namespace Avalonia.UnitTests return Mock.Of(); } - public IGlyphRunBuffer AllocateGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + public IGlyphRunBuffer AllocateGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) { return Mock.Of(); } - public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) { return Mock.Of(); } - public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) + public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) { return Mock.Of(); } From 889b80511a4fb8eaf4715264a72ed0e99898d37a Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 14 Oct 2022 15:01:10 +0200 Subject: [PATCH 4/5] Fix redundant IsExternalInit --- src/Avalonia.Base/IsExternalInit.cs | 14 ++++++++++++++ src/Avalonia.Base/Media/FontMetrics.cs | 5 ----- 2 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 src/Avalonia.Base/IsExternalInit.cs diff --git a/src/Avalonia.Base/IsExternalInit.cs b/src/Avalonia.Base/IsExternalInit.cs new file mode 100644 index 0000000000..c6ddf762ad --- /dev/null +++ b/src/Avalonia.Base/IsExternalInit.cs @@ -0,0 +1,14 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +namespace System.Runtime.CompilerServices +{ + /// + /// Reserved to be used by the compiler for tracking metadata. + /// This class should not be used by developers in source code. + /// + [ExcludeFromCodeCoverage, DebuggerNonUserCode] + internal static class IsExternalInit + { + } +} diff --git a/src/Avalonia.Base/Media/FontMetrics.cs b/src/Avalonia.Base/Media/FontMetrics.cs index 3924119a3e..1cd01675db 100644 --- a/src/Avalonia.Base/Media/FontMetrics.cs +++ b/src/Avalonia.Base/Media/FontMetrics.cs @@ -56,8 +56,3 @@ public int StrikethroughThickness { get; init; } } } - -namespace System.Runtime.CompilerServices -{ - public class IsExternalInit { } -} From 7f71df6b9e180c27f097b429bf0506d55b2ccb59 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 14 Oct 2022 16:25:18 +0200 Subject: [PATCH 5/5] Add file as link --- src/Avalonia.Base/Avalonia.Base.csproj | 5 ++++- src/Avalonia.Base/IsExternalInit.cs | 14 -------------- 2 files changed, 4 insertions(+), 15 deletions(-) delete mode 100644 src/Avalonia.Base/IsExternalInit.cs diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index 1cb29e4e37..402bc3a099 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -21,6 +21,9 @@ + + + @@ -38,7 +41,7 @@ - + diff --git a/src/Avalonia.Base/IsExternalInit.cs b/src/Avalonia.Base/IsExternalInit.cs deleted file mode 100644 index c6ddf762ad..0000000000 --- a/src/Avalonia.Base/IsExternalInit.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - -namespace System.Runtime.CompilerServices -{ - /// - /// Reserved to be used by the compiler for tracking metadata. - /// This class should not be used by developers in source code. - /// - [ExcludeFromCodeCoverage, DebuggerNonUserCode] - internal static class IsExternalInit - { - } -}