You've already forked FrameTour-BE
feat(image): 添加水印缩放功能支持
- 在 WatermarkConfig 中新增 scale 字段用于控制整体缩放倍数 - 在 WatermarkStage 中读取并传递 scale 参数到 WatermarkInfo - 在 PrinterDefaultWatermarkOperator 中实现所有位置和尺寸的缩放逻辑 - 对偏移量、边距、字体大小、二维码尺寸等应用缩放因子 - 更新图像绘制相关参数计算方式以支持动态缩放 - 优化二维码圆形背景和头像绘制的缩放处理 - 确保缩放后的水印元素保持相对位置和视觉一致性
This commit is contained in:
@@ -34,4 +34,11 @@ public class WatermarkConfig {
|
||||
* 二维码文件
|
||||
*/
|
||||
private final File qrcodeFile;
|
||||
|
||||
/**
|
||||
* 缩放倍数,用于将所有定位和大小乘以该倍数
|
||||
* 默认值为 1.0(不缩放)
|
||||
*/
|
||||
@Builder.Default
|
||||
private final Double scale = 1.0;
|
||||
}
|
||||
|
||||
@@ -170,6 +170,12 @@ public class WatermarkStage extends AbstractPipelineStage<PhotoProcessContext> {
|
||||
info.setQrcodeFile(qrcodeFile);
|
||||
}
|
||||
|
||||
// 从 config 读取缩放倍数
|
||||
Double scale = config.getScale();
|
||||
if (scale != null) {
|
||||
info.setScale(scale);
|
||||
}
|
||||
|
||||
// 根据旋转状态自己处理 offsetLeft
|
||||
if (context.isRotationApplied()) {
|
||||
if (context.getImageRotation() == 90) {
|
||||
|
||||
@@ -33,6 +33,13 @@ public class WatermarkInfo {
|
||||
private Integer offsetLeft;
|
||||
private Integer offsetRight;
|
||||
|
||||
/**
|
||||
* 缩放倍数,用于将所有定位和大小乘以该倍数
|
||||
* 例如: scale=2.0 表示所有尺寸和位置都放大2倍
|
||||
* null 表示使用默认值1.0(不缩放)
|
||||
*/
|
||||
private Double scale;
|
||||
|
||||
public String getDatetimeLine() {
|
||||
if (datetimeLine == null) {
|
||||
datetimeLine = DateUtil.format(datetime, dtFormat);
|
||||
|
||||
@@ -64,11 +64,14 @@ public class PrinterDefaultWatermarkOperator implements IOperator {
|
||||
|
||||
@Override
|
||||
public File process(WatermarkInfo info) throws ImageWatermarkException {
|
||||
// 获取四边偏移值,优先使用传入的值,否则使用默认值
|
||||
int offsetTop = info.getOffsetTop() != null ? info.getOffsetTop() : DEFAULT_OFFSET_TOP;
|
||||
int offsetBottom = info.getOffsetBottom() != null ? info.getOffsetBottom() : DEFAULT_OFFSET_BOTTOM;
|
||||
int offsetLeft = info.getOffsetLeft() != null ? info.getOffsetLeft() : DEFAULT_OFFSET_LEFT;
|
||||
int offsetRight = info.getOffsetRight() != null ? info.getOffsetRight() : DEFAULT_OFFSET_RIGHT;
|
||||
// 获取缩放倍数,默认为1.0(不缩放)
|
||||
double scale = info.getScale() != null ? info.getScale() : 1.0;
|
||||
|
||||
// 获取四边偏移值,优先使用传入的值,否则使用默认值,并应用缩放
|
||||
int offsetTop = (int) ((info.getOffsetTop() != null ? info.getOffsetTop() : DEFAULT_OFFSET_TOP) * scale);
|
||||
int offsetBottom = (int) ((info.getOffsetBottom() != null ? info.getOffsetBottom() : DEFAULT_OFFSET_BOTTOM) * scale);
|
||||
int offsetLeft = (int) ((info.getOffsetLeft() != null ? info.getOffsetLeft() : DEFAULT_OFFSET_LEFT) * scale);
|
||||
int offsetRight = (int) ((info.getOffsetRight() != null ? info.getOffsetRight() : DEFAULT_OFFSET_RIGHT) * scale);
|
||||
|
||||
BufferedImage baseImage;
|
||||
BufferedImage qrcodeImage;
|
||||
@@ -86,17 +89,26 @@ public class PrinterDefaultWatermarkOperator implements IOperator {
|
||||
} catch (IOException e) {
|
||||
throw new ImageWatermarkException("图片打开失败");
|
||||
}
|
||||
|
||||
// 应用缩放到所有常量
|
||||
int scaledExtraBorder = (int) (EXTRA_BORDER_PX * scale);
|
||||
int scaledOffsetY = (int) (OFFSET_Y * scale);
|
||||
int scaledQrcodeSize = (int) (QRCODE_SIZE * scale);
|
||||
int scaledQrcodeOffsetY = (int) (QRCODE_OFFSET_Y * scale);
|
||||
int scaledScenicFontSize = (int) (SCENIC_FONT_SIZE * scale);
|
||||
int scaledDatetimeFontSize = (int) (DATETIME_FONT_SIZE * scale);
|
||||
|
||||
// 新图像画布
|
||||
BufferedImage newImage = new BufferedImage(baseImage.getWidth() + 2 * EXTRA_BORDER_PX, baseImage.getHeight() + 2 * EXTRA_BORDER_PX, BufferedImage.TYPE_INT_RGB);
|
||||
BufferedImage newImage = new BufferedImage(baseImage.getWidth() + 2 * scaledExtraBorder, baseImage.getHeight() + 2 * scaledExtraBorder, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g2d = newImage.createGraphics();
|
||||
g2d.setColor(BG_COLOR);
|
||||
g2d.fillRect(0, 0, newImage.getWidth(), newImage.getHeight());
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g2d.drawImage(baseImage, EXTRA_BORDER_PX, EXTRA_BORDER_PX, null);
|
||||
int newQrcodeHeight = QRCODE_SIZE;
|
||||
g2d.drawImage(baseImage, scaledExtraBorder, scaledExtraBorder, null);
|
||||
int newQrcodeHeight = scaledQrcodeSize;
|
||||
int newQrcodeWidth = (int) (newQrcodeHeight * 1.0 / qrcodeImage.getHeight() * qrcodeImage.getWidth());
|
||||
Font scenicFont = new Font(defaultFontName, Font.BOLD, SCENIC_FONT_SIZE);
|
||||
Font datetimeFont = new Font(defaultFontName, Font.BOLD, DATETIME_FONT_SIZE);
|
||||
Font scenicFont = new Font(defaultFontName, Font.BOLD, scaledScenicFontSize);
|
||||
Font datetimeFont = new Font(defaultFontName, Font.BOLD, scaledDatetimeFontSize);
|
||||
FontMetrics scenicFontMetrics = g2d.getFontMetrics(scenicFont);
|
||||
FontMetrics datetimeFontMetrics = g2d.getFontMetrics(datetimeFont);
|
||||
int scenicLineHeight = scenicFontMetrics.getHeight();
|
||||
@@ -106,13 +118,14 @@ public class PrinterDefaultWatermarkOperator implements IOperator {
|
||||
|
||||
// 二维码放置在左下角,距离左边缘图片宽度的5%,再加上左侧偏移
|
||||
int qrcodeOffsetX = (int) (newImage.getWidth() * QRCODE_LEFT_MARGIN_RATIO) + offsetLeft;
|
||||
int qrcodeOffsetY = EXTRA_BORDER_PX + baseImage.getHeight() - OFFSET_Y - newQrcodeHeight - offsetBottom;
|
||||
int qrcodeOffsetY = scaledExtraBorder + baseImage.getHeight() - scaledOffsetY - newQrcodeHeight - offsetBottom;
|
||||
Shape originalClip = g2d.getClip();
|
||||
|
||||
// 创建比二维码大10像素的白色圆形背景
|
||||
int whiteCircleSize = Math.max(newQrcodeWidth, newQrcodeHeight) + 10;
|
||||
// 创建比二维码大10像素的白色圆形背景(10像素也要缩放)
|
||||
int whiteCirclePadding = (int) (10 * scale);
|
||||
int whiteCircleSize = Math.max(newQrcodeWidth, newQrcodeHeight) + whiteCirclePadding;
|
||||
int whiteCircleX = qrcodeOffsetX - (whiteCircleSize - newQrcodeWidth) / 2;
|
||||
int whiteCircleY = qrcodeOffsetY + QRCODE_OFFSET_Y - (whiteCircleSize - newQrcodeHeight) / 2;
|
||||
int whiteCircleY = qrcodeOffsetY + scaledQrcodeOffsetY - (whiteCircleSize - newQrcodeHeight) / 2;
|
||||
|
||||
// 绘制白色圆形背景
|
||||
g2d.setColor(Color.WHITE);
|
||||
@@ -122,7 +135,7 @@ public class PrinterDefaultWatermarkOperator implements IOperator {
|
||||
// 用白色圆形尺寸裁切二维码(保持二维码原始尺寸,但用大圆裁切)
|
||||
Ellipse2D qrcodeCircle = new Ellipse2D.Double(whiteCircleX, whiteCircleY, whiteCircleSize, whiteCircleSize);
|
||||
g2d.setClip(qrcodeCircle);
|
||||
g2d.drawImage(qrcodeImage, qrcodeOffsetX, qrcodeOffsetY + QRCODE_OFFSET_Y, newQrcodeWidth, newQrcodeHeight, null);
|
||||
g2d.drawImage(qrcodeImage, qrcodeOffsetX, qrcodeOffsetY + scaledQrcodeOffsetY, newQrcodeWidth, newQrcodeHeight, null);
|
||||
g2d.setClip(originalClip);
|
||||
|
||||
// 在圆形二维码中央绘制圆形头像
|
||||
@@ -130,7 +143,7 @@ public class PrinterDefaultWatermarkOperator implements IOperator {
|
||||
// 计算圆形头像的尺寸和位置
|
||||
int avatarDiameter = (int) (newQrcodeHeight * 0.45);
|
||||
int avatarX = qrcodeOffsetX + (newQrcodeWidth - avatarDiameter) / 2;
|
||||
int avatarY = qrcodeOffsetY + QRCODE_OFFSET_Y + (newQrcodeHeight - avatarDiameter) / 2;
|
||||
int avatarY = qrcodeOffsetY + scaledQrcodeOffsetY + (newQrcodeHeight - avatarDiameter) / 2;
|
||||
|
||||
// 保存当前的渲染设置
|
||||
RenderingHints originalHints = g2d.getRenderingHints();
|
||||
@@ -149,10 +162,10 @@ public class PrinterDefaultWatermarkOperator implements IOperator {
|
||||
double faceHeight = faceImage.getHeight();
|
||||
double scaleX = avatarDiameter / faceWidth;
|
||||
double scaleY = avatarDiameter / faceHeight;
|
||||
double scale = Math.max(scaleX, scaleY); // 使用较大的缩放比例以填满圆形
|
||||
double faceScale = Math.max(scaleX, scaleY); // 使用较大的缩放比例以填满圆形
|
||||
|
||||
int scaledWidth = (int) (faceWidth * scale);
|
||||
int scaledHeight = (int) (faceHeight * scale);
|
||||
int scaledWidth = (int) (faceWidth * faceScale);
|
||||
int scaledHeight = (int) (faceHeight * faceScale);
|
||||
|
||||
// 计算居中位置
|
||||
int faceDrawX = avatarX + (avatarDiameter - scaledWidth) / 2;
|
||||
@@ -167,7 +180,7 @@ public class PrinterDefaultWatermarkOperator implements IOperator {
|
||||
}
|
||||
|
||||
// 计算文字与二维码垂直居中对齐的Y坐标
|
||||
int qrcodeTop = qrcodeOffsetY + QRCODE_OFFSET_Y;
|
||||
int qrcodeTop = qrcodeOffsetY + scaledQrcodeOffsetY;
|
||||
int qrcodeBottom = qrcodeTop + newQrcodeHeight;
|
||||
int qrcodeCenter = (qrcodeTop + qrcodeBottom) / 2;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user