diff --git a/examples/renderer/18-debug-text/debug-text.c b/examples/renderer/18-debug-text/debug-text.c index 85bc66ca8..b4a2ca666 100644 --- a/examples/renderer/18-debug-text/debug-text.c +++ b/examples/renderer/18-debug-text/debug-text.c @@ -62,6 +62,9 @@ SDL_AppResult SDL_AppIterate(void *appstate) SDL_RenderDebugText(renderer, 14, 65, "It can be scaled."); SDL_SetRenderScale(renderer, 1.0f, 1.0f); SDL_RenderDebugText(renderer, 64, 350, "This only does ASCII chars. So this laughing emoji won't draw: 🤣"); + + SDL_RenderDebugTextF(renderer, 0, 0, "This program has been running for %llu seconds.", SDL_GetTicks() / 1000); + SDL_RenderPresent(renderer); /* put it all on the screen! */ return SDL_APP_CONTINUE; /* carry on with the program! */ diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index 5a16d1e88..0aabb8f33 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -2530,10 +2530,68 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetRenderVSync(SDL_Renderer *renderer, int * * \since This function is available since SDL 3.1.6. * + * \sa SDL_RenderDebugTextF + * \sa SDL_RenderDebugTextV * \sa SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE */ extern SDL_DECLSPEC bool SDLCALL SDL_RenderDebugText(SDL_Renderer *renderer, float x, float y, const char *str); +/** + * Draw debug text to an SDL_Renderer. + * + * This function will render a printf() style format string to a renderer. Note + * that this is a convinence function for debugging, with severe limitations, + * and is not intended to be used for production apps and games. + * + * For the full list of limitations and other useful information, + * see SDL_RenderDebugText. + * + * \param renderer the renderer which should draw the text. + * \param x the x coordinate where the top-left corner of the text will draw. + * \param y the y coordinate where the top-left corner of the text will draw. + * \param fmt the format string to draw. + * \param ... format arguments + * \returns true on success or false on failure; call SDL_GetError() for more + * information. + * + * \threadsafety This function should only be called on the main thread. + * + * \since This function is available since SDL 3.1.7. + * + * \sa SDL_RenderDebugText + * \sa SDL_RenderDebugTextV + * \sa SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE + */ +extern SDL_DECLSPEC bool SDLCALL SDL_RenderDebugTextF(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) SDL_PRINTF_VARARG_FUNC(4); + +/** + * Draw debug text to an SDL_Renderer. + * + * This function will render a printf() style format string to a renderer. Note + * that this is a convinence function for debugging, with severe limitations, + * and is not intended to be used for production apps and games. + * + * For the full list of limitations and other useful information, + * see SDL_RenderDebugText. + * + * \param renderer the renderer which should draw the text. + * \param x the x coordinate where the top-left corner of the text will draw. + * \param y the y coordinate where the top-left corner of the text will draw. + * \param fmt the format string to draw. + * \param ap a variable argument lists representing the format arguments + * \returns true on success or false on failure; call SDL_GetError() for more + * information. + * + * \threadsafety This function should only be called on the main thread. + * + * \since This function is available since SDL 3.1.7. + * + * \sa SDL_RenderDebugText + * \sa SDL_RenderDebugTextF + * \sa SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE + */ +extern SDL_DECLSPEC bool SDLCALL SDL_RenderDebugTextV(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap) SDL_PRINTF_VARARG_FUNCV(4); + /* Ends C function definitions when using C++ */ #ifdef __cplusplus } diff --git a/src/dynapi/SDL_dynapi.c b/src/dynapi/SDL_dynapi.c index da414879b..3229b5014 100644 --- a/src/dynapi/SDL_dynapi.c +++ b/src/dynapi/SDL_dynapi.c @@ -149,6 +149,16 @@ static void SDL_InitDynamicAPI(void); va_end(ap); \ return result; \ } \ + _static bool SDLCALL SDL_RenderDebugTextF##name(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \ + { \ + bool result; \ + va_list ap; \ + initcall; \ + va_start(ap, fmt); \ + result = jump_table.SDL_RenderDebugTextV(renderer, x, y, fmt, ap); \ + va_end(ap); \ + return result; \ + } \ _static void SDLCALL SDL_Log##name(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \ { \ va_list ap; \ diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index d575bcef9..7d4ee58d2 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -1207,6 +1207,8 @@ SDL3_0.0.0 { SDL_SetGPUAllowedFramesInFlight; SDL_RenderTextureAffine; SDL_WaitAndAcquireGPUSwapchainTexture; + SDL_RenderDebugTextF; + SDL_RenderDebugTextV; # extra symbols go here (don't modify this line) local: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 608d59ca1..6031adab1 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -1232,3 +1232,5 @@ #define SDL_SetGPUAllowedFramesInFlight SDL_SetGPUAllowedFramesInFlight_REAL #define SDL_RenderTextureAffine SDL_RenderTextureAffine_REAL #define SDL_WaitAndAcquireGPUSwapchainTexture SDL_WaitAndAcquireGPUSwapchainTexture_REAL +#define SDL_RenderDebugTextF SDL_RenderDebugTextF_REAL +#define SDL_RenderDebugTextV SDL_RenderDebugTextV_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 461159528..cedc79f58 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1238,3 +1238,7 @@ SDL_DYNAPI_PROC(bool,SDL_RunOnMainThread,(SDL_MainThreadCallback a,void *b,bool SDL_DYNAPI_PROC(bool,SDL_SetGPUAllowedFramesInFlight,(SDL_GPUDevice *a,Uint32 b),(a,b),return) SDL_DYNAPI_PROC(bool,SDL_RenderTextureAffine,(SDL_Renderer *a,SDL_Texture *b,const SDL_FRect *c,const SDL_FPoint *d,const SDL_FPoint *e,const SDL_FPoint *f),(a,b,c,d,e,f),return) SDL_DYNAPI_PROC(bool,SDL_WaitAndAcquireGPUSwapchainTexture,(SDL_GPUCommandBuffer *a,SDL_Window *b,SDL_GPUTexture **c,Uint32 *d,Uint32 *e),(a,b,c,d,e),return) +#ifndef SDL_DYNAPI_PROC_NO_VARARGS +SDL_DYNAPI_PROC(bool,SDL_RenderDebugTextF,(SDL_Renderer *a,float b,float c,SDL_PRINTF_FORMAT_STRING const char *d,...),(a,b,c,d),return) +#endif +SDL_DYNAPI_PROC(bool,SDL_RenderDebugTextV,(SDL_Renderer *a,float b,float c,SDL_PRINTF_FORMAT_STRING const char *d,va_list e),(a,b,c,d,e),return) diff --git a/src/dynapi/gendynapi.py b/src/dynapi/gendynapi.py index a12e68f9f..c780dac6d 100755 --- a/src/dynapi/gendynapi.py +++ b/src/dynapi/gendynapi.py @@ -168,9 +168,11 @@ def parse_header(header_path: Path) -> list[SdlProcedure]: func = func.replace(" SDL_PRINTF_VARARG_FUNC(1)", "") func = func.replace(" SDL_PRINTF_VARARG_FUNC(2)", "") func = func.replace(" SDL_PRINTF_VARARG_FUNC(3)", "") + func = func.replace(" SDL_PRINTF_VARARG_FUNC(4)", "") func = func.replace(" SDL_PRINTF_VARARG_FUNCV(1)", "") func = func.replace(" SDL_PRINTF_VARARG_FUNCV(2)", "") func = func.replace(" SDL_PRINTF_VARARG_FUNCV(3)", "") + func = func.replace(" SDL_PRINTF_VARARG_FUNCV(4)", "") func = func.replace(" SDL_WPRINTF_VARARG_FUNC(3)", "") func = func.replace(" SDL_WPRINTF_VARARG_FUNCV(3)", "") func = func.replace(" SDL_SCANF_VARARG_FUNC(2)", "") diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index a7b4c5e25..241916d44 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -3995,7 +3995,7 @@ bool SDL_RenderTextureAffine(SDL_Renderer *renderer, SDL_Texture *texture, xy[0] = real_dstrect.x; xy[1] = real_dstrect.y; } - + // (maxx, miny) if (right) { xy[2] = right->x; @@ -4025,7 +4025,7 @@ bool SDL_RenderTextureAffine(SDL_Renderer *renderer, SDL_Texture *texture, result = QueueCmdGeometry( renderer, texture, - xy, xy_stride, + xy, xy_stride, &texture->color, 0 /* color_stride */, uv, uv_stride, num_vertices, indices, num_indices, size_indices, @@ -5589,3 +5589,46 @@ bool SDL_RenderDebugText(SDL_Renderer *renderer, float x, float y, const char *s return result; } + +bool SDL_RenderDebugTextF(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + bool result = SDL_RenderDebugTextV(renderer, x, y, fmt, ap); + va_end(ap); + + return result; +} + +bool SDL_RenderDebugTextV(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap) +{ + // Probably for the best to check this here, so we don't do a bunch of string formatting before realizing the renderer isn't even valid... + CHECK_RENDERER_MAGIC(renderer, false); + + va_list apc; + va_copy(apc, ap); // vsnprintf mangles ap, so copy it so it can be used again later + int len = SDL_vsnprintf(NULL, 0, fmt, apc); + va_end(apc); + + if (len < 0) { + return SDL_SetError("Failed to format debug text"); + } + + char *buf = SDL_malloc(len + 1); + if (buf == NULL) { + return SDL_OutOfMemory(); + } + + len = SDL_vsnprintf(buf, len + 1, fmt, ap); + if (len < 0) { + SDL_free(buf); + return SDL_SetError("Failed to format debug text"); + } + + bool result = SDL_RenderDebugText(renderer, x, y, buf); + + SDL_free(buf); + + return result; +}