Direct Drawing
OpenBOR direct drawing lets scripts place text, sprites, dots, lines, boxes, and complete off-screen surfaces into the video output. Two related paths are available: queued drawing, which joins the engine's normal depth-sorted render queue, and target-screen drawing, which changes a selected screen immediately.
This article describes the direct-draw API, resource ownership, ordering, color and blend handling, drawmethod control, and off-screen composition.
Parts of the system
Direct drawing uses four main objects.
| Object | Purpose |
|---|---|
| Sprite | Image data loaded by loadsprite() or supplied by the engine.
|
| Sprite queue | Depth-sorted list used by drawsprite(), drawline(), drawbox(), and the other queued functions.
|
| Screen | Pixel surface used as a drawing target. The engine's main surface is vscreen; script-created surfaces are commonly called subscreens.
|
| Drawmethod | Shared transform and color state used for scaling, flipping, rotation, clipping, tinting, transparency, and other effects. |
Queued drawing and target-screen drawing
Queued functions record a command for later rendering. The engine sorts those commands with its other render entries, then draws them during the normal video pass. Calls must be submitted again on every frame where the result should remain visible.
Target-screen functions write pixels immediately. Their results remain on a script-created screen until another operation overwrites the pixels, clearscreen() clears them, or free() releases the screen.
| Queued function | Immediate target-screen equivalent | Key difference |
|---|---|---|
drawstring()
|
drawstringtoscreen()
|
Queued text accepts a Z value; target-screen text follows call order. |
drawsprite()
|
drawspritetoscreen()
|
Queued sprites accept Z and sort ID values; target-screen sprites do not. |
drawdot()
|
drawdottoscreen()
|
Queued dots accept Z; target-screen dots are written immediately. |
drawline()
|
drawlinetoscreen()
|
Queued lines accept Z; target-screen lines follow call order. |
drawbox()
|
drawboxtoscreen()
|
Queued boxes accept Z; target-screen boxes follow call order. |
drawscreen()
|
Not applicable | Places an already-drawn screen into the queue for composition. |
Coordinates and order
Screen coordinates begin at the upper-left corner. X increases toward the right, while Y increases toward the bottom.
Queued entries sort by Z first and sort ID second. Larger values render later and therefore appear above entries with smaller values. Sort ID is available only to drawsprite(); it provides a stable secondary order when several sprites share the same Z value.
Immediate target-screen operations have no Z value. Later calls overwrite earlier pixels where the results overlap.
Colors and blend modes
Use rgbcolor() to create packed colors in the format expected by the current platform.
int red = rgbcolor(255, 0, 0);
int green = rgbcolor(0, 255, 0);
int blue = rgbcolor(0, 0, 255);
int charcoal = rgbcolor(32, 36, 40);
Each component normally ranges from 0 to 255. Raw hexadecimal RGB literals are less portable because the packed channel order can differ by platform.
Shape functions and drawscreen() accept an optional blend mode.
| Constant | Effect |
|---|---|
BLEND_MODE_MODEL
|
Uses the applicable model or drawmethod behavior. |
BLEND_MODE_NONE
|
Copies the source without blending. |
BLEND_MODE_ALPHA
|
Screen-style lightening blend. |
BLEND_MODE_ALPHA_NEGATIVE
|
Multiply-style darkening blend. |
BLEND_MODE_OVERLAY
|
Overlay blend. |
BLEND_MODE_HARDLIGHT
|
Hard-light blend. |
BLEND_MODE_DODGE
|
Color-dodge blend. |
BLEND_MODE_AVERAGE
|
Equal average of source and destination. |
int blend = openborconstant("BLEND_MODE_ALPHA");
drawline(20, 40, 180, 40, 1000, rgbcolor(255, 192, 64), blend);
Loading and releasing sprites
loadsprite
void sprite = loadsprite("data/sprites/interface/marker.gif");
void masked = loadsprite("data/sprites/interface/glow.gif",
"data/sprites/interface/glow_mask.gif");
Each call creates a separate sprite outside the normal sprite cache. Load reusable artwork once during setup instead of loading it every frame.
Transparent borders are trimmed while the sprite is prepared. Internal offsets preserve the visible artwork's position relative to the X and Y values passed to a draw function. Mask artwork should match the source image's dimensions and transparent layout.
Inspecting a sprite
getgfxproperty() reads dimensions and other information from sprites, screens, and bitmaps.
int width = getgfxproperty(sprite, "width");
int height = getgfxproperty(sprite, "height");
int centerx = getgfxproperty(sprite, "centerx");
int centery = getgfxproperty(sprite, "centery");
Supported property names include width, height, centerx, centery, srcwidth, srcheight, palette, pixelformat, and pixel. The pixel property also needs X and Y arguments.
int sample = getgfxproperty(sprite, "pixel", 4, 6);
Coordinates outside the image return 0.
Lifetime example
Store loaded pointers when multiple scripts need the same resource. Release every script-owned sprite with free() when its useful lifetime ends.
level.c
void main()
{
void marker = loadsprite("data/sprites/interface/marker.gif");
setglobalvar("direct_draw_marker", marker);
}
updated.c
void main()
{
void marker = getglobalvar("direct_draw_marker");
if(!isempty(marker)) {
drawsprite(marker, 160, 100, 1000, 0);
}
}
endlevel.c
void main()
{
void marker = getglobalvar("direct_draw_marker");
if(!isempty(marker)) {
free(marker);
setglobalvar("direct_draw_marker", NULL());
}
}
Never pass an engine-owned sprite or screen to free(). Only objects returned to the script by allocation or loading functions belong to the script.
Queued drawing functions
drawsprite
drawsprite(sprite, x, y, z, sortid);
Queues a sprite at x, y, and z. The optional sortid defaults to 0. Current common drawmethod settings are copied into the queue entry when the call is made.
drawsprite(marker, 160, 100, 1000, 0);
drawstring
drawstring(x, y, font, value, z);
Queues text using the selected font index. The value is converted to text, and the optional Z value defaults to 0.
drawstring(12, 12, 0, "READY", 1000);
drawstring(12, 24, 0, getglobalvar("score"), 1000);
drawdot
drawdot(x, y, z, color, blend);
Queues one pixel. The optional blend argument may be omitted.
drawline
drawline(x1, y1, x2, y2, z, color, blend);
Queues a line between two screen coordinates. The optional blend argument may be omitted.
int white = rgbcolor(255, 255, 255);
drawline(20, 30, 140, 30, 1000, white);
drawbox
drawbox(x, y, width, height, z, color, blend);
Queues a filled rectangle. The optional blend argument may be omitted.
int back = rgbcolor(16, 20, 28);
int blend = openborconstant("BLEND_MODE_ALPHA");
drawbox(8, 8, 144, 36, 990, back, blend);
drawscreen
drawscreen(screen, x, y, z, blend);
Queues a complete screen at the requested position and depth. The optional blend argument changes more than the blend mode: supplying it also makes zero-valued black pixels transparent. Omitting it preserves the current common drawmethod behavior, so black pixels remain opaque unless background transparency is already enabled there.
// Opaque zero-valued background unless the common drawmethod says otherwise.
drawscreen(panel, 0, 160, 1000);
// Zero-valued background becomes transparent.
int blend = openborconstant("BLEND_MODE_ALPHA");
drawscreen(panel, 0, 160, 1000, blend);
Screens and off-screen composition
allocscreen
void screen = allocscreen(width, height);
Creates a script-owned 32-bit screen and clears its pixels to zero. Internal allocation rounds the requested width down to a multiple of four, so widths divisible by four are the safest choice.
void panel = allocscreen(320, 80);
Use openborvariant("vscreen") to obtain the engine-owned main video surface. Never release vscreen with free().
clearscreen
clearscreen(screen);
Fills the target's pixel buffer with zero. Script-created screens retain previous drawing until cleared or overwritten.
Target-screen functions
drawstringtoscreen(screen, x, y, font, value);
drawspritetoscreen(sprite, screen, x, y);
drawdottoscreen(screen, x, y, color, blend);
drawlinetoscreen(screen, x1, y1, x2, y2, color, blend);
drawboxtoscreen(screen, x, y, width, height, color, blend);
The target screen is the first argument for text and shapes. drawspritetoscreen() is the exception: its sprite comes first and its target screen comes second. Blend arguments are required by the target-screen dot, line, and box functions; use BLEND_MODE_NONE when no blending is wanted.
drawspritetoscreen() and the target-screen shape functions use the current common drawmethod. drawstringtoscreen() prints directly with the selected font and does not use that drawmethod.
Composition example
The following setup creates a retained panel, redraws its contents, then places the result into the normal Z-sorted queue.
level.c
void main()
{
void panel = allocscreen(320, 80);
void icon = loadsprite("data/sprites/interface/icon.gif");
setglobalvar("direct_draw_panel", panel);
setglobalvar("direct_draw_icon", icon);
}
updated.c
void main()
{
void panel = getglobalvar("direct_draw_panel");
void icon = getglobalvar("direct_draw_icon");
int none = openborconstant("BLEND_MODE_NONE");
int alpha = openborconstant("BLEND_MODE_ALPHA");
clearscreen(panel);
drawboxtoscreen(panel, 4, 4, 312, 72, rgbcolor(24, 28, 40), none);
drawlinetoscreen(panel, 4, 4, 315, 4, rgbcolor(224, 224, 255), none);
drawspritetoscreen(icon, panel, 12, 12);
drawstringtoscreen(panel, 52, 16, 0, "MISSION STATUS");
// Supplying a blend also treats the cleared black area as transparent.
drawscreen(panel, 0, 160, 1000, alpha);
}
endlevel.c
void main()
{
void panel = getglobalvar("direct_draw_panel");
void icon = getglobalvar("direct_draw_icon");
if(!isempty(panel)) free(panel);
if(!isempty(icon)) free(icon);
setglobalvar("direct_draw_panel", NULL());
setglobalvar("direct_draw_icon", NULL());
}
Common drawmethod
The common drawmethod is global rendering state shared by compatible direct-draw calls. Obtain it and the read-only default state through system variants.
void common = openborvariant("drawmethod_common");
void defaults = openborvariant("drawmethod_default");
Use set_drawmethod_property() with named property constants. Useful properties include the following groups.
| Purpose | Property constants |
|---|---|
| Enable and flip flags | DRAWMETHOD_PROPERTY_CONFIG
|
| Position and pivot | DRAWMETHOD_PROPERTY_CENTER_X, DRAWMETHOD_PROPERTY_CENTER_Y
|
| Scale and rotation | DRAWMETHOD_PROPERTY_SCALE_X, DRAWMETHOD_PROPERTY_SCALE_Y, DRAWMETHOD_PROPERTY_ROTATE
|
| Blend and fill | DRAWMETHOD_PROPERTY_ALPHA, DRAWMETHOD_PROPERTY_FILL_COLOR
|
| Color channels and tint | DRAWMETHOD_PROPERTY_CHANNEL_RED, DRAWMETHOD_PROPERTY_CHANNEL_GREEN, DRAWMETHOD_PROPERTY_CHANNEL_BLUE, DRAWMETHOD_PROPERTY_TINT_COLOR, DRAWMETHOD_PROPERTY_TINT_MODE
|
| Clip rectangle | DRAWMETHOD_PROPERTY_CLIP_POSITION_X, DRAWMETHOD_PROPERTY_CLIP_POSITION_Y, DRAWMETHOD_PROPERTY_CLIP_SIZE_X, DRAWMETHOD_PROPERTY_CLIP_SIZE_Y
|
| Repetition and span | DRAWMETHOD_PROPERTY_REPEAT_X, DRAWMETHOD_PROPERTY_REPEAT_Y, DRAWMETHOD_PROPERTY_SPAN_X, DRAWMETHOD_PROPERTY_SPAN_Y
|
| Palette remap | DRAWMETHOD_PROPERTY_COLORSET_INDEX, DRAWMETHOD_PROPERTY_COLORSET_TABLE
|
| Water effects | DRAWMETHOD_PROPERTY_WATER_MODE and the perspective, size, wave, speed, and time properties.
|
Configuration flags are combined with the bitwise OR operator.
| Flag | Purpose |
|---|---|
DRAWMETHOD_CONFIG_ENABLED
|
Enables common drawmethod processing. |
DRAWMETHOD_CONFIG_BACKGROUND_TRANSPARENCY
|
Treats zero-valued background pixels as transparent. |
DRAWMETHOD_CONFIG_FLIP_X
|
Flips horizontally. |
DRAWMETHOD_CONFIG_FLIP_Y
|
Flips vertically. |
DRAWMETHOD_CONFIG_FLIP_ROTATE
|
Changes flip handling for rotated output. |
Scale values use 256 as 100 percent. The next example flips a sprite horizontally, scales it to 150 percent, queues it, then restores the shared state.
void common = openborvariant("drawmethod_common");
void defaults = openborvariant("drawmethod_default");
int config = openborconstant("DRAWMETHOD_CONFIG_ENABLED")
| openborconstant("DRAWMETHOD_CONFIG_FLIP_X");
copy_drawmethod(common, defaults);
set_drawmethod_property(common,
openborconstant("DRAWMETHOD_PROPERTY_CONFIG"),
config);
set_drawmethod_property(common,
openborconstant("DRAWMETHOD_PROPERTY_SCALE_X"),
384);
set_drawmethod_property(common,
openborconstant("DRAWMETHOD_PROPERTY_SCALE_Y"),
384);
drawsprite(sprite, 160, 100, 1000, 0);
copy_drawmethod(common, defaults);
Queued entries receive a copy of the common drawmethod at submission time. Restoring the common state immediately after drawsprite() therefore does not undo the queued sprite's transform.
Legacy scripts may use setdrawmethod() and changedrawmethod(). Named drawmethod properties are clearer for new code and reduce dependence on positional arguments or string property names.
Queue control
drawspriteq
drawspriteq(screen, newonly, minz, maxz, dx, dy);
Renders entries from the current sprite queue to a selected screen. Passing an empty first argument targets vscreen. Optional arguments default to all entries, the full Z range, and zero offset.
void snapshot = getglobalvar("direct_draw_snapshot");
drawspriteq(snapshot, 0, 500, 1500, 0, 0);
The function does not remove rendered entries. Its newonly option matters when the queue is locked; ordinary scripts normally use 0.
clearspriteq
clearspriteq();
Clears the active sprite queue, or only its new portion while the queue is locked. Normal game rendering shares this queue with backgrounds, entities, shadows, and interface elements, so routine scripts should not call clearspriteq(). Reserve it for controlled rendering workflows where removing every pending entry is intentional.
Script timing
Queued and immediate calls behave differently around the engine's frame clear.
| Script stage | Direct-draw behavior |
|---|---|
update.c
|
Suitable for queueing commands. Immediate writes to vscreen are erased by the main screen clear that follows.
|
| Native render setup | Clears vscreen and queues backgrounds, entities, shadows, and status elements.
|
updated.c
|
Suitable for queueing with final knowledge of game state. Immediate writes to vscreen survive because the main clear has already happened.
|
| Queue render | Sorts entries by Z and sort ID, then draws them to vscreen.
|
| Frame end | Presents the video output and clears the queue for the next frame. |
Queued calls made from either update.c or updated.c still take part in the final Z sort. Direct writes to a retained subscreen can be made at other times because that surface is not part of the automatic main-screen clear.
Limits and performance
The current sprite queue holds up to 5,000 entries. Extra submissions are ignored once the queue is full. Text also consumes queue entries because each printed glyph is represented as a sprite.
Sprite loading and screen allocation are setup operations, not frame-by-frame drawing operations. Reusing allocations avoids file access, decoding work, heap churn, and leak risk.
Large subscreens cost memory and fill bandwidth, but also consume just one sprite each in the que. Keep off-screen surfaces near the size of the content being composed, use widths divisible by four, and redraw only when their contents need to change.
Practical guidance
- Load script-owned sprites once, retain their pointers, and release them with
free(). - Use queued functions when Z ordering with entities or interface elements matters.
- Use target-screen functions to build reusable panels, effects, masks, or snapshots.
- Clear a subscreen before rebuilding it unless retained pixels are intentional.
- Create colors with
rgbcolor()and select blends with named constants. - Treat the common drawmethod as shared state: configure it, submit the draw, then restore the default state.
- Supply
drawscreen()'s blend argument when zero-valued background pixels should become transparent. - Keep immediate
vscreendrawing in a stage after the engine's main clear, such asupdated.c. - Avoid
clearspriteq()during normal gameplay because native rendering uses the same queue. - Resubmit queued artwork every frame where it should remain visible.