最近对着微软的教程学习SkiaSharp的概念及用法,由于之前使用GDI+绘制过坐标系、印章等程序,准备使用SkiaSharp、SKControl控件编写简单的印章设计功能,并能用印章对图片盖章。本文实现创建印章背景、序列化及反序列化印章对象等功能。
VS2022新建Winform项目,在Nuget包管理器中搜索并安装以下包:
SkiaSharp
SkiaSharp.Views
SkiaSharp.Views.Forms
SkiaSharp.Views.Forms.WPF
印章一般是白底红边框,或者没有边框,本文暂不考虑印章内部的文字、线条、圆/椭圆等样式,定义以下类型保存印章的背景色、尺寸、边框颜色、边框宽度等信息。
public class SealInfo
{
/// <summary>
/// 印章名称
/// </summary>
public string Name { get; set; }=string.Empty;
/// <summary>
/// 印章宽度
/// </summary>
public float Width { get; set; } = 0;
/// <summary>
/// 印章高度
/// </summary>
public float Height { get; set; } = 0;
/// <summary>
/// 尺寸单位类型,默认为毫米
/// </summary>
public UnitType UnitType { get; set; } = UnitType.Mm;
/// <summary>
/// 印章背景色,默认白色
/// </summary>
public SKColor BgColor { get; set; }=SKColors.White;
/// <summary>
/// 是否有边框
/// </summary>
public bool HasBorder { get; set; } = false;
/// <summary>
/// 边框宽度
/// </summary>
public float BorderWidth { get; set; } = 1;
/// <summary>
/// 边框颜色
/// </summary>
public SKColor BorderColor { get;set; } = SKColors.Red;
}
支持两种尺寸单位类型:毫米、像素。一般都是按毫米设置印章尺寸,因此绘制印章时需要从毫米转换到像素(SkiaSharp尺寸单位默认为像素,暂时没有看到设置其它计量单位的方式)。像素和毫米转换时需要用到控件的DPI,GDI+的Graphics对象中支持获取DpiX和DpiY,但是SKCanvas中没有DPI数据,而是保存在SKControl的DeviceDpi属性,且仅有单个属性,并未区分X和Y方向。
同时关于SKColor和System.Drawing.Color之间的转换,SkiaSharp.Views.Desktop.Common程序集的扩展类中提供了扩展函数ToDrawingColor,支持将SKColor转换为System.Drawing.Color,而程序集System.Drawing.Primitives的ColorTranslator类的函数ToHtml支持将System.Drawing.Color转换为十六进制字符串,然后调用SKColor.Parse转换为SKColor实例,以下示例代码调用Winform的颜色选择组件设置背景颜色:
ColorDialog colorDialog = new ColorDialog();
colorDialog.Color = m_seal.BgColor.ToDrawingColor();
if (colorDialog.ShowDialog() == DialogResult.OK)
{
//m_seal.BgColor = SKColor.Parse(String.Format("#{0:X2}{1:X2}{2:X2}", colorDialog.Color.R, colorDialog.Color.G, colorDialog.Color.B));
m_seal.BgColor = SKColor.Parse(System.Drawing.ColorTranslator.ToHtml(colorDialog.Color));
txtBgColor.Text = m_seal.BgColor.ToString();
}
调用System.Text.Json.JsonSerializer序列化及反序列化SealInfo对象实例,下图是序列化结果,但是反序列化时SKColor对象的值没有正确获取,还不清楚问题原因。
最后是主要绘图代码及程序运行效果:
Func<float, int, float> unitConverter = m_currSeal.UnitType == UnitType.Pixel ? CommonFunction.Pixel2Pixel : CommonFunction.MM2Pixel; ;
SKCanvas canvas = e.Surface.Canvas;
canvas.Clear();
SKPaint sKPaint = new SKPaint();
sKPaint.Style = SKPaintStyle.Fill;
sKPaint.Color = m_currSeal.BgColor;
canvas.DrawRect(0, 0, unitConverter(m_currSeal.Width, skBoard.DeviceDpi), unitConverter(m_currSeal.Height, skBoard.DeviceDpi), sKPaint);
if(m_currSeal.HasBorder)
{
sKPaint.Style = SKPaintStyle.Stroke;
sKPaint.Color = m_currSeal.BorderColor;
float borderWidth= unitConverter(m_currSeal.BorderWidth, skBoard.DeviceDpi);
sKPaint.StrokeWidth= borderWidth;
SKRect rect = new SKRect(borderWidth / 2, borderWidth / 2, unitConverter(m_currSeal.Width, skBoard.DeviceDpi) - borderWidth / 2, unitConverter(m_currSeal.Height, skBoard.DeviceDpi) - borderWidth / 2);
canvas.DrawRect(rect, sKPaint);
}
参考文献:
[1]https://learn.microsoft.com/zh-cn/previous-versions/xamarin/xamarin-forms/user-interface/graphics/skiasharp/