-
-
Notifications
You must be signed in to change notification settings - Fork 11.7k
How to handle multiple fonts (e.g. regular, bold, etc.) and an icon font (e.g. FontAwesome)? + 1.92 update #8749
Description
Version/Branch of Dear ImGui:
Version 1.92, Branch: docking
Back-ends:
Custom/Unreal Engine
Compiler, OS:
Windows 10 + MSVC 14.38.33130
Full config/build information:
No response
Details:
Multiple Text Fonts + one Icon Font
Hello there!
I'm trying to understand how one is supposed to handle adding multiple fonts for text, such as Roboto-Regular and Roboto-Bold, while also adding a font for icons, such as FontAwesome.
I do understand how to add a given font:
ImGuiIO& IO = ImGui::GetIO();
const FString RobotoRegularFontPath = FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Regular.ttf");
if (FPaths::FileExists(*RobotoRegularFontPath))
{
IO.Fonts->AddFontFromFileTTF(TCHAR_TO_UTF8(*RobotoRegularFontPath));
}
I also think I understand, based on some 1.92 update notes, that it's expected to use the MergeMode on the icon font, to merge it into the text font:
// Above this is the RobotoRegular code.
ImFontConfig FontAwesomeFontConfig_Solid = ImFontConfig();
static constexpr ImWchar SolidIconRanges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; // Is not needed anymore by now.
FontAwesomeFontConfig_Solid.FontDataOwnedByAtlas = false;
FontAwesomeFontConfig_Solid.FontData = (void*)fa_solid_900;
FontAwesomeFontConfig_Solid.FontDataSize = fa_solid_900_size;
FontAwesomeFontConfig_Solid.SizePixels = 14.0f;
FontAwesomeFontConfig_Solid.GlyphRanges = SolidIconRanges; // Is not needed anymore by now.
FontAwesomeFontConfig_Solid.MergeMode = true;
ImFormatString(FontAwesomeFontConfig_Solid.Name, IM_ARRAYSIZE(FontAwesomeFontConfig_Solid.Name), "%s, %.0fpx", "fa-solid-900.tff", FontAwesomeFontConfig_Solid.SizePixels);
IO.Fonts->AddFont(&FontAwesomeFontConfig_Solid);
(Note: I might still be missing some changes, such as the GlyphRanges not being needed, as I'm still discovering some of the required changes. You can gladly point those out if you notice them.)
However, if I were to add a second text font, such as Roboto-Bold, without also merging the icon font into that one too, I would end up losing access to the icon font when using the Roboto-Bold font via PushFont for some of my text, right?
If that's true, is the idea to define the above FontAwesomeFontConfig_Solid once before adding the Roboto-XYZ fonts, and then always adding the FontAwesomeFontConfig_Solid after each time I add a Roboto font? So basically this:
// FontAwesomeFontConfig_Solid code here.
const FString RobotoRegularFontPath = FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Regular.ttf");
if (FPaths::FileExists(*RobotoRegularFontPath))
{
IO.Fonts->AddFontFromFileTTF(TCHAR_TO_UTF8(*RobotoRegularFontPath));
IO.Fonts->AddFont(&FontAwesomeFontConfig_Solid);
}
const FString RobotoBoldFontPath = FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Bold.ttf");
if (FPaths::FileExists(*RobotoBoldFontPath))
{
IO.Fonts->AddFontFromFileTTF(TCHAR_TO_UTF8(*RobotoBoldFontPath));
IO.Fonts->AddFont(&FontAwesomeFontConfig_Solid);
}
If that's correct, does that add any overhead, given the icon font is added multiple times?
And if that's not correct, what is the suggested way of handling this?
1.92 Update
I'm still in the process of updating to 1.92, and while going over the update notes, I have problems understanding what some of the notes want to tell me.
Font Merging
font inputs are now scanned in order for the first font input which the desired glyph.
That sentence doesn't read like proper English to me. Either this is a language barrier on my end, or there are mistakes in it.
If you are merging fonts you may have glyphs that you expected to load from Font Source 2 which exists in Font Source 1.
I believe this tries to tell me that if Font Source 1 has glyphs in the same range of glyphs that Font Source 2 covers, we can tell Font Source 1 to exclude those, so they don't end up overlapping?
After the update and when using a new backend, those glyphs may now loaded from Font Source 1!
That part I also can't follow. Does this mean that, if previously there were issues with this overlap of glyph range, this can now be resolved by excluding the glyphs when adding Font Source 1? If I didn't have any issues with this before, then I assume this is all irrelevant to me?
Removed ImFontAtlas function
The update notes say that a lot of the ImFontAtlas functions are now obsolete and have been removed.
The ones I was using up until now were:
- IsBuilt()
- GetTexDataAsRGBA32()
- SetTexID()
At the beginning of a frame, before calling ImGui::NewFrame(), I was doing this:
if (!IO.Fonts->IsBuilt() || !FontAtlasTexturePtr.IsValid())
{
uint8* TextureData;
int32 TextureWidth, TextureHeight, BytesPerPixel;
IO.Fonts->GetTexDataAsRGBA32(&TextureData, &TextureWidth, &TextureHeight, &BytesPerPixel);
#if WITH_ENGINE
const FImageView TextureView(TextureData, TextureWidth, TextureHeight, ERawImageFormat::BGRA8);
FontAtlasTexturePtr.Reset(FImageUtils::CreateTexture2DFromImage(TextureView));
#else
FontAtlasTexturePtr = FSlateDynamicImageBrush::CreateWithImageData(
TEXT("ImGuiFontAtlas"), FVector2D(TextureWidth, TextureHeight),
TArray(TextureDataRaw, TextureWidth * TextureHeight * BytesPerPixel));
#endif
IO.Fonts->SetTexID(FontAtlasTexturePtr.Get());
}
You can ignore the #if WITH_ENGINE part, that's Unreal Engine specific.
The update notes state:
The new protocol for backends to handle textures doesn't need them. Kept redirection functions (will obsolete).
Does this refer to the section Textures: added partial texture update protocol. in the update notes?
If so, do I understand correctly that I now have to actively handle the different ImTextureStatus values etc.?
I don't think I had to do any of this before. In the current implementation, I have the code that I posted above for the FontAtlasTexturePtr and then code in the rendering section of a Slate (Unreal Engine) Widget, which grabs the TextureID (now TexRef.GetTexID()) and simply uses that for an FSlateDrawElement. Those TextureIDs are usually either UTextures or FSlateBrushs, which are either assets from the project, or already runtime-created within Unreal Engine (user) code.
If I set io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures does this only affect the FontAtlasTexture or will this then also call for the other Textures, such as an asset already loaded by Unreal Engine?
Kind regards
Cedric
Screenshots/Video:
No response