Problems with BMP{FileHeader, InfoHeader} structures - c

I want to load a bmp file into memory in C. I've found many different types of BmpFileHeader and BmpInfoHeader structures. The last one that I've taken it from msdn.microsoft.com but it still does not read properly from binary file. With test file lena
Image Size: -1076791624
Memory could not be allocated
What's the point I am missing? Thanks.
#include <stdio.h>
#include <stdlib.h>
typedef struct tagBITMAPFILEHEADER {
unsigned short bfType;
unsigned int bfSize;
short bfReserved1;
short bfReserved2;
unsigned int bfOffBits;
} BITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER {
unsigned int biSize;
int biWidth;
int biHeight;
short biPlanes;
short biBitCount;
unsigned int biCompression;
unsigned int biSizeImage;
int biXPelsPerMeter;
int biYPelsPerMeter;
unsigned int biClrUsed;
unsigned int biClrImportant;
} BITMAPINFOHEADER;
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("Usage: %s input.bmp\n", argv[0]);
exit(-1);
}
FILE *filePtr;
BITMAPFILEHEADER bitmapFileHeader;
BITMAPINFOHEADER bitmapInfoHeader;
unsigned char *bitmapImage;
int imageIdx=0;
unsigned char tempRGB;
filePtr = fopen(argv[1],"rb");
if (filePtr == NULL)
{
printf("File could not opened\n");
exit(-1);
}
//read the bitmap file header
fread(&bitmapFileHeader, sizeof(bitmapFileHeader), 1, filePtr);
if (bitmapFileHeader.bfType !=0x4D42)
{
fclose(filePtr);
printf("Not a bmp file\n");
exit(-1);
}
fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);
bitmapImage = (unsigned char*)malloc(sizeof(unsigned char)*bitmapInfoHeader.biSizeImage);
printf("Image Size: %d\n", bitmapInfoHeader.biSizeImage);
if (!bitmapImage)
{
free(bitmapImage);
fclose(filePtr);
printf("Memory could not be allocated\n");
exit(-1);
}
//swap the r and b values to get RGB (bitmap is BGR)
for (imageIdx = 0; imageIdx < bitmapInfoHeader.biSizeImage; imageIdx+=3)
{
tempRGB = bitmapImage[imageIdx];
bitmapImage[imageIdx] = bitmapImage[imageIdx + 2];
bitmapImage[imageIdx + 2] = tempRGB;
}
int i;
for(i = 0; i < bitmapInfoHeader.biSizeImage; i+=3){
printf("R: %c G: %c B: %c\n", bitmapImage[i], bitmapImage[i + 1], bitmapImage[i + 2]);
}
fclose(filePtr);
return 0;
}

The first thing that I would do is to find an authoritative statement on the byte layout of these headers and then, assuming that you want to continue to use the structs that you have defined, add some asserts on the field offsets, for example:
assert(offsetof(BITMAPFILEHEADER, bfType) == 0); // or whatever
assert(offsetof(BITMAPFILEHEADER, bfSize) == 2); // or whatever
assert(offsetof(BITMAPFILEHEADER, bfOffBits) == 10); // or whatever
etc.
Chances are that your use of unsigned int, short, etc plus your compiler options (especially structure packing) might mean that your struct fields do not match the actual BMP file data (in offset, and maybe in length).

Your bitmapInfoHeader is uninitialized!
That is, you read the bitmapFileHeader, and then set the file pointer to where the actual bitmap data should be. But you didn't read the bitmapInfoHeader, which contains all the information about the bitmap (dimensions, bpp and etc.)
P.S.
Next time please try do debug your code befure posting the question.

There exist several versions of the BMP file format and most of them differ in the size of the headers. If you want to be able to read all valid BMP files, you'll need to support them all. The lena image seems to be a Windows V3 version.
There's a good description of the different versions in Wikipedia.
Furthermore, I don't see no endianness handling in your code. If you're machine isn't little endian, it will not work.
Finally, the alignment of the struct members is a problem. Your compiler will most likely align the bfSize member at offset 4 but it should be at offset 2. (See the Wikipedia article for a solution.)

Related

save bmp file using c on linux

