SkiaSharp 1.56.1 API 变更全解析:新增类型、新增方法与破坏性调整实战指南

原创2026-10-12 14:14:501,972 阅读
文章标签:图形学跨平台

SkiaSharp 1.56.1 API 变更全解析:新增类型、新增方法与破坏性调整实战指南

导读

本文以 SkiaSharp 1.56.1 官方 API Diff(documentation/docfx/releases/1.56.1/SkiaSharp/SkiaSharp.md)为骨架,系统梳理 SkiaSharp.dll 在本版本中的全部变更:包括 SKBitmap、SKCanvas、SKCodec、SKDocument、SKPaint、SKPath 等核心类型新增的方法与属性,以及 SK3dView、SKFontManager、SKMask、SKSvgCanvas、StringUtilities 等十余个全新类型。读完本文,你将掌握 1.56.1 版本带来的能力升级点(SVG 输出画布、扫描线解码、蒙版处理、PDF 元数据、3D 视图变换等),并了解哪些旧 API 已被标记废弃、应如何平滑迁移。

一、版本背景与本文范围

本篇文章对应的变更记录是 1.56.1 版本中 SkiaSharp.dll 的公共 API 差异清单,覆盖 SkiaSharp 命名空间下的类型变更。它不涉及 HarfBuzzSharp 等其他程序集,也不包含 native 库本身的升级细节。变更可以归纳为四类:

  • 新增方法/属性:为既有类型补充能力,如位图子集提取、扫描线解码、PDF 元数据等;
  • 废弃(Obsolete)调整:如 SKCanvas.DrawText(沿路径绘制)被 DrawTextOnPath 取代,SKStrokeJoin.Mitter 更名为 Miter;
  • 可重写性调整:SKWStream 的若干成员从非 virtual 调整为 virtual;
  • 全新类型:覆盖 3D 视图、蒙版、字体管理、SVG 输出、字符串编码等新场景。

下面按类型逐一展开,并结合当前仓库源码(binding/SkiaSharp 目录)给出实现依据与使用建议。

二、SKBitmap:位图子集、Alpha 通道与像素地址访问

2.1 ExtractSubset:提取位图子集

新增方法:

public bool ExtractSubset (SKBitmap destination, SKRectI subset);

该方法把当前位图中由 subset 矩形指定的像素区域写入 destination。从实现看(SKBitmap.cs),它直接调用 SkiaApi.sk_bitmap_extract_subset,并在调用前后用 GC.KeepAlive 保护源位图与目标位图句柄,避免 GC 提前回收原生对象。文档注释特别强调:尽可能让目标位图共享源位图的像素内存,仅"指向"其中的子区域;若颜色类型不支持共享,则会做一次本地拷贝。因此:

  • 适合做局部裁剪而不想复制整块像素的场景;
  • subset 必须位于源位图边界内,否则返回 false。

2.2 ExtractAlpha:提取 Alpha 层

新增四个重载:

public bool ExtractAlpha (SKBitmap destination);
public bool ExtractAlpha (SKBitmap destination, SKPaint paint);
public bool ExtractAlpha (SKBitmap destination, out SKPointI offset);
public bool ExtractAlpha (SKBitmap destination, SKPaint paint, out SKPointI offset);

实现上(SKBitmap.cs),无 paint 的重载内部统一转发到 ExtractAlpha (destination, null, out var offset),即使用空画笔;无 offset 参数的重载则将偏移丢弃。核心语义是:把位图的 Alpha 层填充到 destination,offset 用于给出目标位图相对源位图的左上角坐标,以便后续把提取结果"视觉对齐"回原位置;paint 则在提取时应用画笔(例如叠加滤镜效果)。

典型用法是分离前景与背景、生成阴影或高光层:

var source = SKBitmap.Decode (imageBytes);
var alphaLayer = new SKBitmap (source.Width, source.Height);
var offset = new SKPointI ();
if (source.ExtractAlpha (alphaLayer, out offset))
{
    // offset 为提取层相对源图的偏移,绘制时按此对齐
}

2.3 像素地址访问:GetAddr 系列

新增四个按坐标返回像素内存地址的方法:

public IntPtr GetAddr (int x, int y);
public ushort GetAddr16 (int x, int y);
public uint GetAddr32 (int x, int y);
public byte GetAddr8 (int x, int y);

