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

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

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

举报

8个回答
bargagebaobei 回答时间: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管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版