libpng 源码的使用 第三节:读 (续4 底层读接口)

接上篇 

libpng 源码的使用 第三节:读 (续3 上层读取接口(用户主要使用的接口))

本篇是 底层读接口

如果您要走低级路线,则现在可以读取所有文件信息,直到实际的图像数据为止。 您可以通过调用png_read_info()来实现。

png_read_info(png_ptr, info_ptr);

这将处理所有块,直到但不包括图像数据。

这还将一些数据从PNG文件复制到解码结构中,以用于以后的转换。 复制的要点是:

1)来自gAMA块的PNG文件gamma。 这将覆盖先前调用png_set_gamma或png_set_alpha_mode所提供的默认值。

2)在libpng-1.5.4之前,来自bKGd块的背景色。 这会损坏早期调用png_set_background所提供的信息,从而导致意外行为。 Libpng-1.5.4不再执行此操作。

3)每个组件值中的有效位数。 Libpng使用它通过减少内部查找表的大小来优化伽玛处理。

4)来自tRNS块的透明颜色信息。 可以在以后调用png_set_tRNS进行修改。

查询信息结构

一旦读取了信息,便使用函数从info_ptr中获取信息。 请注意,在png_read_end()读取图像后的块数据之前,可能不会完全填充这些字段。


    png_get_IHDR(png_ptr, info_ptr, &width, &height,
       &bit_depth, &color_type, &interlace_type,
       &compression_type, &filter_method);

    width          - holds the width of the image
                     in pixels (up to 2^31).

    height         - holds the height of the image
                     in pixels (up to 2^31).

    bit_depth      - holds the bit depth of one of the
                     image channels.  (valid values are
                     1, 2, 4, 8, 16 and depend also on
                     the color_type.  See also
                     significant bits (sBIT) below).

    color_type     - describes which color/alpha channels
                         are present.
                     PNG_COLOR_TYPE_GRAY
                        (bit depths 1, 2, 4, 8, 16)
                     PNG_COLOR_TYPE_GRAY_ALPHA
                        (bit depths 8, 16)
                     PNG_COLOR_TYPE_PALETTE
                        (bit depths 1, 2, 4, 8)
                     PNG_COLOR_TYPE_RGB
                        (bit_depths 8, 16)
                     PNG_COLOR_TYPE_RGB_ALPHA
                        (bit_depths 8, 16)

                     PNG_COLOR_MASK_PALETTE
                     PNG_COLOR_MASK_COLOR
                     PNG_COLOR_MASK_ALPHA

    interlace_type - (PNG_INTERLACE_NONE or
                     PNG_INTERLACE_ADAM7)

    compression_type - (must be PNG_COMPRESSION_TYPE_BASE
                     for PNG 1.0)

    filter_method  - (must be PNG_FILTER_TYPE_BASE
                     for PNG 1.0, and can also be
                     PNG_INTRAPIXEL_DIFFERENCING if
                     the PNG datastream is embedded in
                     a MNG-1.0 datastream)

    Any of width, height, color_type, bit_depth,
    interlace_type, compression_type, or filter_method can
    be NULL if you are not interested in their values.

    Note that png_get_IHDR() returns 32-bit data into
    the application's width and height variables.
    This is an unsafe situation if these are not png_uint_32
    variables.  In such situations, the
    png_get_image_width() and png_get_image_height()
    functions described below are safer.

    width            = png_get_image_width(png_ptr,
                         info_ptr);

    height           = png_get_image_height(png_ptr,
                         info_ptr);

    bit_depth        = png_get_bit_depth(png_ptr,
                         info_ptr);

    color_type       = png_get_color_type(png_ptr,
                         info_ptr);

    interlace_type   = png_get_interlace_type(png_ptr,
                         info_ptr);

    compression_type = png_get_compression_type(png_ptr,
                         info_ptr);

    filter_method    = png_get_filter_type(png_ptr,
                         info_ptr);

    channels = png_get_channels(png_ptr, info_ptr);

    channels       - number of channels of info for the
                     color type (valid values are 1 (GRAY,
                     PALETTE), 2 (GRAY_ALPHA), 3 (RGB),
                     4 (RGB_ALPHA or RGB + filler byte))

    rowbytes = png_get_rowbytes(png_ptr, info_ptr);

    rowbytes       - number of bytes needed to hold a row
                     This value, the bit_depth, color_type,
                     and the number of channels can change
                     if you use transforms such as
                     png_set_expand(). See
                     png_read_update_info(), below.

    signature = png_get_signature(png_ptr, info_ptr);

    signature      - holds the signature read from the
                     file (if any).  The data is kept in
                     the same offset it would be if the
                     whole signature were read (i.e. if an
                     application had already read in 4
                     bytes of signature before starting
                     libpng, the remaining 4 bytes would
                     be in signature[4] through signature[7]
                     (see png_set_sig_bytes())).

这些也很重要,但是它们的有效性取决于是否已读取该块。 如果已读取数据,则png_get_valid(png_ptr,info_ptr,PNG_INFO_ <chunk>)和png_get_ <chunk>(png_ptr,info_ptr,...)函数返回非零值,如果丢失则返回零。 如果它们是简单数据类型,则直接设置png_get_ <chunk>的参数,或者对于任何复杂类型,返回指向info_ptr的指针。

