前言

在日常工作过程中,通常一些C端平台会伴随着海报生成与分享业务。因为随着移动互联网的迅猛发展,社交分享已成为我们日常生活的重要组成部分。海报分享作为一种直观、生动的分享方式,被广泛应用于各类应用场景中,如产品推广、活动宣传、内容分享等。本文主要介绍通过Java进行海报生成的原理解析与代码示例。

主流实现方式

  1. 使用图形库生成海报

    这种方式主要依赖于Java的图形库,如 Java 2D API,来绘制海报内容.主要核心类为Graphics、Graphics2D,这种方式可以完全自定义海报的内容和样式,但是需要更多的代码来实现复杂的绘制逻辑

  2. 使用模板引擎生成海报HTML

    这种方式可以利用模板引擎(如 ThymeleafFreeMarker 等)来生成包含海报内容的HTML页面。你可以在HTML中定义海报的布局和样式,并使用模板引擎的语法来插入动态数据。生成HTML后,可以使用Java的Web框架(如Spring MVC)将其渲染为图片。这种方式可以实现较为复杂的海报布局和样式,并且可以利用CSS和JavaScript来增强海报的交互性

  3. 使用第三方库生成海报

    这种方式主要是通过引用第三方海报生成库,如 jquery-qrcode(虽然这是JS库,但可以在Java后端配合前端使用)用于生成二维码海报,或者有一些Java库可以直接生成带有文本和图片的海报。这些库通常提供了丰富的API和配置选项,可以方便地生成各种样式的海报。使用第三方库可以大大简化开发过程

  4. 调用其他在线API生成海报

    有些在线服务提供了海报生成的API接口,可以通过Java调用这些API来生成海报。这种方式通常只需要发送包含海报图片、内容和样式的请求到API接口,然后接收返回的海报图片。无需在本地进行复杂的绘制和渲染操作,但可能需要支付一定的费用,并且受限于API接口的功能和性能

本文主要介绍第一种方式,通过Java基础类库实现海报生成

