使用libpng 1.6.39渐进式读取时始终提示IDAT压缩数据截断
libpng渐进式读取报错"Truncated compressed data in IDAT"排查
我在Linux环境下使用libpng 1.6.39(搭配zlib 1.2.13、GCC/C++ 12)的渐进式读取功能,但无论读取哪张PNG图像,都会收到警告:
Truncated compressed data in IDAT
且完全无法获取图像数据。
我的代码如下:
#include "png.h" #include <unistd.h> #include <iostream> #include <iomanip> #include <cstring> void errorFn(png_structp png_ptr, png_const_charp error_msg) { std::cout << "user_error_fn(): " << error_msg << std::endl; _exit(1); } void warningFn(png_structp png_ptr, png_const_charp warning_msg) { std::cout << "user_warning_fn(): " << warning_msg << std::endl; } void infoCallback(png_structp png_ptr, png_infop info) { std::cout << "infoCallback()" << std::endl; } void rowCallback(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass) { std::cout << "rowCallback(): row_num=" << std::dec << row_num << " pass=" << std::dec << pass << std::endl; } void endCallback(png_structp png_ptr, png_infop info) { std::cout << "endCallback(): " << std::endl; } int main(int argc, char const *argv[]) { FILE *fp(fopen("./libpng/1.6.39/contrib/testpngs/rgb-8.png", "rb")); std::cout << "fp=" << static_cast<void const *>(fp) << std::endl; std::cout << "fseek: " << fseek(fp, 0, SEEK_END) << std::endl; size_t const size(ftell(fp)); std::cout << "ftell: " << size << std::endl; fseek(fp, 0, SEEK_SET); png_bytep buf(new png_byte[size]); memset(buf, 0, size); std::cout << "fread: " << fread(buf, 1, size, fp) << std::endl; fclose(fp); png_structp png_ptr(png_create_read_struct(PNG_LIBPNG_VER_STRING, buf, errorFn, warningFn)); png_infop info_ptr(png_create_info_struct(png_ptr)); png_set_progressive_read_fn(png_ptr, buf, infoCallback, rowCallback, endCallback); png_process_data(png_ptr, info_ptr, buf, size); png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); delete[] buf; return 0; }
程序输出示例如下:
fp=0x47eeb0 fseek: 0 ftell: 759 fread: 759 infoCallback() user_warning_fn(): Truncated compressed data in IDAT endCallback():
我原本预期rowCallback()会被多次调用,且不会出现任何警告或错误。请问我哪里操作有误?
问题排查与修正
你的核心错误有两个:错误复用图像缓冲区作为lib私有数据指针,以及未配置渐进式读取的必要解码参数,具体如下:
1. 私有数据指针误用
png_create_read_struct和png_set_progressive_read_fn的第二个参数是用户私有数据指针,你直接传入了存储图像数据的buf,这会导致libpng内部操作破坏原始图像数据,进而触发"Truncated compressed data"警告。
应该传入独立的用户数据(比如nullptr,如果不需要自定义数据的话),避免干扰图像缓冲区。
2. 缺少解码格式配置与初始化
渐进式读取需要在infoCallback中明确配置图像格式转换规则,并启动行读取初始化,否则libpng无法解码出行数据,rowCallback自然不会触发。
修正后的完整代码
#include "png.h" #include <unistd.h> #include <iostream> #include <iomanip> #include <cstring> void errorFn(png_structp png_ptr, png_const_charp error_msg) { std::cout << "user_error_fn(): " << error_msg << std::endl; _exit(1); } void warningFn(png_structp png_ptr, png_const_charp warning_msg) { std::cout << "user_warning_fn(): " << warning_msg << std::endl; } void infoCallback(png_structp png_ptr, png_infop info) { std::cout << "infoCallback()" << std::endl; // 配置通用格式转换,适配多数PNG图像 png_set_expand(png_ptr); png_set_strip_16(png_ptr); png_set_packing(png_ptr); png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER); // 转换为RGBA格式 // 初始化渐进式行读取缓冲区 png_start_read_image(png_ptr); } void rowCallback(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass) { std::cout << "rowCallback(): row_num=" << std::dec << row_num << " pass=" << std::dec << pass << std::endl; } void endCallback(png_structp png_ptr, png_infop info) { std::cout << "endCallback(): " << std::endl; } int main(int argc, char const *argv[]) { FILE *fp(fopen("./libpng/1.6.39/contrib/testpngs/rgb-8.png", "rb")); std::cout << "fp=" << static_cast<void const *>(fp) << std::endl; std::cout << "fseek: " << fseek(fp, 0, SEEK_END) << std::endl; size_t const size(ftell(fp)); std::cout << "ftell: " << size << std::endl; fseek(fp, 0, SEEK_SET); png_bytep buf(new png_byte[size]); memset(buf, 0, size); std::cout << "fread: " << fread(buf, 1, size, fp) << std::endl; fclose(fp); // 私有数据指针改为nullptr,避免干扰图像缓冲区 png_structp png_ptr(png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, errorFn, warningFn)); png_infop info_ptr(png_create_info_struct(png_ptr)); png_set_progressive_read_fn(png_ptr, nullptr, infoCallback, rowCallback, endCallback); png_process_data(png_ptr, info_ptr, buf, size); png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); delete[] buf; return 0; }
修正说明
- 将用户私有数据指针从
buf改为nullptr,确保图像数据不被libpng误修改 - 在
infoCallback中添加格式转换规则,并调用png_start_read_image初始化行读取逻辑,保证rowCallback能正常触发
内容的提问来源于stack exchange,提问作者DrWatson
相关产品推荐
相关产品推荐

