由于个人想学习DirectFB的使用。而 DirectFB依赖于libpng和libjpeg库,所以需要简单了解下libpng库的使用方法。本文转自:http://www.cnblogs.com/xiaoxiaoboke/archive/2012/02/13/2349765.html。此篇文章讲解的非常不错。我个人会有简单的调整。新增了一个小例子对使用来个简单的小结。

libpng的数据结构

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

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

libpng的使用

0、判断是否为libpng数据

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

1、初始化libpng

   1: /* Create and initialize the png_struct with the desired error handler
   2:  * functions.  If you want to use the default stderr and longjump method,
   3:  * you can supply NULL for the last three parameters.  We also supply the
   4:  * the compiler header file version, so that we know if the application
   5:  * was compiled with a compatible version of the library.  REQUIRED
   6:  */
   7: png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
   8:    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的初始化:

   1: png_structp png_ptr = png_create_read_struct_2
   2:    (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr,
   3:     user_error_fn, user_warning_fn, (png_voidp)
   4:     user_mem_ptr, user_malloc_fn, user_free_fn)

2、创建图像信息——png_infop变量

   1: /* Allocate/initialize the memory for image information.  REQUIRED. */
   2: info_ptr = png_create_info_struct(png_ptr);
   3: if (info_ptr == NULL)
   4: {
   5:    png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
   6:    return (ERROR);
   7: }

如前面所说,用户将会通过png_infop变量来获得图片的信息,设置图片解码参数等。

3、设置错误返回点

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

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

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

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

4、设置libpng的数据源

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

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

   1: /* One of the following I/O initialization methods is REQUIRED */
   2: def streams /* PNG file I/O method 1 */
   3: /* Set up the input control if you are using standard C streams */
   4: png_init_io(png_ptr, fp);

用户自定义回调函数设置libpng数据源的代码如下:

   1: /* If you are using replacement read functions, instead of calling
   2:  * png_init_io() here you would call:
   3:  */
   4: png_set_read_fn(png_ptr, (void *)user_io_ptr, user_read_fn);
   5: /* 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)

高层读取函数如下:

   1: /*
   2:  * If you have enough memory to read in the entire image at once,
   3:  * and you need to specify only transforms that can be controlled
   4:  * with one of the PNG_TRANSFORM_* bits (this presently excludes
   5:  * dithering, filling, setting background, and doing gamma
   6:  * adjustment), then you can read the entire image (including
   7:  * pixels) into the info structure with this call:
   8:  */
   9: 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数据:

   1: row_pointers = png_get_rows(png_ptr, info_ptr);

底层处理

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

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

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

b)查询图像信息

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

   1: png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
   2:     &interlace_type, int_p_NULL, int_p_NULL);

c)设置png输出参数(转换参数)

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

   1: // expand images of all color-type and bit-depth to 3x8 bit RGB images
   2: // let the library process things like alpha, transparency, background
   3: if (bit_depth == 16)
   4:     png_set_strip_16(png_ptr);
   5: if (color_type == PNG_COLOR_TYPE_PALETTE)
   6:     png_set_expand(png_ptr);
   7: if (bit_depth<8)
   8:     png_set_expand(png_ptr);
   9: if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
  10:     png_set_expand(png_ptr);
  11: if (color_type == PNG_COLOR_TYPE_GRAY ||
  12:     color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
  13:     png_set_gray_to_rgb(png_ptr);

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

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

   1: png_set_read_user_transform_fn(png_ptr,
   2:     read_transform_fn);

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

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

   1: png_set_user_transform_info(png_ptr, user_ptr,
   2:     user_depth, user_channels);

usr_ptr是用户自定义的数据结构,在用户自定义转换函数read_transform_fn中可以通过png_get_user_transform_ptr函数得到该数据结构,例如:

   1: voidp read_user_transform_ptr =
   2:     png_get_user_transform_ptr(png_ptr);

d)更新png数据的详细信息

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

   1: png_read_update_info(png_ptr, info_ptr);

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

