你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

各位大佬指点下怎么使用Libpng库解码PNG图片

[复制链接]
搬砖工 提问时间:2018-8-16 13:35 /
如题,各位大佬指点下。我没解码过PNG图片,有其他好的解码方式也行。能给个借鉴代码的最佳,谢谢各位大佬了
收藏 评论8 发布时间:2018-8-16 13:35

举报

8个回答
琦子 回答时间:2018-8-16 13:59:34
可以参考这个帖子 说的很详细 http://blog.csdn.net/go_to_learn/article/details/8070686
其中一部分是这样说的:

libpng的数据结构

    png_structp变量是在libpng初始化的时候创建,由libpng库内部使用,代表libpng的是调用上下文,库的使用者不应该对这个变量进行访问。调用libpng的API的时候,需要把这个参数作为第一个参数传入。

    png_infop变量,初始化完成libpng之后,可以从libpng中获得该类型变量指针。这个变量保存了png图片数据的信息,库的使用者可以修 改和查阅该变量,比如:查阅图片信息,修改图片解码参数。在早期的版本中直接访问该变量的成员,最新的版本建议是通过API来访问这些成员。

libpng的使用

0、判断是否为libpng数据

    这步是可选的,在利用libpng继续数据处理之前,可以调用png_sig_cmp函数来检查是否为png数据,请参阅libpng手册了解详细内容。

1、初始化libpng

    /* Create and initialize the png_struct with the desired error handler
     * functions.  If you want to use the default stderr and longjump method,
     * you can supply NULL for the last three parameters.  We also supply the
     * the compiler header file version, so that we know if the application
     * was compiled with a compatible version of the library.  REQUIRED
     */
    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
    png_voidp user_error_ptr, user_error_fn, user_warning_fn);
  初始化libpng的时候,用户可以指定自定义错误处理函数,如果不需要指定自定义错误处理函数,则传NULL即可。 png_create_read_struct函数返回一个png_structp变量,前面已经提到该变量不应该被用户访问,应该在以后调用 libpng的函数时传递给libpng库。

    如果你需要提供自定义内存管理模块则需要调用png_create_read_struct_2来完成对libpng的初始化:

    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)
2、创建图像信息——png_infop变量

    /* Allocate/initialize the memory for image information.  REQUIRED. */
    info_ptr = png_create_info_struct(png_ptr);
    if (info_ptr == NULL)
    {
       png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
       return (ERROR);
    }
如前面所说,用户将会通过png_infop变量来获得图片的信息,设置图片解码参数等。

3、设置错误返回点

    上文libjpeg解码jpeg图片中提到用setjmp/longjmp函数来处理异常。libpng库默认集成这种机制来完成异常处理,如下代码初始化错误返回点:

    /* Set error handling if you are using the setjmp/longjmp method (this is
     * the normal method of doing things with libpng).  REQUIRED unless you
     * set up your own error handlers in the png_create_read_struct() earlier.
     */
    if (setjmp(png_jmpbuf(png_ptr)))
    {
       /* Free all of the memory associated with the png_ptr and info_ptr */
       png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
       /* If we get here, we had a problem reading the file */
      return (ERROR);
   }

    正如上面注释中提到情况,只有在初始化libpng的时候未指定用户自定义的错误处理函数情况下,才需要设置错误返回点。如果设置了用户自定义的错误处理函数,libpng将会调用用户自定义错误处理函数,而不会返回到这个调用点。

    当libpng库出现错误的时候,libpng将会自动调用longjmp函数返回到这个点。在这个点我们可以进行必要的清理工作。

4、设置libpng的数据源

    我在上文《图像解码之一——使用libjpeg解码jpeg图片》中提到,一个好的代码库应该能够运行用户输入各式各样的数据,而不能把输入数据定死。libpng在这方面做得非常的好,它提供了默认的文件输入流的支持,并且提供了用户自定义回调函数来完成png数据的输入。

    对于文件流数据数据设置代码如下:

    /* One of the following I/O initialization methods is REQUIRED */
    def streams /* PNG file I/O method 1 */
    /* Set up the input control if you are using standard C streams */
    png_init_io(png_ptr, fp);
用户自定义回调函数设置libpng数据源的代码如下:

    /* If you are using replacement read functions, instead of calling
     * png_init_io() here you would call:
     */
    png_set_read_fn(png_ptr, (void *)user_io_ptr, user_read_fn);
    /* where user_io_ptr is a structure you want available to the callbacks */

