文章目录

  • 背景
  • png格式简介
  • 图片压缩实操
    • 使用OpenViewerFX
    • 使用pngquant(JNI调用)
      • 在linux下构建
      • 在win下构建
      • JNI调用
      • 关于pngquant的jni动态链接库文件下载

背景

需求是大大的减小彩信图片的体积,可以发送更多数量的彩信图片,图片的内容是报表,颜色单一。最开始把透明色去掉了,位深从32->24,但是最近测试发现彩信图片体积还是太大了,这就需要去学习图片压缩的知识了

png格式简介

PNG格式有8位、24位、32位三种形式,其中8位PNG支持两种不同的透明形式(索引透明和alpha透明),24位PNG不支持透明,32位PNG在24位基础上增加了8位透明通道,因此可展现256级透明程度。位深度越大图片体积越大,能够表示的色彩越丰富。

png有种索引彩色模式,采用8位调色板将RGB彩色图像转换为索引彩色图像。图像中保存的不再是各个像素的彩色信息,而是从图像中挑选出来的具有代表性的颜色编号,每一编号对应一种颜色,图像的数据量也因此减少,这对彩色图像的传播非常有利。

通过索引png,可以大大减小图片体积,如果图片中的颜色种类小于256,那么就可以使用索引格式的PNG,如果图片原本的颜色大于256种,那么可以通过矢量量化的方法来创建一个索引PNG格式(有损压缩)

上面只是一个简单的介绍,如果要深入了解可以看看:
https://www.jianshu.com/p/5ad19825a3d0
https://www.jianshu.com/p/324744087e24

图片压缩实操

我这里的场景非常适合png索引图模式,因为图片内容是表格报表,颜色最多只有4种,远远小于索引图的256色,如果颜色丰富的那种照片之类的还是用jpg压缩吧。。。

这里重点介绍png量化转索引图
看网上框架有:tinypng、pngquant、OpenViewerFX6.6.14等等

根据资料显示,tinypng、pngquant、ImageAlpha、pngnq都是有损压缩,基本采用的都是quantization算法,将32位的PNG图片转换为8位的PNG图片,减少图片的颜色数;pngcrush、optipng、pngout、advpng都是无损压缩,采用的都是基于LZ/Huffman的DEFLATE算法,减少图片IDAT
chunk区域的数据。一般有损压缩的压缩率会大大高于无损压缩。

我这里使用开源的pngquant框架和OpenViewerFX6.6.14来操作

使用OpenViewerFX

<dependency><groupId>org.jpedal</groupId><artifactId>OpenViewerFX</artifactId><version>6.6.14</version>
</dependency>
PngCompressor.compress(inputStream, outputStream);

这个的使用极其方便,就一句,input进output出,就是不像pngquant有很多参数可以选,如果只是简单的压缩,这个就足够了

28k(24位) -> 12.6k(8位),发现已经成功的转为了索引图

使用pngquant(JNI调用)

使用pngquant框架,这是一款比较流行的png压缩开源框架,它是一种有损压缩,并将其转为索引图,这种转换大大减小了文件大小(通常高达70%),并保留了完全的alpha透明度。生成的图像与所有web浏览器和操作系统兼容。

它是c语言写的,这里麻烦一点,需要使用jni的方式调用,这里就当练习下jni使用了

官方参考文档: https://pngquant.org/lib/

首先去https://github.com/ImageOptim/libimagequant下载源码,发现它是基于makefile构建的.

文档说使用make java就可以构建了,dll的话就是make java-dll

本地环境: centOS7.3 64位、win7 64位、jdk1.8、gcc9.3.0、GNU make4.3

在linux下构建

在linux下构建比较简单,直接cd到目录下去make java

遇到的问题有:
1、configure文件格式的问题,把内容复制下来重新copy一份即可

2、bad value错误

make clean
make distclean
./configure --extra-cflags="-fPIC"
make java

带上fPIC再试一次就可以了


编译完成后就有了libimagequant.jnilib文件
当然,在linux上我们需要的是libimagequant.so而不是libimagequant.jnilib(这是macOS上使用的)
Make java的时候输出的最后一个命令,我们copy下来修改一下

gcc -g -fno-math-errno -funroll-loops -fomit-frame-pointer -Wall -std=c99 -I. -O3 -DNDEBUG -DUSE_SSE=1 -msse -mfpmath=sse -Wno-unknown-pragmas -fexcess-precision=fast  -fPIC -lm -I'/usr/lib/jvm/java-1.8.0-openjdk/include' -I'/usr/lib/jvm/java-1.8.0-openjdk/include/linux' -I'/usr/lib/jvm/java-1.8.0-openjdk/include/win32' -I'/usr/lib/jvm/java-1.8.0-openjdk/include/darwin' -shared -o libimagequant.so org/pngquant/PngQuant.c libimagequant.a

