I would like to generate a BMP file from RGB values that I have stored already.
I'm programming on OS X so I can't use the predefined BMP headers.
I've tried doing the below but preview says that the file is corrupted.
void bitmap(Image * image) {
typedef struct /**** BMP file header structure ****/
{
unsigned short bfType; /* Magic number for file */
unsigned int bfSize; /* Size of file */
unsigned short bfReserved1; /* Reserved */
unsigned short bfReserved2; /* ... */
unsigned int bfOffBits; /* Offset to bitmap data */
} BITMAPFILEHEADER;
typedef struct /**** BMP file info structure ****/
{
unsigned int biSize; /* Size of info header */
int biWidth; /* Width of image */
int biHeight; /* Height of image */
unsigned short biPlanes; /* Number of color planes */
unsigned short biBitCount; /* Number of bits per pixel */
unsigned int biCompression; /* Type of compression to use */
unsigned int biSizeImage; /* Size of image data */
int biXPelsPerMeter; /* X pixels per meter */
int biYPelsPerMeter; /* Y pixels per meter */
unsigned int biClrUsed; /* Number of colors used */
unsigned int biClrImportant; /* Number of important colors */
} BITMAPINFOHEADER;
BITMAPFILEHEADER bfh;
BITMAPINFOHEADER bih;
bfh.bfType = 0x4d42;
bfh.bfReserved1 = 0;
bfh.bfReserved2 = 0;
bfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bfh.bfOffBits = 0x36;
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biWidth = image->getWidth();
bih.biHeight = image->getHeight();
bih.biPlanes = 1;
bih.biBitCount = 24;
bih.biCompression = 0;
bih.biSizeImage = 0;
bih.biXPelsPerMeter = 0x0ec4;
bih.biYPelsPerMeter = 0x0ec4;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
FILE *file = fopen("a.bmp", "wb");
if (!file) {
cout << "File not found";
return;
}
fwrite(&bfh, 1, sizeof(bfh), file);
fwrite(&bih, 1, sizeof(bfh), file);
for (int x = 0; x < image->getWidth(); x++) {
for (int y = 0; y < image->getHeight(); y++) {
float r = image->getPixel(x, y).r;
float g = image->getPixel(x, y).g;
float b = image->getPixel(x, y).b;
fwrite(&r, 1, 1, file);
fwrite(&g, 1, 1, file);
fwrite(&b, 1, 1, file);
}
}
}
I'm not sure that I understand the structure correctly. I've tried reading about it but I must be missing something.
Here is the hex output of the file
42 4D 00 02 38 00 00 00 00 00 00 00 36 00 00 00 28 00 00 00 80 02 00 00 E0 01 00 00 01 00 18 00 00 00 00 00 00 00 00 00 C4 0E 00 00 C4 0E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0 0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
The question is good because it enlightens some pitfalls. Here are all issues in your code. By fixing them, everything works as expected:
Your bfSize field does not include the size of the bitmap
General: The BITMAPFILEHEADER
becomes to large, since it begins with a 2 byte value followed by a 4 byte value. Padding rules say that this struct the first field will be 4 bytes instead of 2. Solution: Write the magic number separately by excluding it from BITMAPFILEHEADER
:
unsigned short magic=0x4d42; //This field is _not_ included in `BITMAPFILEHEADER` fwrite(&magic,1,sizeof(magic),file); // Write the remaining part of the header (if you did not get an I/O error...)
This modification also implies that BITMAPFILEHEADER::bfSize
is 2+sizeof(BITMAPFILEHADER) + sizeof(BITMAPINFOHEADER)+ biWidth*biHeight*3
You have also passed sizeof(bfh)
for booth bfh
and bih
, so the complete BITMAPINFOHEADER
is never written
The BMP file format requires that each scanline is DWORD-aligned so depending on the width, you may need to write zero bytes after each scanline
If you think the first scanline is up, your image is upside down, since your height is positive.
Bitmap files are stored row -wise, so you should loop over x -coordinates in the innermost loop
You have also write the wrong pixel values. You should write unsigned char
instead of float. You write the correct size (1 byte), but it will not be the correct value:
unsigned char r = image->getPixel(x, y).r/255; //If 1.0f is white.
Also, bitmaps are BGR(A) and not RGB(A).
Assuming you have a nice width, a working example is
void bitmap()
{
typedef struct /**** BMP file header structure ****/
{
unsigned int bfSize; /* Size of file */
unsigned short bfReserved1; /* Reserved */
unsigned short bfReserved2; /* ... */
unsigned int bfOffBits; /* Offset to bitmap data */
} BITMAPFILEHEADER;
typedef struct /**** BMP file info structure ****/
{
unsigned int biSize; /* Size of info header */
int biWidth; /* Width of image */
int biHeight; /* Height of image */
unsigned short biPlanes; /* Number of color planes */
unsigned short biBitCount; /* Number of bits per pixel */
unsigned int biCompression; /* Type of compression to use */
unsigned int biSizeImage; /* Size of image data */
int biXPelsPerMeter; /* X pixels per meter */
int biYPelsPerMeter; /* Y pixels per meter */
unsigned int biClrUsed; /* Number of colors used */
unsigned int biClrImportant; /* Number of important colors */
} BITMAPINFOHEADER;
BITMAPFILEHEADER bfh;
BITMAPINFOHEADER bih;
/* Magic number for file. It does not fit in the header structure due to alignment requirements, so put it outside */
unsigned short bfType=0x4d42;
bfh.bfReserved1 = 0;
bfh.bfReserved2 = 0;
bfh.bfSize = 2+sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)+640*480*3;
bfh.bfOffBits = 0x36;
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biWidth = 640;
bih.biHeight = 480;
bih.biPlanes = 1;
bih.biBitCount = 24;
bih.biCompression = 0;
bih.biSizeImage = 0;
bih.biXPelsPerMeter = 5000;
bih.biYPelsPerMeter = 5000;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
FILE *file = fopen("a.bmp", "wb");
if (!file)
{
printf("Could not write file\n");
return;
}
/*Write headers*/
fwrite(&bfType,1,sizeof(bfType),file);
fwrite(&bfh, 1, sizeof(bfh), file);
fwrite(&bih, 1, sizeof(bih), file);
/*Write bitmap*/
for (int y = bih.biHeight-1; y>=0; y--) /*Scanline loop backwards*/
{
for (int x = 0; x < bih.biWidth; x++) /*Column loop forwards*/
{
/*compute some pixel values*/
unsigned char r = 255*((float)x/bih.biWidth);
unsigned char g = 255*((float)y/bih.biHeight);
unsigned char b = 0;
fwrite(&b, 1, 1, file);
fwrite(&g, 1, 1, file);
fwrite(&r, 1, 1, file);
}
}
fclose(file);
}
user877329's Code worked for me. (Just some minor changes because im using C) Note: Just pay attention, to the 'size' of the Variables, because it can be different, depending on which platform you Compile.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.