Draw shapes immediately against a renderer

BASIC 7.0's graphics verbs -- DRAW, BOX, CIRCLE, PAINT, SSHAPE and GSHAPE -- all
plot against the current screen right now rather than adding to a scene, and
draw.h had exactly one function in it. This adds akgl_draw_point, _line, _rect,
_filled_rect, _circle, _flood_fill, _copy_region and _paste_region, each taking
the akgl_RenderBackend the host already initialized.

Color is an argument rather than a current-color global, so there is no second
copy of state to disagree with the caller's own, and each call restores the
renderer's draw color when it is done. SDL3 has no circle and no flood fill: the
circle is a midpoint circle, and the fill reads the target back, walks the
region with a fixed 4096-entry span stack, and blits back only the bounding box
of what changed. Exhausting that stack reports AKERR_OUTOFBOUNDS and says in the
header that the region is left partially filled.

tests/draw.c draws into a 64x64 software renderer under the dummy video driver
and reads the pixels back, so it needs no display and no offscreen harness.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-31 07:05:36 -04:00
parent 1ddc64010a
commit dba0f8db89
5 changed files with 1214 additions and 0 deletions

29
TODO.md
View File

@@ -683,6 +683,35 @@ a consumer is the wanted outcome. Each entry says what the BASIC verb needs, wha
harness described under "Remaining work": render a known shape, read the target back, and
compare against a reference surface with the existing `akgl_compare_sdl_surfaces`.
**Resolved.** `include/akgl/draw.h` now declares `akgl_draw_point`, `_line`,
`_rect`, `_filled_rect`, `_circle`, `_flood_fill`, `_copy_region` and
`_paste_region`, all taking the `akgl_RenderBackend *` the host initialized,
in the shape of `akgl_render_2d_draw_texture`. Decisions worth knowing:
- **Color is an argument, not state.** There is no current-color global to
get out of step with the caller's own. Each call saves and restores the
renderer's draw color, so drawing a line does not change what the host's
next `SDL_RenderClear` paints. `tests/draw.c` asserts that.
- **The circle is a midpoint circle**, integer arithmetic with eight-way
symmetry, plotted eight points per step through `SDL_RenderPoints`.
- **The flood fill reads the target back, fills on the CPU, and blits only
the bounding box of what changed.** It keeps a fixed
`AKGL_DRAW_MAX_FLOOD_SPANS` (4096) stack of horizontal runs at file scope
rather than recursing per pixel; running out reports `AKERR_OUTOFBOUNDS`
and leaves the region partially filled, which is stated in the header. It
is therefore not reentrant — neither is anything else that draws to a
single `SDL_Renderer`.
- `_copy_region` allocates when `*dest` is `NULL` and otherwise copies into
the caller's surface, matching `akgl_get_json_string_value` and friends. A
region that would be clipped by the target edge is refused rather than
silently returning a smaller surface.
`tests/draw.c` draws into a 64x64 software renderer under the dummy video
driver and reads pixels back, so it did not need the offscreen harness. That
harness is still wanted for `src/renderer.c`, `src/text.c` and `src/assets.c`.
`akgl_draw_background` is untouched and still outside the error protocol
(item 35).
3. **No audio API at all.** `SDL3_mixer` is a vendored dependency and `registry.h` declares
`AKGL_REGISTRY_MUSIC`, but there is no `src/audio.c`, no `include/akgl/audio.h`, and no
`akgl_*` symbol that opens a mixer, loads a chunk, or plays a note. BASIC 7.0's sound verbs