参考资料:
Writing PNG Images (PNG: The Definitive Guide)
Reading PNG Images (PNG: The Definitive Guide)

我要讲的三个大部分分别是:
1、libpng是什么,能做什么?
2、怎样让自己的程序可以使用libpng库?
3、怎样借助libpng读写PNG文件

1、libpng是什么?
libpng是一款C语言编写的比较底层的读写PNG文件的跨平台的库。借助它,你可以轻松读写PNG文件的每一行像素。
因为PNG文件是经过压缩而且格式复杂的图形文件(有的PNG文件甚至像GIF文件一样带动画效果)
而且PNG可以是带透明通道的真彩色图像、不带透明通道的真彩色图像、索引颜色、灰度颜色等各种格式,如果大家都自己写程序分析PNG文件就会显得很麻烦、很累。因此,通过使用libpng你就能直接使用现成的函数、程序来读写PNG文件了。
注:libpng的官网说,“PNG”这个词的发音是“拼”(ping),而不是“批恩鸡”(pee en gee,也就是直接读字母),也不是其它的单词发音(什么pinj、pig之类的发音)。
但是如果你不是以英语为母语的话(咱都是中国人呢~~)你就不必蛋疼地在意这个发音的问题,直接见人就说“批恩鸡”就行啦。(当我没说)

2、怎样让自己的程序可以使用libpng库?
有很多种方法。
方法1:上网下载libpng的DLL、LIB文件以及头文件,然后在自己的程序里,包含png.h,链接libpng.lib,就可以了。但是这样的话你的程序需要libpng.dll才能运行,而libpng使用了 zlib所以可能你还需要zlib.dll才能运行。因此你还需要下载zlib的头文件、lib、DLL。
方法2:直接下载libpng的源码和zlib的源码,然后把.c文件和.h文件都加入到自己的工程里面。这招最好使因为这样便于调试。只是你的程序会很大因为你的程序直接集成了libpng和zlib。
方法3:下载libpng的源码和zlib的源码,自己将其编译为DLL或LIB,然后包含png.h,链接LIB文件,就能使用。不过你如果没编译好也可能会出问题。

3、怎样借助libpng读写PNG文件
首先来讲如何写入PNG文件。
第一步:初始化libpng库。
当你需要读一个PNG文件或者写一个PNG文件的时候,你需要先定义两个结构体指针:

[C] 纯文本查看 复制代码
?
1
2
png_structp png_ptr=NULL; //libpng的结构体
png_infop   info_ptr=NULL; //libpng的信息

你可以把上面的结构体指针定义为全局变量使用。
每这两个结构体对应一个PNG文件。因此当你要同时操作多个PNG文件的时候,你就需要定义多个 png_structp和 png_infop来处理这些PNG文件了。
因为是要写文件,所以要这样初始化:

[C] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
int iRetVal;
png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
if (!png_ptr)
     goto 错误处理;
info_ptr=png_create_info_struct(png_ptr);
if (!info_ptr)
{
     png_destroy_write_struct(&png_ptr,NULL);
     goto 错误处理;
}
iRetVal= setjmp (png_jmpbuf(png_ptr)); //安装错误处理跳转点
//当libpng内部出现错误的时候,libpng会调用longjmp直接跳转到这里运行。
if (iRetVal) //setjmp的返回值就是libpng跳转后提供的错误代码(貌似总是1,但是还是请大家看libpng的官方文档)
{
     fprintf (stderr, "错误码:%d\n" ,iRetVal);
     goto 错误处理;
}

只要最后png_ptr和info_ptr都不是NULL就行了。否则就算是出错了。
这里可以看到libpng使用了setjmp来做错误处理。 有关setjmp的信息请点这里进去看。
这两个结构体有对应的释放函数: png_destroy_write_struct
结束对一个PNG的访问之后,你只需像这样调用这个函数:

[C] 纯文本查看 复制代码
?
1
png_destroy_write_struct(&png_ptr,&info_ptr);

就可以了。
接下来打开文件准备写文件。还是大家熟悉的C语言文件流。