如果你已经使用png_sig_cmp函数来检查了png数据,需要调用png_set_sig_bytes函数来告诉libpng库,这样库处理数据的时候将会跳过相应的数据,具体请参考libpng手册。

5、png图像处理

    这步有两种设置方案一种称为高层处理,一种称为底层处理。

高层处理

    当用户的内存足够大,可以一次性读入所有的png数据,并且输出数据格式为如下libpng预定义数据类型时,可以用高层函数,下libpng预定义数据类型为:

PNG_TRANSFORM_IDENTITY          No transformation

PNG_TRANSFORM_STRIP_16          Strip 16-bit samples to

                                              8 bits

PNG_TRANSFORM_STRIP_ALPHA     Discard the alpha channel

PNG_TRANSFORM_PACKING           Expand 1, 2 and 4-bit

                                              samples to bytes

PNG_TRANSFORM_PACKSWAP        Change order of packed

                                              pixels to LSB first

PNG_TRANSFORM_EXPAND            Perform set_expand()

PNG_TRANSFORM_INVERT_MONO   Invert monochrome images

PNG_TRANSFORM_SHIFT              Normalize pixels to the

                                              sBIT depth

PNG_TRANSFORM_BGR                 Flip RGB to BGR, RGBA

                                              to BGRA

PNG_TRANSFORM_SWAP_ALPHA     Flip RGBA to ARGB or GA

                                              to AG

PNG_TRANSFORM_INVERT_ALPHA  Change alpha from opacity

                                              to transparency

PNG_TRANSFORM_SWAP_ENDIAN   Byte-swap 16-bit samples

PNG_TRANSFORM_GRAY_TO_RGB   Expand grayscale samples

                                              to RGB (or GA to RGBA)

    高层读取函数如下:

    /*
     * If you have enough memory to read in the entire image at once,
     * and you need to specify only transforms that can be controlled
     * with one of the PNG_TRANSFORM_* bits (this presently excludes
     * dithering, filling, setting background, and doing gamma
     * adjustment), then you can read the entire image (including
     * pixels) into the info structure with this call:
     */
    png_read_png(png_ptr, info_ptr, png_transforms, png_voidp_NULL);
    该函数将会把所有的图片数据解码到info_ptr数据结构中。png_transforms为整型参数,为上面libpng预定义的数据类型进行or操作得到。调用了该函数,就不可以再调用png_set_transform函数来设置输出数据。

    该函数相当于调用底层函数(下文将会介绍)如下调用顺序:

a)调用png_read_info函数获得图片信息。

b)根据png_transforms所指示的,调用png_set_transform设置输出格式转换的函数。

c)调用png_read_image来解码整个图片的数据到内存。

d)调用png_read_end结束图片解码。

    当你调用png_read_png之后,则可以调用如下函数得到png数据:

row_pointers = png_get_rows(png_ptr, info_ptr);

底层处理

a)读取输入png数据的图片信息:

    /* The call to png_read_info() gives us all of the information from the
     * PNG file before the first IDAT (image data chunk).  REQUIRED
     */
    png_read_info(png_ptr, info_ptr);

该函数将会把输入png数据的信息读入到info_ptr数据结构中。

b)查询图像信息

    前面提到png_read_info将会把输入png数据的信息读入到info_ptr数据结构中,接下来需要调用API查询该信息。

    png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
        &interlace_type, int_p_NULL, int_p_NULL);
