Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fonts at runtime after initial initiation? #2311

Closed
nnoc opened this issue Jan 27, 2019 · 11 comments
Closed

Fonts at runtime after initial initiation? #2311

nnoc opened this issue Jan 27, 2019 · 11 comments

Comments

@nnoc
Copy link

nnoc commented Jan 27, 2019

Hi!
Hope format is correct and I'll go straight to the matter / deleted most of the format for things I don't have. I've been looking at related issues but I didn't see anyone trying this.

Version: // dear imgui, v1.67 WIP
Branch: master

My Question:
Is it even possible to use io.Fonts->AddFontFromFileTTF(filePath.c_str(), fontSize); at runtime after the initial initiation of ImGui?
It does load the font but doesn't build the atlas somehow: when I try to use the loaded font, it throws an error at,
IM_ASSERT(font && font->IsLoaded());
in ImGui::SetCurrentFont(ImFont* font) when trying to use ImGui::PushFont(ImFont* font)

Thank you for your time.

@ocornut
Copy link
Owner

ocornut commented Jan 27, 2019

Hello,

You need to rebuild the atlas if you add new fonts. Atlas needs to be rebuilt before NewFrame().

@nnoc
Copy link
Author

nnoc commented Jan 29, 2019

I can't rebuild Atlas in runtime?
Say in a scenario where an user decides to load a custom font.
I can't really do this, can I?

@ocornut
Copy link
Owner

ocornut commented Jan 29, 2019

Yes you can.. just call Fonts->Build() et Fonts->GetTexDataAsXXX() and reupload the texture..

@zejal
Copy link

zejal commented Feb 3, 2019

Hello, I am trying to implement similar feature but does not seem to work. I call Fonts->AddFontFromFileTTF() + Fonts->Build() before NewFrame() but everything in main window disappears (modifying code in example_win32_directx11 main.cpp file).

    // Main loop
    MSG msg;
    ZeroMemory(&msg, sizeof(msg));
    while (msg.message != WM_QUIT)
    {
          if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) {…};
          if (load_font_file)  {  // set to true if some button has been hit
                 const auto * font = io.Fonts->AddFontFromFileTTF(new_font_file.c_str(), 12.0f);
                 io.Fonts->Build();
                 load_font_file = false;
           }
           // Start the Dear ImGui frame
           ImGui_ImplDX11_NewFrame();
           ImGui_ImplWin32_NewFrame();
           ImGui::NewFrame();
          ....
      ....

Is there any missing function call? You mention "reupload the texture", but I have no idea on how to do this.
Thanks in advance.

EDIT: Adding call to ImGui_ImplDX11_InvalidateDeviceObjects(); after io.Fonts->Build(); seems to solve the problem.

@ocornut
Copy link
Owner

ocornut commented Feb 3, 2019

Adding call to ImGui_ImplDX11_InvalidateDeviceObjects(); after io.Fonts->Build(); seems to solve the problem.

Good to hear!

@GordonEldest
Copy link

Is there any missing function call? You mention "reupload the texture", but I have no idea on how to do this.
Thanks in advance.

EDIT: Adding call to ImGui_ImplDX11_InvalidateDeviceObjects(); after io.Fonts->Build(); seems to solve the problem.

ImGui_ImplDX11_InvalidateDeviceObjects() may be overkill
Reason:
This will trigger a call to ImGui_ImplDX11_CreateDeviceObjects() which do the heavy lifting to create every shaders etc ...

While ok once in a while, For those who want more optimal use of CPU, AND feel ready to modify the imgui_impl_dx11.cpp (not a big deal) hereafter a suggestion:

For good reason (at least to ease avoiding resources leak) the ImGui_ImplDX11_CreateFontsTexture(); is a static procedure in imgui_impl_dx11.cpp
Circumvent that by creating hereafter code in global void ImGui_ImplDX11_ReCreateFontsTexture()
(tail it in imgui_impl_dx11.cpp, tail is safer and easier to track)
which will in charge of Releasing any previous resources (did seems doing the work for me) and just reload the texture.

void ImGui_ImplDX11_ReCreateFontsTexture(){
    if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; }
    if (g_pFontTextureView) { g_pFontTextureView->Release(); g_pFontTextureView = NULL; ImGui::GetIO().Fonts->SetTexID(NULL);}
    ImGui_ImplDX11_CreateFontsTexture();
}

Usage:
in main loop, locate ImGui_ImplDX11_NewFrame() and put below code BEFORE it, like:

extern void ImGui_ImplDX11_ReCreateFontsTexture();
blabla code
...

		if (g_bAtlasNeedUpdate) {
			io.Fonts->Build();
			ImGui_ImplDX11_ReCreateFontsTexture();
		}
		ImGui_ImplDX11_NewFrame();
		ImGui_ImplWin32_NewFrame();
		ImGui::NewFrame();

