From 927df56807dcc9c28859b7008adf6076fd17d13e Mon Sep 17 00:00:00 2001 From: Mikko Mononen Date: Tue, 28 Apr 2026 09:32:09 +0300 Subject: [PATCH] DrawList: Textured Round Corners: Added flags to disable textured rounded corners (1962) - added ImFontAtlasFlags_NoBakedRoundCorners which controls if rounded corners should be baked to texture - added ImDrawListFlags_RoundCornersUseTex which controls if we should try to use textured roudned corners - fixed how _FringeScaleIsInteger is calculated (we want to detect integer pixel scale) - fixed how _FringeScale is used with textured rounded corners --- imgui.cpp | 2 ++ imgui.h | 4 +++- imgui_draw.cpp | 46 +++++++++++++++++++++++++++++----------------- 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 6eee724e5..55b315c37 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5779,6 +5779,8 @@ static void SetupDrawListSharedData() g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines; if (g.Style.AntiAliasedLinesUseTex && !(g.IO.Fonts->Flags & ImFontAtlasFlags_NoBakedLines)) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex; + if (!(g.IO.Fonts->Flags & ImFontAtlasFlags_NoBakedRoundCorners)) + g.DrawListSharedData.InitialFlags |= ImDrawListFlags_RoundCornersUseTex; if (g.Style.AntiAliasedFill) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill; if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) diff --git a/imgui.h b/imgui.h index ebc363e3d..ee2050981 100644 --- a/imgui.h +++ b/imgui.h @@ -3464,6 +3464,7 @@ enum ImDrawListFlags_ ImDrawListFlags_AntiAliasedFill = 1 << 2, // Enable anti-aliased edge around filled shapes (rounded rectangles, circles). ImDrawListFlags_AllowVtxOffset = 1 << 3, // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled. ImDrawListFlags_TextNoPixelSnap = 1 << 4, // Disable automatically snapping AddText() calls to pixel boundaries. + ImDrawListFlags_RoundCornersUseTex = 1 << 5, // Enable using textures instead of strokes to draw rounded corners/circles where possible. }; // Draw command list @@ -3496,7 +3497,7 @@ struct ImDrawList 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 _InvFringeScale; // [internal] 1.0 / _FringeScale // FIXME: Consider renaming to _PixelDensity. - bool _FringeScaleIsInteger; // [Internal] true if _FringeScale is a whole number, used to select fast path for rendering + bool _FringeScaleIsInteger; // [Internal] true if 1/_FringeScale is a whole number, used to select fast path for rendering const char* _OwnerName; // Pointer to owner window's name for debugging // If you want to create ImDrawList instances, pass them ImGui::GetDrawListSharedData(). @@ -3860,6 +3861,7 @@ enum ImFontAtlasFlags_ ImFontAtlasFlags_NoPowerOfTwoHeight = 1 << 0, // Don't round the height to next power of two ImFontAtlasFlags_NoMouseCursors = 1 << 1, // Don't build software mouse cursors into the atlas (save a little texture memory) ImFontAtlasFlags_NoBakedLines = 1 << 2, // Don't build thick line textures into the atlas (save a little texture memory, allow support for point/nearest filtering). The AntiAliasedLinesUseTex features uses them, otherwise they will be rendered using polygons (more expensive for CPU/GPU). + ImFontAtlasFlags_NoBakedRoundCorners= 1 << 3, // Don't build round corners into the atlas. }; // Load and rasterize multiple TTF/OTF fonts into a same texture. The font atlas will build a single texture holding: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 062548a8e..276f0d7d6 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -731,7 +731,7 @@ void ImDrawList::_SetPixelDensity(float pixel_density) IM_ASSERT(pixel_density > 0.0f); _FringeScale = 1.0f / pixel_density; _InvFringeScale = pixel_density; - _FringeScaleIsInteger = ImIsTruncated(_FringeScale); + _FringeScaleIsInteger = ImIsTruncated(_InvFringeScale); } // Reserve space for a number of vertices and indices. @@ -1525,7 +1525,8 @@ void ImDrawList::AddLineH(float min_x, float max_x, float y, ImU32 col, float th y -= thickness * 0.5f; else if (stroke_pos == ImDrawFlags_StrokeOutside) y -= thickness; - if (ImIsTruncated4(min_x, max_x, y, thickness)) + + if (_FringeScaleIsInteger && ImIsTruncated4(min_x, max_x, y, thickness)) { PrimReserve(6, 4); PrimRect(ImVec2(min_x, y), ImVec2(max_x, y + thickness), col); @@ -1553,10 +1554,11 @@ void ImDrawList::AddLineV(float x, float min_y, float max_y, ImU32 col, float th x += thickness * 0.5f; else if (stroke_pos == ImDrawFlags_StrokeOutside) x -= thickness; - if (ImIsTruncated4(x, min_y, max_y, thickness)) + + if (_FringeScaleIsInteger && ImIsTruncated4(x, min_y, max_y, thickness)) { PrimReserve(6, 4); - PrimRect(ImTrunc(ImVec2(x, min_y)), ImTrunc(ImVec2(x + thickness, max_y)), col); + PrimRect(ImVec2(x, min_y), ImVec2(x + thickness, max_y), col); } else { @@ -1711,8 +1713,9 @@ void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, fl return; } + const bool is_truncated = _FringeScaleIsInteger && ImIsTruncated4(p_min.x, p_min.y, p_max.x, p_min.y) && ImIsTruncated4(rounding, thickness, 0.0f, 0.0f); ImDrawFlags stroke_pos = (flags & ImDrawFlags_StrokeMask_); - if ((stroke_pos == ImDrawFlags_StrokeInside || stroke_pos == ImDrawFlags_StrokeOutside) && ImIsTruncated4(p_min.x, p_min.y, p_max.x, p_min.y) && ImIsTruncated(rounding) && ImIsTruncated(thickness)) + if ((stroke_pos == ImDrawFlags_StrokeInside || stroke_pos == ImDrawFlags_StrokeOutside) && is_truncated) { if ((flags & ImDrawFlags_RoundCornersMask_) == 0) flags |= ImDrawFlags_RoundCornersAll; @@ -1720,8 +1723,8 @@ void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, fl rounding = ImMin(rounding, ImFabs(p_max.x - p_min.x) * (((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f) - 1.0f); rounding = ImMin(rounding, ImFabs(p_max.y - p_min.y) * (((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f) - 1.0f); - int s_rounding = (int)rounding; - int s_thickness = (int)thickness; + int s_rounding = (int)(rounding / _FringeScale); + int s_thickness = (int)(thickness / _FringeScale); ImVec2 s_min = p_min; ImVec2 s_max = p_max; @@ -1734,7 +1737,7 @@ void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, fl if (s_rounding <= 0 || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone) { - const float t = (float)s_thickness; + const float t = (float)s_thickness * _FringeScale; PrimReserve(6*4, 4*4); PrimRect(ImVec2(p_min.x, p_min.y), ImVec2(p_max.x, p_min.y + t), col); PrimRect(ImVec2(p_min.x, p_min.y + t), ImVec2(p_min.x + t, p_max.y - t), col); @@ -1742,13 +1745,14 @@ void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, fl PrimRect(ImVec2(p_min.x, p_max.y - t), ImVec2(p_max.x, p_max.y), col); return; } - if (s_thickness <= IM_DRAWLIST_TEX_CORNERS_THICKNESS_MAX && s_rounding <= IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX) + if ((Flags & ImDrawListFlags_RoundCornersUseTex) && s_thickness <= IM_DRAWLIST_TEX_CORNERS_THICKNESS_MAX && s_rounding <= IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX) { - const int size = ImMax(s_rounding, s_thickness); + IM_ASSERT_PARANOID(!(_Data->Font->OwnerAtlas->Flags & ImFontAtlasFlags_NoBakedRoundCorners)); + const int size = ImMax(s_rounding, s_thickness); // This is matching the baking calculations. const int idx = (s_thickness - 1) * IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX + s_rounding - 1; - IM_ASSERT(idx >= 0 && idx < IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX * IM_DRAWLIST_TEX_CORNERS_THICKNESS_MAX); + IM_ASSERT_PARANOID(idx >= 0 && idx < IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX * IM_DRAWLIST_TEX_CORNERS_THICKNESS_MAX); const ImVec4 tex_uvs = _Data->TexUvCornerStrokes[idx]; - _AddRectBaked(s_min, s_max, col, (float)size, (float)thickness, tex_uvs, flags); + _AddRectBaked(s_min, s_max, col, (float)size * _FringeScale, (float)thickness, tex_uvs, flags); return; } } @@ -1832,16 +1836,21 @@ void ImDrawList::AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 c rounding = ImMin(rounding, ImFabs(p_max.x - p_min.x) * (((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f) - 1.0f); rounding = ImMin(rounding, ImFabs(p_max.y - p_min.y) * (((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f) - 1.0f); - const int s_rounding = (int)rounding; + + const int s_rounding = (int)(rounding / _FringeScale); if (s_rounding <= 0 || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone) { PrimReserve(6, 4); PrimRect(p_min, p_max, col); } - else if (s_rounding <= IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX) + else if ((Flags & ImDrawListFlags_RoundCornersUseTex) && s_rounding <= IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX) { - ImVec4 tex_uvs = _Data->TexUvCornerFills[s_rounding - 1]; - _AddRectFilledBaked(p_min, p_max, col, ImMax(2.0f, (float)s_rounding), tex_uvs, flags); + IM_ASSERT_PARANOID(!(_Data->Font->OwnerAtlas->Flags & ImFontAtlasFlags_NoBakedRoundCorners)); + const int size = ImMax(2, s_rounding); // This is matching the baking calculations. + const int idx = s_rounding - 1; + IM_ASSERT_PARANOID(idx >= 0 && idx < IM_DRAWLIST_TEX_CORNERS_ROUNDING_MAX); + ImVec4 tex_uvs = _Data->TexUvCornerFills[idx]; + _AddRectFilledBaked(p_min, p_max, col, (float)size * _FringeScale, tex_uvs, flags); } else { @@ -4008,11 +4017,14 @@ static ImU8 SampleCornerStroke(float x, float y, float cx, float cy, float outer const float aa_width = 1.0f; const float outer = 1.0f - (d - outer_r + aa_width * 0.5f) / aa_width; const float inner = (d - inner_r + aa_width * 0.5f) / aa_width; - return (ImU8)(ImClamp(ImMin(inner, outer), 0.0f, 1.0f) * 255.f); + return (ImU8)(ImClamp(ImMin(inner, outer), 0.0f, 1.0f) * 255.0f); } static void ImFontAtlasBuildUpdateTexDataCorners(ImFontAtlas* atlas) { + if (atlas->Flags & ImFontAtlasFlags_NoBakedRoundCorners) + return; + // Pack and store identifier so we can refresh UV coordinates on texture resize. ImTextureData* tex = atlas->TexData; ImFontAtlasBuilder* builder = atlas->Builder;