From 7a533700d0bcb54be20ec448e5b7802ad6ac677b Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Thu, 9 Jun 2022 12:24:39 +0200 Subject: [PATCH 1/4] Implement GlyphRun.BuildGeometry --- samples/RenderDemo/Pages/GlyphRunPage.xaml | 12 +- samples/RenderDemo/Pages/GlyphRunPage.xaml.cs | 113 +++++++++++++----- src/Avalonia.Base/Media/GlyphRun.cs | 19 +++ src/Avalonia.Base/Media/PlatformGeometry.cs | 24 ++++ .../Platform/IPlatformRenderInterface.cs | 8 ++ .../Avalonia.Skia/PlatformRenderInterface.cs | 32 +++++ .../Avalonia.Direct2D1/Direct2D1Platform.cs | 28 +++++ .../Media/GlyphRunTests.cs | 66 ++++++++++ ...ould_Render_GlyphRun_Geometry.expected.png | Bin 0 -> 1476 bytes ...ould_Render_GlyphRun_Geometry.expected.png | Bin 0 -> 1170 bytes 10 files changed, 269 insertions(+), 33 deletions(-) create mode 100644 src/Avalonia.Base/Media/PlatformGeometry.cs create mode 100644 tests/Avalonia.RenderTests/Media/GlyphRunTests.cs create mode 100644 tests/TestFiles/Direct2D1/Media/GlyphRun/Should_Render_GlyphRun_Geometry.expected.png create mode 100644 tests/TestFiles/Skia/Media/GlyphRun/Should_Render_GlyphRun_Geometry.expected.png diff --git a/samples/RenderDemo/Pages/GlyphRunPage.xaml b/samples/RenderDemo/Pages/GlyphRunPage.xaml index c2914e8847..7db58e5286 100644 --- a/samples/RenderDemo/Pages/GlyphRunPage.xaml +++ b/samples/RenderDemo/Pages/GlyphRunPage.xaml @@ -2,13 +2,13 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:local="clr-namespace:RenderDemo.Pages" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="RenderDemo.Pages.GlyphRunPage"> - - - - + + + diff --git a/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs b/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs index 7f85606957..674ed8e61f 100644 --- a/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs +++ b/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs @@ -9,14 +9,6 @@ namespace RenderDemo.Pages { public class GlyphRunPage : UserControl { - private Image _imageControl; - private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface; - private readonly Random _rand = new Random(); - private ushort[] _glyphIndices = new ushort[1]; - private char[] _characters = new char[1]; - private float _fontSize = 20; - private int _direction = 10; - public GlyphRunPage() { this.InitializeComponent(); @@ -25,19 +17,43 @@ namespace RenderDemo.Pages private void InitializeComponent() { AvaloniaXamlLoader.Load(this); + } + } + + public class GlyphRunControl : Control + { + private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface; + private readonly Random _rand = new Random(); + private ushort[] _glyphIndices = new ushort[1]; + private char[] _characters = new char[1]; + private float _fontSize = 20; + private int _direction = 10; - _imageControl = this.FindControl("imageControl"); - _imageControl.Source = new DrawingImage(); + private DispatcherTimer _timer; - DispatcherTimer.Run(() => + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + _timer = new DispatcherTimer + { + Interval = TimeSpan.FromSeconds(1) + }; + + _timer.Tick += (s,e) => { - UpdateGlyphRun(); + InvalidateVisual(); + }; + + _timer.Start(); + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + _timer.Stop(); - return true; - }, TimeSpan.FromSeconds(1)); + _timer = null; } - private void UpdateGlyphRun() + public override void Render(DrawingContext context) { var c = (char)_rand.Next(65, 90); @@ -57,27 +73,70 @@ namespace RenderDemo.Pages _characters[0] = c; - var scale = (double)_fontSize / _glyphTypeface.DesignEmHeight; + var glyphRun = new GlyphRun(_glyphTypeface, _fontSize, _characters, _glyphIndices); - var drawingGroup = new DrawingGroup(); + context.DrawGlyphRun(Brushes.Black, glyphRun); + } + } - var glyphRunDrawing = new GlyphRunDrawing + public class GlyphRunGeometryControl : Control + { + private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface; + private readonly Random _rand = new Random(); + private ushort[] _glyphIndices = new ushort[1]; + private char[] _characters = new char[1]; + private float _fontSize = 20; + private int _direction = 10; + + private DispatcherTimer _timer; + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + _timer = new DispatcherTimer { - Foreground = Brushes.Black, - GlyphRun = new GlyphRun(_glyphTypeface, _fontSize, _characters, _glyphIndices) + Interval = TimeSpan.FromSeconds(1) }; - drawingGroup.Children.Add(glyphRunDrawing); - - var geometryDrawing = new GeometryDrawing + _timer.Tick += (s, e) => { - Pen = new Pen(Brushes.Black), - Geometry = new RectangleGeometry { Rect = new Rect(glyphRunDrawing.GlyphRun.Size) } + InvalidateVisual(); }; - drawingGroup.Children.Add(geometryDrawing); + _timer.Start(); + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + _timer.Stop(); + + _timer = null; + } + + public override void Render(DrawingContext context) + { + var c = (char)_rand.Next(65, 90); + + if (_fontSize + _direction > 200) + { + _direction = -10; + } + + if (_fontSize + _direction < 20) + { + _direction = 10; + } + + _fontSize += _direction; + + _glyphIndices[0] = _glyphTypeface.GetGlyph(c); + + _characters[0] = c; + + var glyphRun = new GlyphRun(_glyphTypeface, _fontSize, _characters, _glyphIndices); + + var geometry = glyphRun.BuildGeometry(); - (_imageControl.Source as DrawingImage).Drawing = drawingGroup; + context.DrawGeometry(Brushes.Green, null, geometry); } } } diff --git a/src/Avalonia.Base/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs index 22be8d8865..25c35a28e5 100644 --- a/src/Avalonia.Base/Media/GlyphRun.cs +++ b/src/Avalonia.Base/Media/GlyphRun.cs @@ -194,6 +194,25 @@ namespace Avalonia.Media } } + /// + /// Obtains geometry for the glyph run. + /// + /// The geometry returned contains the combined geometry of all glyphs in the glyph run. + public Geometry BuildGeometry() + { + var platformRenderInterface = AvaloniaLocator.Current.GetRequiredService(); + + var geometryImpl = platformRenderInterface.BuildGlyphRunGeometry(this, out var scale); + + var geometry = new PlatformGeometry(geometryImpl); + + var transform = new MatrixTransform(Matrix.CreateTranslation(geometry.Bounds.Left, -geometry.Bounds.Top) * scale); + + geometry.Transform = transform; + + return geometry; + } + /// /// Retrieves the offset from the leading edge of the /// to the leading or trailing edge of a caret stop containing the specified character hit. diff --git a/src/Avalonia.Base/Media/PlatformGeometry.cs b/src/Avalonia.Base/Media/PlatformGeometry.cs new file mode 100644 index 0000000000..f25a14540f --- /dev/null +++ b/src/Avalonia.Base/Media/PlatformGeometry.cs @@ -0,0 +1,24 @@ +using Avalonia.Platform; + +namespace Avalonia.Media +{ + internal class PlatformGeometry : Geometry + { + private readonly IGeometryImpl _geometryImpl; + + public PlatformGeometry(IGeometryImpl geometryImpl) + { + _geometryImpl = geometryImpl; + } + + public override Geometry Clone() + { + return new PlatformGeometry(_geometryImpl); + } + + protected override IGeometryImpl? CreateDefiningGeometry() + { + return _geometryImpl; + } + } +} diff --git a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs index 0eeefddf0b..bfa9e70fce 100644 --- a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs +++ b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs @@ -58,6 +58,14 @@ namespace Avalonia.Platform /// A combined geometry. IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2); + /// + /// Created a geometry implementation for the glyph run. + /// + /// The glyph run to build a geometry from. + /// The scaling of the produces geometry. + /// The geometry returned contains the combined geometry of all glyphs in the glyph run. + IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun, out Matrix scale); + /// /// Creates a renderer. /// diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index d3c3585cd0..dc1b7785e2 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -62,6 +62,38 @@ namespace Avalonia.Skia return new CombinedGeometryImpl(combineMode, g1, g2); } + public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun, out Matrix scale) + { + if (glyphRun.GlyphTypeface.PlatformImpl is not GlyphTypefaceImpl glyphTypeface) + { + throw new InvalidOperationException("PlatformImpl can't be null."); + } + + var fontRenderingEmSize = (float)glyphRun.FontRenderingEmSize; + var glyphs = glyphRun.GlyphIndices.ToArray(); + var skFont = new SKFont(glyphTypeface.Typeface, fontRenderingEmSize) + { + Size = fontRenderingEmSize, + Edging = SKFontEdging.Antialias, + Hinting = SKFontHinting.None, + LinearMetrics = true + }; + + SKPath path = null; + var matrix = SKMatrix.Identity; + + skFont.GetGlyphPaths(glyphs, (p, m) => + { + matrix = m; + + path = p; + }); + + scale = Matrix.CreateScale(matrix.ScaleX, matrix.ScaleY); + + return new StreamGeometryImpl(path); + } + /// public IBitmapImpl LoadBitmap(string fileName) { diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs index d9e992bb80..04025f92e4 100644 --- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs +++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs @@ -159,6 +159,34 @@ namespace Avalonia.Direct2D1 public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) => new GeometryGroupImpl(fillRule, children); public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2) => new CombinedGeometryImpl(combineMode, g1, g2); + public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun, out Matrix scale) + { + if (glyphRun.GlyphTypeface.PlatformImpl is not GlyphTypefaceImpl glyphTypeface) + { + throw new InvalidOperationException("PlatformImpl can't be null."); + } + + var pathGeometry = new SharpDX.Direct2D1.PathGeometry(Direct2D1Factory); + + using (var sink = pathGeometry.Open()) + { + var glyphs = new short[glyphRun.GlyphIndices.Count]; + + for (int i = 0; i < glyphRun.GlyphIndices.Count; i++) + { + glyphs[i] = (short)glyphRun.GlyphIndices[i]; + } + + glyphTypeface.FontFace.GetGlyphRunOutline((float)glyphRun.FontRenderingEmSize, glyphs, null, null, false, !glyphRun.IsLeftToRight, sink); + + sink.Close(); + } + + scale = Matrix.Identity; + + return new StreamGeometryImpl(pathGeometry); + } + /// public IBitmapImpl LoadBitmap(string fileName) { diff --git a/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs b/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs new file mode 100644 index 0000000000..734e4d5012 --- /dev/null +++ b/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs @@ -0,0 +1,66 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Controls.Shapes; +using Avalonia.Media; +using Avalonia.Media.Imaging; +using Xunit; + +#if AVALONIA_SKIA +namespace Avalonia.Skia.RenderTests +#else +namespace Avalonia.Direct2D1.RenderTests.Media +#endif +{ + public class GlyphRunTests : TestBase + { + public GlyphRunTests() + : base(@"Media\GlyphRun") + { + } + + [Fact] + public async Task Should_Render_GlyphRun_Geometry() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 80, + Height = 90, + Child = new GlyphRunGeometryControl() + }; + + await RenderToFile(target); + + CompareImages(); + } + + public class GlyphRunGeometryControl : Control + { + private readonly Geometry _geometry; + + public GlyphRunGeometryControl() + { + var glyphTypeface = Typeface.Default.GlyphTypeface; + + var glyphIndices = new[] { glyphTypeface.GetGlyph('A') }; + + var characters = new[] { 'A' }; + + var glyphRun = new GlyphRun(glyphTypeface, 100, characters, glyphIndices); + + _geometry = glyphRun.BuildGeometry(); + } + + protected override Size MeasureOverride(Size availableSize) + { + return _geometry.Bounds.Size; + } + + public override void Render(DrawingContext context) + { + context.DrawGeometry(Brushes.Green, null, _geometry); + } + } + } +} diff --git a/tests/TestFiles/Direct2D1/Media/GlyphRun/Should_Render_GlyphRun_Geometry.expected.png b/tests/TestFiles/Direct2D1/Media/GlyphRun/Should_Render_GlyphRun_Geometry.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..0b40e667a408be1bda12d7749d0496a85a20ce3e GIT binary patch literal 1476 zcmV;#1v~nQP)Px#1ZP1_K>z@;j|==^1pojAZb?KzRCr$PoWE;SK@i9H#KOYD!a^ZJiWDg zks?A0g%l|w1Po~5fAQ}A0!vF3B18yiks^hKMT8V7QYr+rNMVt}!oos0XKwb)C3!nD z@4fvMvLCqjb|K*1cW2+ue0QIwP$(1%g+iflCF$9FK2&BJzSVczbFpLk=CN%j<(j?c zL(e?3EskXn`i^ZUB`*Oha~-Z5)M3&7U2t-Xz$N%hQB$C2p4wJY@)A%teaFl~#6Ga? zWaJ}YWp2au5p|R_6FdkxMc^8IZctOrDhPkmwv&*LfbF6Q#Y5qfk%xelnTP8fb!s`X z_lqQ+#(4A4mE38fB~4b?dYTzuo^x0)~Qp=z8RWtwp~xcD9j-OSKxDtnl%i~ z7r5?I$H_Vj1SZ$fNiU!rJ-cRL4&i=G-CD4$!-0rSS^?2Gpz6cLA&B?Gwm;f?J&7ul z)Dg(nvqSUFwy^!ywp^gWpeqACl2$-IYW~7!i<-c|9KiJ}b!x$9Ah#F=A)SELXzJn= zcJUcRJNw*|qAO!h)yb@o)YGfLqdEnw0 zqR%}|2af4INyKR!UqE@KlokppLJ>3=?%Q@y;tEK{0q@_*&p3=ij8Lw$ix!2kBM>cn zwC-ITfH+*TJjok-cd-Ej7TjQnE1;bFNmKI_`EPNcf*TC+1f)8ZGY+tHlb?TMC{lTw zD?()wGXl{#;HK0Lh;m}vI^-x^vI7^4cmn#IzNetsP@ixU!A~&65s>Or&N%o5A8y~H z9*Wpq>Np5IjpGO?{|I;KCLyOi({=!Z5MDqs4pyn-K$H8q!N}#GodKgTMg&SH4vrxD z_H4U3gLB4fJ$Qn_y>Zon@B(%-4s^&7ELT7+2`eDgqYKn=qR;(Y$aa6QX&hESxgOnv zAMZZ4(F8dP=k0)cC7gho)22SA?d$5BIPg1t$4GX+W?&l;Dgtp&f-?><@oiqZNoeeT&nFnd z2q-_SH{r)(0_-1MWIwm}&iEIQPR`Fz$3d9sOGLK&{lDtfH?QHE#Y-?OL-Wvz-3h-D zNE_O`agYn1azfuz80*cXuN^dC1qQsc!QfZGfVNQpyqsY0Cm@}amyCnF@UWQp6Yz3H z-xxF)xc5^;{0K_iO!S?DILegOV9~h5E&%A^yw^*FOce@AQ zuGOB59|8L#@wg%#zvSIu)dxr4yn^cs>NpshC-B``IYZs2Fui&&y{5XAL_9>ZVbx(8 z`*;RQ#sU6__qG+E!RYvhbYKZjFmU_UPPz*yk6yQ3Hy`M>O{+AauE+ol%?;}_yj{c0jrVYEd8G?_^#Ve+s=ak zIbk~iFU#U_9$ij22LZyp$8v_|0_uzdoG$ze(y8#gn6!yNT=^g<6^kNK3qQG(tYNoEnddNz(|FUR6NSIWKxNIpHD%jA$@4 z6Y#Q6-!ZzJP!)xG0m(R!4k)H~UbN`aEb)Ciy@0x5S`erhdK?hyU`shen>jF$BlgVTpXTyZ)boGd%}JK za^v9c&-6VJNUzQJggrBmWlIo$_Lk-bLmvT|ad5y3{0EDM$0Op#I)y7ASU$ZnFr9s? zWTxFD+JMFeLoWeYtqO@Yww-BeFtis?F%Ep<=oS~2Tk@17xYSQfoM>j?w?bQv2mICU=fsXP!Ri|+q0X002J$^RqAKfNQMqEfH~r1tMAr$Xt#m_)b$iPSMo(qQOvjv|K)g`xyr+ zL5Ojsom40Jdz7Ks{lx}@g@A1O?hS4GQi2duv?p99U~l6t7_!fO`^Ebc3N>K>nI45MP#gEPjS9L5Oj=oi52h zqFoD?42E0*mFXKY`|N{OryUE>1=Ng#{U?~duPAkzCQMESDm5Ww^w$?{PdLOMd7Mlp klgVT Date: Thu, 9 Jun 2022 12:44:22 +0200 Subject: [PATCH 2/4] Skia - Correctly translate current position when multiple glyphs are added to a path --- .../Avalonia.Skia/PlatformRenderInterface.cs | 15 ++++++---- .../Media/GlyphRunTests.cs | 27 ++++++++++++++---- ...ould_Render_GlyphRun_Geometry.expected.png | Bin 1476 -> 5566 bytes ...ould_Render_GlyphRun_Geometry.expected.png | Bin 1170 -> 4372 bytes 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index dc1b7785e2..727677c82e 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -70,7 +70,6 @@ namespace Avalonia.Skia } var fontRenderingEmSize = (float)glyphRun.FontRenderingEmSize; - var glyphs = glyphRun.GlyphIndices.ToArray(); var skFont = new SKFont(glyphTypeface.Typeface, fontRenderingEmSize) { Size = fontRenderingEmSize, @@ -79,15 +78,19 @@ namespace Avalonia.Skia LinearMetrics = true }; - SKPath path = null; + SKPath path = new SKPath(); var matrix = SKMatrix.Identity; - skFont.GetGlyphPaths(glyphs, (p, m) => + var currentX = 0f; + + foreach (var glyph in glyphRun.GlyphIndices) { - matrix = m; + var p = skFont.GetGlyphPath(glyph); + + path.AddPath(p, currentX, 0); - path = p; - }); + currentX += p.Bounds.Right; + } scale = Matrix.CreateScale(matrix.ScaleX, matrix.ScaleY); diff --git a/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs b/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs index 734e4d5012..2de2dda29b 100644 --- a/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs +++ b/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Avalonia.Controls; +using Avalonia.Controls.Documents; using Avalonia.Controls.Shapes; using Avalonia.Media; using Avalonia.Media.Imaging; @@ -25,9 +26,21 @@ namespace Avalonia.Direct2D1.RenderTests.Media Decorator target = new Decorator { Padding = new Thickness(8), - Width = 80, - Height = 90, - Child = new GlyphRunGeometryControl() + Width = 200, + Height = 100, + Child = new GlyphRunGeometryControl + { + [TextElement.ForegroundProperty] = new LinearGradientBrush + { + StartPoint = new RelativePoint(0, 0.5, RelativeUnit.Relative), + EndPoint = new RelativePoint(1, 0.5, RelativeUnit.Relative), + GradientStops = + { + new GradientStop { Color = Colors.Red, Offset = 0 }, + new GradientStop { Color = Colors.Blue, Offset = 1 } + } + } + } }; await RenderToFile(target); @@ -43,9 +56,9 @@ namespace Avalonia.Direct2D1.RenderTests.Media { var glyphTypeface = Typeface.Default.GlyphTypeface; - var glyphIndices = new[] { glyphTypeface.GetGlyph('A') }; + var glyphIndices = new[] { glyphTypeface.GetGlyph('A'), glyphTypeface.GetGlyph('B'), glyphTypeface.GetGlyph('C') }; - var characters = new[] { 'A' }; + var characters = new[] { 'A', 'B', 'C' }; var glyphRun = new GlyphRun(glyphTypeface, 100, characters, glyphIndices); @@ -59,7 +72,9 @@ namespace Avalonia.Direct2D1.RenderTests.Media public override void Render(DrawingContext context) { - context.DrawGeometry(Brushes.Green, null, _geometry); + var foreground = TextElement.GetForeground(this); + + context.DrawGeometry(foreground, null, _geometry); } } } diff --git a/tests/TestFiles/Direct2D1/Media/GlyphRun/Should_Render_GlyphRun_Geometry.expected.png b/tests/TestFiles/Direct2D1/Media/GlyphRun/Should_Render_GlyphRun_Geometry.expected.png index 0b40e667a408be1bda12d7749d0496a85a20ce3e..40ccc9fdcddbfd3af4673109e9558f2a30f958e3 100644 GIT binary patch literal 5566 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 delta 1465 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 klgVT Date: Thu, 9 Jun 2022 13:53:16 +0200 Subject: [PATCH 3/4] Fix mocks --- src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs | 7 +++++++ .../VisualTree/MockRenderInterface.cs | 5 +++++ tests/Avalonia.Benchmarks/NullRenderingPlatform.cs | 5 +++++ tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs | 7 +++++++ 4 files changed, 24 insertions(+) diff --git a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs index addc248d58..6471b87bfd 100644 --- a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs +++ b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs @@ -114,6 +114,13 @@ namespace Avalonia.Headless return new HeadlessGlyphRunStub(); } + public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun, out Matrix scale) + { + scale = Matrix.Identity; + + return new HeadlessGeometryStub(new Rect(glyphRun.Size)); + } + class HeadlessGeometryStub : IGeometryImpl { public HeadlessGeometryStub(Rect bounds) diff --git a/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs b/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs index add8f7fd73..183177495a 100644 --- a/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs +++ b/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs @@ -121,6 +121,11 @@ namespace Avalonia.Base.UnitTests.VisualTree throw new NotImplementedException(); } + public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun, out Matrix scale) + { + throw new NotImplementedException(); + } + class MockStreamGeometry : IStreamGeometryImpl { private MockStreamGeometryContext _impl = new MockStreamGeometryContext(); diff --git a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs index 5cbb3b2c49..51e75b6611 100644 --- a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs +++ b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs @@ -117,6 +117,11 @@ namespace Avalonia.Benchmarks return new NullGlyphRun(); } + public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun, out Matrix scale) + { + throw new NotImplementedException(); + } + public bool SupportsIndividualRoundRects => true; public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul; diff --git a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs index 376121c269..c385e1c3eb 100644 --- a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs +++ b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs @@ -122,6 +122,13 @@ namespace Avalonia.UnitTests return Mock.Of(); } + public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun, out Matrix scale) + { + scale = Matrix.Identity; + + return Mock.Of(); + } + public bool SupportsIndividualRoundRects { get; set; } public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul; From e48c984443e60664db79752bb5f058e6df2b0bf3 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Thu, 9 Jun 2022 14:23:02 +0200 Subject: [PATCH 4/4] Update tests --- .../Media/GlyphRunTests.cs | 2 +- ...ould_Render_GlyphRun_Geometry.expected.png | Bin 5566 -> 5326 bytes ...ould_Render_GlyphRun_Geometry.expected.png | Bin 4372 -> 4228 bytes 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs b/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs index 2de2dda29b..6a8884a33a 100644 --- a/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs +++ b/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs @@ -54,7 +54,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media public GlyphRunGeometryControl() { - var glyphTypeface = Typeface.Default.GlyphTypeface; + var glyphTypeface = new Typeface(TestFontFamily).GlyphTypeface; var glyphIndices = new[] { glyphTypeface.GetGlyph('A'), glyphTypeface.GetGlyph('B'), glyphTypeface.GetGlyph('C') }; diff --git a/tests/TestFiles/Direct2D1/Media/GlyphRun/Should_Render_GlyphRun_Geometry.expected.png b/tests/TestFiles/Direct2D1/Media/GlyphRun/Should_Render_GlyphRun_Geometry.expected.png index 40ccc9fdcddbfd3af4673109e9558f2a30f958e3..7f1e0d29a1940266121b7f610f3763d3e08e4268 100644 GIT binary patch delta 5310 zcmZWtXE2-r!wk_&lsG+V5S(6u=o}I?qL<@{9wkb&<55ra-lB^xx)bG&IGxjb3DF~Z z2%_bC-=FV%`)6loc4q(W>})6h7;lmyK;!pPRms2)dYDI%L}M5{&?P4ZCOz`s|IV1{ zNvzrS%uWiHex`VVYY?jZ)F(*;my3WnH{^A9il(QbnYDjLTOrP?^R4l`)#&aQ5;l>f zl!7?JXl74Jwu2{u^~8Zxv2x$Dv>P#)F|FMj`_;y#rs=y??77x>!DZzy|6`v^z`HB> zF8KH|5MUw*@3t1EC;MOGis71kr(Et<%CzBWn2TXLMit%?~fFC z{)pVyMg2RrR-?SZU>{(~8t!0-Wxug5^=#GG2F~T3D=p-n3HncTxyVisQ+FBAOQ@SpkCn~K6o&VDGso~}$)=Lx zyxe7G7mw>FF6&9mh6Y-7_e5=q@V|3|jNr-l@Y!!kzs7T+F5v&m@TQF+*DFo=ihvRx zznII&BSxaEI96DcKyhBQ5n9(ywIFtknJgIW+68stKmE&05;Gy&QA~tBv4o?w)tame zt;GT143}*CSnvFSeXmyH^@Xu7_qbo1wa-GlgCbLc3(lRX6n`X^em#H@8@qIyDQzB{ z>%Ut*p=_BMROu9D8K`#e>2f3DfXAGgKlQx{F&uRzpWbEu+B{vxh8MBIZz*76GbR^O zd1}_dPJj<78TbXo8yD>Mo#yjE?L1<>)64+Ycc0N$Gc>(wTXtW4Yavr=#k7VWk$%S5 zx#A>Rfj?fy5HsR*R}QbQa{6^>5oeq&eKumiO!Pg0Q>gJlpiZyKk>G=OuiXPBk8~;4 z@SlDhwBWX1TKa}?#exY-Bo|K}bQU~kr^39a>haZgk>&6OQNPHV6{{)`5xCn1yDg0a zB(r2uBy)v9LA8#f?O{d!?xR!HCFV6`zN%ZnZ9+q=TLkjzNnH&=NLeG^mS0;*Ouez22}T{3X-*1M61x70$S% z3IziZ^e?|vzk3JfMD48($~&&;`Iqg$2Q5NzQ8%6l9!ofg5(aIc_6=p=NV{GqIM(zY z!ghE_)zz5F;8G)yZ^# z-{Mb09GS|!U#@=r?cVS+{mKxfl4WSD*HCatpT;ueR$TB2nOe5+eBAorgFmk>J!1S< zh0}f9UQ@Bq+6nsY7dbrnc_lCak;CY7S(_s5(H{+W+iEsPSV!43)#S2Z`AJY+*pR?4 zsik{9I58-_lK=) zz5C^s15D&zM@UXR1u3V1I*Ex6UFPTbJ8xT}rPK_`B3-MdluL)E{bb+|<)MRVQ;a#Y zu@9z~nN&-23AS}QnY0gGeNR^Qx-hQl+X=Ymv+(md@i;A3Z1&A5vU|aaKvhy$I1T;tUd4sCI-p&0HS1(K;s%E51(k z#c87vt6Nd`69x2dN}j9X*Xo7$668u_Trz61K@r=4ZY zd^0L~`%^Nc&y6`)y3i>TmW*SO6If|Cyn@w#tSD})M8cuGu9rBmeY1bXeDB^wMl0tJ zN^14D>-c2cTNEWFtXNH^mSy^8WC|PeZLZ4vp!d#c9Ej4eZu15$EPF{DjZpmNV>8xc zmm#zqCn2HG`EKbg|vBB%C=rnxugo_;dkxE>Csa8C3fniLQ@Ib{oG!GU&wsW#?(97mPC)6jDITCv#cG7F@SA+0UWlgn9^agW6 za(kW?0n!#xFHGt$6Z-3HH~+m+!ts1kIH0AMVNW{^v1|FwtO+ZY>A5VXQN+PVmNZAP zx#LpzrkiRhM%KJ*liZ!s^3x4?!uwUHC+Z~Sr?-zp!!t!!I$@4Yf3e%H#);&mwaj#T z&zCIPV4wbY4$6Hw-wl4L;Iul7VdNPkJw3bc8W=F!^NGMce^&-_m{Z`ZRN@{iGvKJQqB-V1@TV~>Xc2DkD2EhxF zbDk&ODP8$ZrAD;wXDkx^?{9Q_+U`nSQ*W_D42 zY_Yz3$_F(DJG>e2a6Ca@-wnCph0xo{8Ow5inGd5xAlOtuNZPYj zDGSahwun2tv#crz%c4j9T(IJ>=Iu}n2x2qB=-M*3MnlLWUQ7rj4BRz9D5w(#E_6$} z^355=xDEyF=iK8kfMfs{GxdBj1@7we-Egiuj&fW~N~F+&(G20i7%6@79L8R}DZ-;7 z0KxRC8|$e~L*1egMHuj(1_i=SQCSEkg&lHu)Nr&B7_W(8({N(TKruLcjk>hR=1~~( zMd~b-X}hyGW5((N7mDE@te9Bzk|UfvpJUW+Z604v>8_@G4;j^cY4Ax$$|~b4{+C3{i)g&Mc9kW zDuIc6Q}zqVWUSM3v$^)U9{&_&j} zXg#z5|7K?N7h7ulq;Mk7&H9zf+tO5)tmFNo3kRf{3+rDqQ~>#Xd&WF%p0};Pc0>D( z->U7S=kt*2o6X1j=1z|=`Qs$;{;=l{Z^lmZZPNWS1J{4R0Ih7nfBO1cs^PbEorU6c zSrt)DphwaAr1&ut*Z>w__4K*P^swtZf$V9Gwx3Nfy-KYE!h&N;Cx3@zw3gdBwZN`T z(B0G}DVK;yQ@^jVSY)BVgZ)8GD>gjGS>`1t zF=U=y8{PL0;73y6&!s~4NRo52OjtK#S@i|x+bZoyCW+fEt6+5DQ7&Y%##f(-bToq! zTlppPb;b5s8zq}=Z{;;aDQvx5@j@S>E*A{Ob6XR+moA<+JUeu+#P-S$2!;G|U zL<<&dv~$18Q(JD!8YpD>FnO#SRC_XQh?zI>tTEjH#E6ts%VWv-e5EnX=8~*0rrdtF zfF94_(WHE#E#ZGFOPBPqf8qmB?~{oI^F<`p*PHN$*WytcjLBp1R=b=z61JLqri46S z6Y@^**3Cs+dicJ)pm^68g9h*UAX>)n9-@l=E{0X-rqzaNyi5o@Gx%?(8PhjZ$EdOS(44Ac4o%7Mw}eaw8V@#cO%Jn?v@V~X;`7$ z0i|~RSnkP=el>@+QJbSTtcHfjFp#9{1F;`tuJfcUJgu&OYbkKbgym_c&_utw66<1m)weSLKhXu|0bWei&7ky7$%*fgV@lus-b;T z=AwEh==C@7`!Df|55>TbXByr(t-5cbE-um2+Yic&)L=mvzmM*0+}>}=V+OM7fc0ZB zv%}9xG9;;{F|Pac1<2DYvVfgx!Ni1nc3Qq0o$IVnAb0TJ{mNdzU9k6~fzo90kyrM9 zF+7Z0U8nvc3f;3Ubf7_^Fb1K*FX6c{)y+Mo92r&FTnIWp9U(;hBO4d^@=Dd7n&Hbo z&;8%pKqRoVftkyg8EChsmb70oVCPu*$<(au4OhSv<9B{>TvDl;Sq9Uq6(KZNsId=a zJhf@wd%3Vb%x2O@X^t)#bBsoE*;p%wC%RtSA6#@e6VkO(-RE$p;@8FNz!3YUKmfPyJ0k=Umx z3s@!ck^r7EkS_Fv^;IGhmz}*pkCDjLn{r~~4$u6!_>fxK?1e}dSZUknqEmYO~9^n&Pik0swsMQ>`FyuOM6xQb0fhqTj53u1)Jf#v6poI*_v z)kBrVs#AsZ+n53R&=eD3%M42jL(K3smAgQKaRn4=i?2BGv3ABgs zWUw|k?>cJiQWnKi>|if}4)=$+5`YJM=O!j^zbQ1!i>wnMi2!Y|rptYF$fp`7A_PiT zyZDycojs)?!~!!xAdlLAfb{cn5w6=YWNmt{0Y-inqeeO^EVkh06qGc{vv@`w7w?M- zD)5J!PR6Z$AKiCKq_I{KpHn`+Gxy-D2~CMq?8hyh*K%e>QPIK8Gg5@qoa`4N3r<(h zBxf5|8w$oRfw+~@h{(mSYdzF8BrT8FRaqQ_$F}GH%5>Advv_Fhp`IVef{|`CI`bpnSzyNa@12Y&Gv^5@0ePJopHN?mrxf;hXa3hjXM8c= zOuu%!P~l;yBGj%bmGAm#=P{yN56-SQILe-@%AJf_XP;TZ^`gGFlQ?e3 zKPBG-`Wy;Jyq(v@jyAlGZz!KT4K$GaGw-R6xs*xvca^Oh5O2&<8@eQy9lhJ%TASkL z#A*9D%;SINkit35T4}=VR14WP-nN3<3LOO_PW`8NYv0q2o}K!dy`R0o7*qwevW|>O z)1Ku?-1vaU_MRbLoKD}o!}I+O4evxq2+RKhY_SK$oK3NjI1)5Vd%sPvj1?ta88^;9 z_0M`cG24E5_d$!q8VK%;xtUqihetA05QER4nk(}reYVc+R^fRJyHpsjdu*V{S${f= z_C$u`EViGup(NEYFEGhIdx7>O%jDXL`8(=QO^E;-29-kbJ5W z8I($k6Dy@L!1Z}g}pn&$b8s>@>&E7JEIW8xabo)mE5$%m* zBo38vqe~Jr==eHYCn!M_w<%BqEBV49+UCsvJG8A zEjJpME;>!tom4E^R5B{G^LKnDVcY7Og=P3g+=@r1`1D8D`h{9RpYyrA>0?ZDOB176 zYDNg&WOZt&&Ps@`=%?d6{$6$EzbR?X1-mrO*&sS1bMRJ6^2I&s!9lWlJ>a=22_El| zE*;^G18SHGm>x&Ur7~{&_jehx3=?Z{iiFv4?ps?SsThaA`G#`Xlv>(M%G`dYInuTm zVP-3s+$!1p*Ti+BIfbd~FJAHsu^mhpB8k3D=6#osZ5+&rfH3X9NWT#z%_^dF)(P3kxZ_!tzl`x|F~N3!)3rUW{&#M6_rjI?+3$GrEWp5{zD= z_s%(A&s}$a*=zrSXYKWjakuk+ln0(Vsw*oP_*w2{k$yDj%R+Y?jPC9iziIg@+8qj_ zG=M>taP4BqW+}%CdT}(Qb9yHMi+Z+%+xc#wZqBt5D~3mQpdxPX7Xwa<{%QMmKo6{ZAAlBM;ch4R zr{fE{c-8L{|5F6qk=~+TKMNCRPh1?v4-zP={(+QILZaM#>v3y`!h|wSlU?(N@K)x1 zLmog6!yVu~=|$I`Ky=u`^Qbv$bWA{#Ty~p#=%tSQA9o!e!Ms*woO~!bId{h(32_EJ z8vc7_iWa!HW~ZJ4M@#4UTOMU{qp4g^;6Ix^l`ox`{CS`kMV z8d&E>-`Y$Ug&hJpncQG&Eba5hyMlt|-*@nJ7y$FQJ&WVI&nFPq^brT=q1!f{gU{na z-uge!A#D}JC}(5X5%U$eeN(#biAHA{dL)3hk#6(i^j}(aqG^zfTSsIB$=yD=N@nHK z@H`Xh2{`$wxa1qW1SKRGaB7m7N7NvoE7g(oN4*1U%sIkz{DzpW#Q>LCH+slEe2_02 z074+D?l-tHW%@Q3mm2bm-OX@XkMFjxLiZFuCOk%hMa*U5Qc`w~dOx>%_!>Mv#O?V> zCB?`ew-UYUaOf)QZ1?XaYoIiopb#MgZTw0biq?Cm=8P`DpVd zWawIV%3jsyC{|}~8?uw)5@PTNf{7^sb{ichN>MF~xKkx61`Z1C0xF6I5gs|Ebv!~B zYy>;ajN)N@2!l!=pQJ>xuuJ*ayKXkC5~}-7@L=Iv^>AATOZs4}CmsFWt}XM)kKc1n zzGrNFM;zUM#^XeD5gQ*|Ez|uVJ-iyf0ua-XP>bJxBXNMvK{z-6D0S@(#T}Ue-`lhX z-6!MhKw1V95gCiA$8nyq1loY|ty)uFM@|NJ1GzL5vp@dB*?qy_C=k0#Wmd0e6VrJ? zyLPB$noT1GrU90kGcbLoQfurM9+I-9w+x&}Au`%wXYG~xf8eO<_6torpA0OA84g{~ z7(txy_><+p*EyCDa+Eo%Gncs7~C z-~RBAv3rJ&O>WAkoRCY!zF4O0dj76oOOLYROI~s8ws4rgFu+qB)VKx8gK+m9&13#n z+6t3y2$V9w9_f(~r4|^kKvni6O1sM9h||W8=9DjLiFS2jhq{?B4{w9S9ey-+GecVV z49e?Y$tEcVh#fSBy|KhDYbPV)c#k@Lj?EebE!%te)I|)Q;2PQ(lHVdngpsZ`WY7xg zmn88%*0}0WSij`eoLm7Qpa(-Vl{pWB@N>GiDX#yRYtM&77quUp8%$&1J-#rSNtvWj zHT|1;eNWH_dQ2%CF;;>%I~cvde@B}e^2`@ynh0AK>;L=0S2X68Xj>7UHmiw}j4reH zgdWWI5TL~L7E3L5;5{-tM0!&&<&IM5V3|VxE6mZ#5{%I(haUwj9b9l+4 z2i{ble$cLk_YCr{&8U_mw?sI00B;eaGX8y`|G{E5_CS)_J2*cdi-nkS+xEwOi6R3S zAy+>GlhRZVNSe~>L2U3{~Jo$sN;VGh}+S#&gW85;04ZjOqx9K^uOQs z=seFaO6(Sj7QGp_gYcG1m?g5jU+8?%8>cpiW6Rhs0i=wj8El!P;kS0g&4|&4zp4dq zC){0?t0ZWO#?@&yZoMjKO~TE9`wC2!NMSkad~{BD!A?m=NnmJypLc#qA=1;{4i+`2 zH8;bKdTjp(lrGKgL~8$lE4&qg@`DOEC5OuEQ-H`LV_eoGik05C(V);%)HF6wR*UKN zfIzVsU>b}I{jA-4u;ycc2;H$lHyQq7^wTgzjC_;)QXlX!J#fWq zf5lT7%20);N5?K~Nk~C9^W0FY_JeR5d9K;S(X_#qB6mzkRnW4@kO*@PVJJa{Xk1qP zGTN(*QXZ6&--W=9&5Ih^4}5M6Ok@v7ajnDw;MN3GIP&JK6;h#ur_>cfG;jkJ{u19N zNjnk}w8h3S_UD}hbtRF$V_#bI2lM#y+Rk(~lqn>9&Tp~mJo;FmanRaG%j81S%@OPl zsV)0@qJ+Ca2R9*_bO(2PG>)r<^YD2B>jv^9Ug{9I z%NrZz!exI|ojJ`b`#GLpKqZN1Aivm$(X#O-P3pf^u!tc|UCKA>$igwE(aB0~eX%ri0w!#}&k$r!D9`IQA(hrAp4f#ocU=!_sJbEy zEd-<&?=I41$TKN)xNO*9TJ8EJ0*2fFx2nyF{N9HE?kTv+Q^x*->`D7Y9p3zsdZAsH zsDi%;izKLfHXzwv9%qedj0`x}>lPPcMXv1=GK_v``0cmrvN@M?!p?k--`h$Qk$d3F8XSWj}TI8+_~SkM57^E`K#i( ztc+(o+JZwfwY%O7Cbo*#dDP1Rv6iB){kE}+zXZ?E&Q8G2e{GGM{x&2PD>xg&6@H|4 zGe_sXk(eEFkmW<=<^x+;-4u?JtP@>E1N*m+l*ESR$MR)Et+}Q|-jsKOd9YO*NE)gc zDuWmrr({Dj;(m?NDJXL18*Jmb2xxF~wS+*J4dIN=`d(uvQPU{cZEvw`7-ek3sW!Lu z4-upC4?ZH#gDzFe&D>eD4_!3g(Upb@;Pm3(=5ukz@-Q8&z5t9SB3HTiG!=d$5Odaw zu6D0WMw*qxbbE`b6cxvRAbZfJWY03AIHB$*BcIcM8T>^+^GSzAP|fbzKIc8jPr`>;;}YD|Y>MI}KbMKp%ef&A6>t=%#MCb@!^2 ztEXI`j2~`}lZ$lF9krt?{C(yN79&@eRLQK`%Bsg^sWl!thC@_V(W8Djb$`*ACTh8{ z51HGqZ`-ps#ZFQ756(%$8AsSOJD#{nc~eY`%ia?q-Y<@pj>70S(Xa}`mDlyg8c}l%8on+K+NHC$`g6P2* zh`+kNo{S7CJ!x`7Le44CuhAV%2Q&hWMD{D}{8gVVD^T~CP8 zMD()r)()!Y#=NuYay&3cJRGr=mJt-Xlt|ha8X$>*VZyj6B>C4t{(hlpAgwBKr1yY& zNKFtdhabJ`pLz{{KHXUqOeHuMxceR9p~KIH?qNI-MFT${QirN}14k6@K=LFxGj)u3tv`Bm*%82Y^`6Q|MKa*q zhq2K-{<^(m+agAk5#I_4E|g}cV)IgC#_v&1nM|;fwySXDi*V0>mH0=*LrJt|Y`1L7 zsqrLj&Gs(t)HcHD97$9yvU9AXkZ%o=0IAuFI5F&bq=DkBn|Bfjdo8rk^$z7dnXkYL z3nA02Y0neh5!>5B#Q-BK=1|Y0WtMd;4+D%PLwt%nJ>tB;8f5c@t3_iETj|h7&uGD2 z$w>iUNl0ioT4jPh_9-}~rfebduHv+XqrSiLsF>J3;!pRX@Tu5<^%D0>d0D0Pm#Q(a zb+;G7zmC($H=h|un1j{u+_GPCn1osJqfD!9HqKBVaahXy8UXq;mwpV)ccvbJP*%X| zMixyi@Y8tp#c4mguZ=I*ctSgZKb;`-5Xp=6Lrxz>!8ob+)Fgn;!_d-syM}t(Ui!Y4 zSW;2JLXxf(OD-g>F$98@H_w13^9#hh3fxL1E#tzSCU%G4ar%YtJ=p=#ju47mx)neL z8{k8PmP&K0fSSV76WxX?8Hz2B!ZzGBh)y}2jf)Pk_`~cqmrV4JqD~(FU&v;XCNXzB zBvrzYrE+5xwYRyvLYv%!{-GjmTfb;q%5m*4#g|(zG80n*3vaH&unhP#OKSCT^?w+O zT95QbL)CU(CbsW z9wS0aYS>#l4rIUoQLT#&yOYVbf-`^4&VL0%!^DMCext&!+^8$1>JIK+n_6hy3wsGy51PsY zPgu?1Ogx1)lBnB_$)mv+A7U#npA{yqouH-qYiM-ww$umJ&Bx*=o!pmoJdyi6gzy5~ zvOn_S72La{Chj2-K5XCjO#kFmhOCtXFqQcrWT9;#J5O-5_K*;{>c&q5xcpMA>0vqG zeo`Jk%9>N49UaE+-*{Y#8(uo_j@rNzQ1%^hbM=q$uj5_DPf21ciV|tA9-Ztwko5tcW5WfQe9KIy@ ztwO;+NSsRtusw~J2G!;LUx~eW*+OjJHS32bd+vjy_tag81Ex!w2V%uI>r1R)TsukF zO$9G}keGgHPci4pqDwy9I__JA3Z9gj3?zH1+jQ9rm{z!{y52CY&`fx!X{erenSNjT z74e4EJ)0}f+sy$1V_s=}FY20oN$h%@?DRM6U@rih#(Yrm1WWcE0bk3zBOCVq z7=`-r{Hl%nW|H4nFqVCm+SwgbC!9Pp8QPrYG)@@O4B;GT+sTy;eG5GH5foW4=sznd zbKjYQ1@%tyayqZ-2 z6c%L!t1e3>Lxh3l0Ey@>Q39~OOB#)S)>B07G&Rd0JA87X?k#jumPqySLXh{Hxr#te zP{4(rftbjfqP$6LbE0Tf5yjU%H5bMe0Rlg}w6uy``4HN!_-HJXJn&r|PUq=@3= po5KI~ng8G7{y)xh;*w(N0o1(W4ruyeyFL1E)KzqpD-^B6{sVGw$pioZ diff --git a/tests/TestFiles/Skia/Media/GlyphRun/Should_Render_GlyphRun_Geometry.expected.png b/tests/TestFiles/Skia/Media/GlyphRun/Should_Render_GlyphRun_Geometry.expected.png index 5bd64a6ac79e49c34a25802415074442dd911e9b..a8f3aa927782575db62b7a370e9656a504898c29 100644 GIT binary patch delta 4215 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 delta 4361 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