Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Dressedalarm184/lwxgl/llms.txt

Use this file to discover all available pages before exploring further.

LWXGL image elements are palette-indexed pixel buffers. Each pixel stores a single byte — a palette index 0–15 — rather than a direct color value. You draw into the buffer using primitive functions or by writing bytes directly, then call GUpdateImage to push changes to the backing XImage for rendering. The renderer blits the XImage to the back-buffer on every frame, so pixels that have already been flushed persist until you overwrite them.

Creating an Image Canvas

void GCreateImage(int id, int x, int y, int w, int h);
Allocates a w × h canvas positioned at (x, y). Internally this creates an XImage of the same dimensions along with two parallel byte arrays — one for the current pixel data and one shadow copy used by GUpdateImage to detect changes.
ParameterDescription
idElement ID.
x, yTop-left position on screen in pixels.
w, hCanvas width and height in pixels.

Raw pixel buffer access

unsigned char* GGetImageData(int id);
Returns a pointer to the raw pixel buffer (w × h bytes, row-major, left-to-right top-to-bottom). Each byte is a palette index 0–15. You can read or write this buffer directly.

Flushing changes to the screen

void GUpdateImage(int id);
Walks every pixel in the buffer, compares it against the shadow copy, and calls XImage::put_pixel for any pixel that changed. The shadow copy is updated to match. Call this after any modification to the pixel buffer — whether through primitives or direct writes.

Clearing the canvas

void GClearImage(int id, int c);
Fills the entire pixel buffer with palette index c using a single memset call. This is faster than drawing a full-canvas rectangle primitive.

Complete creation example

#include "libLWXGL.h"

void setup(void) {
    // Create a 200×200 canvas at (10, 10)
    GCreateImage(0, 10, 10, 200, 200);

    // Clear the canvas to black (palette index 0)
    GClearImage(0, 0);

    // Draw a white filled rectangle
    GPrimitiveRect(0, 20, 20, 160, 160, -1, 15);

    // Flush all changes to the XImage so they appear on screen
    GUpdateImage(0);
}

Drawing Primitives

The primitive functions write directly into the element’s pixel buffer (img->data). You do not need to call GUpdateImage before calling primitives — but you must call it afterwards for the changes to appear on screen.

Rectangle

void GPrimitiveRect(int id, int x, int y, int w, int h, int fg, int bg);
ParameterDescription
idImage element ID.
x, yTop-left corner of the rectangle relative to the canvas.
w, hRectangle width and height in pixels.
fgPalette index for the outline color. Pass -1 to skip the outline.
bgPalette index for the fill color. Pass -1 for no fill.
Pixels outside the canvas bounds are silently clipped.

Circle

void GPrimitiveCircle(int id, int cx, int cy, int r, int fg, int bg);
ParameterDescription
idImage element ID.
cx, cyCenter of the circle relative to the canvas.
rRadius in pixels.
fgBorder color palette index (-1 to skip border).
bgFill color palette index (-1 for no fill).
The border is one pixel wide (pixels whose squared distance from the center falls within the ring (r−1)² ≤ d² ≤ r²).

Line

void GPrimitiveLine(int id, int x1, int y1, int x2, int y2, int color);
Draws a straight line from (x1, y1) to (x2, y2) using uniform incremental stepping. Pixels outside the canvas are skipped.
ParameterDescription
idImage element ID.
x1, y1Start point relative to the canvas.
x2, y2End point relative to the canvas.
colorPalette index for the line color.

Combined example

// Canvas already created as element 0 (200×200)

// Blue filled circle with a red outline at center (100, 100), radius 60
GPrimitiveCircle(0, 100, 100, 60, 12, 9);   // 12 = red, 9 = blue

// White diagonal line through the circle
GPrimitiveLine(0, 40, 40, 160, 160, 15);    // 15 = white

// Flush all changes
GUpdateImage(0);

Sprite Rendering

void GPrimitiveSprite(int id, int sx, int sy, int color,
                      const char* sprite, int scale);
Draws an RLE-encoded monochrome sprite starting at canvas position (sx, sy) using a single palette color for filled pixels. Integer scaling is supported — each logical pixel is drawn as a scale × scale block.
ParameterDescription
idImage element ID.
sx, syTop-left origin of the sprite on the canvas.
colorPalette index used for filled (#) pixels.
spriteNull-terminated RLE-encoded sprite string.
scaleInteger scale factor (1 = 1:1, 2 = double size, etc.).

RLE encoding format

The sprite string is a compact run-length encoded description of a monochrome bitmap:
TokenMeaning
#Draw one filled pixel (using color) and advance one column.
.Draw one transparent pixel (palette index 0) and advance one column.
$Move to the next row (reset X to sx, advance Y by scale).
>Advance one column without drawing.
N (integer)Repeat the next symbol N times. E.g. 5# = five filled pixels.
[...]Group for repetition. Prefix with a count to repeat the group. E.g. 3[#.] = #.#.#.
!End of sprite.

5×5 cross sprite example

// A 5×5 cross shape:
//   . . # . .
//   . # . . .   <- actually centered column
//   # # # # #
//   . # . . .
//   . . # . .

const char* cross = "..#..$..#..$5#$.#.$..#..!";

// Draw it at (10, 10) in green (palette index 10), 1:1 scale
GPrimitiveSprite(0, 10, 10, 10, cross, 1);

// Draw it doubled at (80, 10)
GPrimitiveSprite(0, 80, 10, 10, cross, 2);

GUpdateImage(0);
More complex sprite using groups and repetition:
// 4-pixel wide horizontal stripe repeated 3 times, separated by blank rows
const char* stripes = "3[4#$4.$]!";
GPrimitiveSprite(0, 0, 0, 15, stripes, 1);
GUpdateImage(0);

Direct Pixel Access

For fine-grained control you can write to the pixel buffer directly. The buffer is row-major: pixel (x, y) lives at index y * width + x.
unsigned char *pixels = GGetImageData(0);

int width = 200; // must match the width passed to GCreateImage

// Set the pixel at (50, 75) to palette color 4
pixels[75 * width + 50] = 4;

// Draw a horizontal gradient across row 100
for (int x = 0; x < width; x++) {
    pixels[100 * width + x] = (unsigned char)(x / (width / 16));
}

// Flush all changes to the screen
GUpdateImage(0);
When animating, call GClearImage at the start of each frame before redrawing. This resets the buffer to a known state so stale pixels from the previous frame do not bleed through.
void on_frame(int tick) {
    GClearImage(0, 0);          // clear to black
    GPrimitiveCircle(0, tick % 200, 100, 20, -1, 9);
    GUpdateImage(0);
}
GRedrawAllImages() forces a full redraw of every image element by invalidating each element’s shadow copy (filling it with 0xFF) and then calling GUpdateImage on it. This is used internally after palette changes (GPaletteModify, GPaletteReset) to ensure all image pixels are re-composited with the new color values. You rarely need to call it directly.

Build docs developers (and LLMs) love