尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
C#实现Gerber查看器:解析、渲染与二次开发实战
简介面向电子工程师、PCB设计师及C#图形编程学习者的Gerber查看器源码项目聚焦CAM/Genesis类Gerber文件的读入与可视化展示帮助读者理解文本指令解析、坐标与单位转换、图形渲染等关键环节。压缩包共35个文件约76KB以11个cs源码文件为核心配合resx与resources资源、config配置、csproj与sln工程文件及可执行程序构成可直接编译运行的完整解决方案目录中GMDO_V4.0体现版本迭代痕迹。已有3646人学习下载说明其在Gerber解析与WinForms/WPF绘图实践上具备参考价值。读者可从中获取文件逐行读取、指令解析器构建、图元对象建模到界面缩放平移的完整实现思路并借鉴层叠加、颜色编码、测量等扩展功能的组织方式适合作为EDA工具开发入门与二次开发的对照样本。1. 拿到一块 PCB 的 Gerber 文件我为什么先打开这个 C# 查看器上周帮同事排查一块四层板的开短路问题对方甩过来一个压缩包里面是十几个.gbr和.drl文件。用在线工具打开要上传、要等、还担心图纸外流装个大型 CAM 软件又得几百兆起步。这种时候我一般会先翻出自己常备的 Gerber 查看器——一个用 C# 写的桌面小工具双击就能跑把 Gerber 读入、渲染、分层显示一条龙做完。它解决的就是「手头有一堆 Gerber 文件想快速看一眼每层长什么样、焊盘和走线对不对」这个高频场景。适合做 PCB 设计、CAM 工程、硬件测试的开发者也适合想研究 Gerber 格式解析和 GDI 图形渲染的同学。下面我按「能干什么 → 怎么跑 → 核心怎么实现 → 怎么改」拆一遍。2. 技术栈与目录结构一个 WinForms 桌面程序的最小骨架2.1 技术选型为什么是 C# WinForms GDI这个项目定位是「本地、轻量、即开即用」的 Gerber 查看器所以技术栈非常克制。语言用 C#界面用 WinForms绘图用 GDISystem.Drawing文件解析全部手写不依赖任何第三方 PCB 库。这套组合的好处是.NET Framework 或 .NET 桌面运行时自带编译出来一个 exe 加几个 dll 就能跑不需要装 Python 环境、不需要 Node也不涉及浏览器上传。Gerber 文件本质是纯文本一行一条命令比如%FSLAX46Y46*%定义坐标格式、G01*切到线性插值、X1000000Y2000000D02*移动画笔、D03*打一个焊盘。解析器要做的就是把这些命令翻译成线段、圆弧、矩形、多边形再交给 GDI 画到画布上。选 GDI 而不是 Direct2D是因为 Gerber 单层图元数量通常在几万到几十万级别GDI 配合双缓冲足够流畅代码也简单得多。常见做法是把「解析」和「渲染」彻底分开解析层输出一个与界面无关的图元列表渲染层只负责把图元列表画出来。这样后面加导出 PNG、加测量工具、换渲染后端都不用动解析代码。我一般会按这个思路组织工程。2.2 目录结构按职责切成四块一个合格的 Gerber 查看器工程目录大致长这样不同版本命名会有出入但职责划分基本一致GerberViewer/ ├── GerberViewer.sln ├── GerberViewer/ │ ├── Program.cs // 入口启动主窗体 │ ├── Forms/ │ │ ├── MainForm.cs // 主界面菜单、工具栏、画布 │ │ └── LayerPanel.cs // 图层列表与显隐控制 │ ├── Gerber/ │ │ ├── GerberParser.cs // 逐行解析 Gerber 命令 │ │ ├── GerberCommand.cs // 命令枚举与参数结构 │ │ ├── Aperture.cs // 光圈D 码定义 │ │ └── GerberDocument.cs // 解析结果图层 图元集合 │ ├── Render/ │ │ ├── GerberRenderer.cs // 图元 - GDI 绘制 │ │ └── ViewTransform.cs // 缩放、平移、坐标映射 │ ├── Drill/ │ │ └── ExcellonParser.cs // 钻孔文件.drl解析 │ └── Utils/ │ └── UnitConverter.cs // 英寸/毫米、坐标格式换算 └── README.mdGerber/只做文本到图元的转换不碰任何 UI 类型Render/只认图元列表不认文件Forms/负责把两者串起来。这种切法在二次开发时特别省事——想加一个新图元类型改GerberCommand和GerberRenderer两处即可主窗体几乎不用动。2.3 环境准备与第一次运行运行环境要求不高Windows 10/11装 .NET Framework 4.7.2 以上或 .NET 6/8 桌面运行时Visual Studio 2022或 Rider能打开 sln 即可。步骤# 1. 克隆或解压源码后进入解决方案目录 cd GerberViewer # 2. 用 dotnet 命令行还原并编译.NET SDK 项目 dotnet restore dotnet build -c Release # 3. 运行如果项目是 .NET Framework用 VS 直接 F5 更稳 dotnet run --project GerberViewer/GerberViewer.csproj启动后主界面一般分三块左侧图层列表每层一个勾选框、中间绘图画布、顶部工具栏打开、缩放、适应窗口、单位切换。点「打开」选中一个 Gerber 文件或整个文件夹程序会逐文件解析并加入图层列表。第一次跑建议拿一块简单板子的顶层铜.gtl和阻焊.gts试图元少出问题好定位。提示如果打开后画布一片空白先确认文件是不是 RS-274X 格式文件头有%FS、%MO这类扩展命令。老式 RS-274D 需要单独的光圈表文件这个查看器通常不处理。3. 核心解析链路一行 Gerber 命令怎么变成屏幕上的线段3.1 坐标格式与单位先把「数」翻译对Gerber 里所有坐标都是整数真实尺寸靠文件头的格式声明还原。%FSLAX46Y46*%的意思是前导零省略L、绝对坐标A、X 和 Y 都是 4 位整数 6 位小数。所以X1000000实际是 1.000000 英寸如果单位是英寸。这一步错了整张图要么缩成一点要么放大到看不见是新手最容易翻车的地方。// UnitConverter.cs public class UnitConverter { public int IntegerDigits { get; private set; } 4; public int DecimalDigits { get; private set; } 6; public bool IsInch { get; private set; } true; // 解析 %FSLAX46Y46*% 这类格式声明 public void ParseFormatSpec(string line) { // 形如 FSLAX46Y46取 X 后两位数字 int xIdx line.IndexOf(X); if (xIdx 0 line.Length xIdx 2) { IntegerDigits line[xIdx 1] - 0; DecimalDigits line[xIdx 2] - 0; } } // 把 Gerber 整数坐标转成毫米内部统一用毫米 public double ToMillimeter(long raw) { double scale Math.Pow(10, DecimalDigits); double value raw / scale; // 先还原成文件单位 return IsInch ? value * 25.4 : value; // 英寸转毫米 } }IntegerDigits和DecimalDigits直接决定缩放系数ToMillimeter把内部坐标统一到毫米后面渲染、测量都基于毫米避免单位混用。注意%MOIN*%/%MOMM*%要单独解析并设置IsInch别只认格式声明忘了单位声明。3.2 命令分发一个 switch 吃掉大部分 Gerber 指令Gerber 的命令分两类扩展命令%...*%和普通命令...*。解析器按行读先判断是不是扩展命令再按首字符分发。核心就是一个状态机当前坐标、当前光圈、当前插值模式G01 直线 / G02 顺时针弧 / G03 逆时针弧、是否处于区域填充G36/G37。// GerberParser.cs节选 public GerberDocument Parse(string filePath) { var doc new GerberDocument(); var state new ParserState(); // 当前坐标、光圈、插值模式 foreach (var rawLine in File.ReadLines(filePath)) { var line rawLine.Trim(); if (line.Length 0) continue; if (line.StartsWith(%)) // 扩展命令 { HandleExtendedCommand(line, doc, state); continue; } // 普通命令可能一行多条按 * 切开 foreach (var cmd in line.Split(*, StringSplitOptions.RemoveEmptyEntries)) { HandleCommand(cmd, doc, state); } } return doc; } private void HandleCommand(string cmd, GerberDocument doc, ParserState state) { if (cmd.StartsWith(G04)) return; // 注释直接跳过 if (cmd.StartsWith(G01)) { state.Interpolation InterpMode.Linear; return; } if (cmd.StartsWith(G02)) { state.Interpolation InterpMode.CwArc; return; } if (cmd.StartsWith(G03)) { state.Interpolation InterpMode.CcwArc; return; } if (cmd.StartsWith(G36)) { state.InRegion true; return; } if (cmd.StartsWith(G37)) { state.InRegion false; doc.FlushRegion(); return; } // D01 画线 / D02 移动 / D03 打焊盘 var (x, y, dCode) ParseCoordinate(cmd, state); switch (dCode) { case 1: doc.AddLine(state.Current, (x, y), state.CurrentAperture); break; case 2: state.Current (x, y); break; case 3: doc.AddFlash((x, y), state.CurrentAperture); break; } if (dCode 1 || dCode 2) state.Current (x, y); }ParserState保存跨行的状态这是关键——Gerber 是流式命令D02移动后下一条D01才知道从哪画到哪。G36/G37之间的线段要收集成闭合多边形再填充不能逐段画线否则区域填充会变成一堆描边。ParseCoordinate里要处理省略坐标只给 X 不给 Y 表示 Y 不变这是另一个高频坑。3.3 光圈与焊盘D 码表怎么建、怎么用D10以上的 D 码是光圈编号具体形状由%ADD10C,0.5*%这类定义给出C圆形、R矩形、O椭圆、P多边形。解析时先建一张Dictionaryint, Aperture遇到D03就按当前光圈形状在坐标处画一个焊盘。// Aperture.cs public class Aperture { public int Code { get; set; } // D 码如 10 public char Shape { get; set; } // C/R/O/P public double[] Params { get; set; } // 尺寸参数单位毫米 public void Draw(Graphics g, PointF center, float scale) { float w (float)(Params[0] * scale); switch (Shape) { case C: g.FillEllipse(Brushes.Gold, center.X - w / 2, center.Y - w / 2, w, w); break; case R: float h (float)(Params[1] * scale); g.FillRectangle(Brushes.Gold, center.X - w / 2, center.Y - h / 2, w, h); break; // O、P 类似按参数画椭圆或多边形 } } }Params里存的是文件单位换算后的毫米值scale是当前视图缩放系数。焊盘用填充而不是描边视觉上更接近 CAM 软件的显示习惯。注意圆形光圈画D01线时线宽就是光圈直径GDI 里用Pen的Width设置即可别用默认 1 像素。4. 渲染与交互缩放平移不飘、图层叠加不乱4.1 视图变换世界坐标到屏幕坐标的一层映射画布上的所有绘制都要经过一次变换世界坐标毫米→ 屏幕坐标像素。把缩放和平移收进一个ViewTransform类鼠标滚轮改Scale、拖拽改Offset绘制时统一调用WorldToScreen。这样无论图元多少交互逻辑只有一处。// ViewTransform.cs public class ViewTransform { public float Scale { get; set; } 1f; // 像素/毫米 public PointF Offset { get; set; } // 屏幕平移量 public PointF WorldToScreen(PointF world) new PointF(world.X * Scale Offset.X, world.Y * Scale Offset.Y); // 以鼠标位置为中心缩放避免缩放时图形跑偏 public void ZoomAt(PointF screenPoint, float factor) { var before ScreenToWorld(screenPoint); Scale * factor; var after ScreenToWorld(screenPoint); Offset new PointF( Offset.X (after.X - before.X) * Scale, Offset.Y (after.Y - before.Y) * Scale); } public PointF ScreenToWorld(PointF screen) new PointF((screen.X - Offset.X) / Scale, (screen.Y - Offset.Y) / Scale); }ZoomAt里先算缩放前鼠标对应的世界坐标缩放后再算一次用差值修正Offset这样滚轮缩放时鼠标下的点始终不动。这是所有图形查看器的通用技巧少了它缩放体验会很别扭。Scale的初始值建议在「适应窗口」时按图形包围盒算出来别硬编码。4.2 双缓冲与分层绘制几十万图元也不闪WinForms 的Panel默认会闪解决办法是开双缓冲并在OnPaint里按图层顺序绘制。每个图层有自己的颜色和显隐开关绘制顺序按图层列表从上到下顶层铜最后画视觉上压在最上面。// MainForm.cs 中的画布绘制 protected override void OnPaint(PaintEventArgs e) { var g e.Graphics; g.SmoothingMode SmoothingMode.AntiAlias; g.Clear(Color.Black); foreach (var layer in _layers.Where(l l.Visible)) { using var pen new Pen(layer.Color, 1f); foreach (var seg in layer.Segments) { var p1 _transform.WorldToScreen(seg.Start); var p2 _transform.WorldToScreen(seg.End); pen.Width Math.Max(1f, (float)(seg.Width * _transform.Scale)); g.DrawLine(pen, p1, p2); } foreach (var flash in layer.Flashes) flash.Aperture.Draw(g, _transform.WorldToScreen(flash.Position), _transform.Scale); } }SmoothingMode.AntiAlias让斜线不锯齿pen.Width随缩放变化放大后线宽跟着变粗符合真实比例。图元特别多时可以按包围盒做视口裁剪只画屏幕内的线段这一条能把大板子的重绘时间从几百毫秒压到几十毫秒。图层颜色建议给一套默认配色顶层红、底层蓝、阻焊绿、丝印白和主流 CAM 软件的习惯对齐用户上手零成本。4.3 钻孔文件Excellon 和 Gerber 不是一回事很多人以为.drl也是 Gerber其实它是 Excellon 格式语法完全不同M48开头、T1C0.3定义刀具、X1000Y2000是钻孔坐标、M30结束。解析器要单独写一个ExcellonParser输出钻孔点列表渲染时画成小圆。单位声明在METRIC/INCH行别漏。// ExcellonParser.cs节选 public ListDrillHit Parse(string path) { var hits new ListDrillHit(); var tools new Dictionaryint, double(); // 刀具号 - 直径(mm) int currentTool 0; foreach (var line in File.ReadLines(path)) { if (line.StartsWith(T) line.Contains(C)) { // T1C0.3 - 刀具 1直径 0.3mm var parts line.Substring(1).Split(C); tools[int.Parse(parts[0])] double.Parse(parts[1]); } else if (line.StartsWith(T)) { currentTool int.Parse(line.Substring(1)); } else if (line.StartsWith(X)) { var (x, y) ParseXY(line); hits.Add(new DrillHit(x, y, tools.GetValueOrDefault(currentTool, 0.3))); } } return hits; }tools字典把刀具号和直径对上currentTool跟踪当前用的是哪把刀。钻孔渲染成实心圆直径按刀具值缩放和焊盘叠在一起就能看出孔位对不对。5. 避坑与排查这几个坑我踩过不止一次5.1 打开后图形缩成一个小点或撑满屏幕现象文件能解析但画布上要么一个像素点要么全是线。原因几乎都是坐标格式没解析对——%FSLAX46Y46*%里的 4 和 6 被当成固定值或者单位声明%MOIN*%没读到导致缩放系数差了几个数量级。解决在ParseFormatSpec里打印出IntegerDigits、DecimalDigits、IsInch三个值和文件头对照「适应窗口」时用图形包围盒反算Scale别用固定初值。5.2 区域填充G36/G37画出来是一堆描边现象铺铜区域显示成网格线而不是实心块。原因是把G36到G37之间的线段逐条DrawLine了没有收集成闭合路径再FillPolygon。解决在ParserState里维护一个ListPointF regionPointsG36开始收集G37时FlushRegion一次性填充。注意区域可能不闭合填充前要判断首尾点距离必要时补一条闭合边。5.3 省略坐标导致线段乱飞现象某些板子的走线突然连到原点或上一个很远的位置。原因是 Gerber 允许省略坐标X1000*表示只改 XY 保持上次的值Y2000*同理。如果解析时把缺失的坐标当 0 处理就会画出错误的线。解决ParseCoordinate里用state.Current兜底只更新命令里出现的那个轴。5.4 大文件打开卡死或内存暴涨现象几十 MB 的 Gerber 打开要十几秒任务管理器内存飙到几个 G。原因是把每个图元都存成独立对象且绘制时全量遍历。解决图元用结构体数组或ListT紧凑存储绘制前按视口包围盒裁剪解析用File.ReadLines流式读别ReadAllLines一次性载入。我一般还会加一个进度条解析超过 1 秒就显示避免用户以为程序挂了。5.5 图层颜色和显隐状态在重开后丢失现象调好的配色和图层开关关掉程序再打开又回到默认。原因是这些状态只存在内存里。解决用Properties.Settings或一个简单的 JSON 配置文件持久化图层名、颜色、可见性启动时按文件名匹配恢复。注意别把绝对路径写死换台机器就失效。6. 二次开发加一个测量工具顺便把解析层解耦拿到这份源码后最值得做的二次开发是加「测量」功能——点两个点算距离这是看 Gerber 时仅次于「看一眼」的高频需求。实现思路很直接在MainForm里加一个测量模式开关鼠标点击时用ScreenToWorld把屏幕坐标转回毫米坐标存两个点OnPaint里画一条高亮线和距离文字。核心就三行// 测量模式下的鼠标点击 var world _transform.ScreenToWorld(e.Location); _measurePoints.Add(world); if (_measurePoints.Count 2) { double dist Math.Sqrt( Math.Pow(_measurePoints[1].X - _measurePoints[0].X, 2) Math.Pow(_measurePoints[1].Y - _measurePoints[0].Y, 2)); _measureText ${dist:F3} mm; }ScreenToWorld是ViewTransform里现成的dist单位是毫米因为解析层已经统一换算过。这里能看出前面「解析与渲染分离」的价值测量只碰视图变换完全不关心 Gerber 语法。再往深改可以把GerberDocument的图元列表暴露成只读接口外面套一层导出模块把线段和焊盘转成 SVG 或 PNG。验证方法很简单拿一块已知尺寸的板子比如 100mm × 80mm 的矩形边框用测量工具量边框误差应在 0.01mm 以内再对比 CAM 软件里同一位置的坐标确认解析没有偏移。改造方向改动文件验证方式测量距离MainForm.cs、ViewTransform.cs量已知尺寸边框误差 0.01mm导出 PNGGerberRenderer.cs 新增方法导出图与屏幕显示逐像素对比支持 RS-274DGerberParser.cs、Aperture.cs用带光圈表的老文件解析不报错图层配色持久化MainForm.cs、配置文件重启后颜色和显隐状态一致血泪经验是改解析层之前先把ParserState的所有字段列出来确认新命令不会破坏跨行状态改渲染层之前先用小文件跑一遍别拿几十 MB 的大板子当试验田。从那以后我每次动 Gerber 解析代码都强制拿三块不同来源的板子各跑一遍——顶层铜、阻焊、钻孔全开确认没有回归再提交。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

