C#怎么给图片添加文字水印_C#如何使用Graphics绘制文本【实战】

5次阅读

必须设置TextRenderingHint.AntiAliasGridFit以启用抗锯齿,否则文字边缘发虚;半透明水印需用Color.FromArgb构造Brush;字体单位为像素且需随图片缩放调整;单行文字居中须手动计算坐标;PNG水印灰边源于Alpha预乘,应改用LockBits或重建Bitmap;Graphics和Bitmap须显式Dispose,Font可复用。

C#怎么给图片添加文字水印_C#如何使用Graphics绘制文本【实战】

Graphics.DrawString 绘制文字前必须设置抗锯齿模式

直接用 Graphics.DrawString 往图片上写文字,常出现文字边缘发虚、断笔或完全看不清——根本不是字体问题,而是默认渲染模式没调。GDI+ 默认用 TextRenderingHint.SystemDefault,在非屏幕设备(比如内存位图)上会降级为位图字体或跳过抗锯齿。

  • 务必在绘制前设 graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit
  • 如果水印要半透明,Brush 必须用 Color.FromArgb(alpha, r, g, b) 构造,不能只改 Pen 透明度(DrawString 不走 Pen)
  • 字体大小单位是像素,不是点(pt),传 12 并不等于 word 里的 12pt;缩放图片时需同步调整字号,否则水印可能糊成一片

文字居中对齐需要手动计算位置,不是靠 StringFormat.Alignment

StringFormat.AlignmentLineAlignment 只控制文本块内多行之间的相对位置,对单行文字“居中到某矩形区域”毫无作用。常见错误是传个 new Rectangle(0, 0, width, height) 就以为文字会自动居中,结果全挤在左上角。

  • graphics.MeasureString(text, font) 拿实际宽高(注意:它测的是逻辑尺寸,受 DPI 影响)
  • 目标区域中心点是 (x + width / 2, y + height / 2),文字基线中心 ≈ (centerX - measuredWidth / 2, centerY + font.SizeInPoints * 0.35f)(0.35 是经验系数,因字体 ascender/descender 不同)
  • 更稳的做法:用 graphics.MeasureCharacterRanges + Region.GetBounds 精确获取文本外框,但开销大,水印场景通常没必要

DrawString 在 PNG 透明背景上显示异常的根源

给 PNG 图片加水印后,文字周围出现灰边或色块,尤其在浅色文字+深色背景组合下明显。这不是 Graphics bug,而是 PNG 解码时 Alpha 通道被预乘(premultiplied alpha)导致颜色值失真。

  • 加载原图时用 Image.FromFile 会触发默认解码,Alpha 已被处理;应改用 Bitmap.LockBits 手动读取原始像素,或加载后立刻复制到新 Bitmap 并指定 PixelFormat.Format32bppArgb
  • 绘制前确保 graphics.CompositingMode = CompositingMode.SourceOver(默认值,但某些封装库会改)
  • 避免在已有 Alpha 的图层上反复 DrawString —— 每次绘制都会叠加一次混合,文字边缘渐变会越来越脏

批量加水印时 Graphics 对象泄漏和性能卡点

循环里对每张图都 new Bitmap → Graphics → DrawString → Save,跑几百张就内存暴涨甚至 OOM。问题不在 DrawString 本身,而在未释放 GDI+ 句柄。

  • Graphics 必须显式 Dispose(),用 using 最安全;Bitmap 同理,别依赖 GC
  • 不要每次重绘都 new Font —— 字体对象可复用,它是线程安全的;把 Font 提到循环外
  • 如果只是加固定文字水印,考虑用 TextureBrush 预先画好水印图案,再用 Graphics.DrawImage 贴图,比逐帧 DrawString 快 3–5 倍

水印位置偏移、透明度不生效、批量处理卡死……这些问题几乎都卡在 Graphics 生命周期、像素格式、坐标换算这三个地方。调通一个,其他基本就顺了。

text=ZqhQzanResources