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

Zlib uncompress解析PNG的IDAT块字节数组返回Z_DATA_ERROR问题排查

问题描述

我正在使用C语言读取PNG图片的IDAT块字节数组,已成功获取图片的其他相关信息及该字节数组。但调用zlib的uncompress()方法对该数组进行解压时出现问题,方法返回Z_DATA_ERROR。

相关代码

int decompress(Chunk * _chunk, Image * _image)
{
    uLongf compressedSize = _chunk->length;

    byte * uncompressedData = NULL;
    uLongf uncompressedSize = 0;

    int ret = uncompress(uncompressedData, &uncompressedSize, _chunk->data, compressedSize);

    if(ret != Z_OK)
    {
        fprintf(stderr, "Error: failed to uncompress IDAT chunk data. ERR CODE: %d\n", ret);
        return -1;
    }

    [ ... ]
}

结构体定义

typedef struct chunk
{
    uint32_t length;
    byte chunkType[4];
    byte *data;
} Chunk;

// byte 类型为 unsigned char

typedef struct image
{
    uint32_t width;
    uint32_t height;
  
    byte bitDepth;
    byte colorType;
    byte compression;
    byte filter;
    byte interlace;
} Image;

测试图片信息

完整十六进制表示

89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52
00 00 00 11 00 00 00 12 04 03 00 00 00 4F D7 28
67 00 00 00 30 50 4C 54 45 00 00 00 80 00 00 00
80 00 80 80 00 00 00 80 80 00 80 00 80 80 80 80
80 C0 C0 C0 FF 00 00 00 FF 00 FF FF 00 00 00 FF
FF 00 FF 00 FF FF FF FF FF 7B 1F B1 C4 00 00 00
09 70 48 59 73 00 00 0E C4 00 00 0E C4 01 95 2B
0E 1B 00 00 00 28 49 44 41 54 08 D7 63 D8 0D 05
1B 18 36 30 00 01 FF FF FF 24 B1 FE FF FF C0 C0
40 0E 6B FF FF FF 20 73 48 60 C1 5D 0A 00 BB 1A
49 27 39 98 BC 6E 00 00 00 00 49 45 4E 44 AE 42
60 82

IDAT块字节数据(不含CRC)

08 D7 63 D8 0D 05 1B 18 36 30 00 01 FF FF FF 24 B1 FE FF FF C0 C0 40 0E 6B FF FF FF 20 73 48 60 C1 5D 0A 00 BB 1A 49 27

注:未处理该块的CRC,但认为这不会造成影响。


问题原因及解决方案

1. 核心错误:未正确初始化解压缓冲区与目标大小

zlib的uncompress()函数要求调用者必须:

  • 提前分配足够内存的缓冲区给uncompressedData
  • 预先设置uncompressedSize为预期的解压后数据总大小(函数执行后会将该值修改为实际解压的字节数)

你的代码中uncompressedData为NULL、uncompressedSize为0,完全不符合函数的调用要求,这是导致Z_DATA_ERROR的直接原因。

修复步骤:

根据PNG图像参数计算解压后的原始数据大小:
原始数据总大小 = 图像高度 × (每行像素数据字节数 + 1)

  • 每行像素数据字节数:根据宽度、位深度、颜色类型计算,需处理位深度不是8倍数的情况(比如4位索引色)
  • +1是因为PNG每一行像素数据前会有一个过滤字节

针对你的测试图片:

  • 宽度=17,高度=18
  • 颜色类型=3(索引色),位深度=4 → 每个像素占4bit,每行像素字节数 = (17×4 +7)/8 = 9
  • 总解压大小 = 18 × (9+1) = 180字节

修改后的代码:

// 辅助函数:根据颜色类型获取通道数
int getChannelCount(byte colorType) {
    switch(colorType) {
        case 0: return 1; // 灰度
        case 2: return 3; // RGB
        case 3: return 1; // 索引色
        case 4: return 2; // 灰度+Alpha
        case 6: return 4; // RGBA
        default: return 0;
    }
}

int decompress(Chunk * _chunk, Image * _image)
{
    uLongf compressedSize = _chunk->length;
    uLongf uncompressedSize = 0;

    // 计算预期的解压后数据大小
    int channelCount = getChannelCount(_image->colorType);
    if (channelCount == 0) {
        fprintf(stderr, "Error: invalid color type\n");
        return -1;
    }

    int rowPixelBytes;
    if (_image->bitDepth == 8 || _image->bitDepth == 16) {
        rowPixelBytes = (_image->width * channelCount * _image->bitDepth) / 8;
    } else {
        // 处理1/2/4位的情况,向上取整
        rowPixelBytes = (_image->width * channelCount * _image->bitDepth + 7) / 8;
    }
    // 加上每行的过滤字节
    uncompressedSize = _image->height * (rowPixelBytes + 1);

    // 分配解压缓冲区
    byte * uncompressedData = malloc(uncompressedSize);
    if (!uncompressedData) {
        fprintf(stderr, "Error: failed to allocate memory for uncompressed data\n");
        return -1;
    }

    int ret = uncompress(uncompressedData, &uncompressedSize, _chunk->data, compressedSize);

    if(ret != Z_OK)
    {
        fprintf(stderr, "Error: failed to uncompress IDAT chunk data. ERR CODE: %d\n", ret);
        free(uncompressedData);
        return -1;
    }

    // ... 后续处理逻辑(比如解析过滤字节、像素数据等)

    // 记得在不需要时释放内存
    // free(uncompressedData);
    return 0;
}

2. 其他可能的验证点

  • IDAT数据完整性:你的测试图片中IDAT块长度为0x28=40字节,提供的IDAT数据正好是40字节(未包含CRC),这部分是正确的。
  • zlib兼容性:确保编译时链接了正确版本的zlib库,头文件包含路径正确,避免因版本不兼容导致的解压失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 10:25:13