使用 libpng 处理 png 图像数据

处理图像三个基本步骤,把图像读进来,对图像做些处理,把图像存回去,现在我还困在第一步(手动划掉)

前期准备

读取 png 格式的图像需要使用 libpng.

Mac OSX 上使用如下指令安装 libpng.

1
brew install libpng

png.h 中声明了 libpng 中公有的函数原型和需要的宏。在所有需要使用 libpng 的模块中都需要包含 png.h 头文件

1
#include "png.h"

初始化环境

基本的 PNG 图像读取程序通常开始于图像文件已经打开(二进制模式)并且其文件流指针已经传递给我们的 libpng 初始化例程 readpng_init. 函数原型如下

1
int readpng_init(FILE *infile, long *pWidth, long *pHeight);

返回值为 0 表示读取成功,其他非 0 值表示错误码。

检查文件特征码

readpng_init 我们首先要读取输入文件的前 8 个字节,保证符合 PNG 的特征码,这样我们才可以进行之后的操作,一般的文件读取中通常会建立 512 字节的缓冲区,但是 libpng 有自己的缓冲机制,并不需要多于 8 字节的缓冲。

1
2
3
4
uint8_t sig[8];
fread(sig, 1, 8, infile);
if (! png_check_sig(sig, 8))
return -1; /* bad signature */

建立 PNG 结构

当文件的特征码通过检查后,我们需要使用 png.h 头文件中的结构保存基本的 png 图像信息。

1
2
3
4
5
6
7
8
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_SRTING, NULL, NULL, NULL);
if (! png_ptr)
return 4; /* out of memory */
png_infop info_ptr = png_create_info_struct(png_ptr);
if (! info_ptr){
png_destory_read_struct(&png_ptr, nullptr, nullptr);
return 4; /* out of memory */
}

这里使用两个结构保存 PNG 图像的信息, png_structppng_infop
png_structp 结构指向 libpng 内部的结构体,在任意给定的时刻跟踪其指向的 png 图像的当前状态。
png_infop 结构体用来表示在用户要求每一次图像变换之后图像的状态。当需要保存每次图像处理前和处理后的状态时需要大量的使用该结构,比如图像编辑器

异常处理

libpng 使用 setjmplongjmp 实现出现异常时的跳转,使用 png_ptr->jmpbuf 保存当前堆栈和寄存器的信息

1
2
3
4
5
if (setjmp(png->jmpbuf))
{
png_destory_read_struct(&png_ptr, &info_ptr, nullptr);
return 2; /* error when save stack */
}

读取图像数据

接下来是真正读取数据的部分代码

1
2
3
png_init_io(png_ptr, infile);
png_set_sig_bytes(png_ptr, 8);
png_read_info(png_ptr, info);

png_init_io 接口将文件流指针存储在 png_structp 结构中以备后续使用。
png_set_sig_byteslibpng 表示这里已经检查过 png 的特征码所以这里不应再文件流中检查特征码。
png_read_info 读取 png 图像文件的元信息,如 IHDR, PLTE 等头部,详见文档, 接下来读取图像中的宽和高

1
2
3
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &colortype, nullptr, nullptr, nullptr);
*pWidth = widht;
*pHeight = height;

后来发现我用 opencv 就方便太多了,(๑•̀ㅂ•́)و✧
这坑就留着了