海报生成流程

  1. 生成二维码

    一般在Java中进行二维码生成大家都会引入 zxing 库进行封装后使用,本文使用 Hutool 工具类库中二维码工具实现(基于前者进行一层封装,使用更简洁)

    1
    2
    3
    4
    5
    6
    // 二维码扫描后跳转链接
    String url = "https://www.deng.cool";
    // 初始化配置类(可指定宽高,设置背景色或添加logo图标等,详情参考官网文档)
    QrConfig config = new QrConfig(400, 400);
    // 生成二维码图片并加载到内存中
    BufferedImage qcCode = QrCodeUtil.generate(url, config);

    示例图:image-20240311173520153

  2. 获得背景图片

    背景图片可能是链接需要去下载后加载到内存中或其他编码格式,本文背景图片存放于项目静态资源目录下直接获取

    1
    2
    3
    4
    5
    // 获取静态资源目录下文件
    Resource backResource = new ClassPathResource("static/poster/poster.jpg");
    // 转为流加载到内存中
    InputStream bgStream = backResource.getInputStream();
    BufferedImage bg = ImageIO.read(bgStream);

    示例图:image-20240311173346223

  3. 创建画布并开启画图

    1
    2
    3
    4
    // 创建画布(因本文海报生成示例需求为在背景图片中添加二维码,故画布宽高跟背景图相同即可)
    BufferedImage canvas = new BufferedImage(bgWidth, bgHeight, BufferedImage.TYPE_INT_RGB);
    // 生成画笔 开启画图
    Graphics g = canvas.getGraphics();

    BufferedImage:是一个带缓冲区的图像类,主要作用是将图片加载到内存中。在内存中,BufferedImage生成的图片拥有一个图像缓冲区,我们可以获取绘图对象、图像缩放、选择图像平滑度等,对图像进行一系列的处理

    **BufferedImage.TYPE_**: 是用于指定图像像素类型的常量,我们需要考虑图像的需求,如是否需要透明度、颜色深度、性能等因素。不同的类型在内存使用和渲染性能上可能有所不同,最常用类型一般为TYPE_INT_ARGBTYPE_INT_RGB,更多介绍请自行百度,与本文关联不大🫡

  4. 画布上加载背景图片

    1
    2
    // 先加载背景图片,宽高和坐标与初始画布一致
    g.drawImage(bg.getScaledInstance(bgWidth, bgHeight, Image.SCALE_SMOOTH), 0, 0, null);

    Image.SCALE_ :为内置的图片缩放算法,通常用于权衡图像质量和缩放性能:

    • Image.SCALE_SMOOTH:使用平滑的缩放算法。这通常会产生高质量的缩放图像,但计算成本可能较高
    • Image.SCALE_FAST:使用快速的缩放算法。这种算法可能在缩放时牺牲一些图像质量,但执行速度更快
    • Image.SCALE_REPLICATE:使用最近邻插值法进行缩放。这是最简单的缩放算法,计算速度快,但可能导致图像出现锯齿状或像素化
    • Image.SCALE_AREA_AVERAGING:使用区域平均法进行缩放。这种方法在缩小图像时特别有用,因为它有助于减少混叠效应,使图像看起来更平滑
  5. 画布上指定坐标绘制二维码

    1
    2
    // 加载二维码图片,后面图片会浮在最上层,根据海报最终所呈现效果设置宽高与在背景图上的坐标位置
    g.drawImage(qcCode.getScaledInstance(qrWidth, qrHeight, Image.SCALE_SMOOTH), 250, 100, null);
  6. 画布上设置文字

    1
    2
    3
    4
    5
    6
    // 设置字体颜色
    g.setColor(Color.darkGray);
    // 设置字体风格大小
    g.setFont(new Font("微软雅黑", Font.PLAIN, 24));
    // 设置字体内容与坐标
    g.drawString("进击的小小程序员", 0, 40);

    Color.darkGray: 是预定义的颜色常量,代表深灰色,也可直接 new Color({r},{g},{b})自定义颜色值

    Font.PLAIN: 是字体样式中的静态常量,用于表示普通样式的字体。其它取值:粗体(Font.BOLD)和斜体(Font.ITALIC

  7. 关闭画笔

    1
    2
    // 释放画布相关系统资源
    g.dispose();
  8. 最终画布图片获取

    1
    2
    3
    4
    // 创建文件输出流
    ByteArrayOutputStream streamImg = new ByteArrayOutputStream();
    // 将画布最后图片写入到输出流中
    ImageIO.write(canvas, "png", streamImg);

    示例图:image-20240311173633546

完整demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
* 测试海报生成方法
* @return
* @throws IOException
*/
@GetMapping("/generate")
public R<String> generate() throws IOException {
System.out.println("=========进入海报生成方法==========");
// 生成二维码
String url = "https://www.deng.cool";
QrConfig config = new QrConfig(400, 400);
BufferedImage qcCode = QrCodeUtil.generate(url, config);
// 拿取背景图
Resource backResource = new ClassPathResource("static/poster/poster.jpg");
InputStream bgStream = backResource.getInputStream();
BufferedImage bg = ImageIO.read(bgStream);
//---------------------------------合成图片步骤-----------------------------
// 背景图片宽高
int bgHeight = bg.getHeight();
int bgWidth = bg.getWidth();
// 二维码图片宽高
int qrHeight = 100;
int qrWidth = 100;
// 创建画布,开启画图
BufferedImage canvas = new BufferedImage(bgWidth, bgHeight, BufferedImage.TYPE_INT_RGB);
Graphics g = canvas.getGraphics();
// 绘制背景图片
g.drawImage(bg.getScaledInstance(bgWidth, bgHeight, Image.SCALE_SMOOTH), 0, 0, null);
// 绘制二维码图片,定位到背景图的所在位置
g.drawImage(qcCode.getScaledInstance(qrWidth, qrHeight, Image.SCALE_SMOOTH), 250, 100, null);
// 设置字体颜色
g.setColor(Color.darkGray);
g.setFont(new Font("微软雅黑", Font.PLAIN, 24));
g.drawString("进击的小小程序员", 0, 40);
// 关掉画笔
g.dispose();
// 创建文件输出流
ByteArrayOutputStream streamImg = new ByteArrayOutputStream();
ImageIO.write(canvas, "png", streamImg);
// 文件流转为base64编码
String base64 = Base64.encode(streamImg.toByteArray());
streamImg.close();
return R.ok("海报生成成功", base64);
}