它们分别以原生指针、16 位、32 位与 8 位粒度返回 (x, y) 处的像素地址,用于高性能的逐像素读写(例如与 SKPixmap、SKMask 的像素缓冲区互操作)。仓库源码中 SKBitmap.cs 有注释提示:GetAddr 与 GetPixel 容易混淆——前者返回的是内存地址(指针),后者返回颜色值,使用时需注意区分。

2.4 InstallMaskPixels:安装蒙版像素

新增方法:

public bool InstallMaskPixels (SKMask mask);

用于把 SKMask 的像素直接安装到位图中(配合下文新增的 SKMask 结构体使用),适合与 SKAutoMaskFreeImage 等蒙版内存管理工具配合,避免中间拷贝。

三、SKCanvas:文字绘制 API 重构与注释(Annotation)能力

3.1 DrawText 沿路径绘制废弃,改用 DrawTextOnPath

本次变更将两个沿路径绘制文本的方法标记为 [Obsolete],并新增对应替代方法:

 [Obsolete ("Use DrawTextOnPath instead.")]
 public void DrawText (string text, SKPath path, float hOffset, float vOffset, SKPaint paint);
 [Obsolete ("Use DrawTextOnPath instead.")]
 public void DrawText (IntPtr buffer, int length, SKPath path, float hOffset, float vOffset, SKPaint paint);

同时新增三个 DrawTextOnPath 重载:

public void DrawTextOnPath (byte[] text, SKPath path, float hOffset, float vOffset, SKPaint paint);
public void DrawTextOnPath (string text, SKPath path, float hOffset, float vOffset, SKPaint paint);
public void DrawTextOnPath (IntPtr buffer, int length, SKPath path, float hOffset, float vOffset, SKPaint paint);

迁移建议:将原 canvas.DrawText (text, path, h, v, paint) 的调用直接替换为 canvas.DrawTextOnPath (text, path, h, v, paint),行为与参数语义保持一致,仅方法名变更以消除命名歧义。

3.2 新增字节数组文本重载

public void DrawPositionedText (byte[] text, SKPoint[] points, SKPaint paint);
public void DrawText (byte[] text, float x, float y, SKPaint paint);
public void DrawText (byte[] text, SKPath path, float hOffset, float vOffset, SKPaint paint); // [Obsolete],同上迁移

其中 DrawPositionedText 允许为每个字符指定独立的绘制位置(points 数组长度应与字符数匹配),用于逐字定位排版;DrawText (byte[], float, float, SKPaint) 则直接以预编码字节绘制文本,配合 StringUtilities.GetEncodedText 使用可以避免字符串到字节的重复转换。

3.3 注释(Annotation)系列:URL、链接目标与命名目标

SKCanvas 新增一组与 PDF 注释相关的绘制方法(实现见 SKCanvas.cs):

public void DrawAnnotation (SKRect rect, string key, SKData value);
public void DrawUrlAnnotation (SKRect rect, SKData value);
public SKData DrawUrlAnnotation (SKRect rect, string value);
public void DrawLinkDestinationAnnotation (SKRect rect, SKData value);
public SKData DrawLinkDestinationAnnotation (SKRect rect, string value);
public void DrawNamedDestinationAnnotation (SKPoint point, SKData value);
public SKData DrawNamedDestinationAnnotation (SKPoint point, string value);
  • DrawAnnotation:通用注释,以 key 标识、SKData 承载值;
  • DrawUrlAnnotation:给矩形区域挂上超链接;string 重载内部通过 SKData.Create (value) 包装后转发给 SKData 重载;
  • DrawNamedDestinationAnnotation:在指定点关联一个"命名目标"(PDF 书签/跳转目标);
  • DrawLinkDestinationAnnotation:让矩形区域链接到某个命名目标。

这三者配合即可在生成的 PDF 文档中实现"点击某区域跳转到另一页/位置"的交互效果,是制作目录、索引页或可点击地图的常用手段。注意 SKData 重载是底层实现,string 重载只是便捷封装,返回值 SKData 可用于后续引用该目标。

四、SKCodec:扫描线(Scanline)解码能力

SKCodec 在本版本中获得了一套完整的逐行解码 API:

