使用Java實(shí)現(xiàn)一個(gè)功能完整的圖片水印工具
前言
在日常開(kāi)發(fā)中,我們經(jīng)常需要給圖片添加水印,比如版權(quán)保護(hù)、品牌展示等場(chǎng)景。本文將介紹一個(gè)功能完整的Java圖片水印工具,支持文字水印和圖片水印,并提供多種位置布局、旋轉(zhuǎn)角度、透明度調(diào)節(jié)等功能。
功能特性
- ? 支持文字水印和圖片水印兩種模式
- ? 五種水印位置:居中、對(duì)角線平鋪、右下角、左上角、自定義坐標(biāo)
- ? 支持水印旋轉(zhuǎn)(任意角度)
- ? 支持透明度調(diào)節(jié)
- ? 支持字體、顏色、大小自定義
- ? 圖片水印自動(dòng)縮放(不超過(guò)原圖1/5)
- ? 抗鋸齒處理,保證圖片質(zhì)量
項(xiàng)目結(jié)構(gòu)
├── WatermarkConfig.java # 水印配置類 └── ImageWatermarkUtil.java # 水印工具類
一、WatermarkConfig 配置類
這個(gè)類用于封裝水印的所有參數(shù),通過(guò)鏈?zhǔn)秸{(diào)用可以方便地配置水印效果。
源代碼
import lombok.Data;
import java.awt.*;
/**
* 圖片水印參數(shù)配置類
*/
@Data
public class WatermarkConfig {
/** 水印文字內(nèi)容 */
private String text;
/** 字體名稱(默認(rèn)宋體) */
private String fontName = "SimSun";
/** 字體大小(默認(rèn)24px) */
private int fontSize = 24;
/** 字體顏色(默認(rèn)灰色半透明) */
private Color color = new Color(128, 128, 128, 100);
/** 水印透明度(0.0-1.0,默認(rèn)0.3) */
private float alpha = 0.3f;
/** 旋轉(zhuǎn)角度(度數(shù),默認(rèn)-30度) */
private int rotateAngle = 0;
/** 水印位置類型 */
private PositionType positionType = PositionType.DIAGONAL;
/** X坐標(biāo)偏移(當(dāng)positionType為CUSTOM時(shí)使用) */
private int x = 0;
/** Y坐標(biāo)偏移(當(dāng)positionType為CUSTOM時(shí)使用) */
private int y = 0;
/** 平鋪水印時(shí)的水平間距(默認(rèn)200px) */
private int horizontalSpacing = 200;
/** 平鋪水印時(shí)的垂直間距(默認(rèn)150px) */
private int verticalSpacing = 150;
/** 水印圖片路徑(圖片水印時(shí)使用) */
private String watermarkImagePath;
public enum PositionType {
/** 居中 */
CENTER,
/** 對(duì)角線平鋪 */
DIAGONAL,
/** 右下角 */
BOTTOM_RIGHT,
/** 左上角 */
TOP_LEFT,
/** 自定義坐標(biāo) */
CUSTOM
}
}
配置參數(shù)說(shuō)明
| 參數(shù) | 類型 | 默認(rèn)值 | 說(shuō)明 |
|---|---|---|---|
| text | String | - | 水印文字內(nèi)容(文字水印必需) |
| fontName | String | SimSun | 字體名稱 |
| fontSize | int | 24 | 字體大小(像素) |
| color | Color | 灰色半透明 | 字體顏色 |
| alpha | float | 0.3 | 透明度(0.0-1.0) |
| rotateAngle | int | 0 | 旋轉(zhuǎn)角度(度數(shù)) |
| positionType | PositionType | DIAGONAL | 水印位置類型 |
| x | int | 0 | 自定義X坐標(biāo)(CUSTOM模式) |
| y | int | 0 | 自定義Y坐標(biāo)(CUSTOM模式) |
| horizontalSpacing | int | 200 | 平鋪水平間距 |
| verticalSpacing | int | 150 | 平鋪垂直間距 |
| watermarkImagePath | String | - | 水印圖片路徑(圖片水印必需) |
二、ImageWatermarkUtil 工具類
這是核心工具類,提供了添加文字水印和圖片水印的方法。
源代碼
import com.sygt.access.config.WatermarkConfig;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
/**
* 圖片水印工具類
* 支持文字水印和圖片水印
*/
public class ImageWatermarkUtil {
/**
* 添加文字水印
*
* @param srcImagePath 源圖片路徑
* @param destImagePath 目標(biāo)圖片路徑
* @param config 水印配置
* @throws IOException IO異常
*/
public static void addTextWatermark(String srcImagePath, String destImagePath,
WatermarkConfig config) throws IOException {
// 讀取原始圖片
BufferedImage srcImage = ImageIO.read(new File(srcImagePath));
// 創(chuàng)建Graphics2D對(duì)象
Graphics2D g2d = srcImage.createGraphics();
try {
// 設(shè)置抗鋸齒
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// 設(shè)置字體
Font font = new Font(config.getFontName(), Font.PLAIN, config.getFontSize());
g2d.setFont(font);
// 設(shè)置顏色和透明度
Color color = config.getColor();
g2d.setColor(new Color(color.getRed(), color.getGreen(),
color.getBlue(), (int)(color.getAlpha() * config.getAlpha())));
// 根據(jù)位置類型繪制水印(旋轉(zhuǎn)邏輯移到drawTextWatermark內(nèi)部處理)
drawTextWatermark(g2d, config, srcImage);
} finally {
g2d.dispose();
}
// 保存圖片
String format = destImagePath.substring(destImagePath.lastIndexOf(".") + 1);
ImageIO.write(srcImage, format, new File(destImagePath));
}
/**
* 添加圖片水印
*
* @param srcImagePath 源圖片路徑
* @param destImagePath 目標(biāo)圖片路徑
* @param config 水印配置
* @throws IOException IO異常
*/
public static void addImageWatermark(String srcImagePath, String destImagePath,
WatermarkConfig config) throws IOException {
BufferedImage srcImage = ImageIO.read(new File(srcImagePath));
BufferedImage watermarkImage = ImageIO.read(new File(config.getWatermarkImagePath()));
Graphics2D g2d = srcImage.createGraphics();
try {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// 設(shè)置圖片水印透明度
AlphaComposite alphaComposite = AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, config.getAlpha());
g2d.setComposite(alphaComposite);
// 繪制圖片水印
drawImageWatermark(g2d, watermarkImage, config, srcImage);
} finally {
g2d.dispose();
}
String format = destImagePath.substring(destImagePath.lastIndexOf(".") + 1);
ImageIO.write(srcImage, format, new File(destImagePath));
}
/**
* 繪制文字水印
*/
private static void drawTextWatermark(Graphics2D g2d, WatermarkConfig config,
BufferedImage srcImage) {
String text = config.getText();
FontMetrics fontMetrics = g2d.getFontMetrics();
int textWidth = fontMetrics.stringWidth(text);
int textHeight = fontMetrics.getHeight();
int imageWidth = srcImage.getWidth();
int imageHeight = srcImage.getHeight();
float rotateAngle = config.getRotateAngle();
switch (config.getPositionType()) {
case CENTER:
int centerX = (imageWidth - textWidth) / 2;
int centerY = (imageHeight + textHeight) / 2;
if (rotateAngle != 0) {
drawRotatedText(g2d, text, centerX + textWidth/2, centerY - textHeight/2,
rotateAngle, centerX, centerY);
} else {
g2d.drawString(text, centerX, centerY);
}
break;
case DIAGONAL:
// 對(duì)角線平鋪水印 - 全局旋轉(zhuǎn)整個(gè)畫布
AffineTransform originalTransform = g2d.getTransform();
if (rotateAngle != 0) {
g2d.rotate(Math.toRadians(rotateAngle));
}
try {
for (int x = -imageWidth; x < imageWidth * 2; x += config.getHorizontalSpacing()) {
for (int y = -imageHeight; y < imageHeight * 2; y += config.getVerticalSpacing()) {
g2d.drawString(text, x, y);
}
}
} finally {
// 恢復(fù)原始變換
g2d.setTransform(originalTransform);
}
break;
case BOTTOM_RIGHT:
int margin = 20;
int bottomRightX = imageWidth - textWidth - margin;
int descent = fontMetrics.getDescent();
int bottomRightY = imageHeight - descent - margin;
if (rotateAngle != 0) {
// 以水印中心為旋轉(zhuǎn)點(diǎn)
int centerXRotate = bottomRightX + textWidth / 2;
int centerYRotate = bottomRightY - textHeight / 2;
drawRotatedText(g2d, text, centerXRotate, centerYRotate,
rotateAngle, bottomRightX, bottomRightY);
} else {
g2d.drawString(text, bottomRightX, bottomRightY);
}
break;
case TOP_LEFT:
int topLeftX = 20;
int topLeftY = textHeight + 20;
if (rotateAngle != 0) {
int centerXRotate = topLeftX + textWidth / 2;
int centerYRotate = topLeftY - textHeight / 2;
drawRotatedText(g2d, text, centerXRotate, centerYRotate,
rotateAngle, topLeftX, topLeftY);
} else {
g2d.drawString(text, topLeftX, topLeftY);
}
break;
case CUSTOM:
int customX = config.getX();
int customY = config.getY();
if (rotateAngle != 0) {
int centerXRotate = customX + textWidth / 2;
int centerYRotate = customY - textHeight / 2;
drawRotatedText(g2d, text, centerXRotate, centerYRotate,
rotateAngle, customX, customY);
} else {
g2d.drawString(text, customX, customY);
}
break;
}
}
/**
* 繪制旋轉(zhuǎn)的文字
*/
private static void drawRotatedText(Graphics2D g2d, String text,
int rotateCenterX, int rotateCenterY,
float rotateAngle, int drawX, int drawY) {
// 保存原始變換狀態(tài)
AffineTransform originalTransform = g2d.getTransform();
try {
// 平移到旋轉(zhuǎn)中心,旋轉(zhuǎn),再平移回去
g2d.translate(rotateCenterX, rotateCenterY);
g2d.rotate(Math.toRadians(rotateAngle));
g2d.translate(-rotateCenterX, -rotateCenterY);
// 繪制文字
g2d.drawString(text, drawX, drawY);
} finally {
// 恢復(fù)原始變換
g2d.setTransform(originalTransform);
}
}
/**
* 繪制圖片水印
*/
private static void drawImageWatermark(Graphics2D g2d, BufferedImage watermarkImage,
WatermarkConfig config, BufferedImage srcImage) {
int watermarkWidth = watermarkImage.getWidth();
int watermarkHeight = watermarkImage.getHeight();
// 調(diào)整水印大小(不超過(guò)原圖的1/5)
int maxWidth = srcImage.getWidth() / 5;
int maxHeight = srcImage.getHeight() / 5;
if (watermarkWidth > maxWidth || watermarkHeight > maxHeight) {
double scale = Math.min((double)maxWidth / watermarkWidth,
(double)maxHeight / watermarkHeight);
watermarkWidth = (int)(watermarkWidth * scale);
watermarkHeight = (int)(watermarkHeight * scale);
}
// 縮放水印圖片
BufferedImage scaledWatermark = new BufferedImage(watermarkWidth, watermarkHeight,
BufferedImage.TYPE_INT_ARGB);
Graphics2D watermarkG2d = scaledWatermark.createGraphics();
try {
watermarkG2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
watermarkG2d.drawImage(watermarkImage, 0, 0, watermarkWidth, watermarkHeight, null);
} finally {
watermarkG2d.dispose();
}
float rotateAngle = config.getRotateAngle();
// 繪制水印
switch (config.getPositionType()) {
case DIAGONAL:
// 平鋪圖片水印 - 全局旋轉(zhuǎn)整個(gè)畫布
AffineTransform originalTransform = g2d.getTransform();
if (rotateAngle != 0) {
g2d.rotate(Math.toRadians(rotateAngle));
}
try {
for (int i = 0; i < srcImage.getWidth(); i += watermarkWidth + 100) {
for (int j = 0; j < srcImage.getHeight(); j += watermarkHeight + 100) {
g2d.drawImage(scaledWatermark, i, j, watermarkWidth, watermarkHeight, null);
}
}
} finally {
g2d.setTransform(originalTransform);
}
return;
default:
// 計(jì)算位置
int x = 0, y = 0;
switch (config.getPositionType()) {
case CENTER:
x = (srcImage.getWidth() - watermarkWidth) / 2;
y = (srcImage.getHeight() - watermarkHeight) / 2;
break;
case BOTTOM_RIGHT:
x = srcImage.getWidth() - watermarkWidth - 20;
y = srcImage.getHeight() - watermarkHeight - 20;
break;
case TOP_LEFT:
x = 20;
y = 20;
break;
case CUSTOM:
x = config.getX();
y = config.getY();
break;
}
if (rotateAngle != 0) {
// 繪制旋轉(zhuǎn)的圖片水印
drawRotatedImage(g2d, scaledWatermark, x + watermarkWidth/2,
y + watermarkHeight/2, rotateAngle, x, y,
watermarkWidth, watermarkHeight);
} else {
g2d.drawImage(scaledWatermark, x, y, watermarkWidth, watermarkHeight, null);
}
break;
}
}
/**
* 繪制旋轉(zhuǎn)的圖片
*/
private static void drawRotatedImage(Graphics2D g2d, BufferedImage image,
int rotateCenterX, int rotateCenterY,
float rotateAngle, int drawX, int drawY,
int width, int height) {
// 保存原始變換狀態(tài)
AffineTransform originalTransform = g2d.getTransform();
try {
// 平移到旋轉(zhuǎn)中心,旋轉(zhuǎn),再平移回去
g2d.translate(rotateCenterX, rotateCenterY);
g2d.rotate(Math.toRadians(rotateAngle));
g2d.translate(-rotateCenterX, -rotateCenterY);
// 繪制圖片
g2d.drawImage(image, drawX, drawY, width, height, null);
} finally {
// 恢復(fù)原始變換
g2d.setTransform(originalTransform);
}
}
}
核心方法說(shuō)明
1. addTextWatermark - 添加文字水印
public static void addTextWatermark(String srcImagePath, String destImagePath,
WatermarkConfig config) throws IOException
參數(shù)說(shuō)明:
srcImagePath: 原始圖片路徑destImagePath: 輸出圖片路徑config: 水印配置對(duì)象
2. addImageWatermark - 添加圖片水印
public static void addImageWatermark(String srcImagePath, String destImagePath,
WatermarkConfig config) throws IOException
參數(shù)說(shuō)明:
srcImagePath: 原始圖片路徑destImagePath: 輸出圖片路徑config: 水印配置對(duì)象(需設(shè)置 watermarkImagePath)
三、使用示例
下面提供完整的使用示例,包含 main 方法測(cè)試。
package com.sygt.access.test;
import com.sygt.access.config.WatermarkConfig;
import com.sygt.access.utils.ImageWatermarkUtil;
import java.awt.Color;
import java.io.IOException;
/**
* 水印工具測(cè)試類
*/
public class WatermarkTest {
public static void main(String[] args) {
// 原始圖片路徑
String srcImagePath = "D:/images/source.jpg";
// 輸出圖片路徑
String destImagePath = "D:/images/output.jpg";
// 水印圖片路徑(圖片水印使用)
String watermarkImagePath = "D:/images/logo.png";
try {
// ========== 示例1:添加文字水?。▽?duì)角線平鋪) ==========
System.out.println("示例1:添加文字水印(對(duì)角線平鋪)");
WatermarkConfig textConfig = new WatermarkConfig();
textConfig.setText("版權(quán)所有 @2024");
textConfig.setFontName("Microsoft YaHei");
textConfig.setFontSize(30);
textConfig.setColor(new Color(128, 128, 128, 150));
textConfig.setAlpha(0.4f);
textConfig.setRotateAngle(-30);
textConfig.setPositionType(WatermarkConfig.PositionType.DIAGONAL);
textConfig.setHorizontalSpacing(300);
textConfig.setVerticalSpacing(200);
ImageWatermarkUtil.addTextWatermark(srcImagePath,
"D:/images/output_text_diagonal.jpg", textConfig);
System.out.println("文字水印添加完成!");
// ========== 示例2:添加文字水?。ㄓ蚁陆牵?==========
System.out.println("\n示例2:添加文字水?。ㄓ蚁陆牵?);
WatermarkConfig textConfig2 = new WatermarkConfig();
textConfig2.setText("僅供內(nèi)部使用");
textConfig2.setFontName("SimHei");
textConfig2.setFontSize(24);
textConfig2.setColor(new Color(255, 0, 0, 200));
textConfig2.setAlpha(0.8f);
textConfig2.setRotateAngle(0);
textConfig2.setPositionType(WatermarkConfig.PositionType.BOTTOM_RIGHT);
ImageWatermarkUtil.addTextWatermark(srcImagePath,
"D:/images/output_text_bottomright.jpg", textConfig2);
System.out.println("文字水印添加完成!");
// ========== 示例3:添加文字水?。ň又行D(zhuǎn)) ==========
System.out.println("\n示例3:添加文字水?。ň又行D(zhuǎn))");
WatermarkConfig textConfig3 = new WatermarkConfig();
textConfig3.setText("機(jī)密文檔");
textConfig3.setFontName("Microsoft YaHei");
textConfig3.setFontSize(60);
textConfig3.setColor(new Color(255, 0, 0, 100));
textConfig3.setAlpha(0.5f);
textConfig3.setRotateAngle(45);
textConfig3.setPositionType(WatermarkConfig.PositionType.CENTER);
ImageWatermarkUtil.addTextWatermark(srcImagePath,
"D:/images/output_text_center.jpg", textConfig3);
System.out.println("文字水印添加完成!");
// ========== 示例4:添加圖片水?。ㄓ蚁陆牵?==========
System.out.println("\n示例4:添加圖片水?。ㄓ蚁陆牵?);
WatermarkConfig imageConfig = new WatermarkConfig();
imageConfig.setWatermarkImagePath(watermarkImagePath);
imageConfig.setAlpha(0.7f);
imageConfig.setRotateAngle(0);
imageConfig.setPositionType(WatermarkConfig.PositionType.BOTTOM_RIGHT);
ImageWatermarkUtil.addImageWatermark(srcImagePath,
"D:/images/output_image_bottomright.jpg", imageConfig);
System.out.println("圖片水印添加完成!");
// ========== 示例5:添加圖片水?。▽?duì)角線平鋪) ==========
System.out.println("\n示例5:添加圖片水?。▽?duì)角線平鋪)");
WatermarkConfig imageConfig2 = new WatermarkConfig();
imageConfig2.setWatermarkImagePath(watermarkImagePath);
imageConfig2.setAlpha(0.2f);
imageConfig2.setRotateAngle(-15);
imageConfig2.setPositionType(WatermarkConfig.PositionType.DIAGONAL);
ImageWatermarkUtil.addImageWatermark(srcImagePath,
"D:/images/output_image_diagonal.jpg", imageConfig2);
System.out.println("圖片水印添加完成!");
// ========== 示例6:添加文字水?。ㄗ远x坐標(biāo)) ==========
System.out.println("\n示例6:添加文字水印(自定義坐標(biāo))");
WatermarkConfig textConfig4 = new WatermarkConfig();
textConfig4.setText("自定義位置水印");
textConfig4.setFontName("Microsoft YaHei");
textConfig4.setFontSize(20);
textConfig4.setColor(new Color(0, 0, 255, 180));
textConfig4.setAlpha(0.7f);
textConfig4.setRotateAngle(0);
textConfig4.setPositionType(WatermarkConfig.PositionType.CUSTOM);
textConfig4.setX(100);
textConfig4.setY(100);
ImageWatermarkUtil.addTextWatermark(srcImagePath,
"D:/images/output_text_custom.jpg", textConfig4);
System.out.println("文字水印添加完成!");
System.out.println("\n所有水印添加任務(wù)完成!");
} catch (IOException e) {
System.err.println("水印添加失?。? + e.getMessage());
e.printStackTrace();
}
}
}
四、效果展示
1. 文字水印效果
| 位置類型 | 效果說(shuō)明 |
|---|---|
| 對(duì)角線平鋪 | 水印文字按指定角度和間距平鋪整個(gè)圖片 |
| 右下角 | 單個(gè)水印位于圖片右下角 |
| 左上角 | 單個(gè)水印位于圖片左上角 |
| 居中 | 單個(gè)水印位于圖片中央 |
| 自定義坐標(biāo) | 水印位于指定的X、Y坐標(biāo)位置 |
2. 圖片水印效果
| 位置類型 | 效果說(shuō)明 |
|---|---|
| 對(duì)角線平鋪 | 水印圖片自動(dòng)縮放后平鋪整個(gè)圖片 |
| 右下角 | 單個(gè)水印位于圖片右下角 |
| 左上角 | 單個(gè)水印位于圖片左上角 |
| 居中 | 單個(gè)水印位于圖片中央 |
| 自定義坐標(biāo) | 水印位于指定的X、Y坐標(biāo)位置 |
五、技術(shù)要點(diǎn)解析
1. 抗鋸齒處理
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
開(kāi)啟抗鋸齒可以避免文字邊緣出現(xiàn)鋸齒,提高水印的清晰度。
2. 透明度控制
對(duì)于文字水印,通過(guò)Color的alpha值控制透明度:
g2d.setColor(new Color(color.getRed(), color.getGreen(),
color.getBlue(), (int)(color.getAlpha() * config.getAlpha())));
對(duì)于圖片水印,使用AlphaComposite控制透明度:
AlphaComposite alphaComposite = AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, config.getAlpha());
g2d.setComposite(alphaComposite);
3. 旋轉(zhuǎn)實(shí)現(xiàn)
使用AffineTransform實(shí)現(xiàn)旋轉(zhuǎn),核心邏輯:
- 保存原始變換狀態(tài)
- 平移到旋轉(zhuǎn)中心
- 執(zhí)行旋轉(zhuǎn)
- 平移回原位置
- 繪制內(nèi)容
- 恢復(fù)原始變換狀態(tài)
AffineTransform originalTransform = g2d.getTransform();
try {
g2d.translate(rotateCenterX, rotateCenterY);
g2d.rotate(Math.toRadians(rotateAngle));
g2d.translate(-rotateCenterX, -rotateCenterY);
g2d.drawString(text, drawX, drawY);
} finally {
g2d.setTransform(originalTransform);
}
4. 圖片自動(dòng)縮放
圖片水印會(huì)自動(dòng)縮放到不超過(guò)原圖的1/5,避免水印過(guò)大影響原圖:
int maxWidth = srcImage.getWidth() / 5;
int maxHeight = srcImage.getHeight() / 5;
if (watermarkWidth > maxWidth || watermarkHeight > maxHeight) {
double scale = Math.min((double)maxWidth / watermarkWidth,
(double)maxHeight / watermarkHeight);
watermarkWidth = (int)(watermarkWidth * scale);
watermarkHeight = (int)(watermarkHeight * scale);
}
六、常見(jiàn)問(wèn)題
Q1:中文亂碼怎么辦?
確保字體名稱正確,可以使用系統(tǒng)已安裝的中文字體:
config.setFontName("Microsoft YaHei"); // 微軟雅黑
config.setFontName("SimHei"); // 黑體
config.setFontName("SimSun"); // 宋體
Q2:如何調(diào)整水印密度?
對(duì)于平鋪水印,調(diào)整水平和垂直間距:
config.setHorizontalSpacing(300); // 水平間距 config.setVerticalSpacing(200); // 垂直間距
Q3:支持的圖片格式有哪些?
支持所有ImageIO支持的格式:JPG、PNG、BMP、GIF等。
Q4:如何批量處理圖片?
遍歷文件目錄,對(duì)每張圖片調(diào)用水印方法即可:
File folder = new File("D:/images");
for (File file : folder.listFiles()) {
if (file.getName().endsWith(".jpg")) {
ImageWatermarkUtil.addTextWatermark(
file.getPath(),
"D:/output/" + file.getName(),
config
);
}
}
七、依賴說(shuō)明
本工具僅使用JDK標(biāo)準(zhǔn)庫(kù),無(wú)需額外依賴。
如果使用Lombok簡(jiǎn)化代碼,需要添加Lombok依賴:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
如果不使用Lombok,需要為WatermarkConfig類添加getter/setter方法。
八、總結(jié)
本文介紹了一個(gè)功能完整的Java圖片水印工具,具有以下特點(diǎn):
- 功能全面:支持文字和圖片水印,多種位置布局
- 靈活配置:通過(guò)配置類輕松定制水印效果
- 代碼清晰:結(jié)構(gòu)清晰,注釋完整,易于理解和擴(kuò)展
- 即插即用:無(wú)需復(fù)雜配置,直接調(diào)用即可使用
以上就是使用Java實(shí)現(xiàn)一個(gè)功能完整的圖片水印工具的詳細(xì)內(nèi)容,更多關(guān)于Java圖片水印工具的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java實(shí)現(xiàn)解析ini文件對(duì)應(yīng)到JavaBean中
ini 文件是Initialization File的縮寫,即初始化文件,是windows的系統(tǒng)配置文件所采用的存儲(chǔ)格式。這篇文章主要介紹了通過(guò)Java實(shí)現(xiàn)解析ini文件對(duì)應(yīng)到JavaBean中,需要的可以參考一下2022-01-01
Java中的延遲隊(duì)列DelayQueue詳細(xì)解析
這篇文章主要介紹了Java中的延遲隊(duì)列DelayQueue詳細(xì)解析,JDK自身支持延遲隊(duì)列的數(shù)據(jù)結(jié)構(gòu),其實(shí)類:java.util.concurrent.DelayQueue,<BR>我們通過(guò)閱讀源碼的方式理解該延遲隊(duì)列類的實(shí)現(xiàn)過(guò)程,需要的朋友可以參考下2023-12-12
redis之基于SpringBoot實(shí)現(xiàn)Redis stream實(shí)時(shí)流事件處理方式
這篇文章主要介紹了redis之基于SpringBoot實(shí)現(xiàn)Redis stream實(shí)時(shí)流事件處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06
reactor-logback的AsyncAppender執(zhí)行流程源碼解讀
這篇文章主要為大家介紹了reactor-logback的AsyncAppender執(zhí)行流程源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
Springboot如何實(shí)現(xiàn)自定義異常數(shù)據(jù)
這篇文章主要介紹了Springboot如何實(shí)現(xiàn)自定義異常數(shù)據(jù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
Java SpringMVC框架開(kāi)發(fā)之?dāng)?shù)據(jù)導(dǎo)出Excel文件格式實(shí)例詳解
這篇文章主要介紹了Java基礎(chǔ)開(kāi)發(fā)之?dāng)?shù)據(jù)導(dǎo)出Excel文件格式實(shí)例詳解,需要的朋友可以參考下2020-02-02

