Henry K.
Henry K.

Reputation: 3

Inflate data from PNG only succeed with specific image?

I had some trouble with trying to inflate data from IDAT block of an PNG image file. So far, I was able to read the IHDR block and return correct data. However, when i tried to decompress to get the raw RGB value of the file, I was able to only succeed on 1 image while getting 0's from the others. This was the code I used for inflate from zlib.

unsigned char temp[4];
fread(&temp,1,4,pf);
unsigned int ihdr_size = convert_endian(temp);

unsigned char blk[ihdr_size + 8];
fread(blk,1,ihdr_size + 8,pf);

if (check_ihdr(blk) == -1){
    fclose(pf);
    return -1;
}
this->width  = convert_endian(blk+4);
this->height = convert_endian(blk+8);
long area = this->width*this->height*3 +this->height+1;
printf("%ld x %ld\n",this->width, this->height);
printf("Bit depth: %1x\n", (unsigned char)blk[12]);
printf("Color type: %1x\n", (unsigned char)blk[13]);

while(fread(&temp,1,4,pf) > 0){
    unsigned int bsize = convert_endian(temp);
    unsigned char chunk[bsize+8] = {0};
    fread(chunk,1, bsize+8,pf);   //8 extra for block name and CRC
    if (chunk_type(chunk)== 1){ //IDAT
        this->picture = new unsigned char[area];

        z_stream infstream;
        infstream.zalloc = Z_NULL;
        infstream.zfree = Z_NULL;
        infstream.opaque = Z_NULL;
        infstream.avail_in = bsize; // size of input
        infstream.next_in = chunk+4; // input char array (without chunk name)
        infstream.avail_out = area; // size of output
        infstream.next_out = this->picture; // output char array

        inflateInit(&infstream);
        if (int er = inflate(&infstream, Z_NO_FLUSH) != Z_STREAM_END){
            printf("Error!\n");
        }
        inflateEnd(&infstream);
        printf("OUT:%d %ld\n", infstream.avail_out, area);
        for (long i = 0; i < 1000; i+=4){
            printf("%x %x %x\n",this->picture[i], this->picture[i+1], this->picture[i+2]);
        }


    }
    printf("B:%x\n",bsize);
    printf("%c%c%c%c\n",chunk[0], chunk[1], chunk[2], chunk[3]);
}

From the above, I had succeed in getting RGB value with this image image#1 However, when I tried with this imageimage2 (the same image but cropped), the program was only able to return the first 3 bytes of the RGB data (while the rest are zeroes). Was there some thing I implemented incorrectly with this code?

Upvotes: 0

Views: 542

Answers (1)

Mark Adler
Mark Adler

Reputation: 112339

You are not interpreting the result correctly. The reason you have a +this->height is that each line of the image is preceded by a filter byte. For the image that "succeeded", the filter bytes are all zero, indicating no filtering. That means that the pixel values appear directly in the decompressed data. Even in that case, you are displaying the incorrect bytes for the pixel values, since you are not looking at and moving past the filter byte.

For the image that did not "succeed", the first line uses filter 1, and the subsequent lines use filter 2. Filter 1 is the Sub filter, indicating that preceding pixel values after the first have subtracted from them the previous pixel value. 2 indicates the Up filter, where each pixel value after the first row has the previous row's pixel value in that position subtracted from it. That's why nearly all (but not all) of the values after the first are zero. Look at the result more carefully.

Upvotes: 1

Related Questions