e)读取png数据

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

   1: png_read_image(png_ptr, row_pointers);

也可以调用png_read_rows一次读入1行或多行到内存中,比如:

   1: for (y = 0; y < height; y++)
   2: {
   3:    png_read_rows(png_ptr, &row_pointers[y], png_bytepp_NULL, 1);
   4: }

f)结束读取数据

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

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

6、释放libpng的内存

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

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

至此png数据解码,全部完成了。

7、例子

void readpng_mytest(char* name)
{
 int i, j;
 int m_width, m_height;
 png_infop info_ptr;             //图片信息的结构体
 png_structp png_ptr;         //初始化结构体,初始生成,调用api时注意传入

FILE* file = fopen(name, "rb");    //打开的文件名
 printf("%s, %d\n", __FUNCTION__, __LINE__);

png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);   //创建初始化libpng库结构体
  info_ptr = png_create_info_struct(png_ptr);                                                 //创建图片信息结构体

setjmp(png_jmpbuf(png_ptr));                              //设置错误的返回点

// 这句很重要
 png_init_io(png_ptr, file);         //把文件加载到libpng库结构体中

// 读文件了
 png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);        //读文件内容到info_ptr中

// 得到文件的宽高色深
 if ((png_ptr != NULL) && (info_ptr != NULL))
 {
  m_width = png_get_image_width(png_ptr, info_ptr);            
  m_height = png_get_image_height(png_ptr, info_ptr);                          //通过png库中的api获取图片的宽度和高度

printf("%s, %d, m_width =%d, m_height = %d\n", __FUNCTION__, __LINE__, m_width, m_height);
 }
 int color_type = png_get_color_type(png_ptr, info_ptr);                          //通过api获取color_type

printf("%s, %d, color_type = %d\n", __FUNCTION__, __LINE__, color_type);

int size = m_height * m_width * 4;

unsigned char *bgra = NULL;
 
 bgra = malloc(size);
 if (NULL == bgra)
 {
  printf("%s, %d, bgra == NULL\n", __FUNCTION__, __LINE__);
  return;
 }
 int pos = 0;

// row_pointers里边就是传说中的rgb数据了

png_bytep* row_pointers = png_get_rows(png_ptr, info_ptr);

// 拷贝!!注意,如果你读取的png没有A通道,就要3位3位的读。还有就是注意字节对其的问题,最简单的就是别用不能被4整除的宽度就行了。读过你实在想用,就要在这里加上相关的对齐处理。

for(i = 0; i < m_height; i++)
 {
    for(j = 0; j < (4 * m_width); j += 4)
    {
     bgra[pos++] = row_pointers[i][j + 2]; // blue
     bgra[pos++] = row_pointers[i][j + 1]; // green
     bgra[pos++] = row_pointers[i][j];   // red
     bgra[pos++] = row_pointers[i][j + 3]; // alpha
    }
 }

// 好了,你可以用这个数据作任何的事情了。。。把它显示出来或者打印出来都行。
/* for (i = 0; i < size; i++ )
 {
  printf("%s, %d, bgra[%d] = %d\n", __FUNCTION__, __LINE__,  i, bgra[i]);
 }*/
 
 png_destroy_read_struct(&png_ptr, &info_ptr, 0);

fclose(file);

return;
}

int main(int argc, const char **argv)

