From 5108603789af559bde144d0848da8297bbf39803 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 3 Aug 2026 19:11:05 +0200 Subject: [PATCH] DrawList: apply current pixel density to DrawList's fringe scale. Added ImDrawList::_SetPixelDensity(). Reword FontRasterizerDensity as CurrentPixelDensity. Amend 94a4322. # Conflicts: # imgui.cpp # imgui.h # imgui_draw.cpp # imgui_internal.h --- docs/CHANGELOG.txt | 6 +++++- imgui.cpp | 17 +++++++++-------- imgui.h | 4 +++- imgui_draw.cpp | 14 ++++++++++---- imgui_internal.h | 7 +++---- 5 files changed, 30 insertions(+), 18 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 69aa3477c..93dc4c9f7 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -51,7 +51,11 @@ Other Changes: - Fonts: - Reworked `AddFontDefault()` to use `io.DisplayFrameBufferScale` as part of the heuristic to select `AddFontDefaultVector()` by default, effectively using ProggyForever instead of - ProggyClean on most Mac setups by default. + ProggyClean on most Apple/Retina setups by default. +- ImDrawList: + - Per-viewport FramebufferScale for Apple/Retina screen is applied to draw lists: + - AA Fringe is scaled accordingly. + - Circle and Curves tessellation error are scaled accordingly. ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index b1791da0e..2eee9d87f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4240,7 +4240,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) Font = NULL; FontBaked = NULL; FontSize = FontSizeBase = FontBakedScale = CurrentDpiScale = 0.0f; - FontRasterizerDensity = 1.0f; + CurrentPixelDensity = 1.0f; IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)(); if (shared_font_atlas == NULL) IO.Fonts->OwnerContext = this; @@ -4711,7 +4711,7 @@ static void SetCurrentWindow(ImGuiWindow* window) if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) { ImGuiViewport* viewport = window->Viewport; - g.FontRasterizerDensity = (viewport->FramebufferScale.x != 0.0f) ? viewport->FramebufferScale.x : g.IO.DisplayFramebufferScale.x; // == SetFontRasterizerDensity() + g.CurrentPixelDensity = (viewport->FramebufferScale.x != 0.0f) ? viewport->FramebufferScale.x : g.IO.DisplayFramebufferScale.x; // == SetPixelDensity() } const bool backup_skip_items = window->SkipItems; window->SkipItems = false; @@ -5308,6 +5308,7 @@ static ImDrawList* GetViewportBgFgDrawList(ImGuiViewportP* viewport, size_t draw if (viewport->BgFgDrawListsLastTimeActive[drawlist_no] != (float)g.Time) { draw_list->_ResetForNewFrame(); + draw_list->_SetPixelDensity(viewport->FramebufferScale.x); draw_list->PushTexture(g.IO.Fonts->TexRef); draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false); viewport->BgFgDrawListsLastTimeActive[drawlist_no] = (float)g.Time; @@ -5591,7 +5592,6 @@ static void SetupDrawListSharedData() g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill; if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset; - g.DrawListSharedData.InitialFringeScale = 1.0f; // FIXME-DPI: Change this for some DPI scaling experiments. } void ImGui::NewFrame() @@ -8081,6 +8081,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Setup draw list and outer clipping rectangle IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0); + window->DrawList->_SetPixelDensity(window->Viewport->FramebufferScale.x); window->DrawList->PushTexture(g.Font->OwnerAtlas->TexRef); PushClipRect(host_rect.Min, host_rect.Max, false); @@ -9049,7 +9050,7 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) // - UnregisterFontAtlas() [Internal] // - SetCurrentFont() [Internal] // - UpdateCurrentFontSize() [Internal] -// - SetFontRasterizerDensity() [Internal] +// - SetPixelDensity() [Internal] // - PushFont() // - PopFont() //----------------------------------------------------------------------------- @@ -9242,7 +9243,7 @@ void ImGui::UpdateCurrentFontSize(float restore_font_size_after_scaling) final_size = GetRoundedFontSize(final_size); final_size = ImClamp(final_size, 1.0f, IMGUI_FONT_SIZE_MAX); if (g.Font != NULL && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures)) - g.Font->CurrentRasterizerDensity = g.FontRasterizerDensity; + g.Font->CurrentRasterizerDensity = g.CurrentPixelDensity; g.FontSize = final_size; g.DrawListSharedData.FontSize = g.FontSize; @@ -9266,13 +9267,13 @@ void ImGui::UpdateCurrentFontSize(float restore_font_size_after_scaling) // Exposed in case user may want to override setting density. // IMPORTANT: Begin()/End() is overriding density. Be considerate of this you change it. -void ImGui::SetFontRasterizerDensity(float rasterizer_density) +void ImGui::SetPixelDensity(float pixel_density) { ImGuiContext& g = *GImGui; IM_ASSERT(g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures); - if (g.FontRasterizerDensity == rasterizer_density) + if (g.CurrentPixelDensity == pixel_density) return; - g.FontRasterizerDensity = rasterizer_density; + g.CurrentPixelDensity = pixel_density; UpdateCurrentFontSize(0.0f); } diff --git a/imgui.h b/imgui.h index 4bf5fe38b..fea82515d 100644 --- a/imgui.h +++ b/imgui.h @@ -3346,7 +3346,8 @@ struct ImDrawList ImVector _ClipRectStack; // [Internal] ImVector _TextureStack; // [Internal] ImVector _CallbacksDataBuf; // [Internal] - float _FringeScale; // [Internal] anti-alias fringe is scaled by this value, this helps to keep things sharp while zooming at vertex buffer content + float _FringeScale; // [Internal] anti-alias fringe is scaled by this value, this helps to keep things sharp while zooming at vertex buffer content. + float _InvFringeScale; // [internal] 1.0 / _FringeScale // FIXME: Consider renaming to _PixelDensity. const char* _OwnerName; // Pointer to owner window's name for debugging // If you want to create ImDrawList instances, pass them ImGui::GetDrawListSharedData(). @@ -3480,6 +3481,7 @@ struct ImDrawList // [Internal helpers] IMGUI_API void _SetDrawListSharedData(ImDrawListSharedData* data); IMGUI_API void _ResetForNewFrame(); + IMGUI_API void _SetPixelDensity(float pixel_density); IMGUI_API void _ClearFreeMemory(); IMGUI_API void _PopUnusedDrawCmd(); IMGUI_API void _TryMergeDrawCmds(); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index a84fc3ef1..a9759c072 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -396,7 +396,6 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) ImDrawListSharedData::ImDrawListSharedData() { memset((void*)this, 0, sizeof(*this)); - InitialFringeScale = 1.0f; for (int i = 0; i < IM_COUNTOF(ArcFastVtx); i++) { const float a = ((float)i * 2 * IM_PI) / (float)IM_COUNTOF(ArcFastVtx); @@ -473,7 +472,7 @@ void ImDrawList::_ResetForNewFrame() _Path.resize(0); _Splitter.Clear(); CmdBuffer.push_back(ImDrawCmd()); - _FringeScale = _Data->InitialFringeScale; + // Caller needs to bet _SetPixelDensity() as well. } void ImDrawList::_ClearFreeMemory() @@ -655,6 +654,7 @@ void ImDrawList::_OnChangedVtxOffset() int ImDrawList::_CalcCircleAutoSegmentCount(float radius) const { // Automatic segment count + radius *= _InvFringeScale; const int radius_idx = (int)(radius + 0.999f); // ceil to never reduce accuracy if (radius_idx >= 0 && radius_idx < IM_COUNTOF(_Data->CircleSegmentCounts)) return _Data->CircleSegmentCounts[radius_idx]; // Use cached value @@ -720,6 +720,12 @@ void ImDrawList::_SetTexture(ImTextureRef tex_ref) _OnChangedTexture(); } +void ImDrawList::_SetPixelDensity(float pixel_density) +{ + _FringeScale = 1.0f / pixel_density; + _InvFringeScale = pixel_density; +} + // Reserve space for a number of vertices and indices. // You must finish filling your reserved data before calling PrimReserve() again, as it may reallocate or // submit the intermediate results. PrimUnreserve() can be used to release unused allocations. @@ -1419,7 +1425,7 @@ void ImDrawList::PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, cons if (num_segments == 0) { IM_ASSERT(_Data->CurveTessellationMaxError > 0.0f); - float max_error_sqr = _Data->CurveTessellationMaxError * _Data->CurveTessellationMaxError; + float max_error_sqr = (_Data->CurveTessellationMaxError * _FringeScale) * (_Data->CurveTessellationMaxError * _FringeScale); PathBezierCubicCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, max_error_sqr, 0); // Auto-tessellated } else @@ -1436,7 +1442,7 @@ void ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, if (num_segments == 0) { IM_ASSERT(_Data->CurveTessellationMaxError > 0.0f); - float max_error_sqr = _Data->CurveTessellationMaxError * _Data->CurveTessellationMaxError; + float max_error_sqr = (_Data->CurveTessellationMaxError * _FringeScale) * (_Data->CurveTessellationMaxError * _FringeScale); PathBezierQuadraticCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, max_error_sqr, 0);// Auto-tessellated } else diff --git a/imgui_internal.h b/imgui_internal.h index a633c8cee..59f005508 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -897,7 +897,6 @@ struct IMGUI_API ImDrawListSharedData float FontScale; // Current font scale (== FontSize / Font->FontSize) float CurveTessellationMaxError; // Tessellation tolerance when using PathBezierCurveTo() float CircleTessellationMaxError; // Number of circle segments to use per pixel of radius for AddCircle() etc - float InitialFringeScale; // Initial scale to apply to AA fringe ImDrawListFlags InitialFlags; // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards) ImVec4 ClipRectFullscreen; // Value for PushClipRectFullscreen() ImVector TempBuffer; // Temporary write buffer @@ -2267,7 +2266,7 @@ struct ImGuiContext float FontSize; // Currently bound font size == line height (== FontSizeBase + externals scales applied in the UpdateCurrentFontSize() function). float FontSizeBase; // Font size before scaling == style.FontSizeBase == value passed to PushFont() when specified. float FontBakedScale; // == FontBaked->Size / FontSize. Scale factor over baked size. Rarely used nowadays, very often == 1.0f. - float FontRasterizerDensity; // Current font density. Used by all calls to GetFontBaked(). + float CurrentPixelDensity; // Current font density. Used by all calls to GetFontBaked(). float CurrentDpiScale; // Current window/viewport DpiScale == CurrentViewport->DpiScale ImDrawListSharedData DrawListSharedData; ImGuiID WithinEndChildID; // Set within EndChild() @@ -3390,8 +3389,8 @@ namespace ImGui IMGUI_API void UnregisterFontAtlas(ImFontAtlas* atlas); IMGUI_API void SetCurrentFont(ImFont* font, float font_size_before_scaling, float font_size_after_scaling); IMGUI_API void UpdateCurrentFontSize(float restore_font_size_after_scaling); - IMGUI_API void SetFontRasterizerDensity(float rasterizer_density); - inline float GetFontRasterizerDensity() { return GImGui->FontRasterizerDensity; } + IMGUI_API void SetPixelDensity(float pixel_density); // was SetFontRasterizerDensity() + inline float GetPixelDensity() { return GImGui->CurrentPixelDensity; } inline float GetRoundedFontSize(float size) { return IM_ROUND(size); } IMGUI_API ImFont* GetDefaultFont(); IMGUI_API void PushPasswordFont();