Thread: Loading a bitmap

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Registered User
    Join Date
    Feb 2006
    Posts
    71

    Loading a bitmap

    I'm trying to load a bitmap and getting weird results when checking what I've loaded into my structures. First I'll show my code:

    Code:
    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    
    using namespace std;
    
    typedef struct
    {
        unsigned short bfType;
        unsigned int bfSize;
        unsigned short bfReserve1;
        unsigned short bfReserve2;
        unsigned int bfOffBits;
    }FILEHEADER;
    
    typedef struct
    {
        unsigned int biSize;
        unsigned int biWidth;
        unsigned int biHeight;
        unsigned short biPlanes;
        unsigned short biBitCount;
        unsigned int biCompression;
        unsigned int biSizeImage;
        unsigned int biXPelsPerMeter;
        unsigned int biYPelsPerMeter;
        unsigned int biClrUsed;
        unsigned int biClrImportant;
    }INFOHEADER;
    
    unsigned char* LoadBitmap(const char* filename, INFOHEADER* bInfo)
    {
        FILEHEADER fHeader;
        unsigned char* bitmap;
        unsigned char aColors[256];
        int infosize = 0;
        FILE* fp;
        
        fp = fopen(filename,"rb");
        
        if(fp == NULL)
        {
            cout << "Couldn't open file!";
            return NULL;
        }
        
        if(fread(&fHeader,sizeof(FILEHEADER),1,fp) < 1)
        {
            cout << "Couldn't read in fileheader!";
            fclose(fp);
            return NULL;
        }
        
        if(fHeader.bfType != 19778)
        {
            cout << "Not a bmp file!";
            fclose(fp);
            return NULL;
        }
        
        if(fread(bInfo,sizeof(INFOHEADER),1,fp) < 1)
        {
            cout << "Couldn't read in infoheader!";
            fclose(fp);
            return NULL;
        }
        
        fseek(fp,fHeader.bfOffBits,SEEK_SET);
        bitmap = (unsigned char*)malloc(bInfo->biSizeImage);
        
        if(!bitmap)
        {
           cout << "Couldn't allocate bitmap memory";
           free(bitmap);
           fclose(fp);
           return NULL;
        }
        
        /*if(fread(bitmap,bInfo->biSizeImage,1,fp)==NULL)
        {
            cout << "Unable to store bitmap!";
            fclose(fp);
            return NULL;
        }*/
        
        cout << bInfo->biSize << endl;
        cout << bInfo->biCompression << endl;
        cout << fHeader.bfType << endl;
        cout << fHeader.bfSize << endl;
        cout << fHeader.bfReserve1 << endl;
        cout << fHeader.bfReserve2 << endl;
        cout << fHeader.bfOffBits << endl;
        return NULL;
    }
    
    int main(void)
    {
        INFOHEADER bmpInfo;
        unsigned char* bitmap;
        
        bitmap = LoadBitmap("C:\\Dev-Cpp\\File IO Projects\\AstralAbraxas.bmp",&bmpInfo);
        cin.get();
        
        return 0;
    }
    All the numbers that I check besides "bfType" are completely way off. Then I realized that 19778 was "BM" backwards, so I thought maybe all the numbers are messed up because of the bytes being stored in it are backwards.... Well I went and took the numbers and converted them to hex and tried reversing the bytes and switching back to decimal to get the right numbers and it didn't work. So I'm pretty much clueless to what I'm doing wrong.

    I'm not really familiar with fread fseek etc either. I never done a whole lot of file i/o before.

  2. #2
    Registered User
    Join Date
    Feb 2006
    Posts
    71
    I figured it out. Turned out by fileheader and infoheader were both 2 bytes too big for some reason, so I changed

    fread(&fHeader,sizeof(FILEHEADER),1,fp) to fread(&fHeader,sizeof(FILEHEADER)-2,1,fp)

    etc and it fixed it x.x

  3. #3
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    I suspect this is actually the alignment inside the struct, which causes the size to be too large. You probably will find that some of the data is incorrect inside the struct.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  4. #4
    Registered User
    Join Date
    Feb 2006
    Posts
    71
    Yeah, I think you're right. After having troubles loading in my bitmap data, I went and checked out what you were saying:

    Code:
        cout << fHeader.bfType << endl;
        cout << fHeader.bfSize << endl;
        cout << fHeader.bfReserve1 << endl;
        cout << fHeader.bfReserve2 << endl;
        cout << fHeader.bfOffBits << endl;
        cout << bInfo->biSize << endl;
        cout << bInfo->biWidth << endl;
        cout << bInfo->biHeight << endl;
        cout << bInfo->biPlanes << endl;
        cout << bInfo->biBitCount << endl;
        cout << bInfo->biCompression << endl;
        cout << bInfo->biSizeImage << endl;
        cout << bInfo->biXPelsPerMeter << endl;
        cout << bInfo->biYPelsPerMeter << endl;
        cout << bInfo->biClrUsed << endl;
        cout << bInfo->biClrImportant << endl;
    prints out:

    19778
    3
    0
    54
    4194304
    40
    224
    337
    1
    24
    0
    226464
    0
    0
    0
    63504384

    How do I fix this? Also, what's wrong with the way I was going to load my bitmap information?

  5. #5
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by Homunculus View Post
    I figured it out. Turned out by fileheader and infoheader were both 2 bytes too big for some reason, so I changed

    fread(&fHeader,sizeof(FILEHEADER),1,fp) to fread(&fHeader,sizeof(FILEHEADER)-2,1,fp)

    etc and it fixed it x.x
    This is why you should not use sizeof() on a struct to load packed binary data from a file. It ignores alignment and endian issues.

    You can't just imagine that the struct overlays perfectly on a section of file data. Things don't work that way.

  6. #6
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by brewbuck View Post
    This is why you should not use sizeof() on a struct to load packed binary data from a file. It ignores alignment and endian issues.

    You can't just imagine that the struct overlays perfectly on a section of file data. Things don't work that way.
    And removing sizeof() will solve exactly which part of the problem? I'm sure you mean the right thing, but sizeof() itself is not a culprit in this case - the struct itself holds 16 bit and 32 bit data intermixed in ways that cause the compiler to add padding - in the middle of the struct. So alignment is very much the issue here.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  7. #7
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by matsp View Post
    And removing sizeof() will solve exactly which part of the problem? I'm sure you mean the right thing, but sizeof() itself is not a culprit in this case - the struct itself holds 16 bit and 32 bit data intermixed in ways that cause the compiler to add padding - in the middle of the struct. So alignment is very much the issue here.

    --
    Mats
    I didn't say that sizeof() was the problem. I (implicitly) was saying that the whole concept of taking a struct, laying it out like some portion of a file format, then using sizeof() to compute its size and read it as a single chunk, is wrong.

    Further, if you follow the rule "never use sizeof() while reading a file format" then you eliminate a whole class of errors, like the aforementioned, as well as assuming a particular endianness and integer width. Without sizeof(), you are forced to read a single byte at a time, which forces you to deal with endianness and width from the very outset instead of ignoring them.
    Last edited by brewbuck; 11-05-2007 at 10:47 AM.

  8. #8
    Reverse Engineer maxorator's Avatar
    Join Date
    Aug 2005
    Location
    Estonia
    Posts
    2,318
    These are the structs provided by Microsoft.
    Code:
    typedef struct tagBITMAPFILEHEADER { 
      WORD    bfType; 
      DWORD   bfSize; 
      WORD    bfReserved1; 
      WORD    bfReserved2; 
      DWORD   bfOffBits; 
    } BITMAPFILEHEADER, *PBITMAPFILEHEADER;
    
    typedef struct tagBITMAPINFOHEADER{
      DWORD  biSize; 
      LONG   biWidth; 
      LONG   biHeight; 
      WORD   biPlanes; 
      WORD   biBitCount; 
      DWORD  biCompression; 
      DWORD  biSizeImage; 
      LONG   biXPelsPerMeter; 
      LONG   biYPelsPerMeter; 
      DWORD  biClrUsed; 
      DWORD  biClrImportant; 
    } BITMAPINFOHEADER, *PBITMAPINFOHEADER;
    Which equal:
    Code:
    typedef struct tagBITMAPFILEHEADER { 
      unsigned short bfType; 
      unsigned long  bfSize; 
      unsigned short bfReserved1; 
      unsigned short bfReserved2; 
      unsigned long  bfOffBits; 
    } BITMAPFILEHEADER, *PBITMAPFILEHEADER;
    
    typedef struct tagBITMAPINFOHEADER{
      unsigned long  biSize; 
      long           biWidth; 
      long           biHeight; 
      unsigned short biPlanes; 
      unsigned short biBitCount; 
      unsigned long  biCompression; 
      unsigned long  biSizeImage; 
      long           biXPelsPerMeter; 
      long           biYPelsPerMeter; 
      unsigned long  biClrUsed; 
      unsigned long  biClrImportant; 
    } BITMAPINFOHEADER, *PBITMAPINFOHEADER;
    I remember I have used these once to read bitmap files. Microsoft itself uses this way too...

    And GameDev has this as a tutorial too: http://www.gamedev.net/reference/art...rticle1966.asp
    Last edited by maxorator; 11-05-2007 at 12:08 PM.
    "The Internet treats censorship as damage and routes around it." - John Gilmore

  9. #9
    Reverse Engineer maxorator's Avatar
    Join Date
    Aug 2005
    Location
    Estonia
    Posts
    2,318
    Maybe add this line in front of the structure (not sure if it's standard):
    Code:
    #pragma pack(2)
    It is not very important, but MSDN says that biWidth, biHeight, biXPelsPerMeter and biYPelsPerMeter are signed integers...

    I'm not sure if it is true but in case if the program is compiled under x64 environment then int would be 8 bytes... maybe it would be better to use long as it is always 4 bytes.
    "The Internet treats censorship as damage and routes around it." - John Gilmore

  10. #10
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by maxorator View Post
    I'm not sure if it is true but in case if the program is compiled under x64 environment then int would be 8 bytes... maybe it would be better to use long as it is always 4 bytes.
    Ehm, it's the other way around. An int is 4 bytes in both 32 and 64 bit, but a long is 64-bit in a 64-bit architecture [although that is not guaranteed either].

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  11. #11
    Registered User
    Join Date
    Nov 2006
    Posts
    519
    I thought the size of int is defined as the width of the data bus, is that really wrong info?

  12. #12
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,666
    > maybe it would be better to use long as it is always 4 bytes.
    No, the guaranteed minimum is 4 bytes (assuming CHAR_BIT is 8). The standard specifies minimum data ranges for each type, but an implementation is free to choose larger ranges if it wants to.

    > I thought the size of int is defined as the width of the data bus, is that really wrong info?
    A couple of counter examples.
    - Your PC is still capable of running DOS compilers with their 16 bit ints, despite your processor having a 32 (or even 64) bit data bus. But back then when it was still an 8086, it would have been a 16 bit bus and a 16 bit processor.
    - The ill-fated Sinclair QL used a processor with an 8-bit bus but a 16/32-bit internal architecture.

    A slightly better heuristic is int is the same size as the processor arithmetic registers.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  13. #13
    Registered User
    Join Date
    Nov 2006
    Posts
    519
    Quote Originally Posted by Salem View Post
    >A slightly better heuristic is int is the same size as the processor arithmetic registers.
    Ok, thank you!

  14. #14
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by Salem View Post
    A slightly better heuristic is int is the same size as the processor arithmetic registers.
    But that doesn't work for 64-bit machines - many 64-bit systems have a int that is 32-bit, and a long that is 64-bit. Or both being 32-bit.

    But the language makes no guarantees about the size of a pointer.

    Note also that LONG and long aren't the same thing - the former is a "local symbol", e.g. something that Microsoft have designed, whilst long is part of the C language.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  15. #15
    Reverse Engineer maxorator's Avatar
    Join Date
    Aug 2005
    Location
    Estonia
    Posts
    2,318
    Then I wonder why MS uses LONG in their structures. :O
    "The Internet treats censorship as damage and routes around it." - John Gilmore

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Loading a bitmap (Without using glaux)
    By Shamino in forum Game Programming
    Replies: 7
    Last Post: 03-16-2006, 09:43 AM
  2. OpenGL -- Bitmaps
    By HQSneaker in forum Game Programming
    Replies: 14
    Last Post: 09-06-2004, 04:04 PM
  3. Loading a Bitmap resource for OpenGL Texture[x]
    By the dead tree in forum Game Programming
    Replies: 4
    Last Post: 08-26-2004, 01:12 PM
  4. Loading bitmap in dll
    By Mithoric in forum Windows Programming
    Replies: 2
    Last Post: 12-22-2003, 01:53 PM
  5. No one can seem to help me, loading bitmap
    By Shadow12345 in forum C++ Programming
    Replies: 7
    Last Post: 12-28-2002, 01:22 PM