Skip to content

Graphics, Images & Colors

Vulpis features a custom OpenGL rendering pipeline that handles everything from hardware-accelerated rounded rectangles to asynchronous texture streaming. This page covers how to style your UI using the Color API, how to display images, and how the engine intelligently manages GPU memory.

1. The Color API

Vulpis supports two primary ways to define colors in your UI styles: HEX Strings and RGBA Tables.

The engine natively parses these formats and applies them to properties like color (for text), BGColor (for backgrounds), and borderColor.

Supported Formats

  1. HEX Strings: Supports both standard 6-character ("#RRGGBB") and 8-character with alpha ("#RRGGBBAA") formats. The # symbol is optional.
  2. RGBA Tables: An array of integers from 0-255: { Red, Green, Blue, Alpha }. If Alpha is omitted, it defaults to 255 (fully opaque).

Example: Styling with Colors

lua
local el = require("utils.core.elements")

function ColorCard()
    return el.VBox({
        style = {
            w = 300, h = 150,
            padding = 20,
            
            -- Using a HEX string for the background
            BGColor = "#282c34", 
            
            -- Using an RGBA table for a 50% transparent white border
            borderColor = { 255, 255, 255, 128 }, 
            borderWidth = 2,
            borderRadius = 10
        },
        children = {
            el.Text({
                text = "Hello, Colors!",
                style = { 
                    -- Using an 8-character HEX for 80% opacity text
                    color = "#61afefCC", 
                    fontSize = 24 
                }
            })
        }
    })
end

2. Texture & Image Registry

Vulpis does not load images blindly. It routes all image requests through a native C++ TextureRegistry that handles asynchronous loading, caching, and automatic memory management.

How the Registry Works

  • Async Loading: When you request an image, the UI thread never freezes. Vulpis loads local files or downloads web URLs on a background thread. While loading, the engine displays a pulsating skeleton loader automatically.
  • Web & Local Caching: Web images (e.g., https://...) are downloaded and saved to your OS cache directory. Local assets are baked into compressed .vtex files for near-instant GPU upload.
  • Ref-Counting (Memory Management): The engine tracks exactly how many UI nodes are using a specific image. If you change an image's src or destroy the node, Vulpis automatically decreases the reference count. When the count hits zero, the texture is purged from GPU memory automatically.

Using the Image Element

The el.Image component is used to display standalone images. Local file paths are automatically resolved relative to your project root, and remote images can be loaded natively by providing a web URL.

lua
local el = require("utils.core.elements")

function AvatarDisplay()
    return el.Image({
        -- Works with local assets or web URLs!
        src = "https://example.com/user_avatar.png",
        style = { 
            w = 120, h = 120, 
            borderRadius = 60 -- Perfectly round avatar
        }
    })
end

Using Background Images (BGImage)

Any standard container (like Box, VBox, or HBox) can have a background image applied to it using the BGImage style property.

lua
local el = require("utils.core.elements")

function HeroBanner()
    return el.Box({
        style = {
            w = "100%", h = 300,
            -- Applies an image behind the children
            BGImage = "textures/landscape.png",
            alignItems = "center",
            justifyContent = "center"
        },
        children = {
            el.Text({ text = "Welcome to Vulpis", style = { color = "#FFFFFF" } })
        }
    })
end

3. Object Fit (Scaling Images)

When an image's source file doesn't perfectly match the width and height you set in your UI, you need to tell Vulpis how to scale it.

You control this using the fit property for Image elements, and the BGFit property for background images.

Scaling Modes

  • fill (Default for Images): The image is stretched or squished to exactly match the width and height of the UI node, ignoring the original aspect ratio.
  • cover (Default for BGImage): The image scales up to completely fill the UI node while maintaining its aspect ratio. Any excess parts of the image that overflow the boundaries are cleanly cropped off.
  • contain: The image scales down so that the entire picture is visible inside the UI node without losing its aspect ratio, leaving empty space (letterboxing) on the sides or top if necessary.

Example: Object Fit in Action

lua
local el = require("utils.core.elements")

function ImageGallery()
	local myImage = "https://example.com/user_avatar.png"

	return el.VBox({
		style = { gap = 20, overflow = "scroll", flexShrink = 1 },
		children = {
			el.Image({
				src = myImage,
				style = { w = 600, h = 300, fit = "fill" },
			}),

			el.Image({
				src = myImage,
				style = { w = 600, h = 300, fit = "cover" },
			}),

			el.Box({
				style = {
					w = 600,
					h = 600,
					BGColor = "#000000",
					BGImage = myImage,
					BGFit = "contain",
				},
			}),
		},
	})
end