libpng加载单色图片时png_get_rows返回异常的解决及检测方法
解决libpng加载单色PNG时行指针复用及单色图检测问题
一、获取完整像素数据(即使是单色图)
libpng会对单色PNG做内存优化,比如让所有行指针指向同一块内存,但你可以手动将行数据复制到自己的缓冲区,确保拿到完整的所有像素数据。修改你的代码如下:
String path = PathHelper::GetAssetsPath(); path += name; // white.png png_uint_32 width, height; png_structp pngStruct{}; png_infop pngInfo{}; FILE* file = 0; unsigned char* data = 0; file = fopen(path, "rb"); FOG_ASSERT(file != 0); pngStruct = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); FOG_ASSERT(pngStruct != 0); pngInfo = png_create_info_struct(pngStruct); FOG_ASSERT(pngInfo != 0); if (setjmp(png_jmpbuf(pngStruct))) { png_destroy_read_struct(&pngStruct, &pngInfo, 0); fclose(file); FOG_ASSERT(false); } png_init_io(pngStruct, file); int numBytes = 0; png_set_sig_bytes(pngStruct, numBytes); // 保留原有转换参数,确保颜色格式完全展开 png_read_png(pngStruct, pngInfo, PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND, NULL); png_get_IHDR(pngStruct, pngInfo, &width, &height, 0, 0, 0, 0, 0); size_t row_bytes = png_get_rowbytes(pngStruct, pngInfo); data = new unsigned char[row_bytes * height]; png_bytepp row_pointers = png_get_rows(pngStruct, pngInfo); // 手动复制每一行数据到自定义缓冲区,规避行指针复用问题 for (png_uint_32 i = 0; i < height; ++i) { memcpy(data + i * row_bytes, row_pointers[i], row_bytes); }
执行完这段代码后,data缓冲区就会存储所有像素的完整数据,不受libpng优化逻辑的影响。
二、检测PNG是否为单色图
提供两种检测方式,按需选择:
1. 基于PNG元信息快速判断(仅适用于特定格式单色图)
通过解析PNG的颜色类型、调色板等元数据快速判断:
- 索引色格式(
PNG_COLOR_TYPE_PALETTE):检查调色板颜色数量是否为1 - 灰度格式(
PNG_COLOR_TYPE_GRAY):结合位深度初步判断(1bit灰度图大概率是单色,但需进一步验证) - RGB/RGBA格式:无法通过元信息快速判断,必须遍历像素
代码示例:
bool isMonochrome = false; int color_type = png_get_color_type(pngStruct, pngInfo); if (color_type == PNG_COLOR_TYPE_PALETTE) { // 获取调色板颜色数量 int palette_count = png_get_plte(pngStruct, pngInfo, NULL, NULL); if (palette_count == 1) { isMonochrome = true; } } else if (color_type == PNG_COLOR_TYPE_GRAY) { png_byte bit_depth = png_get_bit_depth(pngStruct, pngInfo); // 1bit灰度图只有两种灰度值,若为单色图则所有像素值一致 if (bit_depth == 1) { // 这里可结合行指针是否全部相同做初步判断,准确判断仍需遍历 isMonochrome = true; } }
2. 遍历像素检测(最可靠,支持所有格式)
直接获取第一个像素的颜色值,遍历所有像素逐一对比,是最准确的检测方式:
bool isMonochrome = true; size_t pixel_size = row_bytes / width; // 每个像素的字节数(灰度=1,RGB=3,RGBA=4) // 取第一个像素的颜色作为基准 unsigned char* base_pixel = data; // 遍历所有像素 for (png_uint_32 y = 0; y < height; ++y) { unsigned char* current_row = data + y * row_bytes; for (png_uint_32 x = 0; x < width; ++x) { unsigned char* current_pixel = current_row + x * pixel_size; // 对比每个颜色通道 for (size_t c = 0; c < pixel_size; ++c) { if (current_pixel[c] != base_pixel[c]) { isMonochrome = false; goto end_check; // 找到不同像素直接终止检测 } } } } end_check:
内容的提问来源于stack exchange,提问作者DownloaderGames
相关产品推荐
相关产品推荐