仅返回来自gAMA,cHRM,sRGB,iCCP和sBIT块的色彩空间数据,以为应用程序提供有关图像编码方式的信息。 Libpng本身仅在将半透明像素与背景色组合时以及从libpng-1.6.0起在简化的API中的8位sRGB和16位线性像素之间进行转换时,才使用文件gamma进行转换。 如果应用程序调用png_set_rgb_to_gray(),则Libpng在将RGB转换为灰色时(从libpng-1.0.5开始)也使用文件gamma。

   png_get_PLTE(png_ptr, info_ptr, &palette,
                     &num_palette);

    palette        - the palette for the file
                     (array of png_color)

    num_palette    - number of entries in the palette

    png_get_gAMA(png_ptr, info_ptr, &file_gamma);
    png_get_gAMA_fixed(png_ptr, info_ptr, &int_file_gamma);

    file_gamma     - the gamma at which the file is
                     written (PNG_INFO_gAMA)

    int_file_gamma - 100,000 times the gamma at which the
                     file is written

    png_get_cHRM(png_ptr, info_ptr,  &white_x, &white_y, &red_x,
                     &red_y, &green_x, &green_y, &blue_x, &blue_y)
    png_get_cHRM_XYZ(png_ptr, info_ptr, &red_X, &red_Y, &red_Z,
                     &green_X, &green_Y, &green_Z, &blue_X, &blue_Y,
                     &blue_Z)
    png_get_cHRM_fixed(png_ptr, info_ptr, &int_white_x,
                     &int_white_y, &int_red_x, &int_red_y,
                     &int_green_x, &int_green_y, &int_blue_x,
                     &int_blue_y)
    png_get_cHRM_XYZ_fixed(png_ptr, info_ptr, &int_red_X, &int_red_Y,
                     &int_red_Z, &int_green_X, &int_green_Y,
                     &int_green_Z, &int_blue_X, &int_blue_Y,
                     &int_blue_Z)

    {white,red,green,blue}_{x,y}
                     A color space encoding specified using the
                     chromaticities of the end points and the
                     white point. (PNG_INFO_cHRM)

    {red,green,blue}_{X,Y,Z}
                     A color space encoding specified using the
                     encoding end points - the CIE tristimulus
                     specification of the intended color of the red,
                     green and blue channels in the PNG RGB data.
                     The white point is simply the sum of the three
                     end points. (PNG_INFO_cHRM)

    png_get_sRGB(png_ptr, info_ptr, &srgb_intent);

    srgb_intent -    the rendering intent (PNG_INFO_sRGB)
                     The presence of the sRGB chunk
                     means that the pixel data is in the
                     sRGB color space.  This chunk also
                     implies specific values of gAMA and
                     cHRM.

    png_get_iCCP(png_ptr, info_ptr, &name,
       &compression_type, &profile, &proflen);

    name             - The profile name.

    compression_type - The compression type; always
                       PNG_COMPRESSION_TYPE_BASE for PNG 1.0.
                       You may give NULL to this argument to
                       ignore it.

    profile          - International Color Consortium color
                       profile data. May contain NULs.

    proflen          - length of profile data in bytes.

    png_get_sBIT(png_ptr, info_ptr, &sig_bit);

    sig_bit        - the number of significant bits for
                     (PNG_INFO_sBIT) each of the gray,
                     red, green, and blue channels,
                     whichever are appropriate for the
                     given color type (png_color_16)

    png_get_tRNS(png_ptr, info_ptr, &trans_alpha,
                     &num_trans, &trans_color);

    trans_alpha    - array of alpha (transparency)
                     entries for palette (PNG_INFO_tRNS)

    num_trans      - number of transparent entries
                     (PNG_INFO_tRNS)

    trans_color    - graylevel or color sample values of
                     the single transparent color for
                     non-paletted images (PNG_INFO_tRNS)

    png_get_eXIf_1(png_ptr, info_ptr, &num_exif, &exif);
                     (PNG_INFO_eXIf)

    exif           - Exif profile (array of png_byte)

    png_get_hIST(png_ptr, info_ptr, &hist);
                     (PNG_INFO_hIST)

    hist           - histogram of palette (array of
                     png_uint_16)

    png_get_tIME(png_ptr, info_ptr, &mod_time);

    mod_time       - time image was last modified
                    (PNG_VALID_tIME)

    png_get_bKGD(png_ptr, info_ptr, &background);

    background     - background color (of type
                     png_color_16p) (PNG_VALID_bKGD)
                     valid 16-bit red, green and blue
                     values, regardless of color_type

    num_comments   = png_get_text(png_ptr, info_ptr,
                     &text_ptr, &num_text);

    num_comments   - number of comments

    text_ptr       - array of png_text holding image
                     comments

    text_ptr[i].compression - type of compression used
                 on "text" PNG_TEXT_COMPRESSION_NONE
                           PNG_TEXT_COMPRESSION_zTXt
                           PNG_ITXT_COMPRESSION_NONE
                           PNG_ITXT_COMPRESSION_zTXt

    text_ptr[i].key   - keyword for comment.  Must contain
                         1-79 characters.

    text_ptr[i].text  - text comments for current
                         keyword.  Can be empty.

    text_ptr[i].text_length - length of text string,
                 after decompression, 0 for iTXt

    text_ptr[i].itxt_length - length of itxt string,
                 after decompression, 0 for tEXt/zTXt

    text_ptr[i].lang  - language of comment (empty
                         string for unknown).

    text_ptr[i].lang_key  - keyword in UTF-8
                         (empty string for unknown).

/*请注意,仅当使用iTXt块支持构建库时,text_ptr结构的itxt_length,
lang和lang_key成员才存在。 
在libpng-1.4.0之前,该库默认情况下是在没有iTXt支持的情况下构建的。 
还要注意,当支持iTXt时,当“压缩”字段包含PNG_TEXT_COMPRESSION_NONE
或PNG_TEXT_COMPRESSION_zTXt时,它们将包含NULL指针。
*/

    num_text       - number of comments (same as
                     num_comments; you can put NULL here
                     to avoid the duplication)

/*请注意,虽然png_set_text()将接受文本,语言,
     和可以是NULL指针的翻译后的关键字,
     png_get_text返回的结构将始终包含
     常规的以零结尾的C字符串。 他们可能是
     空字符串,但它们永远不会是NULL指针。
*/
    num_spalettes = png_get_sPLT(png_ptr, info_ptr,
       &palette_ptr);

    num_spalettes  - number of sPLT chunks read.

    palette_ptr    - array of palette structures holding
                     contents of one or more sPLT chunks
                     read.

    png_get_oFFs(png_ptr, info_ptr, &offset_x, &offset_y,
       &unit_type);

    offset_x       - positive offset from the left edge
                     of the screen (can be negative)

    offset_y       - positive offset from the top edge
                     of the screen (can be negative)

    unit_type      - PNG_OFFSET_PIXEL, PNG_OFFSET_MICROMETER

    png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y,
       &unit_type);

    res_x          - pixels/unit physical resolution in
                     x direction

    res_y          - pixels/unit physical resolution in
                     x direction

    unit_type      - PNG_RESOLUTION_UNKNOWN,
                     PNG_RESOLUTION_METER

    png_get_sCAL(png_ptr, info_ptr, &unit, &width,
       &height)

    unit        - physical scale units (an integer)

    width       - width of a pixel in physical scale units

    height      - height of a pixel in physical scale units
                 (width and height are doubles)

    png_get_sCAL_s(png_ptr, info_ptr, &unit, &width,
       &height)

    unit        - physical scale units (an integer)

    width       - width of a pixel in physical scale units
                  (expressed as a string)

    height      - height of a pixel in physical scale units
                 (width and height are strings like "2.54")

    num_unknown_chunks = png_get_unknown_chunks(png_ptr,
       info_ptr, &unknowns)

    unknowns          - array of png_unknown_chunk
                        structures holding unknown chunks

    unknowns[i].name  - name of unknown chunk

    unknowns[i].data  - data of unknown chunk

    unknowns[i].size  - size of unknown chunk's data

    unknowns[i].location - position of chunk in file

/*
“ i”的值对应于从PNG文件读取块或使用png_set_unknown_chunks()函数插入块的顺序。
 “ location”的值是以下项的按位“或”

            PNG_HAVE_IHDR  (0x01)
            PNG_HAVE_PLTE  (0x02)
            PNG_AFTER_IDAT (0x08)
*/
/*
pHYs块中的数据可以通过几种方便的形式进行检索:
*/
   res_x = png_get_x_pixels_per_meter(png_ptr,
       info_ptr)

    res_y = png_get_y_pixels_per_meter(png_ptr,
       info_ptr)

    res_x_and_y = png_get_pixels_per_meter(png_ptr,
       info_ptr)

    res_x = png_get_x_pixels_per_inch(png_ptr,
       info_ptr)

    res_y = png_get_y_pixels_per_inch(png_ptr,
       info_ptr)

    res_x_and_y = png_get_pixels_per_inch(png_ptr,
       info_ptr)

    aspect_ratio = png_get_pixel_aspect_ratio(png_ptr,
       info_ptr)

