source/SkiaSharp.Views.Uno/SkiaSharp.Views.Uno.WinUI.Shared/SKXamlCanvas.cs (126 lines of code) (raw):

using System; using Windows.ApplicationModel; using Windows.Graphics.Display; using Windows.UI.Core; #if WINUI using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Data; #else using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Data; #endif #if WINDOWS || WINUI namespace SkiaSharp.Views.Windows #else namespace SkiaSharp.Views.UWP #endif { public partial class SKXamlCanvas : Canvas { private const float DpiBase = 96.0f; private static readonly DependencyProperty ProxyVisibilityProperty = DependencyProperty.Register( "ProxyVisibility", typeof(Visibility), typeof(SKXamlCanvas), new PropertyMetadata(Visibility.Visible, OnVisibilityChanged)); private static bool designMode = DesignMode.DesignModeEnabled; private bool ignorePixelScaling; private bool isVisible = true; // workaround for https://github.com/mono/SkiaSharp/issues/1118 private int loadUnloadCounter = 0; private void Initialize() { if (designMode) return; var display = DisplayInformation.GetForCurrentView(); OnDpiChanged(display); Loaded += OnLoaded; Unloaded += OnUnloaded; SizeChanged += OnSizeChanged; var binding = new Binding { Path = new PropertyPath(nameof(Visibility)), Source = this }; SetBinding(ProxyVisibilityProperty, binding); } public SKSize CanvasSize { get; private set; } public bool IgnorePixelScaling { get => ignorePixelScaling; set { ignorePixelScaling = value; Invalidate(); } } public double Dpi { get; private set; } = 1; public event EventHandler<SKPaintSurfaceEventArgs> PaintSurface; protected virtual void OnPaintSurface(SKPaintSurfaceEventArgs e) { PaintSurface?.Invoke(this, e); } private static void OnVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is SKXamlCanvas canvas && e.NewValue is Visibility visibility) { canvas.isVisible = visibility == Visibility.Visible; canvas.Invalidate(); } } private void OnDpiChanged(DisplayInformation sender, object args = null) { Dpi = sender.LogicalDpi / DpiBase; Invalidate(); } private void OnSizeChanged(object sender, SizeChangedEventArgs e) { Invalidate(); } private void OnLoaded(object sender, RoutedEventArgs e) { loadUnloadCounter++; if (loadUnloadCounter != 1) return; DoLoaded(); var display = DisplayInformation.GetForCurrentView(); display.DpiChanged += OnDpiChanged; OnDpiChanged(display); } private void OnUnloaded(object sender, RoutedEventArgs e) { loadUnloadCounter--; if (loadUnloadCounter != 0) return; DoUnloaded(); var display = DisplayInformation.GetForCurrentView(); display.DpiChanged -= OnDpiChanged; } public new void Invalidate() { if (Dispatcher.HasThreadAccess) DoInvalidate(); else _ = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, DoInvalidate); } partial void DoLoaded(); partial void DoUnloaded(); private SKSizeI CreateSize(out SKSizeI unscaledSize, out float dpi) { unscaledSize = SKSizeI.Empty; dpi = (float)Dpi; var w = ActualWidth; var h = ActualHeight; if (!IsPositive(w) || !IsPositive(h)) return SKSizeI.Empty; unscaledSize = new SKSizeI((int)w, (int)h); return new SKSizeI((int)(w * dpi), (int)(h * dpi)); static bool IsPositive(double value) { return !double.IsNaN(value) && !double.IsInfinity(value) && value > 0; } } } }