绘制文字水印前必须确保:①源图使用PixelFormat.Format32bppPArgb格式加载;②新建Bitmap与源图尺寸及像素格式一致;③Graphics开启TextRenderingHint.AntiAliasGridFit抗锯齿。

用 Graphics 绘制文字水印前必须注意的 3 个前提
直接调用 Graphics.DrawString 很容易出现文字模糊、位置偏移或透明度失效——根本原因是没正确初始化绘图上下文。必须确保:
• 源图片以 PixelFormat.Format32bppPArgb 格式加载(否则 Alpha 通道被忽略,半透明水印变实心);
• 新建的 Bitmap 使用与源图一致的尺寸和像素格式;
• Graphics 实例开启抗锯齿(g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit),否则小字号文字边缘发虚。
DrawString 定位不准?坐标的原点在左上角,不是图片中心
很多人把“居中水印”理解成“在图片宽高一半处画字”,结果文字只显示右下四分之一。实际 DrawString 的 x/y 是文字左上角坐标,不是中心点。计算居中需手动减去文字尺寸:
SizeF textSize = g.MeasureString(watermarkText, font); float x = (img.Width - textSize.Width) / 2; float y = (img.Height - textSize.Height) / 2 + font.GetHeight(g); // 加 font height 才是基线位置
若要右下角加时间戳类水印,推荐用 StringFormat 配合 RectangleF:
var format = new StringFormat { Alignment = StringAlignment.Far, LineAlignment = StringAlignment.Far };
g.DrawString("2024-06", font, brush, new RectangleF(0, 0, img.Width, img.Height), format);
叠加 Logo 图片水印时,DrawImage 的缩放和透明度要分开控制
直接传入原始 Logo 图片会导致尺寸失控或不透明。正确做法是:
• 先用 Bitmap 构造函数指定目标宽高创建缩略版 Logo;
• 再用 Graphics.DrawImage 的重载版本传入 Rectangle 控制绘制区域;
• 透明度不能靠 PNG 自带 Alpha(部分 GDI+ 实现会忽略),必须用 ColorMatrix 调整 Alpha 通道:
var matrix = new ColorMatrix { Matrix33 = 0.5f }; // 50% 透明度
var attr = new ImageAttributes();
attr.SetColorMatrix(matrix);
g.DrawImage(logo, destRect, 0, 0, logo.Width, logo.Height, GraphicsUnit.Pixel, attr);
保存为 JPEG 时水印消失?那是 Save 方法丢掉了 Alpha 通道
JPEG 不支持透明通道,如果水印用了半透明文字或 Logo,保存为 .jpg 会强制将透明区域填成白色(或黑色,取决于解码器)。必须:
• 输出格式选 Png 或 Bmp(保留 Alpha);
• 若硬性要求 JPEG,则先将带水印的图合成到纯白/纯黑背景上再保存;
• 切勿用 img.Save(path, ImageFormat.Jpeg) 直接保存含 Alpha 的 Bitmap,GDI+ 可能静默降级处理,看不出报错但效果异常。
真正难的不是画上去,而是让水印在不同 DPI、不同缩放比例、不同输出格式下都保持预期的大小、位置和透明度——这些细节在开发机上常被忽略,上线后才暴露。