/*
如果不存在数据或res_x为0,则每个返回0 [表示“未知”];
否则,返回0。 如果res_x!= res_y,则res_x_and_y为0
*/
/*
请注意,由于分辨率是在内部存储的,因此英寸换算将不会精确到偶数。
例如,将72 dpi存储为0.28346像素/米,检索时为71.9988 dpi,
因此,如果要显示看起来合理的结果,请确保适当舍入返回值。
*/
/*
oFFs块中的数据可以几种方便的形式检索:
*/
    x_offset = png_get_x_offset_microns(png_ptr, info_ptr);

    y_offset = png_get_y_offset_microns(png_ptr, info_ptr);

    x_offset = png_get_x_offset_inches(png_ptr, info_ptr);

    y_offset = png_get_y_offset_inches(png_ptr, info_ptr);

/*
如果不存在数据或块存在但以像素为单位,
则每个返回0 [如果x和y均为0,则表示“未知”。 
关于不精确的英寸转换的说明也适用于此,
因为以英寸为单位的值不能始终转换为微米并返回,而不会损失一些精度
*/

有关更多信息,请参见PNG规范中的块内容。信任行字节时要小心,因为某些转换可能会增加容纳一行所需的空间(扩展,填充,gray_to_rgb等)。请参见下面的png_read_update_info()。
关于text_ptr和num_text的快速介绍。 PNG存储关键字/文本对的注释,每个块一对,对文本块的数量没有限制,并且其大小限制为2 ^ 31字节。尽管有建议的关键字,但没有要求将使用限制为这些字符串。强烈建议关键字和文本对人类敏感(这才是重点),因此请不要使用缩写。禁止使用非印刷符号。有关更多详细信息,请参见PNG规范。也不需要在关键字后添加文字。
关键字应限制为79个Latin-1字符,且不包含前导或后缀空格,但关键字内允许使用非连续空格。可以多次使用相同的关键字。text_ptr是png_text结构的数组,每个结构都包含一个指向语言字符串的指针,一个指向关键字的指针和一个指向文本字符串的指针。文本字符串,语言代码和translationedkeyword可以为空或NULL指针。关键字/文本对按照接收到的顺序放入数组中。但是,部分或全部文本块可能在图像之后,因此,请确保已阅读所有文本块,不要乱用这些。看完图片后的东西。这将在下面与png_read_end()一起进行的讨论中再​​次提到。
 

输入转换

阅读标题信息后,可以设置该库以处理图像数据的任何特殊转换。将按照它们应该发生的顺序描述各种变换数据的方法。这很重要,因为其中一些会更改数据的颜色类型和/或位深度,而另一些仅会在某些颜色类型和位深度上起作用。
如果您请求的转换对特定的输入数据格式没有任何意义,则将忽略这些转换。但是,由于先前的转换,某些转换可能会产生影响。如果指定了一组相互矛盾的转换,例如添加和删除alpha通道,则无法预测最终结果。
用于透明度值的颜色应以与当前图像数据相同的格式/深度来提供。它以与图像数据相同的格式/深度存储在tRNS块中,因此libpng期望此数据。
用于背景值的颜色取决于need_expand参数,如下所述。
数据将解码到打包为字节的提供的行缓冲区中,除非已告知库将其转换为另一种格式,例如将返回4位/像素的调色板或灰度数据2像素/字节,而最左端的像素为高位除非调用png_set_packing(),否则字节的位。除非在每个RGB三元组之前或之后调用png_set_filler()或png_set_add_alpha()插入填充字节,否则8位RGB数据将以RGB RGB RGB格式存储。
将返回16位RGB数据RRGGBB RRGGBB,首先返回颜色值的最高有效字节,除非调用png_set_scale_16()将其转换为常规RGB RGB三元组,或者调用png_set_filler()或png_set_add alpha()插入两个填充字节,在每个RRGGBB三元组之前或之后。同样,可以使用png_set_filler(),png_set_add_alpha(),png_set_strip_16()或png_set_scale_16()修改8位或16位灰度数据。

以下代码将少于8到8位的灰度图像进行转换,将调色板图像更改为RGB,如果tRNS块中存在透明度信息,则添加完整的alpha通道。 这是位深度为2或4的最有用的非灰度图像,或者如果存在希望以相同方式处理所有图像的多图像查看应用程序,则这是最有用的。

    if (color_type == PNG_COLOR_TYPE_PALETTE)
        png_set_palette_to_rgb(png_ptr);

    if (png_get_valid(png_ptr, info_ptr,
        PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr);

    if (color_type == PNG_COLOR_TYPE_GRAY &&
        bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png_ptr);

前两个函数实际上是libpng版本1.0.4中添加的png_set_expand()的别名,并扩展了函数名称以提高代码的可读性。 在某些将来的版本中,它们实际上可能会做其他事情。

从libpng版本1.2.9开始,添加了png_set_expand_gray_1_2_4_to_8()。 它扩展了样本深度,而无需将tRNS更改为alpha。

从libpng版本1.5.2开始,添加了png_set_expand_16()。 它表现为aspng_set_expand(); 但是,结果通道具有16位而不是8位。当输出颜色或灰色通道线性化时,请使用此位,以避免相当严重的精度损失。

   if (bit_depth < 16)
      png_set_expand_16(png_ptr);

PNG可以具有每个通道16位的文件。 如果每个通道只能处理8位,则会将像素减少到8位。

if (bit_depth == 16)
#if PNG_LIBPNG_VER >= 10504
       png_set_scale_16(png_ptr);
#else
       png_set_strip_16(png_ptr);
#endif
(更精确的“ png_set_scale_16()” API在libpng版本1.5.4中可用)。


如果您需要与图像数据分开处理图像上的Alpha通道(例如,如果将其转换为位图蒙版),则可以使用libpng剥离通道,仅留下RGB或灰度数据:

if (color_type & PNG_COLOR_MASK_ALPHA)
       png_set_strip_alpha(png_ptr);

如果您剥离Alpha通道,则需要找到其他处理信息的方法。 相反,如果您想将图像转换为不带alpha通道的opaqueversion,请使用png_set_background;否则,请使用png_set_background。 见下文。
从libpng版本1.5.2开始,几乎所有有用的扩展都受支持,主要的遗漏是将灰度转换为索引图像(可以在应用程序中轻松完成)和将索引转换为灰度(可以通过对调色板的简单操作来完成) )
在下表中,01表示深度<8的灰度,31表示深度<8的索引,其他数字表示颜色类型,“ T”表示存在tRNS块,A表示存在alpha通道,并且表示tRNS或alpha 存在,但图像中的所有像素都是不透明的。

