Check the renderer backend before drawing text

akgl_text_rendertextat() called renderer->draw_texture() with no check on
the global renderer, its sdl_renderer or the function pointer itself, so a
backend that has an SDL_Renderer but was never bound segfaults on the first
line of text -- the state akgl_render_bind2d() exists to get a host out of.
All three are now checked, with AKERR_NULLPOINTER as the draw entry points
report, and checked before anything is rasterized rather than after.

tests/text.c grows a software renderer with a bound backend and covers all
three refusals plus the successful draw, wrapped and unwrapped: src/text.c
goes from 58% to 100% line coverage. A made-up SDL_Renderer pointer is not
enough to test this -- SDL refuses the bogus handle on its own and the call
fails for the wrong reason, which a deleted check survives.

Also documents what the tests found: SDL_ttf refuses the empty string, so
drawing an empty line is an error while measuring one is not. Filed in
TODO.md rather than pinned in the suite.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-31 12:52:20 -04:00
parent ede3452c49
commit 42b53dcb20
3 changed files with 123 additions and 5 deletions

View File

@@ -64,7 +64,10 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_text_loadfont(char *name, char *filepath
* @param x Left edge of the text, in screen pixels.
* @param y Top edge of the text, in screen pixels.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_NULLPOINTER If @p font or @p text is `NULL`; if SDL_ttf cannot
* @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
* 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.
@@ -73,6 +76,11 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_text_loadfont(char *name, char *filepath
* @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.
* @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".
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_text_rendertextat(TTF_Font *font, char *text, SDL_Color color, int wraplength, int x, int y);
/**