SkiaSharp 1.55.0 版本 API 变更全解析:破坏性改动、新增能力与迁移指南

原创2026-10-12 11:42:301,988 阅读
文章标签:图形学跨平台

SkiaSharp 1.55.0 版本 API 变更全解析:破坏性改动、新增能力与迁移指南

导读

SkiaSharp 1.55.0(发布于 2016 年 11 月 10 日)是 .NET 生态中基于 Google Skia 图形库的跨平台 2D 绘图 API 的一次重要"API 清理"版本:它移除了公共 API 上的可选参数默认值、将 SKCodecOptions.Subset 改为可空类型,并新增了一批沿用至今的核心能力(SKBlendMode 枚举、SKPath 布尔运算、SKRegion、SKPathMeasure、颜色解析等)。本文以 1.55.0 版本发布说明 与 API diff 文档 为骨架,结合当前仓库中的 SkiaSharp 绑定源码 逐项核实这些 API 的现状与底层实现,帮助读者完整理解这次变更的影响面、掌握从 1.54 迁移到 1.55 的具体改造点,并了解这些 API 在现代版本中的实际用法。

本文涉及的 API diff 明细位于 SkiaSharp/SkiaSharp.md(完整变更)与 SkiaSharp/SkiaSharp.breaking.md(仅破坏性变更摘要)。

一、版本概览与变更主题

1.55.0 的整体主题可以概括为三句话:

  • 削减公共 API 上的可选参数:SKCanvas、GRContext、SKPath 上的若干方法不再提供默认参数值,调用方必须显式传参或改用对应的重载,以保证重载解析无歧义、ABI 在未来版本中保持稳定;
  • 让"可选"真正可空:SKCodecOptions.Subset 由 SKRectI 改为 SKRectI?,HasSubset 去掉 setter 变为只读,用 null 明确表达"不进行子集解码";
  • 清理遗留 API:移除 SKCodec.GetValidSubset(旧签名)、SKDocument.Close(旧签名)等遗留辅助方法,并废弃 SKXferMode、SKRegionOperation 等旧命名,统一迁移到 SKBlendMode、SKClipOperation 新命名体系。

本次变更涉及的程序集版本号由 1.54.0.0 提升为 1.55.0.0,属于 ABI 层面的变化,官方明确要求针对新签名重新编译。

二、破坏性变更逐项解析与迁移指南

2.1 公共 API 默认参数值被移除

这是 1.55.0 最具影响力的破坏性变更。以下方法在 1.54 中带有默认参数,调用时可以省略,1.55 起必须显式传入:

类型 方法 1.54 签名(含默认值) 1.55 签名(无默认值)
SKCanvas ClipPath ClipPath(SKPath path, SKRegionOperation operation = 1, bool antialias = false) ClipPath(SKPath path, SKClipOperation operation, bool antialias)
SKCanvas ClipRect ClipRect(SKRect rect, SKRegionOperation operation = 1, bool antialias = false) ClipRect(SKRect rect, SKClipOperation operation, bool antialias)
SKCanvas DrawColor DrawColor(SKColor color, SKXferMode mode = 1) DrawColor(SKColor color, SKBlendMode mode)
GRContext Flush Flush(GRContextFlushBits flagsBitfield = 0) Flush(GRContextFlushBits flagsBitfield)

对应变更在 API diff 文档 的 GRContext 与 SKCanvas 小节中均有明确记录。可以看到,参数类型本身也同步升级:SKRegionOperation → SKClipOperation,SKXferMode → SKBlendMode。

迁移建议:

  • 原来的 canvas.ClipRect(rect) 现在需要写成 canvas.ClipRect(rect, SKClipOperation.Intersect, false),或直接调用新增的无参便捷重载(见下文);
  • 原来的 context.Flush() 现在可调用新增的无参 Flush() 重载。从当前源码 GRContext.cs 可以看到,public void Flush () => Flush (true) 内部会转调 Flush(bool submit, bool synchronous = false),进而调用底层 SkiaApi.gr_direct_context_flush_and_submit 或 gr_direct_context_flush,并配合 GC.KeepAlive 保证原生句柄在调用期间不被回收。

移除默认参数值本身并不改变行为,但它消除了重载解析的歧义,也让 ABI 不依赖编译期默认参数插值,为后续演进留出空间。

