news 2026/10/15 18:17:49

SkiaSharp 2.88.0 macOS 视图 API 演进:PaintSurface 事件参数 Info/RawInfo 重构与迁移指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SkiaSharp 2.88.0 macOS 视图 API 演进:PaintSurface 事件参数 Info/RawInfo 重构与迁移指南
  • 图形学
  • 图像处理
  • 跨平台

【免费下载链接】SkiaSharp

SkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Google's 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 2.88.0 是进入 .NET 6 时代的首个稳定版本,其中SkiaSharp.Views.Mac.dll的程序集版本由 2.80.0.0 提升至 2.88.0.0,并伴随一次重要的视图事件参数重构:SKPaintGLSurfaceEventArgs与SKPaintSurfaceEventArgs新增了以SKImageInfo为核心的构造函数和Info/RawInfo属性,同时废弃了携带GRGlFramebufferInfo的旧构造函数。本文以 SkiaSharp.Views.Mac API diff 为骨架,结合 SkiaSharp.Views 源码 与 macOS 平台视图实现 逐层拆解这一变更的含义、动机与迁移方法,帮助你准确理解Info与RawInfo的区别,并顺利将旧代码迁移到新 API。

一、变更背景:2.88.0 与 .NET 6 时代

根据 2.88.0 版本发布说明,SkiaSharp 2.88.0(2022 年 5 月 22 日发布)是第一个为 .NET 6 构建的稳定版本,带来了 .NET MAUI(compat 与 handlers 两套视图)、Mac Catalyst 一等公民支持、WinUI 与 Blazor WebAssembly 组件,并将 HarfBuzz 刷新至 2.8.2。视图层(SkiaSharp.Views)在这次版本中为各平台统一重构了PaintSurface事件的数据承载方式——这正是本文关联文档所记录的 API 变更。

值得注意的一点是:该变更并非 macOS 独有。对比同目录下的 SkiaSharp.Views.iOS diff 与 SkiaSharp.Views.Android diff 可以看到完全相同的构造函数新增、属性新增与废弃标记。这是因为视图事件参数类本身定义在跨平台共享源码 SKPaintGLSurfaceEventArgs.cs 与 SKPaintSurfaceEventArgs.cs 中,通过条件编译符号(如__MACOS__、__IOS__、__ANDROID__)映射到各自的命名空间(macOS 对应SkiaSharp.Views.Mac)。因此本文的分析结论可原样套用于 iOS、tvOS、Android、Tizen、Windows 等平台。

二、API 变更总览:两个事件参数类的完整 diff

2.1 SKPaintGLSurfaceEventArgs:GL 渲染的事件参数

SKPaintGLSurfaceEventArgs用于承载PaintSurface事件的数据,适用于 GPU 加速(OpenGL)渲染路径,在 macOS 上由SKGLView与SKGLLayer触发。本次变更包含三类内容:

废弃的构造函数(Obsoleted):

[Obsolete ("Use SKPaintGLSurfaceEventArgs(SKSurface, GRBackendRenderTarget, GRSurfaceOrigin, SKColorType) instead.")] public SKPaintGLSurfaceEventArgs (SkiaSharp.SKSurface surface, SkiaSharp.GRBackendRenderTarget renderTarget, SkiaSharp.GRSurfaceOrigin origin, SkiaSharp.SKColorType colorType, SkiaSharp.GRGlFramebufferInfo glInfo);

新增的构造函数(Added):

public SKPaintGLSurfaceEventArgs (SkiaSharp.SKSurface surface, SkiaSharp.GRBackendRenderTarget renderTarget, SkiaSharp.GRSurfaceOrigin origin, SkiaSharp.SKImageInfo info); public SKPaintGLSurfaceEventArgs (SkiaSharp.SKSurface surface, SkiaSharp.GRBackendRenderTarget renderTarget, SkiaSharp.GRSurfaceOrigin origin, SkiaSharp.SKImageInfo info, SkiaSharp.SKImageInfo rawInfo);

新增的属性(Added):

public SkiaSharp.SKImageInfo Info { get; } public SkiaSharp.SKImageInfo RawInfo { get; }

2.2 SKPaintSurfaceEventArgs:CPU 渲染的事件参数

