带圆角LOGO的QrCode二维码实时生成

时间:2022-11-17 14:07:33

最近工作中经常要用到QrCode二维码,研究了一下,写了个带圆角LOGO的JAVA实现,QrCode之所以能在中间放个LOGO图标,是因为编码时的信息冗余。实现的具体代码如下:

方法接口:

import java.io.File;
import java.io.OutputStream;

public interface QRCodeService {

public void generateToStream(String code, OutputStream stream);

public void generateToStream(String code, OutputStream stream, int width);

public void generateToStream(String code, OutputStream stream, int width, int frontColor);

public void generateToStream(String code, OutputStream stream, int width, int frontColor, File logo);
}
接口实现类:

import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

import javax.imageio.ImageIO;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;


@Service("qrCodeService")
public class QRCodeServiceImpl implements QRCodeService {

private static final Logger LOGGER = LoggerFactory.getLogger(QRCodeServiceImpl.class);
// 二维码的宽
private static int WIDTH = 250;
// 中间图片的宽
private static int IMGWIDTH = 60;
// 圆角半径
private static int RADIUS = 10;
// 留白填充宽度
private static int MARGIN = 4;

private static int FRONTCOLOR = 0x00000000;//0x00808080;

/**
* 功能描述:生成普通二维码到输出流
*/
@Override
public void generateToStream(String code, OutputStream stream) {
this.generateToStream(code, stream, WIDTH, FRONTCOLOR, null);
}

@Override
public void generateToStream(String code, OutputStream stream, int width) {
this.generateToStream(code, stream, width, FRONTCOLOR, null);
}

@Override
public void generateToStream(String code, OutputStream stream, int width, int frontColor) {
this.generateToStream(code, stream, width, frontColor, null);
}

@Override
public void generateToStream(String code, OutputStream stream, int width, int frontColor, File logo) {
Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
// 修正容量高
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
// 边框留白
hints.put(EncodeHintType.MARGIN, 1);
BitMatrix matrix = null;
try {
matrix = new MultiFormatWriter().encode(code, BarcodeFormat.QR_CODE, width, width, hints);
} catch (WriterException e) {
LOGGER.error("", e);
}
proc(matrix, stream, frontColor, logo);
}

public void proc(BitMatrix matrix, OutputStream stream, int frontColor, File logo) {
int width = matrix.getWidth();
// 处理后图片的数据
int pixels[] = new int[width * width];
// 中间图片数组数据
int src[][] = null;
boolean hashlogo = false;
if(logo != null){
src = getPic(logo);
hashlogo = true;
}
// 填充色
int margincolor = 0xffffffff;// 白色
int w_half = width / 2;
int frame = MARGIN;
int img_half = IMGWIDTH / 2;
int r = RADIUS;
int near = width / 2 - img_half - frame + r;//101
int far = width / 2 + img_half + frame - r;//149
for (int y = 0; y < width; y++) {
for (int x = 0; x < width; x++) {
if(!hashlogo){
// 二维码
pixels[y * width + x] = matrix.get(x, y) ? frontColor : margincolor;
} else {
// 中间图片
if (x > w_half - img_half && x < w_half + img_half
&& y > w_half - img_half && y < w_half + img_half) {
//
pixels[y * width + x] = src[x - w_half + img_half][y - w_half + img_half];

} else if ((x > w_half - img_half - frame // 左边框
&& x < w_half - img_half + frame && y > w_half - img_half - frame && y < w_half
+ img_half + frame)
|| (x > w_half + img_half - frame // 右边框
&& x < w_half + img_half + frame
&& y > w_half - img_half - frame && y < w_half + img_half
+ frame)
|| (x > w_half - img_half - frame // 上边框
&& x < w_half + img_half + frame
&& y > w_half - img_half - frame && y < w_half - img_half
+ frame)
|| (x > w_half - img_half - frame // 下边框
&& x < w_half + img_half + frame
&& y > w_half + img_half - frame && y < w_half + img_half
+ frame)) {

// 圆角处理
if(x<near && y<near && (near-x)*(near-x)+(near-y)*(near-y)> r*r){
// 左上圆角
pixels[y * width + x] = matrix.get(x, y) ? frontColor : margincolor;
} else if(x>far && y<near && (x-far)*(x-far)+(near-y)*(near-y) > r*r){
// 右上圆角
pixels[y * width + x] = matrix.get(x, y) ? frontColor : margincolor;
} else if(x<near && y>far && (near-x)*(near-x)+(y-far)*(y-far) > r*r){
// 左下圆角
pixels[y * width + x] = matrix.get(x, y) ? frontColor : margincolor;
} else if(x>far && y>far && (x-far)*(x-far)+(y-far)*(y-far) > r*r){
// 右下圆角
pixels[y * width + x] = matrix.get(x, y) ? frontColor : margincolor;
} else {
// 边框填充颜色
pixels[y * width + x] = margincolor;
}
} else {
// 二维码
pixels[y * width + x] = matrix.get(x, y) ? frontColor : margincolor;
}
}
}
}
BufferedImage image = new BufferedImage(width, width, BufferedImage.TYPE_INT_RGB);
image.getRaster().setDataElements(0, 0, width, width, pixels);
try {
ImageIO.write(image, "png", stream);
} catch (IOException e) {
//TODO
}
}

// 图片的压缩、圆角处理,并生成数组
public int[][] getPic(File logo) {
BufferedImage biSrc = null;
try {
biSrc = ImageIO.read(logo);
} catch (IOException e) {

}
BufferedImage biTarget = new BufferedImage(IMGWIDTH, IMGWIDTH, BufferedImage.TYPE_3BYTE_BGR);
biTarget.getGraphics().drawImage(biSrc.getScaledInstance(IMGWIDTH, IMGWIDTH, Image.SCALE_SMOOTH), 0, 0, null);
int src[][] = new int[IMGWIDTH][IMGWIDTH];
// 圆角处理半径
int r = RADIUS;
int max = IMGWIDTH;
int bordercolor = 0x00000000;
int whitecolor = 0xffffffff;
for (int x = 0; x < IMGWIDTH; x++) {
for (int y = 0; y < IMGWIDTH; y++) {
if(x<r&&y<r&&((r-x)*(r-x)+(r-y)*(r-y)>(r-1)*(r-1))){
// 左上圆角
if((r-x)*(r-x)+(r-y)*(r-y)>r*r){
src[x][y] = whitecolor;
} else {
src[x][y] = bordercolor;
}
} else if (x>(max-r)&&y<r&&(x+r-max)*(x+r-max)+(r-y)*(r-y)>(r-1)*(r-1)){
// 右上圆角
if((x+r-max)*(x+r-max)+(r-y)*(r-y)>r*r){
src[x][y] = whitecolor;
}else{
src[x][y] = bordercolor;
}
} else if (x<r&&y>(max-r)&&(r-x)*(r-x)+(y+r-max)*(y+r-max)>(r-1)*(r-1)){
// 左下圆角
if((r-x)*(r-x)+(y+r-max)*(y+r-max)>r*r){
src[x][y] = whitecolor;
}else{
src[x][y] = bordercolor;
}
} else if (x>(max-r)&&y>(max-r)&&(x+r-max)*(x+r-max)+(y+r-max)*(y+r-max)>(r-1)*(r-1)){
// 右下圆角
if((x+r-max)*(x+r-max)+(y+r-max)*(y+r-max)>r*r){
src[x][y] = whitecolor;
}else{
src[x][y] = bordercolor;
}
} else {
if(((x>=r && x<=max-r) && (y==0||y==1||y==max-1||y==max)) || ((y>=r &&y<=max-r) && (x==0||x==1||x==max-1||x==max))){
// 四周除圆角的边框
src[x][y] = bordercolor;
} else {
// 图片值
src[x][y] = biTarget.getRGB(x, y);
}
}
}
}
return src;
}
}
控制器类:

