C# 图片水印解决方案,支持在图片的9个位置(左上、上、右上、左、中、右、左下、下、右下)添加水印。
一、项目结构
ImageWatermark/
├── Program.cs
├── WatermarkProcessor.cs
├── WatermarkConfig.cs
├── ImageHelper.cs
└── ImageWatermark.csproj
二、完整源码实现
2.1 项目文件 (ImageWatermark.csproj)
xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.2" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta15" />
</ItemGroup>
</Project>
2.2 水印位置枚举 (WatermarkConfig.cs)
csharp
namespace ImageWatermark
{
/// <summary>
/// 水印位置枚举(9个位置)
/// </summary>
public enum WatermarkPosition
{
/// <summary>
/// 左上角
/// </summary>
TopLeft = 0,
/// <summary>
/// 上中
/// </summary>
TopCenter = 1,
/// <summary>
/// 右上角
/// </summary>
TopRight = 2,
/// <summary>
/// 左中
/// </summary>
MiddleLeft = 3,
/// <summary>
/// 正中
/// </summary>
Center = 4,
/// <summary>
/// 右中
/// </summary>
MiddleRight = 5,
/// <summary>
/// 左下角
/// </summary>
BottomLeft = 6,
/// <summary>
/// 下中
/// </summary>
BottomCenter = 7,
/// <summary>
/// 右下角
/// </summary>
BottomRight = 8
}
/// <summary>
/// 水印类型
/// </summary>
public enum WatermarkType
{
/// <summary>
/// 图片水印
/// </summary>
Image = 0,
/// <summary>
/// 文字水印
/// </summary>
Text = 1
}
/// <summary>
/// 水印配置类
/// </summary>
public class WatermarkConfiguration
{
/// <summary>
/// 水印类型
/// </summary>
public WatermarkType Type { get; set; } = WatermarkType.Image;
/// <summary>
/// 水印位置
/// </summary>
public WatermarkPosition Position { get; set; } = WatermarkPosition.BottomRight;
/// <summary>
/// 透明度 (0-1)
/// </summary>
public float Opacity { get; set; } = 0.8f;
/// <summary>
/// 边距
/// </summary>
public int Margin { get; set; } = 20;
/// <summary>
/// 水印图片路径(仅图片水印)
/// </summary>
public string? WatermarkImagePath { get; set; }
/// <summary>
/// 水印文字(仅文字水印)
/// </summary>
public string? WatermarkText { get; set; }
/// <summary>
/// 字体大小(仅文字水印)
/// </summary>
public int FontSize { get; set; } = 24;
/// <summary>
/// 字体颜色(仅文字水印)
/// </summary>
public Color FontColor { get; set; } = Color.White;
/// <summary>
/// 字体名称(仅文字水印)
/// </summary>
public string FontFamily { get; set; } = "Arial";
/// <summary>
/// 是否添加阴影
/// </summary>
public bool AddShadow { get; set; } = true;
/// <summary>
/// 阴影颜色
/// </summary>
public Color ShadowColor { get; set; } = Color.Black;
/// <summary>
/// 阴影偏移
/// </summary>
public int ShadowOffset { get; set; } = 2;
/// <summary>
/// 水印旋转角度
/// </summary>
public float RotationAngle { get; set; } = 0f;
/// <summary>
/// 水印缩放比例
/// </summary>
public float Scale { get; set; } = 1.0f;
}
}
2.3 主程序 (Program.cs)
csharp
using System;
using System.IO;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
namespace ImageWatermark
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("=== 图片水印工具(支持9个位置) ===\n");
// 配置参数
string sourceImagePath = "source.jpg"; // 源图片路径
string watermarkImagePath = "watermark.png"; // 水印图片路径
string outputPath = "output_with_watermark.jpg";
// 创建水印配置
var config = new WatermarkConfiguration
{
Type = WatermarkType.Image,
Position = WatermarkPosition.BottomRight, // 右下角
Opacity = 0.7f,
Margin = 30,
WatermarkImagePath = watermarkImagePath,
AddShadow = true,
ShadowColor = Color.Black.WithAlpha(0.5f),
ShadowOffset = 3
};
// 如果要使用文字水印,可以这样配置:
/*
var textConfig = new WatermarkConfiguration
{
Type = WatermarkType.Text,
Position = WatermarkPosition.Center, // 正中
Opacity = 0.8f,
Margin = 20,
WatermarkText = "© 2024 My Company",
FontSize = 36,
FontColor = Color.White.WithAlpha(0.8f),
FontFamily = "Arial",
AddShadow = true
};
*/
try
{
// 1. 加载源图片
Console.WriteLine("正在加载源图片...");
using var sourceImage = Image.Load<Rgba32>(sourceImagePath);
Console.WriteLine($" 尺寸: {sourceImage.Width}x{sourceImage.Height}");
// 2. 应用水印
Console.WriteLine("正在应用水印...");
using var resultImage = WatermarkProcessor.ApplyWatermark(sourceImage, config);
// 3. 保存结果
Console.WriteLine("正在保存结果...");
resultImage.Save(outputPath);
Console.WriteLine($"\n✅ 水印已添加: {outputPath}");
Console.WriteLine($" 水印位置: {config.Position}");
Console.WriteLine($" 透明度: {config.Opacity * 100}%");
}
catch (Exception ex)
{
Console.WriteLine($"\n❌ 错误: {ex.Message}");
Console.WriteLine(ex.StackTrace);
}
Console.WriteLine("\n按任意键退出...");
Console.ReadKey();
}
/// <summary>
/// 批量处理示例
/// </summary>
static void BatchProcessExample()
{
string[] imageFiles = Directory.GetFiles("images", "*.jpg");
var config = new WatermarkConfiguration
{
Type = WatermarkType.Image,
Position = WatermarkPosition.BottomRight,
Opacity = 0.7f,
Margin = 20,
WatermarkImagePath = "watermark.png"
};
foreach (var imageFile in imageFiles)
{
try
{
using var sourceImage = Image.Load<Rgba32>(imageFile);
using var resultImage = WatermarkProcessor.ApplyWatermark(sourceImage, config);
string outputPath = Path.Combine("output", Path.GetFileName(imageFile));
resultImage.Save(outputPath);
Console.WriteLine($"已处理: {imageFile}");
}
catch (Exception ex)
{
Console.WriteLine($"处理 {imageFile} 失败: {ex.Message}");
}
}
}
}
}
2.4 水印处理器 (WatermarkProcessor.cs)
csharp
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Formats.Jpeg;
namespace ImageWatermark
{
public static class WatermarkProcessor
{
/// <summary>
/// 应用水印到图片
/// </summary>
public static Image<Rgba32> ApplyWatermark(
Image<Rgba32> sourceImage,
WatermarkConfiguration config)
{
var resultImage = sourceImage.Clone();
if (config.Type == WatermarkType.Image)
{
return ApplyImageWatermark(resultImage, config);
}
else
{
return ApplyTextWatermark(resultImage, config);
}
}
/// <summary>
/// 应用图片水印
/// </summary>
private static Image<Rgba32> ApplyImageWatermark(
Image<Rgba32> sourceImage,
WatermarkConfiguration config)
{
if (string.IsNullOrEmpty(config.WatermarkImagePath) || !File.Exists(config.WatermarkImagePath))
{
throw new FileNotFoundException("水印图片不存在", config.WatermarkImagePath);
}
using var watermarkImage = Image.Load<Rgba32>(config.WatermarkImagePath);
// 调整水印大小(根据源图片尺寸比例)
var scaledWatermark = ScaleWatermark(watermarkImage, sourceImage, config.Scale);
// 计算水印位置
var position = CalculatePosition(sourceImage.Size, scaledWatermark.Size, config.Position, config.Margin);
// 应用水印
sourceImage.Mutate(ctx =>
{
// 如果设置了旋转
if (config.RotationAngle != 0)
{
scaledWatermark.Mutate(w => w.Rotate(config.RotationAngle));
}
// 设置透明度
if (config.Opacity < 1.0f)
{
scaledWatermark.Mutate(w => w.Opacity(config.Opacity));
}
// 添加阴影
if (config.AddShadow)
{
var shadow = CreateShadow(scaledWatermark, config.ShadowColor, config.ShadowOffset);
ctx.DrawImage(shadow, new Point(position.X + config.ShadowOffset, position.Y + config.ShadowOffset), 1f);
}
// 绘制水印
ctx.DrawImage(scaledWatermark, position, 1f);
});
return sourceImage;
}
/// <summary>
/// 应用文字水印
/// </summary>
private static Image<Rgba32> ApplyTextWatermark(
Image<Rgba32> sourceImage,
WatermarkConfiguration config)
{
if (string.IsNullOrEmpty(config.WatermarkText))
{
throw new ArgumentException("文字水印内容不能为空");
}
sourceImage.Mutate(ctx =>
{
// 创建字体
var font = SixLabors.ImageSharp.Drawing.Text.SystemFonts.CreateFont(
config.FontFamily,
config.FontSize,
FontStyle.Regular);
// 测量文字尺寸
var textSize = TextMeasurer.MeasureSize(config.WatermarkText, new TextOptions(font));
// 计算位置
var position = CalculatePosition(sourceImage.Size, new Size((int)textSize.Width, (int)textSize.Height), config.Position, config.Margin);
// 创建文字画笔
var textBrush = new SolidBrush(config.FontColor.WithAlpha(config.Opacity));
// 添加阴影
if (config.AddShadow)
{
var shadowBrush = new SolidBrush(config.ShadowColor.WithAlpha(config.Opacity * 0.7f));
ctx.DrawText(config.WatermarkText, font, shadowBrush,
new PointF(position.X + config.ShadowOffset, position.Y + config.ShadowOffset));
}
// 绘制文字
ctx.DrawText(config.WatermarkText, font, textBrush, new PointF(position.X, position.Y));
});
return sourceImage;
}
/// <summary>
/// 缩放水印图片
/// </summary>
private static Image<Rgba32> ScaleWatermark(
Image<Rgba32> watermark,
Image<Rgba32> source,
float scale)
{
// 计算合适的水印尺寸(基于源图片尺寸的百分比)
float maxWidth = source.Width * 0.2f * scale; // 最大宽度为源图片的20%
float maxHeight = source.Height * 0.2f * scale; // 最大高度为源图片的20%
float ratio = Math.Min(maxWidth / watermark.Width, maxHeight / watermark.Height);
int newWidth = (int)(watermark.Width * ratio);
int newHeight = (int)(watermark.Height * ratio);
return watermark.Clone(ctx => ctx.Resize(newWidth, newHeight, KnownResamplers.Lanczos3));
}
/// <summary>
/// 计算水印位置
/// </summary>
private static Point CalculatePosition(
Size sourceSize,
Size watermarkSize,
WatermarkPosition position,
int margin)
{
return position switch
{
WatermarkPosition.TopLeft => new Point(margin, margin),
WatermarkPosition.TopCenter => new Point((sourceSize.Width - watermarkSize.Width) / 2, margin),
WatermarkPosition.TopRight => new Point(sourceSize.Width - watermarkSize.Width - margin, margin),
WatermarkPosition.MiddleLeft => new Point(margin, (sourceSize.Height - watermarkSize.Height) / 2),
WatermarkPosition.Center => new Point((sourceSize.Width - watermarkSize.Width) / 2, (sourceSize.Height - watermarkSize.Height) / 2),
WatermarkPosition.MiddleRight => new Point(sourceSize.Width - watermarkSize.Width - margin, (sourceSize.Height - watermarkSize.Height) / 2),
WatermarkPosition.BottomLeft => new Point(margin, sourceSize.Height - watermarkSize.Height - margin),
WatermarkPosition.BottomCenter => new Point((sourceSize.Width - watermarkSize.Width) / 2, sourceSize.Height - watermarkSize.Height - margin),
WatermarkPosition.BottomRight => new Point(sourceSize.Width - watermarkSize.Width - margin, sourceSize.Height - watermarkSize.Height - margin),
_ => new Point(margin, margin)
};
}
/// <summary>
/// 创建阴影效果
/// </summary>
private static Image<Rgba32> CreateShadow(Image<Rgba32> image, Color shadowColor, int offset)
{
var shadow = new Image<Rgba32>(image.Width + offset, image.Height + offset);
shadow.Mutate(ctx =>
{
ctx.Fill(shadowColor);
ctx.DrawImage(image, new Point(offset, offset), 1f);
});
return shadow;
}
/// <summary>
/// 创建平铺水印
/// </summary>
public static Image<Rgba32> ApplyTiledWatermark(
Image<Rgba32> sourceImage,
Image<Rgba32> watermarkImage,
float opacity = 0.3f,
int spacing = 50)
{
var resultImage = sourceImage.Clone();
resultImage.Mutate(ctx =>
{
// 设置水印透明度
watermarkImage.Mutate(w => w.Opacity(opacity));
// 平铺水印
for (int y = 0; y < sourceImage.Height; y += watermarkImage.Height + spacing)
{
for (int x = 0; x < sourceImage.Width; x += watermarkImage.Width + spacing)
{
ctx.DrawImage(watermarkImage, new Point(x, y), 1f);
}
}
});
return resultImage;
}
/// <summary>
/// 创建对角线文字水印
/// </summary>
public static Image<Rgba32> ApplyDiagonalTextWatermark(
Image<Rgba32> sourceImage,
string text,
Color color,
int fontSize = 24,
float opacity = 0.3f)
{
var resultImage = sourceImage.Clone();
resultImage.Mutate(ctx =>
{
var font = SixLabors.ImageSharp.Drawing.Text.SystemFonts.CreateFont("Arial", fontSize, FontStyle.Regular);
var textBrush = new SolidBrush(color.WithAlpha(opacity));
// 计算对角线长度
float diagonalLength = (float)Math.Sqrt(sourceImage.Width * sourceImage.Width + sourceImage.Height * sourceImage.Height);
float textWidth = TextMeasurer.MeasureSize(text, new TextOptions(font)).Width;
int repeatCount = (int)(diagonalLength / textWidth) + 2;
// 创建临时图像用于旋转文字
using var tempImage = new Image<Rgba32>((int)textWidth + 20, fontSize + 20);
tempImage.Mutate(tempCtx =>
{
tempCtx.Fill(Color.Transparent);
tempCtx.DrawText(text, font, textBrush, new PointF(10, 10));
});
// 旋转45度
tempImage.Mutate(tempCtx => tempCtx.Rotate(45));
// 平铺旋转后的文字
for (int y = -tempImage.Height; y < sourceImage.Height + tempImage.Height; y += tempImage.Height + 50)
{
for (int x = -tempImage.Width; x < sourceImage.Width + tempImage.Width; x += tempImage.Width + 100)
{
ctx.DrawImage(tempImage, new Point(x, y), 1f);
}
}
});
return resultImage;
}
}
}
2.5 图像处理工具 (ImageHelper.cs)
csharp
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.PixelFormats;
namespace ImageWatermark
{
public static class ImageHelper
{
/// <summary>
/// 保存图片
/// </summary>
public static void SaveImage(Image<Rgba32> image, string filePath, int quality = 90)
{
var extension = Path.GetExtension(filePath).ToLowerInvariant();
switch (extension)
{
case ".jpg":
case ".jpeg":
image.Save(filePath, new JpegEncoder { Quality = quality });
break;
case ".png":
image.Save(filePath, new PngEncoder());
break;
default:
image.Save(filePath);
break;
}
}
/// <summary>
/// 调整图片大小
/// </summary>
public static Image<Rgba32> ResizeImage(Image<Rgba32> image, int maxWidth, int maxHeight)
{
float ratio = Math.Min(
(float)maxWidth / image.Width,
(float)maxHeight / image.Height
);
int newWidth = (int)(image.Width * ratio);
int newHeight = (int)(image.Height * ratio);
return image.Clone(ctx => ctx.Resize(newWidth, newHeight, KnownResamplers.Lanczos3));
}
/// <summary>
/// 裁剪图片
/// </summary>
public static Image<Rgba32> CropImage(Image<Rgba32> image, int x, int y, int width, int height)
{
return image.Clone(ctx => ctx.Crop(new Rectangle(x, y, width, height)));
}
/// <summary>
/// 旋转图片
/// </summary>
public static Image<Rgba32> RotateImage(Image<Rgba32> image, float angle)
{
return image.Clone(ctx => ctx.Rotate(angle));
}
/// <summary>
/// 添加边框
/// </summary>
public static Image<Rgba32> AddBorder(Image<Rgba32> image, int borderWidth, Color borderColor)
{
var newWidth = image.Width + borderWidth * 2;
var newHeight = image.Height + borderWidth * 2;
var result = new Image<Rgba32>(newWidth, newHeight);
result.Mutate(ctx =>
{
ctx.Fill(borderColor);
ctx.DrawImage(image, new Point(borderWidth, borderWidth), 1f);
});
return result;
}
/// <summary>
/// 创建圆角图片
/// </summary>
public static Image<Rgba32> CreateRoundedImage(Image<Rgba32> image, float cornerRadius)
{
var result = new Image<Rgba32>(image.Width, image.Height);
result.Mutate(ctx =>
{
ctx.Fill(Color.Transparent);
var pathBuilder = new PathBuilder();
pathBuilder.AddRoundedRectangle(0, 0, image.Width, image.Height, cornerRadius);
var path = pathBuilder.Build();
ctx.Clip(path);
ctx.DrawImage(image, Point.Empty, 1f);
});
return result;
}
}
}
三、高级功能扩展
3.1 批量处理工具 (BatchProcessor.cs)
csharp
using System.Text.Json;
namespace ImageWatermark
{
public class BatchConfig
{
public string SourceFolder { get; set; } = "";
public string OutputFolder { get; set; } = "";
public string WatermarkImagePath { get; set; } = "";
public WatermarkPosition Position { get; set; } = WatermarkPosition.BottomRight;
public float Opacity { get; set; } = 0.7f;
public int Margin { get; set; } = 20;
public string[] Extensions { get; set; } = { ".jpg", ".jpeg", ".png", ".bmp" };
}
public static class BatchProcessor
{
public static void ProcessFolder(string configFilePath)
{
var json = File.ReadAllText(configFilePath);
var config = JsonSerializer.Deserialize<BatchConfig>(json);
if (config == null) return;
// 创建输出文件夹
Directory.CreateDirectory(config.OutputFolder);
// 获取所有图片文件
var files = Directory.GetFiles(config.SourceFolder)
.Where(f => config.Extensions.Contains(Path.GetExtension(f).ToLowerInvariant()))
.ToList();
Console.WriteLine($"找到 {files.Count} 个图片文件");
var watermarkConfig = new WatermarkConfiguration
{
Type = WatermarkType.Image,
Position = config.Position,
Opacity = config.Opacity,
Margin = config.Margin,
WatermarkImagePath = config.WatermarkImagePath
};
int processed = 0;
foreach (var file in files)
{
try
{
using var sourceImage = Image.Load<Rgba32>(file);
using var resultImage = WatermarkProcessor.ApplyWatermark(sourceImage, watermarkConfig);
string fileName = Path.GetFileName(file);
string outputPath = Path.Combine(config.OutputFolder, fileName);
resultImage.Save(outputPath);
processed++;
Console.WriteLine($"[{processed}/{files.Count}] 已处理: {fileName}");
}
catch (Exception ex)
{
Console.WriteLine($"处理 {file} 失败: {ex.Message}");
}
}
Console.WriteLine($"\n批量处理完成!成功处理 {processed}/{files.Count} 个文件");
}
public static void ProcessWithDifferentPositions(string sourceFolder, string outputFolder, string watermarkPath)
{
var positions = Enum.GetValues<WatermarkPosition>();
foreach (var position in positions)
{
var folder = Path.Combine(outputFolder, position.ToString());
Directory.CreateDirectory(folder);
var files = Directory.GetFiles(sourceFolder)
.Where(f => new[] { ".jpg", ".jpeg", ".png" }.Contains(Path.GetExtension(f).ToLowerInvariant()))
.ToList();
var config = new WatermarkConfiguration
{
Type = WatermarkType.Image,
Position = position,
Opacity = 0.7f,
Margin = 20,
WatermarkImagePath = watermarkPath
};
foreach (var file in files)
{
using var sourceImage = Image.Load<Rgba32>(file);
using var resultImage = WatermarkProcessor.ApplyWatermark(sourceImage, config);
string fileName = Path.GetFileName(file);
string outputPath = Path.Combine(folder, fileName);
resultImage.Save(outputPath);
}
}
}
}
}
3.2 配置文件示例 (batch_config.json)
json
{
"SourceFolder": "D:\\Photos\\Original",
"OutputFolder": "D:\\Photos\\Watermarked",
"WatermarkImagePath": "D:\\Resources\\logo.png",
"Position": "BottomRight",
"Opacity": 0.7,
"Margin": 20,
"Extensions": [".jpg", ".jpeg", ".png"]
}
参考代码 c#给图片加水印源码(支持9个位置) www.youwenfan.com/contentcsv/116195.html
四、使用说明
4.1 安装依赖
bash
dotnet add package SixLabors.ImageSharp
dotnet add package SixLabors.ImageSharp.Drawing
4.2 准备文件
- 准备源图片(如
source.jpg) - 准备水印图片(如
watermark.png) - 运行程序
4.3 9个位置说明
+----------------+----------------+----------------+
| TopLeft | TopCenter | TopRight |
| (左上角) | (上中) | (右上角) |
+----------------+----------------+----------------+
| MiddleLeft | Center | MiddleRight |
| (左中) | (正中) | (右中) |
+----------------+----------------+----------------+
| BottomLeft | BottomCenter | BottomRight |
| (左下角) | (下中) | (右下角) |
+----------------+----------------+----------------+
4.4 自定义配置示例
csharp
// 1. 右下角图片水印
var config1 = new WatermarkConfiguration
{
Type = WatermarkType.Image,
Position = WatermarkPosition.BottomRight,
Opacity = 0.7f,
Margin = 30
};
// 2. 正中文字水印
var config2 = new WatermarkConfiguration
{
Type = WatermarkType.Text,
Position = WatermarkPosition.Center,
WatermarkText = "© 2024 My Company",
FontSize = 36,
FontColor = Color.White.WithAlpha(0.8f),
AddShadow = true
};
// 3. 平铺水印
var watermarkImage = Image.Load<Rgba32>("watermark.png");
var sourceImage = Image.Load<Rgba32>("source.jpg");
var result = WatermarkProcessor.ApplyTiledWatermark(sourceImage, watermarkImage, 0.3f, 50);
result.Save("tiled_watermark.jpg");
// 4. 对角线文字水印
var diagonalResult = WatermarkProcessor.ApplyDiagonalTextWatermark(
sourceImage,
"CONFIDENTIAL",
Color.Red,
24,
0.3f
);
diagonalResult.Save("diagonal_watermark.jpg");
五、功能特点
支持9个位置 :覆盖图片所有角落和中心位置
支持图片和文字水印 :两种水印类型
透明度控制 :可调节水印透明度
边距调整 :自定义水印与边缘的距离
阴影效果 :增强水印可读性
旋转功能 :支持水印旋转角度
缩放控制 :自动调整水印大小
批量处理 :支持文件夹批量添加水印
平铺水印 :支持重复平铺水印
对角线水印:支持倾斜文字水印