You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用libpng的C代码可读取PNG信息,调用png_read_row时崩溃原因排查

libpng调用png_read_row崩溃问题排查

问题代码

FILE * fp = fopen(Usr.Source, "rb");
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, 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);
GraphicHeight = png_get_image_height(png_ptr, info_ptr); // 返回正确值
GraphicWidth = png_get_image_width(png_ptr, info_ptr); // 返回正确值
png_uint_32 rowBytes = png_get_rowbytes(png_ptr, info_ptr); // 返回预期值
png_bytep pngImg = (png_bytep) malloc(GraphicHeight * rowBytes); // 分配成功
png_bytepp row_pointers = (png_bytepp) malloc(GraphicHeight * sizeof(png_bytep)); 

for (int rw = 0; rw < GraphicHeight; rw++) {
  row_pointers[rw] = pngImg + rw * rowBytes; 
  png_read_row(png_ptr, row_pointers[rw], NULL); // 第0行崩溃
}

崩溃调用栈

#9  0x00007ffff7f96ada in ?? () from /lib/x86_64-linux-gnu/libpng16.so.16
#10 0x00007ffff7f88dbf in png_read_row () from /lib/x86_64-linux-gnu/libpng16.so.16

问题原因

你已经调用了png_read_png函数,这个函数会一次性把整个PNG图像的所有数据读取到内存中,同时将文件指针移动到文件末尾,libpng内部的读取状态也会标记为已完成。此时再调用png_read_row/png_read_rows/png_read_image,这些函数会试图从已经耗尽的文件流中读取数据,导致libpng内部状态异常,最终触发崩溃。

解决方案

你需要选择以下两种读取方式之一,不要混合使用:

方式1:先读取图像信息,再逐行/批量读取数据

适合需要自定义管理图像内存的场景:

FILE * fp = fopen(Usr.Source, "rb");
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop info_ptr = png_create_info_struct(png_ptr);
png_init_io(png_ptr, fp);

// 只读取图像信息,不读取像素数据
png_read_info(png_ptr, info_ptr);

GraphicHeight = png_get_image_height(png_ptr, info_ptr);
GraphicWidth = png_get_image_width(png_ptr, info_ptr);
png_uint_32 rowBytes = png_get_rowbytes(png_ptr, info_ptr);

// 分配图像内存和行指针数组
png_bytep pngImg = (png_bytep) malloc(GraphicHeight * rowBytes);
png_bytepp row_pointers = (png_bytepp) malloc(GraphicHeight * sizeof(png_bytep)); 

for (int rw = 0; rw < GraphicHeight; rw++) {
  row_pointers[rw] = pngImg + rw * rowBytes; 
}

// 可选:批量读取所有行
png_read_image(png_ptr, row_pointers);
// 或者逐行读取
// for (int rw = 0; rw < GraphicHeight; rw++) {
//   png_read_row(png_ptr, row_pointers[rw], NULL);
// }

// 读取结束,清理状态
png_read_end(png_ptr, NULL);

// 这里添加图像数据处理逻辑

// 释放资源
free(row_pointers);
free(pngImg);
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(fp);

方式2:使用png_read_png直接获取已读取的行指针

更简洁,libpng会自动管理图像内存:

FILE * fp = fopen(Usr.Source, "rb");
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, 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);

GraphicHeight = png_get_image_height(png_ptr, info_ptr);
GraphicWidth = png_get_image_width(png_ptr, info_ptr);

// 获取libpng已分配好的行指针数组
png_bytepp row_pointers = png_get_rows(png_ptr, info_ptr);

// 这里添加图像数据处理逻辑,比如遍历每行
// for (int rw = 0; rw < GraphicHeight; rw++) {
//   png_bytep row = row_pointers[rw];
//   // 处理该行像素
// }

// 释放资源:png_destroy_read_struct会自动释放图像内存
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(fp);

总结

  • 绝对不要混合使用png_read_png和png_read_row系列读取函数,两者属于互斥的读取流程。
  • 根据场景选择读取方式:需要自定义内存管理用方式1;追求代码简洁用方式2。

内容的提问来源于stack exchange,提问作者VectorVortec

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 04:01:31