898 字
4 分钟
使用Zxing.Net 创建透明背景艺术二维码

引言#

用过微信的都知道,里面有一个个性化二维码名片的功能;

image-20200805152257211

最近接到需求要做一个类似的好看一点的二维码,微信里面不支持手动添加背景图片,估计是做的模板;但是配色不是我擅长的事,要做很多模板出来让用户选是不现实的,我期望有自动合成颜色的方法,一番搜索之后找到一个商用产品qrcode.studio,里面有一个透明背景的功能,可以实现我的要求。

image-20200805154119073

于是参照实现了一个可以融合背景图片的透明背景图功能

解决方案#

在visual studio中打开从qrcode.studio下载下来的png二维码文件,放大之后可以看到module的形状

image-20200805155633543

因此,在黑的地方画一个点,周围再自动做颜色渐变就可以了;这里的径向渐变可以使用RadialGradientBrush,但是我是Winform,没有这个画刷;于是我参考c# radial gradient brush effect in GDI and winforms使用了PathGradientBrush

主要的代码在这里,完整demo在最后

public override Bitmap Render(BitMatrix matrix, BarcodeFormat format, string content, EncodingOptions options)
{
int width = matrix.Width;
int height = matrix.Height;
Foreground = Color.Black;
bool hasBackImage = false;
var qrCode = Encoder.encode(content, ErrorCorrectionLevel.L,options.Hints);
var qrCodeMatrix = qrCode.Matrix;
Console.WriteLine("-----------------------------");
foreach (var point in qrCodeMatrix.Array)
{
Console.WriteLine(string.Join(string.Empty, point.Select(s =>
{
if (s == 0) return " ";
return s.ToString();
})));
}
var det=new Detector(matrix);
var detPoints = det.detect().Points;
var backgroundBrush = new LinearGradientBrush(
new Rectangle(0, 0, width, height), BackgroundGradientColor, BackgroundGradientColor, LinearGradientMode.Vertical);
var foregroundBrush = new LinearGradientBrush(
new Rectangle(0, 0, width, height), ForegroundGradientColor, ForegroundGradientColor, LinearGradientMode.ForwardDia
var bmp = new Bitmap(width,height);
var gg = Graphics.FromImage(bmp);
if (File.Exists(BackImageFullName))
{
var backImageTemp=new Bitmap(BackImageFullName);
bmp= new Bitmap(backImageTemp, new Size(width, height));
gg=Graphics.FromImage(bmp);
hasBackImage = true;
}
else
{
gg.Clear(BackgroundGradientColor);
}
Point startPoint=new Point(0,0);
Point endPoint=new Point(0,0);
bool isStartPoint = false;
//var foreColor = Color.Black;
//var backColor = Color.White;
var foreColor = ForegroundGradientColor;
var backColor = BackgroundGradientColor;
if (hasBackImage && IsMergeBackColor)
{
var rateX = bmp.Width / qrCodeMatrix.Width;
var rateY = bmp.Height / qrCodeMatrix.Height;
if (rateY > rateX)
{
rateY = rateX;
}
else
{
rateX = rateY;
}
//todo boarder效果
var defaultBorder = 0;
var largeImage = new Bitmap(bmp, qrCodeMatrix.Width * rateX+defaultBorder, qrCodeMatrix.Height * rateY+defaultBord
var largeG = Graphics.FromImage(largeImage);
for (int inputY = 0; inputY < qrCodeMatrix.Height; inputY++)
{
for (int inputX = 0; inputX < qrCodeMatrix.Width; inputX++)
{
Rectangle foreRectangle = new Rectangle(inputX * rateX+defaultBorder, inputY * rateY+defaultBorder, rateX,
//定点位使用黑白,其它位置使用PathGradientBrush根据module点的坐标画出图形
if (IsInDetect(qrCodeMatrix, inputX, inputY))
{
if (qrCodeMatrix[inputX, inputY] == 1)
{
largeG.FillRectangle(new SolidBrush(foreColor), foreRectangle);
}
else
{
largeG.FillRectangle(new SolidBrush(backColor), foreRectangle);
}
}
else
{
using (var ellipsePath = new GraphicsPath())
{
ellipsePath.AddEllipse(foreRectangle);
var brush = new PathGradientBrush(ellipsePath);
var color = bmp.GetPixel(foreRectangle.X, foreRectangle.Y);
brush.CenterPoint = new PointF(foreRectangle.Width / 2 + foreRectangle.X,
foreRectangle.Height / 2 + foreRectangle.Y);
brush.SurroundColors = new[] {color};
brush.FocusScales = new PointF(0, 0);
if (qrCodeMatrix[inputX, inputY] == 1)
{
brush.CenterColor = foreColor;
}
else
{
brush.CenterColor = backColor;
}
largeG.FillRectangle(brush, foreRectangle);
}
}
}
}
return largeImage;
}
for (int x = 0; x < width - 1; x++)
{
for (int y = 0; y < height - 1; y++)
{
if (matrix[x, y])
{
if (!isStartPoint)
{
isStartPoint = true;
startPoint = new Point(x, y);
endPoint = new Point(width - x, height - y);
}
gg.FillRectangle(foregroundBrush, x, y, 1, 1);
}
else
{
if (x < endPoint.X && y < endPoint.Y && x > startPoint.X && y > startPoint.Y)
{
if (hasBackImage)
gg.FillRectangle(backgroundBrush, x, y, 1, 1);
}
}
}
}
if (File.Exists(IconFullName))
{
var icon = new Bitmap(IconFullName);
Image circleIcon;
if (IconShape == IconShape.Round)
{
circleIcon = CutCircle(IconFullName, 0, 0, icon.Height);
}
else
{
circleIcon = icon;
}
circleIcon = new Bitmap(circleIcon, new Size(33, 33));
gg.DrawImage(circleIcon,
new PointF((startPoint.X + endPoint.X) / 2 - circleIcon.Width / 2,
(startPoint.Y + endPoint.Y) / 2 - circleIcon.Height / 2));
}
if (hasBackImage)
gg.DrawRectangle(new Pen(Color.White, 5), startPoint.X - 3, startPoint.Y - 3,
endPoint.X - startPoint.X + 4,
endPoint.Y - startPoint.Y + 4);
return bmp;
}

还是使用林克作为背景,生成的二维码如下:

testQR000007

虽然看起来差了点,但是基本可以实现需求

常见问题#

  • 关于二维码

初次接触二维码建议看一下qrcode.com

  • 如何debug查看生成的图片

中断的时候调用bmp.Save(“123.png“)方法就行了

使用Zxing.Net 创建透明背景艺术二维码
https://blog.wangshuai.app/2020-08-05-使用zxingnet-创建透明背景艺术二维码/
作者
王帅
发布于
2020-08-05
许可协议
CC BY-NC-SA 4.0