Bit masks
A 16 or 32-bit pixel holds its colour directly, and bit masks say which of its bits belong to red, green, blue and alpha. They let a BMP store layouts such as 5-6-5 colour or 8-bit alpha.
00000036 00 F8 00 00 E0 07 00 00 1F 00 00 00
When masks are used
Masks apply when the compression is
BI_BITFIELDS (3), which gives red, green and blue masks, or
BI_ALPHABITFIELDS (6), which adds an alpha mask. Both are only valid at 16
and 32 bits per pixel. BI_ALPHABITFIELDS came from Windows CE and is rarely
seen elsewhere.
With BI_RGB, no masks are stored and the fixed defaults below apply.
Where they are stored
Where the masks sit depends on the version of the DIB header:
| Header | Masks |
|---|---|
BITMAPINFOHEADER, 40 bytes | Straight after the header: 12 bytes (red, green, blue) for BI_BITFIELDS, or 16 bytes (adding alpha) for BI_ALPHABITFIELDS. |
| V2, 52 bytes | Inside the header at offsets 40, 44 and 48. Nothing extra follows the header. |
| V3, V4 and V5 | Inside the header at offsets 40, 44, 48 and 52, including alpha. Nothing extra follows the header. |
Each mask is a little-endian 32-bit number. In the example above, the red mask
00 F8 00 00 is 0x0000F800.
Reading a channel
To read a channel, AND the pixel with its mask and shift the result right by the number of zero bits below the mask. The result runs from 0 to the mask’s maximum, such as 31 for a 5-bit mask, so scale it to 0 to 255 for display:
const shift = trailingZeros(mask);
const max = mask >>> shift;
const value = (pixel & mask) >>> shift;
const channel = Math.round(value * 255 / max);
The set bits of each mask must be one unbroken run, and masks must not overlap. A mask of zero means the channel is absent.
Default and common masks
| Layout | Red | Green | Blue | Alpha |
|---|---|---|---|---|
| 16-bit default, 5-5-5 | 0x7C00 | 0x03E0 | 0x001F | none, top bit unused |
| 16-bit 5-6-5 | 0xF800 | 0x07E0 | 0x001F | none |
| 32-bit default, 8-8-8 | 0x00FF0000 | 0x0000FF00 | 0x000000FF | none, top byte unused |
| 32-bit with alpha | 0x00FF0000 | 0x0000FF00 | 0x000000FF | 0xFF000000 |
The defaults are used with BI_RGB. For a 32-bit BI_RGB image
the top byte is officially unused, though some programs store alpha there; a reader cannot
tell for certain, so writers that want alpha should use BI_BITFIELDS with a
V4 or V5 header and an alpha mask.