新增属性:

public int NextScanline { get; }
public SKCodecScanlineOrder ScanlineOrder { get; }

新增方法:

public int GetOutputScanline (int inputScanline);
public int GetScanlines (IntPtr dst, int countLines, int rowBytes);
public bool SkipScanlines (int countLines);
public SKCodecResult StartScanlineDecode (SKImageInfo info);
public SKCodecResult StartScanlineDecode (SKImageInfo info, SKCodecOptions options);
public SKCodecResult StartScanlineDecode (SKImageInfo info, SKCodecOptions options, SKColorTable colorTable, ref int colorTableCount);
public SKCodecResult StartScanlineDecode (SKImageInfo info, SKCodecOptions options, IntPtr colorTable, ref int colorTableCount);

配套的新枚举:

[Serializable]
public enum SKCodecScanlineOrder {
    BottomUp = 1,
    TopDown = 0,
}

使用流程:先以目标 SKImageInfo 调用 StartScanlineDecode 开始逐行解码,再通过 ScanlineOrder 判断行序(自顶向下还是自底向上),之后反复调用 GetScanlines 取回若干行、用 SkipScanlines 跳过若干行,NextScanline 给出下一个待解码行号,GetOutputScanline 将输入行号映射为输出行号(用于行序转换)。SKColorTable 相关重载用于调色板格式(如 GIF)的渐进式解码。

这种"逐行解码"模式非常适合流式图片处理:不必一次性解码整张图片,即可在解码过程中边读边渲染、边做缩放或滤镜,显著降低内存峰值。

五、SKData:新增字节数组构造器

新增构造器:

public SKData (byte[] bytes, ulong length);

它允许用一段字节数组及其显式长度构造 SKData。相比直接以数组整体构造,该重载适合"数组大于实际有效数据"的场景(例如从缓冲区中取出前 N 字节),length 以 ulong 传递,与底层原生 size_t 语义对齐。SKData 在 Skia 中广泛用于承载编码后的图片、PDF 字节流以及上文提到的注释值。

六、SKDocument:PDF 元数据与 DPI 控制

新增方法:

public static SKDocument CreatePdf (SKWStream stream, SKDocumentPdfMetadata metadata, float dpi);

并新增结构体:

public struct SKDocumentPdfMetadata {
    public string Author { get; set; }
    public System.DateTime? Creation { get; set; }
    public string Creator { get; set; }
    public string Keywords { get; set; }
    public System.DateTime? Modified { get; set; }
    public string Producer { get; set; }
    public string Subject { get; set; }
    public string Title { get; set; }
}

从仓库实现看(SKDocument.cs),CreatePdf 提供了丰富的重载链:string path、Stream、SKWStream 三种输出目标 × 可选 float dpi × 可选 SKDocumentPdfMetadata。不带元数据的重载内部会构造 new SKDocumentPdfMetadata (dpi)(说明该结构体还带一个接收 dpi 的构造器),最终通过 SKDocumentPdfMetadataInternal 与原生 SkiaApi.sk_document_create_pdf 对接。

实战要点:

  • 用 SKDocumentPdfMetadata 设置文档标题、作者、主题、关键词、创建/修改时间等 PDF 元数据;
  • dpi 参数控制文档坐标到物理尺寸的换算基准,影响打印与缩放表现;
  • 生成 PDF 后务必 Dispose 文档对象并刷新输出流,才能保证内容完整写入。

七、SKMaskFilter:模糊滤镜新增 flags 与 occluder 重载

新增方法:

public static SKMaskFilter CreateBlur (SKBlurStyle blurStyle, float sigma, SKBlurMaskFilterFlags flags);
public static SKMaskFilter CreateBlur (SKBlurStyle blurStyle, float sigma, SKRect occluder);
public static SKMaskFilter CreateBlur (SKBlurStyle blurStyle, float sigma, SKRect occluder, SKBlurMaskFilterFlags flags);

配套新枚举:

[Serializable]
[Flags]
public enum SKBlurMaskFilterFlags {
    All = 3,
    HighQuality = 2,
    IgnoreTransform = 1,
    None = 0,
}
  • flags:IgnoreTransform 表示模糊效果忽略画布变换(在屏幕空间计算),HighQuality 表示高质量模糊,All 为两者组合;
  • occluder:传入一个矩形作为"遮挡物"边界,用于优化只需要局部模糊的渲染路径。

