libpng 源码使用和手册机器翻译 第三节:读

上一篇文章简单介绍了libpng ,翻译了手册前两节

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行,快吐了

猜你喜欢

转载自blog.csdn.net/catshit322/article/details/114678598