资讯动态

SkiaSharp 路径基础:用 SKPath 组合连接线与曲线

发布时间:2026/10/14 12:02:08 来源:尧图企业网站定制
图形学图像处理跨平台【免费下载链接】SkiaSharpSkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Googles Skia Graphics Library. It provides a comprehensive 2D API that can be used across mobile, server and desktop models to render images.项目地址https://gitcode.com/gh_mirrors/sk/SkiaSharp点击查看免费下载导读本文围绕 SkiaSharp 的SKPath对象系统讲解图形路径Graphics Path的核心概念——轮廓Contour、当前点Current Point、五种线段/曲线追加方法及其相对坐标变体、Close闭合语义、批量添加几何轮廓的Add*方法以及通过SKPaint.StrokeJoin与StrokeMiter控制拐角外观的机制。读完本文你将能够在 .NET MAUI、WPF、WinUI 等任何 SkiaSharp 场景中用纯几何方式构建并渲染可复用的复杂矢量图形。为什么是否连接是路径的第一要义图形路径Graphics Path最重要的能力之一是精确定义哪些线应该相连、哪些线应该断开。这个区别在视觉上可能非常显著——例如两个三角形顶部顶点的处理一条看似相连的折线与一条真正闭合的轮廓在端点处的渲染结果完全不同。在 SkiaSharp 中图形路径由SKPath对象封装。一个路径是一个或多个轮廓Contour的集合每个轮廓是一组相互连接的直线与曲线。轮廓与轮廓之间彼此不相连但它们可能在视觉上相互重叠有时单个轮廓也会自交。using var path new SKPath(); // 第一个轮廓开放 path.MoveTo(100, 100); path.LineTo(200, 100); path.LineTo(150, 200); // 第二个轮廓闭合 path.MoveTo(300, 100); path.LineTo(400, 100); path.LineTo(350, 200); path.Close();轮廓的起点MoveTo 与当前点每个轮廓一般以SKPath的MoveTo方法开始其参数是一个点既可以传SKPoint值也可以直接传独立的 X、Y 坐标。MoveTo调用会确立轮廓的起点同时设置一个初始的当前点Current Point——它是后续所有追加操作隐含的起点。从源码可以看到SKPath为MoveTo提供了两个重载并在当前仓库版本中被标记为[Obsolete]实际委托给内部的SKPathBuilder完成见 SKPath.cs[Obsolete (Use SKPathBuilder instead.)] public void MoveTo (SKPoint point) { EnsureBuilder (); _builder.MoveTo (point); } [Obsolete (Use SKPathBuilder instead.)] public void MoveTo (float x, float y) { EnsureBuilder (); _builder.MoveTo (x, y); }SKPathBuilder是 SkiaSharp 当前推荐的增量式路径构造入口其底层直接调用SkiaApi.sk_pathbuilder_move_to见 SKPathBuilder.cs支持Snapshot()取得当前路径副本、Detach()移交路径所有权并重置构造器SKPathBuilder.cs。官方推荐的新写法如下using var builder new SKPathBuilder(); builder.MoveTo(0, 0); builder.LineTo(100, 0); builder.LineTo(50, 100); builder.Close(); using var path builder.Detach();追加线段与曲线的五种方法从当前点出发可以用下列方法为轮廓追加一段线或曲线终点会顺理成章地成为新的当前点方法作用关键参数LineTo追加一条直线终点1 个点ArcTo追加圆弧——圆周或椭圆周上的一段线半径、x 轴旋转角、大/小弧、扫掠方向、终点CubicTo追加三次贝塞尔样条2 个控制点 1 个终点QuadTo追加二次贝塞尔样条1 个控制点 1 个终点ConicTo追加有理二次贝塞尔样条可精确渲染圆锥曲线椭圆、抛物线、双曲线1 个控制点 终点 权重w这五种方法都不包含描述完整线/曲线的全部信息它们必须与紧邻的上一次方法调用所确立的当前点协同工作。例如LineTo只接受一个终点因为起点就是当前点。仓库源码完整实现了这些方法的所有重载包括SKPoint与裸float坐标两种形态见 SKPath.cs。其中 SVG 风格圆弧重载ArcTo(SKPoint r, float xAxisRotate, SKPathArcSize largeArc, SKPathDirection sweep, SKPoint xy)支持通过SKPathArcSize枚举选择绘制最小弧还是最大弧、通过SKPathDirection控制顺时针/逆时针扫掠而ArcTo(SKRect oval, float startAngle, float sweepAngle, bool forceMoveTo)则以角度制描述弧段SKPath.cs。ConicTo的权重w决定了曲线拉向控制点的程度w 1时等价于二次贝塞尔w 1趋向椭圆弧w 1趋向双曲线。相对坐标变体R 开头方法族SKPath还定义了与上述六个方法同名、但以R开头的一组方法RMoveToRLineToRArcToRCubicToRQuadToRConicToR代表relative相对。这些方法与不带R的对应方法语法完全相同但坐标都被解释为相对于当前点的偏移量。它们非常适合在可多次调用的方法中绘制路径中重复出现的相似部分——同一个图元函数可以在不同位置反复调用而不必每次都计算绝对坐标。一个值得注意的语义细节在Close之后调用R*方法偏移量是相对闭合所回到的起点计算的而不是相对闭合前的最后一点。仓库测试 PathConsistentAfterClose 专门验证了这一点基于 Skia 上游test_path_close_issue1474using var builder new SKPathBuilder(); builder.RLineTo(0, 100); builder.RLineTo(100, 0); builder.Close(); // 回到 (0,0) builder.RLineTo(50, 50); // 应到达 (50,50)而不是 (150,50) using var path builder.Detach(); Assert.Equal(50, path.LastPoint.X); Assert.Equal(50, path.LastPoint.Y);轮廓的结束MoveTo 与 Close 的语义差异一个轮廓以以下两种方式之一结束再次调用MoveTo/RMoveTo开始一个新轮廓调用Close闭合当前轮廓。Close会自动从当前点向轮廓的第一个点追加一条直线并把路径标记为闭合——闭合路径在渲染时不会绘制任何线帽stroke cap。从源码看Close在 SKPath.cs 中定义其注释明确指出若当前点与轮廓起点不重合会自动追加一条线段。仓库测试 PathConsistentAfterClose 还验证了Close后R*相对方法基于闭合起点的行为说明该语义在 Skia 原生层与托管绑定层均保持一致。完整示例两个三角形轮廓下面的示例来自原文档的Two Triangle Contours页面使用一个SKPath的两个轮廓渲染两个三角形——第一个轮廓开放、第二个轮廓闭合void OnCanvasViewPaintSurface(object? sender, SKPaintSurfaceEventArgs args) { SKImageInfo info args.Info; SKSurface surface args.Surface; SKCanvas canvas surface.Canvas; canvas.Clear(); // 创建路径 SKPath path new SKPath(); // 定义第一个轮廓开放三角形 path.MoveTo(0.5f * info.Width, 0.1f * info.Height); path.LineTo(0.2f * info.Width, 0.4f * info.Height); path.LineTo(0.8f * info.Width, 0.4f * info.Height); path.LineTo(0.5f * info.Width, 0.1f * info.Height); // 定义第二个轮廓闭合三角形 path.MoveTo(0.5f * info.Width, 0.6f * info.Height); path.LineTo(0.2f * info.Width, 0.9f * info.Height); path.LineTo(0.8f * info.Width, 0.9f * info.Height); path.Close(); // 创建两个 SKPaint 对象 SKPaint strokePaint new SKPaint { Style SKPaintStyle.Stroke, Color SKColors.Magenta, StrokeWidth 50 }; SKPaint fillPaint new SKPaint { Style SKPaintStyle.Fill, Color SKColors.Cyan }; // 填充并描边路径 canvas.DrawPath(path, fillPaint); canvas.DrawPath(path, strokePaint); }第一个轮廓由一次MoveTo使用裸 X、Y 坐标而非SKPoint加三次LineTo构成三角形三条边第二个轮廓只有两次LineTo但以Close收尾自动补上最后一条边。差异非常显著第一个轮廓看起来是三条相连的线但末端并未连回起点两条线在顶部互相重叠第二个轮廓则明显闭合且少调用了一次LineTo。填充不闭合轮廓的规则SKCanvas只定义了一个DrawPath方法本示例对其调用了两次以分别填充和描边。所有轮廓都会被填充即使它们并未闭合——在填充未闭合路径时Skia 会假定轮廓起点与终点之间存在一条直线。因此如果你删掉第一个轮廓的最后一次LineTo或者删掉第二个轮廓的Close调用每个轮廓将只剩两条边但仍会像三角形一样被填充。这一点对理解路径几何与填充渲染的区别至关重要。批量添加完整轮廓Add 方法族SKPath还定义了若干一次性添加整个轮廓的方法这些轮廓是否闭合取决于具体方法方法行为AddRect添加闭合矩形轮廓可通过SKPathDirection指定绕行方向还可通过startIndex指定起点角标0..3AddRoundedRect添加闭合圆角矩形轮廓SKRoundRect或SKRect rx/ry两种重载AddCircle添加闭合圆形轮廓圆心 X/Y 半径 方向AddOval添加闭合椭圆轮廓边界SKRect 方向AddArc在椭圆周上添加一段曲线边界SKRect 起始角 扫掠角均为角度制AddPath将另一条路径追加到当前路径可按(dx, dy)平移或应用SKMatrix变换SKPathAddMode.Append作为新轮廓追加Extend则延伸当前最后一个轮廓AddPathReverse以相反顺序添加另一条路径源码中这些方法均以[Obsolete]兼容形式存在于 SKPath.cs并委托给SKPathBuilder的同名方法SKPathBuilder.cs。其中AddPath的Append/Extend模式由SKPathAddMode枚举控制AddPathReverse对应 builder 的ReverseAddPath用于反转轮廓走向——这在实现双边描边路径镜像等效果时非常实用。值得注意的是方向参数SKPathDirection.Clockwise顺时针默认值与SKPathDirection.CounterClockwise逆时针会影响填充规则的判定结果与FillType配合时尤其重要枚举定义见 SKPathDirection.generated.cs。路径只描述几何渲染交给 SKPaint务必牢记SKPath对象只定义几何——一系列点及其连接关系。只有当SKPath与SKPaint对象结合时路径才会以特定颜色、线宽等样式渲染出来。同时传给DrawPath的SKPaint对象定义了整条路径的外观特性。如果你需要绘制多种颜色的内容就必须为每种颜色使用独立的路径或至少独立的绘制调用因为单个SKPaint无法为路径的不同部分分别着色。拐角外观StrokeJoin 与 StrokeMiter正如线条的起止外观由线帽stroke cap定义两条线连接处的外观由**拐角连接stroke join**定义。通过设置SKPaint的StrokeJoin属性为SKStrokeJoin枚举成员来控制枚举成员效果Miter尖锐的拐角默认Round圆角连接Bevel削平/截断式连接枚举定义见 SKStrokeJoin.generated.cs其底层对应 Skia 的sk_stroke_join_tMiter 0、Round 1、Bevel 2。SKPaint.StrokeJoin属性直接映射到原生sk_paint_set_stroke_join调用SKPaint.cs。Stroke Joins 示例原文档的Stroke Joins页面循环遍历三个枚举成员每个都绘制一条粗线50 像素并在其上叠加一条细线2 像素以标示几何端点void OnCanvasViewPaintSurface(object? sender, SKPaintSurfaceEventArgs args) { SKImageInfo info args.Info; SKSurface surface args.Surface; SKCanvas canvas surface.Canvas; canvas.Clear(); SKPaint textPaint new SKPaint { Color SKColors.Black }; SKFont font new SKFont { Size 75 }; SKPaint thickLinePaint new SKPaint { Style SKPaintStyle.Stroke, Color SKColors.Orange, StrokeWidth 50 }; SKPaint thinLinePaint new SKPaint { Style SKPaintStyle.Stroke, Color SKColors.Black, StrokeWidth 2 }; float xText info.Width - 100; float xLine1 100; float xLine2 info.Width - xLine1; float y 2 * font.Spacing; string[] strStrokeJoins { Miter, Round, Bevel }; foreach (string strStrokeJoin in strStrokeJoins) { // 显示文字 canvas.DrawText(strStrokeJoin, xText, y, SKTextAlign.Right, font, textPaint); // 解析枚举值 SKStrokeJoin strokeJoin; Enum.TryParse(strStrokeJoin, out strokeJoin); // 创建路径一个折角 SKPath path new SKPath(); path.MoveTo(xLine1, y - 80); path.LineTo(xLine1, y 80); path.LineTo(xLine2, y 80); // 绘制粗线与细线 thickLinePaint.StrokeJoin strokeJoin; canvas.DrawPath(path, thickLinePaint); canvas.DrawPath(path, thinLinePaint); y 3 * font.Spacing; } }Miter 连接的长度限制Miter 连接由两条线交汇处的一个尖锐尖角构成。当两条线以很小的夹角连接时miter 尖角会变得非常长。为防止出现过度细长的尖角miter 的长度受SKPaint.StrokeMiter属性限制SKPaint.cs映射到原生sk_paint_get/set_stroke_miter。一旦 miter 长度超过该阈值就会被削平退化为 bevel 连接——这是 Skia 遵循 SVG/PostScript 规范的默认保护行为在绘制锐角折线如星形、闪电符号时尤其需要注意调整该值。与相邻主题的衔接路径基础是整个 SkiaSharp 图形绘制体系的地基相邻文档 Lines and Stroke Caps 讲解了单条直线渲染与SKStrokeCapButt/Square/Round的差异并指出SKPointMode.Points/Lines/Polygon三种模式渲染多个点的方式——其中Polygon模式绘制的各段线并不真正相连这正是理解路径连接意义的关键伏笔The Path Fill Types 深入探讨SKPathFillType填充规则后续的 SkiaSharp Curves and Paths 系列则专门讨论SKPath支持的各种曲线类型。相关资源SKPath 核心实现SKPathBuilder 增量构造器SKPaint 描边属性StrokeJoin / StrokeMiter / StrokeCapSKStrokeJoin 枚举定义SKPath 行为测试赞分享图形学图像处理跨平台【免费下载链接】SkiaSharpSkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Googles Skia Graphics Library. It provides a comprehensive 2D API that can be used across mobile, server and desktop models to render images.项目地址https://gitcode.com/gh_mirrors/sk/SkiaSharp点击查看免费下载相关推荐JointJS 曲线连接器Curve Connector实战用动态曲线连接元素JointJS 曲线连接器Curve Connector实战用动态曲线连接元素 JointJS 的 curve 连接器connector能够生成一条随前端UI组件PayPal-Python-SDK终极指南快速掌握RESTful API支付集成PayPal Python SDK终极指南快速掌握RESTful API支付集成 PayPal Python SDK是一款强大的Python开发工具包专为P后端金融科技终极xyflow连接线指南从基础连接到高级自定义连线终极xyflow连接线指南从基础连接到高级自定义连线 xyflow是基于React和Svelte的强大开源库用于构建节点式用户界面UI。它提供了丰富的连前端UI组件图表库上一篇Sollumz3步在Blender中制作GTA V游戏模组的完整指南下一篇Moonlight-Switch终极指南如何将任天堂Switch变身高性能游戏串流终端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