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

Font display does not account for descent and ascent when using ImGuiFreeTypeBuilderFlags_Bitmap #8416

Open
pipiwoaini opened this issue Feb 20, 2025 · 6 comments

Comments

@pipiwoaini
Copy link

Version/Branch of Dear ImGui:

Version 1.9.5, Branch: docking

Back-ends:

imgui_impl_SDL3.cpp + imgui_impl_SDL3Renderer.cpp

Compiler, OS:

Win22+vs2022

Full config/build information:

No response

Details:

“I’m using fonts created with Freetype and FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Bitmap. I noticed that in ImGui, the line height in RenderText is directly calculated using the font size, which results in no spacing between lines of text. I modified the line_height to (fabs(Descent) + fabs(Ascent)) * scale, and it displayed correctly. Is this a bug?”

Screenshots/Video:

No response

Minimal, Complete and Verifiable Example code:

void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip)
{
if (!text_end)
text_end = text_begin + strlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls.

// Align to be pixel perfect
float x = IM_TRUNC(pos.x);
float y = IM_TRUNC(pos.y);
if (y > clip_rect.w)
    return;

const float scale = size / FontSize;
const float line_height = (fabs(Descent) + fabs(Ascent)) * scale
const float origin_x = x;
const bool word_wrap_enabled = (wrap_width > 0.0f);
@ocornut
Copy link
Owner

ocornut commented Feb 20, 2025

Fonts loaded using ImGuiFreeTypeBuilderFlags_Bitmap are calculating size differently:

// Vuhdo: I'm not sure how to deal with font sizes properly. As far as I understand, currently ImGui assumes that the 'pixel_height'
// is a maximum height of an any given glyph, i.e. it's the sum of font's ascender and descender. Seems strange to me.
// NB: FT_Set_Pixel_Sizes() doesn't seem to get us the same result.
FT_Size_RequestRec req;
req.type = (UserFlags & ImGuiFreeTypeBuilderFlags_Bitmap) ? FT_SIZE_REQUEST_TYPE_NOMINAL : FT_SIZE_REQUEST_TYPE_REAL_DIM;
req.width = 0;
req.height = (uint32_t)(pixel_height * 64 * RasterizationDensity);
req.horiResolution = 0;
req.vertResolution = 0;
FT_Request_Size(Face, &req);

We might have an issue to fix but you could specify which font you are using and provide e.g. a repro in the form of font loading code?

@pipiwoaini
Copy link
Author

I’m using the Noto Sans font, and the spacing issue is very easy to reproduce with Chinese characters. Below is my code.
ImFontConfig m_font_config;
float m_dpi = 1.00f;

m_font_config.FontDataOwnedByAtlas = false;
m_font_config.OversampleH = 1;
m_font_config.OversampleV = 1;
m_font_config.PixelSnapH = true;
m_font_config.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Bitmap;
m_font_config.RasterizerDensity = 1.00f;

ImFontAtlas* atlas = ImGui::GetIO().Fonts;
atlas->Clear();

for (auto& iter : g_font.second) {
    vector<uint8_t> m_font_data;
    if (((FONTINFO*)iter)->font_weight == 0) {
        m_font_data = (*g_zip_font)["Regular.ttf"];
    }
    else if (((FONTINFO*)iter)->font_weight == 1) {
        m_font_data = (*g_zip_font)["Medium.ttf"];
    }
    else if (((FONTINFO*)iter)->font_weight == 2) {
        m_font_data = (*g_zip_font)["SemiBold.ttf"];
    }

    ((FONTINFO*)iter)->data = atlas->AddFontFromMemoryTTF(m_font_data.data(), m_font_data.size(), ((FONTINFO*)iter)->font_px, &m_font_config, ((FONTINFO*)iter)->font_range.data());
    if (!((FONTINFO*)iter)->data) {
        printf("Error in %dpx font\n", ((FONTINFO*)iter)->font_px);
    }
    else {
        ((ImFont*)((FONTINFO*)iter)->data)->Scale = m_dpi;
    }
}

atlas->Build();

ImGui_ImplSDLRenderer3_DestroyFontsTexture();
if (ImGui_ImplSDLRenderer3_CreateFontsTexture()) {
    g_breload = false;
}
else {
    g_breload = true;
}

@ocornut
Copy link
Owner

ocornut commented Feb 20, 2025

Could you specify some example characters and show a screenshot?
We rarely have time to handle all issues immediately, so any extra reference is helpful when needing to come back at things.
Thanks!

@pipiwoaini
Copy link
Author

int main(int, char**)
{
// Setup SDL
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD))
{
printf("Error: SDL_Init(): %s\n", SDL_GetError());
return -1;
}