[C] 纯文本查看 复制代码
?
1
2
3
FILE *fp= fopen ( "C:\\TEST.PNG" , "wb" );
if (!fp)
     goto 错误处理;

打开了文件以后,你要让libpng和这个文件流绑定起来,因此你需要调用 png_init_io来完成绑定。

[C] 纯文本查看 复制代码
?
1
png_init_io(png_ptr,fp);

接下来就是关键的部分了:设置PNG文件的属性、写入PNG文件头、写入PNG文件。

[C] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
//设置PNG文件头
png_set_IHDR(png_ptr,info_ptr,
     图像宽度,图像高度, //尺寸
     8, //颜色深度,也就是每个颜色成分占用位数(8表示8位红8位绿8位蓝,如果有透明通道则还会有8位不透明度)
     PNG_COLOR_TYPE_RGB, //颜色类型,PNG_COLOR_TYPE_RGB表示24位真彩深色,PNG_COLOR_TYPE_RGBA表示32位带透明通道真彩色
     PNG_INTERLACE_NONE, //不交错。PNG_INTERLACE_ADAM7表示这个PNG文件是交错格式。交错格式的PNG文件在网络传输的时候能以最快速度显示出图像的大致样子。
     PNG_COMPRESSION_TYPE_BASE, //压缩方式
     PNG_FILTER_TYPE_BASE); //这个不知道,总之填写PNG_FILTER_TYPE_BASE即可。
png_set_packing(png_ptr); //设置打包信息
png_write_info(png_ptr,info_ptr); //写入文件头

执行完这些语句以后,你会发现libpng已经通过文件流指针fp写入了PNG的文件头。
接下来要做的就是写入PNG的图像信息。其实就是把颜色保存到PNG。
不像恶心的BMP居然有“底到上型”和“顶到下型”之分,PNG只有“顶到下型”,因此你不需要考虑行序。
写图的方法之一是调用 png_write_image(png_ptr,行指针数组的指针);这个你不需要考虑交错文件的写入的遍数。
而如果你需要手动写入每一行的数据,你需要调用 png_write_row(png_ptr,一行像素的指针);来进行逐行的像素值写入。
如果你设置了交错格式的PNG,你需要多写入几遍图形数据,你需要调用 png_set_interlace_handling(png_ptr);来得知你需要写入的遍数。如果你没有设置交错格式的PNG,你只需要写入一遍。

以下文本写给小白。高手请略过。
这里需要详细说明图像是怎么写入的。首先说明一下什么是图像。一个图像,是由一个一个的正方形的小像素点组成的。
每个像素点都有自己的颜色值,用三个字节来表示红色、绿色、蓝色的分量。所有的颜色都是用红绿蓝三基色混合搭配调出来的颜色。
然后对于libpng,图像是一行一行写入到文件的。这里所说的“行”和“列”指的是排列起来的像素点。比如一个图像的宽度是1024像素,高度是768像素,那么这个图像就有768行,每行有1024个像素点。
假设你设置的颜色深度是8,颜色类型是 PNG_COLOR_TYPE_RGB,那么你的每个像素点都是由三个字节组成的,这三个字节分别是红色、绿色和蓝色的分量。
而如果颜色类型是 PNG_COLOR_TYPE_RGBA,那么你的每个像素点都是由四个字节组成的,这四个字节分别是红色、绿色和蓝色的分量和不透明度。这样的图像才支持透明颜色的显示。
调用 png_write_row的方法很简单,就是把一行的像素点的颜色设置好,然后调用它: png_write_row(png_ptr,这行像素的第一个像素在内存中的位置);就可以写入一行。
而调用 png_write_image你需要把每一行的像素颜色都设置好,然后建立一个指针数组,这个指针数组的每一个指针都指向每一行的像素。明白吧?

写入好像素以后,调用 png_write_end(png_ptr,info_ptr);把文件的结尾写入。
调用 png_destroy_write_struct(&png_ptr,&info_ptr);结束对这个PNG文件的访问。
最后 fclose(fp);关闭文件。这个时候你会发现,你已经成功地产生了一个PNG文件!而且可以用PS打开了。

