需设TextRenderingHint.ClearTypeGridFit以启用ClearType子像素渲染,避免用AntiAliasGridFit;文字对齐应按ascent计算baseline偏移或用StringFormat居中;PNG透明背景需用Format32bppPArgb格式并确保Brush alpha=255;高DPI下须声明DPI感知或手动ScaleTransform。

DrawString 字体发虚、边缘锯齿严重怎么办
默认用 Graphics.DrawString 直接画文字,尤其在小字号或斜体时,边缘毛糙、灰度不均,不是“没抗锯齿”,而是用了错误的文本渲染模式。GDI+ 默认走的是 TextRenderingHint.SystemDefault,它会把字体交给系统渲染器(比如 GDI 的 bitmap font fallback),绕过 ClearType,结果就是模糊或带色边。
实操建议:
- 必须显式设置
graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit—— 这是 Windows 上最接近 WPF 渲染质量的选项,依赖 ClearType 子像素定位 - 避免用
TextRenderingHint.AntiAliasGridFit:它强制灰阶抗锯齿,不利用子像素,在 LCD 屏上反而更虚、发灰 - 如果目标图像是非 96 DPI(比如高分屏截图后缩放处理),需同步设置
graphics.PageScale或提前缩放字体大小,否则 ClearType 效果失效
DrawString 文字位置偏移、对不齐 baseline
看起来“写歪了”或者 y 坐标和预期差一截,本质是没理解 DrawString 的坐标系起点:它以传入的 PointF 或 RectangleF 左上角为原点,按字体的 **ascent**(上行高度)向上撑开,不是从 baseline 开始画。
常见错误现象:
- 用
e.Graphics.DrawString("A", font, brush, x, y)传入 y = 100,结果“A”整体浮在 100 线上方,像飘着 - 多行文字逐行 + font.Height 累加,但实际行距受
LineSpacing和FontFamily.GetLineSpacing影响,不是简单相加
实操建议:
- 要让文字 baseline 落在指定 y 坐标,得手动计算偏移:
y - font.FontFamily.GetCellAscent(font.Style) * font.Size / font.FontFamily.GetEmHeight(font.Style) - 更稳的方式是用
StringFormat+RectangleF,设stringFormat.LineAlignment = StringAlignment.Center,再配合stringFormat.Alignment = StringAlignment.Center,让文字在矩形内真正居中
在 PNG 透明背景上画黑字却出现灰边
不是字体问题,是 Alpha 混合搞的鬼。PNG 是 premultiplied alpha 格式,而 Graphics 默认使用非 premultiplied 模式绘制,导致文字边缘半透区域和背景做了两次 alpha 混合,灰边就出来了。
使用场景:
- 生成带透明底的图标、水印、头像标签
- 后续要把这张图叠到其他颜色背景上显示
实操建议:
- 创建
Bitmap时务必指定PixelFormat.Format32bppPArgb,而不是默认的Format32bppArgb - 绘图前调用
graphics.CompositingMode = CompositingMode.SourceOver(这是默认值,但显式写上防被改) - 别用
Brush直接填纯黑(Color.Black),改用new SolidBrush(Color.FromArgb(255, 0, 0, 0)),确保 alpha=255,避免 Brush 自动做 alpha 预乘干扰
DrawString 在高 DPI 缩放下文字变小或糊成一片
.NET Framework 4.7+ 和 .NET Core/5+ 对 DPI 感知不同,但共性问题是:如果你没声明程序支持 DPI 感知,Windows 会自动对整个窗体/位图做缩放(StretchBlt),而 DrawString 用的还是原始逻辑坐标,结果就是字体被双重缩放——既按 Font.Size 渲染,又被系统拉伸一次。
性能与兼容性影响:
- 启用 DPI 感知后,
Graphics.DpiX/Y返回真实 DPI,可据此动态调整font.Size;不启用则永远返回 96 - WinForms 中可在
app.manifest加;控制台或类库需调用true/PM SetProcessDpiAwarenessContextAPI - 若只是临时生成图片(如导出报表),建议统一在 96 DPI 环境下绘制,用
Graphics.ScaleTransform手动缩放整个画布,比依赖系统 DPI 更可控
这事没有银弹。ClearType 依赖物理像素排列,DPI 感知影响坐标映射,PNG alpha 涉及混合模型——三个地方任何一个没对齐,文字就废一半。动手前先确认你到底在哪个环节丢掉了控制权。











