diff --git a/src/Avalonia.Base/Media/DrawingContext.cs b/src/Avalonia.Base/Media/DrawingContext.cs index 18d6968168..c38f4d656d 100644 --- a/src/Avalonia.Base/Media/DrawingContext.cs +++ b/src/Avalonia.Base/Media/DrawingContext.cs @@ -4,7 +4,6 @@ using Avalonia.Platform; using Avalonia.Rendering.SceneGraph; using Avalonia.Threading; using Avalonia.Utilities; -using Avalonia.Media.Imaging; namespace Avalonia.Media { @@ -53,12 +52,10 @@ namespace Avalonia.Media /// The image. /// The rect in the image to draw. /// The rect in the output to draw to. - /// The bitmap interpolation mode. - public virtual void DrawImage(IImage source, Rect sourceRect, Rect destRect, - BitmapInterpolationMode bitmapInterpolationMode = default) + public virtual void DrawImage(IImage source, Rect sourceRect, Rect destRect) { _ = source ?? throw new ArgumentNullException(nameof(source)); - source.Draw(this, sourceRect, destRect, bitmapInterpolationMode); + source.Draw(this, sourceRect, destRect); } /// @@ -68,8 +65,7 @@ namespace Avalonia.Media /// The opacity to draw with. /// The rect in the image to draw. /// The rect in the output to draw to. - /// The bitmap interpolation mode. - internal abstract void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default); + internal abstract void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect); /// /// Draws a line. @@ -286,8 +282,7 @@ namespace Avalonia.Media Opacity, Clip, GeometryClip, - OpacityMask, - BitmapBlendMode + OpacityMask } public RestoreState(DrawingContext context, PushedStateType type) @@ -312,8 +307,6 @@ namespace Avalonia.Media _context.PopGeometryClipCore(); else if (_type == PushedStateType.OpacityMask) _context.PopOpacityMaskCore(); - else if (_type == PushedStateType.BitmapBlendMode) - _context.PopBitmapBlendModeCore(); } } @@ -394,16 +387,6 @@ namespace Avalonia.Media } protected abstract void PushOpacityMaskCore(IBrush mask, Rect bounds); - public PushedState PushBitmapBlendMode(BitmapBlendingMode blendingMode) - { - PushBitmapBlendMode(blendingMode); - _states ??= StateStackPool.Get(); - _states.Push(new RestoreState(this, RestoreState.PushedStateType.BitmapBlendMode)); - return new PushedState(this); - } - - protected abstract void PushBitmapBlendModeCore(BitmapBlendingMode blendingMode); - /// /// Pushes a matrix transformation. /// @@ -431,7 +414,6 @@ namespace Avalonia.Media protected abstract void PopGeometryClipCore(); protected abstract void PopOpacityCore(); protected abstract void PopOpacityMaskCore(); - protected abstract void PopBitmapBlendModeCore(); protected abstract void PopTransformCore(); private static bool PenIsVisible(IPen? pen) diff --git a/src/Avalonia.Base/Media/DrawingGroup.cs b/src/Avalonia.Base/Media/DrawingGroup.cs index c96d2aad57..5a5bd50c7c 100644 --- a/src/Avalonia.Base/Media/DrawingGroup.cs +++ b/src/Avalonia.Base/Media/DrawingGroup.cs @@ -196,13 +196,7 @@ namespace Avalonia.Media throw new NotImplementedException(); } - protected override void PushBitmapBlendModeCore(BitmapBlendingMode blendingMode) - { - throw new NotImplementedException(); - } - - internal override void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect, - BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default) + internal override void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect) { throw new NotImplementedException(); } @@ -321,8 +315,6 @@ namespace Avalonia.Media protected override void PopOpacityMaskCore() => Pop(); - protected override void PopBitmapBlendModeCore() => Pop(); - protected override void PopTransformCore() => Pop(); /// diff --git a/src/Avalonia.Base/Media/DrawingImage.cs b/src/Avalonia.Base/Media/DrawingImage.cs index 52fbd87db7..c83e8eb6ee 100644 --- a/src/Avalonia.Base/Media/DrawingImage.cs +++ b/src/Avalonia.Base/Media/DrawingImage.cs @@ -1,6 +1,5 @@ using System; using Avalonia.Metadata; -using Avalonia.Media.Imaging; namespace Avalonia.Media { @@ -43,8 +42,7 @@ namespace Avalonia.Media void IImage.Draw( DrawingContext context, Rect sourceRect, - Rect destRect, - BitmapInterpolationMode bitmapInterpolationMode) + Rect destRect) { var drawing = Drawing; diff --git a/src/Avalonia.Base/Media/EdgeMode.cs b/src/Avalonia.Base/Media/EdgeMode.cs new file mode 100644 index 0000000000..f50a2f7164 --- /dev/null +++ b/src/Avalonia.Base/Media/EdgeMode.cs @@ -0,0 +1,10 @@ +namespace Avalonia.Media +{ + public enum EdgeMode : byte + { + Unspecified, + + Antialias, + Aliased + } +} diff --git a/src/Avalonia.Base/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs index d795cca894..20e0f96ff7 100644 --- a/src/Avalonia.Base/Media/GlyphRun.cs +++ b/src/Avalonia.Base/Media/GlyphRun.cs @@ -153,7 +153,7 @@ namespace Avalonia.Media /// /// Gets the conservative bounding box of the . /// - public Rect Bounds => PlatformImpl.Item.Bounds; + public Rect Bounds => new Rect(new Size(Metrics.WidthIncludingTrailingWhitespace, Metrics.Height)); /// /// @@ -166,7 +166,7 @@ namespace Avalonia.Media /// public Point BaselineOrigin { - get => PlatformImpl.Item.BaselineOrigin; + get => _baselineOrigin ?? new Point(0, Metrics.Baseline); set => Set(ref _baselineOrigin, value); } @@ -676,13 +676,17 @@ namespace Avalonia.Media } } - return new GlyphRunMetrics( - width, - trailingWhitespaceLength, - newLineLength, - firstCluster, - lastCluster - ); + return new GlyphRunMetrics + { + Baseline = -GlyphTypeface.Metrics.Ascent * Scale, + Width = width, + WidthIncludingTrailingWhitespace = widthIncludingTrailingWhitespace, + Height = height, + NewLineLength = newLineLength, + TrailingWhitespaceLength = trailingWhitespaceLength, + FirstCluster = firstCluster, + LastCluster = lastCluster + }; } private int GetTrailingWhitespaceLength(bool isReversed, out int newLineLength, out int glyphCount) @@ -820,10 +824,11 @@ namespace Avalonia.Media private IRef CreateGlyphRunImpl() { var platformImpl = s_renderInterface.CreateGlyphRun( - GlyphTypeface, - FontRenderingEmSize, - GlyphInfos, - _baselineOrigin ?? new Point(0, -GlyphTypeface.Metrics.Ascent * Scale)); + GlyphTypeface, + FontRenderingEmSize, + GlyphInfos, + BaselineOrigin, + Bounds); _platformImpl = RefCountable.Create(platformImpl); @@ -835,5 +840,16 @@ namespace Avalonia.Media _platformImpl?.Dispose(); _platformImpl = null; } + + /// + /// Gets the intersections of specified upper and lower limit. + /// + /// Upper limit. + /// Lower limit. + /// + public IReadOnlyList GetIntersections(float lowerLimit, float upperLimit) + { + return PlatformImpl.Item.GetIntersections(lowerLimit, upperLimit); + } } } diff --git a/src/Avalonia.Base/Media/GlyphRunMetrics.cs b/src/Avalonia.Base/Media/GlyphRunMetrics.cs index 09b183d044..9ca1d5ea12 100644 --- a/src/Avalonia.Base/Media/GlyphRunMetrics.cs +++ b/src/Avalonia.Base/Media/GlyphRunMetrics.cs @@ -2,23 +2,20 @@ { public readonly record struct GlyphRunMetrics { - public GlyphRunMetrics(double width, int trailingWhitespaceLength, int newLineLength, int firstCluster, int lastCluster) - { - Width = width; - TrailingWhitespaceLength = trailingWhitespaceLength; - NewLineLength = newLineLength; - FirstCluster = firstCluster; - LastCluster = lastCluster; - } + public double Baseline { get; init; } - public double Width { get; } + public double Width { get; init; } - public int TrailingWhitespaceLength { get; } + public double WidthIncludingTrailingWhitespace { get; init; } - public int NewLineLength { get; } + public double Height { get; init; } - public int FirstCluster { get; } + public int TrailingWhitespaceLength { get; init; } - public int LastCluster { get; } + public int NewLineLength { get; init; } + + public int FirstCluster { get; init; } + + public int LastCluster { get; init; } } } diff --git a/src/Avalonia.Base/Media/IImage.cs b/src/Avalonia.Base/Media/IImage.cs index cbe25b7b58..4e0b952b88 100644 --- a/src/Avalonia.Base/Media/IImage.cs +++ b/src/Avalonia.Base/Media/IImage.cs @@ -18,11 +18,9 @@ namespace Avalonia.Media /// The drawing context. /// The rect in the image to draw. /// The rect in the output to draw to. - /// The bitmap interpolation mode. void Draw( DrawingContext context, Rect sourceRect, - Rect destRect, - BitmapInterpolationMode bitmapInterpolationMode); + Rect destRect); } } diff --git a/src/Avalonia.Base/Media/ITileBrush.cs b/src/Avalonia.Base/Media/ITileBrush.cs index cb5a591003..586f6053a1 100644 --- a/src/Avalonia.Base/Media/ITileBrush.cs +++ b/src/Avalonia.Base/Media/ITileBrush.cs @@ -39,13 +39,5 @@ namespace Avalonia.Media /// Gets the brush's tile mode. /// TileMode TileMode { get; } - - /// - /// Gets the bitmap interpolation mode. - /// - /// - /// The bitmap interpolation mode. - /// - BitmapInterpolationMode BitmapInterpolationMode { get; } } } diff --git a/src/Avalonia.Base/Media/Imaging/Bitmap.cs b/src/Avalonia.Base/Media/Imaging/Bitmap.cs index c4720d772e..07bb3db100 100644 --- a/src/Avalonia.Base/Media/Imaging/Bitmap.cs +++ b/src/Avalonia.Base/Media/Imaging/Bitmap.cs @@ -224,15 +224,13 @@ namespace Avalonia.Media.Imaging void IImage.Draw( DrawingContext context, Rect sourceRect, - Rect destRect, - BitmapInterpolationMode bitmapInterpolationMode) + Rect destRect) { context.DrawBitmap( PlatformImpl, 1, sourceRect, - destRect, - bitmapInterpolationMode); + destRect); } private static IPlatformRenderInterface GetFactory() diff --git a/src/Avalonia.Base/Media/Imaging/BitmapBlendingMode.cs b/src/Avalonia.Base/Media/Imaging/BitmapBlendingMode.cs index eb39020939..73a3f7b269 100644 --- a/src/Avalonia.Base/Media/Imaging/BitmapBlendingMode.cs +++ b/src/Avalonia.Base/Media/Imaging/BitmapBlendingMode.cs @@ -3,8 +3,10 @@ namespace Avalonia.Media.Imaging /// /// Controls the way the bitmaps are drawn together. /// - public enum BitmapBlendingMode + public enum BitmapBlendingMode : byte { + Unspecified, + /// /// Source is placed over the destination. /// @@ -52,6 +54,6 @@ namespace Avalonia.Media.Imaging /// /// Display the sum of the source image and destination image. /// - Plus, + Plus } } diff --git a/src/Avalonia.Base/Media/Imaging/BitmapInterpolationMode.cs b/src/Avalonia.Base/Media/Imaging/BitmapInterpolationMode.cs index 7cdb5d8b9f..eaa64892a4 100644 --- a/src/Avalonia.Base/Media/Imaging/BitmapInterpolationMode.cs +++ b/src/Avalonia.Base/Media/Imaging/BitmapInterpolationMode.cs @@ -3,12 +3,14 @@ /// /// Controls the performance and quality of bitmap scaling. /// - public enum BitmapInterpolationMode + public enum BitmapInterpolationMode : byte { + Unspecified, + /// - /// Uses the default behavior of the underling render backend. + /// Disable interpolation. /// - Default, + None, /// /// The best performance but worst image quality. @@ -18,7 +20,7 @@ /// /// Good performance and decent image quality. /// - MediumQuality, + MediumQuality, /// /// Highest quality but worst performance. diff --git a/src/Avalonia.Base/Media/Imaging/CroppedBitmap.cs b/src/Avalonia.Base/Media/Imaging/CroppedBitmap.cs index 8cdf5b592a..93556679e9 100644 --- a/src/Avalonia.Base/Media/Imaging/CroppedBitmap.cs +++ b/src/Avalonia.Base/Media/Imaging/CroppedBitmap.cs @@ -83,12 +83,12 @@ namespace Avalonia.Media.Imaging } } - public void Draw(DrawingContext context, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode) + public void Draw(DrawingContext context, Rect sourceRect, Rect destRect) { if (Source is not IBitmap bmp) return; var topLeft = SourceRect.TopLeft.ToPointWithDpi(bmp.Dpi); - Source.Draw(context, sourceRect.Translate(new Vector(topLeft.X, topLeft.Y)), destRect, bitmapInterpolationMode); + Source.Draw(context, sourceRect.Translate(new Vector(topLeft.X, topLeft.Y)), destRect); } } } diff --git a/src/Avalonia.Base/Media/ImmediateDrawingContext.cs b/src/Avalonia.Base/Media/ImmediateDrawingContext.cs index 58b153482d..7ddccc80e7 100644 --- a/src/Avalonia.Base/Media/ImmediateDrawingContext.cs +++ b/src/Avalonia.Base/Media/ImmediateDrawingContext.cs @@ -80,11 +80,10 @@ namespace Avalonia.Media /// The bitmap. /// The rect in the image to draw. /// The rect in the output to draw to. - /// The bitmap interpolation mode. - public void DrawBitmap(IBitmap source, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = default) + public void DrawBitmap(IBitmap source, Rect sourceRect, Rect destRect) { _ = source ?? throw new ArgumentNullException(nameof(source)); - PlatformImpl.DrawBitmap(source.PlatformImpl, 1, sourceRect, destRect, bitmapInterpolationMode); + PlatformImpl.DrawBitmap(source.PlatformImpl, 1, sourceRect, destRect); } /// diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs index 668a907fdf..175038ba75 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs @@ -22,7 +22,6 @@ namespace Avalonia.Media.Immutable /// How the source rectangle will be stretched to fill the destination rect. /// /// The tile mode. - /// The bitmap interpolation mode. public ImmutableImageBrush( IBitmap? source, AlignmentX alignmentX = AlignmentX.Center, @@ -33,8 +32,7 @@ namespace Avalonia.Media.Immutable RelativePoint transformOrigin = default, RelativeRect? sourceRect = null, Stretch stretch = Stretch.Uniform, - TileMode tileMode = TileMode.None, - BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default) + TileMode tileMode = TileMode.None) : base( alignmentX, alignmentY, @@ -44,8 +42,7 @@ namespace Avalonia.Media.Immutable transformOrigin, sourceRect ?? RelativeRect.Fill, stretch, - tileMode, - bitmapInterpolationMode) + tileMode) { Source = source; } diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs index 1ee52365e0..7e139af516 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs @@ -21,7 +21,6 @@ namespace Avalonia.Media.Immutable /// How the source rectangle will be stretched to fill the destination rect. /// /// The tile mode. - /// The bitmap interpolation mode. protected ImmutableTileBrush( AlignmentX alignmentX, AlignmentY alignmentY, @@ -31,8 +30,7 @@ namespace Avalonia.Media.Immutable RelativePoint transformOrigin, RelativeRect sourceRect, Stretch stretch, - TileMode tileMode, - BitmapInterpolationMode bitmapInterpolationMode) + TileMode tileMode) { AlignmentX = alignmentX; AlignmentY = alignmentY; @@ -43,7 +41,6 @@ namespace Avalonia.Media.Immutable SourceRect = sourceRect; Stretch = stretch; TileMode = tileMode; - BitmapInterpolationMode = bitmapInterpolationMode; } /// @@ -60,8 +57,7 @@ namespace Avalonia.Media.Immutable source.TransformOrigin, source.SourceRect, source.Stretch, - source.TileMode, - source.BitmapInterpolationMode) + source.TileMode) { } @@ -95,8 +91,5 @@ namespace Avalonia.Media.Immutable /// public TileMode TileMode { get; } - - /// - public BitmapInterpolationMode BitmapInterpolationMode { get; } } } diff --git a/src/Avalonia.Base/Media/PlatformDrawingContext.cs b/src/Avalonia.Base/Media/PlatformDrawingContext.cs index eb8a93722c..63cbb3069b 100644 --- a/src/Avalonia.Base/Media/PlatformDrawingContext.cs +++ b/src/Avalonia.Base/Media/PlatformDrawingContext.cs @@ -25,6 +25,12 @@ internal sealed class PlatformDrawingContext : DrawingContext, IDrawingContextWi _ownsImpl = ownsImpl; } + public RenderOptions RenderOptions + { + get => _impl.RenderOptions; + set => _impl.RenderOptions = value; + } + protected override void DrawLineCore(IPen pen, Point p1, Point p2) => _impl.DrawLine(pen, p1, p2); @@ -37,9 +43,8 @@ internal sealed class PlatformDrawingContext : DrawingContext, IDrawingContextWi protected override void DrawEllipseCore(IBrush? brush, IPen? pen, Rect rect) => _impl.DrawEllipse(brush, pen, rect); - internal override void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect, - BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default) => - _impl.DrawBitmap(source, opacity, sourceRect, destRect, bitmapInterpolationMode); + internal override void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect) => + _impl.DrawBitmap(source, opacity, sourceRect, destRect); public override void Custom(ICustomDrawOperation custom) => custom.Render(_impl); @@ -65,9 +70,6 @@ internal sealed class PlatformDrawingContext : DrawingContext, IDrawingContextWi protected override void PushOpacityMaskCore(IBrush mask, Rect bounds) => _impl.PushOpacityMask(mask, bounds); - protected override void PushBitmapBlendModeCore(BitmapBlendingMode blendingMode) => - _impl.PushBitmapBlendMode(blendingMode); - protected override void PushTransformCore(Matrix matrix) { _transforms ??= TransformStackPool.Get(); @@ -84,8 +86,6 @@ internal sealed class PlatformDrawingContext : DrawingContext, IDrawingContextWi protected override void PopOpacityMaskCore() => _impl.PopOpacityMask(); - protected override void PopBitmapBlendModeCore() => _impl.PopBitmapBlendMode(); - protected override void PopTransformCore() => _impl.Transform = (_transforms ?? throw new ObjectDisposedException(nameof(PlatformDrawingContext))).Pop(); diff --git a/src/Avalonia.Base/Media/RenderOptions.cs b/src/Avalonia.Base/Media/RenderOptions.cs index 5863d0ac58..639498543b 100644 --- a/src/Avalonia.Base/Media/RenderOptions.cs +++ b/src/Avalonia.Base/Media/RenderOptions.cs @@ -1,36 +1,131 @@ using Avalonia.Media.Imaging; namespace Avalonia.Media -{ - public class RenderOptions +{ + public readonly record struct RenderOptions { + public BitmapInterpolationMode BitmapInterpolationMode { get; init; } + public EdgeMode EdgeMode { get; init; } + public TextRenderingMode TextRenderingMode { get; init; } + public BitmapBlendingMode BitmapBlendingMode { get; init; } + + /// + /// Gets the value of the BitmapInterpolationMode attached property for a visual. + /// + /// The control. + /// The control's left coordinate. + public static BitmapInterpolationMode GetBitmapInterpolationMode(Visual visual) + { + return visual.RenderOptions.BitmapInterpolationMode; + } + /// - /// Defines the property. + /// Sets the value of the BitmapInterpolationMode attached property for a visual. /// - public static readonly StyledProperty BitmapInterpolationModeProperty = - AvaloniaProperty.RegisterAttached( - "BitmapInterpolationMode", - BitmapInterpolationMode.MediumQuality, - inherits: true); + /// The control. + /// The left value. + public static void SetBitmapInterpolationMode(Visual visual, BitmapInterpolationMode value) + { + visual.RenderOptions = visual.RenderOptions with { BitmapInterpolationMode = value }; + } /// - /// Gets the value of the BitmapInterpolationMode attached property for a control. + /// Gets the value of the BitmapBlendingMode attached property for a visual. /// - /// The control. + /// The control. /// The control's left coordinate. - public static BitmapInterpolationMode GetBitmapInterpolationMode(AvaloniaObject element) + public static BitmapBlendingMode GetBitmapBlendingMode(Visual visual) { - return element.GetValue(BitmapInterpolationModeProperty); + return visual.RenderOptions.BitmapBlendingMode; } /// - /// Sets the value of the BitmapInterpolationMode attached property for a control. + /// Sets the value of the BitmapBlendingMode attached property for a visual. /// - /// The control. + /// The control. /// The left value. - public static void SetBitmapInterpolationMode(AvaloniaObject element, BitmapInterpolationMode value) + public static void SetBitmapBlendingMode(Visual visual, BitmapBlendingMode value) { - element.SetValue(BitmapInterpolationModeProperty, value); + visual.RenderOptions = visual.RenderOptions with { BitmapBlendingMode = value }; + } + + /// + /// Gets the value of the EdgeMode attached property for a visual. + /// + /// The control. + /// The control's left coordinate. + public static EdgeMode GetEdgeMode(Visual visual) + { + return visual.RenderOptions.EdgeMode; + } + + /// + /// Sets the value of the EdgeMode attached property for a visual. + /// + /// The control. + /// The left value. + public static void SetEdgeMode(Visual visual, EdgeMode value) + { + visual.RenderOptions = visual.RenderOptions with { EdgeMode = value }; + } + + /// + /// Gets the value of the TextRenderingMode attached property for a visual. + /// + /// The control. + /// The control's left coordinate. + public static TextRenderingMode GetTextRenderingMode(Visual visual) + { + return visual.RenderOptions.TextRenderingMode; + } + + /// + /// Sets the value of the TextRenderingMode attached property for a visual. + /// + /// The control. + /// The left value. + public static void SetTextRenderingMode(Visual visual, TextRenderingMode value) + { + visual.RenderOptions = visual.RenderOptions with { TextRenderingMode = value }; + } + + public RenderOptions MergeWith(RenderOptions other) + { + var bitmapInterpolationMode = BitmapInterpolationMode; + + if (bitmapInterpolationMode == BitmapInterpolationMode.Unspecified) + { + bitmapInterpolationMode = other.BitmapInterpolationMode; + } + + var edgeMode = EdgeMode; + + if (edgeMode == EdgeMode.Unspecified) + { + edgeMode = other.EdgeMode; + } + + var textRenderingMode = TextRenderingMode; + + if (textRenderingMode == TextRenderingMode.Unspecified) + { + textRenderingMode = other.TextRenderingMode; + } + + var bitmapBlendingMode = BitmapBlendingMode; + + if (bitmapBlendingMode == BitmapBlendingMode.Unspecified) + { + bitmapBlendingMode = other.BitmapBlendingMode; + } + + return new RenderOptions + { + BitmapInterpolationMode = bitmapInterpolationMode, + EdgeMode = edgeMode, + TextRenderingMode = textRenderingMode, + BitmapBlendingMode = bitmapBlendingMode + }; } } } diff --git a/src/Avalonia.Base/Media/TextDecoration.cs b/src/Avalonia.Base/Media/TextDecoration.cs index e89a7d8826..8661959aa6 100644 --- a/src/Avalonia.Base/Media/TextDecoration.cs +++ b/src/Avalonia.Base/Media/TextDecoration.cs @@ -1,10 +1,6 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using Avalonia.Collections; -using Avalonia.Collections.Pooled; using Avalonia.Media.TextFormatting; -using Avalonia.Platform; -using Avalonia.Utilities; namespace Avalonia.Media { @@ -218,7 +214,7 @@ namespace Avalonia.Media { var offsetY = glyphRun.BaselineOrigin.Y - origin.Y; - var intersections = glyphRun.PlatformImpl.Item.GetIntersections((float)(thickness * 0.5d - offsetY), (float)(thickness * 1.5d - offsetY)); + var intersections = glyphRun.GetIntersections((float)(thickness * 0.5d - offsetY), (float)(thickness * 1.5d - offsetY)); if (intersections.Count > 0) { diff --git a/src/Avalonia.Base/Media/TextRenderingMode.cs b/src/Avalonia.Base/Media/TextRenderingMode.cs new file mode 100644 index 0000000000..927d2bce73 --- /dev/null +++ b/src/Avalonia.Base/Media/TextRenderingMode.cs @@ -0,0 +1,11 @@ +namespace Avalonia.Media +{ + public enum TextRenderingMode : byte + { + Unspecified, + + SubpixelAntialias, + Antialias, + Alias + } +} diff --git a/src/Avalonia.Base/Media/TileBrush.cs b/src/Avalonia.Base/Media/TileBrush.cs index ab1ee2d604..d7b818a174 100644 --- a/src/Avalonia.Base/Media/TileBrush.cs +++ b/src/Avalonia.Base/Media/TileBrush.cs @@ -83,7 +83,6 @@ namespace Avalonia.Media SourceRectProperty, StretchProperty, TileModeProperty); - RenderOptions.BitmapInterpolationModeProperty.OverrideDefaultValue(BitmapInterpolationMode.Default); } /// @@ -140,17 +139,5 @@ namespace Avalonia.Media get { return (TileMode)GetValue(TileModeProperty); } set { SetValue(TileModeProperty, value); } } - - /// - /// Gets or sets the bitmap interpolation mode. - /// - /// - /// The bitmap interpolation mode. - /// - public BitmapInterpolationMode BitmapInterpolationMode - { - get { return RenderOptions.GetBitmapInterpolationMode(this); } - set { RenderOptions.SetBitmapInterpolationMode(this, value); } - } } } diff --git a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs index 1359ad6603..cb28ff8d47 100644 --- a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs +++ b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs @@ -2,8 +2,8 @@ using System; using Avalonia.Media; using Avalonia.Rendering.SceneGraph; using Avalonia.Utilities; -using Avalonia.Media.Imaging; using Avalonia.Metadata; +using Avalonia.Media.Imaging; namespace Avalonia.Platform { @@ -13,6 +13,11 @@ namespace Avalonia.Platform [Unstable] public interface IDrawingContextImpl : IDisposable { + /// + /// Gets or sets the current render options used to control the rendering behavior of drawing operations. + /// + RenderOptions RenderOptions { get; set; } + /// /// Gets or sets the current transform of the drawing context. /// @@ -31,8 +36,7 @@ namespace Avalonia.Platform /// The opacity to draw with. /// The rect in the image to draw. /// The rect in the output to draw to. - /// The bitmap interpolation mode. - void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default); + void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect); /// /// Draws a bitmap image. @@ -157,17 +161,6 @@ namespace Avalonia.Platform /// void PopGeometryClip(); - /// - /// Pushes a bitmap blending value. - /// - /// The bitmap blending mode. - void PushBitmapBlendMode(BitmapBlendingMode blendingMode); - - /// - /// Pops the latest pushed bitmap blending value. - /// - void PopBitmapBlendMode(); - /// /// Adds a custom draw operation /// diff --git a/src/Avalonia.Base/Platform/IGlyphRunBuffer.cs b/src/Avalonia.Base/Platform/IGlyphRunBuffer.cs deleted file mode 100644 index c1fc7a5967..0000000000 --- a/src/Avalonia.Base/Platform/IGlyphRunBuffer.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Drawing; - -namespace Avalonia.Platform -{ - public interface IGlyphRunBuffer - { - Span GlyphIndices { get; } - - IGlyphRunImpl Build(); - } - - public interface IHorizontalGlyphRunBuffer : IGlyphRunBuffer - { - Span GlyphPositions { get; } - } - - public interface IPositionedGlyphRunBuffer : IGlyphRunBuffer - { - Span GlyphPositions { get; } - } -} diff --git a/src/Avalonia.Base/Platform/IGlyphRunImpl.cs b/src/Avalonia.Base/Platform/IGlyphRunImpl.cs index fccea27c43..2342f32307 100644 --- a/src/Avalonia.Base/Platform/IGlyphRunImpl.cs +++ b/src/Avalonia.Base/Platform/IGlyphRunImpl.cs @@ -1,25 +1,36 @@ using System; using System.Collections.Generic; +using Avalonia.Media; using Avalonia.Metadata; namespace Avalonia.Platform { /// - /// Actual implementation of a glyph run that stores platform dependent resources. + /// An immutable platform representation of a . /// [Unstable] - public interface IGlyphRunImpl : IDisposable + public interface IGlyphRunImpl : IDisposable { /// - /// Gets the conservative bounding box of the glyph run./>. + /// Gets the for the . /// - Rect Bounds { get; } + IGlyphTypeface GlyphTypeface { get; } + + /// + /// Gets the em size used for rendering the . + /// + double FontRenderingEmSize { get; } /// /// Gets the baseline origin of the glyph run./>. /// Point BaselineOrigin { get; } + /// + /// Gets the conservative bounding box of the glyph run./>. + /// + Rect Bounds { get; } + /// /// Gets the intersections of specified upper and lower limit. /// diff --git a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs index 81fe2c046f..c37233d52c 100644 --- a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs +++ b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs @@ -169,8 +169,9 @@ namespace Avalonia.Platform /// The font rendering em size. /// The list of glyphs. /// The baseline origin of the run. Can be null. + /// the conservative bounding box of the run /// An . - IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos, Point baselineOrigin); + IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos, Point baselineOrigin, Rect bounds); /// /// Creates a backend-specific object using a low-level API graphics context diff --git a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs index 814ecdba29..1bf52729a0 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs @@ -260,6 +260,8 @@ public class CompositingRenderer : IRendererWithCompositor if (!comp.Effect.EffectEquals(visual.Effect)) comp.Effect = visual.Effect?.ToImmutable(); + comp.RenderOptions = visual.RenderOptions; + var renderTransform = Matrix.Identity; if (visual.HasMirrorTransform) @@ -272,8 +274,6 @@ public class CompositingRenderer : IRendererWithCompositor renderTransform *= (-offset) * visual.RenderTransform.Value * (offset); } - - comp.TransformMatrix = MatrixUtils.ToMatrix4x4(renderTransform); _recorder.BeginUpdate(comp.DrawList); diff --git a/src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs b/src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs index f81cc5a1a0..ec419e6313 100644 --- a/src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs +++ b/src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs @@ -78,15 +78,14 @@ internal sealed class CompositionDrawingContext : DrawingContext, IDrawingContex } } - internal override void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect, - BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default) + internal override void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect) { var next = NextDrawAs(); if (next == null || - !next.Item.Equals(Transform, source, opacity, sourceRect, destRect, bitmapInterpolationMode)) + !next.Item.Equals(Transform, source, opacity, sourceRect, destRect)) { - Add(new ImageNode(Transform, source, opacity, sourceRect, destRect, bitmapInterpolationMode)); + Add(new ImageNode(Transform, source, opacity, sourceRect, destRect)); } else { @@ -227,20 +226,6 @@ internal sealed class CompositionDrawingContext : DrawingContext, IDrawingContex } } - protected override void PopBitmapBlendModeCore() - { - var next = NextDrawAs(); - - if (next == null || !next.Item.Equals(null)) - { - Add(new BitmapBlendModeNode()); - } - else - { - ++_drawOperationIndex; - } - } - protected override void PopOpacityCore() { var next = NextDrawAs(); @@ -354,21 +339,6 @@ internal sealed class CompositionDrawingContext : DrawingContext, IDrawingContex _needsToPopOpacityMask.Push(needsToPop); } - /// - protected override void PushBitmapBlendModeCore(BitmapBlendingMode blendingMode) - { - var next = NextDrawAs(); - - if (next == null || !next.Item.Equals(blendingMode)) - { - Add(new BitmapBlendModeNode(blendingMode)); - } - else - { - ++_drawOperationIndex; - } - } - private void Add(T node) where T : class, IDrawOperation { if (_drawOperationIndex < _builder.Count) diff --git a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs index 1ec1362a4c..cdce741020 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs @@ -42,15 +42,20 @@ internal class CompositorDrawingContextProxy : IDrawingContextImpl, set => _impl.Transform = (_transform = value) * PostTransform; } + public RenderOptions RenderOptions + { + get => _impl.RenderOptions; + set => _impl.RenderOptions = value; + } + public void Clear(Color color) { _impl.Clear(color); } - public void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect, - BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default) + public void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect) { - _impl.DrawBitmap(source, opacity, sourceRect, destRect, bitmapInterpolationMode); + _impl.DrawBitmap(source, opacity, sourceRect, destRect); } public void DrawBitmap(IRef source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect) @@ -133,16 +138,6 @@ internal class CompositorDrawingContextProxy : IDrawingContextImpl, _impl.PopGeometryClip(); } - public void PushBitmapBlendMode(BitmapBlendingMode blendingMode) - { - _impl.PushBitmapBlendMode(blendingMode); - } - - public void PopBitmapBlendMode() - { - _impl.PopBitmapBlendMode(); - } - public void Custom(ICustomDrawOperation custom) { _impl.Custom(custom); diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs index 3e88b9e77b..45275bdfe1 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs @@ -181,7 +181,7 @@ namespace Avalonia.Rendering.Composition.Server else targetContext.DrawBitmap(RefCountable.CreateUnownedNotClonable(_layer), 1, new Rect(_layerSize), - new Rect(Size), BitmapInterpolationMode.LowQuality); + new Rect(Size)); if (DebugOverlays != RendererDebugOverlays.None) { diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs index 6e7ef85183..853b90be5e 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs @@ -66,6 +66,8 @@ namespace Avalonia.Rendering.Composition.Server if (OpacityMaskBrush != null) canvas.PushOpacityMask(OpacityMaskBrush, boundsRect); + canvas.RenderOptions = RenderOptions; + RenderCore(canvas, currentTransformedClip); // Hack to force invalidation of SKMatrix @@ -122,6 +124,11 @@ namespace Avalonia.Rendering.Composition.Server var wasVisible = IsVisibleInFrame; + if(Parent != null) + { + RenderOptions = RenderOptions.MergeWith(Parent.RenderOptions); + } + // Calculate new parent-relative transform if (_combinedTransformDirty) { diff --git a/src/Avalonia.Base/Rendering/ImmediateRenderer.cs b/src/Avalonia.Base/Rendering/ImmediateRenderer.cs index 9b7d358b1d..4e264f6951 100644 --- a/src/Avalonia.Base/Rendering/ImmediateRenderer.cs +++ b/src/Avalonia.Base/Rendering/ImmediateRenderer.cs @@ -97,7 +97,18 @@ namespace Avalonia.Rendering using (visual.OpacityMask != null ? context.PushOpacityMask(visual.OpacityMask, bounds) : default) using (context.PushTransform(Matrix.Identity)) { - visual.Render(context); + var currentRenderOptions = default(RenderOptions); + + var platformContext = context as PlatformDrawingContext; + + if(platformContext != null) + { + currentRenderOptions = platformContext.RenderOptions; + + platformContext.RenderOptions = visual.RenderOptions.MergeWith(platformContext.RenderOptions); + } + + visual.Render(context); var childrenEnumerable = visual.HasNonUniformZIndexChildren ? visual.VisualChildren.OrderBy(x => x, ZIndexComparer.Instance) @@ -115,6 +126,11 @@ namespace Avalonia.Rendering Render(context, child, childClipRect); } } + + if(platformContext != null) + { + platformContext.RenderOptions = currentRenderOptions; + } } } } diff --git a/src/Avalonia.Base/Rendering/SceneGraph/BitmapBlendModeNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/BitmapBlendModeNode.cs deleted file mode 100644 index b1190a159b..0000000000 --- a/src/Avalonia.Base/Rendering/SceneGraph/BitmapBlendModeNode.cs +++ /dev/null @@ -1,68 +0,0 @@ -using Avalonia.Platform; -using Avalonia.Media.Imaging; - -namespace Avalonia.Rendering.SceneGraph -{ - /// - /// A node in the scene graph which represents an bitmap blending mode push or pop. - /// - internal class BitmapBlendModeNode : IDrawOperation - { - /// - /// Initializes a new instance of the class that represents an - /// push. - /// - /// The to push. - public BitmapBlendModeNode(BitmapBlendingMode bitmapBlend) - { - BlendingMode = bitmapBlend; - } - - /// - /// Initializes a new instance of the class that represents an - /// pop. - /// - public BitmapBlendModeNode() - { - } - - /// - public Rect Bounds => default; - - /// - /// Gets the BitmapBlend to be pushed or null if the operation represents a pop. - /// - public BitmapBlendingMode? BlendingMode { get; } - - /// - public bool HitTest(Point p) => false; - - /// - /// Determines if this draw operation equals another. - /// - /// the how to compare - /// True if the draw operations are the same, otherwise false. - /// - /// The properties of the other draw operation are passed in as arguments to prevent - /// allocation of a not-yet-constructed draw operation object. - /// - public bool Equals(BitmapBlendingMode? blendingMode) => BlendingMode == blendingMode; - - /// - public void Render(IDrawingContextImpl context) - { - if (BlendingMode.HasValue) - { - context.PushBitmapBlendMode(BlendingMode.Value); - } - else - { - context.PopBitmapBlendMode(); - } - } - - public void Dispose() - { - } - } -} diff --git a/src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs index 1c4e63b34a..764c5c65f9 100644 --- a/src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs +++ b/src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs @@ -1,5 +1,4 @@ -using System; -using Avalonia.Media; +using Avalonia.Media; using Avalonia.Platform; using Avalonia.Utilities; @@ -18,7 +17,7 @@ namespace Avalonia.Rendering.SceneGraph /// The glyph run to draw. public GlyphRunNode( Matrix transform, - IImmutableBrush foreground, + IImmutableBrush? foreground, IRef glyphRun) : base(glyphRun.Item.Bounds, transform, foreground) { diff --git a/src/Avalonia.Base/Rendering/SceneGraph/ImageNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/ImageNode.cs index ac946cc8b2..caf0eee175 100644 --- a/src/Avalonia.Base/Rendering/SceneGraph/ImageNode.cs +++ b/src/Avalonia.Base/Rendering/SceneGraph/ImageNode.cs @@ -1,6 +1,5 @@ using Avalonia.Platform; using Avalonia.Utilities; -using Avalonia.Media.Imaging; namespace Avalonia.Rendering.SceneGraph { @@ -17,15 +16,13 @@ namespace Avalonia.Rendering.SceneGraph /// The draw opacity. /// The source rect. /// The destination rect. - /// The bitmap interpolation mode. - public ImageNode(Matrix transform, IRef source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode) + public ImageNode(Matrix transform, IRef source, double opacity, Rect sourceRect, Rect destRect) : base(destRect, transform) { Source = source.Clone(); Opacity = opacity; SourceRect = sourceRect; DestRect = destRect; - BitmapInterpolationMode = bitmapInterpolationMode; SourceVersion = Source.Item.Version; } @@ -53,14 +50,6 @@ namespace Avalonia.Rendering.SceneGraph /// Gets the destination rect. /// public Rect DestRect { get; } - - /// - /// Gets the bitmap interpolation mode. - /// - /// - /// The scaling mode. - /// - public BitmapInterpolationMode BitmapInterpolationMode { get; } /// /// Determines if this draw operation equals another. @@ -70,27 +59,25 @@ namespace Avalonia.Rendering.SceneGraph /// The opacity of the other draw operation. /// The source rect of the other draw operation. /// The dest rect of the other draw operation. - /// The bitmap interpolation mode. /// True if the draw operations are the same, otherwise false. /// /// The properties of the other draw operation are passed in as arguments to prevent /// allocation of a not-yet-constructed draw operation object. /// - public bool Equals(Matrix transform, IRef source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode) + public bool Equals(Matrix transform, IRef source, double opacity, Rect sourceRect, Rect destRect) { return transform == Transform && Equals(source.Item, Source.Item) && source.Item.Version == SourceVersion && opacity == Opacity && sourceRect == SourceRect && - destRect == DestRect && - bitmapInterpolationMode == BitmapInterpolationMode; + destRect == DestRect; } /// public override void Render(IDrawingContextImpl context) { - context.DrawBitmap(Source, Opacity, SourceRect, DestRect, BitmapInterpolationMode); + context.DrawBitmap(Source, Opacity, SourceRect, DestRect); } /// diff --git a/src/Avalonia.Base/Visual.cs b/src/Avalonia.Base/Visual.cs index 79cc760fc6..e626dc5249 100644 --- a/src/Avalonia.Base/Visual.cs +++ b/src/Avalonia.Base/Visual.cs @@ -318,7 +318,9 @@ namespace Avalonia internal CompositionDrawListVisual? CompositionVisual { get; private set; } internal CompositionVisual? ChildCompositionVisual { get; set; } - + + internal RenderOptions RenderOptions { get; set; } + public bool HasNonUniformZIndexChildren { get; private set; } /// diff --git a/src/Avalonia.Base/composition-schema.xml b/src/Avalonia.Base/composition-schema.xml index 91d718dfd8..a24c249eed 100644 --- a/src/Avalonia.Base/composition-schema.xml +++ b/src/Avalonia.Base/composition-schema.xml @@ -30,6 +30,7 @@ + diff --git a/src/Avalonia.Controls/Image.cs b/src/Avalonia.Controls/Image.cs index b14cc78e60..7996b982eb 100644 --- a/src/Avalonia.Controls/Image.cs +++ b/src/Avalonia.Controls/Image.cs @@ -91,9 +91,7 @@ namespace Avalonia.Controls Rect sourceRect = new Rect(sourceSize) .CenterRect(new Rect(destRect.Size / scale)); - var interpolationMode = RenderOptions.GetBitmapInterpolationMode(this); - - context.DrawImage(source, sourceRect, destRect, interpolationMode); + context.DrawImage(source, sourceRect, destRect); } } diff --git a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs index 431989134a..f2d86a2e9d 100644 --- a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs +++ b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; using System.IO; -using System.Numerics; using System.Runtime.InteropServices; using Avalonia.Media; using Avalonia.Platform; -using Avalonia.Rendering; using Avalonia.Rendering.SceneGraph; using Avalonia.Utilities; using Avalonia.Media.Imaging; @@ -125,7 +123,8 @@ namespace Avalonia.Headless IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos, - Point baselineOrigin) + Point baselineOrigin, + Rect bounds) { return new HeadlessGlyphRunStub(); } @@ -136,6 +135,10 @@ namespace Avalonia.Headless public Point BaselineOrigin => new Point(0, 8); + public IGlyphTypeface GlyphTypeface => new HeadlessGlyphTypefaceImpl(); + + public double FontRenderingEmSize => 12; + public void Dispose() { } @@ -372,6 +375,8 @@ namespace Avalonia.Headless public Matrix Transform { get; set; } + public RenderOptions RenderOptions { get; set; } + public void Clear(Color color) { @@ -454,7 +459,7 @@ namespace Avalonia.Headless { } - public void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default) + public void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect) { } diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index f48d45f961..03288ca109 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -5,12 +5,10 @@ using System.Linq; using System.Threading; using Avalonia.Media; using Avalonia.Platform; -using Avalonia.Rendering; using Avalonia.Rendering.SceneGraph; using Avalonia.Rendering.Utilities; using Avalonia.Utilities; using Avalonia.Media.Imaging; -using Avalonia.Skia.Helpers; using SkiaSharp; using ISceneBrush = Avalonia.Media.ISceneBrush; @@ -27,10 +25,8 @@ namespace Avalonia.Skia private readonly Vector _dpi; private readonly Stack _maskStack = new(); private readonly Stack _opacityStack = new(); - private readonly Stack _blendingModeStack = new(); private readonly Matrix? _postTransform; private double _currentOpacity = 1.0f; - private BitmapBlendingMode _currentBlendingMode = BitmapBlendingMode.SourceOver; private readonly bool _canTextUseLcdRendering; private Matrix _currentTransform; private bool _disposed; @@ -171,6 +167,8 @@ namespace Avalonia.Skia public SKCanvas Canvas { get; } public SKSurface? Surface { get; } + public RenderOptions RenderOptions { get; set; } + private void CheckLease() { if (_leased) @@ -185,7 +183,7 @@ namespace Avalonia.Skia } /// - public void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode) + public void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect) { CheckLease(); var drawableImage = (IDrawableBitmapImpl)source.Item; @@ -194,8 +192,8 @@ namespace Avalonia.Skia var paint = SKPaintCache.Shared.Get(); paint.Color = new SKColor(255, 255, 255, (byte)(255 * opacity * (_useOpacitySaveLayer ? 1 : _currentOpacity))); - paint.FilterQuality = bitmapInterpolationMode.ToSKFilterQuality(); - paint.BlendMode = _currentBlendingMode.ToSKBlendMode(); + paint.FilterQuality = RenderOptions.BitmapInterpolationMode.ToSKFilterQuality(); + paint.BlendMode = RenderOptions.BitmapBlendingMode.ToSKBlendMode(); drawableImage.Draw(this, s, d, paint); SKPaintCache.Shared.ReturnReset(paint); @@ -206,7 +204,7 @@ namespace Avalonia.Skia { CheckLease(); PushOpacityMask(opacityMask, opacityMaskRect); - DrawBitmap(source, 1, new Rect(0, 0, source.Item.PixelSize.Width, source.Item.PixelSize.Height), destRect, BitmapInterpolationMode.Default); + DrawBitmap(source, 1, new Rect(0, 0, source.Item.PixelSize.Width, source.Item.PixelSize.Height), destRect); PopOpacityMask(); } @@ -522,7 +520,9 @@ namespace Avalonia.Skia { var glyphRunImpl = (GlyphRunImpl)glyphRun.Item; - Canvas.DrawText(glyphRunImpl.TextBlob, (float)glyphRun.Item.BaselineOrigin.X, + var textBlob = glyphRunImpl.GetTextBlob(RenderOptions); + + Canvas.DrawText(textBlob, (float)glyphRun.Item.BaselineOrigin.X, (float)glyphRun.Item.BaselineOrigin.Y, paintWrapper.Paint); } } @@ -652,21 +652,6 @@ namespace Avalonia.Skia Canvas.Restore(); } - /// - public void PushBitmapBlendMode(BitmapBlendingMode blendingMode) - { - CheckLease(); - _blendingModeStack.Push(_currentBlendingMode); - _currentBlendingMode = blendingMode; - } - - /// - public void PopBitmapBlendMode() - { - CheckLease(); - _currentBlendingMode = _blendingModeStack.Pop(); - } - public void Custom(ICustomDrawOperation custom) { CheckLease(); @@ -914,12 +899,14 @@ namespace Avalonia.Skia context.Clear(Colors.Transparent); context.PushClip(calc.IntermediateClip); context.Transform = calc.IntermediateTransform; + context.RenderOptions = RenderOptions; + context.DrawBitmap( RefCountable.CreateUnownedNotClonable(tileBrushImage), 1, sourceRect, - targetRect, - tileBrush.BitmapInterpolationMode); + targetRect); + context.PopClip(); } @@ -1152,7 +1139,7 @@ namespace Avalonia.Skia { var paintWrapper = new PaintWrapper(paint); - paint.IsAntialias = true; + paint.IsAntialias = RenderOptions.EdgeMode != EdgeMode.Aliased; double opacity = brush.Opacity * (_useOpacitySaveLayer ? 1 :_currentOpacity); diff --git a/src/Skia/Avalonia.Skia/GlyphRunImpl.cs b/src/Skia/Avalonia.Skia/GlyphRunImpl.cs index 0521e238f3..7331740b56 100644 --- a/src/Skia/Avalonia.Skia/GlyphRunImpl.cs +++ b/src/Skia/Avalonia.Skia/GlyphRunImpl.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using Avalonia.Media; +using Avalonia.Media.TextFormatting; using Avalonia.Platform; using SkiaSharp; @@ -7,30 +9,122 @@ namespace Avalonia.Skia { internal class GlyphRunImpl : IGlyphRunImpl { - public GlyphRunImpl(SKTextBlob textBlob, Size size, Point baselineOrigin) + private readonly GlyphTypefaceImpl _glyphTypefaceImpl; + private readonly ushort[] _glyphIndices; + private readonly SKPoint[] _glyphPositions; + + private readonly Dictionary _textBlobCache = new(1); + + public GlyphRunImpl(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, + IReadOnlyList glyphInfos, Point baselineOrigin, Rect bounds) { - TextBlob = textBlob ?? throw new ArgumentNullException(nameof(textBlob)); + if (glyphTypeface == null) + { + throw new ArgumentNullException(nameof(glyphTypeface)); + } + + _glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface; + + if (glyphInfos == null) + { + throw new ArgumentNullException(nameof(glyphInfos)); + } + + var count = glyphInfos.Count; + _glyphIndices = new ushort[count]; + _glyphPositions = new SKPoint[count]; + + var currentX = 0.0; + + for (int i = 0; i < count; i++) + { + var glyphInfo = glyphInfos[i]; + var offset = glyphInfo.GlyphOffset; + + _glyphIndices[i] = glyphInfo.GlyphIndex; - Bounds = new Rect(new Point(baselineOrigin.X, 0), size); + _glyphPositions[i] = new SKPoint((float)(currentX + offset.X), (float)offset.Y); + currentX += glyphInfos[i].GlyphAdvance; + } + + FontRenderingEmSize = fontRenderingEmSize; BaselineOrigin = baselineOrigin; + Bounds = bounds; } - /// - /// Gets the text blob to draw. - /// - public SKTextBlob TextBlob { get; } + public IGlyphTypeface GlyphTypeface => _glyphTypefaceImpl; - public Rect Bounds { get; } + public double FontRenderingEmSize { get; } public Point BaselineOrigin { get; } - public IReadOnlyList GetIntersections(float upperBound, float lowerBound) => - TextBlob.GetIntercepts(lowerBound, upperBound); + public Rect Bounds { get; } + + public SKTextBlob GetTextBlob(RenderOptions renderOptions) + { + var edging = SKFontEdging.SubpixelAntialias; + + switch (renderOptions.TextRenderingMode) + { + case TextRenderingMode.Alias: + edging = SKFontEdging.Alias; + break; + case TextRenderingMode.Antialias: + edging = SKFontEdging.Antialias; + break; + case TextRenderingMode.Unspecified: + edging = renderOptions.EdgeMode == EdgeMode.Aliased ? SKFontEdging.Alias : SKFontEdging.SubpixelAntialias; + break; + } + + if (_textBlobCache.TryGetValue(edging, out var textBlob)) + { + return textBlob; + } - void IDisposable.Dispose() + var font = SKFontCache.Shared.Get(); + + font.LinearMetrics = true; + font.Subpixel = edging == SKFontEdging.SubpixelAntialias; + font.Edging = edging; + font.Hinting = SKFontHinting.Full; + font.Size = (float)FontRenderingEmSize; + font.Typeface = _glyphTypefaceImpl.Typeface; + font.Embolden = (_glyphTypefaceImpl.FontSimulations & FontSimulations.Bold) != 0; + font.SkewX = (_glyphTypefaceImpl.FontSimulations & FontSimulations.Oblique) != 0 ? -0.2f : 0; + + var builder = SKTextBlobBuilderCache.Shared.Get(); + + var runBuffer = builder.AllocatePositionedRun(font, _glyphIndices.Length); + + runBuffer.SetPositions(_glyphPositions); + runBuffer.SetGlyphs(_glyphIndices); + + SKFontCache.Shared.Return(font); + + textBlob = builder.Build(); + + SKTextBlobBuilderCache.Shared.Return(builder); + + _textBlobCache.Add(edging, textBlob); + + return textBlob; + } + + public void Dispose() { - TextBlob.Dispose(); + foreach (var pair in _textBlobCache) + { + pair.Value.Dispose(); + } + } + + public IReadOnlyList GetIntersections(float lowerLimit, float upperLimit) + { + var textBlob = GetTextBlob(default); + + return textBlob.GetIntercepts(lowerLimit, upperLimit); } } } diff --git a/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs b/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs index 797c565ca1..ce292eb826 100644 --- a/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs @@ -1,5 +1,4 @@ using Avalonia.Platform; -using Avalonia.Rendering; namespace Avalonia.Skia { diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index a9a79ff0c5..b486f8ad42 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -5,8 +5,8 @@ using Avalonia.Media; using Avalonia.OpenGL; using Avalonia.Platform; using Avalonia.Media.Imaging; -using Avalonia.Media.TextFormatting; using SkiaSharp; +using Avalonia.Media.TextFormatting; namespace Avalonia.Skia { @@ -209,67 +209,10 @@ namespace Avalonia.Skia return new WriteableBitmapImpl(size, dpi, format, alphaFormat); } - public IGlyphRunImpl CreateGlyphRun( - IGlyphTypeface glyphTypeface, - double fontRenderingEmSize, - IReadOnlyList glyphInfos, - Point baselineOrigin) + public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, + IReadOnlyList glyphInfos, Point baselineOrigin, Rect bounds) { - if (glyphTypeface == null) - { - throw new ArgumentNullException(nameof(glyphTypeface)); - } - - if (glyphInfos == null) - { - throw new ArgumentNullException(nameof(glyphInfos)); - } - - var glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface; - - var font = SKFontCache.Shared.Get(); - - font.LinearMetrics = true; - font.Subpixel = true; - font.Edging = SKFontEdging.SubpixelAntialias; - font.Hinting = SKFontHinting.Full; - font.Size = (float)fontRenderingEmSize; - font.Typeface = glyphTypefaceImpl.Typeface; - font.Embolden = (glyphTypefaceImpl.FontSimulations & FontSimulations.Bold) != 0; - font.SkewX = (glyphTypefaceImpl.FontSimulations & FontSimulations.Oblique) != 0 ? -0.2f : 0; - - - var builder = SKTextBlobBuilderCache.Shared.Get(); - var count = glyphInfos.Count; - - var runBuffer = builder.AllocatePositionedRun(font, count); - - var glyphSpan = runBuffer.GetGlyphSpan(); - var positionSpan = runBuffer.GetPositionSpan(); - - SKFontCache.Shared.Return(font); - - var width = 0.0; - - for (int i = 0; i < count; i++) - { - var glyphInfo = glyphInfos[i]; - var offset = glyphInfo.GlyphOffset; - - glyphSpan[i] = glyphInfo.GlyphIndex; - - positionSpan[i] = new SKPoint((float)(width + offset.X), (float)offset.Y); - - width += glyphInfo.GlyphAdvance; - } - - var scale = fontRenderingEmSize / glyphTypeface.Metrics.DesignEmHeight; - var height = glyphTypeface.Metrics.LineSpacing * scale; - var skTextBlob = builder.Build(); - - SKTextBlobBuilderCache.Shared.Return(builder); - - return new GlyphRunImpl(skTextBlob, new Size(width, height), baselineOrigin); + return new GlyphRunImpl(glyphTypeface, fontRenderingEmSize, glyphInfos, baselineOrigin, bounds); } } } diff --git a/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs b/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs index c66b53284a..a1fdfdbd2e 100644 --- a/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs +++ b/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs @@ -19,7 +19,8 @@ namespace Avalonia.Skia return SKFilterQuality.Medium; case BitmapInterpolationMode.HighQuality: return SKFilterQuality.High; - case BitmapInterpolationMode.Default: + case BitmapInterpolationMode.None: + case BitmapInterpolationMode.Unspecified: return SKFilterQuality.None; default: throw new ArgumentOutOfRangeException(nameof(interpolationMode), interpolationMode, null); @@ -30,6 +31,7 @@ namespace Avalonia.Skia { switch (blendingMode) { + case BitmapBlendingMode.Unspecified: case BitmapBlendingMode.SourceOver: return SKBlendMode.SrcOver; case BitmapBlendingMode.Source: diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs index 826296b055..fc1a358492 100644 --- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs +++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs @@ -160,59 +160,9 @@ namespace Avalonia.Direct2D1 public IStreamGeometryImpl CreateStreamGeometry() => new StreamGeometryImpl(); public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) => new GeometryGroupImpl(fillRule, children); public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, IGeometryImpl g1, IGeometryImpl g2) => new CombinedGeometryImpl(combineMode, g1, g2); - - public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, - IReadOnlyList glyphInfos, Point baselineOrigin) + public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos, Point baselineOrigin, Rect bounds) { - var glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface; - - var glyphCount = glyphInfos.Count; - - var run = new SharpDX.DirectWrite.GlyphRun - { - FontFace = glyphTypefaceImpl.FontFace, - FontSize = (float)fontRenderingEmSize - }; - - var indices = new short[glyphCount]; - - for (var i = 0; i < glyphCount; i++) - { - indices[i] = (short)glyphInfos[i].GlyphIndex; - } - - run.Indices = indices; - - run.Advances = new float[glyphCount]; - - var width = 0.0; - - for (var i = 0; i < glyphCount; i++) - { - var advance = glyphInfos[i].GlyphAdvance; - - width += advance; - - run.Advances[i] = (float)advance; - } - - run.Offsets = new GlyphOffset[glyphCount]; - - for (var i = 0; i < glyphCount; i++) - { - var (x, y) = glyphInfos[i].GlyphOffset; - - run.Offsets[i] = new GlyphOffset - { - AdvanceOffset = (float)x, - AscenderOffset = (float)y - }; - } - - var scale = fontRenderingEmSize / glyphTypeface.Metrics.DesignEmHeight; - var height = glyphTypeface.Metrics.LineSpacing * scale; - - return new GlyphRunImpl(run, new Size(width, height), baselineOrigin); + return new GlyphRunImpl(glyphTypeface, fontRenderingEmSize, glyphInfos, baselineOrigin, bounds); } class D2DApi : IPlatformRenderInterfaceContext diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs index 318b0fe9ae..8968c891e6 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Numerics; using Avalonia.Media; using Avalonia.Platform; -using Avalonia.Rendering; using Avalonia.Rendering.SceneGraph; using Avalonia.Utilities; using Avalonia.Media.Imaging; @@ -76,6 +75,16 @@ namespace Avalonia.Direct2D1.Media set => throw new NotSupportedException(); } + public RenderOptions RenderOptions + { + get => _renderOptions; + set + { + _renderOptions = value; + ApplyRenderOptions(value); + } + } + /// public void Clear(Color color) { @@ -119,15 +128,14 @@ namespace Avalonia.Direct2D1.Media /// The opacity to draw with. /// The rect in the image to draw. /// The rect in the output to draw to. - /// The bitmap interpolation mode. - public void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode) + public void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect) { using (var d2d = ((BitmapImpl)source.Item).GetDirect2DBitmap(_deviceContext)) { - var interpolationMode = GetInterpolationMode(bitmapInterpolationMode); - + var interpolationMode = GetInterpolationMode(RenderOptions.BitmapInterpolationMode); + // TODO: How to implement CompositeMode here? - + _deviceContext.DrawBitmap( d2d.Value, destRect.ToSharpDX(), @@ -143,13 +151,14 @@ namespace Avalonia.Direct2D1.Media switch (interpolationMode) { case BitmapInterpolationMode.LowQuality: - return InterpolationMode.NearestNeighbor; - case BitmapInterpolationMode.MediumQuality: return InterpolationMode.Linear; + case BitmapInterpolationMode.MediumQuality: + return InterpolationMode.MultiSampleLinear; case BitmapInterpolationMode.HighQuality: return InterpolationMode.HighQualityCubic; - case BitmapInterpolationMode.Default: - return InterpolationMode.Linear; + case BitmapInterpolationMode.None: + case BitmapInterpolationMode.Unspecified: + return InterpolationMode.NearestNeighbor; default: throw new ArgumentOutOfRangeException(nameof(interpolationMode), interpolationMode, null); } @@ -158,15 +167,16 @@ namespace Avalonia.Direct2D1.Media public static CompositeMode GetCompositeMode(BitmapBlendingMode blendingMode) { switch (blendingMode) - { + { case BitmapBlendingMode.SourceIn: return CompositeMode.SourceIn; case BitmapBlendingMode.SourceOut: return CompositeMode.SourceOut; + case BitmapBlendingMode.Unspecified: case BitmapBlendingMode.SourceOver: return CompositeMode.SourceOver; case BitmapBlendingMode.SourceAtop: - return CompositeMode.SourceAtop; + return CompositeMode.SourceAtop; case BitmapBlendingMode.DestinationIn: return CompositeMode.DestinationIn; case BitmapBlendingMode.DestinationOut: @@ -193,8 +203,10 @@ namespace Avalonia.Direct2D1.Media /// The rect in the output to draw to. public void DrawBitmap(IRef source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect) { + var interpolationMode = GetInterpolationMode(RenderOptions.BitmapInterpolationMode); + using (var d2dSource = ((BitmapImpl)source.Item).GetDirect2DBitmap(_deviceContext)) - using (var sourceBrush = new BitmapBrush(_deviceContext, d2dSource.Value)) + using (var sourceBrush = new BitmapBrush1(_deviceContext, d2dSource.Value, new BitmapBrushProperties1 { InterpolationMode = interpolationMode })) using (var d2dOpacityMask = CreateBrush(opacityMask, opacityMaskRect.Size)) using (var geometry = new SharpDX.Direct2D1.RectangleGeometry(Direct2D1Platform.Direct2D1Factory, destRect.ToDirect2D())) { @@ -388,9 +400,11 @@ namespace Avalonia.Direct2D1.Media { using (var brush = CreateBrush(foreground, glyphRun.Item.Bounds.Size)) { - var glyphRunImpl = (GlyphRunImpl)glyphRun.Item; + var immutableGlyphRun = (GlyphRunImpl)glyphRun.Item; - _renderTarget.DrawGlyphRun(glyphRun.Item.BaselineOrigin.ToSharpDX(), glyphRunImpl.GlyphRun, + var dxGlyphRun = immutableGlyphRun.GlyphRun; + + _renderTarget.DrawGlyphRun(glyphRun.Item.BaselineOrigin.ToSharpDX(), dxGlyphRun, brush.PlatformBrush, MeasuringMode.Natural); } } @@ -433,6 +447,8 @@ namespace Avalonia.Direct2D1.Media readonly Stack _layers = new Stack(); private readonly Stack _layerPool = new Stack(); + private RenderOptions _renderOptions; + /// /// Pushes an opacity value. /// @@ -546,7 +562,7 @@ namespace Avalonia.Direct2D1.Media return new ImageBrushImpl( sceneBrushContent.Brush, _deviceContext, - new D2DBitmapImpl(intermediate.Bitmap), + new D2DBitmapImpl(intermediate.Bitmap.QueryInterface()), destinationSize); } @@ -608,8 +624,28 @@ namespace Avalonia.Direct2D1.Media { PopLayer(); } - + public void Custom(ICustomDrawOperation custom) => custom.Render(this); public object GetFeature(Type t) => null; + + private void ApplyRenderOptions(RenderOptions renderOptions) + { + _deviceContext.AntialiasMode = renderOptions.EdgeMode != EdgeMode.Aliased ? AntialiasMode.PerPrimitive : AntialiasMode.Aliased; + switch (renderOptions.TextRenderingMode) + { + case TextRenderingMode.Unspecified: + _deviceContext.TextAntialiasMode = renderOptions.EdgeMode != EdgeMode.Aliased ? TextAntialiasMode.Default : TextAntialiasMode.Aliased; + break; + case TextRenderingMode.Alias: + _deviceContext.TextAntialiasMode = TextAntialiasMode.Aliased; + break; + case TextRenderingMode.Antialias: + _deviceContext.TextAntialiasMode = TextAntialiasMode.Grayscale; + break; + case TextRenderingMode.SubpixelAntialias: + _deviceContext.TextAntialiasMode = TextAntialiasMode.Cleartype; + break; + } + } } } diff --git a/src/Windows/Avalonia.Direct2D1/Media/GlyphRunImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/GlyphRunImpl.cs index 2e7a4b67f6..df147c4525 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/GlyphRunImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/GlyphRunImpl.cs @@ -1,31 +1,106 @@ using System; using System.Collections.Generic; +using Avalonia.Media; +using Avalonia.Media.TextFormatting; using Avalonia.Platform; using SharpDX.DirectWrite; +#nullable enable + namespace Avalonia.Direct2D1.Media { internal class GlyphRunImpl : IGlyphRunImpl { - public GlyphRunImpl(GlyphRun glyphRun, Size size, Point baselineOrigin) + private readonly GlyphTypefaceImpl _glyphTypefaceImpl; + + private readonly short[] _glyphIndices; + private readonly float[] _glyphAdvances; + private readonly GlyphOffset[] _glyphOffsets; + + private SharpDX.DirectWrite.GlyphRun? _glyphRun; + + public GlyphRunImpl(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, + IReadOnlyList glyphInfos, Point baselineOrigin, Rect bounds) { - Bounds = new Rect(new Point(baselineOrigin.X, 0), size); + _glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface; + + FontRenderingEmSize = fontRenderingEmSize; BaselineOrigin = baselineOrigin; - GlyphRun = glyphRun; + Bounds = bounds; + + var glyphCount = glyphInfos.Count; + + _glyphIndices = new short[glyphCount]; + + for (var i = 0; i < glyphCount; i++) + { + _glyphIndices[i] = (short)glyphInfos[i].GlyphIndex; + } + + _glyphAdvances = new float[glyphCount]; + + var width = 0.0; + + for (var i = 0; i < glyphCount; i++) + { + var advance = glyphInfos[i].GlyphAdvance; + + width += advance; + + _glyphAdvances[i] = (float)advance; + } + + _glyphOffsets = new GlyphOffset[glyphCount]; + + for (var i = 0; i < glyphCount; i++) + { + var (x, y) = glyphInfos[i].GlyphOffset; + + _glyphOffsets[i] = new GlyphOffset + { + AdvanceOffset = (float)x, + AscenderOffset = (float)y + }; + } + } + + public SharpDX.DirectWrite.GlyphRun GlyphRun + { + get + { + if (_glyphRun != null) + { + return _glyphRun; + } + + _glyphRun = new SharpDX.DirectWrite.GlyphRun + { + FontFace = _glyphTypefaceImpl.FontFace, + FontSize = (float)FontRenderingEmSize, + Advances = _glyphAdvances, + Indices = _glyphIndices, + Offsets = _glyphOffsets + }; + + return _glyphRun; + } } - public Rect Bounds{ get; } + public IGlyphTypeface GlyphTypeface => _glyphTypefaceImpl; + + public double FontRenderingEmSize { get; } public Point BaselineOrigin { get; } - public GlyphRun GlyphRun { get; } + public Rect Bounds { get; } + + public IReadOnlyList GetIntersections(float lowerBound, float upperBound) => Array.Empty(); public void Dispose() { - //GlyphRun?.Dispose(); - } + //_glyphRun?.Dispose(); - public IReadOnlyList GetIntersections(float lowerBound, float upperBound) - => Array.Empty(); + _glyphRun = null; + } } } diff --git a/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs index a08c96c40c..e0611b5870 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs @@ -7,9 +7,7 @@ namespace Avalonia.Direct2D1.Media { internal sealed class ImageBrushImpl : BrushImpl { - private readonly OptionalDispose _bitmap; - - private readonly Avalonia.Media.Imaging.BitmapInterpolationMode _bitmapInterpolationMode; + private readonly OptionalDispose _bitmap; public ImageBrushImpl( ITileBrush brush, @@ -40,8 +38,6 @@ namespace Avalonia.Direct2D1.Media GetBrushProperties(brush, calc.DestinationRect)); } } - - _bitmapInterpolationMode = brush.BitmapInterpolationMode; } public override void Dispose() @@ -103,8 +99,7 @@ namespace Avalonia.Direct2D1.Media context.Clear(Colors.Transparent); context.PushClip(calc.IntermediateClip); context.Transform = calc.IntermediateTransform; - - context.DrawBitmap(RefCountable.CreateUnownedNotClonable(bitmap), 1, rect, rect, _bitmapInterpolationMode); + context.DrawBitmap(RefCountable.CreateUnownedNotClonable(bitmap), 1, rect, rect); context.PopClip(); } diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs index 740efe833f..ac21d7cf6e 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs @@ -1,7 +1,7 @@ using System; using System.IO; using Avalonia.Platform; -using D2DBitmap = SharpDX.Direct2D1.Bitmap; +using D2DBitmap = SharpDX.Direct2D1.Bitmap1; namespace Avalonia.Direct2D1.Media { diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs index 940d4673b5..2373a90ebc 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs @@ -1,7 +1,8 @@ using System; using System.IO; +using Avalonia.Metadata; +using SharpDX.Direct2D1; using SharpDX.WIC; -using Bitmap = SharpDX.Direct2D1.Bitmap; namespace Avalonia.Direct2D1.Media { @@ -10,7 +11,7 @@ namespace Avalonia.Direct2D1.Media /// internal class D2DBitmapImpl : BitmapImpl { - private readonly Bitmap _direct2DBitmap; + private readonly Bitmap1 _direct2DBitmap; /// /// Initialize a new instance of the class @@ -22,7 +23,7 @@ namespace Avalonia.Direct2D1.Media /// or if the render target is a , /// the device associated with this context, to be renderable. /// - public D2DBitmapImpl(Bitmap d2DBitmap) + public D2DBitmapImpl(Bitmap1 d2DBitmap) { _direct2DBitmap = d2DBitmap ?? throw new ArgumentNullException(nameof(d2DBitmap)); } @@ -36,9 +37,9 @@ namespace Avalonia.Direct2D1.Media _direct2DBitmap.Dispose(); } - public override OptionalDispose GetDirect2DBitmap(SharpDX.Direct2D1.RenderTarget target) + public override OptionalDispose GetDirect2DBitmap(SharpDX.Direct2D1.RenderTarget target) { - return new OptionalDispose(_direct2DBitmap, false); + return new OptionalDispose(_direct2DBitmap, false); } public override void Save(Stream stream, int? quality = null) diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs index 6b1ca911fb..2a3bf617f7 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs @@ -5,7 +5,7 @@ using Avalonia.Rendering; using Avalonia.Utilities; using SharpDX; using SharpDX.Direct2D1; -using D2DBitmap = SharpDX.Direct2D1.Bitmap; +using D2DBitmap = SharpDX.Direct2D1.Bitmap1; namespace Avalonia.Direct2D1.Media.Imaging { @@ -14,7 +14,7 @@ namespace Avalonia.Direct2D1.Media.Imaging private readonly BitmapRenderTarget _renderTarget; public D2DRenderTargetBitmapImpl(BitmapRenderTarget renderTarget) - : base(renderTarget.Bitmap) + : base(renderTarget.Bitmap.QueryInterface()) { _renderTarget = renderTarget; } @@ -53,7 +53,7 @@ namespace Avalonia.Direct2D1.Media.Imaging public override OptionalDispose GetDirect2DBitmap(SharpDX.Direct2D1.RenderTarget target) { - return new OptionalDispose(_renderTarget.Bitmap, false); + return new OptionalDispose(_renderTarget.Bitmap.QueryInterface(), false); } public override void Save(Stream stream, int? quality = null) diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs index 533a29f68c..2fa1e5bd7a 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs @@ -1,11 +1,10 @@ using System; using System.IO; -using Avalonia.Direct2D1.Media.Imaging; using Avalonia.Win32.Interop; using SharpDX.WIC; using APixelFormat = Avalonia.Platform.PixelFormat; using AlphaFormat = Avalonia.Platform.AlphaFormat; -using D2DBitmap = SharpDX.Direct2D1.Bitmap; +using D2DBitmap = SharpDX.Direct2D1.Bitmap1; using Avalonia.Platform; using PixelFormat = SharpDX.WIC.PixelFormat; @@ -22,7 +21,7 @@ namespace Avalonia.Direct2D1.Media { switch (interpolationMode) { - case Avalonia.Media.Imaging.BitmapInterpolationMode.Default: + case Avalonia.Media.Imaging.BitmapInterpolationMode.Unspecified: return BitmapInterpolationMode.Fant; case Avalonia.Media.Imaging.BitmapInterpolationMode.LowQuality: @@ -184,7 +183,10 @@ namespace Avalonia.Direct2D1.Media { using var converter = new FormatConverter(Direct2D1Platform.ImagingFactory); converter.Initialize(WicImpl, SharpDX.WIC.PixelFormat.Format32bppPBGRA); - return new OptionalDispose(D2DBitmap.FromWicBitmap(renderTarget, converter), true); + + var d2dBitmap = D2DBitmap.FromWicBitmap(renderTarget, converter).QueryInterface(); + + return new OptionalDispose(d2dBitmap, true); } public override void Save(Stream stream, int? quality = null) diff --git a/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs index 34878bd08d..86f461673e 100644 --- a/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs @@ -51,8 +51,7 @@ namespace Avalonia.Base.UnitTests.Rendering.SceneGraph bitmap, 1, new Rect(1, 1, 1, 1), - new Rect(1, 1, 1, 1), - BitmapInterpolationMode.Default); + new Rect(1, 1, 1, 1)); Assert.Equal(2, bitmap.RefCount); diff --git a/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs b/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs index d494c47a55..37adb03628 100644 --- a/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs +++ b/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs @@ -78,7 +78,7 @@ namespace Avalonia.Base.UnitTests.VisualTree } public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, - IReadOnlyList glyphInfos, Point baselineOrigin) + IReadOnlyList glyphInfos, Point baselineOrigin, Rect bounds) { throw new NotImplementedException(); } diff --git a/tests/Avalonia.Benchmarks/NullDrawingContextImpl.cs b/tests/Avalonia.Benchmarks/NullDrawingContextImpl.cs index 40d504a0ac..3513843367 100644 --- a/tests/Avalonia.Benchmarks/NullDrawingContextImpl.cs +++ b/tests/Avalonia.Benchmarks/NullDrawingContextImpl.cs @@ -15,12 +15,13 @@ namespace Avalonia.Benchmarks public Matrix Transform { get; set; } + public RenderOptions RenderOptions { get; set; } + public void Clear(Color color) { } - public void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect, - BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default) + public void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect) { } diff --git a/tests/Avalonia.Benchmarks/NullGlyphRun.cs b/tests/Avalonia.Benchmarks/NullGlyphRun.cs deleted file mode 100644 index 5b584f302d..0000000000 --- a/tests/Avalonia.Benchmarks/NullGlyphRun.cs +++ /dev/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 GetIntersections(float lowerBound, float upperBound) - { - return null; - } - } -} diff --git a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs index d40abd9f47..55cd9e8d4b 100644 --- a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs +++ b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs @@ -124,9 +124,9 @@ namespace Avalonia.Benchmarks } public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, - IReadOnlyList glyphInfos, Point baselineOrigin) + IReadOnlyList glyphInfos, Point baselineOrigin, Rect bounds) { - return new MockGlyphRun(glyphInfos); + return new MockGlyphRun(glyphTypeface, fontRenderingEmSize, baselineOrigin, bounds); } public IPlatformRenderInterfaceContext CreateBackendContext(IPlatformGraphicsContext graphicsContext) diff --git a/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs b/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs index 772d6e1023..1b5083924a 100644 --- a/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs +++ b/tests/Avalonia.RenderTests/Media/GlyphRunTests.cs @@ -111,6 +111,29 @@ namespace Avalonia.Direct2D1.RenderTests.Media CompareImages(); } + [Win32Fact("For consistent results")] + public async Task Should_Render_GlyphRun_Aliased() + { + var control = new PositionedGlyphRunControl + { + [TextElement.ForegroundProperty] = new SolidColorBrush { Color = Colors.Black } + }; + + RenderOptions.SetTextRenderingMode(control, TextRenderingMode.Alias); + + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 190, + Height = 120, + Child = control + }; + + await RenderToFile(target); + + CompareImages(); + } + public class GlyphRunGeometryControl : Control { public GlyphRunGeometryControl() diff --git a/tests/Avalonia.RenderTests/Shapes/EllipseTests.cs b/tests/Avalonia.RenderTests/Shapes/EllipseTests.cs index 43f15e0fad..0851760987 100644 --- a/tests/Avalonia.RenderTests/Shapes/EllipseTests.cs +++ b/tests/Avalonia.RenderTests/Shapes/EllipseTests.cs @@ -35,5 +35,49 @@ namespace Avalonia.Direct2D1.RenderTests.Shapes await RenderToFile(target); CompareImages(); } + + [Fact] + public async Task Should_Render_Circle_Aliased() + { + var target = new Border + { + Background = Brushes.White, + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Ellipse + { + Stroke = Brushes.Black, + StrokeThickness = 3.5, + } + }; + + RenderOptions.SetEdgeMode(target, EdgeMode.Aliased); + + await RenderToFile(target); + CompareImages(); + } + + [Fact] + public async Task Should_Render_Circle_Antialiased() + { + var target = new Border + { + Background = Brushes.White, + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Ellipse + { + Stroke = Brushes.Black, + StrokeThickness = 3.5, + } + }; + + RenderOptions.SetEdgeMode(target, EdgeMode.Antialias); + + await RenderToFile(target); + CompareImages(); + } } } diff --git a/tests/Avalonia.UnitTests/MockGlyphRun.cs b/tests/Avalonia.UnitTests/MockGlyphRun.cs index 4561d3b3f2..805c0b6a8a 100644 --- a/tests/Avalonia.UnitTests/MockGlyphRun.cs +++ b/tests/Avalonia.UnitTests/MockGlyphRun.cs @@ -1,33 +1,34 @@ using System; using System.Collections.Generic; -using Avalonia.Media.TextFormatting; +using Avalonia.Media; using Avalonia.Platform; namespace Avalonia.UnitTests { public class MockGlyphRun : IGlyphRunImpl { - public MockGlyphRun(IReadOnlyList glyphInfos) + public MockGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, Point baselineOrigin, Rect bounds) { - var width = 0.0; + GlyphTypeface = glyphTypeface; + FontRenderingEmSize = fontRenderingEmSize; + BaselineOrigin = baselineOrigin; + Bounds =bounds; + } - for (var i = 0; i < glyphInfos.Count; ++i) - { - width += glyphInfos[i].GlyphAdvance; - } + public IGlyphTypeface GlyphTypeface { get; } - Bounds = new Rect(new Size(width, 10)); - } + public double FontRenderingEmSize { get; } - public Rect Bounds { get; } + public Point BaselineOrigin { get; } - public Point BaselineOrigin => new Point(0, 8); + public Rect Bounds { get; } public void Dispose() { + } - public IReadOnlyList GetIntersections(float lowerBound, float upperBound) + public IReadOnlyList GetIntersections(float lowerLimit, float upperLimit) => Array.Empty(); } } diff --git a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs index 720755f2b0..8647461c0e 100644 --- a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs +++ b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs @@ -5,7 +5,6 @@ using Avalonia.Media; using Avalonia.Platform; using Avalonia.Media.Imaging; using Avalonia.Media.TextFormatting; -using Avalonia.Rendering; using Moq; namespace Avalonia.UnitTests @@ -149,10 +148,9 @@ namespace Avalonia.UnitTests throw new NotImplementedException(); } - public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, - IReadOnlyList glyphInfos, Point baselineOrigin) + public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos, Point baselineOrigin, Rect bounds) { - return new MockGlyphRun(glyphInfos); + return new MockGlyphRun(glyphTypeface, fontRenderingEmSize, baselineOrigin, bounds); } public IPlatformRenderInterfaceContext CreateBackendContext(IPlatformGraphicsContext graphicsContext) => this; @@ -162,21 +160,6 @@ namespace Avalonia.UnitTests return Mock.Of(); } - public IGlyphRunBuffer AllocateGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) - { - return Mock.Of(); - } - - public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) - { - return Mock.Of(); - } - - public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) - { - return Mock.Of(); - } - public bool SupportsIndividualRoundRects { get; set; } public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul; diff --git a/tests/TestFiles/Direct2D1/Media/GlyphRun/Should_Render_GlyphRun_Aliased.expected.png b/tests/TestFiles/Direct2D1/Media/GlyphRun/Should_Render_GlyphRun_Aliased.expected.png new file mode 100644 index 0000000000..f130809067 Binary files /dev/null and b/tests/TestFiles/Direct2D1/Media/GlyphRun/Should_Render_GlyphRun_Aliased.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Fill_NoTile.expected.png b/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Fill_NoTile.expected.png index 73d147bf77..1864f8b214 100644 Binary files a/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Fill_NoTile.expected.png and b/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Fill_NoTile.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Tile_Fill.expected.png b/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Tile_Fill.expected.png index c38dcfbcfd..7d54539814 100644 Binary files a/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Tile_Fill.expected.png and b/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Tile_Fill.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Tile_UniformToFill.expected.png b/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Tile_UniformToFill.expected.png index 23fc402cd8..8d5c3181f7 100644 Binary files a/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Tile_UniformToFill.expected.png and b/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Tile_UniformToFill.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_UniformToFill_NoTile.expected.png b/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_UniformToFill_NoTile.expected.png index e173fa6cee..7c1287d245 100644 Binary files a/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_UniformToFill_NoTile.expected.png and b/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_UniformToFill_NoTile.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Uniform_NoTile.expected.png b/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Uniform_NoTile.expected.png index de73af8170..b06a6508fe 100644 Binary files a/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Uniform_NoTile.expected.png and b/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Uniform_NoTile.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Media/ImageDrawing/ImageDrawing_Fill.expected.png b/tests/TestFiles/Direct2D1/Media/ImageDrawing/ImageDrawing_Fill.expected.png index acc8532ff9..8404ce831a 100644 Binary files a/tests/TestFiles/Direct2D1/Media/ImageDrawing/ImageDrawing_Fill.expected.png and b/tests/TestFiles/Direct2D1/Media/ImageDrawing/ImageDrawing_Fill.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Fill_NoTile.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Fill_NoTile.expected.png index 110b44a4c0..86d5e7ccef 100644 Binary files a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Fill_NoTile.expected.png and b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Fill_NoTile.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Grip_144_Dpi.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Grip_144_Dpi.expected.png index 1900ade5e2..28370f3bce 100644 Binary files a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Grip_144_Dpi.expected.png and b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Grip_144_Dpi.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_UniformToFill_NoTile.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_UniformToFill_NoTile.expected.png index 2d7ccac2f7..c1898766aa 100644 Binary files a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_UniformToFill_NoTile.expected.png and b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_UniformToFill_NoTile.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Uniform_NoTile.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Uniform_NoTile.expected.png index d9c62a72a8..6840093b98 100644 Binary files a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Uniform_NoTile.expected.png and b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Uniform_NoTile.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Shapes/Ellipse/Should_Render_Circle_Aliased.expected.png b/tests/TestFiles/Direct2D1/Shapes/Ellipse/Should_Render_Circle_Aliased.expected.png new file mode 100644 index 0000000000..d051696d78 Binary files /dev/null and b/tests/TestFiles/Direct2D1/Shapes/Ellipse/Should_Render_Circle_Aliased.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Shapes/Ellipse/Should_Render_Circle_Antialiased.expected.png b/tests/TestFiles/Direct2D1/Shapes/Ellipse/Should_Render_Circle_Antialiased.expected.png new file mode 100644 index 0000000000..06dc7f5450 Binary files /dev/null and b/tests/TestFiles/Direct2D1/Shapes/Ellipse/Should_Render_Circle_Antialiased.expected.png differ diff --git a/tests/TestFiles/Skia/Media/GlyphRun/Should_Render_GlyphRun_Aliased.expected.png b/tests/TestFiles/Skia/Media/GlyphRun/Should_Render_GlyphRun_Aliased.expected.png new file mode 100644 index 0000000000..0075bfcd3a Binary files /dev/null and b/tests/TestFiles/Skia/Media/GlyphRun/Should_Render_GlyphRun_Aliased.expected.png differ diff --git a/tests/TestFiles/Skia/Shapes/Ellipse/Should_Render_Circle_Aliased.expected.png b/tests/TestFiles/Skia/Shapes/Ellipse/Should_Render_Circle_Aliased.expected.png new file mode 100644 index 0000000000..c8b579f821 Binary files /dev/null and b/tests/TestFiles/Skia/Shapes/Ellipse/Should_Render_Circle_Aliased.expected.png differ diff --git a/tests/TestFiles/Skia/Shapes/Ellipse/Should_Render_Circle_Antialiased.expected.png b/tests/TestFiles/Skia/Shapes/Ellipse/Should_Render_Circle_Antialiased.expected.png new file mode 100644 index 0000000000..30df84a194 Binary files /dev/null and b/tests/TestFiles/Skia/Shapes/Ellipse/Should_Render_Circle_Antialiased.expected.png differ