{

/*读png文件内容*/

readpng_mytest("png图片的路径");

/*下面实现复制一个文件的功能,png库中的example*/
   if (argc == 3)
   {
      png_image image; /* The control structure used by libpng */

/* Initialize the 'png_image' structure. */
      memset(&image, 0, (sizeof image));
      image.version = PNG_IMAGE_VERSION;

/* The first argument is the file to read: */
      if (png_image_begin_read_from_file(&image, argv[1]))       //argv[1] 初始文件的路径 读文件数据岛image中
      {
         png_bytep buffer;

/* Set the format in which to read the PNG file; this code chooses a
          * simple sRGB format with a non-associated alpha channel, adequate to
          * store most images.
          */
         image.format = PNG_FORMAT_RGBA;

/* Now allocate enough memory to hold the image in this format; the
          * PNG_IMAGE_SIZE macro uses the information about the image (width,
          * height and format) stored in 'image'.
          */
         buffer = malloc(PNG_IMAGE_SIZE(image));

/* If enough memory was available read the image in the desired format
          * then write the result out to the new file.  'background' is not
          * necessary when reading the image because the alpha channel is
          * preserved; if it were to be removed, for example if we requested
          * PNG_FORMAT_RGB, then either a solid background color would have to
          * be supplied or the output buffer would have to be initialized to the
          * actual background of the image.
          *
          * The fourth argument to png_image_finish_read is the 'row_stride' -
          * this is the number of components allocated for the image in each
          * row.  It has to be at least as big as the value returned by
          * PNG_IMAGE_ROW_STRIDE, but if you just allocate space for the
          * default, minimum, size using PNG_IMAGE_SIZE as above you can pass
          * zero.
          *
          * The final argument is a pointer to a buffer for the colormap;
          * colormaps have exactly the same format as a row of image pixels (so
          * you choose what format to make the colormap by setting
          * image.format).  A colormap is only returned if
          * PNG_FORMAT_FLAG_COLORMAP is also set in image.format, so in this
          * case NULL is passed as the final argument.  If you do want to force
          * all images into an index/color-mapped format then you can use:
          *
          *    PNG_IMAGE_COLORMAP_SIZE(image)
          *
          * to find the maximum size of the colormap in bytes.
          */
         if (buffer != NULL &&
            png_image_finish_read(&image, NULL/*background*/, buffer,
               0/*row_stride*/, NULL/*colormap*/))
         {
            /* Now write the image out to the second argument.  In the write
             * call 'convert_to_8bit' allows 16-bit data to be squashed down to
             * 8 bits; this isn't necessary here because the original read was
             * to the 8-bit format.
             */
            if (png_image_write_to_file(&image, argv[2], 0/*convert_to_8bit*/,        
               buffer, 0/*row_stride*/, NULL/*colormap*/))                //写刚才读取到的文件到新的png文件中,argv[2]存放的是新生成的png文件路径
            {
               /* The image has been written successfully. */
               exit(0);
            }
         }

else
         {
            /* Calling png_free_image is optional unless the simplified API was
             * not run to completion.  In this case if there wasn't enough
             * memory for 'buffer' we didn't complete the read, so we must free
             * the image:
             */
            if (buffer != NULL)
             //  png_free_image(&image);

//else
               free(buffer);
      }

/* Something went wrong reading or writing the image.  libpng stores a
       * textual message in the 'png_image' structure:
       */
      
      fprintf(stderr, "pngtopng: error: %s\n", image.message);
      exit (1);
   }

fprintf(stderr, "pngtopng: usage: pngtopng input-file output-file\n");
    exit(1);
 }

}

总结:

通过上面的介绍,我们可以发现相对于libpng的扩展性非常好。我们基本上没有任何修改libpng库的需求,它的对外接口提供了足够的灵活性,允许我们扩展。从这个角度来讲,libpng库非常值得我们学习它的对外接口的定义。

