Files
libakgl/include/akgl/draw.h

347 lines
18 KiB
C
Raw Normal View History

/**
* @file draw.h
* @brief Declares the public draw API.
*
* Immediate-mode plotting against whichever renderer the caller hands in. This
* is the shape a BASIC-style graphics vocabulary needs -- DRAW, BOX, CIRCLE,
* PAINT, SSHAPE and GSHAPE all say "put this on the screen now" rather than
* "add this to the scene" -- and it sits alongside the actor and tilemap
* rendering rather than replacing it.
*
* Every entry point takes its color as an argument instead of reading a
* current-color global. A caller that has a notion of a current color (a BASIC
* COLOR statement, say) already owns that state and does not need the library
* to keep a second copy that can disagree with it. The renderer's own draw
* color is saved and restored around each call, so drawing a line never changes
* what the next SDL_RenderClear() paints.
*/
Namespace every exported symbol, and bump to 0.5.0 Closes internal-consistency items 1 through 6, 12 and 13. Every include guard is _AKGL_<FILE>_H_, every in-project header include is angled, and every exported function, type and global carries the akgl_ prefix. This is an ABI break; the soname goes to libakgl.so.0.5. TODO.md carries the full rename table. The renames were driven by renaming each declaration and letting the compiler find the uses, not by pattern substitution: renderer, physics and camera are also parameter and struct-member names, and a sed would have rewritten map->physics and every akgl_RenderBackend *renderer parameter without a word. Item 4 turned out not to be cosmetic. The library exported a global called renderer and tests/character.c defined an SDL_Renderer *renderer of its own; the executable's definition preempted the library's, akgl_sprite_load_json read a SDL_Renderer * through an akgl_RenderBackend *, and every texture load in that suite failed. The suite reported success anyway, because libakerror's unhandled-error handler ends in exit(errctx->status), exit keeps only the low byte, and AKGL_ERR_SDL is exactly 256. So character had been green while running one of its four tests, and every suite in the tree was unable to fail on the most common status in a library built on SDL. Both are fixed. tests/testutil.h gains TEST_TRAP_UNHANDLED_ERRORS(), which collapses any status a byte cannot carry onto 1, and every suite installs it. character binds a real backend with akgl_render_2d_bind. Its fourth test then runs for the first time and fails on a defect it has asserted all along, so akgl_heap_release_character now walks state_sprites with AKGL_ITERATOR_OP_RELEASE and destroys the property set before zeroing the slot -- TODO.md Defects item 21 and half of Carried over item 1. AKGL_TIME_ONESEC_MS said "one second in milliseconds" and held 1000000, so akgl_game_state_lock waited roughly sixteen minutes rather than one second. It is AKGL_TIME_ONEMS_NS now, the budget is its own named constant, and tests/game.c holds the mutex from a second thread to assert the wait -- the contended path had no coverage at all. Headers are self-contained and it is enforced: AKGL_PUBLIC_HEADERS drives both install() and a generated translation unit per header, so a header that ships is a header that is checked. Writing that found registry.h, which used SDL_PropertiesID in eight declarations and included no SDL header. 23/23 suites pass, memcheck is clean, reindent --check is clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 23:32:21 -04:00
#ifndef _AKGL_DRAW_H_
#define _AKGL_DRAW_H_
#include <SDL3/SDL.h>
#include <akerror.h>
#include <akgl/renderer.h>
#include <akgl/types.h>
/**
* @brief Spans akgl_draw_flood_fill() will hold while walking a region.
*
* The fill keeps a fixed stack of horizontal runs still to be examined rather
* than recursing per pixel. A region complicated enough to need more than this
* many pending runs at once reports AKERR_OUTOFBOUNDS instead of overflowing;
* an ordinary convex or moderately concave shape needs a few dozen.
*/
#define AKGL_DRAW_MAX_FLOOD_SPANS 4096
/**
* @brief Triangles each corner of akgl_draw_filled_rounded_rect() is built from.
*
* A quarter circle approximated by this many segments is visually round at any
* radius a UI panel plausibly uses; the vertex arrays are fixed at this size,
* so it is a compile-time cost, not a per-call one.
*/
#define AKGL_DRAW_ROUNDED_RECT_SEGMENTS 16
/**
* @brief Most vertices akgl_draw_arc() will spend on one arc.
*
* An arc is a triangle strip between its inner and outer edge, two vertices
* per step, so half of these are positions along the curve. The step count
* scales with the swept angle and is clamped to fit this array -- a full
* circle gets all of them, a sliver gets a few -- rather than allocating.
*/
#define AKGL_DRAW_ARC_MAX_POINTS 64
/**
Document what the functions actually do instead of that they can fail The Doxygen comments were generated from the declarations, so 217 @throws lines across 21 headers read "When the corresponding validation or operation fails" and told a caller nothing beyond the status name. The @param lines were the same shape: every output was "Output destination populated by the function", every instance "Object to initialize, inspect, or modify". Rewritten against the implementations, following the pattern libakstdlib already uses: - @throws names the condition. akgl_sprite_load_json separated AKERR_KEY (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL and AKGL_ERR_HEAP, which it raises and never declared. - Parameters say whether they are required, what a NULL means, and what is written on a failure path. Where an argument is not checked, the doc says so: akgl_heap_next_actor's dest is a crash on NULL, not an error, and akgl_render_2d_frame_start dereferences self before testing it. - The conventions move up to the file blocks so the per-function docs stay short. json_helpers.h states once that absence is an error here and that json_t * results are borrowed; heap.h explains the pool model and the acquire asymmetry; physics.h carries the thrust/environmental/velocity table. - Struct fields, enum values, macros and exported globals are documented, including the dead ones - sprite_w/sprite_h, movetimer, p_scale and timer_gravity are read by nothing, and say so. Also fixes ten comments in error.h and audio.h that opened with /** rather than /**<, so Doxygen attached them to the following entity and rendered the text as part of the macro's value. Verified against the generated HTML. Comments only - no declaration changed. Doxygen builds clean under WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:02:20 -04:00
* @brief Paint an 8x8 grey checkerboard over a region, the way an image editor shows transparency.
*
Fix the type, macro and state-table defects, and the leftover debris Closes internal-consistency items 19 through 36, 38 and 41. Item 37, the ~180 redundant casts, is deliberately left open with its reasoning in TODO.md: the benefit only arrives once the build turns on the warnings those casts suppress, and doing it before that is churn across the two files with the most outstanding functional defects. The two that were real bugs are in the actor state table. AKGL_ACTOR_STATE_STRING_NAMES was declared [AKGL_ACTOR_MAX_STATES+1] and defined [32], so a consumer trusting the declared bound read past the object; and indices 11 and 12 were named UNDEFINED_11 and UNDEFINED_12 where actor.h has MOVING_IN and MOVING_OUT, so no character JSON could bind a sprite to either state. tests/registry.c now walks the whole table -- every entry non-NULL, every entry resolving to its own bit, no two entries sharing a name. The bitmask macros are parenthesized and AKGL_BITMASK_CLEAR has lost the semicolon inside its body. Writing tests/bitmasks.c for that turned up something worth knowing: the obvious test does not catch it. For a bit that is set, the misparse `!(mask & bit) == bit` gives the same answer as the correct one. It only diverges for an unset bit whose value is not 1, and that is the shape the suite uses now. akgl_draw_background was the last public function outside the error protocol. It takes a backend like everything else in draw.h, restores the draw colour it found, and is tested -- TODO.md had it filed under "needs the offscreen renderer harness", which was never true; what it needed was to stop reading the global. All eight registry initializers go through one helper, so the seven that leaked an SDL_PropertiesID on every call after the first no longer do. Fixed in the same place because it is the same function: akgl_registry_init never called akgl_registry_init_properties, which made akgl_set_property a silent no-op for anyone not going through akgl_game_init -- Defects, Known and still open item 3. Also: AKGL_COLLIDE_RECTANGLES (three open parens, two closes) and akgl_Frame deleted, float32_t/float64_t used consistently, the developer-specific debug logging removed from the controller inner loop, the abandoned SDL_GetBasePath comments removed, nine unused locals removed, and dst renamed to dest. akgl_game_update's default flags no longer OR the same bit twice. That changes nothing today, and the reason is Performance item 32: the loop never reads either bit, which is why every actor is updated sixteen times a frame. Still open. 25/25 pass, memcheck clean, reindent --check clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 00:15:36 -04:00
* A diagnostic backdrop rather than a general primitive -- `charviewer` uses it
* so a sprite's transparent pixels are visible instead of blending into black.
Document what the functions actually do instead of that they can fail The Doxygen comments were generated from the declarations, so 217 @throws lines across 21 headers read "When the corresponding validation or operation fails" and told a caller nothing beyond the status name. The @param lines were the same shape: every output was "Output destination populated by the function", every instance "Object to initialize, inspect, or modify". Rewritten against the implementations, following the pattern libakstdlib already uses: - @throws names the condition. akgl_sprite_load_json separated AKERR_KEY (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL and AKGL_ERR_HEAP, which it raises and never declared. - Parameters say whether they are required, what a NULL means, and what is written on a failure path. Where an argument is not checked, the doc says so: akgl_heap_next_actor's dest is a crash on NULL, not an error, and akgl_render_2d_frame_start dereferences self before testing it. - The conventions move up to the file blocks so the per-function docs stay short. json_helpers.h states once that absence is an error here and that json_t * results are borrowed; heap.h explains the pool model and the acquire asymmetry; physics.h carries the thrust/environmental/velocity table. - Struct fields, enum values, macros and exported globals are documented, including the dead ones - sprite_w/sprite_h, movetimer, p_scale and timer_gravity are read by nothing, and say so. Also fixes ten comments in error.h and audio.h that opened with /** rather than /**<, so Doxygen attached them to the following entity and rendered the text as part of the macro's value. Verified against the generated HTML. Comments only - no declaration changed. Doxygen builds clean under WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:02:20 -04:00
*
Fix the type, macro and state-table defects, and the leftover debris Closes internal-consistency items 19 through 36, 38 and 41. Item 37, the ~180 redundant casts, is deliberately left open with its reasoning in TODO.md: the benefit only arrives once the build turns on the warnings those casts suppress, and doing it before that is churn across the two files with the most outstanding functional defects. The two that were real bugs are in the actor state table. AKGL_ACTOR_STATE_STRING_NAMES was declared [AKGL_ACTOR_MAX_STATES+1] and defined [32], so a consumer trusting the declared bound read past the object; and indices 11 and 12 were named UNDEFINED_11 and UNDEFINED_12 where actor.h has MOVING_IN and MOVING_OUT, so no character JSON could bind a sprite to either state. tests/registry.c now walks the whole table -- every entry non-NULL, every entry resolving to its own bit, no two entries sharing a name. The bitmask macros are parenthesized and AKGL_BITMASK_CLEAR has lost the semicolon inside its body. Writing tests/bitmasks.c for that turned up something worth knowing: the obvious test does not catch it. For a bit that is set, the misparse `!(mask & bit) == bit` gives the same answer as the correct one. It only diverges for an unset bit whose value is not 1, and that is the shape the suite uses now. akgl_draw_background was the last public function outside the error protocol. It takes a backend like everything else in draw.h, restores the draw colour it found, and is tested -- TODO.md had it filed under "needs the offscreen renderer harness", which was never true; what it needed was to stop reading the global. All eight registry initializers go through one helper, so the seven that leaked an SDL_PropertiesID on every call after the first no longer do. Fixed in the same place because it is the same function: akgl_registry_init never called akgl_registry_init_properties, which made akgl_set_property a silent no-op for anyone not going through akgl_game_init -- Defects, Known and still open item 3. Also: AKGL_COLLIDE_RECTANGLES (three open parens, two closes) and akgl_Frame deleted, float32_t/float64_t used consistently, the developer-specific debug logging removed from the controller inner loop, the abandoned SDL_GetBasePath comments removed, nine unused locals removed, and dst renamed to dest. akgl_game_update's default flags no longer OR the same bit twice. That changes nothing today, and the reason is Performance item 32: the loop never reads either bit, which is why every actor is updated sixteen times a frame. Still open. 25/25 pass, memcheck clean, reindent --check clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 00:15:36 -04:00
* Until 0.5.0 this was the one function in the library outside the error
* protocol: it returned `void`, drew through the *global* `akgl_renderer`
* without checking it, and left the renderer's draw colour changed. It now
* takes a backend like every other entry point here and restores the colour it
* found, which is also what makes it testable without a world.
*
* @param self The backend to draw through. Required, along with its
* `sdl_renderer`.
* @param w Width of the region to cover, in pixels, starting at x = 0. Zero
* or negative paints nothing and is not reported.
* @param h Height of the region, starting at y = 0. Both round up to whole
* 8px cells, so a 12-pixel height paints 16.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_NULLPOINTER If @p self or `self->sdl_renderer` is `NULL`.
* @throws AKGL_ERR_SDL If the draw colour cannot be read or set, or a cell
* cannot be filled.
*/
Fix the type, macro and state-table defects, and the leftover debris Closes internal-consistency items 19 through 36, 38 and 41. Item 37, the ~180 redundant casts, is deliberately left open with its reasoning in TODO.md: the benefit only arrives once the build turns on the warnings those casts suppress, and doing it before that is churn across the two files with the most outstanding functional defects. The two that were real bugs are in the actor state table. AKGL_ACTOR_STATE_STRING_NAMES was declared [AKGL_ACTOR_MAX_STATES+1] and defined [32], so a consumer trusting the declared bound read past the object; and indices 11 and 12 were named UNDEFINED_11 and UNDEFINED_12 where actor.h has MOVING_IN and MOVING_OUT, so no character JSON could bind a sprite to either state. tests/registry.c now walks the whole table -- every entry non-NULL, every entry resolving to its own bit, no two entries sharing a name. The bitmask macros are parenthesized and AKGL_BITMASK_CLEAR has lost the semicolon inside its body. Writing tests/bitmasks.c for that turned up something worth knowing: the obvious test does not catch it. For a bit that is set, the misparse `!(mask & bit) == bit` gives the same answer as the correct one. It only diverges for an unset bit whose value is not 1, and that is the shape the suite uses now. akgl_draw_background was the last public function outside the error protocol. It takes a backend like everything else in draw.h, restores the draw colour it found, and is tested -- TODO.md had it filed under "needs the offscreen renderer harness", which was never true; what it needed was to stop reading the global. All eight registry initializers go through one helper, so the seven that leaked an SDL_PropertiesID on every call after the first no longer do. Fixed in the same place because it is the same function: akgl_registry_init never called akgl_registry_init_properties, which made akgl_set_property a silent no-op for anyone not going through akgl_game_init -- Defects, Known and still open item 3. Also: AKGL_COLLIDE_RECTANGLES (three open parens, two closes) and akgl_Frame deleted, float32_t/float64_t used consistently, the developer-specific debug logging removed from the controller inner loop, the abandoned SDL_GetBasePath comments removed, nine unused locals removed, and dst renamed to dest. akgl_game_update's default flags no longer OR the same bit twice. That changes nothing today, and the reason is Performance item 32: the loop never reads either bit, which is why every actor is updated sixteen times a frame. Still open. 25/25 pass, memcheck clean, reindent --check clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 00:15:36 -04:00
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_background(akgl_RenderBackend *self, int w, int h);
/**
* @brief Plot a single pixel.
Document what the functions actually do instead of that they can fail The Doxygen comments were generated from the declarations, so 217 @throws lines across 21 headers read "When the corresponding validation or operation fails" and told a caller nothing beyond the status name. The @param lines were the same shape: every output was "Output destination populated by the function", every instance "Object to initialize, inspect, or modify". Rewritten against the implementations, following the pattern libakstdlib already uses: - @throws names the condition. akgl_sprite_load_json separated AKERR_KEY (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL and AKGL_ERR_HEAP, which it raises and never declared. - Parameters say whether they are required, what a NULL means, and what is written on a failure path. Where an argument is not checked, the doc says so: akgl_heap_next_actor's dest is a crash on NULL, not an error, and akgl_render_2d_frame_start dereferences self before testing it. - The conventions move up to the file blocks so the per-function docs stay short. json_helpers.h states once that absence is an error here and that json_t * results are borrowed; heap.h explains the pool model and the acquire asymmetry; physics.h carries the thrust/environmental/velocity table. - Struct fields, enum values, macros and exported globals are documented, including the dead ones - sprite_w/sprite_h, movetimer, p_scale and timer_gravity are read by nothing, and say so. Also fixes ten comments in error.h and audio.h that opened with /** rather than /**<, so Doxygen attached them to the following entity and rendered the text as part of the macro's value. Verified against the generated HTML. Comments only - no declaration changed. Doxygen builds clean under WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:02:20 -04:00
* @param self The backend to draw through. Required, along with its
* `sdl_renderer`.
* @param x Horizontal position in render-target pixels. Outside the target
* is clipped by SDL, not reported.
* @param y Vertical position.
* @param color Colour to plot in, including alpha. Blending follows the
* renderer's current blend mode, which this does not change.
* @return `NULL` on success, otherwise an error context owned by the caller.
Document what the functions actually do instead of that they can fail The Doxygen comments were generated from the declarations, so 217 @throws lines across 21 headers read "When the corresponding validation or operation fails" and told a caller nothing beyond the status name. The @param lines were the same shape: every output was "Output destination populated by the function", every instance "Object to initialize, inspect, or modify". Rewritten against the implementations, following the pattern libakstdlib already uses: - @throws names the condition. akgl_sprite_load_json separated AKERR_KEY (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL and AKGL_ERR_HEAP, which it raises and never declared. - Parameters say whether they are required, what a NULL means, and what is written on a failure path. Where an argument is not checked, the doc says so: akgl_heap_next_actor's dest is a crash on NULL, not an error, and akgl_render_2d_frame_start dereferences self before testing it. - The conventions move up to the file blocks so the per-function docs stay short. json_helpers.h states once that absence is an error here and that json_t * results are borrowed; heap.h explains the pool model and the acquire asymmetry; physics.h carries the thrust/environmental/velocity table. - Struct fields, enum values, macros and exported globals are documented, including the dead ones - sprite_w/sprite_h, movetimer, p_scale and timer_gravity are read by nothing, and say so. Also fixes ten comments in error.h and audio.h that opened with /** rather than /**<, so Doxygen attached them to the following entity and rendered the text as part of the macro's value. Verified against the generated HTML. Comments only - no declaration changed. Doxygen builds clean under WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:02:20 -04:00
* @throws AKERR_NULLPOINTER If @p self or `self->sdl_renderer` is `NULL`.
* @throws AKGL_ERR_SDL If the draw colour cannot be read or set, or if the plot
* itself fails. The message carries `SDL_GetError()`.
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_point(akgl_RenderBackend *self, float32_t x, float32_t y, SDL_Color color);
/**
Document what the functions actually do instead of that they can fail The Doxygen comments were generated from the declarations, so 217 @throws lines across 21 headers read "When the corresponding validation or operation fails" and told a caller nothing beyond the status name. The @param lines were the same shape: every output was "Output destination populated by the function", every instance "Object to initialize, inspect, or modify". Rewritten against the implementations, following the pattern libakstdlib already uses: - @throws names the condition. akgl_sprite_load_json separated AKERR_KEY (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL and AKGL_ERR_HEAP, which it raises and never declared. - Parameters say whether they are required, what a NULL means, and what is written on a failure path. Where an argument is not checked, the doc says so: akgl_heap_next_actor's dest is a crash on NULL, not an error, and akgl_render_2d_frame_start dereferences self before testing it. - The conventions move up to the file blocks so the per-function docs stay short. json_helpers.h states once that absence is an error here and that json_t * results are borrowed; heap.h explains the pool model and the acquire asymmetry; physics.h carries the thrust/environmental/velocity table. - Struct fields, enum values, macros and exported globals are documented, including the dead ones - sprite_w/sprite_h, movetimer, p_scale and timer_gravity are read by nothing, and say so. Also fixes ten comments in error.h and audio.h that opened with /** rather than /**<, so Doxygen attached them to the following entity and rendered the text as part of the macro's value. Verified against the generated HTML. Comments only - no declaration changed. Doxygen builds clean under WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:02:20 -04:00
* @brief Draw a line between two points, endpoints included.
* @param self The backend to draw through. Required, along with its
* `sdl_renderer`.
* @param x1 Horizontal position of the first endpoint.
* @param y1 Vertical position of the first endpoint.
* @param x2 Horizontal position of the second endpoint.
* @param y2 Vertical position of the second endpoint. Coincident endpoints
* draw a single pixel rather than nothing.
* @param color Colour to draw in, including alpha.
* @return `NULL` on success, otherwise an error context owned by the caller.
Document what the functions actually do instead of that they can fail The Doxygen comments were generated from the declarations, so 217 @throws lines across 21 headers read "When the corresponding validation or operation fails" and told a caller nothing beyond the status name. The @param lines were the same shape: every output was "Output destination populated by the function", every instance "Object to initialize, inspect, or modify". Rewritten against the implementations, following the pattern libakstdlib already uses: - @throws names the condition. akgl_sprite_load_json separated AKERR_KEY (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL and AKGL_ERR_HEAP, which it raises and never declared. - Parameters say whether they are required, what a NULL means, and what is written on a failure path. Where an argument is not checked, the doc says so: akgl_heap_next_actor's dest is a crash on NULL, not an error, and akgl_render_2d_frame_start dereferences self before testing it. - The conventions move up to the file blocks so the per-function docs stay short. json_helpers.h states once that absence is an error here and that json_t * results are borrowed; heap.h explains the pool model and the acquire asymmetry; physics.h carries the thrust/environmental/velocity table. - Struct fields, enum values, macros and exported globals are documented, including the dead ones - sprite_w/sprite_h, movetimer, p_scale and timer_gravity are read by nothing, and say so. Also fixes ten comments in error.h and audio.h that opened with /** rather than /**<, so Doxygen attached them to the following entity and rendered the text as part of the macro's value. Verified against the generated HTML. Comments only - no declaration changed. Doxygen builds clean under WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:02:20 -04:00
* @throws AKERR_NULLPOINTER If @p self or `self->sdl_renderer` is `NULL`.
* @throws AKGL_ERR_SDL If the draw colour cannot be read or set, or if the line
* cannot be drawn.
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_line(akgl_RenderBackend *self, float32_t x1, float32_t y1, float32_t x2, float32_t y2, SDL_Color color);
/**
* @brief Draw the outline of a rectangle.
Document what the functions actually do instead of that they can fail The Doxygen comments were generated from the declarations, so 217 @throws lines across 21 headers read "When the corresponding validation or operation fails" and told a caller nothing beyond the status name. The @param lines were the same shape: every output was "Output destination populated by the function", every instance "Object to initialize, inspect, or modify". Rewritten against the implementations, following the pattern libakstdlib already uses: - @throws names the condition. akgl_sprite_load_json separated AKERR_KEY (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL and AKGL_ERR_HEAP, which it raises and never declared. - Parameters say whether they are required, what a NULL means, and what is written on a failure path. Where an argument is not checked, the doc says so: akgl_heap_next_actor's dest is a crash on NULL, not an error, and akgl_render_2d_frame_start dereferences self before testing it. - The conventions move up to the file blocks so the per-function docs stay short. json_helpers.h states once that absence is an error here and that json_t * results are borrowed; heap.h explains the pool model and the acquire asymmetry; physics.h carries the thrust/environmental/velocity table. - Struct fields, enum values, macros and exported globals are documented, including the dead ones - sprite_w/sprite_h, movetimer, p_scale and timer_gravity are read by nothing, and say so. Also fixes ten comments in error.h and audio.h that opened with /** rather than /**<, so Doxygen attached them to the following entity and rendered the text as part of the macro's value. Verified against the generated HTML. Comments only - no declaration changed. Doxygen builds clean under WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:02:20 -04:00
* @param self The backend to draw through. Required, along with its
* `sdl_renderer`.
* @param rect The rectangle, in render-target pixels. Required. A zero or
* negative width or height draws nothing and is not reported.
* @param color Colour to draw in, including alpha.
* @return `NULL` on success, otherwise an error context owned by the caller.
Document what the functions actually do instead of that they can fail The Doxygen comments were generated from the declarations, so 217 @throws lines across 21 headers read "When the corresponding validation or operation fails" and told a caller nothing beyond the status name. The @param lines were the same shape: every output was "Output destination populated by the function", every instance "Object to initialize, inspect, or modify". Rewritten against the implementations, following the pattern libakstdlib already uses: - @throws names the condition. akgl_sprite_load_json separated AKERR_KEY (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL and AKGL_ERR_HEAP, which it raises and never declared. - Parameters say whether they are required, what a NULL means, and what is written on a failure path. Where an argument is not checked, the doc says so: akgl_heap_next_actor's dest is a crash on NULL, not an error, and akgl_render_2d_frame_start dereferences self before testing it. - The conventions move up to the file blocks so the per-function docs stay short. json_helpers.h states once that absence is an error here and that json_t * results are borrowed; heap.h explains the pool model and the acquire asymmetry; physics.h carries the thrust/environmental/velocity table. - Struct fields, enum values, macros and exported globals are documented, including the dead ones - sprite_w/sprite_h, movetimer, p_scale and timer_gravity are read by nothing, and say so. Also fixes ten comments in error.h and audio.h that opened with /** rather than /**<, so Doxygen attached them to the following entity and rendered the text as part of the macro's value. Verified against the generated HTML. Comments only - no declaration changed. Doxygen builds clean under WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:02:20 -04:00
* @throws AKERR_NULLPOINTER If @p self, `self->sdl_renderer`, or @p rect is
* `NULL`.
* @throws AKGL_ERR_SDL If the draw colour cannot be read or set, or if the
* outline cannot be drawn.
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_rect(akgl_RenderBackend *self, SDL_FRect *rect, SDL_Color color);
/**
Document what the functions actually do instead of that they can fail The Doxygen comments were generated from the declarations, so 217 @throws lines across 21 headers read "When the corresponding validation or operation fails" and told a caller nothing beyond the status name. The @param lines were the same shape: every output was "Output destination populated by the function", every instance "Object to initialize, inspect, or modify". Rewritten against the implementations, following the pattern libakstdlib already uses: - @throws names the condition. akgl_sprite_load_json separated AKERR_KEY (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL and AKGL_ERR_HEAP, which it raises and never declared. - Parameters say whether they are required, what a NULL means, and what is written on a failure path. Where an argument is not checked, the doc says so: akgl_heap_next_actor's dest is a crash on NULL, not an error, and akgl_render_2d_frame_start dereferences self before testing it. - The conventions move up to the file blocks so the per-function docs stay short. json_helpers.h states once that absence is an error here and that json_t * results are borrowed; heap.h explains the pool model and the acquire asymmetry; physics.h carries the thrust/environmental/velocity table. - Struct fields, enum values, macros and exported globals are documented, including the dead ones - sprite_w/sprite_h, movetimer, p_scale and timer_gravity are read by nothing, and say so. Also fixes ten comments in error.h and audio.h that opened with /** rather than /**<, so Doxygen attached them to the following entity and rendered the text as part of the macro's value. Verified against the generated HTML. Comments only - no declaration changed. Doxygen builds clean under WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:02:20 -04:00
* @brief Fill a rectangle, outline included.
* @param self The backend to draw through. Required, along with its
* `sdl_renderer`.
* @param rect The rectangle, in render-target pixels. Required. A zero or
* negative width or height fills nothing and is not reported.
* @param color Colour to fill with, including alpha.
* @return `NULL` on success, otherwise an error context owned by the caller.
Document what the functions actually do instead of that they can fail The Doxygen comments were generated from the declarations, so 217 @throws lines across 21 headers read "When the corresponding validation or operation fails" and told a caller nothing beyond the status name. The @param lines were the same shape: every output was "Output destination populated by the function", every instance "Object to initialize, inspect, or modify". Rewritten against the implementations, following the pattern libakstdlib already uses: - @throws names the condition. akgl_sprite_load_json separated AKERR_KEY (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL and AKGL_ERR_HEAP, which it raises and never declared. - Parameters say whether they are required, what a NULL means, and what is written on a failure path. Where an argument is not checked, the doc says so: akgl_heap_next_actor's dest is a crash on NULL, not an error, and akgl_render_2d_frame_start dereferences self before testing it. - The conventions move up to the file blocks so the per-function docs stay short. json_helpers.h states once that absence is an error here and that json_t * results are borrowed; heap.h explains the pool model and the acquire asymmetry; physics.h carries the thrust/environmental/velocity table. - Struct fields, enum values, macros and exported globals are documented, including the dead ones - sprite_w/sprite_h, movetimer, p_scale and timer_gravity are read by nothing, and say so. Also fixes ten comments in error.h and audio.h that opened with /** rather than /**<, so Doxygen attached them to the following entity and rendered the text as part of the macro's value. Verified against the generated HTML. Comments only - no declaration changed. Doxygen builds clean under WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:02:20 -04:00
* @throws AKERR_NULLPOINTER If @p self, `self->sdl_renderer`, or @p rect is
* `NULL`.
* @throws AKGL_ERR_SDL If the draw colour cannot be read or set, or if the fill
* cannot be drawn.
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_filled_rect(akgl_RenderBackend *self, SDL_FRect *rect, SDL_Color color);
/**
* @brief Draw the outline of a circle.
*
* SDL3 has no circle primitive, so this plots one with the midpoint circle
* algorithm -- integer arithmetic, eight-way symmetry, one pass per octant. A
* radius of zero draws the center pixel and nothing else.
*
Document what the functions actually do instead of that they can fail The Doxygen comments were generated from the declarations, so 217 @throws lines across 21 headers read "When the corresponding validation or operation fails" and told a caller nothing beyond the status name. The @param lines were the same shape: every output was "Output destination populated by the function", every instance "Object to initialize, inspect, or modify". Rewritten against the implementations, following the pattern libakstdlib already uses: - @throws names the condition. akgl_sprite_load_json separated AKERR_KEY (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL and AKGL_ERR_HEAP, which it raises and never declared. - Parameters say whether they are required, what a NULL means, and what is written on a failure path. Where an argument is not checked, the doc says so: akgl_heap_next_actor's dest is a crash on NULL, not an error, and akgl_render_2d_frame_start dereferences self before testing it. - The conventions move up to the file blocks so the per-function docs stay short. json_helpers.h states once that absence is an error here and that json_t * results are borrowed; heap.h explains the pool model and the acquire asymmetry; physics.h carries the thrust/environmental/velocity table. - Struct fields, enum values, macros and exported globals are documented, including the dead ones - sprite_w/sprite_h, movetimer, p_scale and timer_gravity are read by nothing, and say so. Also fixes ten comments in error.h and audio.h that opened with /** rather than /**<, so Doxygen attached them to the following entity and rendered the text as part of the macro's value. Verified against the generated HTML. Comments only - no declaration changed. Doxygen builds clean under WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:02:20 -04:00
* @param self The backend to draw through. Required, along with its
* `sdl_renderer`.
* @param x Horizontal position of the centre. Rounded to the nearest whole
* pixel -- the algorithm is integer-only.
* @param y Vertical position of the centre, rounded the same way.
* @param radius Radius in pixels, rounded to the nearest whole pixel. Must not
* be negative; 0 draws the centre pixel alone.
* @param color Colour to draw in, including alpha. The outline is one pixel
* wide and is not anti-aliased.
* @return `NULL` on success, otherwise an error context owned by the caller.
Document what the functions actually do instead of that they can fail The Doxygen comments were generated from the declarations, so 217 @throws lines across 21 headers read "When the corresponding validation or operation fails" and told a caller nothing beyond the status name. The @param lines were the same shape: every output was "Output destination populated by the function", every instance "Object to initialize, inspect, or modify". Rewritten against the implementations, following the pattern libakstdlib already uses: - @throws names the condition. akgl_sprite_load_json separated AKERR_KEY (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL and AKGL_ERR_HEAP, which it raises and never declared. - Parameters say whether they are required, what a NULL means, and what is written on a failure path. Where an argument is not checked, the doc says so: akgl_heap_next_actor's dest is a crash on NULL, not an error, and akgl_render_2d_frame_start dereferences self before testing it. - The conventions move up to the file blocks so the per-function docs stay short. json_helpers.h states once that absence is an error here and that json_t * results are borrowed; heap.h explains the pool model and the acquire asymmetry; physics.h carries the thrust/environmental/velocity table. - Struct fields, enum values, macros and exported globals are documented, including the dead ones - sprite_w/sprite_h, movetimer, p_scale and timer_gravity are read by nothing, and say so. Also fixes ten comments in error.h and audio.h that opened with /** rather than /**<, so Doxygen attached them to the following entity and rendered the text as part of the macro's value. Verified against the generated HTML. Comments only - no declaration changed. Doxygen builds clean under WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:02:20 -04:00
* @throws AKERR_NULLPOINTER If @p self or `self->sdl_renderer` is `NULL`.
* @throws AKERR_OUTOFBOUNDS If @p radius is negative. The message reports it.
* @throws AKGL_ERR_SDL If the draw colour cannot be read or set, or if any of
* the plotting passes fails. Failures inside the loop are recorded and
* reported once at the end rather than aborting mid-circle, so a partial
* arc may already be on the target.
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_circle(akgl_RenderBackend *self, float32_t x, float32_t y, float32_t radius, SDL_Color color);
/**
* @brief Flood the connected region containing one pixel with a color.
*
* SDL3 has no flood fill either, and unlike the shape primitives it cannot be
* done on the GPU side: the region is defined by what is already on the screen.
* This reads the render target back, walks the region on the CPU with a
* bounded span stack, and blits the result over the area it touched.
*
* Filling a region that already holds @p color is a no-op rather than an error.
* A seed outside the render target reports AKERR_OUTOFBOUNDS.
*
Document what the functions actually do instead of that they can fail The Doxygen comments were generated from the declarations, so 217 @throws lines across 21 headers read "When the corresponding validation or operation fails" and told a caller nothing beyond the status name. The @param lines were the same shape: every output was "Output destination populated by the function", every instance "Object to initialize, inspect, or modify". Rewritten against the implementations, following the pattern libakstdlib already uses: - @throws names the condition. akgl_sprite_load_json separated AKERR_KEY (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL and AKGL_ERR_HEAP, which it raises and never declared. - Parameters say whether they are required, what a NULL means, and what is written on a failure path. Where an argument is not checked, the doc says so: akgl_heap_next_actor's dest is a crash on NULL, not an error, and akgl_render_2d_frame_start dereferences self before testing it. - The conventions move up to the file blocks so the per-function docs stay short. json_helpers.h states once that absence is an error here and that json_t * results are borrowed; heap.h explains the pool model and the acquire asymmetry; physics.h carries the thrust/environmental/velocity table. - Struct fields, enum values, macros and exported globals are documented, including the dead ones - sprite_w/sprite_h, movetimer, p_scale and timer_gravity are read by nothing, and say so. Also fixes ten comments in error.h and audio.h that opened with /** rather than /**<, so Doxygen attached them to the following entity and rendered the text as part of the macro's value. Verified against the generated HTML. Comments only - no declaration changed. Doxygen builds clean under WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:02:20 -04:00
* The region is four-connected -- it spreads up, down, left and right, not
* diagonally -- and its boundary is any pixel whose colour differs from the
* seed's, exactly. There is no tolerance, so an anti-aliased edge stops the fill
* at its first blended pixel and leaves a fringe.
*
* @param self The backend to draw through. Required, along with its
* `sdl_renderer`.
* @param x Horizontal position of the seed pixel, in render-target pixels.
* Must be inside the target.
* @param y Vertical position of the seed pixel.
* @param color Colour to fill with. Written over the region rather than blended,
* since the pixels going back are the ones just read out.
* @return `NULL` on success, otherwise an error context owned by the caller.
Document what the functions actually do instead of that they can fail The Doxygen comments were generated from the declarations, so 217 @throws lines across 21 headers read "When the corresponding validation or operation fails" and told a caller nothing beyond the status name. The @param lines were the same shape: every output was "Output destination populated by the function", every instance "Object to initialize, inspect, or modify". Rewritten against the implementations, following the pattern libakstdlib already uses: - @throws names the condition. akgl_sprite_load_json separated AKERR_KEY (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL and AKGL_ERR_HEAP, which it raises and never declared. - Parameters say whether they are required, what a NULL means, and what is written on a failure path. Where an argument is not checked, the doc says so: akgl_heap_next_actor's dest is a crash on NULL, not an error, and akgl_render_2d_frame_start dereferences self before testing it. - The conventions move up to the file blocks so the per-function docs stay short. json_helpers.h states once that absence is an error here and that json_t * results are borrowed; heap.h explains the pool model and the acquire asymmetry; physics.h carries the thrust/environmental/velocity table. - Struct fields, enum values, macros and exported globals are documented, including the dead ones - sprite_w/sprite_h, movetimer, p_scale and timer_gravity are read by nothing, and say so. Also fixes ten comments in error.h and audio.h that opened with /** rather than /**<, so Doxygen attached them to the following entity and rendered the text as part of the macro's value. Verified against the generated HTML. Comments only - no declaration changed. Doxygen builds clean under WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:02:20 -04:00
* @throws AKERR_NULLPOINTER If @p self or `self->sdl_renderer` is `NULL`.
* @throws AKERR_OUTOFBOUNDS If the seed is outside the render target -- the
* message reports both the seed and the target size -- or if the region
* needs more than #AKGL_DRAW_MAX_FLOOD_SPANS pending spans, in which
* case **it is left partially filled**.
* @throws AKGL_ERR_SDL If the render target cannot be read back, converted,
* uploaded, or blitted.
*
* @warning Not reentrant, and not safe from two threads: the span stack is a
* single file-scope array. Nothing that touches an `SDL_Renderer` is
* thread-safe either way.
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_flood_fill(akgl_RenderBackend *self, int x, int y, SDL_Color color);
/**
* @brief Read a rectangle of the render target into a surface.
*
* The save half of SSHAPE/GSHAPE. When `*dest` is `NULL` the function allocates
* the surface and the caller owns it from then on -- release it with
* SDL_DestroySurface(). When `*dest` already points at a surface of exactly
* @p src's dimensions the pixels are copied into it instead, so a caller
* saving the same region repeatedly does not churn allocations.
*
Document what the functions actually do instead of that they can fail The Doxygen comments were generated from the declarations, so 217 @throws lines across 21 headers read "When the corresponding validation or operation fails" and told a caller nothing beyond the status name. The @param lines were the same shape: every output was "Output destination populated by the function", every instance "Object to initialize, inspect, or modify". Rewritten against the implementations, following the pattern libakstdlib already uses: - @throws names the condition. akgl_sprite_load_json separated AKERR_KEY (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL and AKGL_ERR_HEAP, which it raises and never declared. - Parameters say whether they are required, what a NULL means, and what is written on a failure path. Where an argument is not checked, the doc says so: akgl_heap_next_actor's dest is a crash on NULL, not an error, and akgl_render_2d_frame_start dereferences self before testing it. - The conventions move up to the file blocks so the per-function docs stay short. json_helpers.h states once that absence is an error here and that json_t * results are borrowed; heap.h explains the pool model and the acquire asymmetry; physics.h carries the thrust/environmental/velocity table. - Struct fields, enum values, macros and exported globals are documented, including the dead ones - sprite_w/sprite_h, movetimer, p_scale and timer_gravity are read by nothing, and say so. Also fixes ten comments in error.h and audio.h that opened with /** rather than /**<, so Doxygen attached them to the following entity and rendered the text as part of the macro's value. Verified against the generated HTML. Comments only - no declaration changed. Doxygen builds clean under WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:02:20 -04:00
* @param self The backend to read from. Required, along with its `sdl_renderer`.
* @param src Rectangle of the render target to read, in pixels. Required. It
* must fit *entirely* inside the target: SDL would otherwise clip
* the read and hand back a smaller surface than was asked for, which
* a caller pasting it back would not notice.
* @param dest Address of the destination surface. Required, and `*dest` must be
* initialized -- `NULL` to have one allocated (the caller then owns
* it and frees it with `SDL_DestroySurface`), or an existing surface
* of exactly @p src's dimensions to reuse.
* @return `NULL` on success, otherwise an error context owned by the caller.
Document what the functions actually do instead of that they can fail The Doxygen comments were generated from the declarations, so 217 @throws lines across 21 headers read "When the corresponding validation or operation fails" and told a caller nothing beyond the status name. The @param lines were the same shape: every output was "Output destination populated by the function", every instance "Object to initialize, inspect, or modify". Rewritten against the implementations, following the pattern libakstdlib already uses: - @throws names the condition. akgl_sprite_load_json separated AKERR_KEY (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL and AKGL_ERR_HEAP, which it raises and never declared. - Parameters say whether they are required, what a NULL means, and what is written on a failure path. Where an argument is not checked, the doc says so: akgl_heap_next_actor's dest is a crash on NULL, not an error, and akgl_render_2d_frame_start dereferences self before testing it. - The conventions move up to the file blocks so the per-function docs stay short. json_helpers.h states once that absence is an error here and that json_t * results are borrowed; heap.h explains the pool model and the acquire asymmetry; physics.h carries the thrust/environmental/velocity table. - Struct fields, enum values, macros and exported globals are documented, including the dead ones - sprite_w/sprite_h, movetimer, p_scale and timer_gravity are read by nothing, and say so. Also fixes ten comments in error.h and audio.h that opened with /** rather than /**<, so Doxygen attached them to the following entity and rendered the text as part of the macro's value. Verified against the generated HTML. Comments only - no declaration changed. Doxygen builds clean under WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:02:20 -04:00
* @throws AKERR_NULLPOINTER If @p self, `self->sdl_renderer`, @p src, or @p dest
* is `NULL`.
* @throws AKERR_OUTOFBOUNDS If @p src has no area, if it does not fit inside the
* render target, or if a supplied `*dest` is a different size from
* @p src. Each message reports both sets of dimensions.
* @throws AKGL_ERR_SDL If the target size cannot be queried, the pixels cannot
* be read, or the copy into a supplied `*dest` fails.
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_copy_region(akgl_RenderBackend *self, SDL_Rect *src, SDL_Surface **dest);
/**
* @brief Draw a saved surface back onto the render target.
*
* The restore half of SSHAPE/GSHAPE, taking what akgl_draw_copy_region()
* produced. The surface is not consumed and may be pasted as many times as the
* caller likes.
*
Document what the functions actually do instead of that they can fail The Doxygen comments were generated from the declarations, so 217 @throws lines across 21 headers read "When the corresponding validation or operation fails" and told a caller nothing beyond the status name. The @param lines were the same shape: every output was "Output destination populated by the function", every instance "Object to initialize, inspect, or modify". Rewritten against the implementations, following the pattern libakstdlib already uses: - @throws names the condition. akgl_sprite_load_json separated AKERR_KEY (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL and AKGL_ERR_HEAP, which it raises and never declared. - Parameters say whether they are required, what a NULL means, and what is written on a failure path. Where an argument is not checked, the doc says so: akgl_heap_next_actor's dest is a crash on NULL, not an error, and akgl_render_2d_frame_start dereferences self before testing it. - The conventions move up to the file blocks so the per-function docs stay short. json_helpers.h states once that absence is an error here and that json_t * results are borrowed; heap.h explains the pool model and the acquire asymmetry; physics.h carries the thrust/environmental/velocity table. - Struct fields, enum values, macros and exported globals are documented, including the dead ones - sprite_w/sprite_h, movetimer, p_scale and timer_gravity are read by nothing, and say so. Also fixes ten comments in error.h and audio.h that opened with /** rather than /**<, so Doxygen attached them to the following entity and rendered the text as part of the macro's value. Verified against the generated HTML. Comments only - no declaration changed. Doxygen builds clean under WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:02:20 -04:00
* @param self The backend to draw through. Required, along with its
* `sdl_renderer`.
* @param src The surface to paste. Required. Drawn at its own size -- there is
* no scaling -- and *replacing* what is on the target rather than
* blending with it, which is GSHAPE's default behaviour and means a
* saved region's transparent pixels come back as transparent rather
* than letting the background show through.
* @param x Horizontal position of the paste's left edge.
* @param y Vertical position of its top edge. Parts falling outside the
* target are clipped by SDL, not reported.
* @return `NULL` on success, otherwise an error context owned by the caller.
Document what the functions actually do instead of that they can fail The Doxygen comments were generated from the declarations, so 217 @throws lines across 21 headers read "When the corresponding validation or operation fails" and told a caller nothing beyond the status name. The @param lines were the same shape: every output was "Output destination populated by the function", every instance "Object to initialize, inspect, or modify". Rewritten against the implementations, following the pattern libakstdlib already uses: - @throws names the condition. akgl_sprite_load_json separated AKERR_KEY (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL and AKGL_ERR_HEAP, which it raises and never declared. - Parameters say whether they are required, what a NULL means, and what is written on a failure path. Where an argument is not checked, the doc says so: akgl_heap_next_actor's dest is a crash on NULL, not an error, and akgl_render_2d_frame_start dereferences self before testing it. - The conventions move up to the file blocks so the per-function docs stay short. json_helpers.h states once that absence is an error here and that json_t * results are borrowed; heap.h explains the pool model and the acquire asymmetry; physics.h carries the thrust/environmental/velocity table. - Struct fields, enum values, macros and exported globals are documented, including the dead ones - sprite_w/sprite_h, movetimer, p_scale and timer_gravity are read by nothing, and say so. Also fixes ten comments in error.h and audio.h that opened with /** rather than /**<, so Doxygen attached them to the following entity and rendered the text as part of the macro's value. Verified against the generated HTML. Comments only - no declaration changed. Doxygen builds clean under WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:02:20 -04:00
* @throws AKERR_NULLPOINTER If @p self, `self->sdl_renderer`, or @p src is
* `NULL`.
* @throws AKGL_ERR_SDL If the surface cannot be uploaded as a texture, its blend
* mode cannot be set, or the draw fails.
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_paste_region(akgl_RenderBackend *self, SDL_Surface *src, float32_t x, float32_t y);
/**
* @brief Fill a rectangle whose corners are rounded off with quarter circles.
*
* The body is three axis-aligned fills and the corners are triangle fans
* through `SDL_RenderGeometry` -- #AKGL_DRAW_ROUNDED_RECT_SEGMENTS triangles
* each -- so nothing here is anti-aliased, matching every other primitive in
* this header.
*
* @param self The backend to draw through. Required, along with its
* `sdl_renderer`.
* @param rect The rectangle, in render-target pixels. Required. A zero or
* negative width or height fills nothing and is not reported.
* @param radius Corner radius in pixels. Zero or negative falls through to
* akgl_draw_filled_rect() -- a square corner is not an error.
* Larger than half the shorter side is clamped to it, which at
* the limit turns a square into a circle rather than folding the
* corners over each other.
* @param color Colour to fill with, including alpha.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_NULLPOINTER If @p self, `self->sdl_renderer`, or @p rect is
* `NULL`.
* @throws AKGL_ERR_SDL If the draw colour cannot be read or set, or if a band
* or corner cannot be drawn. A failure partway leaves the shape
* partially drawn.
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_filled_rounded_rect(akgl_RenderBackend *self, SDL_FRect *rect, float32_t radius, SDL_Color color);
/**
* @brief Stroke a circular arc of a given thickness between two angles.
*
* Angles are in degrees, 0 pointing along +x (three o'clock) and increasing
* clockwise on screen -- the same convention as the rotation argument the
* render backend's `draw_texture` takes. The stroke runs from the arc's outer
* edge at @p radius inward by @p thickness, drawn as one triangle strip, not
* anti-aliased.
*
* @param self The backend to draw through. Required, along with its
* `sdl_renderer`.
* @param x Horizontal position of the arc's centre.
* @param y Vertical position of the arc's centre.
* @param radius Outer radius in pixels. Must be positive.
* @param start_deg Angle the arc starts at.
* @param end_deg Angle the arc ends at. At or below @p start_deg nothing is
* drawn and nothing is reported, matching the zero-area
* rectangle fills above. Neither angle is normalized, so an
* arc crossing three o'clock is 350 to 370, not 350 to 10; a
* sweep beyond 360 degrees is clamped to one full turn.
* @param thickness Stroke width in pixels, measured inward from @p radius.
* Must be positive; at or above @p radius the arc becomes a
* filled wedge to the centre.
* @param color Colour to stroke in, including alpha.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_NULLPOINTER If @p self or `self->sdl_renderer` is `NULL`.
* @throws AKERR_OUTOFBOUNDS If @p radius or @p thickness is not positive. The
* message reports the offending value.
* @throws AKGL_ERR_SDL If the strip cannot be drawn.
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_arc(akgl_RenderBackend *self, float32_t x, float32_t y, float32_t radius, float32_t start_deg, float32_t end_deg, float32_t thickness, SDL_Color color);
/**
* @brief Restrict every subsequent draw to a rectangle of the render target.
*
* Everything drawn through @p self after this -- primitives here, textures,
* text -- is clipped to @p rect until the restriction is cleared. This is the
* only entry point in this header where a `NULL` rectangle is legal rather
* than an error: it means "clear the restriction", matching what
* `SDL_SetRenderClipRect` does with it, and there is no other value that
* could.
*
* The clip is renderer state, not saved and restored around anything: a
* caller that sets it owns putting it back, the way a `CLEANUP` block puts
* back a draw colour. Leaving it set clips the next frame's world too.
*
* @param self The backend to clip. Required, along with its `sdl_renderer`.
* @param rect Rectangle to clip to, in render-target pixels -- integer, since
* a clip boundary is a pixel boundary. `NULL` clears the clip.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_NULLPOINTER If @p self or `self->sdl_renderer` is `NULL`.
* @throws AKGL_ERR_SDL If the clip cannot be set or cleared.
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_set_clip(akgl_RenderBackend *self, SDL_Rect *rect);
Namespace every exported symbol, and bump to 0.5.0 Closes internal-consistency items 1 through 6, 12 and 13. Every include guard is _AKGL_<FILE>_H_, every in-project header include is angled, and every exported function, type and global carries the akgl_ prefix. This is an ABI break; the soname goes to libakgl.so.0.5. TODO.md carries the full rename table. The renames were driven by renaming each declaration and letting the compiler find the uses, not by pattern substitution: renderer, physics and camera are also parameter and struct-member names, and a sed would have rewritten map->physics and every akgl_RenderBackend *renderer parameter without a word. Item 4 turned out not to be cosmetic. The library exported a global called renderer and tests/character.c defined an SDL_Renderer *renderer of its own; the executable's definition preempted the library's, akgl_sprite_load_json read a SDL_Renderer * through an akgl_RenderBackend *, and every texture load in that suite failed. The suite reported success anyway, because libakerror's unhandled-error handler ends in exit(errctx->status), exit keeps only the low byte, and AKGL_ERR_SDL is exactly 256. So character had been green while running one of its four tests, and every suite in the tree was unable to fail on the most common status in a library built on SDL. Both are fixed. tests/testutil.h gains TEST_TRAP_UNHANDLED_ERRORS(), which collapses any status a byte cannot carry onto 1, and every suite installs it. character binds a real backend with akgl_render_2d_bind. Its fourth test then runs for the first time and fails on a defect it has asserted all along, so akgl_heap_release_character now walks state_sprites with AKGL_ITERATOR_OP_RELEASE and destroys the property set before zeroing the slot -- TODO.md Defects item 21 and half of Carried over item 1. AKGL_TIME_ONESEC_MS said "one second in milliseconds" and held 1000000, so akgl_game_state_lock waited roughly sixteen minutes rather than one second. It is AKGL_TIME_ONEMS_NS now, the budget is its own named constant, and tests/game.c holds the mutex from a second thread to assert the wait -- the contended path had no coverage at all. Headers are self-contained and it is enforced: AKGL_PUBLIC_HEADERS drives both install() and a generated translation unit per header, so a header that ships is a header that is checked. Writing that found registry.h, which used SDL_PropertiesID in eight declarations and included no SDL header. 23/23 suites pass, memcheck is clean, reindent --check is clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 23:32:21 -04:00
#endif //_AKGL_DRAW_H_