SkiaSharp 1.68.0 macOS 视图 API 变更解析:从 SKDelegate 迁移到 PaintSurface 事件模型
图形学图像处理跨平台【免费下载链接】SkiaSharpSkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Googles Skia Graphics Library. It provides a comprehensive 2D API that can be used across mobile, server and desktop models to render images.项目地址https://gitcode.com/gh_mirrors/sk/SkiaSharp点击查看免费下载导读本文以 SkiaSharp.Views.Mac.md 这份官方 API diff 文档为核心系统讲解 SkiaSharp 1.68.0 中 macOSAppKit平台视图控件的 API 变更SKCanvasView、SKCanvasLayer、SKGLView、SKGLLayer四个类型从旧的委托delegate绘制模型整体迁移到PaintSurface事件与OnPaintSurface虚方法模型同时SKPaintGLSurfaceEventArgs以GRBackendRenderTarget取代了GRBackendRenderTargetDesc。读完本文你将理解这次变更的来龙去脉、每个废弃成员与新增成员的对应关系以及如何将既有代码平滑迁移到新 API。一、版本背景与变更总览本次 API 差异API diff的对比基线为1.60.0.0 与 1.68.0.0涉及程序集SkiaSharp.Views.Mac.dll命名空间为SkiaSharp.Views.Mac。变更集中在以下两条主线上绘制入口的统一废弃SKDelegate委托属性与DrawInSurface虚方法新增OnPaintSurface虚方法配合既有的PaintSurface事件形成事件 虚方法双入口绘制模型。这与 Android、iOS、tvOS、Tizen 等平台早已采用的模型保持一致。GPU 后端描述对象升级SKPaintGLSurfaceEventArgs的构造与属性从GRBackendRenderTargetDesc描述结构体切换到GRBackendRenderTarget真实后端渲染目标对象并补充了ColorType与Origin两个属性。所有废弃成员均带有[Obsolete]特性编译器会在迁移时给出明确的替换提示例如Use PaintSurface instead.、Use OnPaintSurface(SKPaintSurfaceEventArgs) instead.等。二、CPU 渲染视图SKCanvasLayer 与 SKCanvasView2.1 SKCanvasLayer委托属性与绘制方法双双废弃SKCanvasLayerCoreAnimation 图层基于CALayer软件渲染在本次版本中发生两处废弃[Obsolete (Use PaintSurface instead.)] public ISKCanvasLayerDelegate SKDelegate { get; set; }[Obsolete (Use OnPaintSurface(SKPaintSurfaceEventArgs) instead.)] public virtual void DrawInSurface (SkiaSharp.SKSurface surface, SkiaSharp.SKImageInfo info);同时新增绘制入口protected virtual void OnPaintSurface (SKPaintSurfaceEventArgs e);从 SKCanvasLayer 源码 可以看到新模型的完整绘制链路DrawInContext(CGContext)通过SKCGSurfaceFactory创建 Skia 表面后直接构造SKPaintSurfaceEventArgs并调用OnPaintSurface随后把结果绘制到 CoreGraphics 上下文public override void DrawInContext(CGContext ctx) { // create the skia context using var surface drawable.CreateSurface(Bounds, ContentsScale, out var info); if (info.Width 0 || info.Height 0) { CanvasSize SKSize.Empty; return; } var userVisibleSize IgnorePixelScaling ? new SKSizeI((int)Bounds.Width, (int)Bounds.Height) : info.Size; CanvasSize userVisibleSize; // draw on the image using SKiaSharp OnPaintSurface(new SKPaintSurfaceEventArgs(surface, info.WithSize(userVisibleSize), info)); // draw the surface to the context drawable.DrawSurface(ctx, Bounds, info, surface); }2.2 SKCanvasView废弃 DrawInSurface新增 OnPaintSurfaceSKCanvasViewAppKit 视图基于NSView软件渲染同样废弃了DrawInSurface[Obsolete (Use OnPaintSurface(SKPaintSurfaceEventArgs) instead.)] public virtual void DrawInSurface (SkiaSharp.SKSurface surface, SkiaSharp.SKImageInfo info);新增protected virtual void OnPaintSurface (SKPaintSurfaceEventArgs e);在 SKCanvasView 源码 中DrawRect覆写负责创建表面、处理高分辨率像素缩放IgnorePixelScaling、调用OnPaintSurface并最终把 Skia 绘制结果合成到当前 CGContextpublic override void DrawRect(CGRect dirtyRect) { base.DrawRect(dirtyRect); // create the skia context using (var surface drawable.CreateSurface(Bounds, Window.BackingScaleFactor, out var info)) { if (info.Width 0 || info.Height 0) { CanvasSize SKSize.Empty; return; } var userVisibleSize IgnorePixelScaling ? new SKSizeI((int)Bounds.Width, (int)Bounds.Height) : info.Size; CanvasSize userVisibleSize; if (IgnorePixelScaling) { var skiaCanvas surface.Canvas; skiaCanvas.Scale((float)Window.BackingScaleFactor); skiaCanvas.Save(); } using (var ctx NSGraphicsContext.CurrentContext.CGContext) { // draw on the image using SKiaSharp OnPaintSurface(new SKPaintSurfaceEventArgs(surface, info.WithSize(userVisibleSize), info)); // draw the surface to the context drawable.DrawSurface(ctx, Bounds, info, surface); } } }值得注意的细节SKPaintSurfaceEventArgs的构造使用了三个参数——surface、info用户可见尺寸info.WithSize(userVisibleSize)与rawInfo原始像素尺寸这正是IgnorePixelScaling语义的体现当IgnorePixelScaling true时画布使用设备无关像素并拉伸到视图虽提升性能但可能出现模糊或像素化。2.3 底层支撑SKCGSurfaceFactory两个 CPU 视图共用的 SKCGSurfaceFactory 内部类揭示了软件渲染的完整数据流它按Rgba8888颜色类型与Premul预乘 Alpha 分配NSMutableData位图内存通过SKSurface.Create(info, bitmapData.MutableBytes, info.RowBytes)创建 Skia 表面绘制完成后再把位图封装成CGImage交给 CoreGraphics 显示。这也是CanvasSize可能与视图大小不一致受像素密度影响的根本原因。三、GPU 渲染视图SKGLView 与 SKGLLayer3.1 SKGLLayer委托与 RenderTargetDesc 参数同时废弃SKGLLayer基于CAOpenGLLayer的 CoreAnimation OpenGL 图层的废弃项最多[Obsolete (Use PaintSurface instead.)] public ISKGLLayerDelegate SKDelegate { get; set; }[Obsolete (Use OnPaintSurface(SKPaintGLSurfaceEventArgs) instead.)] public virtual void DrawInSurface (SkiaSharp.SKSurface surface, SkiaSharp.GRBackendRenderTargetDesc renderTarget);新增protected virtual void OnPaintSurface (SKPaintGLSurfaceEventArgs e);从 SKGLLayer 源码 可以看到 GPU 渲染的完整管线DrawInCGLContext中懒创建GRContext通过glGetIntegerv读取帧缓冲、模板位与采样数构造GRGlFramebufferInfo与GRBackendRenderTarget再用SKSurface.Create(context, renderTarget, surfaceOrigin, colorType)创建 GPU 表面最后在SKAutoCanvasRestore保护下调用OnPaintSurface并canvas.Flush()、context.Flush()// read the info from the buffer int framebuffer 0, stencil 0, samples 0; unsafe { Gles.glGetIntegerv(Gles.GL_FRAMEBUFFER_BINDING, framebuffer); Gles.glGetIntegerv(Gles.GL_STENCIL_BITS, stencil); Gles.glGetIntegerv(Gles.GL_SAMPLES, samples); } var maxSamples context.GetMaxSurfaceSampleCount(colorType); if (samples maxSamples) samples maxSamples; glInfo new GRGlFramebufferInfo((uint)framebuffer, colorType.ToGlSizedFormat()); ... renderTarget new GRBackendRenderTarget(newSize.Width, newSize.Height, samples, stencil, glInfo); ... surface SKSurface.Create(context, renderTarget, surfaceOrigin, colorType); ... var e new SKPaintGLSurfaceEventArgs(surface, renderTarget, surfaceOrigin, colorType); OnPaintSurface(e);注意这里SKPaintGLSurfaceEventArgs的构造已经使用新 APIsurface、renderTarget、surfaceOriginGRSurfaceOrigin.BottomLeft与colorTypeSKColorType.Rgba8888。3.2 SKGLView废弃 DrawInSurface使用 NSOpenGLView 硬件加速SKGLView基于NSOpenGLView的硬件加速视图的变更与SKGLLayer对称[Obsolete (Use OnPaintSurface(SKPaintGLSurfaceEventArgs) instead.)] public virtual void DrawInSurface (SkiaSharp.SKSurface surface, SkiaSharp.GRBackendRenderTargetDesc renderTarget);新增protected virtual void OnPaintSurface (SKPaintGLSurfaceEventArgs e);在 SKGLView 源码 中DrawRect覆写不仅处理了跨 Retina 显示器的缩放重排通过跟踪Window.BackingScaleFactor变化强制Reshape还在每次绘制前检查渲染目标尺寸与有效性按需重建GRBackendRenderTarget与SKSurface绘制完成后依次调用canvas.Flush()、context.Flush()与OpenGLContext.FlushBuffer()完成帧缓冲交换。四、SKPaintGLSurfaceEventArgs从描述结构体到真实渲染目标4.1 废弃的构造与属性SKPaintGLSurfaceEventArgs是 GPU 绘制事件的参数载体本次版本完成了从描述到实体的转变[Obsolete (Use SKPaintGLSurfaceEventArgs(SKSurface, GRBackendRenderTarget, SKColorType, GRSurfaceOrigin) instead.)] public SKPaintGLSurfaceEventArgs (SkiaSharp.SKSurface surface, SkiaSharp.GRBackendRenderTargetDesc renderTarget);[Obsolete (Use BackendRenderTarget instead.)] public SkiaSharp.GRBackendRenderTargetDesc RenderTarget { get; }GRBackendRenderTargetDesc只是一个描述渲染目标参数的结构体而GRBackendRenderTarget是持有真实 GPU 资源帧缓冲 ID、尺寸、采样数、模板位等的渲染目标对象后者在 Skia 中可直接用于创建 GPU 表面。4.2 新增构造与属性新增两个构造函数public SKPaintGLSurfaceEventArgs (SkiaSharp.SKSurface surface, SkiaSharp.GRBackendRenderTarget renderTarget); public SKPaintGLSurfaceEventArgs (SkiaSharp.SKSurface surface, SkiaSharp.GRBackendRenderTarget renderTarget, SkiaSharp.GRSurfaceOrigin origin, SkiaSharp.SKColorType colorType);新增三个属性public SkiaSharp.GRBackendRenderTarget BackendRenderTarget { get; } public SkiaSharp.SKColorType ColorType { get; } public SkiaSharp.GRSurfaceOrigin Origin { get; }在 SKPaintGLSurfaceEventArgs 源码 中可以看到实现细节两参数构造函数默认采用GRSurfaceOrigin.BottomLeft与SKColorType.Rgba8888与 macOS 视图内部的默认值一致四参数构造函数则从renderTarget.Width/Height与colorType合成Info与RawInfopublic SKPaintGLSurfaceEventArgs(SKSurface surface, GRBackendRenderTarget renderTarget) : this(surface, renderTarget, GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888) { } public SKPaintGLSurfaceEventArgs(SKSurface surface, GRBackendRenderTarget renderTarget, GRSurfaceOrigin origin, SKColorType colorType) { Surface surface; BackendRenderTarget renderTarget; ColorType colorType; Origin origin; Info new SKImageInfo(renderTarget.Width, renderTarget.Height, ColorType); RawInfo Info; }与之相对的软件渲染参数类型 SKPaintSurfaceEventArgs 提供了Surface、Info、RawInfo三个属性其中Info是绘制时用户可见的尺寸信息RawInfo是底层表面的原始图像信息。五、迁移指南旧代码改造示例5.1 旧的委托delegate模式在 1.68.0 之前macOS 平台需要实现ISKCanvasLayerDelegate/ISKGLLayerDelegate接口或直接重写DrawInSurface例如// 旧写法现已废弃 public class MyLayerDelegate : ISKCanvasLayerDelegate { public void DrawInSurface(SKSurface surface, SKImageInfo info) { var canvas surface.Canvas; canvas.Clear(SKColors.White); // ... 绘制逻辑 canvas.Flush(); } } myLayer.SKDelegate new MyLayerDelegate();5.2 新的事件event模式迁移后只需订阅PaintSurface事件// 新写法 myView.PaintSurface (sender, e) { var surface e.Surface; var surfaceWidth e.Info.Width; var surfaceHeight e.Info.Height; var canvas surface.Canvas; canvas.Clear(SKColors.White); // 绘制逻辑 canvas.Flush(); };对于 GPU 视图事件参数改为SKPaintGLSurfaceEventArgs通过e.BackendRenderTarget.Width/Height获取尺寸myGLView.PaintSurface (sender, e) { var surface e.Surface; var surfaceWidth e.BackendRenderTarget.Width; var surfaceHeight e.BackendRenderTarget.Height; var canvas surface.Canvas; // 绘制逻辑 canvas.Flush(); };5.3 重写 OnPaintSurface 的注意事项如果选择继承并重写OnPaintSurface必须调用base.OnPaintSurface(e)否则PaintSurface事件不会触发源码中OnPaintSurface的默认实现就是PaintSurface?.Invoke(this, e)见 SKCanvasView.cs 与 SKGLLayer.csprotected override void OnPaintSurface(SKPaintSurfaceEventArgs e) { // 必须先调用基类否则事件不会触发 base.OnPaintSurface(e); var surface e.Surface; var surfaceWidth e.Info.Width; var surfaceHeight e.Info.Height; var canvas surface.Canvas; // 绘制逻辑 canvas.Flush(); }5.4 手工构造新事件参数在自定义渲染管线中若需要手工构造 GPU 事件参数应改用新的构造函数// 旧写法已废弃依赖描述结构体 // var args new SKPaintGLSurfaceEventArgs(surface, renderTargetDesc); // 新写法直接使用真实渲染目标并显式声明原点与颜色类型 var args new SKPaintGLSurfaceEventArgs( surface, renderTarget, // GRBackendRenderTarget GRSurfaceOrigin.BottomLeft, // Origin SKColorType.Rgba8888); // ColorType六、与官方示例对照macOS 示例项目仓库中的 macOS 示例项目 samples/Basic/macOS 演示了本次变更后的推荐用法覆盖 CPU、GPUOpenGL、GPUMetal与自由绘画四类场景CpuViewController.cs 展示了SKCanvasView的典型用法在ViewDidLoad中设置skiaView.IgnorePixelScaling true并订阅PaintSurface事件在事件处理器中通过e.Surface.Canvas、e.Info.Width/Height完成径向渐变背景、半透明圆与文字绘制并在ViewWillDisappear中解除订阅public override void ViewDidLoad() { base.ViewDidLoad(); skiaView.IgnorePixelScaling true; skiaView.PaintSurface OnPaintSurface; } public override void ViewWillDisappear() { base.ViewWillDisappear(); skiaView.PaintSurface - OnPaintSurface; } void OnPaintSurface(object? sender, SKPaintSurfaceEventArgs e) { var canvas e.Surface.Canvas; var width e.Info.Width; var height e.Info.Height; // 使用 SKShader、SKPaint、SKFont 等完成绘制 }GpuGLViewController.cs 展示了SKGLView的实时渲染用法以 60 FPS 的NSTimer触发skiaView.NeedsDisplay true驱动重绘在SKPaintGLSurfaceEventArgs中读取e.Info.Width/Height结合SKRuntimeEffect.BuildShader编译的 SkSL 元球着色器完成 GPU 动画void OnPaintSurface(object? sender, SKPaintGLSurfaceEventArgs e) { var canvas e.Surface.Canvas; var width e.Info.Width; var height e.Info.Height; var builder shaderBuilder.Value; builder.Uniforms[iTime] fpsCounter.ElapsedSeconds; builder.Uniforms[iResolution] new float[] { width, height }; using var shader builder.Build(); using var paint new SKPaint { Shader shader }; canvas.DrawRect(0, 0, width, height, paint); }该示例的运行方式为需要 .NET 8 SDK 与 macOS workloaddotnet workload install macos dotnet build -f net8.0-macos dotnet run -f net8.0-macos七、总结SkiaSharp 1.68.0 对 macOS 视图的 API 重构是一次绘制模型统一SKCanvasView、SKCanvasLayer的DrawInSurface与SKDelegate以及SKGLView、SKGLLayer的DrawInSurface与SKDelegate全部标记为[Obsolete]取而代之的是各平台一致的PaintSurface事件 OnPaintSurface虚方法SKPaintGLSurfaceEventArgs则从基于GRBackendRenderTargetDesc的描述式参数升级为基于GRBackendRenderTarget的实体式参数并新增ColorType、Origin属性以承载更完整的渲染目标信息。对于存量代码迁移路径清晰把实现委托接口或重写DrawInSurface的绘制逻辑平移到PaintSurface事件处理函数或重写OnPaintSurface并记得调用baseGPU 场景中将GRBackendRenderTargetDesc换成GRBackendRenderTarget即可。旧 API 虽然仍可编译使用仅告警但建议尽快迁移以与 Android、iOS、tvOS 等其他平台保持一致并为后续版本移除旧 API 做好准备。赞分享图形学图像处理跨平台【免费下载链接】SkiaSharpSkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Googles Skia Graphics Library. It provides a comprehensive 2D API that can be used across mobile, server and desktop models to render images.项目地址https://gitcode.com/gh_mirrors/sk/SkiaSharp点击查看免费下载相关推荐SkiaSharp 1.68.0 Android 视图 API 变更解读从 OnDraw/OnDrawFrame 迁移到 PaintSurface 事件模型SkiaSharp 1.68.0 Android 视图 API 变更解读从 OnDraw/OnDrawFrame 迁移到 PaintSurface 事件模型图形学图像处理跨平台SkiaSharp.Views.Desktop 1.68.0 API 变更解析SKPaintGLSurfaceEventArgs 迁移指南SkiaSharp.Views.Desktop 1.68.0 API 变更解析SKPaintGLSurfaceEventArgs 迁移指南 导读 本文基于 S图形学图像处理跨平台SkiaSharp 1.68.0 发布解读GPU 渲染目标重构、API 大规模变更与迁移实战指南SkiaSharp 1.68.0 发布解读GPU 渲染目标重构、API 大规模变更与迁移实战指南 本文基于 SkiaSharp 开源仓库的官方发布说明 1.6图形学图像处理跨平台上一篇揭秘Infinite Drum Machine的核心技术t-SNE算法如何让声音可视化下一篇昇腾AI神经网络加速OpenCV CANNOPS模块深度解析与实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考