@Controller
@RequestMapping("")
public class QrCodeController {

private static final Logger LOGGER = LoggerFactory.getLogger(QrCodeController.class);

@Resource(name = "qrCodeService")
QRCodeService qrCodeService;

@RequestMapping("qrcode")
public void generateQrCode(HttpServletRequest request,HttpServletResponse response){
try {
String code = request.getParameter("url");
if(StringUtils.isBlank(code)){
LOGGER.error("url is null");
return;
}
String width = request.getParameter("width");
String color = request.getParameter("color");
String logo = request.getParameter("logo");
OutputStream os = response.getOutputStream();
if (StringUtils.isNotBlank(width) && StringUtils.isNotBlank(color) && StringUtils.isNotBlank(logo)){
int my_width = Integer.valueOf(width);
int my_color = Integer.valueOf(color);
//int my_logo = Integer.valueOf(logo);
LOGGER.info("hash logo");
File logofile = new File("logo.jpg");
qrCodeService.generateToStream(code, os, my_width, my_color, logofile);
} else if (StringUtils.isNotBlank(width) && StringUtils.isNotBlank(color)){
int my_width = Integer.valueOf(width);
int my_color = Integer.valueOf(color);
qrCodeService.generateToStream(code, os, my_width, my_color);
} else if(StringUtils.isNotBlank(width)) {
int my_width = Integer.valueOf(width);
qrCodeService.generateToStream(code, os, my_width);
} else {
qrCodeService.generateToStream(code, os);
}
os.flush();
os.close();
LOGGER.info("generate qrcode succeed");
} catch (IOException e) {
LOGGER.error("generate qrcode error : ", e);
}
}

}
生成效果:

带圆角LOGO的QrCode二维码实时生成