在传统.NET桌面开发中,System.Drawing一直是图形处理的标准选择。但随着应用场景的复杂化和跨平台需求的增长,这个基于GDI+的库逐渐暴露出性能瓶颈和兼容性局限。SkiaSharp作为Google Skia图形库的.NET封装,不仅继承了Skia的高性能渲染能力,更提供了现代化的跨平台支持。本文将带你从工程实践角度,完成从System.Drawing到SkiaSharp的技术升级。
性能对比测试显示,在相同硬件环境下,SkiaSharp的矢量图形渲染速度比System.Drawing快3-5倍,特别是在处理复杂路径和渐变填充时优势更为明显。这主要得益于:
跨平台兼容性方面,我们通过一个简单的对照表说明差异:
| 特性 | System.Drawing | SkiaSharp |
|---|---|---|
| Windows支持 | ✔️ | ✔️ |
| Linux/macOS支持 | ❌ | ✔️ |
| 移动端支持 | ❌ | ✔️ |
| WebAssembly支持 | ❌ | ✔️ |
| HiDPI屏幕适配 | 部分 | 完整 |
实际项目中常见的三个迁移场景:
在Visual Studio 2022中新建.NET 8项目后,通过NuGet安装以下核心包:
bash复制dotnet add package SkiaSharp --version 2.88.6
dotnet add package SkiaSharp.Views.WindowsForms --version 2.88.6 # WinForms项目
dotnet add package SkiaSharp.Views.WPF --version 2.88.6 # WPF项目
注意:务必保持所有SkiaSharp相关包的版本一致,避免因依赖冲突导致的运行时错误。
对于WinForms项目,在窗体设计器中添加SKControl控件后,需要处理PaintSurface事件:
csharp复制private void skControl1_PaintSurface(object sender, SKPaintSurfaceEventArgs e)
{
var canvas = e.Surface.Canvas;
canvas.Clear(SKColors.White);
// 绘制逻辑将在这里实现
}
WPF项目则使用SKElement,在XAML中添加:
xml复制<skia:SKElement x:Name="SkiaElement" PaintSurface="OnPaintSurface" />
中文处理是SkiaSharp迁移中最常遇到的问题。以下是经过实战验证的解决方案:
字体处理最佳实践:
csharp复制var typeface = SKTypeface.FromFamilyName(
"Microsoft YaHei",
SKFontStyleWeight.Normal,
SKFontStyleWidth.Normal,
SKFontStyleSlant.Upright);
var paint = new SKPaint {
Typeface = typeface,
TextSize = 36,
IsAntialias = true,
Color = SKColors.Black
};
抗锯齿优化参数:
csharp复制new SKPaint {
IsAntialias = true, // 启用抗锯齿
LcdRenderText = true, // 液晶屏文本优化
SubpixelText = true // 亚像素渲染
};
自动换行解决方案推荐使用SkiaSharp.Text库:
csharp复制var textBlock = new TextBlock {
Text = "需要自动换行的中文文本内容...",
MaxWidth = 300,
Paint = textPaint
};
textBlock.Paint(canvas, new SKPoint(10, 10));
从System.Drawing到SkiaSharp的常见图形操作对照:
| 操作 | System.Drawing | SkiaSharp |
|---|---|---|
| 画线 | graphics.DrawLine | canvas.DrawLine |
| 矩形填充 | graphics.FillRectangle | canvas.DrawRect (Fill样式) |
| 路径绘制 | graphics.DrawPath | canvas.DrawPath |
| 图像旋转 | graphics.RotateTransform | canvas.RotateDegrees |
| 透明度设置 | Color.FromArgb | SKColor.WithAlpha |
高级图形技巧示例——实现虚线边框与渐变填充:
csharp复制// 虚线样式
var dashPaint = new SKPaint {
Style = SKPaintStyle.Stroke,
Color = SKColors.Blue,
StrokeWidth = 2,
PathEffect = SKPathEffect.CreateDash(new[] { 10f, 5f }, 0)
};
// 渐变填充
var gradient = SKShader.CreateLinearGradient(
new SKPoint(0, 0),
new SKPoint(100, 100),
new[] { SKColors.Red, SKColors.Yellow },
new[] { 0f, 1f },
SKShaderTileMode.Clamp);
var fillPaint = new SKPaint {
Style = SKPaintStyle.Fill,
Shader = gradient
};
canvas.DrawRect(10, 10, 100, 100, fillPaint);
canvas.DrawRect(10, 10, 100, 100, dashPaint);
将绘图结果导出为多种格式是实际项目中的常见需求。以下是PNG和PDF输出的标准做法:
PNG导出:
csharp复制using (var image = SKImage.FromBitmap(bitmap))
using (var data = image.Encode(SKEncodedImageFormat.Png, 100))
using (var stream = File.OpenWrite("output.png"))
{
data.SaveTo(stream);
}
PDF导出:
csharp复制using (var stream = new SKFileWStream("output.pdf"))
using (var document = SKDocument.CreatePdf(stream))
{
var canvas = document.BeginPage(800, 600);
// 绘制内容
document.EndPage();
document.Close();
}
对于需要处理SVG的场景,推荐使用SkiaSharp.Extended.Svg扩展:
csharp复制var svg = new SKSvg();
svg.Load("input.svg");
canvas.DrawPicture(svg.Picture, new SKMatrix(
scaleX, skewX, transX,
skewY, scaleY, transY,
0, 0, 1));
提升SkiaSharp渲染效率的关键策略:
csharp复制// 对象池示例
private readonly ConcurrentBag<SKPaint> _paintPool = new();
SKPaint GetPaint(SKColor color, float strokeWidth)
{
if (!_paintPool.TryTake(out var paint))
{
paint = new SKPaint();
}
paint.Color = color;
paint.StrokeWidth = strokeWidth;
return paint;
}
void ReturnPaint(SKPaint paint)
{
_paintPool.Add(paint);
}
GPU加速配置(仅限支持环境):
csharp复制var glInterface = GRGlInterface.CreateNativeGlInterface();
var context = GRContext.CreateGl(glInterface);
var surface = SKSurface.Create(
context,
true,
new SKImageInfo(width, height));
内存泄漏预防:
csharp复制// 错误示例 - 会导致内存泄漏
var paint = new SKPaint();
canvas.DrawText("text", 10, 10, paint);
// 正确做法
using (var paint = new SKPaint())
{
canvas.DrawText("text", 10, 10, paint);
}
DPI缩放处理:
csharp复制var scale = skControl.DeviceDpi / 96f;
canvas.Scale(scale);
// 或者使用自动适配方案
var info = e.Info;
canvas.Scale(info.Width / (float)skControl.Width);
在多显示器环境测试时,我们发现SkiaSharp对高DPI的支持比System.Drawing更加可靠,特别是在200%缩放比例下仍能保持清晰锐利的文本渲染。