使用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在最后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
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 创建透明背景艺术二维码
http://blog.wangshuai.app/2020-08-05-使用Zxing.Net 创建透明背景艺术二维码/
作者
王帅
发布于
2020年8月5日
许可协议