SkiaSharp 基础绘图实战:在 .NET MAUI 中绘制一个简单圆形

发布时间:2026/10/13 6:35:02

SkiaSharp 基础绘图实战:在 .NET MAUI 中绘制一个简单圆形 图形学图像处理跨平台【免费下载链接】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 官方文档“Drawing Basics”系列中的Simple Circle示例为线索系统讲解在 .NET MAUI 应用中绘制 2D 图形所必需的三个核心构件承载绘图的SKCanvasView视图、负责重绘回调的PaintSurface事件以及定义颜色与线条属性的SKPaint对象。读完本文你将掌握“描边圆形轮廓 填充圆形内部”这一最经典的 SkiaSharp 绘制流程并能从源码层面理解SKSurface、SKCanvas、SKImageInfo之间的协作关系。绘制目标一个红边蓝心的圆示例页面要完成的效果非常直观在页面正中央绘制一个半径为 100 像素的圆圆的外部轮廓为红色描边宽度 25 像素圆的内部填充为蓝色。由于这个图形同时包含两种颜色绘制必须分两步完成——先用Stroke样式描出轮廓再切换为Fill样式填充内部两次调用同一个DrawCircle方法。示例页面类为SimpleCirclePage它派生自 MAUI 的ContentPage并在文件顶部引入了两个 SkiaSharp 命名空间using SkiaSharp; using SkiaSharp.Views.Maui.Controls;其中SkiaSharp提供全部绘图 APISKCanvas、SKPaint、SKColor等而SkiaSharp.Views.Maui.Controls则提供与 .NET MAUI 集成的视图控件SKCanvasView及颜色扩展方法。第一步创建 SKCanvasView 并订阅 PaintSurface绘图的第一步是创建一个SKCanvasView对象来承载图形。它本质上是 SkiaSharp 提供给 MAUI 的“画布宿主控件”在仓库中SKCanvasView.cs 将其定义为public partial class SKCanvasView : View, ISKCanvasView也就是说它本身就是一个标准的 MAUIView可以像其他视图一样参与页面布局甚至与其他 MAUI 控件混排。public SimpleCirclePage() { Title Simple Circle; SKCanvasView canvasView new SKCanvasView(); canvasView.PaintSurface OnCanvasViewPaintSurface; Content canvasView; }这段代码做了三件事设置页面标题、为PaintSurface事件挂接处理函数、把画布视图设为页面内容。此时SKCanvasView会占据页面整个内容区域你也可以在 XAML 中声明它甚至与其他 MAUIView组合布局。所有绘图都发生在 PaintSurface 事件里PaintSurface是 SkiaSharp 视图控件的核心事件所有绘制代码都必须写在这个事件的处理函数中。它并不只在首次显示时触发一次——窗口大小变化、页面重新布局、调用InvalidateSurface手动请求重绘等场景都会再次触发它。因此处理函数必须是无状态的它应当根据每次传入的参数尤其是画布尺寸重新构建完整画面而不是依赖上一次调用留下的中间状态。void OnCanvasViewPaintSurface(object? sender, SKPaintSurfaceEventArgs args) { ... }事件参数Info 与 SurfacePaintSurface的事件参数类型为SKPaintSurfaceEventArgs。在仓库源码 SKPaintSurfaceEventArgs.cs 中它封装了两个核心只读属性属性类型含义InfoSKImageInfo描述绘图表面的尺寸与像素格式最重要的是以像素为单位的宽和高SurfaceSKSurface绘图表面本身代表一块可绘制的画布SKSurface表示“绘制到哪里去”的目标。在本例中绘图表面就是屏幕上的视频显示区域但在其他程序中同一个SKSurface也可以代表一张位图——这正是 SkiaSharp 既能做屏幕渲染、又能离屏绘制图片的底层原因。SKImageInfo则是一个描述图像尺寸和像素类型的结构体。查看 SKImageInfo.cs 源码可以发现除Width、Height之外它还携带ColorType像素颜色类型如Rgba8888/Bgra8888、AlphaType透明度类型和ColorSpace色彩空间等信息并提供BytesSize、RowBytes等内存布局计算属性。值得留意的是事件参数中还包含一个RawInfo属性。从源码注释可知当SKCanvasView.IgnorePixelScaling为true时Info返回用户可见的逻辑尺寸而RawInfo始终返回物理像素尺寸在高 DPI 屏幕上两者往往并不相等。第二步获取 SKCanvas 并清屏SKSurface上最重要的属性是Canvas类型为SKCanvas。SKCanvas是真正的“绘图上下文”所有图形绘制操作都由它完成它同时封装了图形状态包括变换transform和裁剪clip。在 SKSurface.cs 源码中Canvas属性通过SkiaApi.sk_surface_get_canvas(Handle)获取底层 Skia 画布对象。一个典型的PaintSurface处理函数开头如下void OnCanvasViewPaintSurface(object? sender, SKPaintSurfaceEventArgs args) { SKImageInfo info args.Info; SKSurface surface args.Surface; SKCanvas canvas surface.Canvas; canvas.Clear(); ... }canvas.Clear()用透明色清空整个画布为新一轮绘制提供干净的起点。从 SKCanvas.cs 源码可以看到Clear()无参版本等价于Clear(SKColors.Empty)而SKColors.Empty在 SKColors.cs 中定义为new SKColor(0x00000000)即全透明重载版本Clear(SKColor color)则允许你指定一个背景色。底层分别通过sk_canvas_clear与sk_canvas_clear_color4f调用 Skia 原生库。第三步用 SKPaint 描出红色轮廓SKPaint是 SkiaSharp 中定义“如何绘制”的对象——它几乎就是一组绘图属性的集合颜色、样式、线宽、抗锯齿等。第一步先描边void OnCanvasViewPaintSurface(object? sender, SKPaintSurfaceEventArgs args) { ... SKPaint paint new SKPaint { Style SKPaintStyle.Stroke, Color Colors.Red.ToSKColor(), StrokeWidth 25 }; ... }StyleStroke 与 Fill 的差别Style属性决定图形是描边stroke还是填充fill。枚举SKPaintStyle在 SKPaintStyle.generated.cs 中定义共三个成员直接对应原生 Skia 的sk_paint_style_t成员原生值含义Fill0只填充几何图形内部默认值Stroke1只描边几何图形轮廓StrokeAndFill2既描边又填充且两者使用同一颜色这里把Style设为Stroke因此后续绘制的圆只有轮廓、没有内部填充。ColorMAUI 颜色与 SKColor 的转换Color属性要求SKColor类型的值。SKColor是 SkiaSharp 的颜色结构体ARGB 各 8 位。把 .NET MAUI 的Color转成SKColor的推荐方式是调用扩展方法ToSKColor。其实现位于 Extensions.cspublic static SKColor ToSKColor(this Color color) new SKColor((byte)(color.Red * 255), (byte)(color.Green * 255), (byte)(color.Blue * 255), (byte)(color.Alpha * 255));可以看到它把 MAUIColor的 0~1 浮点通道值线性映射到 0~255 的字节范围。Extensions类中还提供了ToSKColorF等其他 MAUI 与 SkiaSharp 之间的转换方法。StrokeWidth描边宽度StrokeWidth指定线条粗细本例设为 25 像素。从 SKPaint.cs 源码看它通过sk_paint_get_stroke_width/sk_paint_set_stroke_width与原生 Skia 交互默认值为 0此时描边使用约 1 像素的细线。第四步调用 DrawCircle 绘制轮廓准备好SKPaint之后就可以调用SKCanvas.DrawCircle绘制圆形void OnCanvasViewPaintSurface(object? sender, SKPaintSurfaceEventArgs args) { ... canvas.DrawCircle(info.Width / 2, info.Height / 2, 100, paint); ... }DrawCircle在 SKCanvas.cs 中有两个重载DrawCircle(float cx, float cy, float radius, SKPaint paint)和DrawCircle(SKPoint c, float radius, SKPaint paint)后者内部会转发到前者。其参数含义如下前两个参数圆心的 X、Y 坐标第三个参数圆半径本例固定 100 像素最后一个参数携带样式属性的SKPaint对象。这里用info.Width / 2和info.Height / 2计算画布宽高的一半从而把圆心放在显示区域的中心。坐标系约定SkiaSharp 的坐标系以显示表面左上角为原点 (0, 0)X 轴向右递增Y 轴向下递增。这一点与数学中常见的坐标系Y 轴向上方向相反初学者最容易混淆。在本文系列中坐标均指像素坐标。第五步切换属性填充蓝色内部第二步绘制改用Fill样式填充圆内部。示例巧妙地复用同一个SKPaint对象——只修改它的Style和Color两个属性然后再次调用DrawCirclevoid OnCanvasViewPaintSurface(object? sender, SKPaintSurfaceEventArgs args) { ... paint.Style SKPaintStyle.Fill; paint.Color SKColors.Blue; canvas.DrawCircle(args.Info.Width / 2, args.Info.Height / 2, 100, paint); }这里展示了获取SKColor的另一种方式直接使用SKColors结构体中预定义的颜色字段。在 SKColors.cs 中SKColors.Blue定义为new SKColor(0xFF0000FF)不透明纯蓝此外还有AliceBlue、BlueViolet等 140 余种预置颜色可直接选用。深入理解为什么 PaintSurface 会被反复调用当你旋转手机或调整模拟器方向时图形会被自动重绘——这正是PaintSurface事件机制的意义所在每次需要重绘时事件都会重新触发处理函数依据最新的args.Info尺寸重建画面。在本系列后续的 integration.md 中你会看到更主动的用法在触摸事件处理函数中调用SKCanvasView.InvalidateSurface()来手动触发一次PaintSurface重绘从而实现“点击切换圆形填充与否”的交互效果。实用建议抗锯齿与 SKPaint 复用抗锯齿SKPaint类提供IsAntialias属性见 SKPaint.cs底层为sk_paint_is_antialias。开启后图形边缘会明显更平滑因此绝大多数实际项目中都建议将其设为true。出于示例简洁性的考虑本文示例页面没有设置它。SKPaint 是轻量对象它本质上只是一组绘图属性的集合。你可以像本示例一样反复修改并复用同一个对象也可以为不同的属性组合创建多个SKPaint对象还可以在PaintSurface事件之外创建和初始化它们并作为字段保存在页面类中避免每帧重绘时重复构造。补充原理25 像素的描边为何看起来更细细心的读者会发现虽然轮廓宽度被设置为 25 像素圆半径的 1/4但最终效果看起来比 25 像素更细。原因在于DrawCircle的参数定义的是抽象几何坐标而描边线以该几何圆为基准向两侧对称展开——一半宽度在圆内侧一半在圆外侧。蓝色内部填充恰好覆盖了内侧那一半约 12.5 像素所以可见的红色轮廓只剩外侧的一半。这一点会在 integration.md 的示例中通过可视化方式进一步展示。相关链接SkiaSharp 绘图基础系列索引包含像素与设备无关单位、基础动画、文本集成、位图基础与透明度等进阶主题与 .NET MAUI 集成通过触摸交互与 MAUI 控件组合创建可交互的 SkiaSharp 图形SKCanvasView 控件实现MAUI 视图层封装与IgnorePixelScaling绑定属性SKPaintSurfaceEventArgs 事件参数Info/Surface/RawInfo的完整定义SKCanvas 绘图方法Clear、DrawCircle等绘制的底层调用SKPaint 属性定义Style、Color、StrokeWidth、IsAntialias赞分享图形学图像处理跨平台【免费下载链接】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绘制复杂图形.NET MAUI高级可视化使用SkiaSharp绘制复杂图形.NET MAUI高级可视化 概述 在跨平台应用开发中实现高性能、高质量的图形绘制一直是开发者面临的挑战。.NET MAU前端移动开发桌面应用跨平台UI组件flet.canvas.Arc 绘图详解在 Flet 中绘制圆弧、扇形与圆环的完整指南flet.canvas.Arc 绘图详解在 Flet 中绘制圆弧、扇形与圆环的完整指南 导读 flet.canvas.Arc 是 Flet 内置画布Canv前端跨平台桌面应用移动开发PaddleSpeech 标点恢复paddlespeech.text.models.ernie_linear 模块源码级解析与实战指南PaddleSpeech 标点恢复paddlespeech.text.models.ernie_linear 模块源码级解析与实战指南 本文围绕 Paddle图形学图像处理跨平台上一篇Pylint redefined-builtinW0622检查详解内置名称遮蔽的规则、例外与配置下一篇如何高效使用Legacy iOS Kit面向开发者的完整iOS设备降级与越狱终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/10/13 6:35:02