读取PNG文件也是类似的步骤,首先你需要初始化libpng库。
你需要先定义两个结构体指针:

[C] 纯文本查看 复制代码
?
1
2
png_structp png_ptr=NULL; //libpng的结构体
png_infop   info_ptr=NULL; //libpng的信息

你可以把上面的结构体指针定义为全局变量使用。
每这两个结构体对应一个PNG文件。因此当你要同时操作多个PNG文件的时候,你就需要定义多个 png_structp和 png_infop来处理这些PNG文件了。
因为是要读文件,所以要这样初始化:

[C] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
int iRetVal;
png_ptr=png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
if (!png_ptr)
     goto 错误处理;
info_ptr=png_create_info_struct(png_ptr);
if (!info_ptr)
{
     png_destroy_read_struct(&png_ptr,NULL,NULL);
     goto 错误处理;
}
iRetVal= setjmp (png_jmpbuf(png_ptr)); //安装错误处理跳转点
//当libpng内部出现错误的时候,libpng会调用longjmp直接跳转到这里运行。
if (iRetVal) //setjmp的返回值就是libpng跳转后提供的错误代码(貌似总是1,但是还是请大家看libpng的官方文档)
{
     fprintf (stderr, "错误码:%d\n" ,iRetVal);
     goto 错误处理;
}

只要最后png_ptr和info_ptr都不是NULL就行了。否则就算是出错了。
这两个结构体有对应的释放函数: png_destroy_read_struct
结束对一个PNG的访问之后,你只需像这样调用这个函数:

[C] 纯文本查看 复制代码
?
1
png_destroy_read_struct(&png_ptr,&info_ptr,NULL);

就可以了。
接下来打开文件准备读文件。还是大家熟悉的C语言文件流。

[C] 纯文本查看 复制代码
?
1
2
3
FILE *fp= fopen ( "C:\\TEST.PNG" , "rb" );
if (!fp)
     goto 错误处理;

打开了文件以后,你要让libpng和这个文件流绑定起来,因此你需要调用 png_init_io来完成绑定。绑定之后,你还需要获取PNG的文件头信息。因此你需要调用 png_read_info(png_ptr, info_ptr);

[C] 纯文本查看 复制代码
?
1
2
png_init_io(png_ptr,fp);
png_read_info(png_ptr, info_ptr);

读取了文件头,你就能获取文件头的信息。比如文件尺寸、位深度等。代码如下:

[C] 纯文本查看 复制代码
?
1
png_get_IHDR(png_ptr,info_ptr,&width,&height,&bit_depth,&color_type,NULL,NULL,NULL);

有些PNG文件是有背景色的,因此你需要处理这些背景色信息。我们可以用 png_get_valid来判断这个PNG是否有背景色信息。 png_get_valid(png_ptr,info_ptr,PNG_INFO_bKGD)返回0表示没有背景色信息,返回非零表示有背景色信息。然后我们调用 png_get_bKGD来读取背景色。

[C] 纯文本查看 复制代码
?
1
2
png_color_16p pBackground;
png_get_bKGD(png_ptr,info_ptr,&pBackground);

大家可以看看 png_color_16p的原型:

[C] 纯文本查看 复制代码
?
1
2
3
4
5
6
7
8
typedef struct png_color_16_struct
{
     png_byte index;
     png_uint_16 red;
     png_uint_16 green;
     png_uint_16 blue;
     png_uint_16 gray;
} png_color_16;

如果这个PNG是调色板颜色的位图,那么 index表示背景色的调色板颜色序号。
red、green、blue表示背景色的颜色值。如果 png_get_IHDR返回的位深度(bit_depth)是16,那么red、green、blue就是16位的颜色值,范围0~65535。(瞬间觉得PNG高大上啊!16+16+16=48,这个比真彩色还要真彩色!屌!)
而如果 png_get_IHDR返回的位深度(bit_depth)是8,那么red、green、blue其实都是8位的颜色值,范围0~255,也就是24位真彩色。
接下来就是关键的步骤了,读取颜色数据。
因为有些PNG是灰度色,有些PNG是索引颜色,有些PNG是48位色,总之各种奇葩。为了便于读取,我们应该先规范一下格式。