2.2 SKCodecOptions.Subset 变为可空

SKCodecOptions 是 SKCodec 解码时的选项结构,本次变更如下:

 public bool HasSubset { get; ---set;--- }
-public SKRectI Subset { get; set; }
+public SKRectI? Subset { get; set; }

同时新增了构造函数 public SKCodecOptions (SKRectI subset)。当前仓库 Definitions.cs 中该类型的现状为:

public SKRectI? Subset { readonly get; set; }
public readonly bool HasSubset => Subset != null;

HasSubset 成为由 Subset != null 推导的只读属性,且 ZeroInitialized、FrameIndex、PriorFrame 等其余选项保持不变(PriorFrame 为 -1 表示没有前置帧)。

为什么改成可空? 可空类型比"默认矩形 + 标志位"的组合更能准确表达语义:Subset 为 null 即表示"不裁剪、解码整幅图像",配合只读的 HasSubset 查询,杜绝了"设置了 Subset 却忘记同步 HasSubset"这类不一致状态。在 SKCodec.cs 等解码入口中,源码会通过 options.HasSubset 判断是否启用子集解码,改造后这一判断与 Subset 的实际值天然保持一致。

迁移建议:

// 1.54:不裁剪需要显式构造一个"空"矩形
var opts = new SKCodecOptions { Subset = new SKRectI() };

// 1.55:直接传 null(或不赋值),语义更清晰
var opts = new SKCodecOptions();   // Subset 默认为 null
var opts2 = new SKCodecOptions(new SKRectI(0, 0, 100, 100)); // 指定子集

2.3 遗留方法移除:SKCodec.GetValidSubset 与 SKDocument.Close

类型 1.54 方法 1.55 变更
SKCodec public void GetValidSubset (ref SKRectI desiredSubset) 移除,替换为 public bool GetValidSubset (ref SKRectI desiredSubset)
SKDocument public bool Close () 移除,替换为 public void Close ()

这两个变更都属于"返回类型收紧":GetValidSubset 现在返回 bool 表示"该 codec 是否支持解码这个子集"(详见 SKCodec.cs,底层调用 SkiaApi.sk_codec_get_valid_subset);SKDocument.Close 改为 void,不再有误导性的返回值。

此外还有一处构造函数的清理:SKPictureRecorder(IntPtr handle, bool owns) 这个面向内部句柄的构造函数被移除,并新增了 public SKCanvas RecordingCanvas { get; } 属性,外部调用方应通过该属性获取录制画布,而不是直接构造句柄。

三、废弃(Obsolete)项:旧命名体系的退场

1.55.0 对一批旧命名 API 打上了 [Obsolete] 标记并给出替代指引,编译器会在使用处给出警告:

  • GRContext.Flush(GRContextFlushBits) → 使用 Flush();
  • SKCanvas.ClipPath/ClipRect(SKPath/SKRect, SKRegionOperation, bool) → 使用 SKClipOperation 版本;
  • SKCanvas.DrawColor(SKColor, SKXferMode) → 使用 SKBlendMode 版本;
  • SKPaint.XferMode 属性 → 使用 SKPaint.BlendMode(当前源码 SKPaint 已全面转向 BlendMode);
  • SKColorFilter.MaxCubeSize/MinCubeSize 字段 → 使用 MaxColorCubeDimension/MinColorCubeDimension,并新增 ColorMatrixSize;
  • SKFontStyleWidth.UltaExpanded(拼写错误)→ 使用 UltraExpanded;
  • SKImageFilter.CreateCompose(位移映射误命名)→ 使用 CreateDisplacementMapEffect;
  • SKPathEffect.Create1DPath(..., SkPath1DPathEffectStyle) → 使用 SKPath1DPathEffectStyle 版本;
  • SKMatrix.MapXY → 使用 MapPoint。

这些废弃并非简单改名:SKBlendMode 与 SKClipOperation 是 1.55.0 全新引入的枚举类型(详见 SKBlendMode.generated.cs,映射自原生 SkBlendMode),它们与 Skia 原生 API 的命名对齐,是后续版本中绘图与裁剪操作的标准入口。

四、1.55.0 新增核心 API 深度解读