提花工艺原理与大/小提花本质区别解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 6:35:02

地图可视化从零实现:行政区边界渲染与Canvas性能优化

lingbot-map 这个项目名第一次出现在我们仓库里时,大家的第一反应都是“这是什么”。项目代号里的人脸是个跟得住的内部玩笑,说是“灵巧机器人”,其实跟机器人没多大关系。它是我们那段时间被地图可视化反复毒打之后沉淀出来的一个模块——负…

2026/10/13 6:30:02

基于Simulink的根轨迹法控制器设计全流程与仿真验证

做控制设计这些年,折腾过不少工具和理论,如果要选一套从课堂知识快速落到工程仿真的经典打法,基于Simulink的根轨迹法控制器设计绝对排得上号。根轨迹法看的是闭环特征根随参数变化的走向,Simulink做的是把传递函数模型拖成框图跑…

2026/10/13 11:11:07

用Python挖掘利率与汇率背后的宏观风险信号

利率和汇率,是宏观金融分析里绕不开的两个变量。我刚开始做固收研究那阵子,带我的前辈提过一句:搞懂利率,你才能看懂债券市场的定价逻辑;搞懂汇率,你才能想明白资金在跨市场之间为什么这样流动。这话糙&…

2026/10/13 11:11:07

Flink Checkpoint排查实战:四个Tab定位慢/失败SubTask

