Thread: Loading a bitmap

  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
    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

  6. #6
    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.

  7. #7
    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?

  8. #8
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    > 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.

  9. #9
    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

  10. #10
    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!

  11. #11
    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.

  12. #12
    Registered User
    Join Date
    Nov 2006
    Posts
    519
    So the size of the pod-types is always compiler-dependent?

  13. #13
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    > 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 is that more for transitional reasons to prevent breakage of existing code, and still claim "64-bit" on the box?

    Though with only 2 widths to play with (16 or 32), int had to be the same as either short or long. But with 16/32/64, the compiler writers now have the freedom to make distinct choices.
    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.

  14. #14
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by Salem View Post
    > 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 is that more for transitional reasons to prevent breakage of existing code, and still claim "64-bit" on the box?
    Not really. It's practical to have a basic integer type that takes "several million" as a count. If you want MUCH more than that, you have 64-bit "long" numbers. Obviously, choosing a 32-bit long would be a matter of "make the least amount of changes to the current software" approach. But a 32-bit "int" is perfectly adequate for almost any software. How often do you use "long long" or "__int64" [as appropriate] in your code?

    Though with only 2 widths to play with (16 or 32), int had to be the same as either short or long. But with 16/32/64, the compiler writers now have the freedom to make distinct choices.
    Exactly, a 32-bit int is "big enough for most things". It takes half the space of a 64-bit integer, and whilst RAM-space may not matter much when you have many gigabytes, it's still important to keep the caches filled with useful stuff, and if the upper 32 bits are always filled with either all ones or all zeros, it's a bit of a waste to stuff this in the cache - and caches, even the largest ones, is still only up to a few gigabytes.

    Edit: Aside from fixing a broken quote tag, I would like to add that Windows and Linux have taken a different approach to the above choices:
    In windows, a long is 32 bits. In Linux, a long is 64 bits.

    --
    Mats
    Last edited by matsp; 11-05-2007 at 07:25 AM.
    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
    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.

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