之前一直觉得实现在图片上添加文字很厉害,后来机缘巧合之下,我也需要自己去实现这个步骤,所以在下面梳理一下踩过的坑和实现的原理,希望能帮到大家。

从网上搜索JAVA相关生成GIF加文字或者加水印会发现已经有很多已经实现的了,不过大都是使用gif4j进行的合成,这种做法在个人使用肯定是没有什么问题,但是要自己公司用或者用于商用的话,就会有很多的License问题,秉着能省则省的原则,所以打算另辟蹊径。

首先要感谢Kevin Weiner提供的实现基础。这是工具类的链接,实现原理就是基于这个来实现的。

链接:https://github.com/rtyley/animated-gif-lib-for-java 尊重原版。

先贴实现代码:

public static void main(String[] args) throws Exception {long timestart = System.currentTimeMillis();InputStream inputStream1 = new FileInputStream("C:\\Users\\TMP\\3_5.gif");new ImageUtil2().getGifSticker("测试问题","SIMSUN.ttf",inputStream1);long timeend = System.currentTimeMillis();System.out.println("用时"+(timeend-timestart));}public void getGifSticker(String markContent,String fontName, InputStream inputStream) throws FontFormatException, IOException  {ByteArrayOutputStream outputStream = new ByteArrayOutputStream();try {GifDecoder decoder = new GifDecoder();//读入gif数据流decoder.read(inputStream);//获取GIF的宽高Dimension dimension = decoder.getFrameSize();int height = (int)dimension.getHeight();//获取字体问津数据流,用于规范生成文字的字体格式InputStream intput = ImageUtil2.class.getResourceAsStream(fontName);//生成字体Font font1 = Font.createFont(Font.TRUETYPE_FONT, intput);//要是想使用deriveFont设置字体大小必须重新指定Font,而且不支持整数型,只能使用浮点类型Font font = font1.deriveFont(25.0f);//读取帧数int frameCount = decoder.getFrameCount();AnimatedGifEncoder encoder = new AnimatedGifEncoder();String url = "C:\\Users\\TMP\\o\\" + + System.currentTimeMillis()+".gif";encoder.start(url);encoder.setRepeat(0);Graphics2D g = null;/*** 对GIF进行拆分* 每一帧进行文字处理* 组装*/for (int i = 0; i < frameCount; i++) {//初始化图像g =  (Graphics2D) decoder.getFrame(i).getGraphics();/*** RenderingHint是对图片像素,锯齿等等做的优化,可保证生成的图片放大锯齿点阵也不会很明显*/g.setRenderingHint(SunHints.KEY_ANTIALIASING, SunHints.VALUE_ANTIALIAS_ON);g.setRenderingHint(SunHints.KEY_TEXT_ANTIALIASING, SunHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
//              g.setRenderingHint(SunHints.KEY_STROKE_CONTROL, SunHints.VALUE_STROKE_PURE);
//              g.setRenderingHint(SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST, 100);
//              g.setRenderingHint(SunHints.KEY_FRACTIONALMETRICS, SunHints.VALUE_FRACTIONALMETRICS_OFF);
//              g.setRenderingHint(SunHints.KEY_RENDERING, SunHints.VALUE_RENDER_DEFAULT);g.setColor(Color.black); g.setFont(font);g.setFont(font);//设置打印文字和坐标g.drawString(markContent, 12, 81);g.dispose();//组装每一帧encoder.addFrame(decoder.getFrame(i));//设置每帧的切换时间if (i != frameCount - 1) {encoder.setDelay(decoder.getDelay(i));}}encoder.finish();byte b[] = outputStream.toByteArray();}finally {try {if (null != outputStream)outputStream.close();} catch (IOException e) {}}}

传入参数 :markContent : 生成文字 fontName 字体文字  inputStream 图片数据流

原理分析:

根据传入的字体文字去当先CLASSPATH下面找到对应字体文件(建议使用ttf文件,对otf的支持还不是很理想,要想要没有License的ttf字体文件欢迎私聊)。

然后根据传入的image数据流进行操作,代码的分析我都在注释中有声明。

后来发现每次生成的所有GIF虽然都很正常,但是发现放大来看文字会有点阵锯齿问题,研究良久,对这个问题也颇有了解,后来根据JDK源码进行了优化,使用边缘抖动模糊进行的一个处理,这样的话如果放大的话边缘处也会很平滑,但是有一点缺点就是会有略微的模糊,不过对比来看,问题不大!

锯齿优化思路如下:

由于是使用的Graphics2D进行的图像的制作和渲染,所以这个支持RenderingHints的一个处理,关于RenderingHints声明如下:

 * The {@code RenderingHints} class defines and manages collections of  keys and associated values which allow an application to provide input  into the choice of algorithms used by other classes which perform rendering and image manipulation services.* The {@link java.awt.Graphics2D} class, and classes that implement  {@link java.awt.image.BufferedImageOp} and  {@link java.awt.image.RasterOp} all provide methods to get and  possibly to set individual or groups of {@code RenderingHints}  keys and their associated values.* When those implementations perform any rendering or image manipulation  operations they should examine the values of any {@code RenderingHints}  that were requested by the caller and tailor the algorithms used accordingly and to the best of their ability.* <p>* Note that since these keys and values are <i>hints</i>, there is  no requirement that a given implementation supports all possible  choices indicated below or that it can respond to requests to modify its choice of algorithm.* The values of the various hint keys may also interact such that  while all variants of a given key are supported in one situation,* the implementation may be more restricted when the values associated with other keys are modified.* For example, some implementations may be able to provide several types of dithering when the antialiasing hint is turned off, but  have little control over dithering when antialiasing is on.* The full set of supported keys and hints may also vary by destination since runtimes may use different underlying modules to render to  the screen, or to {@link java.awt.image.BufferedImage} objects,  or while printing.* <p>* Implementations are free to ignore the hints completely, but should  try to use an implementation algorithm that is as close as possible to the request.* If an implementation supports a given algorithm when any value is used  for an associated hint key, then minimally it must do so when the  value for that key is the exact value that specifies the algorithm.* <p>* The keys used to control the hints are all special values that  subclass the associated {@link RenderingHints.Key} class.* Many common hints are expressed below as static constants in this class, but the list is not meant to be exhaustive.* Other hints may be created by other packages by defining new objects  which subclass the {@code Key} class and defining the associated values.

粗略翻译如下:

* {@code RenderingHints}类定义和管理键和相关值的集合,允许应用程序为执行渲染和图像处理服务的其他类所使用的算法选择提供输入。
 * {@link java.awt.Graphics2D}类和实现{@link java.awt.image.BufferedImageOp}和{@link java.awt.image.RasterOp}的类都提供了获取和可能设置个体的方法或{@code RenderingHints}键组及其相关值。
 *当这些实现执行任何渲染或图像处理操作时,他们应检查调用者请求的任何{@code RenderingHints}的值,并根据他们的能力量身定制所使用的算法。
 * <p>
 *请注意,由于这些键和值是<i>提示</ i>,因此不要求给定的实现支持下面指出的所有可能的选择,或者它可以响应修改其算法选择的请求。
 *各种提示键的值也可以相互作用,以便在一种情况下支持给定键的所有变体,
 *当修改与其他键相关联的值时,实现可能会受到更多限制。
 *例如,某些实现可能能够在关闭抗锯齿提示时提供几种类型的抖动,但在打开抗锯齿时几乎无法控制抖动。
 *完整的受支持的键和提示集也可能因目的地而异,因为运行时可能使用不同的底层模块呈现到屏幕,或{@link java.awt.image.BufferedImage}对象,或者在打印时。
 * <p>
 *实现可以完全忽略提示,但应该尝试使用尽可能接近请求的实现算法。
 *如果某个实现支持给定的算法,当任何值用于关联的提示键时,那么当该键的值是指定算法的确切值时,它必须最低限度地执行此操作。
 * <p>
 *用于控制提示的键都是特殊值,它们是关联的{@link RenderingHints.Key}类的子类。
 *许多常见提示在下面表示为此类中的静态常量,但该列表并不是详尽无遗的。
 *其他提示可以通过定义新的对象来创建,这些对象继承了{@code Key}类并定义了相关的值。

所以我在本地使用的优化有这些:

g.setRenderingHint(SunHints.KEY_ANTIALIASING, SunHints.VALUE_ANTIALIAS_ON);g.setRenderingHint(SunHints.KEY_TEXT_ANTIALIASING, SunHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
//              g.setRenderingHint(SunHints.KEY_STROKE_CONTROL, SunHints.VALUE_STROKE_PURE);
//              g.setRenderingHint(SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST, 100);
//              g.setRenderingHint(SunHints.KEY_FRACTIONALMETRICS, SunHints.VALUE_FRACTIONALMETRICS_OFF);
//              g.setRenderingHint(SunHints.KEY_RENDERING, SunHints.VALUE_RENDER_DEFAULT);

第一行是说明对整个图画的一个矩阵优化,当然如果你和我一样是在图片上添加文字的话,第一行就和第二行没有什么区别。
简而言之,前两行都是对图片点阵锯齿的一个模糊话处理,但是JDK中也说了,会有不可控的抖动会出现,所以在原图放大来看会略微模糊一点。

支持并发,每次读入的图片数据库都有close不用手动去关闭。

以上。

良心手打,有疑问或者好的优化建议欢迎找茬。

JAVA实现生成GIF动态图加文字(完整版无License带锯齿优化处理)相关推荐

  1. 就Java开发的Linux环境,进行完整版的安装,多适用于小白

    本篇博客就Java开发的Linux环境,进行完整版的安装,多适用于小白,当然接受大佬点评: 这是在全新的Linux centOS7.6的系统进行,安装手法以及命令都是根据过往个人操作手法以及经验学习而 ...

  2. 微信小程序:独家微信社群人脉小程序源码带后端控制源码完整版端控带简单教程

    这是一款小编自营的一款人脉小程序系统 小编自营大概有三个多月了吧一直没有给大家公开 本款小程序群二维码自动采集推送的,所以大家不用担心没有群难运营 小编运营几个月不靠一丝一毫的推广每天自然流量都是一千 ...

  3. python与物理结合_Python物理学高效计算(完整版高清带目录)_IT教程网

    资源名称:Python物理学高效计算(完整版高清带目录) 内容简介: 本书介绍了如何通过Python自动化地完成物理领域方面的研究.全书共分为4个部分,以Python代码为示例向读者介绍了如何用Pyt ...

  4. java面试宝典2019_Java面试宝典2019完整版.doc

    Java面试宝典2019完整版.doc PAGE \* MERGEFORMAT2 Java面试宝典2019版 TOC \o "1-3" \h \z \u HYPERLINK \l ...

  5. java派单系统平台源码完整版 带说明

    介绍: Java源码_派单系统平台源码完整版带项目说明 网盘下载地址: http://kekewangLuo.cc/y1eupANcrqW 图片:

  6. Java视频教程从入门到精通(2023完整版)

    java视频教程从入门到精通(2023完整版),Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承.指针等概念,因此Java语言具有功能强大和简单易用两个 ...

  7. 2020年Java最新面试题整理题库完整版

    JAVA面试题 一.JavaSE编程基础 JDK,JRE,JVM三者关系 a. .jdk是JAVA程序开发时用的开发工具包,其内部也有JRE运行环境JRE. b. .JRE是JAVA程序运行时需要的运 ...

  8. java 内存分析之jmap 详细用法完整版(一)

    文章目录 一.jmap下载内存信息命令 二.使用eclipse插件MAT来分析heap profile 前言:当我们遇到内存溢出,想要看详细的内存信息,可以使用java自带工具jmap下载完整的内存信 ...

  9. Java实现小猪佩奇_一入java深似海(1-4期 完整版)

    『课程目录』: ├─第1期 │      开篇 一入 Java 深似?,从此"劝退"成必然.mp4 │      第1节:Java语言基础.mp4 │      第2节:Java面 ...

  10. Java Swing制作2048小游戏【完整版】

    技术讨论群:1005611884 可获取各种资料/代码,群内有Java/Python/C++/C#方向大佬,妹子多 /*** 技术 Swing* 实现思路:* 1. 绘制窗口* 2. 初始化游戏界面* ...

最新文章

  1. “数学不行,干啥也不行”骨灰级程序员:其实你们都是瞎努力
  2. MySQL 8.0版本无法使用 node、Navicat等三方工具连接的问题
  3. 老男孩28期学员苟安邦决心书
  4. tomcat源码分析_Tomcat源码分析(一)--Tomcat的初始化
  5. 决策树会有哪些特性?
  6. 20190725 SecureCRT查看日志修改关键字颜色
  7. 配置ANDROID开发环境的方法
  8. oracle查询小时差,ORACLE小时段 Connect By的查询,感觉还是有点难度的。
  9. 微信小程序之----加载中提示框loading
  10. 微pe工具箱 系统安装教程_微PE工具箱《U盘给电脑装GHOST系统》教程
  11. 江山如画 中国多少绝色
  12. 【ORBSLAM2点线融合】空间直线参数化
  13. 微信开放平台之第三方授权开发
  14. malloc与calloc的区别及实例
  15. 什么是等保?等保流程又具体包含哪些内容呢?
  16. 【整活】修改U盘的图标,让你的U盘与众不同
  17. 【算法】剑指offer - JZ76 删除链表中重复的结点
  18. 处理器后面的字母含义_科普向,笔记本cpu结尾字母的含义
  19. 重新排序(排序不等式+差分)蓝桥
  20. java编程中缩进的意思,解释Java 的语句缩进,请详述,并举例说明(用初级Java程序)...

热门文章

  1. 中国北斗卫星导航系统
  2. 大地测量学基础 (复习)
  3. Python求解拉普拉斯矩阵及其特征值
  4. Python AutoCAD 文件
  5. java 工作流框架都有哪些_java工作流框架有哪些?哪个比较好?
  6. C:\fakepath 问题
  7. 卫星导航开源代码汇总
  8. UE4 创建编辑器模块
  9. 嵌入式linux指纹识别项目,基于嵌入式应用平台的指纹识别系统设计
  10. 万能声卡驱动精灵2016官方版