FROM  01  31   0  0T  0O   2  2T  2O   3  3T  3O  4A  4O  6A  6O
   TO
   01    -  [G]  -   -   -   -   -   -   -   -   -   -   -   -   -
   31   [Q]  Q  [Q] [Q] [Q]  Q   Q   Q   Q   Q   Q  [Q] [Q]  Q   Q
    0    1   G   +   .   .   G   G   G   G   G   G   B   B  GB  GB
   0T    lt  Gt  t   +   .   Gt  G   G   Gt  G   G   Bt  Bt GBt GBt
   0O    lt  Gt  t   .   +   Gt  Gt  G   Gt  Gt  G   Bt  Bt GBt GBt
    2    C   P   C   C   C   +   .   .   C   -   -  CB  CB   B   B
   2T    Ct  -   Ct  C   C   t   +   t   -   -   -  CBt CBt  Bt  Bt
   2O    Ct  -   Ct  C   C   t   t   +   -   -   -  CBt CBt  Bt  Bt
    3   [Q]  p  [Q] [Q] [Q]  Q   Q   Q   +   .   .  [Q] [Q]  Q   Q
   3T   [Qt] p  [Qt][Q] [Q]  Qt  Qt  Qt  t   +   t  [Qt][Qt] Qt  Qt
   3O   [Qt] p  [Qt][Q] [Q]  Qt  Qt  Qt  t   t   +  [Qt][Qt] Qt  Qt
   4A    lA  G   A   T   T   GA  GT  GT  GA  GT  GT  +   BA  G  GBA
   4O    lA GBA  A   T   T   GA  GT  GT  GA  GT  GT  BA  +  GBA  G
   6A    CA  PA  CA  C   C   A   T  tT   PA  P   P   C  CBA  +   BA
   6O    CA PBA  CA  C   C   A  tT   T   PA  P   P  CBA  C   BA  +

在矩阵内,
      “ +”表示“从”和“到”相同的条目。
     “-”表示不支持转换。
     “。”意味着没有必要(tRNS块可以忽略)。
     “ t”表示通过png_set_tRNS获得转换。
     “ A”表示通过png_set_add_alpha()获得转换。
     “ X”表示通过png_set_expand()获得转换。
     “ 1”表示通过以下方式获得转换
         png_set_expand_gray_1_2_4_to_8()(以及png_set_expand() 如果原件或最终件没有透明度格式)。
     “ C”表示通过png_set_gray_to_rgb()获得转换。
     “ G”表示通过png_set_rgb_to_gray()获得转换。
     “ P”表示通过以下方式获得变换
         png_set_expand_palette_to_rgb()。
     “ p”表示通过png_set_packing()获得转换。
     “ Q”表示通过png_set_quantize()获得转换。
     “ T”表示转化是通过
         png_set_tRNS_to_alpha()。
     “ B”表示通过以下方式获得变换
         png_set_background()或png_strip_alpha()。

当一个条目具有多个列出的转换时,所有这些都需要引起正确的整体转换。 当两个转换用逗号分隔时,两者都将完成工作。 当[]中包含转换时,该转换应完成该工作,但当前尚未实现-如果使用建议的转换,则将产生不同的格式。
在PNG文件中,图像中的Alpha通道是不透明度的级别。 如果您需要图像中的Alpha通道成为透明级别而不是不透明度,则可以在读取后反转Alpha通道(或tRNS块数据),以使0完全不透明,而255(在8位或调色板图像中) 或65535(以16位图像显示)是完全透明的,使用

png_set_invert_alpha(png_ptr);

PNG文件将位深度1、2和4的像素打包成尽可能小的字节,例如,对于1个位文件,每字节8个像素。 该代码在不更改像素值的情况下扩展为每字节1像素:
     if (bit_depth <8)
        png_set_packing(png_ptr);

PNG文件的位深度可能为1、2、4、8和16。PNG图像中存储的所有像素都已“缩放”或“移位”到下一个更高的可能的位深度(例如,从5位/范围内的样本 [0,31]至8位/样本,范围为[0,255])。 但是,也可以将PNG像素数据转换回图像的原始位深度。 此调用将像素减小到原始位深度:

png_color_8p sig_bit

  if (png_get_sBIT(png_ptr, info_ptr, &sig_bit))
       png_set_shift(png_ptr, sig_bit);

PNG文件以红色,绿色,蓝色的顺序存储3色像素。 此代码将像素的存储更改为蓝色,绿色,红色:

if (color_type == PNG_COLOR_TYPE_RGB ||
        color_type == PNG_COLOR_TYPE_RGB_ALPHA)
       png_set_bgr(png_ptr);

PNG文件存储RGB像素,打包成3或6个字节。 对于需要以下格式的窗口系统,此代码将minto扩展为4或8个字节:

    if (color_type == PNG_COLOR_TYPE_RGB)
       png_set_filler(png_ptr, filler, PNG_FILLER_BEFORE);

其中“ filler”是要填充的8位或16位数字,其位置是PNG_FILLER_BEFORE或PNG_FILLER_AFTER,具体取决于您是要在RGB之前还是在RGB之后。 填充8位像素时,如果提供了16位数字,则使用该数字的最低有效8位。 此转换不会影响已经具有全alpha通道的图像。 要添加不透明的Alpha通道,请使用filler = 0xffff和PNG_FILLER_AFTER,它们将生成RGBA像素。
请注意,png_set_filler()不会更改颜色类型。 如果您想这样做,则可以使用

    if (color_type == PNG_COLOR_TYPE_RGB ||
       color_type == PNG_COLOR_TYPE_GRAY)
       png_set_add_alpha(png_ptr, filler, PNG_FILLER_AFTER);

其中“ filler”包含要分配给每个像素的alpha值。libpng-1.2.7中添加了png_set_add_alpha()函数。
如果您正在读取具有Alpha通道的图像,并且需要将数据作为ARGB而不是普通的PNG格式RGBA:

    if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
       png_set_swap_alpha(png_ptr);

