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/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/Media/FontManager.cs b/src/Avalonia.Base/Media/FontManager.cs
index 37091b82e3..d92d003c2a 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..1cd01675db
--- /dev/null
+++ b/src/Avalonia.Base/Media/FontMetrics.cs
@@ -0,0 +1,58 @@
+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; }
+ }
+}
diff --git a/src/Avalonia.Base/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs
index 2289f98228..a1cb00e209 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;
@@ -15,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;
@@ -42,7 +43,7 @@ namespace Avalonia.Media
/// The glyph clusters.
/// The bidi level.
public GlyphRun(
- GlyphTypeface glyphTypeface,
+ IGlyphTypeface glyphTypeface,
double fontRenderingEmSize,
ReadOnlySlice characters,
IReadOnlyList glyphIndices,
@@ -69,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 .
@@ -171,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.
@@ -612,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()
@@ -636,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);
@@ -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.Metrics.DesignEmHeight);
+
+ if (GlyphOffsets == null)
+ {
+ if (GlyphTypeface.Metrics.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
deleted file mode 100644
index 45ef04e77f..0000000000
--- a/src/Avalonia.Base/Media/GlyphTypeface.cs
+++ /dev/null
@@ -1,125 +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;
-
- ///
- /// 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 52%
rename from src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs
rename to src/Avalonia.Base/Media/IGlyphTypeface.cs
index 415f34fb29..de2a2309ee 100644
--- a/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs
+++ b/src/Avalonia.Base/Media/IGlyphTypeface.cs
@@ -1,55 +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.
+ /// Gets the number of glyphs held by this glyph typeface.
///
- short DesignEmHeight { get; }
+ int GlyphCount { get; }
///
- /// Gets the recommended distance above the baseline in design em size.
+ /// Gets the font metrics.
///
- 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.
- ///
- bool IsFixedPitch { get; }
+ ///
+ /// The font metrics.
+ ///
+ FontMetrics Metrics { get; }
///
/// Returns an glyph index for the specified codepoint.
@@ -63,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.
///
@@ -89,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/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/IPlatformRenderInterface.cs b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
index e39a4e23df..9d0d7974b4 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(IGlyphTypeface 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(IGlyphTypeface 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(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/HeadlessPlatformRenderInterface.cs b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
index cb23c6c336..fcd7f1e31f 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(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
{
- return new HeadlessGeometryStub(new Rect(glyphRun.Size));
+ return new HeadlessGlyphRunBufferStub();
+ }
+
+ public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ return new HeadlessHorizontalGlyphRunBufferStub();
+ }
+
+ public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface 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..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;
@@ -95,6 +100,8 @@ namespace Avalonia.Headless
public bool IsFixedPitch => true;
+ public int GlyphCount => 1337;
+
public void Dispose()
{
}
@@ -104,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;
@@ -118,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
@@ -134,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 dcb4eac7ca..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,32 @@ 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;
+ 0,
+ IsFixedPitch = Typeface.IsFixedPitch
+ };
- IsFixedPitch = Typeface.IsFixedPitch;
+ GlyphCount = Typeface.GlyphCount;
IsFakeBold = isFakeBold;
@@ -67,39 +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 FontMetrics Metrics { get; }
- ///
- public int UnderlinePosition { get; }
+ public int GlyphCount { get; }
- ///
- public int UnderlineThickness { get; }
-
- ///
- public int StrikethroughPosition { get; }
-
- ///
- public int StrikethroughThickness { get; }
-
- ///
- public bool IsFixedPitch { get; }
-
public bool IsFakeBold { get; }
-
+
public bool IsFakeItalic { get; }
- ///
+ ///
public ushort GetGlyph(uint codepoint)
{
if (Font.TryGetGlyph(codepoint, out var glyph))
@@ -110,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];
@@ -126,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];
@@ -180,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 91fe4fc085..a9696efbd4 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);
@@ -64,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.");
}
@@ -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(IGlyphTypeface 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(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ => new SKHorizontalGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length);
- SKTextBlob textBlob;
+ public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface 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(IGlyphTypeface 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;
- 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(IGlyphTypeface 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(IGlyphTypeface 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(IGlyphTypeface 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/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 81fa8c4bce..8103f89dad 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
{
@@ -160,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.");
}
@@ -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(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
{
- FontFace = glyphTypeface.FontFace,
- FontSize = (float)glyphRun.FontRenderingEmSize
- };
-
- var indices = new short[glyphCount];
+ var glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface;
- 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(IGlyphTypeface 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(IGlyphTypeface 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(IGlyphTypeface 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(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ return new DWHorizontalGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length);
+ }
- return new GlyphRunImpl(run);
+ public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ return new DWPositionedGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length);
}
public bool SupportsIndividualRoundRects => false;
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 4154b44702..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,35 +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; }
+ public FontMetrics Metrics { get; }
- //ToDo: Read font table for these values
- ///
- public int UnderlinePosition { get; }
+ public int GlyphCount { get; set; }
- ///
- public int UnderlineThickness { get; }
-
- ///
- public int StrikethroughPosition { get; }
-
- ///
- public int StrikethroughThickness { get; }
-
- ///
- public bool IsFixedPitch { get; }
-
- ///
+ ///
public ushort GetGlyph(uint codepoint)
{
if (Font.TryGetGlyph(codepoint, out var glyph))
@@ -128,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];
@@ -144,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];
@@ -187,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/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 26a1ab88c7..5f8eb45f71 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(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface 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..34f0dfef11 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(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
{
throw new NotImplementedException();
}
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 32e0434cd4..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,70 +21,49 @@ 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);
+ var metrics = Font.OpenTypeMetrics;
- Descent = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalDescender) / 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),
- LineGap = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalLineGap) / defaultFontRenderingEmSize * DesignEmHeight);
+ UnderlinePosition = (int)(metrics.GetXVariation(OpenTypeMetricsTag.UnderlineOffset) / defaultFontRenderingEmSize * scale),
- UnderlinePosition = (int)(metrics.GetXVariation(OpenTypeMetricsTag.UnderlineOffset) / defaultFontRenderingEmSize * DesignEmHeight);
+ UnderlineThickness = (int)(metrics.GetXVariation(OpenTypeMetricsTag.UnderlineSize) / defaultFontRenderingEmSize * scale),
- UnderlineThickness = (int)(metrics.GetXVariation(OpenTypeMetricsTag.UnderlineSize) / defaultFontRenderingEmSize * DesignEmHeight);
+ StrikethroughPosition = (int)(metrics.GetXVariation(OpenTypeMetricsTag.StrikeoutOffset) / defaultFontRenderingEmSize * scale),
- StrikethroughPosition = (int)(metrics.GetXVariation(OpenTypeMetricsTag.StrikeoutOffset) / defaultFontRenderingEmSize * DesignEmHeight);
+ StrikethroughThickness = (int)(metrics.GetXVariation(OpenTypeMetricsTag.StrikeoutSize) / defaultFontRenderingEmSize * scale),
- StrikethroughThickness = (int)(metrics.GetXVariation(OpenTypeMetricsTag.StrikeoutSize) / defaultFontRenderingEmSize * DesignEmHeight);
+ IsFixedPitch = GetGlyphAdvance(GetGlyph('a')) == GetGlyphAdvance(GetGlyph('b'))
+ };
- IsFixedPitch = GetGlyphAdvance(GetGlyph('a')) == GetGlyphAdvance(GetGlyph('b'));
+ GlyphCount = Face.GlyphCount;
IsFakeBold = isFakeBold;
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))
@@ -95,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];
@@ -111,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];
@@ -130,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/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/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 c9c59a6e32..a1c492a7f1 100644
--- a/tests/Avalonia.UnitTests/MockGlyphTypeface.cs
+++ b/tests/Avalonia.UnitTests/MockGlyphTypeface.cs
@@ -1,19 +1,19 @@
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)
{
@@ -30,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];
@@ -43,5 +50,11 @@ namespace Avalonia.UnitTests
}
public void Dispose() { }
+
+ public bool TryGetTable(uint tag, out byte[] table)
+ {
+ table = null;
+ return false;
+ }
}
}
diff --git a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs
index f6a127b573..586436ef7f 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(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ return Mock.Of();
+ }
+
+ public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ return Mock.Of();
+ }
+
+ public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ return Mock.Of();
+ }
+
public bool SupportsIndividualRoundRects { get; set; }
public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul;