imgui
imgui copied to clipboard
Font display does not account for descent and ascent when using ImGuiFreeTypeBuilderFlags_Bitmap
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);
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?
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;
}
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!
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.
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.
This is the font file, you can use it or download it from Google Fonts Medium.ttf.zip
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.
Linking to #8857 which is related.