Java实现添加文字水印、图片水印

news2024/12/27 12:57:01

 

目录

前言

一、获取原图片对象信息

1、读取本地图片

2、读取网络图片

二、处理水印

三、添加水印

四、获取目标图片

五、完整工具类

六、结果展示


前言

        现在很多人都喜欢在各种平台上分享自己的照片吧,不管是一些制作出来的媒体图片还是精致的人像图片,相信很多小伙伴都会这样。但同时也有一些让人担心的问题,怕有一些人会随意转发图片,或者盗图冒充本人。其实解决这个问题很简单,只要发布之前给图片加个水印就行了。今天分享如何用Java实现添加水印的功能。


水印可以是图片或者文字,操作方便。

java实现给图片添加水印实现步骤:

  • 获取原图片对象信息(本地图片或网络图片)

  • 添加水印(设置水印颜色、字体、坐标等)

  • 处理输出目标图片

一、获取原图片对象信息

获取图片的方式,通常有两种:

  • 一种是通过下载到本地,从本地读取(本地图片);

  • 另外一种是通过网络地址进行读取(网络图片)

1、读取本地图片

通过代码实现读取本地目录下图片,返回的是图片对象,代码如下:

    /**
     * 读取本地图片
     *
     * @param path 本地图片存放路径
     */
    public static Image readLocalPicture(String path) {
        if (null == path) {
            throw new RuntimeException("本地图片路径不能为空");
        }
        // 读取原图片信息 得到文件
        File srcImgFile = new File(path);
        try {
            // 将文件对象转化为图片对象
            return ImageIO.read(srcImgFile);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

2、读取网络图片

    /**
     * 读取网络图片
     *
     * @param path 网络图片地址
     */
    public static Image readNetworkPicture(String path) {
        if (null == path) {
            throw new RuntimeException("网络图片路径不能为空");
        }
        try {
            // 创建一个URL对象,获取网络图片的地址信息
            URL url = new URL(path);
            // 将URL对象输入流转化为图片对象 (url.openStream()方法,获得一个输入流)
            BufferedImage bugImg = ImageIO.read(url.openStream());
            if (null == bugImg) {
                throw new RuntimeException("网络图片地址不正确");
            }
            return bugImg;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

二、处理水印

通过上面的步骤,我们已经获取到了原始图片信息,下面需要创建一个画笔进行水印的添加及处理。水印包含文字水印、图片水印。

画笔可以设置水印颜色、字体大小、字体样式等。

    /**
     * 水印处理
     *
     * @param image     图片对象
     * @param type      水印类型(1-文字水印 2-图片水印)
     * @param watermark 水印内容(文字水印内容/图片水印的存放路径)
     */
    public static void manageWatermark(Image image, Integer type, String watermark) {
        int imgWidth = image.getWidth(null);
        int imgHeight = image.getHeight(null);
        BufferedImage bufImg = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
        // 加水印:
        // 创建画笔
        Graphics2D graphics = bufImg.createGraphics();
        // 绘制原始图片
        graphics.drawImage(image, 0, 0, imgWidth, imgHeight, null);

        // 校验水印的类型
        if (type == 1) {
            if (StringUtils.isEmpty(watermark)) {
                throw new RuntimeException("文字水印内容不能为空");
            }
            // 添加文字水印:
            // 根据图片的背景设置水印颜色
            graphics.setColor(new Color(255, 255, 255, 128));
            // 设置字体  画笔字体样式为微软雅黑,加粗,文字大小为45pt
            graphics.setFont(new Font("微软雅黑", Font.BOLD, 45));
            // 设置水印的坐标(为原图片中间位置)
            int x = (imgWidth - getWatermarkLength(watermark, graphics)) / 2;
            int y = imgHeight / 2;
            // 画出水印 第一个参数是水印内容,第二个参数是x轴坐标,第三个参数是y轴坐标
            graphics.drawString(watermark, x, y);
            graphics.dispose();
        } else {
            // 添加图片水印:
            if (StringUtils.isEmpty(watermark)) {
                throw new RuntimeException("图片水印存放路径不能为空");
            }
            Image srcWatermark = readLocalPicture(watermark);
            int watermarkWidth = srcWatermark.getWidth(null);
            int watermarkHeight = srcWatermark.getHeight(null);
            // 设置 alpha 透明度:alpha 必须是范围 [0.0, 1.0] 之内(包含边界值)的一个浮点数字
            graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.9f));
            // 绘制水印图片  坐标为中间位置
            graphics.drawImage(srcWatermark, (imgWidth - watermarkWidth) / 2,
                    (imgHeight - watermarkHeight) / 2, watermarkWidth, watermarkHeight, null);
            graphics.dispose();
        }
    }

getWatermarkLength() 方法用于计算水印内容的长度:

    /**
     * 获取水印文字的长度
     *
     * @param watermarkContent 文字水印内容
     * @param graphics         图像类
     */
    private static int getWatermarkLength(String watermarkContent, Graphics2D graphics) {
        return graphics.getFontMetrics(graphics.getFont()).charsWidth(watermarkContent.toCharArray(), 0, watermarkContent.length());
    }

Font 字体说明:

  • Font 类的构造函数为:public Font(String familyName, int style, int size)

  • 参数说明:第一个参数为字体类型,第二个参数为字体风格,第三个参数为字体大小

字体的风格有:

  • Font.PLAIN(普通)

  • Font.BOLD(加粗)

  • Font.ITALIC(斜体)

  • Font.BOLD+Font.ITALIC(粗斜体)

PS

1、size字体大小,默认单位是pt(磅),数字越大,字就越大。

2、需要注意是,水印坐标位置。设置不当,就看不到水印效果。

三、添加水印

直接调用上面封装好的方法。

/**
     * 添加水印
     *
     * @param pictureType   图片来源类型(1-本地图片 2-网络图片)
     * @param watermarkType 水印类型(1-文字水印 2-图片水印)
     * @param path          图片路径
     * @param watermark     水印内容(文字水印内容/图片水印的存放路径)
     */
    public static void addWatermark(Integer pictureType, Integer watermarkType, String path, String watermark) {
        if (null == pictureType) {
            throw new RuntimeException("图片来源类型不能为空");
        }
        if (null == watermarkType) {
            throw new RuntimeException("水印类型不能为空");
        }

        Image image;
        if (pictureType == 1) {
            // 读取本地图片
            image = readLocalPicture(path);
        } else {
            // 读取网络图片
            image = readNetworkPicture(path);
        }
        if (watermarkType == 1) {
            // 添加文字水印
            manageWatermark(image, 1, watermark);
        } else {
            // 添加图片水印
            manageWatermark(image, 2, watermark);
        }
    }

四、获取目标图片

经过上面的操作后,我们的图片添加文字水印或图片水印就已经处理完成了。但他现在还保存在Java对象中。

我们想要看得到效果,需要进行处理,保存图片到本地。

        // 定义存储的地址
        String tarImgPath = "C:/Users/admin/Desktop/watermark.jpg";
        // 输出图片
        try {
            FileOutputStream outImgStream = new FileOutputStream(tarImgPath);
            ImageIO.write(bufImg, "png", outImgStream);
            outImgStream.flush();
            outImgStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

五、完整工具类

以上代码,我直接整合成一个工具类。

import org.apache.commons.lang3.StringUtils;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;

/**
 * 添加水印util
 */
public class WatermarkUtil {

    /**
     * 读取本地图片
     *
     * @param path 本地图片存放路径
     */
    public static Image readLocalPicture(String path) {
        if (null == path) {
            throw new RuntimeException("本地图片路径不能为空");
        }
        // 读取原图片信息 得到文件
        File srcImgFile = new File(path);
        try {
            // 将文件对象转化为图片对象
            return ImageIO.read(srcImgFile);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 读取网络图片
     *
     * @param path 网络图片地址
     */
    public static Image readNetworkPicture(String path) {
        if (null == path) {
            throw new RuntimeException("网络图片路径不能为空");
        }
        try {
            // 创建一个URL对象,获取网络图片的地址信息
            URL url = new URL(path);
            // 将URL对象输入流转化为图片对象 (url.openStream()方法,获得一个输入流)
            BufferedImage bugImg = ImageIO.read(url.openStream());
            if (null == bugImg) {
                throw new RuntimeException("网络图片地址不正确");
            }
            return bugImg;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 水印处理
     *
     * @param image     图片对象
     * @param type      水印类型(1-文字水印 2-图片水印)
     * @param watermark 水印内容(文字水印内容/图片水印的存放路径)
     */
    public static String manageWatermark(Image image, Integer type, String watermark) {
        int imgWidth = image.getWidth(null);
        int imgHeight = image.getHeight(null);
        BufferedImage bufImg = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
        // 加水印:
        // 创建画笔
        Graphics2D graphics = bufImg.createGraphics();
        // 绘制原始图片
        graphics.drawImage(image, 0, 0, imgWidth, imgHeight, null);

        // 校验水印的类型
        if (type == 1) {
            if (StringUtils.isEmpty(watermark)) {
                throw new RuntimeException("文字水印内容不能为空");
            }
            // 添加文字水印:
            // 根据图片的背景设置水印颜色
            graphics.setColor(new Color(255, 255, 255, 128));
            // 设置字体  画笔字体样式为微软雅黑,加粗,文字大小为45pt
            graphics.setFont(new Font("微软雅黑", Font.BOLD, 45));
            // 设置水印的坐标(为原图片中间位置)
            int x = (imgWidth - getWatermarkLength(watermark, graphics)) / 2;
            int y = imgHeight / 2;
            // 画出水印 第一个参数是水印内容,第二个参数是x轴坐标,第三个参数是y轴坐标
            graphics.drawString(watermark, x, y);
            graphics.dispose();
        } else {
            // 添加图片水印:
            if (StringUtils.isEmpty(watermark)) {
                throw new RuntimeException("图片水印存放路径不能为空");
            }
            Image srcWatermark = readLocalPicture(watermark);
            int watermarkWidth = srcWatermark.getWidth(null);
            int watermarkHeight = srcWatermark.getHeight(null);
            // 设置 alpha 透明度:alpha 必须是范围 [0.0, 1.0] 之内(包含边界值)的一个浮点数字
            graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.9f));
            // 绘制水印图片  坐标为中间位置
            graphics.drawImage(srcWatermark, (imgWidth - watermarkWidth) / 2,
                    (imgHeight - watermarkHeight) / 2, watermarkWidth, watermarkHeight, null);
            graphics.dispose();
        }
        // 定义存储的地址
        String tarImgPath = "C:/Users/admin/Desktop/watermark.jpg";
        // 输出图片
        try {
            FileOutputStream outImgStream = new FileOutputStream(tarImgPath);
            ImageIO.write(bufImg, "png", outImgStream);
            outImgStream.flush();
            outImgStream.close();
            return "水印添加成功";
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 添加水印
     *
     * @param pictureType   图片来源类型(1-本地图片 2-网络图片)
     * @param watermarkType 水印类型(1-文字水印 2-图片水印)
     * @param path          图片路径
     * @param watermark     水印内容(文字水印内容/图片水印的存放路径)
     */
    public static String addWatermark(Integer pictureType, Integer watermarkType, String path, String watermark) {
        if (null == pictureType) {
            throw new RuntimeException("图片来源类型不能为空");
        }
        if (null == watermarkType) {
            throw new RuntimeException("水印类型不能为空");
        }

        Image image;
        if (pictureType == 1) {
            // 读取本地图片
            image = readLocalPicture(path);
        } else {
            // 读取网络图片
            image = readNetworkPicture(path);
        }
        if (watermarkType == 1) {
            // 添加文字水印
            return = manageWatermark(image, 1, watermark);
        } else {
            // 添加图片水印
            return = manageWatermark(image, 2, watermark);
        }
    }

    /**
     * 获取水印文字的长度
     *
     * @param watermarkContent 文字水印内容
     * @param graphics         图像类
     */
    private static int getWatermarkLength(String watermarkContent, Graphics2D graphics) {
        return graphics.getFontMetrics(graphics.getFont()).charsWidth(watermarkContent.toCharArray(), 0, watermarkContent.length());
    }

    public static void main(String[] args) {
        
        // 本地图片路径:
        String localPath = "C:/Users/admin/Desktop/bg.jpg";
        // 网络图片地址:
        String networkPath = "https://img0.baidu.com/it/u=3708545959,316194769&fm=253&fmt=auto&app=138&f=PNG?w=1000&h=1000";
        // 文字水印内容
        String textWatermark = "Hello World!";
        // 图片水印路径
        String pictureWatermark = "C:\\Users\\admin\\Desktop\\wm.jpg";

        // 本地图片 添加文字水印
        //addWatermark(1, 1, localPath,textWatermark);

        // 网络图片 添加文字水印
        //addWatermark(2, 1, networkPath, textWatermark);

        // 本地图片 添加图片水印
        //addWatermark(1, 2, localPath, pictureWatermark);

        // 网络图片 添加图片水印
        addWatermark(2, 2, networkPath, pictureWatermark);

    }

}

六、结果展示

1、本地图片+文字水印:

2、本地图片+图片水印:

3、网络图片+文字水印:

4、网络图片+图片水印:

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、评论、收藏➕关注,您的支持是我坚持写作最大的动力。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1126920.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

人大金仓三大兼容:MySQL迁移无忧

近日,MySQL 5.7停服事件引发广泛关注。MySQL目前已经成为中国用户使用非常广泛的数据库,其中5.7版本的用户比重又是最高的。随着信息技术应用创新深入各行各业,国产数据库对MySQL的平滑替换成为大势所趋。 作为数据库领域国家队,人…

Jmeter并发压测数据库的TPC值

Apache JMeter 视频讲解演示:https://www.bilibili.com/video/BV1Dh4y1J7NW/ Apache组织开发的基于Java的压力测试工具,常常用来模拟高并发压测场景 下载网址:https://jmeter.apache.org/download_jmeter.cgi 下载二进制包,解…

【深度学习 | Transformer】释放注意力的力量:探索深度学习中的 变形金刚,一文带你读通各个模块 —— 总结篇(三)

🤵‍♂️ 个人主页: AI_magician 📡主页地址: 作者简介:CSDN内容合伙人,全栈领域优质创作者。 👨‍💻景愿:旨在于能和更多的热爱计算机的伙伴一起成长!!&…

【linux系统】服务器安装Pycharm

文章目录 安装pycharm步骤1. 进入pycharm官网2. 上传到服务器3. 安装过程 摘要:pycharm是Python语言的图形化开发工具。因为如果在Linux环境下的Python shell 中直接进行编程,其无法保存与修改,在大型项目当中这是很不方便的,而py…

【废话文学】各种概念混搭

我认为他一定是在主体意识中出现了一种异常的反馈 这种反馈打破了既定的习惯性模式 于是思维意识出现了层阶梯式的神话 我认为通过XXX同志这个主体意识上的问题 要看出他自身的轨迹而带有意念性 这个悲剧带有鲜明的主观色彩和思辨色彩 而不要只听着在对他人生哲学上的虚无上的研…

自动化测试工具的定义及作用

在现代软件开发中,质量和效率是至关重要的。为了确保软件在不断变化的市场中脱颖而出,开发团队需要寻找方法来提高质量、降低错误率,并加速交付速度。自动化测试工具是一种不可或缺的资源,可以帮助开发团队实现这些目标。本文将深…

前三季净利降八成!科大讯飞增长放缓,刘庆峰怎么应对

大数据产业创新服务媒体 ——聚焦数据 改变商业 国内A股上市公司中,科大讯飞一度是唯一一家连续十年营收增长达到25%的高科技企业。财报显示,在三年疫情最为艰难的2021年,科大讯飞依旧录得183亿元营收,同比增长40%;归…

系统设计 - 我们如何通俗的理解那些技术的运行原理 - 第四部分:微服务架构

本心、输入输出、结果 文章目录 系统设计 - 我们如何通俗的理解那些技术的运行原理 - 第四部分:微服务架构前言典型的微服务架构是什么样的微服务的优势 微服务最佳实践在开发微服务时,我们需要遵循以下最佳实践: 微服务通常使用什么技术堆栈…

hdlbits系列verilog解答(向量门操作)-14

文章目录 一、问题描述二、verilog源码三、仿真结果 一、问题描述 构建一个具有两个 3 位输入的电路,用于计算两个向量的按位 OR、两个向量的逻辑 OR 以及两个向量的逆 (NOT)。将b反相输出到out_not上半部分,将a 的反相输出到out…

Vue里面怎么使用站点地图Sitemap做SEO

在Vue中使用站点地图(Sitemap)来进行SEO优化的方法与在其他前端框架中类似。 1:安装vue-router:首先确保Vue应用程序正在使用vue-router来进行路由管理。使用以下命令安装它: npm install vue-router2:创建路由配置::Vue应用程序中需要创建一个路由配置文件。这个文件…

理解了springboot那些约定俗成的Java类命名规范,就很容易读懂springboot的源代码

去阿里面试,由于简历上写了读过 spring 的源码,所以面试官就问到了:看你读过 spring 的源码,可以介绍一下他的流程么? 肚子好像很多,但是脑子关于spring相关概念,很好混乱,回答的…… 那怎办呢?何不试一下,找一下springboot那些约定俗成的Java类命名规范,来辅助自…

[SQL开发笔记]AND OR运算符:基于一个以上的条件对记录进行过滤

上一篇博客示范了在where子句中使用一个条件进行查询,如果需要多个条件限制呢?需要使用and或or运算符; 一、功能描述: 用于基于一个以上的条件对记录进行过滤 二、AND & OR语法详解: 1.and运算符(co…

电子标签模块:让传感器智能化,工程安全监测更便捷

电子标签模块:让传感器智能化,工程安全监测更便捷 在之前的文章《振弦传感器的发展及信息化的核心技术-VM系列振弦采集模块》中,我们提到了河北稳控科技研发并批量生产的激励测读模块(振弦采集模块),该模块…

医院绩效考核系统源码,医院绩效考评管理系统全套成品源码

医院综合绩效核算系统全套源码 (医院实际应用案例自主版权演示) 医院绩效考核系统以医院的发展战略为导向,把科室、员工的绩效考核跟战略发展目标紧密结合,引导医院各个科室、各员工的工作目标跟医院的发展目标结合在一起&#x…

基于遥感影像的分类技术(监督/非监督和面向对象的分类技术)

遥感图像分类技术 “图像分类是将土地覆盖类别分配给像素的过程。例如,类别包括水、城市、森林、农业和草原。”前言 – 人工智能教程 什么是遥感图像分类? 遥感图像分类技术的三种主要类型是: 无监督图像分类监督图像分类基于对象的图像分析…

滤波器设计工具简介

目录 快速入门 设计滤波器 查看其他分析 更改轴单位 标记数据点 优化设计 更改分析参数 导出滤波器 生成 MATLAB 文件 量化滤波器 目标 其他功能 此示例说明如何使用方便的滤波器设计工具替代命令行滤波器设计函数。 滤波器设计工具是 Signal Processing Toolbox™…

Python 下载首页图片

以下是一个使用RoboBrowser和Python下载首页图片的下载器程序,并使用https://www.duoip.cn/get_proxy获取代理: import os import time from robobrowser import RoboBrowser import requests ​ def get_proxy():url "https://www.duoip.cn/get_…

【Linux】Centos 8 服务器部署:阿里云端口开放与应用实例教程

目录 一、基本流程 二、进入实例安全组 (1)进入实例详情 (2)打开安全组列表 三、配置规则 (1)默认安全组 (2)自定义安全组 ① 创建安全组添加入方向端口 ② 编辑安全组导入…

紫光同创FPGA实现图像去雾 基于暗通道先验算法 纯verilog代码加速 提供2套工程源码和技术支持

目录 1、前言免责声明本去雾模块的特点 2、目前我这里已有的图像处理方案3、设计思路框架SD卡初始化SD卡读操作SD卡读图片OV5640摄像头配置及采集HDMA图像缓存输入输出视频HDMA缓冲FIFOHDMA控制模块 图像去雾模块详解HDMI输出 4、PDS工程1详解:SD卡提供有雾图片5、P…

用“qwer”打造类似梦幻西游中比巧克力还丝滑的状态机

点击上方亿元程序员关注和★星标 引言 大家好,我是亿元程序员,一位有着8年游戏行业经验的主程。 本系列是《和8年游戏主程一起学习设计模式》,让糟糕的代码在潜移默化中升华,欢迎大家关注分享收藏订阅。 梦幻西游是流量密码吗&…