Reuse text and geometry related headless mocks in Avalonia unit tests and benchmarks
This commit is contained in:
@@ -12,7 +12,16 @@
|
||||
<Import Project="..\..\..\build\TrimmingEnable.props" />
|
||||
<Import Project="..\..\..\build\NullableEnable.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="..\..\Shared\ModuleInitializer.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Label="InternalsVisibleTo">
|
||||
<InternalsVisibleTo Include="Avalonia.Headless.Vnc, PublicKey=$(AvaloniaPublicKey)" />
|
||||
<InternalsVisibleTo Include="Avalonia.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
|
||||
<InternalsVisibleTo Include="Avalonia.Base.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
|
||||
<InternalsVisibleTo Include="Avalonia.Benchmarks, PublicKey=$(AvaloniaPublicKey)" />
|
||||
<InternalsVisibleTo Include="Avalonia.Controls.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
|
||||
<InternalsVisibleTo Include="Avalonia.Skia.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -130,12 +130,29 @@ namespace Avalonia.Headless
|
||||
IReadOnlyList<GlyphInfo> glyphInfos,
|
||||
Point baselineOrigin)
|
||||
{
|
||||
return new HeadlessGlyphRunStub();
|
||||
return new HeadlessGlyphRunStub(glyphInfos);
|
||||
}
|
||||
|
||||
private class HeadlessGlyphRunStub : IGlyphRunImpl
|
||||
internal class HeadlessGlyphRunStub : IGlyphRunImpl
|
||||
{
|
||||
public Rect Bounds => new Rect(new Size(8, 12));
|
||||
public HeadlessGlyphRunStub(IReadOnlyList<GlyphInfo> glyphInfos)
|
||||
{
|
||||
var width = 0.0;
|
||||
|
||||
for (var i = 0; i < glyphInfos.Count; ++i)
|
||||
{
|
||||
width += glyphInfos[i].GlyphAdvance;
|
||||
}
|
||||
|
||||
Bounds = new Rect(new Size(width, 10));
|
||||
}
|
||||
|
||||
public HeadlessGlyphRunStub()
|
||||
{
|
||||
Bounds = new Rect(new Size(8, 10));
|
||||
}
|
||||
|
||||
public Rect Bounds { get; }
|
||||
|
||||
public Point BaselineOrigin => new Point(0, 8);
|
||||
|
||||
@@ -232,8 +249,11 @@ namespace Avalonia.Headless
|
||||
|
||||
private class HeadlessStreamingGeometryStub : HeadlessGeometryStub, IStreamGeometryImpl
|
||||
{
|
||||
private HeadlessStreamingGeometryContextStub _context;
|
||||
|
||||
public HeadlessStreamingGeometryStub() : base(default)
|
||||
{
|
||||
_context = new HeadlessStreamingGeometryContextStub(this);
|
||||
}
|
||||
|
||||
public IStreamGeometryImpl Clone()
|
||||
@@ -243,13 +263,18 @@ namespace Avalonia.Headless
|
||||
|
||||
public IStreamGeometryContextImpl Open()
|
||||
{
|
||||
return new HeadlessStreamingGeometryContextStub(this);
|
||||
return _context;
|
||||
}
|
||||
|
||||
public override bool FillContains(Point point)
|
||||
{
|
||||
return _context.FillContains(point);
|
||||
}
|
||||
|
||||
private class HeadlessStreamingGeometryContextStub : IStreamGeometryContextImpl
|
||||
{
|
||||
private readonly HeadlessStreamingGeometryStub _parent;
|
||||
private double _x1, _y1, _x2, _y2;
|
||||
private List<Point> points = new List<Point>();
|
||||
public HeadlessStreamingGeometryContextStub(HeadlessStreamingGeometryStub parent)
|
||||
{
|
||||
_parent = parent;
|
||||
@@ -257,19 +282,30 @@ namespace Avalonia.Headless
|
||||
|
||||
private void Track(Point pt)
|
||||
{
|
||||
if (_x1 > pt.X)
|
||||
_x1 = pt.X;
|
||||
if (_x2 < pt.X)
|
||||
_x2 = pt.X;
|
||||
if (_y1 > pt.Y)
|
||||
_y1 = pt.Y;
|
||||
if (_y2 < pt.Y)
|
||||
_y2 = pt.Y;
|
||||
points.Add(pt);
|
||||
}
|
||||
|
||||
public Rect CalculateBounds()
|
||||
{
|
||||
var left = double.MaxValue;
|
||||
var right = double.MinValue;
|
||||
var top = double.MaxValue;
|
||||
var bottom = double.MinValue;
|
||||
|
||||
foreach (var p in points)
|
||||
{
|
||||
left = Math.Min(p.X, left);
|
||||
right = Math.Max(p.X, right);
|
||||
top = Math.Min(p.Y, top);
|
||||
bottom = Math.Max(p.Y, bottom);
|
||||
}
|
||||
|
||||
return new Rect(new Point(left, top), new Point(right, bottom));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_parent.Bounds = new Rect(_x1, _y1, _x2 - _x1, _y2 - _y1);
|
||||
_parent.Bounds = CalculateBounds();
|
||||
}
|
||||
|
||||
public void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection)
|
||||
@@ -301,6 +337,35 @@ namespace Avalonia.Headless
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public bool FillContains(Point point)
|
||||
{
|
||||
// Use the algorithm from https://www.blackpawn.com/texts/pointinpoly/default.html
|
||||
// to determine if the point is in the geometry (since it will always be convex in this situation)
|
||||
for (int i = 0; i < points.Count; i++)
|
||||
{
|
||||
var a = points[i];
|
||||
var b = points[(i + 1) % points.Count];
|
||||
var c = points[(i + 2) % points.Count];
|
||||
|
||||
Vector v0 = c - a;
|
||||
Vector v1 = b - a;
|
||||
Vector v2 = point - a;
|
||||
|
||||
var dot00 = v0 * v0;
|
||||
var dot01 = v0 * v1;
|
||||
var dot02 = v0 * v2;
|
||||
var dot11 = v1 * v1;
|
||||
var dot12 = v1 * v2;
|
||||
|
||||
|
||||
var invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
|
||||
var u = (dot11 * dot02 - dot01 * dot12) * invDenom;
|
||||
var v = (dot00 * dot12 - dot01 * dot02) * invDenom;
|
||||
if ((u >= 0) && (v >= 0) && (u + v < 1)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,7 +431,7 @@ namespace Avalonia.Headless
|
||||
}
|
||||
}
|
||||
|
||||
private class HeadlessDrawingContextStub : IDrawingContextImpl
|
||||
internal class HeadlessDrawingContextStub : IDrawingContextImpl
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Platform;
|
||||
@@ -11,6 +13,7 @@ using Avalonia.Input.Platform;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.Fonts;
|
||||
using Avalonia.Media.TextFormatting;
|
||||
using Avalonia.Media.TextFormatting.Unicode;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Platform.Storage.FileIO;
|
||||
@@ -82,22 +85,22 @@ namespace Avalonia.Headless
|
||||
{
|
||||
public FontMetrics Metrics => new FontMetrics
|
||||
{
|
||||
DesignEmHeight = 1,
|
||||
Ascent = 8,
|
||||
Descent = 4,
|
||||
DesignEmHeight = 10,
|
||||
Ascent = 2,
|
||||
Descent = 10,
|
||||
IsFixedPitch = true,
|
||||
LineGap = 0,
|
||||
UnderlinePosition = 2,
|
||||
UnderlineThickness = 1,
|
||||
StrikethroughPosition = 2,
|
||||
StrikethroughThickness = 1,
|
||||
IsFixedPitch = true
|
||||
StrikethroughThickness = 1
|
||||
};
|
||||
|
||||
public int GlyphCount => 1337;
|
||||
|
||||
public FontSimulations FontSimulations { get; }
|
||||
public FontSimulations FontSimulations => FontSimulations.None;
|
||||
|
||||
public string FamilyName => "Arial";
|
||||
public string FamilyName => "$Default";
|
||||
|
||||
public FontWeight Weight => FontWeight.Normal;
|
||||
|
||||
@@ -111,24 +114,31 @@ namespace Avalonia.Headless
|
||||
|
||||
public ushort GetGlyph(uint codepoint)
|
||||
{
|
||||
return 1;
|
||||
return (ushort)codepoint;
|
||||
}
|
||||
|
||||
public bool TryGetGlyph(uint codepoint, out ushort glyph)
|
||||
{
|
||||
glyph = 1;
|
||||
glyph = 8;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int GetGlyphAdvance(ushort glyph)
|
||||
{
|
||||
return 12;
|
||||
return 8;
|
||||
}
|
||||
|
||||
public int[] GetGlyphAdvances(ReadOnlySpan<ushort> glyphs)
|
||||
{
|
||||
return glyphs.ToArray().Select(x => (int)x).ToArray();
|
||||
var advances = new int[glyphs.Length];
|
||||
|
||||
for (var i = 0; i < advances.Length; i++)
|
||||
{
|
||||
advances[i] = 8;
|
||||
}
|
||||
|
||||
return advances;
|
||||
}
|
||||
|
||||
public ushort[] GetGlyphs(ReadOnlySpan<uint> codepoints)
|
||||
@@ -146,8 +156,8 @@ namespace Avalonia.Headless
|
||||
{
|
||||
metrics = new GlyphMetrics
|
||||
{
|
||||
Height = 10,
|
||||
Width = 8
|
||||
Width = 10,
|
||||
Height = 10
|
||||
};
|
||||
|
||||
return true;
|
||||
@@ -161,42 +171,83 @@ namespace Avalonia.Headless
|
||||
var typeface = options.Typeface;
|
||||
var fontRenderingEmSize = options.FontRenderingEmSize;
|
||||
var bidiLevel = options.BidiLevel;
|
||||
var shapedBuffer = new ShapedBuffer(text, text.Length, typeface, fontRenderingEmSize, bidiLevel);
|
||||
var textSpan = text.Span;
|
||||
var textStartIndex = TextTestHelper.GetStartCharIndex(text);
|
||||
|
||||
return new ShapedBuffer(text, text.Length, typeface, fontRenderingEmSize, bidiLevel);
|
||||
for (var i = 0; i < shapedBuffer.Length;)
|
||||
{
|
||||
var glyphCluster = i + textStartIndex;
|
||||
|
||||
var codepoint = Codepoint.ReadAt(textSpan, i, out var count);
|
||||
|
||||
var glyphIndex = typeface.GetGlyph(codepoint);
|
||||
|
||||
for (var j = 0; j < count; ++j)
|
||||
{
|
||||
shapedBuffer[i + j] = new GlyphInfo(glyphIndex, glyphCluster, 10);
|
||||
}
|
||||
|
||||
i += count;
|
||||
}
|
||||
|
||||
return shapedBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
internal class HeadlessFontManagerStub : IFontManagerImpl
|
||||
{
|
||||
private readonly string _defaultFamilyName;
|
||||
|
||||
public HeadlessFontManagerStub(string defaultFamilyName = "Default")
|
||||
{
|
||||
_defaultFamilyName = defaultFamilyName;
|
||||
}
|
||||
|
||||
public int TryCreateGlyphTypefaceCount { get; private set; }
|
||||
|
||||
public string GetDefaultFontFamilyName()
|
||||
{
|
||||
return "Arial";
|
||||
return _defaultFamilyName;
|
||||
}
|
||||
|
||||
public string[] GetInstalledFontFamilyNames(bool checkForUpdates = false)
|
||||
string[] IFontManagerImpl.GetInstalledFontFamilyNames(bool checkForUpdates)
|
||||
{
|
||||
return new string[] { "Arial" };
|
||||
return new[] { _defaultFamilyName };
|
||||
}
|
||||
|
||||
public bool TryCreateGlyphTypeface(string familyName, FontStyle style, FontWeight weight, FontStretch stretch, out IGlyphTypeface glyphTypeface)
|
||||
public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight,
|
||||
FontStretch fontStretch,
|
||||
CultureInfo? culture, out Typeface fontKey)
|
||||
{
|
||||
glyphTypeface= new HeadlessGlyphTypefaceImpl();
|
||||
fontKey = new Typeface(_defaultFamilyName);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool TryCreateGlyphTypeface(string familyName, FontStyle style, FontWeight weight,
|
||||
FontStretch stretch, [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface)
|
||||
{
|
||||
glyphTypeface = null;
|
||||
|
||||
TryCreateGlyphTypefaceCount++;
|
||||
|
||||
if (familyName == "Unknown")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
glyphTypeface = new HeadlessGlyphTypefaceImpl();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryCreateGlyphTypeface(Stream stream, out IGlyphTypeface glyphTypeface)
|
||||
public virtual bool TryCreateGlyphTypeface(Stream stream, out IGlyphTypeface glyphTypeface)
|
||||
{
|
||||
glyphTypeface = new HeadlessGlyphTypefaceImpl();
|
||||
glyphTypeface = new HeadlessGlyphTypefaceImpl();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, CultureInfo? culture, out Typeface typeface)
|
||||
{
|
||||
typeface = new Typeface("Arial", fontStyle, fontWeight, fontStretch);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal class HeadlessIconLoaderStub : IPlatformIconLoader
|
||||
@@ -249,4 +300,14 @@ namespace Avalonia.Headless
|
||||
return ScreenHelper.ScreenFromWindow(window, AllScreens);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class TextTestHelper
|
||||
{
|
||||
public static int GetStartCharIndex(ReadOnlyMemory<char> text)
|
||||
{
|
||||
if (!MemoryMarshal.TryGetString(text, out _, out var start, out _))
|
||||
throw new InvalidOperationException("text memory should have been a string");
|
||||
return start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Avalonia.Headless;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.UnitTests;
|
||||
using Xunit;
|
||||
@@ -27,7 +28,7 @@ namespace Avalonia.Base.UnitTests.Media
|
||||
[Fact]
|
||||
public void Should_Throw_When_Default_FamilyName_Is_Null()
|
||||
{
|
||||
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface.With(fontManagerImpl: new MockFontManagerImpl(null))))
|
||||
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface.With(fontManagerImpl: new HeadlessFontManagerStub(null!))))
|
||||
{
|
||||
Assert.Throws<InvalidOperationException>(() => FontManager.Current);
|
||||
}
|
||||
@@ -39,7 +40,7 @@ namespace Avalonia.Base.UnitTests.Media
|
||||
var options = new FontManagerOptions { DefaultFamilyName = "MyFont" };
|
||||
|
||||
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface
|
||||
.With(fontManagerImpl: new MockFontManagerImpl())))
|
||||
.With(fontManagerImpl: new HeadlessFontManagerStub())))
|
||||
{
|
||||
AvaloniaLocator.CurrentMutable.Bind<FontManagerOptions>().ToConstant(options);
|
||||
|
||||
@@ -62,7 +63,7 @@ namespace Avalonia.Base.UnitTests.Media
|
||||
};
|
||||
|
||||
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface
|
||||
.With(fontManagerImpl: new MockFontManagerImpl())))
|
||||
.With(fontManagerImpl: new HeadlessFontManagerStub())))
|
||||
{
|
||||
AvaloniaLocator.CurrentMutable.Bind<FontManagerOptions>().ToConstant(options);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Avalonia.Headless;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.TextFormatting;
|
||||
using Avalonia.UnitTests;
|
||||
@@ -179,13 +180,13 @@ namespace Avalonia.Base.UnitTests.Media
|
||||
glyphInfos[i] = new GlyphInfo(0, glyphClusters[i], glyphAdvances[i]);
|
||||
}
|
||||
|
||||
return new GlyphRun(new MockGlyphTypeface(), 10, new string('a', count).AsMemory(), glyphInfos, biDiLevel: bidiLevel);
|
||||
return new GlyphRun(new HeadlessGlyphTypefaceImpl(), 10, new string('a', count).AsMemory(), glyphInfos, biDiLevel: bidiLevel);
|
||||
}
|
||||
|
||||
private static IDisposable Start()
|
||||
{
|
||||
return UnitTestApplication.Start(TestServices.StyledWindow.With(
|
||||
renderInterface: new MockPlatformRenderInterface()));
|
||||
renderInterface: new HeadlessPlatformRenderInterface()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Avalonia.Base.UnitTests.VisualTree;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Presenters;
|
||||
using Avalonia.Controls.Shapes;
|
||||
|
||||
@@ -1,285 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.UnitTests;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Media.TextFormatting;
|
||||
|
||||
namespace Avalonia.Base.UnitTests.VisualTree
|
||||
{
|
||||
class MockRenderInterface : IPlatformRenderInterface, IPlatformRenderInterfaceContext
|
||||
{
|
||||
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsLost => false;
|
||||
|
||||
public object TryGetFeature(Type featureType) => null;
|
||||
|
||||
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(PixelSize size, Vector dpi)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IStreamGeometryImpl CreateStreamGeometry()
|
||||
{
|
||||
return new MockStreamGeometry();
|
||||
}
|
||||
|
||||
public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList<IGeometryImpl> children)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, IGeometryImpl g1, IGeometryImpl g2)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IBitmapImpl LoadBitmap(Stream stream)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IWriteableBitmapImpl LoadWriteableBitmapToWidth(Stream stream, int width,
|
||||
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IWriteableBitmapImpl LoadWriteableBitmapToHeight(Stream stream, int height,
|
||||
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IWriteableBitmapImpl LoadWriteableBitmap(string fileName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IWriteableBitmapImpl LoadWriteableBitmap(Stream stream)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IBitmapImpl LoadBitmap(string fileName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize,
|
||||
IReadOnlyList<GlyphInfo> glyphInfos, Point baselineOrigin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IPlatformRenderInterfaceContext CreateBackendContext(IPlatformGraphicsContext graphicsContext)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public bool SupportsIndividualRoundRects { get; set; }
|
||||
public AlphaFormat DefaultAlphaFormat { get; }
|
||||
public PixelFormat DefaultPixelFormat { get; }
|
||||
public bool IsSupportedBitmapPixelFormat(PixelFormat format) => true;
|
||||
|
||||
public IFontManagerImpl CreateFontManager()
|
||||
{
|
||||
return new MockFontManagerImpl();
|
||||
}
|
||||
|
||||
public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat fmt, AlphaFormat alphaFormat)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IGeometryImpl CreateEllipseGeometry(Rect rect)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IGeometryImpl CreateLineGeometry(Point p1, Point p2)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IGeometryImpl CreateRectangleGeometry(Rect rect)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IBitmapImpl LoadBitmapToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IBitmapImpl LoadBitmapToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IBitmapImpl ResizeBitmap(IBitmapImpl bitmapImpl, PixelSize destinationSize, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
class MockStreamGeometry : IStreamGeometryImpl
|
||||
{
|
||||
private MockStreamGeometryContext _impl = new MockStreamGeometryContext();
|
||||
public Rect Bounds
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public double ContourLength { get; }
|
||||
|
||||
public IStreamGeometryImpl Clone()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public bool FillContains(Point point)
|
||||
{
|
||||
return _impl.FillContains(point);
|
||||
}
|
||||
|
||||
public Rect GetRenderBounds(IPen pen)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IGeometryImpl Intersect(IGeometryImpl geometry)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IStreamGeometryContextImpl Open()
|
||||
{
|
||||
return _impl;
|
||||
}
|
||||
|
||||
public bool StrokeContains(IPen pen, Point point)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ITransformedGeometryImpl WithTransform(Matrix transform)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool TryGetPointAtDistance(double distance, out Point point)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool TryGetPointAndTangentAtDistance(double distance, out Point point, out Point tangent)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool TryGetSegment(double startDistance, double stopDistance, bool startOnBeginFigure, out IGeometryImpl segmentGeometry)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
class MockStreamGeometryContext : IStreamGeometryContextImpl
|
||||
{
|
||||
private List<Point> points = new List<Point>();
|
||||
public void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void BeginFigure(Point startPoint, bool isFilled)
|
||||
{
|
||||
points.Add(startPoint);
|
||||
}
|
||||
|
||||
public void CubicBezierTo(Point point1, Point point2, Point point3)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public void EndFigure(bool isClosed)
|
||||
{
|
||||
}
|
||||
|
||||
public void LineTo(Point point)
|
||||
{
|
||||
points.Add(point);
|
||||
}
|
||||
|
||||
public void QuadraticBezierTo(Point control, Point endPoint)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SetFillRule(FillRule fillRule)
|
||||
{
|
||||
}
|
||||
|
||||
public bool FillContains(Point point)
|
||||
{
|
||||
// Use the algorithm from https://www.blackpawn.com/texts/pointinpoly/default.html
|
||||
// to determine if the point is in the geometry (since it will always be convex in this situation)
|
||||
for (int i = 0; i < points.Count; i++)
|
||||
{
|
||||
var a = points[i];
|
||||
var b = points[(i + 1) % points.Count];
|
||||
var c = points[(i + 2) % points.Count];
|
||||
|
||||
Vector v0 = c - a;
|
||||
Vector v1 = b - a;
|
||||
Vector v2 = point - a;
|
||||
|
||||
var dot00 = v0 * v0;
|
||||
var dot01 = v0 * v1;
|
||||
var dot02 = v0 * v2;
|
||||
var dot11 = v1 * v1;
|
||||
var dot12 = v1 * v2;
|
||||
|
||||
|
||||
var invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
|
||||
var u = (dot11 * dot02 - dot01 * dot12) * invDenom;
|
||||
var v = (dot00 * dot12 - dot01 * dot02) * invDenom;
|
||||
if ((u >= 0) && (v >= 0) && (u + v < 1)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Headless;
|
||||
using Avalonia.Threading;
|
||||
using Avalonia.UnitTests;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
@@ -15,11 +16,7 @@ namespace Avalonia.Benchmarks.Layout
|
||||
|
||||
public ControlsBenchmark()
|
||||
{
|
||||
_app = UnitTestApplication.Start(
|
||||
TestServices.StyledWindow.With(
|
||||
renderInterface: new NullRenderingPlatform(),
|
||||
dispatcherImpl: new NullThreadingPlatform(),
|
||||
standardCursorFactory: new NullCursorFactory()));
|
||||
_app = UnitTestApplication.Start(TestServices.StyledWindow);
|
||||
|
||||
_root = new TestRoot(true, null)
|
||||
{
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
using System;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Platform;
|
||||
|
||||
namespace Avalonia.Benchmarks
|
||||
{
|
||||
internal class NullCursorFactory : ICursorFactory
|
||||
{
|
||||
public ICursorImpl CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot) => new NullCursorImpl();
|
||||
ICursorImpl ICursorFactory.GetCursor(StandardCursorType cursorType) => new NullCursorImpl();
|
||||
|
||||
private class NullCursorImpl : ICursorImpl
|
||||
{
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
using System;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Rendering.SceneGraph;
|
||||
using Avalonia.Utilities;
|
||||
using Avalonia.Media.Imaging;
|
||||
|
||||
namespace Avalonia.Benchmarks
|
||||
{
|
||||
internal class NullDrawingContextImpl : IDrawingContextImpl
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public Matrix Transform { get; set; }
|
||||
|
||||
public void Clear(Color color)
|
||||
{
|
||||
}
|
||||
|
||||
public void DrawBitmap(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect,
|
||||
BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default)
|
||||
{
|
||||
}
|
||||
|
||||
public void DrawBitmap(IRef<IBitmapImpl> source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect)
|
||||
{
|
||||
}
|
||||
|
||||
public void DrawLine(IPen pen, Point p1, Point p2)
|
||||
{
|
||||
}
|
||||
|
||||
public void DrawGeometry(IBrush brush, IPen pen, IGeometryImpl geometry)
|
||||
{
|
||||
}
|
||||
|
||||
public void DrawRectangle(IBrush brush, IPen pen, RoundedRect rect, BoxShadows boxShadows = default)
|
||||
{
|
||||
}
|
||||
|
||||
public void DrawEllipse(IBrush brush, IPen pen, Rect rect)
|
||||
{
|
||||
}
|
||||
|
||||
public void DrawGlyphRun(IBrush foreground, IRef<IGlyphRunImpl> glyphRun)
|
||||
{
|
||||
}
|
||||
|
||||
public IDrawingContextLayerImpl CreateLayer(Size size)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public void PushClip(Rect clip)
|
||||
{
|
||||
}
|
||||
|
||||
public void PushClip(RoundedRect clip)
|
||||
{
|
||||
}
|
||||
|
||||
public void PopClip()
|
||||
{
|
||||
}
|
||||
|
||||
public void PushOpacity(double opacity, Rect bounds)
|
||||
{
|
||||
}
|
||||
|
||||
public void PopOpacity()
|
||||
{
|
||||
}
|
||||
|
||||
public void PushOpacityMask(IBrush mask, Rect bounds)
|
||||
{
|
||||
}
|
||||
|
||||
public void PopOpacityMask()
|
||||
{
|
||||
}
|
||||
|
||||
public void PushGeometryClip(IGeometryImpl clip)
|
||||
{
|
||||
}
|
||||
|
||||
public void PopGeometryClip()
|
||||
{
|
||||
}
|
||||
|
||||
public void PushBitmapBlendMode(BitmapBlendingMode blendingMode)
|
||||
{
|
||||
}
|
||||
|
||||
public void PopBitmapBlendMode()
|
||||
{
|
||||
}
|
||||
|
||||
public void Custom(ICustomDrawOperation custom)
|
||||
{
|
||||
}
|
||||
|
||||
public object GetFeature(Type t) => null;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Avalonia.Platform;
|
||||
|
||||
namespace Avalonia.Benchmarks
|
||||
{
|
||||
internal class NullGlyphRun : IGlyphRunImpl
|
||||
{
|
||||
public Rect Bounds => default;
|
||||
|
||||
public Point BaselineOrigin => default;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public IReadOnlyList<float> GetIntersections(float lowerBound, float upperBound)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.UnitTests;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Media.TextFormatting;
|
||||
using Microsoft.Diagnostics.Runtime;
|
||||
|
||||
namespace Avalonia.Benchmarks
|
||||
{
|
||||
internal class NullRenderingPlatform : IPlatformRenderInterface, IPlatformRenderInterfaceContext
|
||||
{
|
||||
public IGeometryImpl CreateEllipseGeometry(Rect rect)
|
||||
{
|
||||
return new MockStreamGeometryImpl();
|
||||
}
|
||||
|
||||
public IGeometryImpl CreateLineGeometry(Point p1, Point p2)
|
||||
{
|
||||
return new MockStreamGeometryImpl();
|
||||
}
|
||||
|
||||
public IGeometryImpl CreateRectangleGeometry(Rect rect)
|
||||
{
|
||||
return new MockStreamGeometryImpl();
|
||||
}
|
||||
|
||||
public IStreamGeometryImpl CreateStreamGeometry()
|
||||
{
|
||||
return new MockStreamGeometryImpl();
|
||||
}
|
||||
|
||||
public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList<IGeometryImpl> children)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, IGeometryImpl g1, IGeometryImpl g2)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsLost => false;
|
||||
|
||||
public object TryGetFeature(Type featureType) => null;
|
||||
|
||||
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(PixelSize size, Vector dpi)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IBitmapImpl LoadBitmap(string fileName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IBitmapImpl LoadBitmap(Stream stream)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IWriteableBitmapImpl LoadWriteableBitmapToWidth(Stream stream, int width,
|
||||
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IWriteableBitmapImpl LoadWriteableBitmapToHeight(Stream stream, int height,
|
||||
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IWriteableBitmapImpl LoadWriteableBitmap(string fileName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IWriteableBitmapImpl LoadWriteableBitmap(Stream stream)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IBitmapImpl LoadBitmapToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IBitmapImpl LoadBitmapToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IBitmapImpl ResizeBitmap(IBitmapImpl bitmapImpl, PixelSize destinationSize, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IFontManagerImpl CreateFontManager()
|
||||
{
|
||||
return new MockFontManagerImpl();
|
||||
}
|
||||
|
||||
public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun)
|
||||
{
|
||||
return new MockStreamGeometryImpl();
|
||||
}
|
||||
|
||||
public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize,
|
||||
IReadOnlyList<GlyphInfo> glyphInfos, Point baselineOrigin)
|
||||
{
|
||||
return new MockGlyphRun(glyphInfos);
|
||||
}
|
||||
|
||||
public IPlatformRenderInterfaceContext CreateBackendContext(IPlatformGraphicsContext graphicsContext)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public bool SupportsIndividualRoundRects => true;
|
||||
|
||||
public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul;
|
||||
|
||||
public PixelFormat DefaultPixelFormat => PixelFormat.Rgba8888;
|
||||
public bool IsSupportedBitmapPixelFormat(PixelFormat format) => true;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
using System;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Threading;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Threading;
|
||||
|
||||
namespace Avalonia.Benchmarks
|
||||
{
|
||||
internal class NullThreadingPlatform : IDispatcherImpl
|
||||
{
|
||||
public void Signal()
|
||||
{
|
||||
}
|
||||
|
||||
public void UpdateTimer(long? dueTimeInMs)
|
||||
{
|
||||
}
|
||||
|
||||
public bool CurrentThreadIsLoopThread => true;
|
||||
|
||||
#pragma warning disable CS0067
|
||||
public event Action Signaled;
|
||||
public event Action Timer;
|
||||
public long Now => 0;
|
||||
#pragma warning restore CS0067
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Avalonia.Controls.Shapes;
|
||||
using Avalonia.Headless;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Platform;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
@@ -21,9 +22,9 @@ namespace Avalonia.Benchmarks.Rendering
|
||||
_lineFill = new Line { Fill = new SolidColorBrush() };
|
||||
_lineFillAndStroke = new Line { Stroke = new SolidColorBrush(), Fill = new SolidColorBrush() };
|
||||
|
||||
_drawingContext = new PlatformDrawingContext(new NullDrawingContextImpl(), true);
|
||||
_drawingContext = new PlatformDrawingContext(new HeadlessPlatformRenderInterface.HeadlessDrawingContextStub(), true);
|
||||
|
||||
AvaloniaLocator.CurrentMutable.Bind<IPlatformRenderInterface>().ToConstant(new NullRenderingPlatform());
|
||||
AvaloniaLocator.CurrentMutable.Bind<IPlatformRenderInterface>().ToConstant(new HeadlessPlatformRenderInterface());
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
|
||||
@@ -20,10 +20,7 @@ namespace Avalonia.Benchmarks.Styling
|
||||
|
||||
public ControlTheme_Change()
|
||||
{
|
||||
_app = UnitTestApplication.Start(
|
||||
TestServices.StyledWindow.With(
|
||||
renderInterface: new NullRenderingPlatform(),
|
||||
dispatcherImpl: new NullThreadingPlatform()));
|
||||
_app = UnitTestApplication.Start(TestServices.StyledWindow);
|
||||
|
||||
// Simulate an application with a lot of styles by creating a tree of nested panels,
|
||||
// each with a bunch of styles applied.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Headless;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Styling;
|
||||
using Avalonia.UnitTests;
|
||||
@@ -20,12 +21,8 @@ namespace Avalonia.Benchmarks.Styling
|
||||
assetLoader: new AssetLoader(),
|
||||
globalClock: new MockGlobalClock(),
|
||||
platform: new AppBuilder().RuntimePlatform,
|
||||
renderInterface: new MockPlatformRenderInterface(),
|
||||
standardCursorFactory: Mock.Of<ICursorFactory>(),
|
||||
theme: () => CreateTheme(),
|
||||
dispatcherImpl: new NullThreadingPlatform(),
|
||||
fontManagerImpl: new MockFontManagerImpl(),
|
||||
textShaperImpl: new MockTextShaperImpl(),
|
||||
windowingPlatform: new MockWindowingPlatform());
|
||||
|
||||
return UnitTestApplication.Start(services);
|
||||
|
||||
@@ -17,10 +17,7 @@ namespace Avalonia.Benchmarks.Styling
|
||||
|
||||
public Style_Apply_Detach_Complex()
|
||||
{
|
||||
_app = UnitTestApplication.Start(
|
||||
TestServices.StyledWindow.With(
|
||||
renderInterface: new NullRenderingPlatform(),
|
||||
dispatcherImpl: new NullThreadingPlatform()));
|
||||
_app = UnitTestApplication.Start(TestServices.StyledWindow);
|
||||
|
||||
// Simulate an application with a lot of styles by creating a tree of nested panels,
|
||||
// each with a bunch of styles applied.
|
||||
|
||||
@@ -30,10 +30,7 @@ public class HugeTextLayout : IDisposable
|
||||
{
|
||||
_manySmallStrings = Enumerable.Range(0, 1000).Select(_ => RandomString(s_rand.Next(2, 15))).ToArray();
|
||||
|
||||
var testServices = TestServices.StyledWindow.With(
|
||||
renderInterface: new NullRenderingPlatform(),
|
||||
dispatcherImpl: new NullThreadingPlatform(),
|
||||
standardCursorFactory: new NullCursorFactory());
|
||||
var testServices = TestServices.StyledWindow;
|
||||
|
||||
if (s_useSkia)
|
||||
{
|
||||
|
||||
@@ -45,9 +45,6 @@ namespace Avalonia.Benchmarks.Themes
|
||||
private static IDisposable CreateApp()
|
||||
{
|
||||
var services = new TestServices(
|
||||
renderInterface: new NullRenderingPlatform(),
|
||||
dispatcherImpl: new NullThreadingPlatform(),
|
||||
standardCursorFactory: new NullCursorFactory(),
|
||||
theme: () => LoadFluentTheme());
|
||||
|
||||
return UnitTestApplication.Start(services);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Linq;
|
||||
using Avalonia.Controls.Shapes;
|
||||
using Avalonia.Controls.Templates;
|
||||
using Avalonia.Headless;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.UnitTests;
|
||||
using Avalonia.VisualTree;
|
||||
@@ -203,10 +204,10 @@ namespace Avalonia.Controls.UnitTests
|
||||
}
|
||||
|
||||
private static TestServices Services => TestServices.MockThreadingInterface.With(
|
||||
fontManagerImpl: new MockFontManagerImpl(),
|
||||
fontManagerImpl: new HeadlessFontManagerStub(),
|
||||
standardCursorFactory: Mock.Of<ICursorFactory>(),
|
||||
textShaperImpl: new MockTextShaperImpl(),
|
||||
renderInterface: new MockPlatformRenderInterface());
|
||||
textShaperImpl: new HeadlessTextShaperStub(),
|
||||
renderInterface: new HeadlessPlatformRenderInterface());
|
||||
|
||||
private static IControlTemplate CreateTemplate()
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@ using Avalonia.Controls.Presenters;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Controls.Templates;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Headless;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.LogicalTree;
|
||||
@@ -1022,12 +1023,12 @@ namespace Avalonia.Controls.UnitTests
|
||||
return UnitTestApplication.Start(
|
||||
TestServices.MockThreadingInterface.With(
|
||||
focusManager: new FocusManager(),
|
||||
fontManagerImpl: new MockFontManagerImpl(),
|
||||
fontManagerImpl: new HeadlessFontManagerStub(),
|
||||
keyboardDevice: () => new KeyboardDevice(),
|
||||
keyboardNavigation: new KeyboardNavigationHandler(),
|
||||
inputManager: new InputManager(),
|
||||
renderInterface: new MockPlatformRenderInterface(),
|
||||
textShaperImpl: new MockTextShaperImpl()));
|
||||
renderInterface: new HeadlessPlatformRenderInterface(),
|
||||
textShaperImpl: new HeadlessTextShaperStub()));
|
||||
}
|
||||
|
||||
private class ItemsControlWithContainer : ItemsControl, IStyleable
|
||||
|
||||
@@ -7,6 +7,7 @@ using Avalonia.Controls.Presenters;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Controls.Templates;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Headless;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Input.Platform;
|
||||
using Avalonia.Layout;
|
||||
@@ -892,16 +893,16 @@ namespace Avalonia.Controls.UnitTests
|
||||
keyboardDevice: () => new KeyboardDevice(),
|
||||
keyboardNavigation: new KeyboardNavigationHandler(),
|
||||
inputManager: new InputManager(),
|
||||
renderInterface: new MockPlatformRenderInterface(),
|
||||
fontManagerImpl: new MockFontManagerImpl(),
|
||||
textShaperImpl: new MockTextShaperImpl(),
|
||||
renderInterface: new HeadlessPlatformRenderInterface(),
|
||||
fontManagerImpl: new HeadlessFontManagerStub(),
|
||||
textShaperImpl: new HeadlessTextShaperStub(),
|
||||
standardCursorFactory: Mock.Of<ICursorFactory>());
|
||||
|
||||
private static TestServices Services => TestServices.MockThreadingInterface.With(
|
||||
renderInterface: new MockPlatformRenderInterface(),
|
||||
renderInterface: new HeadlessPlatformRenderInterface(),
|
||||
standardCursorFactory: Mock.Of<ICursorFactory>(),
|
||||
textShaperImpl: new MockTextShaperImpl(),
|
||||
fontManagerImpl: new MockFontManagerImpl());
|
||||
textShaperImpl: new HeadlessTextShaperStub(),
|
||||
fontManagerImpl: new HeadlessFontManagerStub());
|
||||
|
||||
private static IControlTemplate CreateTemplate()
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@ using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Controls.Selection;
|
||||
using Avalonia.Controls.Templates;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Headless;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Styling;
|
||||
@@ -1339,12 +1340,12 @@ namespace Avalonia.Controls.UnitTests.Primitives
|
||||
return UnitTestApplication.Start(
|
||||
TestServices.MockThreadingInterface.With(
|
||||
focusManager: new FocusManager(),
|
||||
fontManagerImpl: new MockFontManagerImpl(),
|
||||
fontManagerImpl: new HeadlessFontManagerStub(),
|
||||
keyboardDevice: () => new KeyboardDevice(),
|
||||
keyboardNavigation: new KeyboardNavigationHandler(),
|
||||
inputManager: new InputManager(),
|
||||
renderInterface: new MockPlatformRenderInterface(),
|
||||
textShaperImpl: new MockTextShaperImpl()));
|
||||
renderInterface: new HeadlessPlatformRenderInterface(),
|
||||
textShaperImpl: new HeadlessTextShaperStub()));
|
||||
}
|
||||
|
||||
private class TestSelector : SelectingItemsControl
|
||||
|
||||
@@ -6,6 +6,7 @@ using Avalonia.Controls.Presenters;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Controls.Templates;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Headless;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Input.Platform;
|
||||
using Avalonia.Layout;
|
||||
@@ -1104,14 +1105,14 @@ namespace Avalonia.Controls.UnitTests
|
||||
keyboardNavigation: new KeyboardNavigationHandler(),
|
||||
inputManager: new InputManager(),
|
||||
standardCursorFactory: Mock.Of<ICursorFactory>(),
|
||||
textShaperImpl: new MockTextShaperImpl(),
|
||||
fontManagerImpl: new MockFontManagerImpl());
|
||||
textShaperImpl: new HeadlessTextShaperStub(),
|
||||
fontManagerImpl: new HeadlessFontManagerStub());
|
||||
|
||||
private static TestServices Services => TestServices.MockThreadingInterface.With(
|
||||
standardCursorFactory: Mock.Of<ICursorFactory>(),
|
||||
renderInterface: new MockPlatformRenderInterface(),
|
||||
textShaperImpl: new MockTextShaperImpl(),
|
||||
fontManagerImpl: new MockFontManagerImpl());
|
||||
renderInterface: new HeadlessPlatformRenderInterface(),
|
||||
textShaperImpl: new HeadlessTextShaperStub(),
|
||||
fontManagerImpl: new HeadlessFontManagerStub());
|
||||
|
||||
private IControlTemplate CreateTemplate()
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Linq;
|
||||
using Avalonia.Controls.Presenters;
|
||||
using Avalonia.Controls.Templates;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Headless;
|
||||
using Avalonia.Markup.Data;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.UnitTests;
|
||||
@@ -87,8 +88,8 @@ namespace Avalonia.Controls.UnitTests
|
||||
|
||||
private static TestServices Services => TestServices.MockThreadingInterface.With(
|
||||
standardCursorFactory: Mock.Of<ICursorFactory>(),
|
||||
textShaperImpl: new MockTextShaperImpl(),
|
||||
fontManagerImpl: new MockFontManagerImpl());
|
||||
textShaperImpl: new HeadlessTextShaperStub(),
|
||||
fontManagerImpl: new HeadlessFontManagerStub());
|
||||
|
||||
private static IControlTemplate CreateTemplate()
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Linq;
|
||||
using Avalonia.Controls.Shapes;
|
||||
using Avalonia.Controls.Templates;
|
||||
using Avalonia.Headless;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.UnitTests;
|
||||
using Avalonia.VisualTree;
|
||||
@@ -99,10 +100,10 @@ namespace Avalonia.Controls.UnitTests
|
||||
}
|
||||
|
||||
private static TestServices Services => TestServices.MockThreadingInterface.With(
|
||||
fontManagerImpl: new MockFontManagerImpl(),
|
||||
fontManagerImpl: new HeadlessFontManagerStub(),
|
||||
standardCursorFactory: Mock.Of<ICursorFactory>(),
|
||||
textShaperImpl: new MockTextShaperImpl(),
|
||||
renderInterface: new MockPlatformRenderInterface());
|
||||
textShaperImpl: new HeadlessTextShaperStub(),
|
||||
renderInterface: new HeadlessPlatformRenderInterface());
|
||||
|
||||
private static IControlTemplate CreateTemplate()
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@ using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Controls.Templates;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Data.Core;
|
||||
using Avalonia.Headless;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Input.Platform;
|
||||
using Avalonia.Layout;
|
||||
@@ -1694,12 +1695,12 @@ namespace Avalonia.Controls.UnitTests
|
||||
return UnitTestApplication.Start(
|
||||
TestServices.MockThreadingInterface.With(
|
||||
focusManager: new FocusManager(),
|
||||
fontManagerImpl: new MockFontManagerImpl(),
|
||||
fontManagerImpl: new HeadlessFontManagerStub(),
|
||||
keyboardDevice: () => new KeyboardDevice(),
|
||||
keyboardNavigation: new KeyboardNavigationHandler(),
|
||||
inputManager: new InputManager(),
|
||||
renderInterface: new MockPlatformRenderInterface(),
|
||||
textShaperImpl: new MockTextShaperImpl()));
|
||||
renderInterface: new HeadlessPlatformRenderInterface(),
|
||||
textShaperImpl: new HeadlessTextShaperStub()));
|
||||
}
|
||||
|
||||
private class Node : NotifyingBase
|
||||
|
||||
@@ -402,6 +402,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
|
||||
[Fact]
|
||||
public void Style_Can_Use_NthChild_Selector_With_ItemsRepeater()
|
||||
{
|
||||
GC.KeepAlive(typeof(ItemsRepeater));
|
||||
|
||||
using (UnitTestApplication.Start(TestServices.StyledWindow))
|
||||
{
|
||||
var xaml = @"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Avalonia.Headless;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.UnitTests;
|
||||
using SkiaSharp;
|
||||
@@ -90,7 +91,7 @@ namespace Avalonia.Skia.UnitTests.Media
|
||||
[Fact]
|
||||
public void Should_Only_Try_To_Create_GlyphTypeface_Once()
|
||||
{
|
||||
var fontManagerImpl = new MockFontManagerImpl();
|
||||
var fontManagerImpl = new HeadlessFontManagerStub();
|
||||
|
||||
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface.With(fontManagerImpl: fontManagerImpl)))
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using Avalonia.Headless;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.TextFormatting;
|
||||
using Avalonia.Media.TextFormatting.Unicode;
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using Avalonia.Headless;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.TextFormatting;
|
||||
using Avalonia.UnitTests;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<EmbeddedResource Include="..\Avalonia.UnitTests\Assets\*.ttf" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Headless\Avalonia.Headless\Avalonia.Headless.csproj" />
|
||||
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
|
||||
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj" />
|
||||
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" />
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Threading;
|
||||
|
||||
namespace Avalonia.UnitTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Immediately invokes dispatched jobs on the current thread.
|
||||
/// </summary>
|
||||
public class ImmediateDispatcher : IDispatcher
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool CheckAccess()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Post(Action action, DispatcherPriority priority)
|
||||
{
|
||||
action();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Post(SendOrPostCallback action, object arg, DispatcherPriority priority)
|
||||
{
|
||||
action(arg);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task InvokeAsync(Action action, DispatcherPriority priority)
|
||||
{
|
||||
action();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<TResult> InvokeAsync<TResult>(Func<TResult> function, DispatcherPriority priority)
|
||||
{
|
||||
var result = function();
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task InvokeAsync(Func<Task> function, DispatcherPriority priority)
|
||||
{
|
||||
return function();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<TResult> InvokeAsync<TResult>(Func<Task<TResult>> function, DispatcherPriority priority)
|
||||
{
|
||||
return function();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void VerifyAccess()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Platform;
|
||||
|
||||
namespace Avalonia.UnitTests
|
||||
{
|
||||
public class MockFontManagerImpl : IFontManagerImpl
|
||||
{
|
||||
private readonly string _defaultFamilyName;
|
||||
|
||||
public MockFontManagerImpl(string defaultFamilyName = "Default")
|
||||
{
|
||||
_defaultFamilyName = defaultFamilyName;
|
||||
}
|
||||
|
||||
public int TryCreateGlyphTypefaceCount { get; private set; }
|
||||
|
||||
public string GetDefaultFontFamilyName()
|
||||
{
|
||||
return _defaultFamilyName;
|
||||
}
|
||||
|
||||
string[] IFontManagerImpl.GetInstalledFontFamilyNames(bool checkForUpdates)
|
||||
{
|
||||
return new[] { _defaultFamilyName };
|
||||
}
|
||||
|
||||
public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight,
|
||||
FontStretch fontStretch,
|
||||
CultureInfo culture, out Typeface fontKey)
|
||||
{
|
||||
fontKey = new Typeface(_defaultFamilyName);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool TryCreateGlyphTypeface(string familyName, FontStyle style, FontWeight weight,
|
||||
FontStretch stretch, [NotNullWhen(true)] out IGlyphTypeface glyphTypeface)
|
||||
{
|
||||
glyphTypeface = null;
|
||||
|
||||
TryCreateGlyphTypefaceCount++;
|
||||
|
||||
if (familyName == "Unknown")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
glyphTypeface = new MockGlyphTypeface();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool TryCreateGlyphTypeface(Stream stream, out IGlyphTypeface glyphTypeface)
|
||||
{
|
||||
glyphTypeface = new MockGlyphTypeface();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Avalonia.Media.TextFormatting;
|
||||
using Avalonia.Platform;
|
||||
|
||||
namespace Avalonia.UnitTests
|
||||
{
|
||||
public class MockGlyphRun : IGlyphRunImpl
|
||||
{
|
||||
public MockGlyphRun(IReadOnlyList<GlyphInfo> glyphInfos)
|
||||
{
|
||||
var width = 0.0;
|
||||
|
||||
for (var i = 0; i < glyphInfos.Count; ++i)
|
||||
{
|
||||
width += glyphInfos[i].GlyphAdvance;
|
||||
}
|
||||
|
||||
Bounds = new Rect(new Size(width, 10));
|
||||
}
|
||||
|
||||
public Rect Bounds { get; }
|
||||
|
||||
public Point BaselineOrigin => new Point(0, 8);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public IReadOnlyList<float> GetIntersections(float lowerBound, float upperBound)
|
||||
=> Array.Empty<float>();
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
using System;
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace Avalonia.UnitTests
|
||||
{
|
||||
public class MockGlyphTypeface : IGlyphTypeface
|
||||
{
|
||||
public FontMetrics Metrics => new FontMetrics
|
||||
{
|
||||
DesignEmHeight = 10,
|
||||
Ascent = 2,
|
||||
Descent = 10,
|
||||
IsFixedPitch = true
|
||||
};
|
||||
|
||||
public int GlyphCount => 1337;
|
||||
|
||||
public FontSimulations FontSimulations => throw new NotImplementedException();
|
||||
|
||||
public string FamilyName => "$Default";
|
||||
|
||||
public FontWeight Weight { get; }
|
||||
|
||||
public FontStyle Style { get; }
|
||||
|
||||
public FontStretch Stretch { get; }
|
||||
|
||||
public ushort GetGlyph(uint codepoint)
|
||||
{
|
||||
return (ushort)codepoint;
|
||||
}
|
||||
|
||||
public ushort[] GetGlyphs(ReadOnlySpan<uint> codepoints)
|
||||
{
|
||||
return new ushort[codepoints.Length];
|
||||
}
|
||||
|
||||
public int GetGlyphAdvance(ushort glyph)
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
public bool TryGetGlyph(uint codepoint, out ushort glyph)
|
||||
{
|
||||
glyph = 8;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int[] GetGlyphAdvances(ReadOnlySpan<ushort> glyphs)
|
||||
{
|
||||
var advances = new int[glyphs.Length];
|
||||
|
||||
for (var i = 0; i < advances.Length; i++)
|
||||
{
|
||||
advances[i] = 8;
|
||||
}
|
||||
|
||||
return advances;
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
public bool TryGetTable(uint tag, out byte[] table)
|
||||
{
|
||||
table = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetGlyphMetrics(ushort glyph, out GlyphMetrics metrics)
|
||||
{
|
||||
metrics = new GlyphMetrics
|
||||
{
|
||||
Width = 10,
|
||||
Height = 10
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Media.TextFormatting;
|
||||
using Avalonia.Rendering;
|
||||
using Moq;
|
||||
|
||||
namespace Avalonia.UnitTests
|
||||
{
|
||||
public class MockPlatformRenderInterface : IPlatformRenderInterface, IPlatformRenderInterfaceContext
|
||||
{
|
||||
public IGeometryImpl CreateEllipseGeometry(Rect rect)
|
||||
{
|
||||
return Mock.Of<IGeometryImpl>();
|
||||
}
|
||||
|
||||
public IGeometryImpl CreateLineGeometry(Point p1, Point p2)
|
||||
{
|
||||
return Mock.Of<IGeometryImpl>();
|
||||
}
|
||||
|
||||
public IGeometryImpl CreateRectangleGeometry(Rect rect)
|
||||
{
|
||||
return Mock.Of<IGeometryImpl>(x => x.Bounds == rect);
|
||||
}
|
||||
|
||||
class MockRenderTarget : IRenderTarget
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public IDrawingContextImpl CreateDrawingContext()
|
||||
{
|
||||
var m = new Mock<IDrawingContextImpl>();
|
||||
m.Setup(c => c.CreateLayer(It.IsAny<Size>()))
|
||||
.Returns(() =>
|
||||
{
|
||||
var r = new Mock<IDrawingContextLayerImpl>();
|
||||
r.Setup(r => r.CreateDrawingContext())
|
||||
.Returns(CreateDrawingContext());
|
||||
return r.Object;
|
||||
}
|
||||
);
|
||||
return m.Object;
|
||||
|
||||
}
|
||||
|
||||
public bool IsCorrupted => false;
|
||||
}
|
||||
|
||||
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
|
||||
{
|
||||
return new MockRenderTarget();
|
||||
}
|
||||
|
||||
public bool IsLost => false;
|
||||
|
||||
public object TryGetFeature(Type featureType) => null;
|
||||
|
||||
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(PixelSize size, Vector dpi)
|
||||
{
|
||||
return Mock.Of<IRenderTargetBitmapImpl>();
|
||||
}
|
||||
|
||||
public IStreamGeometryImpl CreateStreamGeometry()
|
||||
{
|
||||
return new MockStreamGeometryImpl();
|
||||
}
|
||||
|
||||
public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList<IGeometryImpl> children)
|
||||
{
|
||||
return Mock.Of<IGeometryImpl>();
|
||||
}
|
||||
|
||||
public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, IGeometryImpl g1, IGeometryImpl g2)
|
||||
{
|
||||
return Mock.Of<IGeometryImpl>();
|
||||
}
|
||||
|
||||
public IWriteableBitmapImpl CreateWriteableBitmap(
|
||||
PixelSize size,
|
||||
Vector dpi,
|
||||
PixelFormat format,
|
||||
AlphaFormat alphaFormat)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IBitmapImpl LoadBitmap(Stream stream)
|
||||
{
|
||||
return Mock.Of<IBitmapImpl>();
|
||||
}
|
||||
|
||||
public IWriteableBitmapImpl LoadWriteableBitmapToWidth(Stream stream, int width,
|
||||
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IWriteableBitmapImpl LoadWriteableBitmapToHeight(Stream stream, int height,
|
||||
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IWriteableBitmapImpl LoadWriteableBitmap(string fileName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IWriteableBitmapImpl LoadWriteableBitmap(Stream stream)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IBitmapImpl LoadBitmap(string fileName)
|
||||
{
|
||||
return Mock.Of<IBitmapImpl>();
|
||||
}
|
||||
|
||||
public IBitmapImpl LoadBitmapToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
|
||||
{
|
||||
return Mock.Of<IBitmapImpl>();
|
||||
}
|
||||
|
||||
public IBitmapImpl LoadBitmapToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
|
||||
{
|
||||
return Mock.Of<IBitmapImpl>();
|
||||
}
|
||||
|
||||
public IBitmapImpl ResizeBitmap(IBitmapImpl bitmapImpl, PixelSize destinationSize, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
|
||||
{
|
||||
return Mock.Of<IBitmapImpl>();
|
||||
}
|
||||
|
||||
public IBitmapImpl LoadBitmap(
|
||||
PixelFormat format,
|
||||
AlphaFormat alphaFormat,
|
||||
IntPtr data,
|
||||
PixelSize size,
|
||||
Vector dpi,
|
||||
int stride)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize,
|
||||
IReadOnlyList<GlyphInfo> glyphInfos, Point baselineOrigin)
|
||||
{
|
||||
return new MockGlyphRun(glyphInfos);
|
||||
}
|
||||
|
||||
public IPlatformRenderInterfaceContext CreateBackendContext(IPlatformGraphicsContext graphicsContext) => this;
|
||||
|
||||
public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun)
|
||||
{
|
||||
return Mock.Of<IGeometryImpl>();
|
||||
}
|
||||
|
||||
public IGlyphRunBuffer AllocateGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
|
||||
{
|
||||
return Mock.Of<IGlyphRunBuffer>();
|
||||
}
|
||||
|
||||
public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
|
||||
{
|
||||
return Mock.Of<IHorizontalGlyphRunBuffer>();
|
||||
}
|
||||
|
||||
public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
|
||||
{
|
||||
return Mock.Of<IPositionedGlyphRunBuffer>();
|
||||
}
|
||||
|
||||
public bool SupportsIndividualRoundRects { get; set; }
|
||||
|
||||
public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul;
|
||||
|
||||
public PixelFormat DefaultPixelFormat => PixelFormat.Rgba8888;
|
||||
public bool IsSupportedBitmapPixelFormat(PixelFormat format) => true;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Platform;
|
||||
|
||||
namespace Avalonia.UnitTests
|
||||
{
|
||||
public class MockStreamGeometryImpl : IStreamGeometryImpl, ITransformedGeometryImpl
|
||||
{
|
||||
private MockStreamGeometryContext _context;
|
||||
|
||||
public MockStreamGeometryImpl()
|
||||
{
|
||||
Transform = Matrix.Identity;
|
||||
_context = new MockStreamGeometryContext();
|
||||
}
|
||||
|
||||
public MockStreamGeometryImpl(Matrix transform)
|
||||
{
|
||||
Transform = transform;
|
||||
_context = new MockStreamGeometryContext();
|
||||
}
|
||||
|
||||
private MockStreamGeometryImpl(Matrix transform, MockStreamGeometryContext context)
|
||||
{
|
||||
Transform = transform;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public IGeometryImpl SourceGeometry { get; }
|
||||
|
||||
public Rect Bounds => _context.CalculateBounds();
|
||||
|
||||
public double ContourLength { get; }
|
||||
|
||||
public Matrix Transform { get; }
|
||||
|
||||
public IStreamGeometryImpl Clone()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public bool FillContains(Point point)
|
||||
{
|
||||
return _context.FillContains(point);
|
||||
}
|
||||
|
||||
public bool StrokeContains(IPen pen, Point point)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public Rect GetRenderBounds(IPen pen) => Bounds;
|
||||
|
||||
public IGeometryImpl Intersect(IGeometryImpl geometry)
|
||||
{
|
||||
return new MockStreamGeometryImpl(Transform);
|
||||
}
|
||||
|
||||
public IStreamGeometryContextImpl Open()
|
||||
{
|
||||
return _context;
|
||||
}
|
||||
|
||||
public ITransformedGeometryImpl WithTransform(Matrix transform)
|
||||
{
|
||||
return new MockStreamGeometryImpl(transform, _context);
|
||||
}
|
||||
|
||||
public bool TryGetPointAtDistance(double distance, out Point point)
|
||||
{
|
||||
point = new Point();
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetPointAndTangentAtDistance(double distance, out Point point, out Point tangent)
|
||||
{
|
||||
point = new Point();
|
||||
tangent = new Point();
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetSegment(double startDistance, double stopDistance, bool startOnBeginFigure, out IGeometryImpl segmentGeometry)
|
||||
{
|
||||
segmentGeometry = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
class MockStreamGeometryContext : IStreamGeometryContextImpl
|
||||
{
|
||||
private List<Point> points = new List<Point>();
|
||||
public void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection)
|
||||
{
|
||||
}
|
||||
|
||||
public void BeginFigure(Point startPoint, bool isFilled)
|
||||
{
|
||||
points.Add(startPoint);
|
||||
}
|
||||
|
||||
public Rect CalculateBounds()
|
||||
{
|
||||
var left = double.MaxValue;
|
||||
var right = double.MinValue;
|
||||
var top = double.MaxValue;
|
||||
var bottom = double.MinValue;
|
||||
|
||||
foreach (var p in points)
|
||||
{
|
||||
left = Math.Min(p.X, left);
|
||||
right = Math.Max(p.X, right);
|
||||
top = Math.Min(p.Y, top);
|
||||
bottom = Math.Max(p.Y, bottom);
|
||||
}
|
||||
|
||||
return new Rect(new Point(left, top), new Point(right, bottom));
|
||||
}
|
||||
|
||||
public void CubicBezierTo(Point point1, Point point2, Point point3)
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public void EndFigure(bool isClosed)
|
||||
{
|
||||
}
|
||||
|
||||
public void LineTo(Point point)
|
||||
{
|
||||
points.Add(point);
|
||||
}
|
||||
|
||||
public void QuadraticBezierTo(Point control, Point endPoint)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SetFillRule(FillRule fillRule)
|
||||
{
|
||||
}
|
||||
|
||||
public bool FillContains(Point point)
|
||||
{
|
||||
// Use the algorithm from https://www.blackpawn.com/texts/pointinpoly/default.html
|
||||
// to determine if the point is in the geometry (since it will always be convex in this situation)
|
||||
for (int i = 0; i < points.Count; i++)
|
||||
{
|
||||
var a = points[i];
|
||||
var b = points[(i + 1) % points.Count];
|
||||
var c = points[(i + 2) % points.Count];
|
||||
|
||||
Vector v0 = c - a;
|
||||
Vector v1 = b - a;
|
||||
Vector v2 = point - a;
|
||||
|
||||
var dot00 = v0 * v0;
|
||||
var dot01 = v0 * v1;
|
||||
var dot02 = v0 * v2;
|
||||
var dot11 = v1 * v1;
|
||||
var dot12 = v1 * v2;
|
||||
|
||||
|
||||
var invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
|
||||
var u = (dot11 * dot02 - dot01 * dot12) * invDenom;
|
||||
var v = (dot00 * dot12 - dot01 * dot02) * invDenom;
|
||||
if ((u >= 0) && (v >= 0) && (u + v < 1)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
using System;
|
||||
using Avalonia.Media.TextFormatting;
|
||||
using Avalonia.Media.TextFormatting.Unicode;
|
||||
using Avalonia.Platform;
|
||||
|
||||
namespace Avalonia.UnitTests
|
||||
{
|
||||
public class MockTextShaperImpl : ITextShaperImpl
|
||||
{
|
||||
public ShapedBuffer ShapeText(ReadOnlyMemory<char> text, TextShaperOptions options)
|
||||
{
|
||||
var typeface = options.Typeface;
|
||||
var fontRenderingEmSize = options.FontRenderingEmSize;
|
||||
var bidiLevel = options.BidiLevel;
|
||||
var shapedBuffer = new ShapedBuffer(text, text.Length, typeface, fontRenderingEmSize, bidiLevel);
|
||||
var textSpan = text.Span;
|
||||
var textStartIndex = TextTestHelper.GetStartCharIndex(text);
|
||||
|
||||
for (var i = 0; i < shapedBuffer.Length;)
|
||||
{
|
||||
var glyphCluster = i + textStartIndex;
|
||||
|
||||
var codepoint = Codepoint.ReadAt(textSpan, i, out var count);
|
||||
|
||||
var glyphIndex = typeface.GetGlyph(codepoint);
|
||||
|
||||
for (var j = 0; j < count; ++j)
|
||||
{
|
||||
shapedBuffer[i + j] = new GlyphInfo(glyphIndex, glyphCluster, 10);
|
||||
}
|
||||
|
||||
i += count;
|
||||
}
|
||||
|
||||
return shapedBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ using System.Collections.Generic;
|
||||
using Avalonia.Controls;
|
||||
using System.Reflection;
|
||||
using Avalonia.Animation;
|
||||
using Avalonia.Headless;
|
||||
using Avalonia.Threading;
|
||||
|
||||
namespace Avalonia.UnitTests
|
||||
@@ -22,25 +23,25 @@ namespace Avalonia.UnitTests
|
||||
public static readonly TestServices StyledWindow = new TestServices(
|
||||
assetLoader: new AssetLoader(),
|
||||
platform: new StandardRuntimePlatform(),
|
||||
renderInterface: new MockPlatformRenderInterface(),
|
||||
standardCursorFactory: Mock.Of<ICursorFactory>(),
|
||||
renderInterface: new HeadlessPlatformRenderInterface(),
|
||||
standardCursorFactory: new HeadlessCursorFactoryStub(),
|
||||
theme: () => CreateSimpleTheme(),
|
||||
dispatcherImpl: Mock.Of<IDispatcherImpl>(x => x.CurrentThreadIsLoopThread == true),
|
||||
fontManagerImpl: new MockFontManagerImpl(),
|
||||
textShaperImpl: new MockTextShaperImpl(),
|
||||
dispatcherImpl: new NullDispatcherImpl(),
|
||||
fontManagerImpl: new HeadlessFontManagerStub(),
|
||||
textShaperImpl: new HeadlessTextShaperStub(),
|
||||
windowingPlatform: new MockWindowingPlatform());
|
||||
|
||||
public static readonly TestServices MockPlatformRenderInterface = new TestServices(
|
||||
assetLoader: new AssetLoader(),
|
||||
renderInterface: new MockPlatformRenderInterface(),
|
||||
fontManagerImpl: new MockFontManagerImpl(),
|
||||
textShaperImpl: new MockTextShaperImpl());
|
||||
renderInterface: new HeadlessPlatformRenderInterface(),
|
||||
fontManagerImpl: new HeadlessFontManagerStub(),
|
||||
textShaperImpl: new HeadlessTextShaperStub());
|
||||
|
||||
public static readonly TestServices MockPlatformWrapper = new TestServices(
|
||||
platform: Mock.Of<IRuntimePlatform>());
|
||||
|
||||
public static readonly TestServices MockThreadingInterface = new TestServices(
|
||||
dispatcherImpl: Mock.Of<IDispatcherImpl>(x => x.CurrentThreadIsLoopThread == true));
|
||||
dispatcherImpl: new NullDispatcherImpl());
|
||||
|
||||
public static readonly TestServices MockWindowingPlatform = new TestServices(
|
||||
windowingPlatform: new MockWindowingPlatform());
|
||||
@@ -51,13 +52,13 @@ namespace Avalonia.UnitTests
|
||||
keyboardNavigation: new KeyboardNavigationHandler(),
|
||||
inputManager: new InputManager(),
|
||||
assetLoader: new AssetLoader(),
|
||||
renderInterface: new MockPlatformRenderInterface(),
|
||||
fontManagerImpl: new MockFontManagerImpl(),
|
||||
textShaperImpl: new MockTextShaperImpl());
|
||||
renderInterface: new HeadlessPlatformRenderInterface(),
|
||||
fontManagerImpl: new HeadlessFontManagerStub(),
|
||||
textShaperImpl: new HeadlessTextShaperStub());
|
||||
|
||||
public static readonly TestServices TextServices = new TestServices(
|
||||
assetLoader: new AssetLoader(),
|
||||
renderInterface: new MockPlatformRenderInterface(),
|
||||
renderInterface: new HeadlessPlatformRenderInterface(),
|
||||
fontManagerImpl: new HarfBuzzFontManagerImpl(),
|
||||
textShaperImpl: new HarfBuzzTextShaperImpl());
|
||||
|
||||
@@ -158,12 +159,5 @@ namespace Avalonia.UnitTests
|
||||
{
|
||||
return new SimpleTheme();
|
||||
}
|
||||
|
||||
private static IPlatformRenderInterface CreateRenderInterfaceMock()
|
||||
{
|
||||
return Mock.Of<IPlatformRenderInterface>(x =>
|
||||
x.CreateStreamGeometry() == Mock.Of<IStreamGeometryImpl>(
|
||||
y => y.Open() == Mock.Of<IStreamGeometryContextImpl>()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Avalonia.UnitTests
|
||||
{
|
||||
public static class TextTestHelper
|
||||
{
|
||||
public static int GetStartCharIndex(ReadOnlyMemory<char> text)
|
||||
{
|
||||
if (!MemoryMarshal.TryGetString(text, out _, out var start, out _))
|
||||
throw new InvalidOperationException("text memory should have been a string");
|
||||
return start;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user