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 give you a pixel-addressable canvas that lives at an arbitrary position in the window. The canvas is backed by a flat byte array — one byte per pixel — where each byte holds a palette index (0–15). You manipulate this buffer directly or through the provided drawing primitives, then call GUpdateImage to flush changes to the screen. A dirty-tracking mechanism ensures only modified pixels are re-transferred on each update, keeping the operation efficient even for large canvases.

GCreateImage

void GCreateImage(int id, int x, int y, int w, int h);
Allocates a new image element and all of its backing storage: a w × h byte pixel buffer (data, zero-initialized), a matching w × h byte previous-frame buffer (prev, used for dirty tracking), and an XImage for X11 compositing. Any existing element at id is freed first.
id
int
required
Element slot index. If an element already exists at this ID it is freed and replaced.
x
int
required
X coordinate of the image’s top-left corner within the window.
y
int
required
Y coordinate of the image’s top-left corner within the window.
w
int
required
Width of the image canvas in pixels.
h
int
required
Height of the image canvas in pixels.
The pixel buffer is zero-initialized, so all pixels start at palette index 0. The XImage data buffer is also zero-initialized, meaning the image will appear as solid color 0 until you paint it and call GUpdateImage.

GGetImageData

unsigned char* GGetImageData(int id);
Returns a pointer to the element’s raw pixel buffer. The buffer is w * h bytes, laid out in row-major order: pixel at column x, row y is at offset y * w + x. Write palette indices (0–15) into this buffer, then call GUpdateImage to push the changes to the screen.
id
int
required
Element slot index of the image to access.
Return value: An unsigned char* pointing to the first byte of the w * h pixel buffer. This is not a copy — it is a direct pointer into the element’s internal storage. The pointer remains valid until the element is deleted with GDeleteElement.
Writing values outside the range 0–15 into the pixel buffer produces undefined rendering results — palette lookups index into a 16-entry color table.

GUpdateImage

void GUpdateImage(int id);
Transfers modified pixels from the pixel buffer into the underlying XImage by scanning every pixel and comparing it against the previous-frame buffer (prev). Only pixels whose value differs from prev are re-encoded into the XImage, and prev is updated to match. This dirty-tracking keeps repeated partial updates efficient.
id
int
required
Element slot index of the image to update.
GUpdateImage only writes to the XImage in memory. The result becomes visible on screen during the next GRenderWindow call (or GSimpleWindowLoop frame), which blits the XImage onto the window’s back buffer.

GClearImage

void GClearImage(int id, int c);
Fills the entire pixel buffer (data) with the single palette index c using memset. Only the data buffer is cleared; the prev buffer is not touched, so a subsequent GUpdateImage will detect every pixel as dirty and re-transfer the full canvas.
id
int
required
Element slot index of the image to clear.
c
int
required
Palette index (0–15) to fill every pixel with.
Call GClearImage at the start of every frame followed by your draw calls and a single GUpdateImage at the end — this gives a clean slate each frame without re-allocating any buffers.

GRedrawAllImages

void GRedrawAllImages();
Forces every image element in the scene to fully retransfer its pixel data on the next update pass by setting all bytes of each image’s prev buffer to 255 and immediately calling GUpdateImage. This bypasses the normal dirty-tracking optimization. It is called automatically by GPaletteModify (when redraw is non-zero) and GPaletteReset because existing XImage pixel data encodes old color values that are no longer valid after a palette change.
You rarely need to call GRedrawAllImages directly. It is provided for cases where the X11 pixel cache may have become stale through external means.

Drawing Primitives

The following functions draw directly into an image element’s pixel buffer. They do not call GUpdateImage — you must call GUpdateImage yourself after finishing all draw operations for a frame. All coordinates are relative to the image canvas’s own top-left corner (0, 0), not the window.

GPrimitiveRect

void GPrimitiveRect(int id, int x, int y, int w, int h, int fg, int bg);
Draws a rectangle into the image’s pixel buffer. The outermost ring of pixels uses fg as the border color; all interior pixels use bg as the fill color. Pixels outside the image bounds are clipped silently. Passing -1 for fg causes the border pixels to be drawn in bg instead (making the entire rectangle a solid fill). Passing -1 for bg skips the interior pixels entirely (hollow rectangle — only the border is drawn). If both are -1, nothing is drawn.
id
int
required
Element slot index of the target image.
x
int
required
X coordinate of the rectangle’s top-left corner, relative to the image canvas.
y
int
required
Y coordinate of the rectangle’s top-left corner, relative to the image canvas.
w
int
required
Width of the rectangle in pixels.
h
int
required
Height of the rectangle in pixels.
fg
int
required
Palette index for the border. Pass -1 to draw the border in bg color (solid rectangle).
bg
int
required
Palette index for the interior fill. Pass -1 to skip the fill (hollow rectangle — border only).
The behavior of fg=-1 differs from bg=-1. Passing fg=-1 does not skip the border — it sets the border color equal to bg, producing a solid filled rectangle. Only bg=-1 skips pixels (the interior), producing a hollow outline.

