在C中使用libpng读取和写入PNG文件

问题描述

我的目标是读取 PNG 文件、更改像素值并使用 libpng 存储更新的 PNG 文件

我按照 official libpng manual 编写了两个名为 read_pngwrite_png函数。示例代码不会更改像素值,因为它是一个 minimum,reproducible example。它也不会检查输入文件是否实际上是一个 PNG 文件

#include <png.h>

png_infop info_ptr;
png_bytepp row_pointers;

void read_png(char *file_name)
{
    FILE *fp = fopen(file_name,"rb");
    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL);
    png_infop info_ptr = png_create_info_struct(png_ptr);
    png_init_io(png_ptr,fp);
    png_read_png(png_ptr,info_ptr,PNG_TRANSFORM_IDENTITY,NULL);
    row_pointers = png_get_rows(png_ptr,info_ptr);
    png_destroy_read_struct(&png_ptr,&info_ptr,NULL);
    fclose(fp);
}

void write_png(char *file_name)
{
    FILE *fp = fopen(file_name,"wb");
    png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL);
    png_init_io(png_ptr,fp);
    png_set_rows(png_ptr,row_pointers);
    png_write_png(png_ptr,NULL);
    png_destroy_write_struct(&png_ptr,&info_ptr);
    fclose(fp);
}

int main(int argc,char *argv[])
{
    read_png(argv[1]);
    write_png(argv[2]);
    return 0;
}

代码编译没有任何错误

$ gcc -o rw_png -lpng rw_png.c

程序可以用两个参数运行。

$ ./rw_png input.png output.png

问题是,虽然创建了 output.png,但它是一个文件,而不是一个 PNG 文件

$ file output.png
output.png: empty

解决方法

libpng 使用 info_struct 来保存有关像素数据外观的信息。因此,您需要将其保留 write_png(或重新创建)。

png_infop info_ptr;  // <-- Global info_ptr (good)
png_bytepp row_pointers; 

void read_png(char *file_name)
{
    FILE *fp = fopen(file_name,"rb");
    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL);
    png_infop info_ptr = png_create_info_struct(png_ptr);  // <-- creating a new,local info_ptr 
    png_init_io(png_ptr,fp);
    png_read_png(png_ptr,info_ptr,PNG_TRANSFORM_IDENTITY,NULL);
    row_pointers = png_get_rows(png_ptr,info_ptr);
    png_destroy_read_struct(&png_ptr,&info_ptr,NULL); // <-- destroying the info_ptr (as well).
    fclose(fp);
}

因此,固定的 read_png 将是:

void read_png(char *file_name)
{
    FILE *fp = fopen(file_name,NULL);
    info_ptr = png_create_info_struct(png_ptr);  
    png_init_io(png_ptr,NULL); 
    fclose(fp);
}

假设 read_png / write_png,您将在 info_ptr 中释放 write_png