把目标文件改为.so,重新编译就可以了

在win下构建

在win下构建可以用于本地开发测试,所以我这里打一个dll
Win下使用cygwin就可以使用make命令构建了,当然,用visual studio也可以,还能避免一些坑。。。
Cygwin是一个可原生运行于Windows系统上的POSXI兼容环境,使用它就可以用到常用的ls、pwd、make、gcc等命令,也是推荐的c++开发工具链

遇到的问题有:
1、Configure文件格式不对,这个解决办法跟linux下一样

2、__int64定义错误
这种解决方法是不对滴,为了引出问题还是记录一下过程

发现报错了,因为gnu中没有 __int64,而引用的win版本的jdk又有这个玩意

当时的解决方法是修改jni_md.h的34行,加上#ifdef else语句判断一下当前环境。或者gcc编译参数中加入宏定义-D__int64=’long long’
我直接修改了编译参数,在Makefile里面

java-dll:$(MAKE) CFLAGS="$(CFLAGS)  -DIMAGEQUANT_EXPORTS" $(JNIDLL)
$(JNIDLL) $(JNIDLLIMP): $(JAVAHEADERS) $(OBJS) org/pngquant/PngQuant.c$(CC) -D__int64='long long' -fPIC -shared -I. $(JAVAINCLUDE) -o $(JNIDLL) $^ $(LDFLAGS) -Wl,--out-implib,$(JNIDLLIMP),--output-def,$(JNIDLLDEF)

在这两处加上-D__int64=’long long’,给__int64这种类型一个宏定义,编译倒是成功编译了,就是运行时有错误

