diff --git a/src/Avalonia.Visuals/Media/Imaging/BitmapScalingMode.cs b/src/Avalonia.Visuals/Media/Imaging/BitmapScalingMode.cs new file mode 100644 index 0000000000..36d239c9d9 --- /dev/null +++ b/src/Avalonia.Visuals/Media/Imaging/BitmapScalingMode.cs @@ -0,0 +1,12 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +namespace Avalonia.Visuals.Media.Imaging +{ + public enum BitmapScalingMode + { + LowQuality, + MediumQuality, + HighQuality + } +} diff --git a/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs b/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs index 04bbe713f2..f511daf9b2 100644 --- a/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs +++ b/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs @@ -7,6 +7,8 @@ using Avalonia.Utilities; namespace Avalonia.Platform { + using Avalonia.Visuals.Media.Imaging; + /// /// Defines the interface through which drawing occurs. /// @@ -30,7 +32,8 @@ namespace Avalonia.Platform /// The opacity to draw with. /// The rect in the image to draw. /// The rect in the output to draw to. - void DrawImage(IRef source, double opacity, Rect sourceRect, Rect destRect); + /// Controls + void DrawImage(IRef source, double opacity, Rect sourceRect, Rect destRect, BitmapScalingMode scalingMode = BitmapScalingMode.LowQuality); /// /// Draws a bitmap image. diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs index f3dbfbd8fb..7172696e81 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs @@ -10,6 +10,8 @@ using Avalonia.VisualTree; namespace Avalonia.Rendering.SceneGraph { + using Avalonia.Visuals.Media.Imaging; + /// /// A drawing context which builds a scene graph. /// @@ -114,13 +116,13 @@ namespace Avalonia.Rendering.SceneGraph } /// - public void DrawImage(IRef source, double opacity, Rect sourceRect, Rect destRect) + public void DrawImage(IRef source, double opacity, Rect sourceRect, Rect destRect, BitmapScalingMode scalingMode = BitmapScalingMode.LowQuality) { var next = NextDrawAs(); - if (next == null || !next.Item.Equals(Transform, source, opacity, sourceRect, destRect)) + if (next == null || !next.Item.Equals(Transform, source, opacity, sourceRect, destRect, scalingMode)) { - Add(new ImageNode(Transform, source, opacity, sourceRect, destRect)); + Add(new ImageNode(Transform, source, opacity, sourceRect, destRect, scalingMode)); } else { diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs index 06fdb3f86c..96a8731715 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs @@ -7,6 +7,8 @@ using Avalonia.Utilities; namespace Avalonia.Rendering.SceneGraph { + using Avalonia.Visuals.Media.Imaging; + /// /// A node in the scene graph which represents an image draw. /// @@ -20,7 +22,8 @@ namespace Avalonia.Rendering.SceneGraph /// The draw opacity. /// The source rect. /// The destination rect. - public ImageNode(Matrix transform, IRef source, double opacity, Rect sourceRect, Rect destRect) + /// + public ImageNode(Matrix transform, IRef source, double opacity, Rect sourceRect, Rect destRect, BitmapScalingMode scalingMode) : base(destRect, transform, null) { Transform = transform; @@ -28,8 +31,11 @@ namespace Avalonia.Rendering.SceneGraph Opacity = opacity; SourceRect = sourceRect; DestRect = destRect; + ScalingMode = scalingMode; } + + /// /// Gets the transform with which the node will be drawn. /// @@ -55,6 +61,14 @@ namespace Avalonia.Rendering.SceneGraph /// public Rect DestRect { get; } + /// + /// Gets or sets the scaling mode. + /// + /// + /// The scaling mode. + /// + public BitmapScalingMode ScalingMode { get; } + /// /// Determines if this draw operation equals another. /// @@ -63,18 +77,20 @@ 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. + /// /// 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) + public bool Equals(Matrix transform, IRef source, double opacity, Rect sourceRect, Rect destRect, BitmapScalingMode scalingMode) { return transform == Transform && Equals(source.Item, Source.Item) && opacity == Opacity && sourceRect == SourceRect && - destRect == DestRect; + destRect == DestRect && + scalingMode == ScalingMode; } /// @@ -83,7 +99,7 @@ namespace Avalonia.Rendering.SceneGraph // TODO: Probably need to introduce some kind of locking mechanism in the case of // WriteableBitmap. context.Transform = Transform; - context.DrawImage(Source, Opacity, SourceRect, DestRect); + context.DrawImage(Source, Opacity, SourceRect, DestRect, ScalingMode); } /// diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs index bdbbdab2b9..27115e164a 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs @@ -13,6 +13,8 @@ using SharpDX.Mathematics.Interop; namespace Avalonia.Direct2D1.Media { + using Avalonia.Visuals.Media.Imaging; + /// /// Draws using Direct2D1. /// @@ -100,16 +102,43 @@ namespace Avalonia.Direct2D1.Media /// The opacity to draw with. /// The rect in the image to draw. /// The rect in the output to draw to. - public void DrawImage(IRef source, double opacity, Rect sourceRect, Rect destRect) + /// + public void DrawImage(IRef source, double opacity, Rect sourceRect, Rect destRect, BitmapScalingMode scalingMode) { using (var d2d = ((BitmapImpl)source.Item).GetDirect2DBitmap(_renderTarget)) { - _renderTarget.DrawBitmap( - d2d.Value, - destRect.ToSharpDX(), - (float)opacity, - BitmapInterpolationMode.Linear, - sourceRect.ToSharpDX()); + if (_renderTarget is DeviceContext deviceContext) + { + deviceContext.DrawBitmap(d2d.Value, destRect.ToDirect2D(), (float)opacity, GetInterpolationMode(scalingMode), sourceRect.ToDirect2D(), null); + } + else + { + var interpolationMode = scalingMode == BitmapScalingMode.LowQuality + ? BitmapInterpolationMode.NearestNeighbor + : BitmapInterpolationMode.Linear; + + _renderTarget.DrawBitmap( + d2d.Value, + destRect.ToSharpDX(), + (float)opacity, + interpolationMode, + sourceRect.ToSharpDX()); + } + } + } + + private static InterpolationMode GetInterpolationMode(BitmapScalingMode scalingMode) + { + switch (scalingMode) + { + case BitmapScalingMode.LowQuality: + return InterpolationMode.NearestNeighbor; + case BitmapScalingMode.MediumQuality: + return InterpolationMode.Linear; + case BitmapScalingMode.HighQuality: + return InterpolationMode.HighQualityCubic; + default: + throw new ArgumentOutOfRangeException(nameof(scalingMode), scalingMode, null); } } diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs index d58f023391..c792334ec1 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs @@ -1,11 +1,15 @@ -using System; +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; using System.IO; using Avalonia.Platform; using SharpDX.WIC; -using D2DBitmap = SharpDX.Direct2D1.Bitmap; namespace Avalonia.Direct2D1.Media { + using SharpDX.Direct2D1; + public abstract class BitmapImpl : IBitmapImpl, IDisposable { public BitmapImpl(ImagingFactory imagingFactory) @@ -17,7 +21,7 @@ namespace Avalonia.Direct2D1.Media public abstract int PixelWidth { get; } public abstract int PixelHeight { get; } - public abstract OptionalDispose GetDirect2DBitmap(SharpDX.Direct2D1.RenderTarget target); + public abstract OptionalDispose GetDirect2DBitmap(RenderTarget target); public void Save(string fileName) {