Skip to content

Trampas de Dev #2: siempre creí que el BMP de 32 bits tenía canal alfa

2026-06-23

Tags: Windows · Trampas de Dev


Durante años di por sentado algo que todo el mundo sabe: el BMP de 32 bits, 4 bytes por píxel, BGRA, el 4.º byte es el alfa. PNG es RGBA, BMP simplemente invierte el orden de bytes — claro, limpio, de cajón.

Hasta que construí una herramienta ICO y la transparencia se rompió de una forma que no tenía absolutamente ningún sentido. Entonces abrí la especificación BMP — y descubrí que lo que creía era mentira.

Un hecho que todo el mundo «sabe»

El principio de la historia es de lo más normal. Quería una herramienta que pudiera desmontar un ICO, previsualizar cada frame y conservar la transparencia. La lógica no podía ser más simple:

  1. Leer los datos BMP de un frame dentro del ICO
  2. Decodificarlo a un mapa de bits
  3. Mostrarlo

Carga el primer frame: color correcto, forma correcta, donde debería haber transparencia, bloques sólidos negros.

Mi primera reacción — este ICO está roto. Pruebo otro, también negro. Pruebo otro más, también negro.

Algo no cuadra. Estos iconos se ven perfectamente transparentes en el Explorador de Windows.

Qué dice la especificación

Fui a revisar la especificación BMP. El clásico BITMAPINFOHEADER tiene 40 bytes, biBitCount = 32 significa 32 bits por píxel. Y entonces vi esa frase clave —

Cuando biCompression es BI_RGB, el byte más significativo de cada DWORD de píxel es unused (sin usar).

Sin usar. No alfa, no transparencia, es «no sabemos qué poner en este byte, lo dejamos vacío».

En otras palabras: en el formato BMP de 32 bits más básico y común, la especificación no define ningún canal alfa. El 4.º byte de cada píxel, según las reglas, es simplemente un padding de relleno.

Eso es exactamente lo contrario de lo que «sabía» desde hace años.

Entonces, ¿cómo es que el alfa «existe»?

BMP se puso al día más tarde. BITMAPV4HEADER (108 bytes) introdujo BI_BITFIELDS, que permite declarar explícitamente un conjunto de máscaras de bits — rojo, verde, azul, y alfa. Solo a partir de esta versión el alfa se convierte oficialmente en parte de BMP.

El problema: la inmensa mayoría de los archivos BMP, incluidos los frames incrustados en ICO, siguen usando la antigua cabecera de 40 bytes, BI_RGB, sin máscaras. Según la especificación, su 4.º byte es simplemente padding.

¿Y por qué los iconos se ven transparentes en Windows?

Porque toda la pila gráfica de Windows llegó a un acuerdo tácito en la era XP: «Vale, todo el mundo va a fingir que ese byte de padding es alfa.» La función de blending de GDI insinuaba que se podía usar así, y los distintos renderizadores siguieron la corriente sin decir nada. Así, «el BMP de 32 bits tiene alfa» se convirtió en un «hecho» que todo el mundo cree pero nadie escribió en ninguna especificación.

ICO en realidad tiene dos sistemas de transparencia

Y lo mejor: ICO no depende en absoluto de ese byte del BMP. Tiene su propio mecanismo de transparencia oficial: la máscara AND.

Los datos BMP de un frame ICO se ven así:

  • Datos de píxel (máscara XOR) — BGR más ese 4.º byte de identidad dudosa
  • Seguidos de una máscara monocromática de 1 bit (máscara AND) — 1 bit por píxel, 1 significa transparente, 0 significa opaco

Esta es la transparencia ICO que la especificación reconoce oficialmente. El alfa de 32 bits es una «mejora» añadida después.

La lógica de renderizado de Windows también es muy pragmática:

  • Si el alfa de 32 bits tiene cualquier valor distinto de cero → usa alfa, hace blending suave
  • Si el alfa es todo ceros → trata como opaco, retrocede a la máscara AND

Dos sistemas coexistiendo, usa el que funcione mejor. Suena considerado — hasta que tú mismo escribes código para parsearlo.

En qué trampa caí exactamente

Volviendo al icono con fondo negro. La verdad es esta:

Mi decodificador (ya sea el BitmapDecoder de WinUI, o el decodificador BMP del navegador) está obedeciendo la especificación BMP al pie de la letra — ve BI_RGB + 32 bits, así que descarta el 4.º byte como padding. Las regiones transparentes se quedan sin alfa, y el color restante con opacidad por defecto se convierte en un bloque negro.

No están rotos. Es el formato el que de por sí no es fiable en este asunto.

Por eso la solución es contraintuitiva: no se trata de «arreglar» el ICO, ni de cambiar de decodificador — se trata de no volver a confiar en los decodificadores. Leer directamente esos 4 bytes crudos, colocar el BGRA en el canvas uno mismo; cuando el alfa sea todo ceros, entonces sí, calcular la máscara AND como Dios manda.

Así es como lo hice tanto en la versión web como en la de escritorio de ICO Unpacker: saltarse el decodificador, parsear los bytes del DIB directamente. De este modo, da igual que ese 4.º byte sea realmente alfa o padding — la transparencia nunca desaparece por arte de magia.

ICO Unpacker también está disponible como app de escritorio gratuita para Windows — mismas funciones, sin navegador, completamente offline.

Entonces, ¿qué hay que recordar?

  • «El BMP de 32 bits tiene canal alfa» es un acuerdo tácito de toda la industria, no un hecho definido por la especificación.
  • En el clásico BITMAPINFOHEADER + BI_RGB, el 4.º byte es padding; el alfa solo se vuelve oficial con BITMAPV4HEADER + BI_BITFIELDS.
  • Si una herramienta «pierde» la transparencia de tu BMP, no es necesariamente un bug suyo — puede que simplemente esté siguiendo la especificación.
  • ICO tiene su propia transparencia oficial (máscara AND); el alfa de 32 bits es una mejora posterior; ambas coexisten, y Windows actúa según convenga.
  • Si de verdad quieres conservar la transparencia, no des rodeos: lee los bytes directamente.

En el Trampas de Dev #1 hablaba de cómo «los conversores online estropean silenciosamente tus frames BMP» — allí era otro quien escribía mal tu alfa. Esta vez es la otra cara: aunque el alfa esté bien escrito, el formato BMP no garantiza que nadie lo reconozca.


Parte de la serie Trampas de Dev.