- 图形学
- 图像处理
- 跨平台
【免费下载链接】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.
导读
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.
相关推荐
SkiaSharp 2.88.0 WinUI 视图 API 解析:SKPaintSurfaceEventArgs 的 Info/RawInfo 演进与 PaintSurface 事件实战
SkiaSharp 2.88.0 WinUI 视图 API 解析:SKPaintSurfaceEventArgs 的 Info/RawInfo 演进与 Pain
图形学图像处理跨平台ng-zorro-antd Resizable 实战:为 nz-table 表头实现拖拽调整列宽
ng zorro antd Resizable 实战:为 nz table 表头实现拖拽调整列宽 本指南聚焦于 ng zorro antd 中 resizabl
图形学图像处理跨平台SkiaSharp 2.88.0 视图层 API 演进:SkiaSharp.Views.UWP 的 SKPaintGLSurfaceEventArgs 与 RawInfo 迁移指南
SkiaSharp 2.88.0 视图层 API 演进:SkiaSharp.Views.UWP 的 SKPaintGLSurfaceEventArgs 与 Ra
图形学图像处理跨平台
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考