Browse Source

Optimize GlyphRun allocations

pull/9153/head
Benedikt Stebner 4 years ago
parent
commit
74fafe3b09
  1. 81
      src/Avalonia.Base/Media/GlyphRun.cs
  2. 5
      src/Avalonia.Base/Media/GlyphTypeface.cs
  3. 22
      src/Avalonia.Base/Platform/IGlyphRunBuffer.cs
  4. 5
      src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs
  5. 37
      src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
  6. 38
      src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
  7. 2
      src/Avalonia.Headless/HeadlessPlatformStubs.cs
  8. 4
      src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs
  9. 182
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
  10. 89
      src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
  11. 2
      src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs
  12. 15
      tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs
  13. 16
      tests/Avalonia.Benchmarks/NullRenderingPlatform.cs
  14. 4
      tests/Avalonia.UnitTests/HarfBuzzGlyphTypefaceImpl.cs
  15. 1
      tests/Avalonia.UnitTests/MockGlyphTypeface.cs
  16. 15
      tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs

81
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<IPlatformRenderInterface>();
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()

5
src/Avalonia.Base/Media/GlyphTypeface.cs

@ -67,6 +67,11 @@ namespace Avalonia.Media
/// </summary>
public bool IsFixedPitch => PlatformImpl.IsFixedPitch;
/// <summary>
/// Gets the number of glyphs held by this glyph typeface.
/// </summary>
public int GlyphCount => PlatformImpl.GlyphCount;
/// <summary>
/// Returns an glyph index for the specified codepoint.
/// </summary>

22
src/Avalonia.Base/Platform/IGlyphRunBuffer.cs

@ -0,0 +1,22 @@
using System;
using System.Drawing;
namespace Avalonia.Platform
{
public interface IGlyphRunBuffer
{
Span<ushort> GlyphIndices { get; }
IGlyphRunImpl Build();
}
public interface IHorizontalGlyphRunBuffer : IGlyphRunBuffer
{
Span<float> GlyphPositions { get; }
}
public interface IPositionedGlyphRunBuffer : IGlyphRunBuffer
{
Span<PointF> GlyphPositions { get; }
}
}

5
src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs

@ -51,6 +51,11 @@ namespace Avalonia.Platform
/// </summary>
bool IsFixedPitch { get; }
/// <summary>
/// Gets the number of glyphs held by this glyph typeface.
/// </summary>
int GlyphCount { get; }
/// <summary>
/// Returns an glyph index for the specified codepoint.
/// </summary>

37
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);
/// <summary>
/// Creates a platform implementation of a glyph run.
/// Allocates a platform glyph run buffer.
/// </summary>
/// <param name="glyphRun">The glyph run.</param>
/// <returns></returns>
IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun);
/// <param name="glyphTypeface">The glyph typeface.</param>
/// <param name="fontRenderingEmSize">The font rendering em size.</param>
/// <param name="length">The length.</param>
/// <returns>An <see cref="IGlyphRunBuffer"/>.</returns>
/// <remarks>
/// This buffer only holds glyph indices.
/// </remarks>
IGlyphRunBuffer AllocateGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length);
/// <summary>
/// Allocates a horizontal platform glyph run buffer.
/// </summary>
/// <param name="glyphTypeface">The glyph typeface.</param>
/// <param name="fontRenderingEmSize">The font rendering em size.</param>
/// <param name="length">The length.</param>
/// <returns>An <see cref="IGlyphRunBuffer"/>.</returns>
/// <remarks>
/// This buffer holds glyph indices and glyph advances.
/// </remarks>
IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length);
/// <summary>
/// Allocates a positioned platform glyph run buffer.
/// </summary>
/// <param name="glyphTypeface">The glyph typeface.</param>
/// <param name="fontRenderingEmSize">The font rendering em size.</param>
/// <param name="length">The length.</param>
/// <returns>An <see cref="IGlyphRunBuffer"/>.</returns>
/// <remarks>
/// This buffer holds glyph indices, glyph advances and glyph positions.
/// </remarks>
IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length);
/// <summary>
/// Gets a value indicating whether the platform directly supports rectangles with rounded corners.

38
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<ushort> GlyphIndices => Span<ushort>.Empty;
public IGlyphRunImpl Build()
{
return new HeadlessGlyphRunStub();
}
}
class HeadlessHorizontalGlyphRunBufferStub : HeadlessGlyphRunBufferStub, IHorizontalGlyphRunBuffer
{
public Span<float> GlyphPositions => Span<float>.Empty;
}
class HeadlessPositionedGlyphRunBufferStub : HeadlessGlyphRunBufferStub, IPositionedGlyphRunBuffer
{
public Span<System.Drawing.PointF> GlyphPositions => Span<System.Drawing.PointF>.Empty;
}
class HeadlessGlyphRunStub : IGlyphRunImpl
{
public void Dispose()

2
src/Avalonia.Headless/HeadlessPlatformStubs.cs

@ -95,6 +95,8 @@ namespace Avalonia.Headless
public bool IsFixedPitch => true;
public int GlyphCount => 1337;
public void Dispose()
{
}

4
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
/// <inheritdoc cref="IGlyphTypefaceImpl"/>
public bool IsFixedPitch { get; }
public int GlyphCount { get; }
public bool IsFakeBold { get; }

182
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<IPlatformOpenGlInterface>();
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<SKTextBlobBuilder> s_textBlobBuilderThreadLocal = new ThreadLocal<SKTextBlobBuilder>(() => new SKTextBlobBuilder());
/// <inheritdoc />
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<ushort> 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<ushort> 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<ushort> GlyphIndices => _buffer.GetGlyphSpan();
public Span<float> 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<ushort> GlyphIndices => _buffer.GetGlyphSpan();
public PixelFormat DefaultPixelFormat { get; }
public Span<PointF> GlyphPositions => MemoryMarshal.Cast<SKPoint, PointF>(_buffer.GetPositionSpan());
}
}
}

89
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<ushort> GlyphIndices => MemoryMarshal.Cast<short, ushort>(_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<float> 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<PointF> GlyphPositions => MemoryMarshal.Cast<GlyphOffset, PointF>(_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;

2
src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs

@ -117,6 +117,8 @@ namespace Avalonia.Direct2D1.Media
/// <inheritdoc cref="IGlyphTypefaceImpl"/>
public bool IsFixedPitch { get; }
public int GlyphCount { get; set; }
/// <inheritdoc cref="IGlyphTypefaceImpl"/>
public ushort GetGlyph(uint codepoint)
{

15
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();

16
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();
}

4
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
/// <inheritdoc cref="IGlyphTypefaceImpl"/>
public bool IsFixedPitch { get; }
public int GlyphCount { get; set; }
public bool IsFakeBold { get; }

1
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)
{

15
tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs

@ -152,6 +152,21 @@ namespace Avalonia.UnitTests
return Mock.Of<IGeometryImpl>();
}
public IGlyphRunBuffer AllocateGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
{
return Mock.Of<IGlyphRunBuffer>();
}
public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
{
return Mock.Of<IHorizontalGlyphRunBuffer>();
}
public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
{
return Mock.Of<IPositionedGlyphRunBuffer>();
}
public bool SupportsIndividualRoundRects { get; set; }
public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul;

Loading…
Cancel
Save