4.1 新枚举:SKBlendMode 与 SKClipOperation

SKBlendMode 完整覆盖了 Porter-Duff 合成算子与 W3C 混合模式,共 29 个值(Clear、Src、Dst、SrcOver、DstOver、SrcIn、DstIn、SrcOut、DstOut、SrcATop、DstATop、Xor、Plus、Modulate、Screen、Overlay、Darken、Lighten、ColorDodge、ColorBurn、HardLight、SoftLight、Difference、Exclusion、Multiply、Hue、Saturation、Color、Luminosity)。在 SKBlendMode.generated.cs 中可以看到每个成员都带有对应混合语义的 XML 文档注释。典型用法:

using (var paint = new SKPaint { BlendMode = SKBlendMode.Multiply })
{
    canvas.DrawRect(rect, paint);
}

SKClipOperation 则只有两个值:Difference(从裁剪区域中减去)与 Intersect(与现有区域求交),对应 Skia 原生 SkClipOp。

4.2 SKCanvas:裁剪边界、九宫格缩放与便捷重载

  • 裁剪边界属性:public SKRect ClipBounds { get; } 与 public SKRectI ClipDeviceBounds { get; }。当前仓库中 SKCanvas.cs 将其实现为 LocalClipBounds/DeviceClipBounds 的包装属性,分别返回局部坐标系与设备坐标系下的裁剪矩形;
  • 九宫格(lattice)绘制:DrawBitmapLattice(SKBitmap, SKLattice, SKRect, SKPaint) 与 DrawImageLattice(SKImage, SKLattice, SKRect, SKPaint),用于 9-patch 式缩放图像而不失真。SKLattice 结构(Definitions.cs)包含 XDivs/YDivs(分割坐标数组)、RectTypes(矩形类型)、Bounds(可选源边界)、Colors(填充色),若 divs 或 bounds 非法,整个 lattice 会在绘制时被忽略;
  • 便捷方法:DrawOval(float cx, float cy, float rx, float ry, SKPaint)、Scale(float s)、ClipRegion(SKRegion, SKClipOperation)。

4.3 SKPath:布尔运算与完整绘图指令集

SKPath 是本次新增方法最多的类型,核心亮点是路径布尔运算与简化:

SKPath result = path1.Op(path2, SKPathOp.Union); // 并集
SKPath simplified = path.Simplify();             // 简化(降阶曲线)

从 SKPath.cs 的源码可见,Op 与 Simplify 均返回新 SKPath 实例,失败时返回 null;底层调用 SkiaApi.sk_pathop_op / sk_pathop_simplify。Simplify 会尽可能降低曲线阶数(三次曲线转二次、二次转直线)。

同时补齐了一整套与原生 Skia 对齐的绘图指令:ArcTo(含圆弧、椭圆弧五种重载)、ConicTo、QuadTo、CubicTo、LineTo、MoveTo 及其相对版本 RArcTo、RConicTo、RQuadTo、RCubicTo、RLineTo、RMoveTo,以及 ConvertConicToQuads(把圆锥曲线转为二次贝塞尔,pow2 控制细分次数)和 Offset(SKPoint)。

4.4 新类型 SKPathMeasure:路径测量

SKPathMeasure 用于沿路径测量长度、取位置与切线:

using (var measure = new SKPathMeasure(path, false, 1.0f))
{
    float length = measure.Length;
    measure.GetPositionAndTangent(distance, out var pos, out var tangent);
    measure.GetSegment(start, stop, dstPath, true); // 截取子路径
}

其 MatrixFlags 枚举(GetPosition=1、GetTangent=2、GetPositionAndTangent=3)配合 GetMatrix 可生成路径动画所需的仿射矩阵。

4.5 新类型 SKRegion:区域布尔运算

SKRegion 提供基于整型矩形区域的集合运算:Contains(包含判定)、Intersects(相交判定)、Op(与矩形/区域执行 SKRegionOperation 布尔运算)、SetPath(由路径生成区域,可附加裁剪区域)、SetRect/SetRegion。常用于命中检测与复杂裁剪区域的预计算。

4.6 SKColor:字符串解析能力

SKColor c1 = SKColor.Parse("#FF8000");          // 成功解析,失败抛 ArgumentException
bool ok = SKColor.TryParse("AARRGGBB", out var c2); // 安全解析

