最近工作中经常要用到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); } } }生成效果: