From 484da7ed55c9f2c05567221962d9c4212dc611c4 Mon Sep 17 00:00:00 2001 From: Mikko Mononen Date: Tue, 28 Apr 2026 18:14:34 +0200 Subject: [PATCH] DrawList: added StrokePos flags: ImDrawFlags_StrokeInside, ImDrawFlags_StrokeOutside, ImDrawFlags_StrokeCenter, ImDrawFlags_StrokeLegacy. --- imgui.h | 17 ++++++++---- imgui_draw.cpp | 72 +++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 75 insertions(+), 14 deletions(-) diff --git a/imgui.h b/imgui.h index f35dd7bfa..c9d5baadd 100644 --- a/imgui.h +++ b/imgui.h @@ -3437,6 +3437,13 @@ enum ImDrawFlags_ ImDrawFlags_Closed = 1 << 9, // PathStroke(), AddPolyline(): specify that shape should be closed. //ImDrawFlags_Closed = 1, // Prior to 1.92.8 (May 2026), ImDrawFlags_Closed was guaranteed to be == 1<<0 == 1 for legacy compatibility reason. Hardcoded use of 1 or true should be replaced. + // Stroke position relative to the shape outline + ImDrawFlags_StrokeInside = 0 << 16, // Draw stroke inside of the shape outline (Default) + ImDrawFlags_StrokeCenter = 1 << 16, // Draw stroke at the center of the shape outline + ImDrawFlags_StrokeOutside = 2 << 16, // Draw stroke outside of the shape outline + ImDrawFlags_StrokeLegacy = 3 << 16, // Use legacy position + ImDrawFlags_StrokeMask_ = 0x03 << 16, + ImDrawFlags_InvalidMask_ = ~0x7FFFFFF0, // == 0x8000000F, }; @@ -3505,8 +3512,8 @@ struct ImDrawList // In future versions we will use textures to provide cheaper and higher-quality circles. // Use AddNgon() and AddNgonFilled() functions if you need to guarantee a specific number of sides. IMGUI_API void AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float thickness = 1.0f); - IMGUI_API void AddLineH(float min_x, float max_x, float y, ImU32 col, float thickness = 1.0f); - IMGUI_API void AddLineV(float x, float min_y, float max_y, ImU32 col, float thickness = 1.0f); + IMGUI_API void AddLineH(float min_x, float max_x, float y, ImU32 col, float thickness = 1.0f, ImDrawFlags flags = 0); + IMGUI_API void AddLineV(float x, float min_y, float max_y, ImU32 col, float thickness = 1.0f, ImDrawFlags flags = 0); IMGUI_API void AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, float thickness = 1.0f, ImDrawFlags flags = 0); // a: upper-left, b: lower-right (== upper-left + size) IMGUI_API void AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0); // a: upper-left, b: lower-right (== upper-left + size) IMGUI_API void AddRectFilledMultiColor(const ImVec2& p_min, const ImVec2& p_max, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left); @@ -3514,11 +3521,11 @@ struct ImDrawList IMGUI_API void AddQuadFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col); IMGUI_API void AddTriangle(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness = 1.0f); IMGUI_API void AddTriangleFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col); - IMGUI_API void AddCircle(const ImVec2& center, float radius, ImU32 col, int num_segments = 0, float thickness = 1.0f); + IMGUI_API void AddCircle(const ImVec2& center, float radius, ImU32 col, int num_segments = 0, float thickness = 1.0f, ImDrawFlags flags = 0); IMGUI_API void AddCircleFilled(const ImVec2& center, float radius, ImU32 col, int num_segments = 0); - IMGUI_API void AddNgon(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness = 1.0f); + IMGUI_API void AddNgon(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness = 1.0f, ImDrawFlags flags = 0); IMGUI_API void AddNgonFilled(const ImVec2& center, float radius, ImU32 col, int num_segments); - IMGUI_API void AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f); + IMGUI_API void AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f, ImDrawFlags flags = 0); IMGUI_API void AddEllipseFilled(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot = 0.0f, int num_segments = 0); IMGUI_API void AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL); IMGUI_API void AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 595245b41..e3bcd229a 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1507,7 +1507,7 @@ void ImDrawList::AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float th AddPolyline(points, 2, col, thickness); } -void ImDrawList::AddLineH(float min_x, float max_x, float y, ImU32 col, float thickness) +void ImDrawList::AddLineH(float min_x, float max_x, float y, ImU32 col, float thickness, ImDrawFlags flags) { if ((col & IM_COL32_A_MASK) == 0) return; @@ -1519,6 +1519,11 @@ void ImDrawList::AddLineH(float min_x, float max_x, float y, ImU32 col, float th return; } + ImDrawFlags stroke_pos = (flags & ImDrawFlags_StrokeMask_); + if (stroke_pos == ImDrawFlags_StrokeCenter) + y -= thickness * 0.5f; + else if (stroke_pos == ImDrawFlags_StrokeOutside) + y -= thickness; if (ImIsTruncated4(min_x, max_x, y, thickness)) { PrimReserve(6, 4); @@ -1530,7 +1535,7 @@ void ImDrawList::AddLineH(float min_x, float max_x, float y, ImU32 col, float th } } -void ImDrawList::AddLineV(float x, float min_y, float max_y, ImU32 col, float thickness) +void ImDrawList::AddLineV(float x, float min_y, float max_y, ImU32 col, float thickness, ImDrawFlags flags) { if ((col & IM_COL32_A_MASK) == 0) return; @@ -1542,6 +1547,11 @@ void ImDrawList::AddLineV(float x, float min_y, float max_y, ImU32 col, float th return; } + ImDrawFlags stroke_pos = (flags & ImDrawFlags_StrokeMask_); + if (stroke_pos == ImDrawFlags_StrokeCenter) + x += thickness * 0.5f; + else if (stroke_pos == ImDrawFlags_StrokeOutside) + x -= thickness; if (ImIsTruncated4(x, min_y, max_y, thickness)) { PrimReserve(6, 4); @@ -1582,7 +1592,20 @@ void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, fl } const ImVec2 offset(thickness * 0.5f, thickness * 0.5f); - PathRect(p_min + offset, p_max - offset, rounding - thickness * 0.5f, flags); + const ImDrawFlags stroke_pos = (flags & ImDrawFlags_StrokeMask_); + if (stroke_pos == ImDrawFlags_StrokeInside) + PathRect(p_min + offset, p_max - offset, rounding - thickness * 0.5f, flags); + else if (stroke_pos == ImDrawFlags_StrokeCenter) + PathRect(p_min, p_max, rounding, flags); + else if (stroke_pos == ImDrawFlags_StrokeOutside) + PathRect(p_min - offset, p_max + offset, rounding + thickness * 0.5f, flags); + else if (stroke_pos == ImDrawFlags_StrokeLegacy) + { + if (Flags & ImDrawListFlags_AntiAliasedLines) + PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.50f, 0.50f), rounding, flags); + else + PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.49f, 0.49f), rounding, flags); // Better looking lower-right corner and rounded non-AA shapes. + } PathStroke(col, thickness, ImDrawFlags_Closed); } @@ -1664,15 +1687,26 @@ void ImDrawList::AddTriangleFilled(const ImVec2& p1, const ImVec2& p2, const ImV PathFillConvex(col); } -void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness) +void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness, ImDrawFlags flags) { if ((col & IM_COL32_A_MASK) == 0 || radius < 0.5f) return; if (g_LEGACY_STROKES) + { radius -= 0.5f; + } else - radius -= thickness * 0.5f; + { + const ImDrawFlags stroke_pos = (flags & ImDrawFlags_StrokeMask_); + if (stroke_pos == ImDrawFlags_StrokeInside) + radius -= thickness * 0.5f; + else if (stroke_pos == ImDrawFlags_StrokeOutside) + radius += thickness * 0.5f; + else if (stroke_pos == ImDrawFlags_StrokeLegacy) + radius -= 0.5f; + } + if (num_segments <= 0) { // Use arc with automatic segment count @@ -1717,15 +1751,25 @@ void ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col, } // Guaranteed to honor 'num_segments' -void ImDrawList::AddNgon(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness) +void ImDrawList::AddNgon(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness, ImDrawFlags flags) { if ((col & IM_COL32_A_MASK) == 0 || num_segments <= 2) return; if (g_LEGACY_STROKES) + { radius -= 0.5f; + } else - radius -= thickness * 0.5f; + { + const ImDrawFlags stroke_pos = (flags & ImDrawFlags_StrokeMask_); + if (stroke_pos == ImDrawFlags_StrokeInside) + radius -= thickness * 0.5f; + else if (stroke_pos == ImDrawFlags_StrokeOutside) + radius += thickness * 0.5f; + else if (stroke_pos == ImDrawFlags_StrokeLegacy) + radius -= 0.5f; + } // Because we are filling a closed shape we remove 1 from the count of segments/points const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments; @@ -1746,16 +1790,26 @@ void ImDrawList::AddNgonFilled(const ImVec2& center, float radius, ImU32 col, in } // Ellipse -void ImDrawList::AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot, int num_segments, float thickness) +void ImDrawList::AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot, int num_segments, float thickness, ImDrawFlags flags) { if ((col & IM_COL32_A_MASK) == 0) return; ImVec2 r; if (g_LEGACY_STROKES) + { r = radius; + } else - r = ImVec2(radius.x - thickness * 0.5f, radius.y - thickness * 0.5f); + { + r = radius; + ImDrawFlags stroke_pos = (flags & ImDrawFlags_StrokeMask_); + if (stroke_pos == ImDrawFlags_StrokeInside) + r -= ImVec2(thickness * 0.5f, thickness * 0.5f); + else if (stroke_pos == ImDrawFlags_StrokeOutside) + r += ImVec2(thickness * 0.5f, thickness * 0.5f); + } + if (num_segments <= 0) num_segments = _CalcCircleAutoSegmentCount(ImMax(r.x, r.y)); // A bit pessimistic, maybe there's a better computation to do here.