I have a project that involves reading a .bmp file into a C program, putting a mask on it, and printing the version with the mask back to a different file. The part I'm having an issue with seems to be the actual process of reading in the file. The first big red flag I'm seeing is that it keeps reading in the wrong resolution. I've searched quite a bit and seen a few scripts to read in a .bmp file as answers to various questions here but using the logic from those scripts hasn't helped.
The primary issue seems to be that rather than reading in the proper dimensions of 200 x 300 on the example image given by my professor, it reads in 13107200 x 65536. However, if I were to include the part of the code that prints to a different file, you would see that the output file has the appropriate resolution. This tells me that I am likely reading in the information properly but not storing it in the way that I think I am.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct HEADER {
unsigned short int Type; // Magic indentifier
unsigned int Size; // File size in bytes
unsigned short int Reserved1, Reserved2;
unsigned int Offset; // Offset to data (in B)
} Header; // -- 14 Bytes
struct INFOHEADER {
unsigned int Size; // Header size in bytes
int Width, Height; // Width / 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 Colors; // Number of colors
unsigned int ImportantColors; // Important colors
} InfoHeader; // -- 40 Bytes
struct PIXEL {
unsigned char Red, Green, Blue; // Intensity of Red, Green, and Blue
}; // -- 3 Bytes
int getFileSize(FILE *input);
int getHexVal(FILE *input);
struct HEADER *getHeader(FILE *input);
struct INFOHEADER *getInfoHeader(FILE *input);
struct PIXEL *getPixel(FILE *input, struct PIXEL *loc);
struct HEADER *printHeader(FILE *output);
struct INFOHEADER *printInfoHeader(FILE *output);
struct PIXEL *printPixel(FILE *output, struct PIXEL *loc);
int main(int argc, char const *argv[]) {
if (argc == 3) {
if (!strcmp(argv[1], argv[2])) {
printf("The input and output file must be different. Please try again.\n");
return 1;
}
// char Matrix[3][3] =
// { { 0, -1, 0 },
// { -1, 4, -1 },
// { 0, -1, 0 }
// };
FILE *input = fopen(argv[1], "rb");
if (!input) return 1;
int i, j;
// getHeader(input);
fread(&Header, sizeof(struct HEADER), 1, input);
if (Header.Type != 0x4D42) {
printf("The specified input file was not a bitmap. Please try again.");
fclose(input);
return 1;
}
// getInfoHeader(input);
fread(&InfoHeader, sizeof(struct INFOHEADER), 1, input);
fseek(input, Header.Offset, SEEK_SET);
struct PIXEL arr[InfoHeader.Width][InfoHeader.Height];
printf("%d %d\n", InfoHeader.Width, InfoHeader.Height);
for (i = 0; i < InfoHeader.Width; i++) {
for (j = 0; j < InfoHeader.Height; j++) {
getPixel(input, arr[i] + j);
printf("%d %d %d\n", arr[i][j].Red, arr[i][j].Green, arr[i][j].Blue);
}
}
fclose(input);
}
}
I can see a lot of problems with your code:
1. Inconsistent sizes of data types
On different platforms, types like int and short can have different sizes. So, int might be one size on one platform, and another size on a different platform. You may need to use exact sized types like uint32_t.
2. Padding and alignment
The headers stored in the files are packed. Your structs are aligned. That means that the compiler inserts padding between members to ensure that members are always aligned for optimal memory access.
There are a variety of ways of dealing with this. You could declare your structs to be packed. That would get you so far, but see the next point.
3. Endianness
If you are reading a Windows bitmap on a big endian system, you have convert from little endian data in the file to big endian data for your system.
4. xResolution, yResolution are the wrong members
These are meant to indicate a physical size of the pixels. In practice they are seldom specified. You meant to read Width and Height.
5. The VLA (gah!)
You are using a variable length array: struct PIXEL arr[InfoHeader.xResolution][InfoHeader.yResolution]. That is liable to lead to stack overflows for large bitmaps. You really need to use dynamically allocated memory for the pixel array.
How would I deal with these issues?
malloc.The types int and short and so on are only guaranteed to have certain minimum sizes. They can vary on different implementations. Even if we assume that an int and short is four and two octets respectively, you will still run into problems when reading and writing your structures.
For example:
struct HEADER {
unsigned short int Type;
unsigned int Size;
unsigned short int Reserved1, Reserved2;
unsigned int Offset;
} Header;
In order to make Size suitably aligned for the processor, the compiler will (typically) insert padding between Type and Size which places Size at offset +4 instead of +2 (assuming the sizes mentioned above).
The best way to read (and write) binary formats is to read the file into an unsigned char * buffer, and then extract the fields from there. Eg.
unsigned long Size = buffer[2] +
buffer[3] * 0x100UL +
buffer[4] * 0x10000UL +
buffer[5] * 0x1000000UL;
or similar.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With