I tried to open bmp file and save it using c code. My code worked well on window compiler(visual studio) but it didn't work on linux using gcc. Even though the format of final output is bmp, it shows like empty file(white). I'm a beginner for using linux and c programming so I'm not sure what's wrong with this happening!
I compiled it using gcc: gcc bmp.c -o bmp.
the code below is what I used.
#define _CRT_SECURE_NO_WARNINGS
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#define WIDTHBYTES(bits) (((bits)+31)/32*4)
#pragma pack(push, 1)
typedef unsigned short WORD;
typedef unsigned long DWORD;
typedef long LONG;
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER {
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
typedef struct tagRGBQUAD {
unsigned char rgbBlue;
unsigned char rgbGreen;
unsigned char rgbRed;
unsigned char rgbReserved;
} RGBQUAD;
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
} BITMAPINFO;
int main(int argc, char* argv[]){
FILE *infile;
FILE *outfile;
BITMAPFILEHEADER hf;
BITMAPINFOHEADER hInfo;
RGBQUAD hRGBpal[256];
unsigned char *lpImg;
int i, j, pos, rwsize;
int nBitCount = 1;
infile = fopen("image.bmp", "rb");
if (infile == NULL)
{
printf("no image");
return 1;
}
fread(&hf, sizeof(BITMAPFILEHEADER), 1, infile);
if (hf.bfType != 0x4d42)
{
printf("ERROR\n");
fclose(infile);
exit(1);
}
fread(&hInfo, sizeof(BITMAPINFOHEADER), 1, infile);
if (hInfo.biBitCount == 8 || hInfo.biBitCount == 16 ||
hInfo.biBitCount == 24 || hInfo.biBitCount == 32)
{
nBitCount = hInfo.biBitCount / 8;
if (hInfo.biBitCount == 8)
{
pos = hf.bfOffBits - hf.bfSize - hInfo.biSize;
if (pos > 0)
{
fread((unsigned char *)hRGBpal, sizeof(unsigned char), pos, infile);
}
}
lpImg = (unsigned char*)malloc(hInfo.biSizeImage);
fseek(infile, hf.bfOffBits, SEEK_SET);
fread(lpImg, sizeof(unsigned char), hInfo.biSizeImage, infile);
fclose(infile);
}
rwsize = WIDTHBYTES(hInfo.biBitCount*hInfo.biWidth);
for (i = 0; i < hInfo.biHeight; i++)
{
for (j = 0; j < hInfo.biWidth; j++)
{
lpImg[i*rwsize + j];
}
}
outfile = fopen("out.bmp", "wb")
if (hInfo.biBitCount == 8)
{
hf.bfOffBits = sizeof(BITMAPFILEHEADER)
+ sizeof(BITMAPINFOHEADER) + sizeof(hRGBpal);
}
fwrite(&hf, sizeof(char), sizeof(BITMAPFILEHEADER), outfile);
fwrite(&hInfo, sizeof(char), sizeof(BITMAPINFOHEADER), outfile);
if (hInfo.biBitCount == 8)
{
fwrite(hRGBpal, sizeof(RGBQUAD), 256, outfile);
}
fwrite(lpImg, sizeof(unsigned char), hInfo.biSizeImage, outfile);
fclose(outfile);
if (lpImg)
free(lpImg);
printf("DONE!");}
This probably goes wrong here:
typedef unsigned short WORD;
typedef unsigned long DWORD;
typedef long LONG;
If you're on 64bit Linux, long has 64 bits. On 64bit Windows, it has only 32bits. For portable code, use fixed-size integers:
typedef uint16_t WORD;
typedef uint32_t DWORD;
typedef int32_t LONG;
On a further note, there's no need to introduce these ugly (my opinion here) winapi typedefs at all. A (sloppy) version of the bitmap file header I'm using (still relying on the "packed struct" feature and merging both structs into one) looks like this:
#pragma pack(push)
#pragma pack(1)
struct bmphdr
{
uint16_t bfType;
uint32_t bfSize;
uint32_t bfReserved;
uint32_t bfOffBits;
uint32_t biSize;
uint32_t biWidth;
int32_t biHeight;
uint16_t biPlanes;
uint16_t biBitCount;
uint32_t biCompression;
uint32_t biSizeImage;
int32_t biXPelsPerMeter;
int32_t biYPelsPerMeter;
uint32_t biClrUsed;
uint32_t biClrImportant;
};
#pragma pack(pop)

Reading and writing BMP files in C memory error

