libgpx is a hand-optimized 1-bit-per-pixel graphics library for Z80 targets.
Primary target today is ZX Spectrum (src/zx).
Partner backend (src/partner) is still in progress.
The public API contract is defined in:
include/libgpx.h
- A strict, portable drawing API for 1bpp framebuffers.
- A ZX Spectrum backend implemented in hand-written Z80 assembly.
- Emulator-based behavioral testing that compares:
- real backend output
- independent oracle/stub output
- Size-focused build flow (ROM-oriented).
include/libgpx.h: public API and data formats.src/zx/: ZX Spectrum implementation.src/partner/: Partner WIP implementation.tests/: emulator harness, ZX tests, visuals, coverage, size entry.archive/: old snapshots (not used by current build).build/,bin/: generated artifacts.
make build -j1: build library plus ZX and Partner test binaries.make tests -j1: run the host ZX emulator suite and Partner emulator tests.make stub-visuals -j1: render stub API scene artifacts tobin/stub-visuals/.make lib-visuals -j1: render stacked real-vs-oracle comparisons tobin/lib-visuals/.make coverage -j1: run host coverage and write.gcovfiles tobuild/coverage/.make lib-size -j1: estimate library payload size using baseline subtraction.make clean: remove generated build artifacts.
- Screen geometry:
256x192pixels. - Pixel VRAM:
0x4000..0x57ff(6144 bytes). - Attribute VRAM exists separately (
0x5800..), but libgpx drawing primitives are 1bpp pixel operations. - Coordinates are signed (
coord), but visible screen space is[0..255] x [0..191].
clipparameters are optional rectangles (rect_t *).rect_tcorners are inclusive (x0,y0throughx1,y1).CO_FOREmeans set pixel bit,CO_BACKmeans clear pixel bit.BM_XORtoggles destination bits regardless ofcolor.- All rendering is clipped to the physical screen in ZX backend.
coord:int16_tsigned coordinate.dim:uint16_tdimension.color:uint8_t(CO_FORE,CO_BACK).bmode:uint8_t(BM_CPY,BM_XOR).gmode:uint8_t(GPXM_DEFAULTcurrently defined).
CO_FORE = 0x01CO_BACK = 0x00BM_CPY = 0x00BM_XOR = 0x01
typedef struct point_s {
coord x;
coord y;
} point_t;
typedef struct rect_s {
coord x0;
coord y0;
coord x1;
coord y1;
} rect_t;Structure plain samples:
point_t p = { .x = 12, .y = 34 };
rect_t clip = { .x0 = 10, .y0 = 10, .x1 = 100, .y1 = 80 };struct gpx_s {
uint16_t width;
uint16_t height;
uint8_t pages;
};Meaning:
width: pixel width.height: pixel height.pages: number of framebuffer pages available.
On ZX this is 256, 192, 1.
Derived values can be computed as stride = width / 8 and size = stride * height.
Signature helpers:
BMP_SIG(enc)BMP_ENC(sig)BMP_STRIDE(sig)BMP_SIG_STRIDE(enc, stride)
Encodings:
BMP_ENC_1BPPBMP_ENC_1BPP_MASKBMP_ENC_TINYS_BMP(standard 1bpp signature)
Bitmap structure:
typedef struct bmp_s {
uint8_t signature;
uint8_t w;
uint8_t h;
uint16_t size;
uint8_t bitmap[];
} bmp_t;Notes:
- bitmap encoding lives in the high nibble of
signature - stride is encoded in the low nibble as
(stride - 1) - masked 1bpp payloads store
(AND, OR)row data inbitmap[] - some masked cursor assets append hotspot bytes after
bitmap[size]
Structure plain sample (static packed bitmap wrapper):
struct bmp8x8_s {
uint8_t signature;
uint8_t w;
uint8_t h;
uint16_t size;
uint8_t bitmap[8];
};
static struct bmp8x8_s checker = {
BMP_SIG_STRIDE(BMP_ENC_1BPP, 1), 8, 8, 8,
{0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55}
};Header fields:
flagsfirst_asciilast_asciiempty_widthmax_glyph_widthglyph_heightadvancedescentdata[]
Flags:
FONT_FLAG_PROPORTIONALFONT_FLAG_OFFSETS_BEFONT_FLAG_VECTOR
Serialized layout:
- bytes
[0..7]: header - bytes
[8..]: glyph offset table + glyph bitmap payloads
Stock bitmap IDs:
GPXSB_CURSOR_CLASSICGPXSB_CURSOR_STDGPXSB_CURSOR_HOURGLASSGPXSB_CURSOR_CARETGPXSB_CURSOR_HAND
Purpose:
- Initialize graphics subsystem/context.
Parameters:
mode: initialization mode (GPXM_DEFAULTavailable).
Returns:
- context pointer (
gpx_t *).
Special cases:
- ZX backend currently uses a static singleton context.
- ZX backend currently clears screen during create.
- On ZX,
modeis currently ignored.
Sample:
gpx_t *g = gpx_create(GPXM_DEFAULT);Purpose:
- Tear down graphics context.
Parameters:
gpx: context pointer.
Returns:
- no return value.
Special cases:
- ZX backend is static; destroy is effectively a no-op.
- Passing
NULLis safe in current ZX behavior.
Sample:
gpx_destroy(g);
gpx_destroy(NULL);Purpose:
- Select display page and/or write page on paged backends.
Parameters:
op: bitmask ofPG_DISPLAY,PG_WRITE, or both.page: target page id (0or1on Partner).
Returns:
- no return value.
Notes:
- Partner supports two pages (
gpx->pages == 2). - ZX backend currently treats this call as a no-op (
gpx->pages == 1). - Current Partner implementation is still WIP and only some page use-cases are exercised by tests.
Purpose:
- Get current display width.
Returns:
- width in pixels.
Special cases:
- ZX:
256after normal init.
Purpose:
- Get current display height.
Returns:
- height in pixels.
Special cases:
- ZX:
192after normal init.
Purpose:
- Clear active screen/framebuffer.
Returns:
- no return value.
Special cases:
- ZX clears pixel VRAM and resets attributes/border to defaults.
Sample:
gpx_clrscr();Purpose:
- Resolve stock bitmap by ID.
Parameters:
which: one ofGPXSB_*values.
Returns:
- bitmap pointer, or
NULLif unsupported.
Special cases:
- Invalid IDs return
NULL.
Sample:
bmp_t *caret = gpx_get_stock_bmp(GPXSB_CURSOR_CARET);Purpose:
- Get default UI/system font.
Returns:
- font pointer.
Sample:
const font_t *sys = gpx_get_system_font();Purpose:
- Get tiny font for compact labels.
Returns:
- font pointer.
Sample:
const font_t *tiny = gpx_get_tiny_font();Purpose:
- Measure rendered text width in pixels.
Parameters:
text: zero-terminated string.font: font blob pointer.
Returns:
- measured width.
Special cases:
text == NULLorfont == NULLreturns0.- Missing/non-representable glyphs use font
empty_width. - Width accumulation uses glyph width + font
advancefor drawn glyphs.
Sample:
coord w = gpx_measure_text("HELLO", gpx_get_system_font());void gpx_draw_text(gpx_t *gpx, coord x, coord y, const char *text, const font_t *font, color c, bmode m, const rect_t *clip)
Purpose:
- Draw text using serialized font glyph bitmaps.
Parameters:
gpx: graphics context.x,y: text baseline/start position used by backend renderer.text: zero-terminated string.font: serialized font data.c: draw color (CO_FORE/CO_BACK).m: mode (BM_CPY/BM_XOR).clip: optional clip rectangle.
Returns:
- no return value.
Special cases:
text == NULLorfont == NULLis a no-op.- Unsupported/missing glyph entries advance by
empty_width. - Glyph draw path uses bitmap renderer (
gpx_draw_bmp).
Sample:
const font_t *f = gpx_get_system_font();
rect_t clip = {0, 0, 255, 191};
gpx_draw_text(g, 8, 16, "HELLO", f, CO_FORE, BM_CPY, &clip);Purpose:
- Draw one pixel.
Parameters:
gpx: graphics context.x,y: pixel position.c:CO_FOREto set,CO_BACKto clear.m:BM_CPYorBM_XOR.clip: optional clipping rectangle.
Returns:
- no return value.
Special cases:
- Out-of-screen coordinates are ignored.
- Clip rejection skips draw.
- In
BM_XOR, color is ignored and destination is toggled.
Sample:
gpx_draw_pixel(g, 10, 10, CO_FORE, BM_CPY, NULL);uint8_t gpx_draw_line(gpx_t *gpx, coord x0, coord y0, coord x1, coord y1, color c, bmode m, uint8_t lpatt, const rect_t *clip)
Purpose:
- Draw line segment with optional pattern.
Parameters:
gpx: graphics context.x0,y0,x1,y1: endpoints.c,m: color and blend mode.lpatt: 8-bit line pattern (0xFF= solid).clip: optional clipping rectangle.
Returns:
- rotated pattern state after drawing, for chaining segments.
Special cases:
- Degenerate line (
x0==x1 && y0==y1) draws at most one pixel. - Internal dispatch uses optimized hline/vline/bresenham paths.
Sample:
uint8_t patt = 0xF0;
patt = gpx_draw_line(g, 0, 0, 100, 30, CO_FORE, BM_CPY, patt, NULL);
patt = gpx_draw_line(g, 100, 30, 140, 50, CO_FORE, BM_CPY, patt, NULL);Purpose:
- Draw rectangle outline.
Parameters:
gpx: graphics context.r: rectangle (inclusive corners).c,m: color and blend mode.lpatt: line pattern.clip: optional clipping rectangle.
Returns:
- no return value.
Special cases:
- ZX normalizes rectangle coordinates (
x0/x1,y0/y1) internally. - ZX top/bottom use
lpatt; left/right are drawn solid in current backend. r == NULLis a no-op.
Sample:
rect_t r = {40, 20, 120, 80};
gpx_draw_rectangle(g, &r, CO_FORE, BM_CPY, 0xFF, NULL);void gpx_fill_rectangle(gpx_t *gpx, rect_t *r, color c, bmode m, uint8_t *fpatt, uint8_t fpatt_len, const rect_t *clip)
Purpose:
- Fill rectangle using repeating row-pattern bytes.
Parameters:
gpx: graphics context.r: rectangle (inclusive corners).c,m: color and blend mode.fpatt: row pattern table.fpatt_len: number of pattern rows.clip: optional clipping rectangle.
Returns:
- no return value.
Special cases:
fpatt_len == 0is a no-op.- ZX normalizes rectangle coordinates internally.
- Pattern is consumed MSB-first from
x0, row-by-row.
Sample:
uint8_t pat[2] = {0xAA, 0x55};
rect_t r = {10, 10, 60, 40};
gpx_fill_rectangle(g, &r, CO_FORE, BM_CPY, pat, 2, NULL);Purpose:
- Blit bitmap at position.
Parameters:
gpx: graphics context.x,y: top-left destination.b: source bitmap.clip: optional clipping rectangle.
Returns:
- no return value.
Special cases:
b == NULLis a no-op.- ZX currently supports
BMP_ENC_1BPPandBMP_ENC_1BPP_MASK. - Unclipped, in-range unmasked blits use a fast path; clipped/masked/edge cases use fallback.
- Partner currently accepts tiny move-stream bitmaps (
BMP_ENC_TINY) and stock tiny cursor assets; raster 1bpp payloads are ignored there.
Sample:
gpx_draw_bmp(g, 32, 24, (bmp_t *)&checker, NULL);#include "libgpx.h"
void main(void)
{
gpx_t *g = gpx_create(GPXM_DEFAULT);
rect_t clip = {0, 0, 255, 191};
gpx_clrscr();
gpx_draw_pixel(g, 10, 10, CO_FORE, BM_CPY, &clip);
gpx_draw_line(g, 20, 20, 100, 40, CO_FORE, BM_CPY, 0xFF, &clip);
rect_t box = {30, 50, 120, 90};
gpx_draw_rectangle(g, &box, CO_FORE, BM_CPY, 0xFF, &clip);
const font_t *font = gpx_get_system_font();
gpx_draw_text(g, 8, 8, "libgpx", font, CO_FORE, BM_CPY, &clip);
__asm
halt
__endasm;
}The header comment mentions additional primitive categories (for example circles/polygons), but the currently exported public API is exactly what is declared in include/libgpx.h and documented above.