summaryrefslogtreecommitdiff
path: root/3rdparty/imgui/imgui_draw.cpp
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/imgui/imgui_draw.cpp')
-rw-r--r--3rdparty/imgui/imgui_draw.cpp358
1 files changed, 196 insertions, 162 deletions
diff --git a/3rdparty/imgui/imgui_draw.cpp b/3rdparty/imgui/imgui_draw.cpp
index 647addc..2393870 100644
--- a/3rdparty/imgui/imgui_draw.cpp
+++ b/3rdparty/imgui/imgui_draw.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.83 WIP
+// dear imgui, v1.88 WIP
// (drawing and font code)
/*
@@ -54,9 +54,12 @@ Index of this file:
// Visual Studio warnings
#ifdef _MSC_VER
-#pragma warning (disable: 4127) // condition expression is constant
-#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
-#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
+#pragma warning (disable: 4127) // condition expression is constant
+#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
+#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
+#pragma warning (disable: 6255) // [Static Analyzer] _alloca indicates failure by raising a stack overflow exception. Consider using _malloca instead.
+#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
+#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer)
#endif
// Clang/GCC warnings with -Weverything
@@ -105,6 +108,9 @@ namespace IMGUI_STB_NAMESPACE
#ifdef _MSC_VER
#pragma warning (push)
#pragma warning (disable: 4456) // declaration of 'xx' hides previous local declaration
+#pragma warning (disable: 6011) // (stb_rectpack) Dereferencing NULL pointer 'cur->next'.
+#pragma warning (disable: 6385) // (stb_truetype) Reading invalid data from 'buffer': the readable size is '_Old_3`kernel_width' bytes, but '3' bytes may be read.
+#pragma warning (disable: 28182) // (stb_rectpack) Dereferencing NULL pointer. 'cur' contains the same NULL value as 'cur->next' did.
#endif
#if defined(__clang__)
@@ -145,7 +151,7 @@ namespace IMGUI_STB_NAMESPACE
#define STBTT_sqrt(x) ImSqrt(x)
#define STBTT_pow(x,y) ImPow(x,y)
#define STBTT_fabs(x) ImFabs(x)
-#define STBTT_ifloor(x) ((int)ImFloorStd(x))
+#define STBTT_ifloor(x) ((int)ImFloorSigned(x))
#define STBTT_iceil(x) ((int)ImCeil(x))
#define STBTT_STATIC
#define STB_TRUETYPE_IMPLEMENTATION
@@ -396,10 +402,9 @@ void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error)
void ImDrawList::_ResetForNewFrame()
{
// Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory.
- // (those should be IM_STATIC_ASSERT() in theory but with our pre C++11 setup the whole check doesn't compile with GCC)
- IM_ASSERT(IM_OFFSETOF(ImDrawCmd, ClipRect) == 0);
- IM_ASSERT(IM_OFFSETOF(ImDrawCmd, TextureId) == sizeof(ImVec4));
- IM_ASSERT(IM_OFFSETOF(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID));
+ IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, ClipRect) == 0);
+ IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, TextureId) == sizeof(ImVec4));
+ IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID));
CmdBuffer.resize(0);
IdxBuffer.resize(0);
@@ -467,6 +472,7 @@ void ImDrawList::_PopUnusedDrawCmd()
void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data)
{
+ IM_ASSERT_PARANOID(CmdBuffer.Size > 0);
ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
IM_ASSERT(curr_cmd->UserCallback == NULL);
if (curr_cmd->ElemCount != 0)
@@ -481,15 +487,30 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data)
}
// Compare ClipRect, TextureId and VtxOffset with a single memcmp()
-#define ImDrawCmd_HeaderSize (IM_OFFSETOF(ImDrawCmd, VtxOffset) + sizeof(unsigned int))
-#define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS) (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize)) // Compare ClipRect, TextureId, VtxOffset
-#define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC) (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize)) // Copy ClipRect, TextureId, VtxOffset
+#define ImDrawCmd_HeaderSize (IM_OFFSETOF(ImDrawCmd, VtxOffset) + sizeof(unsigned int))
+#define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS) (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize)) // Compare ClipRect, TextureId, VtxOffset
+#define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC) (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize)) // Copy ClipRect, TextureId, VtxOffset
+#define ImDrawCmd_AreSequentialIdxOffset(CMD_0, CMD_1) (CMD_0->IdxOffset + CMD_0->ElemCount == CMD_1->IdxOffset)
+
+// Try to merge two last draw commands
+void ImDrawList::_TryMergeDrawCmds()
+{
+ IM_ASSERT_PARANOID(CmdBuffer.Size > 0);
+ ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
+ ImDrawCmd* prev_cmd = curr_cmd - 1;
+ if (ImDrawCmd_HeaderCompare(curr_cmd, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && curr_cmd->UserCallback == NULL && prev_cmd->UserCallback == NULL)
+ {
+ prev_cmd->ElemCount += curr_cmd->ElemCount;
+ CmdBuffer.pop_back();
+ }
+}
// Our scheme may appears a bit unusual, basically we want the most-common calls AddLine AddRect etc. to not have to perform any check so we always have a command ready in the stack.
// The cost of figuring out if a new command has to be added or if we can merge is paid in those Update** functions only.
void ImDrawList::_OnChangedClipRect()
{
// If current command is used with different settings we need to add a new command
+ IM_ASSERT_PARANOID(CmdBuffer.Size > 0);
ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
if (curr_cmd->ElemCount != 0 && memcmp(&curr_cmd->ClipRect, &_CmdHeader.ClipRect, sizeof(ImVec4)) != 0)
{
@@ -500,7 +521,7 @@ void ImDrawList::_OnChangedClipRect()
// Try to merge with previous command if it matches, else use current command
ImDrawCmd* prev_cmd = curr_cmd - 1;
- if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && prev_cmd->UserCallback == NULL)
+ if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && prev_cmd->UserCallback == NULL)
{
CmdBuffer.pop_back();
return;
@@ -512,6 +533,7 @@ void ImDrawList::_OnChangedClipRect()
void ImDrawList::_OnChangedTextureID()
{
// If current command is used with different settings we need to add a new command
+ IM_ASSERT_PARANOID(CmdBuffer.Size > 0);
ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
if (curr_cmd->ElemCount != 0 && curr_cmd->TextureId != _CmdHeader.TextureId)
{
@@ -522,7 +544,7 @@ void ImDrawList::_OnChangedTextureID()
// Try to merge with previous command if it matches, else use current command
ImDrawCmd* prev_cmd = curr_cmd - 1;
- if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && prev_cmd->UserCallback == NULL)
+ if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && prev_cmd->UserCallback == NULL)
{
CmdBuffer.pop_back();
return;
@@ -535,6 +557,7 @@ void ImDrawList::_OnChangedVtxOffset()
{
// We don't need to compare curr_cmd->VtxOffset != _CmdHeader.VtxOffset because we know it'll be different at the time we call this.
_VtxCurrentIdx = 0;
+ IM_ASSERT_PARANOID(CmdBuffer.Size > 0);
ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
//IM_ASSERT(curr_cmd->VtxOffset != _CmdHeader.VtxOffset); // See #3349
if (curr_cmd->ElemCount != 0)
@@ -687,9 +710,11 @@ void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, c
}
// On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superfluous function calls to optimize debug/non-inlined builds.
-// Those macros expects l-values.
-#define IM_NORMALIZE2F_OVER_ZERO(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = 1.0f / ImSqrt(d2); VX *= inv_len; VY *= inv_len; } } while (0)
-#define IM_FIXNORMAL2F(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 < 0.5f) d2 = 0.5f; float inv_lensq = 1.0f / d2; VX *= inv_lensq; VY *= inv_lensq; } while (0)
+// - Those macros expects l-values and need to be used as their own statement.
+// - Those macros are intentionally not surrounded by the 'do {} while (0)' idiom because even that translates to runtime with debug compilers.
+#define IM_NORMALIZE2F_OVER_ZERO(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = ImRsqrt(d2); VX *= inv_len; VY *= inv_len; } } (void)0
+#define IM_FIXNORMAL2F_MAX_INVLEN2 100.0f // 500.0f (see #4053, #3366)
+#define IM_FIXNORMAL2F(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.000001f) { float inv_len2 = 1.0f / d2; if (inv_len2 > IM_FIXNORMAL2F_MAX_INVLEN2) inv_len2 = IM_FIXNORMAL2F_MAX_INVLEN2; VX *= inv_len2; VY *= inv_len2; } } (void)0
// TODO: Thickness anti-aliased lines cap are missing their AA fringe.
// We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds.
@@ -1037,7 +1062,6 @@ void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_
_Path.push_back(center);
return;
}
- IM_ASSERT(a_min_sample <= a_max_sample);
// Calculate arc auto segment step size
if (a_step <= 0)
@@ -1046,17 +1070,7 @@ void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_
// Make sure we never do steps larger than one quarter of the circle
a_step = ImClamp(a_step, 1, IM_DRAWLIST_ARCFAST_TABLE_SIZE / 4);
- // Normalize a_min_sample to always start lie in [0..IM_DRAWLIST_ARCFAST_SAMPLE_MAX] range.
- if (a_min_sample < 0)
- {
- int normalized_sample = a_min_sample % IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
- if (normalized_sample < 0)
- normalized_sample += IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
- a_max_sample += (normalized_sample - a_min_sample);
- a_min_sample = normalized_sample;
- }
-
- const int sample_range = a_max_sample - a_min_sample;
+ const int sample_range = ImAbs(a_max_sample - a_min_sample);
const int a_next_step = a_step;
int samples = sample_range + 1;
@@ -1082,16 +1096,40 @@ void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_
ImVec2* out_ptr = _Path.Data + (_Path.Size - samples);
int sample_index = a_min_sample;
- for (int a = a_min_sample; a <= a_max_sample; a += a_step, sample_index += a_step, a_step = a_next_step)
+ if (sample_index < 0 || sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX)
{
- // a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more
- if (sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX)
- sample_index -= IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
+ sample_index = sample_index % IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
+ if (sample_index < 0)
+ sample_index += IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
+ }
- const ImVec2 s = _Data->ArcFastVtx[sample_index];
- out_ptr->x = center.x + s.x * radius;
- out_ptr->y = center.y + s.y * radius;
- out_ptr++;
+ if (a_max_sample >= a_min_sample)
+ {
+ for (int a = a_min_sample; a <= a_max_sample; a += a_step, sample_index += a_step, a_step = a_next_step)
+ {
+ // a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more
+ if (sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX)
+ sample_index -= IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
+
+ const ImVec2 s = _Data->ArcFastVtx[sample_index];
+ out_ptr->x = center.x + s.x * radius;
+ out_ptr->y = center.y + s.y * radius;
+ out_ptr++;
+ }
+ }
+ else
+ {
+ for (int a = a_min_sample; a >= a_max_sample; a -= a_step, sample_index -= a_step, a_step = a_next_step)
+ {
+ // a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more
+ if (sample_index < 0)
+ sample_index += IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
+
+ const ImVec2 s = _Data->ArcFastVtx[sample_index];
+ out_ptr->x = center.x + s.x * radius;
+ out_ptr->y = center.y + s.y * radius;
+ out_ptr++;
+ }
}
if (extra_max_sample)
@@ -1116,7 +1154,6 @@ void ImDrawList::_PathArcToN(const ImVec2& center, float radius, float a_min, fl
_Path.push_back(center);
return;
}
- IM_ASSERT(a_min <= a_max);
// Note that we are adding a point at both a_min and a_max.
// If you are trying to draw a full closed circle you don't want the overlapping points!
@@ -1136,7 +1173,6 @@ void ImDrawList::PathArcToFast(const ImVec2& center, float radius, int a_min_of_
_Path.push_back(center);
return;
}
- IM_ASSERT(a_min_of_12 <= a_max_of_12);
_PathArcToFastEx(center, radius, a_min_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, a_max_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, 0);
}
@@ -1147,7 +1183,6 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
_Path.push_back(center);
return;
}
- IM_ASSERT(a_min <= a_max);
if (num_segments > 0)
{
@@ -1158,28 +1193,33 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
// Automatic segment count
if (radius <= _Data->ArcFastRadiusCutoff)
{
+ const bool a_is_reverse = a_max < a_min;
+
// We are going to use precomputed values for mid samples.
// Determine first and last sample in lookup table that belong to the arc.
- const int a_min_sample = (int)ImCeil(IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / (IM_PI * 2.0f));
- const int a_max_sample = (int)( IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f));
- const int a_mid_samples = ImMax(a_max_sample - a_min_sample, 0);
+ const float a_min_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / (IM_PI * 2.0f);
+ const float a_max_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f);
+
+ const int a_min_sample = a_is_reverse ? (int)ImFloorSigned(a_min_sample_f) : (int)ImCeil(a_min_sample_f);
+ const int a_max_sample = a_is_reverse ? (int)ImCeil(a_max_sample_f) : (int)ImFloorSigned(a_max_sample_f);
+ const int a_mid_samples = a_is_reverse ? ImMax(a_min_sample - a_max_sample, 0) : ImMax(a_max_sample - a_min_sample, 0);
const float a_min_segment_angle = a_min_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
const float a_max_segment_angle = a_max_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
- const bool a_emit_start = (a_min_segment_angle - a_min) > 0.0f;
- const bool a_emit_end = (a_max - a_max_segment_angle) > 0.0f;
+ const bool a_emit_start = ImAbs(a_min_segment_angle - a_min) >= 1e-5f;
+ const bool a_emit_end = ImAbs(a_max - a_max_segment_angle) >= 1e-5f;
_Path.reserve(_Path.Size + (a_mid_samples + 1 + (a_emit_start ? 1 : 0) + (a_emit_end ? 1 : 0)));
if (a_emit_start)
_Path.push_back(ImVec2(center.x + ImCos(a_min) * radius, center.y + ImSin(a_min) * radius));
- if (a_max_sample >= a_min_sample)
+ if (a_mid_samples > 0)
_PathArcToFastEx(center, radius, a_min_sample, a_max_sample, 0);
if (a_emit_end)
_Path.push_back(ImVec2(center.x + ImCos(a_max) * radius, center.y + ImSin(a_max) * radius));
}
else
{
- const float arc_length = a_max - a_min;
+ const float arc_length = ImAbs(a_max - a_min);
const int circle_segment_count = _CalcCircleAutoSegmentCount(radius);
const int arc_segment_count = ImMax((int)ImCeil(circle_segment_count * arc_length / (IM_PI * 2.0f)), (int)(2.0f * IM_PI / arc_length));
_PathArcToN(center, radius, a_min, a_max, arc_segment_count);
@@ -1444,24 +1484,22 @@ void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int nu
if ((col & IM_COL32_A_MASK) == 0 || radius <= 0.0f)
return;
- // Obtain segment count
if (num_segments <= 0)
{
- // Automatic segment count
- num_segments = _CalcCircleAutoSegmentCount(radius);
+ // Use arc with automatic segment count
+ _PathArcToFastEx(center, radius - 0.5f, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0);
+ _Path.Size--;
}
else
{
// Explicit segment count (still clamp to avoid drawing insanely tessellated shapes)
num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX);
- }
- // 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;
- if (num_segments == 12)
- PathArcToFast(center, radius - 0.5f, 0, 12 - 1);
- else
+ // 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;
PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1);
+ }
+
PathStroke(col, ImDrawFlags_Closed, thickness);
}
@@ -1470,24 +1508,22 @@ void ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col,
if ((col & IM_COL32_A_MASK) == 0 || radius <= 0.0f)
return;
- // Obtain segment count
if (num_segments <= 0)
{
- // Automatic segment count
- num_segments = _CalcCircleAutoSegmentCount(radius);
+ // Use arc with automatic segment count
+ _PathArcToFastEx(center, radius, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0);
+ _Path.Size--;
}
else
{
// Explicit segment count (still clamp to avoid drawing insanely tessellated shapes)
num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX);
- }
- // 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;
- if (num_segments == 12)
- PathArcToFast(center, radius, 0, 12 - 1);
- else
+ // 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;
PathArcTo(center, radius, 0.0f, a_max, num_segments - 1);
+ }
+
PathFillConvex(col);
}
@@ -1697,13 +1733,13 @@ void ImDrawListSplitter::Merge(ImDrawList* draw_list)
for (int i = 1; i < _Count; i++)
{
ImDrawChannel& ch = _Channels[i];
-
- // Equivalent of PopUnusedDrawCmd() for this channel's cmdbuffer and except we don't need to test for UserCallback.
- if (ch._CmdBuffer.Size > 0 && ch._CmdBuffer.back().ElemCount == 0)
+ if (ch._CmdBuffer.Size > 0 && ch._CmdBuffer.back().ElemCount == 0 && ch._CmdBuffer.back().UserCallback == NULL) // Equivalent of PopUnusedDrawCmd()
ch._CmdBuffer.pop_back();
if (ch._CmdBuffer.Size > 0 && last_cmd != NULL)
{
+ // Do not include ImDrawCmd_AreSequentialIdxOffset() in the compare as we rebuild IdxOffset values ourselves.
+ // Manipulating IdxOffset (e.g. by reordering draw commands like done by RenderDimmedBackgroundBehindWindow()) is not supported within a splitter.
ImDrawCmd* next_cmd = &ch._CmdBuffer[0];
if (ImDrawCmd_HeaderCompare(last_cmd, next_cmd) == 0 && last_cmd->UserCallback == NULL && next_cmd->UserCallback == NULL)
{
@@ -1888,37 +1924,38 @@ ImFontConfig::ImFontConfig()
// A work of art lies ahead! (. = white layer, X = black layer, others are blank)
// The 2x2 white texels on the top left are the ones we'll use everywhere in Dear ImGui to render filled shapes.
-const int FONT_ATLAS_DEFAULT_TEX_DATA_W = 108; // Actual texture will be 2 times that + 1 spacing.
+// (This is used when io.MouseDrawCursor = true)
+const int FONT_ATLAS_DEFAULT_TEX_DATA_W = 122; // Actual texture will be 2 times that + 1 spacing.
const int FONT_ATLAS_DEFAULT_TEX_DATA_H = 27;
static const char FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[FONT_ATLAS_DEFAULT_TEX_DATA_W * FONT_ATLAS_DEFAULT_TEX_DATA_H + 1] =
{
- "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX- XX "
- "..- -X.....X- X.X - X.X -X.....X - X.....X- X..X "
- "--- -XXX.XXX- X...X - X...X -X....X - X....X- X..X "
- "X - X.X - X.....X - X.....X -X...X - X...X- X..X "
- "XX - X.X -X.......X- X.......X -X..X.X - X.X..X- X..X "
- "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X- X..XXX "
- "X..X - X.X - X.X - X.X -XX X.X - X.X XX- X..X..XXX "
- "X...X - X.X - X.X - XX X.X XX - X.X - X.X - X..X..X..XX "
- "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X - X..X..X..X.X "
- "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X -XXX X..X..X..X..X"
- "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X -X..XX........X..X"
- "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X -X...X...........X"
- "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X - X..............X"
- "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X - X.............X"
- "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X - X.............X"
- "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X - X............X"
- "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX - X...........X "
- "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------- X..........X "
- "X.X X..X - -X.......X- X.......X - XX XX - - X..........X "
- "XX X..X - - X.....X - X.....X - X.X X.X - - X........X "
- " X..X - X...X - X...X - X..X X..X - - X........X "
- " XX - X.X - X.X - X...XXXXXXXXXXXXX...X - - XXXXXXXXXX "
- "------------ - X - X -X.....................X- ------------------"
- " ----------------------------------- X...XXXXXXXXXXXXX...X - "
- " - X..X X..X - "
- " - X.X X.X - "
- " - XX XX - "
+ "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX- XX - XX XX "
+ "..- -X.....X- X.X - X.X -X.....X - X.....X- X..X -X..X X..X"
+ "--- -XXX.XXX- X...X - X...X -X....X - X....X- X..X -X...X X...X"
+ "X - X.X - X.....X - X.....X -X...X - X...X- X..X - X...X X...X "
+ "XX - X.X -X.......X- X.......X -X..X.X - X.X..X- X..X - X...X...X "
+ "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X- X..XXX - X.....X "
+ "X..X - X.X - X.X - X.X -XX X.X - X.X XX- X..X..XXX - X...X "
+ "X...X - X.X - X.X - XX X.X XX - X.X - X.X - X..X..X..XX - X.X "
+ "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X - X..X..X..X.X - X...X "
+ "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X -XXX X..X..X..X..X- X.....X "
+ "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X -X..XX........X..X- X...X...X "
+ "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X -X...X...........X- X...X X...X "
+ "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X - X..............X-X...X X...X"
+ "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X - X.............X-X..X X..X"
+ "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X - X.............X- XX XX "
+ "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X - X............X--------------"
+ "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX - X...........X - "
+ "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------- X..........X - "
+ "X.X X..X - -X.......X- X.......X - XX XX - - X..........X - "
+ "XX X..X - - X.....X - X.....X - X.X X.X - - X........X - "
+ " X..X - - X...X - X...X - X..X X..X - - X........X - "
+ " XX - - X.X - X.X - X...XXXXXXXXXXXXX...X - - XXXXXXXXXX - "
+ "------------- - X - X -X.....................X- ------------------- "
+ " ----------------------------------- X...XXXXXXXXXXXXX...X - "
+ " - X..X X..X - "
+ " - X.X X.X - "
+ " - XX XX - "
};
static const ImVec2 FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[ImGuiMouseCursor_COUNT][3] =
@@ -1932,6 +1969,7 @@ static const ImVec2 FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[ImGuiMouseCursor_COUNT][3
{ ImVec2(73,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNESW
{ ImVec2(55,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNWSE
{ ImVec2(91,0), ImVec2(17,22), ImVec2( 5, 0) }, // ImGuiMouseCursor_Hand
+ { ImVec2(109,0),ImVec2(13,15), ImVec2( 6, 7) }, // ImGuiMouseCursor_NotAllowed
};
ImFontAtlas::ImFontAtlas()
@@ -1967,6 +2005,7 @@ void ImFontAtlas::ClearInputData()
ConfigData.clear();
CustomRects.clear();
PackIdMouseCursors = PackIdLines = -1;
+ // Important: we leave TexReady untouched
}
void ImFontAtlas::ClearTexData()
@@ -1979,14 +2018,14 @@ void ImFontAtlas::ClearTexData()
TexPixelsAlpha8 = NULL;
TexPixelsRGBA32 = NULL;
TexPixelsUseColors = false;
+ // Important: we leave TexReady untouched
}
void ImFontAtlas::ClearFonts()
{
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
- for (int i = 0; i < Fonts.Size; i++)
- IM_DELETE(Fonts[i]);
- Fonts.clear();
+ Fonts.clear_delete();
+ TexReady = false;
}
void ImFontAtlas::Clear()
@@ -2000,11 +2039,7 @@ void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_wid
{
// Build atlas on demand
if (TexPixelsAlpha8 == NULL)
- {
- if (ConfigData.empty())
- AddFontDefault();
Build();
- }
*out_pixels = TexPixelsAlpha8;
if (out_width) *out_width = TexWidth;
@@ -2063,6 +2098,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
new_font_cfg.DstFont->EllipsisChar = font_cfg->EllipsisChar;
// Invalidate texture
+ TexReady = false;
ClearTexData();
return new_font_cfg.DstFont;
}
@@ -2134,7 +2170,7 @@ ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float si
IM_ASSERT(font_cfg.FontData == NULL);
font_cfg.FontData = ttf_data;
font_cfg.FontDataSize = ttf_size;
- font_cfg.SizePixels = size_pixels;
+ font_cfg.SizePixels = size_pixels > 0.0f ? size_pixels : font_cfg.SizePixels;
if (glyph_ranges)
font_cfg.GlyphRanges = glyph_ranges;
return AddFont(&font_cfg);
@@ -2225,6 +2261,10 @@ bool ImFontAtlas::Build()
{
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
+ // Default font is none are specified
+ if (ConfigData.Size == 0)
+ AddFontDefault();
+
// Select builder
// - Note that we do not reassign to atlas->FontBuilderIO, since it is likely to point to static data which
// may mess with some hot-reloading schemes. If you need to assign to this (for dynamic selection) AND are
@@ -2546,9 +2586,8 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
}
}
- // Cleanup temporary (ImVector doesn't honor destructor)
- for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
- src_tmp_array[src_i].~ImFontBuildSrcData();
+ // Cleanup
+ src_tmp_array.clear_destruct();
ImFontAtlasBuildFinish(atlas);
return true;
@@ -2764,22 +2803,7 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas)
if (atlas->Fonts[i]->DirtyLookupTables)
atlas->Fonts[i]->BuildLookupTable();
- // Ellipsis character is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis).
- // However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character.
- // FIXME: Also note that 0x2026 is currently seldom included in our font ranges. Because of this we are more likely to use three individual dots.
- for (int i = 0; i < atlas->Fonts.size(); i++)
- {
- ImFont* font = atlas->Fonts[i];
- if (font->EllipsisChar != (ImWchar)-1)
- continue;
- const ImWchar ellipsis_variants[] = { (ImWchar)0x2026, (ImWchar)0x0085 };
- for (int j = 0; j < IM_ARRAYSIZE(ellipsis_variants); j++)
- if (font->FindGlyphNoFallback(ellipsis_variants[j]) != NULL) // Verify glyph exists
- {
- font->EllipsisChar = ellipsis_variants[j];
- break;
- }
- }
+ atlas->TexReady = true;
}
// Retrieve list of range (2 int per range, values are inclusive)
@@ -2800,6 +2824,7 @@ const ImWchar* ImFontAtlas::GetGlyphRangesKorean()
0x0020, 0x00FF, // Basic Latin + Latin Supplement
0x3131, 0x3163, // Korean alphabets
0xAC00, 0xD7A3, // Korean characters
+ 0xFFFD, 0xFFFD, // Invalid
0,
};
return &ranges[0];
@@ -2814,6 +2839,7 @@ const ImWchar* ImFontAtlas::GetGlyphRangesChineseFull()
0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana
0x31F0, 0x31FF, // Katakana Phonetic Extensions
0xFF00, 0xFFEF, // Half-width characters
+ 0xFFFD, 0xFFFD, // Invalid
0x4e00, 0x9FAF, // CJK Ideograms
0,
};
@@ -2890,7 +2916,8 @@ const ImWchar* ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon()
0x2000, 0x206F, // General Punctuation
0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana
0x31F0, 0x31FF, // Katakana Phonetic Extensions
- 0xFF00, 0xFFEF // Half-width characters
+ 0xFF00, 0xFFEF, // Half-width characters
+ 0xFFFD, 0xFFFD // Invalid
};
static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00) * 2 + 1] = { 0 };
if (!full_ranges[0])
@@ -2979,7 +3006,8 @@ const ImWchar* ImFontAtlas::GetGlyphRangesJapanese()
0x0020, 0x00FF, // Basic Latin + Latin Supplement
0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana
0x31F0, 0x31FF, // Katakana Phonetic Extensions
- 0xFF00, 0xFFEF // Half-width characters
+ 0xFF00, 0xFFEF, // Half-width characters
+ 0xFFFD, 0xFFFD // Invalid
};
static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00)*2 + 1] = { 0 };
if (!full_ranges[0])
@@ -3052,8 +3080,8 @@ void ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end)
void ImFontGlyphRangesBuilder::AddRanges(const ImWchar* ranges)
{
for (; ranges[0]; ranges += 2)
- for (ImWchar c = ranges[0]; c <= ranges[1]; c++)
- AddChar(c);
+ for (unsigned int c = ranges[0]; c <= ranges[1] && c <= IM_UNICODE_CODEPOINT_MAX; c++) //-V560
+ AddChar((ImWchar)c);
}
void ImFontGlyphRangesBuilder::BuildRanges(ImVector<ImWchar>* out_ranges)
@@ -3078,8 +3106,9 @@ ImFont::ImFont()
{
FontSize = 0.0f;
FallbackAdvanceX = 0.0f;
- FallbackChar = (ImWchar)'?';
+ FallbackChar = (ImWchar)-1;
EllipsisChar = (ImWchar)-1;
+ DotChar = (ImWchar)-1;
FallbackGlyph = NULL;
ContainerAtlas = NULL;
ConfigData = NULL;
@@ -3110,6 +3139,14 @@ void ImFont::ClearOutputData()
MetricsTotalSurface = 0;
}
+static ImWchar FindFirstExistingGlyph(ImFont* font, const ImWchar* candidate_chars, int candidate_chars_count)
+{
+ for (int n = 0; n < candidate_chars_count; n++)
+ if (font->FindGlyphNoFallback(candidate_chars[n]) != NULL)
+ return candidate_chars[n];
+ return (ImWchar)-1;
+}
+
void ImFont::BuildLookupTable()
{
int max_codepoint = 0;
@@ -3152,9 +3189,31 @@ void ImFont::BuildLookupTable()
SetGlyphVisible((ImWchar)' ', false);
SetGlyphVisible((ImWchar)'\t', false);
- // Setup fall-backs
+ // Ellipsis character is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis).
+ // However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character.
+ // FIXME: Note that 0x2026 is rarely included in our font ranges. Because of this we are more likely to use three individual dots.
+ const ImWchar ellipsis_chars[] = { (ImWchar)0x2026, (ImWchar)0x0085 };
+ const ImWchar dots_chars[] = { (ImWchar)'.', (ImWchar)0xFF0E };
+ if (EllipsisChar == (ImWchar)-1)
+ EllipsisChar = FindFirstExistingGlyph(this, ellipsis_chars, IM_ARRAYSIZE(ellipsis_chars));
+ if (DotChar == (ImWchar)-1)
+ DotChar = FindFirstExistingGlyph(this, dots_chars, IM_ARRAYSIZE(dots_chars));
+
+ // Setup fallback character
+ const ImWchar fallback_chars[] = { (ImWchar)IM_UNICODE_CODEPOINT_INVALID, (ImWchar)'?', (ImWchar)' ' };
FallbackGlyph = FindGlyphNoFallback(FallbackChar);
- FallbackAdvanceX = FallbackGlyph ? FallbackGlyph->AdvanceX : 0.0f;
+ if (FallbackGlyph == NULL)
+ {
+ FallbackChar = FindFirstExistingGlyph(this, fallback_chars, IM_ARRAYSIZE(fallback_chars));
+ FallbackGlyph = FindGlyphNoFallback(FallbackChar);
+ if (FallbackGlyph == NULL)
+ {
+ FallbackGlyph = &Glyphs.back();
+ FallbackChar = (ImWchar)FallbackGlyph->Codepoint;
+ }
+ }
+
+ FallbackAdvanceX = FallbackGlyph->AdvanceX;
for (int i = 0; i < max_codepoint + 1; i++)
if (IndexAdvanceX[i] < 0.0f)
IndexAdvanceX[i] = FallbackAdvanceX;
@@ -3179,12 +3238,6 @@ void ImFont::SetGlyphVisible(ImWchar c, bool visible)
glyph->Visible = visible ? 1 : 0;
}
-void ImFont::SetFallbackChar(ImWchar c)
-{
- FallbackChar = c;
- BuildLookupTable();
-}
-
void ImFont::GrowIndex(int new_size)
{
IM_ASSERT(IndexAdvanceX.Size == IndexLookup.Size);
@@ -3469,6 +3522,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
return text_size;
}
+// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, ImWchar c) const
{
const ImFontGlyph* glyph = FindGlyph(c);
@@ -3483,6 +3537,7 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
draw_list->PrimRectUV(ImVec2(pos.x + glyph->X0 * scale, pos.y + glyph->Y0 * scale), ImVec2(pos.x + glyph->X1 * scale, pos.y + glyph->Y1 * scale), ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col);
}
+// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) const
{
if (!text_end)
@@ -3681,9 +3736,9 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
// - RenderArrow()
// - RenderBullet()
// - RenderCheckMark()
-// - RenderMouseCursor()
// - RenderArrowPointingAt()
// - RenderRectFilledRangeH()
+// - RenderRectFilledWithHole()
//-----------------------------------------------------------------------------
// Function in need of a redesign (legacy mess)
// - RenderColorRectWithAlphaCheckerboard()
@@ -3741,27 +3796,6 @@ void ImGui::RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float
draw_list->PathStroke(col, 0, thickness);
}
-void ImGui::RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow)
-{
- if (mouse_cursor == ImGuiMouseCursor_None)
- return;
- IM_ASSERT(mouse_cursor > ImGuiMouseCursor_None && mouse_cursor < ImGuiMouseCursor_COUNT);
-
- ImFontAtlas* font_atlas = draw_list->_Data->Font->ContainerAtlas;
- ImVec2 offset, size, uv[4];
- if (font_atlas->GetMouseCursorTexData(mouse_cursor, &offset, &size, &uv[0], &uv[2]))
- {
- pos -= offset;
- const ImTextureID tex_id = font_atlas->TexID;
- draw_list->PushTextureID(tex_id);
- draw_list->AddImage(tex_id, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow);
- draw_list->AddImage(tex_id, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow);
- draw_list->AddImage(tex_id, pos, pos + size * scale, uv[2], uv[3], col_border);
- draw_list->AddImage(tex_id, pos, pos + size * scale, uv[0], uv[1], col_fill);
- draw_list->PopTextureID();
- }
-}
-
// Render an arrow. 'pos' is position of the arrow tip. half_sz.x is length from base to tip. half_sz.y is length on each side.
void ImGui::RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col)
{
@@ -3850,10 +3884,10 @@ void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, ImRect outer, ImRect
const bool fill_R = (inner.Max.x < outer.Max.x);
const bool fill_U = (inner.Min.y > outer.Min.y);
const bool fill_D = (inner.Max.y < outer.Max.y);
- if (fill_L) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Min.y), ImVec2(inner.Min.x, inner.Max.y), col, rounding, (fill_U ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomLeft));
- if (fill_R) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Min.y), ImVec2(outer.Max.x, inner.Max.y), col, rounding, (fill_U ? 0 : ImDrawFlags_RoundCornersTopRight) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomRight));
- if (fill_U) draw_list->AddRectFilled(ImVec2(inner.Min.x, outer.Min.y), ImVec2(inner.Max.x, inner.Min.y), col, rounding, (fill_L ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersTopRight));
- if (fill_D) draw_list->AddRectFilled(ImVec2(inner.Min.x, inner.Max.y), ImVec2(inner.Max.x, outer.Max.y), col, rounding, (fill_L ? 0 : ImDrawFlags_RoundCornersBottomLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersBottomRight));
+ if (fill_L) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Min.y), ImVec2(inner.Min.x, inner.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_U ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomLeft));
+ if (fill_R) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Min.y), ImVec2(outer.Max.x, inner.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_U ? 0 : ImDrawFlags_RoundCornersTopRight) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomRight));
+ if (fill_U) draw_list->AddRectFilled(ImVec2(inner.Min.x, outer.Min.y), ImVec2(inner.Max.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_L ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersTopRight));
+ if (fill_D) draw_list->AddRectFilled(ImVec2(inner.Min.x, inner.Max.y), ImVec2(inner.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_L ? 0 : ImDrawFlags_RoundCornersBottomLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersBottomRight));
if (fill_L && fill_U) draw_list->AddRectFilled(ImVec2(outer.Min.x, outer.Min.y), ImVec2(inner.Min.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersTopLeft);
if (fill_R && fill_U) draw_list->AddRectFilled(ImVec2(inner.Max.x, outer.Min.y), ImVec2(outer.Max.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersTopRight);
if (fill_L && fill_D) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Max.y), ImVec2(inner.Min.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomLeft);