libsdl-org / SDL_ttf

Support for TrueType (.ttf) font files with Simple Directmedia Layer.
zlib License
402 stars 131 forks source link

Bug: `TTF_DrawRendererText` hangs when drawing text created with wrap length set to 0 #418

Closed JBetz closed 1 month ago

JBetz commented 1 month ago

Some Smalltalk code to reproduce, using only direct API calls so should be easy to translate:

| sdl ttf window renderer textEngine font text |

"setup"
sdl := SDL3Library default.
sdl init_flags: SDL_INIT_VIDEO.
ttf := SDL3TTFLibrary default.
ttf init.
window := sdl createWindow_title: 'zero wrap length bug' w: 10 h: 10 flags: 0.
renderer := sdl createRenderer_window: window name: nil.

"ttf"
textEngine := ttf createRendererTextEngine_renderer: renderer.
font := ttf openFont_file: 'default.ttf' ptsize: 18 asFloat.
text := ttf createTextWrapped_engine: textEngine font: font text: 'foo' length: 3 wrapLength: 0.

"hangs inside this call"
ttf drawRendererText_text: text x: 0 y: 0

"cleanup"
...
slouken commented 1 month ago

Can you provide the font file you're using here?

JBetz commented 1 month ago

Yup, here it is, zipped to make GitHub happy: default.zip

JBetz commented 1 month ago

I've noticed that the same thing happens when I call TTF_RenderText_Blended_Wrapped with wrapLength set to 0, so it doesn't seem to be an issue with the new text engine.

JBetz commented 1 month ago

(Unless of course the old API now uses the text engine internally...

slouken commented 1 month ago

I'm not able to repro here. I'm running showfont.exe -textengine renderer default.ttf 18 foo and it shows up just fine. Can you attach a debugger while it's hung and break to get a stack trace?

JBetz commented 1 month ago

Here's what I got, though it's missing some source info for SDL_ttf. I can try to build it with debug symbols if that's needed.

image

slouken commented 1 month ago

Yeah, symbols would be helpful. :)

JBetz commented 1 month ago

Okay, here's the full stack trace and relevant source in SLD_ttf:

image

slouken commented 1 month ago

Can you step through and see why we're never leaving that loop?

JBetz commented 1 month ago

It looks like left never gets updated and stays at length 3, but needs to be 0 to break out of the do-while loop.

As for places it could be updated, spot is equal to text so we don't hit this block: https://github.com/libsdl-org/SDL_ttf/blob/3adac8ac7ca3be6a5f73a23d930c9f7449f99189/src/SDL_ttf.c#L3403-L3414

Nor this one, since max_count is always 0, and itself is the count return value of TTF_MeasureString: https://github.com/libsdl-org/SDL_ttf/blob/3adac8ac7ca3be6a5f73a23d930c9f7449f99189/src/SDL_ttf.c#L3441-L3469

slouken commented 1 month ago

Why is max_count 0 if wrapLength is 0?

JBetz commented 1 month ago

The measure_width argument passed to TTF_MeasureString is always 0 since it's always equal to wrapLength, which AFAICT means that count will always return 0:

https://github.com/libsdl-org/SDL_ttf/blob/3adac8ac7ca3be6a5f73a23d930c9f7449f99189/src/SDL_ttf.c#L3423-L3430

https://github.com/libsdl-org/SDL_ttf/blob/3adac8ac7ca3be6a5f73a23d930c9f7449f99189/src/SDL_ttf.c#L3115-L3131

https://github.com/libsdl-org/SDL_ttf/blob/3adac8ac7ca3be6a5f73a23d930c9f7449f99189/src/SDL_ttf.c#L3167-L3189

dananderson commented 1 month ago

Here is a simpler reproduction of the issue:

TTF_Text text = TTF_CreateText(text_engine, font, "hello", 0); TTF_DrawRendererText(text, 0, 0);

TTF_DrawRendererText will hang trying to layout the Text object. Single line text layout is broken. I noticed that the test programs and (maybe the tests?) don't do single line text rendering, The fix should include ensuring this code path is tested.

A work around is:

TTF_CreateText_Wrapped(text_engine, font, "hello", 0, INT_MAX)

slouken commented 1 month ago

Fixed, thanks!