在本文中,将探讨如何将一个基于Gdiplus的Windows应用程序迁移到SkiaSharp,同时保持相同的功能和向后兼容性。这个项目是一个基于.NET Core6框架的Windows窗体应用程序,使用C#版本10编写,兼容Windows 7及更高版本。
在Gdiplus中,HatchBrush是一种特殊的画刷,它允许用特定的图案填充一个区域,这种图案被称为HatchStyle。Gdiplus提供了52种内置图案,可以用来创建画刷。以下是一些示例:
每个52种内置的Gdiplus HatchStyle都是基于一个8x8像素矩阵的图案(在某些情况下,有抗锯齿像素,稍后会讨论)。如果仔细观察每种风格,会看到构成图案的基本网格。
以下是一些放大的示例:
为了使用SkiaSharp重建这些图案,第一步是收集每个图案所需的信息。决定让机器来做这项工作,而不是手动分析每个HatchStyle(读作:失去视力)并创建8x8点的数组(或列表)。以下是读取模式像素的方法的代码:
static List<SKPoint> GetHatchPattern(HatchStyle hatch, bool inverted)
{
List<SKPoint> ret = new();
var bitmap = new Bitmap(_matrixSize, _matrixSize);
using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.SmoothingMode = SmoothingMode.Default;
Color color1 = inverted ? Color.White : Color.Black;
Color color2 = inverted ? Color.Black : Color.White;
using (HatchBrush brush = new(hatch, color1, color2))
{
Rectangle rect = new(0, 0, _matrixSize, _matrixSize);
graphics.FillRectangle(brush, rect);
}
int blackValue = hatch switch
{
HatchStyle.ForwardDiagonal or HatchStyle.BackwardDiagonal or HatchStyle.DiagonalCross => -15395563,
_ => Color.Black.ToArgb(),
};
for (int row = 0; row < _matrixSize; row++)
{
for (int col = 0; col < _matrixSize; col++)
{
Color pixel = bitmap.GetPixel(col, row);
if (pixel.ToArgb() == blackValue)
{
ret.Add(new(col, row));
}
}
}
}
return ret;
}
GetHatchPattern方法接受一个HatchStyle类型的参数,并生成一个SKPoint列表,稍后可以在SkiaSharp中使用。该方法还接受一个inverted参数,以便在背景/前景颜色方面反转点列表。
SkiaSharp基于Skia,这是一个开源的跨平台图形库,目前由Google拥有和维护。SkiaSharp使用SKPaint对象来定义路径和线条在表面上的绘制方式。为了能够创建一个hatch效果,需要设置SKPaint对象的PathEffect成员,通过SKPathEffect类的Create2DPath静态方法创建一个效果。
以下是代码:
SKRect rect = canvas.DeviceClipBounds;
using SKPath fillPath = new();
fillPath.AddRect(rect);
// ...
canvas.Clear(SKColors.White);
canvas.Save();
canvas.ClipRect(rect);
canvas.DrawPath(fillPath, paint);
canvas.Restore();
在这个代码片段中,变量canvas是从目标控件(通常来自PaintSurface事件)获得的绘图表面。需要几个步骤才能在特定原点绘制效果,并且一直绘制到区域的末端。首先,需要进行平移,以确保图案的原点与填充的区域的左上角匹配。其次,目标路径包含一个比目标区域大的矩形,但被剪切到它,以确保图案被绘制到区域的右下角。
在许多情况下,"on"点的数组可能会被简化为包括更大的矩形,这些矩形可以同时构成几个"on"点。此外,对于仅由直线组成的图案,也可以尝试Create2DLine PathEffect,以简化图案路径。
然而,在实验中,这两种方法都没有给出好的结果。由于超出知识范围的原因,一旦使用这两种技术之一应用了"优化"的图案,就会由于渲染结果中的伪影而产生不好的结果。
在现实世界的场景中,不会想在运行时重新创建HatchStyles,就像附带的示例所示,而是在项目中预先编译一个点列表,因为HatchStyles是不变的图案。
在项目的附带源代码中,会发现Form1.cs底部有一个Helper区域,其中有两个方法将返回每个52个HatchStyle的适当SKPoint数组,PathPointsNormal和PathPointsReverse。