From 2985de8901e120ce9aeda549b7109d6b19b37081 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Thu, 4 Jul 2024 13:31:32 +0200 Subject: [PATCH 1/3] Do not stretch the background When resizing a view, the background will be stretched making the image distorted and not a great experience. This PR make sure to never scale the background brush and rather keep it top-left aligned. --- source/SkiaSharp.Views/SkiaSharp.Views.WinUI/SKXamlCanvas.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/SkiaSharp.Views/SkiaSharp.Views.WinUI/SKXamlCanvas.cs b/source/SkiaSharp.Views/SkiaSharp.Views.WinUI/SKXamlCanvas.cs index e11e71577a..2c99eac35d 100644 --- a/source/SkiaSharp.Views/SkiaSharp.Views.WinUI/SKXamlCanvas.cs +++ b/source/SkiaSharp.Views/SkiaSharp.Views.WinUI/SKXamlCanvas.cs @@ -233,7 +233,7 @@ private SKImageInfo CreateBitmap(out SKSizeI unscaledSize, out float dpi) ImageSource = bitmap, AlignmentX = AlignmentX.Left, AlignmentY = AlignmentY.Top, - Stretch = Stretch.Fill + Stretch = Stretch.None }; Background = brush; } From 53c51b51e4ff63bdb7171a8c35d0837c606cf9a5 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Fri, 5 Jul 2024 01:41:03 +0800 Subject: [PATCH 2/3] Update SKXamlCanvas.cs --- source/SkiaSharp.Views/SkiaSharp.Views.WinUI/SKXamlCanvas.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/SkiaSharp.Views/SkiaSharp.Views.WinUI/SKXamlCanvas.cs b/source/SkiaSharp.Views/SkiaSharp.Views.WinUI/SKXamlCanvas.cs index 2c99eac35d..e11e71577a 100644 --- a/source/SkiaSharp.Views/SkiaSharp.Views.WinUI/SKXamlCanvas.cs +++ b/source/SkiaSharp.Views/SkiaSharp.Views.WinUI/SKXamlCanvas.cs @@ -233,7 +233,7 @@ private SKImageInfo CreateBitmap(out SKSizeI unscaledSize, out float dpi) ImageSource = bitmap, AlignmentX = AlignmentX.Left, AlignmentY = AlignmentY.Top, - Stretch = Stretch.None + Stretch = Stretch.Fill }; Background = brush; } From 2bb4eacd7d3c64cc77ef9513a6d87e5103294481 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Fri, 5 Jul 2024 01:45:06 +0800 Subject: [PATCH 3/3] Scale the brush instead of stretching it --- .../SkiaSharp.Views.WinUI/SKXamlCanvas.cs | 68 +++++++++++++------ 1 file changed, 48 insertions(+), 20 deletions(-) diff --git a/source/SkiaSharp.Views/SkiaSharp.Views.WinUI/SKXamlCanvas.cs b/source/SkiaSharp.Views/SkiaSharp.Views.WinUI/SKXamlCanvas.cs index e11e71577a..cf9c01233d 100644 --- a/source/SkiaSharp.Views/SkiaSharp.Views.WinUI/SKXamlCanvas.cs +++ b/source/SkiaSharp.Views/SkiaSharp.Views.WinUI/SKXamlCanvas.cs @@ -1,6 +1,5 @@ using System; using Windows.ApplicationModel; -using Windows.Graphics.Display; #if WINDOWS using Microsoft.UI.Dispatching; @@ -39,6 +38,7 @@ public partial class SKXamlCanvas : Canvas private IntPtr pixels; private WriteableBitmap bitmap; + private ImageBrush brush; private bool ignorePixelScaling; private bool isVisible = true; @@ -101,8 +101,13 @@ private static void OnVisibilityChanged(DependencyObject d, DependencyPropertyCh private void OnXamlRootChanged(XamlRoot xamlRoot = null, XamlRootChangedEventArgs e = null) { var root = xamlRoot ?? XamlRoot; - Dpi = root?.RasterizationScale ?? 1.0; - Invalidate(); + var newDpi = root?.RasterizationScale ?? 1.0; + if (newDpi != Dpi) + { + Dpi = newDpi; + UpdateBrushScale(); + Invalidate(); + } } #else private void OnDpiChanged(DisplayInformation sender, object args = null) @@ -141,7 +146,7 @@ private void OnUnloaded(object sender, RoutedEventArgs e) return; #if WINDOWS - if(XamlRoot != null) + if (XamlRoot != null) { XamlRoot.Changed -= OnXamlRootChanged; } @@ -170,7 +175,7 @@ private void DoInvalidate() if (!isVisible) return; - var info = CreateBitmap(out var unscaledSize, out var dpi); + var (info, viewSize, dpi) = CreateBitmap(); if (info.Width <= 0 || info.Height <= 0) { @@ -178,12 +183,18 @@ private void DoInvalidate() return; } - var userVisibleSize = IgnorePixelScaling ? unscaledSize : info.Size; + // This is here because the property name is confusing and backwards. + // True actually means to ignore the pixel scaling of the raw pixel + // size and instead use the view size such that sizes match the XAML + // elements. + var matchUI = IgnorePixelScaling; + + var userVisibleSize = matchUI ? viewSize : info.Size; CanvasSize = userVisibleSize; using (var surface = SKSurface.Create(info, pixels, info.RowBytes)) { - if (IgnorePixelScaling) + if (matchUI) { var canvas = surface.Canvas; canvas.Scale(dpi); @@ -195,19 +206,19 @@ private void DoInvalidate() bitmap.Invalidate(); } - private SKSizeI CreateSize(out SKSizeI unscaledSize, out float dpi) + private (SKSizeI ViewSize, SKSizeI PixelSize, float Dpi) CreateSize() { - unscaledSize = SKSizeI.Empty; - dpi = (float)Dpi; - var w = ActualWidth; var h = ActualHeight; if (!IsPositive(w) || !IsPositive(h)) - return SKSizeI.Empty; + return (SKSizeI.Empty, SKSizeI.Empty, 1); - unscaledSize = new SKSizeI((int)w, (int)h); - return new SKSizeI((int)(w * dpi), (int)(h * dpi)); + var dpi = (float)Dpi; + var viewSize = new SKSizeI((int)w, (int)h); + var pixelSize = new SKSizeI((int)(w * dpi), (int)(h * dpi)); + + return (viewSize, pixelSize, dpi); static bool IsPositive(double value) { @@ -215,10 +226,10 @@ static bool IsPositive(double value) } } - private SKImageInfo CreateBitmap(out SKSizeI unscaledSize, out float dpi) + private (SKImageInfo Info, SKSizeI PixelSize, float Dpi) CreateBitmap() { - var size = CreateSize(out unscaledSize, out dpi); - var info = new SKImageInfo(size.Width, size.Height, SKImageInfo.PlatformColorType, SKAlphaType.Premul); + var (viewSize, pixelSize, dpi) = CreateSize(); + var info = new SKImageInfo(pixelSize.Width, pixelSize.Height, SKImageInfo.PlatformColorType, SKAlphaType.Premul); if (bitmap?.PixelWidth != info.Width || bitmap?.PixelHeight != info.Height) FreeBitmap(); @@ -228,22 +239,39 @@ private SKImageInfo CreateBitmap(out SKSizeI unscaledSize, out float dpi) bitmap = new WriteableBitmap(info.Width, info.Height); pixels = bitmap.GetPixels(); - var brush = new ImageBrush + brush = new ImageBrush { ImageSource = bitmap, AlignmentX = AlignmentX.Left, AlignmentY = AlignmentY.Top, - Stretch = Stretch.Fill + Stretch = Stretch.None }; + UpdateBrushScale(); + Background = brush; } - return info; + return (info, viewSize, dpi); + } + + private void UpdateBrushScale() + { + if (brush == null) + return; + + var scale = 1.0 / Dpi; + + brush.Transform = new ScaleTransform + { + ScaleX = scale, + ScaleY = scale + }; } private void FreeBitmap() { Background = null; + brush = null; bitmap = null; pixels = IntPtr.Zero; }