从Gdiplus到SkiaSharp的图形输出迁移

在本文中,将探讨如何将一个基于Gdiplus的Windows应用程序迁移到SkiaSharp,同时保持相同的功能和向后兼容性。这个项目是一个基于.NET Core6框架的Windows窗体应用程序,使用C#版本10编写,兼容Windows 7及更高版本。

什么是HatchBrush

在Gdiplus中,HatchBrush是一种特殊的画刷,它允许用特定的图案填充一个区域,这种图案被称为HatchStyle。Gdiplus提供了52种内置图案,可以用来创建画刷。以下是一些示例:

  • 百分号20 HatchStyle
  • 对角砖 HatchStyle
  • 对角十字 HatchStyle

什么是HatchStyle

每个52种内置的Gdiplus HatchStyle都是基于一个8x8像素矩阵的图案(在某些情况下,有抗锯齿像素,稍后会讨论)。如果仔细观察每种风格,会看到构成图案的基本网格。

以下是一些放大的示例:

  • 百分号20图案
  • 对角砖图案
  • 对角十字图案

如何使用SkiaSharp重建HatchStyles

为了使用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绘制Hatch效果

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。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485