DrawList: added PushDrawFlag(), PopDrawFlag(), ImSmallStack_<>

This commit is contained in:
ocornut
2026-07-28 15:48:40 +02:00
parent 59014d15e7
commit fb9fd9a250
5 changed files with 52 additions and 18 deletions

View File

@@ -137,6 +137,7 @@ HOW TO UPDATE?
Thickness=2.0f blurry sharp sharp sharp sharp
Thickness=3.0f sharp sharp sharp blurry sharp
------------------------------------------------------------------------
- Added ImDrawList::PushDrawFlag()/PopDrawFlag() to alter certain flags for a scope.
- (Breaking) AddRect, AddCircle, AddNgon, AddEllipse: defaulting to "inside" stroke.
- All closed shapes with thickness=1.0f will appear identical.
- The difference for thickness>1.0f shapes may be minimal since very large strokes

View File

@@ -23582,10 +23582,9 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, con
Selectable(buf, false);
if (fg_draw_list && IsItemHovered())
{
ImDrawFlags backup_flags = fg_draw_list->Flags;
fg_draw_list->Flags &= ~ImDrawFlags_AALines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
fg_draw_list->PushDrawFlag(ImDrawFlags_AALines, false); // Disable AA on triangle outlines is more readable for very large and thin triangles.
fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), 1.0f, ImDrawFlags_Closed);
fg_draw_list->Flags = backup_flags;
fg_draw_list->PopDrawFlag();
}
}
TreePop();
@@ -23601,8 +23600,7 @@ void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, co
// Draw wire-frame version of all triangles
ImRect clip_rect = draw_cmd->ClipRect;
ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
ImDrawFlags backup_flags = out_draw_list->Flags;
out_draw_list->Flags &= ~ImDrawFlags_AALines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
out_draw_list->PushDrawFlag(ImDrawFlags_AALines, false); // Disable AA on triangle outlines is more readable for very large and thin triangles.
for (unsigned int idx_n = draw_cmd->IdxOffset, idx_end = draw_cmd->IdxOffset + draw_cmd->ElemCount; idx_n < idx_end; )
{
ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; // We don't hold on those pointers past iterations as ->AddPolyline() may invalidate them if out_draw_list==draw_list
@@ -23620,7 +23618,7 @@ void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, co
out_draw_list->AddRect(ImTrunc(clip_rect.Min), ImTrunc(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU
out_draw_list->AddRect(ImTrunc(vtxs_rect.Min), ImTrunc(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles
}
out_draw_list->Flags = backup_flags;
out_draw_list->PopDrawFlag();
}
// [DEBUG] Compute mask of inputs with the same codepoint.

24
imgui.h
View File

@@ -3001,6 +3001,19 @@ struct ImGuiStorage
#endif
};
// [Internal] Used for small stacks. Do not use.
template<typename T, int Capacity, typename SZ_T = ImU16>
struct ImSmallStack_
{
T LocalData[Capacity]; // FIXME: should evolve into using heap, e.g union {} with T* HeapData + add SZ_T Capacity.
SZ_T Size = 0;
inline void clear() { Size = 0; }
inline T& back() { IM_ASSERT(Size > 0); return LocalData[Size - 1]; }
inline void push_back(const T& v) { IM_ASSERT(Size < Capacity); memcpy(&LocalData[Size], &v, sizeof(v)); Size++; }
inline void pop_back() { IM_ASSERT(Size > 0); Size--; }
};
// Flags for ImGuiListClipper (currently not fully exposed in function calls: a future refactor will likely add this to ImGuiListClipper::Begin function equivalent)
enum ImGuiListClipperFlags_
{
@@ -3416,9 +3429,8 @@ enum ImDrawFlags_
// About usage of flags:
// - "Prim" column: 'OK' = flag can be used to configure an individual AddXXX() call.
// - "Scope" column: 'OK' = initialized by ImGui based on Style options (e.g. whether anti-aliased is enabled).
// - "Scope" column: 'OK' = initialized by ImGui based on Style options (e.g. whether anti-aliased is enabled) + may be modified using ImDrawList::PushDrawFlag().
// OK(0)/OK(1) indicates whether this flag is set in the default ImGui Style settings.
// (flag will be possible to alter in a scope using PushDrawFlags()).
// - Rounding default to ImDrawFlags_RoundCornersAll when 'rounding > 0'.
// - So you only need to use the _RoundCorners flags if you want a special configuration (e.g. a rectangle with one rounded corner).
@@ -3468,7 +3480,7 @@ enum ImDrawFlags_
ImDrawFlags_UseVtxOffset = 1 << 23, // -- OK(1) // Can emit 'VtxOffset > 0' to allow large meshes with 16-bit ImDrawIdx. Used by default when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled by the backend.
// [Internal]
ImDrawFlags_AllowedInScope_ = ImDrawFlags_AAFill | ImDrawFlags_AALines | ImDrawFlags_AALineEnds | ImDrawFlags_StrokeLegacy | ImDrawFlags_TextNoPixelSnap | ImDrawFlags_UseTexForRoundCorners | ImDrawFlags_UseTexForStrokeLegacy | ImDrawFlags_UseVtxOffset, // [Internal] Values allowed in scope e.g. incoming PushDrawFlags() stack.
ImDrawFlags_AllowedInScope_ = ImDrawFlags_AAFill | ImDrawFlags_AALines | ImDrawFlags_AALineEnds | ImDrawFlags_StrokeLegacy | ImDrawFlags_TextNoPixelSnap | ImDrawFlags_UseTexForRoundCorners | ImDrawFlags_UseTexForStrokeLegacy | ImDrawFlags_UseVtxOffset, // [Internal] Values allowed in PushDrawFlag() scope.
ImDrawFlags_RoundCornersMask_ = ImDrawFlags_RoundCornersAll | ImDrawFlags_RoundCornersNone, // [Internal]
ImDrawFlags_StrokeMask_ = 0x07 << 17, // [Internal]
ImDrawFlags_InvalidMask_ = ~0x7FFFFFF0, // [Internal] == 0x8000000F. Reserved to detect misuses.
@@ -3489,7 +3501,7 @@ struct ImDrawList
ImVector<ImDrawCmd> CmdBuffer; // Draw commands. Typically 1 command = 1 GPU draw call, unless the command is a callback.
ImVector<ImDrawIdx> IdxBuffer; // Index buffer. Each command consume ImDrawCmd::ElemCount of those
ImVector<ImDrawVert> VtxBuffer; // Vertex buffer.
ImDrawFlags Flags; // Current flags for drawing primitives. You may poke into these to adjust anti-aliasing settings per-primitive. Will be exposed as PushDrawFlags().
ImDrawFlags Flags; // Current flags for drawing primitives. You may poke into these to adjust anti-aliasing settings per-primitive. Alter with PushDrawFlag().
// [Internal, used while building lists]
unsigned int _VtxCurrentIdx; // [Internal] generally == VtxBuffer.Size unless we are past 64K vertices, in which case this gets reset to 0.
@@ -3502,6 +3514,7 @@ struct ImDrawList
ImVector<ImVec4> _ClipRectStack; // [Internal]
ImVector<ImTextureRef> _TextureStack; // [Internal]
ImVector<ImU8> _CallbacksDataBuf; // [Internal]
ImSmallStack_<ImDrawFlags,3> _DrawFlagsStack;// [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 1/_FringeScale is a whole number, used to select fast path for rendering
@@ -3517,6 +3530,8 @@ struct ImDrawList
IMGUI_API void PopClipRect();
IMGUI_API void PushTexture(ImTextureRef tex_ref);
IMGUI_API void PopTexture();
IMGUI_API void PushDrawFlag(ImDrawFlags flags, bool enabled); // [BETA] Please notify me if you are using this.
IMGUI_API void PopDrawFlag();
inline ImVec2 GetClipRectMin() const { const ImVec4& cr = _ClipRectStack.back(); return ImVec2(cr.x, cr.y); }
inline ImVec2 GetClipRectMax() const { const ImVec4& cr = _ClipRectStack.back(); return ImVec2(cr.z, cr.w); }
@@ -3658,7 +3673,6 @@ struct ImDrawList
IMGUI_API void _AddRectTinyRounding(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, float thickness, ImDrawFlags flags);
IMGUI_API void _SelectFringeTexture(float screen_thickness, ImVec4* out_tex_uvs, float* out_fringe);
IMGUI_API float _CalculateCenterBiasedOffset(float thickness);
};
// All draw data to render a Dear ImGui frame

View File

@@ -10488,8 +10488,8 @@ static void ShowExampleAppCustomRendering(bool* p_open)
ImGui::PopID();
ImGui::Spacing();
ImDrawFlags backup_draw_list_flags = draw_list->Flags;
draw_list->Flags = (draw_list->Flags & ~ImDrawFlags_AllowedInScope_) | scope_flags; // Equivalent to an hypothetical PushDrawFlags() API
draw_list->PushDrawFlag(ImDrawFlags_AllowedInScope_, false); // Reset all existing (very unusual: done for this demo)
draw_list->PushDrawFlag(scope_flags, true);
const ImDrawFlags flags = prim_stroke_flags | prim_other_flags;
const ImDrawFlags fill_flags = flags & (ImDrawFlags_AAFill | ImDrawFlags_UseTexForRoundCorners);
@@ -10622,11 +10622,12 @@ static void ShowExampleAppCustomRendering(bool* p_open)
}
ImGui::Dummy(ImVec2((sz + spacing) * 15.0f, (sz + spacing) * 4.0f));
draw_list->PopDrawFlag();
draw_list->PopDrawFlag();
ImGui::PopItemFlag();
ImGui::PopItemWidth();
ImGui::EndTabItem();
draw_list->Flags = backup_draw_list_flags;
}
if (ImGui::BeginTabItem("Canvas"))