// Create window with SDL_Renderer graphics context
Uint32 window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN;
SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL3+SDL_Renderer example", 1280, 720, window_flags);
if (window == nullptr)
{
    printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError());
    return -1;
}
SDL_Renderer* renderer = SDL_CreateRenderer(window, nullptr);
SDL_SetRenderVSync(renderer, 1);
if (renderer == nullptr)
{
    SDL_Log("Error: SDL_CreateRenderer(): %s\n", SDL_GetError());
    return -1;
}
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
SDL_ShowWindow(window);

// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls

// Setup Dear ImGui style
ImGui::StyleColorsDark();
//ImGui::StyleColorsLight();

// Setup Platform/Renderer backends
ImGui_ImplSDL3_InitForSDLRenderer(window, renderer);
ImGui_ImplSDLRenderer3_Init(renderer);

ImFontConfig m_font_config;

m_font_config.FontDataOwnedByAtlas = false;
m_font_config.OversampleH = 1;
m_font_config.OversampleV = 1;
m_font_config.PixelSnapH = true;
m_font_config.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Bitmap;
m_font_config.RasterizerDensity = 1;
ImFont* m_font = ImGui::GetIO().Fonts->AddFontFromFileTTF("C:\\Users\\fox\\Desktop\\imgui-master\\Medium.ttf", 13, &m_font_config, ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon());

while (true)
{
    SDL_Event event;
    while (SDL_PollEvent(&event))
    {
        ImGui_ImplSDL3_ProcessEvent(&event);
    }
    if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)
    {
        SDL_Delay(10);
        continue;
    }

    // Start the Dear ImGui frame
    ImGui_ImplSDLRenderer3_NewFrame();
    ImGui_ImplSDL3_NewFrame();
    ImGui::NewFrame();

    ImGui::PushFont(m_font);
    ImGui::ShowDemoWindow();
        ImGui::PopFont();

    // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window.
    {
        static float f = 0.0f;
        static int counter = 0;

        ImGui::Begin("Hello, world!");                          // Create a window called "Hello, world!" and append into it.

        ImGui::PushFont(m_font);
        ImGui::Text("测试文本\n测试文本");
        ImGui::PopFont();

        ImGui::End();
    }


    // Rendering
    ImGui::Render();
    //SDL_RenderSetScale(renderer, io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
    //SDL_SetRenderDrawColorFloat(renderer, clear_color.x, clear_color.y, clear_color.z, clear_color.w);
    SDL_RenderClear(renderer);
    ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), renderer);
    SDL_RenderPresent(renderer);
}

#ifdef EMSCRIPTEN
EMSCRIPTEN_MAINLOOP_END;
#endif

// Cleanup
ImGui_ImplSDLRenderer3_Shutdown();
ImGui_ImplSDL3_Shutdown();
ImGui::DestroyContext();

SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();

return 0;

}
This is the test code. It’s very simple, just use FreeType + ImGuiFreeTypeBuilderFlags_Bitmap to reproduce the issue. The font sinking in showDemoWindow is also caused by Descent and Ascent. The Noto Sans font is developed by Google, and you can download it from the internet.
If the line height is not changed, the screenshot is as follows.
Image
If the line height is changed, the spacing between multiple lines of text will be accurate. Our UI engineer has already done a pixel-level comparison. The screenshot is as follows.
Image

@pipiwoaini
Copy link
Author

This is the font file, you can use it or download it from Google Fonts
Medium.ttf.zip

@ocornut ocornut changed the title “Font display does not account for descent and ascent.” Font display does not account for descent and ascent when using ImGuiFreeTypeBuilderFlags_Bitmap Feb 21, 2025
@ocornut
Copy link
Owner

ocornut commented Feb 21, 2025

Thank you. I think it is specifically caused by ImGuiFreeTypeBuilderFlags_Bitmap mode but it'll be a good occasion to rework and standardize how those sizes are specified.

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

2 participants