目标
用 Java 绘制分享图,需要将文字、用户头像、二维码等元素合成到背景图上。
外部依赖
Maven 依赖:
com.google.zxing
core
(the current version)
源代码
import com.google.zxing.BarcodeFormat;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
/**
*
* 图像合成工具类
*
* Created by scott on 2018-11-13.
*/
public class ImageBuilder {
private Graphics2D graphics;
private BufferedImage bgImage;
/**
*
* BufferedImage bgImage;
* // 从网络加载
* bgImage = ImageIO.read(url);
* // 从文件中加载
* bgImage = ImageIO.read(file);
*
*
* @param bgImage 背景图
*/
public ImageBuilder(BufferedImage bgImage) {
Objects.requireNonNull(bgImage);
this.bgImage = bgImage;
graphics = this.bgImage.createGraphics();
}
public ImageBuilder withText(Font font, Color color, int x, int y, String text) {
Objects.requireNonNull(font, "font 不能为 null");
Objects.requireNonNull(color, "color 不能为 null");
graphics.setFont(font);
graphics.setColor(color);
graphics.drawString(text, x, y);
return this;
}
/**
* 在底图上画圆形头像
*/
public ImageBuilder withCircularAvatar(BufferedImage avatar, int x, int y, int diameter) {
try {
Ellipse2D.Double shape = new Ellipse2D.Double(x, y, diameter, diameter);
graphics.setClip(shape);
graphics.drawImage(avatar.getScaledInstance(diameter, diameter, Image.SCALE_SMOOTH), x, y, null);
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
/**
* 在底图上画二维码
*
* @param x 二维码中心坐标点
* @param y 二维码中心坐标点
*/
public ImageBuilder withQRCode(String contents, int x, int y, int width) {
try {
QRCodeWriter writer = new QRCodeWriter();
// 生成二维码数据点
BitMatrix matrix = writer.encode(contents, BarcodeFormat.QR_CODE, width, width);
int matrixWidth = matrix.getWidth();
// 计算二维码的原点(留2个像素作为边框)
int p = getMatrixOriginPoint(matrix, matrixWidth) - 2;
int aw = matrixWidth / 2 - p;
// 填充二维码背景色
graphics.setColor(Color.WHITE);
graphics.fillRect(x - aw, y - aw, aw * 2, aw * 2);
// 填充二维码
graphics.setColor(Color.BLACK);
for (int i = 0; i < matrixWidth; i++) {
for (int j = 0; j < matrixWidth; j++) {
if (matrix.get(i, j)) {
graphics.fillRect(x - aw + i - p, y - aw + j - p, 1, 1);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
/**
* 获取二维码原点
*/
private int getMatrixOriginPoint(BitMatrix matrix, int matrixWidth) {
for (int i = 0; i < matrixWidth; i++) {
for (int j = 0; j < matrixWidth; j++) {
if (matrix.get(i, j)) {
return i;
}
}
}
return 0;
}
/**
* 在底图上画一个已有的图片
*
* @param width 在底图上要画的图片宽度
* @param height 在底图上要画的图片高度
*/
public ImageBuilder withImage(BufferedImage material, int x, int y, int width, int height) {
graphics.drawImage(material, x, y, width, height, null);
return this;
}
/**
* 在底图上画一个已有的图片
*
* 使用小图片默认
*
*/
public ImageBuilder withImage(BufferedImage material, int x, int y) throws IOException {
return withImage(material, x, y, material.getWidth(), material.getHeight());
}
/**
* 生成图片
*/
public BufferedImage build() {
graphics.dispose();
return bgImage;
}
/**
* 生成本地图片
*
* @param formatName 生成的文件格式名称(例如:jpg、png),要与背景图的格式保持一致
* @param output 生成的图片
*/
public boolean writeTo(String formatName, File output) throws IOException {
return ImageIO.write(bgImage, formatName, output);
}
}
示例
public class ImageBuilderTest {
public static void main(String[] args) {
String inputPath = "C:\\Users\\scott\\Pictures\\01.png";
String outputPath = "C:\\Users\\scott\\Pictures\\01_out.png";
try {
BufferedImage bgImg = ImageIO.read(new File(inputPath));
ImageBuilder builder = new ImageBuilder(bgImg);
String text = "我是共产主义接班人";
builder.withText(new Font("黑体", Font.BOLD, 20), Color.black, 40, 40, text);
builder.build();
ImageIO.write(bgImg, "png", new File(outputPath));
System.out.println("done");
} catch (IOException e) {
e.printStackTrace();
}
}
}