paint
Shapes, text and images drawn over the game for one frame. It lives at xiao.paint.
Shapes last one frame. Draw from a paint listener so they are issued again every frame.
Listeners
Listeners run in the order they were first added. A listener that raises an error is removed and the error is printed.
xiao.paint.listener.add(id: any, callback: function(w: number, h: number))
xiao.paint.listener.add(id: any, callback: function(w: number, h: number))
- Called every frame with the screen size.
- Adding an existing
idreplaces its callback.
xiao.paint.listener.remove(id: any): boolean
xiao.paint.listener.remove(id: any): boolean
- Returns
trueif the listener existed.
xiao.paint.listener.exists(id: any): boolean
xiao.paint.listener.exists(id: any): boolean
- Returns a boolean.
xiao.paint.add(id: any, callback: function(w: number, h: number))
xiao.paint.add(id: any, callback: function(w: number, h: number))
- Alias of
xiao.paint.listener.add.
xiao.paint.remove(id: any): boolean
xiao.paint.remove(id: any): boolean
- Alias of
xiao.paint.listener.remove.
xiao.paint.screen(): number, number
xiao.paint.screen(): number, number
- Returns
w, h.
Points
Any table with numeric x and y fields is accepted. A point that also has a numeric z field is a world position: it is projected to the screen every time it is drawn, and the shape is skipped when the point is behind the camera.
Sizes (size arguments) always use x and y in pixels, even when the origin is a world point.
xiao.paint.point.vector2(x: number, y: number): paint_point
xiao.paint.point.vector2(x: number, y: number): paint_point
- Returns a screen point
{x, y}in pixels.
xiao.paint.point.vector3(x: number, y: number, z: number): paint_point
xiao.paint.point.vector3(x: number, y: number, z: number): paint_point
- Returns a world point
{x, y, z}in blocks.
Colors
Any table with numeric r, g, b fields (0 to 255) and an optional a (default 255) is accepted. Fields can be changed at runtime, for example color.r = 128.
xiao.paint.color.rgb(r: number, g: number, b: number): paint_color
xiao.paint.color.rgb(r: number, g: number, b: number): paint_color
- Alpha is 255.
xiao.paint.color.rgba(r: number, g: number, b: number, a?: number = 255): paint_color
xiao.paint.color.rgba(r: number, g: number, b: number, a?: number = 255): paint_color
- Alpha defaults to 255.
xiao.paint.color.hex(hex: string): paint_color
xiao.paint.color.hex(hex: string): paint_color
#RRGGBBor#RRGGBBAA, the#is optional.
xiao.paint.color.hsv(h: number, s: number, v: number, a?: number): paint_color
xiao.paint.color.hsv(h: number, s: number, v: number, a?: number): paint_color
hin degrees,sandvfrom 0 to 1,afrom 0 to 255.
xiao.paint.color.hsl(h: number, s: number, l: number, a?: number): paint_color
xiao.paint.color.hsl(h: number, s: number, l: number, a?: number): paint_color
hin degrees,sandlfrom 0 to 1,afrom 0 to 255.
Shapes
Most shapes end with outline?: paint_color = {0, 0, 0, 255} and outline_thickness?: number = 0. With the default thickness of 0 no outline is drawn.
xiao.paint.line(p1: paint_point, p2: paint_point, color: paint_color, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
xiao.paint.line(p1: paint_point, p2: paint_point, color: paint_color, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Draws a line between two points.
xiao.paint.circle.filled(center: paint_point, color: paint_color, radius: number, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
xiao.paint.circle.filled(center: paint_point, color: paint_color, radius: number, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Draws a filled circle.
xiao.paint.circle.hollow(center: paint_point, color: paint_color, radius: number, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
xiao.paint.circle.hollow(center: paint_point, color: paint_color, radius: number, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Draws a hollow circle.
xiao.paint.tri.filled(p1: paint_point, p2: paint_point, p3: paint_point, color: paint_color, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
xiao.paint.tri.filled(p1: paint_point, p2: paint_point, p3: paint_point, color: paint_color, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Draws a filled triangle.
xiao.paint.tri.hollow(p1: paint_point, p2: paint_point, p3: paint_point, color: paint_color, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
xiao.paint.tri.hollow(p1: paint_point, p2: paint_point, p3: paint_point, color: paint_color, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Draws a hollow triangle.
xiao.paint.rect.filled(top_left: paint_point, size: paint_point, color: paint_color, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
xiao.paint.rect.filled(top_left: paint_point, size: paint_point, color: paint_color, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Draws a filled rectangle.
xiao.paint.rect.hollow(top_left: paint_point, size: paint_point, color: paint_color, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
xiao.paint.rect.hollow(top_left: paint_point, size: paint_point, color: paint_color, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Draws a hollow rectangle.
xiao.paint.rect.rfilled(rounding: number, top_left: paint_point, size: paint_point, color: paint_color, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
xiao.paint.rect.rfilled(rounding: number, top_left: paint_point, size: paint_point, color: paint_color, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Filled rectangle with rounded corners.
xiao.paint.rect.rhollow(rounding: number, top_left: paint_point, size: paint_point, color: paint_color, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
xiao.paint.rect.rhollow(rounding: number, top_left: paint_point, size: paint_point, color: paint_color, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Hollow rectangle with rounded corners.
xiao.paint.rect.gradient(top_left: paint_point, size: paint_point, up_left: paint_color, up_right: paint_color, bottom_left: paint_color, bottom_right: paint_color, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
xiao.paint.rect.gradient(top_left: paint_point, size: paint_point, up_left: paint_color, up_right: paint_color, bottom_left: paint_color, bottom_right: paint_color, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- One color per corner.
xiao.paint.qrot.filled(center: paint_point, size: paint_point, color: paint_color, rotation: number, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
xiao.paint.qrot.filled(center: paint_point, size: paint_point, color: paint_color, rotation: number, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Centered rectangle, rotation in degrees.
xiao.paint.qrot.hollow(center: paint_point, size: paint_point, color: paint_color, rotation: number, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
xiao.paint.qrot.hollow(center: paint_point, size: paint_point, color: paint_color, rotation: number, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Centered rectangle, rotation in degrees.
xiao.paint.quad.filled(p1: paint_point, p2: paint_point, p3: paint_point, p4: paint_point, color: paint_color, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
xiao.paint.quad.filled(p1: paint_point, p2: paint_point, p3: paint_point, p4: paint_point, color: paint_color, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Draws a filled quad.
xiao.paint.quad.hollow(p1: paint_point, p2: paint_point, p3: paint_point, p4: paint_point, color: paint_color, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
xiao.paint.quad.hollow(p1: paint_point, p2: paint_point, p3: paint_point, p4: paint_point, color: paint_color, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Draws a hollow quad.
xiao.paint.poly.filled(vertices: paint_point[], color: paint_color, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
xiao.paint.poly.filled(vertices: paint_point[], color: paint_color, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- 3 to 4096 vertices, concave shapes allowed.
xiao.paint.poly.hollow(vertices: paint_point[], color: paint_color, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
xiao.paint.poly.hollow(vertices: paint_point[], color: paint_color, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- 3 to 4096 vertices.
Text
Text is outlined only when the outline argument is given. Text longer than 1024 bytes is cut.
Baked fonts stay loaded until unbake, even across Clear VM. The current font goes back to "default" in a fresh VM.
xiao.paint.text.left(point: paint_point, text: string, color: paint_color, outline?: paint_color)
xiao.paint.text.left(point: paint_point, text: string, color: paint_color, outline?: paint_color)
pointis the left edge of the text.
xiao.paint.text.center(point: paint_point, text: string, color: paint_color, outline?: paint_color)
xiao.paint.text.center(point: paint_point, text: string, color: paint_color, outline?: paint_color)
pointis the horizontal center.
xiao.paint.text.right(point: paint_point, text: string, color: paint_color, outline?: paint_color)
xiao.paint.text.right(point: paint_point, text: string, color: paint_color, outline?: paint_color)
pointis the right edge.
xiao.paint.text.bake(font_name: string, font_data: string, size: number): boolean
xiao.paint.text.bake(font_name: string, font_data: string, size: number): boolean
- Loads a TTF or OTF font from memory, returns a boolean.
- A name already baked returns
true.
xiao.paint.text.unbake(font_name: string): boolean
xiao.paint.text.unbake(font_name: string): boolean
- Frees a baked font, returns
trueif it existed.
xiao.paint.text.isbaked(font_name: string): boolean
xiao.paint.text.isbaked(font_name: string): boolean
- Returns a boolean,
"default"is always baked.
xiao.paint.text.font(font_name?: string): string
xiao.paint.text.font(font_name?: string): string
- Selects the font used by the next text calls, returns the current font name.
xiao.paint.text.get_size(text: string): table
xiao.paint.text.get_size(text: string): table
- Returns
{width, height}in pixels with the current font.
xiao.paint.text.get_size(font_name: string, text: string): table
xiao.paint.text.get_size(font_name: string, text: string): table
- Same as above with a given font.
local data = xiao.fs.read("lua/fonts/consola.ttf")
if not xiao.paint.text.bake("Consolas", data, 16) then
error("could not load the font")
end
local point = xiao.paint.point.vector2(40, 40)
local white = xiao.paint.color.rgb(255, 255, 255)
xiao.paint.add("hello", function(w, h)
xiao.paint.text.font("Consolas")
xiao.paint.text.left(point, "hello world", white)
xiao.paint.text.font("default")
end)
See xiao.fs for xiao.fs.read.
Images
An image is freed automatically once Lua no longer references it, and on Clear VM.
xiao.paint.image.bake(data: string): image | false
xiao.paint.image.bake(data: string): image | false
datais a full image file in memory (PNG, JPEG, BMP...).- Returns an image or
false.
xiao.paint.image.bake(width: number, height: number, rgba: string): image | false
xiao.paint.image.bake(width: number, height: number, rgba: string): image | false
rgbaholdswidth * height * 4bytes of RGBA8 pixels, up to 8192 x 8192.- Returns an image or
false.
xiao.paint.image.normal(image: image, top_left: paint_point, size: paint_point, color: paint_color, outline?: paint_color)
xiao.paint.image.normal(image: image, top_left: paint_point, size: paint_point, color: paint_color, outline?: paint_color)
colortints the image, use white to keep it as is.
xiao.paint.image.round(image: image, rounding: number, top_left: paint_point, size: paint_point, color: paint_color, outline?: paint_color)
xiao.paint.image.round(image: image, rounding: number, top_left: paint_point, size: paint_point, color: paint_color, outline?: paint_color)
- Image with rounded corners.
local pixel = xiao.paint.image.bake(1, 1, "\255\0\0\255")
local origin = xiao.paint.point.vector2(50, 50)
local size = xiao.paint.point.vector2(64, 64)
local white = xiao.paint.color.rgb(255, 255, 255)
xiao.paint.add("square", function(w, h)
xiao.paint.image.normal(pixel, origin, size, white)
end)
Types
paint_point
paint_color
Good practice
Create colors and points once outside the listener and reuse them. Change their fields when they must move.
local paint = xiao.paint
local color = paint.color.rgba(255, 255, 255, 255)
local center = paint.point.vector2(0, 0)
paint.add("pulse", function(w, h)
color.r = (os.clock() % 1) * 255
center.x, center.y = w / 2, h / 2
paint.circle.filled(center, color, 8, paint.color.rgb(0, 0, 0), 1)
end)