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..00993eab5d --- /dev/null +++ b/source/SkiaSharp.Views/SkiaSharp.Views.WPF/SKGLElement.cs @@ -0,0 +1,221 @@ +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; + + 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 += SKGLElement_Loaded; + this.Unloaded += SKGLElement_Unloaded; + +#if NETCOREAPP + this.RegisterToEventsDirectly = false; +#endif + + Start(settings); + } + + private void SKGLElement_Unloaded(object sender, RoutedEventArgs e) + { + Release(); + } + private void SKGLElement_Loaded(object sender, RoutedEventArgs e) + { + InvalidateVisual(); + } + + 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 | 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 + OnPaintSurface(new SKPaintGLSurfaceEventArgs(surface, renderTarget, surfaceOrigin, colorType)); + } + + // 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; + } + + Release(); + + disposed = true; + } + + private void Release() + { + canvas = null; + surface?.Dispose(); + surface = null; + renderTarget?.Dispose(); + renderTarget = null; + grContext?.Dispose(); + grContext = null; + } + + 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..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,8 +7,17 @@ SkiaSharp.Views.WPF $(DefineConstants);__DESKTOP__;__WPF__ SkiaSharp Views & Layers for Windows Presentation Foundation (WPF) + false wpf + + + + + + + +