数字藏品平台AI模型部署与优化实战指南

数字藏品平台AI模型部署与优化实战指南

1. 数字藏品平台AI模型部署的特殊性数字藏品平台与传统互联网应用有着本质区别,这直接影响了AI模型的部署方式。首先,数字藏品的核心价值在于稀缺性和唯一性验证,这就要求AI模型必须具备极高的识别准确率和防伪能力。我在为某知名数字艺术平台…

📅 2026/10/25 7:59:30
开发效率与运行性能的平衡策略与实践

开发效率与运行性能的平衡策略与实践

1. 开发效率与运行性能的平衡困境在软件工程实践中,我们常常面临一个经典的两难选择:是追求快速迭代的开发效率,还是确保系统长期稳定的运行性能?这个问题就像在赛车改装时,既要考虑轻量化带来的加速优势,又…

📅 2026/10/25 7:59:30
KV Cache 是什么?一文搞懂大模型推理显存占用与优化

KV Cache 是什么?一文搞懂大模型推理显存占用与优化

手头那个 7B 模型,同样一张 24GB 显卡,训练时跑得好好的,一到上线做推理,并发一上来直接 OOM。相信很多人第一次遇到这个问题时都是一脸懵——代码没变,模型没变,怎么推理反而比训练更吃显存?答…

