Last active
January 24, 2016 05:27
-
-
Save zhaoyibo/a012f53386fd154cc9f4 to your computer and use it in GitHub Desktop.
generate QR Code with zxing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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