Thread: Problem reading BMP

  1. #1
    Registered User Mortissus's Avatar
    Join Date
    Dec 2004
    Location
    Brazil, Porto Alegre
    Posts
    152

    Problem reading BMP

    Hi! I was trying to read, manipulate and save some BMP files. My program simlpy reads a bmp file then prints the size of the file (present inside the bmp file itself). The problem is that the size remains zero. I have used xxd to see the first bytes of the file and the size is there, and it is different from zero.

    Here is the code of bitmap.h:
    Code:
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>
    #include <fcntl.h>
    
    #define BMP_TYPE 19778
    
    typedef struct{
        unsigned short type;                 /* Magic identifier            */
        unsigned int size;                       /* File size in bytes          */
        unsigned short int reserved1, reserved2;
        unsigned int offset;                     /* Offset to image data, bytes */
        unsigned int hsize;               /* Header size in bytes      */
        int width,height;                /* Width and height of image */
        unsigned short int planes;       /* Number of colour planes   */
        unsigned short int bits;         /* Bits per pixel            */
        unsigned int compression;        /* Compression type          */
        unsigned int imagesize;          /* Image size in bytes       */
        int xresolution,yresolution;     /* Pixels per meter          */
        unsigned int ncolours;           /* Number of colours         */
        unsigned int importantcolours;   /* Important colours         */
    } HEADER;
    
    typedef struct{
        HEADER header;
        char* data;
    } BMP;
    
    
    void loadFile( char* fileName, BMP** bmp  );
    void writeFile( char* fileName, BMP* bmp  );
    And here is the code of bitmap.c:

    Code:
    #include "bitmap.h"
    
    void loadFile( char* filenName, BMP** pbmp  ){
    
        struct stat stat_file; 
        FILE* file; 
        BMP* bmp;
    
        // recover file statistics
        stat( filenName, &stat_file );
    
        *pbmp = (BMP*) malloc( stat_file.st_size );
        if( !(*pbmp) ){
    	fprintf(stderr, "It was not possible to allocate some memory.\n");
    	exit(1);
        }
        
        // to ease manipulation
        bmp = *pbmp;
    
        // open bitmap file
        file = fopen( filenName, "r" );
    
        // read all the file into pbmp structure
        fread( bmp, 1, stat_file.st_size, file );
        
        // we must convert from little-endian to big-endian
        // the error is here, inspection of the file 
        // shows that size is non zero
        //  printf("File size is %d\n", ntohl(bmp->header.size) );
        printf("File size is %d\n",  (((char*)bmp)[2]));
    }
    
    void writeFile( char* filenName, BMP* bmp  ){
    
        struct stat stat_file;
    
        int file = open( filenName, O_RDWR );
        if( write( file, bmp, stat_file.st_size ) < stat_file.st_size ){
    	
    	perror( "Error saving file.\n" );
    	exit(1);
        }
    }
    Thanks any help!

  2. #2
    Registered /usr
    Join Date
    Aug 2001
    Location
    Newport, South Wales, UK
    Posts
    1,273
    Mam-ma mi-a! You-a crazziee keeed!

    First, look at this page, where Microsoft has kindly illustrated the structure of a BMP file. Note in particular the presence of an unspecified number of RGBQUAD structures. This is where palette information is stored, and thus where your code's gonna fall over in all sorts of personally amusing ways when you actually get round to testing this thing.

    Unfortunately you can't just pack x RGBQUADs into the massive struct you've defined, so you're gonna have to do yourself a favour and stick a little bit closer to the way MS do it, i.e.
    Code:
    typedef struct{
        HEADER header;
        RGBQUAD *palette;
        char* data;
    } BMP;
    Then determine how many colours are in the palette by examining "bits" and knowing that 8 means 256 RGBQUADs, 4 means 16, etc.
    Code:
    *pbmp = (BMP*) malloc( stat_file.st_size );
    NEIN! Das ist nicht casten sie malloc im C!
    Code:
    file = fopen( filenName, "r" );
    It's not a text file. "rb" please.

    As for your so-called endian problem, your bizarre little cast at the end of that printf statement there serves no one and nothing. Also ntohl converts from network byte order (which if you read the spec is big endian) to host byte order, which for you would appear to be... you guessed it, big endian!.

    If you really want to change the byte order, try this cheap little function for size:-
    Code:
    void SwapDword(unsigned long *ulIn)
    {
        unsigned char *ucIn, ucTemp;
    
        ucIn = (unsigned char *)ulIn;
        ucTemp = *ucIn;
        *ucIn = *(ucIn + 3);
        *(ucIn + 3) = ucTemp;
        ucTemp = *(ucIn + 1);
        *(ucIn + 1) = *(ucIn + 2);
        *(ucIn + 2) = ucTemp;
    }
    Bon appetit.

  3. #3
    Registered User Mortissus's Avatar
    Join Date
    Dec 2004
    Location
    Brazil, Porto Alegre
    Posts
    152
    First, thanks for all answers. Two answers does not appear here, but I have read them in my e-mail. Second, I have changed the open statment to open the file as binary. Also, I have removed the ntohl (i swear it worked). I didnt added the RGBQUAD structure yet, SMurf, since my problem is before I reach this point. I am sorry SMurf, but I dont speak german, what is wrong with my malloc? Just in case you were speaking about the *pbmp, it is because the functions receives a BMP**. Sorry my bad english. After all these modifications I still have the same problem, the size (second field of header) is zero.

    Thanks again.

  4. #4
    old man
    Join Date
    Dec 2005
    Posts
    90
    I also don't think that your struct will work very well ... for example, you're inviting padding problems with shorts intermixed in the header. Depending on your compiler, the struct will very possibly lose sync with your data.

    As for malloc, it makes no sense to cast it to anything, since it only cares about the size argument, and your pointer is already declared -- your pointer is the only tool that matters when it come to accessing the data.

    Here's an example of getting that bmp size field. It solves your problem with endianness (which was likely your main problem all along).

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>
    
    
    void
    die (char *s)
    {
      perror (s);
      printf ("\n");
      exit (EXIT_FAILURE);
    }
    
    
    unsigned int
    get_dw (char *b)
    {
      return ((((((b[3]  << 8) 
                 | b[2]) << 8) 
                 | b[1]) << 8)
                 | b[0]);
    }
    
    
    void
    load_bmp (char *fn)
    {
      struct stat fs;  char *buf;  FILE *fp;
    
      if ((stat (fn, &fs)) == -1)
        die ("stat");
    
      buf = malloc (fs.st_size);
      if (buf == NULL)
        die ("malloc");
    
      fp = fopen (fn, "rb");
      if (fp == NULL)
        die ("fopen");
    
      fread (buf, 1, fs.st_size, fp);
      if (buf[0] != 'B' || buf[1] != 'M')
        printf ("This seems not to be a bmp file ...\n\n");
      else
        printf ("bmp size: %u bytes\n\n", get_dw (buf+2));
    
      fclose (fp);
      free (buf);
    }
    
    
    int
    main (int argc, char **argv)
    {
      if (argc != 2)
      {
        printf ("I need a (single) bmp file ...\n\n");
        exit (EXIT_FAILURE);
      }
    
      load_bmp (argv[1]);
      exit (EXIT_SUCCESS);
    }
    When you get further along with this, of course you'll want to access parts of the file with structs ... all you have to do is assign to the struct pointer the address of the appropriate part in the buffer. This will work better than the monolithic struct you're using now.
    Last edited by eerok; 02-01-2006 at 12:55 AM.

  5. #5
    Registered User Mortissus's Avatar
    Join Date
    Dec 2004
    Location
    Brazil, Porto Alegre
    Posts
    152
    It worked! I understand my errors. I was just trying to do the work in a lazy way.

    Thanks a lot for the help, I will respect more these details (i have work ed with c++ for 4 years, but I never worked with shorts ints and such).

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 22
    Last Post: 12-23-2008, 01:53 PM
  2. Replies: 20
    Last Post: 06-12-2005, 11:53 PM
  3. Problem with reading strings
    By goron350 in forum C++ Programming
    Replies: 8
    Last Post: 06-05-2005, 12:05 AM
  4. Replies: 6
    Last Post: 05-12-2005, 03:39 AM