[C] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
if (colortype==PNG_COLOR_TYPE_PALETTE)
     png_set_palette_to_rgb(png_ptr); //要求转换索引颜色到RGB
if (colortype==PNG_COLOR_TYPE_GRAY && bit_depth<8)
     png_set_expand_gray_1_2_4_to_8(png_ptr); //要求位深度强制8bit
if (bit_depth==16)
     png_set_strip_16(png_ptr); //要求位深度强制8bit
if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS))
     png_set_tRNS_to_alpha(png_ptr);
if (colortype == PNG_COLOR_TYPE_GRAY || colortype == PNG_COLOR_TYPE_GRAY_ALPHA)
     png_set_gray_to_rgb(png_ptr); //灰度必须转换成RGB

经过这些设定以后,我们读取的PNG就一律是R8:G8:B8:A8的4字节格式了(红绿蓝透明均为8位,每像素4字节)
然后准备读取PNG。首先分配一个足够大的内存来存储颜色数据,然后分配一个内存来存储颜色数据每行的指针。
因为颜色已经被规范为32位了所以我们可以直接把每个像素当做一个COLORREF变量。

[C] 纯文本查看 复制代码
?
1
2
3
4
5
6
7
ppLinePtrs=( COLORREF **) malloc (g_dwHeight* sizeof ( COLORREF *)); //列指针
if (!ppLinePtrs)
     goto Error;
i=g_dwHeight;
y=0;
while (i--) //逆行序读取,因为位图是底到上型
     ppLinePtrs[y++]=( COLORREF *)&g_pBits[i*g_dwWidth];

这个时候就是万事俱备的时候,只需要调用png_read_image(png_ptr,(png_bytepp)ppLinePtrs);就能完成读取。
读取完以后,调用png_read_end(png_ptr,info_ptr);结束读取,调用png_destroy_read_struct(&png_ptr,&info_ptr,NULL);销毁结构体,然后fclose(fp);就算一切都搞定了。

接下来我会放上两份源码,一份把BMP转换成PNG(支持把两个BMP合体转换成带透明通道的PNG)
而另一份是结合了分层窗体的技术,把PNG当做分层窗体的界面来显示的源码。
效果不错。发张图晒晒。

雾切响子.PNG (73.78 KB, 下载次数: 99)

下载附件  保存到相册

2014-3-30 11:22 上传

BMP转PNG:
EXE下载:

BMP2PNG.exe

点击文件名下载附件

分层窗体:
EXE下载:

LayeredPNG_bin.7z

点击文件名下载附件

转载自:https://www.0xaa55.com/forum.php?mod=viewthread&tid=425&extra=page%3D1&page=1