八、SKPaint 与 SKPath:文本测量与路径段信息

8.1 SKPaint 新增 byte[] 文本重载

public long BreakText (byte[] text, float maxWidth, out float measuredWidth);
public SKPath GetTextPath (byte[] text, SKPoint[] points);
public SKPath GetTextPath (byte[] text, float x, float y);
public float MeasureText (byte[] text);
public float MeasureText (byte[] text, ref SKRect bounds);

这些重载与 SKCanvas 的 byte[] 文本方法配套:

  • MeasureText (byte[], float):测量预编码文本的宽度,ref SKRect bounds 额外返回包围盒;
  • BreakText:在不超过 maxWidth 的前提下返回可容纳的字符数,measuredWidth 输出实际测量宽度,是文本自动换行/截断的核心 API;
  • GetTextPath:把文本转成路径,SKPoint[] 版本逐字符定位。

8.2 SKPath 新增 SegmentMasks 与 AddPoly

public SKPathSegmentMask SegmentMasks { get; }
public void AddPoly (SKPoint[] points, bool close);

配套新枚举:

[Serializable]
[Flags]
public enum SKPathSegmentMask {
    Conic = 4,
    Cubic = 8,
    Line = 1,
    Quad = 2,
}

SegmentMasks 以位标志汇总路径中包含的段类型(直线/二次/三次/圆锥曲线),可用于快速判断路径复杂度、决定渲染或命中测试策略;AddPoly 则是 AddPolygon 的简化入口,按点序追加多边形,close = true 时自动闭合。

九、SKStrokeJoin 命名修正:Mitter → Miter

 [Obsolete ("Use SKStrokeJoin.Miter instead.")]
 Mitter = 0,

新增值:

Miter = 0,

Mitter 是拼写错误的旧名称(正确拼写应为 Miter),本版本将其标记废弃并引入正确拼写的 Miter(值仍为 0,二进制兼容)。升级后应改用 SKStrokeJoin.Miter,编译器会为旧用法给出警告。

十、SKWStream:成员可重写化

 public ---virtual--- int BytesWritten { get; }
 public ---virtual--- void Flush ()
 public ---virtual--- bool Write (byte[] buffer, int size)

BytesWritten 属性、Flush 与 Write 方法被调整为 virtual,允许派生类(如自定义输出流封装)覆写计数与写入行为。这一调整与新增的 SKManagedWStream(见下)相辅相成,使托管流的自定义扩展成为可能。

十一、全新类型详解

11.1 SK3dView:3D 视图变换

public class SK3dView : SkiaSharp.SKObject, System.IDisposable {
    public SK3dView ();
    public SKMatrix Matrix { get; }
    public void ApplyToCanvas (SKCanvas canvas);
    protected override void Dispose (bool disposing);
    public float DotWithNormal (float dx, float dy, float dz);
    public void GetMatrix (ref SKMatrix matrix);
    public void Restore ();
    public void RotateXDegrees (float degrees);
    public void RotateXRadians (float radians);
    public void RotateYDegrees (float degrees);
    public void RotateYRadians (float radians);
    public void RotateZDegrees (float degrees);
    public void RotateZRadians (float radians);
    public void Save ();
    public void Translate (float x, float y, float z);
    public void TranslateX (float x);
    public void TranslateY (float y);
    public void TranslateZ (float z);
}

SK3dView 为 2D 画布注入简单的 3D 透视能力:通过 Translate/RotateX/Y/Z 组合变换矩阵,Save/Restore 保护现场,ApplyToCanvas 将当前 3D 矩阵应用到 SKCanvas,之后绘制的 2D 内容即带有透视效果。DotWithNormal 用于计算向量与法向量的点积(例如做光照/背面色判断)。典型场景是卡片翻转、倾斜面板等"伪 3D" UI 效果。

11.2 SKMask 与 SKAutoMaskFreeImage:蒙版处理

