Módulo Draw
Draw pinta en la pantalla que abre Display: puntos, líneas, figuras, texto, imágenes, sprites, mapas de tiles y escenas. Pinta en el fotograma que presenta Display.update(), con coordenadas del view: el (0, 0) es su esquina de arriba a la izquierda.

use Display
Display.open(320, 240)
var draw = Display.draw // el módulo Draw
draw.color = 0xFF0000
draw.circle(100, 100, 50)
También lo tienes con use Draw, como Draw.circle(…): es el mismo módulo.
Colores
Un color es un entero 0xRRGGBB: rojo, verde y azul, de 00 a FF cada uno. Las figuras, las líneas y el texto se pintan con el color de draw.color.
| Color | Valor | Ejemplo |
|---|---|---|
| Rojo | 0xFF0000 | draw.color = 0xFF0000 |
| Verde | 0x00FF00 | draw.color = 0x00FF00 |
| Azul | 0x0000FF | draw.color = 0x0000FF |
| Blanco | 0xFFFFFF | draw.color = 0xFFFFFF |
| Negro | 0x000000 | draw.color = 0x000000 |
También puedes formarlo con rgb(r, g, b) (ver Librería estándar). En placa, la pantalla no tiene todos esos colores: cada uno pasa al más parecido que puede mostrar (Memoria y límites).
Plataformas
| Windows | ESP32 / ESP32-S3 | ESP32-P4 | ESP32-S31 | |
|---|---|---|---|---|
font | No cambia nada: el texto sale siempre con la misma fuente | Elige una de las fuentes 0 a 8 | Igual | Igual |
Errores de font | Lanza la excepción | No lanza: escribe el error en el monitor serie | Igual | Igual |
getPixel | El color de lo pintado | Aproximado: en el doble buffer, 256 colores | Aproximado: 16 bits | Aproximado: 16 bits |
cls | Borra el view | Borra el view; en el modo directo, la pantalla | Borra el view | Borra el view |
line, circle, polygon y getPixel | En coordenadas de la pantalla, no del view | Del view | Del view | Del view |
bytes | Estira la imagen a toda la ventana | En x, y | En x, y | En x, y |
Las dos últimas filas son fallos de la VM de Windows: mira Trampas.
Funciones
| Función | Qué hace | Devuelve |
|---|---|---|
cls([color]) | Borra con un color | nil |
setPixel(x, y) | Pinta un punto | nil |
getPixel(x, y) | Lee el color de un punto | El color |
line(x1, y1, x2, y2) | Pinta una línea | nil |
rect(x, y, ancho, alto [, relleno]) | Pinta un rectángulo | nil |
circle(x, y, radio [, relleno]) | Pinta un círculo | nil |
ellipse(x, y, radioX, radioY [, relleno]) | Pinta una elipse | nil |
polygon(vertices [, relleno]) | Pinta un polígono | true |
text(x, y, texto [, color]) | Escribe un texto | nil |
font() / font(n) | Lee o cambia la fuente del texto | La fuente / true |
bitmap(x, y, bitmap [, angulo]) | Pinta una imagen, con su transparente | nil |
bitmapOpaque(x, y, bitmap [, angulo]) | Pinta una imagen sin transparente | nil |
bitmapBatch(bitmap, posiciones) | Pinta una imagen en muchos sitios | nil |
gridBitmap(grid, valor, bitmap, x0, y0, pasoX, pasoY) | Pinta una imagen en las celdas de un GridMap con un valor | nil |
bytes(x, y, ancho, alto, bytes, paleta) | Pinta un bloque de Bytes con una paleta | nil |
sprite(sprite) | Pinta un sprite, o una lista o un array de sprites | nil |
tilemap(grid [, tileset [, vacio]]) | Pinta un mapa de tiles | nil |
scene(escena) | Pinta una escena: su mapa y sus sprites | nil |
Con un argumento de otro tipo, las funciones de Draw no lanzan una excepción: devuelven un valor de error y no pintan nada (ver Errores y excepciones). Las que sí lanzan son tilemap, scene y, en Windows, font.
cls
Borra con un color, o con colorBG si no le das ninguno.
draw.cls() // borra con colorBG
draw.cls(0x000000) // borra con negro
En el modo de doble buffer no hace falta: Display.update() ya empieza cada fotograma borrando el view con colorBG (autoClear). Borra solo el view: lo de fuera, como un marco de loadTFT, se queda. En el ESP32 y el S3, en el modo directo, borra la pantalla entera. El color tiene que ser un entero.
setPixel
Pinta un punto con el color de draw.color.
draw.setPixel(100, 50) // un punto en (100, 50)
draw.setPixel([150, 75]) // también con una lista [x, y]
getPixel
Lee el color de un punto del fotograma que estás pintando, como un entero 0xRRGGBB.
draw.color = 0xFF0000
draw.setPixel(100, 50)
println(draw.getPixel(100, 50).toHex()) // FF0000
- Lee el fotograma que todavía no se ve: el que presentará el próximo
update(). Justo después de unupdate(), lee el fotograma nuevo, ya borrado. - En Windows da el color de lo pintado. Las imágenes se guardan en 16 bits, así que el color de una imagen vuelve aproximado: un
0x123456se lee0x103452. - En placa, el color es aproximado: el doble buffer del ESP32 y el S3 tiene 256 colores, y el del P4 y el S31, 16 bits por píxel.
- En Windows cuenta desde la esquina de la pantalla, no del view (Trampas).
line
Pinta una línea entre dos puntos con el color actual.
draw.line(x1, y1, x2, y2)
| Parámetro | Descripción |
|---|---|
x1, y1 | El punto de inicio |
x2, y2 | El punto final |
draw.color = 0xFFFFFF
draw.line(0, 0, 319, 239) // diagonal blanca
draw.line(160, 0, 160, 240) // línea vertical
rect
Pinta un rectángulo, con contorno o relleno, con el color actual.
draw.rect(x, y, ancho, alto [, relleno])
| Parámetro | Tipo | Descripción |
|---|---|---|
x, y | número | La esquina de arriba a la izquierda |
ancho | número | El ancho |
alto | número | El alto |
relleno | true / false | true para relleno, false para contorno (opcional, por defecto false) |
draw.color = 0xFFFF00
draw.rect(50, 50, 100, 80) // contorno amarillo
draw.rect(50, 50, 100, 80, true) // relleno amarillo
Las coordenadas y las medidas aceptan decimales, que se truncan, en esta función y en las demás figuras. relleno tiene que ser true o false: con otra cosa, no pinta.
circle
Pinta un círculo, con contorno o relleno, con el color actual.
draw.circle(x, y, radio [, relleno])
| Parámetro | Tipo | Descripción |
|---|---|---|
x, y | número | El centro |
radio | número | El radio |
relleno | true / false | true para relleno, false para contorno (opcional, por defecto false) |
draw.color = 0x00FF00
draw.circle(160, 120, 50) // contorno verde
draw.circle(160, 120, 50, true) // relleno verde
ellipse
Pinta una elipse, con contorno o relleno, con el color actual.
draw.ellipse(x, y, radioX, radioY [, relleno])
| Parámetro | Tipo | Descripción |
|---|---|---|
x, y | número | El centro |
radioX, radioY | número | Los radios horizontal y vertical |
relleno | true / false | true para relleno, false para contorno (opcional, por defecto false) |
draw.color = 0xFF00FF
draw.ellipse(160, 120, 80, 40) // contorno magenta
draw.ellipse(160, 120, 80, 40, true) // relleno magenta
polygon
Pinta un polígono a partir de una lista de vértices, con contorno o relleno.
draw.polygon(vertices [, relleno])
| Parámetro | Tipo | Descripción |
|---|---|---|
vertices | lista | Las coordenadas, en orden: [x1, y1, x2, y2, x3, y3, ...] |
relleno | true / false | true para relleno, false para contorno (opcional, por defecto false) |
draw.color = 0xFFFFFF
// Triángulo con contorno
var vertices = [160, 50, 100, 150, 220, 150]
draw.polygon(vertices)
// Pentágono relleno
draw.color = 0xFF8000
var penta = [160, 40, 200, 80, 185, 140, 135, 140, 120, 80]
draw.polygon(penta, true)
Los vértices van en una lista […], con al menos tres vértices (seis números). Con un número impar de elementos devuelve un valor de error y no pinta nada. El relleno funciona también con polígonos cóncavos. Es la única figura que devuelve true.
text
Escribe un texto en la pantalla.
draw.text(x, y, texto [, color])
| Parámetro | Descripción |
|---|---|
x, y | La esquina de arriba a la izquierda del texto |
texto | Una cadena |
color | El color del texto (opcional, usa draw.color si no se especifica) |
draw.text(10, 10, "Hola Mundo", 0x00FF00)
draw.text(10, 30, "Marcador: 100") // con draw.color
var puntos = 250
draw.text(10, 50, "Puntos: " + puntos.toString())
textotiene que ser una cadena: con un número no escribe nada. Pásalo a cadena contoString().- En placa, la fuente es la de
draw.font. En Windows, el texto sale siempre con la misma fuente (font/consola.ttf, de tamaño 13), elijas la que elijas. - Es lo que más cuesta de Draw. En Windows, cada llamada dibuja el texto con la fuente TrueType desde cero: medido en un juego en Windows, cinco textos cortos costaban 3,4 ms de un fotograma de 4,8 ms. Lo que cambia en cada fotograma (puntos, vidas, un reloj) píntalo con imágenes: un sprite con un fotograma por cifra, o
draw.bitmap. Dejadraw.textpara pantallas que no se mueven, como el título. - Con una cadena vacía (
"") no pinta nada.
font
Lee o cambia la fuente del texto.
// La fuente actual (1, la de serie)
var f = draw.font()
// Otra fuente
draw.font(2)
| Fuente | Descripción | Alto | Caracteres |
|---|---|---|---|
0 | Fuente pequeña (mapa de bits 6x8) | 8 px | ASCII completo |
1 | Fuente por defecto (alias de 0) | 8 px | ASCII completo |
2 | Fuente mediana | 16 px | 96 caracteres ASCII |
4 | Fuente grande | 26 px | 96 caracteres ASCII |
6 | Fuente para relojes | 48 px | 0-9 : - . a p m |
7 | Fuente de 7 segmentos | 48 px | 0-9 : - . |
8 | Fuente enorme | 75 px | 0-9 : - . |
El rango es 0 a 8. Los índices 3 y 5 existen por compatibilidad y son un alias de la fuente 0. Con otro valor, o con algo que no es un entero, la fuente no cambia: en Windows lanza ERR_INVALID_VALUE o ERR_TYPE_MISMATCH, y en placa escribe el error en el monitor serie y devuelve un valor de error.
No contienen el alfabeto: están pensadas para relojes y marcadores. Cualquier carácter que no esté en la lista se dibuja como un espacio, no da error. En la fuente 6, la barra vertical | es un espacio estrecho, útil para alinear la hora.
Las fuentes son de las placas. En Windows, draw.font cambia lo que devuelve draw.font(), pero el texto sale igual con cualquiera.
bitmap
Pinta una imagen de Bitmap, con rotación opcional. El color transparente de la imagen no se pinta.
draw.bitmap(x, y, bitmap [, angulo])
| Parámetro | Descripción |
|---|---|
x, y | La esquina de arriba a la izquierda |
bitmap | La imagen, de Bitmap.load |
angulo | Grados de giro, alrededor del centro de la imagen (opcional, 0 por defecto; los decimales se truncan) |
use Display, Bitmap
Display.open(320, 240)
var draw = Display.draw
var img = Bitmap.load("Images/imagen.bmp")
draw.bitmap(100, 50, img) // sin girar
draw.bitmap(100, 50, img, 45) // girada 45 grados
El transparente es el de la imagen: el negro, si no le diste otro al cargarla con Bitmap.load o con tcolor.
bitmapOpaque
Pinta una imagen como draw.bitmap, pero con todos sus píxeles: también los del color transparente.
draw.bitmapOpaque(x, y, bitmap [, angulo])
use Display, Bitmap
Display.open(320, 240)
var draw = Display.draw
var fondo = Bitmap.load("Images/fondo.bmp")
draw.bitmapOpaque(0, 0, fondo) // el negro también se pinta
Sirve para un fondo, o para una imagen con negro puro que tiene que verse, sin cambiar su transparente. Los parámetros son los de draw.bitmap. En Windows cuesta más que draw.bitmap: prepara la imagen en cada llamada.
bitmapBatch
Pinta la misma imagen en muchos sitios con una sola llamada. Es más rápido que llamar a draw.bitmap en un bucle.
draw.bitmapBatch(bitmap, posiciones)
| Parámetro | Descripción |
|---|---|
bitmap | La imagen |
posiciones | Un array ({…}) con las esquinas: {x0, y0, x1, y1, ...}. Con una lista ([…]), o con un número impar de elementos, devuelve un valor de error y no pinta nada |
use Display, Bitmap
Display.open(320, 240)
var draw = Display.draw
var moneda = Bitmap.load("Images/moneda.bmp")
var pos = {10, 20, 50, 20, 90, 20, 130, 20}
draw.bitmapBatch(moneda, pos) // cuatro monedas de una vez
- No gira las imágenes. Un elemento que no es un número se salta.
- El transparente es el de la imagen (el de
Bitmap.loadotcolor), como endraw.bitmap.
bitmapBatch es especialmente útil en ESP32: lee los píxeles de la imagen una sola vez para todas las posiciones, y se salta las que caen fuera del view.
gridBitmap
Pinta una imagen en cada celda de un GridMap que tiene un valor. Sirve para pintar las cosas sueltas de un mapa, como las monedas.
draw.gridBitmap(grid, valor, bitmap, x0, y0, pasoX, pasoY)
| Parámetro | Descripción |
|---|---|
grid | El GridMap |
valor | Un entero de 0 a 255: el valor que tiene que tener la celda |
bitmap | La imagen que se pinta en esas celdas |
x0, y0 | Dónde va, en el view, la celda (0, 0) |
pasoX, pasoY | La distancia en píxeles de una celda a la siguiente |
use Display, GridMap, Bitmap
Display.open(320, 240)
var draw = Display.draw
var mapa = GridMap.new(10, 10)
mapa.set(3, 2, 1)
var pared = Bitmap.load("Images/pared.bmp")
// La imagen "pared" en cada celda con el valor 1
draw.gridBitmap(mapa, 1, pared, 0, 0, 16, 16)
No usa la posición del mapa (x, y): usa x0 e y0. Recorre el mapa entero en cada llamada. El transparente es el de la imagen, también en placa.
bytes
Pinta un bloque de Bytes como una imagen: cada byte es un color de la paleta.
draw.bytes(x, y, ancho, alto, objetoBytes, paleta)
| Parámetro | Descripción |
|---|---|
x, y | La esquina de arriba a la izquierda |
ancho, alto | Las medidas de la imagen en píxeles |
objetoBytes | Un objeto Bytes con un byte por píxel, fila a fila: el número del color en la paleta |
paleta | Un array ({…}) de hasta 256 colores 0xRRGGBB |
use Display, Bytes
Display.open(320, 240)
var draw = Display.draw
var paleta = {0x000000, 0xFF0000, 0x00FF00, 0x0000FF}
var datos = Bytes.new(16) // 4 x 4 píxeles
datos.fill(1) // todos del color 1, rojo
draw.bytes(100, 100, 4, 4, datos, paleta)
x,y,anchoyaltotienen que ser enteros.- Cada byte tiene que ser un color de la paleta: un byte mayor pinta un color cualquiera.
- No tiene transparente: pinta todos los píxeles.
- Si la paleta no es un array, como una lista
[…], devuelve el valor de errorERR_TYPE_MISMATCH, y si el objeto Bytes tiene menos deancho × altobytes,ERR_INDEX_OUT_OF_RANGE. En los dos casos no pinta nada.
En Windows, draw.bytes no usa x ni y: estira la imagen a toda la ventana. En placa se pinta en su sitio y a su tamaño.
sprite
Pinta un sprite, o todos los de una lista o un array.
draw.sprite(sprite)
draw.sprite(listaSprites)
draw.sprite(arraySprites)
| Parámetro | Descripción |
|---|---|
sprite | Un sprite |
listaSprites | Una lista o un array de sprites |
use Display, Bitmap, Sprite
Display.open(320, 240)
var draw = Display.draw
var s = Sprite.new(Bitmap.load("Images/frame1.bmp"))
s.addImage(Bitmap.load("Images/frame2.bmp"))
s.x = 100
s.y = 100
draw.sprite(s)
- El sprite va con su pivote en
x,y, girado (angle) y volteado (flipX,flipY). - Solo pinta los sprites con
active = truey al menos un fotograma. En una lista, se salta lo que no es un sprite.
Cada vez que pinta un sprite, draw.sprite avanza su animación según frameRate, loop y yoyo, y su giro automático, spin. Un sprite que no se pinta no se anima. Pinta cada sprite una sola vez por fotograma: el giro de spin se aplica en cada llamada.
tilemap
Pinta un GridMap como un mapa de tiles: cada celda es el número de un tile del tileset.
draw.tilemap(grid [, tileset [, vacio]])
| Parámetro | Descripción |
|---|---|
grid | El GridMap, con el tamaño del tile puesto (grid.tSize(ancho, alto)) |
tileset | La imagen con los tiles (opcional si el mapa tiene uno en grid.tileset) |
vacio | El tile que no se pinta (opcional; de serie, el de grid.tEmpty(), 255) |
use Display, GridMap, Bitmap
Display.open(320, 240)
var draw = Display.draw
var mapa = GridMap.new(20, 15)
mapa.tSize(16, 16)
mapa.tileset = Bitmap.load("Images/tiles.bmp")
draw.tilemap(mapa) // con el tileset del mapa
draw.tilemap(mapa, mapa.tileset, 0) // el tile 0 tampoco se pinta
- Los tiles se cuentan en el tileset de izquierda a derecha y de arriba abajo, empezando en 0, con el tamaño de
tSize. El valor de cada celda es el tile que se pinta en ella. Las celdas con 255 no se pintan nunca. - La posición del mapa es
grid.xygrid.y: dónde va la celda(0, 0)en el view. Para hacer scroll, cámbialas (con valores negativos el mapa se va a la izquierda y arriba). Solo se pintan los tiles que caen en el view. - Con
wrapXowrapY, el mapa se repite en ese eje. - Lanza una excepción si falta el tileset o el tamaño del tile, o si el tileset es más pequeño que un tile (
ERR_INVALID_VALUE), o si un argumento es de otro tipo (ERR_TYPE_MISMATCH). - El transparente es el del tileset (el de
Bitmap.loadotcolor). - En Windows, el mapa entero se guarda ya pintado, y se vuelve a pintar cuando cambia una celda con
set.
Con una escena, draw.scene pinta el mapa de la escena con la cámara aplicada: no hace falta mover grid.x y grid.y a mano.
scene
Pinta una escena entera: primero su GridMap (si tiene uno, con setGridMap) y después sus sprites, con la cámara aplicada.
draw.scene(escena)
| Parámetro | Descripción |
|---|---|
escena | Un objeto Scene, de Scene.new() |
use Display, Scene
Display.open(320, 240)
Display.fps = 30
var draw = Display.draw
var escena = Scene.new()
// ... sprites, mapa y cámara de la escena
escena.update()
draw.scene(escena)
Display.update()
- El mapa de la escena tiene que tener su tileset en
grid.tileset: si no, lanzaERR_INVALID_VALUE. - Pinta los sprites activos que la cámara ve (con 16 píxeles de margen), en el orden de la lista de la escena. Los que quedan fuera no se pintan, y su animación no avanza.
- Con algo que no es una escena, lanza
ERR_TYPE_MISMATCH.
La cámara de la escena (la que se coloca con scene.setCamera(x, y) o sigue a un sprite con scene.follow(sprite), y se lee con scene.getCamera()) se aplica automáticamente tanto al GridMap como a todos los sprites. Solo se dibujan los sprites marcados como activos y visibles dentro del área de la cámara.
Propiedades
| Propiedad | Tipo | De serie | Lectura / escritura |
|---|---|---|---|
color | entero 0xRRGGBB | 0xFFFFFF (blanco) | Las dos |
colorBG | entero 0xRRGGBB | 0x000000 (negro) | Las dos |
Las dos tienen que ser enteros: un valor de otro tipo no las cambia.
color
El color con el que se pintan los puntos, las líneas, las figuras y el texto.
// Establecer color
draw.color = 0xFF0000
// Obtener color actual
var c = draw.color
colorBG
El color de fondo: el de cls() sin argumentos, y el que usa Display.update() para borrar cada fotograma (con autoClear).
// Establecer color de fondo
draw.colorBG = 0x000080
// Obtener color de fondo actual
var bg = draw.colorBG
Ejemplo completo
Una bola rebota durante diez segundos delante de un suelo y una estrella. Cada fotograma empieza borrado con colorBG.
use Display
Display.open(320, 240)
Display.fps = 30
var draw = Display.draw
draw.colorBG = 0x000020 // update() borra con este color
var bolaX = 160.0
var bolaY = 60.0
var vx = 90.0 // píxeles por segundo
var vy = 60.0
var estrella = [160, 20, 168, 42, 192, 42, 173, 56, 180, 80, 160, 66, 140, 80, 147, 56, 128, 42, 152, 42]
for (var i = 0; i < 300; i++) // 10 segundos
draw.color = 0x228B22
draw.rect(0, 200, 320, 40, true) // el suelo
draw.color = 0xFFD700
draw.polygon(estrella, true)
draw.color = 0xFF4040
draw.circle(bolaX, bolaY, 12, true) // la bola, rellena
draw.color = 0xFFFFFF
draw.circle(bolaX, bolaY, 12) // y su contorno
draw.line(0, 199, 319, 199)
draw.text(8, 8, "Draw", 0xFFFFFF)
Display.update()
bolaX = bolaX + vx * Display.dt
bolaY = bolaY + vy * Display.dt
if (bolaX < 12 or bolaX > 308) then vx = 0 - vx end
if (bolaY < 12 or bolaY > 188) then vy = 0 - vy end
end
Display.close()
Trampas y problemas conocidos
- En Windows,
line,circle,polygonygetPixelno cuentan desde el view. Usan coordenadas de la pantalla. Con un view más pequeño que la pantalla (Display.viewWidth), salen desplazados respecto arect,ellipse,texty las imágenes. Sin view, o con el view del tamaño de la pantalla, no se nota. - En Windows,
draw.bytesestira la imagen a toda la ventana (ver bytes). - En Windows,
draw.tilemapno ve lo que cambiagrid.clear(). Sigue pintando el mapa de antes hasta el siguientegrid.set(). Después de unclear, haz unsetde cualquier celda. - El orden de los sprites de
draw.scenecambia al quitar uno. Al quitar un sprite de la escena, el último de la lista pasa a su hueco: desde entonces se pinta antes que otros a los que antes tapaba. Lo cuenta Capas, del editor de escenas.
Todos los problemas conocidos de JARU, juntos, están en Problemas conocidos.