leandror
leandror

Reputation: 59

How to read BMP file in C?

I am trying to consult the basic information of a BMP. For that, I built a struct for the file header and another for the image header. Referring to a BMP table of values ​​(http://www.dragonwins.com/domains/getteched/bmp/bmpfileformat.htm), I read the values ​​and validated as specified. However, only the bfType is read correctly and other values ​​are filled in with wrong information. On my computer, sizeof(int) = 4

Structs:

typedef struct BmpFileHeader {
   char bfType[2];
   unsigned int bfSize;
   unsigned short int __bfReserved1;
   unsigned short int __bfReserved2;
   unsigned long int bfOffBits;
} BMPFILEHEADER;

typedef struct BmpImageHeader {
   unsigned int biSize;
   int biWidth;
   int biHeight;
   unsigned short int biPlanes;
   unsigned short int biBitCount;
   unsigned int biCompression;
   unsigned int biSizeImage;
   int biXPelsPerMeter;
   int biYPelPerMeter;
   unsigned int biClrUsed;
   unsigned int biClrImportant;  
} BMPIMAGEHEADER;

Print functions:

void printFileHeader(BMPFILEHEADER fileHeader) {
   printf("\nType: %c%c.\n", fileHeader.bfType[0],fileHeader.bfType[1]);
   printf("Size: %d.\n", fileHeader.bfSize);
   printf("Verify (Must be 0 0): %d %d.\n");
   printf("Offset : %d.\n", fileHeader.bfOffBits);
};

void printImageHeader(BMPIMAGEHEADER imageHeader) {
   printf("\nSize of header: %d.\n", imageHeader.biSize);
   printf("Width: %d.\n", imageHeader.biWidth);
   printf("Height: %d.\n", imageHeader.biHeight);
   printf("Color Planes: %d.\n", imageHeader.biPlanes);
   printf("Bits per Pixel: %d.\n", imageHeader.biBitCount);
   printf("Compression: %d.\n", imageHeader.biCompression);
   printf("Image size: %d.\n", imageHeader.biSizeImage);
   printf("Preferred resolution in pixels per meter (X-Y): %d-%d.\n", imageHeader.biXPelsPerMeter, imageHeader.biYPelPerMeter);
   printf("Number color map: %d.\n", imageHeader.biClrUsed);
   printf("Number of significant colors: %d.\n", imageHeader.biClrImportant);
}

Main function:

int main() {
   FILE *image;
   BMPFILEHEADER header;
   BMPIMAGEHEADER imageHeader;

   image = fopen("test.bmp", "rb");

   if(!image) {
      printf("Could not open the file %s.", "test.bmp");
      fclose(image);
      return 1;
   }

   fread(&header, sizeof(BMPFILEHEADER), 1,image);
   
   printf("File header information:");
   printFileHeader(header);

   if(header.bfType[0] != 'B' || header.bfType[1] != 'M') {
      printf("The file %s is not a valid BMP.", "test.bmp");
      return 1;
   }

   fread(&imageHeader, sizeof(BMPIMAGEHEADER), 1, image);
   
   printf("\nImage header information:");
   printImageHeader(imageHeader);

   if(imageHeader.biSize != 40 || imageHeader.biCompression != 0 || imageHeader.biBitCount != 24) {
      printf("The file %s is not a valid BMP.", "test.bmp");
      fclose(image);
      return 1;
   }

   fclose(image);
   return 0;
}

Upvotes: 1

Views: 3069

Answers (1)

Clifford
Clifford

Reputation: 93566

Structure packing and alignment padding are implementation defined, and byte order is platform defined.

If the byte order for your platform is the same as that defined for BMP (little-endian) then you can use whatever compiler extensions your toolchain supports for structure packing. For example in GCC:

typedef struct BmpFileHeader {
   char bfType[2];
   unsigned int bfSize;
   unsigned short int __bfReserved1;
   unsigned short int __bfReserved2;
   unsigned long int bfOffBits;
} __attribute__ ((packed)) BMPFILEHEADER; 

typedef struct BmpImageHeader {
   unsigned int biSize;
   int biWidth;
   int biHeight;
   unsigned short int biPlanes;
   unsigned short int biBitCount;
   unsigned int biCompression;
   unsigned int biSizeImage;
   int biXPelsPerMeter;
   int biYPelPerMeter;
   unsigned int biClrUsed;
   unsigned int biClrImportant;  
} __attribute__ ((packed)) BMPIMAGEHEADER;

Byte order for BMP is little-endian for for integer values; so for x86 and most ARM platforms you may not need to worry about byte-order. Pixel byte order is somewhat less straightforward.

However for a truly portable solution you would have to read the data byte-by-byte and load each member of the structure individually - so called deserialisation.

You would also do well to ensure compliance with the header structure by using stdint.h data types uint8_t, uint16_t, uint32_t, int32_t etc.

Upvotes: 3

Related Questions