C# 生成中间带 Logo 头像的二维码

使用 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;
        }
    }
}
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 准备文件

  1. 准备一个头像图片(如 avatar.png
  2. 运行程序

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 批量生成


六、常见问题解决

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  // 透明背景
);
相关推荐
闪电悠米1 小时前
黑马点评-Redis 消息队列-03_stream_consumer_group
开发语言·数据库·redis·分布式·缓存·junit·lua
8125035331 小时前
第 9 篇:子网掩码:如何划分“小区”
开发语言·php
Jun6261 小时前
QT(12)-制作lib库
开发语言·qt
Java面试题总结1 小时前
C#12 中的 Using Alias
开发语言·windows·c#
加号32 小时前
【C#】 ASCII 码转字符串技术解析
开发语言·c#
Cloud_Shy6182 小时前
解读《Effective Python 3rd Edition》:从练气到老魔(第五章 Item 33 - 35)
开发语言·人工智能·笔记·python·学习方法
星恒随风2 小时前
C++ 类和对象入门(五):初始化列表、explicit 和 static 成员详解
开发语言·c++·笔记·学习·状态模式
艾利克斯冰2 小时前
Java 设计模式-行为型模式(更新中)
java·开发语言·设计模式
倒霉蛋小马3 小时前
Java新特性:record关键字
java·开发语言