You've already forked FrameTour-BE
feat(puzzle): 优化拼图生成逻辑并新增完整测试套件
- 在PuzzleGenerateServiceImpl中改进图片上传逻辑,支持contentType指定 - 在PuzzleImageRenderer中优化背景图片缩放算法,使用原生Java方法提升性能 - 修改scaleImage方法实现,完善多种图片适配模式(COVER、CONTAIN、FILL等) - 新增PuzzleRealScenarioIntegrationTest集成测试类,覆盖真实业务场景 - 添加PuzzleTemplateServiceImplTest单元测试,使用Mockito模拟数据库交互 - 创建MockImageUtil工具类,支持测试过程中生成各类模拟图片 - 构建PuzzleTestDataBuilder测试数据构造器,简化测试模板和元素创建 - 增加RealScenarioTestHelper辅助类,提升测试代码复用性 -
This commit is contained in:
@@ -19,6 +19,7 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Comparator;
|
||||
@@ -148,15 +149,16 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
|
||||
byte[] imageBytes = baos.toByteArray();
|
||||
|
||||
// 生成文件名
|
||||
String fileName = String.format("puzzle/%s/%s.%s",
|
||||
templateCode,
|
||||
String fileName = String.format("%s.%s",
|
||||
UUID.randomUUID().toString().replace("-", ""),
|
||||
outputFormat.toLowerCase()
|
||||
);
|
||||
|
||||
// 使用项目现有的存储工厂上传
|
||||
// 使用项目现有的存储工厂上传(转换为InputStream)
|
||||
try {
|
||||
return StorageFactory.use().uploadFile(imageBytes, "puzzle", fileName);
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes);
|
||||
String contentType = "PNG".equals(outputFormat) ? "image/png" : "image/jpeg";
|
||||
return StorageFactory.use().uploadFile(contentType, inputStream, "puzzle", templateCode, fileName);
|
||||
} catch (Exception e) {
|
||||
log.error("上传图片失败: fileName={}", fileName, e);
|
||||
throw new IOException("图片上传失败", e);
|
||||
|
||||
@@ -109,7 +109,7 @@ public class PuzzleImageRenderer {
|
||||
// 图片背景
|
||||
try {
|
||||
BufferedImage bgImage = downloadImage(template.getBackgroundImage());
|
||||
BufferedImage scaledBg = ImgUtil.scale(bgImage, template.getCanvasWidth(), template.getCanvasHeight());
|
||||
Image scaledBg = bgImage.getScaledInstance(template.getCanvasWidth(), template.getCanvasHeight(), Image.SCALE_SMOOTH);
|
||||
g2d.drawImage(scaledBg, 0, 0, null);
|
||||
} catch (Exception e) {
|
||||
log.error("绘制背景图片失败: {}", template.getBackgroundImage(), e);
|
||||
@@ -151,8 +151,8 @@ public class PuzzleImageRenderer {
|
||||
drawRoundedImage(g2d, scaledImage, element.getXPosition(), element.getYPosition(),
|
||||
element.getWidth(), element.getHeight(), borderRadius);
|
||||
} else {
|
||||
g2d.drawImage(scaledImage, element.getXPosition(), element.getYPosition(),
|
||||
element.getWidth(), element.getHeight(), null);
|
||||
// 直接绘制缩放后的图片,不再进行二次缩放
|
||||
g2d.drawImage(scaledImage, element.getXPosition(), element.getYPosition(), null);
|
||||
}
|
||||
|
||||
// 恢复透明度
|
||||
@@ -167,28 +167,82 @@ public class PuzzleImageRenderer {
|
||||
* 缩放图片
|
||||
*/
|
||||
private BufferedImage scaleImage(BufferedImage source, PuzzleElementEntity element) {
|
||||
String fitMode = StrUtil.isNotBlank(element.getImageFitMode()) ? element.getImageFitMode() : "CONTAIN";
|
||||
String fitMode = StrUtil.isNotBlank(element.getImageFitMode()) ? element.getImageFitMode() : "FILL";
|
||||
int targetWidth = element.getWidth();
|
||||
int targetHeight = element.getHeight();
|
||||
|
||||
switch (fitMode) {
|
||||
case "COVER":
|
||||
// 等比缩放填充(可能裁剪)
|
||||
return ImgUtil.scale(source, element.getWidth(), element.getHeight(), Color.TRANSPARENT);
|
||||
case "FILL":
|
||||
// 拉伸填充
|
||||
return ImgUtil.scale(source, element.getWidth(), element.getHeight());
|
||||
// 等比缩放填充(可能裁剪)- 使用原生Java缩放
|
||||
return scaleImageKeepRatio(source, targetWidth, targetHeight, true);
|
||||
case "CONTAIN":
|
||||
// 等比缩放适应
|
||||
return scaleImageKeepRatio(source, targetWidth, targetHeight, false);
|
||||
case "SCALE_DOWN":
|
||||
// 缩小适应(不放大)
|
||||
if (source.getWidth() <= element.getWidth() && source.getHeight() <= element.getHeight()) {
|
||||
if (source.getWidth() <= targetWidth && source.getHeight() <= targetHeight) {
|
||||
return source;
|
||||
}
|
||||
return ImgUtil.scale(source, element.getWidth(), element.getHeight(), Color.TRANSPARENT);
|
||||
case "CONTAIN":
|
||||
return scaleImageKeepRatio(source, targetWidth, targetHeight, false);
|
||||
case "FILL":
|
||||
default:
|
||||
// 等比缩放适应
|
||||
return ImgUtil.scale(source, element.getWidth(), element.getHeight(), Color.TRANSPARENT);
|
||||
// 拉伸填充到目标尺寸
|
||||
BufferedImage scaled = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g = scaled.createGraphics();
|
||||
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
||||
g.drawImage(source, 0, 0, targetWidth, targetHeight, null);
|
||||
g.dispose();
|
||||
return scaled;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 等比缩放图片
|
||||
*/
|
||||
private BufferedImage scaleImageKeepRatio(BufferedImage source, int targetWidth, int targetHeight, boolean cover) {
|
||||
int sourceWidth = source.getWidth();
|
||||
int sourceHeight = source.getHeight();
|
||||
|
||||
double widthRatio = (double) targetWidth / sourceWidth;
|
||||
double heightRatio = (double) targetHeight / sourceHeight;
|
||||
|
||||
// cover模式使用较大的比例(填充),contain模式使用较小的比例(适应)
|
||||
double ratio = cover ? Math.max(widthRatio, heightRatio) : Math.min(widthRatio, heightRatio);
|
||||
|
||||
int scaledWidth = (int) (sourceWidth * ratio);
|
||||
int scaledHeight = (int) (sourceHeight * ratio);
|
||||
|
||||
// 创建目标尺寸的画布
|
||||
BufferedImage result = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g = result.createGraphics();
|
||||
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
||||
|
||||
// 居中绘制缩放后的图片
|
||||
int x = (targetWidth - scaledWidth) / 2;
|
||||
int y = (targetHeight - scaledHeight) / 2;
|
||||
g.drawImage(source, x, y, scaledWidth, scaledHeight, null);
|
||||
g.dispose();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Image转换为BufferedImage(已废弃,改用直接绘制)
|
||||
*/
|
||||
@Deprecated
|
||||
private BufferedImage toBufferedImage(Image image, int width, int height) {
|
||||
BufferedImage buffered = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g = buffered.createGraphics();
|
||||
|
||||
// 居中绘制
|
||||
int x = (width - image.getWidth(null)) / 2;
|
||||
int y = (height - image.getHeight(null)) / 2;
|
||||
g.drawImage(image, x, y, null);
|
||||
g.dispose();
|
||||
|
||||
return buffered;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制圆角图片
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user