工具类
package com.shuidihuzhu.razor.client.utils;
import lombok.extern.slf4j.Slf4j;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
/**
*
* @date 2021/2/3
* 合成图片
**/
@Slf4j
public class MergeImgUtil {
/**
* @param bigPath 背景图
* @param smallPath 小图
* @param x 宽多少像素
* @param y 高多少像素
*/
public static void mergeImage(String bigPath, String smallPath, int x, int y) {
try {
BufferedImage small = getBufferedImage(smallPath);
BufferedImage big = getBufferedImage(bigPath);
Graphics2D g = big.createGraphics();
g.drawImage(small, x, y, small.getWidth(), small.getHeight(), null);
g.dispose();
ImageIO.write(big, "png", new File(bigPath));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @param bigPath
* @param inputStream
* @param x
* @param y
* @throws IOException
*/
public static void mergeImage(String bigPath, InputStream inputStream, int x, int y) {
try {
BufferedImage small = getBufferedImage(inputStream);
BufferedImage big = getBufferedImage(bigPath);
Graphics2D g = big.createGraphics();
g.drawImage(small, x, y, small.getWidth(), small.getHeight(), null);
g.dispose();
ImageIO.write(big, "jpg", new File(bigPath));
} catch (Exception e) {
e.printStackTrace();
}
}
private static BufferedImage getBufferedImage(String path) {
BufferedImage img = null;
try {
String http = "http";
if (path.contains(http)) {
URL url = new URL(path);
img = ImageIO.read(url);
} else {
img = ImageIO.read(new File(path));
}
} catch (IOException e) {
log.error("获取图片失败");
}
return img;
}
private static BufferedImage getBufferedImage(InputStream inputStream) {
BufferedImage img = null;
try {
img = ImageIO.read(inputStream);
} catch (IOException e) {
log.error("获取图片失败");
}
return img;
}
/**
* 把指定头像切成圆形输出
*
* @param srcImgPath 原头像
* @param tarImgPath 圆形头像
* @param x 要切成大小 坐标 X 宽多少像素
* @param y 要切成大小 坐标 Y 高多少像素
*/
private static void imageToCircular(String srcImgPath, String tarImgPath, int x, int y) throws IOException {
BufferedImage bi1 = ImageIO.read(new File(srcImgPath));
// 根据需要是否使用 BufferedImage.TYPE_INT_ARGB
BufferedImage bi2 = new BufferedImage(bi1.getWidth(), bi1.getHeight(),
BufferedImage.TYPE_INT_RGB);
Ellipse2D.Double shape = new Ellipse2D.Double(0, 0, bi1.getWidth(), bi1
.getHeight());
Graphics2D g2 = bi2.createGraphics();
g2.setBackground(Color.WHITE);
g2.fill(new Rectangle(bi2.getWidth(), bi2.getHeight()));
g2.setClip(shape);
// 使用 setRenderingHint 设置抗锯齿
g2.drawImage(bi1, 0, 0, null);
g2.dispose();
ImageIO.write(bi2, "png", new File(tarImgPath));
}
/**
* @param srcImgPath 源图片路径
* @param tarImgPath 保存的图片路径
* @param waterMarkContent 水印内容
* @param x 坐标 X 宽多少像素
* @param y 坐标 Y 高多少像素
* @param R RGB 数字 R
* @param G RGB 数字 G
* @param B RGB 数字 B
* @param frontSize 文字大小
*/
public static void addWaterMark(String srcImgPath, String tarImgPath, String waterMarkContent, int x, int y,int R,int G,int B,int A,int frontSize) throws IOException {
// 读取原图片信息
得到文件
File srcImgFile = new File(srcImgPath);
文件转化为图片
Image srcImg = ImageIO.read(srcImgFile);
获取图片的宽
int srcImgWidth = srcImg.getWidth(null);
//获取图片的高
int srcImgHeight = srcImg.getHeight(null);
// 加水印
BufferedImage bufImg = new BufferedImage(srcImgWidth, srcImgHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bufImg.createGraphics();
g.drawImage(srcImg, 0, 0, srcImgWidth, srcImgHeight, null);
根据图片的背景设置水印颜色
Color color = new Color(R, G, B, A);
//水印图片色彩以及透明度
g.setColor(color);
设置字体
水印字体
Font font = new Font("微软雅黑", Font.PLAIN, frontSize);
g.setFont(font);
//设置水印的坐标
// int x = srcImgWidth - 2 * getWatermarkLength(waterMarkContent, g);
// int y = srcImgHeight - 2 * getWatermarkLength(waterMarkContent, g);
画出水印
g.drawString(waterMarkContent, x, y);
g.dispose();
// 输出图片
FileOutputStream outImgStream = new FileOutputStream(tarImgPath);
ImageIO.write(bufImg, "png", outImgStream);
log.debug("添加水印完成");
outImgStream.flush();
outImgStream.close();
}
public static int getWatermarkLength(String waterMarkContent, Graphics2D g) {
return g.getFontMetrics(g.getFont()).charsWidth(waterMarkContent.toCharArray(), 0, waterMarkContent.length());
}
/***
*
* @param srcFilePath 源图片文件路径
* @param circularImgSavePath 新生成的图片的保存路径,需要带有保存的文件名和后缀
* @param targetSize 文件的边长,单位:像素,最终得到的是一张正方形的图,所以要求targetSize<=源文件的最小边长
* @param cornerRadius 圆角半径,单位:像素。如果=targetSize那么得到的是圆形图
* @return 文件的保存路径
* @throws IOException
*/
public static String makeCircularImg(String srcFilePath, String circularImgSavePath, int targetSize, int cornerRadius) throws IOException {
BufferedImage bufferedImage = ImageIO.read(new File(srcFilePath));
BufferedImage circularBufferImage = roundImage(bufferedImage, targetSize, cornerRadius);
ImageIO.write(circularBufferImage, "png", new File(circularImgSavePath));
return circularImgSavePath;
}
private static BufferedImage roundImage(BufferedImage image, int targetSize, int cornerRadius) {
BufferedImage outputImage = new BufferedImage(targetSize, targetSize, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = outputImage.createGraphics();
g2.setComposite(AlphaComposite.Src);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.WHITE);
g2.fill(new RoundRectangle2D.Float(0, 0, targetSize, targetSize, cornerRadius, cornerRadius));
g2.setComposite(AlphaComposite.SrcAtop);
g2.drawImage(image, 0, 0, null);
g2.dispose();
return outputImage;
}
/**
* 生成圆角图标
* @param image 图片路径
* @param cornerRadius 圆角半径 720正圆形| 40是圆角形
* @return
*/
public static BufferedImage makeRoundedCorner1(BufferedImage image, int cornerRadius) {
BufferedImage output = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = output.createGraphics();
g2.setComposite(AlphaComposite.Src);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.WHITE);
g2.fill(new RoundRectangle2D.Float(0, 0, image.getWidth(), image.getHeight(), cornerRadius, cornerRadius));
g2.setComposite(AlphaComposite.SrcAtop);
g2.drawImage(image, 0, 0, null);
g2.dispose();
return output;
}
/**
* @param srcImg 原图片
* @param destImg 目标位置
* @param width 期望宽
* @param height 期望高
* @param equalScale 是否等比例缩放
*/
public static void reSize(File srcImg, File destImg, int width,
int height, boolean equalScale) {
String type = getImageType(srcImg);
if (type == null) {
return;
}
if (width < 0 || height < 0) {
return;
}
BufferedImage srcImage = null;
try {
srcImage = ImageIO.read(srcImg);
System.out.println("srcImg size=" + srcImage.getWidth() + "X" + srcImage.getHeight());
} catch (IOException e) {
e.printStackTrace();
return;
}
if (srcImage != null) {
// targetW,targetH分别表示目标长和宽
BufferedImage target = null;
double sx = (double) width / srcImage.getWidth();
double sy = (double) height / srcImage.getHeight();
// 等比缩放
if (equalScale) {
if (sx > sy) {
sx = sy;
width = (int) (sx * srcImage.getWidth());
} else {
sy = sx;
height = (int) (sy * srcImage.getHeight());
}
}
System.out.println("destImg size=" + width + "X" + height);
ColorModel cm = srcImage.getColorModel();
WritableRaster raster = cm.createCompatibleWritableRaster(width, height);
boolean alphaPremultiplied = cm.isAlphaPremultiplied();
target = new BufferedImage(cm, raster, alphaPremultiplied, null);
Graphics2D g = target.createGraphics();
// smoother than exlax:
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.drawRenderedImage(srcImage, AffineTransform.getScaleInstance(sx, sy));
g.dispose();
// 将转换后的图片保存
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(target, type, baos);
FileOutputStream fos = new FileOutputStream(destImg);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 获取文件后缀不带.
*
* @param file 文件后缀名
* @return
*/
private static String getImageType(File file) {
if (file != null && file.exists() && file.isFile()) {
String fileName = file.getName();
int index = fileName.lastIndexOf(".");
if (index != -1 && index < fileName.length() - 1) {
return fileName.substring(index + 1);
}
}
return null;
}
/**
* 图片圆角处理,背景透明化
*
* @param srcImageFile 原图片
* @param result 处理后图片
* @param type 图片格式
* @param cornerRadius 720为圆角
*/
public void makeRoundedCorner(File srcImageFile, File result, String type, int cornerRadius) {
try {
BufferedImage bi1 = ImageIO.read(srcImageFile);
// 根据需要是否使用 BufferedImage.TYPE_INT_ARGB
BufferedImage image = new BufferedImage(bi1.getWidth(), bi1.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Ellipse2D.Double shape = new Ellipse2D.Double(0, 0, bi1.getWidth(), bi1
.getHeight());
Graphics2D g2 = image.createGraphics();
image = g2.getDeviceConfiguration().createCompatibleImage(bi1.getWidth(), bi1.getHeight(), Transparency.TRANSLUCENT);
g2 = image.createGraphics();
g2.setComposite(AlphaComposite.Clear);
g2.fill(new Rectangle(image.getWidth(), image.getHeight()));
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 1.0f));
g2.setClip(shape);
// 使用 setRenderingHint 设置抗锯齿
g2 = image.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.fillRoundRect(0, 0, bi1.getWidth(), bi1.getHeight(), cornerRadius, cornerRadius);
g2.setComposite(AlphaComposite.SrcIn);
g2.drawImage(bi1, 0, 0, bi1.getWidth(), bi1.getHeight(), null);
g2.dispose();
ImageIO.write(image, type, result);
} catch (Exception e) {
// TODO: handle exception
}
}
/**
*
* @param soursePath 原图地址
* @param descPath 新图地址
* @param cornerRadius 圆角半径 720正圆形| 40是圆角形
* @return
* @throws IOException
*/
public static String makeRoundedCorner(String soursePath, String descPath, int cornerRadius) {
BufferedImage image = null;
try {
image = ImageIO.read(new File(soursePath));
/**
* 720正圆形
* 40是圆角形
*/
BufferedImage rounded = MergeImgUtil.makeRoundedCorner1(image, cornerRadius);
ImageIO.write(rounded, "png", new File(descPath));
} catch (IOException e) {
e.printStackTrace();
}
return descPath;
}
public static void main(String[] args) throws IOException {
long startLong = System.currentTimeMillis();
//用户评语底图本地保存地址
String backgroundPath = "/Users/yuechao/Documents/bruce/works/testpaper/" + "backage.png";
System.out.println("backgroundPath :" + backgroundPath);
//试卷评语默认头像地址
String userHeadImagePath = "/Users/yuechao/Documents/bruce/works/testpaper/" + "head.jpg";
System.out.println("userHeadImagePath:" + userHeadImagePath);
//圆形头像地址
String userHeadImageCircularPath = "/Users/yuechao/Documents/bruce/works/testpaper/" + "head_new.png";
makeRoundedCorner(userHeadImagePath,userHeadImageCircularPath,720);
//圆形缩放后的图片
String userHeadImageCircularSmallPath = "/Users/yuechao/Documents/bruce/works/testpaper/" + "head_new_small.png";
reSize(new File(userHeadImageCircularPath),
new File(userHeadImageCircularSmallPath),
100, 100, true);
// MergeImgUtil.imageToCircular(userHeadImagePath, userHeadImageCircularPath, 10, 20);
//把用户头像合成到指定底图上
MergeImgUtil.mergeImage(
backgroundPath,
userHeadImageCircularSmallPath,
86,
958
);
//皇冠地址
//圆形头像地址
String headTest = "/Users/yuechao/Documents/bruce/works/testpaper/" + "head-test.png";
//把用户头像合成到指定底图上
MergeImgUtil.mergeImage(
backgroundPath,
headTest,
50,
916
);
//待存储的地址
String tarImgPath = "/Users/yuechao/Documents/bruce/works/testpaper/" + "backage_new.png";
//用户昵称
String userNickname = "我是张三";
//合成图片
try {
MergeImgUtil.addWaterMark(
backgroundPath,
tarImgPath,
userNickname,
198,
990,
153,31,13,255,
32);
} catch (IOException e) {
e.printStackTrace();
}
long endLong = System.currentTimeMillis();
//time : 1612 //time : 1563
System.out.println("time : " + (endLong - startLong)) ;
}
}
使用示例:
/**
* 生成用户得分评语内容
* @param busiPaperScoreConfPo 用户得分配置信息
* @param busiUserPaperPo 用户答卷信息
* @return 上传到OSS之后的地址
*/
private String getCommentTitleUrl(BusiPaperScoreConfPo busiPaperScoreConfPo, BusiUserPaperPo busiUserPaperPo){
Long startTime = System.currentTimeMillis();
String randomUserPaperCode = Md5Utils.md5Hex(Math.random() + busiUserPaperPo.getUserPaperCode());
//用户评语底图本地保存地址
String backgroundPath = randomUserPaperCode + "_baseImg.png";
log.debug("getCommentTitleUrl backgroundPath = {}", backgroundPath);
//下载用户评语图片到本地
DownloadUtil.download(busiPaperScoreConfPo.getCommentTitleUrl(), backgroundPath);
//试卷评语默认头像地址
String userHeadImagePath = busiUserPaperPo.getUserId() + "_headBaseImg.png";
log.debug("getCommentTitleUrl userHeadImagePath + {}", userHeadImagePath);
//用户头像网络地址
String userHeadImageUrl = apolloConfig.getPaperCommentDefaultHeadImageUrl();
UserInfoModel userInfoModel = userInfoGrpcClient.getUserInfoByUserId(busiUserPaperPo.getUserId());
if(userInfoModel != null && StringUtils.isNotBlank(userInfoModel.getHeadImgUrl())){
log.debug("userInfoModel.getHeadImgUrl() = {}", userInfoModel.getHeadImgUrl());
userHeadImageUrl = userInfoModel.getHeadImgUrl();
}
//下载用户头像到本地
DownloadUtil.download(userHeadImageUrl, userHeadImagePath);
//圆形头像地址
String userHeadImageCircularPath = busiUserPaperPo.getUserId() + "_headCircularBaseImg.png";
//头像变圆
MergeImgUtil.makeRoundedCorner(userHeadImagePath,userHeadImageCircularPath,720);
//圆形缩放后的图片
String userHeadImageCircularSmallPath = busiUserPaperPo.getUserId() + "_headCircularSmallBaseImg.png";
//圆形头像缩放
MergeImgUtil.reSize(
new File(userHeadImageCircularPath),
new File(userHeadImageCircularSmallPath),
apolloConfig.getInvitePosterPapeUserHeadSmallWide(),
apolloConfig.getInvitePosterPapeUserHeadSmallHigh(),
true);
//把用户头像合成到指定底图上
MergeImgUtil.mergeImage(
backgroundPath,
userHeadImageCircularSmallPath,
apolloConfig.getInvitePosterPapeUserHeadWide(),
apolloConfig.getInvitePosterPapeUserHeadHigh()
);
//试卷评语皇冠图片
String paperCommentCrownImageUrl = apolloConfig.getPaperCommentCrownImageUrl();
//皇冠地址
//皇冠地址
String paperCommentCrownImagePath = busiUserPaperPo.getUserId() + "_headCrownBaseImg.png";
//下载皇冠图片到本地
DownloadUtil.download(paperCommentCrownImageUrl, paperCommentCrownImagePath);
//把皇冠合成到指定底图上
MergeImgUtil.mergeImage(
backgroundPath,
paperCommentCrownImagePath,
apolloConfig.getInvitePosterPapeCrownWide(),
apolloConfig.getInvitePosterPapeCrownHigh()
);
//待存储的地址
String tarImgPath = randomUserPaperCode + ".png";
try {
//用户昵称
String userNickname = apolloConfig.getPaperCommentDefaultUserNickname();
if(userInfoModel != null && StringUtils.isNotBlank(userInfoModel.getNickname())){
userNickname = userInfoModel.getNickname();
if(userInfoModel.getNickname().length() > apolloConfig.getUserPaperMaxNickNameLength().longValue()){
userNickname = userInfoModel.getNickname().substring(0, 6);
userNickname = userNickname + "...";
}
}
//合成图片
try {
MergeImgUtil.addWaterMark(
backgroundPath,
tarImgPath,
userNickname,
apolloConfig.getInvitePosterPapeUserNicknameWide(),
apolloConfig.getInvitePosterPapeUserNicknameHigh(),
153,31,13,255,
32);
} catch (IOException e) {
log.error("getCommentTitleUrl addWaterMark has exception." , e);
e.printStackTrace();
//如果合成字体有问题,则直接返回合成头像后的图片并上上传
//用户评语最终合成地址
String commentTitlePath = "/invite/posterUrl/testPaper/" + busiUserPaperPo.getPaperCode() + "/" + busiUserPaperPo.getUserPaperCode() + "/" + backgroundPath;
cosClientWrapper.putObject(commentTitlePath, new File(backgroundPath));
return commentTitlePath;
}
//用户评语最终合成地址
String commentTitlePath = "/invite/posterUrl/testPaper/" + busiUserPaperPo.getPaperCode() + "/" + busiUserPaperPo.getUserPaperCode() + "/" + tarImgPath;
cosClientWrapper.putObject(commentTitlePath, new File(tarImgPath));
return commentTitlePath;
} finally {
new File(backgroundPath).delete();
new File(userHeadImagePath).delete();
new File(userHeadImageCircularPath).delete();
new File(userHeadImageCircularSmallPath).delete();
new File(paperCommentCrownImagePath).delete();
new File(tarImgPath).delete();
Long endTime = System.currentTimeMillis();
log.info("getCommentTitleUrl end endTime = {}, time = {}", endTime, endTime - startTime);
}
}