Saltar al contenido principal

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.

Captura del módulo Bitmap de JARU para cargar imágenes y dibujarlas en proyectos gráficos

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.load lanza ERR_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 × 2 bytes.
  • No lee el canal alfa. La transparencia es un color: el que le das a Bitmap.load o a tcolor, 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​

WindowsESP32 / ESP32-S3ESP32-P4ESP32-S31
De dónde cargaDe la carpeta del programa (Build/flash); con sd:, de la carpeta sd de al ladoDe la memoria flash; con sd:, de la tarjeta SDIgualIgual
size()Redondea cada fila a un múltiplo de 4 bytesancho × alto × 2IgualIgual

Funciones​

FunciónQué haceDevuelve
load(fichero [, transparente])Carga una imagen BMPUn bitmap
writeHeader(fichero, ancho, alto, bpp)Escribe la cabecera de un BMP en un fichero abiertonil
writeLine(fichero, colores)Escribe una fila de píxelesnil
new(a, b, c)No hace nada en esta versiónnil

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ámetroTipoDescripción
ficherocadenaLa ruta de la imagen en el proyecto, como Images/logo.bmp
transparenteentero(Opcional) El color que no se pinta, 0xRRGGBB. Por defecto es 0x000000 (negro)
ErrorCuándo
ERR_FILE_NOT_FOUNDEl fichero no está, o no es un BMP que la VM pueda leer (ver Formatos).
ERR_OUT_OF_MEMORYNo hay memoria para la imagen.
ERR_TYPE_MISMATCHfichero no es una cadena, o transparente no es un entero.
ERR_INVALID_ARG_NUMBERNo 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()
Dónde están los ficheros

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.

La transparencia se compara en 16 bits

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ámetroTipoDescripción
ficheroficheroUn fichero de File, abierto con "wb"
ancho, altoenteroLas medidas de la imagen, mayores que 0
bppenteroBits 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 alto filas de ancho colores cada una.
ErrorCuándo
ERR_INVALID_VALUEbpp no es 24, o ancho o alto no son mayores que 0.
ERR_TYPE_MISMATCHfichero no es un fichero, o las medidas no son enteros.
ERR_FILE_WRITE_FAILEl fichero no está abierto o no se puede escribir.
ERR_INVALID_ARG_NUMBERNo 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 ancho colores.
  • Con una lista vacía no escribe nada.
ErrorCuándo
ERR_TYPE_MISMATCHfichero no es un fichero, colores no es una lista, o un elemento no es un entero.
ERR_FILE_WRITE_FAILEl fichero no está abierto o no se puede escribir.
ERR_OUT_OF_MEMORYNo hay memoria para preparar la fila.
ERR_INVALID_ARG_NUMBERNo 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étodoQué haceDevuelve
width()El ancho en píxelesUn entero
height()El alto en píxelesUn entero
bpp()Los bits por píxel: siempre 16, porque la VM convierte la imagen a RGB565 al cargarlaUn entero
size()Los bytes que ocupa la imagen en memoriaUn entero
tcolor(color)Cambia el color transparentenil

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).

Uso de memoria

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: tcolor necesita 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 con draw.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.new no hace nada en esta versión (ver new).

Todos los problemas conocidos de JARU, juntos, están en Problemas conocidos.