View File

@@ -476,6 +476,7 @@ void ImDrawList::_ResetForNewFrame()
_ClipRectStack.resize(0);
_TextureStack.resize(0);
_CallbacksDataBuf.resize(0);
_DrawFlagsStack.clear();
_Path.resize(0);
_Splitter.Clear();
CmdBuffer.push_back(ImDrawCmd());
@@ -494,6 +495,7 @@ void ImDrawList::_ClearFreeMemory()
_ClipRectStack.clear();
_TextureStack.clear();
_CallbacksDataBuf.clear();
_DrawFlagsStack.clear();
_Path.clear();
_Splitter.ClearFreeMemory();
}
@@ -717,6 +719,24 @@ void ImDrawList::PopTexture()
_OnChangedTexture();
}
// IMPORTANT: currently limited to 3 deep
void ImDrawList::PushDrawFlag(ImDrawFlags flags, bool enabled)
{
IM_ASSERT((flags & ~ImDrawFlags_AllowedInScope_) == 0);
_DrawFlagsStack.push_back(Flags);
if (enabled)
Flags |= flags;
else
Flags &= ~flags;
}
void ImDrawList::PopDrawFlag()
{
IM_ASSERT_USER_ERROR_RET(_DrawFlagsStack.Size > 0, "Calling PopDrawFlags() too many times!");
Flags = _DrawFlagsStack.back();
_DrawFlagsStack.pop_back();
}
// This is used by ImGui::PushFont()/PopFont(). It works because we never use _TextureIdStack[] elsewhere than in PushTexture()/PopTexture().
void ImDrawList::_SetTexture(ImTextureRef tex_ref)
{
@@ -3144,7 +3164,7 @@ void ImDrawList::AddNgon(const ImVec2& center, float radius, ImU32 col, int num_
radius = ImMax(0.01f, radius);
// Check overestimate first before testing details.
// Check overestimate first before testing details (+1.0f is intended, not FringeScale)
if (radius < (thickness * 2.0f + 1.0f))
{
const float unit_apothem = ImCos(IM_PI / (float)num_segments);
@@ -5349,7 +5369,7 @@ static void ImFontAtlasBuildUpdateTexDataCorners(ImFontAtlas* atlas)
const int pitch = tex->Width;
for (int ly = 0; ly < h; ly++)
{
for (int lx = 0; lx < h; lx++)
for (int lx = 0; lx < w; lx++)
write_ptr[lx] = SampleCorner((float)lx + 0.5f, (float)ly + 0.5f, line_normals, line_distances, num_lines) & DEBUG_TEX_CORNER_U8(lx, h);
write_ptr += pitch;
}
@@ -5360,7 +5380,7 @@ static void ImFontAtlasBuildUpdateTexDataCorners(ImFontAtlas* atlas)
const int pitch = tex->Width;
for (int ly = 0; ly < h; ly++)
{
for (int lx = 0; lx < h; lx++)
for (int lx = 0; lx < w; lx++)
write_ptr[lx] = IM_COL32(255, 255, 255, SampleCorner((float)lx + 0.5f, (float)ly + 0.5f, line_normals, line_distances, num_lines)) & DEBUG_TEX_CORNER_U32(lx, h);
write_ptr += pitch;
}