Java后端頭像合成到背景圖上面代碼實(shí)踐
最近在做一個(gè)需求;本來頭像懸浮到背景圖上的功能是由前端來做的,但是自己閑來沒事,然后自己用java后端寫了一個(gè)工具類,供以后使用,在這里寫帖子分享出來,有需要的可以借用!僅供參考!
一、第一張頭像合成到背景圖固定的位置上
需要用戶頭像合成到固定位置的背景圖上,然后昵稱懸浮在頭像下面;最后返回一個(gè)圖片的地址
我們先在項(xiàng)目本地目錄新建一個(gè)文件夾,存放背景圖和頭像圖片,如圖所示;

首先我們需要定義幾個(gè)固定的參數(shù)(實(shí)際項(xiàng)目中參數(shù)從以上請(qǐng)求返回值中獲?。?;
String bgFileName = "backgroud.png"; //背景圖文件名
String avatarFileName = "autohotkey.svg"; //頭像文件名
String nickname = "鄭州小一"; //頭像文件名
String outputFileName = "telegramPhoto"; //輸出文件名
// 本地文件存儲(chǔ)路徑 - 使用Windows兼容的絕對(duì)路徑格式
String basePath = "d:\\IdeaProjects\\mahjong_java\\gameServer\\src\\core\\file\\";
// 確保路徑格式正確
if (!basePath.endsWith("\\")) {
basePath += "\\";
}然后我們調(diào)用圖片合成方法傳參:
//調(diào)用圖像合成方法;接收合成后的圖片完整路徑 String imgURL = CompositeImages.compositeImage(basePath,bgFileName,avatarFileName,nickname)
然后我們?cè)谫x值給接口返回參數(shù)即可;
以下是圖片合成類的全部代碼:
/**
* 合成圖片工具類:將頭像合成到背景圖上
* @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) {
// 構(gòu)建完整的文件路徑
String bgImagePath = basePath + bgFileName;
String avatarImagePath = basePath + avatarFileName;
String tempOutputPath = basePath + "composite\\" + outputFileName + "_temp.png";
String outputPath = basePath + "composite\\" + outputFileName;
// 添加詳細(xì)日志以調(diào)試路徑問題
CommLogD.info("嘗試訪問背景圖片路徑: " + bgImagePath);
File bgFile = new File(bgImagePath);
CommLogD.info("背景圖片文件存在: " + bgFile.exists() + ", 是文件: " + bgFile.isFile() + ", 可讀: " + bgFile.canRead());
// 驗(yàn)證輸入圖片是否存在
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坐標(biāo)
int avatarY = 240; //頭像Y坐標(biāo)
int avatarWidth = 100; //頭像寬度
int avatarHeight = 100; //頭像高度
// 執(zhí)行頭像合成到臨時(shí)文件
boolean success = ImageUtils.compositeAvatar(
bgImagePath,
avatarImagePath,
tempOutputPath,
avatarX,
avatarY,
avatarWidth,
avatarHeight,
"png" // 固定輸出為png格式,支持透明
);
if (!success) {
CommLogD.error("頭像合成失敗");
// 清理臨時(shí)文件
File tempFile = new File(tempOutputPath);
if (tempFile.exists()) {
tempFile.delete();
}
return null;
}
// 如果提供了昵稱,則繪制昵稱
if (nickname != null && !nickname.isEmpty()) {
// 計(jì)算昵稱繪制位置(頭像下方居中)
int textX = avatarX; // 稍微偏移一點(diǎn)
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"
);
// 清理臨時(shí)文件
File tempFile = new File(tempOutputPath);
if (tempFile.exists()) {
tempFile.delete();
}
} else {
// 如果沒有昵稱,直接將臨時(shí)文件重命名為輸出文件
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;
}
}
/**
* 將兩個(gè)頭像合成到背景圖上的方法,并在頭像下方添加昵稱
* @param bgFileName 背景圖文件名(不含路徑)
* @param avatar1FileName 第一個(gè)頭像文件名(不含路徑)
* @param nickname1 第一個(gè)頭像對(duì)應(yīng)的用戶昵稱
* @param avatar2FileName 第二個(gè)頭像文件名(不含路徑)
* @param nickname2 第二個(gè)頭像對(duì)應(yīng)的用戶昵稱
* @param outputFileName 輸出文件名(不含路徑)
* @return 合成后的圖片完整路徑,失敗返回null
*/
public static String compositeImage2(String basePath, String bgFileName, String avatar1FileName, String nickname1, String avatar2FileName, String nickname2,
String outputFileName) {
// 構(gòu)建完整的文件路徑
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;
// 添加詳細(xì)日志以調(diào)試路徑問題
CommLogD.info("嘗試訪問背景圖片路徑: " + bgImagePath);
File bgFile = new File(bgImagePath);
CommLogD.info("背景圖片文件存在: " + bgFile.exists() + ", 是文件: " + bgFile.isFile() + ", 可讀: " + bgFile.canRead());
// 驗(yàn)證輸入圖片是否存在
if (!ImageUtils.isImageValid(bgImagePath)) {
CommLogD.error("背景圖片不存在或無效: " + bgImagePath);
return null;
}
// 驗(yàn)證第一個(gè)頭像
CommLogD.info("嘗試訪問第一個(gè)頭像圖片路徑: " + avatar1ImagePath);
File avatar1File = new File(avatar1ImagePath);
CommLogD.info("第一個(gè)頭像圖片文件存在: " + avatar1File.exists() + ", 是文件: " + avatar1File.isFile() + ", 可讀: " + avatar1File.canRead());
if (!ImageUtils.isImageValid(avatar1ImagePath)) {
CommLogD.error("第一個(gè)頭像圖片不存在或無效: " + avatar1ImagePath);
return null;
}
// 驗(yàn)證第二個(gè)頭像
CommLogD.info("嘗試訪問第二個(gè)頭像圖片路徑: " + avatar2ImagePath);
File avatar2File = new File(avatar2ImagePath);
CommLogD.info("第二個(gè)頭像圖片文件存在: " + avatar2File.exists() + ", 是文件: " + avatar2File.isFile() + ", 可讀: " + avatar2File.canRead());
if (!ImageUtils.isImageValid(avatar2ImagePath)) {
CommLogD.error("第二個(gè)頭像圖片不存在或無效: " + avatar2ImagePath);
return null;
}
int avatar1X = 22; //頭像X坐標(biāo)
int avatar1Y = 240; //頭像Y坐標(biāo)
int avatar1Width = 100; //頭像寬度
int avatar1Height = 100; //頭像高度
// 先合成第一個(gè)頭像到背景圖,保存為臨時(shí)文件
boolean successFirst = ImageUtils.compositeAvatar(
bgImagePath,
avatar1ImagePath,
tempOutputPath1,
avatar1X,
avatar1Y,
avatar1Width,
avatar1Height,
"png" // 固定輸出為png格式,支持透明
);
if (!successFirst) {
CommLogD.error("合成第一個(gè)頭像失敗");
return null;
}
int avatar2X = 600; //頭像X坐標(biāo)
int avatar2Y = 240; //頭像Y坐標(biāo)
int avatar2Width = 100; //頭像寬度
int avatar2Height = 100; //頭像高度
// 再將第二個(gè)頭像合成到臨時(shí)文件上,得到最終結(jié)果
boolean successSecond = ImageUtils.compositeAvatar(
tempOutputPath1,
avatar2ImagePath,
tempOutputPath2,
avatar2X,
avatar2Y,
avatar2Width,
avatar2Height,
"png"
);
// 刪除第一個(gè)臨時(shí)文件
File tempFile1 = new File(tempOutputPath1);
if (tempFile1.exists()) {
tempFile1.delete();
}
if (!successSecond) {
CommLogD.info("兩個(gè)頭像合成成功: " + outputPath);
// 刪除第二個(gè)臨時(shí)文件
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); // 白色文字
// 繪制第一個(gè)昵稱(如果有)
if (nickname1 != null && !nickname1.isEmpty()) {
int text1X = avatar1X; // 稍微偏移一點(diǎn)
int text1Y = avatar1Y + avatar1Height + 26; // 頭像下方,加上字體大小
successDrawText = ImageUtils.drawTextOnImage(
currentPath,
outputPath,
nickname1,
text1X,
text1Y,
fontSize,
fontColor,
"png"
);
if (!successDrawText) {
CommLogD.error("繪制第一個(gè)昵稱失敗");
// 刪除臨時(shí)文件
File tempFile2 = new File(tempOutputPath2);
if (tempFile2.exists()) {
tempFile2.delete();
}
return null;
}
currentPath = outputPath;
}
// 繪制第二個(gè)昵稱(如果有)
if (nickname2 != null && !nickname2.isEmpty()) {
int text2X = avatar2X; // 稍微偏移一點(diǎn)
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("繪制第二個(gè)昵稱失敗");
// 清理文件
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;
}
// 如果創(chuàng)建了最終文件,需要重命名
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)) {
// 如果沒有第二個(gè)昵稱,直接將臨時(shí)文件重命名為輸出文件
File tempFile2 = new File(tempOutputPath2);
if (outputFile.exists()) {
outputFile.delete();
}
tempFile2.renameTo(outputFile);
}
// 刪除可能存在的臨時(shí)文件
File tempFile2 = new File(tempOutputPath2);
if (tempFile2.exists()) {
tempFile2.delete();
}
CommLogD.info("兩個(gè)頭像和昵稱合成成功: " + outputPath);
return outputPath;
}
/**
* 將三個(gè)頭像合成到背景圖上的方法
* @param basePath 基礎(chǔ)路徑
* @param bgFileName 背景圖文件名(不含路徑)
* @param avatar1FileName 第一個(gè)頭像文件名(不含路徑)
* @param nickname1 第一個(gè)頭像對(duì)應(yīng)的用戶昵稱
* @param avatar2FileName 第二個(gè)頭像文件名(不含路徑)
* @param nickname2 第二個(gè)頭像對(duì)應(yīng)的用戶昵稱
* @param avatar3FileName 第三個(gè)頭像文件名(不含路徑)
* @param nickname3 第三個(gè)頭像對(duì)應(yīng)的用戶昵稱
* @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) {
// 構(gòu)建完整的文件路徑
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;
// 添加詳細(xì)日志以調(diào)試路徑問題
CommLogD.info("嘗試訪問背景圖片路徑: " + bgImagePath);
File bgFile = new File(bgImagePath);
CommLogD.info("背景圖片文件存在: " + bgFile.exists() + ", 是文件: " + bgFile.isFile() + ", 可讀: " + bgFile.canRead());
// 驗(yàn)證輸入圖片是否存在
if (!ImageUtils.isImageValid(bgImagePath)) {
CommLogD.error("背景圖片不存在或無效: " + bgImagePath);
return null;
}
// 驗(yàn)證第一個(gè)頭像
CommLogD.info("嘗試訪問第一個(gè)頭像圖片路徑: " + avatar1ImagePath);
File avatar1File = new File(avatar1ImagePath);
CommLogD.info("第一個(gè)頭像圖片文件存在: " + avatar1File.exists() + ", 是文件: " + avatar1File.isFile() + ", 可讀: " + avatar1File.canRead());
if (!ImageUtils.isImageValid(avatar1ImagePath)) {
CommLogD.error("第一個(gè)頭像圖片不存在或無效: " + avatar1ImagePath);
return null;
}
// 驗(yàn)證第二個(gè)頭像
CommLogD.info("嘗試訪問第二個(gè)頭像圖片路徑: " + avatar2ImagePath);
File avatar2File = new File(avatar2ImagePath);
CommLogD.info("第二個(gè)頭像圖片文件存在: " + avatar2File.exists() + ", 是文件: " + avatar2File.isFile() + ", 可讀: " + avatar2File.canRead());
if (!ImageUtils.isImageValid(avatar2ImagePath)) {
CommLogD.error("第二個(gè)頭像圖片不存在或無效: " + avatar2ImagePath);
return null;
}
// 驗(yàn)證第三個(gè)頭像
CommLogD.info("嘗試訪問第三個(gè)頭像圖片路徑: " + avatar3ImagePath);
File avatar3File = new File(avatar3ImagePath);
CommLogD.info("第三個(gè)頭像圖片文件存在: " + avatar3File.exists() + ", 是文件: " + avatar3File.isFile() + ", 可讀: " + avatar3File.canRead());
if (!ImageUtils.isImageValid(avatar3ImagePath)) {
CommLogD.error("第三個(gè)頭像圖片不存在或無效: " + avatar3ImagePath);
return null;
}
// 第一個(gè)頭像的位置和大小
int avatar1X = 22; // 頭像X坐標(biāo)
int avatar1Y = 240; // 頭像Y坐標(biāo)
int avatar1Width = 100; // 頭像寬度
int avatar1Height = 100; // 頭像高度
// 先合成第一個(gè)頭像到背景圖,保存為臨時(shí)文件1
boolean successFirst = ImageUtils.compositeAvatar(
bgImagePath,
avatar1ImagePath,
tempOutputPath1,
avatar1X,
avatar1Y,
avatar1Width,
avatar1Height,
"png" // 固定輸出為png格式,支持透明
);
if (!successFirst) {
CommLogD.error("合成第一個(gè)頭像失敗");
return null;
}
// 第二個(gè)頭像的位置和大小
int avatar2X = 600; // 頭像X坐標(biāo)
int avatar2Y = 240; // 頭像Y坐標(biāo)
int avatar2Width = 100; // 頭像寬度
int avatar2Height = 100; // 頭像高度
// 再將第二個(gè)頭像合成到臨時(shí)文件1上,得到臨時(shí)文件2
boolean successSecond = ImageUtils.compositeAvatar(
tempOutputPath1,
avatar2ImagePath,
tempOutputPath2,
avatar2X,
avatar2Y,
avatar2Width,
avatar2Height,
"png"
);
if (!successSecond) {
CommLogD.error("合成第二個(gè)頭像失敗");
// 刪除已生成的臨時(shí)文件
File tempFile1 = new File(tempOutputPath1);
if (tempFile1.exists()) {
tempFile1.delete();
}
return null;
}
// 第三個(gè)頭像的位置和大小
int avatar3X = 310; // 頭像X坐標(biāo)
int avatar3Y = 450; // 頭像Y坐標(biāo)
int avatar3Width = 100; // 頭像寬度
int avatar3Height = 100; // 頭像高度
// 將第三個(gè)頭像合成到臨時(shí)文件2上,得到臨時(shí)文件3
boolean successThird = ImageUtils.compositeAvatar(
tempOutputPath2,
avatar3ImagePath,
tempOutputPath3,
avatar3X,
avatar3Y,
avatar3Width,
avatar3Height,
"png"
);
// 刪除臨時(shí)文件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("合成第三個(gè)頭像失敗");
// 刪除臨時(shí)文件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); // 白色文字
// 繪制第一個(gè)昵稱(如果有)
if (nickname1 != null && !nickname1.isEmpty()) {
int text1X = avatar1X; // 稍微偏移一點(diǎn)
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("繪制第一個(gè)昵稱失敗");
// 刪除臨時(shí)文件
File tempFile3 = new File(tempOutputPath3);
if (tempFile3.exists()) {
tempFile3.delete();
}
return null;
}
currentPath = nextPath;
}
// 繪制第二個(gè)昵稱(如果有)
if (nickname2 != null && !nickname2.isEmpty()) {
int text2X = avatar2X; // 稍微偏移一點(diǎn)
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("繪制第二個(gè)昵稱失敗");
// 清理文件
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;
}
// 繪制第三個(gè)昵稱(如果有)
if (nickname3 != null && !nickname3.isEmpty()) {
int text3X = avatar3X; // 稍微偏移一點(diǎn)
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("繪制第三個(gè)昵稱失敗");
// 清理文件
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)) {
// 如果沒有昵稱,直接將臨時(shí)文件重命名為輸出文件
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);
}
// 刪除可能存在的臨時(shí)文件
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("三個(gè)頭像和昵稱合成成功: " + outputPath);
return outputPath;
}
/**
* 將四個(gè)頭像合成到背景圖上的方法,并在頭像下方添加昵稱
* @param basePath 基礎(chǔ)路徑
* @param bgFileName 背景圖文件名(不含路徑)
* @param avatar1FileName 第一個(gè)頭像文件名(不含路徑)
* @param nickname1 第一個(gè)頭像對(duì)應(yīng)的用戶昵稱
* @param avatar2FileName 第二個(gè)頭像文件名(不含路徑)
* @param nickname2 第二個(gè)頭像對(duì)應(yīng)的用戶昵稱
* @param avatar3FileName 第三個(gè)頭像文件名(不含路徑)
* @param nickname3 第三個(gè)頭像對(duì)應(yīng)的用戶昵稱
* @param avatar4FileName 第四個(gè)頭像文件名(不含路徑)
* @param nickname4 第四個(gè)頭像對(duì)應(yīng)的用戶昵稱
* @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) {
// 構(gòu)建完整的文件路徑
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;
// 添加詳細(xì)日志以調(diào)試路徑問題
CommLogD.info("嘗試訪問背景圖片路徑: " + bgImagePath);
File bgFile = new File(bgImagePath);
CommLogD.info("背景圖片文件存在: " + bgFile.exists() + ", 是文件: " + bgFile.isFile() + ", 可讀: " + bgFile.canRead());
// 驗(yàn)證輸入圖片是否存在
if (!ImageUtils.isImageValid(bgImagePath)) {
CommLogD.error("背景圖片不存在或無效: " + bgImagePath);
return null;
}
// 驗(yàn)證第一個(gè)頭像
CommLogD.info("嘗試訪問第一個(gè)頭像圖片路徑: " + avatar1ImagePath);
File avatar1File = new File(avatar1ImagePath);
CommLogD.info("第一個(gè)頭像圖片文件存在: " + avatar1File.exists() + ", 是文件: " + avatar1File.isFile() + ", 可讀: " + avatar1File.canRead());
if (!ImageUtils.isImageValid(avatar1ImagePath)) {
CommLogD.error("第一個(gè)頭像圖片不存在或無效: " + avatar1ImagePath);
return null;
}
// 驗(yàn)證第二個(gè)頭像
CommLogD.info("嘗試訪問第二個(gè)頭像圖片路徑: " + avatar2ImagePath);
File avatar2File = new File(avatar2ImagePath);
CommLogD.info("第二個(gè)頭像圖片文件存在: " + avatar2File.exists() + ", 是文件: " + avatar2File.isFile() + ", 可讀: " + avatar2File.canRead());
if (!ImageUtils.isImageValid(avatar2ImagePath)) {
CommLogD.error("第二個(gè)頭像圖片不存在或無效: " + avatar2ImagePath);
return null;
}
// 驗(yàn)證第三個(gè)頭像
CommLogD.info("嘗試訪問第三個(gè)頭像圖片路徑: " + avatar3ImagePath);
File avatar3File = new File(avatar3ImagePath);
CommLogD.info("第三個(gè)頭像圖片文件存在: " + avatar3File.exists() + ", 是文件: " + avatar3File.isFile() + ", 可讀: " + avatar3File.canRead());
if (!ImageUtils.isImageValid(avatar3ImagePath)) {
CommLogD.error("第三個(gè)頭像圖片不存在或無效: " + avatar3ImagePath);
return null;
}
// 驗(yàn)證第四個(gè)頭像
CommLogD.info("嘗試訪問第四個(gè)頭像圖片路徑: " + avatar4ImagePath);
File avatar4File = new File(avatar4ImagePath);
CommLogD.info("第四個(gè)頭像圖片文件存在: " + avatar4File.exists() + ", 是文件: " + avatar4File.isFile() + ", 可讀: " + avatar4File.canRead());
if (!ImageUtils.isImageValid(avatar4ImagePath)) {
CommLogD.error("第四個(gè)頭像圖片不存在或無效: " + avatar4ImagePath);
return null;
}
// 第一個(gè)頭像的位置和大小
int avatar1X = 22; // 頭像X坐標(biāo)
int avatar1Y = 240; // 頭像Y坐標(biāo)
int avatar1Width = 100; // 頭像寬度
int avatar1Height = 100; // 頭像高度
// 先合成第一個(gè)頭像到背景圖,保存為臨時(shí)文件1
boolean successFirst = ImageUtils.compositeAvatar(
bgImagePath,
avatar1ImagePath,
tempOutputPath1,
avatar1X,
avatar1Y,
avatar1Width,
avatar1Height,
"png" // 固定輸出為png格式,支持透明
);
if (!successFirst) {
CommLogD.error("合成第一個(gè)頭像失敗");
return null;
}
// 第二個(gè)頭像的位置和大小
int avatar2X = 600; // 頭像X坐標(biāo)
int avatar2Y = 240; // 頭像Y坐標(biāo)
int avatar2Width = 100; // 頭像寬度
int avatar2Height = 100; // 頭像高度
// 再將第二個(gè)頭像合成到臨時(shí)文件1上,得到臨時(shí)文件2
boolean successSecond = ImageUtils.compositeAvatar(
tempOutputPath1,
avatar2ImagePath,
tempOutputPath2,
avatar2X,
avatar2Y,
avatar2Width,
avatar2Height,
"png"
);
if (!successSecond) {
CommLogD.error("合成第二個(gè)頭像失敗");
// 刪除已生成的臨時(shí)文件
File tempFile1 = new File(tempOutputPath1);
if (tempFile1.exists()) {
tempFile1.delete();
}
return null;
}
// 第三個(gè)頭像的位置和大小
int avatar3X = 310; // 頭像X坐標(biāo)
int avatar3Y = 450; // 頭像Y坐標(biāo)
int avatar3Width = 100; // 頭像寬度
int avatar3Height = 100; // 頭像高度
// 將第三個(gè)頭像合成到臨時(shí)文件2上,得到臨時(shí)文件3
boolean successThird = ImageUtils.compositeAvatar(
tempOutputPath2,
avatar3ImagePath,
tempOutputPath3,
avatar3X,
avatar3Y,
avatar3Width,
avatar3Height,
"png"
);
// 刪除臨時(shí)文件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("合成第三個(gè)頭像失敗");
// 刪除臨時(shí)文件3
File tempFile3 = new File(tempOutputPath3);
if (tempFile3.exists()) {
tempFile3.delete();
}
return null;
}
// 第四個(gè)頭像的位置和大?。ㄔO(shè)置為頂部居中)
int avatar4X = 310; // 頭像X坐標(biāo)
int avatar4Y = 35; // 頭像Y坐標(biāo)
int avatar4Width = 100; // 頭像寬度
int avatar4Height = 100; // 頭像高度
// 將第四個(gè)頭像合成到臨時(shí)文件3上,得到臨時(shí)文件4
boolean successFourth = ImageUtils.compositeAvatar(
tempOutputPath3,
avatar4ImagePath,
tempOutputPath4,
avatar4X,
avatar4Y,
avatar4Width,
avatar4Height,
"png"
);
// 刪除臨時(shí)文件3
File tempFile3 = new File(tempOutputPath3);
if (tempFile3.exists()) {
tempFile3.delete();
}
if (!successFourth) {
CommLogD.error("合成第四個(gè)頭像失敗");
// 刪除臨時(shí)文件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); // 白色文字
// 繪制第一個(gè)昵稱(如果有)
if (nickname1 != null && !nickname1.isEmpty()) {
int text1X = avatar1X; // 稍微偏移一點(diǎn)
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("繪制第一個(gè)昵稱失敗");
// 刪除臨時(shí)文件
File tempFile4 = new File(tempOutputPath4);
if (tempFile4.exists()) {
tempFile4.delete();
}
return null;
}
currentPath = nextPath;
}
// 繪制第二個(gè)昵稱(如果有)
if (nickname2 != null && !nickname2.isEmpty()) {
int text2X = avatar2X; // 稍微偏移一點(diǎn)
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("繪制第二個(gè)昵稱失敗");
// 清理文件
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;
}
// 繪制第三個(gè)昵稱(如果有)
if (nickname3 != null && !nickname3.isEmpty()) {
int text3X = avatar3X; // 稍微偏移一點(diǎn)
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("繪制第三個(gè)昵稱失敗");
// 清理文件
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;
}
// 繪制第四個(gè)昵稱(如果有)
if (nickname4 != null && !nickname4.isEmpty()) {
int text4X = avatar4X; // 稍微偏移一點(diǎn)
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("繪制第四個(gè)昵稱失敗");
// 清理文件
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)) {
// 如果沒有昵稱,直接將臨時(shí)文件重命名為輸出文件
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);
}
// 刪除可能存在的臨時(shí)文件
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("四個(gè)頭像和昵稱合成成功: " + 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) {
// 本地文件存儲(chǔ)路徑 - 使用Windows兼容的絕對(duì)路徑格式
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;
// 添加詳細(xì)日志以調(diào)試路徑問題
CommLogD.info("嘗試訪問背景圖片路徑: " + bgImagePath);
File bgFile = new File(bgImagePath);
CommLogD.info("背景圖片文件存在: " + bgFile.exists() + ", 是文件: " + bgFile.isFile() + ", 可讀: " + bgFile.canRead());
// 驗(yàn)證輸入圖片是否存在
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;
}
// 執(zhí)行居中合成
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坐標(biāo)
* @param avatarY 頭像在背景圖上的Y坐標(biāo)
* @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")) {
// 對(duì)于SVG格式,我們需要特殊處理
// 這里使用簡(jiǎn)化的方式,創(chuàng)建一個(gè)占位圖像
// 實(shí)際項(xiàng)目中可以使用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;
}
}
// 創(chuàng)建Graphics2D對(duì)象進(jìn)行繪制
Graphics2D g2d = bgImage.createGraphics();
// 設(shè)置繪制質(zhì)量
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;
}
}
/**
* 創(chuàng)建SVG占位圖像
* 在沒有SVG庫的情況下,創(chuàng)建一個(gè)簡(jiǎn)單的占位圖像
* 實(shí)際項(xiàng)目中可以使用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();
// 設(shè)置背景為透明
g2d.setComposite(AlphaComposite.Clear);
g2d.fillRect(0, 0, width, height);
g2d.setComposite(AlphaComposite.Src);
// 繪制一個(gè)簡(jiǎn)單的彩色圓形作為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;
}
// 計(jì)算頭像居中的位置
int bgWidth = bgImage.getWidth();
int bgHeight = bgImage.getHeight();
int avatarX = (bgWidth - avatarWidth) / 2;
int avatarY = (bgHeight - avatarHeight) / 2;
// 調(diào)用合成方法
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 目標(biāo)寬度
* @param targetHeight 目標(biāo)高度
* @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;
}
// 創(chuàng)建縮放后的圖片
BufferedImage resizedImage = new BufferedImage(targetWidth, targetHeight,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = resizedImage.createGraphics();
// 設(shè)置縮放質(zhì)量
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;
}
}
/**
* 獲取圖片的字節(jié)數(shù)組
* @param imagePath 圖片路徑
* @param format 圖片格式
* @return 字節(jié)數(shù)組
*/
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);
// 對(duì)于SVG,我們只驗(yàn)證文件存在且可讀,不嘗試用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坐標(biāo)
* @param y 文本起始Y坐標(biāo)
* @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;
}
// 創(chuàng)建Graphics2D對(duì)象進(jìn)行繪制
Graphics2D g2d = image.createGraphics();
// 設(shè)置繪制質(zhì)量
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
// 設(shè)置字體和顏色
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;
}
}
}二、將一張頭像合成到背景圖中間固定的位置上
首先我們需要定義幾個(gè)固定的參數(shù)(實(shí)際項(xiàng)目中參數(shù)從以上請(qǐng)求返回值中獲?。?,還是如一所示
//調(diào)用圖像合成方法;接收合成后的圖片完整路徑 String imgURL = CompositeImages.compositeImageToCenter(basePath,bgFileName,backName,20,20)
這個(gè)是將頭像合成到背景圖中心位置的便捷方法;返回值也是一個(gè)String類型的url圖片地址;
三、總結(jié)
我們根據(jù)自己的需要調(diào)用圖片合成類相關(guān)的方法即可返回一個(gè)String類型的url圖片地址;后期我們?nèi)绻枰祷氐氖且粋€(gè)在線可訪問的圖片鏈接,可以考慮把返回的圖片上傳到云存儲(chǔ)即可。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
以用戶名注冊(cè)為例分析三種Action獲取數(shù)據(jù)的方式
這篇文章主要介紹了以用戶名注冊(cè)為例分析三種Action獲取數(shù)據(jù)的方式的相關(guān)資料,需要的朋友可以參考下2016-03-03
使用IDEA啟動(dòng)項(xiàng)目build時(shí)出現(xiàn)OOM相關(guān)異常的解決
這篇文章主要介紹了使用IDEA啟動(dòng)項(xiàng)目build時(shí)出現(xiàn)OOM相關(guān)異常的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-06-06
Java Socket編程心跳包創(chuàng)建實(shí)例解析
這篇文章主要介紹了Java Socket編程心跳包創(chuàng)建實(shí)例解析,具有一定借鑒價(jià)值,需要的朋友可以參考下2017-12-12
Java快速實(shí)現(xiàn)短信登錄的詳細(xì)流程
短信登錄是基于短信驗(yàn)證碼的身份驗(yàn)證方式,在許多網(wǎng)站和應(yīng)用中被廣泛使用,通常被認(rèn)為比傳統(tǒng)的用戶名和密碼更安全,因?yàn)轵?yàn)證碼是一次性使用的,且只有用戶能夠接收到短信,本文給大家介紹了Java快速實(shí)現(xiàn)短信登錄的詳細(xì)流程,需要的朋友可以參考下2025-08-08
已有的springcloud+mybatis項(xiàng)目升級(jí)為mybatis-plus的方法
這篇文章主要介紹了已有的springcloud+mybatis項(xiàng)目升級(jí)為mybatis-plus,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
SpringBoot整合Mybatis實(shí)現(xiàn)多數(shù)據(jù)源配置與跨數(shù)據(jù)源事務(wù)實(shí)例
開發(fā)中經(jīng)常有這樣的需要: 讀寫分離。微服務(wù)環(huán)境下可以實(shí)現(xiàn)一個(gè)服務(wù)讀取一個(gè)數(shù)據(jù)庫,另一個(gè)服務(wù)寫庫。但是在實(shí)際應(yīng)用中有時(shí)也需要在一個(gè)服務(wù)中讀寫不同的數(shù)據(jù)庫??梢栽谝粋€(gè)SpringBoot單體項(xiàng)目中配置多個(gè)數(shù)據(jù)源解決讀寫庫分離2022-11-11

