Java 版 "国庆头像生成器":8 年老开发的实用小工具
作为一名摸爬滚打 8 年的 Java 开发,平时要么跟分布式、微服务打交道,要么陷在业务逻辑的 "泥潭" 里,偶尔做点轻量级的小工具反而觉得解压。眼看国庆要到,大家都在换国庆主题头像,干脆用 Java 写个 "国庆头像生成器"------ 不用依赖复杂框架,JDK 自带的 API 就能搞定,还能灵活调整国庆元素,比网上的在线工具更可控。
一、需求拆解:国庆头像要啥核心功能?
先想清楚用户(其实就是自己和同事)的核心需求:
- 基础功能:给原始头像叠加 "国庆元素"(国旗边框、五角星、"国庆快乐" 文字);
- 灵活性:元素大小、位置可调整,避免遮挡人脸;
- 易用性:支持常见图片格式(JPG/PNG),生成后直接保存到本地;
- 轻量无依赖 :只靠 JDK 自带的
java.awt
和javax.imageio
,不用引第三方包。
二、技术选型:老开发的 "极简主义"
既然是小工具,就别搞花里胡哨的:
- 图形处理核心:
BufferedImage
(Java 2D 核心类,处理像素级操作); - 图片读写:
ImageIO
(处理图片文件的读取和保存,支持主流格式); - 图形绘制:
Graphics2D
(比基础的Graphics
多了抗锯齿、旋转等高级功能,让元素更细腻)。
为啥不用 OpenCV 之类的?没必要 ------ 国庆头像不需要复杂的图像识别,JDK 原生 API 完全够用,还能避免 "为了拧螺丝带扳手" 的冗余。
三、核心代码实现:一步步造轮子
1. 第一步:读取原始头像
先写个工具方法读取本地图片,处理常见异常(文件不存在、格式不支持):
java
/**
* 读取本地图片为 BufferedImage
* @param imgPath 图片路径(如 "D:/avatar.jpg")
* @return BufferedImage 对象
*/
public static BufferedImage readImage(String imgPath) {
File imgFile = new File(imgPath);
if (!imgFile.exists()) {
throw new RuntimeException("图片文件不存在:" + imgPath);
}
try {
BufferedImage image = ImageIO.read(imgFile);
// 统一缩放为正方形(避免头像变形,这里设 400x400,可调整)
return resizeImage(image, 400, 400);
} catch (IOException e) {
throw new RuntimeException("读取图片失败:" + e.getMessage());
}
}
/**
* 图片缩放(保持比例,避免变形)
*/
private static BufferedImage resizeImage(BufferedImage source, int targetWidth, int targetHeight) {
BufferedImage resized = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = resized.createGraphics();
// 关键:开启抗锯齿,让缩放后的图片不模糊
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.drawImage(source, 0, 0, targetWidth, targetHeight, null);
g2d.dispose();
return resized;
}
2. 第二步:叠加国庆元素(核心逻辑)
这一步是重点,我们要加三个核心元素:红色边框、黄色五角星、"国庆快乐" 文字。
(1)画红色国庆边框
边框宽度设为 20 像素,颜色用国旗红(RGB:238, 35, 35),直接在头像外层画矩形:
arduino
/**
* 绘制国庆红色边框
* @param image 原始头像
* @param borderWidth 边框宽度
*/
private static void drawNationalBorder(BufferedImage image, int borderWidth) {
Graphics2D g2d = image.createGraphics();
// 1. 设置边框颜色(国旗红)
Color borderColor = new Color(238, 35, 35);
g2d.setColor(borderColor);
// 2. 画边框(注意:x/y 从 0 开始,宽高要减去 1,避免超出图片范围)
int width = image.getWidth();
int height = image.getHeight();
g2d.fillRect(0, 0, width, borderWidth); // 上边框
g2d.fillRect(0, height - borderWidth, width, borderWidth); // 下边框
g2d.fillRect(0, borderWidth, borderWidth, height - 2 * borderWidth); // 左边框
g2d.fillRect(width - borderWidth, borderWidth, borderWidth, height - 2 * borderWidth); // 右边框
g2d.dispose();
}
(2)画黄色五角星
五角星的难点是计算五个顶点的坐标,这里用 "外接圆半径" 来控制大小,位置放在右上角(避免挡脸):
scss
/**
* 绘制黄色五角星
* @param image 头像图片
* @param radius 五角星外接圆半径
* @param x 五角星左上角 x 坐标
* @param y 五角星左上角 y 坐标
*/
private static void drawFivePointedStar(BufferedImage image, int radius, int x, int y) {
Graphics2D g2d = image.createGraphics();
// 1. 计算五角星五个顶点的坐标(数学逻辑:每个顶点间隔 72°)
int[] xPoints = new int[5];
int[] yPoints = new int[5];
for (int i = 0; i < 5; i++) {
// 角度计算:从 90° 开始(让五角星的一个角朝上),每次加 72°
double angle = Math.toRadians(90 + i * 72);
xPoints[i] = x + (int) (radius * Math.cos(angle));
yPoints[i] = y + (int) (radius * Math.sin(angle));
// 调整内凹点(五角星的"缺口"):内圆半径是外圆的 0.382 倍(黄金比例)
angle += Math.toRadians(36);
xPoints[i] = (xPoints[i] + x + (int) (radius * 0.382 * Math.cos(angle))) / 2;
yPoints[i] = (yPoints[i] + y + (int) (radius * 0.382 * Math.sin(angle))) / 2;
}
// 2. 绘制并填充五角星(国旗黄:255, 215, 0)
Color starColor = new Color(255, 215, 0);
g2d.setColor(starColor);
g2d.fillPolygon(xPoints, yPoints, 5);
g2d.dispose();
}
(3)加 "国庆快乐" 文字
文字放在底部居中,字体用黑体,颜色和五角星一致,避免突兀:
ini
/**
* 添加"国庆快乐"文字
* @param image 头像图片
* @param fontSize 字体大小
*/
private static void drawNationalText(BufferedImage image, int fontSize) {
Graphics2D g2d = image.createGraphics();
// 1. 设置文字样式(黑体、加粗,抗锯齿)
Font font = new Font("黑体", Font.BOLD, fontSize);
g2d.setFont(font);
g2d.setColor(new Color(255, 215, 0)); // 同五角星颜色
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
// 2. 计算文字位置(底部居中)
String text = "国庆快乐";
FontMetrics metrics = g2d.getFontMetrics();
int textWidth = metrics.stringWidth(text);
int textHeight = metrics.getAscent();
int x = (image.getWidth() - textWidth) / 2;
int y = image.getHeight() - 20; // 距离底部 20 像素
// 3. 绘制文字(加个白色阴影,更醒目)
g2d.drawString(text, x + 1, y + 1); // 阴影偏移 1 像素
g2d.setColor(new Color(255, 215, 0));
g2d.drawString(text, x, y);
g2d.dispose();
}
3. 第三步:保存生成的头像
最后写个方法把处理后的 BufferedImage
保存到本地,支持 JPG/PNG 格式:
typescript
/**
* 保存图片到本地
* @param image 处理后的头像
* @param savePath 保存路径(如 "D:/national-avatar.jpg")
* @param format 图片格式("jpg" 或 "png")
*/
public static void saveImage(BufferedImage image, String savePath, String format) {
if (!"jpg".equalsIgnoreCase(format) && !"png".equalsIgnoreCase(format)) {
throw new RuntimeException("不支持的图片格式:" + format);
}
try {
File saveFile = new File(savePath);
// 若父目录不存在,自动创建
if (!saveFile.getParentFile().exists()) {
saveFile.getParentFile().mkdirs();
}
ImageIO.write(image, format, saveFile);
System.out.println("头像生成成功!保存路径:" + savePath);
} catch (IOException e) {
throw new RuntimeException("保存图片失败:" + e.getMessage());
}
}
四、测试:跑起来看看效果
写个 main
方法测试一下,流程很简单:读原图 → 加元素 → 保存:
scss
public static void main(String[] args) {
// 1. 读取原始头像(替换成你的头像路径)
BufferedImage avatar = readImage("D:/my-avatar.png");
// 2. 叠加国庆元素(参数可根据需求调整)
drawNationalBorder(avatar, 20); // 20 像素红色边框
drawFivePointedStar(avatar, 30, avatar.getWidth() - 70, 30); // 右上角五角星(半径 30)
drawNationalText(avatar, 30); // 30 号字体的"国庆快乐"
// 3. 保存生成的国庆头像
saveImage(avatar, "D:/national-avatar-2024.jpg", "jpg");
}
运行后打开保存的图片,效果应该是:400x400 的正方形头像,红色边框,右上角黄色五角星,底部居中 "国庆快乐" 文字 ------ 没有模糊、没有遮挡,比网上随便找的工具更合心意。
五、老开发的优化思路:让工具更实用
作为 8 年开发,写完基础功能还得想优化点:
- 批量生成:加个遍历文件夹的逻辑,一次性给团队所有人生成头像;
- Web 化:集成到 Spring Boot,加个简单的前端页面(用 Vue/React 写个上传组件),做成团队内部的小网站;
- 更多元素 :扩展支持灯笼、彩带等国庆元素(提前准备好 PNG 素材,用
g2d.drawImage
叠加); - 人脸避让:如果想更精准,可集成百度 AI 的人脸检测 API,根据人脸位置调整元素位置(不过对小工具来说有点超标,按需选择)。
六、总结:小工具里的 "开发思维"
其实写这个头像生成器,核心不是技术多高深,而是 "从需求出发,用最简单的方案解决问题"------ 这也是老开发多年的习惯:不盲目追求新技术,而是根据场景选最合适的工具。
如果你的需求更复杂(比如加动态效果、支持更多格式),可以留言告诉我,咱们再一起优化;如果只是想给同事们快速生成国庆头像,上面的代码直接 copy 过去就能用,改改路径和参数就行~
最后祝大家国庆快乐,代码没 Bug,上线不加班!