SKPaintSurfaceEventArgs服务于 CPU 渲染路径,在 macOS 上由SKCanvasView触发。本次变更为其补充了"原始图像信息"维度:

新增的构造函数:

public SKPaintSurfaceEventArgs (SkiaSharp.SKSurface surface, SkiaSharp.SKImageInfo info, SkiaSharp.SKImageInfo rawInfo);

新增的属性:

public SkiaSharp.SKImageInfo RawInfo { get; }

两个类均新增了RawInfo属性,这正是本次重构的语义核心,下文将结合源码详细展开。

三、源码级解析:构造函数链与 Info/RawInfo 语义

3.1 SKPaintGLSurfaceEventArgs 的完整构造函数链

在 SKPaintGLSurfaceEventArgs.cs 中可以看到,该类现在共有四个构造函数,呈链式结构:

// 便捷入口:默认 BottomLeft 原点 + Rgba8888 颜色类型 public SKPaintGLSurfaceEventArgs(SKSurface surface, GRBackendRenderTarget renderTarget) : this(surface, renderTarget, GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888) { } // 显式指定 origin 与 colorType;Info 由 renderTarget 尺寸推导 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; } // 新增:直接以 SKImageInfo 描述表面;rawInfo 缺省时与 info 相同 public SKPaintGLSurfaceEventArgs(SKSurface surface, GRBackendRenderTarget renderTarget, GRSurfaceOrigin origin, SKImageInfo info) : this(surface, renderTarget, origin, info, info) { } // 新增:info 与 rawInfo 分离,可分别描述"绘制逻辑尺寸"与"底层真实尺寸" public SKPaintGLSurfaceEventArgs(SKSurface surface, GRBackendRenderTarget renderTarget, GRSurfaceOrigin origin, SKImageInfo info, SKImageInfo rawInfo) { Surface = surface; BackendRenderTarget = renderTarget; ColorType = info.ColorType; Origin = origin; Info = info; RawInfo = rawInfo; }

观察可知几个关键行为:

  • ColorType现在从info.ColorType推导,而不是像旧 API 那样单独传入;若使用三参数的colorType版本,Info会自动以renderTarget.Width/Height与传入的ColorType构造(GRBackendRenderTarget 的 Width/Height 属性 最终通过gr_backendrendertarget_get_width/heightP/Invoke 获取原生值);
  • Info与RawInfo默认相等:在colorType版本中两者都指向同一SKImageInfo(RawInfo = Info),三参数的info版本中则通过this(surface, renderTarget, origin, info, info)让两者相同;
  • 只有五参数版本允许两者分离,这也是RawInfo属性真正发挥作用的入口。

3.2 SKPaintSurfaceEventArgs 的构造函数链

SKPaintSurfaceEventArgs.cs 的逻辑更加简洁:

public SKPaintSurfaceEventArgs(SKSurface surface, SKImageInfo info) : this(surface, info, info) { } public SKPaintSurfaceEventArgs(SKSurface surface, SKImageInfo info, SKImageInfo rawInfo) { Surface = surface; Info = info; RawInfo = rawInfo; }

原先的(surface, info)两参数构造函数被保留并委托给新增的三参数版本,RawInfo默认与Info相同,向后兼容性得到保证。

3.3 Info 与 RawInfo 到底有什么区别?

从文档注释与源码调用可以确认两者的语义(源码注释):

  • Info:当前表面(surface)的图像信息,即绘制代码应使用的逻辑尺寸——例如在IgnorePixelScaling场景下它对应视图的"设备无关"尺寸;
  • RawInfo:底层表面(underlying surface)的原始图像信息,即实际物理像素尺寸与颜色类型,用于需要精确感知真实缓冲区大小的场合。

在SKCanvasView的 macOS 实现中,两者的分离有非常具体的用例(详见下节):当IgnorePixelScaling开启时,Info是视图的 Bounds 尺寸(逻辑点),而RawInfo是计入BackingScaleFactor后的物理像素尺寸——绘制者用Info定位,而底层缓冲区依然以RawInfo为准。

四、macOS 平台实测:三个视图组件的触发路径

4.1 SKGLView:GPU 加速视图(NSOpenGLView)

SKGLView.cs 是 macOS 上的硬件加速视图,继承自NSOpenGLView,内部固定使用SKColorType.Rgba8888与GRSurfaceOrigin.BottomLeft。在DrawRect中,它按需重建GRBackendRenderTarget与SKSurface,随后构造事件参数并触发绘制:

using (new SKAutoCanvasRestore(canvas, true)) { // 触发 PaintSurface 事件 var e = new SKPaintGLSurfaceEventArgs(surface, renderTarget, surfaceOrigin, colorType); OnPaintSurface(e); } // flush 到 OpenGL canvas.Flush(); context.Flush(); OpenGLContext.FlushBuffer();

这里的colorType版本构造函数会把Info自动推导为renderTarget的宽高 ×Rgba8888。在事件处理器中,你可以通过e.BackendRenderTarget.Width/Height或新的e.Info.Width/Height获取尺寸(官方示例注释):

myView.PaintSurface += (sender, e) => { var surface = e.Surface; var surfaceWidth = e.BackendRenderTarget.Width; var surfaceHeight = e.BackendRenderTarget.Height; var canvas = surface.Canvas; // 在 canvas 上绘制 canvas.Flush(); };

4.2 SKGLLayer:Core Animation OpenGL 层

SKGLLayer.cs 继承自CAOpenGLLayer,适合嵌入 Core Animation 层级结构。其DrawInCGLContext方法在每次需要绘制时按Bounds * ContentsScale计算像素尺寸并重建渲染目标,最终同样以new SKPaintGLSurfaceEventArgs(surface, renderTarget, surfaceOrigin, colorType)触发事件(源码位置),绘制完成后调用canvas.Flush()与context.Flush()将内容提交给 OpenGL。

4.3 SKCanvasView:CPU 渲染视图——RawInfo 的真实用例

SKCanvasView.cs 是 CPU 渲染路径(基于 Core Graphics 表面工厂),其DrawRect中正是使用了本次新增的五参数/三参数重载来区分逻辑尺寸与物理尺寸:

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) { // Info = 用户可见尺寸,RawInfo = 底层实际像素尺寸 OnPaintSurface(new SKPaintSurfaceEventArgs(surface, info.WithSize(userVisibleSize), info)); drawable.DrawSurface(ctx, Bounds, info, surface); } }

这正是RawInfo引入的典型动机:当IgnorePixelScaling为true时,Info被WithSize调整为视图的逻辑尺寸(绘制代码无需关心像素密度),而RawInfo保留了CreateSurface返回的物理像素信息(底层缓冲区的真实宽高与颜色类型)。这也呼应了 2.88.0 修复的 "IgnorePixelScaling 现在按预期工作" 这一 Bug Fix——重构后逻辑尺寸与物理尺寸被显式分开,不再互相干扰。

从源码结构可以推断:任何需要"以逻辑坐标绘制、但以物理尺寸感知缓冲区"的场景,都应该读取RawInfo而非Info。

五、为什么废弃 glInfo 构造函数?

diff 中被废弃的构造函数多携带一个GRGlFramebufferInfo glInfo参数:

[Obsolete("Use SKPaintGLSurfaceEventArgs(SKSurface, GRBackendRenderTarget, GRSurfaceOrigin, SKColorType) instead.")] public SKPaintGLSurfaceEventArgs(SKSurface surface, GRBackendRenderTarget renderTarget, GRSurfaceOrigin origin, SKColorType colorType, GRGlFramebufferInfo glInfo);

在当前的共享源码中该构造函数已完全移除,GRBackendRenderTarget自身已封装了帧缓冲信息(Width、Height、SampleCount、IsValid等均通过 P/Invoke 查询原生句柄,见 GRBackendRenderTarget.cs)。也就是说,glInfo参数在信息表达上是冗余的——renderTarget本身就携带了足够信息,同时Info由SKImageInfo统一描述(包含宽高、ColorType、AlphaType、ColorSpace,见 SKImageInfo.cs),比单独传一个颜色类型加一个帧缓冲结构更完整、更一致。这既是 API 精简,也是向"以SKImageInfo为中心描述表面"这一统一模型靠拢。

六、迁移指南:从旧 API 平滑升级

6.1 使用旧的 glInfo 构造函数的代码

旧写法(2.80 及更早):

var e = new SKPaintGLSurfaceEventArgs(surface, renderTarget, origin, colorType, glInfo);

迁移到新 API 的最简形式(编译器会提示 Obsolete 警告,指向此写法):

var e = new SKPaintGLSurfaceEventArgs(surface, renderTarget, origin, colorType);

由于colorType版本内部会自动以renderTarget.Width/Height构造Info,行为与旧 API 完全等价。若你需要显式控制颜色类型与透明度(例如希望Info携带特定SKAlphaType或SKColorSpace),可使用新增的SKImageInfo版本:

var info = new SKImageInfo(renderTarget.Width, renderTarget.Height, colorType, SKAlphaType.Premul); var e = new SKPaintGLSurfaceEventArgs(surface, renderTarget, origin, info);

6.2 自定义视图/绘制逻辑中使用 RawInfo

若你手动构造事件参数并希望区分逻辑尺寸与物理尺寸(例如实现自定义的像素缩放绘制),使用五参数版本:

var e = new SKPaintGLSurfaceEventArgs( surface, renderTarget, origin, info, // Info:绘制逻辑使用的尺寸 rawInfo); // RawInfo:底层缓冲区的真实尺寸

6.3 事件处理器侧的读取建议

  • 绘制布局使用e.Info.Width/e.Info.Height(SKPaintSurfaceEventArgs与SKPaintGLSurfaceEventArgs均可用,比e.BackendRenderTarget.Width更直接);
  • 需要感知真实像素缓冲区(如截图、像素级操作、离屏纹理大小)时读取e.RawInfo;
  • SKPaintGLSurfaceEventArgs的ColorType/Origin属性继续保留,与Info/RawInfo并存。

七、小结

SkiaSharp 2.88.0 对SKPaintGLSurfaceEventArgs与SKPaintSurfaceEventArgs的重构,本质上是将"表面信息"从零散的GRGlFramebufferInfo + SKColorType组合,收敛为以SKImageInfo为核心、以Info/RawInfo双视角描述表面的统一模型:Info面向绘制逻辑,RawInfo面向底层真实缓冲区。macOS 的SKCanvasView(CPU)、SKGLView与SKGLLayer(GPU)三套组件均已按新模型触发事件,iOS/Android 等其他平台的 diff 与此完全一致,迁移成本极低——旧构造函数被[Obsolete]标记但未立即删除,编译器会给出明确的方向指引。对于正在升级到 SkiaSharp 2.88.0+ 的 macOS 应用,按照本文第六节的示例替换构造函数即可平滑完成迁移,并可立即受益于更准确的尺寸语义。

  • 图形学
  • 图像处理
  • 跨平台

【免费下载链接】SkiaSharp

SkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Google's 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
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 18:17:02

GBase 8s数据库root dbspace保留页浅析

南大通用GBase 8s数据库(gbase database)root dbspace 初始 chunk 最前面 12 个页面是保留页,每一页都存放数据库的控制信息与跟踪元数据,是数据库运行的基础元数据载体。我们可以通过 oncheck 工具查看保留页内容,还可…

作者头像 李华
网站建设 2026/10/15 18:16:05

Osip SIP信令解析与注入实战:从抓包到注册全流程调试

简介:本资源是一套基于Osip开源SIP协议栈的完整开发示例工程,面向通信协议初学者、嵌入式VoIP开发者及C网络编程实践者,旨在解决SIP信令交互核心逻辑难上手、Osip API封装不直观等实际问题。压缩包含166个文件,总大小1.64MB&#…

作者头像 李华
网站建设 2026/10/15 18:15:51

ESP32C3-SuperMini PlatformIO板级配置:从USB CDC到Flash分区实战

简介:ESP32-C3 SuperMini 开发板以超小尺寸和 RISC-V 架构受到众多物联网及嵌入式开发者关注,尤其适合可穿戴设备、传感器节点等体积受限的场景,但 PlatformIO 官方仓库中并未直接内置该板卡的板级定义,导致创建项目时无法找到对应…

作者头像 李华
网站建设 2026/10/15 18:15:39

Linux 下 FastExport 1.7 安装配置与大数据导出实战

1. 为什么还要在 Linux 上折腾 FastExport 这种"老古董" FastExport 是 Teradata 生态里专门用来做 大批量数据导出 的并行工具,走的是 FastLoad 那条底层通道,跟普通的 BTEQ 导出完全不是一个量级。BTEQ 导出几千万行数据,你可能…

作者头像 李华