libpng库的使用讲解相关推荐

  1. centos php yum gd库,Centos7下PHP安装gd库的实例讲解

    Centos7下安装php, 或php的gd库支持,都是很简单的. 一切通过yum搞定. yum install php-gd 以上命令就搞定php-gd的支持,快快看看phpinfo是否支持了? 可 ...

  2. 计算机基础access数据库操作题,2021年3月全国计算机等级考试二级Access数据库程序设计题库及答案讲解...

    原标题:2021年3月全国计算机等级考试二级Access数据库程序设计题库及答案讲解 资料来源:学盛通学习网547所大学考研专业课(历年真题及模拟题可在线作答,系统自动评分,出答案及解析) 本题库是详 ...

  3. C语言答案解析,C语言题库带详细讲解答案解析.doc

    C语言题库带详细讲解答案解析.doc 下载提示(请认真阅读)1.请仔细阅读文档,确保文档完整性,对于不预览.不比对内容而直接下载带来的问题本站不予受理. 2.下载的文档,不会出现我们的网址水印. 3. ...

  4. libjpeg库和libpng库的移植和使用

    目录 1.libjpeg库移植 2.zlib库移植 3.libpng库移植 4.开源库的使用方法 1.libjpeg库移植 [1]下载源码与准备交叉工具链 libjpegv6下载地址 [2]解压源码并 ...

  5. linux上安装libpng库以及zlib库

    hello ,大家好,我是jordy,一只空着的杯子:欢迎各位朋友光临我的博客,多多沟通 ,我的QQ :   1760282809 363232564 http://blog.csdn.net/baw ...

  6. OpenCV3.X 编译 ——libpng库问题相关解决方法

    OpenCV3.X 编译 --libpng库相关问题解决方法 简介 在ubuntu16.04 编译的, Opencv版本 OpenCV3.0 OpenCV3.2 原本电脑自带 libpng1.2.0, ...

  7. HCIP H12-221 题库 71-120题 讲解

    HCIP H12-221 题库 71-120题 讲解 71.Agreate命令(aggregateipy4-address{mask|mask-length}[as-set|attribute-pol ...

  8. libpng库编码图片为png(RGB压缩为png图片:与ffmpeg视频解码存储为png图片)

    //*====== 参考:1.http://blog.csdn.net/solstice/article/details/2062 2.libpng库的example.c文件 ======*// 一. ...

  9. 使用libpng库读PNG图片

    使用libpng库读PNG图片 void read_png_image(string path, unsigned char **data, int* s)//"icon1.png" ...

最新文章

  1. 导入Java文件还是class文件_java程序运行的时候,是把所有的class文件都加载到内存吗?还是用的什么加载什么?...
  2. Day 08 周六下午的活动
  3. Fiddler之弱网测试(Web)
  4. C#设计模式之1-工厂方法模式
  5. OpenStack 已死?
  6. 如何向 Linux Kernel 提交 Patch
  7. COLING 2020 | 一种从科学文献中提取关键词的基于自蒸馏的联合学习方法
  8. 创建Maven web工程不能解析EL表达式的解决办法
  9. Java基础梳理(一)
  10. treetable php,第105款插件:jquery.treetable.js的使用
  11. java 导出word 带图片
  12. 易宝支付 CTO 陈斌:如何做一个好的 CTO
  13. ftp工具FileZilla下载安装配置
  14. 视频画中画效果该怎么实现?这款软件让你一秒成大神
  15. 典型相关分析 python_CCA典型关联分析原理与Python案例
  16. 2019年的最后一天,你会想些啥?
  17. 遍历日历 LocalDate
  18. Qt信号和槽函数连接不成功原因
  19. 文件宝局域网传输/播放功能Windows10系统经验贴(感谢文件宝用户@卡卡罗特 和@24K 純情)...
  20. k型热电偶分度表_热电偶补偿导线

热门文章

  1. Android笔记:Dialog显示图片
  2. 知识共享许可 cc 协议
  3. python的答辩问题及答案_计算机毕设答辩时都会问到哪些问题?
  4. vue 点击打开新窗口
  5. java开发的日常工作内容,都是精髓!
  6. 【机器学习|数学基础】Mathematics for Machine Learning系列之线性代数(20):用配方法化二次型为标准形
  7. 在你最穷的时候,是怎么翻身的?
  8. Python对csv、ini、xml、excel等格式文件操作用例
  9. python生成器与迭代器_python 生成器与迭代器(yield 用法)
  10. 生鲜行业B2B电商平台解决方案,提高企业交易流程标准化和透明度