What Is BMP?
BMP (Bitmap, also called DIB β Device Independent Bitmap) is a raster image format developed by Microsoft for Windows in 1987. It is one of the simplest image formats: minimal metadata overhead, no complex compression algorithms (by default), and a straightforward binary layout that maps directly to how modern GPUs and CPUs store pixel data in memory.
Despite being largely overshadowed by PNG and JPEG for file exchange, BMP remains ubiquitous: Windows stores wallpapers, cursors, and system resources as BMP; it is the standard format for Windows Clipboard bitmaps; and it serves as a reliable intermediate format for image processing pipelines where compression artifacts must be avoided.
File Structure
A BMP file has three main sections:
1. File Header (BITMAPFILEHEADER β 14 bytes)
Offset Size Field
0 2 Signature: "BM" (0x42 0x4D)
2 4 FileSize: total file size in bytes
6 2 Reserved1: always 0
8 2 Reserved2: always 0
10 4 DataOffset: byte offset to pixel data from start of file
The "BM" signature (0x4D42 in little-endian) is the universal BMP identifier. Other historical variants exist: "BA" (OS/2 bitmap array), "CI" (color icon), "CP" (color pointer), "IC" (icon), "PT" (pointer) β but these are extremely rare.
2. DIB Header
BMP supports several DIB (Device Independent Bitmap) header variants, distinguished by their size at byte offset 14:
| Header Name | Size | Windows Version | Notes |
|---|---|---|---|
| BITMAPCOREHEADER | 12 bytes | Windows 2.x / OS/2 1.x | Ancient; 16-bit width/height |
| BITMAPINFOHEADER | 40 bytes | Windows 3.x (1990) | Most common; RGB + RLE |
| BITMAPV4HEADER | 108 bytes | Windows 95 | Adds color space, ICC intent |
| BITMAPV5HEADER | 124 bytes | Windows 98 / 2000 | Adds ICC profile data |
The BITMAPINFOHEADER (40 bytes) is by far the most common:
Offset Size Field
14 4 HeaderSize: 40
18 4 Width: image width in pixels (signed 32-bit)
22 4 Height: image height in pixels (signed 32-bit, negative = top-down)
26 2 ColorPlanes: always 1
28 2 BitsPerPixel: 1, 4, 8, 16, 24, or 32
30 4 Compression: 0=BI_RGB, 1=BI_RLE8, 2=BI_RLE4, 3=BI_BITFIELDS
34 4 ImageSize: raw size of pixel data (0 allowed for BI_RGB)
38 4 XPixelsPerMeter: horizontal resolution
42 4 YPixelsPerMeter: vertical resolution
46 4 ColorsUsed: entries in color table (0 = use max for bit depth)
50 4 ColorsImportant: minimum colors for display (0 = all)
Negative Height: When height is negative, the image is stored top-down (first row in file = top row of image). When height is positive, the image is stored bottom-up (first row in file = bottom row of image). Bottom-up is the BMP default and the most common layout β this surprises many developers who assume raster formats are always top-down.
3. Pixel Data
Pixel data begins at the offset specified in the File Header. Each row (scan line) is padded to a 4-byte boundary:
Row stride = ceil(Width Γ BitsPerPixel / 8 / 4) Γ 4
For a 100-pixel wide 24-bit BMP: ceil(100 Γ 24 / 8 / 4) Γ 4 = ceil(300/4) Γ 4 = 300 bytes (already aligned). For a 101-pixel wide 24-bit BMP: ceil(303/4) Γ 4 = 304 bytes (3 bytes of padding per row).
Color Depths
BMP supports multiple bit depths:
1-bit (Monochrome)
2 colors from a 2-entry color table. Each byte stores 8 pixels. Used for fax images, document scanning, and printer bitmaps.
4-bit (16 colors)
Each byte stores 2 pixels (high nibble = left pixel, low nibble = right pixel). Requires a 16-entry color table. Windows 3.1 standard graphics palette.
8-bit (256 colors)
Each byte stores 1 pixel index into a 256-entry color table. Supports RLE8 compression. Classic VGA palette; still used for Windows icons and indexed graphics.
16-bit
Each 2-byte word represents one pixel. Default (BI_RGB): 5 bits red, 5 bits green, 5 bits blue (RGB555, 1 bit unused). With BI_BITFIELDS: color masks specify exact bit layout β common are RGB565 (5r/6g/5b) and ARGB1555.
24-bit (True Color)
3 bytes per pixel: Blue, Green, Red (note: BGR order, not RGB). The most common BMP format. No color table needed. Stores 16.7 million colors without compression.
Pixel data: [B][G][R][B][G][R]...
Blue then Green then Red β opposite of RGB convention
The BGR byte order is a historical artifact of little-endian x86 architecture β it maps directly to a 32-bit integer in little-endian byte order without bit manipulation.
32-bit (True Color + Alpha)
4 bytes per pixel: Blue, Green, Red, Alpha. Requires BITMAPV4HEADER or BITMAPV5HEADER, or BI_BITFIELDS to specify the alpha channel mask. Windows GDI+ uses 32-bit BGRA bitmaps natively. Premultiplied alpha variants also exist (PBGRA).
Compression Modes
BI_RGB (0) β No Compression
Raw pixel data, padded to 4-byte row boundaries. Default and most compatible.
BI_RLE8 (1) β Run-Length Encoding for 8-bit
Two-byte pairs: [count][value] for runs, or [0][n][pixel1]...[pixelN] for absolute mode. Effective for images with large uniform areas. Escape codes: [0][0] = end of line, [0][1] = end of bitmap, [0][2][dx][dy] = delta (jump).
BI_RLE4 (2) β Run-Length Encoding for 4-bit
Similar to RLE8 but for 4-bit indexed images. Each data byte holds two pixel indices.
BI_BITFIELDS (3) β Bit Field Masks
Not a compression method β specifies 32-bit color masks for 16-bit and 32-bit BMPs. Immediately after the DIB header, three 4-byte masks (or four with BITMAPV4HEADER) define the bit layout for red, green, blue, and alpha channels.
BI_JPEG (4) and BI_PNG (5)
Defined in the spec but extremely rare. Allows embedding JPEG or PNG data within a BMP container β supported only by specific GDI+ contexts, not general-purpose image viewers.
The Color Table
For bit depths β€ 8, a color table (palette) follows the DIB header. Each entry is a RGBQUAD structure:
struct RGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved; // must be 0
};
The ColorsUsed field in the DIB header specifies how many entries are present. For 8-bit BMPs with full 256-color palette: 256 Γ 4 = 1,024 bytes of color table.
Reading and Writing BMP
Python (Pillow)
from PIL import Image
# Read BMP
img = Image.open('photo.bmp')
print(img.size, img.mode) # (width, height), 'RGB' or 'L' or 'RGBA'
# Convert to BMP from any format
img = Image.open('photo.jpg')
img.save('photo.bmp')
# Save as 8-bit indexed BMP
img_indexed = img.convert('P') # 8-bit palette
img_indexed.save('photo_8bit.bmp')
C (manual parsing)
#pragma pack(push, 1)
typedef struct {
uint16_t signature; // 0x4D42 = "BM"
uint32_t fileSize;
uint16_t reserved1;
uint16_t reserved2;
uint32_t dataOffset;
} BITMAPFILEHEADER;
typedef struct {
uint32_t headerSize; // 40 for BITMAPINFOHEADER
int32_t width;
int32_t height;
uint16_t planes;
uint16_t bitsPerPixel;
uint32_t compression;
uint32_t imageSize;
int32_t xPPM;
int32_t yPPM;
uint32_t colorsUsed;
uint32_t colorsImportant;
} BITMAPINFOHEADER;
#pragma pack(pop)
FFmpeg
# Extract BMP frames from video (one per second)
ffmpeg -i video.mp4 -vf fps=1 frame_%04d.bmp
# Convert PNG to 24-bit BMP
ffmpeg -i input.png output.bmp
# Convert 32-bit BMP (with alpha) to PNG
ffmpeg -i input.bmp output.png
ImageMagick
# Convert to BMP with specific bit depth
magick input.jpg -depth 8 output.bmp
# Convert to 24-bit BMP (no alpha)
magick input.png -flatten output.bmp
# Get BMP info
magick identify -verbose output.bmp | grep -E "Format|Geometry|Depth|Type"
BMP vs PNG vs TIFF
| Feature | BMP | PNG | TIFF |
|---|---|---|---|
| Compression | None (default) | Lossless (Deflate) | Optional (LZW, Deflate, none) |
| Typical size | Largest | ~60% of BMP | Similar to BMP or smaller |
| Alpha channel | 32-bit only | Yes (8 or 16-bit) | Yes |
| Color depths | 1, 4, 8, 16, 24, 32-bit | 1β16 bit per channel | 1β32 bit per channel |
| Metadata | Minimal | tEXt/iTXt chunks | Rich (XMP, EXIF, IPTC) |
| Multi-page | No | No (use APNG) | Yes |
| Browser support | Partial | Universal | No |
| Best use case | Windows internal use | Web graphics | Professional imaging |
When to Use BMP
Use BMP when:
- Writing Windows GDI/GDI+ code where HBITMAP is the native type
- Storing intermediate frames in image processing pipelines where compression overhead matters
- Maximum compatibility with legacy Windows applications
- Exact pixel-for-pixel round-tripping is required (no compression losses, no metadata stripping)
Don't use BMP when:
- File size matters (PNG is 30β70% smaller with lossless compression)
- Sharing across platforms (PNG and TIFF have better cross-platform support)
- Web delivery (browsers have inconsistent BMP support)
- Professional photography (TIFF with metadata support)
BMP's longevity is a testament to its simplicity β the format's complete transparency makes it ideal for low-level programming, system interfaces, and scenarios where "what you store is exactly what you get" is the top priority.