3、jni运行时error
这一步是在程序运行中报错的,可以先看下面jin的使用再看这步报错

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x0000000180136797, pid=9848, tid=0x0000000000001c3c
#
# JRE version: Java(TM) SE Runtime Environment (8.0_191-b12) (build 1.8.0_191-b12)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.191-b12 mixed mode windows-amd64 compressed oops)
# Problematic frame:
# C  [cygwin1.dll+0xf6797]
……
Stack: [0x0000000002580000,0x0000000002680000],  sp=0x000000000267f6a0,  free space=1021k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [cygwin1.dll+0xf6797]
C  [cygwin1.dll+0x9431f]Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  org.pngquant.PngQuant.liq_attr_create()J+0
j  org.pngquant.PngQuant.<init>()V+5
j  com.ai.base.tool.pngquant.PngQuantTest.main([Ljava/lang/String;)V+4
v  ~StubRoutines::call_stub

在调用create的时候直接error了,说是cygwin1.dll里面报错,这个cygwin1.dll是POSIX系统调用的模拟层,使用cygwin构建的所有程序都依赖了cygwin1.dll,所以这里不是关键。
在看下面,报错是在liq_attr_create方法中,就是编译出来的dll有问题,不能正常运行。
解决方法是:换用MinGW编译器,MinGW相比CygWin/gcc来讲,更加贴近win32。因为它几乎支持所有的Win32API。

直接修改configure文件,大概在17行的位置

# make gcc default compiler unless CC is already set
CC=${CC:-x86_64-w64-mingw32-gcc}

把编译器换成这个,我这里是用的cygwin的组件,如果没有的话,去setup一遍cygwin,把这个组件选择安装上
然后重新编译一次,编译过后体积大了100多k,不过终于能正常运行了


关于编译好的pngquant动态链接库文件,文章最后有下载

JNI调用

Jni这里就不多介绍了
在pngquant源码中,lib\org\pngquant目录下找到java文件
这个makefile脚本会调用javah生成头文件

它长这个样子,可以发现是javah自动生成的,下面的方法名是根据:Java+包名+类名+方法名生成的
所以如果生成好了,在项目中引用时不要修改包名,不然找不到方法

如果遇到找不到方法,那么就确认一下动态链接库的方法名是否正确

通过nm命令查看,它可以列举文件中的符号,当然也可以看到方法名

我这里的包结构是这样的

Win下System.loadLibrary的是imagequant.dll(不带lib前缀),linux下就是libimagequant.so(带lib前缀)

public class PngQuantTest {public static void main(String[] args) {BufferedImage newImg;PngQuant pngQuant = new PngQuant();try {newImg = pngQuant.getRemapped(ImageIO.read(new File("D:\\test\\92.png")));ImageIO.write(newImg, "png", new File("D:\\test\\new_92.png"));} catch (IOException e) {e.printStackTrace();}pngQuant.close();}
}

最后写一个测试方法,运行添加jvm参数: -Djava.library.path=./lib,然后测试成功!

关于jni还有个比jni性能好一点的javacpp有兴趣可以看看
https://github.com/bytedeco/javacpp

关于pngquant的jni动态链接库文件下载

https://download.csdn.net/download/w57685321/12706816

java中png转索引图压缩图片相关推荐

  1. Java 中调用 Apache API 实现图片文件的 压缩 与 解压 实例

    < Java 中调用 Apache API 实现图片文件的 压缩 与 解压 > 为什么不直接使用 Java JDK 中自带的 API 呢?必须使用 Apache API 实现文件的压缩与解 ...

  2. java中生成pdf,插入图片,页眉、页脚、表格

    全栈工程师开发手册 (作者:栾鹏) java教程全解 java中生成pdf,插入图片,页眉.页脚.表格 import com.lowagie.text.*; import com.lowagie.te ...

  3. 压缩图片大小的java代码_java按比例压缩图片的源代码,用java如何把图片处理到指定大小...

    [要分析某个网页中的代码构成,需要某个结点下的内容.用此原始方法可以得到整个网页的源码.其实更简单的方法是使用 WebClient 或 HtmlUtil 等开源方式 .public class Ht ...

  4. java 使用Thumbnailator 上传图片 并压缩图片大小

    http://www.cnblogs.com/miskis/p/5500822.html java 上传图片 并压缩图片大小 Thumbnailator 是一个优秀的图片处理的Google开源Java ...

  5. java中图片与像素矩阵转换,java - Java中具有矩阵乘法的图片转换不起作用 - 堆栈内存溢出...

    我正在用Java实现图片转换. 到目前为止,我已经实现了以下类: 矩阵 (持有一个3x3矩阵,该矩阵将用于与Vector相乘) 向量 (用于与变换矩阵相乘以生成原始图像像素的新位置) PictureT ...

  6. Java中JFrame窗口添加背景图片

    知识储备: 1.JFrame中是有很多层次关系的,具体可以去自己查一下: 主要思路: 1.把图片添加到标签里(把标签的大小设为和图片大小相同),把标签放在分层面板的最底层: 2.把窗口面板设为内容面板 ...

  7. Java中关于超长字符串压缩,解压缩问题

    问题:数据库中varchar类型的长度设置为5000,通过一个富文本编辑器编辑了通知内容,接收到了内容字段长度为8000多,unruly数据库时候,数据库报错(字段太长,显示下标越界异常) 解决办法: ...

  8. java中如何gzip_Java如何压缩Gzip格式的文件?

    在此代码示例中,我们将学习如何使用gzip压缩来压缩文件.从本质上讲,gzip只能压缩一个文件,不能将其用于压缩目录以及该目录中的所有文件. 您将用于以gzip格式压缩文件的类包括GZipOutput ...

  9. java 中导出word后压缩文件_Java批量导出word压缩后的zip文件案例

    一.js代码,由于参数比较大所以利用form表单使用post导出 function export_word(){ var selectedRows = $("#dg").datag ...

最新文章

  1. JavaScript获取DOM元素位置和尺寸大小
  2. mysql数据库操作手册
  3. 吴恩达深度学习 —— 2.11 向量化
  4. JBox2D手机游戏引擎介绍(附jbox2d官网网址)
  5. shell脚本工具之awk命令
  6. ITester软件测试小栈历时9个月文章汇总
  7. 用iMindMap制作健康生活计划思维导图
  8. sqlplus 命令导入数据文件
  9. 录制课程视频用什么软件?微课录屏软件等你来选!
  10. 宝塔面板的数据库地址配置
  11. Anycloud平台LOGO生成方法
  12. python交易是什么意思_Py交易是什么意思?Py交易是什么梗?
  13. 帕累托图(Pareto Diagram,排列图)
  14. java天眼培训_Java天眼大型分布式跟踪系统 附带源码_IT教程网
  15. 单应性矩阵 matlab,单应性矩阵MATLAB程序
  16. 【从零开始学习 SystemVerilog】11.2、SystemVerilog 断言—— Immediate Assertions(立即断言)
  17. PortTunnel端口映射后,取外网IP
  18. ZOJ 2112 Dynamic Rankings(主席树-动态第k大)
  19. 汇正财经靠谱吗?沪深创集体调整
  20. Redis入门到精通-Redis安全性

热门文章

  1. 2020杭电计算机考研经验帖
  2. CSS3各种手型样式集合
  3. 记录一次阿里云服务器被攻击事件
  4. Git使用学习(七、版本回滚)
  5. 「DLP-KDD 2021征文」及上届论文全集,包含深度学习推荐/广告系统、多目标、模型服务等
  6. 除了“带地球去流浪”,还有哪些脑洞大开的太空计划?
  7. ES6——TDZ(暂时性死区)
  8. STM32CubeIDE 介绍及安装
  9. 应用商店-华为应用市场
  10. #踩过的坑# 企业微信被封了怎么办?