当前 SKColor.cs 的实现支持 #AARRGGBB、#RRGGBB、#ARGB、#RGB 四种格式(带不带 # 均可),内部先 Trim 再去掉前导 #,3 位格式会把每个 nibble 复制为整字节(如 "F" → 0xFF)。这一能力在后续版本中进一步扩展为 ReadOnlySpan<char> 重载,成为高性能颜色解析的基础(仓库 benchmarks/SkiaSharp.Benchmarks/Benchmarks/ 下即有 SKColorParseBenchmark 等基准验证其性能表现)。

4.7 SKPaint 与 SKTypeface 的增强

  • SKPaint:新增 BlendMode、只读 FontSpacing、Clone()、GetFillPath(SKPath, SKPath, float)(可带 cullRect 重载)与 GetFontMetrics(out SKFontMetrics, float),使"由画笔生成填充路径"和"测量字体"成为一等公民;
  • SKTypeface:新增 FontSlant、FontWeight、FontWidth、Style 只读属性,把字重、字宽、倾斜度从 SKFontStyle 直接暴露到 typeface 上。

4.8 SKMatrix 与 SKRect 的几何能力

  • SKMatrix:MapRect 从静态方法改为实例方法;新增 MapPoint(SKPoint)/MapPoint(float, float)、Concat、PreConcat、PostConcat,组合变换更直观;
  • SKRect/SKRectI:新增 MidX/MidY 中点、Standardized(归一化,保证 left≤right、top≤bottom)、AspectFit/AspectFill(按目标尺寸等比缩放,分别为"完整容纳"与"填满裁剪"),为图片适配布局提供现成工具。

五、Views 与 Forms 程序集的同步更新

本次发布同时更新了各平台视图绑定程序集,API diff 导航页 列出了 SkiaSharp.Views(Android、Desktop、Mac、UWP、WPF、iOS、tvOS)与 SkiaSharp.Views.Forms 各平台程序集的 API diff 文档(分别位于 SkiaSharp.Views 与 SkiaSharp.Views.Forms 目录)。这意味着从 1.55.0 起,Xamarin.Forms 与各原生平台下的 SKCanvasView/SKGLView 等视图组件同样基于新签名重新编译,建议在升级 SkiaSharp 主库时同步升级这些视图包。

六、升级实操清单

将现有项目从 1.54.x 升级到 1.55.0 时,建议按以下清单逐项排查:

  1. 全局搜索 XferMode:替换为 BlendMode(SKPaint、DrawColor 等);
  2. 全局搜索 SKRegionOperation:裁剪相关调用替换为 SKClipOperation;
  3. 检查 ClipRect/ClipPath/DrawColor 调用:补全被省略的参数,或改用新重载;
  4. 检查 GRContext.Flush(flags) 调用:改用无参 Flush() 或 Flush(bool submit, bool synchronous);
  5. 检查 SKCodecOptions 使用:Subset 改为可空赋值,HasSubset 不再可写;
  6. 检查 SKCodec.GetValidSubset 与 SKDocument.Close:适配新的 bool/void 返回值;
  7. 关注编译警告:[Obsolete] 标记会提示所有待迁移的旧 API,逐一按消息中的替代指引处理;
  8. 同步升级视图包:SkiaSharp.Views.* 与 SkiaSharp.Views.Forms 各平台程序集一并更新;
  9. 重新编译并验证 ABI:程序集版本已升为 1.55.0.0,必须针对新签名重新编译,否则运行时可能遇到 MissingMethodException。

结语

SkiaSharp 1.55.0 是一次典型的"为长期演进而收紧"的 API 清理版本:移除默认参数、可空化可选配置、废弃旧命名,同时一次性补齐了 SKBlendMode、路径布尔运算、SKPathMeasure、SKRegion、颜色解析等至今仍在使用的基础能力。理解这次变更,不仅有助于老项目平滑迁移,也能更清晰地看到 SkiaSharp 与原生 Skia 命名体系逐步对齐的设计脉络。若需查阅每个 API 的完整签名变更,可对照 完整 API diff;若仅关注破坏性变更,可阅读 breaking 摘要。

登录后查看全文
SkiaSharp