source/SkiaSharp.Views/SkiaSharp.Views/Platform/macOS/SKGLView.cs (136 lines of code) (raw):

using System; using System.ComponentModel; using AppKit; using CoreGraphics; using Foundation; using SkiaSharp.Views.GlesInterop; #if HAS_UNO_WINUI namespace SkiaSharp.Views.Windows #elif HAS_UNO namespace SkiaSharp.Views.UWP #else namespace SkiaSharp.Views.Mac #endif { [Register(nameof(SKGLView))] [DesignTimeVisible(true)] #if HAS_UNO internal #else public #endif partial class SKGLView : NSOpenGLView { private const SKColorType colorType = SKColorType.Rgba8888; private const GRSurfaceOrigin surfaceOrigin = GRSurfaceOrigin.BottomLeft; private GRContext context; private GRGlFramebufferInfo glInfo; private GRBackendRenderTarget renderTarget; private SKSurface surface; private SKCanvas canvas; private SKSizeI lastSize; private SKSizeI newSize; // created in code public SKGLView() { Initialize(); } // created in code public SKGLView(CGRect frame) : base(frame) { Initialize(); } // created via designer public SKGLView(IntPtr p) : base(p) { } // created via designer public override void AwakeFromNib() { Initialize(); } private void Initialize() { WantsBestResolutionOpenGLSurface = true; var attrs = new NSOpenGLPixelFormatAttribute[] { //NSOpenGLPixelFormatAttribute.OpenGLProfile, (NSOpenGLPixelFormatAttribute)NSOpenGLProfile.VersionLegacy, NSOpenGLPixelFormatAttribute.Accelerated, NSOpenGLPixelFormatAttribute.DoubleBuffer, NSOpenGLPixelFormatAttribute.Multisample, NSOpenGLPixelFormatAttribute.ColorSize, (NSOpenGLPixelFormatAttribute)32, NSOpenGLPixelFormatAttribute.AlphaSize, (NSOpenGLPixelFormatAttribute)8, NSOpenGLPixelFormatAttribute.DepthSize, (NSOpenGLPixelFormatAttribute)24, NSOpenGLPixelFormatAttribute.StencilSize, (NSOpenGLPixelFormatAttribute)8, NSOpenGLPixelFormatAttribute.SampleBuffers, (NSOpenGLPixelFormatAttribute)1, NSOpenGLPixelFormatAttribute.Samples, (NSOpenGLPixelFormatAttribute)4, (NSOpenGLPixelFormatAttribute)0, }; PixelFormat = new NSOpenGLPixelFormat(attrs); } public SKSize CanvasSize => lastSize; public GRContext GRContext => context; public override void PrepareOpenGL() { base.PrepareOpenGL(); // create the context var glInterface = GRGlInterface.Create(); context = GRContext.CreateGl(glInterface); } public override void Reshape() { base.Reshape(); // get the new surface size var size = ConvertSizeToBacking(Bounds.Size); newSize = new SKSizeI((int)size.Width, (int)size.Height); } private nfloat lastBackingScaleFactor = 0; public override void DrawRect(CGRect dirtyRect) { // Track if the scale of the display has changed and if so force the SKGLView to reshape itself. // If this is not done, the output will scale correctly when the window is dragged from a non-retina to a retina display. if (Window != null && lastBackingScaleFactor != Window.BackingScaleFactor) { bool isFirstDraw = lastBackingScaleFactor == 0; lastBackingScaleFactor = Window.BackingScaleFactor; if (!isFirstDraw) { Reshape(); // A redraw will also be necessary. Invoke later or the request will be ignored Invoke(() => { NeedsDisplay = true; }, 0); // do not proceed at the wrong scale return; } } base.DrawRect(dirtyRect); Gles.glClear(Gles.GL_COLOR_BUFFER_BIT | Gles.GL_DEPTH_BUFFER_BIT | Gles.GL_STENCIL_BUFFER_BIT); // create the render target if (renderTarget == null || lastSize != newSize || !renderTarget.IsValid) { // create or update the dimensions lastSize = newSize; // read the info from the buffer Gles.glGetIntegerv(Gles.GL_FRAMEBUFFER_BINDING, out var framebuffer); Gles.glGetIntegerv(Gles.GL_STENCIL_BITS, out var stencil); Gles.glGetIntegerv(Gles.GL_SAMPLES, out var samples); var maxSamples = context.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(context, renderTarget, surfaceOrigin, colorType); canvas = surface.Canvas; } using (new SKAutoCanvasRestore(canvas, true)) { // start drawing var e = new SKPaintGLSurfaceEventArgs(surface, renderTarget, surfaceOrigin, colorType); OnPaintSurface(e); } // flush the SkiaSharp contents to GL canvas.Flush(); context.Flush(); OpenGLContext.FlushBuffer(); } public event EventHandler<SKPaintGLSurfaceEventArgs> PaintSurface; protected virtual void OnPaintSurface(SKPaintGLSurfaceEventArgs e) { PaintSurface?.Invoke(this, e); } } }