SkiaSharp 1.55.0 版本 API 变更全解析:破坏性改动、新增能力与迁移指南
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 时,建议按以下清单逐项排查:
- 全局搜索
XferMode:替换为BlendMode(SKPaint、DrawColor等); - 全局搜索
SKRegionOperation:裁剪相关调用替换为SKClipOperation; - 检查
ClipRect/ClipPath/DrawColor调用:补全被省略的参数,或改用新重载; - 检查
GRContext.Flush(flags)调用:改用无参Flush()或Flush(bool submit, bool synchronous); - 检查
SKCodecOptions使用:Subset改为可空赋值,HasSubset不再可写; - 检查
SKCodec.GetValidSubset与SKDocument.Close:适配新的bool/void返回值; - 关注编译警告:
[Obsolete]标记会提示所有待迁移的旧 API,逐一按消息中的替代指引处理; - 同步升级视图包:
SkiaSharp.Views.*与SkiaSharp.Views.Forms各平台程序集一并更新; - 重新编译并验证 ABI:程序集版本已升为
1.55.0.0,必须针对新签名重新编译,否则运行时可能遇到MissingMethodException。
结语
SkiaSharp 1.55.0 是一次典型的"为长期演进而收紧"的 API 清理版本:移除默认参数、可空化可选配置、废弃旧命名,同时一次性补齐了 SKBlendMode、路径布尔运算、SKPathMeasure、SKRegion、颜色解析等至今仍在使用的基础能力。理解这次变更,不仅有助于老项目平滑迁移,也能更清晰地看到 SkiaSharp 与原生 Skia 命名体系逐步对齐的设计脉络。若需查阅每个 API 的完整签名变更,可对照 完整 API diff;若仅关注破坏性变更,可阅读 breaking 摘要。