在圖片版權保護、品牌標識等場景中,加入浮水印是常見需求。C# System.Drawing 命名空間提供強大的 GDI+ 繪圖功能,可實作文字浮水印與圖片浮水印的疊加。本文提供完整可執行的範例程式碼,涵蓋字型自動縮放、陰影效果、半透明度控制、圖片背景透明化等實用技巧。
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
namespace WaterMarkDemo
{
class WaterMark
{
[STAThread]
static void Main(string[] args)
{
// === 設定工作目錄與素材路徑 ===
string workingDir = @"C:\Projects\WaterMark";
string photoPath = Path.Combine(workingDir, "watermark_photo.jpg");
string watermarkPath = Path.Combine(workingDir, "watermark.bmp");
string outputPath = Path.Combine(workingDir, "watermark_final.jpg");
// === 載入原始照片 ===
using (Image imgPhoto = Image.FromFile(photoPath))
{
int phWidth = imgPhoto.Width;
int phHeight = imgPhoto.Height;
// 建立與原圖同尺寸的 Bitmap 作為畫布
using (Bitmap bmPhoto = new Bitmap(phWidth, phHeight, PixelFormat.Format24bppRgb))
{
bmPhoto.SetResolution(imgPhoto.HorizontalResolution, imgPhoto.VerticalResolution);
using (Graphics grPhoto = Graphics.FromImage(bmPhoto))
{
// 設定高品質繪圖
grPhoto.SmoothingMode = SmoothingMode.AntiAlias;
grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;
// 將原圖繪製到畫布
grPhoto.DrawImage(imgPhoto,
new Rectangle(0, 0, phWidth, phHeight),
0, 0, phWidth, phHeight, GraphicsUnit.Pixel);
// ==========================================
// 步驟 1:繪製文字浮水印(版權資訊)
// ==========================================
string copyright = "Copyright © 2024 - My Company";
// 自動尋找適合圖片寬度的最大字型大小
int[] fontSizes = { 48, 36, 28, 24, 20, 16, 14, 12, 10 };
Font crFont = null;
SizeF crSize = SizeF.Empty;
foreach (int size in fontSizes)
{
using (Font testFont = new Font("Arial", size, FontStyle.Bold))
{
crSize = grPhoto.MeasureString(copyright, testFont);
if (crSize.Width < phWidth * 0.9) // 留 10% 邊距
{
crFont = new Font("Arial", size, FontStyle.Bold);
break;
}
}
}
if (crFont == null) crFont = new Font("Arial", 10, FontStyle.Bold);
// 計算置中下方位置(距底部 5%)
int yPixelsFromBottom = (int)(phHeight * 0.05);
float yPos = (phHeight - yPixelsFromBottom) - (crSize.Height / 2);
float xCenter = phWidth / 2f;
StringFormat strFormat = new StringFormat { Alignment = StringAlignment.Center };
// 陰影效果:先畫深色偏移 1 像素
using (SolidBrush shadowBrush = new SolidBrush(Color.FromArgb(153, 0, 0, 0)))
{
grPhoto.DrawString(copyright, crFont, shadowBrush,
new PointF(xCenter + 1, yPos + 1), strFormat);
}
// 主文字:半透明白色
using (SolidBrush mainBrush = new SolidBrush(Color.FromArgb(153, 255, 255, 255)))
{
grPhoto.DrawString(copyright, crFont, mainBrush,
new PointF(xCenter, yPos), strFormat);
}
// ==========================================
// 步驟 2:疊加圖片浮水印(Logo)
// ==========================================
using (Image imgWatermark = new Bitmap(watermarkPath))
{
int wmWidth = imgWatermark.Width;
int wmHeight = imgWatermark.Height;
// 建立新 Bitmap 承接已含文字浮水印的畫面
using (Bitmap bmWatermark = new Bitmap(bmPhoto))
{
bmWatermark.SetResolution(imgPhoto.HorizontalResolution, imgPhoto.VerticalResolution);
using (Graphics grWatermark = Graphics.FromImage(bmWatermark))
{
// 設定 ImageAttributes 實現兩階段色彩轉換
using (ImageAttributes imageAttr = new ImageAttributes())
{
// 階段 1:將浮水印圖片的特定背景色(此處為純綠 #00FF00)轉為全透明
ColorMap colorMap = new ColorMap
{
OldColor = Color.FromArgb(255, 0, 255, 0), // 綠色背景
NewColor = Color.FromArgb(0, 0, 0, 0) // 全透明
};
imageAttr.SetRemapTable(new[] { colorMap }, ColorAdjustType.Bitmap);
// 階段 2:ColorMatrix 控制整體不透明度(0.3 = 30% 不透明)
float[][] matrixElements = {
new float[] {1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // R
new float[] {0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, // G
new float[] {0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, // B
new float[] {0.0f, 0.0f, 0.0f, 0.3f, 0.0f}, // Alpha (關鍵:0.3f)
new float[] {0.0f, 0.0f, 0.0f, 0.0f, 1.0f} // W
};
ColorMatrix wmColorMatrix = new ColorMatrix(matrixElements);
imageAttr.SetColorMatrix(wmColorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
// 計算右上角位置(留 10 像素邊距)
int xPos = (phWidth - wmWidth) - 10;
int yPosWm = 10;
// 繪製浮水印圖片
grWatermark.DrawImage(imgWatermark,
new Rectangle(xPos, yPosWm, wmWidth, wmHeight),
0, 0, wmWidth, wmHeight,
GraphicsUnit.Pixel,
imageAttr);
}
}
// 儲存最終結果
bmWatermark.Save(outputPath, ImageFormat.Jpeg);
Console.WriteLine($"浮水印已套用,輸出:{outputPath}");
}
}
}
}
}
}
}
}
透過迴圈嘗試不同字型大小,配合 Graphics.MeasureString 測量寬度,選擇不超過圖片寬度 90% 的最大字型,確保各尺寸圖片都能完整顯示版權文字。
先以深色半透明筆刷(Alpha=153)繪製偏移 (1,1) 的文字,再繪製主體白色文字,形成微妙陰影,提升在複雜背景下的可讀性。
所有實作 IDisposable 的 GDI+ 物件(Bitmap、Graphics、Font、Brush、ImageAttributes)均以 using 區塊包裹,確保非受控資源及時釋放,避免 GDI+ 物件洩漏導致「generic error」例外。
| 需求 | 調整方式 |
|---|---|
| 改變浮水印位置 | 修改 xPos、yPosWm 計算邏輯(置中、左下、平鋪等) |
| 調整透明度 | 修改 ColorMatrix 中的 0.3f 數值(0.0~1.0) |
| 浮水印素材非綠背景 | 修改 ColorMap.OldColor 對應實際背景色 |
| 支援 PNG 透明通道素材 | 可省略 ColorMap 步驟,直接使用 ColorMatrix 控制整體透明度 |
| 輸出格式 | 變更 ImageFormat.Jpeg 為 Png、Bmp 等 |
透過 System.Drawing 的 Graphics、Bitmap、ImageAttributes 與 ColorMatrix 組合,可實作專業級的雙重浮水印功能。關鍵在於:文字自適應排版、陰影增強可讀性、圖片背景透明化與 Alpha 混合兩階段處理、嚴謹的資源釋放。此範例可直接整合至批次處理服務、上傳處理管線或桌面應用程式中。