对于某些用途,您可能希望将灰度图像表示为RGB。 这段代码将完成该转换:

    if (color_type == PNG_COLOR_TYPE_GRAY ||
        color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
       png_set_gray_to_rgb(png_ptr);

相反,您可以将RGB或RGBA图像转换为灰度或带有alpha的灰度。

    if (color_type == PNG_COLOR_TYPE_RGB ||
        color_type == PNG_COLOR_TYPE_RGB_ALPHA)
       png_set_rgb_to_gray(png_ptr, error_action,
          double red_weight, double green_weight);

    error_action = 1: silently do the conversion

    error_action = 2: issue a warning if the original
                      image has any pixel where
                      red != green or red != blue

    error_action = 3: issue an error and abort the
                      conversion if the original
                      image has any pixel where
                      red != green or red != blue

    red_weight:       weight of red component

    green_weight:     weight of green component
                      If either weight is negative, default
                      weights are used.

在相应的定点API中,red_weight和green_weight值仅按比例缩放100,000:

   png_set_rgb_to_gray(png_ptr, error_action,
       png_fixed_point red_weight,
       png_fixed_point green_weight);

如果将error_action设置为1或2,则可以在处理完图像行之后使用png_get_rgb_to_gray_status(png_ptr)函数检查图像是否真的为灰色。如果图像为灰色,它将返回一个png_byte为零,如果存在则返回1。 任何非灰色像素。 不论error_action设置如何,都会使用绿色通道数据sBIT将背景和sBIT数据静默转换为灰度。

默认值来自PNG文件cHRM块(如果存在); 否则,默认值对应于ITU-R建议709,以及sRGB色彩空间,如Charles Poynton的颜色常见问题解答,版权所有(c)2006-11-28 Charles Poynton,在第9节中所建议的:

<http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html#RTFToC9>
    Y = 0.2126 * R + 0.7152 * G + 0.0722 * B

从1998年到2002年,本文档的先前版本建议使用略有不同的公式:

Y = 0.212671 * R + 0.715160 * G + 0.072169 * B

Libpng使用整数近似值:

Y = (6968 * R + 23434 * G + 2366 * B)/32768

如果可以确定图像伽玛值,则在线性色彩空间中进行计算。
已经描述了png_set_background()函数; 它告诉libpng对提供的背景色使用alpha或简单透明的图像进行合成。 为了与libpng-1.5.4之前的libpng版本兼容,即使您不想在bKGD块中使用该颜色(如果存在),也建议您在读取文件头之后调用该函数。
如果PNG文件包含bKGD块(PNG_INFO_bKGD有效),则可以使用此颜色,或提供其他更适合当前显示的颜色(例如,网页的背景色)。 您需要告诉libpng颜色的表示方式,包括颜色中分量值的格式(位数)和颜色的伽玛编码。 该函数带有两个参数,background_gamma_mode和need_expand来传达此信息。 但是,只有两种组合可能有用:

    png_color_16 my_background;
    png_color_16p image_background;

    if (png_get_bKGD(png_ptr, info_ptr, &image_background))
       png_set_background(png_ptr, image_background,
           PNG_BACKGROUND_GAMMA_FILE, 1/*needs to be expanded*/, 1);
    else
       png_set_background(png_ptr, &my_background,
           PNG_BACKGROUND_GAMMA_SCREEN, 0/*do not expand*/, 1);

上面描述了第二个调用-my_background的格式为libpng产生的最终显示,输出格式。因为您现在知道PNG的格式,所以可以避免选择8位或16位输出并保留调色板图像的情况(可以适当地修改调色板的颜色,并删除tRNS块。)但是,如果要这样做这一点,请务必小心,不要在不首先检查它们是否适用的情况下要求进行改造!
在第一个调用中,背景色具有PNG文件的原始位深和颜色类型。因此,对于调色板图像,颜色将作为paletteindex提供;对于低位灰度图像,颜色是image_background-> gray中的减小的位值。
如果在读取文件头之前没有调用png_set_gamma(),例如,如果您需要代码与libpng-1.5.4之前的旧版本的libpng兼容,则可以在此处调用它。
如果调用了png_set_alpha_mode(),则不要调用它;这样做会损坏png_set_alpha_mode()设置的设置。 (如果支持png_set_alpha_mode(),那么您当然可以在读取PNGheader之前先进行png_set_gamma()。)
此API无条件设置屏幕和文件的伽玛值,因此它将覆盖PNG文件中的值,除非在开始读取PNG文件之前调用了该值。因此,在以下位置调用它时,必须始终使用PNG文件值来调用它:

   if (png_get_gAMA(png_ptr, info_ptr, &file_gamma))
      png_set_gamma(png_ptr, screen_gamma, file_gamma);

   else
      png_set_gamma(png_ptr, screen_gamma, 0.45455);
/*
如果您需要将RGB文件缩小为调色板文件,或者如果调色板文件的条目超过屏幕上的大小,则
png_set_quantize()会执行此操作。 请注意,这是一个简单的匹配量化,仅能找到最接近的可用颜色。
 对于优化的调色板,这应该可以很好地工作,但是对于线性色块来说,这应该是非常不好的。
 如果传递的调色板大于maximum_colors,则文件将减少调色板中的颜色数,因此适合maximum_colors。
 如果存在直方图,则在减少调色板时,libpng将使用它来做出更明智的选择。
 如果没有直方图,则可能做得不好。
*/
   if (color_type & PNG_COLOR_MASK_COLOR)
   {
      if (png_get_valid(png_ptr, info_ptr,
          PNG_INFO_PLTE))
      {
         png_uint_16p histogram = NULL;

         png_get_hIST(png_ptr, info_ptr,
             &histogram);
         png_set_quantize(png_ptr, palette, num_palette,
            max_screen_colors, histogram, 1);
      }

      else
      {
         png_color std_color_cube[MAX_SCREEN_COLORS] =
            { ... colors ... };

         png_set_quantize(png_ptr, std_color_cube,
            MAX_SCREEN_COLORS, MAX_SCREEN_COLORS,
            NULL,0);
      }
   }

PNG文件将单色描述为黑色为零,白色为1,以下代码将对此进行反转(使黑色为1,白色为零):

   if (bit_depth == 1 && color_type == PNG_COLOR_TYPE_GRAY)
      png_set_invert_mono(png_ptr);

此函数还可用于反转灰度和灰度alpha图像:

   if (color_type == PNG_COLOR_TYPE_GRAY ||
       color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
      png_set_invert_mono(png_ptr);

PNG文件以网络字节顺序(big-endian,即,最高有效位在前)存储16位像素。 这段代码将存储方式更改为另一种方式(小尾数,即最低有效位在前,PC进行存储的方式):

    if (bit_depth == 16)
       png_set_swap(png_ptr);

如果使用的是像素压缩图像(1、2或4位/像素),并且需要更改像素压缩为字节的顺序,则可以使用:

    if (bit_depth < 8)
       png_set_packswap(png_ptr);

最后,如果现有的转换函数都不满足您的需求,则可以编写自己的转换函数。 这是通过使用以下命令设置回调来完成的

    png_set_read_user_transform_fn(png_ptr,
        read_transform_fn);

您必须提供以下函数:

    void read_transform_fn(png_structp png_ptr, png_row_infop
        row_info, png_bytep data)

有关工作示例,请参见pngtest.c。 在处理完所有其他转换之后,将调用您的函数。 如果您自己进行隔行扫描,请注意隔行扫描图像-行的宽度是'row_info'中的宽度,而不是整个图像的宽度。
如果受支持,libpng提供了两个信息例程,可用于查找处理图像的位置:

png_get_current_pass_number(png_structp png_ptr);
   png_get_current_row_number(png_structp png_ptr);

不要尝试在转换回调之外使用这些函数-首先,仅当支持用户转换时,才支持它们;其次,除非调用时实际在处理该行,否则它们很可能返回意外的结果。
对于interlacedimages,返回的值是输入子图像图像中的行。 给定隔行扫描的子图像像素(row,col,pass),请使用PNG_ROW_FROM_PASS_ROW(行,通过)和PNG_COL_FROM_PASS_COL(行,通过)来查找输出像素(x,y)。
上面的隔行处理讨论包含有关如何使用这些值的更多信息。
您还可以设置指向用户结构的指针,以供您的回调函数使用,并且可以通知libpng您的transformfunction将使用该函数更改通道数或位深度

    png_set_user_transform_info(png_ptr, user_ptr,
        user_depth, user_channels);

用户的应用程序(不是libpng)负责分配和释放用户结构所需的任何内存。
您可以通过函数png_get_user_transform_ptr()检索指针。 例如:

    voidp read_user_transform_ptr =
        png_get_user_transform_ptr(png_ptr);

最后要处理的是隔行扫描; 这将在下面详细介绍,但是如果要libpng处理隔行图像的扩展,则必须在此处调用该函数。

 number_of_passes = png_set_interlace_handling(png_ptr);
设置转换后,libpng可以更新您的png_info结构,以反映您对此调用所请求的任何转换。 

    png_read_update_info(png_ptr, info_ptr);

这对于更新信息结构的rowbytesfield最为有用,因此您可以使用它来分配图像内存。 如果上面的调用中给出了正确的screen_gamma和背景,则此功能还将使用正确的screen_gamma和背景更新您的调色板。 您只能使用特定的info_ptr调用png_read_update_info()一次。
调用png_read_update_info()之后,可以分配保存图像所需的任何内存。 对于所有形式的图像,行数据都是原始字节数据。 由于实际分配因应用程序而异,因此将不给出示例。 如果要分配一个大块,则将需要构建指向每一行的指针数组,因为下面的某些功能将需要它。
确保平台可以分配所需的缓冲区。libpng在内部检查超大宽度,但是如果使用多行缓冲区,则需要自己检查number_of_rows * width * pixel_size:

   /* 防止整数溢出 */
   if (number_of_rows > PNG_SIZE_MAX/(width*pixel_size)) {
        png_error(png_ptr,"image_data buffer would be too large");
   }

请记住:在调用png_read_update_info()之前,png_get _ *()函数将返回与原始PNG图像对应的值。在调用png_read_update_info之后,这些值是指libpng将输出的图像。因此,在调用png_read_update_info()之前,必须先调用所有png_set_functions。这对于png_set_interlace_handling()尤其重要-如果要调用png_read_update_info(),则必须先调用png_set_interlace_handling(),除非要接收隔行扫描的输出。
读取图像数据
分配内存后,您可以读取图像数据。最简单的方法是一个函数调用。如果您分配足够的内存来容纳整个图像,则可以调用png_read_image(),libpng将读取所有图像数据并将其放入提供的存储区域中。您将需要向每行传递一个指针数组。
此函数会自动处理隔行扫描,因此您无需调用png_set_interlace_handling()(除非您多次调用png_read_update_info())或多次调用此函数,或调用png_read_rows()所需的其他任何东西。

 png_read_image(png_ptr, row_pointers);

其中row_pointers是:

png_bytep row_pointers[height];

您可以指向void或char或任何用于像素的对象。
如果您不想一次读取整个图像,则可以改用png_read_rows()。 如果没有隔行扫描(checkinterlace_type == PNG_INTERLACE_NONE),这很简单:

    png_read_rows(png_ptr, row_pointers, NULL,
        number_of_rows);

其中row_pointers与png_read_image()调用中的相同。
如果您一次只执行一行,则可以使用单个row_pointer而不是row_pointers数组来执行此操作:

    png_bytep row_pointer = row;
    png_read_row(png_ptr, row_pointer, NULL);

如果文件是隔行扫描的(IHDR块中的interlace_type!= 0),事情会变得有些困难。当前PNG的唯一隔行扫描(PNG规范版本1.2)隔行扫描类型是(interlace_type == PNG_INTERLACE_ADAM7);一种有点复杂的2D隔行扫描方案,称为Adam7,它将一个图像分解为基于8x8网格的七个不同大小的较小图像。此数字(从libpng 1.5定义)为png.h中的PNG_INTERLACE_ADAM7_PASSES
libpng可以填充这些图像,也可以“照原样”提供给您。让libpng为您处理隔行扫描通常会更好。如果您要填充图像,有两种方法可以完成。 PNG规范中提到的一种方法是将每个像素扩展为覆盖尚未读取的像素(“矩形”方法)。这会导致第一遍图像出现块状,随着读取更多像素,图像会逐渐平滑。另一种方法是“闪光”方法,该方法仅在像素的最终位置绘制像素,而图像的其余部分则保留在读取开始之前初始化时所用的任何颜色。第一种方法通常看起来更好,但趋向于变慢,因为要在行中放入更多像素。
如果您可能希望libpng扩展图像,请在调用png_start_read_image()或png_read_update_info()之前调用此方法:

    if (interlace_type == PNG_INTERLACE_ADAM7)
       number_of_passes
           = png_set_interlace_handling(png_ptr);

这将返回所需的通过次数。当前为7,但是如果添加其他隔行扫描类型,则可能会更改。即使文件不是隔行扫描,也可以调用此函数,该函数将返回一遍。然后,您需要读取整个图像“ number_of_passes”次。每次将像素从当前通道分配到输出图像中的正确位置,因此您需要在每个通道中为png_read_rows提供相同的行。
如果您不想在每次通过后都显示图像,而是要等到读完整个图像后,请使用sparkleeffect。这种效果更快,并且两种方法的最终结果都完全相同。如果您打算在每次通过后显示图像,通常认为“矩形”效果更好。
如果只希望“闪闪发光”效果,只需使用第三个参数NULL正常调用png_read_row()或png_read_rows()即可。确保在图像number_of_passes次上传递,并且在两次调用之间不要更改行中的数据。您可以更改数据的位置,而不能更改数据。每一遍仅写入适合该遍的像素,并假定来自先前遍的数据仍然有效。

    png_read_rows(png_ptr, row_pointers, NULL,
        number_of_rows);

    or
    png_read_row(png_ptr, row_pointers, NULL);

如果只需要第一个效果(矩形),则除了在第三个参数中传递行缓冲区,然后将第二个参数保留为NULL之外,执行与之前相同的操作。

    png_read_rows(png_ptr, NULL, row_pointers,
        number_of_rows);

    or
    png_read_row(png_ptr, NULL, row_pointers);

如果您不希望libpng处理隔行扫描细节,只需调用png_read_rows()PNG_INTERLACE_ADAM7_PASSES次即可读取所有图像。 但是,您几乎肯定需要将像素从每个子图像分配到正确的位置。 这就是一切变得非常棘手的地方。
如果要检索单独的图像,则必须将正确的行数传递给png_read_rows()的每个后续调用。 对于小图像而言,计算变得相当复杂,其中一些子图像甚至可能不存在,因为它们的宽度或高度最终为零。libpng提供了两个宏来帮助您使用1.5及更高版本:

   png_uint_32 width = PNG_PASS_COLS(image_width, pass_number);
   png_uint_32 height = PNG_PASS_ROWS(image_height, pass_number);

这些分别告诉您与编号的通道对应的子图像的宽度和高度。 “通过”的范围是0到6-这可能会造成混淆,因为规范指的是与1到7相同的通过!请注意,在调用png_read_rows()之前必须同时检查宽度和高度,并且如果两者都不为零,则不要为该遍调用它。
当然,您可以逐行读取每个子图像。如果要生成最佳代码以对隔行扫描图像进行逐像素转换,则这是最佳方法。读取每遍图像的每一行,对其进行转换,然后将其写到新的隔行图像中。
如果您想自己去隔行扫描,libpng会提供进一步的宏来帮助您告诉像素输出图像中的放置位置。因为隔行扫描方案是矩形的-子图像像素始终排列在矩形网格上-您需要做的所有事情知道每次通过是第一个像素的输出图像中的起始列和行加上每个像素之间的间距。从libpng 1.5开始,有四个宏可以检索此信息:

   png_uint_32 x = PNG_PASS_START_COL(pass);
   png_uint_32 y = PNG_PASS_START_ROW(pass);
   png_uint_32 xStep = 1U << PNG_PASS_COL_SHIFT(pass);
   png_uint_32 yStep = 1U << PNG_PASS_ROW_SHIFT(pass);

这些使您可以编写明显的循环:

 png_uint_32 input_y = 0;
   png_uint_32 output_y = PNG_PASS_START_ROW(pass);

   while (output_y < output_image_height)
   {
      png_uint_32 input_x = 0;
      png_uint_32 output_x = PNG_PASS_START_COL(pass);

      while (output_x < output_image_width)
      {
         image[output_y][output_x] =
             subimage[pass][input_y][input_x++];

         output_x += xStep;
      }

      ++input_y;
      output_y += yStep;
   }

请注意,连续输出行和列之间的步长将作为班次返回。 这是可能的,因为子图像中的像素始终是原始图像中2、1、2、4或8个像素的2的幂。 实际上,在输入坐标的情况下,您可能需要直接计算输出坐标。 libpng为此提供了另外两个宏:

   png_uint_32 output_x = PNG_COL_FROM_PASS_COL(input_x, pass);
   png_uint_32 output_y = PNG_ROW_FROM_PASS_ROW(input_y, pass);

最后,提供了一对宏来告诉您特定的图像行或列是否出现在给定的传递中:

   int col_in_pass = PNG_COL_IN_INTERLACE_PASS(output_x, pass);
   int row_in_pass = PNG_ROW_IN_INTERLACE_PASS(output_y, pass);

请记住,除了上述内容之外,您可能还需要检查通行证的宽度和高度,以确保通行证甚至存在!
幸运的是,您现在已经确信您不想自己进行隔行处理。 实际上,通常这样做的唯一好的理由是,如果您要逐像素处理PNG文件,并且不想在隔行扫描时将整个文件加载到内存中。
libpng包含一个测试程序pngvalid,该程序说明了隔行图像的读写。 如果无法在您的代码中使用隔行扫描,并且不想将其留给libpng(推荐的方法),请参阅pngvalid.c如何做到这一点。

完成顺序读取
通过低级界面读取完图像后,就可以完成文件读取了。
如果要使用其他crc操作来处理图像数据后几英寸的CRC错误,则可以再次调用png_set_crc_action()。
如果您对注释或时间感兴趣,这些注释或时间可能存储在图像数据之前或之后,则如果要保留图像分离前后的注释,则应传递单独的png_infostruct。

    png_infop end_info = png_create_info_struct(png_ptr);

    if (!end_info)
    {
       png_destroy_read_struct(&png_ptr, &info_ptr,
           (png_infopp)NULL);
       return ERROR;
    }

   png_read_end(png_ptr, end_info);

如果您不感兴趣,仍应调用png_read_end(),但可以传递NULL,从而避免创建end_info结构的需要.libpng将在IDAT之后不处理任何块,除了跳过它们之外,也许(取决于 是否已调用png_set_crc_action)在查找IEND块时检查其CRC。

   png_read_end(png_ptr, (png_infop)NULL);

如果不调用png_read_end(),则文件指针将指向最后一个IDAT之后的第一个块,如果您希望读取超出PNG数据流末尾的内容,结果可能不是您想要的。
完成后,可以释放libpng分配的所有内存,如下所示:

   png_destroy_read_struct(&png_ptr, &info_ptr,
       &end_info);

或者,如果您未创建end_info结构,

   png_destroy_read_struct(&png_ptr, &info_ptr,
       (png_infopp)NULL);

也可以使用以下函数分别释放指向libpng分配的存储的info_ptr成员:

    png_free_data(png_ptr, info_ptr, mask, seq)

    mask - identifies data to be freed, a mask
           containing the bitwise OR of one or
           more of
             PNG_FREE_PLTE, PNG_FREE_TRNS,
             PNG_FREE_HIST, PNG_FREE_ICCP,
             PNG_FREE_PCAL, PNG_FREE_ROWS,
             PNG_FREE_SCAL, PNG_FREE_SPLT,
             PNG_FREE_TEXT, PNG_FREE_UNKN,
           or simply PNG_FREE_ALL

    seq  - sequence number of item to be freed
           (-1 for all items)

当相关存储已被释放,尚未分配,或者由用户而非libpng分配时,可以安全地调用此函数,并且在这些情况下将不执行任何操作。如果只有一项,则将忽略“ seq”参数 允许选择数据类型(例如PLTE)中的任何一个。 如果“ seq”不为-1,并且掩码中标识的数据类型允许多个项目,例如text或sPLT,则仅释放结构中的第n个项目,其中n为“ seq”。
默认行为是仅释放由libpng在内部分配的数据。 可以更改此设置,以使libpng不会释放数据,或者它将释放用户使用png_malloc()或png_calloc()分配并通过png_set _ *()函数传入的数据,

    png_data_freer(png_ptr, info_ptr, freer, mask)

    freer  - one of
               PNG_DESTROY_WILL_FREE_DATA
               PNG_SET_WILL_FREE_DATA
               PNG_USER_WILL_FREE_DATA

    mask   - which data elements are affected
             same choices as in png_free_data()

此函数仅影响已分配的数据。您可以在读取PNG数据之后但在调用任何png_set _ *()函数之前调用此函数,以控制用户或png_set _ *()函数是否负责释放任何现有的数据。可能存在,并且在png_set _ *()函数控制用户或png_destroy _ *()是否应该释放数据之后再次出现。当用户对libpng分配的数据负责时,应用程序必须使用png_free()释放它,并且当用户将负责用户分配的数据的责任转移给libpng时,用户必须使用png_malloc()或png_calloc()进行分配。
如果您在一个块中分配了row_pointers(如上面在高级读取接口的说明中所建议的那样),则您不得将释放它的责任转移给png_set_rows或png_read_destroy函数,因为它们还将尝试释放单个row_pointers [i]。
如果您分别分配了text_ptr.text,text_ptr.lang和text_ptr.translated_keyword,则不要将释放text_ptr的责任转移给libpng,因为libpng填充png_text结构时,它将这些成员与键成员结合在一起,而png_free_data()将仅释放text_ptr。钥匙。同样,如果您将释放text_ptr的责任从libpng转移到您的应用程序,则您的应用程序不得单独释放这些成员。
png_free_data()函数将关闭所有释放的“有效”标志。如果您需要关闭由您的应用程序而非libpng释放的块的标志,则可以使用

png_set_invalid(png_ptr, info_ptr, mask);

    mask - identifies the chunks to be made invalid,
           containing the bitwise OR of one or
           more of
             PNG_INFO_gAMA, PNG_INFO_sBIT,
             PNG_INFO_cHRM, PNG_INFO_PLTE,
             PNG_INFO_tRNS, PNG_INFO_bKGD,
             PNG_INFO_eXIf,
             PNG_INFO_hIST, PNG_INFO_pHYs,
             PNG_INFO_oFFs, PNG_INFO_tIME,
             PNG_INFO_pCAL, PNG_INFO_sRGB,
             PNG_INFO_iCCP, PNG_INFO_sPLT,
             PNG_INFO_sCAL, PNG_INFO_IDAT

更多有关读取PNG图像的更紧凑的示例,请参见文件example.c。
逐次读取PNG文件
渐进式阅读器与非渐进式阅读器略有不同。 而不是调用png_read_info(),png_read_rows()和png_read_end(),而是对png_process_data()进行一次调用,当它具有信息,行或图像的末尾时将调用回调。 您可以使用png_set_progressive_read_fn()设置这些回调。 您不必担心libpng的输入/输出功能,因为您直接在png_process_data()中为库提供了数据。 我假设您已经阅读了上面有关读取PNG文件的部分,所以我仅着重介绍它们之间的差异(尽管我将展示所有代码)。

png_structp png_ptr;
png_infop info_ptr;
 /*  有关如何在应用程序中初始化渐进式阅读器的示例代码片段。 */
 int
 initialize_png_reader()
 {
    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;

    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;
    }

    if (setjmp(png_jmpbuf(png_ptr)))
    {
       png_destroy_read_struct(&png_ptr, &info_ptr,
          (png_infopp)NULL);
       return ERROR;
    }

    /* 这是新的。 您可以提供在标题信息有效时,每行完成时以及图像完成时要调用的函数。
 如果不使用所有功能,则可以指定NULL参数。 
即使所有三个函数均为NULL,也需要调用png_set_progressive_read_fn()。
 您可以将任何结构用作user_ptr(广播到函数调用的void指针),并使用该函数从回调内部检索指针

          png_get_progressive_ptr(png_ptr);

       这将返回一个void指针,您必须对其进行适当的转换。
     */
    png_set_progressive_read_fn(png_ptr, (void *)user_ptr,
        info_callback, row_callback, end_callback);

    return 0;
 }

 /* 收到数据块时调用的代码片段 */
 int
 process_data(png_bytep buffer, png_uint_32 length)
 {
    if (setjmp(png_jmpbuf(png_ptr)))
    {
       png_destroy_read_struct(&png_ptr, &info_ptr,
           (png_infopp)NULL);
       return ERROR;
    }

    /* 这也是新的。 只需从文件流中为它提供大量数据(当然是按顺序)。
 在具有分段内存模型的计算机上,不要给它超过64K的内存。 
该库似乎运行良好,大小为4K。 
尽管可以在必要时提供更少的空间(我假设可以提供1字节的块,但我还没有尝试过少于256字节)。 
当此函数返回时,您可能想要显示在行回调中生成的所有行(如果尚未在此显示)。
     */
    png_process_data(png_ptr, info_ptr, buffer, length);

    /* 此时,如果您想处理数据,则可以调用png_process_data_skip;
库将跳过您自己的数据。 
它只是返回要跳过的字节数(并在下一个png_process_data调用中停止libpng跳过该字节数)。
*/
    return 0;
 }

 /* 当提供了足够的数据以便读取所有标头时,
    将调用此函数(由上面的png_set_progressive_read_fn()设置)。
 */
 void
 info_callback(png_structp png_ptr, png_infop info)
 {
    /* 在此处进行任何设置,包括设置“读取PNG文件”部分中提到的任何转换。
     目前,您必须在设置所有转换后(即使您未设置任何设置)
    调用png_start_read_image()或png_read_update_info()。 
    您可能会在png_process_data()返回之前开始获取行,
    因此这是您为此做的最后一次机会。

       如果您不想自己做隔行扫描,则可以在此处打开隔行扫描处理。

       如果需要,您可以在此时通过调用png_process_data_pause停止处理原始输入数据。 
    这将返回上一个png_process_data调用中未处理的字节数-您可以确保下一个调用再次看到这些字节。 
    如果您不想这样做,可以通过设置'save'参数(请参阅png.h)来使libpng缓存未读的字节,
    但是libpng必须在内部复制数据。
     */
 }

 /* 每行图像数据完整时调用此函数 */
 void
 row_callback(png_structp png_ptr, png_bytep new_row,
    png_uint_32 row_num, int pass)
 {
    /* 如果图像是隔行扫描的,并且您打开了隔行扫描处理程序,
则每次关隘中的每一行都会调用此函数。 这些行中的某些行将不会与上一遍更改相同。
 当不更改行时,new_row变量将为NULL。 
行和关隘是按顺序调用的,因此您实际上并不需要row_num和关隘,
但我提供它们是因为它可以使您的生活更轻松。

       如果未打开隔行扫描处理,则当隔行扫描图像时,
将为每个子图像的每一行调用回调。 在这种情况下,
“ row_num”是子图像中的行,而不是在所有其他情况下的输出图像中的行。

       对于已打开libpng隔行扫描处理的隔行扫描图像的非NULL行,
必须调用png_progressive_combine_row()传递行和旧行。 
如果可以简化代码,则可以针对NULL行(它将仅返回)和非隔行图像(仅对您执行memcpy)调用此函数。
 因此,如果您打开隔行扫描处理,则可以在所有情况下都可以执行此操作。
     */

        png_progressive_combine_row(png_ptr, old_row,
          new_row);

    /* 其中old_row是先前为该行显示的内容。 
        请注意,第一遍(pass == 0,实际上)将完全覆盖旧行,因此不必初始化行。
        第一次通过之后(仅适用于隔行扫描的图像),您将必须通过当前行,
        并且该函数将合并旧行和新行。 
        您也可以在此回调中调用png_process_data_pause-参见上文。
    */
 }

 void
 end_callback(png_structp png_ptr, png_infop info)
 {
    /* 在读取了整个图像之后,包括在图像之后的所有块(直到并包括IEND),都将调用此函数。
     尽管有些数据可能已添加到注释和时间字段中,但通常将具有与标题中相同的信息块。 
    大多数人在这里不会做太多事情,也许会设置一个标记来将图像标记为完成。
     */
 }

第三节读取机器翻译完,下一篇翻译重头戏 写入

猜你喜欢

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