GPrimitiveCircle

void GPrimitiveCircle(int id, int cx, int cy, int r, int fg, int bg);
Draws a circle into the image buffer using a filled raster scan. The border ring consists of pixels whose squared distance from the center falls between (r-1)² and inclusive (one pixel wide). Interior pixels have squared distance less than (r-1)². Pass -1 for fg to skip the border, or -1 for bg to skip the fill. Pixels outside the image bounds are clipped.
id
int
required
Element slot index of the target image.
cx
int
required
X coordinate of the circle’s center, relative to the image canvas.
cy
int
required
Y coordinate of the circle’s center, relative to the image canvas.
r
int
required
Radius of the circle in pixels.
fg
int
required
Palette index for the border ring. Pass -1 to skip the border (filled circle only).
bg
int
required
Palette index for the interior fill. Pass -1 to draw only the border ring.

GPrimitiveLine

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 incremental floating-point stepping. The number of steps equals max(|dx|, |dy|), and each step advances by fractional amounts in both axes with coordinates rounded to the nearest pixel. Pixels outside the image bounds are skipped.
id
int
required
Element slot index of the target image.
x1
int
required
X coordinate of the line’s start point, relative to the image canvas.
y1
int
required
Y coordinate of the line’s start point, relative to the image canvas.
x2
int
required
X coordinate of the line’s end point, relative to the image canvas.
y2
int
required
Y coordinate of the line’s end point, relative to the image canvas.
color
int
required
Palette index (0–15) for the line color.

GPrimitiveSprite

void GPrimitiveSprite(int id, int sx, int sy, int color, const char* sprite, int scale);
Draws a monochrome sprite encoded as a compact RLE string at position (sx, sy) within the image canvas. Each foreground (#) pixel is painted with color; transparent (.) pixels write palette index 0. The scale parameter multiplies every logical pixel into a scale × scale block, enabling integer-scaled sprites without separate assets.
id
int
required
Element slot index of the target image.
sx
int
required
X coordinate of the sprite’s top-left corner, relative to the image canvas.
sy
int
required
Y coordinate of the sprite’s top-left corner, relative to the image canvas.
color
int
required
Palette index (0–15) used to paint foreground (#) pixels.
sprite
const char*
required
Null-terminated RLE sprite string. See the format reference below.
scale
int
required
Pixel scale factor. 1 = 1:1, 2 = each logical pixel becomes a 2×2 block, etc.

Sprite String Format

The sprite string is processed character-by-character from left to right. An optional numeric prefix accumulates a repeat count before a command character. Digits accumulate a decimal count; a non-digit command character consumes the count (defaulting to 1 if no prefix was given).
TokenMeaning
#Draw one foreground pixel (palette index = color parameter), advance X by scale.
.Draw one transparent pixel (palette index 0), advance X by scale.
$Newline: reset X to sx, advance Y by scale. A preceding count N advances Y by N × scale.
>Skip right: advance X by scale without drawing. A preceding count N advances X by N × scale.
N<cmd>Repeat command <cmd> N times (e.g. 5# = five foreground pixels).
N[...]Repeat the group [...] N times (e.g. 3[#.] = #.#.#.). Groups may nest arbitrarily.
!End of sprite — processing stops immediately.
The > skip token does not draw any pixels — it only advances the X cursor. For example, 4> moves the cursor 4 logical pixels to the right (i.e., 4 × scale actual pixels) without writing anything.
Terminate every sprite string with ! to make the end of data explicit and guard against accidentally processing trailing characters in the string literal.

Example

The example below creates a 128×128 image canvas, clears it to a dark background, draws a filled circle and a diagonal line, then updates the image each frame. A second primitive call adds a hollow rectangle border.
#include "libLWXGL.h"

#define ID_CANVAS 0

void draw_frame(int frame) {
    /* Clear to dark background (palette index 1) */
    GClearImage(ID_CANVAS, 1);

    /* Filled circle: center (64,64), radius 40.
       fg=10 (bright green border), bg=2 (dark green fill) */
    GPrimitiveCircle(ID_CANVAS, 64, 64, 40, 10, 2);

    /* Diagonal line across the full canvas: white (index 15) */
    GPrimitiveLine(ID_CANVAS, 0, 0, 127, 127, 15);

    /* Hollow rectangle border around the canvas edge.
       fg=7 (light gray border), bg=-1 (no fill) */
    GPrimitiveRect(ID_CANVAS, 0, 0, 128, 128, 7, -1);

    /* Flush dirty pixels to the XImage */
    GUpdateImage(ID_CANVAS);
}

int main(void) {
    GCreateWindow(256, 256, "Image Demo", 0);

    /* 128x128 canvas positioned at (64, 64) in the window */
    GCreateImage(ID_CANVAS, 64, 64, 128, 128);

    GSimpleWindowLoop(60, draw_frame);
    GDeleteWindow();
    return 0;
}

Build docs developers (and LLMs) love