使用 ZXing.Net 生成二维码,并在中间嵌入圆形 Logo 头像。
一、项目结构
QRCoderWithLogo/
├── Program.cs
├── QRCodeGenerator.cs
├── LogoProcessor.cs
├── ImageHelper.cs
└── QRCoderWithLogo.csproj
二、源码实现
2.1 项目文件 (QRCoderWithLogo.csproj)
xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ZXing.Net" Version="0.16.9" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.2" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta15" />
</ItemGroup>
</Project>
2.2 主程序 (Program.cs)
csharp
using System;
using System.IO;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png;
namespace QRCoderWithLogo
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("=== 生成带 Logo 的二维码 ===\n");
// 配置参数
string qrContent = "https://github.com/your-profile";
string logoPath = "avatar.png"; // 头像文件路径
string outputPath = "qrcode_with_logo.png";
int qrSize = 500; // 二维码尺寸
int logoSize = 100; // Logo尺寸
bool makeLogoCircular = true; // 是否将Logo裁剪成圆形
int borderWidth = 5; // Logo边框宽度
try
{
// 1. 生成基础二维码
Console.WriteLine("正在生成二维码...");
using var qrImage = QRCodeGenerator.GenerateQRCode(
content: qrContent,
size: qrSize,
foregroundColor: Color.Black,
backgroundColor: Color.White
);
// 2. 处理Logo
Console.WriteLine("正在处理Logo头像...");
using var logoImage = LogoProcessor.LoadAndProcessLogo(
logoPath: logoPath,
targetSize: logoSize,
makeCircular: makeLogoCircular,
borderWidth: borderWidth,
borderColor: Color.White
);
// 3. 将Logo嵌入二维码中心
Console.WriteLine("正在合成最终图像...");
using var finalImage = ImageHelper.MergeImages(
baseImage: qrImage,
overlayImage: logoImage,
position: ImageHelper.ImagePosition.Center
);
// 4. 保存结果
Console.WriteLine("正在保存图像...");
using var stream = File.Create(outputPath);
finalImage.Save(stream, new PngEncoder());
Console.WriteLine($"\n 二维码已生成: {outputPath}");
Console.WriteLine($" 尺寸: {qrSize}x{qrSize}");
Console.WriteLine($" Logo尺寸: {logoSize}x{logoSize}");
Console.WriteLine($" 圆形Logo: {makeLogoCircular}");
}
catch (Exception ex)
{
Console.WriteLine($"\n 错误: {ex.Message}");
Console.WriteLine(ex.StackTrace);
}
Console.WriteLine("\n按任意键退出...");
Console.ReadKey();
}
}
}
2.3 二维码生成器 (QRCodeGenerator.cs)
csharp
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using ZXing;
using ZXing.Common;
using ZXing.QrCode;
namespace QRCoderWithLogo
{
public static class QRCodeGenerator
{
/// <summary>
/// 生成二维码图像
/// </summary>
public static Image<Rgba32> GenerateQRCode(
string content,
int size = 500,
Color? foregroundColor = null,
Color? backgroundColor = null)
{
var fgColor = foregroundColor ?? Color.Black;
var bgColor = backgroundColor ?? Color.White;
// 创建二维码写入器
var writer = new BarcodeWriterPixelData
{
Format = BarcodeFormat.QR_CODE,
Options = new QrCodeEncodingOptions
{
Width = size,
Height = size,
Margin = 1,
CharacterSet = "UTF-8",
ErrorCorrection = ZXing.QrCode.Internal.ErrorCorrectionLevel.H // 高纠错级别,适合Logo
}
};
// 生成二维码像素数据
var pixelData = writer.Write(content);
// 转换为ImageSharp图像
var image = new Image<Rgba32>(pixelData.Width, pixelData.Height);
// 填充像素
for (int y = 0; y < pixelData.Height; y++)
{
for (int x = 0; x < pixelData.Width; x++)
{
var pixel = pixelData.Pixels[y * pixelData.Width + x];
image[x, y] = pixel == 0 ? bgColor : fgColor;
}
}
return image;
}
/// <summary>
/// 生成带有自定义配置的二维码
/// </summary>
public static Image<Rgba32> GenerateQRCodeAdvanced(
string content,
int size = 500,
int margin = 1,
ErrorCorrectionLevel errorCorrection = ErrorCorrectionLevel.H,
Color foregroundColor = default,
Color backgroundColor = default)
{
if (foregroundColor == default) foregroundColor = Color.Black;
if (backgroundColor == default) backgroundColor = Color.White;
var writer = new BarcodeWriterPixelData
{
Format = BarcodeFormat.QR_CODE,
Options = new QrCodeEncodingOptions
{
Width = size,
Height = size,
Margin = margin,
CharacterSet = "UTF-8",
ErrorCorrection = errorCorrection
}
};
var pixelData = writer.Write(content);
var image = new Image<Rgba32>(pixelData.Width, pixelData.Height);
image.Process(ctx =>
{
ctx.Clear(backgroundColor);
for (int y = 0; y < pixelData.Height; y++)
{
for (int x = 0; x < pixelData.Width; x++)
{
var pixel = pixelData.Pixels[y * pixelData.Width + x];
if (pixel == 0)
{
image[x, y] = foregroundColor;
}
}
}
});
return image;
}
}
}
2.4 Logo 处理器 (LogoProcessor.cs)
csharp
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.PixelFormats;
namespace QRCoderWithLogo
{
public static class LogoProcessor
{
/// <summary>
/// 加载并处理Logo图像
/// </summary>
public static Image<Rgba32> LoadAndProcessLogo(
string logoPath,
int targetSize = 100,
bool makeCircular = true,
int borderWidth = 5,
Color? borderColor = null)
{
var borderClr = borderColor ?? Color.White;
// 1. 加载原始Logo
using var originalLogo = Image.Load<Rgba32>(logoPath);
// 2. 调整尺寸(保持比例)
var resizedLogo = ResizeImageProportionally(originalLogo, targetSize);
// 3. 创建最终图像(带边框)
var finalSize = targetSize + borderWidth * 2;
var finalLogo = new Image<Rgba32>(finalSize, finalSize);
// 4. 填充背景色
finalLogo.Mutate(ctx => ctx.Fill(borderClr));
// 5. 如果是圆形,裁剪Logo
if (makeCircular)
{
resizedLogo.Mutate(ctx =>
{
// 创建圆形裁剪路径
var center = new PointF(finalSize / 2f, finalSize / 2f);
var radius = targetSize / 2f;
ctx.Clip(new EllipsePolygon(center, radius));
});
}
// 6. 将Logo绘制到中心
finalLogo.Mutate(ctx =>
{
var position = new Point(borderWidth, borderWidth);
ctx.DrawImage(resizedLogo, position, 1f);
});
return finalLogo;
}
/// <summary>
/// 按比例调整图像尺寸
/// </summary>
private static Image<Rgba32> ResizeImageProportionally(Image<Rgba32> image, int targetSize)
{
float ratio = Math.Min(
(float)targetSize / image.Width,
(float)targetSize / image.Height
);
int newWidth = (int)(image.Width * ratio);
int newHeight = (int)(image.Height * ratio);
var resized = image.Clone(ctx =>
{
ctx.Resize(newWidth, newHeight, KnownResamplers.Lanczos3);
});
return resized;
}
/// <summary>
/// 创建圆形Logo
/// </summary>
public static Image<Rgba32> CreateCircularLogo(
Image<Rgba32> logo,
int borderWidth = 5,
Color? borderColor = null)
{
var borderClr = borderColor ?? Color.White;
int size = Math.Max(logo.Width, logo.Height) + borderWidth * 2;
var circularLogo = new Image<Rgba32>(size, size);
// 填充边框
circularLogo.Mutate(ctx =>
{
ctx.Fill(borderClr);
// 创建圆形裁剪区域
var center = new PointF(size / 2f, size / 2f);
var radius = (size - borderWidth * 2) / 2f;
ctx.Clip(new EllipsePolygon(center, radius));
ctx.DrawImage(logo, new Point(borderWidth, borderWidth), 1f);
});
return circularLogo;
}
/// <summary>
/// 从URL下载Logo
/// </summary>
public static async Task<Image<Rgba32>> DownloadLogoAsync(string url)
{
using var httpClient = new HttpClient();
var imageBytes = await httpClient.GetByteArrayAsync(url);
using var stream = new MemoryStream(imageBytes);
return await Image.LoadAsync<Rgba32>(stream);
}
}
}
2.5 图像处理工具 (ImageHelper.cs)
csharp
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.PixelFormats;
namespace QRCoderWithLogo
{
public static class ImageHelper
{
public enum ImagePosition
{
Center,
TopLeft,
TopRight,
BottomLeft,
BottomRight
}
/// <summary>
/// 将叠加图像合并到基础图像
/// </summary>
public static Image<Rgba32> MergeImages(
Image<Rgba32> baseImage,
Image<Rgba32> overlayImage,
ImagePosition position = ImagePosition.Center)
{
var result = baseImage.Clone();
result.Mutate(ctx =>
{
var overlayPosition = CalculatePosition(baseImage.Size, overlayImage.Size, position);
ctx.DrawImage(overlayImage, overlayPosition, 1f);
});
return result;
}
/// <summary>
/// 计算叠加图像的位置
/// </summary>
private static Point CalculatePosition(Size baseSize, Size overlaySize, ImagePosition position)
{
return position switch
{
ImagePosition.Center => new Point(
(baseSize.Width - overlaySize.Width) / 2,
(baseSize.Height - overlaySize.Height) / 2
),
ImagePosition.TopLeft => new Point(0, 0),
ImagePosition.TopRight => new Point(baseSize.Width - overlaySize.Width, 0),
ImagePosition.BottomLeft => new Point(0, baseSize.Height - overlaySize.Height),
ImagePosition.BottomRight => new Point(
baseSize.Width - overlaySize.Width,
baseSize.Height - overlaySize.Height
),
_ => new Point(
(baseSize.Width - overlaySize.Width) / 2,
(baseSize.Height - overlaySize.Height) / 2
)
};
}
/// <summary>
/// 创建带圆角的图像
/// </summary>
public static Image<Rgba32> CreateRoundedImage(
Image<Rgba32> image,
float cornerRadius,
Color? backgroundColor = null)
{
var bgColor = backgroundColor ?? Color.Transparent;
var rounded = new Image<Rgba32>(image.Width, image.Height);
rounded.Mutate(ctx =>
{
ctx.Fill(bgColor);
// 创建圆角矩形路径
var pathBuilder = new PathBuilder();
pathBuilder.AddRoundedRectangle(0, 0, image.Width, image.Height, cornerRadius);
var path = pathBuilder.Build();
ctx.Clip(path);
ctx.DrawImage(image, new Point(0, 0), 1f);
});
return rounded;
}
/// <summary>
/// 添加阴影效果
/// </summary>
public static Image<Rgba32> AddShadow(
Image<Rgba32> image,
int shadowOffset = 5,
float shadowOpacity = 0.3f,
Color? shadowColor = null)
{
var shColor = shadowColor ?? Color.Black;
var result = new Image<Rgba32>(image.Width + shadowOffset, image.Height + shadowOffset);
result.Mutate(ctx =>
{
// 绘制阴影
ctx.Fill(shColor.WithAlpha(shadowOpacity));
ctx.DrawImage(image, new Point(shadowOffset, shadowOffset), 1f);
// 绘制原图
ctx.DrawImage(image, Point.Empty, 1f);
});
return result;
}
/// <summary>
/// 保存图像到文件
/// </summary>
public static void SaveImage(Image<Rgba32> image, string filePath, int quality = 90)
{
var encoder = new SixLabors.ImageSharp.Formats.Jpeg.JpegEncoder
{
Quality = quality
};
image.Save(filePath, encoder);
}
}
}
三、高级功能扩展
3.1 批量生成工具 (BatchGenerator.cs)
csharp
using System.Text.Json;
namespace QRCoderWithLogo
{
public class QRCodeConfig
{
public string Content { get; set; } = "";
public string LogoPath { get; set; } = "";
public string OutputPath { get; set; } = "";
public int Size { get; set; } = 500;
public int LogoSize { get; set; } = 100;
public bool CircularLogo { get; set; } = true;
public int BorderWidth { get; set; } = 5;
}
public static class BatchGenerator
{
public static void GenerateFromConfig(string configFilePath)
{
var json = File.ReadAllText(configFilePath);
var configs = JsonSerializer.Deserialize<List<QRCodeConfig>>(json);
foreach (var config in configs!)
{
Console.WriteLine($"生成: {config.OutputPath}");
using var qr = QRCodeGenerator.GenerateQRCode(
config.Content,
config.Size
);
using var logo = LogoProcessor.LoadAndProcessLogo(
config.LogoPath,
config.LogoSize,
config.CircularLogo,
config.BorderWidth
);
using var final = ImageHelper.MergeImages(qr, logo);
final.Save(config.OutputPath);
}
}
public static void GenerateFromCsv(string csvPath)
{
var lines = File.ReadAllLines(csvPath);
for (int i = 1; i < lines.Length; i++) // 跳过表头
{
var parts = lines[i].Split(',');
if (parts.Length >= 3)
{
var config = new QRCodeConfig
{
Content = parts[0],
LogoPath = parts[1],
OutputPath = parts[2]
};
GenerateSingle(config);
}
}
}
private static void GenerateSingle(QRCodeConfig config)
{
using var qr = QRCodeGenerator.GenerateQRCode(
config.Content,
config.Size
);
using var logo = LogoProcessor.LoadAndProcessLogo(
config.LogoPath,
config.LogoSize,
config.CircularLogo,
config.BorderWidth
);
using var final = ImageHelper.MergeImages(qr, logo);
final.Save(config.OutputPath);
}
}
}
3.2 配置示例 (config.json)
json
[
{
"Content": "https://github.com/user1",
"LogoPath": "avatars/user1.png",
"OutputPath": "qrcodes/user1.png",
"Size": 500,
"LogoSize": 100,
"CircularLogo": true,
"BorderWidth": 5
},
{
"Content": "https://github.com/user2",
"LogoPath": "avatars/user2.png",
"OutputPath": "qrcodes/user2.png",
"Size": 500,
"LogoSize": 100,
"CircularLogo": true,
"BorderWidth": 5
},
{
"Content": "https://github.com/user3",
"LogoPath": "avatars/user3.png",
"OutputPath": "qrcodes/user3.png",
"Size": 500,
"LogoSize": 100,
"CircularLogo": false,
"BorderWidth": 3
}
]
四、使用说明
4.1 安装依赖
bash
dotnet add package ZXing.Net
dotnet add package SixLabors.ImageSharp
dotnet add package SixLabors.ImageSharp.Drawing
4.2 准备文件
- 准备一个头像图片(如
avatar.png) - 运行程序
4.3 自定义配置
csharp
// 修改这些参数来自定义效果
string qrContent = "https://your-link.com"; // 二维码内容
string logoPath = "your-logo.png"; // Logo路径
int qrSize = 800; // 二维码尺寸
int logoSize = 150; // Logo尺寸
bool makeLogoCircular = true; // 圆形Logo
int borderWidth = 8; // 边框宽度
参考代码 C# 生成中间带logo头像的二维码 www.youwenfan.com/contentcsv/116161.html
五、效果特点
高纠错级别 :使用 H 级纠错,即使中间有 Logo 也能正常扫描
圆形 Logo :自动将 Logo 裁剪成圆形,更美观
白色边框 :Logo 周围添加白色边框,提高对比度
高质量缩放 :使用 Lanczos3 算法缩放,保持清晰度
支持透明背景 :可以生成带透明背景的二维码
批量生成:支持从 JSON/CSV 批量生成
六、常见问题解决
6.1 Logo 太大导致无法扫描
csharp
// 建议Logo尺寸不超过二维码的1/4
int logoSize = qrSize / 4; // 推荐比例
6.2 二维码太模糊
csharp
// 增加二维码尺寸
int qrSize = 800; // 更大的尺寸,更清晰的像素
6.3 需要透明背景
csharp
// 修改二维码生成时的背景色
using var qrImage = QRCodeGenerator.GenerateQRCode(
content: qrContent,
size: qrSize,
foregroundColor: Color.Black,
backgroundColor: Color.Transparent // 透明背景
);