【C】libpng的使用相关推荐

  1. Java在linux新建png_教你如何使用libpng显示PNG图片

    libpng是一个跨平台的png解码库,方便易用.我这里不说它怎么移植,基本上如果是支持fopen之类函数的平台都可以支持.其实他的移植非常简单,zlib可能麻烦一点,但相信一般人都能搞定.主要是文件 ...

  2. mac下安装libpng环境

    用go写一个爬虫工具时需要使用一个go的库,而这个库有需要使用libpng库,不然编译就会提示说 png.h找不到等之类的信息,于是想到应该和windows一样需要安装gcc环境,然后让gcc里安装l ...

  3. windows下使用cmake编译zlib与libpng libjpeg 留此备份

    win7下使用VS2010编译jpeglib 1.下载源代码下载地址:http://www.ijg.org/files/,     选择最新版本的windows版本压缩包,进行下载.     jpeg ...

  4. 最快的PNG图像解码器!速度提升2.75倍,比老大哥“libpng”还安全

    艳艳 发自 凹非寺 量子位 报道 | 公众号 QbitAI 提到PNG,大多数人都不会感到陌生. 这种位图格式在图像领域使用频率仅次于JPEG. 然而在"解码PNG"这件事上,23 ...

  5. Visual studio中编译和使用libpng和zlib

    Visual studio中编译和使用libpng和zlib https://blog.csdn.net/jinzhuojun/article/details/7972747 转载于:https:// ...

  6. 【Android 内存优化】Android 原生 API 图片压缩原理 ( Bitmap_compress 方法解析 | Skia 二维图形库 | libjpeg 函数库 | libpng 函数库 )

    文章目录 一. 图片质量压缩方法 二. Skia 二维图形库 三. libjpeg.libpng 函数库引入 在博客 [Android 内存优化]图片文件压缩 ( Android 原生 API 提供的 ...

  7. VC++编译libpng

    目录 第1章简介    1 第2章 Visual C++6.0    2 2.1 打开项目    2 2.2 编译宏    3 2.2.1 小结    5 第3章 Visual C++2010     ...

  8. 解决9.png malformed以及libpng warning: iCCP

    2019独角兽企业重金招聘Python工程师标准>>> 错误一:AAPT err(Facade for ********): ERROR: 9-pathc image ******* ...

  9. Qt:解决使用png图片时,报错libpng warning: iCCP: known incorrect sRGB profile的问题

    在Qt中,如果使用某些格式png图片,可能会报警告libpng warning: iCCP: known incorrect sRGB profile,虽然没什么影响,但是看到这个警告非常的烦. 网上 ...

  10. linux安装zlib_Linux zlib和libpng安装(LAMP环境搭建)

    本节讲解 Linux 在搭建 LAMP 环境过程中对 zlib 和 libpng 源码包的安装. 安装zlib源码包 zlib 是提供数据压缩用的函数库,使用 DEFLATE 算法,最初是为 libp ...

最新文章

  1. 女生读计算机专业好,女生选择计算机专业就读好吗?
  2. 在你休息时,你的大脑运动皮层中重放习得的神经放电序列
  3. JS+HTML画图的几种方法
  4. ES6 import export
  5. Python生成随机数的方法
  6. win2008r2 mysql 远程_SQL SERVER 2008 R2如何开启数据库的远程连接(转)
  7. pptx打不开,未安装该文件类型的文本转换程序~[解决方案]
  8. 数组玩法(1):下标移位
  9. 鸿蒙系统能玩魔兽世界吗,苹果M1可以玩魔兽世界吗 M1芯片能玩魔兽吗
  10. 大创和互联网加_大创?科研立项?互联网+大赛?创业大赛?……你还在纠结吗?...
  11. 通用技术标模板,技术方案书
  12. Java实现图片转pdf、pdf合并
  13. 计算导论与c语言基础pdf下载,Cousera 计算导论与C语言基础 学习笔记
  14. 04、Flutter FFI 字符串
  15. 最新电脑cpu性能排行服务器,服务器cpu性能排行,手把手教你服务器cpu性能排行...
  16. Mac常见问题:如何使用文件保险箱加密 Mac 上的启动磁盘!
  17. 不同的经络,不同的线程
  18. 沈阳市计算机学校1996届,生命科学学院1996届应用生物班校友回母校举行毕业20周年联谊会...
  19. excel一列前加一固定值
  20. 如何用Word批量制作专属邀请函

热门文章

  1. FastDFS,Redis,Solr,ActiveMQ核心技术整合一
  2. 运行Form时提示参数无效直接崩溃的解决办法
  3. Java Complier, JVM, JIT(Just In Time Compiler) 三者之间的关系
  4. 2023年1月9日:fastadmin在列表操作列区域添加按钮及控制已有按钮显示
  5. 数据通信--ASCII码通信16进制通信的区别
  6. 强强联合,极智嘉(Geek+)携磅旗科技共同赋能广东洲明智慧物流升级
  7. 英文期刊等级查询网址
  8. cocos creator慢镜头
  9. 蓝桥杯2016第七届C语言B组省赛习题题解——习题B.生日蜡烛
  10. 37种传感器(十一)之金属触摸模块+Stduino NanoUNO