上一篇文章简单介绍了libpng ,翻译了手册前两节
本篇继续翻译 读和写 的内容
读
现在,我们将依次介绍在读取PNG文件时可能调用的函数,并简要解释每个函数的语法和用途。更多细节请参见example.c和png.h。虽然下一节将介绍渐进式读取,但您仍然需要本节中讨论的一些函数来读取PNG文件。
设置
在使用libpng之前,您需要执行1/0初始化(*),因此如果它不起作用,您无需撤销太多。当然,你也要确保你是,事实上,处理PNG文件。Libpng提供了一个简单的检查,以确定一个文件是否是PNG文件。要使用它,将文件的前1到8个字节传递给函数ong_sig_cmp(),如果字节匹配PNG签名的对应字节,则返回e (false),否则返回非零(true)。当然,传入的字节越多,预测的准确性就越高。
如果你打算保持文件指针用于libpng打开,你必须确保不超过8个字节, 并且必须调用 png_set_sig_bytes() 使用从一开始读取的字节数作为参数。Libpng将只检查程序没有读取的字节(如果有的话)。
(*):如果你不使用标准的I/O函数,你将需要用自定义函数替换它们。请参阅自定义libpng下的讨论。
FILE *fp = fopen(file_name, "rb");
if (!fp)
{
return ERROR;
}
if (fread(header, 1, number, fp) != number)
{
return ERROR;
}
is_png = !png_sig_cmp(header, 0, number);
if (!is_png)
{
return NOT_PNG;
}
接下来,png_struct 和 png_info 需要被分配和初始化。为了确保这些结构的大小是正确的,即使使用动态链接的libpng,也有函数来初始化和分配结构。如果需要,我们还传递库版本、指向错误处理函数的可选指针,以及指向错误函数使用的数据结构体的指针(如果使用默认错误处理程序,指针和函数可以为空)。如果创建结构失败,结构分配函数会悄悄地返回NULL,因此应用程序应该检查这一点
png_structp png_ptr = png_create_read_struct
(PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr,
user_error_fn, user_warning_fn);
if (!png_ptr)
return ERROR;
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
{
png_destroy_read_struct(&png_ptr,
(png_infopp)NULL, (png_infopp)NULL);
return ERROR;
}
如果你想使用你自己的内存分配程序,使用一个libpng,它是用 PNG_USER_MEM_SUPPORTED 宏定义,并使用 png_create_read_struct_2() 而不是 png_create_read_struct():
png_structp png_ptr = png_create_read_struct_2
(PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr,
user_error_fn, user_warning_fn, (png_voidp)
user_mem_ptr, user_malloc_fn, user_free_fn);
传递给png_create_read struct()的错误处理例程和传递给png_create_struct2()的内存alloc/free例程只有在不使用libpng提供的错误处理和内存alloc/free函数时才有必要。
当libpng遇到错误时,它期望你进行回调操作。因此,您将需要调用setjmp并传递你的png jmpbuf (png_ptr)。如果您从不同的地方读取文件例程时,每次输入一个将调用png*()函数的新例程时,都需要更新longimp缓冲区。
有关setimp/longimp的更多信息,请参阅编译器的setimp/longimp文档。有关libpng错误处理的更多信息,请参阅下面自定义libpng一节中关于libpng错误处理的讨论。如果出现错误,并且libpng longjmp返回到setjmp,您将需要调用png_destroy_read_struct() 来释放内存。
if (setjmp(png_jmpbuf(png_ptr)))
{
png_destroy_read_struct(&png_ptr, &info_ptr,
&end_info);
fclose(fp);
return ERROR;
}
如果你没有创建一个end info结构,传递(png infopp)NULL而不是&end info。
如果您宁愿避免复杂的setimp/longimp问题,您可以使用PNG NO SETJMP编译libpng,在这种情况下错误将导致调用PNG_ABORT(),该函数默认为abort()。
您可以#define PNG ABORT() 为函数定义一些比 abort() 更有用的功能,只要函数不返回。
现在需要设置输入代码。libpng的默认值是使用c函数fread()。如果你使用这个,你将需要在函数png_init_io()中传递一个有效的 FILE * 文件指针。确保该文件是以二进制模式打开的。如果希望以另一种方式处理读取数据,则不需要调用png_init_io()函数,但是必须实现下面自定义libpng一节中讨论的libpng I/O 方法。
png_init_io(png_ptr, fp);
如果您之前打开了该文件并从开始处读取任何签名,以便查看这是否是一个PNG文件,那么您需要让libpng知道从文件开始处缺少一些字节。
png_set_sig_bytes(png_ptr, number);
您可以更改在读取压缩数据时使用的zlib压缩缓冲区大小
png_set_compression_buffer_size(png_ptr, buffer_size);
其中默认大小为8192字节。请注意,缓冲区的大小会立即改变,缓冲区会立即重新分配,而不是设置一个标志以便稍后进行操作。
如果您希望以不同于默认的方式处理CRC错误,请使用
png_set_crc_action(png_ptr, crit_action, ancil_action);
png_set_crc_action()的值说明libpng如何处理辅助块和关键块中的CRC错误,以及是否使用包含的数据在其中。从libpng-1.6.26开始,它还控制了在读取IDAT块时如何处理ADLER32错误。注意,不可能“丢弃”关键块中的数据。
选择 crit_action 选项
PNG_CRC_DEFAULT 0 error/quit
PNG_CRC_ERROR_QUIT 1 error/quit
PNG_CRC_WARN_USE 3 warn/use data
PNG_CRC_QUIET_USE 4 quiet/use data
PNG_CRC_NO_CHANGE 5 use the current value
选择 ancil_action 选项
PNG_CRC_DEFAULT 0 error/quit
PNG_CRC_ERROR_QUIT 1 error/quit
PNG_CRC_WARN_DISCARD 2 warn/discard data
PNG_CRC_WARN_USE 3 warn/use data
PNG_CRC_QUIET_USE 4 quiet/use data
PNG_CRC_NO_CHANGE 5 use the current value
当crit_action的设置为PNG_CRC_QUIET_USE 时,CRC和ADLER32校验和不仅被忽略,而且不被评估。
设置回调函数
您可以设置一个回调函数来处理输入流中的任何未知块。你必须提供函数
read_chunk_callback(png_structp png_ptr,
png_unknown_chunkp chunk);
{
/* The unknown chunk structure contains your
chunk data, along with similar data for any other
unknown chunks: */
png_byte name[5];
png_byte *data;
size_t size;
/* Note that libpng has already taken care of
the CRC handling */
/* put your code here. Search for your chunk in the
unknown chunk structure, process it, and return one
of the following: */
return -n; /* chunk had an error */
return 0; /* did not recognize */
return n; /* success */
}
(你可以用你喜欢的名字,不一定是“read_chunk_callback”)
要通知libpng关于你的函数,请使用
png_set_read_user_chunk_fn(png_ptr, user_chunk_ptr,
read_chunk_callback);
这不仅命名了回调函数,还命名了一个可以检索的用户指针
png_get_user_chunk_ptr(png_ptr);
如果你调用png_set_read_user_chunk_fn()函数,那么所有回调函数没有处理的未知块将在读取时保存。你可以通过返回'1' ("handled")而不是'0'来丢弃它们。这个行为将在libpng 1.7中改变,并且由png_set_keep_unknown_chunks函数,如下所述,将在回调函数返回e时使用。如果你想要现有的行为,你应该设置全局 PNG_HANDLE_CHUNK_IF_SAFE,如果现在安全;这兼容所有当前版本的libpng和1.7。如果你保持默认值,或者PNG_HANDLE_CHUNK_NEVER,Libpng 1.6会发出警告,并且回调函数返回0。
此时,您可以设置一个回调函数,它将在读取每一行之后被调用,您可以使用它来控制进度表或类似的东西。它在pngtest.c中进行了演示。你必须提供一个函数
void read_row_callback(png_structp png_ptr,
png_uint_32 row, int pass);
{
/* put your code here */
}
使用下面的语句将你的函数引入libpng中
png_set_read_status_fn(png_ptr, read_row_callback);
当这个函数被调用时,行已经被完全处理,'row'和'pass'指向下一个要处理的行。连续逐行情况下这只是处理只是一个低于传入行数,并将永远是0。对于交错的情况也一样,除非行值0,在这种情况下,行就处理前的最后一个。因为交错可能会跳过一遍,你不能确定前面的一遍只是'pass-1';如果您确实需要知道从回调中记录的最后一次传递(row, pass)是什么,并且每次使用最后一次记录的值。
与用户转换一样,您可以使用PNG_ROW_FROM_PASS_ROW 宏定义。
看到572行,快吐了