Sam iets
Sam iets

Reputation: 11

Z_DATA_ERROR when trying to inflate png IDAT chunk

I'm having trouble inflating the png IDAT chunk back to RGB data.

void PNG::IDAT()
{
    int index = 0;
    char CMF = m_data[index];
    index++;

    //big endian
    char CM = CMF & 0b00001111;
    char CINFO = CMF & 0b11110000; 
    //For CM = 8, CINFO is the base-2 logarithm of the LZ77 window  size, minus eight(CINFO = 7 indicates a 32K window size).

    char FLG = m_data[index];
    index++;

    char FCHECK = FLG & 0b00011111; 
    //The FCHECK value must be such that CMF and FLG, when viewed as a 16 - bit unsigned integer stored in MSB order(CMF * 256 + FLG),  is a multiple of 31. //effort
    char FDICT =  FLG & 0b00100000;
    char FLEVEl = FLG & 0b11000000;

    char DICTID[4];
    if (FDICT > 0)
    {
        memcpy(DICTID, &m_data[index], 4);
        index += 4;
    }

    uLong outputLength = compressBound(m_length); 
    char* output = new char[outputLength];


    z_stream infstream;
    infstream.zalloc = Z_NULL;
    infstream.zfree = Z_NULL;
    infstream.opaque = Z_NULL;
    infstream.avail_in = m_length; // size of input
    infstream.next_in = (Bytef *)m_data; // input char array
    infstream.avail_out = outputLength; // size of output
    infstream.next_out = (Bytef *)output; // output char array

    inflateInit2(&infstream, 16 + MAX_WBITS);
    inflate(&infstream, Z_NO_FLUSH);
    inflateEnd(&infstream);


    for (size_t i = 0; i < outputLength; i+= 3)
    {
        pixel temp;
        temp.r = output[i + 0];
        temp.g = output[i + 1];
        temp.b = output[i + 2];
        m_pixels.push_back(temp);
    }
}

Inflate returns error code -3, which means "Z_DATA_ERROR". I've followed the RFC-1950 and RFC-1951 standards but I'm confused as to which bytes actually need to be streamed into the inflate function and which need to be stripped. m_data is literally just the data from the chunk without the length, type and CRC. m_length in turn is only the length given by said chunk.

Input is also just plain RGB, with compression mode 0, filter mode 0 and interlace mode 0.

CM is 8.

CMINFO is 112.

FCHECK is 30.

FDICT is 0.

FLEVEL is 64.

TL;DR: what exactly does the inflate function from zlib want/need?

Here's also a picture of the hex values of the image I'm trying to read. picture link because stackoverflow doesn't allow new users to post pics

Upvotes: 0

Views: 762

Answers (1)

Mark Adler
Mark Adler

Reputation: 112324

The 78 5e ed d1 ... right after the IDAT is the start of the zlib stream. It is 288 bytes long, and is a valid zlib stream, as is all of the PNG data. If you have read the data correctly, fed the correct portion to inflate, and provided enough output space (see #2 below), then it will work.

Some comments on your code:

  1. You do not need to try to decode the zlib header. Just feed the whole thing to inflate.
  2. compressBound() is of no use here. That is only for compression, not decompression. That 228 bytes of compressed data decompresses to 47,234 bytes. Far more than you have allocated space for.
  3. The resulting decompressed data is not the raw RGB pixels. Each line of the image starts with a filter byte, and the remaining bytes in the line need to be interpreted accordingly.
  4. You need to check for return code and errors from the zlib functions. Always check return codes. Always.

Upvotes: 1

Related Questions