LIBPNG

libpng 库的源码包中有个 example.c ,里面包含PNG文件读/写的示例代码,参考示例代码和注释(虽然是英文的),可以了解大致的用法。


以下是读取PNG图片的图像数据的代码,使用前还需要按自己的需求补充剩余代码。

  1. #include <png.h>  
  2. #define PNG_BYTES_TO_CHECK 4  
  3.   
  4. int load_png_image( const char *filepath, /* 其它参数 */ )  
  5. {  
  6.         FILE *fp;  
  7.         png_structp png_ptr;  
  8.         png_infop info_ptr;  
  9.         png_bytep* row_pointers;  
  10.         char buf[PNG_BYTES_TO_CHECK];  
  11.         int w, h, x, y, temp, color_type;  
  12.   
  13.         fp = fopen( filepath, "rb" );  
  14.         if( fp == NULL ) {   
  15.                 return /* 返回值 */;  
  16.         }  
  17.           
  18.         png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 );  
  19.         info_ptr = png_create_info_struct( png_ptr );  
  20.   
  21.         setjmp( png_jmpbuf(png_ptr) );   
  22.         /* 读取PNG_BYTES_TO_CHECK个字节的数据 */  
  23.         temp = fread( buf, 1, PNG_BYTES_TO_CHECK, fp );  
  24.         /* 若读到的数据并没有PNG_BYTES_TO_CHECK个字节 */  
  25.         if( temp < PNG_BYTES_TO_CHECK ) {  
  26.                 fclose(fp);  
  27.                 png_destroy_read_struct( &png_ptr, &info_ptr, 0);  
  28.                 return /* 返回值 */;  
  29.         }  
  30.         /* 检测数据是否为PNG的签名 */  
  31.         temp = png_sig_cmp( (png_bytep)buf, (png_size_t)0, PNG_BYTES_TO_CHECK );  
  32.         /* 如果不是PNG的签名,则说明该文件不是PNG文件 */  
  33.         if( temp != 0 ) {  
  34.                 fclose(fp);  
  35.                 png_destroy_read_struct( &png_ptr, &info_ptr, 0);  
  36.                 return /* 返回值 */;  
  37.         }  
  38.           
  39.         /* 复位文件指针 */  
  40.         rewind( fp );  
  41.         /* 开始读文件 */  
  42.         png_init_io( png_ptr, fp );   
  43.         /* 读取PNG图片信息 */  
  44.         png_read_png( png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0 );  
  45.         /* 获取图像的色彩类型 */  
  46.         color_type = png_get_color_type( png_ptr, info_ptr );  
  47.         /* 获取图像的宽高 */  
  48.         w = png_get_image_width( png_ptr, info_ptr );  
  49.         h = png_get_image_height( png_ptr, info_ptr );  
  50.         /* 获取图像的所有行像素数据,row_pointers里边就是rgba数据 */  
  51.         row_pointers = png_get_rows( png_ptr, info_ptr );  
  52.         /* 根据不同的色彩类型进行相应处理 */  
  53.         switch( color_type ) {  
  54.         case PNG_COLOR_TYPE_RGB_ALPHA:  
  55.                 for( y=0; y<h; ++y ) {  
  56.                         for( x=0; x<w*4; ) {  
  57.                                 /* 以下是RGBA数据,需要自己补充代码,保存RGBA数据 */  
  58.                                 /* 目标内存 */ = row_pointers[y][x++]; // red  
  59.                                 /* 目标内存 */ = row_pointers[y][x++]; // green  
  60.                                 /* 目标内存 */ = row_pointers[y][x++]; // blue  
  61.                                 /* 目标内存 */ = row_pointers[y][x++]; // alpha  
  62.                         }  
  63.                 }  
  64.                 break;  
  65.   
  66.         case PNG_COLOR_TYPE_RGB:  
  67.                 for( y=0; y<h; ++y ) {  
  68.                         for( x=0; x<w*3; ) {  
  69.                                 /* 目标内存 */ = row_pointers[y][x++]; // red  
  70.                                 /* 目标内存 */ = row_pointers[y][x++]; // green  
  71.                                 /* 目标内存 */ = row_pointers[y][x++]; // blue  
  72.                         }  
  73.                 }  
  74.                 break;  
  75.         /* 其它色彩类型的图像就不读了 */  
  76.         default:  
  77.                 fclose(fp);  
  78.                 png_destroy_read_struct( &png_ptr, &info_ptr, 0);  
  79.                 return /* 返回值 */;  
  80.         }  
  81.         png_destroy_read_struct( &png_ptr, &info_ptr, 0);  
  82.         return 0;  
  83. }  