I am attempting to write a program to invert the color of an image. My problem is when I try to read from the DIB header in my BMP file.
When I try to get the file header size and use fread(dib.fileheader, 4,1,fp); . I get an error that says "The memory could not be written". I have attached it for further clarification.
Here is my code:
#include <stdio.h>
#include <string.h>
struct BMP {
char filetype[2]; // must be BM, must check if BM
unsigned int filesize;
short reserved1;
short reserved2;
unsigned int dataoffset;
};
struct DIB {
unsigned int fileheader;
unsigned int headersize;
int width;
int height;
short planes;
short bitsperpixel; /* we only support the value 24 here */
unsigned int compression; /* we do not support compression */
unsigned int bitmapsize;
int horizontalres;
int verticalres;
unsigned int numcolors;
unsigned int importantcolors
};
struct pixel {
int val;
char * def;
struct listitem * next;
};
void invertImage(char fileName[]){
struct BMP bmp;
struct DIB dib;
FILE *fp = fopen(fileName, "rb");
//FileType
fread(bmp.filetype, 1,2,fp);
printf("Value is %c\n", bmp.filetype[1]);
//Check if file format is BM
if(bmp.filetype[0] != 'B' && bmp.filetype[1] !='M'){
printf("Wrong format");
}
//Size of the file in bytes
fread(bmp.filesize, 4,1,fp);
printf("Value is %d\n", bmp.filesize);
//Go to dataoffset
fseek(fp,10,SEEK_CUR);
fread(bmp.dataoffset, 4,1,fp);
printf("Offset is %d\n", bmp.dataoffset);
fread(dib.fileheader, 4,1,fp);
printf("File header is %d bytes\n", dib.fileheader);
fclose(fp);
}
int main(int argc, char *argv[] ){
printf("Program name %s\n", argv[0]);
if( strcmp(argv[1],"-invert") == 0) {
printf("Invert\n");
printf("File name is %s\n", argv[2] );
invertImage(argv[2]);
}
else {
printf("Greyscale\n");
//greyScaleImage();
}
return 0;
}
Also here is the output to my program.
fread expects a pointer to memory to write the file contents. So you need to give it the address of dib.fileheader
fread( &dib.fileheader, sizeof(dib.fileheader),1,fp );
printf("File header is %d bytes\n", dib.fileheader);
It's complaining because it's interpreting whatever is in dib.fileheader as the address to write to, which may or may not be valid for your program.
Note that the following is ok as it stands because bmp.filetype is already a pointer to char - it's a char[]
fread(bmp.filetype, 1,2,fp);
printf("Value is %c\n", bmp.filetype[1]);
To make it a bit more general, you could write
fread(bmp.filetype, sizeof(bmp.filetype), 1, fp);
printf("Value is %c\n", bmp.filetype[1]);
I tried to compile your code. You must make some change in your code.
fread(bmp.filesize, 4,1,fp); --- > fread(&bmp.filesize, 4,1,fp);
fread(bmp.dataoffset, 4,1,fp); --- > fread(&bmp.dataoffset, 4,1,fp);
fread(dib.fileheader, 4,1,fp); --- > fread(&dib.fileheader, 4,1,fp);
declare the function : void invertImage(char fileName[]);

Bitmap color - C