前阵子生产集群上一个实时报表作业的 Checkpoint 连续红了三四次,业务方已经看到分钟级延迟还在涨。我打开 Flink Web UI 的 Checkpoint 页面,第一反应是盯着 Overview 顶部的状态卡:上一次是 Failed,再上一次也是 Failed。但说实…

2026/10/13 11:11:07

跑腿平台怎么选?闪送、美团跑腿、趣来达模式与成本全对比

先问一句:你有多久没用跑腿了?说实话,我以前也总觉得这类App是“懒人专用”,跟自己没什么关系。直到有一次工作日要给客户送合同,自己实在走不开,顺手下了个闪送单,40分钟送达、回传签收照片&am…

2026/10/13 11:11:07

规范的AI论文平台梯队榜(2026 精选)

基于综合性能、学术适配度、用户口碑和功能完整性,以下是当前主流AI论文写作工具的权威排名,按综合推荐指数从高到低排列,并标注核心优势与适用场景。🏆 第一梯队:全流程学术解决方案(★★★★★&#xff0…

2026/10/13 11:11:07

C语言main函数参数解析与命令行工具开发实践

1. 主函数传参的本质与应用场景在C语言开发中,main函数作为程序入口点,其参数传递机制是每个开发者必须掌握的基础知识。很多人初学时会疑惑:为什么main函数需要参数?这些参数从哪里来?实际上,这种设计体现…

2026/10/13 11:06:06

t3code:三层代码质量门禁,让低级故障不再上线

t3code 这个名字,看起来像是某个开源项目的代号,其实是我自己内部用了快两年的代码质量管控方案代号。以前我们团队经常出现这种场景:功能开发完,本地跑一遍没问题,代码 review 也只是看看逻辑对不对,结果合…

2026/10/13 5:19:27

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/12 12:01:26

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/12 12:54:07

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/13 0:04:41

基于Triton部署YOLO目标检测:从训练脚本到线上服务

简介:这份资源面向希望将目标检测模型落地到生产环境的深度学习开发者与算法工程师,聚焦于用 Triton Inference Server 部署 YOLOv9 的完整实战方案,解决从训练权重到线上推理服务之间的工程化难题。压缩包共 16 个文件,约 886KB&…

2026/10/13 0:04:41

手把手教你云端部署Qwen-Image-2.1:从GPU选型到在线文生图服务

Qwen-Image-2.1 这个开源的图像生成模型,最近在内容创作和 AI 应用圈子里热度相当高。它属于通义 Qwen 系列里的视觉生成分支,让我眼前一亮的是它对中文长文本的理解能力——以前用那些老方案生成带中文文字的图,十个里九个是“乱码印刷体”&…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