Módulo Bitmap
Bitmap carga imágenes BMP para pintarlas con Draw o hacer sprites con ellas, y escribe ficheros BMP desde tu programa. Cada imagen cargada es un bitmap: un objeto con sus medidas y su color transparente.

use Bitmap
Formatos
- Lee BMP sin comprimir de 16, 24 o 32 bits por píxel (también los que llevan máscaras de color), guardados de arriba abajo o de abajo arriba.
- No lee los BMP de 1, 4 u 8 bits (los de paleta), los comprimidos, ni otros formatos como PNG o JPG:
Bitmap.loadlanzaERR_FILE_NOT_FOUND, con el mensaje «Cannot load bitmap file», aunque el fichero exista. - Al cargarla, la VM pasa la imagen a 16 bits por píxel (RGB565), en Windows y en placa. Cada color pasa al más parecido de 16 bits, y la imagen ocupa unos
ancho × alto × 2bytes. - No lee el canal alfa. La transparencia es un color: el que le das a
Bitmap.loado atcolor, de serie el negro.
El editor de imágenes del IDE exporta al compilar las imágenes del proyecto a BMP de los que lee la VM.
Plataformas
| Windows | ESP32 / ESP32-S3 | ESP32-P4 | ESP32-S31 | |
|---|---|---|---|---|
| De dónde carga | De la carpeta del programa (Build/flash); con sd:, de la carpeta sd de al lado | De la memoria flash; con sd:, de la tarjeta SD | Igual | Igual |
size() | Redondea cada fila a un múltiplo de 4 bytes | ancho × alto × 2 | Igual | Igual |
Funciones
| Función | Qué hace | Devuelve |
|---|---|---|
load(fichero [, transparente]) | Carga una imagen BMP | Un bitmap |
writeHeader(fichero, ancho, alto, bpp) | Escribe la cabecera de un BMP en un fichero abierto | nil |
writeLine(fichero, colores) | Escribe una fila de píxeles | nil |
new(a, b, c) | No hace nada en esta versión | nil |
Si algo falla, las funciones de Bitmap lanzan una excepción, que se captura con try (ver Errores y excepciones).
load
Bitmap.load(fichero [, transparente]) carga una imagen BMP y devuelve un bitmap para pintarlo o hacer un sprite con él.
| Parámetro | Tipo | Descripción |
|---|---|---|
fichero | cadena | La ruta de la imagen en el proyecto, como Images/logo.bmp |
transparente | entero | (Opcional) El color que no se pinta, 0xRRGGBB. Por defecto es 0x000000 (negro) |
| Error | Cuándo |
|---|---|
ERR_FILE_NOT_FOUND | El fichero no está, o no es un BMP que la VM pueda leer (ver Formatos). |
ERR_OUT_OF_MEMORY | No hay memoria para la imagen. |
ERR_TYPE_MISMATCH | fichero no es una cadena, o transparente no es un entero. |
ERR_INVALID_ARG_NUMBER | No le pasas uno o dos argumentos. |
use Bitmap
use Display
// Abrir la pantalla
Display.open(320, 240)
// Cargar un bitmap
var imagen = Bitmap.load("Images/logo.bmp")
// Mostrar información del bitmap
println("Ancho: ", imagen.width())
println("Alto: ", imagen.height())
println("Ocupa: ", imagen.size(), " bytes")
Con otro color transparente, por ejemplo el magenta, el negro de la imagen sí se pinta:
use Bitmap
use Display
Display.open(320, 240)
// Cargar bitmap con magenta (0xFF00FF) como color transparente
var sprite = Bitmap.load("Images/personaje.bmp", 0xFF00FF)
// El color magenta no se dibujará, permitiendo ver el fondo
Una vez cargada, la pintas con las funciones de Draw:
use Bitmap
use Display
Display.open(320, 240)
var draw = Display.draw
var fondo = Bitmap.load("Images/fondo.bmp")
var personaje = Bitmap.load("Images/heroe.bmp", 0xFF00FF)
// El fondo en (0, 0), sin transparente
draw.bitmapOpaque(0, 0, fondo)
// El personaje, centrado
var x = (Display.width - personaje.width()) div 2
var y = (Display.height - personaje.height()) div 2
draw.bitmap(x, y, personaje)
Display.update()
La ruta es la de la imagen en el proyecto. Al compilar, el IDE copia a la carpeta del programa (Build/flash) las imágenes que tienen Include in Build encendida, y Run las graba con el programa en la memoria flash de la placa (Grabar el firmware y ejecutar). Con sd: delante ("sd:fotos/playa.bmp"), la imagen se lee de la tarjeta SD; en Windows, de la carpeta sd que está junto a Build/flash. Más sobre las rutas en File.
El transparente se compara cuando la imagen ya está en 16 bits. Con el negro, también son transparentes los colores casi negros, como 0x050205, que en 16 bits son el mismo color.
writeHeader
Bitmap.writeHeader(fichero, ancho, alto, bpp) escribe la cabecera de un BMP de 24 bits en un fichero abierto para escribir en binario. Después, escribe sus filas con writeLine.
| Parámetro | Tipo | Descripción |
|---|---|---|
fichero | fichero | Un fichero de File, abierto con "wb" |
ancho, alto | entero | Las medidas de la imagen, mayores que 0 |
bpp | entero | Bits por píxel: solo 24 |
use Bitmap, File
var f = File.open("dos.bmp", "wb")
Bitmap.writeHeader(f, 2, 1, 24) // 2 x 1 píxeles
Bitmap.writeLine(f, [0xFF0000, 0x0000FF]) // rojo y azul
f.close()
- Escribe los 54 bytes de la cabecera de un BMP sin comprimir, guardado de arriba abajo: la primera fila que escribes es la de arriba.
- Después hay que escribir
altofilas deanchocolores cada una.
| Error | Cuándo |
|---|---|
ERR_INVALID_VALUE | bpp no es 24, o ancho o alto no son mayores que 0. |
ERR_TYPE_MISMATCH | fichero no es un fichero, o las medidas no son enteros. |
ERR_FILE_WRITE_FAIL | El fichero no está abierto o no se puede escribir. |
ERR_INVALID_ARG_NUMBER | No le pasas cuatro argumentos. |
writeLine
Bitmap.writeLine(fichero, colores) escribe una fila de píxeles: una lista de colores 0xRRGGBB, de izquierda a derecha.
use Bitmap, File
var f = File.open("franjas.bmp", "wb")
Bitmap.writeHeader(f, 3, 2, 24)
Bitmap.writeLine(f, [0xFF0000, 0x00FF00, 0x0000FF]) // la fila de arriba
Bitmap.writeLine(f, [0xFFFFFF, 0x000000, 0x808080]) // la de abajo
f.close()
- Añade el relleno que pide el formato BMP: no tienes que hacer nada.
- No comprueba el ancho de la cabecera: escribe tantos píxeles como colores tenga la lista. Pásale siempre
anchocolores. - Con una lista vacía no escribe nada.
| Error | Cuándo |
|---|---|
ERR_TYPE_MISMATCH | fichero no es un fichero, colores no es una lista, o un elemento no es un entero. |
ERR_FILE_WRITE_FAIL | El fichero no está abierto o no se puede escribir. |
ERR_OUT_OF_MEMORY | No hay memoria para preparar la fila. |
ERR_INVALID_ARG_NUMBER | No le pasas dos argumentos. |
new
Bitmap.new(a, b, c) está en el módulo, pero en esta versión no crea nada: con tres cadenas devuelve nil, y con otros argumentos lanza ERR_INVALID_ARG_NUMBER o ERR_TYPE_MISMATCH. No la uses. Para hacer una imagen desde el programa, escríbela con writeHeader y writeLine y cárgala con load.
Objetos que devuelve
Bitmap.load devuelve un bitmap. Sus métodos se llaman con paréntesis:
| Método | Qué hace | Devuelve |
|---|---|---|
width() | El ancho en píxeles | Un entero |
height() | El alto en píxeles | Un entero |
bpp() | Los bits por píxel: siempre 16, porque la VM convierte la imagen a RGB565 al cargarla | Un entero |
size() | Los bytes que ocupa la imagen en memoria | Un entero |
tcolor(color) | Cambia el color transparente | nil |
Con un argumento de más, los cuatro primeros lanzan ERR_INVALID_ARG_NUMBER.
size
Los bytes que ocupa la imagen: ancho × alto × 2, porque cada píxel va en 16 bits. En Windows, cada fila se redondea a un múltiplo de 4 bytes, así que una imagen de ancho impar ocupa un poco más (una de 15 × 10 ocupa 320 bytes).
Los bitmaps ocupan memoria significativa, especialmente en dispositivos ESP32 con recursos limitados. Ten en cuenta:
- Un bitmap de 100x100 píxeles a 16 bits ocupa aproximadamente 20 KB
- Un bitmap de 320x240 píxeles a 16 bits ocupa aproximadamente 150 KB
Usa memFree() para monitorear la memoria disponible antes de cargar imágenes grandes. Cuánta memoria tiene cada placa está en Memoria y límites.
use Bitmap
// Verificar memoria antes de cargar
println("Memoria libre: ", memFree(), " bytes")
var imagen = Bitmap.load("Images/grande.bmp")
println("Memoria después de cargar: ", memFree(), " bytes")
println("Tamaño del bitmap: ", imagen.size(), " bytes")
tcolor
bitmap.tcolor(color) cambia el color transparente del bitmap: desde ese momento, ese color no se pinta, y el de antes sí.
use Bitmap
var nave = Bitmap.load("Images/nave.bmp") // transparente: el negro
nave.tcolor(0xFF00FF) // ahora, el magenta
- Cuenta en todo lo que pinta esa imagen, también en los sprites hechos con ella con
Sprite.new. - No hay forma de leerlo:
tcolornecesita el color. - Sin argumento lanza
ERR_INVALID_ARG_NUMBER, y con algo que no es un entero,ERR_TYPE_MISMATCH.
Ejemplo completo
El programa escribe un BMP de 64 × 32 (un degradado con un marco negro), lo carga y lo pinta de dos formas: girando, sin el marco, porque el negro es transparente, y con bitmapOpaque, con el marco.
use Display, Bitmap, File
// 1. Escribir un BMP de 64 x 32: un degradado con un marco negro
var f = File.open("degradado.bmp", "wb")
Bitmap.writeHeader(f, 64, 32, 24)
for (var y = 0; y < 32; y++)
var fila = []
for (var x = 0; x < 64; x++)
if (x == 0 or y == 0 or x == 63 or y == 31) then
fila.append(0x000000) // el marco, negro
else
fila.append(rgb(x * 4, 0, 255 - y * 8))
end
end
Bitmap.writeLine(f, fila)
end
f.close()
// 2. Cargarlo y pintarlo
Display.open(320, 240)
Display.fps = 30
var draw = Display.draw
var img = Bitmap.load("degradado.bmp") // el negro es transparente
println(img.width(), " x ", img.height(), ": ", img.size(), " bytes") // 64 x 32: 4096 bytes
var x0 = (Display.width - img.width()) div 2
for (var i = 0; i < 90; i++) // 3 segundos
draw.bitmap(x0, 40, img, i * 4) // girando, sin el marco
draw.bitmapOpaque(x0, 150, img) // con el marco negro
Display.update()
end
Display.close()
Trampas y problemas conocidos
- El negro es transparente si no dices otra cosa. Una imagen con negro puro que tiene que verse sale con agujeros. Cárgala con otro transparente (
Bitmap.load(fichero, 0xFF00FF)) o píntala condraw.bitmapOpaque. Lo mismo pasa con los sprites del editor de sprites sin Remove background. - Un BMP de 8 bits da «Cannot load bitmap file», aunque el fichero esté. Guárdalo en 24 bits, o deja que lo exporte el editor de imágenes.
Bitmap.newno hace nada en esta versión (ver new).
Todos los problemas conocidos de JARU, juntos, están en Problemas conocidos.