From c724039ca5dd3078be86fdf23c49e1be1339da3f Mon Sep 17 00:00:00 2001 From: gmurray81 Date: Fri, 18 Nov 2022 16:25:36 -0500 Subject: [PATCH 01/10] add SKGLElement --- .../SkiaSharp.Views.WPF/SKGLElement.cs | 272 ++++++++++++++++++ .../SkiaSharp.Views.WPF.csproj | 8 + 2 files changed, 280 insertions(+) create mode 100644 source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs diff --git a/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs b/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs new file mode 100644 index 0000000000..f36ce6d73d --- /dev/null +++ b/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs @@ -0,0 +1,272 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using OpenTK.Graphics; +using System.Windows.Media.Media3D; +using SkiaSharp.Views.Desktop; +using OpenTK.Wpf; +using SkiaSharp; +using OpenTK.Graphics.OpenGL; +using OpenTK.Platform.Windows; +using OpenTK; +using System.Windows.Interop; +using OpenTK.Platform; +#if NETCOREAPP || NET +using OpenTK.Mathematics; +#endif + +namespace SkiaSharp.Views.WPF +{ + [DefaultEvent("PaintSurface")] + [DefaultProperty("Name")] + public class SKGLElement : GLWpfControl, IDisposable + { + private const SKColorType colorType = SKColorType.Rgba8888; + private const GRSurfaceOrigin surfaceOrigin = GRSurfaceOrigin.BottomLeft; + + private bool designMode; + + private GRContext grContext; + private GRGlFramebufferInfo glInfo; + private GRBackendRenderTarget renderTarget; + private SKSurface surface; + private SKCanvas canvas; + + private SKSizeI lastSize; + private SKGLElementWindowListener listener; + + public SKGLElement() + : base() + { + Initialize(); + } + + private void Initialize() + { + designMode = DesignerProperties.GetIsInDesignMode(this); + var settings = new GLWpfControlSettings() { MajorVersion = 2, MinorVersion = 1, RenderContinuously = false }; + + this.Render += OnPaint; + + this.Loaded += (s, e) => + { + SKGLElementWindowListener listener = new SKGLElementWindowListener(this); + this.listener = listener; + }; + + Start(settings); + } + + + private class SKGLElementWindowListener: IDisposable + { + private WeakReference toDestroy; + private WeakReference theWindow; + public SKGLElementWindowListener(SKGLElement toDestroy) + { + this.toDestroy = new WeakReference(toDestroy); + var window = System.Windows.Window.GetWindow(toDestroy); + if (window != null) + { + theWindow = new WeakReference(window); + window.Closing += Window_Closing; + } + } + + private void Window_Closing(object sender, CancelEventArgs e) + { + SKGLElement target = null; + if (toDestroy.TryGetTarget(out target)) + { + if (target != null) + { + target.Dispose(); + } + } + + } + + private bool disposed = false; + + + protected virtual void Dispose(bool disposing) + { + if (disposed) + { + return; + } + + Window window; + if (theWindow != null && theWindow.TryGetTarget(out window)) + { + if (window != null) + { + window.Closing -= Window_Closing; + } + } + theWindow = null; + + disposed = true; + } + + public void Dispose() + { + Dispose(true); + } + } + + + public SKSize CanvasSize => lastSize; + + public GRContext GRContext => grContext; + + [Category("Appearance")] + public event EventHandler PaintSurface; + + private SKSizeI GetSize() + { + var currentWidth = ActualWidth; + var currentHeight = ActualHeight; + + if (currentWidth < 0 || + currentHeight < 0) + { + currentWidth = 0; + currentHeight = 0; + } + + PresentationSource source = PresentationSource.FromVisual(this); + + double dpiX = 1.0; + double dpiY = 1.0; + if (source != null) + { + dpiX = source.CompositionTarget.TransformToDevice.M11; + dpiY = source.CompositionTarget.TransformToDevice.M22; + } + + return new SKSizeI((int)(currentWidth * dpiX), (int)(currentHeight * dpiY)); + } + + protected override void OnRender(DrawingContext drawingContext) + { + if (grContext != null) + { + grContext.ResetContext(); + } + base.OnRender(drawingContext); + } + + protected virtual void OnPaint(TimeSpan e) + { + if (disposed) + { + return; + } + if (designMode) + { + return; + } + + // create the contexts if not done already + if (grContext == null) + { + var glInterface = GRGlInterface.Create(); + grContext = GRContext.CreateGl(glInterface); + } + + // get the new surface size + var newSize = GetSize(); + + GL.ClearColor(Color4.Transparent); + GL.Clear(ClearBufferMask.ColorBufferBit); + GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit); + + // manage the drawing surface + if (renderTarget == null || lastSize != newSize || !renderTarget.IsValid) + { + + // create or update the dimensions + lastSize = newSize; + + GL.GetInteger(GetPName.FramebufferBinding, out var framebuffer); + GL.GetInteger(GetPName.StencilBits, out var stencil); + GL.GetInteger(GetPName.Samples, out var samples); + var maxSamples = grContext.GetMaxSurfaceSampleCount(colorType); + if (samples > maxSamples) + samples = maxSamples; + glInfo = new GRGlFramebufferInfo((uint)framebuffer, colorType.ToGlSizedFormat()); + + // destroy the old surface + surface?.Dispose(); + surface = null; + canvas = null; + + // re-create the render target + renderTarget?.Dispose(); + renderTarget = new GRBackendRenderTarget(newSize.Width, newSize.Height, samples, stencil, glInfo); + } + + // create the surface + if (surface == null) + { + surface = SKSurface.Create(grContext, renderTarget, surfaceOrigin, colorType); + canvas = surface.Canvas; + } + + using (new SKAutoCanvasRestore(canvas, true)) + { + // start drawing +#pragma warning disable CS0612 // Type or member is obsolete + OnPaintSurface(new SKPaintGLSurfaceEventArgs(surface, renderTarget, surfaceOrigin, colorType, glInfo)); +#pragma warning restore CS0612 // Type or member is obsolete + } + + // update the control + canvas.Flush(); + } + + protected virtual void OnPaintSurface(SKPaintGLSurfaceEventArgs e) + { + // invoke the event + PaintSurface?.Invoke(this, e); + } + + private bool disposed = false; + + + protected virtual void Dispose(bool disposing) + { + if (disposed) + { + return; + } + + + canvas = null; + surface?.Dispose(); + surface = null; + renderTarget?.Dispose(); + renderTarget = null; + grContext?.Dispose(); + grContext = null; + listener?.Dispose(); + listener = null; + + disposed = true; + } + + public void Dispose() + { + Dispose(true); + } + } + +} diff --git a/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SkiaSharp.Views.WPF.csproj b/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SkiaSharp.Views.WPF.csproj index 8108387533..dfd6e9a7af 100644 --- a/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SkiaSharp.Views.WPF.csproj +++ b/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SkiaSharp.Views.WPF.csproj @@ -9,6 +9,14 @@ SkiaSharp Views & Layers for Windows Presentation Foundation (WPF) wpf + + + + + + + + From 865315e8240ed485130f5bd6b5698a8be6f8b332 Mon Sep 17 00:00:00 2001 From: gmurray81 Date: Mon, 5 Dec 2022 16:18:50 -0500 Subject: [PATCH 02/10] revert unintentional change to build props --- source/SkiaSharp.Build.props | 6 ------ 1 file changed, 6 deletions(-) diff --git a/source/SkiaSharp.Build.props b/source/SkiaSharp.Build.props index 0cb92531a1..063695028b 100644 --- a/source/SkiaSharp.Build.props +++ b/source/SkiaSharp.Build.props @@ -21,12 +21,6 @@ true - - $(GIT_SHA) - $(GIT_COMMIT) - $(GIT_BRANCH_NAME) - - true latest From e5925099ca508ee19cdc3a896334e229719f5bed Mon Sep 17 00:00:00 2001 From: gmurray81 Date: Wed, 21 Feb 2024 09:44:37 -0500 Subject: [PATCH 03/10] fix bad merge --- source/SkiaSharp.Build.props | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/SkiaSharp.Build.props b/source/SkiaSharp.Build.props index 063695028b..52f754f6e3 100644 --- a/source/SkiaSharp.Build.props +++ b/source/SkiaSharp.Build.props @@ -20,6 +20,12 @@ true true + + + $(GIT_SHA) + $(GIT_COMMIT) + $(GIT_BRANCH_NAME) + true From 146b51306c0cb88fa6ba46a2f9d3d70a738b1c70 Mon Sep 17 00:00:00 2001 From: gmurray81 Date: Wed, 21 Feb 2024 09:50:01 -0500 Subject: [PATCH 04/10] remove whitespace diff --- source/SkiaSharp.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/SkiaSharp.Build.props b/source/SkiaSharp.Build.props index 52f754f6e3..0cb92531a1 100644 --- a/source/SkiaSharp.Build.props +++ b/source/SkiaSharp.Build.props @@ -20,7 +20,7 @@ true true - + $(GIT_SHA) $(GIT_COMMIT) From 6f2310dbf8dc24b29e7aad1ec57a17a36ad457df Mon Sep 17 00:00:00 2001 From: gmurray81 Date: Wed, 21 Feb 2024 11:35:41 -0500 Subject: [PATCH 05/10] update package conditional, remove obselete member --- source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs | 4 +--- .../SkiaSharp.Views.WPF/SkiaSharp.Views.WPF.csproj | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs b/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs index f36ce6d73d..b1c6787700 100644 --- a/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs +++ b/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs @@ -224,9 +224,7 @@ protected virtual void OnPaint(TimeSpan e) using (new SKAutoCanvasRestore(canvas, true)) { // start drawing -#pragma warning disable CS0612 // Type or member is obsolete - OnPaintSurface(new SKPaintGLSurfaceEventArgs(surface, renderTarget, surfaceOrigin, colorType, glInfo)); -#pragma warning restore CS0612 // Type or member is obsolete + OnPaintSurface(new SKPaintGLSurfaceEventArgs(surface, renderTarget, surfaceOrigin, colorType)); } // update the control diff --git a/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SkiaSharp.Views.WPF.csproj b/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SkiaSharp.Views.WPF.csproj index dfd6e9a7af..9d7032dd53 100644 --- a/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SkiaSharp.Views.WPF.csproj +++ b/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SkiaSharp.Views.WPF.csproj @@ -13,7 +13,7 @@ - + From 5397d91df1dc1bd5181efe9dc23a050b36d34a33 Mon Sep 17 00:00:00 2001 From: gmurray81 Date: Wed, 21 Feb 2024 15:23:52 -0500 Subject: [PATCH 06/10] update to newer glwpfcontrol version --- .../SkiaSharp.Views.WPF/SkiaSharp.Views.WPF.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SkiaSharp.Views.WPF.csproj b/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SkiaSharp.Views.WPF.csproj index 9d7032dd53..14367c1eb1 100644 --- a/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SkiaSharp.Views.WPF.csproj +++ b/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SkiaSharp.Views.WPF.csproj @@ -15,7 +15,7 @@ - + From 0872c0ffcf6f0c563a38782e7ebf15ad7f5eb87d Mon Sep 17 00:00:00 2001 From: gmurray81 Date: Wed, 21 Feb 2024 15:30:53 -0500 Subject: [PATCH 07/10] prevent opentk from automatically registering events --- source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs b/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs index b1c6787700..faed75cc81 100644 --- a/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs +++ b/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs @@ -61,7 +61,11 @@ private void Initialize() this.listener = listener; }; - Start(settings); +#if NETCOREAPP + this.RegisterToEventsDirectly = false; +#endif + + Start(settings); } From 6751f0cb26b06ab3d2ba496575440eeafbfb578e Mon Sep 17 00:00:00 2001 From: gmurray81 Date: Wed, 21 Feb 2024 23:23:33 -0500 Subject: [PATCH 08/10] simplify releasing the managed resources --- .../SkiaSharp.Views.WPF/SKGLElement.cs | 194 +++++++----------- 1 file changed, 71 insertions(+), 123 deletions(-) diff --git a/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs b/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs index faed75cc81..3df811b063 100644 --- a/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs +++ b/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs @@ -40,7 +40,6 @@ public class SKGLElement : GLWpfControl, IDisposable private SKCanvas canvas; private SKSizeI lastSize; - private SKGLElementWindowListener listener; public SKGLElement() : base() @@ -50,16 +49,13 @@ public SKGLElement() private void Initialize() { - designMode = DesignerProperties.GetIsInDesignMode(this); - var settings = new GLWpfControlSettings() { MajorVersion = 2, MinorVersion = 1, RenderContinuously = false }; - - this.Render += OnPaint; + designMode = DesignerProperties.GetIsInDesignMode(this); + var settings = new GLWpfControlSettings() { MajorVersion = 2, MinorVersion = 1, RenderContinuously = false }; - this.Loaded += (s, e) => - { - SKGLElementWindowListener listener = new SKGLElementWindowListener(this); - this.listener = listener; - }; + this.Render += OnPaint; + + this.Loaded += SKGLElement_Loaded; + this.Unloaded += SKGLElement_Unloaded; #if NETCOREAPP this.RegisterToEventsDirectly = false; @@ -68,64 +64,14 @@ private void Initialize() Start(settings); } - - private class SKGLElementWindowListener: IDisposable - { - private WeakReference toDestroy; - private WeakReference theWindow; - public SKGLElementWindowListener(SKGLElement toDestroy) - { - this.toDestroy = new WeakReference(toDestroy); - var window = System.Windows.Window.GetWindow(toDestroy); - if (window != null) - { - theWindow = new WeakReference(window); - window.Closing += Window_Closing; - } - } - - private void Window_Closing(object sender, CancelEventArgs e) - { - SKGLElement target = null; - if (toDestroy.TryGetTarget(out target)) - { - if (target != null) - { - target.Dispose(); - } - } - - } - - private bool disposed = false; - - - protected virtual void Dispose(bool disposing) - { - if (disposed) - { - return; - } - - Window window; - if (theWindow != null && theWindow.TryGetTarget(out window)) - { - if (window != null) - { - window.Closing -= Window_Closing; - } - } - theWindow = null; - - disposed = true; - } - - public void Dispose() - { - Dispose(true); - } + private void SKGLElement_Unloaded(object sender, RoutedEventArgs e) + { + Release(); + } + private void SKGLElement_Loaded(object sender, RoutedEventArgs e) + { + InvalidateVisual(); } - public SKSize CanvasSize => lastSize; @@ -134,50 +80,50 @@ public void Dispose() [Category("Appearance")] public event EventHandler PaintSurface; - private SKSizeI GetSize() + private SKSizeI GetSize() { - var currentWidth = ActualWidth; - var currentHeight = ActualHeight; + var currentWidth = ActualWidth; + var currentHeight = ActualHeight; - if (currentWidth < 0 || - currentHeight < 0) + if (currentWidth < 0 || + currentHeight < 0) { - currentWidth = 0; - currentHeight = 0; - } - - PresentationSource source = PresentationSource.FromVisual(this); - - double dpiX = 1.0; - double dpiY = 1.0; - if (source != null) - { - dpiX = source.CompositionTarget.TransformToDevice.M11; - dpiY = source.CompositionTarget.TransformToDevice.M22; + currentWidth = 0; + currentHeight = 0; } - return new SKSizeI((int)(currentWidth * dpiX), (int)(currentHeight * dpiY)); - } + PresentationSource source = PresentationSource.FromVisual(this); - protected override void OnRender(DrawingContext drawingContext) - { - if (grContext != null) - { - grContext.ResetContext(); - } - base.OnRender(drawingContext); - } - - protected virtual void OnPaint(TimeSpan e) - { - if (disposed) - { - return; - } - if (designMode) - { - return; - } + double dpiX = 1.0; + double dpiY = 1.0; + if (source != null) + { + dpiX = source.CompositionTarget.TransformToDevice.M11; + dpiY = source.CompositionTarget.TransformToDevice.M22; + } + + return new SKSizeI((int)(currentWidth * dpiX), (int)(currentHeight * dpiY)); + } + + protected override void OnRender(DrawingContext drawingContext) + { + if (grContext != null) + { + grContext.ResetContext(); + } + base.OnRender(drawingContext); + } + + protected virtual void OnPaint(TimeSpan e) + { + if (disposed) + { + return; + } + if (designMode) + { + return; + } // create the contexts if not done already if (grContext == null) @@ -187,11 +133,11 @@ protected virtual void OnPaint(TimeSpan e) } // get the new surface size - var newSize = GetSize(); + var newSize = GetSize(); - GL.ClearColor(Color4.Transparent); - GL.Clear(ClearBufferMask.ColorBufferBit); - GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit); + GL.ClearColor(Color4.Transparent); + GL.Clear(ClearBufferMask.ColorBufferBit); + GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit); // manage the drawing surface if (renderTarget == null || lastSize != newSize || !renderTarget.IsValid) @@ -201,7 +147,7 @@ protected virtual void OnPaint(TimeSpan e) lastSize = newSize; GL.GetInteger(GetPName.FramebufferBinding, out var framebuffer); - GL.GetInteger(GetPName.StencilBits, out var stencil); + GL.GetInteger(GetPName.StencilBits, out var stencil); GL.GetInteger(GetPName.Samples, out var samples); var maxSamples = grContext.GetMaxSurfaceSampleCount(colorType); if (samples > maxSamples) @@ -241,17 +187,23 @@ protected virtual void OnPaintSurface(SKPaintGLSurfaceEventArgs e) PaintSurface?.Invoke(this, e); } - private bool disposed = false; + private bool disposed = false; - protected virtual void Dispose(bool disposing) - { - if (disposed) + protected virtual void Dispose(bool disposing) + { + if (disposed) { - return; - } + return; + } + + Release(); + + disposed = true; + } - + private void Release() + { canvas = null; surface?.Dispose(); surface = null; @@ -259,13 +211,9 @@ protected virtual void Dispose(bool disposing) renderTarget = null; grContext?.Dispose(); grContext = null; - listener?.Dispose(); - listener = null; - - disposed = true; - } + } - public void Dispose() + public void Dispose() { Dispose(true); } From cbd3d923b9c6b5cdf01cbb8cad13933cf2552ba2 Mon Sep 17 00:00:00 2001 From: gmurray81 Date: Thu, 22 Feb 2024 21:03:51 -0500 Subject: [PATCH 09/10] remove unecessary duplicate clear --- source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs b/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs index 3df811b063..00993eab5d 100644 --- a/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs +++ b/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs @@ -136,7 +136,6 @@ protected virtual void OnPaint(TimeSpan e) var newSize = GetSize(); GL.ClearColor(Color4.Transparent); - GL.Clear(ClearBufferMask.ColorBufferBit); GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit); // manage the drawing surface From fef2c4ee449ecad2428de25c245b720aca869129 Mon Sep 17 00:00:00 2001 From: gmurray81 Date: Fri, 1 Mar 2024 17:32:25 -0500 Subject: [PATCH 10/10] block signing for OpenTK 4+ --- .../SkiaSharp.Views.WPF/SkiaSharp.Views.WPF.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SkiaSharp.Views.WPF.csproj b/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SkiaSharp.Views.WPF.csproj index 14367c1eb1..cc8ba3e40c 100644 --- a/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SkiaSharp.Views.WPF.csproj +++ b/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SkiaSharp.Views.WPF.csproj @@ -7,6 +7,7 @@ SkiaSharp.Views.WPF $(DefineConstants);__DESKTOP__;__WPF__ SkiaSharp Views & Layers for Windows Presentation Foundation (WPF) + false wpf