-
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! :)
-
Mam-ma mi-a! You-a crazziee keeed! :eek:
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! :mad:
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.
-
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.
-
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.
-
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).