📅 2026/10/25 7:59:30
MORE NEWS

更多资讯

📰

自学AI应用开发半年后,我为什么还是报了训练营?

1. 半年自学AI应用开发,我卡在了“能跑通”和“能交付”之间去年年初,我给自己定了个目标:用半年时间自学AI应用开发,争取能独立做出一个能拿得出手的项目。当时我的底气来自哪里?一是网上教程铺天盖地,从大…

📰

AI应用开发学习路径:先懂RAG与Agent原理再动手做项目

1. 为什么我不建议你上来就做项目1.1 一个很常见的误区:把“做项目”当成学习路径这两年AI应用开发的热度不用我多说,RAG、Agent、LangChain、ReAct这些词几乎天天在技术社区刷屏。我身边不少做Java后端的、做前端的、甚至做测试的朋友,都动了…

📰

从零搭建神经网络开发环境:TensorFlow与Keras实战指南

1. 从零搭建神经网络开发环境:工具链选型与安装实操很多人学神经网络卡在第一步——环境装不上。不是缺这个库就是版本冲突,折腾两三天热情就磨没了。我带过不少新人,发现八成以上的环境问题都出在“版本不匹配”和“装错包”这两件事上。这一…

📰

三月CV顶会观察:多模态、3D重建与模式识别落地实操

1. 三月的学术界在忙什么1.1 大模型之外,CV圈的真实热点又到三月中旬,这大概是计算机视觉方向最热闹的一段时间。CVPR 的投稿周期刚过,NeurIPS 的摘要提交还没开始,大家手里攒着的实验数据刚好到了一个能拿出来晒的阶段&#xff0…

📰

嵌入式工程师的端侧AI重构:从CNN物理层开始

1. 这不是“重学CNN”,而是嵌入式工程师的端侧AI能力重构我拆过不下二十块工业级ARM Cortex-M7开发板,写过裸机驱动、移植过RT-Thread、在Zynq上跑过PL端加速的OpenCV流水线——但第一次把ResNet-18量化后部署到RK3399上跑通实时目标检测时,盯…

📰

传统PID、BP与RBF神经网络自适应PID对比:原理、实现与选型指南

1. 项目缘起与核心思路拆解1.1 为什么还要折腾PID做控制这行的,没人不知道PID。比例、积分、微分三个环节,结构简单、物理意义明确、调参经验成熟,工业现场里超过八成的回路还是PID在扛。但问题也恰恰出在这里——固定参数的PID,本…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