@ocornut
Copy link
Owner

ocornut commented Apr 22, 2021

#3761 will provide a more generic solution to this.

@GordonEldest
Copy link

GordonEldest commented Apr 22, 2021

Yes!
Nicer.
Cross platforms (99%)

In DX11 far more elegant use of g_pd3dDeviceContext->UpdateSubresource(g_pFontTexture, 0, &box, pixels, g_FontTextureWidth * 4, 0);
I didn't look other backend implementation.

Suggestion:
Implement a check/reject that Font Texture Map is less than 4Kx32K , DX11 Crash is inelegant on this one.
Better be early, somewhere like in AddFont return NULL.
It may require to call ImFontAtlasBuildWithStbTruetype to know final rendered size,. This is less elegant that doing a batch on next frame but surely somebody will hit Texture map size limit even if look crazy high!
It happen to me during my tests!

At least reject (even if silently in release) the UpdateSubresource if map is too big.
Great work!

@Renardjojo
Copy link

Renardjojo commented May 26, 2021

Hello, I am trying to implement similar feature but does not seem to work. I call Fonts->AddFontFromFileTTF() + Fonts->Build() >before NewFrame() but everything in main window disappears (modifying code in example_win32_directx11 main.cpp file).

I solving this problem in OpenGL thanks to this code (and without Build function):

            ImGui::GetIO().Fonts->AddFontFromFileTTF(path, pixel_size);
            ImGui_ImplOpenGL3_CreateFontsTexture();

Hope that this can help

@spaderthomas
Copy link

spaderthomas commented Jun 2, 2022

I am also running into trouble updating fonts at runtime. To me, this seems sufficient:

// Remove the fonts you had
 ImGui::GetIO().Fonts->Clear();

// Add some new fonts
ImGui::GetIO().Fonts->AddFontFromFileTTF(path, size); 

// The next part uses the GLFW + GL3 implementation as an example -- that's what I'm using.
// It is copied verbatim from ImGui_ImplGlfwGL3_CreateFontsTexture, but removes the code that
// creates the GL devices

// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);

// Upload texture to OpenGL
glBindTexture(GL_TEXTURE_2D, g_FontTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

However, this is not working. InvalidateDeviceObjects() followed by CreateDeviceObjects() does work. Reading through the source code, it is unclear what we are doing when we invalidate / create device objects that is necessary versus simply rebuilding and reuploading the texture.

I also consulted the PR above (https://github.com/ocornut/imgui/pull/3761/files#), and it appears that the GL3 implementation is doing just what I describe.

Could you help me understand what I am missing? Thank you kindly!

@KulaGGin
Copy link

KulaGGin commented May 3, 2023

ImGui_ImplDX11_InvalidateDeviceObjects() may be overkill Reason: This will trigger a call to ImGui_ImplDX11_CreateDeviceObjects() which do the heavy lifting to create every shaders etc ...

While ok once in a while, For those who want more optimal use of CPU, AND feel ready to modify the imgui_impl_dx11.cpp (not a big deal) hereafter a suggestion:

For good reason (at least to ease avoiding resources leak) the ImGui_ImplDX11_CreateFontsTexture(); is a static procedure in imgui_impl_dx11.cpp Circumvent that by creating hereafter code in global void ImGui_ImplDX11_ReCreateFontsTexture() (tail it in imgui_impl_dx11.cpp, tail is safer and easier to track) which will in charge of Releasing any previous resources (did seems doing the work for me) and just reload the texture.

void ImGui_ImplDX11_ReCreateFontsTexture(){
    if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; }
    if (g_pFontTextureView) { g_pFontTextureView->Release(); g_pFontTextureView = NULL; ImGui::GetIO().Fonts->SetTexID(NULL);}
    ImGui_ImplDX11_CreateFontsTexture();
}

Usage: in main loop, locate ImGui_ImplDX11_NewFrame() and put below code BEFORE it, like:

extern void ImGui_ImplDX11_ReCreateFontsTexture();
blabla code
...

		if (g_bAtlasNeedUpdate) {
			io.Fonts->Build();
			ImGui_ImplDX11_ReCreateFontsTexture();
		}
		ImGui_ImplDX11_NewFrame();
		ImGui_ImplWin32_NewFrame();
		ImGui::NewFrame();

This works. I think this is too low level and requires users to know deep implementation details. I want to change the default font, but it doesn't mean that I know how the whole ImGui and DirectX work. I think we as users shouldn't concern ourselves with such low level implementation details.
What if we could just do:

ImGui::GetIO().FontDefault = ImGui::GetIO().Fonts->AddFontFromFileTTF(fontPath, sizePixels);

To set the default font. And let the framework invalidate objects, build fonts, recreate font textures, etc as they're required?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants