Java实现底图和二维码图片合并工具类

用于将二维码图片(或任何其他图片)叠加在底图上的指定位置,并在二维码下方添加可调整位置的编号文本,最终合成一张新图片。我们将创建一个可复用的工具类,并详细解释每个步骤和依赖。


1. 实现思路

  1. 加载底图:读取作为背景的底图文件。

  2. 加载覆盖图:读取需要叠加的二维码图片文件。

  3. 计算覆盖位置 :确定二维码图片在底图上的起始坐标 (x, y)

  4. 创建合成画布:创建一个与底图尺寸相同的空白图像作为画布。

  5. 绘制底图:将底图绘制到画布上。

  6. 绘制覆盖图 :在计算好的位置 (x, y) 将二维码图片绘制到画布上。

  7. 绘制编号文本

    • 计算文本绘制的位置(相对于二维码图片的位置)。

    • 设置文本字体、颜色、大小。

    • 将文本绘制到画布上(二维码图片下方)。

  8. 保存合成图:将绘制完成的画布保存为新的图片文件。

  9. 封装工具类:将上述逻辑封装在一个易于使用的工具类中,允许传入位置参数、编号文本、字体样式等。


2. 完整工具类代码:ImageOverlayUtil.java

java 复制代码
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

/**
 * 图片叠加工具类
 * 功能:将一张图片(如二维码)叠加到另一张底图的指定位置,并在叠加图下方添加可定位的编号文本。
 */
public class ImageOverlayUtil {

    /**
     * 合成图片并添加编号文本
     *
     * @param baseImagePath   底图文件路径
     * @param overlayImagePath 要叠加的图片文件路径 (如二维码)
     * @param outputImagePath  合成后图片的输出路径
     * @param overlayX        叠加图片在底图上的左上角 X 坐标
     * @param overlayY        叠加图片在底图上的左上角 Y 坐标
     * @param serialNumber    要显示的编号文本 (如 "SN:12345")
     * @param textX           文本相对于叠加图片左上角的 X 偏移量 (用于精确定位文本)
     * @param textY           文本相对于叠加图片左上角的 Y 偏移量 (通常为叠加图高度 + 间距)
     * @param fontSize        文本字体大小
     * @param fontName        文本字体名称 (如 "SansSerif", "Serif")
     * @param fontStyle       文本字体样式 (Font.PLAIN, Font.BOLD, Font.ITALIC)
     * @param textColor       文本颜色 (如 Color.BLACK)
     * @throws IOException 如果图片加载或保存失败
     */
    public static void createCompositeImage(
            String baseImagePath,
            String overlayImagePath,
            String outputImagePath,
            int overlayX,
            int overlayY,
            String serialNumber,
            int textX,
            int textY,
            int fontSize,
            String fontName,
            int fontStyle,
            Color textColor) throws IOException {

        // 1. 加载底图和叠加图
        BufferedImage baseImage = ImageIO.read(new File(baseImagePath));
        BufferedImage overlayImage = ImageIO.read(new File(overlayImagePath));

        // 2. 创建与底图尺寸相同的画布 (使用底图的类型)
        BufferedImage combined = new BufferedImage(
                baseImage.getWidth(),
                baseImage.getHeight(),
                BufferedImage.TYPE_INT_ARGB); // 使用 ARGB 支持透明背景

        // 3. 获取画布的 Graphics2D 对象 (用于绘制)
        Graphics2D g2d = combined.createGraphics();

        try {
            // 4. 设置合成选项 (启用抗锯齿和文本抗锯齿)
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

            // 5. 将底图绘制到画布上 (覆盖整个画布)
            g2d.drawImage(baseImage, 0, 0, null);

            // 6. 将二维码图片绘制到画布上的指定位置
            g2d.drawImage(overlayImage, overlayX, overlayY, null);

            // 7. 设置文本字体
            Font font = new Font(fontName, fontStyle, fontSize);
            g2d.setFont(font);
            g2d.setColor(textColor);

            // 8. 计算文本绘制的绝对坐标 (相对于整个画布)
            //    文本位置 = 叠加图位置 + 文本偏移量
            //int textAbsoluteX = overlayX + textX;
            //int textAbsoluteY = overlayY + textY;

            // (可选: 居中文本 - 需要计算文本宽度)
            // 如果需要将文本在叠加图下方居中显示,可以计算文本宽度并调整 textAbsoluteX
            // FontMetrics fontMetrics = g2d.getFontMetrics();
            // int textWidth = fontMetrics.stringWidth(serialNumber);
            // int centerX = overlayX + (overlayImage.getWidth() - textWidth) / 2;
            // g2d.drawString(serialNumber, centerX, textAbsoluteY); // 使用居中坐标

            // 9. 绘制文本到画布
            //g2d.drawString(serialNumber, textAbsoluteX, textAbsoluteY);


            // 8. 计算文本绘制的绝对坐标 (相对于整个画布),并在叠加图下方居中显示文本
            FontMetrics fontMetrics = g2d.getFontMetrics();
            int textWidth = fontMetrics.stringWidth(serialNumber);
            int centerX = overlayX + (overlayImage.getWidth() - textWidth) / 2; // 计算文本的X轴居中位置
            int centerY = overlayY + overlayImage.getHeight() + textY; // Y轴位置为叠加图像底部加上指定偏移

            // 9. 绘制文本到画布(使用计算出的居中位置)
            g2d.drawString(serialNumber, centerX, centerY);

        } finally {
            // 10. 释放 Graphics2D 资源 (重要!)
            g2d.dispose();
        }

        // 11. 保存合成后的图片到文件
        String format = outputImagePath.substring(outputImagePath.lastIndexOf('.') + 1).toLowerCase();
        ImageIO.write(combined, format, new File(outputImagePath));
    }

    /**
     * 计算文本在叠加图下方居中时所需的 X 偏移量。
     * 辅助方法,用于简化调用。
     *
     * @param g2d         Graphics2D 对象 (需提前设置好字体)
     * @param overlayImage 叠加的图片对象
     * @param text        要计算的文本
     * @return 居中文本所需的 X 偏移量 (相对于叠加图左上角)
     */
    private static int calculateCenterTextOffsetX(Graphics2D g2d, BufferedImage overlayImage, String text) {
        if (text == null || text.isEmpty()) return 0;
        FontMetrics fontMetrics = g2d.getFontMetrics();
        int textWidth = fontMetrics.stringWidth(text);
        return (overlayImage.getWidth() - textWidth) / 2;
    }
}

3. 代码详细解释

  1. 方法签名 (createCompositeImage):

    • 接收所有必要的参数:底图路径、叠加图路径、输出路径、叠加图位置 (overlayX, overlayY)、编号文本 serialNumber、文本位置偏移 (textX, textY)、字体参数 (fontSize, fontName, fontStyle)、文本颜色 textColor

    • textXtextY 是相对于叠加图左上角的偏移量。例如,如果 textY = overlayImage.getHeight() + 10,则文本将出现在二维码图片下方 10 像素处。textX 可以设为 0 表示左对齐,或者用 calculateCenterTextOffsetX 计算居中。

  2. 加载图片 (ImageIO.read):

    • 使用 ImageIO.read(File) 加载底图和叠加图到 BufferedImage 对象中。这是 Java 标准库读取图片的标准方式。
  3. 创建合成画布 (new BufferedImage(...)):

    • 创建一个新的 BufferedImage,其宽度和高度与底图相同。

    • BufferedImage.TYPE_INT_ARGB 指定使用带 Alpha 通道(透明度)的格式,这对于叠加带透明度的图片(如某些二维码)很有用。如果底图和叠加图都是不透明 JPG,使用 TYPE_INT_RGB 也可以。

  4. 获取 Graphics2D 对象 (combined.createGraphics()):

    • Graphics2D 是 Java 2D API 的核心类,用于在图像上执行绘图操作(绘制图片、文本、形状等)。
  5. 设置渲染提示 (setRenderingHint):

    • KEY_ANTIALIASINGVALUE_ANTIALIAS_ON:开启图形抗锯齿,使叠加图的边缘更平滑。

    • KEY_TEXT_ANTIALIASINGVALUE_TEXT_ANTIALIAS_ON:开启文本抗锯齿,使绘制的文本更清晰。

  6. 绘制底图 (g2d.drawImage(baseImage, 0, 0, null)):

    • 将底图绘制到画布的 (0, 0) 位置,覆盖整个画布,作为背景。
  7. 绘制叠加图 (g2d.drawImage(overlayImage, overlayX, overlayY, null)):

    • 在底图上的指定坐标 (overlayX, overlayY) 处绘制二维码图片。overlayXoverlayY 是相对于底图左上角的位置。
  8. 设置文本样式:

    • 创建 Font 对象指定字体名称、样式(普通、粗体、斜体)和大小。

    • 设置 Graphics2D 的当前字体和颜色。

  9. 计算文本绝对坐标:

    • 文本的绝对坐标 = 叠加图位置 (overlayX, overlayY) + 文本偏移量 (textX, textY)

    • textX:通常用于水平对齐(0 左对齐,居中需要计算,见辅助方法)。

    • textY:通常设置为 overlayImage.getHeight() + verticalSpacing,其中 verticalSpacing 是希望文本距离二维码底部的像素数(例如 10px)。

  10. 居中文本辅助方法 (calculateCenterTextOffsetX):

    • 这是一个可选方法。它计算给定文本在叠加图宽度范围内居中显示时所需的水平偏移量 textX

    • 它使用 FontMetrics.stringWidth(text) 获取文本在当前字体下的像素宽度。

    • 然后计算 (overlayImageWidth - textWidth) / 2 得到居中的偏移量。

  11. 绘制文本 (g2d.drawString(serialNumber, textAbsoluteX, textAbsoluteY)):

    • 使用 drawString 方法在计算好的绝对坐标处绘制编号文本。
  12. 释放资源 (g2d.dispose()):

    • finally 块中调用 dispose() 非常重要。Graphics2D 对象使用了系统资源,使用完毕后必须释放,否则可能导致资源泄漏(如 Windows 上的 GDI 句柄耗尽)。
  13. 保存合成图 (ImageIO.write(combined, format, new File(outputImagePath))):

    • 根据输出文件路径的扩展名(.png, .jpg)确定图片格式。

    • 使用 ImageIO.write 将合成后的 BufferedImage 对象写入到指定文件中。


4. 使用示例

假设有以下文件:

  • 底图:/path/to/background.jpg

  • 二维码图:/path/to/qrcode.png

  • 输出:/path/to/output.png

  • 希望二维码放在底图的 (100, 200) 位置。

  • 编号文本 "SN:20240401",放在二维码下方居中位置,距离二维码底部 15 像素。

  • 字体:黑体,粗体,18号,黑色。

java 复制代码
public class Main {
    public static void main(String[] args) {
        try {
            // 定义参数
            String baseImage = "/path/to/background.jpg";
            String overlayImage = "/path/to/qrcode.png";
            String outputImage = "/path/to/output.png";
            int qrX = 100; // 二维码 X 坐标
            int qrY = 200; // 二维码 Y 坐标
            String serial = "SN:20240401";
            int textYOffset = 0; // 先设为0,后面计算居中时会调整
            int fontSize = 18;
            String fontName = "黑体"; // 或 "SimHei"
            int fontStyle = Font.BOLD;
            Color textColor = Color.BLACK;

            // 临时加载叠加图以计算文本居中偏移量 (实际工具类内部处理)
            BufferedImage qrCodeImg = ImageIO.read(new File(overlayImage));
            // 创建一个临时Graphics2D环境计算文本宽度 (模拟)
            BufferedImage tempImg = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
            Graphics2D tempG2d = tempImg.createGraphics();
            tempG2d.setFont(new Font(fontName, fontStyle, fontSize));
            int centerTextX = ImageOverlayUtil.calculateCenterTextOffsetX(tempG2d, qrCodeImg, serial);
            tempG2d.dispose();

            // 设置文本的 Y 偏移量 = 二维码高度 + 间距 (15px)
            int textYOffsetFinal = qrCodeImg.getHeight() + 15;

            // 调用工具方法合成图片
            ImageOverlayUtil.createCompositeImage(
                    baseImage,
                    overlayImage,
                    outputImage,
                    qrX,
                    qrY,
                    serial,
                    centerTextX, // 使用计算出的居中偏移量作为 textX
                    textYOffsetFinal, // 二维码高度 + 15px
                    fontSize,
                    fontName,
                    fontStyle,
                    textColor);

            System.out.println("合成图片成功生成: " + outputImage);

        } catch (IOException e) {
            e.printStackTrace();
            System.err.println("图片合成失败: " + e.getMessage());
        }
    }
}

解释示例代码:

  1. 加载二维码图片:用于获取其宽度和高度,以便计算文本位置。

  2. 创建临时 Graphics2D :为了使用 calculateCenterTextOffsetX 方法计算文本居中所需的 textX 偏移量。注意在实际工具类内部调用时,Graphics2D 对象已经创建并设置了字体。

  3. 计算 textYOffsetFinaltextY 偏移量设置为二维码高度 qrCodeImg.getHeight() 加上想要的间距 15

  4. 调用 createCompositeImage :传入所有参数,特别是计算好的 centerTextX (居中) 和 textYOffsetFinal (二维码底部下方15px)。

  5. 错误处理 :捕获并打印 IOException


5. 工具类增强点 (实际项目建议)

  1. 异常处理细化 :工具类目前抛出 IOException。在实际项目中,可以定义更具体的自定义异常(如 ImageLoadException, ImageWriteException)。

  2. 日志记录:添加日志记录(如 SLF4J + Logback)记录操作过程和潜在错误。

  3. 图片格式支持 :确保 ImageIO 支持输入/输出的图片格式(.jpg, .png, .bmp 等)。如需更多格式,可能需要使用 javax.imageio 插件或第三方库(如 TwelveMonkeys ImageIO)。

  4. 内存优化

    • 处理大图时注意内存消耗。

    • 确保 Graphics2D 对象在 finally 块中释放 (dispose())。

    • 考虑使用 try-with-resources 管理 ImageInputStream 等资源(但 ImageIO.read 通常内部处理关闭)。

  5. 文本绘制增强

    • 支持多行文本(需要计算行高和换行)。

    • 支持文本边框 (Stroke) 或背景填充。

    • 支持文本旋转。

    • 更精确的文本位置计算(考虑基线)。

  6. 叠加图缩放 :如果需要调整二维码大小,可以在绘制前使用 g2d.drawImage(overlayImage, x, y, width, height, null) 进行缩放。

  7. 透明度处理 :如果叠加图有透明度(PNG),TYPE_INT_ARGB 格式能正确显示。底图如果是 PNG 带透明,合成时也需注意。

  8. 性能:如果需要合成大量图片,考虑性能优化(如对象复用、异步处理)。

  9. 输入校验:在工具方法开头添加参数校验(非空、文件存在、坐标有效、字体有效等)。


6. 总结

这个 ImageOverlayUtil 工具类提供了一个灵活、可复用的解决方案,用于在 Java 应用程序中合成图片,包括将二维码(或其他图片)叠加到底图的指定位置,并在其下方添加可定位的编号文本。它完全基于 Java 标准库 (java.awt, javax.imageio),无需额外依赖。通过参数化控制位置、文本内容和样式,可以适应各种业务场景。示例代码展示了如何调用该工具类,特别是如何计算文本位置以实现居中显示。

在将其集成到实际项目时,请考虑增强点(如异常处理、日志、性能优化)以适应具体需求和环境。

相关推荐
我不是8神4 小时前
Qt 知识点全面总结
开发语言·qt
我是Superman丶4 小时前
【异常】Spring Ai Alibaba 流式输出卡住无响应的问题
java·后端·spring
墨雨晨曦884 小时前
Nacos
java
Ralph_Y4 小时前
多重继承与虚继承
开发语言·c++
invicinble4 小时前
seata的认识与实际开发要做的事情
java
今晚务必早点睡4 小时前
写一个Python接口:发送支付成功短信
开发语言·python
jghhh014 小时前
基于C#实现与三菱FX系列PLC串口通信
开发语言·算法·c#·信息与通信
ada7_4 小时前
LeetCode(python)22.括号生成
开发语言·数据结构·python·算法·leetcode·职场和发展
喵了meme4 小时前
C语言实战练习
c语言·开发语言
imkaifan5 小时前
bind函数--修改this指向,返回一个函数
开发语言·前端·javascript·bind函数