2026-07-30 01:10:31 -04:00
|
|
|
/**
|
|
|
|
|
* @file text.h
|
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 Loading fonts and drawing or measuring strings with them.
|
|
|
|
|
*
|
|
|
|
|
* Fonts are `TTF_Font *` handles kept in the #AKGL_REGISTRY_FONT property
|
|
|
|
|
* registry under a caller-chosen name; there is no akgl font type wrapping them.
|
|
|
|
|
* SDL_ttf must be initialized (akgl_game_init does it) before any of this.
|
|
|
|
|
*
|
|
|
|
|
* The two measure functions do not touch the renderer, so they are usable
|
|
|
|
|
* before -- or entirely without -- a window. Drawing is immediate mode: each
|
|
|
|
|
* akgl_text_rendertextat() call rasterizes, uploads, blits, and throws the
|
|
|
|
|
* texture away, which is fine for a HUD line and wrong for a large body of
|
|
|
|
|
* static text redrawn every frame.
|
2026-07-30 01:10:31 -04:00
|
|
|
*/
|
|
|
|
|
|
2026-05-06 22:49:05 -04:00
|
|
|
#ifndef _TEXT_H_
|
|
|
|
|
#define _TEXT_H_
|
|
|
|
|
|
2026-07-31 06:55:25 -04:00
|
|
|
#include <SDL3/SDL.h>
|
2026-05-06 22:49:05 -04:00
|
|
|
#include <SDL3_ttf/SDL_ttf.h>
|
2026-07-31 06:55:25 -04:00
|
|
|
#include <akerror.h>
|
2026-05-06 22:49:05 -04:00
|
|
|
|
2026-07-30 01:10:31 -04:00
|
|
|
/**
|
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 Open a TrueType font at one size and publish it in the font registry.
|
|
|
|
|
*
|
|
|
|
|
* A size is baked into the handle, so the same file at two sizes is two calls
|
|
|
|
|
* under two names. Nothing releases these: the handles live until the process
|
|
|
|
|
* ends.
|
|
|
|
|
*
|
|
|
|
|
* @param name Registry key to publish the font under. Required. An existing
|
|
|
|
|
* entry with the same name is replaced, and the font it
|
Fix every memory defect the checker found, and bump to 0.4.0
Six findings, all of them libakgl's, all closed. The memcheck run is clean.
Not one of the four json_load_file calls in src/ was ever matched by a
json_decref, so every asset load abandoned its parsed document: 1.5 KB per
sprite, 2.1 KB per character, 9 KB per load of the 2x2 fixture map, and on
the order of a megabyte for a real one. Each loader now releases it in the
CLEANUP block it already had, on the success path as well as the failure
one. akgl_registry_load_properties needed its loop moved inside the ATTEMPT
block first: props is a borrowed reference into the document and is read
after the block ends, so every exit from that loop leaked the whole tree.
akgl_get_property copied a fixed AKGL_MAX_STRING_LENGTH bytes out of what
SDL handed back, which is SDL's strdup of the value -- four bytes for
"0.0". That read up to 4 KiB past the end of somebody else's allocation on
every property read, returned whatever was there past the terminator, and
would have faulted on a value that landed at the end of a page. It copies
the value and its terminator now, and refuses a value too long for an
akgl_String rather than truncating it into an unterminated buffer. The
header note that described the overread as a quirk describes correct
behaviour instead.
The four savegame name tables wrote a fixed-width field starting at the
registry key, and SDL sizes that allocation to the name. They read past it
on every entry and put what they found into the save file: up to half a
kilobyte of this process's heap per registered object, in a file a player
might send to somebody. They stage through a zeroed buffer now, and a
negative-array-size typedef fails the build if a table's width ever outgrows
it.
akgl_controller_list_keyboards never freed the array SDL_GetKeyboards
allocated for it.
A font could be opened and published and never handed back -- there was no
way to close one, so a game that changed fonts between scenes leaked ten
kilobytes each time, and loading over a live name leaked the font it
displaced. akgl_text_unloadfont is that missing half, and akgl_text_loadfont
calls it when it replaces a name, after the new font has opened so a failed
reload leaves the caller with the font they had. A new public symbol takes
the version to 0.4.0 and the soname with it: an 0.3 consumer cannot be
handed this library and told it is the same ABI.
tests/registry.c fills a destination with a sentinel and asserts the bytes
past the terminator survive a read, which fails against the old copy.
tests/text.c covers unload, double unload, unloading a name that was never
registered, and replacement closing the displaced font. The JSON releases
have no test of their own and cannot sensibly have one -- nothing in the
public API can observe a jansson refcount -- so the memcheck run is their
test, which is an argument for gating it rather than against.
The two remaining findings are in deps/semver's own unit test, which is
vendored. They are suppressed by function name, so a rewrite of those cases
comes back as a finding.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 15:49:11 -04:00
|
|
|
* displaced is closed through akgl_text_unloadfont -- but only
|
|
|
|
|
* after the new one has opened, so a failed load leaves the
|
|
|
|
|
* caller with the font they already had.
|
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 filepath Path to a `.ttf`/`.otf` file. Required. Used verbatim -- not
|
|
|
|
|
* resolved against `SDL_GetBasePath()`.
|
|
|
|
|
* @param size Point size to rasterize at. Passed straight to SDL_ttf, which
|
|
|
|
|
* rejects anything that is not positive.
|
2026-07-30 01:10:31 -04:00
|
|
|
* @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 name or @p filepath is `NULL`.
|
|
|
|
|
* @throws AKGL_ERR_SDL If the font cannot be opened -- missing, unreadable, not
|
|
|
|
|
* a font, or a @p size SDL_ttf refuses. The message carries
|
|
|
|
|
* `SDL_GetError()`.
|
|
|
|
|
* @throws AKERR_KEY If the font cannot be written into #AKGL_REGISTRY_FONT --
|
|
|
|
|
* in practice, because akgl_registry_init has not run.
|
2026-07-30 01:10:31 -04:00
|
|
|
*/
|
2026-05-06 23:18:42 -04:00
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_text_loadfont(char *name, char *filepath, int size);
|
Fix every memory defect the checker found, and bump to 0.4.0
Six findings, all of them libakgl's, all closed. The memcheck run is clean.
Not one of the four json_load_file calls in src/ was ever matched by a
json_decref, so every asset load abandoned its parsed document: 1.5 KB per
sprite, 2.1 KB per character, 9 KB per load of the 2x2 fixture map, and on
the order of a megabyte for a real one. Each loader now releases it in the
CLEANUP block it already had, on the success path as well as the failure
one. akgl_registry_load_properties needed its loop moved inside the ATTEMPT
block first: props is a borrowed reference into the document and is read
after the block ends, so every exit from that loop leaked the whole tree.
akgl_get_property copied a fixed AKGL_MAX_STRING_LENGTH bytes out of what
SDL handed back, which is SDL's strdup of the value -- four bytes for
"0.0". That read up to 4 KiB past the end of somebody else's allocation on
every property read, returned whatever was there past the terminator, and
would have faulted on a value that landed at the end of a page. It copies
the value and its terminator now, and refuses a value too long for an
akgl_String rather than truncating it into an unterminated buffer. The
header note that described the overread as a quirk describes correct
behaviour instead.
The four savegame name tables wrote a fixed-width field starting at the
registry key, and SDL sizes that allocation to the name. They read past it
on every entry and put what they found into the save file: up to half a
kilobyte of this process's heap per registered object, in a file a player
might send to somebody. They stage through a zeroed buffer now, and a
negative-array-size typedef fails the build if a table's width ever outgrows
it.
akgl_controller_list_keyboards never freed the array SDL_GetKeyboards
allocated for it.
A font could be opened and published and never handed back -- there was no
way to close one, so a game that changed fonts between scenes leaked ten
kilobytes each time, and loading over a live name leaked the font it
displaced. akgl_text_unloadfont is that missing half, and akgl_text_loadfont
calls it when it replaces a name, after the new font has opened so a failed
reload leaves the caller with the font they had. A new public symbol takes
the version to 0.4.0 and the soname with it: an 0.3 consumer cannot be
handed this library and told it is the same ABI.
tests/registry.c fills a destination with a sentinel and asserts the bytes
past the terminator survive a read, which fails against the old copy.
tests/text.c covers unload, double unload, unloading a name that was never
registered, and replacement closing the displaced font. The JSON releases
have no test of their own and cannot sensibly have one -- nothing in the
public API can observe a jansson refcount -- so the memcheck run is their
test, which is an argument for gating it rather than against.
The two remaining findings are in deps/semver's own unit test, which is
vendored. They are suppressed by function name, so a rewrite of those cases
comes back as a finding.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 15:49:11 -04:00
|
|
|
/**
|
|
|
|
|
* @brief Close a loaded font and take it out of the font registry.
|
|
|
|
|
*
|
|
|
|
|
* The other half of akgl_text_loadfont, and for a while the half that did not
|
|
|
|
|
* exist: a font could be opened and published but never handed back, so a game
|
|
|
|
|
* that changed fonts between scenes had no way to reclaim the one it had
|
|
|
|
|
* finished with. A `TTF_Font` is about ten kilobytes once FreeType's own
|
|
|
|
|
* structures are counted.
|
|
|
|
|
*
|
|
|
|
|
* The registry entry is cleared before the font is closed, so a font is never
|
|
|
|
|
* reachable through #AKGL_REGISTRY_FONT after it has gone back to SDL_ttf.
|
|
|
|
|
*
|
|
|
|
|
* @param name Registry key the font was published under. Required.
|
|
|
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
|
|
|
|
* @throws AKERR_NULLPOINTER If @p name is `NULL`.
|
|
|
|
|
* @throws AKERR_KEY If no font is registered under @p name -- including the
|
|
|
|
|
* case where it was already unloaded, which makes a double unload an
|
|
|
|
|
* error rather than a double close.
|
|
|
|
|
*
|
|
|
|
|
* @warning Anything still holding the `TTF_Font *` -- a caller that fetched it
|
|
|
|
|
* from the registry earlier, a pending akgl_text_rendertextat -- is
|
|
|
|
|
* left with a dangling pointer. Fonts are not reference counted.
|
|
|
|
|
*/
|
|
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_text_unloadfont(char *name);
|
2026-07-30 01:10:31 -04:00
|
|
|
/**
|
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 Rasterize a string and blit it at a screen position, in one call.
|
|
|
|
|
*
|
|
|
|
|
* Renders blended (anti-aliased, alpha-blended) through SDL_ttf, uploads the
|
|
|
|
|
* result to a texture, draws it through the global `renderer`, and destroys both
|
|
|
|
|
* the texture and the surface before returning. The text is drawn at its natural
|
|
|
|
|
* size -- @p x and @p y are the top-left corner, not a centre.
|
|
|
|
|
*
|
|
|
|
|
* Coordinates are screen coordinates, not world ones: this does not go through
|
|
|
|
|
* the camera, so a HUD stays put while the world scrolls under it.
|
|
|
|
|
*
|
|
|
|
|
* @param font Font to render with, from akgl_text_loadfont. Required.
|
|
|
|
|
* @param text UTF-8 text. Required. May contain newlines, which break
|
|
|
|
|
* lines on either path.
|
|
|
|
|
* @param color Text colour, including alpha.
|
|
|
|
|
* @param wraplength Wrap width in pixels. Greater than 0 wraps on word
|
|
|
|
|
* boundaries at that width; 0 or less draws a single line and
|
|
|
|
|
* breaks only on newlines in @p text.
|
|
|
|
|
* @param x Left edge of the text, in screen pixels.
|
|
|
|
|
* @param y Top edge of the text, in screen pixels.
|
2026-07-30 01:10:31 -04:00
|
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
2026-07-31 12:52:20 -04:00
|
|
|
* @throws AKERR_NULLPOINTER If @p font or @p text is `NULL`; if the global
|
|
|
|
|
* `renderer`, its `sdl_renderer`, or its `draw_texture` is `NULL` --
|
|
|
|
|
* that last one is the state a backend is in between being allocated
|
|
|
|
|
* and being run through akgl_render_bind2d(); if SDL_ttf cannot
|
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
|
|
|
* rasterize the string; or if the surface cannot be uploaded as a
|
|
|
|
|
* texture. The last two carry `SDL_GetError()` and are a reused status
|
|
|
|
|
* rather than a pointer problem.
|
|
|
|
|
* @throws AKERR_* Whatever the backend's `draw_texture` raises.
|
|
|
|
|
*
|
|
|
|
|
* @note On a failure after rasterizing -- the texture upload, or the draw -- the
|
|
|
|
|
* surface and texture are not destroyed, because the error returns before
|
|
|
|
|
* the cleanup. Repeated failures leak.
|
2026-07-31 12:52:20 -04:00
|
|
|
* @note The empty string is **refused**, not drawn as nothing: SDL_ttf reports
|
|
|
|
|
* "Text has zero width" and this passes that on as `AKERR_NULLPOINTER`.
|
|
|
|
|
* akgl_text_measure() accepts it, so the two disagree. A caller drawing a
|
|
|
|
|
* line of text that may be empty has to check for it. TODO.md, "Known and
|
|
|
|
|
* still open".
|
2026-07-30 01:10:31 -04:00
|
|
|
*/
|
2026-05-06 23:18:42 -04:00
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_text_rendertextat(TTF_Font *font, char *text, SDL_Color color, int wraplength, int x, int y);
|
2026-07-31 06:55:25 -04:00
|
|
|
/**
|
|
|
|
|
* @brief Report the size, in pixels, that @p text would occupy on one line.
|
|
|
|
|
*
|
|
|
|
|
* Nothing is drawn and no renderer is required. A caller building a character
|
|
|
|
|
* grid measures one cell with this -- the advance width of a single glyph in a
|
|
|
|
|
* monospaced font -- and derives the rest of the grid from it.
|
|
|
|
|
*
|
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 font Font to measure with, from akgl_text_loadfont. Required.
|
|
|
|
|
* @param text UTF-8 text to measure. Required. The empty string is legal and
|
|
|
|
|
* measures 0 wide by one line high.
|
|
|
|
|
* @param w Receives the width in pixels. Required.
|
|
|
|
|
* @param h Receives the height in pixels -- one line, whatever @p text
|
|
|
|
|
* contains, since this form does not wrap. Required.
|
2026-07-31 06:55:25 -04:00
|
|
|
* @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 font, @p text, @p w, or @p h is `NULL`.
|
|
|
|
|
* @throws AKGL_ERR_SDL If SDL_ttf cannot measure the string -- a corrupt font,
|
|
|
|
|
* or text that is not valid UTF-8. The message carries `SDL_GetError()`.
|
2026-07-31 06:55:25 -04:00
|
|
|
*/
|
|
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_text_measure(TTF_Font *font, char *text, int *w, int *h);
|
|
|
|
|
/**
|
|
|
|
|
* @brief Report the size, in pixels, that @p text would occupy when wrapped.
|
|
|
|
|
*
|
|
|
|
|
* The companion to akgl_text_measure() for the wrapping case, matching the
|
|
|
|
|
* @p wraplength argument akgl_text_rendertextat() already takes: a string
|
|
|
|
|
* longer than @p wraplength reports the height of every line it breaks onto.
|
|
|
|
|
* A @p wraplength of zero wraps only on newlines in @p text.
|
|
|
|
|
*
|
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 font Font to measure with, from akgl_text_loadfont. Required.
|
|
|
|
|
* @param text UTF-8 text to measure. Required.
|
|
|
|
|
* @param wraplength Wrap width in pixels. 0 wraps on newlines only. Negative is
|
|
|
|
|
* refused rather than passed through: SDL_ttf reads a negative
|
|
|
|
|
* width as a very large unsigned one and silently stops
|
|
|
|
|
* wrapping, which would return a measurement that is wrong
|
|
|
|
|
* rather than an error.
|
|
|
|
|
* @param w Receives the width in pixels: the longest line, not
|
|
|
|
|
* @p wraplength. Required.
|
|
|
|
|
* @param h Receives the height in pixels, covering every line the text
|
|
|
|
|
* wraps onto. Required.
|
2026-07-31 06:55:25 -04:00
|
|
|
* @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 font, @p text, @p w, or @p h is `NULL`.
|
|
|
|
|
* @throws AKERR_OUTOFBOUNDS If @p wraplength is negative.
|
|
|
|
|
* @throws AKGL_ERR_SDL If SDL_ttf cannot measure the string -- a corrupt font,
|
|
|
|
|
* or text that is not valid UTF-8. The message carries `SDL_GetError()`.
|
2026-07-31 06:55:25 -04:00
|
|
|
*/
|
|
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_text_measure_wrapped(TTF_Font *font, char *text, int wraplength, int *w, int *h);
|
2026-05-06 22:49:05 -04:00
|
|
|
|
|
|
|
|
#endif // _TEXT_H_
|