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.

Image elements provide a pixel-level canvas you can write to with palette color indices, then flush to screen with UpdateImage. Unlike other LWXGL elements that are declarative, image elements expose a raw byte buffer so you can implement any rendering logic — from simple fills to custom bitmap fonts — entirely in C.

Creating an Image Canvas

Use CreateImage to allocate an image element:
void CreateImage(int id, int x, int y, int w, int h);
The id parameter is an integer slot used to reference this element in all subsequent calls. x and y are the element’s position within the window. Passing 0 or a negative value for w or h tells LWXGL to compute the dimension automatically so the image fills to the right (or bottom) edge of the back-buffer, offset by the given amount. For example, CreateImage(0, 10, 10, 0, -10) creates an image that fills the full remaining width and leaves a 10-pixel gap at the bottom.
/* Full-window canvas */
CreateImage(0, 0, 0, 0, 0);

/* Fixed-size 200×100 canvas at (20, 40) */
CreateImage(1, 20, 40, 200, 100);

The Image Data Buffer

GetImageData returns a pointer to the raw pixel buffer for an image element:
unsigned char *GetImageData(int id);
The buffer is a flat, row-major array of w × h bytes. Each byte is a palette index in the range 015 (mapping to LWXGL’s 16-color palette). Index 0 corresponds to CLR_BLACK, index 15 to CLR_WHITE, and so on. To update the screen, write index values into the buffer and then call UpdateImage:
void UpdateImage(int id);
UpdateImage performs a dirty-pixel comparison against an internal shadow copy. Only pixels that have changed since the last call are transferred to the X11 pixmap, so calling it every frame even on a mostly-static image is inexpensive.
unsigned char *pixels = GetImageData(0);
int w = 320, h = 240;

/* Paint a diagonal gradient */
for (int y = 0; y < h; y++)
    for (int x = 0; x < w; x++)
        pixels[y * w + x] = (x + y) % 16;

UpdateImage(0);

Clearing an Image

ClearImage fills the entire data buffer with a single palette index in one call:
void ClearImage(int id, int color_index);
This is equivalent to memset-ing the buffer but uses the element’s actual dimensions automatically. Call UpdateImage afterward to push the cleared canvas to screen.
ClearImage(0, CLR_BLACK);   /* fill with black */
UpdateImage(0);

Primitive Drawing Functions

LWXGL provides several built-in drawing routines that write directly into an image element’s data buffer. None of them call UpdateImage — you must do that yourself after composing a frame.

PrimitiveRect

void PrimitiveRect(int id, int x, int y, int w, int h, int fg, int bg);
Draws a filled rectangle. fg is the border (outline) color and bg is the fill color. If fg is CLR_NONE (-1), it is treated as equal to bg — the border is drawn in the fill color rather than skipped. Pass CLR_NONE for bg to skip filling interior pixels entirely, drawing only the border.
/* Solid filled rectangle with white border and blue fill */
PrimitiveRect(0, 10, 10, 80, 50, CLR_WHITE, CLR_BLUE);

/* Uniform rectangle — border drawn in fill color (fg becomes bg) */
PrimitiveRect(0, 10, 10, 80, 50, CLR_NONE, CLR_BLUE);

/* Border only, no fill */
PrimitiveRect(0, 100, 10, 80, 50, CLR_RED, CLR_NONE);

PrimitiveCircle

void PrimitiveCircle(int id, int cx, int cy, int r, int fg, int bg);
Draws a filled circle centered at (cx, cy) with radius r. fg is the border color and bg is the fill color. Pass CLR_NONE (-1) for fg to skip drawing the border, or for bg to skip drawing the fill.
/* Filled circle with border */
PrimitiveCircle(0, 160, 120, 40, CLR_YELLOW, CLR_ORANGE);

PrimitiveLine

void PrimitiveLine(int id, int x1, int y1, int x2, int y2, int color);
Draws a single-pixel line from (x1, y1) to (x2, y2) using incremental interpolation. Pixels outside the image bounds are clipped silently.
PrimitiveLine(0, 0, 0, 319, 239, CLR_GREEN);  /* diagonal across canvas */

PrimitiveTriangle

void PrimitiveTriangle(int id, int x1, int y1, int x2, int y2, int x3, int y3, int fg, int bg);
Draws a filled triangle defined by three vertices. fg is the edge color (drawn with PrimitiveLine along each side) and bg is the fill color. Pass CLR_NONE for either to omit it.
PrimitiveTriangle(0, 160, 20, 60, 180, 260, 180, CLR_WHITE, CLR_CYAN);

