Graphics.DrawImage叠加水印需控制位置、大小与透明度,优先用带Alpha通道的PNG;缩放设HighQualityBicubic,文字水印须开启ClearTypeGridFit抗锯齿并正确设置对齐;批量处理推荐LockBits提升性能,保存JPG时必须指定Quality、ColorDepth等Encoder参数。

用 Graphics.DrawImage 叠加水印最直接,但透明度和缩放容易出错
核心是把原图当底图、水印图当覆盖层,用 Graphics 绘制。不是简单贴图,得控制位置、大小、透明度——否则水印要么糊成一团,要么完全盖不住背景。
- 水印图建议用带 Alpha 通道的 PNG;若用 JPG,
ColorMatrix调透明度会失真,优先走ImageAttributes.SetColorMatrix配合DrawImage的重载版本 - 计算水印位置时别硬写像素值,用原图宽高做比例:比如右下角留 2% 边距,就取
width * 0.98 - watermark.Width - 缩放水印要等比:
Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic,否则小图拉大后锯齿明显
文字水印要用 Graphics.DrawString,字体和抗锯齿必须手动开
直接画文字比贴图更灵活(可动态加时间、版权信息),但默认渲染像马赛克——因为 GDI+ 默认关了文本抗锯齿,且不支持半透色。
- 必须设
graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit,否则中文发虚 - 颜色不能只用
Color.FromArgb(128, 0, 0, 0),得配合StringFormat设置Alignment和LineAlignment居中,不然文字偏移难对齐 - 如果要斜着铺满背景(常见防盗效果),别用旋转整个
Graphics——开销大还影响后续绘制;改用Matrix只变换文字路径,再传给DrawString
Bitmap.LockBits 方式性能高,但新手极易内存泄漏或崩溃
批量处理图片(如网站上传后自动加水印)时,Graphics 绘制每张图都要新建对象、触发 GC,而 LockBits 直接操作像素内存,快 3–5 倍。代价是:指针操作一错就 AccessViolationException。
- 务必配对使用
UnlockBits,哪怕在catch块里也要包一层try/finally - 注意像素格式:
PixelFormat.Format32bppArgb才有 Alpha 通道,用错会导致水印不透明或变色 - 叠加逻辑得自己写循环:对每个水印像素,按透明度公式
dst = src * alpha + dst * (1 - alpha)计算,别直接赋值
保存时别忽略 EncoderParameters,JPG 质量和色彩空间很关键
水印加上去了,但保存成 JPG 时若没设参数,系统用默认质量(常为 75%),反复编辑会劣化;更隐蔽的问题是:某些设备读取 sRGB 外的色彩空间会偏色。
- 强制指定编码器:
ImageCodecInfo.GetImageEncoders().First(x => x.MimeType == "image/jpeg") - 质量参数必须设:
new EncoderParameter(Encoder.Quality, 92L),低于 85 就可能让文字水印出现块状模糊 - 加上
Encoder.ColorDepth和Encoder.Compression参数,避免部分安卓相册显示异常
水印位置、透明度、保存参数这三处,调一次容易,批量处理时只要漏一个条件,整批图就废掉——尤其是定时任务里跑的代码,出问题不会报错,只会静默产出低质图。