public struct SKMask {
    public SKMask (SKRectI bounds, uint rowBytes, SKMaskFormat format);
    public SKMask (IntPtr image, SKRectI bounds, uint rowBytes, SKMaskFormat format);
    public SKRectI Bounds { get; }
    public SKMaskFormat Format { get; }
    public IntPtr Image { get; }
    public bool IsEmpty { get; }
    public uint RowBytes { get; }
    public long AllocateImage ();
    public static IntPtr AllocateImage (long size);
    public long ComputeImageSize ();
    public long ComputeTotalImageSize ();
    public static SKMask Create (byte[] image, SKRectI bounds, uint rowBytes, SKMaskFormat format);
    public void FreeImage ();
    public static void FreeImage (IntPtr image);
    public IntPtr GetAddr (int x, int y);
    public byte GetAddr1 (int x, int y);
    public ushort GetAddr16 (int x, int y);
    public uint GetAddr32 (int x, int y);
    public byte GetAddr8 (int x, int y);
}

配套枚举:

[Serializable]
public enum SKMaskFormat {
    A8 = 1,
    Argb32 = 3,
    BW = 0,
    Lcd16 = 4,
    ThreeD = 2,
}

SKMask 是 Skia 蒙版(单通道覆盖/形状描述)的托管镜像:Bounds 为像素边界、RowBytes 为每行字节数、Format 决定像素布局(1-bit BW、A8 灰度、LCD 子像素、3D 或 ARGB32)。AllocateImage/FreeImage 管理蒙版像素内存,GetAddr 系列按坐标取地址。它与 SKBitmap.InstallMaskPixels、SKAutoMaskFreeImage 配合构成完整的蒙版生命周期管理方案:

public class SKAutoMaskFreeImage : System.IDisposable {
    public SKAutoMaskFreeImage (IntPtr maskImage);
    public virtual void Dispose ();
}

SKAutoMaskFreeImage 以 RAII 方式包装 maskImage 指针,Dispose 时自动释放,防止蒙版像素内存泄漏。

11.3 SKFontManager:字体管理与字符匹配

public class SKFontManager : SkiaSharp.SKObject, System.IDisposable {
    public static SKFontManager Default { get; }
    public int FontFamilyCount { get; }
    protected override void Dispose (bool disposing);
    public string GetFamilyName (int index);
    public string[] GetFontFamilies ();
    public SKTypeface MatchCharacter (char character);
    public SKTypeface MatchCharacter (int character);
    public SKTypeface MatchCharacter (string familyName, char character);
    public SKTypeface MatchCharacter (string familyName, int character);
    public SKTypeface MatchCharacter (string familyName, string[] bcp47, char character);
    public SKTypeface MatchCharacter (string familyName, string[] bcp47, int character);
    public SKTypeface MatchCharacter (string familyName, SKFontStyleWeight weight, SKFontStyleWidth width, SKFontStyleSlant slant, string[] bcp47, char character);
    public SKTypeface MatchCharacter (string familyName, SKFontStyleWeight weight, SKFontStyleWidth width, SKFontStyleSlant slant, string[] bcp47, int character);
    public SKTypeface MatchCharacter (string familyName, int weight, int width, SKFontStyleSlant slant, string[] bcp47, int character);
}

SKFontManager 提供系统字体管理入口:

  • Default 获取系统默认字体管理器;
  • FontFamilyCount/GetFontFamilies/GetFamilyName 枚举全部字体族;
  • MatchCharacter 是核心能力:按字符(char 或 Unicode 码点 int)查找能渲染该字符的字形,可附加字体族名、BCP-47 语言标签(如 "zh-Hans")、字重/字宽/倾斜等约束条件。这是实现"字体回退(font fallback)"、自动补齐缺字字形(如 CJK 生僻字、Emoji)的关键 API。

11.4 SKManagedWStream:托管流封装

public class SKManagedWStream : SkiaSharp.SKWStream, System.IDisposable {
    public SKManagedWStream (System.IO.Stream managedStream);
    public SKManagedWStream (System.IO.Stream managedStream, bool disposeManagedStream);
    protected override void Dispose (bool disposing);
}

把 .NET System.IO.Stream 包装为 SKWStream,供 SKDocument.CreatePdf、SKSvgCanvas.Create 等接受原生输出流的 API 直接写入内存流、文件流或网络流。disposeManagedStream 控制 Dispose 时是否同时释放底层托管流。它正是 SKSvgCanvas.Create (SKRect, Stream) 的内部实现基础(见 SKSVG.cs:内部创建 SKManagedWStream 后交给 SKObject.Owned 管理)。

