3

I am trying to create a method for a class that simply reads the PNG file up to the end of the IHDR Image header (without its CRC32 chunk. The trouble comes with every "more than one byte" integers (i.e IHDR data chunk length, width and height). Here is my code:

#include <iostream>
#include <fstream>
using namespace std;

typedef struct PNG_HEADER pngHeader;
struct PNG_HEADER{
    unsigned char PNGSignature[8];
    size_t nb;
    unsigned char ImageHeader[4];
    size_t width;
    size_t height;
    unsigned char bitDepth;
    unsigned char colorType;
    unsigned char compressionMethod;
    unsigned char filterMethod;
    unsigned char interlaceMethod;
};

class testPNG{

public:

    bool readPNGHeader(string filename){
        pngHeader PNGheader;

        ifstream file(filename.data(), std::ios_base::binary);

        if(!file.is_open())
            return false;

        if( !file.read((char *)&PNGheader, sizeof(PNGheader)))
            return false;


        for(int i = 0; i < 8; i++)
                printf("%d ", PNGheader.PNGSignature[i]);

        printf("\n");
        printf("%d\n", PNGheader.nb);

        for(int i = 0; i < 4; i++)
                printf("%d ", PNGheader.ImageHeader[i]);

        printf("\n");
        printf("%d\n", PNGheader.width );
        printf("%d\n", PNGheader.height );
        printf("%d\n", PNGheader.bitDepth );
        printf("%d\n", PNGheader.colorType );
        printf("%d\n", PNGheader.compressionMethod );
        printf("%d\n", PNGheader.filterMethod );
        printf("%d\n", PNGheader.interlaceMethod );

        return true;
    }
};


int main(void)
{
    testPNG test;
    test.readPNGHeader("test.png");

    return 0;
}

And the printed result is this (comments not shown on the console obviously):

137 80 78 71 13 10 26 10 //[PNG Signature OK!][1]
218103808                //this should read 13 as the length is the sum of the number of byte needed for each data field contained in the IHDR Data chunk that follows the IHDR Image Header chunk.
73 72 68 82              //[IHDR Image Header chunk OK!][2]
1879244800               //fail to give the correct width
973078528                //fail to give the correct height
8                        // OK!
6                        // OK!
0                        // OK!
0                        // OK!
0                        // OK!

As it is written on the w3c website; the length value (of data chunk) is stored in "A four-byte unsigned integer" . The same goes with the width and height of the image. And so I tried unsigned int and unsigned short too but nothing seems to work.

Even though I used printfs (I don't know how to format chars as ints with cout), I am looking for a C++ solution if possible.

Thank you

Paiku Han
  • 581
  • 2
  • 16
  • 38

1 Answers1

4

Your machine, or your compiler, uses a reverse order to store multi-byte values in.

See "7.1 Integers and byte order" in that same reference:

All integers that require more than one byte shall be in network byte order ...

followed by a diagram that illustrates it.

To get the correct value for your number of bytes, reverse them using one of the predefined functions (of which I can never recall which one does what; please read How do you write (portably) reverse network byte order?) or write your own.

Your sample value 218103808 shows 0xD000000 when printed in hex; reversing the bytes produces the correct, expected result 0xD or 13.

Community
  • 1
  • 1
Jongware
  • 22,200
  • 8
  • 54
  • 100
  • I used the [gcc](http://stackoverflow.com/questions/105252/how-do-i-convert-between-big-endian-and-little-endian-values-in-c) though. because the one in the link you gave (if it does swap the bytes) give me the same result as the non-swapped bytes version. – Paiku Han Mar 14 '14 at 19:09
  • I tried reversing the byte order in a correct PNG and got the same bad result that you did: File: gray-wrong_endian.png (118 bytes) chunk IHDR at offset 0x0000c, length 218103808: EOF while reading data – Glenn Randers-Pehrson Mar 14 '14 at 19:39
  • @PaikuHan: any of the answers in my link should work as well. For such a simple task, I usually use the method in the accepted answer -- shifting bits around manually. It's barely worth writing a function for, a macro works fine. – Jongware Mar 14 '14 at 21:34
  • @Jongware well I tried the accepted answer and it didn't works... that's why I went for the gcc functions since I knew/know now that the trouble lies in the byte order. – Paiku Han Mar 15 '14 at 15:08
  • @GlennRanders-Pehrson without your code no one can tell exactly what went wrong. Check it the picture was read. what i've done was simple as replacing all my printfs with something like this printf("%d\n", __builtin_bswap32 (PNGheader.width) ); . It only works with gcc though – Paiku Han Mar 15 '14 at 15:09
  • 1
    @PaikuHan I'm not asking what went wrong with my code. I was simply agreeing with jongware that the problem is in your byte order, and validated my answer by swapping the byte order of a good file to get the same bad answer you got. There are macros/functions in libpng to read a long integer from a PNG datastream in the right byte order. See png_get_uint_32 in pngread.c (I know you aren't using libpng, but it's open source and you may copy the macros into your own code if you want). – Glenn Randers-Pehrson Mar 15 '14 at 15:31
  • @GlennRanders-Pehrson OK sorry I misunderstood. I am gonna check libpng's code though. I have the feeling it's gonna be an interesting reading. – Paiku Han Mar 15 '14 at 17:03