Skip to content

Instantly share code, notes, and snippets.

@NHollmann

NHollmann/bmp.c

Created Apr 1, 2021
Embed
What would you like to do?
Windows Bitmap
// Windows Bitmap in C example.
// Compile with: gcc bmp.c -o bmp
// Tutorial: https://nicolashollmann.de/blog/writing-bmp-files/
#include <stdio.h>
#include <stdint.h>
#define WIDTH 300
#define HEIGHT 200
// This changes the padding behaviour of the
// compiler. I come to this later.
#pragma pack(push, 1)
typedef struct {
uint16_t bfType;
uint32_t bfSize;
uint16_t bfReserved1;
uint16_t bfReserved2;
uint32_t bfOffBits;
} BitmapFileHeader;
typedef struct {
uint32_t biSize;
int32_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;
} BitmapInfoHeader;
// This restores the padding behaviour.
#pragma pack(pop)
int main()
{
FILE* outfile = fopen("out.bmp", "wb");
// Both headers have a combined size of 54 bytes.
// We need WIDTH * HEIGHT pixels and each of them needs
// three bytes (one for each channel).
uint32_t filesize = 54 + WIDTH * HEIGHT * 3;
// First we fill the file header.
BitmapFileHeader fileHeader;
fileHeader.bfType = 'B' | 'M' << 8; // reversed because of Little Endian
fileHeader.bfSize = filesize;
fileHeader.bfReserved1 = 0;
fileHeader.bfReserved2 = 0;
fileHeader.bfOffBits = 54; // both headers have a size of 54 byte
// Next we fill the info header.
BitmapInfoHeader infoHeader;
infoHeader.biSize = 40; // this header is 40 bytes long
infoHeader.biWidth = WIDTH;
infoHeader.biHeight = HEIGHT;
infoHeader.biPlanes = 1; // must be 1
infoHeader.biBitCount = 24; // 24 bit per pixel = 1 byte per channel
infoHeader.biCompression = 0; // no compression
infoHeader.biSizeImage = 0; // 0 because we don't use compression
infoHeader.biXPelsPerMeter = 0;
infoHeader.biYPelsPerMeter = 0;
infoHeader.biClrUsed = 0;
infoHeader.biClrImportant = 0;
// Then we write them both to the file.
// This only works because of the padding changes
// and ignores endianess. Normaly you should NOT
// do this, but this simplifices this example.
fwrite(&fileHeader, sizeof(BitmapFileHeader), 1, outfile);
fwrite(&infoHeader, sizeof(BitmapInfoHeader), 1, outfile);
// We write the image from bottom to top.
// In this example the image data is created
// procedurally, so we really don't need to do it
// this way. But if you would read the image data
// from an array, you would use the following loops:
for (int y = HEIGHT - 1; y >= 0; y--)
{
for (int x = 0; x < WIDTH; x++)
{
// Image data creation:
int r = (x / (float)WIDTH) * 255;
int g = (y / (float)WIDTH) * 255;
int b = (y / (float)HEIGHT) * 255;
// We write a single pixel in BGR order
unsigned char colors[3] = {b, g, r};
fwrite(colors, 3, 1, outfile);
}
}
fclose(outfile);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment