0

I am trying to read a bitmap (.bmp) image header into a struct in c.

typedef unsigned short WORD;
typedef unsigned long DWORD;

typedef struct _BITMAPFILEHEADER {
    WORD Type;
    DWORD Size;
    WORD Reserved1;
    WORD Reserved2;
    DWORD OffBits;
} BITMAPFILEHEADER;

My code to read the bitmap file

FILE *fp;
BITMAPFILEHEADER header;

fp = fopen(file,"rb");
if (fp == NULL) {
    printf("cannot open file!\n");
    return 1;
}

fread(&header, sizeof(BITMAPFILEHEADER), 1, fp);

printf("Type: %02x\n", header.Type);
printf("Size: %04x\n", header.Size);
printf("Reserved: %02x\n", header.Reserved1);
printf("Reserved: %02x\n", header.Reserved2);
printf("Offset: %04x\n", header.OffBits);

What everything should equal:
Type: 0x424d
Size: 0x00060436
Reserved1: 0x00
Reserved2: 0x00
Offset: 0x00000436

What is actually happning (printf output):
Type: 0x424d
Size: 0x0006
Reserved: 0x002
Reserved: 0x436
Offset: 0x280000

(my os is 32-bit ubuntu if that helps)

melpomene
  • 84,125
  • 8
  • 85
  • 148
irishpatrick
  • 137
  • 1
  • 13
  • 1
    If you need fixed width integers, use the ones provided by the standard. Don't use homebrew types. Also read abolut `struct` layout and how to properly (de)serialise/marshall an octet stream. – too honest for this site Jun 12 '16 at 21:37
  • 5
    http://c-faq.com/struct/io.html, http://c-faq.com/struct/padding.html – melpomene Jun 12 '16 at 21:48
  • 2
    How was the data written to the file? You need to mimic the way that it was written when reading the data. If the structure has padding (it almost certainly does) but the data was written piecemeal (as if there was no padding — it almost certainly was), then the data cannot be read as you've done it. You'd need to read each element in turn in a separate `fread()` operation — and you should check that each `fread()` is successful before using what you think it returned. – Jonathan Leffler Jun 12 '16 at 22:13
  • It would help to show the contents of the file. I'd guess from your output that the file actually starts `4D 42 36 04 06 00 00 00` – M.M Jun 13 '16 at 02:33
  • The standard bit map header file needs to be "packed". – chux - Reinstate Monica Jun 13 '16 at 04:46

1 Answers1

0

When reading this kind of files I think a good approach is to first read them as a stream of bytes (unsigned char) and then, if and when needed, to interpret part of the file contents according to the proper data types.

In your case, for instance, I would define the BITMAPFILEHEADER struct like this:

typedef struct _BITMAPFILEHEADER {
    unsigned char   Type[ 2 ];
    unsigned int    Size;          // Size of the BMP file in bytes 
    unsigned char   Reserved1[ 2 ];
    unsigned char   Reserved2[ 2 ];
    unsigned int    OffBits;       // Starting address of the pixel array 
} BITMAPFILEHEADER;

Then I would read the header contents as a block of 14 bytes and, at last, I would go on to fill the BITMAPFILEHEADER struct properly.

Below you can find a simple program that reads the header of a BMP file and prints it onto the screen.

int main
    (
    )
    {
    // Defines a few variables
    FILE*           fp   =  0;
    unsigned char   h[ 14 ];

    // Opens the BMP file
    fp   =   fopen( "img.bmp", "rb" );

    if ( fp == NULL )
        {
        printf( "Cannot open file\n" );
        return -1;
        }

    // Reads the BMP header (14 bytes)
    fread( h, 14, 1, fp );

    // Prints the header contents
    printf( "Type:     %02x%02x\n", h[ 0 ], h[ 1 ] );
    printf( "Size:     %02x%02x%02x%02x\n", h[ 2 ], h[ 3 ], h[ 4 ], h[ 5 ] );
    printf( "Reserved: %02x%02x\n", h[ 6 ], h[ 7 ] );
    printf( "Reserved: %02x%02x\n", h[ 8 ], h[ 9 ] );
    printf( "Offset:   %02x%02x%02x%02x\n", h[ 10 ], h[ 11 ], h[ 12 ], h[ 13 ] );

    return 0;
    }

NOTE 1 - Padding: From the BMP file format specification, we know that the header is 14-byte long while a printf( "%d", sizeof(BITMAPFILEHEADER) ) will show a different (greater!) number due to padding (see comments to your question).

NOTE 2 - Endiannes: When writing 2 or 4 bytes into a short or long respectively, you have to take into account endiannes. This means that you have to know how values are written into the file (as for the Bitmap header, they are represented using little endian notation) and how they are treated by your machine (probably in little endian notation).

mw215
  • 323
  • 1
  • 8
  • 1
    Assuming you meant `unsigned char Type[2];`, your struct definition doesn't really help as there will still be padding before `Size`. It would be better to read an unsigned char buffer of the expected size, as you do in your sample program. But your program has endianness issues. – M.M Jun 13 '16 at 02:34
  • 1
    BMP files are always on little-endian afaik. – rici Jun 13 '16 at 02:50
  • @M.M Of course I meant `unsigned char Type[2];`. Just an oversight - as you can see I used the correct syntax when defining the `h` array in `main`. I edited my answer accordingly; thank you very much for pointing it out! – mw215 Jun 13 '16 at 10:12
  • @M.M I never meant to use my struct as the target of a `fread`, as you correctly noticed, so padding is not a problem. Not knowing the purpose of the OP’s code, my struct was meant to be as similar as possible to the original one while showing it is a reasonable choice to interpret bytes [2-5] and bytes [10-13] as unsigned integers - since they are a size and an offset, respectively - while leaving the rest as a sequence of raw bytes. As for endianness, I clearly prints the BMP header field by field as a sequence of raw bytes - just as the OP seems to do – but then I added NOTE 2. – mw215 Jun 13 '16 at 11:28
  • @rici You're right ([Bitmap file header](https://en.wikipedia.org/wiki/BMP_file_format#Bitmap_file_header)). I’ve just added that further detail to my answer. – mw215 Jun 13 '16 at 11:47
  • SO what is the point of your struct? The only reason to read a bitmap file is to use the integer values in the header; if you want to use a structure to hold those values nicely then the struct would contain integers, not char arrays. – M.M Jun 13 '16 at 12:07
  • @M.M As I wrote above, I don't know the purpose of the OP’s code. I just tried to provide a hopefully 'better' version of his struct for whatever purpose he had in mind. – mw215 Jun 13 '16 at 13:22