使用libpng读取PNG像素数据遇阻,寻求最后步骤解决方案
解决libpng读取像素数据失败的问题
核心问题分析
你的代码存在几个关键问题,导致无法正确读取像素数据:
- 缺少libpng错误处理机制:libpng依赖
setjmp/longjmp处理错误,未设置的话出错会直接终止程序,且无法获取具体错误信息。 - 错误的API使用逻辑:
png_read_png已经完成了像素数据的读取与内存分配,后续调用png_read_image属于重复操作且会触发错误;另外你尝试打印像素时用了%s格式符,会把单个字节当作字符串指针,引发未定义行为。 - 内存分配缺失:使用
png_read_image时,未给行指针数组分配有效内存空间。
修正后的完整代码
#include <png.h> #include <pngconf.h> #include <stdio.h> #include <stdlib.h> #include <setjmp.h> #define BYTES_TO_CHECK 8 // 全局错误跳转缓冲区 jmp_buf png_error_jmpbuf; // libpng错误回调函数 void png_error_fn(png_structp png_ptr, png_const_charp msg) { fprintf(stderr, "PNG Error: %s\n", msg); longjmp(png_error_jmpbuf, 1); } // 校验PNG文件并返回文件指针 FILE *check_png(char *file_name) { char buffer[BYTES_TO_CHECK]; FILE *fP = fopen(file_name, "rb"); if (fP == NULL) { fprintf(stderr, "FILE NOT FOUND!\n"); exit(EXIT_FAILURE); } size_t read_bytes = fread(buffer, 1, BYTES_TO_CHECK, fP); if (read_bytes != BYTES_TO_CHECK) { fprintf(stderr, "Failed to read PNG signature\n"); fclose(fP); exit(EXIT_FAILURE); } int check = png_sig_cmp(buffer, 0, BYTES_TO_CHECK); if (check != 0) { fprintf(stderr, "NOT A PNG\n"); fclose(fP); exit(EXIT_FAILURE); } return fP; } int main() { png_structp png_ptr = NULL; png_infop info_ptr = NULL; png_uint_32 height, width; int bit_depth, color_type; png_bytepp row_pointers = NULL; printf("ASCII Generator\nPlease Enter the PNG file name\t"); char file_name[100] = "google.png"; // scanf("%s", file_name); FILE *fP = check_png(file_name); // 创建读取结构体,指定错误回调 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, png_error_fn, NULL); if (png_ptr == NULL) { fprintf(stderr, "ERROR PNG POINTER IS NULL\n"); fclose(fP); exit(EXIT_FAILURE); } // 创建信息结构体 info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { fprintf(stderr, "ERROR INFO POINTER IS NULL\n"); png_destroy_read_struct(&png_ptr, NULL, NULL); fclose(fP); exit(EXIT_FAILURE); } // 设置错误跳转点,捕获libpng的错误 if (setjmp(png_error_jmpbuf)) { // 发生错误时清理所有资源 png_destroy_read_struct(&png_ptr, &info_ptr, NULL); fclose(fP); exit(EXIT_FAILURE); } // 初始化IO png_init_io(png_ptr, fP); png_set_sig_bytes(png_ptr, BYTES_TO_CHECK); // 读取PNG文件信息和像素数据(自动分配内存) png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); // 获取图像基本信息 height = png_get_image_height(png_ptr, info_ptr); width = png_get_image_width(png_ptr, info_ptr); bit_depth = png_get_bit_depth(png_ptr, info_ptr); color_type = png_get_color_type(png_ptr, info_ptr); printf("Image Size: %ux%u\nBit Depth: %d\nColor Type: %d\n", width, height, bit_depth, color_type); // 获取由png_read_png分配好的像素行指针 row_pointers = png_get_rows(png_ptr, info_ptr); if (row_pointers == NULL) { fprintf(stderr, "Failed to get row pointers\n"); goto cleanup; } // 示例:打印第0行第50个像素的通道值(根据颜色类型适配) printf("Sample pixel data (row 0, column 50): "); if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) { int channel_count = (color_type == PNG_COLOR_TYPE_RGB) ? 3 : 4; png_byte r = row_pointers[0][50 * channel_count]; png_byte g = row_pointers[0][50 * channel_count + 1]; png_byte b = row_pointers[0][50 * channel_count + 2]; printf("R: %u, G: %u, B: %u", r, g, b); if (channel_count == 4) { png_byte a = row_pointers[0][50 * 4 + 3]; printf(", A: %u", a); } printf("\n"); } else if (color_type == PNG_COLOR_TYPE_GRAY) { png_byte gray = row_pointers[0][50]; printf("Gray: %u\n", gray); } cleanup: // 统一清理资源 png_destroy_read_struct(&png_ptr, &info_ptr, NULL); fclose(fP); return EXIT_SUCCESS; }
关键修正点说明
- 添加错误处理机制:通过
setjmp和自定义错误回调,捕获libpng的错误并友好退出,同时确保所有资源被正确释放。 - 正确使用
png_read_png:该函数会自动完成像素数据的读取和内存分配,调用后直接通过png_get_rows获取行指针即可,无需再调用其他读取像素的API。 - 修复像素打印逻辑:根据图像的颜色类型(RGB、RGBA、灰度)计算通道偏移量,用
%u格式符打印单个字节的数值,避免字符串指针错误。 - 版本号兼容性:用
PNG_LIBPNG_VER_STRING宏代替硬编码的版本号,保证代码在不同libpng版本下的兼容性。 - 完善资源清理:所有代码分支都确保释放libpng结构体和关闭文件,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Cxyeinx
相关产品推荐
相关产品推荐

