Dev Pitfalls #2: I Always Thought 32-Bit BMP Had an Alpha Channel
2026-06-23
Tags: Windows · Dev Pitfalls
For years, I believed something that seemed like plain common sense: 32-bit BMP, 4 bytes per pixel, BGRA, the 4th byte is alpha. PNG is RGBA, BMP is just the byte order flipped — clean, tidy, self-evident.
Then I built an ICO tool, and transparency broke in a way that made no sense whatsoever. I opened the BMP spec — and realized I'd been believing a lie.
A "Fact" Everyone Knows
The story starts innocently enough. I wanted a tool that could crack open an ICO, preview each frame, and preserve transparency. The logic couldn't be simpler:
- Read the BMP data from a frame inside the ICO
- Decode it into a bitmap
- Display it
The first frame loaded: colors right, shape right, every transparent area turned into a solid black block.
My first reaction — the ICO file is broken. Tried another one, still black. Another one, still black.
Something wasn't right. These icons displayed with perfect transparency in Windows Explorer.
What the Spec Actually Says
I went and read the BMP spec. The classic BITMAPINFOHEADER is 40 bytes, biBitCount = 32 means 32 bits per pixel. Then I saw the critical line —
When biCompression is BI_RGB, the high-order byte of each pixel DWORD is unused.
Unused. Not alpha, not transparency — just "we don't know what to put here, leave it empty."
In other words: in the most basic, most common 32-bit BMP format, the spec defines no alpha channel at all. The 4th byte of each pixel is, officially, meaningless padding.
This was the exact opposite of what I had "known" for years.
So Where Did Alpha Come From?
BMP did eventually catch up. BITMAPV4HEADER (108 bytes) introduced BI_BITFIELDS, allowing you to explicitly declare a set of bit masks — red, green, blue, and alpha. Only at this point did alpha become officially sanctioned in BMP.
The problem: the vast majority of BMP files, including the frames embedded inside ICO files, still use the old 40-byte header, BI_RGB, no masks. Per spec, their 4th byte is padding.
So why do icons look transparent in Windows?
Because the entire Windows graphics stack reached an unspoken gentlemen's agreement sometime in the XP era: "Fine, let's all pretend that padding byte is alpha." GDI's blending functions hinted you could use it that way, and every renderer quietly went along. And so "32-bit BMP has alpha" became a "fact" that everyone believes — but nobody wrote into the spec.
ICO Actually Has Two Transparency Systems
Here's the best part: ICO doesn't even rely on that one BMP byte. It has its own official transparency mechanism: the AND mask.
A single ICO frame's BMP data looks like this:
- Pixel data (XOR mask) — just BGR plus that identity-questionable 4th byte
- Immediately followed by a 1-bit monochrome mask (AND mask) — 1 bit per pixel, 1 means transparent, 0 means opaque
This is the ICO transparency that the spec explicitly endorses. The 32-bit alpha is a later add-on, an "enhancement."
Windows makes a very pragmatic choice at render time:
- If the 32-bit alpha contains any non-zero value → use alpha, do smooth blending
- If alpha is all zeros → treat as opaque, fall back to the AND mask
Two systems coexist, and Windows uses whichever works. Sounds thoughtful — until you're the one writing code to parse it.
The Trap I Fell Into
Back to that black-background icon. The truth is:
My decoder (whether WinUI's BitmapDecoder, or a browser's BMP decoder) was faithfully following the BMP spec — it saw BI_RGB + 32-bit, and dutifully discarded the 4th byte as padding. The transparent regions lost their alpha, and the remaining colors paired with default opacity produced solid black blocks.
They weren't broken. The format itself is unreliable in this regard.
So the fix is counterintuitive: don't "fix" the ICO, don't swap decoders — stop trusting decoders. Read those 4 raw bytes directly, push BGRA into the canvas yourself; when alpha is all zeros, fall back to properly computing the AND mask.
That's exactly what I did in both the web and desktop versions of ICO Unpacker: bypass the decoder, parse the DIB bytes directly. This way, whether that 4th byte is alpha or padding, transparency never vanishes into thin air.
ICO Unpacker is also available as a free Windows desktop app — same functionality, no browser needed. Works fully offline.
What to Actually Remember
- "32-bit BMP has an alpha channel" is an industry-wide gentleman's agreement, not a spec-defined fact.
- Under the classic
BITMAPINFOHEADER+BI_RGB, the 4th byte is padding; alpha only becomes official withBITMAPV4HEADER+BI_BITFIELDS. - A tool "losing" your BMP transparency isn't necessarily a bug — it might just be following the spec.
- ICO has its own official transparency (AND mask); 32-bit alpha is a later enhancement. Both coexist, and Windows picks whichever is available.
- If you truly want to preserve transparency, don't take detours: read the raw bytes.
The previous article, Dev Pitfalls #1, covered how "online converters silently break your BMP frames" — that's someone else writing your alpha wrong. This article covers the flip side: even when alpha is written correctly, the BMP format itself doesn't guarantee anyone will recognize it.
This article is part of the Dev Pitfalls series.