pthom / imgui_bundle

Dear ImGui Bundle: an extensive set of Ready-to-use widgets and libraries, based on ImGui. Start your first app in 5 lines of code, or less. Whether you prefer Python or C++, this pack has your back!
https://pthom.github.io/imgui_bundle/
MIT License
647 stars 64 forks source link

Issues with text sizes #237

Closed Rewasvat closed 2 weeks ago

Rewasvat commented 1 month ago

Over the past few days I've been having some issues with text rendering in my project, so I thought it was best to ask about it. Not sure if it's a imgui-bundle (python) or regular imgui issue, but came here anyway.

In my project I need to be able to render text:

For custom fonts I've been using hello_imgui.load_font with FontLoadingParams(merge_to_last_font=False, inside_assets=False). And have a cache system to allow loading the same font over and over with different font-sizes

For rendering the text I've been using ImDrawList's add_text, passing the custom ImFont with the font-size to match the desired text area.

For the custom sizes part, initially I tried scaling the font_size arg to add_text, but that led to blurry/ugly scaled text. So I made the font-caching system I mentioned earlier, using things like imgui.calc_text_size and so to calculate text sizes (from a "default" fixed font-size), and thus calculate the desired font-size for the area in which I wanted to draw the text.

Loading the same font with different font-sizes probably isn't very efficient but it solved the blurry text issue. However, now I have a issue that the size of the text isn't what I expected, mostly its height. It's kinda like the text, as imgui renders it (and checks in calc_text_size), has padding at the top/bottom of the glyphs. The following image shows this: image

I appreciate any help solving this. Frankly I'm not sure if the things I did are correct. I find imgui's documentation in general kinda confusing. So what should/can I do to properly render quality text at any size, in any position, with custom TTF files? Or how could I calculate that "top/bottom padding" of the font in order to take it into account and fix the calculation of text size/areas?

pthom commented 1 month ago

Hello,

ImGui Bundle uses Dear ImGui's font rendering. Only the loading may differ, since it provides helpers that will eventually call ImGui font loading functions. For information, Dear ImGui can use either stb for the font rendering, or freetype. When using ImGui Bundle, the rendering is done via FreeType.

For the custom sizes part, initially I tried scaling the font_size arg to add_text, but that led to blurry/ugly scaled text.

For performance reasons, Dear ImGui will preload all the font glyphs at the desired size into a texture. This way, text rendering is then handled by pasting part of this texture onto the screen. This is the reason why it might lead to blurry text when rendering at sizes different from those that were loaded. Please note that loading many fonts at many different sizes might overload your GPU memory.

the blue horizontal lines is where I wanted the text height. the green lines is the size returned from calc_text_size in this example, I had to offset the Y pos, and scale the text area height by 1.45 so the text height matches the desired area height. the "bottom padding" seems to be to account for the "tails" or "bottom parts" of glyphs like q / g / p and so on (not sure the name of this), but the "top padding" has no discernible reason.

I think you might need to have a further look into font rendering, and may be at freetype documentation. The terms you're referring to are "Ascent" and "Descent." Here is what a conversation with ChatGPT gave me; you might find interesting info there (expecially at the end) https://chatgpt.com/share/f1cc254a-0a31-403e-8f63-64085d6be3b9

Rewasvat commented 1 month ago

Sorry for the delay, been busy and only now managed to come back to this.

Good to know imgui-bundle already uses freetype. Don't recall seeing that in the docs/manual.

As for the performance aspect, I had that in mind already. In my project, for now, don't really mind that - the benefits of custom sizes will be better for UX than the performance costs. Also it's kind of a personal project, for now I don't think much people will use it for that to matter. I probably could change the text rendering to better suit my needs but that would require fiddling around with the native (C++) side of dear-imgui/imgui-bundle. This project is so far python-only (well, in what I code) and I like to keep it that way for simplicity.

However, I did have two ideas that might mitigate the performance issue:

Any pointers/hints on how I can do those? It's been some days since I've worked on this so I might've missed something but I don't recall seeing anything about unloading a font... And as for the partial glyph loading, I remember seeing stuff in the API about that using some predefined glyph-ranges but dunno at the moment how to create my own glyph-range for loading. Maybe would also need to merge the loading font into another? Remember I tried that initially when loading my custom TTFs and it had caused an error đŸ¤”

As for the proper text size calculation, gonna check that and test using the font's ascent/descent for calculation, thanks! When I have any results I'll get back here to let you and anyone else reading know.

pthom commented 1 month ago

Deleting/freeing memory from ImFonts I loaded that haven't been used in a while.

I don't think it's possible to clear only one font. But you can:

imgui.get_io().fonts.clear()
pthom commented 1 month ago

Only loading the glyphs I'm currently using, instead of the default load of all glyphs.

See https://github.com/pthom/imgui_bundle/blob/7d8fd2159b62389f8c8d2a9eecaa16482a957e29/bindings/imgui_bundle/hello_imgui.pyi#L881-L888

and https://github.com/pthom/imgui_bundle/blob/main/bindings/imgui_bundle/demos_python/demos_immapp/demo_font_common_glyph_range.py

Rewasvat commented 1 month ago

Thanks. I'll test those eventually.

As for the size calculation, I got it working with the font.descent value. After some trial & error, I figured 2 (out of 3) fonts I use have a 2 * descent space at the top, and the expected descent space at the bottom. The other font was only a single descent space at the top and bottom. Not the prettiest solution but it works fine. And since I most likely won't change the fonts I use or add new ones in the foreseeable future, it'll do.

I also had to put a max font size limit in place, since depending on context the code could try to use a font with size 230 (or larger) and that would crash imgui since the fixed buffer to load glyphs couldn't handle such large images.

pthom commented 2 weeks ago

I'm closing this issue please reopen it if you have more related questions