在Android平台用lodepng与SDL 3.0从内存解码PNG遇错误27
在Android上用lodepng搭配SDL 3.0 RWOps解码PNG失败:错误27(PNG文件小于PNG头)
问题重现
使用SDL 3.0的RWOps读取Android设备上的PNG文件,再用lodepng解码时,始终报错:
lodepng_decode32 failed loading textures/texture.png with error 27: PNG file is smaller than a PNG header
读取文件的代码:
// 获取Android设备上的路径,打开文件并返回数据 char *load_binary_file_on_android(const char *path) { const char *full_android_path = get_path_on_android(path); SDL_Log("Android path: %s", full_android_path); char *data; // 打开二进制文件 SDL_RWops *io = SDL_RWFromFile(full_android_path, "rb"); if (io != NULL) { size_t data_size = (size_t) SDL_RWsize(io); data = (char *) SDL_malloc(data_size + 1); if (SDL_RWread(io, data, data_size) != data_size) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SDL_RWread failed: %s", SDL_GetError()); } SDL_Log("Reading file %s\n%08b", full_android_path, data); SDL_RWclose(io); } else { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SDL_RWops failed: %s", SDL_GetError()); } SDL_free(full_android_path); return data; }
解码代码:
const unsigned char *raw_pixel_data = load_binary_file_on_android(file_name); unsigned char *data; unsigned int width, height; unsigned int error = lodepng_decode32(&data, &width, &height, &raw_pixel_data, sizeof(unsigned char));
问题分析
错误27的核心原因是lodepng接收到的数据源长度严重不足,具体问题出在两处:
- lodepng参数错误:最后一个参数传入
sizeof(unsigned char)(值为1),相当于告诉lodepng只有1字节的PNG数据,远小于PNG文件头的最小8字节要求。 - 文件加载函数缺陷:
- 没有返回实际读取的文件大小,调用方无法传递正确的长度给lodepng
- 错误处理不完善,读取失败时仍可能返回未初始化的无效数据
- 用
%08b打印二进制数据的方式无效,无法验证文件内容是否正确 - 二进制文件额外分配+1内存属于冗余操作(仅字符串需要
\0结尾)
修正方案
1. 修复文件加载函数
修改函数,返回文件数据的同时输出实际字节数,增加PNG头验证:
// 修改后:返回文件数据,同时通过out_data_size输出字节数 char* load_binary_file_on_android(const char* path, size_t* out_data_size) { *out_data_size = 0; const char* full_android_path = get_path_on_android(path); SDL_Log("Android path: %s", full_android_path); char* data = NULL; SDL_RWops* io = SDL_RWFromFile(full_android_path, "rb"); if (io != NULL) { const size_t data_size = (size_t)SDL_RWsize(io); if (data_size == 0) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "File is empty: %s", full_android_path); SDL_RWclose(io); SDL_free(full_android_path); return NULL; } // 二进制文件不需要+1内存 data = (char*)SDL_malloc(data_size); if (!data) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SDL_malloc failed: %s", SDL_GetError()); SDL_RWclose(io); SDL_free(full_android_path); return NULL; } // 以1字节为单位读取,更准确判断读取量 const size_t read_bytes = SDL_RWread(io, data, 1, data_size); if (read_bytes != data_size) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SDL_RWread failed: read %zu bytes, expected %zu: %s", read_bytes, data_size, SDL_GetError()); SDL_free(data); data = NULL; } else { *out_data_size = data_size; // 验证PNG签名:PNG文件以\x89PNG\r\n\x1a\n开头 if (data_size >= 8 && memcmp(data, "\x89PNG\r\n\x1a\n", 8) == 0) { SDL_Log("File is a valid PNG (header matches)"); } else { SDL_LogWarning(SDL_LOG_CATEGORY_APPLICATION, "File does not look like a valid PNG"); } } SDL_RWclose(io); } else { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SDL_RWops failed: %s", SDL_GetError()); } SDL_free(full_android_path); return data; }
2. 修复解码代码
传递正确的文件长度给lodepng,完善错误处理:
size_t png_data_size = 0; // 转换为unsigned char类型,匹配lodepng的参数要求 const unsigned char* raw_png_data = (const unsigned char*)load_binary_file_on_android(file_name, &png_data_size); if (!raw_png_data || png_data_size == 0) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to load PNG file"); return; } unsigned char* decoded_data = NULL; unsigned int width = 0, height = 0; // 最后一个参数传入实际的PNG文件大小 unsigned int error = lodepng_decode32(&decoded_data, &width, &height, raw_png_data, png_data_size); if (error != 0) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "lodepng_decode32 failed loading %s with error %u: %s", file_name, error, lodepng_error_text(error)); SDL_free((void*)raw_png_data); return; } // 此处使用解码后的decoded_data、width、height进行后续操作 // ... // 用完后释放内存 SDL_free((void*)raw_png_data); lodepng_free(decoded_data);
关键修正点
- 必须向
lodepng_decode32传递完整的PNG文件字节数,而非固定的1字节 - 文件加载函数需返回实际读取的大小,确保lodepng能获取完整数据源
- 增加PNG签名验证,提前确认读取的文件是有效的PNG格式
- 完善错误处理流程,避免无效数据流入解码步骤
内容的提问来源于stack exchange,提问作者zachaz35
相关产品推荐
相关产品推荐

