Saltar al contenido principal

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.

Captura del módulo Draw de JARU para dibujar texto, líneas, rectángulos, círculos, bitmaps y sprites

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.

ColorValorEjemplo
Rojo0xFF0000draw.color = 0xFF0000
Verde0x00FF00draw.color = 0x00FF00
Azul0x0000FFdraw.color = 0x0000FF
Blanco0xFFFFFFdraw.color = 0xFFFFFF
Negro0x000000draw.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​

WindowsESP32 / ESP32-S3ESP32-P4ESP32-S31
fontNo cambia nada: el texto sale siempre con la misma fuenteElige una de las fuentes 0 a 8IgualIgual
Errores de fontLanza la excepciónNo lanza: escribe el error en el monitor serieIgualIgual
getPixelEl color de lo pintadoAproximado: en el doble buffer, 256 coloresAproximado: 16 bitsAproximado: 16 bits
clsBorra el viewBorra el view; en el modo directo, la pantallaBorra el viewBorra el view
line, circle, polygon y getPixelEn coordenadas de la pantalla, no del viewDel viewDel viewDel view
bytesEstira la imagen a toda la ventanaEn x, yEn x, yEn x, y

Las dos últimas filas son fallos de la VM de Windows: mira Trampas.

Funciones​

FunciónQué haceDevuelve
cls([color])Borra con un colornil
setPixel(x, y)Pinta un puntonil
getPixel(x, y)Lee el color de un puntoEl color
line(x1, y1, x2, y2)Pinta una líneanil
rect(x, y, ancho, alto [, relleno])Pinta un rectángulonil
circle(x, y, radio [, relleno])Pinta un círculonil
ellipse(x, y, radioX, radioY [, relleno])Pinta una elipsenil
polygon(vertices [, relleno])Pinta un polígonotrue
text(x, y, texto [, color])Escribe un textonil
font() / font(n)Lee o cambia la fuente del textoLa fuente / true
bitmap(x, y, bitmap [, angulo])Pinta una imagen, con su transparentenil
bitmapOpaque(x, y, bitmap [, angulo])Pinta una imagen sin transparentenil
bitmapBatch(bitmap, posiciones)Pinta una imagen en muchos sitiosnil
gridBitmap(grid, valor, bitmap, x0, y0, pasoX, pasoY)Pinta una imagen en las celdas de un GridMap con un valornil
bytes(x, y, ancho, alto, bytes, paleta)Pinta un bloque de Bytes con una paletanil
sprite(sprite)Pinta un sprite, o una lista o un array de spritesnil
tilemap(grid [, tileset [, vacio]])Pinta un mapa de tilesnil
scene(escena)Pinta una escena: su mapa y sus spritesnil

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 un update(), 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 0x123456 se lee 0x103452.
  • 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ámetroDescripción
x1, y1El punto de inicio
x2, y2El 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ámetroTipoDescripción
x, ynúmeroLa esquina de arriba a la izquierda
anchonúmeroEl ancho
altonúmeroEl alto
rellenotrue / falsetrue 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ámetroTipoDescripción
x, ynúmeroEl centro
radionúmeroEl radio
rellenotrue / falsetrue 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ámetroTipoDescripción
x, ynúmeroEl centro
radioX, radioYnúmeroLos radios horizontal y vertical
rellenotrue / falsetrue 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ámetroTipoDescripción
verticeslistaLas coordenadas, en orden: [x1, y1, x2, y2, x3, y3, ...]
rellenotrue / falsetrue 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)
info

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ámetroDescripción
x, yLa esquina de arriba a la izquierda del texto
textoUna cadena
colorEl 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())
  • texto tiene que ser una cadena: con un número no escribe nada. Pásalo a cadena con toString().
  • 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. Deja draw.text para 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)
FuenteDescripciónAltoCaracteres
0Fuente pequeña (mapa de bits 6x8)8 pxASCII completo
1Fuente por defecto (alias de 0)8 pxASCII completo
2Fuente mediana16 px96 caracteres ASCII
4Fuente grande26 px96 caracteres ASCII
6Fuente para relojes48 px0-9 : - . a p m
7Fuente de 7 segmentos48 px0-9 : - .
8Fuente enorme75 px0-9 : - .
Rango válido

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.

Fuentes 6, 7 y 8

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ámetroDescripción
x, yLa esquina de arriba a la izquierda
bitmapLa imagen, de Bitmap.load
anguloGrados 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ámetroDescripción
bitmapLa imagen
posicionesUn 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.load o tcolor), como en draw.bitmap.
Rendimiento

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ámetroDescripción
gridEl GridMap
valorUn entero de 0 a 255: el valor que tiene que tener la celda
bitmapLa imagen que se pinta en esas celdas
x0, y0Dónde va, en el view, la celda (0, 0)
pasoX, pasoYLa 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ámetroDescripción
x, yLa esquina de arriba a la izquierda
ancho, altoLas medidas de la imagen en píxeles
objetoBytesUn objeto Bytes con un byte por píxel, fila a fila: el número del color en la paleta
paletaUn 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, ancho y alto tienen 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 error ERR_TYPE_MISMATCH, y si el objeto Bytes tiene menos de ancho × alto bytes, ERR_INDEX_OUT_OF_RANGE. En los dos casos no pinta nada.
En Windows, la imagen ocupa toda la ventana

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ámetroDescripción
spriteUn sprite
listaSpritesUna 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 = true y al menos un fotograma. En una lista, se salta lo que no es un sprite.
Animaciones

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ámetroDescripción
gridEl GridMap, con el tamaño del tile puesto (grid.tSize(ancho, alto))
tilesetLa imagen con los tiles (opcional si el mapa tiene uno en grid.tileset)
vacioEl 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.x y grid.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 wrapX o wrapY, 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.load o tcolor).
  • En Windows, el mapa entero se guarda ya pintado, y se vuelve a pintar cuando cambia una celda con set.
Scroll

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ámetroDescripción
escenaUn 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, lanza ERR_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.
tip

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​

PropiedadTipoDe serieLectura / escritura
colorentero 0xRRGGBB0xFFFFFF (blanco)Las dos
colorBGentero 0xRRGGBB0x000000 (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, polygon y getPixel no cuentan desde el view. Usan coordenadas de la pantalla. Con un view más pequeño que la pantalla (Display.viewWidth), salen desplazados respecto a rect, ellipse, text y las imágenes. Sin view, o con el view del tamaño de la pantalla, no se nota.
  • En Windows, draw.bytes estira la imagen a toda la ventana (ver bytes).
  • En Windows, draw.tilemap no ve lo que cambia grid.clear(). Sigue pintando el mapa de antes hasta el siguiente grid.set(). Después de un clear, haz un set de cualquier celda.
  • El orden de los sprites de draw.scene cambia 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.