11.5 SKSvgCanvas 与 SKXmlWriter:SVG 输出

public class SKSvgCanvas {
    public static SKCanvas Create (SKRect bounds, SKXmlWriter writer);
}
public class SKXmlStreamWriter : SkiaSharp.SKXmlWriter, System.IDisposable {
    public SKXmlStreamWriter (SKWStream stream);
    protected override void Dispose (bool disposing);
}
public abstract class SKXmlWriter : SkiaSharp.SKObject, System.IDisposable {
}

这是本版本最值得关注的新能力之一:把 2D 绘制命令直接输出为 SVG。仓库中的 SKSVG.cs 提供了两个便捷重载:Create (SKRect bounds, Stream stream) 与 Create (SKRect bounds, SKWStream stream),bounds 对应根 SVG 元素的 viewBox 视口。源码注释明确提醒两点(SKSVG.cs):

  • 画布会缓冲部分绘制命令,在画布实例被 Dispose 之前,输出不保证完整合法;
  • 输出流必须在画布生命周期内保持有效。

典型用法:

using var stream = File.Create ("output.svg");
using var canvas = SKSvgCanvas.Create (new SKRect (0, 0, 800, 600), stream);
// 正常调用 canvas.DrawXxx(...)
// Dispose 后 SVG 才是完整可用的

11.6 StringUtilities:文本编码工具

public static class StringUtilities {
    public static byte[] GetEncodedText (string text, SKTextEncoding encoding);
    public static string GetString (byte[] data, SKTextEncoding encoding);
    public static string GetString (IntPtr data, int dataLength, SKTextEncoding encoding);
    public static int GetUnicodeCharacterCode (string character, SKTextEncoding encoding);
}

集中提供字符串与编码间的互转:GetEncodedText 按指定 SKTextEncoding(如 UTF-8、UTF-16)把字符串编码为字节,供 SKCanvas.DrawText (byte<a href="https://link.gitcode.com/i/b6d53e59365c9f91ec8b1734eb87cd14" target="_blank">], ...)、SKPaint.MeasureText (byte[], ...) 等 byte[] 重载直接使用;GetString 反向解码(支持托管数组与原生指针);GetUnicodeCharacterCode 返回单个字符在指定编码下的码点。SKColorParseBenchmark、GetEncodedTextBenchmark 等基准(见 [benchmarks/SkiaSharp.Benchmarks/Benchmarks/GetEncodedTextBenchmark.cs)也验证了这一系列 API 是文本路径上的高频调用点。

十二、升级迁移清单

变更点 旧用法 新用法
沿路径绘制文本 canvas.DrawText (text, path, h, v, paint) canvas.DrawTextOnPath (text, path, h, v, paint)
描边连接样式 SKStrokeJoin.Mitter SKStrokeJoin.Miter
PDF 输出带元数据 仅 CreatePdf (stream) CreatePdf (stream, metadata, dpi)
SVG 输出 无 SKSvgCanvas.Create (bounds, stream)
文本编码转换 手动 Encoding.GetBytes StringUtilities.GetEncodedText
逐行解码图片 一次性 SKCodec.GetPixels StartScanlineDecode + GetScanlines
蒙版内存管理 手工 AllocateImage/FreeImage SKAutoMaskFreeImage 自动释放

十三、总结

SkiaSharp 1.56.1 的 API 变更集中在三个方向:一是文本与排版体系完善(DrawTextOnPath 更名、byte[] 文本重载、StringUtilities 编码工具、SKFontManager 字体回退);二是文档输出能力增强(PDF 元数据/DPI、SVG 画布、注释与超链接);三是底层像素与解码基础设施补齐(SKMask、SKCodec 扫描线解码、SKBitmap 子集/Alpha 提取)。对升级用户而言,本文第二部分到第九部分列出的废弃项是唯一需要动手修改的破坏性变更,其余新增 API 均可按需渐进采用。更多细节可对照仓库中的完整 API Diff 文档(documentation/docfx/releases/1.56.1/SkiaSharp/SkiaSharp.md)与对应源码(binding/SkiaSharp)继续深入。

登录后查看全文
SkiaSharp