以下是生成png图片文件的代码,也就是照搬了 example.c 里的 write_png() 的代码,稍微翻译了主要的注释。

  1. /* 写入 png 文件 */  
  2. void write_png(char *file_name /* , ... 其他图像信息相关的参数 ... */)  
  3. {  
  4.    FILE *fp;  
  5.    png_structp png_ptr;  
  6.    png_infop info_ptr;  
  7.    png_colorp palette;  
  8.   
  9.    /* 打开需要写入的文件 */  
  10.    fp = fopen(file_name, "wb");  
  11.    if (fp == NULL)  
  12.       return (ERROR);  
  13.   
  14.    /* 创建并初始化 png_struct 及其所需的错误处理函数,如果你想使用默 
  15.     * 认的 stderr 和 longjump() 方法,你可以将最后三个参数设为 NULL, 
  16.     * 在使用动态链接库的情况下,我们也会检测函数库版本是否与在编译时 
  17.     * 使用的版本是否兼容。(必要) 
  18.     */  
  19.    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,  
  20.       png_voidp user_error_ptr, user_error_fn, user_warning_fn);  
  21.   
  22.    if (png_ptr == NULL)  
  23.    {  
  24.       fclose(fp);  
  25.       return (ERROR);  
  26.    }  
  27.   
  28.    /* 分配内存并初始化图像信息数据。(必要)*/  
  29.    info_ptr = png_create_info_struct(png_ptr);  
  30.    if (info_ptr == NULL)  
  31.    {  
  32.       fclose(fp);  
  33.       png_destroy_write_struct(&png_ptr,  NULL);  
  34.       return (ERROR);  
  35.    }  
  36.   
  37.    /* 设置错误处理。如果你在调用 png_create_write_struct() 时没 
  38.     * 有设置错误处理函数,那么这段代码是必须写的。*/  
  39.    if (setjmp(png_jmpbuf(png_ptr)))  
  40.    {  
  41.       /* 如果程序跑到这里了,那么写入文件时出现了问题 */  
  42.       fclose(fp);  
  43.       png_destroy_write_struct(&png_ptr, &info_ptr);  
  44.       return (ERROR);  
  45.    }  
  46.   
  47.    /* 下面的 I/O 初始化函数有一个是必需的 */  
  48.   
  49. #ifdef streams /* I/O 初始化方法 1 */  
  50.    /* 设置输出控制,如果你使用的是 C 的标准 I/O 流 */  
  51.    png_init_io(png_ptr, fp);  
  52.   
  53. #else no_streams /* I/O 初始化方法 2 */  
  54.    /* 如果你是要替换写入函数,而不想调用 png_init_io(),那么需要指定三个参数: 
  55.     * I/O相关的指针,假设为 user_io_ptr 
  56.     * 自定义的写入函数,假设为 user_write_fn 
  57.     * 自定义的I/O刷新函数,假设为 user_IO_flush_function 
  58.     */  
  59.    png_set_write_fn(png_ptr, (void *)user_io_ptr, user_write_fn,  
  60.       user_IO_flush_function);  
  61.    /* 你需要在某个地方构造一个可用 user_io_ptr 给回调函数使用 */  
  62. #endif no_streams /* 只能选择一种初始化方式 */  
  63.   
  64. #ifdef hilevel  
  65.    /* 这是一种简单的做法,前提是你已经已经有了全部图像信息。 
  66.     * 你可以使用 | 运算符合并多个 PNG_TRANSFORM 标志到这个 
  67.     * png_transforms 整型变量中 */  
  68.    png_write_png(png_ptr, info_ptr, png_transforms, NULL);  
  69.   
  70. #else  
  71.    /* 这是一种复杂的做法 */  
  72.   
  73.    /* (必需)在这里设置图像的信息,宽度、高度的上限是 2^31。 
  74.     * bit_depth 取值必需是 1、2、4、8 或者 16, 但是可用的值也依赖于 color_type。 
  75.     * color_type 可选值有: PNG_COLOR_TYPE_GRAY、PNG_COLOR_TYPE_GRAY_ALPHA、 
  76.     * PNG_COLOR_TYPE_PALETTE、PNG_COLOR_TYPE_RGB、PNG_COLOR_TYPE_RGB_ALPHA。 
  77.     * interlace 可以是 PNG_INTERLACE_NONE 或 PNG_INTERLACE_ADAM7, 
  78.     * 而 compression_type 和 filter_type 目前必需是 PNG_COMPRESSION_TYPE_BASE 
  79.     * 和 and PNG_FILTER_TYPE_BASE。 
  80.     */  
  81.    png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_???,  
  82.       PNG_INTERLACE_????, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);  
  83.   
  84.    /* 如果要调色板的话,在这里设置调色板,对于索引图像,这个是必需的 */  
  85.    palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH  
  86.              * (sizeof (png_color)));  
  87.    /* ... 设置调色板的颜色集 ... */  
  88.    png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH);  
  89.    /* 在销毁 png 结构前的这段期间,你不能释放调色板,因为 png_set_PLTE 
  90.     * 只是在你分配的调色板上建立引用,并未另外建立副本 */  
  91.   
  92.    /* 标注位(sBIT)块 */  
  93.    png_color_8 sig_bit;  
  94.   
  95.    /* 如果我们处理的是灰度图像,则这样 */  
  96.    sig_bit.gray = true_bit_depth;  
  97.   
  98.    /* 否则,我们处理的是彩色图像 */  
  99.    sig_bit.red = true_red_bit_depth;  
  100.    sig_bit.green = true_green_bit_depth;  
  101.    sig_bit.blue = true_blue_bit_depth;  
  102.   
  103.    /* 如果这个图像有 alpha 通道 */  
  104.    sig_bit.alpha = true_alpha_bit_depth;  
  105.   
  106.    png_set_sBIT(png_ptr, info_ptr, &sig_bit);  
  107.   
  108.   
  109.    /* (可选)如果你怀疑图像伽马值的正确性 */  
  110.    png_set_gAMA(png_ptr, info_ptr, gamma);  
  111.   
  112.    /* (可选)写注释文本到图像里 */  
  113.    {  
  114.       png_text text_ptr[3];  
  115.   
  116.       char key0[]="Title";  
  117.       char text0[]="Mona Lisa";  
  118.       text_ptr[0].key = key0;  
  119.       text_ptr[0].text = text0;  
  120.       text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE;  
  121.       text_ptr[0].itxt_length = 0;  
  122.       text_ptr[0].lang = NULL;  
  123.       text_ptr[0].lang_key = NULL;  
  124.   
  125.       char key1[]="Author";  
  126.       char text1[]="Leonardo DaVinci";  
  127.       text_ptr[1].key = key1;  
  128.       text_ptr[1].text = text1;  
  129.       text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE;  
  130.       text_ptr[1].itxt_length = 0;  
  131.       text_ptr[1].lang = NULL;  
  132.       text_ptr[1].lang_key = NULL;  
  133.   
  134.       char key2[]="Description";  
  135.       char text2[]="<long text>";  
  136.       text_ptr[2].key = key2;  
  137.       text_ptr[2].text = text2;  
  138.       text_ptr[2].compression = PNG_TEXT_COMPRESSION_zTXt;  
  139.       text_ptr[2].itxt_length = 0;  
  140.       text_ptr[2].lang = NULL;  
  141.       text_ptr[2].lang_key = NULL;  
  142.   
  143.       png_set_text(write_ptr, write_info_ptr, text_ptr, 3);  
  144.    }  
  145.   
  146.    /* Other optional chunks like cHRM, bKGD, tRNS, tIME, oFFs, pHYs */  
  147.   
  148.    /* Note that if sRGB is present the gAMA and cHRM chunks must be ignored 
  149.     * on read and, if your application chooses to write them, they must 
  150.     * be written in accordance with the sRGB profile 
  151.     */  
  152.   
  153.    /* 写入文件头部信息(必需) */  
  154.    png_write_info(png_ptr, info_ptr);  
  155.   
  156.    /* If you want, you can write the info in two steps, in case you need to 
  157.     * write your private chunk ahead of PLTE: 
  158.     * 
  159.     *   png_write_info_before_PLTE(write_ptr, write_info_ptr); 
  160.     *   write_my_chunk(); 
  161.     *   png_write_info(png_ptr, info_ptr); 
  162.     * 
  163.     * However, given the level of known- and unknown-chunk support in 1.2.0 
  164.     * and up, this should no longer be necessary. 
  165.     */  
  166.   
  167.    /* Once we write out the header, the compression type on the text 
  168.     * chunk gets changed to PNG_TEXT_COMPRESSION_NONE_WR or 
  169.     * PNG_TEXT_COMPRESSION_zTXt_WR, so it doesn't get written out again 
  170.     * at the end. 
  171.     */  
  172.   
  173.    /* Set up the transformations you want.  Note that these are 
  174.     * all optional.  Only call them if you want them. 
  175.     */  
  176.   
  177.    /* Invert monochrome pixels */  
  178.    png_set_invert_mono(png_ptr);  
  179.   
  180.    /* Shift the pixels up to a legal bit depth and fill in 
  181.     * as appropriate to correctly scale the image. 
  182.     */  
  183.    png_set_shift(png_ptr, &sig_bit);  
  184.   
  185.    /* Pack pixels into bytes */  
  186.    png_set_packing(png_ptr);  
  187.   
  188.    /* Swap location of alpha bytes from ARGB to RGBA */  
  189.    png_set_swap_alpha(png_ptr);  
  190.   
  191.    /* Get rid of filler (OR ALPHA) bytes, pack XRGB/RGBX/ARGB/RGBA into 
  192.     * RGB (4 channels -> 3 channels). The second parameter is not used. 
  193.     */  
  194.    png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);  
  195.   
  196.    /* Flip BGR pixels to RGB */  
  197.    png_set_bgr(png_ptr);  
  198.   
  199.    /* Swap bytes of 16-bit files to most significant byte first */  
  200.    png_set_swap(png_ptr);  
  201.   
  202.    /* Swap bits of 1, 2, 4 bit packed pixel formats */  
  203.    png_set_packswap(png_ptr);  
  204.   
  205.    /* 启用交错处理,如果你没有使用 png_write_image() */  
  206.    if (interlacing != 0)  
  207.       number_passes = png_set_interlace_handling(png_ptr);  
  208.   
  209.    else  
  210.       number_passes = 1;  
  211.   
  212.    /* 这是最简单的图像写入方法。(你或许有不同的内存布局,因此你需要选择一个最适合你的方法) 
  213.     * 如果你自己不是逐行写入,则需要使用第一种方法。 
  214.     */  
  215.    png_uint_32 k, height, width;  
  216.   
  217.    /* 在这个示例代码中,"image" 是一个一维的字节数组(每个元素占一个字节空间) */  
  218.    png_byte image[height*width*bytes_per_pixel];  
  219.   
  220.    png_bytep row_pointers[height];  
  221.   
  222.    if (height > PNG_UINT_32_MAX/(sizeof (png_bytep)))  
  223.      png_error (png_ptr, "Image is too tall to process in memory");  
  224.   
  225.    /* 将这些像素行指针指向你的 "image" 字节数组中对应的位置,即:指向每行像素的起始处 */  
  226.    for (k = 0; k < height; k++)  
  227.      row_pointers[k] = image + k*width*bytes_per_pixel;  
  228.   
  229.    /* 必需在下面的输出方式中选择一个 */  
  230.   
  231. #ifdef entire /* 一次调用就将整个图像写进文件 */  
  232.    png_write_image(png_ptr, row_pointers);  
  233.   
  234.    /* 其他的写出方式:交错写出 */  
  235.   
  236. #else no_entire /* 用一个或多个扫描线写出图像数据 */  
  237.   
  238.    /* 扫描次数为 1 的是非交错的图像,其它的则是交错图像。*/  
  239.    for (pass = 0; pass < number_passes; pass++)  
  240.    {  
  241.       /* 一次性写几行 */  
  242.       png_write_rows(png_ptr, &row_pointers[first_row], number_of_rows);  
  243.   
  244.       /* 如果你一次性只写一行像素,可以用下面的代码 */  
  245.       for (y = 0; y < height; y++)  
  246.          png_write_rows(png_ptr, &row_pointers[y], 1);  
  247.    }  
  248. #endif no_entire /*只能选择一种输出方式 */  
  249.   
  250.    /* You can write optional chunks like tEXt, zTXt, and tIME at the end 
  251.     * as well.  Shouldn't be necessary in 1.2.0 and up as all the public 
  252.     * chunks are supported and you can use png_set_unknown_chunks() to 
  253.     * register unknown chunks into the info structure to be written out. 
  254.     */  
  255.   
  256.    /* 必需调用这个函数完成写入文件其余部分 */  
  257.    png_write_end(png_ptr, info_ptr);  
  258. #endif hilevel  
  259.   
  260.    /* If you png_malloced a palette, free it here (don't free info_ptr->palette, 
  261.     * as recommended in versions 1.0.5m and earlier of this example; if 
  262.     * libpng mallocs info_ptr->palette, libpng will free it).  If you 
  263.     * allocated it with malloc() instead of png_malloc(), use free() instead 
  264.     * of png_free(). 
  265.     */  
  266.    png_free(png_ptr, palette);  
  267.    palette = NULL;  
  268.   
  269.    /* Similarly, if you png_malloced any data that you passed in with 
  270.     * png_set_something(), such as a hist or trans array, free it here, 
  271.     * when you can be sure that libpng is through with it. 
  272.     */  
  273.    png_free(png_ptr, trans);  
  274.    trans = NULL;  
  275.    /* Whenever you use png_free() it is a good idea to set the pointer to 
  276.     * NULL in case your application inadvertently tries to png_free() it 
  277.     * again.  When png_free() sees a NULL it returns without action, thus 
  278.     * avoiding the double-free security problem. 
  279.     */  
  280.   
  281.    /* 写完后清理并释放已分配的内存 */  
  282.    png_destroy_write_struct(&png_ptr, &info_ptr);  
  283.   
  284.    /* 关闭文件 */  
  285.    fclose(fp);  
  286.   
  287.    /* That's it */  
  288.    return (OK);  
  289. }  


