Graphics.DrawString最可靠,需用Bitmap副本、设TextRenderingHint、防中文乱码、防资源泄露。

用 Graphics.DrawString 直接画文字最可靠
别碰 Bitmap.SetPixel 逐点写,也别用第三方库搞复杂叠加——C# 自带的 Graphics 类足够稳,且支持抗锯齿、透明度、旋转。核心是把原图加载为 Bitmap,用 Graphics.FromImage 获取绘图上下文,再调用 DrawString。
常见错误是:直接在 JPG 上绘图后保存成 PNG,结果透明背景变黑;或没设 SmoothingMode,文字边缘锯齿严重。
- 务必先
new Bitmap(originalImage)创建可写副本,别在只读流上操作 - 设置
graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit,否则小字号发虚 - 水印文字颜色建议用半透明黑:
Color.FromArgb(128, 0, 0, 0),比纯白/纯黑更不易被一键去水印 - 如果原图是 JPG,保存时必须用
ImageFormat.Jpeg,不能用Png否则会引入无意义透明通道
位置和角度得手动算,没有“自动居中”参数
DrawString 的坐标是左上角起点,不支持 CSS 式的 center 或 bottom-right。你想斜着打在右下角?得自己算宽高、缩放、偏移。
典型翻车场景:文字超出图片边界(保存后被裁掉)、旋转后锚点错位导致水印飘到图外、多行文字行距写死导致重叠。
- 获取文字尺寸用
graphics.MeasureString(text, font),别用Font.Height硬估 - 斜向水印推荐先
graphics.TranslateTransform移动原点,再graphics.RotateTransform(-30),最后从 (0, 0) 开始画,比硬算坐标安全 - 右下角定位公式:
x = bitmap.Width - textSize.Width - padding,y = bitmap.Height - padding(注意MeasureString返回的是逻辑高度,y 要减的是textSize.Height,不是基线偏移)
中文乱码?检查字体是否真支持 GB2312/UTF-8
用 "SimSun" 或 "Microsoft YaHei" 不等于就一定能显示中文——如果系统没装这个字体,GDI+ 会静默 fallback 到默认字体(通常是 Arial),结果就是方块或空格。
更隐蔽的问题:某些服务器环境(如 Linux + .NET 6+)默认不带中文字体,new Font("微软雅黑", 12) 可能抛 ArgumentException。
- 安全做法是先查系统字体集合:
new InstalledFontCollection().Families,确认存在再创建Font - 实在不确定,用
FontFamily.GenericSansSerif替代具体名称,至少保证可渲染 - 避免用
FontStyle.Bold | FontStyle.Italic组合,某些字体不提供合成样式,会降级失败
批量加水印时别让 Graphics 和 Font 泄露
每个 Graphics 实例都绑着 GDI+ 句柄,Font 也是非托管资源。循环里反复 new Graphics 不 Dispose,跑几百张图后必触发 OutOfMemoryException,错误信息是 "Out of system resources",跟内存无关,是句柄耗尽。
另一个坑:用 using (var g = Graphics.FromImage(bmp)) 后,又把 bmp 拿去另存,结果保存失败或图像损坏——因为 Graphics 释放时可能清空位图缓冲区。
-
Graphics和Font都必须显式Dispose(),不能靠 GC - 写法优先用
using块,但确保Bitmap生命周期长于Graphics - 如果要复用字体,把它提到循环外并单独
Dispose,别每次 new
字体加载、坐标计算、资源释放这三处最容易漏,改完记得用不同尺寸图、不同系统环境各跑一遍。


















