当前位置:首页 » 《随便一记》 » 正文

Java给图片增加水印,根据图片大小自适应,右下角/斜角/平铺

6 人参与  2022年11月30日 16:41  分类 : 《随便一记》  评论

点击全文阅读


Hi,I’m Shendi
最近写自己的文件服务器,上传图片时需要自动增加水印,在这里记录一下



文章目录

效果展示读取图片从 byte[] 读取图片 获取画板绘制水印根据图片大小自适应水印大小右下角文字水印斜角水印平铺水印图片水印 输出图片


水印就是在图片上绘画,文字水印是最常见的,比如CSDN文章里图片右下角就会有文字水印

在 Java 中,给图片添加水印一般可以分为以下几步

读取图片获取/创建图片画板将水印内容绘制到图片中输出图片

效果展示

下面展示的是我所使用的水印效果

测试图片是百度拿的


原图
测试图片

加上水印后
加上水印后的图片
因我的要求不高,所以仅仅对角水印就可以了,可以根据自己需求绘制



读取图片

因为需要在图片上绘制,需要使用到 java.awt.image.BufferedImage 这个类

创建此类对象的方法有多种,这里只列出使用 ImageIO 将原图读取为 BufferedImage 的方式

// read 函数还可以传递输入流,例如直接使用FileInputStream -但需要自己关闭流BufferedImage img = ImageIO.read(new File("文件地址"));


从 byte[] 读取图片

// 图片数据byte[] imgData;ByteArrayInputStream bInput = new ByteArrayInputStream(imgData);BufferedImage img = ImageIO.read(bInput);


获取画板

Graphics g = img.getGraphics();

上面这种拿到的画板可以画线条圆圈文字等,但是不能旋转

需要旋转的话使用 Graphics2D

Graphics2D g = img.createGraphics();


绘制水印

绘制文字的函数

// 参数一为需要绘制的文字,参数2,3为绘制的位置img.drawString("文字", x, y);

经过测试,在坐标 0,0 绘制的文字显示的位置是在左上角,但文字是往上显示的,如下图所示
在这里插入图片描述
所以如果想让文字在最左上角显示的话,y需要加上文字的大小

// 设置文字大小,参数一为字体名称,二为样式,三为大小g.setFont(new Font("黑体", Font.PLAIN, 20));img.drawString("Shendi", x, 20);

效果如下
在这里插入图片描述


根据图片大小自适应水印大小

首先需要知道如何获取图片大小,拿到BufferedImage,可以通过以下函数获取

// width宽, height高int width = img.getWidth();int height = img.getHeight();

拿到宽高后,实现自适应就很简单了,按比例设置文字大小即可
例如

// 这里根据自己需求操作int fontSize = (width+height) / 40;g.setFont(new Font("黑体", Font.PLAIN, fontSize));


右下角文字水印

上面已经实现左上角水印了,而且将图片位置拿到了,于是右下角水印就非常简单了

因为在右下角,文字是往右显示的,所以需要得到文字显示占用的宽度,经过测试

单个汉字占用宽度=文字大小
单个字母数字符号占用=文字大小 / 2

中文转bytes占3字节,字母数字只占一字节
于是封装了以下函数

/** * 获取字符串占用的宽度 * <br> * @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a> * @param str字符串 * @param fontSize文字大小 * @return 字符串占用的宽度 */public static int getStrWidth(String str, int fontSize) {char[] chars = str.toCharArray();int fontSize2 = fontSize / 2;int width = 0;for (char c : chars) {int len = String.valueOf(c).getBytes().length;// 汉字为3,其余1// 可能还有一些特殊字符占用2等等,统统计为汉字if (len != 1) {width += fontSize;} else {width += fontSize2;}}return width;}

接下来将文字绘制到右下角就可以看到效果了

String str = "Shendi 砷碲";g.setFont(new Font("黑体", Font.PLAIN, fontSize));g.drawString(str, width - getStrWidth(str, fontSize), height);

在这里插入图片描述

可以给 x 往左移动一点,y往上移动一点,不要在最右下角
水印颜色一般都是透明的,设置透明度达到比较好的效果

String str = "Shendi 砷碲";// 这里的 new Color四个参数为 rgba,值可以为 0-255, r红,g绿,b蓝,a透明度g.setColor(new Color(0, 0, 0, 50));g.setFont(new Font("黑体", Font.PLAIN, fontSize));g.drawString(str, width - getStrWidth(str, fontSize) - 30, height - 30);

效果如下
在这里插入图片描述

换张图效果如下
在这里插入图片描述

完整代码如下

BufferedImage img = ImageIO.read(new File("图片位置"));int width = img.getWidth();int height = img.getHeight();int fontSize = (width+height) / 40;String str = "Shendi 砷碲";Graphics2D g = img.createGraphics();g.setColor(new Color(0, 0, 0, 50));g.setFont(new Font("黑体", Font.PLAIN, fontSize));g.drawString(str, width - getStrWidth(str, fontSize) - 30, height - 30);g.dispose();ImageIO.write(img, "png", new File("水印图片保存地址"));


斜角水印

右下角水印只有一个,而且所在位置很容易被p图去掉

于是我使用多个水印,简单的排列的话可以横着,竖着,但是斜着是比较好的,所以我使用了这种,也是最开始的展示效果

绘制一定数量的水印,将图片宽高按比例分成 n 份,然后循环绘制水印

BufferedImage img = ImageIO.read(new File("图片地址"));int width = img.getWidth();int height = img.getHeight();// 绘制几个水印/分成几份int num = 5;// 每一份x,y的大小int splitX = width / num, splitY = height / num;// 每一份中间的位置int centerX = splitX / 2, centerY = splitY / 2;int fontSize = (width+height) / 40;Graphics2D g = img.createGraphics();g.setFont(new Font("黑体", Font.PLAIN, fontSize));g.setColor(new Color(0, 0, 0, 30));// 设置旋转角度,可以为0-1g.rotate(0.2);for (int i = 1; i <= num; i++) {int x = splitX * i - centerX - fontSize, y = splitY * i - centerY - fontSize;g.drawString("砷碲测试水印", x, y);}g.dispose();ImageIO.write(img, "png", new File("输出位置"));

效果如下
在这里插入图片描述

绘制了五个水印,其中一个因为旋转而超出图片范围了,问题不大



平铺水印

暴力横竖二层循环绘制就可以了,最后加个旋转

首先需要计算横排能绘制多少个,竖排能绘制多少个

String str = "砷碲测试水印 Shendi";// 文字占用宽度int xWidth = getStrWidth(str, fontSize);// x,y可以绘制的数量,多加一个补充空白int xCanNum = width / xWidth + 1;int yCanNum = height / fontSize + 1;

按顺序绘制,所以需要有文字间隔,这里使用文字大小作为间隔

// 间隔int split = fontSize;

设置颜色,旋转,文字大小
接下来循环绘制即可

// i是y轴, 文字默认在y坐标上面,所以这里初始化1for (int i = 1; i <= yCanNum; i++) {int y = fontSize * i + split * i;for (int j = 0; j < xCanNum; j++) {int x = xWidth * j + split * j;g.drawString(str, x, y);}}

效果如下
在这里插入图片描述

这种效果不理想,加上旋转后,旋转点是左上角,于是要给 y 减去一定的数值将文字上拉
我这里就使用 y - (文字大小+间隔大小) * j

for (int i = 1; i <= yCanNum; i++) {int y = fontSize * i + split * i;for (int j = 0; j < xCanNum; j++) {int x = xWidth * j + split * j;g.drawString(str, x, y-(fontSize+split)*j);}}

对于这种水印,文字显得太大了,将文字改小,间距拉大

int fontSize = (width+height) / 80;int split = fontSize*2;

效果如下
在这里插入图片描述
在这里插入图片描述

完整代码如下

BufferedImage img = ImageIO.read(new File("图片文件"));int width = img.getWidth();int height = img.getHeight();int fontSize = (width+height) / 80;Graphics2D g = img.createGraphics();g.setFont(new Font("黑体", Font.PLAIN, fontSize));g.setColor(new Color(0, 0, 0, 30));g.rotate(0.2);String str = "砷碲测试水印 Shendi";// 间隔int split = fontSize*2;// 文字占用的宽度int xWidth = getStrWidth(str, fontSize);// x,y可以绘制的数量,多加一个补充空白intxCanNum = width / xWidth + 1;int yCanNum = height / fontSize + 1;for (int i = 1; i <= yCanNum; i++) {int y = fontSize * i + split * i;for (int j = 0; j < xCanNum; j++) {int x = xWidth * j + split * j;g.drawString(str, x, y-(fontSize+split)*j);}}g.dispose();ImageIO.write(img, "png", new File("水印图片保存地址"));

图片水印

绘制文字水印使用 g.drawString,绘制图片水印使用 g.drawImage
例如

BufferedImage wmImg = ImageIO.read(new File("水印图"));g.drawImage(wmImg, x, y, null);

效果如下
在这里插入图片描述

与文字水印绘制方法一致,但是需要计算坐标和水印图片大小等



输出图片

使用 ImageIO.write 输出

ImageIO.write(img, "png", new File("水印图片保存地址"));

其中第一个参数为图片,第二个参数为图片格式,第三个参数为保存地址

需要注意的是,第二个参数只能为 ImageIO.getReaderFormatNames() 中的项
在这里插入图片描述

如果是图片,最好传 png,使用jpg增加透明度可能 ImageIO.write 为 false(写入失败)



END

一键三连嘛?


点击全文阅读


本文链接:http://zhangshiyu.com/post/49307.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1