最近在做一个需求;本来头像悬浮到背景图上的功能是由前端来做的,但是自己闲来没事,然后自己用java后端写了一个工具类,供以后使用,在这里写帖子分享出来,有需要的可以借用!仅供参考!
一、第一张头像合成到背景图固定的位置上
需要用户头像合成到固定位置的背景图上,然后昵称悬浮在头像下面;最后返回一个图片的地址
我们先在项目本地目录新建一个文件夹,存放背景图和头像图片,如图所示;

首先我们需要定义几个固定的参数(实际项目中参数从以上请求返回值中获取);
string bgfilename = "backgroud.png"; //背景图文件名
string avatarfilename = "autohotkey.svg"; //头像文件名
string nickname = "郑州小一"; //头像文件名
string outputfilename = "telegramphoto"; //输出文件名
// 本地文件存储路径 - 使用windows兼容的绝对路径格式
string basepath = "d:\\ideaprojects\\mahjong_java\\gameserver\\src\\core\\file\\";
// 确保路径格式正确
if (!basepath.endswith("\\")) {
basepath += "\\";
}然后我们调用图片合成方法传参:
//调用图像合成方法;接收合成后的图片完整路径 string imgurl = compositeimages.compositeimage(basepath,bgfilename,avatarfilename,nickname)
然后我们在赋值给接口返回参数即可;
以下是图片合成类的全部代码:
/**
* 合成图片工具类:将头像合成到背景图上
* @return 合成后的图片完整路径,失败返回null
*/
public class compositeimages{
/**
* 合成图片方法:将头像合成到背景图上
* @param bgfilename 背景图文件名(不含路径)
* @param avatarfilename 头像文件名(不含路径)
* @param outputfilename 输出文件名(不含路径)
* @return 合成后的图片完整路径,失败返回null
*/
public static string compositeimage(string basepath, string bgfilename, string avatarfilename, string nickname,string outputfilename) {
// 构建完整的文件路径
string bgimagepath = basepath + bgfilename;
string avatarimagepath = basepath + avatarfilename;
string tempoutputpath = basepath + "composite\\" + outputfilename + "_temp.png";
string outputpath = basepath + "composite\\" + outputfilename;
// 添加详细日志以调试路径问题
commlogd.info("尝试访问背景图片路径: " + bgimagepath);
file bgfile = new file(bgimagepath);
commlogd.info("背景图片文件存在: " + bgfile.exists() + ", 是文件: " + bgfile.isfile() + ", 可读: " + bgfile.canread());
// 验证输入图片是否存在
if (!imageutils.isimagevalid(bgimagepath)) {
commlogd.error("背景图片不存在或无效: " + bgimagepath);
return null;
}
commlogd.info("尝试访问头像图片路径: " + avatarimagepath);
file avatarfile = new file(avatarimagepath);
commlogd.info("头像图片文件存在: " + avatarfile.exists() + ", 是文件: " + avatarfile.isfile() + ", 可读: " + avatarfile.canread());
if (!imageutils.isimagevalid(avatarimagepath)) {
commlogd.error("头像图片不存在或无效: " + avatarimagepath);
return null;
}
int avatarx = 22; //头像x坐标
int avatary = 240; //头像y坐标
int avatarwidth = 100; //头像宽度
int avatarheight = 100; //头像高度
// 执行头像合成到临时文件
boolean success = imageutils.compositeavatar(
bgimagepath,
avatarimagepath,
tempoutputpath,
avatarx,
avatary,
avatarwidth,
avatarheight,
"png" // 固定输出为png格式,支持透明
);
if (!success) {
commlogd.error("头像合成失败");
// 清理临时文件
file tempfile = new file(tempoutputpath);
if (tempfile.exists()) {
tempfile.delete();
}
return null;
}
// 如果提供了昵称,则绘制昵称
if (nickname != null && !nickname.isempty()) {
// 计算昵称绘制位置(头像下方居中)
int textx = avatarx; // 稍微偏移一点
int texty = avatary + avatarheight + 26; // 头像下方,加上字体大小
int fontsize = 24;
java.awt.color fontcolor = new java.awt.color(255, 255, 255); // 白色文字
// 绘制昵称
success = imageutils.drawtextonimage(
tempoutputpath,
outputpath,
nickname,
textx,
texty,
fontsize,
fontcolor,
"png"
);
// 清理临时文件
file tempfile = new file(tempoutputpath);
if (tempfile.exists()) {
tempfile.delete();
}
} else {
// 如果没有昵称,直接将临时文件重命名为输出文件
file tempfile = new file(tempoutputpath);
file outputfile = new file(outputpath);
// 确保输出目录存在
file parentdir = outputfile.getparentfile();
if (parentdir != null && !parentdir.exists()) {
parentdir.mkdirs();
}
// 先删除可能存在的输出文件
if (outputfile.exists()) {
outputfile.delete();
}
success = tempfile.renameto(outputfile);
}
if (success) {
commlogd.info("图片合成成功: " + outputpath);
return outputpath;
} else {
commlogd.error("图片合成失败");
return null;
}
}
/**
* 将两个头像合成到背景图上的方法,并在头像下方添加昵称
* @param bgfilename 背景图文件名(不含路径)
* @param avatar1filename 第一个头像文件名(不含路径)
* @param nickname1 第一个头像对应的用户昵称
* @param avatar2filename 第二个头像文件名(不含路径)
* @param nickname2 第二个头像对应的用户昵称
* @param outputfilename 输出文件名(不含路径)
* @return 合成后的图片完整路径,失败返回null
*/
public static string compositeimage2(string basepath, string bgfilename, string avatar1filename, string nickname1, string avatar2filename, string nickname2,
string outputfilename) {
// 构建完整的文件路径
string bgimagepath = basepath + bgfilename;
string avatar1imagepath = basepath + avatar1filename;
string avatar2imagepath = basepath + avatar2filename;
string tempoutputpath1 = basepath + "composite\\" + outputfilename + "_temp1.png";
string tempoutputpath2 = basepath + "composite\\" + outputfilename + "_temp2.png";
string outputpath = basepath + "composite\\" + outputfilename;
// 添加详细日志以调试路径问题
commlogd.info("尝试访问背景图片路径: " + bgimagepath);
file bgfile = new file(bgimagepath);
commlogd.info("背景图片文件存在: " + bgfile.exists() + ", 是文件: " + bgfile.isfile() + ", 可读: " + bgfile.canread());
// 验证输入图片是否存在
if (!imageutils.isimagevalid(bgimagepath)) {
commlogd.error("背景图片不存在或无效: " + bgimagepath);
return null;
}
// 验证第一个头像
commlogd.info("尝试访问第一个头像图片路径: " + avatar1imagepath);
file avatar1file = new file(avatar1imagepath);
commlogd.info("第一个头像图片文件存在: " + avatar1file.exists() + ", 是文件: " + avatar1file.isfile() + ", 可读: " + avatar1file.canread());
if (!imageutils.isimagevalid(avatar1imagepath)) {
commlogd.error("第一个头像图片不存在或无效: " + avatar1imagepath);
return null;
}
// 验证第二个头像
commlogd.info("尝试访问第二个头像图片路径: " + avatar2imagepath);
file avatar2file = new file(avatar2imagepath);
commlogd.info("第二个头像图片文件存在: " + avatar2file.exists() + ", 是文件: " + avatar2file.isfile() + ", 可读: " + avatar2file.canread());
if (!imageutils.isimagevalid(avatar2imagepath)) {
commlogd.error("第二个头像图片不存在或无效: " + avatar2imagepath);
return null;
}
int avatar1x = 22; //头像x坐标
int avatar1y = 240; //头像y坐标
int avatar1width = 100; //头像宽度
int avatar1height = 100; //头像高度
// 先合成第一个头像到背景图,保存为临时文件
boolean successfirst = imageutils.compositeavatar(
bgimagepath,
avatar1imagepath,
tempoutputpath1,
avatar1x,
avatar1y,
avatar1width,
avatar1height,
"png" // 固定输出为png格式,支持透明
);
if (!successfirst) {
commlogd.error("合成第一个头像失败");
return null;
}
int avatar2x = 600; //头像x坐标
int avatar2y = 240; //头像y坐标
int avatar2width = 100; //头像宽度
int avatar2height = 100; //头像高度
// 再将第二个头像合成到临时文件上,得到最终结果
boolean successsecond = imageutils.compositeavatar(
tempoutputpath1,
avatar2imagepath,
tempoutputpath2,
avatar2x,
avatar2y,
avatar2width,
avatar2height,
"png"
);
// 删除第一个临时文件
file tempfile1 = new file(tempoutputpath1);
if (tempfile1.exists()) {
tempfile1.delete();
}
if (!successsecond) {
commlogd.info("两个头像合成成功: " + outputpath);
// 删除第二个临时文件
file tempfile2 = new file(tempoutputpath2);
if (tempfile2.exists()) {
tempfile2.delete();
}
return null;
}
// 确保输出目录存在
file outputfile = new file(outputpath);
file parentdir = outputfile.getparentfile();
if (parentdir != null && !parentdir.exists()) {
parentdir.mkdirs();
}
// 绘制昵称到图片上
boolean successdrawtext = true;
string currentpath = tempoutputpath2;
int fontsize = 24;
java.awt.color fontcolor = new java.awt.color(255, 255, 255); // 白色文字
// 绘制第一个昵称(如果有)
if (nickname1 != null && !nickname1.isempty()) {
int text1x = avatar1x; // 稍微偏移一点
int text1y = avatar1y + avatar1height + 26; // 头像下方,加上字体大小
successdrawtext = imageutils.drawtextonimage(
currentpath,
outputpath,
nickname1,
text1x,
text1y,
fontsize,
fontcolor,
"png"
);
if (!successdrawtext) {
commlogd.error("绘制第一个昵称失败");
// 删除临时文件
file tempfile2 = new file(tempoutputpath2);
if (tempfile2.exists()) {
tempfile2.delete();
}
return null;
}
currentpath = outputpath;
}
// 绘制第二个昵称(如果有)
if (nickname2 != null && !nickname2.isempty()) {
int text2x = avatar2x; // 稍微偏移一点
int text2y = avatar2y + avatar2height + 26; // 头像下方,加上字体大小
string nextpath = (currentpath.equals(tempoutputpath2)) ? outputpath : outputpath + "_final.png";
successdrawtext = imageutils.drawtextonimage(
currentpath,
nextpath,
nickname2,
text2x,
text2y,
fontsize,
fontcolor,
"png"
);
if (!successdrawtext) {
commlogd.error("绘制第二个昵称失败");
// 清理文件
file tempfile2 = new file(tempoutputpath2);
if (tempfile2.exists()) {
tempfile2.delete();
}
if (currentpath.equals(outputpath)) {
file outputfileobj = new file(outputpath);
if (outputfileobj.exists()) {
outputfileobj.delete();
}
}
return null;
}
// 如果创建了最终文件,需要重命名
if (nextpath.equals(outputpath + "_final.png")) {
file finalfile = new file(nextpath);
file targetfile = new file(outputpath);
if (targetfile.exists()) {
targetfile.delete();
}
finalfile.renameto(targetfile);
}
} else if (currentpath.equals(tempoutputpath2)) {
// 如果没有第二个昵称,直接将临时文件重命名为输出文件
file tempfile2 = new file(tempoutputpath2);
if (outputfile.exists()) {
outputfile.delete();
}
tempfile2.renameto(outputfile);
}
// 删除可能存在的临时文件
file tempfile2 = new file(tempoutputpath2);
if (tempfile2.exists()) {
tempfile2.delete();
}
commlogd.info("两个头像和昵称合成成功: " + outputpath);
return outputpath;
}
/**
* 将三个头像合成到背景图上的方法
* @param basepath 基础路径
* @param bgfilename 背景图文件名(不含路径)
* @param avatar1filename 第一个头像文件名(不含路径)
* @param nickname1 第一个头像对应的用户昵称
* @param avatar2filename 第二个头像文件名(不含路径)
* @param nickname2 第二个头像对应的用户昵称
* @param avatar3filename 第三个头像文件名(不含路径)
* @param nickname3 第三个头像对应的用户昵称
* @param outputfilename 输出文件名(不含路径)
* @return 合成后的图片完整路径,失败返回null
*/
public static string compositeimage3(string basepath, string bgfilename, string avatar1filename,string nickname1,
string avatar2filename,string nickname2, string avatar3filename,string nickname3, string outputfilename) {
// 构建完整的文件路径
string bgimagepath = basepath + bgfilename;
string avatar1imagepath = basepath + avatar1filename;
string avatar2imagepath = basepath + avatar2filename;
string avatar3imagepath = basepath + avatar3filename;
string tempoutputpath1 = basepath + "composite\\" + outputfilename + "_temp1.png";
string tempoutputpath2 = basepath + "composite\\" + outputfilename + "_temp2.png";
string tempoutputpath3 = basepath + "composite\\" + outputfilename + "_temp3.png";
string outputpath = basepath + "composite\\" + outputfilename;
// 添加详细日志以调试路径问题
commlogd.info("尝试访问背景图片路径: " + bgimagepath);
file bgfile = new file(bgimagepath);
commlogd.info("背景图片文件存在: " + bgfile.exists() + ", 是文件: " + bgfile.isfile() + ", 可读: " + bgfile.canread());
// 验证输入图片是否存在
if (!imageutils.isimagevalid(bgimagepath)) {
commlogd.error("背景图片不存在或无效: " + bgimagepath);
return null;
}
// 验证第一个头像
commlogd.info("尝试访问第一个头像图片路径: " + avatar1imagepath);
file avatar1file = new file(avatar1imagepath);
commlogd.info("第一个头像图片文件存在: " + avatar1file.exists() + ", 是文件: " + avatar1file.isfile() + ", 可读: " + avatar1file.canread());
if (!imageutils.isimagevalid(avatar1imagepath)) {
commlogd.error("第一个头像图片不存在或无效: " + avatar1imagepath);
return null;
}
// 验证第二个头像
commlogd.info("尝试访问第二个头像图片路径: " + avatar2imagepath);
file avatar2file = new file(avatar2imagepath);
commlogd.info("第二个头像图片文件存在: " + avatar2file.exists() + ", 是文件: " + avatar2file.isfile() + ", 可读: " + avatar2file.canread());
if (!imageutils.isimagevalid(avatar2imagepath)) {
commlogd.error("第二个头像图片不存在或无效: " + avatar2imagepath);
return null;
}
// 验证第三个头像
commlogd.info("尝试访问第三个头像图片路径: " + avatar3imagepath);
file avatar3file = new file(avatar3imagepath);
commlogd.info("第三个头像图片文件存在: " + avatar3file.exists() + ", 是文件: " + avatar3file.isfile() + ", 可读: " + avatar3file.canread());
if (!imageutils.isimagevalid(avatar3imagepath)) {
commlogd.error("第三个头像图片不存在或无效: " + avatar3imagepath);
return null;
}
// 第一个头像的位置和大小
int avatar1x = 22; // 头像x坐标
int avatar1y = 240; // 头像y坐标
int avatar1width = 100; // 头像宽度
int avatar1height = 100; // 头像高度
// 先合成第一个头像到背景图,保存为临时文件1
boolean successfirst = imageutils.compositeavatar(
bgimagepath,
avatar1imagepath,
tempoutputpath1,
avatar1x,
avatar1y,
avatar1width,
avatar1height,
"png" // 固定输出为png格式,支持透明
);
if (!successfirst) {
commlogd.error("合成第一个头像失败");
return null;
}
// 第二个头像的位置和大小
int avatar2x = 600; // 头像x坐标
int avatar2y = 240; // 头像y坐标
int avatar2width = 100; // 头像宽度
int avatar2height = 100; // 头像高度
// 再将第二个头像合成到临时文件1上,得到临时文件2
boolean successsecond = imageutils.compositeavatar(
tempoutputpath1,
avatar2imagepath,
tempoutputpath2,
avatar2x,
avatar2y,
avatar2width,
avatar2height,
"png"
);
if (!successsecond) {
commlogd.error("合成第二个头像失败");
// 删除已生成的临时文件
file tempfile1 = new file(tempoutputpath1);
if (tempfile1.exists()) {
tempfile1.delete();
}
return null;
}
// 第三个头像的位置和大小
int avatar3x = 310; // 头像x坐标
int avatar3y = 450; // 头像y坐标
int avatar3width = 100; // 头像宽度
int avatar3height = 100; // 头像高度
// 将第三个头像合成到临时文件2上,得到临时文件3
boolean successthird = imageutils.compositeavatar(
tempoutputpath2,
avatar3imagepath,
tempoutputpath3,
avatar3x,
avatar3y,
avatar3width,
avatar3height,
"png"
);
// 删除临时文件1和2
file tempfile1 = new file(tempoutputpath1);
if (tempfile1.exists()) {
tempfile1.delete();
}
file tempfile2 = new file(tempoutputpath2);
if (tempfile2.exists()) {
tempfile2.delete();
}
if (!successthird) {
commlogd.error("合成第三个头像失败");
// 删除临时文件3
file tempfile3 = new file(tempoutputpath3);
if (tempfile3.exists()) {
tempfile3.delete();
}
return null;
}
// 确保输出目录存在
file outputfile = new file(outputpath);
file parentdir = outputfile.getparentfile();
if (parentdir != null && !parentdir.exists()) {
parentdir.mkdirs();
}
// 绘制昵称到图片上
boolean successdrawtext = true;
string currentpath = tempoutputpath3;
int fontsize = 24;
java.awt.color fontcolor = new java.awt.color(255, 255, 255); // 白色文字
// 绘制第一个昵称(如果有)
if (nickname1 != null && !nickname1.isempty()) {
int text1x = avatar1x; // 稍微偏移一点
int text1y = avatar1y + avatar1height + 26; // 头像下方,加上字体大小
string nextpath = (currentpath.equals(tempoutputpath3)) ? outputpath : outputpath + "_final1.png";
successdrawtext = imageutils.drawtextonimage(
currentpath,
nextpath,
nickname1,
text1x,
text1y,
fontsize,
fontcolor,
"png"
);
if (!successdrawtext) {
commlogd.error("绘制第一个昵称失败");
// 删除临时文件
file tempfile3 = new file(tempoutputpath3);
if (tempfile3.exists()) {
tempfile3.delete();
}
return null;
}
currentpath = nextpath;
}
// 绘制第二个昵称(如果有)
if (nickname2 != null && !nickname2.isempty()) {
int text2x = avatar2x; // 稍微偏移一点
int text2y = avatar2y + avatar2height + 26; // 头像下方,加上字体大小
string nextpath = (currentpath.equals(tempoutputpath3)) ? outputpath :
(currentpath.equals(outputpath)) ? outputpath + "_final2.png" : outputpath + "_final3.png";
successdrawtext = imageutils.drawtextonimage(
currentpath,
nextpath,
nickname2,
text2x,
text2y,
fontsize,
fontcolor,
"png"
);
if (!successdrawtext) {
commlogd.error("绘制第二个昵称失败");
// 清理文件
file tempfile3 = new file(tempoutputpath3);
if (tempfile3.exists()) {
tempfile3.delete();
}
if (currentpath.equals(outputpath)) {
file outputfileobj = new file(outputpath);
if (outputfileobj.exists()) {
outputfileobj.delete();
}
}
return null;
}
currentpath = nextpath;
}
// 绘制第三个昵称(如果有)
if (nickname3 != null && !nickname3.isempty()) {
int text3x = avatar3x; // 稍微偏移一点
int text3y = avatar3y + avatar3height + 26; // 头像下方,加上字体大小
string nextpath;
if (currentpath.equals(tempoutputpath3)) {
nextpath = outputpath;
} else if (currentpath.equals(outputpath)) {
nextpath = outputpath + "_final4.png";
} else {
nextpath = outputpath + "_final5.png";
}
successdrawtext = imageutils.drawtextonimage(
currentpath,
nextpath,
nickname3,
text3x,
text3y,
fontsize,
fontcolor,
"png"
);
if (!successdrawtext) {
commlogd.error("绘制第三个昵称失败");
// 清理文件
file tempfile3 = new file(tempoutputpath3);
if (tempfile3.exists()) {
tempfile3.delete();
}
if (currentpath.equals(outputpath) || currentpath.contains("_final")) {
file outputfileobj = new file(currentpath);
if (outputfileobj.exists()) {
outputfileobj.delete();
}
}
return null;
}
currentpath = nextpath;
} else if (currentpath.equals(tempoutputpath3)) {
// 如果没有昵称,直接将临时文件重命名为输出文件
file tempfile3 = new file(tempoutputpath3);
if (outputfile.exists()) {
outputfile.delete();
}
tempfile3.renameto(outputfile);
}
// 处理可能生成的中间文件,确保最终文件名正确
if (currentpath.contains("_final")) {
file finalfile = new file(currentpath);
file targetfile = new file(outputpath);
if (targetfile.exists()) {
targetfile.delete();
}
finalfile.renameto(targetfile);
}
// 删除可能存在的临时文件
file tempfile3 = new file(tempoutputpath3);
if (tempfile3.exists()) {
tempfile3.delete();
}
// 删除可能存在的中间文件
for (int i = 1; i <= 5; i++) {
file intermediatefile = new file(outputpath + "_final" + i + ".png");
if (intermediatefile.exists()) {
intermediatefile.delete();
}
}
commlogd.info("三个头像和昵称合成成功: " + outputpath);
return outputpath;
}
/**
* 将四个头像合成到背景图上的方法,并在头像下方添加昵称
* @param basepath 基础路径
* @param bgfilename 背景图文件名(不含路径)
* @param avatar1filename 第一个头像文件名(不含路径)
* @param nickname1 第一个头像对应的用户昵称
* @param avatar2filename 第二个头像文件名(不含路径)
* @param nickname2 第二个头像对应的用户昵称
* @param avatar3filename 第三个头像文件名(不含路径)
* @param nickname3 第三个头像对应的用户昵称
* @param avatar4filename 第四个头像文件名(不含路径)
* @param nickname4 第四个头像对应的用户昵称
* @param outputfilename 输出文件名(不含路径)
* @return 合成后的图片完整路径,失败返回null
*/
public static string compositeimage4(string basepath, string bgfilename, string avatar1filename,string nickname1,
string avatar2filename,string nickname2, string avatar3filename, string nickname3, string avatar4filename,
string nickname4,string outputfilename) {
// 构建完整的文件路径
string bgimagepath = basepath + bgfilename;
string avatar1imagepath = basepath + avatar1filename;
string avatar2imagepath = basepath + avatar2filename;
string avatar3imagepath = basepath + avatar3filename;
string avatar4imagepath = basepath + avatar4filename;
string tempoutputpath1 = basepath + "composite\\" + outputfilename + "_temp1.png";
string tempoutputpath2 = basepath + "composite\\" + outputfilename + "_temp2.png";
string tempoutputpath3 = basepath + "composite\\" + outputfilename + "_temp3.png";
string tempoutputpath4 = basepath + "composite\\" + outputfilename + "_temp4.png";
string outputpath = basepath + "composite\\" + outputfilename;
// 添加详细日志以调试路径问题
commlogd.info("尝试访问背景图片路径: " + bgimagepath);
file bgfile = new file(bgimagepath);
commlogd.info("背景图片文件存在: " + bgfile.exists() + ", 是文件: " + bgfile.isfile() + ", 可读: " + bgfile.canread());
// 验证输入图片是否存在
if (!imageutils.isimagevalid(bgimagepath)) {
commlogd.error("背景图片不存在或无效: " + bgimagepath);
return null;
}
// 验证第一个头像
commlogd.info("尝试访问第一个头像图片路径: " + avatar1imagepath);
file avatar1file = new file(avatar1imagepath);
commlogd.info("第一个头像图片文件存在: " + avatar1file.exists() + ", 是文件: " + avatar1file.isfile() + ", 可读: " + avatar1file.canread());
if (!imageutils.isimagevalid(avatar1imagepath)) {
commlogd.error("第一个头像图片不存在或无效: " + avatar1imagepath);
return null;
}
// 验证第二个头像
commlogd.info("尝试访问第二个头像图片路径: " + avatar2imagepath);
file avatar2file = new file(avatar2imagepath);
commlogd.info("第二个头像图片文件存在: " + avatar2file.exists() + ", 是文件: " + avatar2file.isfile() + ", 可读: " + avatar2file.canread());
if (!imageutils.isimagevalid(avatar2imagepath)) {
commlogd.error("第二个头像图片不存在或无效: " + avatar2imagepath);
return null;
}
// 验证第三个头像
commlogd.info("尝试访问第三个头像图片路径: " + avatar3imagepath);
file avatar3file = new file(avatar3imagepath);
commlogd.info("第三个头像图片文件存在: " + avatar3file.exists() + ", 是文件: " + avatar3file.isfile() + ", 可读: " + avatar3file.canread());
if (!imageutils.isimagevalid(avatar3imagepath)) {
commlogd.error("第三个头像图片不存在或无效: " + avatar3imagepath);
return null;
}
// 验证第四个头像
commlogd.info("尝试访问第四个头像图片路径: " + avatar4imagepath);
file avatar4file = new file(avatar4imagepath);
commlogd.info("第四个头像图片文件存在: " + avatar4file.exists() + ", 是文件: " + avatar4file.isfile() + ", 可读: " + avatar4file.canread());
if (!imageutils.isimagevalid(avatar4imagepath)) {
commlogd.error("第四个头像图片不存在或无效: " + avatar4imagepath);
return null;
}
// 第一个头像的位置和大小
int avatar1x = 22; // 头像x坐标
int avatar1y = 240; // 头像y坐标
int avatar1width = 100; // 头像宽度
int avatar1height = 100; // 头像高度
// 先合成第一个头像到背景图,保存为临时文件1
boolean successfirst = imageutils.compositeavatar(
bgimagepath,
avatar1imagepath,
tempoutputpath1,
avatar1x,
avatar1y,
avatar1width,
avatar1height,
"png" // 固定输出为png格式,支持透明
);
if (!successfirst) {
commlogd.error("合成第一个头像失败");
return null;
}
// 第二个头像的位置和大小
int avatar2x = 600; // 头像x坐标
int avatar2y = 240; // 头像y坐标
int avatar2width = 100; // 头像宽度
int avatar2height = 100; // 头像高度
// 再将第二个头像合成到临时文件1上,得到临时文件2
boolean successsecond = imageutils.compositeavatar(
tempoutputpath1,
avatar2imagepath,
tempoutputpath2,
avatar2x,
avatar2y,
avatar2width,
avatar2height,
"png"
);
if (!successsecond) {
commlogd.error("合成第二个头像失败");
// 删除已生成的临时文件
file tempfile1 = new file(tempoutputpath1);
if (tempfile1.exists()) {
tempfile1.delete();
}
return null;
}
// 第三个头像的位置和大小
int avatar3x = 310; // 头像x坐标
int avatar3y = 450; // 头像y坐标
int avatar3width = 100; // 头像宽度
int avatar3height = 100; // 头像高度
// 将第三个头像合成到临时文件2上,得到临时文件3
boolean successthird = imageutils.compositeavatar(
tempoutputpath2,
avatar3imagepath,
tempoutputpath3,
avatar3x,
avatar3y,
avatar3width,
avatar3height,
"png"
);
// 删除临时文件1和2
file tempfile1 = new file(tempoutputpath1);
if (tempfile1.exists()) {
tempfile1.delete();
}
file tempfile2 = new file(tempoutputpath2);
if (tempfile2.exists()) {
tempfile2.delete();
}
if (!successthird) {
commlogd.error("合成第三个头像失败");
// 删除临时文件3
file tempfile3 = new file(tempoutputpath3);
if (tempfile3.exists()) {
tempfile3.delete();
}
return null;
}
// 第四个头像的位置和大小(设置为顶部居中)
int avatar4x = 310; // 头像x坐标
int avatar4y = 35; // 头像y坐标
int avatar4width = 100; // 头像宽度
int avatar4height = 100; // 头像高度
// 将第四个头像合成到临时文件3上,得到临时文件4
boolean successfourth = imageutils.compositeavatar(
tempoutputpath3,
avatar4imagepath,
tempoutputpath4,
avatar4x,
avatar4y,
avatar4width,
avatar4height,
"png"
);
// 删除临时文件3
file tempfile3 = new file(tempoutputpath3);
if (tempfile3.exists()) {
tempfile3.delete();
}
if (!successfourth) {
commlogd.error("合成第四个头像失败");
// 删除临时文件4
file tempfile4 = new file(tempoutputpath4);
if (tempfile4.exists()) {
tempfile4.delete();
}
return null;
}
// 确保输出目录存在
file outputfile = new file(outputpath);
file parentdir = outputfile.getparentfile();
if (parentdir != null && !parentdir.exists()) {
parentdir.mkdirs();
}
// 绘制昵称到图片上
boolean successdrawtext = true;
string currentpath = tempoutputpath4;
int fontsize = 24;
java.awt.color fontcolor = new java.awt.color(255, 255, 255); // 白色文字
// 绘制第一个昵称(如果有)
if (nickname1 != null && !nickname1.isempty()) {
int text1x = avatar1x; // 稍微偏移一点
int text1y = avatar1y + avatar1height + 26; // 头像下方,加上字体大小
string nextpath = (currentpath.equals(tempoutputpath4)) ? outputpath : outputpath + "_final1.png";
successdrawtext = imageutils.drawtextonimage(
currentpath,
nextpath,
nickname1,
text1x,
text1y,
fontsize,
fontcolor,
"png"
);
if (!successdrawtext) {
commlogd.error("绘制第一个昵称失败");
// 删除临时文件
file tempfile4 = new file(tempoutputpath4);
if (tempfile4.exists()) {
tempfile4.delete();
}
return null;
}
currentpath = nextpath;
}
// 绘制第二个昵称(如果有)
if (nickname2 != null && !nickname2.isempty()) {
int text2x = avatar2x; // 稍微偏移一点
int text2y = avatar2y + avatar2height + 26; // 头像下方,加上字体大小
string nextpath = (currentpath.equals(tempoutputpath4)) ? outputpath :
(currentpath.equals(outputpath)) ? outputpath + "_final2.png" : outputpath + "_final3.png";
successdrawtext = imageutils.drawtextonimage(
currentpath,
nextpath,
nickname2,
text2x,
text2y,
fontsize,
fontcolor,
"png"
);
if (!successdrawtext) {
commlogd.error("绘制第二个昵称失败");
// 清理文件
file tempfile4 = new file(tempoutputpath4);
if (tempfile4.exists()) {
tempfile4.delete();
}
if (currentpath.equals(outputpath)) {
file outputfileobj = new file(outputpath);
if (outputfileobj.exists()) {
outputfileobj.delete();
}
}
return null;
}
currentpath = nextpath;
}
// 绘制第三个昵称(如果有)
if (nickname3 != null && !nickname3.isempty()) {
int text3x = avatar3x; // 稍微偏移一点
int text3y = avatar3y + avatar3height + 26; // 头像下方,加上字体大小
string nextpath;
if (currentpath.equals(tempoutputpath4)) {
nextpath = outputpath;
} else if (currentpath.equals(outputpath)) {
nextpath = outputpath + "_final4.png";
} else {
nextpath = outputpath + "_final5.png";
}
successdrawtext = imageutils.drawtextonimage(
currentpath,
nextpath,
nickname3,
text3x,
text3y,
fontsize,
fontcolor,
"png"
);
if (!successdrawtext) {
commlogd.error("绘制第三个昵称失败");
// 清理文件
file tempfile4 = new file(tempoutputpath4);
if (tempfile4.exists()) {
tempfile4.delete();
}
if (currentpath.equals(outputpath) || currentpath.contains("_final")) {
file outputfileobj = new file(currentpath);
if (outputfileobj.exists()) {
outputfileobj.delete();
}
}
return null;
}
currentpath = nextpath;
}
// 绘制第四个昵称(如果有)
if (nickname4 != null && !nickname4.isempty()) {
int text4x = avatar4x; // 稍微偏移一点
int text4y = avatar4y + avatar4height + 26; // 头像下方,加上字体大小
string nextpath;
if (currentpath.equals(tempoutputpath4)) {
nextpath = outputpath;
} else if (currentpath.equals(outputpath)) {
nextpath = outputpath + "_final6.png";
} else if (currentpath.contains("_final")) {
nextpath = outputpath + "_final7.png";
} else {
nextpath = outputpath + "_final8.png";
}
successdrawtext = imageutils.drawtextonimage(
currentpath,
nextpath,
nickname4,
text4x,
text4y,
fontsize,
fontcolor,
"png"
);
if (!successdrawtext) {
commlogd.error("绘制第四个昵称失败");
// 清理文件
file tempfile4 = new file(tempoutputpath4);
if (tempfile4.exists()) {
tempfile4.delete();
}
if (currentpath.equals(outputpath) || currentpath.contains("_final")) {
file outputfileobj = new file(currentpath);
if (outputfileobj.exists()) {
outputfileobj.delete();
}
}
return null;
}
currentpath = nextpath;
} else if (currentpath.equals(tempoutputpath4)) {
// 如果没有昵称,直接将临时文件重命名为输出文件
file tempfile4 = new file(tempoutputpath4);
if (outputfile.exists()) {
outputfile.delete();
}
tempfile4.renameto(outputfile);
}
// 处理可能生成的中间文件,确保最终文件名正确
if (currentpath.contains("_final")) {
file finalfile = new file(currentpath);
file targetfile = new file(outputpath);
if (targetfile.exists()) {
targetfile.delete();
}
finalfile.renameto(targetfile);
}
// 删除可能存在的临时文件
file tempfile4 = new file(tempoutputpath4);
if (tempfile4.exists()) {
tempfile4.delete();
}
// 删除可能存在的中间文件
for (int i = 1; i <= 8; i++) {
file intermediatefile = new file(outputpath + "_final" + i + ".png");
if (intermediatefile.exists()) {
intermediatefile.delete();
}
}
commlogd.info("四个头像和昵称合成成功: " + outputpath);
return outputpath;
}
/**
* 将头像合成到背景图中心位置的便捷方法
* @param bgfilename 背景图文件名
* @param avatarfilename 头像文件名
* @param outputfilename 输出文件名
* @param avatarwidth 头像宽度
* @param avatarheight 头像高度
* @return 合成后的图片完整路径,失败返回null
*/
public static string compositeimagetocenter(string bgfilename, string avatarfilename, string outputfilename,
int avatarwidth, int avatarheight) {
// 本地文件存储路径 - 使用windows兼容的绝对路径格式
string basepath = "d:\\ideaprojects\\mahjong_java\\gameserver\\src\\core\\file\\";
if (!basepath.endswith("\\")) {
basepath += "\\";
}
string bgimagepath = basepath + bgfilename;
string avatarimagepath = basepath + avatarfilename;
string outputpath = basepath + "composite\\" + outputfilename;
// 添加详细日志以调试路径问题
commlogd.info("尝试访问背景图片路径: " + bgimagepath);
file bgfile = new file(bgimagepath);
commlogd.info("背景图片文件存在: " + bgfile.exists() + ", 是文件: " + bgfile.isfile() + ", 可读: " + bgfile.canread());
// 验证输入图片是否存在
if (!imageutils.isimagevalid(bgimagepath)) {
commlogd.error("背景图片不存在或无效: " + bgimagepath);
return null;
}
commlogd.info("尝试访问头像图片路径: " + avatarimagepath);
file avatarfile = new file(avatarimagepath);
commlogd.info("头像图片文件存在: " + avatarfile.exists() + ", 是文件: " + avatarfile.isfile() + ", 可读: " + avatarfile.canread());
if (!imageutils.isimagevalid(avatarimagepath)) {
commlogd.error("头像图片不存在或无效: " + avatarimagepath);
return null;
}
// 执行居中合成
boolean success = imageutils.compositeavatartocenter(
bgimagepath,
avatarimagepath,
outputpath,
avatarwidth,
avatarheight,
"png"
);
if (success) {
commlogd.info("头像居中合成成功: " + outputpath);
return outputpath;
} else {
commlogd.error("头像居中合成失败");
return null;
}
}
}
下面是图片工具类的代码:
/**
* 图片处理工具类
* 提供图片合成、缩放等功能
*/
public class imageutils {
/**
* 将头像合成到背景图上
* @param bgimagepath 背景图路径
* @param avatarimagepath 头像路径
* @param outputpath 输出图片路径
* @param avatarx 头像在背景图上的x坐标
* @param avatary 头像在背景图上的y坐标
* @param avatarwidth 头像宽度
* @param avatarheight 头像高度
* @param format 输出图片格式 (jpg, png等)
* @return 是否成功
*/
public static boolean compositeavatar(string bgimagepath, string avatarimagepath,
string outputpath, int avatarx, int avatary,
int avatarwidth, int avatarheight, string format) {
try {
// 加载背景图
bufferedimage bgimage = imageio.read(new file(bgimagepath));
if (bgimage == null) {
commlogd.error("background image not found or format not supported: " + bgimagepath);
return false;
}
// 加载头像
bufferedimage avatarimage = null;
// 检查是否为svg格式头像
if (avatarimagepath.tolowercase().endswith(".svg")) {
// 对于svg格式,我们需要特殊处理
// 这里使用简化的方式,创建一个占位图像
// 实际项目中可以使用apache batik库来渲染svg
commlogd.info("processing svg avatar: " + avatarimagepath);
avatarimage = createsvgplaceholder(avatarwidth, avatarheight);
} else {
// 加载非svg头像
avatarimage = imageio.read(new file(avatarimagepath));
if (avatarimage == null) {
commlogd.error("avatar image not found or format not supported: " + avatarimagepath);
return false;
}
}
// 创建graphics2d对象进行绘制
graphics2d g2d = bgimage.creategraphics();
// 设置绘制质量
g2d.setrenderinghint(renderinghints.key_interpolation,
renderinghints.value_interpolation_bilinear);
g2d.setrenderinghint(renderinghints.key_rendering,
renderinghints.value_render_quality);
g2d.setrenderinghint(renderinghints.key_antialiasing,
renderinghints.value_antialias_on);
// 绘制头像到背景图指定位置和大小
g2d.drawimage(avatarimage, avatarx, avatary, avatarwidth, avatarheight, null);
g2d.dispose();
// 确保输出目录存在
file outputfile = new file(outputpath);
file parentdir = outputfile.getparentfile();
if (parentdir != null && !parentdir.exists()) {
parentdir.mkdirs();
}
// 保存合成后的图片
boolean result = imageio.write(bgimage, format, outputfile);
if (!result) {
commlogd.error("failed to write image, unsupported format: " + format);
}
return result;
} catch (exception e) {
commlogd.error("error compositing images: " + e.getmessage(), e);
return false;
}
}
/**
* 创建svg占位图像
* 在没有svg库的情况下,创建一个简单的占位图像
* 实际项目中可以使用apache batik库来正确渲染svg
*
* @param width 宽度
* @param height 高度
* @return 占位图像
*/
private static bufferedimage createsvgplaceholder(int width, int height) {
bufferedimage image = new bufferedimage(width, height, bufferedimage.type_int_argb);
graphics2d g2d = image.creategraphics();
// 设置背景为透明
g2d.setcomposite(alphacomposite.clear);
g2d.fillrect(0, 0, width, height);
g2d.setcomposite(alphacomposite.src);
// 绘制一个简单的彩色圆形作为svg占位符
g2d.setcolor(new color(70, 130, 180, 200)); // steel blue with some transparency
g2d.filloval(0, 0, width, height);
// 可以在这里添加更多的占位符样式
g2d.dispose();
return image;
}
/**
* 将头像合成到背景图中心位置
* @param bgimagepath 背景图路径
* @param avatarimagepath 头像路径
* @param outputpath 输出图片路径
* @param avatarwidth 头像宽度
* @param avatarheight 头像高度
* @param format 输出图片格式
* @return 是否成功
*/
public static boolean compositeavatartocenter(string bgimagepath, string avatarimagepath,
string outputpath, int avatarwidth,
int avatarheight, string format) {
try {
// 加载背景图获取尺寸
bufferedimage bgimage = imageio.read(new file(bgimagepath));
if (bgimage == null) {
commlogd.error("background image not found or format not supported: " + bgimagepath);
return false;
}
// 计算头像居中的位置
int bgwidth = bgimage.getwidth();
int bgheight = bgimage.getheight();
int avatarx = (bgwidth - avatarwidth) / 2;
int avatary = (bgheight - avatarheight) / 2;
// 调用合成方法
return compositeavatar(bgimagepath, avatarimagepath, outputpath,
avatarx, avatary, avatarwidth, avatarheight, format);
} catch (exception e) {
commlogd.error("error compositing avatar to center: " + e.getmessage(), e);
return false;
}
}
/**
* 缩放图片
* @param imagepath 原始图片路径
* @param outputpath 输出图片路径
* @param targetwidth 目标宽度
* @param targetheight 目标高度
* @param format 输出格式
* @return 是否成功
*/
public static boolean resizeimage(string imagepath, string outputpath,
int targetwidth, int targetheight, string format) {
try {
bufferedimage originalimage = imageio.read(new file(imagepath));
if (originalimage == null) {
commlogd.error("image not found or format not supported: " + imagepath);
return false;
}
// 创建缩放后的图片
bufferedimage resizedimage = new bufferedimage(targetwidth, targetheight,
bufferedimage.type_int_argb);
graphics2d g = resizedimage.creategraphics();
// 设置缩放质量
g.setrenderinghint(renderinghints.key_interpolation,
renderinghints.value_interpolation_bilinear);
g.setrenderinghint(renderinghints.key_rendering,
renderinghints.value_render_quality);
g.setrenderinghint(renderinghints.key_antialiasing,
renderinghints.value_antialias_on);
// 绘制缩放后的图片
g.drawimage(originalimage, 0, 0, targetwidth, targetheight, null);
g.dispose();
// 确保输出目录存在
file outputfile = new file(outputpath);
file parentdir = outputfile.getparentfile();
if (parentdir != null && !parentdir.exists()) {
parentdir.mkdirs();
}
// 保存图片
return imageio.write(resizedimage, format, outputfile);
} catch (exception e) {
commlogd.error("error resizing image: " + e.getmessage(), e);
return false;
}
}
/**
* 获取图片的字节数组
* @param imagepath 图片路径
* @param format 图片格式
* @return 字节数组
*/
public static byte[] getimagebytes(string imagepath, string format) {
try {
bufferedimage image = imageio.read(new file(imagepath));
if (image == null) {
commlogd.error("image not found or format not supported: " + imagepath);
return null;
}
bytearrayoutputstream baos = new bytearrayoutputstream();
imageio.write(image, format, baos);
return baos.tobytearray();
} catch (exception e) {
commlogd.error("error getting image bytes: " + e.getmessage(), e);
return null;
}
}
/**
* 检查图片文件是否存在且可访问
* @param imagepath 图片路径
* @return 是否有效
*/
public static boolean isimagevalid(string imagepath) {
try {
file imagefile = new file(imagepath);
if (!imagefile.exists() || !imagefile.isfile() || !imagefile.canread()) {
return false;
}
// 特殊处理svg格式文件
if (imagepath.tolowercase().endswith(".svg")) {
commlogd.info("svg image file detected: " + imagepath);
// 对于svg,我们只验证文件存在且可读,不尝试用imageio读取
return true;
}
bufferedimage image = imageio.read(imagefile);
boolean valid = image != null;
if (!valid) {
commlogd.info("non-svg image format not supported or invalid: " + imagepath);
}
return valid;
} catch (exception e) {
commlogd.error("error validating image: " + e.getmessage());
return false;
}
}
/**
* 在图片上绘制文本
* @param imagepath 图片路径
* @param outputpath 输出图片路径
* @param text 要绘制的文本
* @param x 文本起始x坐标
* @param y 文本起始y坐标
* @param fontsize 字体大小
* @param fontcolor 字体颜色
* @param format 输出图片格式
* @return 是否成功
*/
public static boolean drawtextonimage(string imagepath, string outputpath, string text,
int x, int y, int fontsize, color fontcolor, string format) {
try {
// 加载图片
bufferedimage image = imageio.read(new file(imagepath));
if (image == null) {
commlogd.error("image not found or format not supported: " + imagepath);
return false;
}
// 创建graphics2d对象进行绘制
graphics2d g2d = image.creategraphics();
// 设置绘制质量
g2d.setrenderinghint(renderinghints.key_text_antialiasing,
renderinghints.value_text_antialias_on);
g2d.setrenderinghint(renderinghints.key_rendering,
renderinghints.value_render_quality);
// 设置字体和颜色
font font = new font("microsoft yahei", font.plain, fontsize);
g2d.setfont(font);
g2d.setcolor(fontcolor);
// 绘制文本
g2d.drawstring(text, x, y);
g2d.dispose();
// 确保输出目录存在
file outputfile = new file(outputpath);
file parentdir = outputfile.getparentfile();
if (parentdir != null && !parentdir.exists()) {
parentdir.mkdirs();
}
// 保存绘制后的图片
return imageio.write(image, format, outputfile);
} catch (exception e) {
commlogd.error("error drawing text on image: " + e.getmessage(), e);
return false;
}
}
}二、将一张头像合成到背景图中间固定的位置上
首先我们需要定义几个固定的参数(实际项目中参数从以上请求返回值中获取),还是如一所示
//调用图像合成方法;接收合成后的图片完整路径 string imgurl = compositeimages.compositeimagetocenter(basepath,bgfilename,backname,20,20)
这个是将头像合成到背景图中心位置的便捷方法;返回值也是一个string类型的url图片地址;
三、总结
我们根据自己的需要调用图片合成类相关的方法即可返回一个string类型的url图片地址;后期我们如果需要返回的是一个在线可访问的图片链接,可以考虑把返回的图片上传到云存储即可。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论