删掉那些可选的代码,write_png() 的主要代码也就这些:

    1. /* 写入 png 文件 */  
    2. void write_png(char *file_name /* , ... 其他图像信息相关的参数 ... */)  
    3. {  
    4.    FILE *fp;  
    5.    png_structp png_ptr;  
    6.    png_infop info_ptr;  
    7.    png_colorp palette;  
    8.   
    9.    /* 打开需要写入的文件 */  
    10.    fp = fopen(file_name, "wb");  
    11.    if (fp == NULL)  
    12.       return (ERROR);  
    13.   
    14.    /* 创建并初始化 png_struct 及其所需的错误处理函数,如果你想使用默 
    15.     * 认的 stderr 和 longjump() 方法,你可以将最后三个参数设为 NULL, 
    16.     * 在使用动态链接库的情况下,我们也会检测函数库版本是否与在编译时 
    17.     * 使用的版本是否兼容。(必要) 
    18.     */  
    19.    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);  
    20.   
    21.    if (png_ptr == NULL)  
    22.    {  
    23.       fclose(fp);  
    24.       return (ERROR);  
    25.    }  
    26.   
    27.    /* 分配内存并初始化图像信息数据。(必要)*/  
    28.    info_ptr = png_create_info_struct(png_ptr);  
    29.    if (info_ptr == NULL)  
    30.    {  
    31.       fclose(fp);  
    32.       png_destroy_write_struct(&png_ptr,  NULL);  
    33.       return (ERROR);  
    34.    }  
    35.   
    36.    /* 设置错误处理。如果你在调用 png_create_write_struct() 时没 
    37.     * 有设置错误处理函数,那么这段代码是必须写的。*/  
    38.    if (setjmp(png_jmpbuf(png_ptr)))  
    39.    {  
    40.       /* 如果程序跑到这里了,那么写入文件时出现了问题 */  
    41.       fclose(fp);  
    42.       png_destroy_write_struct(&png_ptr, &info_ptr);  
    43.       return (ERROR);  
    44.    }  
    45.   
    46.    /* 设置输出控制,如果你使用的是 C 的标准 I/O 流 */  
    47.    png_init_io(png_ptr, fp);  
    48.   
    49.    /* 这是一种复杂的做法 */  
    50.   
    51.    /* (必需)在这里设置图像的信息,宽度、高度的上限是 2^31。 
    52.     * bit_depth 取值必需是 1、2、4、8 或者 16, 但是可用的值也依赖于 color_type。 
    53.     * color_type 可选值有: PNG_COLOR_TYPE_GRAY、PNG_COLOR_TYPE_GRAY_ALPHA、 
    54.     * PNG_COLOR_TYPE_PALETTE、PNG_COLOR_TYPE_RGB、PNG_COLOR_TYPE_RGB_ALPHA。 
    55.     * interlace 可以是 PNG_INTERLACE_NONE 或 PNG_INTERLACE_ADAM7, 
    56.     * 而 compression_type 和 filter_type 目前必需是 PNG_COMPRESSION_TYPE_BASE 
    57.     * 和 and PNG_FILTER_TYPE_BASE。 
    58.     */  
    59.    png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_???,  
    60.       PNG_INTERLACE_????, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);  
    61.   
    62.    /* 写入文件头部信息(必需) */  
    63.    png_write_info(png_ptr, info_ptr);  
    64.   
    65.    png_uint_32 k, height, width;  
    66.   
    67.    /* 在这个示例代码中,"image" 是一个一维的字节数组(每个元素占一个字节空间) */  
    68.    png_byte image[height*width*bytes_per_pixel];  
    69.   
    70.    png_bytep row_pointers[height];  
    71.   
    72.    if (height > PNG_UINT_32_MAX/(sizeof (png_bytep)))  
    73.      png_error (png_ptr, "Image is too tall to process in memory");  
    74.   
    75.    /* 将这些像素行指针指向你的 "image" 字节数组中对应的位置,即:指向每行像素的起始处 */  
    76.    for (k = 0; k < height; k++)  
    77.      row_pointers[k] = image + k*width*bytes_per_pixel;  
    78.   
    79.    /* 一次调用就将整个图像写进文件 */  
    80.    png_write_image(png_ptr, row_pointers);  
    81.    /* 必需调用这个函数完成写入文件其余部分 */  
    82.    png_write_end(png_ptr, info_ptr);  
    83.    /* 写完后清理并释放已分配的内存 */  
    84.    png_destroy_write_struct(&png_ptr, &info_ptr);  
    85.    /* 关闭文件 */  
    86.    fclose(fp);  
    87.   
    88.    /* That's it */  
    89.    return (OK);  
原文地址:https://www.cnblogs.com/zhoug2020/p/6287552.html