c)设置png输出参数(转换参数)

    这步非常重要,用户可以指定输出数据的格式,比如RGB888,ARGB8888等等输出数据格式。通过png_set_xxxxx函数来实现,例如如下代码:

    // expand images of all color-type and bit-depth to 3x8 bit RGB images
    // let the library process things like alpha, transparency, background
    if (bit_depth == 16)
        png_set_strip_16(png_ptr);
    if (color_type == PNG_COLOR_TYPE_PALETTE)
        png_set_expand(png_ptr);
    if (bit_depth<8)
        png_set_expand(png_ptr);
    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
       png_set_expand(png_ptr);
   if (color_type == PNG_COLOR_TYPE_GRAY ||
       color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
       png_set_gray_to_rgb(png_ptr);

如上代码将会把图像转换成RGB888的数据格式。这种转换函数还很多,请参阅libpng手册了解他们的作用。


    虽然有很多设置输出参数的函数可以调用,但是用户的需求是无限的,很多输出格式libpng并不是原生支持的,比如YUV565,RGB565,YUYV 等等。幸好libpng提供了自定义转换函数的功能,可以让用户注册转换回调函数给libpng库,在libpng对输出数据进行转换的时候,先对 png_set_xxxxx函数设置的参数进行转换,最后将会调用用户自定义的转换函数进行转换。

   png_set_read_user_transform_fn(png_ptr,
       read_transform_fn);

  read_transform_fn为用户自定义的数据转换函数。具体实现可以参考pngtest.c中的实现。

    另外你可以通过png_set_user_transform_info告诉libpng你的转换函数的用户自定义数据结构和输出数据的详细信息,比如颜 色深度,颜色通道(channel)等等。你可能会问为什么要告诉libpng呢?libpng将会根据这些信息来更新png图片详细信息,后面会介绍。 定义如下:

png_set_user_transform_info(png_ptr, user_ptr,
        user_depth, user_channels);
   usr_ptr是用户自定义的数据结构,在用户自定义转换函数read_transform_fn中可以通过png_get_user_transform_ptr函数得到该数据结构,例如:

  voidp read_user_transform_ptr =
       png_get_user_transform_ptr(png_ptr);
d)更新png数据的详细信息

    经过前面的设置png数据的图片信息肯定会有一些变化,则需要调用png_read_update_info函数更新图片的详细信息:

png_read_update_info(png_ptr, info_ptr);

  该函数将会更新保存于info_ptr变量中的图片数据信息,然后可以再调用png_get_IHDR重新查询图片信息。

e)读取png数据

    可以到用png_read_image函数,一次性把所有的数据读入内存,例如:

png_read_image(png_ptr, row_pointers);
也可以调用png_read_rows一次读入1行或多行到内存中,比如:

    for (y = 0; y < height; y++)
    {
       png_read_rows(png_ptr, &row_pointers[y], png_bytepp_NULL, 1);
    }
f)结束读取数据

通过png_read_end结束读取png数据,代码如下:

/* Read rest of file, and get additional chunks in info_ptr - REQUIRED */
    png_read_end(png_ptr, info_ptr);

6、释放libpng的内存

    调用png_destroy_read_struct来释放libpng的内存,代码如下:

/* Clean up after the read, and free any memory allocated - REQUIRED */
    png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);

评分

参与人数 2ST金币 +20 蝴蝶豆 +4 收起 理由
zero99 + 4
细品黑夜 + 20

查看全部评分

搬砖工 回答时间:2018-8-16 14:04:57
bargagebaobei 发表于 2018-8-16 13:59
可以参考这个帖子 说的很详细 http://blog.csdn.net/go_to_learn/article/details/8070686
其中一部分是这 ...

好的,谢谢大佬指点。有基于STM32的demo什么的么。小弟想借鉴借鉴,没做过,想借鉴借鉴
STM1024 回答时间:2018-8-16 15:58:30
你可以研究一下png 的文件格式,这个:http://blog.csdn.net/bisword/article/details/2777121
然后你自己也可以实现,当然用库更方便,但是需要学习,而且很多地方需要仔细看

评分

参与人数 2ST金币 +20 蝴蝶豆 +3 收起 理由
zero99 + 3
细品黑夜 + 20

查看全部评分

搬砖工 回答时间:2018-8-16 16:09:37
stm1024 发表于 2018-8-16 15:58
你可以研究一下png 的文件格式,这个:http://blog.csdn.net/bisword/article/details/2777121
然后你自己 ...

我没做过相关的处理,大佬能介绍个demo学习学习么
feixiang20 回答时间:2018-8-16 23:50:51

评分

参与人数 2ST金币 +20 蝴蝶豆 +2 收起 理由
细品黑夜 + 20
zero99 + 2

查看全部评分

butterflyspring 回答时间:2018-8-17 12:11:40
http://www.libpng.org/
你上这里看看,有惊喜

评分

参与人数 2ST金币 +20 蝴蝶豆 +2 收起 理由
细品黑夜 + 20
zero99 + 2

查看全部评分

搬砖工 回答时间:2018-8-17 13:18:17
stm1024 发表于 2018-8-16 15:58
你可以研究一下png 的文件格式,这个:http://blog.csdn.net/bisword/article/details/2777121
然后你自己 ...

谢谢指导
搬砖工 回答时间:2018-8-17 13:18:38
butterflyspring 发表于 2018-8-17 12:11
http://www.libpng.org/
你上这里看看,有惊喜

  谢谢指导

所属标签

相似问题

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版