I still cannot access the color bits from a bitmap image. The problem is that, after saving the bitmap's content into a buffer, I do not know:
Where to start the loop (If i start from 0 i think it will erase the headers)?
How to access the bytes and make the changes (transform a color from the BMP into a desired one from the output)?
And, how to insert the buffer into a new bitmap file?
All the images I want to modify have rows which are divisible by 4 (I have to insert 0 when a specific byte is padding) and 24 bits per pixel. Even few tips would be much appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "bmp_header.h"
int main(void)
{
FILE *f;
f = fopen("captcha.bmp","rb");
if ((f = fopen("captcha.bmp", "rb")) == NULL)
{
printf("Error opening file %s.\n", "captcha.bmp");
exit(1);
}
fread(&BMP_header,sizeof(BMP_header),1,f);
fread(&BMP_info_header,sizeof(BMP_info_header),1,f);
fseek(f,BMP_header.imageDataOffset,SEEK_SET);
int rows = (BMP_info_header.bitPix * BMP_info_header.width + 31 ) /32 * 4 ;
char *PixelArray =malloc( rows * abs(BMP_info_header.height)*sizeof(char));
int i;
for( i =sizeof(BMP_header)+sizeof(BMP_info_header); i<=(rows * abs(BMP_info_header.height))-2;i+=3)
{
PixelArray[i]=255; // just a random value to test if this makese any sense
PixelArray[i+1]=255;
PixelArray[i+2]=255;
}
return 0;
}
And, here is bmp_header.h's content:
#pragma pack(1)
struct bmp_fileheader
{
unsigned char fileMarker1; /* 'B' */
unsigned char fileMarker2; /* 'M' */
unsigned int bfSize; /* File's size */
unsigned short unused1;
unsigned short unused2;
unsigned int imageDataOffset; /* Offset to the start of image data */
}BMP_header,BMP_header_out;
struct bmp_infoheader
{
unsigned int biSize; /* Size of the info header - 40 bytes */
signed int width; /* Width of the image */
signed int height; /* Height of the image */
unsigned short planes;
unsigned short bitPix;
unsigned int biCompression;
unsigned int biSizeImage; /* Size of the image data */
int biXPelsPerMeter;
int biYPelsPerMeter;
unsigned int biClrUsed;
unsigned int biClrImportant;
}BMP_info_header,BMP_info_header_out;
#pragma pack()
Alright, I wrote some code to write out a completely black bitmap file with the original dimensions of the old bitmap file.
This will not work with all types of bitmaps. 16-color bitmaps, for instance, have a color palette after the info header that my program does not account for. The bits per pixel also needs to be divisible by 8. If those preconditions are met, as I believe they are in 24-bit bitmaps, then this program should work.
The main code here is in getNewImageData. We calculate the row size for the image with the same formula that Wikipedia uses - it calculates the required bits and then pads that to a multiple of four bytes and then converts bits to bytes. Then we set all of the pixel array memory to zero (mostly me being paranoid about leaving values in the pad bytes). Then we run along each row and edit each pixel. The innermost for loop corresponds to each pixel. One iteration through the middle for loop writes to one pixel.
You can obviously modify this code to read the pixel data into a malloced section of memory and then edit the pixel data in place before writing it back out. This sample code does not read the input pixel data and just writes out a black bitmap of the same dimensions as the input file.
Edit: I guess I should mention what you were doing wrong.
You called fopen twice, leaving an open file pointer hanging somewhere in memory.
rows should probably be renamed to rowSize since it's the size of a row of pixels in bytes
PixelArray is not being freed.
Your for loop starts from an offset (the size of the two headers) and then is bounded by the size of the pixel data. The for loop should start from 0 and go to the size of the pixel data (unless you are reading pixel data in as you do this... which you probably shouldn't do).
Your for loop tries to write to all of the pixel data at once and doesn't account for the fact that there is anywhere between 0 and 31 bits of padding on the end of each row of pixels (my program assumes the padding is only 0, 8, 16, or 24 bits).
Here is the code:
#include <stdio.h>
#include "bmp_header.h"
#include <stdlib.h>
int readBitmapHeaders(char* fileLocation, bmp_fileheader* fileheader, bmp_infoheader* infoheader)
{
FILE* f;
f = fopen(fileLocation, "rb");
if (!f)
{
printf("Error opening file %s.\n", fileLocation);
return 1;
}
fread(fileheader, sizeof(bmp_fileheader), 1, f);
fread(infoheader, sizeof(bmp_infoheader), 1, f);
fclose(f);
return 0;
}
int writeBitmap(char* fileName, bmp_fileheader* fileheader, bmp_infoheader* infoheader, char* pixelArray, size_t pixelArraySize)
{
FILE* out;
out = fopen(fileName, "wb");
if (!out)
{
printf("Error opening file %s.\n", fileName);
return 1;
}
fwrite(fileheader, sizeof(bmp_fileheader), 1, out);
fwrite(infoheader, sizeof(bmp_infoheader), 1, out);
fwrite(pixelArray, pixelArraySize, 1, out);
fclose(out);
return 0;
}
char* getNewImageData(bmp_infoheader* infoheader, size_t* imageSize)
{
//rowsize is padded to 4 bytes
size_t rowSize = (infoheader->bitPix * infoheader->width + 31) / 32 * 4;
size_t pixelArraySize = rowSize * abs(infoheader->height);
char* pixelArray = (char*)malloc(pixelArraySize);
if (!pixelArray)
{
return NULL;
}
memset(pixelArray, 0, pixelArraySize);
size_t bytesPerPixel = infoheader->bitPix / 8;
for (int i = 0; i < infoheader->height; i++)
{
for (int j = 0; j < infoheader->width; j++)
{
size_t offset = rowSize * i + bytesPerPixel * j;
for (size_t k = 0; k < bytesPerPixel; k++)
{
pixelArray[offset + k] = 0;
}
}
}
if (imageSize)
{
*imageSize = pixelArraySize;
}
return pixelArray;
}
int main()
{
char* fileLocation = "test.bmp";
bmp_fileheader header;
bmp_infoheader infoheader;
int readResult = readBitmapHeaders(fileLocation, &header, &infoheader);
if (readResult)
{
return readResult;
}
size_t pixelArraySize;
char* pixelArray = getNewImageData(&infoheader, &pixelArraySize);
if (!pixelArray)
{
printf("%s", "Failed to create the new image data. Exiting with fatal error.\n");
return 1;
}
char* outFile = "out.bmp";
int writeResult = writeBitmap(outFile, &header, &infoheader, pixelArray, pixelArraySize);
free(pixelArray);
return writeResult;
}
I changed the bitmap header file a little to typedef the structs and make life easier (at least for me):
#pragma once
#pragma pack(1)
typedef struct _bmp_fileheader
{
unsigned char fileMarker1; /* 'B' */
unsigned char fileMarker2; /* 'M' */
unsigned int bfSize; /* File's size */
unsigned short unused1;
unsigned short unused2;
unsigned int imageDataOffset; /* Offset to the start of image data */
} bmp_fileheader;
typedef struct _bmp_infoheader
{
unsigned int biSize; /* Size of the info header - 40 bytes */
signed int width; /* Width of the image */
signed int height; /* Height of the image */
unsigned short planes;
unsigned short bitPix;
unsigned int biCompression;
unsigned int biSizeImage; /* Size of the image data */
int biXPelsPerMeter;
int biYPelsPerMeter;
unsigned int biClrUsed;
unsigned int biClrImportant;
} bmp_infoheader;
#pragma pack()

Creating bmp file in C

I am trying to create .bmp file (filled with one colour for testing purposes).
Here is code that I'm using:
#include <stdio.h>
#define BI_RGB 0
typedef unsigned int UINT;
typedef unsigned long DWORD;
typedef long int LONG;
typedef unsigned short WORD;
typedef unsigned char BYTE;
typedef struct tagBITMAPFILEHEADER {
UINT bfType;
DWORD bfSize;
UINT bfReserved1;
UINT bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER {
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
typedef struct COLORREF_RGB
{
BYTE cRed;
BYTE cGreen;
BYTE cBlue;
}COLORREF_RGB;
int main(int argc, char const *argv[])
{
BITMAPINFOHEADER bih;
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biWidth = 600;
bih.biHeight = 600;
bih.biSizeImage = bih.biWidth * bih.biHeight * 3;
bih.biPlanes = 1;
bih.biBitCount = 24;
bih.biCompression = BI_RGB;
bih.biXPelsPerMeter = 2835;
bih.biYPelsPerMeter = 2835;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
COLORREF_RGB rgb;
rgb.cRed = 0;
rgb.cGreen = 0;
rgb.cBlue = 0;
BITMAPFILEHEADER bfh;
bfh.bfType = 0x424D;
bfh.bfReserved1 = 0;
bfh.bfReserved2 = 0;
bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + bih.biSize;
bfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
bih.biWidth * bih.biHeight * 4;
FILE *f;
f = fopen("test.bmp","wb");
fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, f);
fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, f);
int i,j;
for(i = 0; i < bih.biHeight; i++)
{
for(j = 0; j < bih.biWidth; j++)
{
fwrite(&rgb,sizeof(COLORREF_RGB),1,f);
}
}
fclose(f);
return 0;
}
and jet every time I compile and run it I get error saying that its not valid BMP image. I double checked all values in multiple references and still can't find error here.
Did I misunderstood something and what am I doing wrong here?
Also not sure if important but I am using Ubuntu 14.04 to compile this.
EDIT
Found one more issue :
bfh.bfType = 0x424D;
should be
bfh.bfType = 0x4D42;
But still can't see image.
First of all, you set:
bfSize Specifies the size of the file, in bytes.
to invalid value, your code resulted into 1440112 while size of file is actually 1080112
bfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
bih.biWidth * bih.biHeight * sizeof(COLORREF_RGB);
Because sizeof(COLORREF_RGB) is actually 4 not 3.
Another mistake is that size of your structs and types:
expected size actual size*
typedef unsigned int UINT; // 2 4
typedef unsigned long DWORD; // 4 8
typedef long int LONG; // 4 8
typedef unsigned short WORD; // 2 2
typedef unsigned char BYTE; // 1 1
* I'm using gcc on x86_64 architecture
Your offsets just don't match with offsets on wikipedia, reference you are using was probably written for 16 bit compiler on 16 bit OS (as pointed out by cup in a comment) so it assumes int to be 2B type.
Using values from stdint.h worked for me (guide on stdint.h for Visual Studio here):
#include <stdint.h>
typedef uint16_t UINT;
typedef uint32_t DWORD;
typedef int32_t LONG;
typedef uint16_t WORD;
typedef uint8_t BYTE;
And last but not least you have to turn off memory alignment as suggested by Weather Vane.
I believe your field sizes to be incorrect, try this
#pragma pack(push, 1)
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
#pragma pack(pop)
You are trying to map a C structure to some externally-defined binary format There are a number of problems with this:
Size
typedef unsigned int UINT;
typedef unsigned long DWORD;
typedef long int LONG;
typedef unsigned short WORD;
typedef unsigned char BYTE;
The C language only specify the minimum sizes of these types. Their actual sizes can and do vary between different compilers and operating systems. The sizes and offset of the members in your structures may not be what you expect. The only size that you can rely on (on almost any system that you are likely to encounter these days) is char being 8 bits.
Padding
typedef struct tagBITMAPFILEHEADER {
UINT bfType;
DWORD bfSize;
UINT bfReserved1;
UINT bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
It is common for DWORD to be twice as large as a UINT, and in fact your program depends on it. This usually means that the compiler will introduce padding between bfType and bfSize to give the latter appropriate alignment for its type. The .bmp format has no such padding.
Order
BITMAPFILEHEADER bfh;
bfh.bfType = 0x424D;
...
fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, f);
Another problem is that the C language does not specify the endianess of the types. The .bfType member may be stored as [42][4D] (Big-Endian) or [4D][42] (Little-Endian) in memory. The .bmp format specifically requires the values to be stored in Little-Endian order.
Solution
You might be able to solve some of these problems by using compiler-specific extensions (such as #pragma's or compiler switches), but probably not all of them.
The only way to properly write an externally-defined binary format, is to use an array of unsigned char and write the values a byte at a time. Personally, I would write a set of helper functions for specific types:
void w32BE (unsigned char *p, unsigned long ul)
{
p[0] = (ul >> 24) & 0xff;
p[1] = (ul >> 16) & 0xff;
p[2] = (ul >> 8) & 0xff;
p[3] = (ul ) & 0xff;
}
void w32LE (unsigned char *p, unsigned long ul)
{
p[0] = (ul ) & 0xff;
p[1] = (ul >> 8) & 0xff;
p[2] = (ul >> 16) & 0xff;
p[3] = (ul >> 24) & 0xff;
}
/* And so on */
and some functions for writing the entire .bmp file or sections of it:
int function write_header (FILE *f, BITMAPFILEHEADER bfh)
{
unsigned char buf[14];
w16LE (buf , bfh.bfType);
w32LE (buf+ 2, bfh.bfSize);
w16LE (buf+ 6, bfh.bfReserved1);
w16LE (buf+ 8, bfh.bfReserved2);
w32LE (buf+10, bfh.bfOffBits);
return fwrite (buf, sizeof buf, 1, f);
}

I get segmentation fault reading infoheader on a BMP using fread. How do I fix this please?

This is got me pretty stuck, how do I fix this? I know I haven't got error checking, but they aren't required i'd guess since it's restricted to my desktop. It obveously can't be EOF. It's for the infoheader struct, fileheader works fine. Do i need to take a new line or something?
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
unsigned char fileMarker1; /* 'B' */
unsigned char fileMarker2; /* 'M' */
unsigned int bfSize;
unsigned short unused1;
unsigned short unused2;
unsigned int imageDataOffset; /* Offset to the start of image data */
}FILEHEADER;
typedef struct
{
unsigned int biSize;
int width; /* Width of the image */
int height; /* Height of the image */
unsigned short planes;
unsigned short bitPix;
unsigned int biCompression;
unsigned int biSizeImage;
int biXPelsPerMeter;
int biYPelsPerMeter;
unsigned int biClrUsed;
unsigned int biClrImportant;
}INFOHEADER;
typedef struct
{
unsigned char b; /* Blue value */
unsigned char g; /* Green value */
unsigned char r; /* Red value */
}IMAGECOMPONENT;
int fileheadfunc(FILE *image);
int infoheadfunc(FILE *image);
int main( int argc, char *argv[] )
{
char *filename; /* *threshholdInput = argv[2]; */
FILE *image;
int filehead, infohead;
filename = argv[1];
/* int threshhold = atoi(threshholdInput); */
if (argc != 2)
{
printf(" Incorrect Number Of Command Line Arguments\n");
return(0);
}
image = fopen( filename, "r");
if (image == NULL)
{
fprintf(stderr, "Error, cannot find file %s\n", filename);
exit(1);
}
filehead = fileheadfunc(image);
infohead = infoheadfunc(image);
fclose(image);
return(0);
}
int fileheadfunc(FILE *image)
{
FILEHEADER *header;
long pos;
fseek (image , 0 , SEEK_SET);
fread( (unsigned char*)header, sizeof(FILEHEADER), 1, image );
if ( (*header).fileMarker1 != 'B' || (*header).fileMarker2 != 'M' )
{
fprintf(stderr, "Incorrect file format");
exit(1);
}
printf("This is a bitmap!\n");
pos = ftell(image);
printf("%ld\n", pos);
printf("%zu\n", sizeof(FILEHEADER));
return(0);
}
int infoheadfunc(FILE *image)
{
INFOHEADER *iheader;
fseek (image, 0, SEEK_CUR );
fread( (unsigned int*)iheader, sizeof(INFOHEADER), 1, image );
printf("Width: %i\n", (*iheader).width);
printf("Height: %i\n", (*iheader).height);
return(0);
}
You're not actually allocating any storage for the BMP header data structures, e.g. you need to change this:
int fileheadfunc(FILE *image)
{
FILEHEADER *header;
long pos;
fseek(image, 0, SEEK_SET);
fread((unsigned char*)header, sizeof(FILEHEADER), 1, image);
...
to this:
int fileheadfunc(FILE *image)
{
FILEHEADER header; // <<<
long pos;
fseek(image, 0, SEEK_SET);
fread(&header, sizeof(FILEHEADER), 1, image); // <<<
...
Also, as previously noted in one of the comments above, you need #pragma pack(1) (or equivalent if you're not using gcc or a gcc-compatible compiler) prior to your struct definitions to eliminate unwanted padding. (NB: use #pragma pack() after your struct definitions to restore normal struct padding/alignment.)
There are two problems with the code:
Alignment
For performance reasons the compiler will, unless instructed to do otherwise, arrange struct fields on its "natural boundaries", effectively leaving uninitialised gaps between byte-size fields. Add
#pragma pack(1)
before the struct definitions and you should be fine. It's also easy to test: just print out the struct size without and with pragma pack in place, and you'll see the difference.
Allocation
As Paul R already said, you should allocate space for the headers, not just provide a pointer to the structures. The fact that fileheadfunc works is a coincidence, there just wasn't anything in the way that got smashed when data got written outside of the allocated space.
A last one, just for prevention sake: should you ever want to return the read structures to the calling program, do not just return a pointer to the structure allocated in the function as that will cause problems similat to the unallocated variables you have now. Allocate them in the calling function, and pass a pointer to that variable to the header read functions.
EDIT clarification regarding the last point:
DON'T
FILEHEADER * fileheadfunc(FILE *image)
{
FILEHEADER header;
...
return &header; // returns an address on the function stack that will
// disappear once you return
}
DO
int fileheadfunc(FILE *image, FILEHEADER *header)
{
...
}
which will be called like this
...
FILEHEADER header;
returnvalue = fileheaderfunc(imagefile,&header);
EDIT2: just noticed that the way you read the DIB header is not correct. There are several variations of that header, with different sizes. So after reading the file header you first need to read 4 bytes into an unsigned int and based on the value read select the correct DIB header structure to use (don't forget you already read its first field!) or tell the user you encountered an unsupported file format.

Resources