Skip to content

Instantly share code, notes, and snippets.

@zhaoyibo
Last active January 24, 2016 05:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zhaoyibo/a012f53386fd154cc9f4 to your computer and use it in GitHub Desktop.
Save zhaoyibo/a012f53386fd154cc9f4 to your computer and use it in GitHub Desktop.
generate QR Code with zxing
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
/**
* 二维码生成工具类
*
* @author zhaoyibochn@gmail.com
*/
public class QRCodeUtils {
// logo 宽度
private static final int LOGO_WIDTH = 110;
private static final int LOGO_HEIGHT = LOGO_WIDTH;
// logo 边框宽度
private static final int LOGO_FRAME_WIDTH = 5;
// logo 边框颜色
private static final int LOGO_FRAME_COLOR = 0xffffffff;
// logo 边缘颜色
private static final int LOGO_BORDER_COLOR = 0x00000000;
// logo 圆角半径
private static final int LOGO_RADIUS = 10;
// 二维码前景色
private static final int QRCODE_FOREGROUND_COLOR = 0x00000000;
// 二维码背景色
private static final int QRCODE_BACKGROUND_COLOR = 0xffffffff;
// 二维码四周留白宽度
private static final int QRCODE_SPECIFIES_MARGIN = 1;
// 二维码写码器
private static final MultiFormatWriter mutiWriter = new MultiFormatWriter();
private static final Logger logger = LoggerFactory.getLogger(QRCodeUtils.class);
/**
* 将生成的二维码输出到流中
*
* @param content
* 二维码存储的信息
* @param width
* 二维码的宽度
* @param height
* 二维码的高度
* @param logo
* 输入流: Logo 源
* @param out
* 输出流
*/
public static void generateToStream(String content, int width, int height, InputStream logo,
OutputStream out) {
try {
ImageIO.write(genQRCode(content, width, height, logo), "jpg", out);
} catch (IOException e) {
e.printStackTrace();
logger.warn("Oops.", e);
} catch (WriterException e) {
e.printStackTrace();
logger.warn("Oops.", e);
}
}
/**
* 生成二维码
*
* @param content
* 二维码存储的信息
* @param width
* 二维码的宽度
* @param height
* 二维码的高度
* @param logo
* InputSteam of the logo
* @return {@link BufferedImage}
* @throws WriterException
* @throws IOException
*/
private static BufferedImage genQRCode(String content, int width, int height, InputStream logo)
throws WriterException, IOException {
int r = LOGO_RADIUS;
// 读取 logo 源图像
int logoPixels[][] = null;
boolean hasLogo = false;
if (logo != null) {
try {
logoPixels = getLogoPixels(scale(logo, LOGO_WIDTH, LOGO_HEIGHT, true), r);
hasLogo = true;
} catch (IOException e) {
e.printStackTrace();
logger.warn("Oops. Read the logo failed.", e);
}
}
Map<EncodeHintType, Object> hint = new HashMap<>();
hint.put(EncodeHintType.CHARACTER_SET, "utf-8");
hint.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
hint.put(EncodeHintType.MARGIN, QRCODE_SPECIFIES_MARGIN);
// 生成二维码
BitMatrix matrix = mutiWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hint);
int foregroundColor = QRCODE_FOREGROUND_COLOR;
int backgroundColor = QRCODE_BACKGROUND_COLOR;
// 二维矩阵转为一维像素数组
int halfW = matrix.getWidth() / 2;
int halfH = matrix.getHeight() / 2;
int[] pixels = new int[width * height];
int logoHalfWidth = LOGO_WIDTH / 2;
int near = halfW - logoHalfWidth - LOGO_FRAME_WIDTH + r;
int far = halfW + logoHalfWidth + LOGO_FRAME_WIDTH - r;
for (int y = 0; y < matrix.getHeight(); y++) {
for (int x = 0; x < matrix.getWidth(); x++) {
if (!hasLogo) {
// 没有 logo 的二维码
pixels[y * width + x] = matrix.get(x, y) ? foregroundColor : backgroundColor;
} else {
// logo
if (x > halfW - logoHalfWidth && x < halfW + logoHalfWidth
&& y > halfH - logoHalfWidth && y < halfH + logoHalfWidth) {
pixels[y * width + x] = logoPixels[x - halfW + logoHalfWidth][y - halfH
+ logoHalfWidth];
}
// 在图片四周形成边框
else if ((x > halfW - logoHalfWidth - LOGO_FRAME_WIDTH
&& x < halfW - logoHalfWidth + LOGO_FRAME_WIDTH
&& y > halfH - logoHalfWidth - LOGO_FRAME_WIDTH
&& y < halfH + logoHalfWidth + LOGO_FRAME_WIDTH)
|| (x > halfW + logoHalfWidth - LOGO_FRAME_WIDTH
&& x < halfW + logoHalfWidth + LOGO_FRAME_WIDTH
&& y > halfH - logoHalfWidth - LOGO_FRAME_WIDTH
&& y < halfH + logoHalfWidth + LOGO_FRAME_WIDTH)
|| (x > halfW - logoHalfWidth - LOGO_FRAME_WIDTH
&& x < halfW + logoHalfWidth + LOGO_FRAME_WIDTH
&& y > halfH - logoHalfWidth - LOGO_FRAME_WIDTH
&& y < halfH - logoHalfWidth + LOGO_FRAME_WIDTH)
|| (x > halfW - logoHalfWidth - LOGO_FRAME_WIDTH
&& x < halfW + logoHalfWidth + LOGO_FRAME_WIDTH
&& y > halfH + logoHalfWidth - LOGO_FRAME_WIDTH
&& y < halfH + logoHalfWidth + LOGO_FRAME_WIDTH)) {
// 圆角处理
if (x < near && y < near
&& (near - x) * (near - x) + (near - y) * (near - y) > r * r) {
// 左上圆角
pixels[y * width + x] = matrix.get(x, y) ? foregroundColor
: backgroundColor;
} else if (x > far && y < near
&& (x - far) * (x - far) + (near - y) * (near - y) > r * r) {
// 右上圆角
pixels[y * width + x] = matrix.get(x, y) ? foregroundColor
: backgroundColor;
} else if (x < near && y > far
&& (near - x) * (near - x) + (y - far) * (y - far) > r * r) {
// 左下圆角
pixels[y * width + x] = matrix.get(x, y) ? foregroundColor
: backgroundColor;
} else if (x > far && y > far
&& (x - far) * (x - far) + (y - far) * (y - far) > r * r) {
// 右下圆角
pixels[y * width + x] = matrix.get(x, y) ? foregroundColor
: backgroundColor;
} else {
// 边框填充颜色
pixels[y * width + x] = backgroundColor;
}
} else {
pixels[y * width + x] = matrix.get(x, y) ? foregroundColor
: backgroundColor;
}
}
}
}
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
image.getRaster().setDataElements(0, 0, width, height, pixels);
return image;
}
/**
* 对图片进行圆角处理, 并生成数组
*
* @param logo
* BufferedImage of the log
* @param radius
* 圆角半径
* @return logo 像素数组
*/
public static int[][] getLogoPixels(BufferedImage logo, int radius) {
int borderColor = LOGO_BORDER_COLOR;
int whiteColor = LOGO_FRAME_COLOR;
int srcPixels[][] = new int[LOGO_WIDTH][LOGO_HEIGHT];
int r = radius;// 圆角处理半径
int max = LOGO_WIDTH;
for (int x = 0; x < LOGO_WIDTH; x++) {
for (int y = 0; y < LOGO_HEIGHT; 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) {
srcPixels[x][y] = whiteColor;
} else {
srcPixels[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) {
srcPixels[x][y] = whiteColor;
} else {
srcPixels[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) {
srcPixels[x][y] = whiteColor;
} else {
srcPixels[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) {
srcPixels[x][y] = whiteColor;
} else {
srcPixels[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))) {
// 四周除圆角的边框
srcPixels[x][y] = borderColor;
} else {
// 图片值
srcPixels[x][y] = logo.getRGB(x, y);
}
}
}
}
return srcPixels;
}
/**
* 把传入的原始图像按高度和宽度进行缩放,生成符合要求的图标
*
* @param src
* 源
* @param height
* 目标高度
* @param width
* 目标宽度
* @param hasFiller
* 比例不对时是否需要补白:true 为补白; false 为不补白;
* @return {@link BufferedImage}
* @throws IOException
*/
private static BufferedImage scale(InputStream src, int height, int width, boolean hasFiller)
throws IOException {
double ratio = 0.0D; // 缩放比例
BufferedImage srcImage = ImageIO.read(src);
Image destImage = srcImage.getScaledInstance(width, height, BufferedImage.SCALE_SMOOTH);
// 计算比例
if ((srcImage.getHeight() > height) || (srcImage.getWidth() > width)) {
if (srcImage.getHeight() > srcImage.getWidth()) {
ratio = (new Integer(height)).doubleValue() / srcImage.getHeight();
} else {
ratio = (new Integer(width)).doubleValue() / srcImage.getWidth();
}
AffineTransformOp op = new AffineTransformOp(
AffineTransform.getScaleInstance(ratio, ratio), null);
destImage = op.filter(srcImage, null);
}
// 补白
if (hasFiller) {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D graphic = image.createGraphics();
graphic.setColor(Color.white);
graphic.fillRect(0, 0, width, height);
if (width == destImage.getWidth(null))
graphic.drawImage(destImage, 0, (height - destImage.getHeight(null)) / 2,
destImage.getWidth(null), destImage.getHeight(null), Color.white, null);
else graphic.drawImage(destImage, (width - destImage.getWidth(null)) / 2, 0,
destImage.getWidth(null), destImage.getHeight(null), Color.white, null);
graphic.dispose();
destImage = image;
}
return (BufferedImage) destImage;
}
public static void main(String[] args) {
InputStream in = null;
try {
in = new URL("https://dn-codingon.qbox.me/images/avatar/me.jpeg").openStream();
generateToStream("https://codingon.com", 500, 500, in,
new FileOutputStream("/Users/yibo/Desktop/qrcode.jpg"));
} catch (IOException e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(in);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment