import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.ClassUtils;
import org.apache.commons.lang.StringUtils;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import static sun.net.www.ParseUtil.toURI;
/**
* @Date : 2020/11/16
* @Time : 12:01
* @Description : 图片添加文字水印,或者图片水印,图片灰度操作,图片裁剪,按倍率放大图片,按倍率缩小图片
*/
@Slf4j
public class ImageMarkUtil {
//水印文字
public static String MARK_TEXT = "水印";
//水印字体
public static String FONT_NAME = "微软雅黑";
//水印文字粗细
public static int FONT_STYLE = Font.BOLD;
//水印文字大小
public static int FONT_SIZE = 30;
public static int MARGIN_ALL = 50;
//水印颜色
public static Color FONT_COLOR = Color.RED;
//水印的角度
public static int RADIANS = 30;
//水印透明度
public static float ALPHA = 0.5F;
//指定水印位置
public static int X = 0;
public static int Y = 0;
//默认的水印图片样式地址
public static String LOGO_IMAGE_URL = "classpath:static/image/viewfile.gif";
/**
* @param srcPath 目标图片路径
* @param toPath 图片输出路径
* @Description 设置图片水印
*/
public static void addWaterMark(String srcPath, String toPath) {
File file = getFile(srcPath);
//添加水印,返回处理后的图片对象
BufferedImage bufferedImage = addWaterMark(getFile(srcPath), 1);
//将处理后的图片输出到指定路径
outPutImage(file.getName(), toPath, bufferedImage);
}
/**
* @param srcPath 目标图片路径
* @param toPath 图片输出路径
* @Description 设置文字水印
*/
public static void addTextMark(String srcPath, String toPath) {
File file = getFile(srcPath);
//添加水印,返回处理后的图片对象
BufferedImage bufferedImage = addWaterMark(file, 0);
//将处理后的图片输出到指定路径
outPutImage(file.getName(), toPath, bufferedImage);
}
/**
* 图片灰度操作
*
* @param srcPath 处理的图片对象
* @param toPath 目标文件地址
* @throws Exception
*/
public static void imageGray(String srcPath, String toPath) {
try {
File file = getFile(srcPath);
BufferedImage bufferedImage = ImageIO.read(file);
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ColorConvertOp op = new ColorConvertOp(cs, null);
bufferedImage = op.filter(bufferedImage, null);
//将处理后的图片输出到指定路径
outPutImage(file.getName(), toPath, bufferedImage);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(StringUtils.isBlank(e.getMessage()) ? "图片灰白化异常" : e.getMessage());
}
}
/**
* 对图片裁剪,并把裁剪新图片保存
*
* @param srcPath 读取源图片路径
* @param toPath 写入图片路径
* @param x 剪切起始点x坐标
* @param y 剪切起始点y坐标
* @param width 剪切宽度
* @param height 剪切高度
* @throws IOException
*/
public static void cropImage(String srcPath, String toPath,
int x, int y, int width, int height) throws IOException {
try {
File file = getFile(srcPath);
ImageInputStream iis = ImageIO.createImageInputStream(new FileInputStream(file));
Iterator it = ImageIO.getImageReadersByFormatName(suffix(file));
ImageReader reader = (ImageReader) it.next();
//获取图片流
reader.setInput(iis, true);
ImageReadParam param = reader.getDefaultReadParam();
//定义一个矩形
Rectangle rect = new Rectangle(x, y, width, height);
//提供一个 BufferedImage,将其用作解码像素数据的目标。
param.setSourceRegion(rect);
BufferedImage bufferedImage = reader.read(0, param);
//将处理后的图片输出到指定路径
outPutImage(file.getName(), toPath, bufferedImage);
} catch (IOException e) {
e.printStackTrace();
throw e;
}
}
/**
* 按倍率缩小图片
*
* @param srcPath 读取图片路径
* @param toPath 写入图片路径
* @param widthRatio 宽度缩小比例
* @param heightRatio 高度缩小比例
* @throws RuntimeException
*/
public static void reduceImageByRatio(String srcPath, String toPath, int widthRatio, int heightRatio) {
try {
File file = getFile(srcPath);
// 构造Image对象
BufferedImage srcBuffer = ImageIO.read(file);
// 按比例缩减图像
BufferedImage imageBuffer = imageShrinkRatio(srcBuffer, widthRatio, heightRatio, true);
//将处理后的图片输出到指定路径
outPutImage(file.getName(), toPath, imageBuffer);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(StringUtils.isBlank(e.getMessage()) ? "按倍率缩小图片异常" : e.getMessage());
}
}
/**
* 按倍率放大图片
*
* @param srcPath 读取图形路径
* @param toPath 写入图片路径
* @param widthRatio 宽度放大比例
* @param heightRatio 高度放大比例
* @throws IOException
*/
public static void enlargementImageByRatio(String srcPath, String toPath, int widthRatio, int heightRatio) throws IOException {
try {
File file = getFile(srcPath);
//读入文件
String prefix = suffix(file);
// 构造Image对象
BufferedImage srcBuffer = ImageIO.read(file);
// 按比例缩减图像
BufferedImage imageBuffer = imageShrinkRatio(srcBuffer, widthRatio, heightRatio, false);
//将处理后的图片输出到指定路径
outPutImage(file.getName(), toPath, imageBuffer);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(StringUtils.isBlank(e.getMessage()) ? "按倍率放大图片异常" : e.getMessage());
}
}
/**
* 获取文件后缀名
*
* @param file
* @return
*/
private final static String suffix(File file) {
String fileName = file.getName();
return fileName.substring(fileName.indexOf(".") + 1);
}
/**
* 按照给点的比例放大或者缩小图像
* 当比例小于等于0时不发生任何变化
*
* @param originalImage 图像数据
* @param withdRatio 宽度缩减比例
* @param heightRatio 高度缩减比例
* @param isShrink 是否缩小
* @return 图像数据
*/
private static BufferedImage imageShrinkRatio(BufferedImage originalImage, Integer withdRatio, Integer heightRatio, boolean isShrink) {
if (withdRatio <= 0) {
withdRatio = 1;
}
if (heightRatio <= 0) {
heightRatio = 1;
}
int width = 0;
int height = 0;
if (isShrink) {
width = originalImage.getWidth() / withdRatio;
height = originalImage.getHeight() / heightRatio;
} else {
width = originalImage.getWidth() * withdRatio;
height = originalImage.getHeight() * heightRatio;
}
BufferedImage newImage = new BufferedImage(width, height, originalImage.getType());
Graphics g = newImage.getGraphics();
g.drawImage(originalImage, 0, 0, width, height, null);
g.dispose();
return newImage;
}
/**
* 添加图片水印操作,返回BufferedImage对象
*
* @param file File实例
* @param markType 0文字水印, 1图片水印
* @return 处理后的图片对象
* @throws Exception
*/
private static BufferedImage addWaterMark(File file, int markType) {
BufferedImage targetImage = null;
try {
// 加载待处理图片文件
Image img = ImageIO.read(file);
int width = img.getWidth(null);
int height = img.getHeight(null);
//创建目标图象文件
targetImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics2D = targetImage.createGraphics();
//使用绘图工具对象,将原图绘制到缓存图片对象
graphics2D.drawImage(img, 0, 0, img.getWidth(null), img.getHeight(null), null);
graphics2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, ALPHA));
graphics2D.rotate(Math.toRadians(RADIANS), targetImage.getHeight() / 2, targetImage.getHeight() / 2);//图层以中心为基点,转30度的弧度。
//0文字水印, 1图片水印
if (0 == markType) {
graphics2D.setFont(new Font(FONT_NAME, FONT_STYLE, FONT_SIZE));
graphics2D.setColor(FONT_COLOR);
//如果没有指定水印位置,默认是平铺,
if (X == 0 || Y == 0) {
//S1-获取文字水印的宽、高
int width1 = FONT_SIZE * getTextLength(MARK_TEXT);
int height1 = FONT_SIZE;
int x = -width / 2;
int y = -height / 2;
while (x < width * 1.6) {
y = -height / 2;
while (y < height * 1.6) {
graphics2D.drawString(MARK_TEXT, x, y);
y += height1 + MARGIN_ALL;
}
x += width1 + MARGIN_ALL;
}
} else {
graphics2D.drawString(MARK_TEXT, X, Y);
}
} else if (1 == markType) {
// 加载水印图片文件
Image markImg = ImageIO.read(getFile(LOGO_IMAGE_URL));
if (X == 0 || Y == 0) {
int x = -width / 2;
int y = -height / 2;
while (x < width * 1.6) {
y = -height / 2;
while (y < height * 1.6) {
graphics2D.drawImage(markImg, x, y, null);
y += markImg.getHeight(null) + MARGIN_ALL;
}
x += markImg.getWidth(null) + MARGIN_ALL;
}
} else {
graphics2D.drawImage(markImg, X, Y, null);
}
}
graphics2D.dispose();
} catch (Exception e) {
throw new RuntimeException(StringUtils.isBlank(e.getMessage()) ? "添加水印操作异常" : e.getMessage());
}
return targetImage;
}
/**
* @param fileName 文件名称
* @param realUploadPath 输出路径
* @param bufferedImage 处理后的图片对象
* @Description 将处理后的图片输出到指定路径
*/
private static void outPutImage(String fileName, String realUploadPath, BufferedImage bufferedImage) {
OutputStream os = null;
try {
os = new FileOutputStream(getFile(realUploadPath) + File.separator + fileName);
// 创建图像编码工具类
JPEGImageEncoder jpegImageEncoder = JPEGCodec.createJPEGEncoder(os);
// 使用图片编码工具类,输出缓存图像到模板图片文件
jpegImageEncoder.encode(bufferedImage);
} catch (Exception e) {
throw new RuntimeException(StringUtils.isBlank(e.getMessage()) ? "图片输出异常" : e.getMessage());
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
log.info(e.getMessage());
}
}
}
}
/**
* @param resourceLocation 路径
* @return 新的 File实例
* @Description 通过将给定的路径名的字符串转换成一个抽象路径名创建一个新的 File实例。
*/
private static File getFile(String resourceLocation) {
if (StringUtils.isBlank(resourceLocation)) {
throw new RuntimeException("Resource location must not be null");
}
if (resourceLocation.startsWith("classpath:")) {
String path = resourceLocation.substring("classpath:".length());
String description = "class path resource [" + path + "]";
ClassLoader cl = getDefaultClassLoader();
URL url = cl != null ? cl.getResource(path) : ClassLoader.getSystemResource(path);
if (url == null) {
throw new RuntimeException(description + " cannot be resolved to absolute file path because it does not exist");
} else {
return getFile(url, description);
}
} else {
try {
return getFile(new URL(resourceLocation), "URL");
} catch (MalformedURLException var5) {
return new File(resourceLocation);
}
}
}
/**
* @param resourceUrl 路径
* @param description 操作描述
* @return 新的 File实例
* @Description 通过将给定的URL到一个抽象路径名创建一个新的 File实例。
*/
private static File getFile(URL resourceUrl, String description) {
if (resourceUrl == null) {
throw new RuntimeException("Resource URL must not be null");
}
if (!"file".equals(resourceUrl.getProtocol())) {
throw new RuntimeException(description + " cannot be resolved to absolute file path because it does not reside in the file system: " + resourceUrl);
} else {
return new File(toURI(resourceUrl).getSchemeSpecificPart());
}
}
private static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
} catch (Throwable var3) {
}
if (cl == null) {
cl = ClassUtils.class.getClassLoader();
if (cl == null) {
try {
cl = ClassLoader.getSystemClassLoader();
} catch (Throwable var2) {
}
}
}
return cl;
}
private static int getTextLength(String text) {
int length = text.length();
for (int i = 0; i < text.length(); i++) {
String s = String.valueOf(text.charAt(i));
if (s.getBytes().length > 1) {
length++;
}
}
length = length % 2 == 0 ? length / 2 : length / 2 + 1;
return length;
}
}