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

在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接收到的数据源长度严重不足,具体问题出在两处:

  1. lodepng参数错误:最后一个参数传入sizeof(unsigned char)(值为1),相当于告诉lodepng只有1字节的PNG数据,远小于PNG文件头的最小8字节要求。
  2. 文件加载函数缺陷:
    • 没有返回实际读取的文件大小,调用方无法传递正确的长度给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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 22:05:59