PrimitiveSprite

void PrimitiveSprite(int id, int sx, int sy, int color, const char *sprite, int scale);
Draws a monochrome sprite encoded as an RLE string (see Sprite RLE Format below). sx and sy are the top-left draw position. color is the palette index used for filled pixels — background pixels ('.') are always drawn as CLR_BLACK (index 0). scale is an integer pixel multiplier: 1 for 1:1, 2 for double-size, and so on.
const char *arrow = "2>#$#>3#$5#$#>3#$2>#";
PrimitiveSprite(0, 50, 50, CLR_WHITE, arrow, 2);

Sprite RLE Format

Sprites passed to PrimitiveSprite use a compact run-length encoding string. The characters and their meanings are:
CharacterMeaning
#Draw a filled pixel in color
.Draw a background pixel (CLR_BLACK)
$Move to the start of the next row
>Advance (skip) one pixel without drawing
09Repeat count prefix for the next token
[]Repeat the enclosed group (count prefix applies)
A digit or sequence of digits before a character or group sets the repeat count for that token. A repeat count of zero means the default of 1.
3#    →  ###          (three filled pixels)
2.    →  ..           (two background pixels)
3>    →               (skip three pixels)
2[3#.]→  ###.###.     (group "3#." repeated twice)
Example — a 5×5 cross:
/* Renders:
 *   ..#..
 *   ..#..
 *   #####
 *   ..#..
 *   ..#..
 */
const char *cross = "2>#$2>#$5#$2>#$2>#";
PrimitiveSprite(0, 10, 10, CLR_WHITE, cross, 1);
Breaking down the string token by token:
  • 2> — skip 2 pixels
  • # — draw 1 filled pixel
  • $ — move to the next row (reset x to sx, advance y by scale)
  • 5# — draw 5 filled pixels on row 3
  • $ — move to the next row, and so on
The '!' character acts as an early-terminator inside the RLE parser and will stop sprite rendering if it appears in the string. Do not use '!' in sprite data.

Per-Pixel Transform

ApplyPixelFunc iterates every pixel in an image and replaces its color index with the value returned by a user-supplied function:
void ApplyPixelFunc(int id, int (*f)(int x, int y, int current_color));
The callback receives the pixel’s x and y coordinates plus its current palette index. The return value is taken modulo 16, so it is safe to return any integer. This is useful for palette cycling, brightness shifts, or procedural effects.
int invert(int x, int y, int c) {
    return 15 - c;   /* invert all colors */
}

ApplyPixelFunc(0, invert);
UpdateImage(0);

Custom Font Rendering

LWXGL supports per-image bitmap fonts for rendering text into image canvases. Setting the font:
void SetImageFont(int id, unsigned char *font_buffer, int height);
font_buffer must be a 256 × height-byte array where each character glyph occupies height bytes. Each byte encodes one row of 8 pixels, MSB first (bit 7 is the leftmost pixel). The buffer is copied internally, so it is safe to free or reuse after the call. Rendering text:
void DrawString(int id, int x, int y, const char *text, int color);
Renders text at pixel position (x, y) using the font set by SetImageFont. Characters are spaced 9 pixels apart. A newline character ('\n') advances the cursor down by height pixels and resets the x position to x. Call UpdateImage after drawing to push the result to screen.
SetImageFont(0, my_font_8x8, 8);
DrawString(0, 4, 4, "Hello, LWXGL!", CLR_WHITE);
UpdateImage(0);

Complete Example

The following example creates a 320×240 image canvas, draws a filled rectangle and a circle onto it, then enters the main loop.
#include "libLWXGL.h"

void on_frame(int tick, float dt) {
    /* Drawing happens once before the loop; nothing to update per frame */
    (void)tick; (void)dt;
}

int main(void) {
    CreateWindow(320, 240, "Primitives Demo", CLR_BLACK);

    /* Create a full-window image canvas (element id 0) */
    CreateImage(0, 0, 0, 320, 240);

    /* Draw a blue rectangle with a white border */
    PrimitiveRect(0, 20, 20, 120, 80, CLR_WHITE, CLR_BLUE);

    /* Draw a filled yellow circle with a red border */
    PrimitiveCircle(0, 220, 120, 50, CLR_RED, CLR_YELLOW);

    /* Push both draws to screen */
    UpdateImage(0);

    MainWindowLoop(60, on_frame);
    TerminateWindow();
    return 0;
}

Build docs developers (and LLMs) love